From 49058d5dfbdaa3d774463e980b1969b57ed0d8c9 Mon Sep 17 00:00:00 2001 From: "yefu.chen" Date: Thu, 20 Aug 2020 10:23:45 +0800 Subject: [PATCH] start 2.0 Signed-off-by: yefu.chen --- .all-contributorsrc | 78 - .clang-format | 27 - .clang-tidy | 33 - .clang-tidy-ignore | 17 - .codacy.yaml | 7 - .codebeatignore | 3 - .env | 6 - .github/ISSUE_TEMPLATE/bug_report.md | 29 - .../ISSUE_TEMPLATE/documentation-request.md | 35 - .github/ISSUE_TEMPLATE/feature_request.md | 20 - .github/ISSUE_TEMPLATE/general-question.md | 10 - .github/PULL_REQUEST_TEMPLATE.md | 18 - .github/workflows/core.yml | 118 - .gitignore | 35 - .hadolint.yaml | 6 - .mergify/config.yml | 19 - CHANGELOG.md | 888 - CODEOWNERS | 9 - CODE_OF_CONDUCT.md | 48 - COMMUNITY.md | 99 - CONTRIBUTING.md | 135 - DESIGN.md | 13 - INSTALL.md | 282 - LICENSE | 201 - NOTICE.md | 24 - OSSMETADATA | 2 - README.md | 92 - README_CN.md | 95 - RELEASE.md | 17 - SECURITY.md | 15 - SUPPORT.md | 14 - ci/docker/centos-7-core.dockerfile | 33 - ci/docker/ubuntu-18.04-core.dockerfile | 43 - ci/jenkins/Jenkinsfile | 244 - ci/jenkins/pod/docker-pod.yaml | 34 - ...vus-cpu-version-centos7-build-env-pod.yaml | 50 - ...cpu-version-ubuntu18.04-build-env-pod.yaml | 50 - ...vus-gpu-version-centos7-build-env-pod.yaml | 52 - ...gpu-version-ubuntu18.04-build-env-pod.yaml | 52 - ci/jenkins/pod/testEnvironment.yaml | 33 - ci/jenkins/scripts/mail.py | 36 - ci/jenkins/scripts/requirements.txt | 2 - ci/jenkins/scripts/yaml_processor.py | 536 - ci/jenkins/step/build.groovy | 14 - ci/jenkins/step/cleanupShardsDev.groovy | 12 - ci/jenkins/step/cleanupSingleDev.groovy | 12 - ci/jenkins/step/coverage.groovy | 16 - ci/jenkins/step/package.groovy | 8 - ci/jenkins/step/publishImages.groovy | 50 - ci/jenkins/step/shardsDevNightlyTest.groovy | 35 - ci/jenkins/step/singleDevNightlyTest.groovy | 60 - ci/jenkins/step/singleDevTest.groovy | 32 - ci/jenkins/step/unittest.groovy | 5 - ci/scripts/before-install.sh | 10 - ci/scripts/build.sh | 170 - ci/scripts/check_ccache.sh | 106 - ci/scripts/coverage.sh | 104 - ci/scripts/run_unittest.sh | 121 - ci/scripts/update_ccache.sh | 104 - codecov.yaml | 31 - core/.gitignore | 14 - core/CMakeLists.txt | 332 - core/build-support/code_style_clion.xml | 38 - core/build-support/cpplint.py | 6476 ----- core/build-support/lint_exclusions.txt | 11 - core/build-support/lintutils.py | 110 - core/build-support/run_clang_format.py | 142 - core/build-support/run_clang_tidy.py | 126 - core/build-support/run_cpplint.py | 132 - core/build.sh | 150 - core/centos7_build_deps.sh | 12 - core/cmake/BuildUtils.cmake | 204 - core/cmake/DefineOptions.cmake | 173 - core/cmake/FindClangTools.cmake | 109 - core/cmake/ThirdPartyPackages.cmake | 1160 - core/conf/demo/server_config.yaml | 189 - core/conf/server_config.template | 190 - core/conf/tracing_config.json | 26 - core/coverage.sh | 132 - core/scripts/migration/README.md | 28 - core/scripts/migration/mysql_4_to_6.sql | 4 - core/scripts/migration/mysql_6_to_4.sql | 3 - core/scripts/migration/sqlite_4_to_6.sql | 4 - core/scripts/migration/sqlite_6_to_4.sql | 7 - core/scripts/start_server.sh | 4 - core/scripts/stop_server.sh | 16 - core/src/CMakeLists.txt | 343 - core/src/cache/Cache.h | 104 - core/src/cache/Cache.inl | 191 - core/src/cache/CacheMgr.h | 72 - core/src/cache/CacheMgr.inl | 137 - core/src/cache/CpuCacheMgr.cpp | 66 - core/src/cache/CpuCacheMgr.h | 42 - core/src/cache/DataObj.h | 28 - core/src/cache/GpuCacheMgr.cpp | 94 - core/src/cache/GpuCacheMgr.h | 62 - core/src/cache/LRU.h | 116 - core/src/codecs/AttrsFormat.h | 44 - core/src/codecs/AttrsIndexFormat.h | 33 - core/src/codecs/Codec.h | 63 - core/src/codecs/DeletedDocsFormat.h | 43 - core/src/codecs/IdBloomFilterFormat.h | 43 - core/src/codecs/IdIndexFormat.h | 33 - core/src/codecs/VectorIndexFormat.h | 42 - core/src/codecs/VectorsFormat.h | 48 - .../src/codecs/default/DefaultAttrsFormat.cpp | 222 - core/src/codecs/default/DefaultAttrsFormat.h | 68 - core/src/codecs/default/DefaultCodec.cpp | 65 - core/src/codecs/default/DefaultCodec.h | 53 - .../default/DefaultDeletedDocsFormat.cpp | 180 - .../codecs/default/DefaultDeletedDocsFormat.h | 57 - .../default/DefaultIdBloomFilterFormat.cpp | 83 - .../default/DefaultIdBloomFilterFormat.h | 59 - .../default/DefaultVectorIndexFormat.cpp | 159 - .../codecs/default/DefaultVectorIndexFormat.h | 60 - .../codecs/default/DefaultVectorsFormat.cpp | 190 - .../src/codecs/default/DefaultVectorsFormat.h | 73 - core/src/config/Config.cpp | 2883 --- core/src/config/Config.h | 570 - core/src/config/ConfigMgr.h | 41 - core/src/config/ConfigNode.cpp | 229 - core/src/config/ConfigNode.h | 89 - core/src/config/Utils.cpp | 85 - core/src/config/Utils.h | 23 - core/src/config/YamlConfigMgr.cpp | 102 - core/src/config/YamlConfigMgr.h | 65 - .../src/config/handler/CacheConfigHandler.cpp | 96 - core/src/config/handler/CacheConfigHandler.h | 64 - core/src/config/handler/ConfigHandler.h | 39 - .../config/handler/EngineConfigHandler.cpp | 76 - core/src/config/handler/EngineConfigHandler.h | 55 - .../handler/GpuResourceConfigHandler.cpp | 167 - .../config/handler/GpuResourceConfigHandler.h | 94 - core/src/context/HybridSearchContext.h | 39 - core/src/db/Constants.h | 29 - core/src/db/DB.h | 181 - core/src/db/DBFactory.cpp | 42 - core/src/db/DBFactory.h | 33 - core/src/db/DBImpl.cpp | 2845 --- core/src/db/DBImpl.h | 374 - core/src/db/IDGenerator.cpp | 125 - core/src/db/IDGenerator.h | 80 - core/src/db/IndexFailedChecker.cpp | 120 - core/src/db/IndexFailedChecker.h | 51 - core/src/db/Options.cpp | 95 - core/src/db/Options.h | 88 - core/src/db/Types.h | 63 - core/src/db/Utils.cpp | 336 - core/src/db/Utils.h | 84 - core/src/db/engine/EngineFactory.cpp | 59 - core/src/db/engine/EngineFactory.h | 39 - core/src/db/engine/ExecutionEngine.h | 155 - core/src/db/engine/ExecutionEngineImpl.cpp | 1320 - core/src/db/engine/ExecutionEngineImpl.h | 147 - core/src/db/insert/MemManager.h | 74 - core/src/db/insert/MemManagerFactory.cpp | 34 - core/src/db/insert/MemManagerFactory.h | 29 - core/src/db/insert/MemManagerImpl.cpp | 295 - core/src/db/insert/MemManagerImpl.h | 117 - core/src/db/insert/MemTable.cpp | 416 - core/src/db/insert/MemTable.h | 98 - core/src/db/insert/MemTableFile.cpp | 254 - core/src/db/insert/MemTableFile.h | 87 - core/src/db/insert/VectorSource.cpp | 184 - core/src/db/insert/VectorSource.h | 75 - core/src/db/merge/MergeAdaptiveStrategy.cpp | 89 - core/src/db/merge/MergeAdaptiveStrategy.h | 29 - core/src/db/merge/MergeLayeredStrategy.cpp | 165 - core/src/db/merge/MergeLayeredStrategy.h | 29 - core/src/db/merge/MergeManager.h | 62 - core/src/db/merge/MergeManagerFactory.cpp | 26 - core/src/db/merge/MergeManagerFactory.h | 29 - core/src/db/merge/MergeManagerImpl.cpp | 95 - core/src/db/merge/MergeManagerImpl.h | 54 - core/src/db/merge/MergeSimpleStrategy.cpp | 25 - core/src/db/merge/MergeSimpleStrategy.h | 29 - core/src/db/merge/MergeStrategy.h | 38 - core/src/db/merge/MergeTask.cpp | 128 - core/src/db/merge/MergeTask.h | 36 - core/src/db/meta/FilesHolder.cpp | 237 - core/src/db/meta/FilesHolder.h | 113 - core/src/db/meta/Meta.cpp | 27 - core/src/db/meta/Meta.h | 183 - core/src/db/meta/MetaConsts.h | 38 - core/src/db/meta/MetaFactory.cpp | 71 - core/src/db/meta/MetaFactory.h | 32 - core/src/db/meta/MetaTypes.h | 207 - core/src/db/meta/MySQLConnectionPool.cpp | 85 - core/src/db/meta/MySQLConnectionPool.h | 81 - core/src/db/meta/MySQLMetaImpl.cpp | 3209 --- core/src/db/meta/MySQLMetaImpl.h | 191 - core/src/db/meta/SqliteMetaImpl.cpp | 2265 -- core/src/db/meta/SqliteMetaImpl.h | 187 - core/src/db/wal/WalBuffer.cpp | 679 - core/src/db/wal/WalBuffer.h | 126 - core/src/db/wal/WalDefinations.h | 60 - core/src/db/wal/WalFileHandler.cpp | 141 - core/src/db/wal/WalFileHandler.h | 65 - core/src/db/wal/WalManager.cpp | 696 - core/src/db/wal/WalManager.h | 234 - core/src/db/wal/WalMetaHandler.cpp | 72 - core/src/db/wal/WalMetaHandler.h | 50 - core/src/grpc/cpp_gen.sh | 9 - core/src/grpc/gen-milvus/milvus.grpc.pb.cc | 1723 -- core/src/grpc/gen-milvus/milvus.grpc.pb.h | 7040 ------ core/src/grpc/gen-milvus/milvus.pb.cc | 20489 --------------- core/src/grpc/gen-milvus/milvus.pb.h | 14035 ----------- core/src/grpc/gen-status/status.grpc.pb.cc | 24 - core/src/grpc/gen-status/status.grpc.pb.h | 46 - core/src/grpc/gen-status/status.pb.cc | 461 - core/src/grpc/gen-status/status.pb.h | 360 - core/src/grpc/milvus.proto | 704 - core/src/grpc/status.proto | 35 - core/src/index/.gitignore | 1 - core/src/index/CMakeLists.txt | 106 - core/src/index/archive/KnowhereResource.cpp | 116 - core/src/index/archive/KnowhereResource.h | 29 - core/src/index/build.sh | 83 - core/src/index/cmake/BuildUtilsCore.cmake | 204 - core/src/index/cmake/DefineOptionsCore.cmake | 162 - core/src/index/cmake/FindArrow.cmake | 431 - core/src/index/cmake/FindOpenBLAS.cmake | 93 - .../index/cmake/ThirdPartyPackagesCore.cmake | 627 - core/src/index/knowhere/CMakeLists.txt | 125 - .../knowhere/knowhere/common/BinarySet.h | 76 - .../index/knowhere/knowhere/common/Config.h | 22 - .../index/knowhere/knowhere/common/Dataset.h | 61 - .../knowhere/knowhere/common/Exception.cpp | 45 - .../knowhere/knowhere/common/Exception.h | 49 - .../index/knowhere/knowhere/common/Log.cpp | 85 - core/src/index/knowhere/knowhere/common/Log.h | 74 - .../index/knowhere/knowhere/common/Timer.cpp | 85 - .../index/knowhere/knowhere/common/Timer.h | 49 - .../index/knowhere/knowhere/common/Typedef.h | 29 - .../src/index/knowhere/knowhere/index/Index.h | 45 - .../index/preprocessor/Preprocessor.h | 30 - .../index/vector_index/ConfAdapter.cpp | 303 - .../knowhere/index/vector_index/ConfAdapter.h | 98 - .../index/vector_index/ConfAdapterMgr.cpp | 53 - .../index/vector_index/ConfAdapterMgr.h | 51 - .../vector_index/FaissBaseBinaryIndex.cpp | 51 - .../index/vector_index/FaissBaseBinaryIndex.h | 42 - .../index/vector_index/FaissBaseIndex.cpp | 57 - .../index/vector_index/FaissBaseIndex.h | 45 - .../index/vector_index/IndexAnnoy.cpp | 174 - .../knowhere/index/vector_index/IndexAnnoy.h | 74 - .../index/vector_index/IndexBinaryIDMAP.cpp | 162 - .../index/vector_index/IndexBinaryIDMAP.h | 81 - .../index/vector_index/IndexBinaryIVF.cpp | 231 - .../index/vector_index/IndexBinaryIVF.h | 98 - .../knowhere/index/vector_index/IndexHNSW.cpp | 217 - .../knowhere/index/vector_index/IndexHNSW.h | 67 - .../index/vector_index/IndexIDMAP.cpp | 233 - .../knowhere/index/vector_index/IndexIDMAP.h | 92 - .../knowhere/index/vector_index/IndexIVF.cpp | 353 - .../knowhere/index/vector_index/IndexIVF.h | 101 - .../index/vector_index/IndexIVFPQ.cpp | 100 - .../knowhere/index/vector_index/IndexIVFPQ.h | 49 - .../index/vector_index/IndexIVFSQ.cpp | 81 - .../knowhere/index/vector_index/IndexIVFSQ.h | 45 - .../knowhere/index/vector_index/IndexNSG.cpp | 184 - .../knowhere/index/vector_index/IndexNSG.h | 82 - .../index/vector_index/IndexSPTAG.cpp | 241 - .../knowhere/index/vector_index/IndexSPTAG.h | 77 - .../knowhere/index/vector_index/IndexType.cpp | 94 - .../knowhere/index/vector_index/IndexType.h | 69 - .../knowhere/index/vector_index/VecIndex.h | 152 - .../index/vector_index/VecIndexFactory.cpp | 90 - .../index/vector_index/VecIndexFactory.h | 41 - .../vector_index/adapter/SptagAdapter.cpp | 84 - .../index/vector_index/adapter/SptagAdapter.h | 37 - .../vector_index/adapter/VectorAdapter.cpp | 41 - .../vector_index/adapter/VectorAdapter.h | 39 - .../index/vector_index/gpu/GPUIndex.h | 50 - .../index/vector_index/gpu/IndexGPUIDMAP.cpp | 153 - .../index/vector_index/gpu/IndexGPUIDMAP.h | 62 - .../index/vector_index/gpu/IndexGPUIVF.cpp | 159 - .../index/vector_index/gpu/IndexGPUIVF.h | 60 - .../index/vector_index/gpu/IndexGPUIVFPQ.cpp | 73 - .../index/vector_index/gpu/IndexGPUIVFPQ.h | 47 - .../index/vector_index/gpu/IndexGPUIVFSQ.cpp | 64 - .../index/vector_index/gpu/IndexGPUIVFSQ.h | 43 - .../vector_index/gpu/IndexIVFSQHybrid.cpp | 290 - .../index/vector_index/gpu/IndexIVFSQHybrid.h | 103 - .../index/vector_index/gpu/Quantizer.h | 33 - .../vector_index/helpers/BuilderSuspend.h | 30 - .../index/vector_index/helpers/Cloner.cpp | 74 - .../index/vector_index/helpers/Cloner.h | 28 - .../helpers/FaissGpuResourceMgr.cpp | 134 - .../helpers/FaissGpuResourceMgr.h | 130 - .../index/vector_index/helpers/FaissIO.cpp | 70 - .../index/vector_index/helpers/FaissIO.h | 54 - .../vector_index/helpers/IndexParameter.cpp | 48 - .../vector_index/helpers/IndexParameter.h | 68 - .../helpers/SPTAGParameterMgr.cpp | 69 - .../vector_index/helpers/SPTAGParameterMgr.h | 55 - .../index/vector_index/impl/nsg/Distance.cpp | 247 - .../index/vector_index/impl/nsg/Distance.h | 35 - .../index/vector_index/impl/nsg/NSG.cpp | 893 - .../index/vector_index/impl/nsg/NSG.h | 150 - .../index/vector_index/impl/nsg/NSGHelper.cpp | 67 - .../index/vector_index/impl/nsg/NSGHelper.h | 25 - .../index/vector_index/impl/nsg/NSGIO.cpp | 68 - .../index/vector_index/impl/nsg/NSGIO.h | 29 - .../index/vector_index/impl/nsg/Neighbor.h | 46 - .../.github/ISSUE_TEMPLATE/bug_report.md | 35 - .../.github/ISSUE_TEMPLATE/feature_request.md | 17 - core/src/index/thirdparty/SPTAG/.gitignore | 91 - .../thirdparty/SPTAG/AnnService.users.props | 18 - .../SPTAG/AnnService/Aggregator.vcxproj | 178 - .../AnnService/Aggregator.vcxproj.filters | 44 - .../SPTAG/AnnService/CMakeLists.txt | 41 - .../SPTAG/AnnService/Client.vcxproj | 145 - .../SPTAG/AnnService/Client.vcxproj.filters | 32 - .../SPTAG/AnnService/CoreLibrary.vcxproj | 192 - .../AnnService/CoreLibrary.vcxproj.filters | 202 - .../SPTAG/AnnService/IndexBuilder.vcxproj | 173 - .../AnnService/IndexBuilder.vcxproj.filters | 32 - .../SPTAG/AnnService/IndexSearcher.vcxproj | 170 - .../AnnService/IndexSearcher.vcxproj.filters | 25 - .../SPTAG/AnnService/Server.vcxproj | 153 - .../SPTAG/AnnService/Server.vcxproj.filters | 56 - .../SPTAG/AnnService/SocketLib.vcxproj | 123 - .../AnnService/SocketLib.vcxproj.filters | 65 - .../inc/Aggregator/AggregatorContext.h | 67 - .../Aggregator/AggregatorExecutionContext.h | 53 - .../inc/Aggregator/AggregatorService.h | 88 - .../inc/Aggregator/AggregatorSettings.h | 39 - .../AnnService/inc/Client/ClientWrapper.h | 80 - .../SPTAG/AnnService/inc/Client/Options.h | 42 - .../SPTAG/AnnService/inc/Core/BKT/Index.h | 132 - .../inc/Core/BKT/ParameterDefinitionList.h | 38 - .../SPTAG/AnnService/inc/Core/Common.h | 162 - .../SPTAG/AnnService/inc/Core/Common/BKTree.h | 490 - .../AnnService/inc/Core/Common/CommonUtils.h | 178 - .../AnnService/inc/Core/Common/DataUtils.h | 116 - .../AnnService/inc/Core/Common/Dataset.h | 229 - .../inc/Core/Common/DistanceUtils.h | 610 - .../inc/Core/Common/FineGrainedLock.h | 51 - .../SPTAG/AnnService/inc/Core/Common/Heap.h | 105 - .../SPTAG/AnnService/inc/Core/Common/KDTree.h | 342 - .../inc/Core/Common/NeighborhoodGraph.h | 403 - .../inc/Core/Common/QueryResultSet.h | 96 - .../Core/Common/RelativeNeighborhoodGraph.h | 123 - .../AnnService/inc/Core/Common/WorkSpace.h | 185 - .../inc/Core/Common/WorkSpacePool.h | 43 - .../AnnService/inc/Core/CommonDataStructure.h | 226 - .../AnnService/inc/Core/DefinitionList.h | 59 - .../SPTAG/AnnService/inc/Core/KDT/Index.h | 132 - .../inc/Core/KDT/ParameterDefinitionList.h | 36 - .../SPTAG/AnnService/inc/Core/MetadataSet.h | 114 - .../SPTAG/AnnService/inc/Core/SearchQuery.h | 193 - .../SPTAG/AnnService/inc/Core/SearchResult.h | 26 - .../SPTAG/AnnService/inc/Core/VectorIndex.h | 129 - .../SPTAG/AnnService/inc/Core/VectorSet.h | 73 - .../AnnService/inc/Helper/ArgumentsParser.h | 253 - .../AnnService/inc/Helper/Base64Encode.h | 33 - .../AnnService/inc/Helper/BufferStream.h | 39 - .../AnnService/inc/Helper/CommonHelper.h | 40 - .../SPTAG/AnnService/inc/Helper/Concurrent.h | 97 - .../AnnService/inc/Helper/ConcurrentSet.h | 148 - .../AnnService/inc/Helper/SimpleIniReader.h | 99 - .../AnnService/inc/Helper/StringConvert.h | 374 - .../AnnService/inc/Helper/VectorSetReader.h | 59 - .../Helper/VectorSetReaders/DefaultReader.h | 108 - .../AnnService/inc/IndexBuilder/Options.h | 39 - .../AnnService/inc/IndexBuilder/ThreadPool.h | 27 - .../SPTAG/AnnService/inc/Server/QueryParser.h | 56 - .../inc/Server/SearchExecutionContext.h | 80 - .../AnnService/inc/Server/SearchExecutor.h | 56 - .../AnnService/inc/Server/SearchService.h | 73 - .../AnnService/inc/Server/ServiceContext.h | 44 - .../AnnService/inc/Server/ServiceSettings.h | 41 - .../SPTAG/AnnService/inc/Socket/Client.h | 68 - .../SPTAG/AnnService/inc/Socket/Common.h | 25 - .../SPTAG/AnnService/inc/Socket/Connection.h | 100 - .../AnnService/inc/Socket/ConnectionManager.h | 73 - .../SPTAG/AnnService/inc/Socket/Packet.h | 142 - .../AnnService/inc/Socket/RemoteSearchQuery.h | 99 - .../AnnService/inc/Socket/ResourceManager.h | 190 - .../SPTAG/AnnService/inc/Socket/Server.h | 55 - .../inc/Socket/SimpleSerialization.h | 174 - .../SPTAG/AnnService/packages.config | 10 - .../src/Aggregator/AggregatorContext.cpp | 86 - .../Aggregator/AggregatorExecutionContext.cpp | 51 - .../src/Aggregator/AggregatorService.cpp | 366 - .../src/Aggregator/AggregatorSettings.cpp | 14 - .../SPTAG/AnnService/src/Aggregator/main.cpp | 19 - .../AnnService/src/Client/ClientWrapper.cpp | 235 - .../SPTAG/AnnService/src/Client/Options.cpp | 27 - .../SPTAG/AnnService/src/Client/main.cpp | 78 - .../AnnService/src/Core/BKT/BKTIndex.cpp | 403 - .../src/Core/Common/NeighborhoodGraph.cpp | 17 - .../src/Core/Common/WorkSpacePool.cpp | 66 - .../AnnService/src/Core/KDT/KDTIndex.cpp | 396 - .../SPTAG/AnnService/src/Core/MetadataSet.cpp | 280 - .../SPTAG/AnnService/src/Core/VectorIndex.cpp | 428 - .../SPTAG/AnnService/src/Core/VectorSet.cpp | 96 - .../AnnService/src/Helper/ArgumentsParser.cpp | 86 - .../AnnService/src/Helper/Base64Encode.cpp | 240 - .../AnnService/src/Helper/CommonHelper.cpp | 144 - .../AnnService/src/Helper/Concurrent.cpp | 71 - .../AnnService/src/Helper/SimpleIniReader.cpp | 242 - .../AnnService/src/Helper/VectorSetReader.cpp | 44 - .../Helper/VectorSetReaders/DefaultReader.cpp | 514 - .../AnnService/src/IndexBuilder/Options.cpp | 25 - .../src/IndexBuilder/ThreadPool.cpp | 54 - .../AnnService/src/IndexBuilder/main.cpp | 100 - .../AnnService/src/IndexSearcher/main.cpp | 278 - .../AnnService/src/Server/QueryParser.cpp | 209 - .../src/Server/SearchExecutionContext.cpp | 220 - .../AnnService/src/Server/SearchExecutor.cpp | 113 - .../AnnService/src/Server/SearchService.cpp | 262 - .../AnnService/src/Server/ServiceContext.cpp | 88 - .../AnnService/src/Server/ServiceSettings.cpp | 14 - .../SPTAG/AnnService/src/Server/main.cpp | 19 - .../SPTAG/AnnService/src/Socket/Client.cpp | 141 - .../SPTAG/AnnService/src/Socket/Common.cpp | 11 - .../AnnService/src/Socket/Connection.cpp | 403 - .../src/Socket/ConnectionManager.cpp | 133 - .../SPTAG/AnnService/src/Socket/Packet.cpp | 177 - .../src/Socket/RemoteSearchQuery.cpp | 210 - .../SPTAG/AnnService/src/Socket/Server.cpp | 136 - .../src/index/thirdparty/SPTAG/CMakeLists.txt | 91 - core/src/index/thirdparty/SPTAG/Dockerfile | 35 - core/src/index/thirdparty/SPTAG/LICENSE | 21 - core/src/index/thirdparty/SPTAG/README.md | 144 - core/src/index/thirdparty/SPTAG/SPTAG.sdf | Bin 65536 -> 0 bytes core/src/index/thirdparty/SPTAG/SPTAG.sln | 211 - .../thirdparty/SPTAG/Test/CMakeLists.txt | 32 - .../index/thirdparty/SPTAG/Test/Test.vcxproj | 183 - .../SPTAG/Test/Test.vcxproj.filters | 48 - .../thirdparty/SPTAG/Test/Test.vcxproj.user | 7 - .../index/thirdparty/SPTAG/Test/inc/Test.h | 7 - .../thirdparty/SPTAG/Test/packages.config | 5 - .../thirdparty/SPTAG/Test/src/AlgoTest.cpp | 159 - .../SPTAG/Test/src/Base64HelperTest.cpp | 43 - .../SPTAG/Test/src/CommonHelperTest.cpp | 93 - .../SPTAG/Test/src/DistanceTest.cpp | 58 - .../SPTAG/Test/src/IniReaderTest.cpp | 40 - .../SPTAG/Test/src/StringConvertTest.cpp | 128 - .../index/thirdparty/SPTAG/Test/src/main.cpp | 41 - .../thirdparty/SPTAG/Wrappers/CLRCore.vcxproj | 141 - .../SPTAG/Wrappers/CLRCore.vcxproj.filters | 32 - .../thirdparty/SPTAG/Wrappers/CMakeLists.txt | 171 - .../SPTAG/Wrappers/CsharpClient.vcxproj | 191 - .../Wrappers/CsharpClient.vcxproj.filters | 41 - .../SPTAG/Wrappers/CsharpCore.vcxproj | 134 - .../SPTAG/Wrappers/CsharpCore.vcxproj.filters | 40 - .../SPTAG/Wrappers/JavaClient.vcxproj | 191 - .../SPTAG/Wrappers/JavaClient.vcxproj.filters | 41 - .../SPTAG/Wrappers/JavaCore.vcxproj | 134 - .../SPTAG/Wrappers/JavaCore.vcxproj.filters | 40 - .../SPTAG/Wrappers/PythonClient.vcxproj | 189 - .../Wrappers/PythonClient.vcxproj.filters | 41 - .../SPTAG/Wrappers/PythonCore.vcxproj | 132 - .../SPTAG/Wrappers/PythonCore.vcxproj.filters | 40 - .../SPTAG/Wrappers/PythonCore.vcxproj.user | 4 - .../SPTAG/Wrappers/inc/CLRCoreInterface.h | 113 - .../SPTAG/Wrappers/inc/ClientInterface.h | 63 - .../SPTAG/Wrappers/inc/CoreInterface.h | 65 - .../SPTAG/Wrappers/inc/CsharpClient.i | 16 - .../SPTAG/Wrappers/inc/CsharpCommon.i | 125 - .../SPTAG/Wrappers/inc/CsharpCore.i | 17 - .../SPTAG/Wrappers/inc/JavaClient.i | 16 - .../SPTAG/Wrappers/inc/JavaCommon.i | 60 - .../thirdparty/SPTAG/Wrappers/inc/JavaCore.i | 17 - .../SPTAG/Wrappers/inc/ManagedObject.h | 80 - .../SPTAG/Wrappers/inc/PythonClient.i | 16 - .../SPTAG/Wrappers/inc/PythonCommon.i | 117 - .../SPTAG/Wrappers/inc/PythonCore.i | 16 - .../SPTAG/Wrappers/inc/TransferDataType.h | 19 - .../thirdparty/SPTAG/Wrappers/packages.config | 6 - .../SPTAG/Wrappers/src/AssemblyInfo.cpp | 36 - .../SPTAG/Wrappers/src/CLRCoreInterface.cpp | 212 - .../SPTAG/Wrappers/src/ClientInterface.cpp | 250 - .../SPTAG/Wrappers/src/CoreInterface.cpp | 230 - .../thirdparty/SPTAG/azure-pipelines.yml | 230 - .../thirdparty/SPTAG/docs/GettingStart.md | 286 - .../index/thirdparty/SPTAG/docs/Parameters.md | 159 - .../index/thirdparty/SPTAG/docs/img/sptag.png | Bin 163816 -> 0 bytes core/src/index/thirdparty/annoy/LICENSE | 202 - core/src/index/thirdparty/annoy/RELEASE.md | 15 - .../thirdparty/annoy/examples/mmap_test.py | 14 - .../annoy/examples/precision_test.cpp | 176 - .../annoy/examples/precision_test.py | 46 - .../annoy/examples/s_compile_cpp.sh | 7 - .../thirdparty/annoy/examples/simple_test.py | 10 - .../thirdparty/annoy/src/annoygomodule.h | 92 - .../thirdparty/annoy/src/annoygomodule.i | 96 - .../src/index/thirdparty/annoy/src/annoylib.h | 1411 -- .../thirdparty/annoy/src/annoyluamodule.cc | 318 - .../index/thirdparty/annoy/src/annoymodule.cc | 632 - .../index/thirdparty/annoy/src/kissrandom.h | 106 - core/src/index/thirdparty/annoy/src/mman.h | 238 - core/src/index/thirdparty/build.sh | 42 - core/src/index/thirdparty/faiss/.dockerignore | 1 - core/src/index/thirdparty/faiss/.gitignore | 21 - core/src/index/thirdparty/faiss/AutoTune.cpp | 719 - core/src/index/thirdparty/faiss/AutoTune.h | 212 - .../index/thirdparty/faiss/BuilderSuspend.cpp | 35 - .../index/thirdparty/faiss/BuilderSuspend.h | 33 - .../index/thirdparty/faiss/CODE_OF_CONDUCT.md | 2 - .../index/thirdparty/faiss/CONTRIBUTING.md | 53 - .../src/index/thirdparty/faiss/Clustering.cpp | 526 - core/src/index/thirdparty/faiss/Clustering.h | 129 - core/src/index/thirdparty/faiss/DirectMap.cpp | 267 - core/src/index/thirdparty/faiss/DirectMap.h | 120 - core/src/index/thirdparty/faiss/Dockerfile | 29 - core/src/index/thirdparty/faiss/FaissHook.cpp | 109 - core/src/index/thirdparty/faiss/FaissHook.h | 40 - core/src/index/thirdparty/faiss/INSTALL.md | 353 - core/src/index/thirdparty/faiss/IVFlib.cpp | 364 - core/src/index/thirdparty/faiss/IVFlib.h | 136 - core/src/index/thirdparty/faiss/Index.cpp | 186 - core/src/index/thirdparty/faiss/Index.h | 272 - .../index/thirdparty/faiss/Index2Layer.cpp | 437 - core/src/index/thirdparty/faiss/Index2Layer.h | 86 - .../index/thirdparty/faiss/IndexBinary.cpp | 89 - core/src/index/thirdparty/faiss/IndexBinary.h | 196 - .../thirdparty/faiss/IndexBinaryFlat.cpp | 132 - .../index/thirdparty/faiss/IndexBinaryFlat.h | 61 - .../thirdparty/faiss/IndexBinaryFromFloat.cpp | 79 - .../thirdparty/faiss/IndexBinaryFromFloat.h | 53 - .../thirdparty/faiss/IndexBinaryHNSW.cpp | 326 - .../index/thirdparty/faiss/IndexBinaryHNSW.h | 57 - .../thirdparty/faiss/IndexBinaryHash.cpp | 496 - .../index/thirdparty/faiss/IndexBinaryHash.h | 120 - .../index/thirdparty/faiss/IndexBinaryIVF.cpp | 975 - .../index/thirdparty/faiss/IndexBinaryIVF.h | 234 - core/src/index/thirdparty/faiss/IndexFlat.cpp | 540 - core/src/index/thirdparty/faiss/IndexFlat.h | 187 - core/src/index/thirdparty/faiss/IndexHNSW.cpp | 1142 - core/src/index/thirdparty/faiss/IndexHNSW.h | 171 - core/src/index/thirdparty/faiss/IndexIVF.cpp | 1013 - core/src/index/thirdparty/faiss/IndexIVF.h | 396 - .../index/thirdparty/faiss/IndexIVFFlat.cpp | 472 - .../src/index/thirdparty/faiss/IndexIVFFlat.h | 110 - .../src/index/thirdparty/faiss/IndexIVFPQ.cpp | 1239 - core/src/index/thirdparty/faiss/IndexIVFPQ.h | 160 - .../index/thirdparty/faiss/IndexIVFPQR.cpp | 219 - core/src/index/thirdparty/faiss/IndexIVFPQR.h | 66 - .../thirdparty/faiss/IndexIVFSpectralHash.cpp | 333 - .../thirdparty/faiss/IndexIVFSpectralHash.h | 75 - core/src/index/thirdparty/faiss/IndexLSH.cpp | 226 - core/src/index/thirdparty/faiss/IndexLSH.h | 91 - .../index/thirdparty/faiss/IndexLattice.cpp | 143 - .../src/index/thirdparty/faiss/IndexLattice.h | 69 - core/src/index/thirdparty/faiss/IndexPQ.cpp | 1190 - core/src/index/thirdparty/faiss/IndexPQ.h | 202 - .../thirdparty/faiss/IndexPreTransform.cpp | 289 - .../thirdparty/faiss/IndexPreTransform.h | 93 - .../index/thirdparty/faiss/IndexReplicas.cpp | 124 - .../index/thirdparty/faiss/IndexReplicas.h | 77 - .../index/thirdparty/faiss/IndexSQHybrid.cpp | 183 - .../index/thirdparty/faiss/IndexSQHybrid.h | 66 - .../thirdparty/faiss/IndexScalarQuantizer.cpp | 325 - .../thirdparty/faiss/IndexScalarQuantizer.h | 128 - .../index/thirdparty/faiss/IndexShards.cpp | 318 - core/src/index/thirdparty/faiss/IndexShards.h | 101 - .../index/thirdparty/faiss/InvertedLists.cpp | 856 - .../index/thirdparty/faiss/InvertedLists.h | 409 - core/src/index/thirdparty/faiss/LICENSE | 21 - core/src/index/thirdparty/faiss/Makefile | 123 - .../index/thirdparty/faiss/MatrixStats.cpp | 252 - core/src/index/thirdparty/faiss/MatrixStats.h | 62 - .../index/thirdparty/faiss/MetaIndexes.cpp | 379 - core/src/index/thirdparty/faiss/MetaIndexes.h | 135 - core/src/index/thirdparty/faiss/MetricType.h | 41 - .../thirdparty/faiss/OnDiskInvertedLists.cpp | 674 - .../thirdparty/faiss/OnDiskInvertedLists.h | 127 - core/src/index/thirdparty/faiss/README.md | 91 - .../thirdparty/faiss/VectorTransform.cpp | 1158 - .../index/thirdparty/faiss/VectorTransform.h | 322 - .../thirdparty/faiss/acinclude/ax_blas.m4 | 234 - .../faiss/acinclude/ax_check_cpu.m4 | 26 - .../faiss/acinclude/ax_cxx_compile_stdcxx.m4 | 972 - .../thirdparty/faiss/acinclude/ax_lapack.m4 | 132 - .../faiss/acinclude/fa_check_cuda.m4 | 67 - .../thirdparty/faiss/acinclude/fa_numpy.m4 | 20 - .../thirdparty/faiss/acinclude/fa_prog_nm.m4 | 16 - .../faiss/acinclude/fa_prog_swig.m4 | 11 - .../thirdparty/faiss/acinclude/fa_python.m4 | 21 - .../index/thirdparty/faiss/benchs/README.md | 338 - .../faiss/benchs/bench_all_ivf/README.md | 20 - .../benchs/bench_all_ivf/bench_all_ivf.py | 308 - .../benchs/bench_all_ivf/bench_kmeans.py | 118 - .../faiss/benchs/bench_all_ivf/datasets.py | 235 - .../bench_all_ivf/parse_bench_all_ivf.py | 268 - .../bench_all_ivf/run_on_cluster_generic.bash | 249 - .../faiss/benchs/bench_for_interrupt.py | 155 - .../thirdparty/faiss/benchs/bench_gpu_1bn.py | 747 - .../faiss/benchs/bench_gpu_sift1m.py | 94 - .../thirdparty/faiss/benchs/bench_hnsw.py | 158 - .../thirdparty/faiss/benchs/bench_index_pq.py | 22 - .../faiss/benchs/bench_pairwise_distances.py | 36 - .../faiss/benchs/bench_polysemous_1bn.py | 254 - .../faiss/benchs/bench_polysemous_sift1m.py | 47 - .../faiss/benchs/bench_scalar_quantizer.py | 85 - .../faiss/benchs/bench_vector_ops.py | 85 - .../index/thirdparty/faiss/benchs/datasets.py | 45 - .../faiss/benchs/distributed_ondisk/README.md | 198 - .../distributed_ondisk/combined_index.py | 194 - .../distributed_ondisk/distributed_kmeans.py | 411 - .../distributed_query_demo.py | 70 - .../distributed_ondisk/make_index_vslice.py | 117 - .../distributed_ondisk/make_trained_index.py | 52 - .../distributed_ondisk/merge_to_ondisk.py | 96 - .../faiss/benchs/distributed_ondisk/rpc.py | 252 - .../distributed_ondisk/run_on_cluster.bash | 263 - .../distributed_ondisk/search_server.py | 221 - .../thirdparty/faiss/benchs/kmeans_mnist.py | 86 - .../faiss/benchs/link_and_code/README.md | 157 - .../link_and_code/bench_link_and_code.py | 304 - .../faiss/benchs/link_and_code/datasets.py | 235 - .../benchs/link_and_code/neighbor_codec.py | 239 - .../thirdparty/faiss/build-aux/config.guess | 1473 -- .../thirdparty/faiss/build-aux/config.sub | 1836 -- .../thirdparty/faiss/build-aux/install-sh | 501 - core/src/index/thirdparty/faiss/build.sh | 3 - .../thirdparty/faiss/c_api/AutoTune_c.cpp | 83 - .../index/thirdparty/faiss/c_api/AutoTune_c.h | 64 - .../thirdparty/faiss/c_api/Clustering_c.cpp | 145 - .../thirdparty/faiss/c_api/Clustering_c.h | 123 - .../index/thirdparty/faiss/c_api/INSTALL.md | 100 - .../thirdparty/faiss/c_api/IndexFlat_c.cpp | 140 - .../thirdparty/faiss/c_api/IndexFlat_c.h | 115 - .../thirdparty/faiss/c_api/IndexIVFFlat_c.cpp | 64 - .../thirdparty/faiss/c_api/IndexIVFFlat_c.h | 58 - .../thirdparty/faiss/c_api/IndexIVF_c.cpp | 99 - .../index/thirdparty/faiss/c_api/IndexIVF_c.h | 142 - .../thirdparty/faiss/c_api/IndexLSH_c.cpp | 37 - .../index/thirdparty/faiss/c_api/IndexLSH_c.h | 40 - .../faiss/c_api/IndexPreTransform_c.cpp | 21 - .../faiss/c_api/IndexPreTransform_c.h | 32 - .../thirdparty/faiss/c_api/IndexShards_c.cpp | 44 - .../thirdparty/faiss/c_api/IndexShards_c.h | 42 - .../index/thirdparty/faiss/c_api/Index_c.cpp | 105 - .../index/thirdparty/faiss/c_api/Index_c.h | 183 - .../src/index/thirdparty/faiss/c_api/Makefile | 89 - .../thirdparty/faiss/c_api/MetaIndexes_c.cpp | 49 - .../thirdparty/faiss/c_api/MetaIndexes_c.h | 49 - .../thirdparty/faiss/c_api/clone_index_c.cpp | 23 - .../thirdparty/faiss/c_api/clone_index_c.h | 32 - .../index/thirdparty/faiss/c_api/error_c.h | 42 - .../thirdparty/faiss/c_api/error_impl.cpp | 27 - .../index/thirdparty/faiss/c_api/error_impl.h | 16 - .../index/thirdparty/faiss/c_api/example_c.c | 97 - .../index/thirdparty/faiss/c_api/faiss_c.h | 58 - .../faiss/c_api/gpu/GpuAutoTune_c.cpp | 96 - .../faiss/c_api/gpu/GpuAutoTune_c.h | 56 - .../faiss/c_api/gpu/GpuClonerOptions_c.cpp | 52 - .../faiss/c_api/gpu/GpuClonerOptions_c.h | 68 - .../thirdparty/faiss/c_api/gpu/GpuIndex_c.cpp | 17 - .../thirdparty/faiss/c_api/gpu/GpuIndex_c.h | 30 - .../faiss/c_api/gpu/GpuIndicesOptions_c.h | 38 - .../faiss/c_api/gpu/GpuResources_c.cpp | 86 - .../faiss/c_api/gpu/GpuResources_c.h | 66 - .../index/thirdparty/faiss/c_api/gpu/Makefile | 63 - .../c_api/gpu/StandardGpuResources_c.cpp | 54 - .../faiss/c_api/gpu/StandardGpuResources_c.h | 53 - .../faiss/c_api/gpu/example_gpu_c.c | 106 - .../thirdparty/faiss/c_api/gpu/macros_impl.h | 42 - .../faiss/c_api/impl/AuxIndexStructures_c.cpp | 220 - .../faiss/c_api/impl/AuxIndexStructures_c.h | 149 - .../faiss/c_api/index_factory_c.cpp | 26 - .../thirdparty/faiss/c_api/index_factory_c.h | 30 - .../thirdparty/faiss/c_api/index_io_c.cpp | 42 - .../index/thirdparty/faiss/c_api/index_io_c.h | 50 - .../thirdparty/faiss/c_api/macros_impl.h | 110 - .../index/thirdparty/faiss/clone_index.cpp | 156 - core/src/index/thirdparty/faiss/clone_index.h | 48 - .../index/thirdparty/faiss/conda/Dockerfile | 33 - .../faiss/conda/conda_build_config.yaml | 7 - .../thirdparty/faiss/conda/faiss-gpu/build.sh | 16 - .../conda/faiss-gpu/conda_build_config.yaml | 11 - .../faiss/conda/faiss-gpu/meta.yaml | 41 - .../faiss/conda/faiss-gpu/run_test.py | 16 - .../thirdparty/faiss/conda/faiss/build.sh | 16 - .../thirdparty/faiss/conda/faiss/meta.yaml | 36 - .../thirdparty/faiss/conda/faiss/run_test.py | 14 - core/src/index/thirdparty/faiss/configure | 7998 ------ core/src/index/thirdparty/faiss/configure.ac | 70 - .../src/index/thirdparty/faiss/demos/Makefile | 21 - .../index/thirdparty/faiss/demos/README.md | 28 - .../thirdparty/faiss/demos/demo_auto_tune.py | 170 - .../thirdparty/faiss/demos/demo_imi_flat.cpp | 151 - .../thirdparty/faiss/demos/demo_imi_pq.cpp | 199 - .../faiss/demos/demo_ivfpq_indexing.cpp | 146 - .../thirdparty/faiss/demos/demo_ondisk_ivf.py | 112 - .../thirdparty/faiss/demos/demo_sift1M.cpp | 252 - .../faiss/demos/demo_weighted_kmeans.cpp | 185 - .../example_makefiles/makefile.inc.Linux | 140 - .../example_makefiles/makefile.inc.Mac.brew | 99 - .../example_makefiles/makefile.inc.Mac.port | 106 - core/src/index/thirdparty/faiss/faiss | 1 - .../thirdparty/faiss/gpu/GpuAutoTune.cpp | 95 - .../index/thirdparty/faiss/gpu/GpuAutoTune.h | 27 - .../index/thirdparty/faiss/gpu/GpuCloner.cpp | 478 - .../index/thirdparty/faiss/gpu/GpuCloner.h | 88 - .../thirdparty/faiss/gpu/GpuClonerOptions.cpp | 30 - .../thirdparty/faiss/gpu/GpuClonerOptions.h | 58 - .../index/thirdparty/faiss/gpu/GpuDistance.cu | 157 - .../index/thirdparty/faiss/gpu/GpuDistance.h | 145 - .../thirdparty/faiss/gpu/GpuFaissAssert.h | 29 - .../index/thirdparty/faiss/gpu/GpuIndex.cu | 485 - .../src/index/thirdparty/faiss/gpu/GpuIndex.h | 160 - .../faiss/gpu/GpuIndexBinaryFlat.cu | 290 - .../thirdparty/faiss/gpu/GpuIndexBinaryFlat.h | 90 - .../thirdparty/faiss/gpu/GpuIndexFlat.cu | 386 - .../index/thirdparty/faiss/gpu/GpuIndexFlat.h | 188 - .../index/thirdparty/faiss/gpu/GpuIndexIVF.cu | 324 - .../index/thirdparty/faiss/gpu/GpuIndexIVF.h | 94 - .../thirdparty/faiss/gpu/GpuIndexIVFFlat.cu | 264 - .../thirdparty/faiss/gpu/GpuIndexIVFFlat.h | 87 - .../thirdparty/faiss/gpu/GpuIndexIVFPQ.cu | 471 - .../thirdparty/faiss/gpu/GpuIndexIVFPQ.h | 145 - .../faiss/gpu/GpuIndexIVFSQHybrid.cu | 357 - .../faiss/gpu/GpuIndexIVFSQHybrid.h | 105 - .../faiss/gpu/GpuIndexIVFScalarQuantizer.cu | 295 - .../faiss/gpu/GpuIndexIVFScalarQuantizer.h | 101 - .../thirdparty/faiss/gpu/GpuIndicesOptions.h | 30 - .../thirdparty/faiss/gpu/GpuResources.cpp | 52 - .../index/thirdparty/faiss/gpu/GpuResources.h | 73 - .../faiss/gpu/StandardGpuResources.cpp | 303 - .../faiss/gpu/StandardGpuResources.h | 114 - .../faiss/gpu/impl/BinaryDistance.cu | 316 - .../faiss/gpu/impl/BinaryDistance.cuh | 21 - .../faiss/gpu/impl/BinaryFlatIndex.cu | 88 - .../faiss/gpu/impl/BinaryFlatIndex.cuh | 69 - .../thirdparty/faiss/gpu/impl/BroadcastSum.cu | 360 - .../faiss/gpu/impl/BroadcastSum.cuh | 51 - .../thirdparty/faiss/gpu/impl/Distance.cu | 448 - .../thirdparty/faiss/gpu/impl/Distance.cuh | 216 - .../faiss/gpu/impl/DistanceUtils.cuh | 343 - .../thirdparty/faiss/gpu/impl/FlatIndex.cu | 388 - .../thirdparty/faiss/gpu/impl/FlatIndex.cuh | 139 - .../faiss/gpu/impl/GeneralDistance.cuh | 432 - .../faiss/gpu/impl/GpuScalarQuantizer.cuh | 607 - .../thirdparty/faiss/gpu/impl/IVFAppend.cu | 369 - .../thirdparty/faiss/gpu/impl/IVFAppend.cuh | 53 - .../thirdparty/faiss/gpu/impl/IVFBase.cu | 379 - .../thirdparty/faiss/gpu/impl/IVFBase.cuh | 151 - .../thirdparty/faiss/gpu/impl/IVFFlat.cu | 413 - .../thirdparty/faiss/gpu/impl/IVFFlat.cuh | 71 - .../thirdparty/faiss/gpu/impl/IVFFlatScan.cu | 542 - .../thirdparty/faiss/gpu/impl/IVFFlatScan.cuh | 40 - .../index/thirdparty/faiss/gpu/impl/IVFPQ.cu | 811 - .../index/thirdparty/faiss/gpu/impl/IVFPQ.cuh | 161 - .../thirdparty/faiss/gpu/impl/IVFUtils.cu | 78 - .../thirdparty/faiss/gpu/impl/IVFUtils.cuh | 119 - .../faiss/gpu/impl/IVFUtilsSelect1.cu | 199 - .../faiss/gpu/impl/IVFUtilsSelect2.cu | 218 - .../index/thirdparty/faiss/gpu/impl/L2Norm.cu | 331 - .../thirdparty/faiss/gpu/impl/L2Norm.cuh | 29 - .../thirdparty/faiss/gpu/impl/L2Select.cu | 264 - .../thirdparty/faiss/gpu/impl/L2Select.cuh | 23 - .../thirdparty/faiss/gpu/impl/Metrics.cuh | 52 - .../faiss/gpu/impl/PQCodeDistances-inl.cuh | 574 - .../faiss/gpu/impl/PQCodeDistances.cu | 589 - .../faiss/gpu/impl/PQCodeDistances.cuh | 46 - .../thirdparty/faiss/gpu/impl/PQCodeLoad.cuh | 357 - .../impl/PQScanMultiPassNoPrecomputed-inl.cuh | 623 - .../gpu/impl/PQScanMultiPassNoPrecomputed.cu | 627 - .../gpu/impl/PQScanMultiPassNoPrecomputed.cuh | 49 - .../gpu/impl/PQScanMultiPassPrecomputed.cu | 573 - .../gpu/impl/PQScanMultiPassPrecomputed.cuh | 42 - .../faiss/gpu/impl/RemapIndices.cpp | 43 - .../thirdparty/faiss/gpu/impl/RemapIndices.h | 24 - .../faiss/gpu/impl/VectorResidual.cu | 148 - .../faiss/gpu/impl/VectorResidual.cuh | 41 - .../faiss/gpu/perf/IndexWrapper-inl.h | 71 - .../thirdparty/faiss/gpu/perf/IndexWrapper.h | 39 - .../faiss/gpu/perf/PerfBinaryFlat.cu | 125 - .../faiss/gpu/perf/PerfClustering.cpp | 115 - .../thirdparty/faiss/gpu/perf/PerfFlat.cu | 149 - .../thirdparty/faiss/gpu/perf/PerfIVFFlat.cu | 146 - .../thirdparty/faiss/gpu/perf/PerfIVFPQ.cu | 157 - .../faiss/gpu/perf/PerfIVFPQAdd.cpp | 139 - .../thirdparty/faiss/gpu/perf/PerfSelect.cu | 71 - .../thirdparty/faiss/gpu/perf/WriteIndex.cpp | 102 - .../index/thirdparty/faiss/gpu/perf/slow.py | 24 - .../index/thirdparty/faiss/gpu/test/Makefile | 51 - .../faiss/gpu/test/TestGpuDistance.cu | 180 - .../faiss/gpu/test/TestGpuIndexBinaryFlat.cpp | 130 - .../faiss/gpu/test/TestGpuIndexFlat.cpp | 393 - .../faiss/gpu/test/TestGpuIndexIVFFlat.cpp | 550 - .../faiss/gpu/test/TestGpuIndexIVFPQ.cpp | 558 - .../faiss/gpu/test/TestGpuMemoryException.cpp | 84 - .../faiss/gpu/test/TestGpuSelect.cu | 193 - .../thirdparty/faiss/gpu/test/TestUtils.cpp | 315 - .../thirdparty/faiss/gpu/test/TestUtils.h | 93 - .../gpu/test/demo_ivfpq_indexing_gpu.cpp | 159 - .../faiss/gpu/test/test_gpu_index.py | 314 - .../faiss/gpu/test/test_gpu_index_ivfsq.py | 229 - .../faiss/gpu/test/test_pytorch_faiss.py | 215 - .../faiss/gpu/utils/BlockSelectFloat.cu | 146 - .../faiss/gpu/utils/BlockSelectHalf.cu | 150 - .../faiss/gpu/utils/BlockSelectImpl.cuh | 106 - .../faiss/gpu/utils/BlockSelectKernel.cuh | 259 - .../faiss/gpu/utils/Comparators.cuh | 46 - .../faiss/gpu/utils/ConversionOperators.cuh | 138 - .../thirdparty/faiss/gpu/utils/CopyUtils.cuh | 107 - .../thirdparty/faiss/gpu/utils/DeviceDefs.cuh | 48 - .../faiss/gpu/utils/DeviceMemory.cpp | 77 - .../thirdparty/faiss/gpu/utils/DeviceMemory.h | 71 - .../faiss/gpu/utils/DeviceTensor-inl.cuh | 228 - .../faiss/gpu/utils/DeviceTensor.cuh | 113 - .../thirdparty/faiss/gpu/utils/DeviceUtils.cu | 206 - .../thirdparty/faiss/gpu/utils/DeviceUtils.h | 191 - .../faiss/gpu/utils/DeviceVector.cuh | 191 - .../thirdparty/faiss/gpu/utils/Float16.cu | 42 - .../thirdparty/faiss/gpu/utils/Float16.cuh | 81 - .../faiss/gpu/utils/HostTensor-inl.cuh | 180 - .../thirdparty/faiss/gpu/utils/HostTensor.cuh | 91 - .../thirdparty/faiss/gpu/utils/Limits.cuh | 82 - .../faiss/gpu/utils/LoadStoreOperators.cuh | 94 - .../faiss/gpu/utils/MathOperators.cuh | 561 - .../faiss/gpu/utils/MatrixMult-inl.cuh | 160 - .../thirdparty/faiss/gpu/utils/MatrixMult.cu | 94 - .../thirdparty/faiss/gpu/utils/MatrixMult.cuh | 59 - .../faiss/gpu/utils/MemorySpace.cpp | 89 - .../thirdparty/faiss/gpu/utils/MemorySpace.h | 44 - .../faiss/gpu/utils/MergeNetworkBlock.cuh | 289 - .../faiss/gpu/utils/MergeNetworkUtils.cuh | 24 - .../faiss/gpu/utils/MergeNetworkWarp.cuh | 510 - .../faiss/gpu/utils/NoTypeTensor.cuh | 123 - .../index/thirdparty/faiss/gpu/utils/Pair.cuh | 69 - .../thirdparty/faiss/gpu/utils/PtxUtils.cuh | 76 - .../faiss/gpu/utils/ReductionOperators.cuh | 73 - .../thirdparty/faiss/gpu/utils/Reductions.cuh | 142 - .../thirdparty/faiss/gpu/utils/Select.cuh | 563 - .../faiss/gpu/utils/StackDeviceMemory.cpp | 239 - .../faiss/gpu/utils/StackDeviceMemory.h | 129 - .../thirdparty/faiss/gpu/utils/StaticUtils.h | 83 - .../thirdparty/faiss/gpu/utils/Tensor-inl.cuh | 746 - .../thirdparty/faiss/gpu/utils/Tensor.cuh | 656 - .../faiss/gpu/utils/ThrustAllocator.cuh | 69 - .../thirdparty/faiss/gpu/utils/Timer.cpp | 60 - .../index/thirdparty/faiss/gpu/utils/Timer.h | 52 - .../thirdparty/faiss/gpu/utils/Transpose.cuh | 154 - .../faiss/gpu/utils/WarpSelectFloat.cu | 94 - .../faiss/gpu/utils/WarpSelectHalf.cu | 98 - .../faiss/gpu/utils/WarpSelectKernel.cuh | 72 - .../faiss/gpu/utils/WarpShuffles.cuh | 119 - .../utils/blockselect/BlockSelectFloat1.cu | 15 - .../utils/blockselect/BlockSelectFloat128.cu | 15 - .../utils/blockselect/BlockSelectFloat256.cu | 15 - .../utils/blockselect/BlockSelectFloat32.cu | 15 - .../utils/blockselect/BlockSelectFloat64.cu | 15 - .../blockselect/BlockSelectFloatF1024.cu | 14 - .../blockselect/BlockSelectFloatF2048.cu | 17 - .../utils/blockselect/BlockSelectFloatF512.cu | 14 - .../blockselect/BlockSelectFloatT1024.cu | 14 - .../blockselect/BlockSelectFloatT2048.cu | 17 - .../utils/blockselect/BlockSelectFloatT512.cu | 14 - .../gpu/utils/blockselect/BlockSelectHalf1.cu | 17 - .../utils/blockselect/BlockSelectHalf128.cu | 17 - .../utils/blockselect/BlockSelectHalf256.cu | 17 - .../utils/blockselect/BlockSelectHalf32.cu | 17 - .../utils/blockselect/BlockSelectHalf64.cu | 17 - .../utils/blockselect/BlockSelectHalfF1024.cu | 16 - .../utils/blockselect/BlockSelectHalfF2048.cu | 19 - .../utils/blockselect/BlockSelectHalfF512.cu | 16 - .../utils/blockselect/BlockSelectHalfT1024.cu | 16 - .../utils/blockselect/BlockSelectHalfT2048.cu | 19 - .../utils/blockselect/BlockSelectHalfT512.cu | 16 - .../gpu/utils/blockselect/BlockSelectImpl.cuh | 106 - .../gpu/utils/warpselect/WarpSelectFloat1.cu | 15 - .../utils/warpselect/WarpSelectFloat128.cu | 15 - .../utils/warpselect/WarpSelectFloat256.cu | 15 - .../gpu/utils/warpselect/WarpSelectFloat32.cu | 15 - .../gpu/utils/warpselect/WarpSelectFloat64.cu | 15 - .../utils/warpselect/WarpSelectFloatF1024.cu | 14 - .../utils/warpselect/WarpSelectFloatF2048.cu | 17 - .../utils/warpselect/WarpSelectFloatF512.cu | 14 - .../utils/warpselect/WarpSelectFloatT1024.cu | 14 - .../utils/warpselect/WarpSelectFloatT2048.cu | 17 - .../utils/warpselect/WarpSelectFloatT512.cu | 14 - .../gpu/utils/warpselect/WarpSelectHalf1.cu | 17 - .../gpu/utils/warpselect/WarpSelectHalf128.cu | 17 - .../gpu/utils/warpselect/WarpSelectHalf256.cu | 17 - .../gpu/utils/warpselect/WarpSelectHalf32.cu | 17 - .../gpu/utils/warpselect/WarpSelectHalf64.cu | 17 - .../utils/warpselect/WarpSelectHalfF1024.cu | 16 - .../utils/warpselect/WarpSelectHalfF2048.cu | 19 - .../utils/warpselect/WarpSelectHalfF512.cu | 16 - .../utils/warpselect/WarpSelectHalfT1024.cu | 16 - .../utils/warpselect/WarpSelectHalfT2048.cu | 19 - .../utils/warpselect/WarpSelectHalfT512.cu | 16 - .../gpu/utils/warpselect/WarpSelectImpl.cuh | 47 - .../faiss/impl/AuxIndexStructures.cpp | 322 - .../faiss/impl/AuxIndexStructures.h | 257 - .../index/thirdparty/faiss/impl/FaissAssert.h | 95 - .../thirdparty/faiss/impl/FaissException.cpp | 66 - .../thirdparty/faiss/impl/FaissException.h | 71 - core/src/index/thirdparty/faiss/impl/HNSW.cpp | 817 - core/src/index/thirdparty/faiss/impl/HNSW.h | 275 - .../faiss/impl/PolysemousTraining.cpp | 954 - .../faiss/impl/PolysemousTraining.h | 158 - .../faiss/impl/ProductQuantizer-inl.h | 138 - .../faiss/impl/ProductQuantizer.cpp | 759 - .../thirdparty/faiss/impl/ProductQuantizer.h | 237 - .../thirdparty/faiss/impl/ScalarQuantizer.cpp | 182 - .../thirdparty/faiss/impl/ScalarQuantizer.h | 242 - .../faiss/impl/ScalarQuantizerCodec.h | 603 - .../faiss/impl/ScalarQuantizerCodec_avx.h | 576 - .../faiss/impl/ScalarQuantizerCodec_avx512.h | 661 - .../faiss/impl/ScalarQuantizerDC.cpp | 39 - .../thirdparty/faiss/impl/ScalarQuantizerDC.h | 41 - .../faiss/impl/ScalarQuantizerDC_avx.cpp | 54 - .../faiss/impl/ScalarQuantizerDC_avx.h | 41 - .../faiss/impl/ScalarQuantizerDC_avx512.cpp | 62 - .../faiss/impl/ScalarQuantizerDC_avx512.h | 41 - .../faiss/impl/ScalarQuantizerOp.cpp | 295 - .../thirdparty/faiss/impl/ScalarQuantizerOp.h | 71 - .../thirdparty/faiss/impl/ThreadedIndex-inl.h | 192 - .../thirdparty/faiss/impl/ThreadedIndex.h | 80 - .../thirdparty/faiss/impl/index_read.cpp | 920 - .../thirdparty/faiss/impl/index_write.cpp | 682 - core/src/index/thirdparty/faiss/impl/io.cpp | 252 - core/src/index/thirdparty/faiss/impl/io.h | 136 - .../thirdparty/faiss/impl/lattice_Zn.cpp | 713 - .../index/thirdparty/faiss/impl/lattice_Zn.h | 199 - .../index/thirdparty/faiss/index_factory.cpp | 425 - .../index/thirdparty/faiss/index_factory.h | 24 - core/src/index/thirdparty/faiss/index_io.h | 75 - .../index/thirdparty/faiss/makefile.inc.in | 46 - .../index/thirdparty/faiss/misc/test_blas.cpp | 84 - .../index/thirdparty/faiss/python/Makefile | 39 - .../index/thirdparty/faiss/python/faiss.py | 812 - .../index/thirdparty/faiss/python/setup.py | 50 - .../thirdparty/faiss/python/swigfaiss.swig | 1109 - .../src/index/thirdparty/faiss/tests/Makefile | 37 - .../index/thirdparty/faiss/tests/common.py | 128 - .../faiss/tests/test_binary_factory.py | 35 - .../faiss/tests/test_binary_flat.cpp | 64 - .../faiss/tests/test_binary_hashindex.py | 183 - .../thirdparty/faiss/tests/test_binary_io.py | 217 - .../faiss/tests/test_build_blocks.py | 600 - .../faiss/tests/test_dealloc_invlists.cpp | 183 - .../faiss/tests/test_extra_distances.py | 143 - .../thirdparty/faiss/tests/test_factory.py | 64 - .../thirdparty/faiss/tests/test_index.py | 639 - .../faiss/tests/test_index_accuracy.py | 673 - .../faiss/tests/test_index_binary.py | 375 - .../tests/test_index_binary_from_float.py | 200 - .../faiss/tests/test_index_composite.py | 571 - .../index/thirdparty/faiss/tests/test_io.py | 220 - .../thirdparty/faiss/tests/test_ivflib.py | 78 - .../faiss/tests/test_ivfpq_codec.cpp | 67 - .../faiss/tests/test_ivfpq_indexing.cpp | 98 - .../faiss/tests/test_lowlevel_ivf.cpp | 566 - .../thirdparty/faiss/tests/test_merge.cpp | 257 - .../thirdparty/faiss/tests/test_meta_index.py | 264 - .../faiss/tests/test_omp_threads.cpp | 14 - .../faiss/tests/test_omp_threads_py.py | 15 - .../faiss/tests/test_ondisk_ivf.cpp | 220 - .../faiss/tests/test_oom_exception.py | 37 - .../faiss/tests/test_pairs_decoding.cpp | 189 - .../faiss/tests/test_params_override.cpp | 231 - .../faiss/tests/test_pq_encoding.cpp | 98 - .../faiss/tests/test_referenced_objects.py | 106 - .../faiss/tests/test_sliding_ivf.cpp | 240 - .../faiss/tests/test_standalone_codec.py | 314 - .../faiss/tests/test_threaded_index.cpp | 253 - .../faiss/tests/test_transfer_invlists.cpp | 159 - .../thirdparty/faiss/tutorial/cpp/1-Flat.cpp | 98 - .../faiss/tutorial/cpp/2-IVFFlat.cpp | 81 - .../thirdparty/faiss/tutorial/cpp/3-IVFPQ.cpp | 122 - .../thirdparty/faiss/tutorial/cpp/4-GPU.cpp | 119 - .../thirdparty/faiss/tutorial/cpp/5-GPU.cpp | 234 - .../faiss/tutorial/cpp/5-Multiple-GPUs.cpp | 100 - .../thirdparty/faiss/tutorial/cpp/6-GPU.cpp | 255 - .../thirdparty/faiss/tutorial/cpp/6-RUN.cpp | 247 - .../thirdparty/faiss/tutorial/cpp/7-GPU.cpp | 347 - .../thirdparty/faiss/tutorial/cpp/8-GPU.cpp | 479 - .../faiss/tutorial/cpp/9-BinaryFlat.cpp | 115 - .../thirdparty/faiss/tutorial/cpp/Makefile | 25 - .../faiss/tutorial/cpp/faiss_test.cpp | 378 - .../faiss/tutorial/python/1-Flat.py | 29 - .../faiss/tutorial/python/2-IVFFlat.py | 34 - .../faiss/tutorial/python/3-IVFPQ.py | 32 - .../thirdparty/faiss/tutorial/python/4-GPU.py | 57 - .../faiss/tutorial/python/5-Multiple-GPUs.py | 35 - .../thirdparty/faiss/utils/BinaryDistance.cpp | 337 - .../thirdparty/faiss/utils/BinaryDistance.h | 62 - .../faiss/utils/ConcurrentBitset.cpp | 55 - .../thirdparty/faiss/utils/ConcurrentBitset.h | 62 - .../src/index/thirdparty/faiss/utils/Heap.cpp | 120 - core/src/index/thirdparty/faiss/utils/Heap.h | 543 - .../thirdparty/faiss/utils/WorkerThread.cpp | 126 - .../thirdparty/faiss/utils/WorkerThread.h | 61 - .../thirdparty/faiss/utils/distances.cpp | 1073 - .../index/thirdparty/faiss/utils/distances.h | 271 - .../thirdparty/faiss/utils/distances_avx.h | 32 - .../thirdparty/faiss/utils/distances_avx512.h | 32 - .../thirdparty/faiss/utils/distances_simd.cpp | 624 - .../faiss/utils/distances_simd_avx.cpp | 213 - .../faiss/utils/distances_simd_avx512.cpp | 250 - .../faiss/utils/extra_distances.cpp | 374 - .../thirdparty/faiss/utils/extra_distances.h | 55 - .../thirdparty/faiss/utils/hamming-inl.h | 472 - .../index/thirdparty/faiss/utils/hamming.cpp | 981 - .../index/thirdparty/faiss/utils/hamming.h | 243 - .../thirdparty/faiss/utils/instruction_set.h | 355 - .../thirdparty/faiss/utils/jaccard-inl.h | 389 - .../index/thirdparty/faiss/utils/random.cpp | 192 - .../src/index/thirdparty/faiss/utils/random.h | 60 - .../thirdparty/faiss/utils/substructure-inl.h | 302 - .../faiss/utils/superstructure-inl.h | 302 - .../index/thirdparty/faiss/utils/utils.cpp | 723 - core/src/index/thirdparty/faiss/utils/utils.h | 182 - .../src/index/thirdparty/hnswlib/bruteforce.h | 164 - core/src/index/thirdparty/hnswlib/hnswalg.h | 1147 - core/src/index/thirdparty/hnswlib/hnswlib.h | 98 - core/src/index/thirdparty/hnswlib/space_ip.h | 253 - core/src/index/thirdparty/hnswlib/space_l2.h | 245 - .../thirdparty/hnswlib/visited_list_pool.h | 86 - core/src/index/thirdparty/versions.txt | 6 - core/src/index/unittest/CMakeLists.txt | 203 - core/src/index/unittest/Helper.h | 134 - core/src/index/unittest/SPTAG.cpp | 44 - .../unittest/faiss_benchmark/CMakeLists.txt | 53 - .../index/unittest/faiss_benchmark/README.md | 25 - .../faiss_benchmark/faiss_benchmark_test.cpp | 570 - .../faiss_benchmark/faiss_bitset_test.cpp | 573 - .../index/unittest/faiss_ori/CMakeLists.txt | 50 - .../unittest/faiss_ori/gpuresource_test.cpp | 170 - core/src/index/unittest/kdtree.cpp | 138 - .../metric_alg_benchmark/CMakeLists.txt | 17 - .../metric_benchmark_test.cpp | 415 - core/src/index/unittest/sift.50NN.graph | Bin 2040000 -> 0 bytes core/src/index/unittest/siftsmall_base.fvecs | Bin 5160000 -> 0 bytes core/src/index/unittest/test_annoy.cpp | 315 - core/src/index/unittest/test_binaryidmap.cpp | 133 - core/src/index/unittest/test_binaryivf.cpp | 152 - core/src/index/unittest/test_common.cpp | 45 - .../index/unittest/test_customized_index.cpp | 232 - core/src/index/unittest/test_gpuresource.cpp | 302 - core/src/index/unittest/test_hnsw.cpp | 261 - core/src/index/unittest/test_idmap.cpp | 246 - .../index/unittest/test_instructionset.cpp | 95 - core/src/index/unittest/test_ivf.cpp | 405 - core/src/index/unittest/test_knowhere.cpp | 52 - core/src/index/unittest/test_nsg.cpp | 168 - core/src/index/unittest/test_sptag.cpp | 143 - core/src/index/unittest/test_vecindex.cpp | 119 - core/src/index/unittest/test_wrapper.cpp | 447 - core/src/index/unittest/utils.cpp | 303 - core/src/index/unittest/utils.h | 113 - core/src/main.cpp | 145 - core/src/metrics/MetricBase.h | 208 - core/src/metrics/Metrics.cpp | 39 - core/src/metrics/Metrics.h | 250 - core/src/metrics/SystemInfo.cpp | 410 - core/src/metrics/SystemInfo.h | 123 - .../metrics/prometheus/PrometheusMetrics.cpp | 262 - .../metrics/prometheus/PrometheusMetrics.h | 660 - core/src/query/BinaryQuery.cpp | 222 - core/src/query/BinaryQuery.h | 38 - core/src/query/BooleanQuery.h | 87 - core/src/query/GeneralQuery.h | 107 - core/src/scheduler/Algorithm.cpp | 99 - core/src/scheduler/Algorithm.h | 26 - core/src/scheduler/BuildMgr.cpp | 16 - core/src/scheduler/BuildMgr.h | 65 - core/src/scheduler/CPUBuilder.cpp | 66 - core/src/scheduler/CPUBuilder.h | 55 - core/src/scheduler/CircleQueue.h | 113 - core/src/scheduler/Definition.h | 42 - core/src/scheduler/JobMgr.cpp | 175 - core/src/scheduler/JobMgr.h | 75 - core/src/scheduler/ResourceFactory.cpp | 31 - core/src/scheduler/ResourceFactory.h | 32 - core/src/scheduler/ResourceMgr.cpp | 259 - core/src/scheduler/ResourceMgr.h | 137 - core/src/scheduler/SchedInst.cpp | 112 - core/src/scheduler/SchedInst.h | 186 - core/src/scheduler/Scheduler.cpp | 138 - core/src/scheduler/Scheduler.h | 85 - core/src/scheduler/TaskCreator.cpp | 74 - core/src/scheduler/TaskCreator.h | 52 - core/src/scheduler/TaskTable.cpp | 294 - core/src/scheduler/TaskTable.h | 225 - core/src/scheduler/Utils.cpp | 33 - core/src/scheduler/Utils.h | 22 - core/src/scheduler/action/Action.h | 39 - .../scheduler/action/PushTaskToNeighbour.cpp | 122 - core/src/scheduler/event/Event.h | 49 - core/src/scheduler/event/EventDump.cpp | 52 - core/src/scheduler/event/FinishTaskEvent.h | 43 - core/src/scheduler/event/LoadCompletedEvent.h | 43 - core/src/scheduler/event/StartUpEvent.h | 38 - .../scheduler/event/TaskTableUpdatedEvent.h | 39 - core/src/scheduler/interface/interfaces.h | 37 - core/src/scheduler/job/BuildIndexJob.cpp | 72 - core/src/scheduler/job/BuildIndexJob.h | 94 - core/src/scheduler/job/DeleteJob.cpp | 55 - core/src/scheduler/job/DeleteJob.h | 68 - core/src/scheduler/job/Job.cpp | 37 - core/src/scheduler/job/Job.h | 67 - core/src/scheduler/job/SearchJob.cpp | 94 - core/src/scheduler/job/SearchJob.h | 148 - core/src/scheduler/resource/Connection.h | 56 - core/src/scheduler/resource/CpuResource.cpp | 40 - core/src/scheduler/resource/CpuResource.h | 37 - core/src/scheduler/resource/DiskResource.cpp | 39 - core/src/scheduler/resource/DiskResource.h | 37 - core/src/scheduler/resource/GpuResource.cpp | 38 - core/src/scheduler/resource/GpuResource.h | 38 - core/src/scheduler/resource/Node.cpp | 60 - core/src/scheduler/resource/Node.h | 68 - core/src/scheduler/resource/Resource.cpp | 225 - core/src/scheduler/resource/Resource.h | 201 - core/src/scheduler/resource/TestResource.cpp | 40 - core/src/scheduler/resource/TestResource.h | 38 - .../src/scheduler/selector/BuildIndexPass.cpp | 63 - core/src/scheduler/selector/BuildIndexPass.h | 50 - core/src/scheduler/selector/FaissFlatPass.cpp | 76 - core/src/scheduler/selector/FaissFlatPass.h | 51 - .../scheduler/selector/FaissIVFFlatPass.cpp | 77 - .../src/scheduler/selector/FaissIVFFlatPass.h | 51 - .../src/scheduler/selector/FaissIVFPQPass.cpp | 79 - core/src/scheduler/selector/FaissIVFPQPass.h | 51 - .../scheduler/selector/FaissIVFSQ8HPass.cpp | 77 - .../src/scheduler/selector/FaissIVFSQ8HPass.h | 51 - .../scheduler/selector/FaissIVFSQ8Pass.cpp | 77 - core/src/scheduler/selector/FaissIVFSQ8Pass.h | 51 - core/src/scheduler/selector/FallbackPass.cpp | 38 - core/src/scheduler/selector/FallbackPass.h | 34 - core/src/scheduler/selector/Optimizer.cpp | 36 - core/src/scheduler/selector/Optimizer.h | 48 - core/src/scheduler/selector/Pass.h | 40 - core/src/scheduler/task/BuildIndexTask.cpp | 247 - core/src/scheduler/task/BuildIndexTask.h | 40 - core/src/scheduler/task/DeleteTask.cpp | 33 - core/src/scheduler/task/DeleteTask.h | 35 - core/src/scheduler/task/Path.h | 95 - core/src/scheduler/task/SearchTask.cpp | 498 - core/src/scheduler/task/SearchTask.h | 68 - core/src/scheduler/task/Task.h | 89 - core/src/scheduler/task/TestTask.cpp | 47 - core/src/scheduler/task/TestTask.h | 45 - core/src/scheduler/tasklabel/BroadcastLabel.h | 30 - core/src/scheduler/tasklabel/SpecResLabel.h | 50 - core/src/scheduler/tasklabel/TaskLabel.h | 48 - core/src/search/Task.cpp | 223 - core/src/search/Task.h | 93 - core/src/search/TaskInst.cpp | 116 - core/src/search/TaskInst.h | 82 - core/src/segment/Attr.cpp | 166 - core/src/segment/Attr.h | 89 - core/src/segment/AttrIndex.h | 49 - core/src/segment/Attrs.h | 38 - core/src/segment/AttrsIndex.h | 33 - core/src/segment/DeletedDocs.cpp | 47 - core/src/segment/DeletedDocs.h | 68 - core/src/segment/IdBloomFilter.cpp | 85 - core/src/segment/IdBloomFilter.h | 73 - core/src/segment/IdIndex.h | 32 - core/src/segment/SegmentReader.cpp | 152 - core/src/segment/SegmentReader.h | 71 - core/src/segment/SegmentWriter.cpp | 355 - core/src/segment/SegmentWriter.h | 97 - core/src/segment/Types.h | 44 - core/src/segment/VectorIndex.h | 60 - core/src/segment/Vectors.cpp | 178 - core/src/segment/Vectors.h | 99 - core/src/segment/VectorsIndex.h | 33 - core/src/server/DBWrapper.cpp | 296 - core/src/server/DBWrapper.h | 58 - core/src/server/Server.cpp | 415 - core/src/server/Server.h | 56 - core/src/server/context/ConnectionContext.h | 30 - core/src/server/context/Context.cpp | 108 - core/src/server/context/Context.h | 104 - core/src/server/delivery/RequestHandler.cpp | 331 - core/src/server/delivery/RequestHandler.h | 154 - core/src/server/delivery/RequestQueue.cpp | 77 - core/src/server/delivery/RequestQueue.h | 44 - core/src/server/delivery/RequestScheduler.cpp | 163 - core/src/server/delivery/RequestScheduler.h | 70 - .../CreateHybridCollectionRequest.cpp | 120 - .../CreateHybridCollectionRequest.h | 50 - .../DescribeHybridCollectionRequest.cpp | 73 - .../DescribeHybridCollectionRequest.h | 45 - .../hybrid_request/HybridSearchRequest.cpp | 135 - .../hybrid_request/HybridSearchRequest.h | 50 - .../hybrid_request/InsertEntityRequest.cpp | 179 - .../hybrid_request/InsertEntityRequest.h | 50 - .../server/delivery/request/BaseRequest.cpp | 158 - .../src/server/delivery/request/BaseRequest.h | 234 - .../server/delivery/request/CmdRequest.cpp | 69 - core/src/server/delivery/request/CmdRequest.h | 39 - .../delivery/request/CompactRequest.cpp | 85 - .../server/delivery/request/CompactRequest.h | 47 - .../request/CountCollectionRequest.cpp | 89 - .../delivery/request/CountCollectionRequest.h | 41 - .../request/CreateCollectionRequest.cpp | 114 - .../request/CreateCollectionRequest.h | 43 - .../delivery/request/CreateIndexRequest.cpp | 119 - .../delivery/request/CreateIndexRequest.h | 41 - .../request/CreatePartitionRequest.cpp | 112 - .../delivery/request/CreatePartitionRequest.h | 40 - .../delivery/request/DeleteByIDRequest.cpp | 93 - .../delivery/request/DeleteByIDRequest.h | 48 - .../request/DescribeCollectionRequest.cpp | 79 - .../request/DescribeCollectionRequest.h | 41 - .../delivery/request/DescribeIndexRequest.cpp | 90 - .../delivery/request/DescribeIndexRequest.h | 41 - .../request/DropCollectionRequest.cpp | 91 - .../delivery/request/DropCollectionRequest.h | 38 - .../delivery/request/DropIndexRequest.cpp | 81 - .../delivery/request/DropIndexRequest.h | 38 - .../delivery/request/DropPartitionRequest.cpp | 88 - .../delivery/request/DropPartitionRequest.h | 40 - .../server/delivery/request/FlushRequest.cpp | 86 - .../server/delivery/request/FlushRequest.h | 46 - .../delivery/request/GetVectorIDsRequest.cpp | 84 - .../delivery/request/GetVectorIDsRequest.h | 49 - .../request/GetVectorsByIDRequest.cpp | 95 - .../delivery/request/GetVectorsByIDRequest.h | 49 - .../delivery/request/HasCollectionRequest.cpp | 60 - .../delivery/request/HasCollectionRequest.h | 41 - .../delivery/request/HasPartitionRequest.cpp | 82 - .../delivery/request/HasPartitionRequest.h | 42 - .../server/delivery/request/InsertRequest.cpp | 185 - .../server/delivery/request/InsertRequest.h | 43 - .../request/PreloadCollectionRequest.cpp | 80 - .../request/PreloadCollectionRequest.h | 39 - .../request/ReLoadSegmentsRequest.cpp | 89 - .../delivery/request/ReLoadSegmentsRequest.h | 42 - .../delivery/request/SearchByIDRequest.cpp | 152 - .../delivery/request/SearchByIDRequest.h | 55 - .../delivery/request/SearchCombineRequest.cpp | 434 - .../delivery/request/SearchCombineRequest.h | 66 - .../server/delivery/request/SearchRequest.cpp | 184 - .../server/delivery/request/SearchRequest.h | 100 - .../request/ShowCollectionInfoRequest.cpp | 81 - .../request/ShowCollectionInfoRequest.h | 48 - .../request/ShowCollectionsRequest.cpp | 55 - .../delivery/request/ShowCollectionsRequest.h | 40 - .../request/ShowPartitionsRequest.cpp | 88 - .../delivery/request/ShowPartitionsRequest.h | 42 - .../delivery/strategy/RequestStrategy.h | 39 - .../delivery/strategy/SearchReqStrategy.cpp | 83 - .../delivery/strategy/SearchReqStrategy.h | 34 - .../server/grpc_impl/GrpcRequestHandler.cpp | 1132 - .../src/server/grpc_impl/GrpcRequestHandler.h | 413 - core/src/server/grpc_impl/GrpcServer.cpp | 131 - core/src/server/grpc_impl/GrpcServer.h | 55 - .../GrpcInterceptorHookHandler.cpp | 31 - .../interceptor/GrpcInterceptorHookHandler.h | 34 - .../grpc_impl/interceptor/SpanInterceptor.cpp | 42 - .../grpc_impl/interceptor/SpanInterceptor.h | 53 - core/src/server/init/CpuChecker.cpp | 71 - core/src/server/init/CpuChecker.h | 26 - core/src/server/init/GpuChecker.cpp | 274 - core/src/server/init/GpuChecker.h | 58 - core/src/server/init/InstanceLockCheck.cpp | 93 - core/src/server/init/InstanceLockCheck.h | 36 - core/src/server/init/StorageChecker.cpp | 113 - core/src/server/init/StorageChecker.h | 26 - core/src/server/web_impl/Constants.cpp | 91 - core/src/server/web_impl/Constants.h | 57 - core/src/server/web_impl/README.md | 1604 -- core/src/server/web_impl/Types.h | 77 - core/src/server/web_impl/WebServer.cpp | 98 - core/src/server/web_impl/WebServer.h | 61 - .../web_impl/component/AppComponent.hpp | 76 - .../web_impl/controller/WebController.hpp | 773 - .../src/server/web_impl/dto/CollectionDto.hpp | 62 - core/src/server/web_impl/dto/ConfigDto.hpp | 49 - core/src/server/web_impl/dto/DevicesDto.hpp | 39 - core/src/server/web_impl/dto/Dto.h | 24 - core/src/server/web_impl/dto/IndexDto.hpp | 38 - core/src/server/web_impl/dto/PartitionDto.hpp | 42 - core/src/server/web_impl/dto/StatusDto.hpp | 34 - core/src/server/web_impl/dto/VectorDto.hpp | 33 - .../web_impl/handler/WebRequestHandler.cpp | 1818 -- .../web_impl/handler/WebRequestHandler.h | 252 - core/src/server/web_impl/utils/Util.cpp | 75 - core/src/server/web_impl/utils/Util.h | 36 - core/src/storage/FSHandler.h | 42 - core/src/storage/IOReader.h | 41 - core/src/storage/IOWriter.h | 38 - core/src/storage/Operation.h | 51 - core/src/storage/disk/DiskIOReader.cpp | 48 - core/src/storage/disk/DiskIOReader.h | 59 - core/src/storage/disk/DiskIOWriter.cpp | 42 - core/src/storage/disk/DiskIOWriter.h | 57 - core/src/storage/disk/DiskOperation.cpp | 71 - core/src/storage/disk/DiskOperation.h | 58 - core/src/storage/s3/S3ClientMock.h | 105 - core/src/storage/s3/S3ClientWrapper.cpp | 284 - core/src/storage/s3/S3ClientWrapper.h | 69 - core/src/storage/s3/S3IOReader.cpp | 45 - core/src/storage/s3/S3IOReader.h | 59 - core/src/storage/s3/S3IOWriter.cpp | 43 - core/src/storage/s3/S3IOWriter.h | 56 - core/src/tracing/TextMapCarrier.cpp | 65 - core/src/tracing/TextMapCarrier.h | 43 - core/src/tracing/TraceContext.cpp | 40 - core/src/tracing/TraceContext.h | 41 - core/src/tracing/TracerUtil.cpp | 81 - core/src/tracing/TracerUtil.h | 39 - core/src/utils/BlockingQueue.h | 69 - core/src/utils/BlockingQueue.inl | 80 - core/src/utils/CommonUtil.cpp | 280 - core/src/utils/CommonUtil.h | 66 - core/src/utils/Error.h | 137 - core/src/utils/Exception.h | 56 - core/src/utils/Json.h | 17 - core/src/utils/Log.cpp | 52 - core/src/utils/Log.h | 188 - core/src/utils/LogUtil.cpp | 271 - core/src/utils/LogUtil.h | 47 - core/src/utils/SignalUtil.cpp | 66 - core/src/utils/SignalUtil.h | 26 - core/src/utils/Status.cpp | 133 - core/src/utils/Status.h | 79 - core/src/utils/StringHelpFunctions.cpp | 172 - core/src/utils/StringHelpFunctions.h | 76 - core/src/utils/ThreadPool.h | 114 - core/src/utils/TimeRecorder.cpp | 99 - core/src/utils/TimeRecorder.h | 65 - core/src/utils/ValidationUtil.cpp | 670 - core/src/utils/ValidationUtil.h | 104 - core/src/version.h.in | 15 - core/start_server.sh | 4 - core/stop_server.sh | 16 - core/thirdparty/dablooms/dablooms.cpp | 596 - core/thirdparty/dablooms/dablooms.h | 80 - core/thirdparty/dablooms/murmur.cpp | 120 - core/thirdparty/dablooms/murmur.h | 12 - .../thirdparty/easyloggingpp/easylogging++.cc | 3299 --- core/thirdparty/easyloggingpp/easylogging++.h | 5193 ---- core/thirdparty/nlohmann/json.hpp | 21006 ---------------- core/thirdparty/versions.txt | 17 - core/ubuntu_build_deps.sh | 14 - core/unittest/CMakeLists.txt | 235 - core/unittest/db/CMakeLists.txt | 42 - core/unittest/db/test_db.cpp | 1410 -- core/unittest/db/test_db_mysql.cpp | 480 - core/unittest/db/test_delete.cpp | 865 - core/unittest/db/test_engine.cpp | 271 - core/unittest/db/test_hybrid_db.cpp | 479 - core/unittest/db/test_mem.cpp | 513 - core/unittest/db/test_meta.cpp | 796 - core/unittest/db/test_meta_mysql.cpp | 850 - core/unittest/db/test_misc.cpp | 225 - core/unittest/db/test_search.cpp | 394 - core/unittest/db/test_search_by_id.cpp | 614 - core/unittest/db/test_wal.cpp | 1110 - core/unittest/db/utils.cpp | 433 - core/unittest/db/utils.h | 165 - core/unittest/main.cpp | 25 - core/unittest/metrics/CMakeLists.txt | 36 - core/unittest/metrics/test_metricbase.cpp | 62 - core/unittest/metrics/test_metrics.cpp | 169 - core/unittest/metrics/test_prometheus.cpp | 105 - core/unittest/metrics/utils.cpp | 88 - core/unittest/metrics/utils.h | 79 - core/unittest/scheduler/CMakeLists.txt | 39 - core/unittest/scheduler/test_action.cpp | 34 - core/unittest/scheduler/test_algorithm.cpp | 188 - core/unittest/scheduler/test_event.cpp | 53 - core/unittest/scheduler/test_job.cpp | 45 - core/unittest/scheduler/test_node.cpp | 97 - core/unittest/scheduler/test_resource.cpp | 287 - .../scheduler/test_resource_factory.cpp | 29 - core/unittest/scheduler/test_resource_mgr.cpp | 207 - core/unittest/scheduler/test_scheduler.cpp | 265 - core/unittest/scheduler/test_selector.cpp | 97 - core/unittest/scheduler/test_task.cpp | 193 - core/unittest/scheduler/test_tasktable.cpp | 448 - core/unittest/server/CMakeLists.txt | 56 - core/unittest/server/test_cache.cpp | 255 - core/unittest/server/test_check.cpp | 202 - core/unittest/server/test_config.cpp | 1377 - core/unittest/server/test_rpc.cpp | 1583 -- core/unittest/server/test_util.cpp | 875 - core/unittest/server/test_web.cpp | 1674 -- core/unittest/server/utils.cpp | 172 - core/unittest/server/utils.h | 27 - core/unittest/storage/CMakeLists.txt | 40 - core/unittest/storage/test_disk.cpp | 83 - core/unittest/storage/test_s3_client.cpp | 201 - core/unittest/storage/utils.cpp | 59 - core/unittest/storage/utils.h | 25 - core/unittest/thirdparty/CMakeLists.txt | 34 - core/unittest/thirdparty/test_dablooms.cpp | 253 - core/unittest/thirdparty/utils.cpp | 77 - core/unittest/thirdparty/utils.h | 54 - docker-compose.yml | 67 - docker/alertmanager.yml | 19 - docker/build_env/cpu/centos7/Dockerfile | 42 - .../cpu/centos7/docker-entrypoint.sh | 10 - docker/build_env/cpu/ubuntu16.04/Dockerfile | 52 - .../cpu/ubuntu16.04/docker-entrypoint.sh | 10 - docker/build_env/cpu/ubuntu18.04/Dockerfile | 52 - .../cpu/ubuntu18.04/docker-entrypoint.sh | 10 - docker/build_env/gpu/centos7/Dockerfile | 44 - .../gpu/centos7/docker-entrypoint.sh | 10 - docker/build_env/gpu/ubuntu16.04/Dockerfile | 50 - .../gpu/ubuntu16.04/docker-entrypoint.sh | 10 - docker/build_env/gpu/ubuntu18.04/Dockerfile | 50 - .../gpu/ubuntu18.04/docker-entrypoint.sh | 10 - docker/deploy/cpu/centos7/Dockerfile | 25 - docker/deploy/cpu/ubuntu16.04/Dockerfile | 28 - docker/deploy/cpu/ubuntu18.04/Dockerfile | 29 - docker/deploy/gpu/centos7/Dockerfile | 27 - docker/deploy/gpu/ubuntu16.04/Dockerfile | 33 - docker/deploy/gpu/ubuntu18.04/Dockerfile | 33 - docker/docker-compose-monitor.yml | 58 - docker/prometheus.yml | 38 - docker/server_down.yml | 8 - docker/test_env/Dockerfile | 30 - docker/test_env/docker-entrypoint.sh | 9 - docs/README.md | 25 - docs/test_report/ivfflat_test_report_cn.md | 215 - docs/test_report/ivfflat_test_report_en.md | 214 - ...vus_ivfsq8_test_report_detailed_version.md | 206 - ..._ivfsq8_test_report_detailed_version_cn.md | 207 - ...us_ivfsq8h_test_report_detailed_version.md | 179 - ...ivfsq8h_test_report_detailed_version_cn.md | 180 - sdk/CMakeLists.txt | 200 - sdk/README.md | 112 - sdk/build-support/code_style_clion.xml | 38 - sdk/build-support/cpplint.py | 6476 ----- sdk/build-support/lint_exclusions.txt | 5 - sdk/build-support/lintutils.py | 110 - sdk/build-support/run_clang_format.py | 142 - sdk/build-support/run_clang_tidy.py | 126 - sdk/build-support/run_cpplint.py | 132 - sdk/build.sh | 89 - sdk/cmake/BuildUtils.cmake | 204 - sdk/cmake/DefineOptions.cmake | 133 - sdk/cmake/FindClangTools.cmake | 109 - sdk/cmake/ThirdPartyPackages.cmake | 321 - sdk/examples/CMakeLists.txt | 20 - sdk/examples/binary_vector/CMakeLists.txt | 27 - sdk/examples/binary_vector/main.cpp | 73 - sdk/examples/binary_vector/src/ClientTest.cpp | 193 - sdk/examples/binary_vector/src/ClientTest.h | 20 - sdk/examples/hybrid/CMakeLists.txt | 27 - sdk/examples/hybrid/main.cpp | 73 - sdk/examples/hybrid/src/ClientTest.cpp | 155 - sdk/examples/hybrid/src/ClientTest.h | 46 - sdk/examples/partition/CMakeLists.txt | 28 - sdk/examples/partition/main.cpp | 73 - sdk/examples/partition/src/ClientTest.cpp | 223 - sdk/examples/partition/src/ClientTest.h | 20 - sdk/examples/qps/CMakeLists.txt | 28 - sdk/examples/qps/main.cpp | 180 - sdk/examples/qps/src/ClientTest.cpp | 445 - sdk/examples/qps/src/ClientTest.h | 97 - sdk/examples/simple/CMakeLists.txt | 27 - sdk/examples/simple/main.cpp | 73 - sdk/examples/simple/src/ClientTest.cpp | 322 - sdk/examples/simple/src/ClientTest.h | 88 - sdk/examples/utils/ThreadPool.h | 112 - sdk/examples/utils/TimeRecorder.cpp | 29 - sdk/examples/utils/TimeRecorder.h | 30 - sdk/examples/utils/Utils.cpp | 297 - sdk/examples/utils/Utils.h | 79 - sdk/grpc-gen/gen-milvus/milvus.grpc.pb.cc | 1723 -- sdk/grpc-gen/gen-milvus/milvus.grpc.pb.h | 7040 ------ sdk/grpc-gen/gen-milvus/milvus.pb.cc | 20489 --------------- sdk/grpc-gen/gen-milvus/milvus.pb.h | 14035 ----------- sdk/grpc-gen/gen-status/status.grpc.pb.cc | 24 - sdk/grpc-gen/gen-status/status.grpc.pb.h | 46 - sdk/grpc-gen/gen-status/status.pb.cc | 461 - sdk/grpc-gen/gen-status/status.pb.h | 360 - sdk/grpc/ClientProxy.cpp | 778 - sdk/grpc/ClientProxy.h | 151 - sdk/grpc/GrpcClient.cpp | 528 - sdk/grpc/GrpcClient.h | 124 - sdk/include/BooleanQuery.h | 68 - sdk/include/Field.h | 64 - sdk/include/GeneralQuery.h | 96 - sdk/include/MilvusApi.h | 585 - sdk/include/Status.h | 85 - sdk/interface/ConnectionImpl.cpp | 224 - sdk/interface/ConnectionImpl.h | 151 - sdk/interface/Status.cpp | 98 - sdk/thirdparty/nlohmann/json.hpp | 21006 ---------------- shards/.dockerignore | 14 - shards/Dockerfile | 12 - shards/Makefile | 41 - shards/README.md | 266 - shards/README_CN.md | 261 - shards/all_in_one/all_in_one.yml | 58 - shards/all_in_one/probe_test.py | 25 - shards/all_in_one/ro_server.yml | 187 - shards/all_in_one/wr_server.yml | 187 - shards/all_in_one_with_mysql/all_in_one.yml | 77 - shards/all_in_one_with_mysql/mysqld.cnf | 28 - shards/all_in_one_with_mysql/probe_test.py | 25 - shards/all_in_one_with_mysql/ro_server.yml | 187 - shards/all_in_one_with_mysql/wr_server.yml | 187 - shards/conftest.py | 44 - shards/discovery/__init__.py | 37 - shards/discovery/factory.py | 22 - .../discovery/plugins/kubernetes_provider.py | 396 - shards/discovery/plugins/static_provider.py | 80 - .../kubernetes_demo/mishards_auxiliary.yaml | 67 - .../kubernetes_demo/mishards_configmap.yaml | 200 - shards/kubernetes_demo/mishards_data_pvc.yaml | 57 - shards/kubernetes_demo/mishards_proxy.yaml | 88 - shards/kubernetes_demo/mishards_rbac.yaml | 24 - .../mishards_stateful_servers.yaml | 74 - .../mishards_write_servers.yaml | 76 - shards/kubernetes_demo/start.sh | 368 - shards/manager.py | 18 - shards/mishards/.env.example | 37 - shards/mishards/__init__.py | 47 - shards/mishards/connections.py | 292 - shards/mishards/db_base.py | 54 - shards/mishards/exception_codes.py | 11 - shards/mishards/exception_handlers.py | 91 - shards/mishards/exceptions.py | 47 - shards/mishards/factories.py | 54 - shards/mishards/grpc_utils/__init__.py | 37 - .../mishards/grpc_utils/grpc_args_parser.py | 129 - .../mishards/grpc_utils/grpc_args_wrapper.py | 4 - shards/mishards/grpc_utils/test_grpc.py | 75 - shards/mishards/hash_ring.py | 150 - shards/mishards/main.py | 15 - shards/mishards/models.py | 81 - shards/mishards/router/__init__.py | 43 - shards/mishards/router/factory.py | 17 - .../plugins/file_based_hash_ring_router.py | 143 - shards/mishards/server.py | 134 - shards/mishards/service_handler.py | 738 - shards/mishards/settings.py | 81 - shards/mishards/test_connections.py | 239 - shards/mishards/test_models.py | 39 - shards/mishards/test_server.py | 278 - shards/mishards/topology.py | 146 - shards/mishards/utilities.py | 20 - shards/requirements.txt | 38 - shards/setup.cfg | 4 - shards/tracer/__init__.py | 43 - shards/tracer/factory.py | 27 - shards/tracer/plugins/jaeger_factory.py | 37 - shards/utils/__init__.py | 18 - shards/utils/colors.py | 72 - shards/utils/logger_helper.py | 187 - shards/utils/pluginextension.py | 16 - shards/utils/plugins/__init__.py | 40 - tests/milvus-java-test/.gitignore | 4 - tests/milvus-java-test/MilvusSDkJavaTest.iml | 68 - tests/milvus-java-test/README.md | 29 - tests/milvus-java-test/bin/run.sh | 0 .../ci/function/file_transfer.groovy | 10 - .../ci/jenkinsfile/cleanup.groovy | 13 - .../ci/jenkinsfile/deploy_server.groovy | 16 - .../ci/jenkinsfile/integration_test.groovy | 13 - .../ci/jenkinsfile/notify.groovy | 15 - .../jenkinsfile/upload_unit_test_out.groovy | 13 - tests/milvus-java-test/ci/main_jenkinsfile | 110 - .../pod_containers/milvus-testframework.yaml | 13 - tests/milvus-java-test/milvus-java-test.iml | 2 - tests/milvus-java-test/pom.xml | 154 - .../src/main/java/com/MainClass.java | 185 - .../src/main/java/com/TestAddVectors.java | 176 - .../src/main/java/com/TestCollection.java | 142 - .../main/java/com/TestCollectionCount.java | 77 - .../src/main/java/com/TestCollectionInfo.java | 71 - .../src/main/java/com/TestCompact.java | 60 - .../src/main/java/com/TestConnect.java | 88 - .../src/main/java/com/TestDeleteVectors.java | 150 - .../src/main/java/com/TestFlush.java | 102 - .../src/main/java/com/TestGetVectorByID.java | 92 - .../src/main/java/com/TestIndex.java | 295 - .../src/main/java/com/TestMix.java | 228 - .../src/main/java/com/TestPS.java | 133 - .../src/main/java/com/TestPartition.java | 153 - .../src/main/java/com/TestPing.java | 25 - .../src/main/java/com/TestSearchByIds.java | 273 - .../src/main/java/com/TestSearchVectors.java | 318 - .../src/main/java/com/Utils.java | 79 - tests/milvus-java-test/testng.xml | 8 - tests/milvus_benchmark/.gitignore | 10 - tests/milvus_benchmark/README.md | 38 - tests/milvus_benchmark/__init__.py | 0 tests/milvus_benchmark/assets/Parameters.png | Bin 50835 -> 0 bytes .../gpu_search_performance_random50m-yaml.png | Bin 66119 -> 0 bytes ...milvus-nightly-performance-new-jenkins.png | Bin 45034 -> 0 bytes .../ci/function/file_transfer.groovy | 10 - .../ci/jenkinsfile/cleanup.groovy | 13 - .../ci/jenkinsfile/deploy_test.groovy | 21 - .../ci/jenkinsfile/notify.groovy | 15 - .../ci/jenkinsfile/publishDailyImages.groovy | 46 - tests/milvus_benchmark/ci/main_jenkinsfile | 152 - .../pod_containers/milvus-testframework.yaml | 13 - tests/milvus_benchmark/ci/publish_jenkinsfile | 103 - .../ci/scripts/yaml_processor.py | 536 - tests/milvus_benchmark/client.py | 372 - tests/milvus_benchmark/conf/log_config.conf | 28 - .../milvus_benchmark/conf/server_config.yaml | 34 - .../conf/server_config.yaml.cpu | 31 - .../conf/server_config.yaml.multi | 33 - .../conf/server_config.yaml.single | 32 - tests/milvus_benchmark/docker_runner.py | 366 - tests/milvus_benchmark/k8s_runner.py | 590 - tests/milvus_benchmark/local_runner.py | 330 - tests/milvus_benchmark/main.py | 172 - tests/milvus_benchmark/operation.py | 10 - tests/milvus_benchmark/parser.py | 86 - tests/milvus_benchmark/report.py | 10 - tests/milvus_benchmark/requirements.txt | 11 - tests/milvus_benchmark/runner.py | 288 - tests/milvus_benchmark/scheduler/070.json | 53 - tests/milvus_benchmark/scheduler/070_cpu.json | 29 - .../milvus_benchmark/scheduler/070_data.json | 58 - .../scheduler/070_data_eros.json | 11 - .../milvus_benchmark/scheduler/080_data.json | 63 - tests/milvus_benchmark/scheduler/acc.json | 15 - tests/milvus_benchmark/scheduler/ann_acc.json | 11 - tests/milvus_benchmark/scheduler/apollo.json | 15 - tests/milvus_benchmark/scheduler/athena.json | 11 - tests/milvus_benchmark/scheduler/build.json | 11 - tests/milvus_benchmark/scheduler/clean.json | 11 - .../milvus_benchmark/scheduler/crud_add.json | 11 - .../scheduler/crud_build.json | 12 - .../scheduler/crud_flush.json | 11 - .../scheduler/crud_search.json | 11 - .../scheduler/crud_stability.json | 11 - tests/milvus_benchmark/scheduler/debug.json | 11 - .../scheduler/default_config.json | 53 - tests/milvus_benchmark/scheduler/eros.json | 11 - .../milvus_benchmark/scheduler/file_size.json | 11 - tests/milvus_benchmark/scheduler/hnsw.json | 11 - tests/milvus_benchmark/scheduler/insert.json | 11 - tests/milvus_benchmark/scheduler/jaccard.json | 11 - .../milvus_benchmark/scheduler/poseidon.json | 27 - tests/milvus_benchmark/scheduler/search.json | 11 - .../scheduler/search_performance.json | 11 - tests/milvus_benchmark/scheduler/sift1b.json | 11 - .../milvus_benchmark/scheduler/stability.json | 11 - .../scripts/default_config.json | 86 - tests/milvus_benchmark/scripts/scheduler.py | 28 - .../suites/cpu_accuracy_ann.yaml | 173 - .../suites/gpu_accuracy_ann.yaml | 172 - tests/milvus_benchmark/utils.py | 401 - tests/milvus_go_test/client_test.go | 432 - tests/milvus_groundtruth/README.md | 34 - .../milvus_groundtruth/milvus_ground_truth.py | 281 - tests/milvus_python_test/.dockerignore | 14 - tests/milvus_python_test/.gitignore | 13 - tests/milvus_python_test/Dockerfile | 15 - tests/milvus_python_test/README.md | 23 - .../collection/test_collection.py | 1138 - .../collection/test_collection_count.py | 722 - .../collection/test_collection_stats.py | 281 - tests/milvus_python_test/conftest.py | 277 - tests/milvus_python_test/docker-entrypoint.sh | 9 - .../milvus_python_test/entity/test_delete.py | 559 - .../entity/test_get_entity_by_id.py | 466 - .../milvus_python_test/entity/test_insert.py | 1462 -- .../entity/test_list_id_in_segment.py | 496 - .../milvus_python_test/entity/test_search.py | 1240 - .../entity/test_search_by_id.py | 530 - tests/milvus_python_test/pytest.ini | 14 - tests/milvus_python_test/requirements.txt | 12 - .../requirements_cluster.txt | 25 - .../requirements_no_pymilvus.txt | 23 - tests/milvus_python_test/run.sh | 4 - .../stability/test_mysql.py | 53 - .../stability/test_restart.py | 134 - tests/milvus_python_test/test_compact.py | 1030 - tests/milvus_python_test/test_config.py | 1533 -- tests/milvus_python_test/test_connect.py | 241 - tests/milvus_python_test/test_flush.py | 294 - tests/milvus_python_test/test_index.py | 1887 -- tests/milvus_python_test/test_mix.py | 166 - tests/milvus_python_test/test_partition.py | 433 - tests/milvus_python_test/test_ping.py | 146 - tests/milvus_python_test/test_wal.py | 129 - tests/milvus_python_test/utils.py | 696 - 1683 files changed, 425673 deletions(-) delete mode 100644 .all-contributorsrc delete mode 100644 .clang-format delete mode 100644 .clang-tidy delete mode 100644 .clang-tidy-ignore delete mode 100644 .codacy.yaml delete mode 100644 .codebeatignore delete mode 100644 .env delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/documentation-request.md delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md delete mode 100644 .github/ISSUE_TEMPLATE/general-question.md delete mode 100644 .github/PULL_REQUEST_TEMPLATE.md delete mode 100644 .github/workflows/core.yml delete mode 100644 .gitignore delete mode 100644 .hadolint.yaml delete mode 100644 .mergify/config.yml delete mode 100644 CHANGELOG.md delete mode 100644 CODEOWNERS delete mode 100644 CODE_OF_CONDUCT.md delete mode 100644 COMMUNITY.md delete mode 100644 CONTRIBUTING.md delete mode 100644 DESIGN.md delete mode 100644 INSTALL.md delete mode 100644 LICENSE delete mode 100644 NOTICE.md delete mode 100644 OSSMETADATA delete mode 100644 README.md delete mode 100644 README_CN.md delete mode 100644 RELEASE.md delete mode 100644 SECURITY.md delete mode 100644 SUPPORT.md delete mode 100644 ci/docker/centos-7-core.dockerfile delete mode 100644 ci/docker/ubuntu-18.04-core.dockerfile delete mode 100644 ci/jenkins/Jenkinsfile delete mode 100644 ci/jenkins/pod/docker-pod.yaml delete mode 100644 ci/jenkins/pod/milvus-cpu-version-centos7-build-env-pod.yaml delete mode 100644 ci/jenkins/pod/milvus-cpu-version-ubuntu18.04-build-env-pod.yaml delete mode 100644 ci/jenkins/pod/milvus-gpu-version-centos7-build-env-pod.yaml delete mode 100644 ci/jenkins/pod/milvus-gpu-version-ubuntu18.04-build-env-pod.yaml delete mode 100644 ci/jenkins/pod/testEnvironment.yaml delete mode 100644 ci/jenkins/scripts/mail.py delete mode 100644 ci/jenkins/scripts/requirements.txt delete mode 100755 ci/jenkins/scripts/yaml_processor.py delete mode 100644 ci/jenkins/step/build.groovy delete mode 100644 ci/jenkins/step/cleanupShardsDev.groovy delete mode 100644 ci/jenkins/step/cleanupSingleDev.groovy delete mode 100644 ci/jenkins/step/coverage.groovy delete mode 100644 ci/jenkins/step/package.groovy delete mode 100644 ci/jenkins/step/publishImages.groovy delete mode 100644 ci/jenkins/step/shardsDevNightlyTest.groovy delete mode 100644 ci/jenkins/step/singleDevNightlyTest.groovy delete mode 100644 ci/jenkins/step/singleDevTest.groovy delete mode 100644 ci/jenkins/step/unittest.groovy delete mode 100755 ci/scripts/before-install.sh delete mode 100755 ci/scripts/build.sh delete mode 100755 ci/scripts/check_ccache.sh delete mode 100755 ci/scripts/coverage.sh delete mode 100755 ci/scripts/run_unittest.sh delete mode 100755 ci/scripts/update_ccache.sh delete mode 100644 codecov.yaml delete mode 100644 core/.gitignore delete mode 100644 core/CMakeLists.txt delete mode 100644 core/build-support/code_style_clion.xml delete mode 100755 core/build-support/cpplint.py delete mode 100644 core/build-support/lint_exclusions.txt delete mode 100755 core/build-support/lintutils.py delete mode 100755 core/build-support/run_clang_format.py delete mode 100755 core/build-support/run_clang_tidy.py delete mode 100755 core/build-support/run_cpplint.py delete mode 100755 core/build.sh delete mode 100755 core/centos7_build_deps.sh delete mode 100644 core/cmake/BuildUtils.cmake delete mode 100644 core/cmake/DefineOptions.cmake delete mode 100644 core/cmake/FindClangTools.cmake delete mode 100644 core/cmake/ThirdPartyPackages.cmake delete mode 100644 core/conf/demo/server_config.yaml delete mode 100644 core/conf/server_config.template delete mode 100644 core/conf/tracing_config.json delete mode 100755 core/coverage.sh delete mode 100644 core/scripts/migration/README.md delete mode 100644 core/scripts/migration/mysql_4_to_6.sql delete mode 100644 core/scripts/migration/mysql_6_to_4.sql delete mode 100644 core/scripts/migration/sqlite_4_to_6.sql delete mode 100644 core/scripts/migration/sqlite_6_to_4.sql delete mode 100755 core/scripts/start_server.sh delete mode 100755 core/scripts/stop_server.sh delete mode 100644 core/src/CMakeLists.txt delete mode 100644 core/src/cache/Cache.h delete mode 100644 core/src/cache/Cache.inl delete mode 100644 core/src/cache/CacheMgr.h delete mode 100644 core/src/cache/CacheMgr.inl delete mode 100644 core/src/cache/CpuCacheMgr.cpp delete mode 100644 core/src/cache/CpuCacheMgr.h delete mode 100644 core/src/cache/DataObj.h delete mode 100644 core/src/cache/GpuCacheMgr.cpp delete mode 100644 core/src/cache/GpuCacheMgr.h delete mode 100644 core/src/cache/LRU.h delete mode 100644 core/src/codecs/AttrsFormat.h delete mode 100644 core/src/codecs/AttrsIndexFormat.h delete mode 100644 core/src/codecs/Codec.h delete mode 100644 core/src/codecs/DeletedDocsFormat.h delete mode 100644 core/src/codecs/IdBloomFilterFormat.h delete mode 100644 core/src/codecs/IdIndexFormat.h delete mode 100644 core/src/codecs/VectorIndexFormat.h delete mode 100644 core/src/codecs/VectorsFormat.h delete mode 100644 core/src/codecs/default/DefaultAttrsFormat.cpp delete mode 100644 core/src/codecs/default/DefaultAttrsFormat.h delete mode 100644 core/src/codecs/default/DefaultCodec.cpp delete mode 100644 core/src/codecs/default/DefaultCodec.h delete mode 100644 core/src/codecs/default/DefaultDeletedDocsFormat.cpp delete mode 100644 core/src/codecs/default/DefaultDeletedDocsFormat.h delete mode 100644 core/src/codecs/default/DefaultIdBloomFilterFormat.cpp delete mode 100644 core/src/codecs/default/DefaultIdBloomFilterFormat.h delete mode 100644 core/src/codecs/default/DefaultVectorIndexFormat.cpp delete mode 100644 core/src/codecs/default/DefaultVectorIndexFormat.h delete mode 100644 core/src/codecs/default/DefaultVectorsFormat.cpp delete mode 100644 core/src/codecs/default/DefaultVectorsFormat.h delete mode 100644 core/src/config/Config.cpp delete mode 100644 core/src/config/Config.h delete mode 100644 core/src/config/ConfigMgr.h delete mode 100644 core/src/config/ConfigNode.cpp delete mode 100644 core/src/config/ConfigNode.h delete mode 100644 core/src/config/Utils.cpp delete mode 100644 core/src/config/Utils.h delete mode 100644 core/src/config/YamlConfigMgr.cpp delete mode 100644 core/src/config/YamlConfigMgr.h delete mode 100644 core/src/config/handler/CacheConfigHandler.cpp delete mode 100644 core/src/config/handler/CacheConfigHandler.h delete mode 100644 core/src/config/handler/ConfigHandler.h delete mode 100644 core/src/config/handler/EngineConfigHandler.cpp delete mode 100644 core/src/config/handler/EngineConfigHandler.h delete mode 100644 core/src/config/handler/GpuResourceConfigHandler.cpp delete mode 100644 core/src/config/handler/GpuResourceConfigHandler.h delete mode 100644 core/src/context/HybridSearchContext.h delete mode 100644 core/src/db/Constants.h delete mode 100644 core/src/db/DB.h delete mode 100644 core/src/db/DBFactory.cpp delete mode 100644 core/src/db/DBFactory.h delete mode 100644 core/src/db/DBImpl.cpp delete mode 100644 core/src/db/DBImpl.h delete mode 100644 core/src/db/IDGenerator.cpp delete mode 100644 core/src/db/IDGenerator.h delete mode 100644 core/src/db/IndexFailedChecker.cpp delete mode 100644 core/src/db/IndexFailedChecker.h delete mode 100644 core/src/db/Options.cpp delete mode 100644 core/src/db/Options.h delete mode 100644 core/src/db/Types.h delete mode 100644 core/src/db/Utils.cpp delete mode 100644 core/src/db/Utils.h delete mode 100644 core/src/db/engine/EngineFactory.cpp delete mode 100644 core/src/db/engine/EngineFactory.h delete mode 100644 core/src/db/engine/ExecutionEngine.h delete mode 100644 core/src/db/engine/ExecutionEngineImpl.cpp delete mode 100644 core/src/db/engine/ExecutionEngineImpl.h delete mode 100644 core/src/db/insert/MemManager.h delete mode 100644 core/src/db/insert/MemManagerFactory.cpp delete mode 100644 core/src/db/insert/MemManagerFactory.h delete mode 100644 core/src/db/insert/MemManagerImpl.cpp delete mode 100644 core/src/db/insert/MemManagerImpl.h delete mode 100644 core/src/db/insert/MemTable.cpp delete mode 100644 core/src/db/insert/MemTable.h delete mode 100644 core/src/db/insert/MemTableFile.cpp delete mode 100644 core/src/db/insert/MemTableFile.h delete mode 100644 core/src/db/insert/VectorSource.cpp delete mode 100644 core/src/db/insert/VectorSource.h delete mode 100644 core/src/db/merge/MergeAdaptiveStrategy.cpp delete mode 100644 core/src/db/merge/MergeAdaptiveStrategy.h delete mode 100644 core/src/db/merge/MergeLayeredStrategy.cpp delete mode 100644 core/src/db/merge/MergeLayeredStrategy.h delete mode 100644 core/src/db/merge/MergeManager.h delete mode 100644 core/src/db/merge/MergeManagerFactory.cpp delete mode 100644 core/src/db/merge/MergeManagerFactory.h delete mode 100644 core/src/db/merge/MergeManagerImpl.cpp delete mode 100644 core/src/db/merge/MergeManagerImpl.h delete mode 100644 core/src/db/merge/MergeSimpleStrategy.cpp delete mode 100644 core/src/db/merge/MergeSimpleStrategy.h delete mode 100644 core/src/db/merge/MergeStrategy.h delete mode 100644 core/src/db/merge/MergeTask.cpp delete mode 100644 core/src/db/merge/MergeTask.h delete mode 100644 core/src/db/meta/FilesHolder.cpp delete mode 100644 core/src/db/meta/FilesHolder.h delete mode 100644 core/src/db/meta/Meta.cpp delete mode 100644 core/src/db/meta/Meta.h delete mode 100644 core/src/db/meta/MetaConsts.h delete mode 100644 core/src/db/meta/MetaFactory.cpp delete mode 100644 core/src/db/meta/MetaFactory.h delete mode 100644 core/src/db/meta/MetaTypes.h delete mode 100644 core/src/db/meta/MySQLConnectionPool.cpp delete mode 100644 core/src/db/meta/MySQLConnectionPool.h delete mode 100644 core/src/db/meta/MySQLMetaImpl.cpp delete mode 100644 core/src/db/meta/MySQLMetaImpl.h delete mode 100644 core/src/db/meta/SqliteMetaImpl.cpp delete mode 100644 core/src/db/meta/SqliteMetaImpl.h delete mode 100644 core/src/db/wal/WalBuffer.cpp delete mode 100644 core/src/db/wal/WalBuffer.h delete mode 100644 core/src/db/wal/WalDefinations.h delete mode 100644 core/src/db/wal/WalFileHandler.cpp delete mode 100644 core/src/db/wal/WalFileHandler.h delete mode 100644 core/src/db/wal/WalManager.cpp delete mode 100644 core/src/db/wal/WalManager.h delete mode 100644 core/src/db/wal/WalMetaHandler.cpp delete mode 100644 core/src/db/wal/WalMetaHandler.h delete mode 100755 core/src/grpc/cpp_gen.sh delete mode 100644 core/src/grpc/gen-milvus/milvus.grpc.pb.cc delete mode 100644 core/src/grpc/gen-milvus/milvus.grpc.pb.h delete mode 100644 core/src/grpc/gen-milvus/milvus.pb.cc delete mode 100644 core/src/grpc/gen-milvus/milvus.pb.h delete mode 100644 core/src/grpc/gen-status/status.grpc.pb.cc delete mode 100644 core/src/grpc/gen-status/status.grpc.pb.h delete mode 100644 core/src/grpc/gen-status/status.pb.cc delete mode 100644 core/src/grpc/gen-status/status.pb.h delete mode 100644 core/src/grpc/milvus.proto delete mode 100644 core/src/grpc/status.proto delete mode 100644 core/src/index/.gitignore delete mode 100644 core/src/index/CMakeLists.txt delete mode 100644 core/src/index/archive/KnowhereResource.cpp delete mode 100644 core/src/index/archive/KnowhereResource.h delete mode 100755 core/src/index/build.sh delete mode 100644 core/src/index/cmake/BuildUtilsCore.cmake delete mode 100644 core/src/index/cmake/DefineOptionsCore.cmake delete mode 100644 core/src/index/cmake/FindArrow.cmake delete mode 100644 core/src/index/cmake/FindOpenBLAS.cmake delete mode 100644 core/src/index/cmake/ThirdPartyPackagesCore.cmake delete mode 100644 core/src/index/knowhere/CMakeLists.txt delete mode 100644 core/src/index/knowhere/knowhere/common/BinarySet.h delete mode 100644 core/src/index/knowhere/knowhere/common/Config.h delete mode 100644 core/src/index/knowhere/knowhere/common/Dataset.h delete mode 100644 core/src/index/knowhere/knowhere/common/Exception.cpp delete mode 100644 core/src/index/knowhere/knowhere/common/Exception.h delete mode 100644 core/src/index/knowhere/knowhere/common/Log.cpp delete mode 100644 core/src/index/knowhere/knowhere/common/Log.h delete mode 100644 core/src/index/knowhere/knowhere/common/Timer.cpp delete mode 100644 core/src/index/knowhere/knowhere/common/Timer.h delete mode 100644 core/src/index/knowhere/knowhere/common/Typedef.h delete mode 100644 core/src/index/knowhere/knowhere/index/Index.h delete mode 100644 core/src/index/knowhere/knowhere/index/preprocessor/Preprocessor.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/ConfAdapter.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/ConfAdapter.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/ConfAdapterMgr.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/ConfAdapterMgr.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/FaissBaseBinaryIndex.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/FaissBaseBinaryIndex.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/FaissBaseIndex.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/FaissBaseIndex.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexAnnoy.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexAnnoy.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexBinaryIDMAP.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexBinaryIDMAP.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexBinaryIVF.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexBinaryIVF.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexHNSW.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexHNSW.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexIDMAP.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexIDMAP.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexIVF.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexIVF.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexIVFPQ.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexIVFPQ.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexIVFSQ.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexIVFSQ.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexNSG.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexNSG.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexSPTAG.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexSPTAG.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexType.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/IndexType.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/VecIndex.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/VecIndexFactory.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/VecIndexFactory.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/adapter/SptagAdapter.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/adapter/SptagAdapter.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/adapter/VectorAdapter.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/adapter/VectorAdapter.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/gpu/GPUIndex.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIDMAP.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIDMAP.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVF.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVF.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFPQ.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFPQ.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFSQ.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFSQ.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexIVFSQHybrid.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexIVFSQHybrid.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/gpu/Quantizer.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/helpers/BuilderSuspend.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/helpers/Cloner.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/helpers/Cloner.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/helpers/FaissGpuResourceMgr.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/helpers/FaissGpuResourceMgr.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/helpers/FaissIO.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/helpers/FaissIO.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/helpers/IndexParameter.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/helpers/IndexParameter.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/helpers/SPTAGParameterMgr.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/helpers/SPTAGParameterMgr.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/Distance.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/Distance.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSG.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSG.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSGHelper.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSGHelper.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSGIO.cpp delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSGIO.h delete mode 100644 core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/Neighbor.h delete mode 100644 core/src/index/thirdparty/SPTAG/.github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 core/src/index/thirdparty/SPTAG/.github/ISSUE_TEMPLATE/feature_request.md delete mode 100644 core/src/index/thirdparty/SPTAG/.gitignore delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService.users.props delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/Aggregator.vcxproj delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/Aggregator.vcxproj.filters delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/CMakeLists.txt delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/Client.vcxproj delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/Client.vcxproj.filters delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/CoreLibrary.vcxproj delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/CoreLibrary.vcxproj.filters delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/IndexBuilder.vcxproj delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/IndexBuilder.vcxproj.filters delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/IndexSearcher.vcxproj delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/IndexSearcher.vcxproj.filters delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/Server.vcxproj delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/Server.vcxproj.filters delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/SocketLib.vcxproj delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/SocketLib.vcxproj.filters delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Aggregator/AggregatorContext.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Aggregator/AggregatorExecutionContext.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Aggregator/AggregatorService.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Aggregator/AggregatorSettings.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Client/ClientWrapper.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Client/Options.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/BKT/Index.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/BKT/ParameterDefinitionList.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/BKTree.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/CommonUtils.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/DataUtils.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/Dataset.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/DistanceUtils.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/FineGrainedLock.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/Heap.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/KDTree.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/NeighborhoodGraph.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/QueryResultSet.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/RelativeNeighborhoodGraph.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/WorkSpace.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/WorkSpacePool.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/CommonDataStructure.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/DefinitionList.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/KDT/Index.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/KDT/ParameterDefinitionList.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/MetadataSet.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/SearchQuery.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/SearchResult.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/VectorIndex.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Core/VectorSet.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/ArgumentsParser.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/Base64Encode.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/BufferStream.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/CommonHelper.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/Concurrent.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/ConcurrentSet.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/SimpleIniReader.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/StringConvert.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/VectorSetReader.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/VectorSetReaders/DefaultReader.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/IndexBuilder/Options.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/IndexBuilder/ThreadPool.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Server/QueryParser.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Server/SearchExecutionContext.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Server/SearchExecutor.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Server/SearchService.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Server/ServiceContext.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Server/ServiceSettings.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Client.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Common.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Connection.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/ConnectionManager.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Packet.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/RemoteSearchQuery.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/ResourceManager.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Server.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/SimpleSerialization.h delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/packages.config delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/AggregatorContext.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/AggregatorExecutionContext.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/AggregatorService.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/AggregatorSettings.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/main.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Client/ClientWrapper.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Client/Options.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Client/main.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Core/BKT/BKTIndex.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Core/Common/NeighborhoodGraph.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Core/Common/WorkSpacePool.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Core/KDT/KDTIndex.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Core/MetadataSet.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Core/VectorIndex.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Core/VectorSet.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Helper/ArgumentsParser.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Helper/Base64Encode.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Helper/CommonHelper.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Helper/Concurrent.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Helper/SimpleIniReader.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Helper/VectorSetReader.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Helper/VectorSetReaders/DefaultReader.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/IndexBuilder/Options.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/IndexBuilder/ThreadPool.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/IndexBuilder/main.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/IndexSearcher/main.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Server/QueryParser.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Server/SearchExecutionContext.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Server/SearchExecutor.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Server/SearchService.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Server/ServiceContext.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Server/ServiceSettings.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Server/main.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Client.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Common.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Connection.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Socket/ConnectionManager.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Packet.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Socket/RemoteSearchQuery.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Server.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/CMakeLists.txt delete mode 100644 core/src/index/thirdparty/SPTAG/Dockerfile delete mode 100644 core/src/index/thirdparty/SPTAG/LICENSE delete mode 100644 core/src/index/thirdparty/SPTAG/README.md delete mode 100644 core/src/index/thirdparty/SPTAG/SPTAG.sdf delete mode 100644 core/src/index/thirdparty/SPTAG/SPTAG.sln delete mode 100644 core/src/index/thirdparty/SPTAG/Test/CMakeLists.txt delete mode 100644 core/src/index/thirdparty/SPTAG/Test/Test.vcxproj delete mode 100644 core/src/index/thirdparty/SPTAG/Test/Test.vcxproj.filters delete mode 100644 core/src/index/thirdparty/SPTAG/Test/Test.vcxproj.user delete mode 100644 core/src/index/thirdparty/SPTAG/Test/inc/Test.h delete mode 100644 core/src/index/thirdparty/SPTAG/Test/packages.config delete mode 100644 core/src/index/thirdparty/SPTAG/Test/src/AlgoTest.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/Test/src/Base64HelperTest.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/Test/src/CommonHelperTest.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/Test/src/DistanceTest.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/Test/src/IniReaderTest.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/Test/src/StringConvertTest.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/Test/src/main.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/CLRCore.vcxproj delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/CLRCore.vcxproj.filters delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/CMakeLists.txt delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/CsharpClient.vcxproj delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/CsharpClient.vcxproj.filters delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/CsharpCore.vcxproj delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/CsharpCore.vcxproj.filters delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/JavaClient.vcxproj delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/JavaClient.vcxproj.filters delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/JavaCore.vcxproj delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/JavaCore.vcxproj.filters delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/PythonClient.vcxproj delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/PythonClient.vcxproj.filters delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/PythonCore.vcxproj delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/PythonCore.vcxproj.filters delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/PythonCore.vcxproj.user delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/inc/CLRCoreInterface.h delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/inc/ClientInterface.h delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/inc/CoreInterface.h delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/inc/CsharpClient.i delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/inc/CsharpCommon.i delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/inc/CsharpCore.i delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/inc/JavaClient.i delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/inc/JavaCommon.i delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/inc/JavaCore.i delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/inc/ManagedObject.h delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/inc/PythonClient.i delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/inc/PythonCommon.i delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/inc/PythonCore.i delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/inc/TransferDataType.h delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/packages.config delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/src/AssemblyInfo.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/src/CLRCoreInterface.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/src/ClientInterface.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/Wrappers/src/CoreInterface.cpp delete mode 100644 core/src/index/thirdparty/SPTAG/azure-pipelines.yml delete mode 100644 core/src/index/thirdparty/SPTAG/docs/GettingStart.md delete mode 100644 core/src/index/thirdparty/SPTAG/docs/Parameters.md delete mode 100644 core/src/index/thirdparty/SPTAG/docs/img/sptag.png delete mode 100644 core/src/index/thirdparty/annoy/LICENSE delete mode 100644 core/src/index/thirdparty/annoy/RELEASE.md delete mode 100644 core/src/index/thirdparty/annoy/examples/mmap_test.py delete mode 100644 core/src/index/thirdparty/annoy/examples/precision_test.cpp delete mode 100644 core/src/index/thirdparty/annoy/examples/precision_test.py delete mode 100755 core/src/index/thirdparty/annoy/examples/s_compile_cpp.sh delete mode 100644 core/src/index/thirdparty/annoy/examples/simple_test.py delete mode 100644 core/src/index/thirdparty/annoy/src/annoygomodule.h delete mode 100644 core/src/index/thirdparty/annoy/src/annoygomodule.i delete mode 100644 core/src/index/thirdparty/annoy/src/annoylib.h delete mode 100644 core/src/index/thirdparty/annoy/src/annoyluamodule.cc delete mode 100644 core/src/index/thirdparty/annoy/src/annoymodule.cc delete mode 100644 core/src/index/thirdparty/annoy/src/kissrandom.h delete mode 100644 core/src/index/thirdparty/annoy/src/mman.h delete mode 100755 core/src/index/thirdparty/build.sh delete mode 100644 core/src/index/thirdparty/faiss/.dockerignore delete mode 100644 core/src/index/thirdparty/faiss/.gitignore delete mode 100644 core/src/index/thirdparty/faiss/AutoTune.cpp delete mode 100644 core/src/index/thirdparty/faiss/AutoTune.h delete mode 100644 core/src/index/thirdparty/faiss/BuilderSuspend.cpp delete mode 100644 core/src/index/thirdparty/faiss/BuilderSuspend.h delete mode 100644 core/src/index/thirdparty/faiss/CODE_OF_CONDUCT.md delete mode 100644 core/src/index/thirdparty/faiss/CONTRIBUTING.md delete mode 100644 core/src/index/thirdparty/faiss/Clustering.cpp delete mode 100644 core/src/index/thirdparty/faiss/Clustering.h delete mode 100644 core/src/index/thirdparty/faiss/DirectMap.cpp delete mode 100644 core/src/index/thirdparty/faiss/DirectMap.h delete mode 100644 core/src/index/thirdparty/faiss/Dockerfile delete mode 100644 core/src/index/thirdparty/faiss/FaissHook.cpp delete mode 100644 core/src/index/thirdparty/faiss/FaissHook.h delete mode 100644 core/src/index/thirdparty/faiss/INSTALL.md delete mode 100644 core/src/index/thirdparty/faiss/IVFlib.cpp delete mode 100644 core/src/index/thirdparty/faiss/IVFlib.h delete mode 100644 core/src/index/thirdparty/faiss/Index.cpp delete mode 100644 core/src/index/thirdparty/faiss/Index.h delete mode 100644 core/src/index/thirdparty/faiss/Index2Layer.cpp delete mode 100644 core/src/index/thirdparty/faiss/Index2Layer.h delete mode 100644 core/src/index/thirdparty/faiss/IndexBinary.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexBinary.h delete mode 100644 core/src/index/thirdparty/faiss/IndexBinaryFlat.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexBinaryFlat.h delete mode 100644 core/src/index/thirdparty/faiss/IndexBinaryFromFloat.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexBinaryFromFloat.h delete mode 100644 core/src/index/thirdparty/faiss/IndexBinaryHNSW.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexBinaryHNSW.h delete mode 100644 core/src/index/thirdparty/faiss/IndexBinaryHash.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexBinaryHash.h delete mode 100644 core/src/index/thirdparty/faiss/IndexBinaryIVF.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexBinaryIVF.h delete mode 100644 core/src/index/thirdparty/faiss/IndexFlat.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexFlat.h delete mode 100644 core/src/index/thirdparty/faiss/IndexHNSW.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexHNSW.h delete mode 100644 core/src/index/thirdparty/faiss/IndexIVF.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexIVF.h delete mode 100644 core/src/index/thirdparty/faiss/IndexIVFFlat.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexIVFFlat.h delete mode 100644 core/src/index/thirdparty/faiss/IndexIVFPQ.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexIVFPQ.h delete mode 100644 core/src/index/thirdparty/faiss/IndexIVFPQR.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexIVFPQR.h delete mode 100644 core/src/index/thirdparty/faiss/IndexIVFSpectralHash.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexIVFSpectralHash.h delete mode 100644 core/src/index/thirdparty/faiss/IndexLSH.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexLSH.h delete mode 100644 core/src/index/thirdparty/faiss/IndexLattice.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexLattice.h delete mode 100644 core/src/index/thirdparty/faiss/IndexPQ.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexPQ.h delete mode 100644 core/src/index/thirdparty/faiss/IndexPreTransform.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexPreTransform.h delete mode 100644 core/src/index/thirdparty/faiss/IndexReplicas.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexReplicas.h delete mode 100644 core/src/index/thirdparty/faiss/IndexSQHybrid.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexSQHybrid.h delete mode 100644 core/src/index/thirdparty/faiss/IndexScalarQuantizer.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexScalarQuantizer.h delete mode 100644 core/src/index/thirdparty/faiss/IndexShards.cpp delete mode 100644 core/src/index/thirdparty/faiss/IndexShards.h delete mode 100644 core/src/index/thirdparty/faiss/InvertedLists.cpp delete mode 100644 core/src/index/thirdparty/faiss/InvertedLists.h delete mode 100644 core/src/index/thirdparty/faiss/LICENSE delete mode 100644 core/src/index/thirdparty/faiss/Makefile delete mode 100644 core/src/index/thirdparty/faiss/MatrixStats.cpp delete mode 100644 core/src/index/thirdparty/faiss/MatrixStats.h delete mode 100644 core/src/index/thirdparty/faiss/MetaIndexes.cpp delete mode 100644 core/src/index/thirdparty/faiss/MetaIndexes.h delete mode 100644 core/src/index/thirdparty/faiss/MetricType.h delete mode 100644 core/src/index/thirdparty/faiss/OnDiskInvertedLists.cpp delete mode 100644 core/src/index/thirdparty/faiss/OnDiskInvertedLists.h delete mode 100644 core/src/index/thirdparty/faiss/README.md delete mode 100644 core/src/index/thirdparty/faiss/VectorTransform.cpp delete mode 100644 core/src/index/thirdparty/faiss/VectorTransform.h delete mode 100644 core/src/index/thirdparty/faiss/acinclude/ax_blas.m4 delete mode 100644 core/src/index/thirdparty/faiss/acinclude/ax_check_cpu.m4 delete mode 100644 core/src/index/thirdparty/faiss/acinclude/ax_cxx_compile_stdcxx.m4 delete mode 100644 core/src/index/thirdparty/faiss/acinclude/ax_lapack.m4 delete mode 100644 core/src/index/thirdparty/faiss/acinclude/fa_check_cuda.m4 delete mode 100644 core/src/index/thirdparty/faiss/acinclude/fa_numpy.m4 delete mode 100644 core/src/index/thirdparty/faiss/acinclude/fa_prog_nm.m4 delete mode 100644 core/src/index/thirdparty/faiss/acinclude/fa_prog_swig.m4 delete mode 100644 core/src/index/thirdparty/faiss/acinclude/fa_python.m4 delete mode 100644 core/src/index/thirdparty/faiss/benchs/README.md delete mode 100644 core/src/index/thirdparty/faiss/benchs/bench_all_ivf/README.md delete mode 100644 core/src/index/thirdparty/faiss/benchs/bench_all_ivf/bench_all_ivf.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/bench_all_ivf/bench_kmeans.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/bench_all_ivf/datasets.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/bench_all_ivf/parse_bench_all_ivf.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/bench_all_ivf/run_on_cluster_generic.bash delete mode 100644 core/src/index/thirdparty/faiss/benchs/bench_for_interrupt.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/bench_gpu_1bn.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/bench_gpu_sift1m.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/bench_hnsw.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/bench_index_pq.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/bench_pairwise_distances.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/bench_polysemous_1bn.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/bench_polysemous_sift1m.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/bench_scalar_quantizer.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/bench_vector_ops.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/datasets.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/distributed_ondisk/README.md delete mode 100644 core/src/index/thirdparty/faiss/benchs/distributed_ondisk/combined_index.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/distributed_ondisk/distributed_kmeans.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/distributed_ondisk/distributed_query_demo.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/distributed_ondisk/make_index_vslice.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/distributed_ondisk/make_trained_index.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/distributed_ondisk/merge_to_ondisk.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/distributed_ondisk/rpc.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/distributed_ondisk/run_on_cluster.bash delete mode 100644 core/src/index/thirdparty/faiss/benchs/distributed_ondisk/search_server.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/kmeans_mnist.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/link_and_code/README.md delete mode 100644 core/src/index/thirdparty/faiss/benchs/link_and_code/bench_link_and_code.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/link_and_code/datasets.py delete mode 100644 core/src/index/thirdparty/faiss/benchs/link_and_code/neighbor_codec.py delete mode 100755 core/src/index/thirdparty/faiss/build-aux/config.guess delete mode 100755 core/src/index/thirdparty/faiss/build-aux/config.sub delete mode 100755 core/src/index/thirdparty/faiss/build-aux/install-sh delete mode 100755 core/src/index/thirdparty/faiss/build.sh delete mode 100644 core/src/index/thirdparty/faiss/c_api/AutoTune_c.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/AutoTune_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/Clustering_c.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/Clustering_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/INSTALL.md delete mode 100644 core/src/index/thirdparty/faiss/c_api/IndexFlat_c.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/IndexFlat_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/IndexIVFFlat_c.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/IndexIVFFlat_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/IndexIVF_c.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/IndexIVF_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/IndexLSH_c.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/IndexLSH_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/IndexPreTransform_c.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/IndexPreTransform_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/IndexShards_c.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/IndexShards_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/Index_c.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/Index_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/Makefile delete mode 100644 core/src/index/thirdparty/faiss/c_api/MetaIndexes_c.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/MetaIndexes_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/clone_index_c.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/clone_index_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/error_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/error_impl.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/error_impl.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/example_c.c delete mode 100644 core/src/index/thirdparty/faiss/c_api/faiss_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/gpu/GpuAutoTune_c.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/gpu/GpuAutoTune_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/gpu/GpuClonerOptions_c.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/gpu/GpuClonerOptions_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/gpu/GpuIndex_c.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/gpu/GpuIndex_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/gpu/GpuIndicesOptions_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/gpu/GpuResources_c.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/gpu/GpuResources_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/gpu/Makefile delete mode 100644 core/src/index/thirdparty/faiss/c_api/gpu/StandardGpuResources_c.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/gpu/StandardGpuResources_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/gpu/example_gpu_c.c delete mode 100644 core/src/index/thirdparty/faiss/c_api/gpu/macros_impl.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/impl/AuxIndexStructures_c.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/impl/AuxIndexStructures_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/index_factory_c.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/index_factory_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/index_io_c.cpp delete mode 100644 core/src/index/thirdparty/faiss/c_api/index_io_c.h delete mode 100644 core/src/index/thirdparty/faiss/c_api/macros_impl.h delete mode 100644 core/src/index/thirdparty/faiss/clone_index.cpp delete mode 100644 core/src/index/thirdparty/faiss/clone_index.h delete mode 100644 core/src/index/thirdparty/faiss/conda/Dockerfile delete mode 100644 core/src/index/thirdparty/faiss/conda/conda_build_config.yaml delete mode 100644 core/src/index/thirdparty/faiss/conda/faiss-gpu/build.sh delete mode 100644 core/src/index/thirdparty/faiss/conda/faiss-gpu/conda_build_config.yaml delete mode 100644 core/src/index/thirdparty/faiss/conda/faiss-gpu/meta.yaml delete mode 100644 core/src/index/thirdparty/faiss/conda/faiss-gpu/run_test.py delete mode 100644 core/src/index/thirdparty/faiss/conda/faiss/build.sh delete mode 100644 core/src/index/thirdparty/faiss/conda/faiss/meta.yaml delete mode 100644 core/src/index/thirdparty/faiss/conda/faiss/run_test.py delete mode 100755 core/src/index/thirdparty/faiss/configure delete mode 100644 core/src/index/thirdparty/faiss/configure.ac delete mode 100644 core/src/index/thirdparty/faiss/demos/Makefile delete mode 100644 core/src/index/thirdparty/faiss/demos/README.md delete mode 100644 core/src/index/thirdparty/faiss/demos/demo_auto_tune.py delete mode 100644 core/src/index/thirdparty/faiss/demos/demo_imi_flat.cpp delete mode 100644 core/src/index/thirdparty/faiss/demos/demo_imi_pq.cpp delete mode 100644 core/src/index/thirdparty/faiss/demos/demo_ivfpq_indexing.cpp delete mode 100644 core/src/index/thirdparty/faiss/demos/demo_ondisk_ivf.py delete mode 100644 core/src/index/thirdparty/faiss/demos/demo_sift1M.cpp delete mode 100644 core/src/index/thirdparty/faiss/demos/demo_weighted_kmeans.cpp delete mode 100644 core/src/index/thirdparty/faiss/example_makefiles/makefile.inc.Linux delete mode 100644 core/src/index/thirdparty/faiss/example_makefiles/makefile.inc.Mac.brew delete mode 100644 core/src/index/thirdparty/faiss/example_makefiles/makefile.inc.Mac.port delete mode 120000 core/src/index/thirdparty/faiss/faiss delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuAutoTune.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuAutoTune.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuCloner.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuCloner.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuClonerOptions.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuClonerOptions.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuDistance.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuDistance.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuFaissAssert.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuIndex.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuIndex.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuIndexBinaryFlat.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuIndexBinaryFlat.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuIndexFlat.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuIndexFlat.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuIndexIVF.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuIndexIVF.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuIndexIVFFlat.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuIndexIVFFlat.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuIndexIVFPQ.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuIndexIVFPQ.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuIndexIVFSQHybrid.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuIndexIVFSQHybrid.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuIndexIVFScalarQuantizer.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuIndexIVFScalarQuantizer.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuIndicesOptions.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuResources.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/GpuResources.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/StandardGpuResources.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/StandardGpuResources.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/BinaryDistance.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/BinaryDistance.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/BinaryFlatIndex.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/BinaryFlatIndex.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/BroadcastSum.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/BroadcastSum.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/Distance.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/Distance.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/DistanceUtils.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/FlatIndex.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/FlatIndex.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/GeneralDistance.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/GpuScalarQuantizer.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/IVFAppend.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/IVFAppend.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/IVFBase.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/IVFBase.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/IVFFlat.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/IVFFlat.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/IVFFlatScan.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/IVFFlatScan.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/IVFPQ.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/IVFPQ.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/IVFUtils.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/IVFUtils.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/IVFUtilsSelect1.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/IVFUtilsSelect2.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/L2Norm.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/L2Norm.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/L2Select.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/L2Select.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/Metrics.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/PQCodeDistances-inl.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/PQCodeDistances.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/PQCodeDistances.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/PQCodeLoad.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassNoPrecomputed-inl.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassNoPrecomputed.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassNoPrecomputed.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassPrecomputed.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassPrecomputed.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/RemapIndices.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/RemapIndices.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/VectorResidual.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/impl/VectorResidual.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/perf/IndexWrapper-inl.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/perf/IndexWrapper.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/perf/PerfBinaryFlat.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/perf/PerfClustering.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/perf/PerfFlat.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/perf/PerfIVFFlat.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/perf/PerfIVFPQ.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/perf/PerfIVFPQAdd.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/perf/PerfSelect.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/perf/WriteIndex.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/perf/slow.py delete mode 100644 core/src/index/thirdparty/faiss/gpu/test/Makefile delete mode 100644 core/src/index/thirdparty/faiss/gpu/test/TestGpuDistance.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/test/TestGpuIndexBinaryFlat.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/test/TestGpuIndexFlat.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/test/TestGpuIndexIVFFlat.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/test/TestGpuIndexIVFPQ.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/test/TestGpuMemoryException.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/test/TestGpuSelect.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/test/TestUtils.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/test/TestUtils.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/test/demo_ivfpq_indexing_gpu.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/test/test_gpu_index.py delete mode 100644 core/src/index/thirdparty/faiss/gpu/test/test_gpu_index_ivfsq.py delete mode 100644 core/src/index/thirdparty/faiss/gpu/test/test_pytorch_faiss.py delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/BlockSelectFloat.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/BlockSelectHalf.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/BlockSelectImpl.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/BlockSelectKernel.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/Comparators.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/ConversionOperators.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/CopyUtils.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/DeviceDefs.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/DeviceMemory.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/DeviceMemory.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/DeviceTensor-inl.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/DeviceTensor.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/DeviceUtils.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/DeviceUtils.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/DeviceVector.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/Float16.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/Float16.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/HostTensor-inl.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/HostTensor.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/Limits.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/LoadStoreOperators.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/MathOperators.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/MatrixMult-inl.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/MatrixMult.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/MatrixMult.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/MemorySpace.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/MemorySpace.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/MergeNetworkBlock.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/MergeNetworkUtils.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/MergeNetworkWarp.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/NoTypeTensor.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/Pair.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/PtxUtils.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/ReductionOperators.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/Reductions.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/Select.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/StackDeviceMemory.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/StackDeviceMemory.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/StaticUtils.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/Tensor-inl.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/Tensor.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/ThrustAllocator.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/Timer.cpp delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/Timer.h delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/Transpose.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/WarpSelectFloat.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/WarpSelectHalf.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/WarpSelectKernel.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/WarpShuffles.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat1.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat128.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat256.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat32.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat64.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatF1024.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatF2048.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatF512.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatT1024.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatT2048.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatT512.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf1.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf128.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf256.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf32.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf64.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfF1024.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfF2048.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfF512.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfT1024.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfT2048.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfT512.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectImpl.cuh delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat1.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat128.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat256.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat32.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat64.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatF1024.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatF2048.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatF512.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatT1024.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatT2048.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatT512.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf1.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf128.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf256.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf32.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf64.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfF1024.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfF2048.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfF512.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfT1024.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfT2048.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfT512.cu delete mode 100644 core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectImpl.cuh delete mode 100644 core/src/index/thirdparty/faiss/impl/AuxIndexStructures.cpp delete mode 100644 core/src/index/thirdparty/faiss/impl/AuxIndexStructures.h delete mode 100644 core/src/index/thirdparty/faiss/impl/FaissAssert.h delete mode 100644 core/src/index/thirdparty/faiss/impl/FaissException.cpp delete mode 100644 core/src/index/thirdparty/faiss/impl/FaissException.h delete mode 100644 core/src/index/thirdparty/faiss/impl/HNSW.cpp delete mode 100644 core/src/index/thirdparty/faiss/impl/HNSW.h delete mode 100644 core/src/index/thirdparty/faiss/impl/PolysemousTraining.cpp delete mode 100644 core/src/index/thirdparty/faiss/impl/PolysemousTraining.h delete mode 100644 core/src/index/thirdparty/faiss/impl/ProductQuantizer-inl.h delete mode 100644 core/src/index/thirdparty/faiss/impl/ProductQuantizer.cpp delete mode 100644 core/src/index/thirdparty/faiss/impl/ProductQuantizer.h delete mode 100644 core/src/index/thirdparty/faiss/impl/ScalarQuantizer.cpp delete mode 100644 core/src/index/thirdparty/faiss/impl/ScalarQuantizer.h delete mode 100644 core/src/index/thirdparty/faiss/impl/ScalarQuantizerCodec.h delete mode 100644 core/src/index/thirdparty/faiss/impl/ScalarQuantizerCodec_avx.h delete mode 100644 core/src/index/thirdparty/faiss/impl/ScalarQuantizerCodec_avx512.h delete mode 100644 core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC.cpp delete mode 100644 core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC.h delete mode 100644 core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC_avx.cpp delete mode 100644 core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC_avx.h delete mode 100644 core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC_avx512.cpp delete mode 100644 core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC_avx512.h delete mode 100644 core/src/index/thirdparty/faiss/impl/ScalarQuantizerOp.cpp delete mode 100644 core/src/index/thirdparty/faiss/impl/ScalarQuantizerOp.h delete mode 100644 core/src/index/thirdparty/faiss/impl/ThreadedIndex-inl.h delete mode 100644 core/src/index/thirdparty/faiss/impl/ThreadedIndex.h delete mode 100644 core/src/index/thirdparty/faiss/impl/index_read.cpp delete mode 100644 core/src/index/thirdparty/faiss/impl/index_write.cpp delete mode 100644 core/src/index/thirdparty/faiss/impl/io.cpp delete mode 100644 core/src/index/thirdparty/faiss/impl/io.h delete mode 100644 core/src/index/thirdparty/faiss/impl/lattice_Zn.cpp delete mode 100644 core/src/index/thirdparty/faiss/impl/lattice_Zn.h delete mode 100644 core/src/index/thirdparty/faiss/index_factory.cpp delete mode 100644 core/src/index/thirdparty/faiss/index_factory.h delete mode 100644 core/src/index/thirdparty/faiss/index_io.h delete mode 100644 core/src/index/thirdparty/faiss/makefile.inc.in delete mode 100644 core/src/index/thirdparty/faiss/misc/test_blas.cpp delete mode 100644 core/src/index/thirdparty/faiss/python/Makefile delete mode 100644 core/src/index/thirdparty/faiss/python/faiss.py delete mode 100644 core/src/index/thirdparty/faiss/python/setup.py delete mode 100644 core/src/index/thirdparty/faiss/python/swigfaiss.swig delete mode 100644 core/src/index/thirdparty/faiss/tests/Makefile delete mode 100644 core/src/index/thirdparty/faiss/tests/common.py delete mode 100644 core/src/index/thirdparty/faiss/tests/test_binary_factory.py delete mode 100644 core/src/index/thirdparty/faiss/tests/test_binary_flat.cpp delete mode 100644 core/src/index/thirdparty/faiss/tests/test_binary_hashindex.py delete mode 100644 core/src/index/thirdparty/faiss/tests/test_binary_io.py delete mode 100644 core/src/index/thirdparty/faiss/tests/test_build_blocks.py delete mode 100644 core/src/index/thirdparty/faiss/tests/test_dealloc_invlists.cpp delete mode 100644 core/src/index/thirdparty/faiss/tests/test_extra_distances.py delete mode 100644 core/src/index/thirdparty/faiss/tests/test_factory.py delete mode 100644 core/src/index/thirdparty/faiss/tests/test_index.py delete mode 100644 core/src/index/thirdparty/faiss/tests/test_index_accuracy.py delete mode 100644 core/src/index/thirdparty/faiss/tests/test_index_binary.py delete mode 100644 core/src/index/thirdparty/faiss/tests/test_index_binary_from_float.py delete mode 100644 core/src/index/thirdparty/faiss/tests/test_index_composite.py delete mode 100644 core/src/index/thirdparty/faiss/tests/test_io.py delete mode 100644 core/src/index/thirdparty/faiss/tests/test_ivflib.py delete mode 100644 core/src/index/thirdparty/faiss/tests/test_ivfpq_codec.cpp delete mode 100644 core/src/index/thirdparty/faiss/tests/test_ivfpq_indexing.cpp delete mode 100644 core/src/index/thirdparty/faiss/tests/test_lowlevel_ivf.cpp delete mode 100644 core/src/index/thirdparty/faiss/tests/test_merge.cpp delete mode 100644 core/src/index/thirdparty/faiss/tests/test_meta_index.py delete mode 100644 core/src/index/thirdparty/faiss/tests/test_omp_threads.cpp delete mode 100644 core/src/index/thirdparty/faiss/tests/test_omp_threads_py.py delete mode 100644 core/src/index/thirdparty/faiss/tests/test_ondisk_ivf.cpp delete mode 100644 core/src/index/thirdparty/faiss/tests/test_oom_exception.py delete mode 100644 core/src/index/thirdparty/faiss/tests/test_pairs_decoding.cpp delete mode 100644 core/src/index/thirdparty/faiss/tests/test_params_override.cpp delete mode 100644 core/src/index/thirdparty/faiss/tests/test_pq_encoding.cpp delete mode 100644 core/src/index/thirdparty/faiss/tests/test_referenced_objects.py delete mode 100644 core/src/index/thirdparty/faiss/tests/test_sliding_ivf.cpp delete mode 100644 core/src/index/thirdparty/faiss/tests/test_standalone_codec.py delete mode 100644 core/src/index/thirdparty/faiss/tests/test_threaded_index.cpp delete mode 100644 core/src/index/thirdparty/faiss/tests/test_transfer_invlists.cpp delete mode 100644 core/src/index/thirdparty/faiss/tutorial/cpp/1-Flat.cpp delete mode 100644 core/src/index/thirdparty/faiss/tutorial/cpp/2-IVFFlat.cpp delete mode 100644 core/src/index/thirdparty/faiss/tutorial/cpp/3-IVFPQ.cpp delete mode 100644 core/src/index/thirdparty/faiss/tutorial/cpp/4-GPU.cpp delete mode 100644 core/src/index/thirdparty/faiss/tutorial/cpp/5-GPU.cpp delete mode 100644 core/src/index/thirdparty/faiss/tutorial/cpp/5-Multiple-GPUs.cpp delete mode 100644 core/src/index/thirdparty/faiss/tutorial/cpp/6-GPU.cpp delete mode 100644 core/src/index/thirdparty/faiss/tutorial/cpp/6-RUN.cpp delete mode 100644 core/src/index/thirdparty/faiss/tutorial/cpp/7-GPU.cpp delete mode 100644 core/src/index/thirdparty/faiss/tutorial/cpp/8-GPU.cpp delete mode 100644 core/src/index/thirdparty/faiss/tutorial/cpp/9-BinaryFlat.cpp delete mode 100644 core/src/index/thirdparty/faiss/tutorial/cpp/Makefile delete mode 100644 core/src/index/thirdparty/faiss/tutorial/cpp/faiss_test.cpp delete mode 100644 core/src/index/thirdparty/faiss/tutorial/python/1-Flat.py delete mode 100644 core/src/index/thirdparty/faiss/tutorial/python/2-IVFFlat.py delete mode 100644 core/src/index/thirdparty/faiss/tutorial/python/3-IVFPQ.py delete mode 100644 core/src/index/thirdparty/faiss/tutorial/python/4-GPU.py delete mode 100644 core/src/index/thirdparty/faiss/tutorial/python/5-Multiple-GPUs.py delete mode 100644 core/src/index/thirdparty/faiss/utils/BinaryDistance.cpp delete mode 100644 core/src/index/thirdparty/faiss/utils/BinaryDistance.h delete mode 100644 core/src/index/thirdparty/faiss/utils/ConcurrentBitset.cpp delete mode 100644 core/src/index/thirdparty/faiss/utils/ConcurrentBitset.h delete mode 100644 core/src/index/thirdparty/faiss/utils/Heap.cpp delete mode 100644 core/src/index/thirdparty/faiss/utils/Heap.h delete mode 100644 core/src/index/thirdparty/faiss/utils/WorkerThread.cpp delete mode 100644 core/src/index/thirdparty/faiss/utils/WorkerThread.h delete mode 100644 core/src/index/thirdparty/faiss/utils/distances.cpp delete mode 100644 core/src/index/thirdparty/faiss/utils/distances.h delete mode 100644 core/src/index/thirdparty/faiss/utils/distances_avx.h delete mode 100644 core/src/index/thirdparty/faiss/utils/distances_avx512.h delete mode 100644 core/src/index/thirdparty/faiss/utils/distances_simd.cpp delete mode 100644 core/src/index/thirdparty/faiss/utils/distances_simd_avx.cpp delete mode 100644 core/src/index/thirdparty/faiss/utils/distances_simd_avx512.cpp delete mode 100644 core/src/index/thirdparty/faiss/utils/extra_distances.cpp delete mode 100644 core/src/index/thirdparty/faiss/utils/extra_distances.h delete mode 100644 core/src/index/thirdparty/faiss/utils/hamming-inl.h delete mode 100644 core/src/index/thirdparty/faiss/utils/hamming.cpp delete mode 100644 core/src/index/thirdparty/faiss/utils/hamming.h delete mode 100644 core/src/index/thirdparty/faiss/utils/instruction_set.h delete mode 100644 core/src/index/thirdparty/faiss/utils/jaccard-inl.h delete mode 100644 core/src/index/thirdparty/faiss/utils/random.cpp delete mode 100644 core/src/index/thirdparty/faiss/utils/random.h delete mode 100644 core/src/index/thirdparty/faiss/utils/substructure-inl.h delete mode 100644 core/src/index/thirdparty/faiss/utils/superstructure-inl.h delete mode 100644 core/src/index/thirdparty/faiss/utils/utils.cpp delete mode 100644 core/src/index/thirdparty/faiss/utils/utils.h delete mode 100644 core/src/index/thirdparty/hnswlib/bruteforce.h delete mode 100644 core/src/index/thirdparty/hnswlib/hnswalg.h delete mode 100644 core/src/index/thirdparty/hnswlib/hnswlib.h delete mode 100644 core/src/index/thirdparty/hnswlib/space_ip.h delete mode 100644 core/src/index/thirdparty/hnswlib/space_l2.h delete mode 100644 core/src/index/thirdparty/hnswlib/visited_list_pool.h delete mode 100644 core/src/index/thirdparty/versions.txt delete mode 100644 core/src/index/unittest/CMakeLists.txt delete mode 100644 core/src/index/unittest/Helper.h delete mode 100644 core/src/index/unittest/SPTAG.cpp delete mode 100644 core/src/index/unittest/faiss_benchmark/CMakeLists.txt delete mode 100644 core/src/index/unittest/faiss_benchmark/README.md delete mode 100644 core/src/index/unittest/faiss_benchmark/faiss_benchmark_test.cpp delete mode 100644 core/src/index/unittest/faiss_benchmark/faiss_bitset_test.cpp delete mode 100644 core/src/index/unittest/faiss_ori/CMakeLists.txt delete mode 100644 core/src/index/unittest/faiss_ori/gpuresource_test.cpp delete mode 100644 core/src/index/unittest/kdtree.cpp delete mode 100644 core/src/index/unittest/metric_alg_benchmark/CMakeLists.txt delete mode 100644 core/src/index/unittest/metric_alg_benchmark/metric_benchmark_test.cpp delete mode 100644 core/src/index/unittest/sift.50NN.graph delete mode 100644 core/src/index/unittest/siftsmall_base.fvecs delete mode 100644 core/src/index/unittest/test_annoy.cpp delete mode 100644 core/src/index/unittest/test_binaryidmap.cpp delete mode 100644 core/src/index/unittest/test_binaryivf.cpp delete mode 100644 core/src/index/unittest/test_common.cpp delete mode 100644 core/src/index/unittest/test_customized_index.cpp delete mode 100644 core/src/index/unittest/test_gpuresource.cpp delete mode 100644 core/src/index/unittest/test_hnsw.cpp delete mode 100644 core/src/index/unittest/test_idmap.cpp delete mode 100644 core/src/index/unittest/test_instructionset.cpp delete mode 100644 core/src/index/unittest/test_ivf.cpp delete mode 100644 core/src/index/unittest/test_knowhere.cpp delete mode 100644 core/src/index/unittest/test_nsg.cpp delete mode 100644 core/src/index/unittest/test_sptag.cpp delete mode 100644 core/src/index/unittest/test_vecindex.cpp delete mode 100644 core/src/index/unittest/test_wrapper.cpp delete mode 100644 core/src/index/unittest/utils.cpp delete mode 100644 core/src/index/unittest/utils.h delete mode 100644 core/src/main.cpp delete mode 100644 core/src/metrics/MetricBase.h delete mode 100644 core/src/metrics/Metrics.cpp delete mode 100644 core/src/metrics/Metrics.h delete mode 100644 core/src/metrics/SystemInfo.cpp delete mode 100644 core/src/metrics/SystemInfo.h delete mode 100644 core/src/metrics/prometheus/PrometheusMetrics.cpp delete mode 100644 core/src/metrics/prometheus/PrometheusMetrics.h delete mode 100644 core/src/query/BinaryQuery.cpp delete mode 100644 core/src/query/BinaryQuery.h delete mode 100644 core/src/query/BooleanQuery.h delete mode 100644 core/src/query/GeneralQuery.h delete mode 100644 core/src/scheduler/Algorithm.cpp delete mode 100644 core/src/scheduler/Algorithm.h delete mode 100644 core/src/scheduler/BuildMgr.cpp delete mode 100644 core/src/scheduler/BuildMgr.h delete mode 100644 core/src/scheduler/CPUBuilder.cpp delete mode 100644 core/src/scheduler/CPUBuilder.h delete mode 100644 core/src/scheduler/CircleQueue.h delete mode 100644 core/src/scheduler/Definition.h delete mode 100644 core/src/scheduler/JobMgr.cpp delete mode 100644 core/src/scheduler/JobMgr.h delete mode 100644 core/src/scheduler/ResourceFactory.cpp delete mode 100644 core/src/scheduler/ResourceFactory.h delete mode 100644 core/src/scheduler/ResourceMgr.cpp delete mode 100644 core/src/scheduler/ResourceMgr.h delete mode 100644 core/src/scheduler/SchedInst.cpp delete mode 100644 core/src/scheduler/SchedInst.h delete mode 100644 core/src/scheduler/Scheduler.cpp delete mode 100644 core/src/scheduler/Scheduler.h delete mode 100644 core/src/scheduler/TaskCreator.cpp delete mode 100644 core/src/scheduler/TaskCreator.h delete mode 100644 core/src/scheduler/TaskTable.cpp delete mode 100644 core/src/scheduler/TaskTable.h delete mode 100644 core/src/scheduler/Utils.cpp delete mode 100644 core/src/scheduler/Utils.h delete mode 100644 core/src/scheduler/action/Action.h delete mode 100644 core/src/scheduler/action/PushTaskToNeighbour.cpp delete mode 100644 core/src/scheduler/event/Event.h delete mode 100644 core/src/scheduler/event/EventDump.cpp delete mode 100644 core/src/scheduler/event/FinishTaskEvent.h delete mode 100644 core/src/scheduler/event/LoadCompletedEvent.h delete mode 100644 core/src/scheduler/event/StartUpEvent.h delete mode 100644 core/src/scheduler/event/TaskTableUpdatedEvent.h delete mode 100644 core/src/scheduler/interface/interfaces.h delete mode 100644 core/src/scheduler/job/BuildIndexJob.cpp delete mode 100644 core/src/scheduler/job/BuildIndexJob.h delete mode 100644 core/src/scheduler/job/DeleteJob.cpp delete mode 100644 core/src/scheduler/job/DeleteJob.h delete mode 100644 core/src/scheduler/job/Job.cpp delete mode 100644 core/src/scheduler/job/Job.h delete mode 100644 core/src/scheduler/job/SearchJob.cpp delete mode 100644 core/src/scheduler/job/SearchJob.h delete mode 100644 core/src/scheduler/resource/Connection.h delete mode 100644 core/src/scheduler/resource/CpuResource.cpp delete mode 100644 core/src/scheduler/resource/CpuResource.h delete mode 100644 core/src/scheduler/resource/DiskResource.cpp delete mode 100644 core/src/scheduler/resource/DiskResource.h delete mode 100644 core/src/scheduler/resource/GpuResource.cpp delete mode 100644 core/src/scheduler/resource/GpuResource.h delete mode 100644 core/src/scheduler/resource/Node.cpp delete mode 100644 core/src/scheduler/resource/Node.h delete mode 100644 core/src/scheduler/resource/Resource.cpp delete mode 100644 core/src/scheduler/resource/Resource.h delete mode 100644 core/src/scheduler/resource/TestResource.cpp delete mode 100644 core/src/scheduler/resource/TestResource.h delete mode 100644 core/src/scheduler/selector/BuildIndexPass.cpp delete mode 100644 core/src/scheduler/selector/BuildIndexPass.h delete mode 100644 core/src/scheduler/selector/FaissFlatPass.cpp delete mode 100644 core/src/scheduler/selector/FaissFlatPass.h delete mode 100644 core/src/scheduler/selector/FaissIVFFlatPass.cpp delete mode 100644 core/src/scheduler/selector/FaissIVFFlatPass.h delete mode 100644 core/src/scheduler/selector/FaissIVFPQPass.cpp delete mode 100644 core/src/scheduler/selector/FaissIVFPQPass.h delete mode 100644 core/src/scheduler/selector/FaissIVFSQ8HPass.cpp delete mode 100644 core/src/scheduler/selector/FaissIVFSQ8HPass.h delete mode 100644 core/src/scheduler/selector/FaissIVFSQ8Pass.cpp delete mode 100644 core/src/scheduler/selector/FaissIVFSQ8Pass.h delete mode 100644 core/src/scheduler/selector/FallbackPass.cpp delete mode 100644 core/src/scheduler/selector/FallbackPass.h delete mode 100644 core/src/scheduler/selector/Optimizer.cpp delete mode 100644 core/src/scheduler/selector/Optimizer.h delete mode 100644 core/src/scheduler/selector/Pass.h delete mode 100644 core/src/scheduler/task/BuildIndexTask.cpp delete mode 100644 core/src/scheduler/task/BuildIndexTask.h delete mode 100644 core/src/scheduler/task/DeleteTask.cpp delete mode 100644 core/src/scheduler/task/DeleteTask.h delete mode 100644 core/src/scheduler/task/Path.h delete mode 100644 core/src/scheduler/task/SearchTask.cpp delete mode 100644 core/src/scheduler/task/SearchTask.h delete mode 100644 core/src/scheduler/task/Task.h delete mode 100644 core/src/scheduler/task/TestTask.cpp delete mode 100644 core/src/scheduler/task/TestTask.h delete mode 100644 core/src/scheduler/tasklabel/BroadcastLabel.h delete mode 100644 core/src/scheduler/tasklabel/SpecResLabel.h delete mode 100644 core/src/scheduler/tasklabel/TaskLabel.h delete mode 100644 core/src/search/Task.cpp delete mode 100644 core/src/search/Task.h delete mode 100644 core/src/search/TaskInst.cpp delete mode 100644 core/src/search/TaskInst.h delete mode 100644 core/src/segment/Attr.cpp delete mode 100644 core/src/segment/Attr.h delete mode 100644 core/src/segment/AttrIndex.h delete mode 100644 core/src/segment/Attrs.h delete mode 100644 core/src/segment/AttrsIndex.h delete mode 100644 core/src/segment/DeletedDocs.cpp delete mode 100644 core/src/segment/DeletedDocs.h delete mode 100644 core/src/segment/IdBloomFilter.cpp delete mode 100644 core/src/segment/IdBloomFilter.h delete mode 100644 core/src/segment/IdIndex.h delete mode 100644 core/src/segment/SegmentReader.cpp delete mode 100644 core/src/segment/SegmentReader.h delete mode 100644 core/src/segment/SegmentWriter.cpp delete mode 100644 core/src/segment/SegmentWriter.h delete mode 100644 core/src/segment/Types.h delete mode 100644 core/src/segment/VectorIndex.h delete mode 100644 core/src/segment/Vectors.cpp delete mode 100644 core/src/segment/Vectors.h delete mode 100644 core/src/segment/VectorsIndex.h delete mode 100644 core/src/server/DBWrapper.cpp delete mode 100644 core/src/server/DBWrapper.h delete mode 100644 core/src/server/Server.cpp delete mode 100644 core/src/server/Server.h delete mode 100644 core/src/server/context/ConnectionContext.h delete mode 100644 core/src/server/context/Context.cpp delete mode 100644 core/src/server/context/Context.h delete mode 100644 core/src/server/delivery/RequestHandler.cpp delete mode 100644 core/src/server/delivery/RequestHandler.h delete mode 100644 core/src/server/delivery/RequestQueue.cpp delete mode 100644 core/src/server/delivery/RequestQueue.h delete mode 100644 core/src/server/delivery/RequestScheduler.cpp delete mode 100644 core/src/server/delivery/RequestScheduler.h delete mode 100644 core/src/server/delivery/hybrid_request/CreateHybridCollectionRequest.cpp delete mode 100644 core/src/server/delivery/hybrid_request/CreateHybridCollectionRequest.h delete mode 100644 core/src/server/delivery/hybrid_request/DescribeHybridCollectionRequest.cpp delete mode 100644 core/src/server/delivery/hybrid_request/DescribeHybridCollectionRequest.h delete mode 100644 core/src/server/delivery/hybrid_request/HybridSearchRequest.cpp delete mode 100644 core/src/server/delivery/hybrid_request/HybridSearchRequest.h delete mode 100644 core/src/server/delivery/hybrid_request/InsertEntityRequest.cpp delete mode 100644 core/src/server/delivery/hybrid_request/InsertEntityRequest.h delete mode 100644 core/src/server/delivery/request/BaseRequest.cpp delete mode 100644 core/src/server/delivery/request/BaseRequest.h delete mode 100644 core/src/server/delivery/request/CmdRequest.cpp delete mode 100644 core/src/server/delivery/request/CmdRequest.h delete mode 100644 core/src/server/delivery/request/CompactRequest.cpp delete mode 100644 core/src/server/delivery/request/CompactRequest.h delete mode 100644 core/src/server/delivery/request/CountCollectionRequest.cpp delete mode 100644 core/src/server/delivery/request/CountCollectionRequest.h delete mode 100644 core/src/server/delivery/request/CreateCollectionRequest.cpp delete mode 100644 core/src/server/delivery/request/CreateCollectionRequest.h delete mode 100644 core/src/server/delivery/request/CreateIndexRequest.cpp delete mode 100644 core/src/server/delivery/request/CreateIndexRequest.h delete mode 100644 core/src/server/delivery/request/CreatePartitionRequest.cpp delete mode 100644 core/src/server/delivery/request/CreatePartitionRequest.h delete mode 100644 core/src/server/delivery/request/DeleteByIDRequest.cpp delete mode 100644 core/src/server/delivery/request/DeleteByIDRequest.h delete mode 100644 core/src/server/delivery/request/DescribeCollectionRequest.cpp delete mode 100644 core/src/server/delivery/request/DescribeCollectionRequest.h delete mode 100644 core/src/server/delivery/request/DescribeIndexRequest.cpp delete mode 100644 core/src/server/delivery/request/DescribeIndexRequest.h delete mode 100644 core/src/server/delivery/request/DropCollectionRequest.cpp delete mode 100644 core/src/server/delivery/request/DropCollectionRequest.h delete mode 100644 core/src/server/delivery/request/DropIndexRequest.cpp delete mode 100644 core/src/server/delivery/request/DropIndexRequest.h delete mode 100644 core/src/server/delivery/request/DropPartitionRequest.cpp delete mode 100644 core/src/server/delivery/request/DropPartitionRequest.h delete mode 100644 core/src/server/delivery/request/FlushRequest.cpp delete mode 100644 core/src/server/delivery/request/FlushRequest.h delete mode 100644 core/src/server/delivery/request/GetVectorIDsRequest.cpp delete mode 100644 core/src/server/delivery/request/GetVectorIDsRequest.h delete mode 100644 core/src/server/delivery/request/GetVectorsByIDRequest.cpp delete mode 100644 core/src/server/delivery/request/GetVectorsByIDRequest.h delete mode 100644 core/src/server/delivery/request/HasCollectionRequest.cpp delete mode 100644 core/src/server/delivery/request/HasCollectionRequest.h delete mode 100644 core/src/server/delivery/request/HasPartitionRequest.cpp delete mode 100644 core/src/server/delivery/request/HasPartitionRequest.h delete mode 100644 core/src/server/delivery/request/InsertRequest.cpp delete mode 100644 core/src/server/delivery/request/InsertRequest.h delete mode 100644 core/src/server/delivery/request/PreloadCollectionRequest.cpp delete mode 100644 core/src/server/delivery/request/PreloadCollectionRequest.h delete mode 100644 core/src/server/delivery/request/ReLoadSegmentsRequest.cpp delete mode 100644 core/src/server/delivery/request/ReLoadSegmentsRequest.h delete mode 100644 core/src/server/delivery/request/SearchByIDRequest.cpp delete mode 100644 core/src/server/delivery/request/SearchByIDRequest.h delete mode 100644 core/src/server/delivery/request/SearchCombineRequest.cpp delete mode 100644 core/src/server/delivery/request/SearchCombineRequest.h delete mode 100644 core/src/server/delivery/request/SearchRequest.cpp delete mode 100644 core/src/server/delivery/request/SearchRequest.h delete mode 100644 core/src/server/delivery/request/ShowCollectionInfoRequest.cpp delete mode 100644 core/src/server/delivery/request/ShowCollectionInfoRequest.h delete mode 100644 core/src/server/delivery/request/ShowCollectionsRequest.cpp delete mode 100644 core/src/server/delivery/request/ShowCollectionsRequest.h delete mode 100644 core/src/server/delivery/request/ShowPartitionsRequest.cpp delete mode 100644 core/src/server/delivery/request/ShowPartitionsRequest.h delete mode 100644 core/src/server/delivery/strategy/RequestStrategy.h delete mode 100644 core/src/server/delivery/strategy/SearchReqStrategy.cpp delete mode 100644 core/src/server/delivery/strategy/SearchReqStrategy.h delete mode 100644 core/src/server/grpc_impl/GrpcRequestHandler.cpp delete mode 100644 core/src/server/grpc_impl/GrpcRequestHandler.h delete mode 100644 core/src/server/grpc_impl/GrpcServer.cpp delete mode 100644 core/src/server/grpc_impl/GrpcServer.h delete mode 100644 core/src/server/grpc_impl/interceptor/GrpcInterceptorHookHandler.cpp delete mode 100644 core/src/server/grpc_impl/interceptor/GrpcInterceptorHookHandler.h delete mode 100644 core/src/server/grpc_impl/interceptor/SpanInterceptor.cpp delete mode 100644 core/src/server/grpc_impl/interceptor/SpanInterceptor.h delete mode 100644 core/src/server/init/CpuChecker.cpp delete mode 100644 core/src/server/init/CpuChecker.h delete mode 100644 core/src/server/init/GpuChecker.cpp delete mode 100644 core/src/server/init/GpuChecker.h delete mode 100644 core/src/server/init/InstanceLockCheck.cpp delete mode 100644 core/src/server/init/InstanceLockCheck.h delete mode 100644 core/src/server/init/StorageChecker.cpp delete mode 100644 core/src/server/init/StorageChecker.h delete mode 100644 core/src/server/web_impl/Constants.cpp delete mode 100644 core/src/server/web_impl/Constants.h delete mode 100644 core/src/server/web_impl/README.md delete mode 100644 core/src/server/web_impl/Types.h delete mode 100644 core/src/server/web_impl/WebServer.cpp delete mode 100644 core/src/server/web_impl/WebServer.h delete mode 100644 core/src/server/web_impl/component/AppComponent.hpp delete mode 100644 core/src/server/web_impl/controller/WebController.hpp delete mode 100644 core/src/server/web_impl/dto/CollectionDto.hpp delete mode 100644 core/src/server/web_impl/dto/ConfigDto.hpp delete mode 100644 core/src/server/web_impl/dto/DevicesDto.hpp delete mode 100644 core/src/server/web_impl/dto/Dto.h delete mode 100644 core/src/server/web_impl/dto/IndexDto.hpp delete mode 100644 core/src/server/web_impl/dto/PartitionDto.hpp delete mode 100644 core/src/server/web_impl/dto/StatusDto.hpp delete mode 100644 core/src/server/web_impl/dto/VectorDto.hpp delete mode 100644 core/src/server/web_impl/handler/WebRequestHandler.cpp delete mode 100644 core/src/server/web_impl/handler/WebRequestHandler.h delete mode 100644 core/src/server/web_impl/utils/Util.cpp delete mode 100644 core/src/server/web_impl/utils/Util.h delete mode 100644 core/src/storage/FSHandler.h delete mode 100644 core/src/storage/IOReader.h delete mode 100644 core/src/storage/IOWriter.h delete mode 100644 core/src/storage/Operation.h delete mode 100644 core/src/storage/disk/DiskIOReader.cpp delete mode 100644 core/src/storage/disk/DiskIOReader.h delete mode 100644 core/src/storage/disk/DiskIOWriter.cpp delete mode 100644 core/src/storage/disk/DiskIOWriter.h delete mode 100644 core/src/storage/disk/DiskOperation.cpp delete mode 100644 core/src/storage/disk/DiskOperation.h delete mode 100644 core/src/storage/s3/S3ClientMock.h delete mode 100644 core/src/storage/s3/S3ClientWrapper.cpp delete mode 100644 core/src/storage/s3/S3ClientWrapper.h delete mode 100644 core/src/storage/s3/S3IOReader.cpp delete mode 100644 core/src/storage/s3/S3IOReader.h delete mode 100644 core/src/storage/s3/S3IOWriter.cpp delete mode 100644 core/src/storage/s3/S3IOWriter.h delete mode 100644 core/src/tracing/TextMapCarrier.cpp delete mode 100644 core/src/tracing/TextMapCarrier.h delete mode 100644 core/src/tracing/TraceContext.cpp delete mode 100644 core/src/tracing/TraceContext.h delete mode 100644 core/src/tracing/TracerUtil.cpp delete mode 100644 core/src/tracing/TracerUtil.h delete mode 100644 core/src/utils/BlockingQueue.h delete mode 100644 core/src/utils/BlockingQueue.inl delete mode 100644 core/src/utils/CommonUtil.cpp delete mode 100644 core/src/utils/CommonUtil.h delete mode 100644 core/src/utils/Error.h delete mode 100644 core/src/utils/Exception.h delete mode 100644 core/src/utils/Json.h delete mode 100644 core/src/utils/Log.cpp delete mode 100644 core/src/utils/Log.h delete mode 100644 core/src/utils/LogUtil.cpp delete mode 100644 core/src/utils/LogUtil.h delete mode 100644 core/src/utils/SignalUtil.cpp delete mode 100644 core/src/utils/SignalUtil.h delete mode 100644 core/src/utils/Status.cpp delete mode 100644 core/src/utils/Status.h delete mode 100644 core/src/utils/StringHelpFunctions.cpp delete mode 100644 core/src/utils/StringHelpFunctions.h delete mode 100644 core/src/utils/ThreadPool.h delete mode 100644 core/src/utils/TimeRecorder.cpp delete mode 100644 core/src/utils/TimeRecorder.h delete mode 100644 core/src/utils/ValidationUtil.cpp delete mode 100644 core/src/utils/ValidationUtil.h delete mode 100644 core/src/version.h.in delete mode 100755 core/start_server.sh delete mode 100755 core/stop_server.sh delete mode 100644 core/thirdparty/dablooms/dablooms.cpp delete mode 100644 core/thirdparty/dablooms/dablooms.h delete mode 100644 core/thirdparty/dablooms/murmur.cpp delete mode 100644 core/thirdparty/dablooms/murmur.h delete mode 100644 core/thirdparty/easyloggingpp/easylogging++.cc delete mode 100644 core/thirdparty/easyloggingpp/easylogging++.h delete mode 100644 core/thirdparty/nlohmann/json.hpp delete mode 100644 core/thirdparty/versions.txt delete mode 100755 core/ubuntu_build_deps.sh delete mode 100644 core/unittest/CMakeLists.txt delete mode 100644 core/unittest/db/CMakeLists.txt delete mode 100644 core/unittest/db/test_db.cpp delete mode 100644 core/unittest/db/test_db_mysql.cpp delete mode 100644 core/unittest/db/test_delete.cpp delete mode 100644 core/unittest/db/test_engine.cpp delete mode 100644 core/unittest/db/test_hybrid_db.cpp delete mode 100644 core/unittest/db/test_mem.cpp delete mode 100644 core/unittest/db/test_meta.cpp delete mode 100644 core/unittest/db/test_meta_mysql.cpp delete mode 100644 core/unittest/db/test_misc.cpp delete mode 100644 core/unittest/db/test_search.cpp delete mode 100644 core/unittest/db/test_search_by_id.cpp delete mode 100644 core/unittest/db/test_wal.cpp delete mode 100644 core/unittest/db/utils.cpp delete mode 100644 core/unittest/db/utils.h delete mode 100644 core/unittest/main.cpp delete mode 100644 core/unittest/metrics/CMakeLists.txt delete mode 100644 core/unittest/metrics/test_metricbase.cpp delete mode 100644 core/unittest/metrics/test_metrics.cpp delete mode 100644 core/unittest/metrics/test_prometheus.cpp delete mode 100644 core/unittest/metrics/utils.cpp delete mode 100644 core/unittest/metrics/utils.h delete mode 100644 core/unittest/scheduler/CMakeLists.txt delete mode 100644 core/unittest/scheduler/test_action.cpp delete mode 100644 core/unittest/scheduler/test_algorithm.cpp delete mode 100644 core/unittest/scheduler/test_event.cpp delete mode 100644 core/unittest/scheduler/test_job.cpp delete mode 100644 core/unittest/scheduler/test_node.cpp delete mode 100644 core/unittest/scheduler/test_resource.cpp delete mode 100644 core/unittest/scheduler/test_resource_factory.cpp delete mode 100644 core/unittest/scheduler/test_resource_mgr.cpp delete mode 100644 core/unittest/scheduler/test_scheduler.cpp delete mode 100644 core/unittest/scheduler/test_selector.cpp delete mode 100644 core/unittest/scheduler/test_task.cpp delete mode 100644 core/unittest/scheduler/test_tasktable.cpp delete mode 100644 core/unittest/server/CMakeLists.txt delete mode 100644 core/unittest/server/test_cache.cpp delete mode 100644 core/unittest/server/test_check.cpp delete mode 100644 core/unittest/server/test_config.cpp delete mode 100644 core/unittest/server/test_rpc.cpp delete mode 100644 core/unittest/server/test_util.cpp delete mode 100644 core/unittest/server/test_web.cpp delete mode 100644 core/unittest/server/utils.cpp delete mode 100644 core/unittest/server/utils.h delete mode 100644 core/unittest/storage/CMakeLists.txt delete mode 100644 core/unittest/storage/test_disk.cpp delete mode 100644 core/unittest/storage/test_s3_client.cpp delete mode 100644 core/unittest/storage/utils.cpp delete mode 100644 core/unittest/storage/utils.h delete mode 100644 core/unittest/thirdparty/CMakeLists.txt delete mode 100644 core/unittest/thirdparty/test_dablooms.cpp delete mode 100644 core/unittest/thirdparty/utils.cpp delete mode 100644 core/unittest/thirdparty/utils.h delete mode 100644 docker-compose.yml delete mode 100644 docker/alertmanager.yml delete mode 100644 docker/build_env/cpu/centos7/Dockerfile delete mode 100755 docker/build_env/cpu/centos7/docker-entrypoint.sh delete mode 100644 docker/build_env/cpu/ubuntu16.04/Dockerfile delete mode 100755 docker/build_env/cpu/ubuntu16.04/docker-entrypoint.sh delete mode 100644 docker/build_env/cpu/ubuntu18.04/Dockerfile delete mode 100755 docker/build_env/cpu/ubuntu18.04/docker-entrypoint.sh delete mode 100644 docker/build_env/gpu/centos7/Dockerfile delete mode 100755 docker/build_env/gpu/centos7/docker-entrypoint.sh delete mode 100644 docker/build_env/gpu/ubuntu16.04/Dockerfile delete mode 100755 docker/build_env/gpu/ubuntu16.04/docker-entrypoint.sh delete mode 100644 docker/build_env/gpu/ubuntu18.04/Dockerfile delete mode 100755 docker/build_env/gpu/ubuntu18.04/docker-entrypoint.sh delete mode 100644 docker/deploy/cpu/centos7/Dockerfile delete mode 100644 docker/deploy/cpu/ubuntu16.04/Dockerfile delete mode 100644 docker/deploy/cpu/ubuntu18.04/Dockerfile delete mode 100644 docker/deploy/gpu/centos7/Dockerfile delete mode 100644 docker/deploy/gpu/ubuntu16.04/Dockerfile delete mode 100644 docker/deploy/gpu/ubuntu18.04/Dockerfile delete mode 100644 docker/docker-compose-monitor.yml delete mode 100644 docker/prometheus.yml delete mode 100644 docker/server_down.yml delete mode 100644 docker/test_env/Dockerfile delete mode 100755 docker/test_env/docker-entrypoint.sh delete mode 100644 docs/README.md delete mode 100644 docs/test_report/ivfflat_test_report_cn.md delete mode 100644 docs/test_report/ivfflat_test_report_en.md delete mode 100644 docs/test_report/milvus_ivfsq8_test_report_detailed_version.md delete mode 100644 docs/test_report/milvus_ivfsq8_test_report_detailed_version_cn.md delete mode 100755 docs/test_report/milvus_ivfsq8h_test_report_detailed_version.md delete mode 100644 docs/test_report/milvus_ivfsq8h_test_report_detailed_version_cn.md delete mode 100644 sdk/CMakeLists.txt delete mode 100644 sdk/README.md delete mode 100644 sdk/build-support/code_style_clion.xml delete mode 100755 sdk/build-support/cpplint.py delete mode 100644 sdk/build-support/lint_exclusions.txt delete mode 100755 sdk/build-support/lintutils.py delete mode 100755 sdk/build-support/run_clang_format.py delete mode 100755 sdk/build-support/run_clang_tidy.py delete mode 100755 sdk/build-support/run_cpplint.py delete mode 100755 sdk/build.sh delete mode 100644 sdk/cmake/BuildUtils.cmake delete mode 100644 sdk/cmake/DefineOptions.cmake delete mode 100644 sdk/cmake/FindClangTools.cmake delete mode 100644 sdk/cmake/ThirdPartyPackages.cmake delete mode 100644 sdk/examples/CMakeLists.txt delete mode 100644 sdk/examples/binary_vector/CMakeLists.txt delete mode 100644 sdk/examples/binary_vector/main.cpp delete mode 100644 sdk/examples/binary_vector/src/ClientTest.cpp delete mode 100644 sdk/examples/binary_vector/src/ClientTest.h delete mode 100644 sdk/examples/hybrid/CMakeLists.txt delete mode 100644 sdk/examples/hybrid/main.cpp delete mode 100644 sdk/examples/hybrid/src/ClientTest.cpp delete mode 100644 sdk/examples/hybrid/src/ClientTest.h delete mode 100644 sdk/examples/partition/CMakeLists.txt delete mode 100644 sdk/examples/partition/main.cpp delete mode 100644 sdk/examples/partition/src/ClientTest.cpp delete mode 100644 sdk/examples/partition/src/ClientTest.h delete mode 100644 sdk/examples/qps/CMakeLists.txt delete mode 100644 sdk/examples/qps/main.cpp delete mode 100644 sdk/examples/qps/src/ClientTest.cpp delete mode 100644 sdk/examples/qps/src/ClientTest.h delete mode 100644 sdk/examples/simple/CMakeLists.txt delete mode 100644 sdk/examples/simple/main.cpp delete mode 100644 sdk/examples/simple/src/ClientTest.cpp delete mode 100644 sdk/examples/simple/src/ClientTest.h delete mode 100644 sdk/examples/utils/ThreadPool.h delete mode 100644 sdk/examples/utils/TimeRecorder.cpp delete mode 100644 sdk/examples/utils/TimeRecorder.h delete mode 100644 sdk/examples/utils/Utils.cpp delete mode 100644 sdk/examples/utils/Utils.h delete mode 100644 sdk/grpc-gen/gen-milvus/milvus.grpc.pb.cc delete mode 100644 sdk/grpc-gen/gen-milvus/milvus.grpc.pb.h delete mode 100644 sdk/grpc-gen/gen-milvus/milvus.pb.cc delete mode 100644 sdk/grpc-gen/gen-milvus/milvus.pb.h delete mode 100644 sdk/grpc-gen/gen-status/status.grpc.pb.cc delete mode 100644 sdk/grpc-gen/gen-status/status.grpc.pb.h delete mode 100644 sdk/grpc-gen/gen-status/status.pb.cc delete mode 100644 sdk/grpc-gen/gen-status/status.pb.h delete mode 100644 sdk/grpc/ClientProxy.cpp delete mode 100644 sdk/grpc/ClientProxy.h delete mode 100644 sdk/grpc/GrpcClient.cpp delete mode 100644 sdk/grpc/GrpcClient.h delete mode 100644 sdk/include/BooleanQuery.h delete mode 100644 sdk/include/Field.h delete mode 100644 sdk/include/GeneralQuery.h delete mode 100644 sdk/include/MilvusApi.h delete mode 100644 sdk/include/Status.h delete mode 100644 sdk/interface/ConnectionImpl.cpp delete mode 100644 sdk/interface/ConnectionImpl.h delete mode 100644 sdk/interface/Status.cpp delete mode 100644 sdk/thirdparty/nlohmann/json.hpp delete mode 100644 shards/.dockerignore delete mode 100644 shards/Dockerfile delete mode 100644 shards/Makefile delete mode 100644 shards/README.md delete mode 100644 shards/README_CN.md delete mode 100644 shards/all_in_one/all_in_one.yml delete mode 100644 shards/all_in_one/probe_test.py delete mode 100644 shards/all_in_one/ro_server.yml delete mode 100644 shards/all_in_one/wr_server.yml delete mode 100644 shards/all_in_one_with_mysql/all_in_one.yml delete mode 100644 shards/all_in_one_with_mysql/mysqld.cnf delete mode 100644 shards/all_in_one_with_mysql/probe_test.py delete mode 100644 shards/all_in_one_with_mysql/ro_server.yml delete mode 100644 shards/all_in_one_with_mysql/wr_server.yml delete mode 100644 shards/conftest.py delete mode 100644 shards/discovery/__init__.py delete mode 100644 shards/discovery/factory.py delete mode 100644 shards/discovery/plugins/kubernetes_provider.py delete mode 100644 shards/discovery/plugins/static_provider.py delete mode 100644 shards/kubernetes_demo/mishards_auxiliary.yaml delete mode 100644 shards/kubernetes_demo/mishards_configmap.yaml delete mode 100644 shards/kubernetes_demo/mishards_data_pvc.yaml delete mode 100644 shards/kubernetes_demo/mishards_proxy.yaml delete mode 100644 shards/kubernetes_demo/mishards_rbac.yaml delete mode 100644 shards/kubernetes_demo/mishards_stateful_servers.yaml delete mode 100644 shards/kubernetes_demo/mishards_write_servers.yaml delete mode 100755 shards/kubernetes_demo/start.sh delete mode 100644 shards/manager.py delete mode 100644 shards/mishards/.env.example delete mode 100644 shards/mishards/__init__.py delete mode 100644 shards/mishards/connections.py delete mode 100644 shards/mishards/db_base.py delete mode 100644 shards/mishards/exception_codes.py delete mode 100644 shards/mishards/exception_handlers.py delete mode 100644 shards/mishards/exceptions.py delete mode 100644 shards/mishards/factories.py delete mode 100644 shards/mishards/grpc_utils/__init__.py delete mode 100644 shards/mishards/grpc_utils/grpc_args_parser.py delete mode 100644 shards/mishards/grpc_utils/grpc_args_wrapper.py delete mode 100644 shards/mishards/grpc_utils/test_grpc.py delete mode 100644 shards/mishards/hash_ring.py delete mode 100644 shards/mishards/main.py delete mode 100644 shards/mishards/models.py delete mode 100644 shards/mishards/router/__init__.py delete mode 100644 shards/mishards/router/factory.py delete mode 100644 shards/mishards/router/plugins/file_based_hash_ring_router.py delete mode 100644 shards/mishards/server.py delete mode 100644 shards/mishards/service_handler.py delete mode 100644 shards/mishards/settings.py delete mode 100644 shards/mishards/test_connections.py delete mode 100644 shards/mishards/test_models.py delete mode 100644 shards/mishards/test_server.py delete mode 100644 shards/mishards/topology.py delete mode 100644 shards/mishards/utilities.py delete mode 100644 shards/requirements.txt delete mode 100644 shards/setup.cfg delete mode 100644 shards/tracer/__init__.py delete mode 100644 shards/tracer/factory.py delete mode 100644 shards/tracer/plugins/jaeger_factory.py delete mode 100644 shards/utils/__init__.py delete mode 100644 shards/utils/colors.py delete mode 100644 shards/utils/logger_helper.py delete mode 100644 shards/utils/pluginextension.py delete mode 100644 shards/utils/plugins/__init__.py delete mode 100644 tests/milvus-java-test/.gitignore delete mode 100644 tests/milvus-java-test/MilvusSDkJavaTest.iml delete mode 100644 tests/milvus-java-test/README.md delete mode 100644 tests/milvus-java-test/bin/run.sh delete mode 100644 tests/milvus-java-test/ci/function/file_transfer.groovy delete mode 100644 tests/milvus-java-test/ci/jenkinsfile/cleanup.groovy delete mode 100644 tests/milvus-java-test/ci/jenkinsfile/deploy_server.groovy delete mode 100644 tests/milvus-java-test/ci/jenkinsfile/integration_test.groovy delete mode 100644 tests/milvus-java-test/ci/jenkinsfile/notify.groovy delete mode 100644 tests/milvus-java-test/ci/jenkinsfile/upload_unit_test_out.groovy delete mode 100644 tests/milvus-java-test/ci/main_jenkinsfile delete mode 100644 tests/milvus-java-test/ci/pod_containers/milvus-testframework.yaml delete mode 100644 tests/milvus-java-test/milvus-java-test.iml delete mode 100644 tests/milvus-java-test/pom.xml delete mode 100644 tests/milvus-java-test/src/main/java/com/MainClass.java delete mode 100644 tests/milvus-java-test/src/main/java/com/TestAddVectors.java delete mode 100644 tests/milvus-java-test/src/main/java/com/TestCollection.java delete mode 100644 tests/milvus-java-test/src/main/java/com/TestCollectionCount.java delete mode 100644 tests/milvus-java-test/src/main/java/com/TestCollectionInfo.java delete mode 100644 tests/milvus-java-test/src/main/java/com/TestCompact.java delete mode 100644 tests/milvus-java-test/src/main/java/com/TestConnect.java delete mode 100644 tests/milvus-java-test/src/main/java/com/TestDeleteVectors.java delete mode 100644 tests/milvus-java-test/src/main/java/com/TestFlush.java delete mode 100644 tests/milvus-java-test/src/main/java/com/TestGetVectorByID.java delete mode 100644 tests/milvus-java-test/src/main/java/com/TestIndex.java delete mode 100644 tests/milvus-java-test/src/main/java/com/TestMix.java delete mode 100644 tests/milvus-java-test/src/main/java/com/TestPS.java delete mode 100644 tests/milvus-java-test/src/main/java/com/TestPartition.java delete mode 100644 tests/milvus-java-test/src/main/java/com/TestPing.java delete mode 100644 tests/milvus-java-test/src/main/java/com/TestSearchByIds.java delete mode 100644 tests/milvus-java-test/src/main/java/com/TestSearchVectors.java delete mode 100644 tests/milvus-java-test/src/main/java/com/Utils.java delete mode 100644 tests/milvus-java-test/testng.xml delete mode 100644 tests/milvus_benchmark/.gitignore delete mode 100644 tests/milvus_benchmark/README.md delete mode 100644 tests/milvus_benchmark/__init__.py delete mode 100644 tests/milvus_benchmark/assets/Parameters.png delete mode 100644 tests/milvus_benchmark/assets/gpu_search_performance_random50m-yaml.png delete mode 100644 tests/milvus_benchmark/assets/milvus-nightly-performance-new-jenkins.png delete mode 100644 tests/milvus_benchmark/ci/function/file_transfer.groovy delete mode 100644 tests/milvus_benchmark/ci/jenkinsfile/cleanup.groovy delete mode 100644 tests/milvus_benchmark/ci/jenkinsfile/deploy_test.groovy delete mode 100644 tests/milvus_benchmark/ci/jenkinsfile/notify.groovy delete mode 100644 tests/milvus_benchmark/ci/jenkinsfile/publishDailyImages.groovy delete mode 100644 tests/milvus_benchmark/ci/main_jenkinsfile delete mode 100644 tests/milvus_benchmark/ci/pod_containers/milvus-testframework.yaml delete mode 100644 tests/milvus_benchmark/ci/publish_jenkinsfile delete mode 100755 tests/milvus_benchmark/ci/scripts/yaml_processor.py delete mode 100644 tests/milvus_benchmark/client.py delete mode 100644 tests/milvus_benchmark/conf/log_config.conf delete mode 100644 tests/milvus_benchmark/conf/server_config.yaml delete mode 100644 tests/milvus_benchmark/conf/server_config.yaml.cpu delete mode 100644 tests/milvus_benchmark/conf/server_config.yaml.multi delete mode 100644 tests/milvus_benchmark/conf/server_config.yaml.single delete mode 100644 tests/milvus_benchmark/docker_runner.py delete mode 100644 tests/milvus_benchmark/k8s_runner.py delete mode 100644 tests/milvus_benchmark/local_runner.py delete mode 100644 tests/milvus_benchmark/main.py delete mode 100644 tests/milvus_benchmark/operation.py delete mode 100644 tests/milvus_benchmark/parser.py delete mode 100644 tests/milvus_benchmark/report.py delete mode 100644 tests/milvus_benchmark/requirements.txt delete mode 100644 tests/milvus_benchmark/runner.py delete mode 100644 tests/milvus_benchmark/scheduler/070.json delete mode 100644 tests/milvus_benchmark/scheduler/070_cpu.json delete mode 100644 tests/milvus_benchmark/scheduler/070_data.json delete mode 100644 tests/milvus_benchmark/scheduler/070_data_eros.json delete mode 100644 tests/milvus_benchmark/scheduler/080_data.json delete mode 100644 tests/milvus_benchmark/scheduler/acc.json delete mode 100644 tests/milvus_benchmark/scheduler/ann_acc.json delete mode 100644 tests/milvus_benchmark/scheduler/apollo.json delete mode 100644 tests/milvus_benchmark/scheduler/athena.json delete mode 100644 tests/milvus_benchmark/scheduler/build.json delete mode 100644 tests/milvus_benchmark/scheduler/clean.json delete mode 100644 tests/milvus_benchmark/scheduler/crud_add.json delete mode 100644 tests/milvus_benchmark/scheduler/crud_build.json delete mode 100644 tests/milvus_benchmark/scheduler/crud_flush.json delete mode 100644 tests/milvus_benchmark/scheduler/crud_search.json delete mode 100644 tests/milvus_benchmark/scheduler/crud_stability.json delete mode 100644 tests/milvus_benchmark/scheduler/debug.json delete mode 100644 tests/milvus_benchmark/scheduler/default_config.json delete mode 100644 tests/milvus_benchmark/scheduler/eros.json delete mode 100644 tests/milvus_benchmark/scheduler/file_size.json delete mode 100644 tests/milvus_benchmark/scheduler/hnsw.json delete mode 100644 tests/milvus_benchmark/scheduler/insert.json delete mode 100644 tests/milvus_benchmark/scheduler/jaccard.json delete mode 100644 tests/milvus_benchmark/scheduler/poseidon.json delete mode 100644 tests/milvus_benchmark/scheduler/search.json delete mode 100644 tests/milvus_benchmark/scheduler/search_performance.json delete mode 100644 tests/milvus_benchmark/scheduler/sift1b.json delete mode 100644 tests/milvus_benchmark/scheduler/stability.json delete mode 100644 tests/milvus_benchmark/scripts/default_config.json delete mode 100644 tests/milvus_benchmark/scripts/scheduler.py delete mode 100644 tests/milvus_benchmark/suites/cpu_accuracy_ann.yaml delete mode 100644 tests/milvus_benchmark/suites/gpu_accuracy_ann.yaml delete mode 100644 tests/milvus_benchmark/utils.py delete mode 100644 tests/milvus_go_test/client_test.go delete mode 100644 tests/milvus_groundtruth/README.md delete mode 100644 tests/milvus_groundtruth/milvus_ground_truth.py delete mode 100644 tests/milvus_python_test/.dockerignore delete mode 100644 tests/milvus_python_test/.gitignore delete mode 100644 tests/milvus_python_test/Dockerfile delete mode 100644 tests/milvus_python_test/README.md delete mode 100644 tests/milvus_python_test/collection/test_collection.py delete mode 100644 tests/milvus_python_test/collection/test_collection_count.py delete mode 100644 tests/milvus_python_test/collection/test_collection_stats.py delete mode 100644 tests/milvus_python_test/conftest.py delete mode 100755 tests/milvus_python_test/docker-entrypoint.sh delete mode 100644 tests/milvus_python_test/entity/test_delete.py delete mode 100644 tests/milvus_python_test/entity/test_get_entity_by_id.py delete mode 100644 tests/milvus_python_test/entity/test_insert.py delete mode 100644 tests/milvus_python_test/entity/test_list_id_in_segment.py delete mode 100644 tests/milvus_python_test/entity/test_search.py delete mode 100755 tests/milvus_python_test/entity/test_search_by_id.py delete mode 100644 tests/milvus_python_test/pytest.ini delete mode 100644 tests/milvus_python_test/requirements.txt delete mode 100644 tests/milvus_python_test/requirements_cluster.txt delete mode 100644 tests/milvus_python_test/requirements_no_pymilvus.txt delete mode 100644 tests/milvus_python_test/run.sh delete mode 100644 tests/milvus_python_test/stability/test_mysql.py delete mode 100644 tests/milvus_python_test/stability/test_restart.py delete mode 100644 tests/milvus_python_test/test_compact.py delete mode 100644 tests/milvus_python_test/test_config.py delete mode 100644 tests/milvus_python_test/test_connect.py delete mode 100644 tests/milvus_python_test/test_flush.py delete mode 100644 tests/milvus_python_test/test_index.py delete mode 100644 tests/milvus_python_test/test_mix.py delete mode 100644 tests/milvus_python_test/test_partition.py delete mode 100644 tests/milvus_python_test/test_ping.py delete mode 100644 tests/milvus_python_test/test_wal.py delete mode 100644 tests/milvus_python_test/utils.py diff --git a/.all-contributorsrc b/.all-contributorsrc deleted file mode 100644 index a82faceb7a..0000000000 --- a/.all-contributorsrc +++ /dev/null @@ -1,78 +0,0 @@ -{ - "files": [ - "README.md" - ], - "imageSize": 100, - "commit": false, - "contributors": [ - { - "login": "zerowe-seven", - "name": "zerowe-seven", - "avatar_url": "https://avatars0.githubusercontent.com/u/57790060?v=4", - "profile": "https://github.com/zerowe-seven", - "contributions": [ - "code" - ] - }, - { - "login": "erdustiggen", - "name": "erdustiggen", - "avatar_url": "https://avatars1.githubusercontent.com/u/25433850?v=4", - "profile": "https://github.com/erdustiggen", - "contributions": [ - "code" - ] - }, - { - "login": "gaolizhou", - "name": "gaolizhou", - "avatar_url": "https://avatars2.githubusercontent.com/u/2884044?v=4", - "profile": "https://github.com/gaolizhou", - "contributions": [ - "code" - ] - }, - { - "login": "akihoni", - "name": "Sijie Zhang", - "avatar_url": "https://avatars0.githubusercontent.com/u/36330442?v=4", - "profile": "https://github.com/akihoni", - "contributions": [ - "doc" - ] - }, - { - "login": "PizzaL", - "name": "PizzaL", - "avatar_url": "https://avatars0.githubusercontent.com/u/5666666?v=4", - "profile": "https://github.com/PizzaL", - "contributions": [ - "code" - ] - }, - { - "login": "levylll", - "name": "levylll", - "avatar_url": "https://avatars2.githubusercontent.com/u/5645285?v=4", - "profile": "https://github.com/levylll", - "contributions": [ - "code" - ] - }, - { - "login": "aaronjin2010", - "name": "aaronjin2010", - "avatar_url": "https://avatars1.githubusercontent.com/u/48044391?v=4", - "profile": "https://github.com/aaronjin2010", - "contributions": [ - "code" - ] - } - ], - "contributorsPerLine": 7, - "projectName": "milvus", - "projectOwner": "milvus-io", - "repoType": "github", - "repoHost": "https://github.com", - "skipCi": true -} diff --git a/.clang-format b/.clang-format deleted file mode 100644 index 73dc2aeb8f..0000000000 --- a/.clang-format +++ /dev/null @@ -1,27 +0,0 @@ -# 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 limitations -# under the License. ---- -BasedOnStyle: Google -DerivePointerAlignment: false -ColumnLimit: 120 -IndentWidth: 4 -AccessModifierOffset: -3 -AlwaysBreakAfterReturnType: All -AllowShortBlocksOnASingleLine: false -AllowShortFunctionsOnASingleLine: false -AllowShortIfStatementsOnASingleLine: false -AlignTrailingComments: true diff --git a/.clang-tidy b/.clang-tidy deleted file mode 100644 index 1695fa6685..0000000000 --- a/.clang-tidy +++ /dev/null @@ -1,33 +0,0 @@ -# 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 limitations -# under the License. ---- -Checks: 'clang-diagnostic-*,clang-analyzer-*,-clang-analyzer-alpha*,google-*,modernize-*,readability-*' -# produce HeaderFilterRegex from cpp/build-support/lint_exclusions.txt with: -# echo -n '^('; sed -e 's/*/\.*/g' cpp/build-support/lint_exclusions.txt | tr '\n' '|'; echo ')$' -HeaderFilterRegex: '^(.*cmake-build-debug.*|.*cmake-build-release.*|.*cmake_build.*|.*src/core/thirdparty.*|.*thirdparty.*|.*easylogging++.*|.*SqliteMetaImpl.cpp|.*src/grpc.*|.*src/core.*|.*src/wrapper.*)$' -AnalyzeTemporaryDtors: true -ChainedConditionalReturn: 1 -ChainedConditionalAssignment: 1 -CheckOptions: - - key: google-readability-braces-around-statements.ShortStatementLines - value: '1' - - key: google-readability-function-size.StatementThreshold - value: '800' - - key: google-readability-namespace-comments.ShortNamespaceLines - value: '10' - - key: google-readability-namespace-comments.SpacesBeforeComments - value: '2' diff --git a/.clang-tidy-ignore b/.clang-tidy-ignore deleted file mode 100644 index d0a78df219..0000000000 --- a/.clang-tidy-ignore +++ /dev/null @@ -1,17 +0,0 @@ -# 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 limitations -# under the License. -# diff --git a/.codacy.yaml b/.codacy.yaml deleted file mode 100644 index 6a2596d0fd..0000000000 --- a/.codacy.yaml +++ /dev/null @@ -1,7 +0,0 @@ -exclude_paths: - - 'core/src/index/thirdparty/**' - - 'sdk/build-support/**' - - 'core/build-support/**' - - 'core/src/grpc/gen-milvus/**' - - 'core/src/grpc/gen-status/**' - - 'sdk/grpc-gen/**' diff --git a/.codebeatignore b/.codebeatignore deleted file mode 100644 index 269df014b6..0000000000 --- a/.codebeatignore +++ /dev/null @@ -1,3 +0,0 @@ -core/src/index/thirdparty/** -sdk/build-support/** -core/build-support/** \ No newline at end of file diff --git a/.env b/.env deleted file mode 100644 index 3299802e25..0000000000 --- a/.env +++ /dev/null @@ -1,6 +0,0 @@ -REPO=milvusdb/milvus-dev -MILVUS_INSTALL_PREFIX=/var/lib/milvus -ARCH=amd64 -UBUNTU=18.04 -CENTOS=7 -CUDA=10.1 \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 40cf60f3dd..0000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -name: "\U0001F41B Bug report" -about: Create a bug report to help us improve Milvus -title: '' -labels: '' -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**Steps/Code to reproduce behavior** -Follow this [guide](http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports) to craft a minimal bug report. This helps us reproduce the issue you're having and resolve the issue more quickly. - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Environment details** -- Hardware/Softward conditions (OS, CPU, GPU, Memory) -- Method of installation (Docker, or from source) -- Milvus version (v0.3.1, or v0.4.0) -- Milvus configuration (Settings you made in `server_config.yaml`) - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/documentation-request.md b/.github/ISSUE_TEMPLATE/documentation-request.md deleted file mode 100644 index 133fb9e1e9..0000000000 --- a/.github/ISSUE_TEMPLATE/documentation-request.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: "\U0001F4DD Documentation request" -about: Report incorrect or needed documentation -title: '' -labels: '' -assignees: '' - ---- - -## Report incorrect documentation - -**Location of incorrect documentation** -Provide links and line numbers if applicable. - -**Describe the problems or issues found in the documentation** -A clear and concise description of what you found to be incorrect. - -**Steps taken to verify documentation is incorrect** -List any steps you have taken: - -**Suggested fix for documentation** -Detail proposed changes to fix the documentation if you have any. - ---- - -## Report needed documentation - -**Report needed documentation** -A clear and concise description of what documentation you believe it is needed and why. - -**Describe the documentation you'd like** -A clear and concise description of what you want to happen. - -**Steps taken to search for needed documentation** -List any steps you have taken: diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 01bceb3321..0000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: "\U0001F680 Feature request" -about: Suggest an idea for Milvus -title: '' -labels: '' -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. E.g. I wish I could use Milvus to do [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context, code examples, or references to existing implementations about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/general-question.md b/.github/ISSUE_TEMPLATE/general-question.md deleted file mode 100644 index d49fce1817..0000000000 --- a/.github/ISSUE_TEMPLATE/general-question.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: "\U0001F914 General question" -about: Ask a general question about Milvus -title: '' -labels: '' -assignees: '' - ---- - -**What is your question?** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 307fa950df..0000000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,18 +0,0 @@ -**What type of PR is this?** - -api-change / bug / design / documentation / feature - - -**What this PR does / why we need it:** - - -**Which issue(s) this PR fixes:** - -Fixes # - - -**Special notes for your reviewer:** - - -**Additional documentation (e.g. design docs, usage docs, etc.):** - diff --git a/.github/workflows/core.yml b/.github/workflows/core.yml deleted file mode 100644 index 41a6e3214d..0000000000 --- a/.github/workflows/core.yml +++ /dev/null @@ -1,118 +0,0 @@ -name: Core - -# This workflow is triggered on pushes or pull request to the repository. -on: - push: - # file paths to consider in the event. Optional; defaults to all. - paths: - - 'ci/**' - - 'core/**' - - '.github/workflows/core.yml' - - '!**.md' - - '!ci/jenkins/**' - pull_request: - # file paths to consider in the event. Optional; defaults to all. - paths: - - 'ci/**' - - 'core/**' - - '.github/workflows/core.yml' - - '!**.md' - - '!ci/jenkins/**' - -jobs: - ubuntu: - name: AMD64 Ubuntu ${{ matrix.ubuntu }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - ubuntu: [18.04] - env: - UBUNTU: ${{ matrix.ubuntu }} - steps: - # This step checks out a copy of your repository. - - name: Checkout Milvus - uses: actions/checkout@v1 - - name: Check Dockerfile - uses: mgrachev/action-hadolint@v1 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - reporter: github-pr-check # Default is github-pr-check - - name: Docker Pull - shell: bash - run: | - docker-compose pull --ignore-pull-failures db - docker-compose pull --ignore-pull-failures ubuntu-core - - name: Docker Build - shell: bash - run: | - docker-compose build ubuntu-core - docker rmi $(docker images | grep '' | awk '{print $3}') || exit 0 - - name: Docker Run - run: | - docker-compose run --use-aliases -d db - docker-compose run ubuntu-core - - name: Docker Push - if: success() && github.event_name == 'push' && github.repository == 'milvus-io/milvus' - continue-on-error: true - shell: bash - run: | - docker login -u ${{ secrets.DOCKERHUB_USER }} \ - -p ${{ secrets.DOCKERHUB_TOKEN }} - docker-compose push ubuntu-core - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1 - with: - token: ${{ secrets.CODECOV_TOKEN }} - file: ./ci/scripts/output_new.info - name: ubuntu-${{ matrix.ubuntu }}-unittests - flags: cpu_version_ubuntu_18_04_unittest - yml: ./codecov.yaml - - centos: - name: AMD64 CentOS ${{ matrix.centos }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - centos: [7] - env: - CENTOS: ${{ matrix.centos }} - steps: - - name: Checkout Milvus - uses: actions/checkout@v1 - - name: Check Dockerfile - uses: mgrachev/action-hadolint@v1 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - reporter: github-pr-check # Default is github-pr-check - - name: Docker Pull - shell: bash - run: | - docker-compose pull --ignore-pull-failures db - docker-compose pull --ignore-pull-failures centos-core - - name: Docker Build - shell: bash - run: | - docker-compose build centos-core - docker rmi $(docker images | grep '' | awk '{print $3}') || exit 0 - - name: Docker Run - run: | - docker-compose run --use-aliases -d db - docker-compose run centos-core - - name: Docker Push - if: success() && github.event_name == 'push' && github.repository == 'milvus-io/milvus' - continue-on-error: true - shell: bash - run: | - docker login -u ${{ secrets.DOCKERHUB_USER }} \ - -p ${{ secrets.DOCKERHUB_TOKEN }} - docker-compose push centos-core - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1 - with: - token: ${{ secrets.CODECOV_TOKEN }} - file: ./ci/scripts/output_new.info - name: centos-${{ matrix.centos }}-unittests - flags: cpu_version_centos7_unittest - yml: ./codecov.yaml diff --git a/.gitignore b/.gitignore deleted file mode 100644 index b6c4247481..0000000000 --- a/.gitignore +++ /dev/null @@ -1,35 +0,0 @@ -# CLion generated files -core/cmake-build-debug/ -core/cmake-build-release/ -core/cmake_build -core/.idea/ -core/thirdparty/knowhere_build - -.idea/ -.ycm_extra_conf.py -__pycache__ - -# vscode generated files -.vscode - -build -cmake-build-debug -cmake-build-release -cmake_build - -# Compiled source -*.a -*.so -*.so.* -*.o -*.lo -*.tar.gz -*.log -.coverage -*.pyc -cov_html/ - -# temp -shards/all_in_one_with_mysql/metadata/ -shards/mishards/.env -*.swp diff --git a/.hadolint.yaml b/.hadolint.yaml deleted file mode 100644 index a024f89710..0000000000 --- a/.hadolint.yaml +++ /dev/null @@ -1,6 +0,0 @@ -ignored: - - DL3003 - - DL3007 - - DL3008 -# disable following sourced files - - SC1091 \ No newline at end of file diff --git a/.mergify/config.yml b/.mergify/config.yml deleted file mode 100644 index 7b1ad7deb3..0000000000 --- a/.mergify/config.yml +++ /dev/null @@ -1,19 +0,0 @@ -pull_request_rules: - - name: Automatic merge on approval - conditions: - - "label=automerge" - - "#approved-reviews-by>=1" - - "#review-requested=0" - - "#changes-requested-reviews-by=0" - - "status-success=DCO" - - "status-success=continuous-integration/jenkins/pr-merge" - actions: - merge: - method: squash - - - name: Ask to resolve conflict - conditions: - - conflict - actions: - comment: - message: This pull request is now in conflicts. Could you fix it? 🙏 diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 2511c98150..0000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,888 +0,0 @@ -# Changelog - -Please mark all change in change log and use the issue from GitHub - -# Milvus 0.10.3 (TBD) -## Bug - -## Feature - -## Improvement -- \#3213 Allow users to specify a distance type at runtime for Flat index - -## Task - -# Milvus 0.10.2 (2020-08-15) - -## Bug -- \#2890 Fix the index size caculation in cache -- \#2952 Fix the result merging of IVF_PQ IP -- \#2975 Fix config UT failed -- \#3012 If the cache is too small, queries using multiple GPUs will cause to crash -- \#3133 Reverse query result in mishards if metric type is IP - -## Feature - -## Improvement -- \#2653 Improve IVF search performance when NQ and nProbe are both large -- \#2828 Let Faiss not to compile half float by default - -## Task - -# Milvus 0.10.1 (2020-07-20) - -## Bug -- \#2487 Enlarge timeout value for creating collection -- \#2487 HotFix release lock failed on NAS -- \#2557 Fix random crash of INSERT_DUPLICATE_ID case -- \#2578 Result count doesn't match target vectors count -- \#2585 Support IVF_PQ IP on GPU -- \#2598 Fix Milvus docker image report illegal instruction -- \#2617 Fix HNSW and RNSG index files size -- \#2637 Suit the range of HNSW parameters -- \#2642 Create index failed and server crashed -- \#2649 Search parameter of annoy has conflict with document -- \#2690 Remove body parser in show-partitions endpoints -- \#2692 Milvus hangs during multi-thread concurrent search -- \#2739 Fix mishards start failed -- \#2752 Milvus formats vectors data to double-precision and return to http client -- \#2767 Fix a bug of getting wrong nprobe limitation in knowhere on GPU version -- \#2768 After building the index, the number of vectors increases -- \#2774 Server down during loading data -- \#2776 Fix too many data copies during creating IVF index -- \#2813 To implemente RNSG IP - -## Feature - -## Improvement -- \#2932 Upgrade mishards for milvus 0.10.1 - -## Task - -# Milvus 0.10.0 (2020-06-15) - -## Bug -- \#2367 Fix inconsistent reading and writing when using mishards -- \#2368 Make read node detect delete behavior -- \#2394 Drop collection timeout if too many partitions created on collection -- \#2549 Launch server fail using demo config -- \#2564 cache.cache_size range check error - -## Feature -- \#2363 Update branch version -- \#2510 Upgrade Milvus config - -## Improvement -- \#2381 Upgrade FAISS to 1.6.3 -- \#2429 Fix Milvus 0.9.1 performance degrade issue -- \#2441 Improve Knowhere code coverage -- \#2466 Optimize k-selection implemention of faiss gpu version -- \#2489 Add exception throw on mysql meta error -- \#2495 Add creating lock file failure reason. -- \#2516 Improve unit test coverage -- \#2548 Upgrade mishards for milvus v0.10.0 - -## Task - -# Milvus 0.9.1 (2020-05-29) - -## Bug -- \#2366 Reduce SQL execution times for collection contains lot of partitions -- \#2378 Duplicate data after server restart -- \#2395 Fix large nq cudaMalloc error -- \#2399 The nlist set by the user may not take effect -- \#2403 MySQL max_idle_time is 10 by default -- \#2450 The deleted vectors may be found on GPU -- \#2456 openblas library install failed - -## Feature - -## Improvement -- \#2353 Remove log_config from code and scripts -- \#2370 Clean compile warning -- \#2410 Logging build index progress -- \#2461 Upgrade mishards for milvus 0.9.1 - -# Milvus 0.9.0 (2020-05-15) - -## Bug -- \#1705 Limit the insert data batch size -- \#1776 Error out when index SQ8H run in CPU mode -- \#1925 To flush all collections, flush cannot work -- \#1929 Skip MySQL meta schema field width check -- \#1946 Fix load index file CPU2GPU fail during searching -- \#1955 Switch create_index operation to background once client break connection -- \#1997 Index file missed after compact -- \#2002 Remove log error msg `Attributes is null` -- \#2073 Fix CheckDBConfigBackendUrl error message -- \#2076 CheckMetricConfigAddress error message -- \#2120 Fix Search expected failed if search params set invalid -- \#2121 Allow regex match partition tag when search -- \#2128 Check has_partition params -- \#2131 Distance/ID returned is not correct if searching with duplicate ids -- \#2141 Fix server start failed if wal directory exist -- \#2169 Fix SingleIndexTest.IVFSQHybrid unittest -- \#2194 Fix get collection info failed -- \#2196 Fix server start failed if wal is disabled -- \#2203 0.8.0 id=-1 is returned when total count < topk -- \#2228 Fix show partitions failed in http module -- \#2231 Use server_config to define hard-delete delay time for segment files -- \#2261 Re-define result returned by has_collection if collection in delete state -- \#2264 Milvus opened too many files when the metric_config.enable_monitor=true -- \#2266 Server hangs when using multi-clients to query different collections -- \#2280 has_partition should return true for `_default` - -## Feature -- \#1751 Add api SearchByID -- \#1752 Add api GetVectorsByID -- \#1962 Add api HasPartition -- \#1965 FAISS/NSG/HNSW/ANNOY use unified distance calculation algorithm -- \#2054 Check if CPU instruction sets are illegal -- \#2057 Add a config parameter to switch off http server -- \#2059 Add lock file avoid multiple instances modifying data at the same time -- \#2064 Warn when use SQLite as metadata management -- \#2111 Check GPU environment before start server -- \#2206 Log file rotating -- \#2240 Obtain running rpc requests information -- \#2268 Intelligently detect openblas library in system to avoid installing from source code every time -- \#2283 Suspend the building tasks when any query comand arrives. - -## Improvement -- \#221 Refactor LOG macro -- \#833 Catch exception in RolloutHandler and output in stderr -- \#1796 Compile Openblas with source code to improve the performance -- \#1942 Background merge file strategy -- \#2039 Support Milvus run on SSE CPUs -- \#2149 Merge server_cpu_config.template and server_gpu_config.template -- \#2153 Upgrade thirdparty oatpp to v1.0.0 -- \#2167 Merge log_config.conf with server_config.yaml -- \#2173 Check storage permission -- \#2178 Using elkan K-Means to improve IVF -- \#2185 Change id to string format in http module -- \#2186 Update endpoints in http module -- \#2190 Fix memory usage is twice of index size when using GPU searching -- \#2248 Use hostname and port as instance label of metrics -- \#2252 Upgrade mishards APIs and requirements -- \#2256 k-means clustering algorithm use only Euclidean distance metric -- \#2300 Upgrade mishrads configuration to version 0.4 -- \#2311 Update mishards methods -- \#2330 Change url for behavior 'get_entities_by_id' -- \#2347 Update http document for v0.9.0 -- \#2358 Upgrade mishards for v0.9.0 - -## Task - -# Milvus 0.8.0 (2020-04-15) - -## Bug -- \#1276 SQLite throw exception after create 50000+ partitions in a table -- \#1762 Server is not forbidden to create new partition which tag is `_default` -- \#1789 Fix multi-client search cause server crash -- \#1832 Fix crash in tracing module -- \#1873 Fix index file serialize to incorrect path -- \#1881 Fix bad alloc when index files lost -- \#1883 Fix inserted vectors becomes all zero when index_file_size >= 2GB -- \#1901 Search failed with flat index -- \#1903 Fix invalid annoy result -- \#1910 C++ SDK GetIDsInSegment could not work for large dataset - -## Feature -- \#261 Integrate ANNOY into Milvus -- \#1655 GPU index support delete vectors -- \#1660 IVF PQ CPU support deleted vectors searching -- \#1661 HNSW support deleted vectors searching -- \#1825 Add annoy index type in C++ sdk -- \#1849 NSG support deleted vectors searching -- \#1893 Log config information and device information - -## Improvement -- \#1627 Move read/write index APIs into codec -- \#1784 Add Substructure and Superstructure in http module -- \#1858 Disable S3 build -- \#1882 Add index annoy into http module -- \#1885 Optimize knowhere unittest -- \#1886 Refactor log on search and insert request -- \#1897 Heap pop and push can be realized by heap_swap_top -- \#1921 Use TimeRecorder instead of chrono -- \#1928 Fix too many data and uid copies when loading files -- \#1930 Upgrade mishards to v0.8.0 - -## Task - -# Milvus 0.7.1 (2020-03-29) - -## Bug -- \#1301 Data in WAL may be accidentally inserted into a new table with the same name. -- \#1634 Fix search demo bug in HTTP doc -- \#1635 Vectors can be returned by searching after vectors deleted if `cache_insert_data` set true -- \#1648 The cache cannot be used all when the vector type is binary -- \#1651 Check validity of dimension when collection metric type is binary one -- \#1663 PQ index parameter 'm' validation -- \#1686 API search_in_files cannot work correctly when vectors is stored in certain non-default partition -- \#1689 Fix SQ8H search fail on SIFT-1B dataset -- \#1667 Create index failed with type: rnsg if metric_type is IP -- \#1708 NSG search crashed -- \#1724 Remove unused unittests -- \#1728 Optimize request handler to combine similar query -- \#1734 Opentracing for combined search request -- \#1735 Fix search out of memory with ivf_flat -- \#1747 Expected error status if search with partition_tag not existed -- \#1756 Fix memory exhausted during searching -- \#1781 Fix search hang with SQ8H -- \#1812 Fix incorrect request method in search example in http readme -- \#1818 Duplicate data generated after restart milvus server - -## Feature -- \#1603 BinaryFlat add 2 Metric: Substructure and Superstructure - -## Improvement -- \#267 Improve search performance: reduce delay -- \#342 Knowhere and Wrapper refactor -- \#1537 Optimize raw vector and uids read/write -- \#1546 Move Config.cpp to config directory -- \#1547 Rename storage/file to storage/disk and rename classes -- \#1548 Move store/Directory to storage/Operation and add FSHandler -- \#1572 Optimize config cpu/gpu cache_capacity setter -- \#1619 Improve compact performance -- \#1649 Fix Milvus crash on old CPU -- \#1653 IndexFlat (SSE) and IndexBinaryFlat performance improvement for small NQ -- \#1678 Remove CUSTOMIZATION macro -- \#1698 Upgrade mishards to v0.7.0 -- \#1719 Improve Milvus log -- \#1754 Optimize behavior to get file ids from metadata in mishards -- \#1799 Update docker images to 0.7.1 in mishards - -## Task - -# Milvus 0.7.0 (2020-03-11) - -## Bug -- \#715 Milvus crash when searching and building index simultaneously using SQ8H -- \#744 Don't return partition table for show_tables -- \#770 Server unittest run failed on low-end server -- \#805 IVFTest.gpu_seal_test unittest failed -- \#831 Judge branch error in CommonUtil.cpp -- \#977 Server crash when create tables concurrently -- \#990 Check gpu resources setting when assign repeated value -- \#995 Table count set to 0 if no tables found -- \#1010 Improve error message when offset or page_size is equal 0 -- \#1022 Check if partition name is valid -- \#1028 Check if table exists when show partitions -- \#1029 Check if table exists when try to delete partition -- \#1066 Optimize http insert and search speed -- \#1022 Check if partition name is legal -- \#1028 Check if table exists when show partitions -- \#1029 Check if table exists when try to delete partition -- \#1066 Optimize http insert and search speed -- \#1067 Add binary vectors support in http server -- \#1075 Improve error message when page size or offset is illegal -- \#1082 Check page_size or offset value to avoid float -- \#1115 Http server support load table into memory -- \#1152 Error log output continuously after server start -- \#1211 Server down caused by searching with index_type: HNSW -- \#1240 Update license declaration -- \#1298 Unit test failed when on CPU2GPU case -- \#1359 Negative distance value returned when searching with HNSW index type -- \#1429 Server crashed when searching vectors with GPU -- \#1476 Fix vectors results bug when getting vectors from segments -- \#1484 Index type changed to IDMAP after compacted -- \#1491 Server crashed during adding vectors -- \#1499 Fix duplicated ID number issue -- \#1504 Avoid possible race condition between delete and search -- \#1507 set_config for insert_buffer_size is wrong -- \#1510 Add set interfaces for WAL configurations -- \#1511 Fix big integer cannot pass to server correctly -- \#1517 Result is not correct when search vectors in multi partition, index type is RNSG -- \#1518 Table count did not match after deleting vectors and compact -- \#1521 Make cache_insert_data take effect in-service -- \#1525 Add setter API for config preload_table -- \#1529 Fix server crash when cache_insert_data enabled -- \#1530 Set table file with correct engine type in meta -- \#1532 Search with ivf_flat failed with open-dataset: sift-256-hamming -- \#1535 Degradation searching performance with metric_type: binary_idmap -- \#1549 Fix server/wal config setting bug -- \#1556 Index file not created after table and index created -- \#1560 Search crashed with Super-high dimensional binary vector -- \#1564 Too low recall for glove-200-angular, ivf_pq index -- \#1571 Meta engine type become IDMAP after dropping index for BINARY table -- \#1574 Set all existing bitset in cache when applying deletes -- \#1577 Row count incorrect if delete vectors then create index -- \#1580 Old segment folder not removed after merge/compact if create_index is called before adding data -- \#1590 Server down caused by failure to write file during concurrent mixed operations -- \#1598 Server down during mixed operations -- \#1601 External link bug in HTTP doc -- \#1609 Refine Compact function -- \#1808 Building index params check for Annoy -- \#1852 Search index type failed with reason `failed to load index file` - -## Feature -- \#216 Add CLI to get server info -- \#343 Add Opentracing -- \#665 Support get/set config via CLI -- \#759 Put C++ sdk out of milvus/core -- \#766 If partition tag is similar, wrong partition is searched -- \#771 Add server build commit info interface -- \#788 Add web server into server module -- \#813 Add push mode for prometheus monitor -- \#815 Support MinIO storage -- \#823 Support binary vector tanimoto/jaccard/hamming metric -- \#830 Support WAL(write-ahead logging) -- \#853 Support HNSW -- \#861 Support DeleteById / SearchByID / GetVectorById / Flush -- \#910 Change Milvus c++ standard to c++17 -- \#1122 Support AVX-512 in FAISS -- \#1204 Add api to get table data information -- \#1250 Support CPU profiling -- \#1302 Get all record IDs in a segment by given a segment id -- \#1461 Add crud APIs and segments APIs into http module -- \#1463 Update config version to 0.2 -- \#1531 Remove S3 related config - -## Improvement -- \#738 Use Openblas / lapack from apt install -- \#758 Enhance config description -- \#791 Remove Arrow -- \#834 Add cpu mode for built-in Faiss -- \#848 Add ready-to-use config files to the Milvus repo for enhanced user experince -- \#860 Remove redundant checks in CacheMgr's constructor -- \#908 Move "primary_path" and "secondary_path" to storage config -- \#931 Remove "collector" from config -- \#966 Update NOTICE.md -- \#1002 Rename minio to s3 in Storage Config section -- \#1078 Move 'insert_buffer_size' to Cache Config section -- \#1105 Error message is not clear when creating IVFSQ8H index without gpu resources -- \#740, #849, #878, #972, #1033, #1161, #1173, #1199, #1190, #1223, #1222, #1257, #1264, #1269, #1164, #1303, #1304, #1324, #1388, #1459 Various fixes and improvements for Milvus documentation. -- \#1297 Hide partition_name parameter, avid user directly access partition table -- \#1234 Do S3 server validation check when Milvus startup -- \#1263 Allow system conf modifiable and some take effect directly -- \#1310 Add default partition tag for a table -- \#1320 Remove debug logging from faiss -- \#1426 Support to configure whether to enabled autoflush and the autoflush interval -- \#1444 Improve delete -- \#1448 General proto api for NNS libraries -- \#1480 Add return code for AVX512 selection -- \#1524 Update config "preload_table" description -- \#1544 Update resources name in HTTP module -- \#1567 Update yaml config description - -## Task -- \#1327 Exclude third-party code from codebeat -- \#1331 Exclude third-party code from codacy - -# Milvus 0.6.0 (2019-12-07) - -## Bug -- \#228 Memory usage increased slowly during searching vectors -- \#246 Exclude src/external folder from code coverage for jenkin ci -- \#248 Reside src/external in thirdparty -- \#316 Some files not merged after vectors added -- \#327 Search does not use GPU when index type is FLAT -- \#331 Add exception handle when search fail -- \#340 Test cases run failed on 0.6.0 -- \#353 Rename config.h.in to version.h.in -- \#374 sdk_simple return empty result -- \#377 Create partition success if tag name only contains spaces -- \#397 sdk_simple return incorrect result -- \#399 Create partition should be failed if partition tag existed -- \#412 Message returned is confused when partition created with null partition name -- \#416 Drop the same partition success repeatally -- \#440 Query API in customization still uses old version -- \#440 Server cannot startup with gpu_resource_config.enable=false in GPU version -- \#458 Index data is not compatible between 0.5 and 0.6 -- \#465 Server hang caused by searching with nsg index -- \#485 Increase code coverage rate -- \#486 gpu no usage during index building -- \#497 CPU-version search performance decreased -- \#504 The code coverage rate of core/src/scheduler/optimizer is too low -- \#509 IVF_PQ index build trapped into dead loop caused by invalid params -- \#513 Unittest DELETE_BY_RANGE sometimes failed -- \#523 Erase file data from cache once the file is marked as deleted -- \#527 faiss benchmark not compatible with faiss 1.6.0 -- \#530 BuildIndex stop when do build index and search simultaneously -- \#532 Assigin value to `table_name` from confest shell -- \#533 NSG build failed with MetricType Inner Product -- \#543 client raise exception in shards when search results is empty -- \#545 Avoid dead circle of build index thread when error occurs -- \#547 NSG build failed using GPU-edition if set gpu_enable false -- \#548 NSG search accuracy is too low -- \#552 Server down during building index_type: IVF_PQ using GPU-edition -- \#561 Milvus server should report exception/error message or terminate on mysql metadata backend error -- \#579 Build index hang in GPU version when gpu_resources disabled -- \#596 Frequently insert operation cost too much disk space -- \#599 Build index log is incorrect -- \#602 Optimizer specify wrong gpu_id -- \#606 No log generated during building index with CPU -- \#616 IP search metric_type is not supported by IVF_PQ index -- \#631 FAISS isn't compiled with O3 option -- \#636 (CPU) Create index PQ should be failed if table metric type set Inner Product -- \#649 Typo "partiton" should be "partition" -- \#654 Random crash when frequently insert vector one by one -- \#658 Milvus error out when building SQ8H index without GPU resources -- \#668 Update badge of README -- \#670 Random failure of unittest db_test::SEARCH_TEST -- \#674 Server down in stability test -- \#696 Metric_type changed from IP to L2 -- \#705 Fix search SQ8H crash without GPU resource - -## Feature -- \#12 Pure CPU version for Milvus -- \#77 Support table partition -- \#127 Support new Index type IVFPQ -- \#226 Experimental shards middleware for Milvus -- \#227 Support new index types SPTAG-KDT and SPTAG-BKT -- \#346 Support build index with multiple gpu -- \#420 Update shards merge part to match v0.5.3 -- \#488 Add log in scheduler/optimizer -- \#502 C++ SDK support IVFPQ and SPTAG -- \#560 Add version in server config file -- \#605 Print more messages when server start -- \#644 Add a new rpc command to get milvus build version whether cpu or gpu -- \#709 Show last commit id when server start - -## Improvement -- \#255 Add ivfsq8 test report detailed version -- \#260 C++ SDK README -- \#266 RPC request source code refactor -- \#274 Logger the time cost during preloading data -- \#275 Rename C++ SDK IndexType -- \#284 Change C++ SDK to shared library -- \#306 Use int64 for all config integer -- \#310 Add Q&A for 'protocol https not supported or disable in libcurl' issue -- \#314 add Find FAISS in CMake -- \#322 Add option to enable / disable prometheus -- \#354 Build migration scripts into milvus docker image -- \#358 Add more information in build.sh and install.md -- \#404 Add virtual method Init() in Pass abstract class -- \#409 Add a Fallback pass in optimizer -- \#433 C++ SDK query result is not easy to use -- \#449 Add ShowPartitions example for C++ SDK -- \#470 Small raw files should not be build index -- \#584 Intergrate internal FAISS -- \#611 Remove MILVUS_CPU_VERSION -- \#634 FAISS GPU version is compiled with O0 -- \#737 Refactor server module to separate Grpc from server handler and scheduler - -## Task - -# Milvus 0.5.3 (2019-11-13) - -## Bug -- \#258 Bytes type in proto cause big-endian/little-endian problem - -## Feature - -## Improvement -- \#204 improve grpc performance in search -- \#207 Add more unittest for config set/get -- \#208 Optimize unittest to support run single test more easily -- \#284 Change C++ SDK to shared library -- \#260 C++ SDK README - -## Task - -# Milvus 0.5.2 (2019-11-07) - -## Bug -- \#194 Search faild: message="Table file doesn't exist" - -## Feature - -## Improvement -- \#190 Update default config:use_blas_threshold to 1100 and server version printout to 0.5.2 - -## Task - -# Milvus 0.5.1 (2019-11-04) - -## Bug -- \#134 JFrog cache error -- \#161 Search IVFSQHybrid crash on gpu -- \#169 IVF_FLAT search out of memory - -## Feature -- \#90 The server start error messages could be improved to enhance user experience -- \#104 test_scheduler core dump -- \#115 Using new structure for tasktable -- \#139 New config option use_gpu_threshold -- \#146 Add only GPU and only CPU version for IVF_SQ8 and IVF_FLAT -- \#164 Add CPU version for building index - -## Improvement -- \#64 Improvement dump function in scheduler -- \#80 Print version information into log during server start -- \#82 Move easyloggingpp into "external" directory -- \#92 Speed up CMake build process -- \#96 Remove .a file in milvus/lib for docker-version -- \#118 Using shared_ptr instead of weak_ptr to avoid performance loss -- \#122 Add unique id for Job -- \#130 Set task state MOVED after resource copy it completed -- \#149 Improve large query optimizer pass -- \#156 Not return error when search_resources and index_build_device set cpu -- \#159 Change the configuration name from 'use_gpu_threshold' to 'gpu_search_threshold' -- \#168 Improve result reduce -- \#175 add invalid config unittest - -## Task - -# Milvus 0.5.0 (2019-10-21) - -## Bug -- MS-568 Fix gpuresource free error -- MS-572 Milvus crash when get SIGINT -- MS-577 Unittest Query randomly hung -- MS-587 Count get wrong result after adding vectors and index built immediately -- MS-599 Search wrong result when table created with metric_type: IP -- MS-601 Docker logs error caused by get CPUTemperature error -- MS-605 Server going down during searching vectors -- MS-620 Get table row counts display wrong error code -- MS-622 Delete vectors should be failed if date range is invalid -- MS-624 Search vectors failed if time ranges long enough -- MS-637 Out of memory when load too many tasks -- MS-639 SQ8H index created failed and server hang -- MS-640 Cache object size calculate incorrect -- MS-641 Segment fault(signal 11) in PickToLoad -- MS-644 Search crashed with index-type: flat -- MS-647 grafana display average cpu-temp -- MS-652 IVFSQH quantization double free -- MS-650 SQ8H index create issue -- MS-653 When config check fail, Milvus close without message -- MS-654 Describe index timeout when building index -- MS-658 Fix SQ8 Hybrid can't search -- MS-665 IVF_SQ8H search crash when no GPU resource in search_resources -- \#9 Change default gpu_cache_capacity to 4 -- \#20 C++ sdk example get grpc error -- \#23 Add unittest to improve code coverage -- \#31 make clang-format failed after run build.sh -l -- \#39 Create SQ8H index hang if using github server version -- \#30 Some troubleshoot messages in Milvus do not provide enough information -- \#48 Config unittest failed -- \#59 Topk result is incorrect for small dataset - -## Improvement -- MS-552 Add and change the easylogging library -- MS-553 Refine cache code -- MS-555 Remove old scheduler -- MS-556 Add Job Definition in Scheduler -- MS-557 Merge Log.h -- MS-558 Refine status code -- MS-562 Add JobMgr and TaskCreator in Scheduler -- MS-566 Refactor cmake -- MS-574 Milvus configuration refactor -- MS-578 Make sure milvus5.0 don't crack 0.3.1 data -- MS-585 Update namespace in scheduler -- MS-606 Speed up result reduce -- MS-608 Update TODO names -- MS-609 Update task construct function -- MS-611 Add resources validity check in ResourceMgr -- MS-619 Add optimizer class in scheduler -- MS-626 Refactor DataObj to support cache any type data -- MS-648 Improve unittest -- MS-655 Upgrade SPTAG -- \#42 Put union of index_build_device and search resources to gpu_pool -- \#67 Avoid linking targets multiple times in cmake - -## Feature -- MS-614 Preload table at startup -- MS-627 Integrate new index: IVFSQHybrid -- MS-631 IVFSQ8H Index support -- MS-636 Add optimizer in scheduler for FAISS_IVFSQ8H - -## Task -- MS-554 Change license to Apache 2.0 -- MS-561 Add contributing guidelines, code of conduct and README docs -- MS-567 Add NOTICE.md -- MS-569 Complete the NOTICE.md -- MS-575 Add Clang-format & Clang-tidy & Cpplint -- MS-586 Remove BUILD_FAISS_WITH_MKL option -- MS-590 Refine cmake code to support cpplint -- MS-600 Reconstruct unittest code -- MS-602 Remove zilliz namespace -- MS-610 Change error code base value from hex to decimal -- MS-624 Re-organize project directory for open-source -- MS-635 Add compile option to support customized faiss -- MS-660 add ubuntu_build_deps.sh -- \#18 Add all test cases - -# Milvus 0.4.0 (2019-09-12) - -## Bug -- MS-119 The problem of combining the log files -- MS-121 The problem that user can't change the time zone -- MS-411 Fix metric unittest linking error -- MS-412 Fix gpu cache logical error -- MS-416 ExecutionEngineImpl::GpuCache has not return value cause crash -- MS-417 YAML sequence load disable cause scheduler startup failed -- MS-413 Create index failed and server exited -- MS-427 Describe index error after drop index -- MS-432 Search vectors params nprobe need to check max number -- MS-431 Search vectors params nprobe: 0/-1, expected result: raise exception -- MS-331 Crate Table : when table exists, error code is META_FAILED(code=15) rather than ILLEGAL TABLE NAME(code=9)) -- MS-430 Search no result if index created with FLAT -- MS-443 Create index hang again -- MS-436 Delete vectors failed if index created with index_type: IVF_FLAT/IVF_SQ8 -- MS-449 Add vectors twice success, once with ids, the other no ids -- MS-450 server hang after run stop_server.sh -- MS-458 Keep building index for one file when no gpu resource -- MS-461 Mysql meta unittest failed -- MS-462 Run milvus server twices, should display error -- MS-463 Search timeout -- MS-467 mysql db test failed -- MS-470 Drop index success, which table not created -- MS-471 code coverage run failed -- MS-492 Drop index failed if index have been created with index_type: FLAT -- MS-493 Knowhere unittest crash -- MS-453 GPU search error when nprobe set more than 1024 -- MS-474 Create index hang if use branch-0.3.1 server config -- MS-510 unittest out of memory and crashed -- MS-507 Dataset 10m-512, index type sq8,performance in-normal when set CPU_CACHE to 16 or 64 -- MS-543 SearchTask fail without exception -- MS-582 grafana displays changes frequently - -## Improvement -- MS-327 Clean code for milvus -- MS-336 Scheduler interface -- MS-344 Add TaskTable Test -- MS-345 Add Node Test -- MS-346 Add some implementation of scheduler to solve compile error -- MS-348 Add ResourceFactory Test -- MS-350 Remove knowhere submodule -- MS-354 Add task class and interface in scheduler -- MS-355 Add copy interface in ExcutionEngine -- MS-357 Add minimum schedule function -- MS-359 Add cost test in new scheduler -- MS-361 Add event in resource -- MS-364 Modify tasktableitem in tasktable -- MS-365 Use tasktableitemptr instead in event -- MS-366 Implement TaskTable -- MS-368 Implement cost.cpp -- MS-371 Add TaskTableUpdatedEvent -- MS-373 Add resource test -- MS-374 Add action definition -- MS-375 Add Dump implementation for Event -- MS-376 Add loader and executor enable flag in Resource avoid diskresource execute task -- MS-377 Improve process thread trigger in ResourceMgr, Scheduler and TaskTable -- MS-378 Debug and Update normal_test in scheduler unittest -- MS-379 Add Dump implementation in Resource -- MS-380 Update resource loader and executor, work util all finished -- MS-383 Modify condition variable usage in scheduler -- MS-384 Add global instance of ResourceMgr and Scheduler -- MS-389 Add clone interface in Task -- MS-390 Update resource construct function -- MS-391 Add PushTaskToNeighbourHasExecutor action -- MS-394 Update scheduler unittest -- MS-400 Add timestamp record in task state change function -- MS-402 Add dump implementation for TaskTableItem -- MS-406 Add table flag for meta -- MS-403 Add GpuCacheMgr -- MS-404 Release index after search task done avoid memory increment continues -- MS-405 Add delete task support -- MS-407 Reconstruct MetricsCollector -- MS-408 Add device_id in resource construct function -- MS-409 Using new scheduler -- MS-413 Remove thrift dependency -- MS-410 Add resource config comment -- MS-414 Add TaskType in Scheduler::Task -- MS-415 Add command tasktable to dump all tasktables -- MS-418 Update server_config.template file, set CPU compute only default -- MS-419 Move index_file_size from IndexParam to TableSchema -- MS-421 Add TaskLabel in scheduler -- MS-422 Support DeleteTask in Multi-GpuResource case -- MS-428 Add PushTaskByDataLocality in scheduler -- MS-440 Add DumpTaskTables in sdk -- MS-442 Merge Knowhere -- MS-445 Rename CopyCompleted to LoadCompleted -- MS-451 Update server_config.template file, set GPU compute default -- MS-455 Distribute tasks by minimal cost in scheduler -- MS-460 Put transport speed as weight when choosing neighbour to execute task -- MS-459 Add cache for pick function in tasktable -- MS-476 Improve search performance -- MS-482 Change search stream transport to unary in grpc -- MS-487 Define metric type in CreateTable -- MS-488 Improve code format in scheduler -- MS-495 cmake: integrated knowhere -- MS-496 Change the top_k limitation from 1024 to 2048 -- MS-502 Update tasktable_test in scheduler -- MS-504 Update node_test in scheduler -- MS-505 Install core unit test and add to coverage -- MS-508 Update normal_test in scheduler -- MS-532 Add grpc server unittest -- MS-511 Update resource_test in scheduler -- MS-517 Update resource_mgr_test in scheduler -- MS-518 Add schedinst_test in scheduler -- MS-519 Add event_test in scheduler -- MS-520 Update resource_test in scheduler -- MS-524 Add some unittest in event_test and resource_test -- MS-525 Disable parallel reduce in SearchTask -- MS-527 Update scheduler_test and enable it -- MS-528 Hide some config used future -- MS-530 Add unittest for SearchTask->Load -- MS-531 Disable next version code -- MS-533 Update resource_test to cover dump function -- MS-523 Config file validation -- MS-539 Remove old task code -- MS-546 Add simple mode resource_config -- MS-570 Add prometheus docker-compose file -- MS-576 Scheduler refactor -- MS-592 Change showtables stream transport to unary - -## Feature -- MS-343 Implement ResourceMgr -- MS-338 NewAPI: refine code to support CreateIndex -- MS-339 NewAPI: refine code to support DropIndex -- MS-340 NewAPI: implement DescribeIndex - -## Task -- MS-297 disable mysql unit test - -# Milvus 0.3.1 (2019-07-10) - -## Bug - -- MS-148 Disable cleanup if mode is read only -- MS-149 Fixed searching only one index file issue in distributed mode -- MS-153 Fix c_str error when connecting to MySQL -- MS-157 Fix changelog -- MS-190 Use env variable to switch mem manager and fix cmake -- MS-217 Fix SQ8 row count bug -- MS-224 Return AlreadyExist status in MySQLMetaImpl::CreateTable if table already exists -- MS-232 Add MySQLMetaImpl::UpdateTableFilesToIndex and set maximum_memory to default if config value = 0 -- MS-233 Remove mem manager log -- MS-230 Change parameter name: Maximum_memory to insert_buffer_size -- MS-234 Some case cause background merge thread stop -- MS-235 Some test cases random fail -- MS-236 Add MySQLMetaImpl::HasNonIndexFiles -- MS-257 Update bzip2 download url -- MS-288 Update compile scripts -- MS-330 Stability test failed caused by server core dumped -- MS-347 Build index hangs again -- MS-382 fix MySQLMetaImpl::CleanUpFilesWithTTL unknown column bug - -## Improvement -- MS-156 Add unittest for merge result functions -- MS-152 Delete assert in MySQLMetaImpl and change MySQLConnectionPool impl -- MS-204 Support multi db_path -- MS-206 Support SQ8 index type -- MS-208 Add buildinde interface for C++ SDK -- MS-212 Support Inner product metric type -- MS-241 Build Faiss with MKL if using Intel CPU; else build with OpenBlas -- MS-242 Clean up cmake and change MAKE_BUILD_ARGS to be user defined variable -- MS-245 Improve search result transfer performance -- MS-248 Support AddVector/SearchVector profiling -- MS-256 Add more cache config -- MS-260 Refine log -- MS-249 Check machine hardware during initialize -- MS-261 Update faiss version to 1.5.3 and add BUILD_FAISS_WITH_MKL as an option -- MS-266 Improve topk reduce time by using multi-threads -- MS-275 Avoid sqlite logic error excetion -- MS-278 add IndexStatsHelper -- MS-313 add GRPC -- MS-325 add grpc status return for C++ sdk and modify some format -- MS-278 Add IndexStatsHelper -- MS-312 Set openmp thread number by config -- MS-305 Add CPU core percent metric -- MS-310 Add milvus CPU utilization ratio and CPU/GPU temperature metrics -- MS-324 Show error when there is not enough gpu memory to build index -- MS-328 Check metric type on server start -- MS-332 Set grpc and thrift server run concurrently -- MS-352 Add hybrid index - -## Feature -- MS-180 Add new mem manager -- MS-195 Add nlist and use_blas_threshold conf -- MS-137 Integrate knowhere - -## Task - -- MS-125 Create 0.3.1 release branch -- MS-306 Optimize build efficiency - -# Milvus 0.3.0 (2019-06-30) - -## Bug -- MS-104 Fix unittest lcov execution error -- MS-102 Fix build script file condition error -- MS-80 Fix server hang issue -- MS-89 Fix compile failed, libgpufaiss.a link missing -- MS-90 Fix arch match incorrect on ARM -- MS-99 Fix compilation bug -- MS-110 Avoid huge file size - -## Improvement -- MS-82 Update server startup welcome message -- MS-83 Update vecwise to Milvus -- MS-77 Performance issue of post-search action -- MS-22 Enhancement for MemVector size control -- MS-92 Unify behavior of debug and release build -- MS-98 Install all unit test to installation directory -- MS-115 Change is_startup of metric_config switch from true to on -- MS-122 Archive criteria config -- MS-124 HasTable interface -- MS-126 Add more error code -- MS-128 Change default db path - -## Feature - -- MS-57 Implement index load/search pipeline -- MS-56 Add version information when server is started -- MS-64 Different table can have different index type -- MS-52 Return search score -- MS-66 Support time range query -- MS-68 Remove rocksdb from third-party -- MS-70 cmake: remove redundant libs in src -- MS-71 cmake: fix faiss dependency -- MS-72 cmake: change prometheus source to git -- MS-73 cmake: delete civetweb -- MS-65 Implement GetTableRowCount interface -- MS-45 Implement DeleteTable interface -- MS-75 cmake: change faiss version to 1.5.2; add CUDA gencode -- MS-81 Fix faiss ptx issue; change cuda gencode -- MS-84 cmake: add arrow, jemalloc and jsoncons third party; default build option OFF -- MS-85 add NetIO metric -- MS-96 add new query interface for specified files -- MS-97 Add S3 SDK for MinIO Storage -- MS-105 Add MySQL -- MS-130 Add prometheus_test -- MS-144 Add nprobe config -- MS-147 Enable IVF -- MS-130 Add prometheus_test - -## Task -- MS-74 Change README.md in cpp -- MS-88 Add support for arm architecture - -# Milvus 0.2.0 (2019-05-31) - -## Bug - -- MS-32 Fix thrift error -- MS-34 Fix prometheus-cpp thirdparty -- MS-67 Fix license check bug -- MS-76 Fix pipeline crash bug -- MS-100 CMake: fix AWS build issue -- MS-101 Change AWS build type to Release - -## Improvement - -- MS-20 Clean Code Part 1 - -## Feature - -- MS-5 Implement Auto Archive Feature -- MS-6 Implement SDK interface part 1 -- MS-16 Implement metrics without prometheus -- MS-21 Implement SDK interface part 2 -- MS-26 CMake. Add thirdparty packages -- MS-31 CMake: add prometheus -- MS-33 CMake: add -j4 to make third party packages build faster -- MS-27 Support gpu config and disable license build config in cmake -- MS-47 Add query vps metrics -- MS-37 Add query, cache usage, disk write speed and file data size metrics -- MS-30 Use faiss v1.5.2 -- MS-54 CMake: Change Thrift third party URL to github.com -- MS-69 Prometheus: add all proposed metrics - -## Task - -- MS-1 Add CHANGELOG.md -- MS-4 Refactor the vecwise_engine code structure -- MS-62 Search range to all if no date specified diff --git a/CODEOWNERS b/CODEOWNERS deleted file mode 100644 index a0d43d19da..0000000000 --- a/CODEOWNERS +++ /dev/null @@ -1,9 +0,0 @@ -# Each line is a component followed by one or more owners. - -* @JinHai-CN -/sdk @scsven -/shards @XuPeng-SH -/core/ @scsven -/core/src/db/meta @yhmo -/core/src/index/knowhere @cydrain -/core/src/index/ @shengjun1985 \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 296d5d9a07..0000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,48 +0,0 @@ -# Milvus Code of Conduct - -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment include: - -- Using welcoming and inclusive language. -- Being respectful of differing viewpoints and experiences. -- Gracefully accepting constructive criticism. -- Focusing on what is best for the community. -- Showing empathy towards other community members. - -Examples of unacceptable behavior by participants include: - -- The use of sexualized language or imagery and unwelcome sexual attention or advances. -- Trolling, insulting/derogatory comments, and personal or political attacks. -- Public or private harassment. -- Publishing others' private information, such as a physical or electronic address, without explicit permission. -- Conduct which could reasonably be considered inappropriate for the forum in which it occurs. - -All Milvus forums and spaces are meant for professional interactions, and any behavior which could reasonably be considered inappropriate in a professional setting is unacceptable. - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies to all content on milvus.io, Milvus’s GitHub organization, or any other official Milvus web presence allowing for community interactions, as well as at all official Milvus events, whether offline or online. - -The Code of Conduct also applies within all project spaces and in public spaces whenever an individual is representing Milvus or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed or de facto representative at an online or offline event. Representation of Milvus may be further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at conduct@lfai.foundation. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html - -For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq - diff --git a/COMMUNITY.md b/COMMUNITY.md deleted file mode 100644 index ed96c0840e..0000000000 --- a/COMMUNITY.md +++ /dev/null @@ -1,99 +0,0 @@ -# Community Roles - - - -- [TSC Members](#tsc-members) -- [Committers](#committers) -- [Reviewers](#reviewers) -- [Contributors](#contributors) - - - -## TSC Members - -The Technical Steering Committee (TSC) functions as the core management team that oversees the Milvus community. The TSC decides the roadmap of the project and makes major decisions related to the community. - -TSC members have the following responsibilities: - -- Coordinate the technical direction of the project. -- Approve project or system proposals. -- Decide on formal releases of the project's software. -- Organize and remove sub-projects. -- Coordinate any marketing, events, or communications regarding the project. - -To become a TSC member, a committer must be sponsored by a TSC member and the sponsorship must be approved by 2/3 of all TSC members. - -The TSC members are listed below: - -- [cxie](https://github.com/cxie) -- [JinHai-CN](https://github.com/JinHai-CN) - -## Committers - -Committers lead project feature discussions and oversee the overall project quality. - -Committers have the following responsibilities: - -- Lead feature design discussions and implementation. -- Ensure the overall project quality and approve PRs. -- Participate in product release, feature planning, and roadmap design. -- Have a constructive and friendly attitude in all community interactions. -- Mentor reviewers and contributors. - -To become a committer, a reviewer must have contributed broadly throughout the Milvus project. A reviewer must also be sponsored by a committer and the sponsorship must be approved by the TSC. - -The committers are listed below: - -- [JinHai-CN](https://github.com/JinHai-CN) - -## Reviewers - -Reviewers review new code contributions and ensure the quality of existing code. - -Reviewers have the following responsibilities: - -- Participate in feature design discussion and implementation. -- Ensure the quality of owned code modules. -- Ensure the technical accuracy of documentation. -- Quickly respond to issues and PRs and conduct code reviews. - -To become a reviewer, a contributor must have provided continued and quality contribution to the Milvus project for at least 6 months and have contributed at least one major component where the contributor has taken an ownership role. - -The reviewers are listed below: - -- [XuPeng-SH](https://github.com/XuPeng-SH) -- [yhmo](https://github.com/yhmo) -- [scsven](https://github.com/scsven) -- [cydrain](https://github.com/cydrain) -- [shengjun1985](https://github.com/shengjun1985) - -## Contributors - -Contributors can be anyone who has successfully submitted at least one PR to the Milvus project. - -The contributors are listed below: - -- [ZhifengZhang-CN](https://github.com/ZhifengZhang-CN) -- [tinkerlin](https://github.com/tinkerlin) -- [youny626](https://github.com/youny626) -- [fishpenguin](https://github.com/fishpenguin) -- [BossZou](https://github.com/BossZou) -- [del-zhenwu](https://github.com/del-zhenwu) -- [jielinxu](https://github.com/jielinxu) -- [yamasite](https://github.com/yamasite) -- [Yukikaze-CZR](https://github.com/Yukikaze-CZR) -- [Heisenberg-Y](https://github.com/Heisenberg-Y) -- [sahuang](https://github.com/sahuang) -- [op-hunter](https://github.com/op-hunter) -- [GuanyunFeng](https://github.com/GuanyunFeng) -- [thywdy](https://github.com/thywdy) -- [erdustiggen](https://github.com/erdustiggen) -- [akihoni](https://github.com/akihoni) -- [shiyu22](https://github.com/shiyu22) -- [shengjh](https://github.com/shengjh) -- [dvzubarev](https://github.com/dvzubarev) -- [aaronjin2010](https://github.com/aaronjin2010) -- [ReigenAraka](https://github.com/ReigenAraka) -- [JackLCL](https://github.com/JackLCL) -- [Bennu-Li](https://github.com/Bennu-Li) -- [ABNER-1](https://github.com/ABNER-1) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index b397420584..0000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,135 +0,0 @@ -# Contributing to Milvus - -First of all, thanks for taking the time to contribute to Milvus! It's people like you that help Milvus come to fruition. :tada: - -The following are a set of guidelines for contributing to Milvus. Following these guidelines helps contributing to this project easy and transparent. These are mostly guideline, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. - -As for everything else in the project, the contributions to Milvus are governed by our [Code of Conduct](CODE_OF_CONDUCT.md). - -## Contribution Checklist - -Before you make any contributions, make sure you follow this list. - -- Read [Contributing to Milvus](CONTRIBUTING.md). -- Check if the changes are consistent with the [coding style](CONTRIBUTING.md#coding-style), and format your code accordingly. -- Run [unit tests](CONTRIBUTING.md#run-unit-test-with-code-coverage) and check your code coverage rate. - -## What contributions can I make? - -Contributions to Milvus fall into the following categories. - -1. To report a bug or a problem with documentation, please file an [issue](https://github.com/milvus-io/milvus/issues/new/choose) providing the details of the problem. If you believe the issue needs priority attention, please comment on the issue to notify the team. -2. To propose a new feature, please file a new feature request [issue](https://github.com/milvus-io/milvus/issues/new/choose). Describe the intended feature and discuss the design and implementation with the team and community. Once the team agrees that the plan looks good, go ahead and implement it, following the [Contributing code](CONTRIBUTING.md#contributing-code). -3. To implement a feature or bug-fix for an existing outstanding issue, follow the [Contributing code](CONTRIBUTING.md#contributing-code). If you need more context on a particular issue, comment on the issue to let people know. - -## How can I contribute? - -### Contributing code - -If you have improvements to Milvus, send us your pull requests! For those just getting started, see [GitHub workflow](#github-workflow). Make sure to refer to the related issue in your pull request's comment and update [CHANGELOG.md](CHANGELOG.md). - -The Milvus team members will review your pull requests, and once it is accepted, the status of the projects to which it is associated will be changed to **Reviewer approved**. This means we are working on submitting your pull request to the internal repository. After the change has been submitted internally, your pull request will be merged automatically on GitHub. - -### GitHub workflow - -Please create a new branch from an up-to-date master on your fork. - -1. Fork the repository on GitHub. -2. Clone your fork to your local machine with `git clone git@github.com:/milvus-io/milvus.git`. -3. Create a branch with `git checkout -b my-topic-branch`. -4. Make your changes, commit, then push to to GitHub with `git push --set-upstream origin my-topic-branch`. You must record your changes in [CHANGELOG.md](CHANGELOG.md) with issue numbers and descriptions. -5. Visit GitHub and make your pull request. - -If you have an existing local repository, please update it before you start, to minimize the chance of merge conflicts. - -```shell -git remote add upstream git@github.com:milvus-io/milvus.git -git checkout master -git pull upstream master -git checkout -b my-topic-branch -``` - -### General guidelines - -Before sending your pull requests for review, make sure your changes are consistent with the guidelines and follow the Milvus coding style. - -- Include unit tests when you contribute new features, as they help to prove that your code works correctly, and also guard against future breaking changes to lower the maintenance cost. -- Bug fixes also require unit tests, because the presence of bugs usually indicates insufficient test coverage. -- Keep API compatibility in mind when you change code in Milvus. Reviewers of your pull request will comment on any API compatibility issues. -- When you contribute a new feature to Milvus, the maintenance burden is (by default) transferred to the Milvus team. This means that the benefit of the contribution must be compared against the cost of maintaining the feature. - -### Developer Certificate of Origin (DCO) - -All contributions to this project must be accompanied by acknowledgment of, and agreement to, the [Developer Certificate of Origin](https://developercertificate.org/). Acknowledgment of and agreement to the Developer Certificate of Origin _must_ be included in the comment section of each contribution and _must_ take the form of `Signed-off-by: {{Full Name}} <{{email address}}>` (without the `{}`). Contributions without this acknowledgment will be required to add it before being accepted. If contributors are unable or unwilling to agree to the Developer Certificate of Origin, their contribution will not be included. - -Contributors sign-off that they adhere to DCO by adding the following Signed-off-by line to commit messages: - -```text -This is my commit message - -Signed-off-by: Random J Developer -``` - -Git also has a `-s` command line option to append this automatically to your commit message: - -```shell -$ git commit -s -m 'This is my commit message' -``` - -## Coding Style -The coding style used in Milvus generally follow [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html). -And we made the following changes based on the guide: - -- 4 spaces for indentation -- Adopt .cpp file extension instead of .cc extension -- 120-character line length -- Camel-Cased file names - -### Format code - -Install clang-format -```shell -$ sudo apt-get install clang-format -$ rm cmake_build/CMakeCache.txt -``` -Check code style -```shell -$ ./build.sh -l -``` -To format the code -```shell -$ cd cmake_build -$ make clang-format -``` - -## Run unit test with code coverage - -Before submitting your PR, make sure you have run unit test, and your code coverage rate is >= 90%. - -Install lcov -```shell -$ sudo apt-get install lcov -``` -Run unit test and generate code for code coverage check -```shell -$ ./build.sh -u -c -``` - -Run MySQL docker -```shell -docker pull mysql:latest -docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:latest -``` - -Run code coverage - -```shell -$ ./coverage.sh -u root -p 123456 -t 127.0.0.1 -``` - -Or start your own MySQL server, and then run code coverage - -```shell -$ ./coverage.sh -u ${MYSQL_USERNAME} -p ${MYSQL_PASSWORD} -t ${MYSQL_SERVER_IP} -``` - diff --git a/DESIGN.md b/DESIGN.md deleted file mode 100644 index a1d7e2736e..0000000000 --- a/DESIGN.md +++ /dev/null @@ -1,13 +0,0 @@ -# Milvus Design Documents - -If you would like to propose a new feature for Milvus, it is recommended that you submit a design document following the [design document template](https://docs.google.com/document/d/1KhWs9b53K6WQUZ_FGWhIaeTraaSqsLQF7v2v68sAh_c/edit?usp=sharing). - -The following list contains existing design documents for Milvus. - -- [Support DSL interface](https://docs.google.com/document/d/1U83LY36TyaG3WD67Q9HWg9saD3qQcz9BfMcEScgwQPM/edit?usp=sharing) -- [Support delete/search by ID, attribute filtering, ID de-duplication](https://docs.google.com/document/d/1CDKdTj_DnE90YaZrPgsMaphqOTkMdbKETNrsFKj_Bco/edit?usp=sharing) -- [Support write-ahead logging](https://docs.google.com/document/d/12N8RC_wJb2dvEKY9jrlh8hU_eH8jxQVBewoPuHNqcXE/edit?usp=sharing) -- [Support in-service config modification](https://docs.google.com/document/d/1pK1joWJgAHM5nVp3q005iLbLqU5bn9InWeBy0mRAoSg/edit?usp=sharing) -- [Support Multi-Storage](https://docs.google.com/document/d/1iwwLH4Jtm3OXIVb7jFYsfmcbOyX6AWZKaNJAaXC7-cw/edit?usp=sharing) -- [Support AVX-512](https://docs.google.com/document/d/1do6_JgRCYdcV95sTPE6rLoiBK8wAcZki5Ypp7jbgqK0/edit?usp=sharing) -- [Refactor Knowhere](https://docs.google.com/document/d/1HY27EXV4UjJhDEmJ9t4Rjh7I1sB8iJHvqvliM6HHLS8/edit?usp=sharing) diff --git a/INSTALL.md b/INSTALL.md deleted file mode 100644 index 1e3d8b06a2..0000000000 --- a/INSTALL.md +++ /dev/null @@ -1,282 +0,0 @@ -# Install Milvus from Source Code - - - -- [Build from source](#build-from-source) - - - [Requirements](#requirements) - - - [Compilation](#compilation) - - - [Launch Milvus server](#launch-milvus-server) - -- [Compile Milvus on Docker](#compile-milvus-on-docker) - - - [Step 1 Pull Milvus Docker images](#step-1-pull-milvus-docker-images) - - - [Step 2 Start the Docker container](#step-2-start-the-docker-container) - - - [Step 3 Download Milvus source code](#step-3-download-milvus-source-code) - - - [Step 4 Compile Milvus in the container](#step-4-compile-milvus-in-the-container) - -- [Troubleshooting](#troubleshooting) - - - [Error message: `protocol https not supported or disabled in libcurl`](#error-message-protocol-https-not-supported-or-disabled-in-libcurl) - - - [Error message: `internal compiler error`](#error-message-internal-compiler-error) - - - [Error message: `error while loading shared libraries: libmysqlpp.so.3`](#error-message-error-while-loading-shared-libraries-libmysqlppso3) - - - [CMake version is not supported](#cmake-version-is-not-supported) - - - -## Build from source - -### Requirements - -- Operating system - - - Ubuntu 18.04 or higher - - - CentOS 7 - - > Note: If your Linux operating system does not meet the requirements, we recommend that you pull a Docker image of [Ubuntu 18.04](https://docs.docker.com/install/linux/docker-ce/ubuntu/) or [CentOS 7](https://docs.docker.com/install/linux/docker-ce/centos/) as your compilation environment. - -- GCC 7.0 or higher to support C++ 17 - -- CMake 3.12 or higher - -- Git - -For GPU-enabled version, you will also need: - -- CUDA 10.0 or higher - -- NVIDIA driver 418 or higher - -### Compilation - -#### Step 1 Install dependencies - -##### Install in Ubuntu - -```shell -$ cd [Milvus root path]/core -$ ./ubuntu_build_deps.sh -``` - -##### Install in CentOS - -```shell -$ cd [Milvus root path]/core -$ ./centos7_build_deps.sh -``` - -#### Step 2 Build - -```shell -$ cd [Milvus root path]/core -$ ./build.sh -t Debug -``` - -or - -```shell -$ ./build.sh -t Release -``` - -By default, it will build CPU-only version. To build GPU version, add `-g` option. - -```shell -$ ./build.sh -g -``` - -If you want to know the complete build options, run the following command. - -```shell -$./build.sh -h -``` - -When the build is completed, everything that you need in order to run Milvus will be installed under `[Milvus root path]/core/milvus`. - -### Launch Milvus server - -```shell -$ cd [Milvus root path]/core/milvus -``` - -Add `lib/` directory to `LD_LIBRARY_PATH` - -```shell -$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:[Milvus root path]/core/milvus/lib -``` - -Then start Milvus server: - -```shell -$ cd scripts -$ ./start_server.sh -``` - -To stop Milvus server, run: - -```shell -$ ./stop_server.sh -``` - -## Compile Milvus on Docker - -With the following Docker images, you should be able to compile Milvus on any Linux platform that runs Docker. To build a GPU supported Milvus, you need to install [NVIDIA Docker](https://github.com/NVIDIA/nvidia-docker/) first. - -### Step 1 Pull Milvus Docker images - -Pull CPU-only image: - -```shell -$ docker pull milvusdb/milvus-cpu-build-env:latest -``` - -Pull GPU-enabled image: - -```shell -$ docker pull milvusdb/milvus-gpu-build-env:latest -``` -### Step 2 Start the Docker container - -Start a CPU-only container: - -```shell -$ docker run -it -p 19530:19530 -d milvusdb/milvus-cpu-build-env:latest -``` - -Start a GPU container: - -- For nvidia docker 2: - -```shell -$ docker run --runtime=nvidia -it -p 19530:19530 -d milvusdb/milvus-gpu-build-env:latest -``` - -- For nvidia container toolkit: - -```shell -docker run --gpus all -it -p 19530:19530 -d milvusdb/milvus-gpu-build-env:latest -``` - -To enter the container: - -```shell -$ docker exec -it [container_id] bash -``` - -### Step 3 Download Milvus source code - -Download latest Milvus source code: - -```shell -$ cd /home -$ git clone https://github.com/milvus-io/milvus -``` - -To enter its core directory: - -```shell -$ cd ./milvus/core -``` - -### Step 4 Compile Milvus in the container - -If you are using a CPU-only image: - -1. run `build.sh`: - -```shell -$ ./build.sh -t Release -``` - -2. Start Milvus server: - -```shell -$ ./start_server.sh -``` - -If you are using a GPU-enabled image: - -1. Add cuda library path to `LD_LIBRARY_PATH`: - -```shell -$ export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH -``` - -2. Add cuda binary path to `PATH`: - -```shell -$ export PATH=/usr/local/cuda/bin:$PATH -``` - -3. Add a `-g` parameter to run `build.sh`: - -```shell -$ ./build.sh -g -t Release -``` - -4. Start Milvus server: - -```shell -$ ./start_server.sh -``` - -## Troubleshooting - -### Error message: `protocol https not supported or disabled in libcurl` - -Follow the steps below to solve this problem: - -1. Make sure you have `libcurl4-openssl-dev` installed in your system. -2. Try reinstalling the latest CMake from source with `--system-curl` option: - - ```shell - $ ./bootstrap --system-curl - $ make - $ sudo make install - ``` - - If the `--system-curl` command doesn't work, you can also reinstall CMake in **Ubuntu Software** on your local computer. - -### Error message: `internal compiler error` - -Try increasing the memory allocated to Docker. If this doesn't work, you can reduce the number of threads in CMake build in `[Milvus root path]/core/build.sh`. - -```shell -make -j 8 install || exit 1 # The default number of threads is 8. -``` - -Note: You might also need to configure CMake build for faiss in `[Milvus root path]/core/src/index/thirdparty/faiss`. - -### Error message: `error while loading shared libraries: libmysqlpp.so.3` - -Follow the steps below to solve this problem: - -1. Check whether `libmysqlpp.so.3` is correctly installed. -2. If `libmysqlpp.so.3` is installed, check whether it is added to `LD_LIBRARY_PATH`. - -### CMake version is not supported - -Follow the steps below to install a supported version of CMake: - -1. Remove the unsupported version of CMake. -2. Get CMake 3.12 or higher. Here we get CMake 3.12. - - ```shell - $ wget https://cmake.org/files/v3.12/cmake-3.12.2-Linux-x86_64.tar.gz - ``` - -3. Extract the file and install CMake. - - ```shell - $ tar zxvf cmake-3.12.2-Linux-x86_64.tar.gz - $ mv cmake-3.12.2-Linux-x86_64 /opt/cmake-3.12.2 - $ ln -sf /opt/cmake-3.12.2/bin/* /usr/bin/ - ``` diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 261eeb9e9f..0000000000 --- a/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed 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 - limitations under the License. diff --git a/NOTICE.md b/NOTICE.md deleted file mode 100644 index b7cf8653e6..0000000000 --- a/NOTICE.md +++ /dev/null @@ -1,24 +0,0 @@ -| Name | License | -| ---------------- | -------------------------------------------------------------------------------------------------------------------------------| -| Boost | [Boost Software License](https://github.com/boostorg/boost/blob/master/LICENSE_1_0.txt) | -| FAISS | [MIT](https://github.com/facebookresearch/faiss/blob/master/LICENSE) | -| Gtest | [BSD 3-Clause](https://github.com/google/googletest/blob/master/LICENSE) | -| LAPACK | [LAPACK](https://github.com/Reference-LAPACK/lapack/blob/master/LICENSE) | -| MySQLPP | [LGPL 2.1](https://tangentsoft.com/mysqlpp/artifact/b128a66dab867923) | -| OpenBLAS | [BSD 3-Clause](https://github.com/xianyi/OpenBLAS/blob/develop/LICENSE) | -| Prometheus | [Apache 2.0](https://github.com/prometheus/prometheus/blob/master/LICENSE) | -| SQLite | [Public Domain](https://www.sqlite.org/copyright.html) | -| SQLite-ORM | [BSD 3-Clause](https://github.com/fnc12/sqlite_orm/blob/master/LICENSE) | -| yaml-cpp | [MIT](https://github.com/jbeder/yaml-cpp/blob/master/LICENSE) | -| ZLIB | [ZLIB](http://zlib.net/zlib_license.html) | -| libunwind | [MIT](https://github.com/libunwind/libunwind/blob/master/LICENSE) | -| gperftools | [BSD 3-Clause](https://github.com/gperftools/gperftools/blob/master/COPYING) | -| grpc | [Apache 2.0](https://github.com/grpc/grpc/blob/master/LICENSE) | -| EASYLOGGINGPP | [MIT](https://github.com/zuhd-org/easyloggingpp/blob/master/LICENSE) | -| Json | [MIT](https://github.com/nlohmann/json/blob/develop/LICENSE.MIT) | -| opentracing-cpp | [Apache 2.0](https://github.com/opentracing/opentracing-cpp/blob/master/LICENSE) | -| libfiu | [BOLA](https://github.com/albertito/libfiu/blob/master/LICENSE) | -| aws-sdk-cpp | [Apache 2.0](https://github.com/aws/aws-sdk-cpp/blob/master/LICENSE) | -| SPTAG | [MIT](https://github.com/microsoft/SPTAG/blob/master/LICENSE) | -| hnswlib | [Apache 2.0](https://github.com/nmslib/hnswlib/blob/master/LICENSE) | -| annoy | [Apache 2.0](https://github.com/spotify/annoy/blob/master/LICENSE) | diff --git a/OSSMETADATA b/OSSMETADATA deleted file mode 100644 index aa07431d06..0000000000 --- a/OSSMETADATA +++ /dev/null @@ -1,2 +0,0 @@ -osslifecycle=active - diff --git a/README.md b/README.md deleted file mode 100644 index 9f42cce522..0000000000 --- a/README.md +++ /dev/null @@ -1,92 +0,0 @@ -![Milvuslogo](https://github.com/milvus-io/docs/blob/master/assets/milvus_logo.png) - -[![Slack](https://img.shields.io/badge/Join-Slack-orange)](https://join.slack.com/t/milvusio/shared_invite/zt-e0u4qu3k-bI2GDNys3ZqX1YCJ9OM~GQ) -![GitHub](https://img.shields.io/github/license/milvus-io/milvus) -![Docker pulls](https://img.shields.io/docker/pulls/milvusdb/milvus) - -[![Build Status](http://internal.zilliz.com:18080/jenkins/job/milvus-ci/job/master/badge/icon)](http://internal.zilliz.com:18080/jenkins/job/milvus-ci/job/master/) -[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/3563/badge)](https://bestpractices.coreinfrastructure.org/projects/3563) -[![codecov](https://codecov.io/gh/milvus-io/milvus/branch/master/graph/badge.svg)](https://codecov.io/gh/milvus-io/milvus) -[![codebeat badge](https://codebeat.co/badges/e030a4f6-b126-4475-a938-4723d54ec3a7?style=plastic)](https://codebeat.co/projects/github-com-milvus-io-milvus-master) -[![CodeFactor Grade](https://www.codefactor.io/repository/github/milvus-io/milvus/badge)](https://www.codefactor.io/repository/github/milvus-io/milvus) -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/c4bb2ccfb51b47f99e43bfd1705edd95)](https://app.codacy.com/gh/milvus-io/milvus?utm_source=github.com&utm_medium=referral&utm_content=milvus-io/milvus&utm_campaign=Badge_Grade_Dashboard) - -English | [中文版](README_CN.md) - -## What is Milvus - -As an open source vector similarity search engine, Milvus is easy-to-use, highly reliable, scalable, robust, and blazing fast. Adopted by over 100 organizations and institutions worldwide, Milvus empowers applications in a variety of fields, including image processing, computer vision, natural language processing, voice recognition, recommender systems, drug discovery, etc. - -Milvus has the following architecture: - -![arch](https://github.com/milvus-io/docs/blob/v0.7.1/assets/milvus_arch.png) - -For more detailed introduction of Milvus and its architecture, see [Milvus overview](https://www.milvus.io/docs/about_milvus/overview.md). Keep up-to-date with newest releases and latest updates by reading Milvus [release notes](https://www.milvus.io/docs/releases/release_notes.md). - -Milvus is an [LF AI Foundation](https://lfai.foundation/) incubation project. Learn more at [lfai.foundation](https://lfai.foundation/). - -## Get started - -### Install Milvus - -See the [Milvus install guide](https://www.milvus.io/docs/guides/get_started/install_milvus/install_milvus.md) to install Milvus using Docker. To install Milvus from source code, see [build from source](INSTALL.md). - -### Try example programs - -Try an example program with Milvus using [Python](https://www.milvus.io/docs/guides/get_started/example_code.md), [Java](https://github.com/milvus-io/milvus-sdk-java/tree/master/examples), [Go](https://github.com/milvus-io/milvus-sdk-go/tree/master/examples), or [C++ example code](https://github.com/milvus-io/milvus/tree/master/sdk/examples). - -## Supported clients - -- [Go](https://github.com/milvus-io/milvus-sdk-go) -- [Python](https://github.com/milvus-io/pymilvus) -- [Java](https://github.com/milvus-io/milvus-sdk-java) -- [C++](https://github.com/milvus-io/milvus/tree/master/sdk) -- [RESTful API](https://github.com/milvus-io/milvus/tree/master/core/src/server/web_impl) -- [Node.js](https://www.npmjs.com/package/@arkie-ai/milvus-client) (Provided by [arkie](https://www.arkie.cn/)) - -## Application scenarios - -You can use Milvus to build intelligent systems in a variety of AI application scenarios. Refer to [Milvus Scenarios](https://milvus.io/scenarios) for live demos. You can also refer to [Milvus Bootcamp](https://github.com/milvus-io/bootcamp) for detailed solutions and application scenarios. - -## Benchmark - -See our [test reports](https://github.com/milvus-io/milvus/tree/master/docs) for more information about performance benchmarking of different indexes in Milvus. - -## Roadmap - -To learn what's coming up soon in Milvus, read our [Roadmap](https://github.com/milvus-io/milvus/projects). - -It is a Work in Progress, and is subject to reasonable adjustments when necessary. And we greatly welcome any comments/requirements/suggestions regarding Milvus roadmap.:clap: - -## Contribution guidelines - -Contributions are welcomed and greatly appreciated. Please read our [contribution guidelines](CONTRIBUTING.md) for detailed contribution workflow. This project adheres to the [code of conduct](CODE_OF_CONDUCT.md) of Milvus. By participating, you are expected to uphold this code. - -We use [GitHub issues](https://github.com/milvus-io/milvus/issues) to track issues and bugs. For general questions and public discussions, please join our community. - -## Join our community - -:heart:To connect with other users and contributors, welcome to join our [Slack channel](https://join.slack.com/t/milvusio/shared_invite/zt-e0u4qu3k-bI2GDNys3ZqX1YCJ9OM~GQ). - -See our [community](https://github.com/milvus-io/community) repository to learn about our governance and access more community resources. - - -## Resources - -- [Milvus.io](https://www.milvus.io) - -- [Milvus FAQ](https://www.milvus.io/docs/faq/operational_faq.md) - -- [Milvus Medium](https://medium.com/@milvusio) - -- [Milvus CSDN](https://zilliz.blog.csdn.net/) - -- [Milvus Twitter](https://twitter.com/milvusio) - -- [Milvus Facebook](https://www.facebook.com/io.milvus.5) - -- [Milvus design docs](DESIGN.md) - -## License - -[Apache License 2.0](LICENSE) diff --git a/README_CN.md b/README_CN.md deleted file mode 100644 index bbdad455bb..0000000000 --- a/README_CN.md +++ /dev/null @@ -1,95 +0,0 @@ -![Milvuslogo](https://github.com/milvus-io/docs/blob/master/assets/milvus_logo.png) - -[![Slack](https://img.shields.io/badge/Join-Slack-orange)](https://join.slack.com/t/milvusio/shared_invite/zt-e0u4qu3k-bI2GDNys3ZqX1YCJ9OM~GQ) - -![GitHub](https://img.shields.io/github/license/milvus-io/milvus) -![Docker pulls](https://img.shields.io/docker/pulls/milvusdb/milvus) - -[![Build Status](http://internal.zilliz.com:18080/jenkins/job/milvus-ci/job/master/badge/icon)](http://internal.zilliz.com:18080/jenkins/job/milvus-ci/job/master/) -[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/3563/badge)](https://bestpractices.coreinfrastructure.org/projects/3563) -[![codecov](https://codecov.io/gh/milvus-io/milvus/branch/master/graph/badge.svg)](https://codecov.io/gh/milvus-io/milvus) -[![codebeat badge](https://codebeat.co/badges/e030a4f6-b126-4475-a938-4723d54ec3a7?style=plastic)](https://codebeat.co/projects/github-com-milvus-io-milvus-master) -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/c4bb2ccfb51b47f99e43bfd1705edd95)](https://app.codacy.com/gh/milvus-io/milvus?utm_source=github.com&utm_medium=referral&utm_content=milvus-io/milvus&utm_campaign=Badge_Grade_Dashboard) - -[English](README.md) | 中文版 - -# 欢迎来到 Milvus - -## Milvus 是什么 - -Milvus 是一款开源的特征向量相似度搜索引擎,具有使用方便、实用可靠、易于扩展、稳定高效和搜索迅速等特点,在全球范围内被上百家组织和机构所采用。Milvus 已经被广泛应用于多个领域,其中包括图像处理、机器视觉、自然语言处理、语音识别、推荐系统以及新药发现等。 - -Milvus 的架构如下: - -![arch](https://github.com/milvus-io/docs/raw/v0.7.1/assets/milvus_arch.png) - -若要了解 Milvus 详细介绍和整体架构,请访问 [Milvus 简介](https://www.milvus.io/cn/docs/about_milvus/overview.md)。您可以通过 [版本发布说明](https://www.milvus.io/cn/docs/releases/release_notes.md) 获取最新版本的功能和更新。 - -Milvus是一个[LF AI基金会](https://lfai.foundation/)的孵化项目。获取更多,请访问[lfai.foundation](https://lfai.foundation/)。 - -## Milvus 快速上手 - -### 安装 Milvus - -请参阅 [Milvus 安装指南](https://www.milvus.io/cn/docs/guides/get_started/install_milvus/install_milvus.md) 使用 Docker 容器安装 Milvus。若要基于源码编译,请访问 [源码安装](INSTALL.md)。 - -### 尝试示例代码 - -您可以尝试用 [Python](https://www.milvus.io/cn/docs/guides/get_started/example_code.md),[Java](https://github.com/milvus-io/milvus-sdk-java/tree/master/examples),[Go](https://github.com/milvus-io/milvus-sdk-go/tree/master/examples),或者 [C++](https://github.com/milvus-io/milvus/tree/master/sdk/examples) 运行 Milvus 示例代码。 - -## 支持的客户端 - -- [Go](https://github.com/milvus-io/milvus-sdk-go) -- [Python](https://github.com/milvus-io/pymilvus) -- [Java](https://github.com/milvus-io/milvus-sdk-java) -- [C++](https://github.com/milvus-io/milvus/tree/master/sdk) -- [RESTful API](https://github.com/milvus-io/milvus/tree/master/core/src/server/web_impl) -- [Node.js](https://www.npmjs.com/package/@arkie-ai/milvus-client) (由 [arkie](https://www.arkie.cn/) 提供) - -## 应用场景 - -Milvus 可以应用于多种 AI 场景。您可以访问 [Milvus 应用场景](https://milvus.io/scenarios) 体验在线场景展示。您也可以访问 [Milvus 训练营](https://github.com/milvus-io/bootcamp) 了解更详细的应用场景和解决方案。 - -## 性能基准测试 - -关于 Milvus 性能基准的更多信息,请参考[测试报告](https://github.com/milvus-io/milvus/tree/master/docs)。 - -## 路线图 - -您可以参考我们的[路线图](https://github.com/milvus-io/milvus/projects),了解 Milvus 即将实现的新特性。 - -路线图尚未完成,并且可能会存在合理改动。我们欢迎各种针对路线图的意见、需求和建议。 - -## 贡献者指南 - -我们由衷欢迎您推送贡献。关于贡献流程的详细信息,请参阅[贡献者指南](https://github.com/milvus-io/milvus/blob/master/CONTRIBUTING.md)。本项目遵循 Milvus [行为准则](https://github.com/milvus-io/milvus/blob/master/CODE_OF_CONDUCT.md)。如果您希望参与本项目,请遵守该准则的内容。 - -我们使用 [GitHub issues](https://github.com/milvus-io/milvus/issues) 追踪问题和补丁。若您希望提出问题或进行讨论,请加入我们的社区。 - -## 加入 Milvus 社区 - -欢迎加入我们的 [Slack 频道](https://join.slack.com/t/milvusio/shared_invite/zt-e0u4qu3k-bI2GDNys3ZqX1YCJ9OM~GQ)以便与其他用户和贡献者进行交流。 - -## 加入 Milvus 技术交流微信群 - -![qrcode](https://github.com/milvus-io/docs/blob/v0.7.0/assets/qrcode.png) - -## 相关链接 - -- [Milvus.io](https://www.milvus.io) - -- [Milvus 常见问题](https://www.milvus.io/cn/docs/faq/operational_faq.md) - -- [Milvus Medium](https://medium.com/@milvusio) - -- [Milvus CSDN](https://zilliz.blog.csdn.net/) - -- [Milvus Twitter](https://twitter.com/milvusio) - -- [Milvus Facebook](https://www.facebook.com/io.milvus.5) - -- [Milvus 设计文档](DESIGN.md) - -## 许可协议 - -[Apache 许可协议 2.0 版](https://github.com/milvus-io/milvus/blob/master/LICENSE) diff --git a/RELEASE.md b/RELEASE.md deleted file mode 100644 index 35f059d3d3..0000000000 --- a/RELEASE.md +++ /dev/null @@ -1,17 +0,0 @@ -# Milvus Release Methodology and Criterias - -## Release methodology - -Milvus releases are packages that have been approved for general public release, with varying degrees of caveat regarding their perceived quality or potential for change. -They are stable releases intended for everyday usage by developers and non-developers. - -Project versioning follows the specification of [Semantic Versioning 2.0.0](https://semver.org/). - -## Release criteria - -- Milvus core test code coverage must be at least 90%. -- Reported bugs should not have any critical issues. -- All bugs, new features, enhancements must be tested. -- All documents need to be reviewed with no broken link. -- Pressure testing, stability testing, accuracy testing and performance testing results should be evaluated. - diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 5dd59cbf03..0000000000 --- a/SECURITY.md +++ /dev/null @@ -1,15 +0,0 @@ -# Security Policy - -## Supported versions - -The following versions of Milvus are currently being supported with security updates. - -| Version | Supported | -| --------- | ------------------ | -| 0.6.0 | ✔️ | -| <= 0.5.3 | :x: | - -## Reporting a vulnerability - -To report a security vulnerability, please reach out to the Milvus team via . - diff --git a/SUPPORT.md b/SUPPORT.md deleted file mode 100644 index b1951f309b..0000000000 --- a/SUPPORT.md +++ /dev/null @@ -1,14 +0,0 @@ -# Support for deploying and using Milvus - -We use GitHub for tracking bugs and feature requests. If you need any support for using Milvus, please refer to the following resources below. - -## Documentation -- [User Documentation](https://www.milvus.io/docs/guides/get_started/install_milvus/install_milvus.md) -- [Troubleshooting Guide](https://www.milvus.io/docs/v0.6.0/guides/troubleshoot.md) -- [FAQ](https://www.milvus.io/docs/v0.6.0/faq/operational_faq.md) - -## Real-time chat -[Slack](https://join.slack.com/t/milvusio/shared_invite/enQtNzY1OTQ0NDI3NjMzLWNmYmM1NmNjOTQ5MGI5NDhhYmRhMGU5M2NhNzhhMDMzY2MzNDdlYjM5ODQ5MmE3ODFlYzU3YjJkNmVlNDQ2ZTk): The #general channel is the place where people offer support. - -## Other -[Bootcamp](https://github.com/milvus-io/bootcamp): It provides more scenario-based applications and demos of Milvus. diff --git a/ci/docker/centos-7-core.dockerfile b/ci/docker/centos-7-core.dockerfile deleted file mode 100644 index 6b97217b26..0000000000 --- a/ci/docker/centos-7-core.dockerfile +++ /dev/null @@ -1,33 +0,0 @@ -ARG arch=amd64 -FROM ${arch}/centos:7 - -# pipefail is enabled for proper error detection in the `wget` -# step -SHELL ["/bin/bash", "-o", "pipefail", "-c"] - -RUN yum install -y epel-release centos-release-scl-rh && yum install -y wget curl which && \ - wget -qO- "https://cmake.org/files/v3.14/cmake-3.14.3-Linux-x86_64.tar.gz" | tar --strip-components=1 -xz -C /usr/local && \ - yum install -y make automake git python3-pip libcurl-devel python3-devel boost-static mysql-devel \ - devtoolset-7-gcc devtoolset-7-gcc-c++ devtoolset-7-gcc-gfortran llvm-toolset-7.0-clang llvm-toolset-7.0-clang-tools-extra \ - mysql lcov && \ - rm -rf /var/cache/yum/* && \ - echo "source scl_source enable devtoolset-7" >> /etc/profile.d/devtoolset-7.sh && \ - echo "source scl_source enable llvm-toolset-7.0" >> /etc/profile.d/llvm-toolset-7.sh - -ENV CLANG_TOOLS_PATH="/opt/rh/llvm-toolset-7.0/root/usr/bin" - -RUN source /etc/profile.d/devtoolset-7.sh && \ - wget https://github.com/xianyi/OpenBLAS/archive/v0.3.9.tar.gz && \ - tar zxvf v0.3.9.tar.gz && cd OpenBLAS-0.3.9 && \ - make TARGET=CORE2 DYNAMIC_ARCH=1 DYNAMIC_OLDER=1 USE_THREAD=0 USE_OPENMP=0 FC=gfortran CC=gcc COMMON_OPT="-O3 -g -fPIC" FCOMMON_OPT="-O3 -g -fPIC -frecursive" NMAX="NUM_THREADS=128" LIBPREFIX="libopenblas" LAPACKE="NO_LAPACKE=1" INTERFACE64=0 NO_STATIC=1 && \ - make PREFIX=/usr install && \ - cd .. && rm -rf OpenBLAS-0.3.9 && rm v0.3.9.tar.gz - -RUN yum install -y ccache && \ - rm -rf /var/cache/yum/* - -# use login shell to activate environment un the RUN commands -SHELL [ "/bin/bash", "-c", "-l" ] - -# use login shell when running the container -ENTRYPOINT [ "/bin/bash", "-c", "-l" ] diff --git a/ci/docker/ubuntu-18.04-core.dockerfile b/ci/docker/ubuntu-18.04-core.dockerfile deleted file mode 100644 index a034674675..0000000000 --- a/ci/docker/ubuntu-18.04-core.dockerfile +++ /dev/null @@ -1,43 +0,0 @@ -ARG arch=amd64 -FROM ${arch}/ubuntu:18.04 - -# pipefail is enabled for proper error detection in the `wget | apt-key add` -# step -SHELL ["/bin/bash", "-o", "pipefail", "-c"] - -ENV DEBIAN_FRONTEND noninteractive - -RUN apt-get update && apt-get install -y --no-install-recommends wget ca-certificates gnupg2 && \ - wget -P /tmp https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2019.PUB && \ - apt-key add /tmp/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2019.PUB && \ - sh -c 'echo deb https://apt.repos.intel.com/mkl all main > /etc/apt/sources.list.d/intel-mkl.list' && \ - wget -qO- "https://cmake.org/files/v3.14/cmake-3.14.3-Linux-x86_64.tar.gz" | tar --strip-components=1 -xz -C /usr/local && \ - apt-get update && apt-get install -y --no-install-recommends \ - g++ git gfortran lsb-core \ - libboost-serialization-dev libboost-filesystem-dev libboost-system-dev libboost-regex-dev \ - curl libtool automake libssl-dev pkg-config libcurl4-openssl-dev python3-pip \ - clang-format-6.0 clang-tidy-6.0 \ - lcov mysql-client libmysqlclient-dev intel-mkl-gnu-2019.5-281 intel-mkl-core-2019.5-281 && \ - apt-get remove --purge -y && \ - rm -rf /var/lib/apt/lists/* - -RUN ln -s /usr/lib/x86_64-linux-gnu/libmysqlclient.so \ - /usr/lib/x86_64-linux-gnu/libmysqlclient_r.so - -RUN sh -c 'echo export LD_LIBRARY_PATH=/opt/intel/compilers_and_libraries_2019.5.281/linux/mkl/lib/intel64:\$LD_LIBRARY_PATH > /etc/profile.d/mkl.sh' - -RUN wget https://github.com/xianyi/OpenBLAS/archive/v0.3.9.tar.gz && \ - tar zxvf v0.3.9.tar.gz && cd OpenBLAS-0.3.9 && \ - make TARGET=CORE2 DYNAMIC_ARCH=1 DYNAMIC_OLDER=1 USE_THREAD=0 USE_OPENMP=0 FC=gfortran CC=gcc COMMON_OPT="-O3 -g -fPIC" FCOMMON_OPT="-O3 -g -fPIC -frecursive" NMAX="NUM_THREADS=128" LIBPREFIX="libopenblas" LAPACKE="NO_LAPACKE=1" INTERFACE64=0 NO_STATIC=1 && \ - make PREFIX=/usr install && \ - cd .. && rm -rf OpenBLAS-0.3.9 && rm v0.3.9.tar.gz - -RUN apt-get update && apt-get install -y --no-install-recommends ccache && \ - apt-get remove --purge -y && \ - rm -rf /var/lib/apt/lists/* - -# use login shell to activate environment un the RUN commands -SHELL [ "/bin/bash", "-c", "-l" ] - -# use login shell when running the container -ENTRYPOINT [ "/bin/bash", "-c", "-l" ] diff --git a/ci/jenkins/Jenkinsfile b/ci/jenkins/Jenkinsfile deleted file mode 100644 index 663fd8ea8d..0000000000 --- a/ci/jenkins/Jenkinsfile +++ /dev/null @@ -1,244 +0,0 @@ -#!/usr/bin/env groovy - -String cron_timezone = "TZ=Asia/Shanghai" -String cron_string = BRANCH_NAME == "0.10.3" ? "50 22 * * * " : "" - -pipeline { - agent none - - triggers { - cron """${cron_timezone} - ${cron_string}""" - } - - options { - timestamps() - } - - parameters{ - choice choices: ['Release', 'Debug'], description: 'Build Type', name: 'BUILD_TYPE' - string defaultValue: 'registry.zilliz.com', description: 'DOCKER REGISTRY URL', name: 'DOKCER_REGISTRY_URL', trim: true - string defaultValue: 'ba070c98-c8cc-4f7c-b657-897715f359fc', description: 'DOCKER CREDENTIALS ID', name: 'DOCKER_CREDENTIALS_ID', trim: true - string defaultValue: 'http://192.168.1.201/artifactory/milvus', description: 'JFROG ARTFACTORY URL', name: 'JFROG_ARTFACTORY_URL', trim: true - string defaultValue: '1a527823-d2b7-44fd-834b-9844350baf14', description: 'JFROG CREDENTIALS ID', name: 'JFROG_CREDENTIALS_ID', trim: true - } - - environment { - PROJECT_NAME = "milvus" - MILVUS_ROOT_PATH="/var/lib" - MILVUS_INSTALL_PREFIX="${env.MILVUS_ROOT_PATH}/${env.PROJECT_NAME}" - LOWER_BUILD_TYPE = params.BUILD_TYPE.toLowerCase() - SEMVER = "${BRANCH_NAME.contains('/') ? BRANCH_NAME.substring(BRANCH_NAME.lastIndexOf('/') + 1) : BRANCH_NAME}" - PIPELINE_NAME = "milvus-ci" - HELM_BRANCH = "0.10.1" - } - - stages { - stage ('Milvus CI') { - matrix { - agent none - axes { - axis { - name 'OS_NAME' - values 'ubuntu18.04', 'centos7' - } - - axis { - name 'CPU_ARCH' - values 'amd64' - } - - axis { - name 'BINARY_VERSION' - values 'gpu', 'cpu' - } - } - when { - not { - expression {return OS_NAME == "ubuntu18.04" && !isTimeTriggeredBuild()} - } - } - - environment { - PACKAGE_VERSION = VersionNumber([ - versionNumberString : '${SEMVER}-${BINARY_VERSION}-${OS_NAME}-${CPU_ARCH}-${LOWER_BUILD_TYPE}-${BUILD_DATE_FORMATTED, "yyyyMMdd"}-${BUILDS_TODAY}' - ]); - DOCKER_VERSION = "${SEMVER}-${BINARY_VERSION}-${OS_NAME}-${LOWER_BUILD_TYPE}" - } - - stages { - stage("Milvus Build and Unittest") { - agent { - kubernetes { - label "${OS_NAME}-${BINARY_VERSION}-build-${SEMVER}-${env.PIPELINE_NAME}-${env.BUILD_NUMBER}" - defaultContainer 'jnlp' - yamlFile "ci/jenkins/pod/milvus-${BINARY_VERSION}-version-${OS_NAME}-build-env-pod.yaml" - } - } - - stages { - stage('Build') { - steps { - container("milvus-${BINARY_VERSION}-build-env") { - script { - try{ - load "${env.WORKSPACE}/ci/jenkins/step/build.groovy" - } catch (Exception e) { - containerLog "milvus-${BINARY_VERSION}-build-env" - throw e - } - } - } - } - } - - stage('Unittest') { - steps { - container("milvus-${BINARY_VERSION}-build-env") { - script { - if ("${BINARY_VERSION}" == "gpu") { - load "${env.WORKSPACE}/ci/jenkins/step/unittest.groovy" - } else { - echo "Skip Unittest" - } - } - } - } - } - - stage('Code Coverage') { - steps { - container("milvus-${BINARY_VERSION}-build-env") { - script { - if ("${BINARY_VERSION}" == "gpu") { - load "${env.WORKSPACE}/ci/jenkins/step/coverage.groovy" - } else { - echo "Skip Code Coverage" - } - } - } - } - } - - stage('Upload Package') { - steps { - container("milvus-${BINARY_VERSION}-build-env") { - script { - load "${env.WORKSPACE}/ci/jenkins/step/package.groovy" - } - } - } - } - } - } - - stage('Publish Docker images') { - agent { - kubernetes { - label "${OS_NAME}-${BINARY_VERSION}-publish-${SEMVER}-${env.PIPELINE_NAME}-${env.BUILD_NUMBER}" - defaultContainer 'jnlp' - yamlFile 'ci/jenkins/pod/docker-pod.yaml' - } - } - - stages { - stage('Publish') { - steps { - container('publish-images') { - script { - load "${env.WORKSPACE}/ci/jenkins/step/publishImages.groovy" - } - } - } - } - } - } - - stage('Deploy to Development') { - environment { - FROMAT_SEMVER = "${env.SEMVER}".replaceAll("\\.", "-").replaceAll("_", "-") - FORMAT_OS_NAME = "${OS_NAME}".replaceAll("\\.", "-").replaceAll("_", "-") - HELM_RELEASE_NAME = "${env.PIPELINE_NAME}-${env.FROMAT_SEMVER}-${env.BUILD_NUMBER}-single-${FORMAT_OS_NAME}-${BINARY_VERSION}".toLowerCase() - SHARDS_HELM_RELEASE_NAME = "${env.PIPELINE_NAME}-${env.FROMAT_SEMVER}-${env.BUILD_NUMBER}-shards-${FORMAT_OS_NAME}-${BINARY_VERSION}".toLowerCase() - DEV_TEST_ARTIFACTS = "_artifacts/${FROMAT_SEMVER}/${FORMAT_OS_NAME}/${BINARY_VERSION}" - } - - agent { - kubernetes { - label "${OS_NAME}-${BINARY_VERSION}-dev-test-${SEMVER}-${env.PIPELINE_NAME}-${env.BUILD_NUMBER}" - defaultContainer 'jnlp' - yamlFile 'ci/jenkins/pod/testEnvironment.yaml' - } - } - - stages { - stage('Dev Test') { - steps { - container('milvus-test-env') { - script { - sh "mkdir -p ${env.DEV_TEST_ARTIFACTS}" - boolean isNightlyTest = isTimeTriggeredBuild() - if (isNightlyTest) { - load "${env.WORKSPACE}/ci/jenkins/step/singleDevNightlyTest.groovy" - load "${env.WORKSPACE}/ci/jenkins/step/shardsDevNightlyTest.groovy" - } else { - load "${env.WORKSPACE}/ci/jenkins/step/singleDevTest.groovy" - } - } - } - } - } - } - - post { - cleanup { - container('milvus-test-env') { - script { - archiveArtifacts artifacts: "${env.DEV_TEST_ARTIFACTS}/**", allowEmptyArchive: true - load "${env.WORKSPACE}/ci/jenkins/step/cleanupSingleDev.groovy" - boolean isNightlyTest = isTimeTriggeredBuild() - if (isNightlyTest) { - load "${env.WORKSPACE}/ci/jenkins/step/cleanupShardsDev.groovy" - } - } - } - } - } - } - } - } - } - } - post { - unsuccessful { - script { - boolean isNightlyTest = isTimeTriggeredBuild() - if (isNightlyTest) { - // Send an email only if the build status has changed from green/unstable to red - emailext subject: '$DEFAULT_SUBJECT', - body: '$DEFAULT_CONTENT', - recipientProviders: [ - [$class: 'DevelopersRecipientProvider'], - [$class: 'RequesterRecipientProvider'] - ], - replyTo: '$DEFAULT_REPLYTO', - to: 'dev.milvus@zilliz.com' - } - } - } - } -} - -boolean isEmptyChangelog() { - if (currentBuild.changeSets.size() == 0) { - return true - } - return false -} - -boolean isTimeTriggeredBuild() { - if (currentBuild.getBuildCauses('hudson.triggers.TimerTrigger$TimerTriggerCause').size() != 0) { - return true - } - return false -} diff --git a/ci/jenkins/pod/docker-pod.yaml b/ci/jenkins/pod/docker-pod.yaml deleted file mode 100644 index 4484d2de44..0000000000 --- a/ci/jenkins/pod/docker-pod.yaml +++ /dev/null @@ -1,34 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - labels: - app: publish - componet: docker -spec: - containers: - - name: publish-images - image: registry.zilliz.com/library/docker:v1.0.0 - securityContext: - privileged: true - command: - - cat - tty: true - resources: - limits: - memory: "8Gi" - cpu: "2" - requests: - memory: "2Gi" - cpu: "1" - volumeMounts: - - name: docker-sock - mountPath: /var/run/docker.sock - volumes: - - name: docker-sock - hostPath: - path: /var/run/docker.sock - tolerations: - - key: dedicated - operator: Equal - value: milvus - effect: NoSchedule diff --git a/ci/jenkins/pod/milvus-cpu-version-centos7-build-env-pod.yaml b/ci/jenkins/pod/milvus-cpu-version-centos7-build-env-pod.yaml deleted file mode 100644 index 31f1f2abd3..0000000000 --- a/ci/jenkins/pod/milvus-cpu-version-centos7-build-env-pod.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: milvus-cpu-build-env - labels: - app: milvus - componet: cpu-build-env -spec: - containers: - - name: milvus-cpu-build-env - image: registry.zilliz.com/milvus/milvus-cpu-build-env:v0.9.0-centos7 - env: - - name: POD_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: OS_NAME - value: "centos7" - - name: BUILD_ENV_IMAGE_ID - value: "f2386d84d312e42891c8c70219b12fde014c21fbdbc0e59bede7e7609b1ba58b" - command: - - cat - tty: true - resources: - limits: - memory: "14Gi" - cpu: "6.0" - requests: - memory: "8Gi" - cpu: "4.0" - - name: milvus-mysql - image: mysql:5.6 - env: - - name: MYSQL_ROOT_PASSWORD - value: 123456 - ports: - - containerPort: 3306 - name: mysql - resources: - limits: - memory: "500Mi" - cpu: "0.5" - requests: - memory: "200Mi" - cpu: "0.2" - tolerations: - - key: dedicated - operator: Equal - value: milvus - effect: NoSchedule diff --git a/ci/jenkins/pod/milvus-cpu-version-ubuntu18.04-build-env-pod.yaml b/ci/jenkins/pod/milvus-cpu-version-ubuntu18.04-build-env-pod.yaml deleted file mode 100644 index c7f57226bc..0000000000 --- a/ci/jenkins/pod/milvus-cpu-version-ubuntu18.04-build-env-pod.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: milvus-cpu-build-env - labels: - app: milvus - componet: cpu-build-env -spec: - containers: - - name: milvus-cpu-build-env - image: registry.zilliz.com/milvus/milvus-cpu-build-env:v0.9.0-ubuntu18.04 - env: - - name: POD_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: OS_NAME - value: "ubuntu18.04" - - name: BUILD_ENV_IMAGE_ID - value: "4719a06f1b77393fed7a4336058baab74745715a431193d3876e9b51262505bd" - command: - - cat - tty: true - resources: - limits: - memory: "14Gi" - cpu: "6.0" - requests: - memory: "8Gi" - cpu: "4.0" - - name: milvus-mysql - image: mysql:5.6 - env: - - name: MYSQL_ROOT_PASSWORD - value: 123456 - ports: - - containerPort: 3306 - name: mysql - resources: - limits: - memory: "500Mi" - cpu: "0.5" - requests: - memory: "200Mi" - cpu: "0.2" - tolerations: - - key: dedicated - operator: Equal - value: milvus - effect: NoSchedule diff --git a/ci/jenkins/pod/milvus-gpu-version-centos7-build-env-pod.yaml b/ci/jenkins/pod/milvus-gpu-version-centos7-build-env-pod.yaml deleted file mode 100644 index fc6bf831b0..0000000000 --- a/ci/jenkins/pod/milvus-gpu-version-centos7-build-env-pod.yaml +++ /dev/null @@ -1,52 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: milvus-gpu-build-env - labels: - app: milvus - componet: gpu-build-env -spec: - containers: - - name: milvus-gpu-build-env - image: registry.zilliz.com/milvus/milvus-gpu-build-env:v0.9.0-centos7 - env: - - name: POD_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: OS_NAME - value: "centos7" - - name: BUILD_ENV_IMAGE_ID - value: "7087442c4c5a7a7adbd7324c58b7b1ac19a25acfd86d6017b5752c4c6521f90e" - command: - - cat - tty: true - resources: - limits: - memory: "14Gi" - cpu: "6.0" - # nvidia.com/gpu: 1 - aliyun.com/gpu-mem: 2 # GiB - requests: - memory: "8Gi" - cpu: "4.0" - - name: milvus-mysql - image: mysql:5.6 - env: - - name: MYSQL_ROOT_PASSWORD - value: 123456 - ports: - - containerPort: 3306 - name: mysql - resources: - limits: - memory: "500Mi" - cpu: "0.5" - requests: - memory: "200Mi" - cpu: "0.2" - tolerations: - - key: dedicated - operator: Equal - value: milvus - effect: NoSchedule diff --git a/ci/jenkins/pod/milvus-gpu-version-ubuntu18.04-build-env-pod.yaml b/ci/jenkins/pod/milvus-gpu-version-ubuntu18.04-build-env-pod.yaml deleted file mode 100644 index d0e42a888f..0000000000 --- a/ci/jenkins/pod/milvus-gpu-version-ubuntu18.04-build-env-pod.yaml +++ /dev/null @@ -1,52 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: milvus-gpu-build-env - labels: - app: milvus - componet: gpu-build-env -spec: - containers: - - name: milvus-gpu-build-env - image: registry.zilliz.com/milvus/milvus-gpu-build-env:v0.9.0-ubuntu18.04 - env: - - name: POD_IP - valueFrom: - fieldRef: - fieldPath: status.podIP - - name: OS_NAME - value: "ubuntu18.04" - - name: BUILD_ENV_IMAGE_ID - value: "0aa65ebac377834ceb9644c320f114b97b488d11762948770b994f73e5ae518f" - command: - - cat - tty: true - resources: - limits: - memory: "14Gi" - cpu: "6.0" - # nvidia.com/gpu: 1 - aliyun.com/gpu-mem: 2 # GiB - requests: - memory: "8Gi" - cpu: "4.0" - - name: milvus-mysql - image: mysql:5.6 - env: - - name: MYSQL_ROOT_PASSWORD - value: 123456 - ports: - - containerPort: 3306 - name: mysql - resources: - limits: - memory: "500Mi" - cpu: "0.5" - requests: - memory: "200Mi" - cpu: "0.2" - tolerations: - - key: dedicated - operator: Equal - value: milvus - effect: NoSchedule diff --git a/ci/jenkins/pod/testEnvironment.yaml b/ci/jenkins/pod/testEnvironment.yaml deleted file mode 100644 index ff79299cbb..0000000000 --- a/ci/jenkins/pod/testEnvironment.yaml +++ /dev/null @@ -1,33 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - labels: - app: milvus - componet: test-env -spec: - containers: - - name: milvus-test-env - image: registry.zilliz.com/milvus/milvus-test-env:v0.2 - command: - - cat - tty: true - resources: - limits: - memory: "8Gi" - cpu: "4.0" - requests: - memory: "4Gi" - cpu: "2.0" - volumeMounts: - - name: kubeconf - mountPath: /root/.kube/ - readOnly: true - volumes: - - name: kubeconf - secret: - secretName: test-cluster-config - tolerations: - - key: dedicated - operator: Equal - value: milvus - effect: NoSchedule diff --git a/ci/jenkins/scripts/mail.py b/ci/jenkins/scripts/mail.py deleted file mode 100644 index 960a189833..0000000000 --- a/ci/jenkins/scripts/mail.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -import sys -import logging -from email.mime.text import MIMEText -from email.header import Header -import smtplib - -SMS_DEFAULT_TO_LIST = [ - "dev.milvus@zilliz.com", -] - -def send_email(subject, content, token, receivers=None): - sender = 'test@zilliz.com' - message = MIMEText(content, 'html', 'utf-8') - message['From'] = Header("Daily Test") - message['To'] = Header("dev.milvus") - message['Subject'] = Header(subject, 'utf-8') - try: - smtp_obj = smtplib.SMTP('smtp.exmail.qq.com') - if receivers is None: - receivers = SMS_DEFAULT_TO_LIST - smtp_obj.login(sender, token) - result = smtp_obj.sendmail(sender, receivers, message.as_string()) - except smtplib.SMTPException as e: - logging.error(str(e)) - finally: - smtp_obj.quit() - - -if __name__ == "__main__": - if len(sys.argv) != 4: - sys.exit() - subject = sys.argv[1] - content = sys.argv[2] - token = sys.argv[3] - send_email(subject, content, token) diff --git a/ci/jenkins/scripts/requirements.txt b/ci/jenkins/scripts/requirements.txt deleted file mode 100644 index 2f81afcfad..0000000000 --- a/ci/jenkins/scripts/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -ruamel.yaml==0.16.5 -ruamel.yaml.clib==0.2.0 diff --git a/ci/jenkins/scripts/yaml_processor.py b/ci/jenkins/scripts/yaml_processor.py deleted file mode 100755 index 0e6d7dbbf4..0000000000 --- a/ci/jenkins/scripts/yaml_processor.py +++ /dev/null @@ -1,536 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import argparse -from argparse import Namespace -import os, shutil -import getopt -from ruamel.yaml import YAML, yaml_object -from ruamel.yaml.comments import CommentedSeq, CommentedMap -from ruamel.yaml.tokens import CommentToken - -## -yaml = YAML(typ="rt") -## format yaml file -yaml.indent(mapping=2, sequence=4, offset=2) - - -############################################ -# Comment operation -# -############################################ -def _extract_comment(_comment): - """ - remove '#' at start of comment - """ - # if _comment is empty, do nothing - if not _comment: - return _comment - - # str_ = _comment.lstrip(" ") - str_ = _comment.strip() - str_ = str_.lstrip("#") - - return str_ - - -def _add_eol_comment(element, *args, **kwargs): - """ - add_eol_comment - args --> (comment, key) - """ - if element is None or \ - (not isinstance(element, CommentedMap) and - not isinstance(element, CommentedSeq)) or \ - args[0] is None or \ - len(args[0]) == 0: - return - - comment = args[0] - # comment is empty, do nothing - if not comment: - return - - key = args[1] - try: - element.yaml_add_eol_comment(*args, **kwargs) - except Exception: - element.ca.items.pop(key, None) - element.yaml_add_eol_comment(*args, **kwargs) - - -def _map_comment(_element, _key): - origin_comment = "" - token = _element.ca.items.get(_key, None) - if token is not None: - try: - origin_comment = token[2].value - except Exception: - try: - # comment is below element, add profix "#\n" - col = _element.lc.col + 2 - space_list = [" " for i in range(col)] - space_str = "".join(space_list) - - origin_comment = "\n" + "".join([space_str + t.value for t in token[3]]) - except Exception: - pass - - return origin_comment - - -def _seq_comment(_element, _index): - # get target comment - _comment = "" - token = _element.ca.items.get(_index, None) - if token is not None: - _comment = token[0].value - - return _comment - - -def _start_comment(_element): - _comment = "" - cmt = _element.ca.comment - try: - _comment = cmt[1][0].value - except Exception: - pass - - return _comment - - -def _comment_counter(_comment): - """ - - counter comment tips and split into list - """ - - x = lambda l: l.strip().strip("#").strip() - - _counter = [] - if _comment.startswith("\n"): - _counter.append("") - _counter.append(x(_comment[1:])) - - return _counter - elif _comment.startswith("#\n"): - _counter.append("") - _counter.append(x(_comment[2:])) - else: - index = _comment.find("\n") - _counter.append(x(_comment[:index])) - _counter.append(x(_comment[index + 1:])) - - return _counter - - -def _obtain_comment(_m_comment, _t_comment): - if not _m_comment or not _t_comment: - return _m_comment or _t_comment - - _m_counter = _comment_counter(_m_comment) - _t_counter = _comment_counter(_t_comment) - - if not _m_counter[0] and not _t_counter[1]: - comment = _t_comment + _m_comment - elif not _m_counter[1] and not _t_counter[0]: - comment = _m_comment + _t_comment - elif _t_counter[0] and _t_counter[1]: - comment = _t_comment - elif not _t_counter[0] and not _t_counter[1]: - comment = _m_comment - elif not _m_counter[0] and not _m_counter[1]: - comment = _t_comment - else: - if _t_counter[0]: - comment = _m_comment.replace(_m_counter[0], _t_counter[0], 1) - else: - comment = _m_comment.replace(_m_counter[1], _t_counter[1], 1) - - i = comment.find("\n\n") - while i >= 0: - comment = comment.replace("\n\n\n", "\n\n", 1) - i = comment.find("\n\n\n") - - return comment - - -############################################ -# Utils -# -############################################ -def _get_update_par(_args): - _dict = _args.__dict__ - - # file path - _in_file = _dict.get("f", None) or _dict.get("file", None) - # tips - _tips = _dict.get('tips', None) or "Input \"-h\" for more information" - # update - _u = _dict.get("u", None) or _dict.get("update", None) - # apppend - _a = _dict.get('a', None) or _dict.get('append', None) - # out stream group - _i = _dict.get("i", None) or _dict.get("inplace", None) - _o = _dict.get("o", None) or _dict.get("out_file", None) - - return _in_file, _u, _a, _i, _o, _tips - - -############################################ -# Element operation -# -############################################ -def update_map_element(element, key, value, comment, _type): - """ - element: - key: - value: - comment: - _type: value type. - """ - if element is None or not isinstance(element, CommentedMap): - print("Only key-value update support") - sys.exit(1) - - origin_comment = _map_comment(element, key) - - sub_element = element.get(key, None) - if isinstance(sub_element, CommentedMap) or isinstance(sub_element, CommentedSeq): - print("Only support update a single value") - - element.update({key: value}) - - comment = _obtain_comment(origin_comment, comment) - _add_eol_comment(element, _extract_comment(comment), key) - - -def update_seq_element(element, value, comment, _type): - if element is None or not isinstance(element, CommentedSeq): - print("Param `-a` only use to append yaml list") - sys.exit(1) - element.append(str(value)) - - comment = _obtain_comment("", comment) - _add_eol_comment(element, _extract_comment(comment), len(element) - 1) - - -def run_update(code, keys, value, comment, _app): - key_list = keys.split(".") - - space_str = ":\n " - key_str = "{}".format(key_list[0]) - for key in key_list[1:]: - key_str = key_str + space_str + key - space_str = space_str + " " - if not _app: - yaml_str = """{}: {}""".format(key_str, value) - else: - yaml_str = "{}{}- {}".format(key_str, space_str, value) - - if comment: - yaml_str = "{} # {}".format(yaml_str, comment) - - mcode = yaml.load(yaml_str) - - _merge(code, mcode) - - -def _update(code, _update, _app, _tips): - if not _update: - return code - - _update_list = [l.strip() for l in _update.split(",")] - for l in _update_list: - try: - variant, comment = l.split("#") - except ValueError: - variant = l - comment = None - - try: - keys, value = variant.split("=") - run_update(code, keys, value, comment, _app) - except ValueError: - print("Invalid format. print command \"--help\" get more info.") - sys.exit(1) - - return code - - -def _backup(in_file_p): - backup_p = in_file_p + ".bak" - - if os.path.exists(backup_p): - os.remove(backup_p) - - if not os.path.exists(in_file_p): - print("File {} not exists.".format(in_file_p)) - sys.exit(1) - - shutil.copyfile(in_file_p, backup_p) # 复制文件 - - -def _recovery(in_file_p): - backup_p = in_file_p + ".bak" - - if not os.path.exists(in_file_p): - print("File {} not exists.".format(in_file_p)) - sys.exit(1) - elif not os.path.exists(backup_p): - print("Backup file not exists") - sys.exit(0) - - os.remove(in_file_p) - - os.rename(backup_p, in_file_p) - - -# master merge target -def _merge(master, target): - if type(master) != type(target): - print("yaml format not match:\n") - yaml.dump(master, sys.stdout) - print("\n&&\n") - yaml.dump(target, sys.stdout) - - sys.exit(1) - - ## item is a sequence - if isinstance(target, CommentedSeq): - for index in range(len(target)): - # get target comment - target_comment = _seq_comment(target, index) - - master_index = len(master) - - target_item = target[index] - - if isinstance(target_item, CommentedMap): - merge_flag = False - for idx in range(len(master)): - if isinstance(master[idx], CommentedMap): - if master[idx].keys() == target_item.keys(): - _merge(master[idx], target_item) - # nonlocal merge_flag - master_index = idx - merge_flag = True - break - - if merge_flag is False: - master.append(target_item) - elif target_item not in master: - master.append(target[index]) - else: - # merge(master[index], target[index]) - pass - - # # remove enter signal in previous item - previous_comment = _seq_comment(master, master_index - 1) - _add_eol_comment(master, _extract_comment(previous_comment), master_index - 1) - - origin_comment = _seq_comment(master, master_index) - comment = _obtain_comment(origin_comment, target_comment) - if len(comment) > 0: - _add_eol_comment(master, _extract_comment(comment) + "\n\n", len(master) - 1) - - ## item is a map - elif isinstance(target, CommentedMap): - for item in target: - if item == "flag": - print("") - origin_comment = _map_comment(master, item) - target_comment = _map_comment(target, item) - - # get origin start comment - origin_start_comment = _start_comment(master) - - # get target start comment - target_start_comment = _start_comment(target) - - m = master.get(item, default=None) - if m is None or \ - (not (isinstance(m, CommentedMap) or - isinstance(m, CommentedSeq))): - master.update({item: target[item]}) - - else: - _merge(master[item], target[item]) - - comment = _obtain_comment(origin_comment, target_comment) - if len(comment) > 0: - _add_eol_comment(master, _extract_comment(comment), item) - - start_comment = _obtain_comment(origin_start_comment, target_start_comment) - if len(start_comment) > 0: - master.yaml_set_start_comment(_extract_comment(start_comment)) - - -def _save(_code, _file): - with open(_file, 'w') as wf: - yaml.dump(_code, wf) - - -def _load(_file): - with open(_file, 'r') as rf: - code = yaml.load(rf) - return code - - -############################################ -# sub parser process operation -# -############################################ -def merge_yaml(_args): - _dict = _args.__dict__ - - _m_file = _dict.get("merge_file", None) - _in_file, _u, _a, _i, _o, _tips = _get_update_par(_args) - - if not (_in_file and _m_file): - print(_tips) - sys.exit(1) - - code = _load(_in_file) - mcode = _load(_m_file) - - _merge(code, mcode) - - _update(code, _u, _a, _tips) - - if _i: - _backup(_in_file) - _save(code, _in_file) - elif _o: - _save(code, _o) - else: - print(_tips) - sys.exit(1) - - -def update_yaml(_args): - _in_file, _u, _a, _i, _o, _tips = _get_update_par(_args) - - if not _in_file or not _u: - print(_tips) - sys.exit(1) - - code = _load(_in_file) - - if _i and _o: - print(_tips) - sys.exit(1) - - _update(code, _u, _a, _tips) - - if _i: - _backup(_in_file) - _save(code, _in_file) - elif _o: - _save(code, _o) - - -def reset(_args): - _dict = _args.__dict__ - _f = _dict.get('f', None) or _dict.get('file', None) - - if _f: - _recovery(_f) - else: - _t = _dict.get('tips', None) or "Input \"-h\" for more information" - print(_t) - - -############################################ -# Cli operation -# -############################################ -def _set_merge_parser(_parsers): - """ - config merge parser - """ - - merge_parser = _parsers.add_parser("merge", help="merge with another yaml file") - - _set_merge_parser_arg(merge_parser) - _set_update_parser_arg(merge_parser) - - merge_parser.set_defaults( - function=merge_yaml, - tips=merge_parser.format_help() - ) - - -def _set_merge_parser_arg(_parser): - """ - config parser argument for merging - """ - - _parser.add_argument("-m", "--merge-file", help="indicate merge yaml file") - - -def _set_update_parser(_parsers): - """ - config merge parser - """ - - update_parser = _parsers.add_parser("update", help="update with another yaml file") - _set_update_parser_arg(update_parser) - - update_parser.set_defaults( - function=update_yaml, - tips=update_parser.format_help() - ) - - -def _set_update_parser_arg(_parser): - """ - config parser argument for updating - """ - - _parser.add_argument("-f", "--file", help="source yaml file") - _parser.add_argument('-u', '--update', help="update with args, instance as \"a.b.c=d# d comment\"") - _parser.add_argument('-a', '--append', action="store_true", help="append to a seq") - - group = _parser.add_mutually_exclusive_group() - group.add_argument("-o", "--out-file", help="indicate output yaml file") - group.add_argument("-i", "--inplace", action="store_true", help="indicate whether result store in origin file") - - -def _set_reset_parser(_parsers): - """ - config merge parser - """ - - reset_parser = _parsers.add_parser("reset", help="reset yaml file") - - # indicate yaml file - reset_parser.add_argument('-f', '--file', help="indicate input yaml file") - - reset_parser.set_defaults( - function=reset, - tips=reset_parser.format_help() - ) - - -def main(): - parser = argparse.ArgumentParser() - sub_parsers = parser.add_subparsers() - - # set merge command - _set_merge_parser(sub_parsers) - - # set update command - _set_update_parser(sub_parsers) - - # set reset command - _set_reset_parser(sub_parsers) - - # parse argument and run func - args = parser.parse_args() - args.function(args) - - -if __name__ == '__main__': - main() diff --git a/ci/jenkins/step/build.groovy b/ci/jenkins/step/build.groovy deleted file mode 100644 index 45d644b588..0000000000 --- a/ci/jenkins/step/build.groovy +++ /dev/null @@ -1,14 +0,0 @@ -timeout(time: 120, unit: 'MINUTES') { - dir ("ci/scripts") { - withCredentials([usernamePassword(credentialsId: "${params.JFROG_CREDENTIALS_ID}", usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) { - def checkResult = sh(script: "./check_ccache.sh -l ${params.JFROG_ARTFACTORY_URL}/ccache", returnStatus: true) - - if ("${BINARY_VERSION}" == "gpu") { - sh "/bin/bash --login -c \". ./before-install.sh && ./build.sh -t ${params.BUILD_TYPE} -j3 -i ${env.MILVUS_INSTALL_PREFIX} --with_fiu --coverage -l -g -u\"" - } else { - sh "/bin/bash --login -c \". ./before-install.sh && ./build.sh -t ${params.BUILD_TYPE} -j3 -i ${env.MILVUS_INSTALL_PREFIX} --with_fiu --coverage -l -u\"" - } - sh "./update_ccache.sh -l ${params.JFROG_ARTFACTORY_URL}/ccache -u ${USERNAME} -p ${PASSWORD}" - } - } -} diff --git a/ci/jenkins/step/cleanupShardsDev.groovy b/ci/jenkins/step/cleanupShardsDev.groovy deleted file mode 100644 index 266244d5f8..0000000000 --- a/ci/jenkins/step/cleanupShardsDev.groovy +++ /dev/null @@ -1,12 +0,0 @@ -try { - def helmResult = sh script: "helm status -n milvus ${env.SHARDS_HELM_RELEASE_NAME}", returnStatus: true - if (!helmResult) { - sh "helm uninstall -n milvus ${env.SHARDS_HELM_RELEASE_NAME}" - } -} catch (exc) { - def helmResult = sh script: "helm status -n milvus ${env.SHARDS_HELM_RELEASE_NAME}", returnStatus: true - if (!helmResult) { - sh "helm uninstall -n milvus ${env.SHARDS_HELM_RELEASE_NAME}" - } - throw exc -} diff --git a/ci/jenkins/step/cleanupSingleDev.groovy b/ci/jenkins/step/cleanupSingleDev.groovy deleted file mode 100644 index 171da361f7..0000000000 --- a/ci/jenkins/step/cleanupSingleDev.groovy +++ /dev/null @@ -1,12 +0,0 @@ -try { - def helmResult = sh script: "helm status -n milvus ${env.HELM_RELEASE_NAME}", returnStatus: true - if (!helmResult) { - sh "helm uninstall -n milvus ${env.HELM_RELEASE_NAME}" - } -} catch (exc) { - def helmResult = sh script: "helm status -n milvus ${env.HELM_RELEASE_NAME}", returnStatus: true - if (!helmResult) { - sh "helm uninstall -n milvus ${env.HELM_RELEASE_NAME}" - } - throw exc -} diff --git a/ci/jenkins/step/coverage.groovy b/ci/jenkins/step/coverage.groovy deleted file mode 100644 index 36cd0f915e..0000000000 --- a/ci/jenkins/step/coverage.groovy +++ /dev/null @@ -1,16 +0,0 @@ -timeout(time: 30, unit: 'MINUTES') { - dir ("ci/scripts") { - sh "./coverage.sh" - boolean isNightlyTest = currentBuild.getBuildCauses('hudson.triggers.TimerTrigger$TimerTriggerCause').size() != 0 ? true : false - String formatFlag = "${BINARY_VERSION}-version-${OS_NAME}-unittest".replaceAll("\\.", "_").replaceAll("-", "_") - if (isNightlyTest) { - withCredentials([[$class: 'StringBinding', credentialsId: "milvus-ci-codecov-token", variable: 'CODECOV_TOKEN']]) { - sh "curl -s https://codecov.io/bash | bash -s - -f output_new.info -U \"--proxy http://proxy.zilliz.tech:1088\" -A \"--proxy http://proxy.zilliz.tech:1088\" -n ${BINARY_VERSION}-version-${OS_NAME}-unittest -F nightly -F ${formatFlag} || echo \"Codecov did not collect coverage reports\"" - } - } else { - withCredentials([[$class: 'StringBinding', credentialsId: "milvus-ci-codecov-token", variable: 'CODECOV_TOKEN']]) { - sh "curl -s https://codecov.io/bash | bash -s - -f output_new.info -U \"--proxy http://proxy.zilliz.tech:1088\" -A \"--proxy http://proxy.zilliz.tech:1088\" -n ${BINARY_VERSION}-version-${OS_NAME}-unittest -F ${formatFlag} || echo \"Codecov did not collect coverage reports\"" - } - } - } -} diff --git a/ci/jenkins/step/package.groovy b/ci/jenkins/step/package.groovy deleted file mode 100644 index e65e79b42a..0000000000 --- a/ci/jenkins/step/package.groovy +++ /dev/null @@ -1,8 +0,0 @@ -sh "rm -rf ${env.MILVUS_INSTALL_PREFIX}/unittest" -sh "tar -zcvf ./${env.PROJECT_NAME}-${env.PACKAGE_VERSION}.tar.gz -C ${env.MILVUS_ROOT_PATH}/ ${env.PROJECT_NAME}" -withCredentials([usernamePassword(credentialsId: "${params.JFROG_CREDENTIALS_ID}", usernameVariable: 'JFROG_USERNAME', passwordVariable: 'JFROG_PASSWORD')]) { - def uploadStatus = sh(returnStatus: true, script: "curl -u${JFROG_USERNAME}:${JFROG_PASSWORD} -T ./${env.PROJECT_NAME}-${env.PACKAGE_VERSION}.tar.gz ${params.JFROG_ARTFACTORY_URL}/milvus/package/${env.PROJECT_NAME}-${env.PACKAGE_VERSION}.tar.gz") - if (uploadStatus != 0) { - error("\" ${env.PROJECT_NAME}-${env.PACKAGE_VERSION}.tar.gz \" upload to \" ${params.JFROG_ARTFACTORY_URL}/milvus/package/${env.PROJECT_NAME}-${env.PACKAGE_VERSION}.tar.gz \" failed!") - } -} diff --git a/ci/jenkins/step/publishImages.groovy b/ci/jenkins/step/publishImages.groovy deleted file mode 100644 index 8960ddd44f..0000000000 --- a/ci/jenkins/step/publishImages.groovy +++ /dev/null @@ -1,50 +0,0 @@ -dir ("docker/deploy/${BINARY_VERSION}/${OS_NAME}") { - def binaryPackage = "${PROJECT_NAME}-${PACKAGE_VERSION}.tar.gz" - - withCredentials([usernamePassword(credentialsId: "${params.JFROG_CREDENTIALS_ID}", usernameVariable: 'JFROG_USERNAME', passwordVariable: 'JFROG_PASSWORD')]) { - def downloadStatus = sh(returnStatus: true, script: "curl -u${JFROG_USERNAME}:${JFROG_PASSWORD} -O ${params.JFROG_ARTFACTORY_URL}/milvus/package/${binaryPackage}") - - if (downloadStatus != 0) { - error("\" Download \" ${params.JFROG_ARTFACTORY_URL}/milvus/package/${binaryPackage} \" failed!") - } - } - sh "tar zxvf ${binaryPackage}" - def imageName = "${PROJECT_NAME}/engine:${DOCKER_VERSION}" - - try { - deleteImages("${imageName}", true) - - def customImage = docker.build("${imageName}") - - deleteImages("${params.DOKCER_REGISTRY_URL}/${imageName}", true) - - docker.withRegistry("https://${params.DOKCER_REGISTRY_URL}", "${params.DOCKER_CREDENTIALS_ID}") { - customImage.push() - } - } catch (exc) { - throw exc - } finally { - deleteImages("${imageName}", true) - deleteImages("${params.DOKCER_REGISTRY_URL}/${imageName}", true) - } -} - -boolean deleteImages(String imageName, boolean force) { - def imageNameStr = imageName.trim() - def isExistImage = sh(returnStatus: true, script: "docker inspect --type=image ${imageNameStr} 2>&1 > /dev/null") - if (isExistImage == 0) { - def deleteImageStatus = 0 - if (force) { - def imageID = sh(returnStdout: true, script: "docker inspect --type=image --format \"{{.ID}}\" ${imageNameStr}") - deleteImageStatus = sh(returnStatus: true, script: "docker rmi -f ${imageID}") - } else { - deleteImageStatus = sh(returnStatus: true, script: "docker rmi ${imageNameStr}") - } - - if (deleteImageStatus != 0) { - return false - } - } - return true -} - diff --git a/ci/jenkins/step/shardsDevNightlyTest.groovy b/ci/jenkins/step/shardsDevNightlyTest.groovy deleted file mode 100644 index 8a4524c4e3..0000000000 --- a/ci/jenkins/step/shardsDevNightlyTest.groovy +++ /dev/null @@ -1,35 +0,0 @@ -timeout(time: 180, unit: 'MINUTES') { - dir ('milvus-helm') { - sh 'helm version' - sh 'helm repo add stable https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts' - sh 'helm repo update' - checkout([$class: 'GitSCM', branches: [[name: "${env.HELM_BRANCH}"]], userRemoteConfigs: [[url: "https://github.com/milvus-io/milvus-helm.git", name: 'origin', refspec: "+refs/heads/${env.HELM_BRANCH}:refs/remotes/origin/${env.HELM_BRANCH}"]]]) - // sh 'helm dep update' - - retry(3) { - try { - dir ('charts/milvus') { - if ("${BINARY_VERSION}" == "CPU") { - sh "helm install --wait --timeout 300s --set cluster.enabled=true --set persistence.enabled=true --set image.repository=registry.zilliz.com/milvus/engine --set mishards.image.tag=test --set mishards.image.pullPolicy=Always --set image.tag=${DOCKER_VERSION} --set image.pullPolicy=Always --set service.type=ClusterIP --set image.resources.requests.memory=8Gi --set image.resources.requests.cpu=2.0 --set image.resources.limits.memory=12Gi --set image.resources.limits.cpu=4.0 -f ci/db_backend/mysql_${BINARY_VERSION}_values.yaml --namespace milvus ${env.SHARDS_HELM_RELEASE_NAME} ." - } else { - sh "helm install --wait --timeout 300s --set cluster.enabled=true --set persistence.enabled=true --set image.repository=registry.zilliz.com/milvus/engine --set mishards.image.tag=test --set mishards.image.pullPolicy=Always --set gpu.enabled=true --set image.tag=${DOCKER_VERSION} --set image.pullPolicy=Always --set service.type=ClusterIP -f ci/db_backend/mysql_${BINARY_VERSION}_values.yaml --namespace milvus ${env.SHARDS_HELM_RELEASE_NAME} ." - } - } - } catch (exc) { - def helmStatusCMD = "helm get manifest --namespace milvus ${env.SHARDS_HELM_RELEASE_NAME} | kubectl describe -n milvus -f - && \ - kubectl logs --namespace milvus -l \"app=milvus,release=${env.SHARDS_HELM_RELEASE_NAME}\" -c milvus && \ - helm status -n milvus ${env.SHARDS_HELM_RELEASE_NAME}" - def helmResult = sh script: helmStatusCMD, returnStatus: true - if (!helmResult) { - sh "helm uninstall -n milvus ${env.SHARDS_HELM_RELEASE_NAME} && sleep 1m" - } - throw exc - } - } - } - - dir ("tests/milvus_python_test") { - sh 'python3 -m pip install -r requirements.txt' - sh "pytest . --level=2 --alluredir=\"test_out/dev/shards/\" --ip ${env.SHARDS_HELM_RELEASE_NAME}.milvus.svc.cluster.local >> ${WORKSPACE}/${env.DEV_TEST_ARTIFACTS}/milvus_${BINARY_VERSION}_shards_dev_test.log" - } -} diff --git a/ci/jenkins/step/singleDevNightlyTest.groovy b/ci/jenkins/step/singleDevNightlyTest.groovy deleted file mode 100644 index da06fd41ab..0000000000 --- a/ci/jenkins/step/singleDevNightlyTest.groovy +++ /dev/null @@ -1,60 +0,0 @@ -timeout(time: 180, unit: 'MINUTES') { - dir ('milvus-helm') { - sh 'helm version' - sh 'helm repo add stable https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts' - sh 'helm repo update' - checkout([$class: 'GitSCM', branches: [[name: "${env.HELM_BRANCH}"]], userRemoteConfigs: [[url: "https://github.com/milvus-io/milvus-helm.git", name: 'origin', refspec: "+refs/heads/${env.HELM_BRANCH}:refs/remotes/origin/${env.HELM_BRANCH}"]]]) - // sh 'helm dep update' - - retry(3) { - try { - dir ('charts/milvus') { - sh "helm install --wait --timeout 300s --set image.repository=registry.zilliz.com/milvus/engine --set image.tag=${DOCKER_VERSION} --set image.pullPolicy=Always --set service.type=ClusterIP --set image.resources.requests.memory=8Gi --set image.resources.requests.cpu=2.0 --set image.resources.limits.memory=12Gi --set image.resources.limits.cpu=4.0 -f ci/db_backend/mysql_${BINARY_VERSION}_values.yaml -f ci/filebeat/values.yaml --namespace milvus ${env.HELM_RELEASE_NAME} ." - } - } catch (exc) { - def helmStatusCMD = "helm get manifest --namespace milvus ${env.HELM_RELEASE_NAME} | kubectl describe -n milvus -f - && \ - kubectl logs --namespace milvus -l \"app=milvus,release=${env.HELM_RELEASE_NAME}\" -c milvus && \ - helm status -n milvus ${env.HELM_RELEASE_NAME}" - def helmResult = sh script: helmStatusCMD, returnStatus: true - if (!helmResult) { - sh "helm uninstall -n milvus ${env.HELM_RELEASE_NAME} && sleep 1m" - } - throw exc - } - } - } - - dir ("tests/milvus_python_test") { - // sh 'python3 -m pip install -r requirements.txt -i http://pypi.douban.com/simple --trusted-host pypi.douban.com' - sh 'python3 -m pip install -r requirements.txt' - sh "pytest . --level=2 --alluredir=\"test_out/dev/single/mysql\" --ip ${env.HELM_RELEASE_NAME}.milvus.svc.cluster.local >> ${WORKSPACE}/${env.DEV_TEST_ARTIFACTS}/milvus_${BINARY_VERSION}_mysql_dev_test.log" - } - // sqlite database backend test - load "ci/jenkins/step/cleanupSingleDev.groovy" - - if (!fileExists('milvus-helm/charts/milvus')) { - dir ("milvus-helm") { - checkout([$class: 'GitSCM', branches: [[name:"${env.HELM_BRANCH}"]], userRemoteConfigs: [[url: "https://github.com/milvus-io/milvus-helm.git", name: 'origin', refspec: "+refs/heads/${env.HELM_BRANCH}:refs/remotes/origin/${env.HELM_BRANCH}"]]]) - } - } - retry(3) { - try { - dir ("milvus-helm/charts/milvus") { - sh "helm install --wait --timeout 300s --set image.repository=registry.zilliz.com/milvus/engine --set image.tag=${DOCKER_VERSION} --set image.pullPolicy=Always --set service.type=ClusterIP --set image.resources.requests.memory=8Gi --set image.resources.requests.cpu=2.0 --set image.resources.limits.memory=12Gi --set image.resources.limits.cpu=4.0 -f ci/db_backend/sqlite_${BINARY_VERSION}_values.yaml -f ci/filebeat/values.yaml --namespace milvus ${env.HELM_RELEASE_NAME} ." - } - } catch (exc) { - def helmStatusCMD = "helm get manifest --namespace milvus ${env.HELM_RELEASE_NAME} | kubectl describe -n milvus -f - && \ - kubectl logs --namespace milvus -l \"app=milvus,release=${env.HELM_RELEASE_NAME}\" -c milvus && \ - helm status -n milvus ${env.HELM_RELEASE_NAME}" - def helmResult = sh script: helmStatusCMD, returnStatus: true - if (!helmResult) { - sh "helm uninstall -n milvus ${env.HELM_RELEASE_NAME} && sleep 1m" - } - throw exc - } - } - dir ("tests/milvus_python_test") { - sh "pytest . --level=2 --alluredir=\"test_out/dev/single/sqlite\" --ip ${env.HELM_RELEASE_NAME}.milvus.svc.cluster.local >> ${WORKSPACE}/${env.DEV_TEST_ARTIFACTS}/milvus_${BINARY_VERSION}_sqlite_dev_test.log" - sh "pytest . --level=1 --ip ${env.HELM_RELEASE_NAME}.milvus.svc.cluster.local --port=19121 --handler=HTTP >> ${WORKSPACE}/${env.DEV_TEST_ARTIFACTS}/milvus_${BINARY_VERSION}_sqlite_http_dev_test.log" - } -} diff --git a/ci/jenkins/step/singleDevTest.groovy b/ci/jenkins/step/singleDevTest.groovy deleted file mode 100644 index 9f8f662562..0000000000 --- a/ci/jenkins/step/singleDevTest.groovy +++ /dev/null @@ -1,32 +0,0 @@ -timeout(time: 120, unit: 'MINUTES') { - dir ('milvus-helm') { - sh 'helm version' - sh 'helm repo add stable https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts' - sh 'helm repo update' - checkout([$class: 'GitSCM', branches: [[name: "${env.HELM_BRANCH}"]], userRemoteConfigs: [[url: "https://github.com/milvus-io/milvus-helm.git", name: 'origin', refspec: "+refs/heads/${env.HELM_BRANCH}:refs/remotes/origin/${env.HELM_BRANCH}"]]]) - // sh 'helm dep update' - - retry(3) { - try { - dir ('charts/milvus') { - sh "helm install --wait --timeout 300s --set image.repository=registry.zilliz.com/milvus/engine --set persistence.enabled=true --set image.tag=${DOCKER_VERSION} --set image.pullPolicy=Always --set service.type=ClusterIP -f ci/db_backend/mysql_${BINARY_VERSION}_values.yaml -f ci/filebeat/values.yaml --namespace milvus ${env.HELM_RELEASE_NAME} ." - } - } catch (exc) { - def helmStatusCMD = "helm get manifest --namespace milvus ${env.HELM_RELEASE_NAME} | kubectl describe -n milvus -f - && \ - kubectl logs --namespace milvus -l \"app=milvus,release=${env.HELM_RELEASE_NAME}\" -c milvus && \ - helm status -n milvus ${env.HELM_RELEASE_NAME}" - def helmResult = sh script: helmStatusCMD, returnStatus: true - if (!helmResult) { - sh "helm uninstall -n milvus ${env.HELM_RELEASE_NAME} && sleep 1m" - } - throw exc - } - } - } - dir ("tests/milvus_python_test") { - // sh 'python3 -m pip install -r requirements.txt -i http://pypi.douban.com/simple --trusted-host pypi.douban.com' - sh 'python3 -m pip install -r requirements.txt' - sh "pytest . --alluredir=\"test_out/dev/single/mysql\" --level=1 --ip ${env.HELM_RELEASE_NAME}.milvus.svc.cluster.local --service ${env.HELM_RELEASE_NAME} >> ${WORKSPACE}/${env.DEV_TEST_ARTIFACTS}/milvus_${BINARY_VERSION}_mysql_dev_test.log" - // sh "pytest test_restart.py --alluredir=\"test_out/dev/single/mysql\" --level=3 --ip ${env.HELM_RELEASE_NAME}.milvus.svc.cluster.local --service ${env.HELM_RELEASE_NAME}" - } -} diff --git a/ci/jenkins/step/unittest.groovy b/ci/jenkins/step/unittest.groovy deleted file mode 100644 index 5e59378a81..0000000000 --- a/ci/jenkins/step/unittest.groovy +++ /dev/null @@ -1,5 +0,0 @@ -timeout(time: 30, unit: 'MINUTES') { - dir ("ci/scripts") { - sh "./run_unittest.sh -i ${env.MILVUS_INSTALL_PREFIX} --mysql_user=root --mysql_password=123456 --mysql_host=\"127.0.0.1\"" - } -} diff --git a/ci/scripts/before-install.sh b/ci/scripts/before-install.sh deleted file mode 100755 index fab0bcb007..0000000000 --- a/ci/scripts/before-install.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -set -ex - -export CCACHE_COMPRESS=1 -export CCACHE_COMPRESSLEVEL=5 -export CCACHE_COMPILERCHECK=content -export PATH=/usr/lib/ccache/:$PATH - -set +ex diff --git a/ci/scripts/build.sh b/ci/scripts/build.sh deleted file mode 100755 index 3260fb13d9..0000000000 --- a/ci/scripts/build.sh +++ /dev/null @@ -1,170 +0,0 @@ -#!/bin/bash - -set -e - -SOURCE="${BASH_SOURCE[0]}" -while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink - DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" - SOURCE="$(readlink "$SOURCE")" - [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located -done -SCRIPTS_DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" - -MILVUS_CORE_DIR="${SCRIPTS_DIR}/../../core" -CORE_BUILD_DIR="${MILVUS_CORE_DIR}/cmake_build" - -HELP=" -Usage: - $0 [flags] [Arguments] - - clean Remove all existing build artifacts and configuration (start over) - -i [INSTALL_PREFIX] or --install_prefix=[INSTALL_PREFIX] - Install directory used by install. - -t [BUILD_TYPE] or --build_type=[BUILD_TYPE] - Build type (default: Release) - -j[N] or --jobs=[N] Allow N jobs at once; infinite jobs with no arg. - -l Run cpplint & check clang-format - -n No make and make install step - -g Building for the architecture of the GPU in the system - --with_mkl Build with MKL (default: OFF) - --with_fiu Build with FIU (default: OFF) - -c or --coverage Build Code Coverage - -u or --tests Build unittest case - -p or --privileges Install command with elevated privileges - -v or --verbose A level above ‘basic’; includes messages about which makefiles were parsed, prerequisites that did not need to be rebuilt - -h or --help Print help information - - -Use \"$0 --help\" for more information about a given command. -" - -ARGS=`getopt -o "i:t:j::lngcupvh" -l "install_prefix::,build_type::,jobs::,with_mkl,with_fiu,coverage,tests,privileges,help" -n "$0" -- "$@"` - -eval set -- "${ARGS}" - -while true ; do - case "$1" in - -i|--install_prefix) - # o has an optional argument. As we are in quoted mode, - # an empty parameter will be generated if its optional - # argument is not found. - case "$2" in - "") echo "Option install_prefix, no argument"; exit 1 ;; - *) INSTALL_PREFIX=$2 ; shift 2 ;; - esac ;; - -t|--build_type) - case "$2" in - "") echo "Option build_type, no argument"; exit 1 ;; - *) BUILD_TYPE=$2 ; shift 2 ;; - esac ;; - -j|--jobs) - case "$2" in - "") PARALLEL_LEVEL=""; shift 2 ;; - *) PARALLEL_LEVEL=$2 ; shift 2 ;; - esac ;; - -g) echo "Building for the architecture of the GPU in the system..." ; GPU_VERSION="ON" ; shift ;; - --with_mkl) echo "Build with MKL" ; WITH_MKL="ON" ; shift ;; - --with_fiu) echo "Build with FIU" ; FIU_ENABLE="ON" ; shift ;; - --coverage) echo "Build code coverage" ; BUILD_COVERAGE="ON" ; shift ;; - -u|--tests) echo "Build unittest cases" ; BUILD_UNITTEST="ON" ; shift ;; - -n) echo "No build and install step" ; COMPILE_BUILD="OFF" ; shift ;; - -l) RUN_CPPLINT="ON" ; shift ;; - -p|--privileges) PRIVILEGES="ON" ; shift ;; - -v|--verbose) VERBOSE="1" ; shift ;; - -h|--help) echo -e "${HELP}" ; exit 0 ;; - --) shift ; break ;; - *) echo "Internal error!" ; exit 1 ;; - esac -done - -# Set defaults for vars modified by flags to this script -CUDA_COMPILER=/usr/local/cuda/bin/nvcc -INSTALL_PREFIX=${INSTALL_PREFIX:="/var/lib/milvus"} -VERBOSE=${VERBOSE:=""} -BUILD_TYPE=${BUILD_TYPE:="Release"} -BUILD_UNITTEST=${BUILD_UNITTEST:="OFF"} -BUILD_COVERAGE=${BUILD_COVERAGE:="OFF"} -COMPILE_BUILD=${COMPILE_BUILD:="ON"} -GPU_VERSION=${GPU_VERSION:="OFF"} -RUN_CPPLINT=${RUN_CPPLINT:="OFF"} -WITH_MKL=${WITH_MKL:="OFF"} -FIU_ENABLE=${FIU_ENABLE:="OFF"} -PRIVILEGES=${PRIVILEGES:="OFF"} -CLEANUP=${CLEANUP:="OFF"} -PARALLEL_LEVEL=${PARALLEL_LEVEL:="8"} - -for arg do -if [[ $arg == "clean" ]];then - echo "Remove all existing build artifacts and configuration..." - if [ -d ${CORE_BUILD_DIR} ]; then - find ${CORE_BUILD_DIR} -mindepth 1 -delete - rmdir ${CORE_BUILD_DIR} || true - fi - exit 0 -fi -done - -if [[ ! -d ${CORE_BUILD_DIR} ]]; then - mkdir ${CORE_BUILD_DIR} -fi - -echo -e "===\n=== ccache statistics before build\n===" -ccache --show-stats - -pushd ${CORE_BUILD_DIR} - -CMAKE_CMD="cmake \ --DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} --DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ --DCMAKE_CUDA_COMPILER=${CUDA_COMPILER} \ --DMILVUS_GPU_VERSION=${GPU_VERSION} \ --DBUILD_UNIT_TEST=${BUILD_UNITTEST} \ --DBUILD_COVERAGE=${BUILD_COVERAGE} \ --DFAISS_WITH_MKL=${WITH_MKL} \ --DArrow_SOURCE=AUTO \ --DFAISS_SOURCE=AUTO \ --DOpenBLAS_SOURCE=AUTO \ --DMILVUS_WITH_FIU=${FIU_ENABLE} \ -${MILVUS_CORE_DIR}" -echo ${CMAKE_CMD} -${CMAKE_CMD} - -if [[ ${RUN_CPPLINT} == "ON" ]]; then - # cpplint check - make lint - if [ $? -ne 0 ]; then - echo "ERROR! cpplint check failed" - exit 1 - fi - echo "cpplint check passed!" - - # clang-format check - make check-clang-format - if [ $? -ne 0 ]; then - echo "ERROR! clang-format check failed" - exit 1 - fi - echo "clang-format check passed!" - -# # clang-tidy check -# make check-clang-tidy -# if [ $? -ne 0 ]; then -# echo "ERROR! clang-tidy check failed" -# rm -f CMakeCache.txt -# exit 1 -# fi -# echo "clang-tidy check passed!" -fi - -if [[ ${COMPILE_BUILD} == "ON" ]];then - # compile and build - make -j${PARALLEL_LEVEL} VERBOSE=${VERBOSE} || exit 1 - - if [[ ${PRIVILEGES} == "ON" ]];then - sudo make install || exit 1 - else - make install || exit 1 - fi -fi - -popd diff --git a/ci/scripts/check_ccache.sh b/ci/scripts/check_ccache.sh deleted file mode 100755 index 61055b3770..0000000000 --- a/ci/scripts/check_ccache.sh +++ /dev/null @@ -1,106 +0,0 @@ -#!/bin/bash - -HELP=" -Usage: - $0 [flags] [Arguments] - - -l [ARTIFACTORY_URL] Artifactory URL - --cache_dir=[CCACHE_DIR] Ccache directory - -f [FILE] or --file=[FILE] Ccache compress package file - -h or --help Print help information - - -Use \"$0 --help\" for more information about a given command. -" - -ARGS=$(getopt -o "l:f:h" -l "cache_dir::,file::,help" -n "$0" -- "$@") - -eval set -- "${ARGS}" - -while true ; do - case "$1" in - -l) - # o has an optional argument. As we are in quoted mode, - # an empty parameter will be generated if its optional - # argument is not found. - case "$2" in - "") echo "Option Artifactory URL, no argument"; exit 1 ;; - *) ARTIFACTORY_URL=$2 ; shift 2 ;; - esac ;; - --cache_dir) - case "$2" in - "") echo "Option cache_dir, no argument"; exit 1 ;; - *) CCACHE_DIR=$2 ; shift 2 ;; - esac ;; - -f|--file) - case "$2" in - "") echo "Option file, no argument"; exit 1 ;; - *) PACKAGE_FILE=$2 ; shift 2 ;; - esac ;; - -h|--help) echo -e "${HELP}" ; exit 0 ;; - --) shift ; break ;; - *) echo "Internal error!" ; exit 1 ;; - esac -done - -# Set defaults for vars modified by flags to this script -CCACHE_DIR=${CCACHE_DIR:="${HOME}/.ccache"} -PACKAGE_FILE=${PACKAGE_FILE:="ccache-${OS_NAME}-${BUILD_ENV_IMAGE_ID}.tar.gz"} -BRANCH_NAMES=$(git log --decorate | head -n 1 | sed 's/.*(\(.*\))/\1/' | sed 's=[a-zA-Z]*\/==g' | awk -F", " '{$1=""; print $0}') - -if [[ -z "${ARTIFACTORY_URL}" || "${ARTIFACTORY_URL}" == "" ]];then - echo "You have not input ARTIFACTORY_URL !" - exit 1 -fi - -function check_ccache() { - BRANCH=$1 - echo "fetching ${BRANCH}/${PACKAGE_FILE}" - wget -q --spider "${ARTIFACTORY_URL}/${BRANCH}/${PACKAGE_FILE}" - return $? -} - -function download_file() { - BRANCH=$1 - wget -q "${ARTIFACTORY_URL}/${BRANCH}/${PACKAGE_FILE}" && \ - mkdir -p "${CCACHE_DIR}" && \ - tar zxf "${PACKAGE_FILE}" -C "${CCACHE_DIR}" && \ - rm ${PACKAGE_FILE} - return $? -} - -if [[ -n "${CHANGE_TARGET}" && "${BRANCH_NAME}" =~ "PR-" ]];then - check_ccache ${CHANGE_TARGET} - if [[ $? == 0 ]];then - download_file ${CHANGE_TARGET} - if [[ $? == 0 ]];then - echo "found cache" - exit 0 - fi - fi - - check_ccache ${BRANCH_NAME} - if [[ $? == 0 ]];then - download_file ${BRANCH_NAME} - if [[ $? == 0 ]];then - echo "found cache" - exit 0 - fi - fi -fi - -for CURRENT_BRANCH in ${BRANCH_NAMES} -do - if [[ "${CURRENT_BRANCH}" != "HEAD" ]];then - check_ccache ${CURRENT_BRANCH} - if [[ $? == 0 ]];then - download_file ${CURRENT_BRANCH} - if [[ $? == 0 ]];then - echo "found cache" - exit 0 - fi - fi - fi -done - -echo "could not download cache" && exit 1 diff --git a/ci/scripts/coverage.sh b/ci/scripts/coverage.sh deleted file mode 100755 index 401794c96a..0000000000 --- a/ci/scripts/coverage.sh +++ /dev/null @@ -1,104 +0,0 @@ -#!/bin/bash - -SOURCE="${BASH_SOURCE[0]}" -while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink - DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" - SOURCE="$(readlink "$SOURCE")" - [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located -done -SCRIPTS_DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" - -HELP=" -Usage: - $0 [flags] [Arguments] - - -b Core Code build directory - -c Codecov token - -h or --help Print help information - - -Use \"$0 --help\" for more information about a given command. -" - -ARGS=`getopt -o "b:c:h" -l "help" -n "$0" -- "$@"` - -eval set -- "${ARGS}" - -while true ; do - case "$1" in - -b) - # o has an optional argument. As we are in quoted mode, - # an empty parameter will be generated if its optional - # argument is not found. - case "$2" in - "") echo "Option CORE_BUILD_DIR, no argument"; exit 1 ;; - *) CORE_BUILD_DIR=$2 ; shift 2 ;; - esac ;; - -c) - case "$2" in - "") echo "Option CODECOV_TOKEN, no argument"; exit 1 ;; - *) CODECOV_TOKEN=$2 ; shift 2 ;; - esac ;; - -h|--help) echo -e "${HELP}" ; exit 0 ;; - --) shift ; break ;; - *) echo "Internal error!" ; exit 1 ;; - esac -done - -# Set defaults for vars modified by flags to this script -MILVUS_CORE_DIR="${SCRIPTS_DIR}/../../core" -CORE_BUILD_DIR=${CORE_BUILD_DIR:="${MILVUS_CORE_DIR}/cmake_build"} - -LCOV_CMD="lcov" -# LCOV_GEN_CMD="genhtml" - -FILE_INFO_BASE="base.info" -FILE_INFO_MILVUS="server.info" -FILE_INFO_OUTPUT="output.info" -FILE_INFO_OUTPUT_NEW="output_new.info" -DIR_LCOV_OUTPUT="lcov_out" - -DIR_GCNO="${CORE_BUILD_DIR}" -DIR_UNITTEST="${INSTALL_PREFIX}/unittest" - -cd ${SCRIPTS_DIR} - -# delete old code coverage info files -rm -rf ${DIR_LCOV_OUTPUT} -rm -f ${FILE_INFO_BASE} ${FILE_INFO_MILVUS} ${FILE_INFO_OUTPUT} ${FILE_INFO_OUTPUT_NEW} - -# get baseline -${LCOV_CMD} -c -i -d ${DIR_GCNO} -o "${FILE_INFO_BASE}" -if [ $? -ne 0 ]; then - echo "gen baseline coverage run failed" - exit -1 -fi - -# gen code coverage -${LCOV_CMD} -d ${DIR_GCNO} -o "${FILE_INFO_MILVUS}" -c -# merge coverage -${LCOV_CMD} -a ${FILE_INFO_BASE} -a ${FILE_INFO_MILVUS} -o "${FILE_INFO_OUTPUT}" - -# remove third party from tracefiles -${LCOV_CMD} -r "${FILE_INFO_OUTPUT}" -o "${FILE_INFO_OUTPUT_NEW}" \ - "/usr/*" \ - "*/boost/*" \ - "*/cmake_build/*_ep-prefix/*" \ - "*/src/index/cmake_build*" \ - "*/src/index/thirdparty*" \ - "*/src/grpc*" \ - "*/src/metrics/MetricBase.h" \ - "*/src/server/Server.cpp" \ - "*/src/server/DBWrapper.cpp" \ - "*/src/server/grpc_impl/GrpcServer.cpp" \ - "*/thirdparty/*" - -if [ $? -ne 0 ]; then - echo "gen ${FILE_INFO_OUTPUT_NEW} failed" - exit 2 -fi - -if [[ -n ${CODECOV_TOKEN} ]];then - export CODECOV_TOKEN="${CODECOV_TOKEN}" - curl -s https://codecov.io/bash | bash -s - -f output_new.info || echo "Codecov did not collect coverage reports" -fi diff --git a/ci/scripts/run_unittest.sh b/ci/scripts/run_unittest.sh deleted file mode 100755 index 658b283a04..0000000000 --- a/ci/scripts/run_unittest.sh +++ /dev/null @@ -1,121 +0,0 @@ -#!/bin/bash - -set -e - -SOURCE="${BASH_SOURCE[0]}" -while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink - DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" - SOURCE="$(readlink "$SOURCE")" - [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located -done -SCRIPTS_DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" - -HELP=" -Usage: - $0 [flags] [Arguments] - - -i [INSTALL_PREFIX] or --install_prefix=[INSTALL_PREFIX] - Install directory used by install. - --mysql_user=[MYSQL_USER_NAME] MySQL User Name - --mysql_password=[MYSQL_PASSWORD] - MySQL Password - --mysql_host=[MYSQL_HOST] MySQL Host - -h or --help Print help information - - -Use \"$0 --help\" for more information about a given command. -" - -ARGS=`getopt -o "i:h" -l "install_prefix::,mysql_user::,mysql_password::,mysql_host::,help" -n "$0" -- "$@"` - -eval set -- "${ARGS}" - -while true ; do - case "$1" in - -i|--install_prefix) - # o has an optional argument. As we are in quoted mode, - # an empty parameter will be generated if its optional - # argument is not found. - case "$2" in - "") echo "Option install_prefix, no argument"; exit 1 ;; - *) INSTALL_PREFIX=$2 ; shift 2 ;; - esac ;; - --mysql_user) - case "$2" in - "") echo "Option mysql_user, no argument"; exit 1 ;; - *) MYSQL_USER_NAME=$2 ; shift 2 ;; - esac ;; - --mysql_password) - case "$2" in - "") echo "Option mysql_password, no argument"; exit 1 ;; - *) MYSQL_PASSWORD=$2 ; shift 2 ;; - esac ;; - --mysql_host) - case "$2" in - "") echo "Option mysql_host, no argument"; exit 1 ;; - *) MYSQL_HOST=$2 ; shift 2 ;; - esac ;; - -h|--help) echo -e "${HELP}" ; exit 0 ;; - --) shift ; break ;; - *) echo "Internal error!" ; exit 1 ;; - esac -done - -# Set defaults for vars modified by flags to this script -INSTALL_PREFIX=${INSTALL_PREFIX:="/var/lib/milvus"} -MYSQL_USER_NAME=${MYSQL_USER_NAME:="root"} -MYSQL_PASSWORD=${MYSQL_PASSWORD:="123456"} -MYSQL_HOST=${MYSQL_HOST:="127.0.0.1"} -MYSQL_PORT=${MYSQL_PORT:="3306"} -DIR_UNITTEST="${INSTALL_PREFIX}/unittest" - -if [ -d ${INSTALL_PREFIX}/lib ]; then - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${INSTALL_PREFIX}/lib -fi - -if [ ! -d ${DIR_UNITTEST} ]; then - echo "The unittest folder does not exist!" - exit 1 -fi - -pushd ${SCRIPTS_DIR} - -MYSQL_DB_NAME=milvus_`date +%s%N` - -function mysql_exc() -{ - cmd=$1 - mysql -h${MYSQL_HOST} -u${MYSQL_USER_NAME} -p${MYSQL_PASSWORD} -e "${cmd}" - if [ $? -ne 0 ]; then - echo "mysql $cmd run failed" - fi -} - -mysql_exc "CREATE DATABASE IF NOT EXISTS ${MYSQL_DB_NAME};" -mysql_exc "GRANT ALL PRIVILEGES ON ${MYSQL_DB_NAME}.* TO '${MYSQL_USER_NAME}'@'%';" -mysql_exc "FLUSH PRIVILEGES;" -mysql_exc "USE ${MYSQL_DB_NAME};" - -for test in `ls ${DIR_UNITTEST}`; do - echo $test - case ${test} in - test_db) - # set run args for test_db - args="mysql://${MYSQL_USER_NAME}:${MYSQL_PASSWORD}@${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DB_NAME}" - ;; - *test_*) - args="" - ;; - esac - # run unittest - ${DIR_UNITTEST}/${test} "${args}" - if [ $? -ne 0 ]; then - echo ${args} - echo ${DIR_UNITTEST}/${test} "run failed" - exit 1 - fi -done - -mysql_exc "DROP DATABASE IF EXISTS ${MYSQL_DB_NAME};" - -popd \ No newline at end of file diff --git a/ci/scripts/update_ccache.sh b/ci/scripts/update_ccache.sh deleted file mode 100755 index 6c72080564..0000000000 --- a/ci/scripts/update_ccache.sh +++ /dev/null @@ -1,104 +0,0 @@ -#!/bin/bash - -HELP=" -Usage: - $0 [flags] [Arguments] - - -l [ARTIFACTORY_URL] Artifactory URL - --cache_dir=[CCACHE_DIR] Ccache directory - -f [FILE] or --file=[FILE] Ccache compress package file - -u [USERNAME] Artifactory Username - -p [PASSWORD] Artifactory Password - -h or --help Print help information - - -Use \"$0 --help\" for more information about a given command. -" - -ARGS=$(getopt -o "l:f:u:p:h" -l "cache_dir::,file::,help" -n "$0" -- "$@") - -eval set -- "${ARGS}" - -while true ; do - case "$1" in - -l) - # o has an optional argument. As we are in quoted mode, - # an empty parameter will be generated if its optional - # argument is not found. - case "$2" in - "") echo "Option Artifactory URL, no argument"; exit 1 ;; - *) ARTIFACTORY_URL=$2 ; shift 2 ;; - esac ;; - --cache_dir) - case "$2" in - "") echo "Option cache_dir, no argument"; exit 1 ;; - *) CCACHE_DIR=$2 ; shift 2 ;; - esac ;; - -u) - case "$2" in - "") echo "Option Username, no argument"; exit 1 ;; - *) USERNAME=$2 ; shift 2 ;; - esac ;; - -p) - case "$2" in - "") echo "Option Password, no argument"; exit 1 ;; - *) PASSWORD=$2 ; shift 2 ;; - esac ;; - -f|--file) - case "$2" in - "") echo "Option file, no argument"; exit 1 ;; - *) PACKAGE_FILE=$2 ; shift 2 ;; - esac ;; - -h|--help) echo -e "${HELP}" ; exit 0 ;; - --) shift ; break ;; - *) echo "Internal error!" ; exit 1 ;; - esac -done - -# Set defaults for vars modified by flags to this script -CCACHE_DIR=${CCACHE_DIR:="${HOME}/.ccache"} -PACKAGE_FILE=${PACKAGE_FILE:="ccache-${OS_NAME}-${BUILD_ENV_IMAGE_ID}.tar.gz"} -BRANCH_NAME=$(git log --decorate | head -n 1 | sed 's/.*(\(.*\))/\1/' | sed 's/.*, //' | sed 's=[a-zA-Z]*\/==g') - -if [[ -z "${ARTIFACTORY_URL}" || "${ARTIFACTORY_URL}" == "" ]];then - echo "You have not input ARTIFACTORY_URL !" - exit 1 -fi - -if [[ ! -d "${CCACHE_DIR}" ]]; then - echo "\"${CCACHE_DIR}\" directory does not exist !" - exit 1 -fi - -function check_ccache() { - BRANCH=$1 - wget -q --spider "${ARTIFACTORY_URL}/${BRANCH}/${PACKAGE_FILE}" - return $? -} - -if [[ -n "${CHANGE_TARGET}" && "${BRANCH_NAME}" =~ "PR-" ]]; then - check_ccache ${CHANGE_TARGET} - if [[ $? == 0 ]];then - echo "Skip Update ccache package ..." && exit 0 - fi -fi - -echo -e "===\n=== ccache statistics after build\n===" -ccache --show-stats - -if [[ "${BRANCH_NAME}" != "HEAD" ]];then - REMOTE_PACKAGE_PATH="${ARTIFACTORY_URL}/${BRANCH_NAME}" - echo "Updating ccache package file: ${PACKAGE_FILE}" - tar zcf ./"${PACKAGE_FILE}" -C "${CCACHE_DIR}" . - echo "Uploading ccache package file ${PACKAGE_FILE} to ${REMOTE_PACKAGE_PATH}" - curl -u"${USERNAME}":"${PASSWORD}" -T "${PACKAGE_FILE}" "${REMOTE_PACKAGE_PATH}"/"${PACKAGE_FILE}" - if [[ $? == 0 ]];then - echo "Uploading ccache package file success !" - exit 0 - else - echo "Uploading ccache package file fault !" - exit 1 - fi -fi - -echo "Skip Update ccache package ..." diff --git a/codecov.yaml b/codecov.yaml deleted file mode 100644 index 2ddf708b04..0000000000 --- a/codecov.yaml +++ /dev/null @@ -1,31 +0,0 @@ -#Configuration File for CodeCov -coverage: - notify: - require_ci_to_pass: yes - - precision: 2 - round: down - range: "70...100" - - status: - project: - default: - threshold: 0.2 #Allow the coverage to drop by threshold%, and posting a success status. - patch: yes - changes: no - -comment: - layout: "reach, diff, flags, files" - behavior: default - require_changes: no - -flags: - nightly: - joined: false - -ignore: - - "LICENSES" - - ".git" - - "*.yml" - - "*.md" - - "docs/.*" diff --git a/core/.gitignore b/core/.gitignore deleted file mode 100644 index bd2e040880..0000000000 --- a/core/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -milvus/ -conf/server_config.yaml -conf/server_config.yaml.ori -conf/log_config.conf -src/config.h -src/version.h -lcov_out/ -base.info -output.info -output_new.info -server.info -*.pyc -src/grpc/python_gen.h -src/grpc/python/ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt deleted file mode 100644 index 373c42f4f1..0000000000 --- a/core/CMakeLists.txt +++ /dev/null @@ -1,332 +0,0 @@ -#------------------------------------------------------------------------------- -# Copyright (C) 2019-2020 Zilliz. All rights reserved. -# -# Licensed 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 limitations under the License. -#------------------------------------------------------------------------------- - - -cmake_minimum_required(VERSION 3.12) -message(STATUS "Building using CMake version: ${CMAKE_VERSION}") - -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") - -# get build time -MACRO(GET_CURRENT_TIME CURRENT_TIME) - execute_process(COMMAND "date" +"%Y-%m-%d %H:%M.%S" OUTPUT_VARIABLE ${CURRENT_TIME}) -ENDMACRO(GET_CURRENT_TIME) - -GET_CURRENT_TIME(BUILD_TIME) -string(REGEX REPLACE "\n" "" BUILD_TIME ${BUILD_TIME}) -message(STATUS "Build time = ${BUILD_TIME}") - -if (NOT DEFINED CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build.") -endif () - -# get Milvus version via branch name -set(GIT_BRANCH_NAME_REGEX "[0-9]+\\.[0-9]+\\.[0-9]") - -MACRO(GET_GIT_BRANCH_NAME GIT_BRANCH_NAME) - execute_process(COMMAND sh "-c" "git log --decorate | head -n 1 | sed 's/.*(\\(.*\\))/\\1/' | sed 's/.*, //' | sed 's=[a-zA-Z]*\/==g'" - OUTPUT_VARIABLE ${GIT_BRANCH_NAME}) - if (NOT GIT_BRANCH_NAME MATCHES "${GIT_BRANCH_NAME_REGEX}") - execute_process(COMMAND "git" rev-parse --abbrev-ref HEAD OUTPUT_VARIABLE ${GIT_BRANCH_NAME}) - endif () - if (NOT GIT_BRANCH_NAME MATCHES "${GIT_BRANCH_NAME_REGEX}") - execute_process(COMMAND "git" symbolic-ref --short -q HEAD HEAD OUTPUT_VARIABLE ${GIT_BRANCH_NAME}) - endif () -ENDMACRO(GET_GIT_BRANCH_NAME) - -GET_GIT_BRANCH_NAME(GIT_BRANCH_NAME) -message(STATUS "GIT_BRANCH_NAME = ${GIT_BRANCH_NAME}") -if (NOT GIT_BRANCH_NAME STREQUAL "") - string(REGEX REPLACE "\n" "" GIT_BRANCH_NAME ${GIT_BRANCH_NAME}) -endif () - -set(MILVUS_VERSION "${GIT_BRANCH_NAME}") -string(REGEX MATCH "${GIT_BRANCH_NAME_REGEX}" MILVUS_VERSION "${MILVUS_VERSION}") - -# get last commit id -MACRO(GET_LAST_COMMIT_ID LAST_COMMIT_ID) - execute_process(COMMAND sh "-c" "git log --decorate | head -n 1 | awk '{print $2}'" - OUTPUT_VARIABLE ${LAST_COMMIT_ID}) -ENDMACRO(GET_LAST_COMMIT_ID) - -GET_LAST_COMMIT_ID(LAST_COMMIT_ID) -message(STATUS "LAST_COMMIT_ID = ${LAST_COMMIT_ID}") -if (NOT LAST_COMMIT_ID STREQUAL "") - string(REGEX REPLACE "\n" "" LAST_COMMIT_ID ${LAST_COMMIT_ID}) - set(LAST_COMMIT_ID "${LAST_COMMIT_ID}") -else () - set(LAST_COMMIT_ID "Unknown") -endif () - -# set build type -if (CMAKE_BUILD_TYPE STREQUAL "Release") - set(BUILD_TYPE "Release") -else () - set(BUILD_TYPE "Debug") -endif () -message(STATUS "Build type = ${BUILD_TYPE}") - -project(milvus VERSION "${MILVUS_VERSION}") -project(milvus_engine LANGUAGES CXX) - -unset(CMAKE_EXPORT_COMPILE_COMMANDS CACHE) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -set(MILVUS_VERSION_MAJOR "${milvus_VERSION_MAJOR}") -set(MILVUS_VERSION_MINOR "${milvus_VERSION_MINOR}") -set(MILVUS_VERSION_PATCH "${milvus_VERSION_PATCH}") - -if (MILVUS_VERSION_MAJOR STREQUAL "" - OR MILVUS_VERSION_MINOR STREQUAL "" - OR MILVUS_VERSION_PATCH STREQUAL "") - message(WARNING "Failed to determine Milvus version from git branch name") - set(MILVUS_VERSION "0.10.3") -endif () - -message(STATUS "Build version = ${MILVUS_VERSION}") -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/src/version.h @ONLY) - -message(STATUS "Milvus version: " - "${MILVUS_VERSION_MAJOR}.${MILVUS_VERSION_MINOR}.${MILVUS_VERSION_PATCH} " - "(full: '${MILVUS_VERSION}')") - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED on) - -if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)") - message(STATUS "Building milvus_engine on x86 architecture") - set(MILVUS_BUILD_ARCH x86_64) -elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "(ppc)") - message(STATUS "Building milvus_engine on ppc architecture") - set(MILVUS_BUILD_ARCH ppc64le) -else () - message(WARNING "Unknown processor type") - message(WARNING "CMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}") - set(MILVUS_BUILD_ARCH unknown) -endif () - -# Ensure that a default make is set -if ("${MAKE}" STREQUAL "") - if (NOT MSVC) - find_program(MAKE make) - endif () -endif () - -find_path(MYSQL_INCLUDE_DIR - NAMES "mysql.h" - PATH_SUFFIXES "mysql") -if (${MYSQL_INCLUDE_DIR} STREQUAL "MYSQL_INCLUDE_DIR-NOTFOUND") - message(FATAL_ERROR "Could not found MySQL include directory") -else () - include_directories(${MYSQL_INCLUDE_DIR}) -endif () - -set(MILVUS_SOURCE_DIR ${PROJECT_SOURCE_DIR}) -set(MILVUS_BINARY_DIR ${PROJECT_BINARY_DIR}) -set(MILVUS_ENGINE_SRC ${PROJECT_SOURCE_DIR}/src) -set(MILVUS_THIRDPARTY_SRC ${PROJECT_SOURCE_DIR}/thirdparty) - -include(ExternalProject) -include(DefineOptions) -include(BuildUtils) -include(ThirdPartyPackages) - -if (MILVUS_USE_CCACHE) - find_program(CCACHE_FOUND ccache) - if (CCACHE_FOUND) - message(STATUS "Using ccache: ${CCACHE_FOUND}") - set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_FOUND}) - set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_FOUND}) - # let ccache preserve C++ comments, because some of them may be - # meaningful to the compiler - set(ENV{CCACHE_COMMENTS} "1") - endif (CCACHE_FOUND) -endif () - -if (MILVUS_GPU_VERSION) - message(STATUS "Building Milvus GPU version") - add_compile_definitions("MILVUS_GPU_VERSION") - enable_language(CUDA) - find_package(CUDA 10 REQUIRED) - set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS} -Xcompiler -fPIC -std=c++11 -D_FORCE_INLINES --expt-extended-lambda") -else () - message(STATUS "Building Milvus CPU version") -endif () - -if (MILVUS_WITH_PROMETHEUS) - add_compile_definitions("MILVUS_WITH_PROMETHEUS") -endif () - -message("ENABLE_CPU_PROFILING = ${ENABLE_CPU_PROFILING}") -if (ENABLE_CPU_PROFILING STREQUAL "ON") - ADD_DEFINITIONS(-DENABLE_CPU_PROFILING) -endif() - -if (MILVUS_WITH_FIU) - add_compile_definitions("FIU_ENABLE") -endif () - -if (CMAKE_BUILD_TYPE STREQUAL "Release") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -fPIC -DELPP_THREAD_SAFE -fopenmp") - if (MILVUS_GPU_VERSION) - set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS} -O3") - endif () -else () - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -fPIC -DELPP_THREAD_SAFE -fopenmp") - if (MILVUS_GPU_VERSION) - set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS} -O0 -g") - endif () -endif () - -config_summary() -add_subdirectory(src) - -if (BUILD_UNIT_TEST STREQUAL "ON") - if (BUILD_COVERAGE STREQUAL "ON") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") - endif () - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DELPP_DISABLE_LOGS") - add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/unittest) -endif () - -add_custom_target(Clean-All COMMAND ${CMAKE_BUILD_TOOL} clean) - -if ("${MILVUS_DB_PATH}" STREQUAL "") - set(MILVUS_DB_PATH "${CMAKE_INSTALL_PREFIX}") -endif () - -if (MILVUS_GPU_VERSION) - set(GPU_ENABLE "true") - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/conf/server_config.template - ${CMAKE_CURRENT_SOURCE_DIR}/conf/server_config.yaml - @ONLY) -else () - set(GPU_ENABLE "false") - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/conf/server_config.template - ${CMAKE_CURRENT_SOURCE_DIR}/conf/server_config.yaml - @ONLY) -endif () - -install(DIRECTORY scripts/ - DESTINATION scripts - FILE_PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ - GROUP_EXECUTE GROUP_READ - WORLD_EXECUTE WORLD_READ - FILES_MATCHING PATTERN "*.sh") -install(DIRECTORY scripts/migration - DESTINATION scripts - FILE_PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ - GROUP_EXECUTE GROUP_READ - WORLD_EXECUTE WORLD_READ) -install(FILES - conf/server_config.yaml - DESTINATION - conf) - -find_package(Python COMPONENTS Interpreter Development) -find_package(ClangTools) -set(BUILD_SUPPORT_DIR "${CMAKE_SOURCE_DIR}/build-support") - -# -# "make lint" target -# -if (NOT MILVUS_VERBOSE_LINT) - set(MILVUS_LINT_QUIET "--quiet") -endif () - -if (NOT LINT_EXCLUSIONS_FILE) - # source files matching a glob from a line in this file - # will be excluded from linting (cpplint, clang-tidy, clang-format) - set(LINT_EXCLUSIONS_FILE ${BUILD_SUPPORT_DIR}/lint_exclusions.txt) -endif () - -find_program(CPPLINT_BIN NAMES cpplint cpplint.py HINTS ${BUILD_SUPPORT_DIR}) -message(STATUS "Found cpplint executable at ${CPPLINT_BIN}") - -# -# "make lint" targets -# -add_custom_target(lint - ${PYTHON_EXECUTABLE} - ${BUILD_SUPPORT_DIR}/run_cpplint.py - --cpplint_binary - ${CPPLINT_BIN} - --exclude_globs - ${LINT_EXCLUSIONS_FILE} - --source_dir - ${CMAKE_CURRENT_SOURCE_DIR} - ${MILVUS_LINT_QUIET}) - -# -# "make clang-format" and "make check-clang-format" targets -# -if (${CLANG_FORMAT_FOUND}) - # runs clang format and updates files in place. - add_custom_target(clang-format - ${PYTHON_EXECUTABLE} - ${BUILD_SUPPORT_DIR}/run_clang_format.py - --clang_format_binary - ${CLANG_FORMAT_BIN} - --exclude_globs - ${LINT_EXCLUSIONS_FILE} - --source_dir - ${CMAKE_CURRENT_SOURCE_DIR}/src - --fix - ${MILVUS_LINT_QUIET}) - - # runs clang format and exits with a non-zero exit code if any files need to be reformatted - add_custom_target(check-clang-format - ${PYTHON_EXECUTABLE} - ${BUILD_SUPPORT_DIR}/run_clang_format.py - --clang_format_binary - ${CLANG_FORMAT_BIN} - --exclude_globs - ${LINT_EXCLUSIONS_FILE} - --source_dir - ${CMAKE_CURRENT_SOURCE_DIR}/src - ${MILVUS_LINT_QUIET}) -endif () - -# -# "make clang-tidy" and "make check-clang-tidy" targets -# -if (${CLANG_TIDY_FOUND}) - # runs clang-tidy and attempts to fix any warning automatically - add_custom_target(clang-tidy - ${PYTHON_EXECUTABLE} - ${BUILD_SUPPORT_DIR}/run_clang_tidy.py - --clang_tidy_binary - ${CLANG_TIDY_BIN} - --exclude_globs - ${LINT_EXCLUSIONS_FILE} - --compile_commands - ${CMAKE_BINARY_DIR}/compile_commands.json - --source_dir - ${CMAKE_CURRENT_SOURCE_DIR}/src - --fix - ${MILVUS_LINT_QUIET}) - - # runs clang-tidy and exits with a non-zero exit code if any errors are found. - add_custom_target(check-clang-tidy - ${PYTHON_EXECUTABLE} - ${BUILD_SUPPORT_DIR}/run_clang_tidy.py - --clang_tidy_binary - ${CLANG_TIDY_BIN} - --exclude_globs - ${LINT_EXCLUSIONS_FILE} - --compile_commands - ${CMAKE_BINARY_DIR}/compile_commands.json - --source_dir - ${CMAKE_CURRENT_SOURCE_DIR}/src - ${MILVUS_LINT_QUIET}) -endif () diff --git a/core/build-support/code_style_clion.xml b/core/build-support/code_style_clion.xml deleted file mode 100644 index f2edeec3b6..0000000000 --- a/core/build-support/code_style_clion.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/core/build-support/cpplint.py b/core/build-support/cpplint.py deleted file mode 100755 index 8437d8cbaf..0000000000 --- a/core/build-support/cpplint.py +++ /dev/null @@ -1,6476 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2009 Google Inc. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -"""Does google-lint on c++ files. - -The goal of this script is to identify places in the code that *may* -be in non-compliance with google style. It does not attempt to fix -up these problems -- the point is to educate. It does also not -attempt to find all problems, or to ensure that everything it does -find is legitimately a problem. - -In particular, we can get very confused by /* and // inside strings! -We do a small hack, which is to ignore //'s with "'s after them on the -same line, but it is far from perfect (in either direction). -""" - -import codecs -import copy -import getopt -import glob -import itertools -import math # for log -import os -import re -import sre_compile -import string -import sys -import unicodedata -import xml.etree.ElementTree - -# if empty, use defaults -_header_extensions = set([]) - -# if empty, use defaults -_valid_extensions = set([]) - - -# Files with any of these extensions are considered to be -# header files (and will undergo different style checks). -# This set can be extended by using the --headers -# option (also supported in CPPLINT.cfg) -def GetHeaderExtensions(): - if not _header_extensions: - return set(['h', 'hpp', 'hxx', 'h++', 'cuh']) - return _header_extensions - -# The allowed extensions for file names -# This is set by --extensions flag -def GetAllExtensions(): - if not _valid_extensions: - return GetHeaderExtensions().union(set(['c', 'cc', 'cpp', 'cxx', 'c++', 'cu'])) - return _valid_extensions - -def GetNonHeaderExtensions(): - return GetAllExtensions().difference(GetHeaderExtensions()) - - -_USAGE = """ -Syntax: cpplint.py [--verbose=#] [--output=emacs|eclipse|vs7|junit] - [--filter=-x,+y,...] - [--counting=total|toplevel|detailed] [--repository=path] - [--root=subdir] [--linelength=digits] [--recursive] - [--exclude=path] - [--headers=ext1,ext2] - [--extensions=hpp,cpp,...] - [file] ... - - The style guidelines this tries to follow are those in - https://google.github.io/styleguide/cppguide.html - - Every problem is given a confidence score from 1-5, with 5 meaning we are - certain of the problem, and 1 meaning it could be a legitimate construct. - This will miss some errors, and is not a substitute for a code review. - - To suppress false-positive errors of a certain category, add a - 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) - suppresses errors of all categories on that line. - - The files passed in will be linted; at least one file must be provided. - Default linted extensions are %s. - Other file types will be ignored. - Change the extensions with the --extensions flag. - - Flags: - - output=emacs|eclipse|vs7|junit - By default, the output is formatted to ease emacs parsing. Output - compatible with eclipse (eclipse), Visual Studio (vs7), and JUnit - XML parsers such as those used in Jenkins and Bamboo may also be - used. Other formats are unsupported. - - verbose=# - Specify a number 0-5 to restrict errors to certain verbosity levels. - Errors with lower verbosity levels have lower confidence and are more - likely to be false positives. - - quiet - Supress output other than linting errors, such as information about - which files have been processed and excluded. - - filter=-x,+y,... - Specify a comma-separated list of category-filters to apply: only - error messages whose category names pass the filters will be printed. - (Category names are printed with the message and look like - "[whitespace/indent]".) Filters are evaluated left to right. - "-FOO" and "FOO" means "do not print categories that start with FOO". - "+FOO" means "do print categories that start with FOO". - - Examples: --filter=-whitespace,+whitespace/braces - --filter=whitespace,runtime/printf,+runtime/printf_format - --filter=-,+build/include_what_you_use - - To see a list of all the categories used in cpplint, pass no arg: - --filter= - - counting=total|toplevel|detailed - The total number of errors found is always printed. If - 'toplevel' is provided, then the count of errors in each of - the top-level categories like 'build' and 'whitespace' will - also be printed. If 'detailed' is provided, then a count - is provided for each category like 'build/class'. - - repository=path - The top level directory of the repository, used to derive the header - guard CPP variable. By default, this is determined by searching for a - path that contains .git, .hg, or .svn. When this flag is specified, the - given path is used instead. This option allows the header guard CPP - variable to remain consistent even if members of a team have different - repository root directories (such as when checking out a subdirectory - with SVN). In addition, users of non-mainstream version control systems - can use this flag to ensure readable header guard CPP variables. - - Examples: - Assuming that Alice checks out ProjectName and Bob checks out - ProjectName/trunk and trunk contains src/chrome/ui/browser.h, then - with no --repository flag, the header guard CPP variable will be: - - Alice => TRUNK_SRC_CHROME_BROWSER_UI_BROWSER_H_ - Bob => SRC_CHROME_BROWSER_UI_BROWSER_H_ - - If Alice uses the --repository=trunk flag and Bob omits the flag or - uses --repository=. then the header guard CPP variable will be: - - Alice => SRC_CHROME_BROWSER_UI_BROWSER_H_ - Bob => SRC_CHROME_BROWSER_UI_BROWSER_H_ - - root=subdir - The root directory used for deriving header guard CPP variables. This - directory is relative to the top level directory of the repository which - by default is determined by searching for a directory that contains .git, - .hg, or .svn but can also be controlled with the --repository flag. If - the specified directory does not exist, this flag is ignored. - - Examples: - Assuming that src is the top level directory of the repository, the - header guard CPP variables for src/chrome/browser/ui/browser.h are: - - No flag => CHROME_BROWSER_UI_BROWSER_H_ - --root=chrome => BROWSER_UI_BROWSER_H_ - --root=chrome/browser => UI_BROWSER_H_ - - linelength=digits - This is the allowed line length for the project. The default value is - 80 characters. - - Examples: - --linelength=120 - - recursive - Search for files to lint recursively. Each directory given in the list - of files to be linted is replaced by all files that descend from that - directory. Files with extensions not in the valid extensions list are - excluded. - - exclude=path - Exclude the given path from the list of files to be linted. Relative - paths are evaluated relative to the current directory and shell globbing - is performed. This flag can be provided multiple times to exclude - multiple files. - - Examples: - --exclude=one.cc - --exclude=src/*.cc - --exclude=src/*.cc --exclude=test/*.cc - - extensions=extension,extension,... - The allowed file extensions that cpplint will check - - Examples: - --extensions=%s - - headers=extension,extension,... - The allowed header extensions that cpplint will consider to be header files - (by default, only files with extensions %s - will be assumed to be headers) - - Examples: - --headers=%s - - cpplint.py supports per-directory configurations specified in CPPLINT.cfg - files. CPPLINT.cfg file can contain a number of key=value pairs. - Currently the following options are supported: - - set noparent - filter=+filter1,-filter2,... - exclude_files=regex - linelength=80 - root=subdir - - "set noparent" option prevents cpplint from traversing directory tree - upwards looking for more .cfg files in parent directories. This option - is usually placed in the top-level project directory. - - The "filter" option is similar in function to --filter flag. It specifies - message filters in addition to the |_DEFAULT_FILTERS| and those specified - through --filter command-line flag. - - "exclude_files" allows to specify a regular expression to be matched against - a file name. If the expression matches, the file is skipped and not run - through the linter. - - "linelength" specifies the allowed line length for the project. - - The "root" option is similar in function to the --root flag (see example - above). - - CPPLINT.cfg has an effect on files in the same directory and all - subdirectories, unless overridden by a nested configuration file. - - Example file: - filter=-build/include_order,+build/include_alpha - exclude_files=.*\\.cc - - The above example disables build/include_order warning and enables - build/include_alpha as well as excludes all .cc from being - processed by linter, in the current directory (where the .cfg - file is located) and all subdirectories. -""" % (list(GetAllExtensions()), - ','.join(list(GetAllExtensions())), - GetHeaderExtensions(), - ','.join(GetHeaderExtensions())) - -# We categorize each error message we print. Here are the categories. -# We want an explicit list so we can list them all in cpplint --filter=. -# If you add a new error message with a new category, add it to the list -# here! cpplint_unittest.py should tell you if you forget to do this. -_ERROR_CATEGORIES = [ - 'build/class', - 'build/c++11', - 'build/c++14', - 'build/c++tr1', - 'build/deprecated', - 'build/endif_comment', - 'build/explicit_make_pair', - 'build/forward_decl', - 'build/header_guard', - 'build/include', - 'build/include_subdir', - 'build/include_alpha', - 'build/include_order', - 'build/include_what_you_use', - 'build/namespaces_literals', - 'build/namespaces', - 'build/printf_format', - 'build/storage_class', - 'legal/copyright', - 'readability/alt_tokens', - 'readability/braces', - 'readability/casting', - 'readability/check', - 'readability/constructors', - 'readability/fn_size', - 'readability/inheritance', - 'readability/multiline_comment', - 'readability/multiline_string', - 'readability/namespace', - 'readability/nolint', - 'readability/nul', - 'readability/strings', - 'readability/todo', - 'readability/utf8', - 'runtime/arrays', - 'runtime/casting', - 'runtime/explicit', - 'runtime/int', - 'runtime/init', - 'runtime/invalid_increment', - 'runtime/member_string_references', - 'runtime/memset', - 'runtime/indentation_namespace', - 'runtime/operator', - 'runtime/printf', - 'runtime/printf_format', - 'runtime/references', - 'runtime/string', - 'runtime/threadsafe_fn', - 'runtime/vlog', - 'whitespace/blank_line', - 'whitespace/braces', - 'whitespace/comma', - 'whitespace/comments', - 'whitespace/empty_conditional_body', - 'whitespace/empty_if_body', - 'whitespace/empty_loop_body', - 'whitespace/end_of_line', - 'whitespace/ending_newline', - 'whitespace/forcolon', - 'whitespace/indent', - 'whitespace/line_length', - 'whitespace/newline', - 'whitespace/operators', - 'whitespace/parens', - 'whitespace/semicolon', - 'whitespace/tab', - 'whitespace/todo', - ] - -# These error categories are no longer enforced by cpplint, but for backwards- -# compatibility they may still appear in NOLINT comments. -_LEGACY_ERROR_CATEGORIES = [ - 'readability/streams', - 'readability/function', - ] - -# The default state of the category filter. This is overridden by the --filter= -# flag. By default all errors are on, so only add here categories that should be -# off by default (i.e., categories that must be enabled by the --filter= flags). -# All entries here should start with a '-' or '+', as in the --filter= flag. -_DEFAULT_FILTERS = ['-build/include_alpha'] - -# The default list of categories suppressed for C (not C++) files. -_DEFAULT_C_SUPPRESSED_CATEGORIES = [ - 'readability/casting', - ] - -# The default list of categories suppressed for Linux Kernel files. -_DEFAULT_KERNEL_SUPPRESSED_CATEGORIES = [ - 'whitespace/tab', - ] - -# We used to check for high-bit characters, but after much discussion we -# decided those were OK, as long as they were in UTF-8 and didn't represent -# hard-coded international strings, which belong in a separate i18n file. - -# C++ headers -_CPP_HEADERS = frozenset([ - # Legacy - 'algobase.h', - 'algo.h', - 'alloc.h', - 'builtinbuf.h', - 'bvector.h', - 'complex.h', - 'defalloc.h', - 'deque.h', - 'editbuf.h', - 'fstream.h', - 'function.h', - 'hash_map', - 'hash_map.h', - 'hash_set', - 'hash_set.h', - 'hashtable.h', - 'heap.h', - 'indstream.h', - 'iomanip.h', - 'iostream.h', - 'istream.h', - 'iterator.h', - 'list.h', - 'map.h', - 'multimap.h', - 'multiset.h', - 'ostream.h', - 'pair.h', - 'parsestream.h', - 'pfstream.h', - 'procbuf.h', - 'pthread_alloc', - 'pthread_alloc.h', - 'rope', - 'rope.h', - 'ropeimpl.h', - 'set.h', - 'slist', - 'slist.h', - 'stack.h', - 'stdiostream.h', - 'stl_alloc.h', - 'stl_relops.h', - 'streambuf.h', - 'stream.h', - 'strfile.h', - 'strstream.h', - 'tempbuf.h', - 'tree.h', - 'type_traits.h', - 'vector.h', - # 17.6.1.2 C++ library headers - 'algorithm', - 'array', - 'atomic', - 'bitset', - 'chrono', - 'codecvt', - 'complex', - 'condition_variable', - 'deque', - 'exception', - 'forward_list', - 'fstream', - 'functional', - 'future', - 'initializer_list', - 'iomanip', - 'ios', - 'iosfwd', - 'iostream', - 'istream', - 'iterator', - 'limits', - 'list', - 'locale', - 'map', - 'memory', - 'mutex', - 'new', - 'numeric', - 'ostream', - 'queue', - 'random', - 'ratio', - 'regex', - 'scoped_allocator', - 'set', - 'sstream', - 'stack', - 'stdexcept', - 'streambuf', - 'string', - 'strstream', - 'system_error', - 'thread', - 'tuple', - 'typeindex', - 'typeinfo', - 'type_traits', - 'unordered_map', - 'unordered_set', - 'utility', - 'valarray', - 'vector', - # 17.6.1.2 C++ headers for C library facilities - 'cassert', - 'ccomplex', - 'cctype', - 'cerrno', - 'cfenv', - 'cfloat', - 'cinttypes', - 'ciso646', - 'climits', - 'clocale', - 'cmath', - 'csetjmp', - 'csignal', - 'cstdalign', - 'cstdarg', - 'cstdbool', - 'cstddef', - 'cstdint', - 'cstdio', - 'cstdlib', - 'cstring', - 'ctgmath', - 'ctime', - 'cuchar', - 'cwchar', - 'cwctype', - ]) - -# Type names -_TYPES = re.compile( - r'^(?:' - # [dcl.type.simple] - r'(char(16_t|32_t)?)|wchar_t|' - r'bool|short|int|long|signed|unsigned|float|double|' - # [support.types] - r'(ptrdiff_t|size_t|max_align_t|nullptr_t)|' - # [cstdint.syn] - r'(u?int(_fast|_least)?(8|16|32|64)_t)|' - r'(u?int(max|ptr)_t)|' - r')$') - - -# These headers are excluded from [build/include] and [build/include_order] -# checks: -# - Anything not following google file name conventions (containing an -# uppercase character, such as Python.h or nsStringAPI.h, for example). -# - Lua headers. -_THIRD_PARTY_HEADERS_PATTERN = re.compile( - r'^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$') - -# Pattern for matching FileInfo.BaseName() against test file name -_test_suffixes = ['_test', '_regtest', '_unittest'] -_TEST_FILE_SUFFIX = '(' + '|'.join(_test_suffixes) + r')$' - -# Pattern that matches only complete whitespace, possibly across multiple lines. -_EMPTY_CONDITIONAL_BODY_PATTERN = re.compile(r'^\s*$', re.DOTALL) - -# Assertion macros. These are defined in base/logging.h and -# testing/base/public/gunit.h. -_CHECK_MACROS = [ - 'DCHECK', 'CHECK', - 'EXPECT_TRUE', 'ASSERT_TRUE', - 'EXPECT_FALSE', 'ASSERT_FALSE', - ] - -# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE -_CHECK_REPLACEMENT = dict([(macro_var, {}) for macro_var in _CHECK_MACROS]) - -for op, replacement in [('==', 'EQ'), ('!=', 'NE'), - ('>=', 'GE'), ('>', 'GT'), - ('<=', 'LE'), ('<', 'LT')]: - _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement - _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement - _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement - _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement - -for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), - ('>=', 'LT'), ('>', 'LE'), - ('<=', 'GT'), ('<', 'GE')]: - _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement - _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement - -# Alternative tokens and their replacements. For full list, see section 2.5 -# Alternative tokens [lex.digraph] in the C++ standard. -# -# Digraphs (such as '%:') are not included here since it's a mess to -# match those on a word boundary. -_ALT_TOKEN_REPLACEMENT = { - 'and': '&&', - 'bitor': '|', - 'or': '||', - 'xor': '^', - 'compl': '~', - 'bitand': '&', - 'and_eq': '&=', - 'or_eq': '|=', - 'xor_eq': '^=', - 'not': '!', - 'not_eq': '!=' - } - -# Compile regular expression that matches all the above keywords. The "[ =()]" -# bit is meant to avoid matching these keywords outside of boolean expressions. -# -# False positives include C-style multi-line comments and multi-line strings -# but those have always been troublesome for cpplint. -_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( - r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)') - - -# These constants define types of headers for use with -# _IncludeState.CheckNextIncludeOrder(). -_C_SYS_HEADER = 1 -_CPP_SYS_HEADER = 2 -_LIKELY_MY_HEADER = 3 -_POSSIBLE_MY_HEADER = 4 -_OTHER_HEADER = 5 - -# These constants define the current inline assembly state -_NO_ASM = 0 # Outside of inline assembly block -_INSIDE_ASM = 1 # Inside inline assembly block -_END_ASM = 2 # Last line of inline assembly block -_BLOCK_ASM = 3 # The whole block is an inline assembly block - -# Match start of assembly blocks -_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)' - r'(?:\s+(volatile|__volatile__))?' - r'\s*[{(]') - -# Match strings that indicate we're working on a C (not C++) file. -_SEARCH_C_FILE = re.compile(r'\b(?:LINT_C_FILE|' - r'vim?:\s*.*(\s*|:)filetype=c(\s*|:|$))') - -# Match string that indicates we're working on a Linux Kernel file. -_SEARCH_KERNEL_FILE = re.compile(r'\b(?:LINT_KERNEL_FILE)') - -_regexp_compile_cache = {} - -# {str, set(int)}: a map from error categories to sets of linenumbers -# on which those errors are expected and should be suppressed. -_error_suppressions = {} - -# The root directory used for deriving header guard CPP variable. -# This is set by --root flag. -_root = None - -# The top level repository directory. If set, _root is calculated relative to -# this directory instead of the directory containing version control artifacts. -# This is set by the --repository flag. -_repository = None - -# Files to exclude from linting. This is set by the --exclude flag. -_excludes = None - -# Whether to supress PrintInfo messages -_quiet = False - -# The allowed line length of files. -# This is set by --linelength flag. -_line_length = 80 - -try: - xrange(1, 0) -except NameError: - # -- pylint: disable=redefined-builtin - xrange = range - -try: - unicode -except NameError: - # -- pylint: disable=redefined-builtin - basestring = unicode = str - -try: - long(2) -except NameError: - # -- pylint: disable=redefined-builtin - long = int - -if sys.version_info < (3,): - # -- pylint: disable=no-member - # BINARY_TYPE = str - itervalues = dict.itervalues - iteritems = dict.iteritems -else: - # BINARY_TYPE = bytes - itervalues = dict.values - iteritems = dict.items - -def unicode_escape_decode(x): - if sys.version_info < (3,): - return codecs.unicode_escape_decode(x)[0] - else: - return x - -# {str, bool}: a map from error categories to booleans which indicate if the -# category should be suppressed for every line. -_global_error_suppressions = {} - - - - -def ParseNolintSuppressions(filename, raw_line, linenum, error): - """Updates the global list of line error-suppressions. - - Parses any NOLINT comments on the current line, updating the global - error_suppressions store. Reports an error if the NOLINT comment - was malformed. - - Args: - filename: str, the name of the input file. - raw_line: str, the line of input text, with comments. - linenum: int, the number of the current line. - error: function, an error handler. - """ - matched = Search(r'\bNOLINT(NEXTLINE)?\b(\([^)]+\))?', raw_line) - if matched: - if matched.group(1): - suppressed_line = linenum + 1 - else: - suppressed_line = linenum - category = matched.group(2) - if category in (None, '(*)'): # => "suppress all" - _error_suppressions.setdefault(None, set()).add(suppressed_line) - else: - if category.startswith('(') and category.endswith(')'): - category = category[1:-1] - if category in _ERROR_CATEGORIES: - _error_suppressions.setdefault(category, set()).add(suppressed_line) - elif category not in _LEGACY_ERROR_CATEGORIES: - error(filename, linenum, 'readability/nolint', 5, - 'Unknown NOLINT error category: %s' % category) - - -def ProcessGlobalSuppresions(lines): - """Updates the list of global error suppressions. - - Parses any lint directives in the file that have global effect. - - Args: - lines: An array of strings, each representing a line of the file, with the - last element being empty if the file is terminated with a newline. - """ - for line in lines: - if _SEARCH_C_FILE.search(line): - for category in _DEFAULT_C_SUPPRESSED_CATEGORIES: - _global_error_suppressions[category] = True - if _SEARCH_KERNEL_FILE.search(line): - for category in _DEFAULT_KERNEL_SUPPRESSED_CATEGORIES: - _global_error_suppressions[category] = True - - -def ResetNolintSuppressions(): - """Resets the set of NOLINT suppressions to empty.""" - _error_suppressions.clear() - _global_error_suppressions.clear() - - -def IsErrorSuppressedByNolint(category, linenum): - """Returns true if the specified error category is suppressed on this line. - - Consults the global error_suppressions map populated by - ParseNolintSuppressions/ProcessGlobalSuppresions/ResetNolintSuppressions. - - Args: - category: str, the category of the error. - linenum: int, the current line number. - Returns: - bool, True iff the error should be suppressed due to a NOLINT comment or - global suppression. - """ - return (_global_error_suppressions.get(category, False) or - linenum in _error_suppressions.get(category, set()) or - linenum in _error_suppressions.get(None, set())) - - -def Match(pattern, s): - """Matches the string with the pattern, caching the compiled regexp.""" - # The regexp compilation caching is inlined in both Match and Search for - # performance reasons; factoring it out into a separate function turns out - # to be noticeably expensive. - if pattern not in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].match(s) - - -def ReplaceAll(pattern, rep, s): - """Replaces instances of pattern in a string with a replacement. - - The compiled regex is kept in a cache shared by Match and Search. - - Args: - pattern: regex pattern - rep: replacement text - s: search string - - Returns: - string with replacements made (or original string if no replacements) - """ - if pattern not in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].sub(rep, s) - - -def Search(pattern, s): - """Searches the string for the pattern, caching the compiled regexp.""" - if pattern not in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) - return _regexp_compile_cache[pattern].search(s) - - -def _IsSourceExtension(s): - """File extension (excluding dot) matches a source file extension.""" - return s in GetNonHeaderExtensions() - - -class _IncludeState(object): - """Tracks line numbers for includes, and the order in which includes appear. - - include_list contains list of lists of (header, line number) pairs. - It's a lists of lists rather than just one flat list to make it - easier to update across preprocessor boundaries. - - Call CheckNextIncludeOrder() once for each header in the file, passing - in the type constants defined above. Calls in an illegal order will - raise an _IncludeError with an appropriate error message. - - """ - # self._section will move monotonically through this set. If it ever - # needs to move backwards, CheckNextIncludeOrder will raise an error. - _INITIAL_SECTION = 0 - _MY_H_SECTION = 1 - _C_SECTION = 2 - _CPP_SECTION = 3 - _OTHER_H_SECTION = 4 - - _TYPE_NAMES = { - _C_SYS_HEADER: 'C system header', - _CPP_SYS_HEADER: 'C++ system header', - _LIKELY_MY_HEADER: 'header this file implements', - _POSSIBLE_MY_HEADER: 'header this file may implement', - _OTHER_HEADER: 'other header', - } - _SECTION_NAMES = { - _INITIAL_SECTION: "... nothing. (This can't be an error.)", - _MY_H_SECTION: 'a header this file implements', - _C_SECTION: 'C system header', - _CPP_SECTION: 'C++ system header', - _OTHER_H_SECTION: 'other header', - } - - def __init__(self): - self.include_list = [[]] - self._section = None - self._last_header = None - self.ResetSection('') - - def FindHeader(self, header): - """Check if a header has already been included. - - Args: - header: header to check. - Returns: - Line number of previous occurrence, or -1 if the header has not - been seen before. - """ - for section_list in self.include_list: - for f in section_list: - if f[0] == header: - return f[1] - return -1 - - def ResetSection(self, directive): - """Reset section checking for preprocessor directive. - - Args: - directive: preprocessor directive (e.g. "if", "else"). - """ - # The name of the current section. - self._section = self._INITIAL_SECTION - # The path of last found header. - self._last_header = '' - - # Update list of includes. Note that we never pop from the - # include list. - if directive in ('if', 'ifdef', 'ifndef'): - self.include_list.append([]) - elif directive in ('else', 'elif'): - self.include_list[-1] = [] - - def SetLastHeader(self, header_path): - self._last_header = header_path - - def CanonicalizeAlphabeticalOrder(self, header_path): - """Returns a path canonicalized for alphabetical comparison. - - - replaces "-" with "_" so they both cmp the same. - - removes '-inl' since we don't require them to be after the main header. - - lowercase everything, just in case. - - Args: - header_path: Path to be canonicalized. - - Returns: - Canonicalized path. - """ - return header_path.replace('-inl.h', '.h').replace('-', '_').lower() - - def IsInAlphabeticalOrder(self, clean_lines, linenum, header_path): - """Check if a header is in alphabetical order with the previous header. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - header_path: Canonicalized header to be checked. - - Returns: - Returns true if the header is in alphabetical order. - """ - # If previous section is different from current section, _last_header will - # be reset to empty string, so it's always less than current header. - # - # If previous line was a blank line, assume that the headers are - # intentionally sorted the way they are. - if (self._last_header > header_path and - Match(r'^\s*#\s*include\b', clean_lines.elided[linenum - 1])): - return False - return True - - def CheckNextIncludeOrder(self, header_type): - """Returns a non-empty error message if the next header is out of order. - - This function also updates the internal state to be ready to check - the next include. - - Args: - header_type: One of the _XXX_HEADER constants defined above. - - Returns: - The empty string if the header is in the right order, or an - error message describing what's wrong. - - """ - error_message = ('Found %s after %s' % - (self._TYPE_NAMES[header_type], - self._SECTION_NAMES[self._section])) - - last_section = self._section - - if header_type == _C_SYS_HEADER: - if self._section <= self._C_SECTION: - self._section = self._C_SECTION - else: - self._last_header = '' - return error_message - elif header_type == _CPP_SYS_HEADER: - if self._section <= self._CPP_SECTION: - self._section = self._CPP_SECTION - else: - self._last_header = '' - return error_message - elif header_type == _LIKELY_MY_HEADER: - if self._section <= self._MY_H_SECTION: - self._section = self._MY_H_SECTION - else: - self._section = self._OTHER_H_SECTION - elif header_type == _POSSIBLE_MY_HEADER: - if self._section <= self._MY_H_SECTION: - self._section = self._MY_H_SECTION - else: - # This will always be the fallback because we're not sure - # enough that the header is associated with this file. - self._section = self._OTHER_H_SECTION - else: - assert header_type == _OTHER_HEADER - self._section = self._OTHER_H_SECTION - - if last_section != self._section: - self._last_header = '' - - return '' - - -class _CppLintState(object): - """Maintains module-wide state..""" - - def __init__(self): - self.verbose_level = 1 # global setting. - self.error_count = 0 # global count of reported errors - # filters to apply when emitting error messages - self.filters = _DEFAULT_FILTERS[:] - # backup of filter list. Used to restore the state after each file. - self._filters_backup = self.filters[:] - self.counting = 'total' # In what way are we counting errors? - self.errors_by_category = {} # string to int dict storing error counts - - # output format: - # "emacs" - format that emacs can parse (default) - # "eclipse" - format that eclipse can parse - # "vs7" - format that Microsoft Visual Studio 7 can parse - # "junit" - format that Jenkins, Bamboo, etc can parse - self.output_format = 'emacs' - - # For JUnit output, save errors and failures until the end so that they - # can be written into the XML - self._junit_errors = [] - self._junit_failures = [] - - def SetOutputFormat(self, output_format): - """Sets the output format for errors.""" - self.output_format = output_format - - def SetVerboseLevel(self, level): - """Sets the module's verbosity, and returns the previous setting.""" - last_verbose_level = self.verbose_level - self.verbose_level = level - return last_verbose_level - - def SetCountingStyle(self, counting_style): - """Sets the module's counting options.""" - self.counting = counting_style - - def SetFilters(self, filters): - """Sets the error-message filters. - - These filters are applied when deciding whether to emit a given - error message. - - Args: - filters: A string of comma-separated filters (eg "+whitespace/indent"). - Each filter should start with + or -; else we die. - - Raises: - ValueError: The comma-separated filters did not all start with '+' or '-'. - E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" - """ - # Default filters always have less priority than the flag ones. - self.filters = _DEFAULT_FILTERS[:] - self.AddFilters(filters) - - def AddFilters(self, filters): - """ Adds more filters to the existing list of error-message filters. """ - for filt in filters.split(','): - clean_filt = filt.strip() - if clean_filt: - self.filters.append(clean_filt) - for filt in self.filters: - if not (filt.startswith('+') or filt.startswith('-')): - raise ValueError('Every filter in --filters must start with + or -' - ' (%s does not)' % filt) - - def BackupFilters(self): - """ Saves the current filter list to backup storage.""" - self._filters_backup = self.filters[:] - - def RestoreFilters(self): - """ Restores filters previously backed up.""" - self.filters = self._filters_backup[:] - - def ResetErrorCounts(self): - """Sets the module's error statistic back to zero.""" - self.error_count = 0 - self.errors_by_category = {} - - def IncrementErrorCount(self, category): - """Bumps the module's error statistic.""" - self.error_count += 1 - if self.counting in ('toplevel', 'detailed'): - if self.counting != 'detailed': - category = category.split('/')[0] - if category not in self.errors_by_category: - self.errors_by_category[category] = 0 - self.errors_by_category[category] += 1 - - def PrintErrorCounts(self): - """Print a summary of errors by category, and the total.""" - for category, count in sorted(iteritems(self.errors_by_category)): - self.PrintInfo('Category \'%s\' errors found: %d\n' % - (category, count)) - if self.error_count > 0: - self.PrintInfo('Total errors found: %d\n' % self.error_count) - - def PrintInfo(self, message): - if not _quiet and self.output_format != 'junit': - sys.stderr.write(message) - - def PrintError(self, message): - if self.output_format == 'junit': - self._junit_errors.append(message) - else: - sys.stderr.write(message) - - def AddJUnitFailure(self, filename, linenum, message, category, confidence): - self._junit_failures.append((filename, linenum, message, category, - confidence)) - - def FormatJUnitXML(self): - num_errors = len(self._junit_errors) - num_failures = len(self._junit_failures) - - testsuite = xml.etree.ElementTree.Element('testsuite') - testsuite.attrib['name'] = 'cpplint' - testsuite.attrib['errors'] = str(num_errors) - testsuite.attrib['failures'] = str(num_failures) - - if num_errors == 0 and num_failures == 0: - testsuite.attrib['tests'] = str(1) - xml.etree.ElementTree.SubElement(testsuite, 'testcase', name='passed') - - else: - testsuite.attrib['tests'] = str(num_errors + num_failures) - if num_errors > 0: - testcase = xml.etree.ElementTree.SubElement(testsuite, 'testcase') - testcase.attrib['name'] = 'errors' - error = xml.etree.ElementTree.SubElement(testcase, 'error') - error.text = '\n'.join(self._junit_errors) - if num_failures > 0: - # Group failures by file - failed_file_order = [] - failures_by_file = {} - for failure in self._junit_failures: - failed_file = failure[0] - if failed_file not in failed_file_order: - failed_file_order.append(failed_file) - failures_by_file[failed_file] = [] - failures_by_file[failed_file].append(failure) - # Create a testcase for each file - for failed_file in failed_file_order: - failures = failures_by_file[failed_file] - testcase = xml.etree.ElementTree.SubElement(testsuite, 'testcase') - testcase.attrib['name'] = failed_file - failure = xml.etree.ElementTree.SubElement(testcase, 'failure') - template = '{0}: {1} [{2}] [{3}]' - texts = [template.format(f[1], f[2], f[3], f[4]) for f in failures] - failure.text = '\n'.join(texts) - - xml_decl = '\n' - return xml_decl + xml.etree.ElementTree.tostring(testsuite, 'utf-8').decode('utf-8') - - -_cpplint_state = _CppLintState() - - -def _OutputFormat(): - """Gets the module's output format.""" - return _cpplint_state.output_format - - -def _SetOutputFormat(output_format): - """Sets the module's output format.""" - _cpplint_state.SetOutputFormat(output_format) - - -def _VerboseLevel(): - """Returns the module's verbosity setting.""" - return _cpplint_state.verbose_level - - -def _SetVerboseLevel(level): - """Sets the module's verbosity, and returns the previous setting.""" - return _cpplint_state.SetVerboseLevel(level) - - -def _SetCountingStyle(level): - """Sets the module's counting options.""" - _cpplint_state.SetCountingStyle(level) - - -def _Filters(): - """Returns the module's list of output filters, as a list.""" - return _cpplint_state.filters - - -def _SetFilters(filters): - """Sets the module's error-message filters. - - These filters are applied when deciding whether to emit a given - error message. - - Args: - filters: A string of comma-separated filters (eg "whitespace/indent"). - Each filter should start with + or -; else we die. - """ - _cpplint_state.SetFilters(filters) - -def _AddFilters(filters): - """Adds more filter overrides. - - Unlike _SetFilters, this function does not reset the current list of filters - available. - - Args: - filters: A string of comma-separated filters (eg "whitespace/indent"). - Each filter should start with + or -; else we die. - """ - _cpplint_state.AddFilters(filters) - -def _BackupFilters(): - """ Saves the current filter list to backup storage.""" - _cpplint_state.BackupFilters() - -def _RestoreFilters(): - """ Restores filters previously backed up.""" - _cpplint_state.RestoreFilters() - -class _FunctionState(object): - """Tracks current function name and the number of lines in its body.""" - - _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. - _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. - - def __init__(self): - self.in_a_function = False - self.lines_in_function = 0 - self.current_function = '' - - def Begin(self, function_name): - """Start analyzing function body. - - Args: - function_name: The name of the function being tracked. - """ - self.in_a_function = True - self.lines_in_function = 0 - self.current_function = function_name - - def Count(self): - """Count line in current function body.""" - if self.in_a_function: - self.lines_in_function += 1 - - def Check(self, error, filename, linenum): - """Report if too many lines in function body. - - Args: - error: The function to call with any errors found. - filename: The name of the current file. - linenum: The number of the line to check. - """ - if not self.in_a_function: - return - - if Match(r'T(EST|est)', self.current_function): - base_trigger = self._TEST_TRIGGER - else: - base_trigger = self._NORMAL_TRIGGER - trigger = base_trigger * 2**_VerboseLevel() - - if self.lines_in_function > trigger: - error_level = int(math.log(self.lines_in_function / base_trigger, 2)) - # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... - if error_level > 5: - error_level = 5 - error(filename, linenum, 'readability/fn_size', error_level, - 'Small and focused functions are preferred:' - ' %s has %d non-comment lines' - ' (error triggered by exceeding %d lines).' % ( - self.current_function, self.lines_in_function, trigger)) - - def End(self): - """Stop analyzing function body.""" - self.in_a_function = False - - -class _IncludeError(Exception): - """Indicates a problem with the include order in a file.""" - pass - - -class FileInfo(object): - """Provides utility functions for filenames. - - FileInfo provides easy access to the components of a file's path - relative to the project root. - """ - - def __init__(self, filename): - self._filename = filename - - def FullName(self): - """Make Windows paths like Unix.""" - return os.path.abspath(self._filename).replace('\\', '/') - - def RepositoryName(self): - r"""FullName after removing the local path to the repository. - - If we have a real absolute path name here we can try to do something smart: - detecting the root of the checkout and truncating /path/to/checkout from - the name so that we get header guards that don't include things like - "C:\Documents and Settings\..." or "/home/username/..." in them and thus - people on different computers who have checked the source out to different - locations won't see bogus errors. - """ - fullname = self.FullName() - - if os.path.exists(fullname): - project_dir = os.path.dirname(fullname) - - # If the user specified a repository path, it exists, and the file is - # contained in it, use the specified repository path - if _repository: - repo = FileInfo(_repository).FullName() - root_dir = project_dir - while os.path.exists(root_dir): - # allow case insensitive compare on Windows - if os.path.normcase(root_dir) == os.path.normcase(repo): - return os.path.relpath(fullname, root_dir).replace('\\', '/') - one_up_dir = os.path.dirname(root_dir) - if one_up_dir == root_dir: - break - root_dir = one_up_dir - - if os.path.exists(os.path.join(project_dir, ".svn")): - # If there's a .svn file in the current directory, we recursively look - # up the directory tree for the top of the SVN checkout - root_dir = project_dir - one_up_dir = os.path.dirname(root_dir) - while os.path.exists(os.path.join(one_up_dir, ".svn")): - root_dir = os.path.dirname(root_dir) - one_up_dir = os.path.dirname(one_up_dir) - - prefix = os.path.commonprefix([root_dir, project_dir]) - return fullname[len(prefix) + 1:] - - # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by - # searching up from the current path. - root_dir = current_dir = os.path.dirname(fullname) - while current_dir != os.path.dirname(current_dir): - if (os.path.exists(os.path.join(current_dir, ".git")) or - os.path.exists(os.path.join(current_dir, ".hg")) or - os.path.exists(os.path.join(current_dir, ".svn"))): - root_dir = current_dir - current_dir = os.path.dirname(current_dir) - - if (os.path.exists(os.path.join(root_dir, ".git")) or - os.path.exists(os.path.join(root_dir, ".hg")) or - os.path.exists(os.path.join(root_dir, ".svn"))): - prefix = os.path.commonprefix([root_dir, project_dir]) - return fullname[len(prefix) + 1:] - - # Don't know what to do; header guard warnings may be wrong... - return fullname - - def Split(self): - """Splits the file into the directory, basename, and extension. - - For 'chrome/browser/browser.cc', Split() would - return ('chrome/browser', 'browser', '.cc') - - Returns: - A tuple of (directory, basename, extension). - """ - - googlename = self.RepositoryName() - project, rest = os.path.split(googlename) - return (project,) + os.path.splitext(rest) - - def BaseName(self): - """File base name - text after the final slash, before the final period.""" - return self.Split()[1] - - def Extension(self): - """File extension - text following the final period, includes that period.""" - return self.Split()[2] - - def NoExtension(self): - """File has no source file extension.""" - return '/'.join(self.Split()[0:2]) - - def IsSource(self): - """File has a source file extension.""" - return _IsSourceExtension(self.Extension()[1:]) - - -def _ShouldPrintError(category, confidence, linenum): - """If confidence >= verbose, category passes filter and is not suppressed.""" - - # There are three ways we might decide not to print an error message: - # a "NOLINT(category)" comment appears in the source, - # the verbosity level isn't high enough, or the filters filter it out. - if IsErrorSuppressedByNolint(category, linenum): - return False - - if confidence < _cpplint_state.verbose_level: - return False - - is_filtered = False - for one_filter in _Filters(): - if one_filter.startswith('-'): - if category.startswith(one_filter[1:]): - is_filtered = True - elif one_filter.startswith('+'): - if category.startswith(one_filter[1:]): - is_filtered = False - else: - assert False # should have been checked for in SetFilter. - if is_filtered: - return False - - return True - - -def Error(filename, linenum, category, confidence, message): - """Logs the fact we've found a lint error. - - We log where the error was found, and also our confidence in the error, - that is, how certain we are this is a legitimate style regression, and - not a misidentification or a use that's sometimes justified. - - False positives can be suppressed by the use of - "cpplint(category)" comments on the offending line. These are - parsed into _error_suppressions. - - Args: - filename: The name of the file containing the error. - linenum: The number of the line containing the error. - category: A string used to describe the "category" this bug - falls under: "whitespace", say, or "runtime". Categories - may have a hierarchy separated by slashes: "whitespace/indent". - confidence: A number from 1-5 representing a confidence score for - the error, with 5 meaning that we are certain of the problem, - and 1 meaning that it could be a legitimate construct. - message: The error message. - """ - if _ShouldPrintError(category, confidence, linenum): - _cpplint_state.IncrementErrorCount(category) - if _cpplint_state.output_format == 'vs7': - _cpplint_state.PrintError('%s(%s): warning: %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) - elif _cpplint_state.output_format == 'eclipse': - sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence)) - elif _cpplint_state.output_format == 'junit': - _cpplint_state.AddJUnitFailure(filename, linenum, message, category, - confidence) - else: - final_message = '%s:%s: %s [%s] [%d]\n' % ( - filename, linenum, message, category, confidence) - sys.stderr.write(final_message) - -# Matches standard C++ escape sequences per 2.13.2.3 of the C++ standard. -_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( - r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') -# Match a single C style comment on the same line. -_RE_PATTERN_C_COMMENTS = r'/\*(?:[^*]|\*(?!/))*\*/' -# Matches multi-line C style comments. -# This RE is a little bit more complicated than one might expect, because we -# have to take care of space removals tools so we can handle comments inside -# statements better. -# The current rule is: We only clear spaces from both sides when we're at the -# end of the line. Otherwise, we try to remove spaces from the right side, -# if this doesn't work we try on left side but only if there's a non-character -# on the right. -_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( - r'(\s*' + _RE_PATTERN_C_COMMENTS + r'\s*$|' + - _RE_PATTERN_C_COMMENTS + r'\s+|' + - r'\s+' + _RE_PATTERN_C_COMMENTS + r'(?=\W)|' + - _RE_PATTERN_C_COMMENTS + r')') - - -def IsCppString(line): - """Does line terminate so, that the next symbol is in string constant. - - This function does not consider single-line nor multi-line comments. - - Args: - line: is a partial line of code starting from the 0..n. - - Returns: - True, if next character appended to 'line' is inside a - string constant. - """ - - line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" - return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 - - -def CleanseRawStrings(raw_lines): - """Removes C++11 raw strings from lines. - - Before: - static const char kData[] = R"( - multi-line string - )"; - - After: - static const char kData[] = "" - (replaced by blank line) - ""; - - Args: - raw_lines: list of raw lines. - - Returns: - list of lines with C++11 raw strings replaced by empty strings. - """ - - delimiter = None - lines_without_raw_strings = [] - for line in raw_lines: - if delimiter: - # Inside a raw string, look for the end - end = line.find(delimiter) - if end >= 0: - # Found the end of the string, match leading space for this - # line and resume copying the original lines, and also insert - # a "" on the last line. - leading_space = Match(r'^(\s*)\S', line) - line = leading_space.group(1) + '""' + line[end + len(delimiter):] - delimiter = None - else: - # Haven't found the end yet, append a blank line. - line = '""' - - # Look for beginning of a raw string, and replace them with - # empty strings. This is done in a loop to handle multiple raw - # strings on the same line. - while delimiter is None: - # Look for beginning of a raw string. - # See 2.14.15 [lex.string] for syntax. - # - # Once we have matched a raw string, we check the prefix of the - # line to make sure that the line is not part of a single line - # comment. It's done this way because we remove raw strings - # before removing comments as opposed to removing comments - # before removing raw strings. This is because there are some - # cpplint checks that requires the comments to be preserved, but - # we don't want to check comments that are inside raw strings. - matched = Match(r'^(.*?)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line) - if (matched and - not Match(r'^([^\'"]|\'(\\.|[^\'])*\'|"(\\.|[^"])*")*//', - matched.group(1))): - delimiter = ')' + matched.group(2) + '"' - - end = matched.group(3).find(delimiter) - if end >= 0: - # Raw string ended on same line - line = (matched.group(1) + '""' + - matched.group(3)[end + len(delimiter):]) - delimiter = None - else: - # Start of a multi-line raw string - line = matched.group(1) + '""' - else: - break - - lines_without_raw_strings.append(line) - - # TODO(unknown): if delimiter is not None here, we might want to - # emit a warning for unterminated string. - return lines_without_raw_strings - - -def FindNextMultiLineCommentStart(lines, lineix): - """Find the beginning marker for a multiline comment.""" - while lineix < len(lines): - if lines[lineix].strip().startswith('/*'): - # Only return this marker if the comment goes beyond this line - if lines[lineix].strip().find('*/', 2) < 0: - return lineix - lineix += 1 - return len(lines) - - -def FindNextMultiLineCommentEnd(lines, lineix): - """We are inside a comment, find the end marker.""" - while lineix < len(lines): - if lines[lineix].strip().endswith('*/'): - return lineix - lineix += 1 - return len(lines) - - -def RemoveMultiLineCommentsFromRange(lines, begin, end): - """Clears a range of lines for multi-line comments.""" - # Having // dummy comments makes the lines non-empty, so we will not get - # unnecessary blank line warnings later in the code. - for i in range(begin, end): - lines[i] = '/**/' - - -def RemoveMultiLineComments(filename, lines, error): - """Removes multiline (c-style) comments from lines.""" - lineix = 0 - while lineix < len(lines): - lineix_begin = FindNextMultiLineCommentStart(lines, lineix) - if lineix_begin >= len(lines): - return - lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) - if lineix_end >= len(lines): - error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, - 'Could not find end of multi-line comment') - return - RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) - lineix = lineix_end + 1 - - -def CleanseComments(line): - """Removes //-comments and single-line C-style /* */ comments. - - Args: - line: A line of C++ source. - - Returns: - The line with single-line comments removed. - """ - commentpos = line.find('//') - if commentpos != -1 and not IsCppString(line[:commentpos]): - line = line[:commentpos].rstrip() - # get rid of /* ... */ - return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) - - -class CleansedLines(object): - """Holds 4 copies of all lines with different preprocessing applied to them. - - 1) elided member contains lines without strings and comments. - 2) lines member contains lines without comments. - 3) raw_lines member contains all the lines without processing. - 4) lines_without_raw_strings member is same as raw_lines, but with C++11 raw - strings removed. - All these members are of , and of the same length. - """ - - def __init__(self, lines): - self.elided = [] - self.lines = [] - self.raw_lines = lines - self.num_lines = len(lines) - self.lines_without_raw_strings = CleanseRawStrings(lines) - for linenum in range(len(self.lines_without_raw_strings)): - self.lines.append(CleanseComments( - self.lines_without_raw_strings[linenum])) - elided = self._CollapseStrings(self.lines_without_raw_strings[linenum]) - self.elided.append(CleanseComments(elided)) - - def NumLines(self): - """Returns the number of lines represented.""" - return self.num_lines - - @staticmethod - def _CollapseStrings(elided): - """Collapses strings and chars on a line to simple "" or '' blocks. - - We nix strings first so we're not fooled by text like '"http://"' - - Args: - elided: The line being processed. - - Returns: - The line with collapsed strings. - """ - if _RE_PATTERN_INCLUDE.match(elided): - return elided - - # Remove escaped characters first to make quote/single quote collapsing - # basic. Things that look like escaped characters shouldn't occur - # outside of strings and chars. - elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) - - # Replace quoted strings and digit separators. Both single quotes - # and double quotes are processed in the same loop, otherwise - # nested quotes wouldn't work. - collapsed = '' - while True: - # Find the first quote character - match = Match(r'^([^\'"]*)([\'"])(.*)$', elided) - if not match: - collapsed += elided - break - head, quote, tail = match.groups() - - if quote == '"': - # Collapse double quoted strings - second_quote = tail.find('"') - if second_quote >= 0: - collapsed += head + '""' - elided = tail[second_quote + 1:] - else: - # Unmatched double quote, don't bother processing the rest - # of the line since this is probably a multiline string. - collapsed += elided - break - else: - # Found single quote, check nearby text to eliminate digit separators. - # - # There is no special handling for floating point here, because - # the integer/fractional/exponent parts would all be parsed - # correctly as long as there are digits on both sides of the - # separator. So we are fine as long as we don't see something - # like "0.'3" (gcc 4.9.0 will not allow this literal). - if Search(r'\b(?:0[bBxX]?|[1-9])[0-9a-fA-F]*$', head): - match_literal = Match(r'^((?:\'?[0-9a-zA-Z_])*)(.*)$', "'" + tail) - collapsed += head + match_literal.group(1).replace("'", '') - elided = match_literal.group(2) - else: - second_quote = tail.find('\'') - if second_quote >= 0: - collapsed += head + "''" - elided = tail[second_quote + 1:] - else: - # Unmatched single quote - collapsed += elided - break - - return collapsed - - -def FindEndOfExpressionInLine(line, startpos, stack): - """Find the position just after the end of current parenthesized expression. - - Args: - line: a CleansedLines line. - startpos: start searching at this position. - stack: nesting stack at startpos. - - Returns: - On finding matching end: (index just after matching end, None) - On finding an unclosed expression: (-1, None) - Otherwise: (-1, new stack at end of this line) - """ - for i in xrange(startpos, len(line)): - char = line[i] - if char in '([{': - # Found start of parenthesized expression, push to expression stack - stack.append(char) - elif char == '<': - # Found potential start of template argument list - if i > 0 and line[i - 1] == '<': - # Left shift operator - if stack and stack[-1] == '<': - stack.pop() - if not stack: - return (-1, None) - elif i > 0 and Search(r'\boperator\s*$', line[0:i]): - # operator<, don't add to stack - continue - else: - # Tentative start of template argument list - stack.append('<') - elif char in ')]}': - # Found end of parenthesized expression. - # - # If we are currently expecting a matching '>', the pending '<' - # must have been an operator. Remove them from expression stack. - while stack and stack[-1] == '<': - stack.pop() - if not stack: - return (-1, None) - if ((stack[-1] == '(' and char == ')') or - (stack[-1] == '[' and char == ']') or - (stack[-1] == '{' and char == '}')): - stack.pop() - if not stack: - return (i + 1, None) - else: - # Mismatched parentheses - return (-1, None) - elif char == '>': - # Found potential end of template argument list. - - # Ignore "->" and operator functions - if (i > 0 and - (line[i - 1] == '-' or Search(r'\boperator\s*$', line[0:i - 1]))): - continue - - # Pop the stack if there is a matching '<'. Otherwise, ignore - # this '>' since it must be an operator. - if stack: - if stack[-1] == '<': - stack.pop() - if not stack: - return (i + 1, None) - elif char == ';': - # Found something that look like end of statements. If we are currently - # expecting a '>', the matching '<' must have been an operator, since - # template argument list should not contain statements. - while stack and stack[-1] == '<': - stack.pop() - if not stack: - return (-1, None) - - # Did not find end of expression or unbalanced parentheses on this line - return (-1, stack) - - -def CloseExpression(clean_lines, linenum, pos): - """If input points to ( or { or [ or <, finds the position that closes it. - - If lines[linenum][pos] points to a '(' or '{' or '[' or '<', finds the - linenum/pos that correspond to the closing of the expression. - - TODO(unknown): cpplint spends a fair bit of time matching parentheses. - Ideally we would want to index all opening and closing parentheses once - and have CloseExpression be just a simple lookup, but due to preprocessor - tricks, this is not so easy. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - pos: A position on the line. - - Returns: - A tuple (line, linenum, pos) pointer *past* the closing brace, or - (line, len(lines), -1) if we never find a close. Note we ignore - strings and comments when matching; and the line we return is the - 'cleansed' line at linenum. - """ - - line = clean_lines.elided[linenum] - if (line[pos] not in '({[<') or Match(r'<[<=]', line[pos:]): - return (line, clean_lines.NumLines(), -1) - - # Check first line - (end_pos, stack) = FindEndOfExpressionInLine(line, pos, []) - if end_pos > -1: - return (line, linenum, end_pos) - - # Continue scanning forward - while stack and linenum < clean_lines.NumLines() - 1: - linenum += 1 - line = clean_lines.elided[linenum] - (end_pos, stack) = FindEndOfExpressionInLine(line, 0, stack) - if end_pos > -1: - return (line, linenum, end_pos) - - # Did not find end of expression before end of file, give up - return (line, clean_lines.NumLines(), -1) - - -def FindStartOfExpressionInLine(line, endpos, stack): - """Find position at the matching start of current expression. - - This is almost the reverse of FindEndOfExpressionInLine, but note - that the input position and returned position differs by 1. - - Args: - line: a CleansedLines line. - endpos: start searching at this position. - stack: nesting stack at endpos. - - Returns: - On finding matching start: (index at matching start, None) - On finding an unclosed expression: (-1, None) - Otherwise: (-1, new stack at beginning of this line) - """ - i = endpos - while i >= 0: - char = line[i] - if char in ')]}': - # Found end of expression, push to expression stack - stack.append(char) - elif char == '>': - # Found potential end of template argument list. - # - # Ignore it if it's a "->" or ">=" or "operator>" - if (i > 0 and - (line[i - 1] == '-' or - Match(r'\s>=\s', line[i - 1:]) or - Search(r'\boperator\s*$', line[0:i]))): - i -= 1 - else: - stack.append('>') - elif char == '<': - # Found potential start of template argument list - if i > 0 and line[i - 1] == '<': - # Left shift operator - i -= 1 - else: - # If there is a matching '>', we can pop the expression stack. - # Otherwise, ignore this '<' since it must be an operator. - if stack and stack[-1] == '>': - stack.pop() - if not stack: - return (i, None) - elif char in '([{': - # Found start of expression. - # - # If there are any unmatched '>' on the stack, they must be - # operators. Remove those. - while stack and stack[-1] == '>': - stack.pop() - if not stack: - return (-1, None) - if ((char == '(' and stack[-1] == ')') or - (char == '[' and stack[-1] == ']') or - (char == '{' and stack[-1] == '}')): - stack.pop() - if not stack: - return (i, None) - else: - # Mismatched parentheses - return (-1, None) - elif char == ';': - # Found something that look like end of statements. If we are currently - # expecting a '<', the matching '>' must have been an operator, since - # template argument list should not contain statements. - while stack and stack[-1] == '>': - stack.pop() - if not stack: - return (-1, None) - - i -= 1 - - return (-1, stack) - - -def ReverseCloseExpression(clean_lines, linenum, pos): - """If input points to ) or } or ] or >, finds the position that opens it. - - If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the - linenum/pos that correspond to the opening of the expression. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - pos: A position on the line. - - Returns: - A tuple (line, linenum, pos) pointer *at* the opening brace, or - (line, 0, -1) if we never find the matching opening brace. Note - we ignore strings and comments when matching; and the line we - return is the 'cleansed' line at linenum. - """ - line = clean_lines.elided[linenum] - if line[pos] not in ')}]>': - return (line, 0, -1) - - # Check last line - (start_pos, stack) = FindStartOfExpressionInLine(line, pos, []) - if start_pos > -1: - return (line, linenum, start_pos) - - # Continue scanning backward - while stack and linenum > 0: - linenum -= 1 - line = clean_lines.elided[linenum] - (start_pos, stack) = FindStartOfExpressionInLine(line, len(line) - 1, stack) - if start_pos > -1: - return (line, linenum, start_pos) - - # Did not find start of expression before beginning of file, give up - return (line, 0, -1) - - -def CheckForCopyright(filename, lines, error): - """Logs an error if no Copyright message appears at the top of the file.""" - - # We'll say it should occur by line 10. Don't forget there's a - # dummy line at the front. - for line in range(1, min(len(lines), 11)): - if re.search(r'Copyright', lines[line], re.I): break - else: # means no copyright line was found - error(filename, 0, 'legal/copyright', 5, - 'No copyright message found. ' - 'You should have a line: "Copyright [year] "') - - -def GetIndentLevel(line): - """Return the number of leading spaces in line. - - Args: - line: A string to check. - - Returns: - An integer count of leading spaces, possibly zero. - """ - indent = Match(r'^( *)\S', line) - if indent: - return len(indent.group(1)) - else: - return 0 - - -def GetHeaderGuardCPPVariable(filename): - """Returns the CPP variable that should be used as a header guard. - - Args: - filename: The name of a C++ header file. - - Returns: - The CPP variable that should be used as a header guard in the - named file. - - """ - - # Restores original filename in case that cpplint is invoked from Emacs's - # flymake. - filename = re.sub(r'_flymake\.h$', '.h', filename) - filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename) - # Replace 'c++' with 'cpp'. - filename = filename.replace('C++', 'cpp').replace('c++', 'cpp') - - fileinfo = FileInfo(filename) - file_path_from_root = fileinfo.RepositoryName() - if _root: - suffix = os.sep - # On Windows using directory separator will leave us with - # "bogus escape error" unless we properly escape regex. - if suffix == '\\': - suffix += '\\' - file_path_from_root = re.sub('^' + _root + suffix, '', file_path_from_root) - return re.sub(r'[^a-zA-Z0-9]', '_', file_path_from_root).upper() + '_' - - -def CheckForHeaderGuard(filename, clean_lines, error): - """Checks that the file contains a header guard. - - Logs an error if no #ifndef header guard is present. For other - headers, checks that the full pathname is used. - - Args: - filename: The name of the C++ header file. - clean_lines: A CleansedLines instance containing the file. - error: The function to call with any errors found. - """ - - # Don't check for header guards if there are error suppression - # comments somewhere in this file. - # - # Because this is silencing a warning for a nonexistent line, we - # only support the very specific NOLINT(build/header_guard) syntax, - # and not the general NOLINT or NOLINT(*) syntax. - raw_lines = clean_lines.lines_without_raw_strings - for i in raw_lines: - if Search(r'//\s*NOLINT\(build/header_guard\)', i): - return - - # Allow pragma once instead of header guards - for i in raw_lines: - if Search(r'^\s*#pragma\s+once', i): - return - - cppvar = GetHeaderGuardCPPVariable(filename) - - ifndef = '' - ifndef_linenum = 0 - define = '' - endif = '' - endif_linenum = 0 - for linenum, line in enumerate(raw_lines): - linesplit = line.split() - if len(linesplit) >= 2: - # find the first occurrence of #ifndef and #define, save arg - if not ifndef and linesplit[0] == '#ifndef': - # set ifndef to the header guard presented on the #ifndef line. - ifndef = linesplit[1] - ifndef_linenum = linenum - if not define and linesplit[0] == '#define': - define = linesplit[1] - # find the last occurrence of #endif, save entire line - if line.startswith('#endif'): - endif = line - endif_linenum = linenum - - if not ifndef or not define or ifndef != define: - error(filename, 0, 'build/header_guard', 5, - 'No #ifndef header guard found, suggested CPP variable is: %s' % - cppvar) - return - - # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ - # for backward compatibility. - if ifndef != cppvar: - error_level = 0 - if ifndef != cppvar + '_': - error_level = 5 - - ParseNolintSuppressions(filename, raw_lines[ifndef_linenum], ifndef_linenum, - error) - error(filename, ifndef_linenum, 'build/header_guard', error_level, - '#ifndef header guard has wrong style, please use: %s' % cppvar) - - # Check for "//" comments on endif line. - ParseNolintSuppressions(filename, raw_lines[endif_linenum], endif_linenum, - error) - match = Match(r'#endif\s*//\s*' + cppvar + r'(_)?\b', endif) - if match: - if match.group(1) == '_': - # Issue low severity warning for deprecated double trailing underscore - error(filename, endif_linenum, 'build/header_guard', 0, - '#endif line should be "#endif // %s"' % cppvar) - return - - # Didn't find the corresponding "//" comment. If this file does not - # contain any "//" comments at all, it could be that the compiler - # only wants "/**/" comments, look for those instead. - no_single_line_comments = True - for i in xrange(1, len(raw_lines) - 1): - line = raw_lines[i] - if Match(r'^(?:(?:\'(?:\.|[^\'])*\')|(?:"(?:\.|[^"])*")|[^\'"])*//', line): - no_single_line_comments = False - break - - if no_single_line_comments: - match = Match(r'#endif\s*/\*\s*' + cppvar + r'(_)?\s*\*/', endif) - if match: - if match.group(1) == '_': - # Low severity warning for double trailing underscore - error(filename, endif_linenum, 'build/header_guard', 0, - '#endif line should be "#endif /* %s */"' % cppvar) - return - - # Didn't find anything - error(filename, endif_linenum, 'build/header_guard', 5, - '#endif line should be "#endif // %s"' % cppvar) - - -def CheckHeaderFileIncluded(filename, include_state, error): - """Logs an error if a source file does not include its header.""" - - # Do not check test files - fileinfo = FileInfo(filename) - if Search(_TEST_FILE_SUFFIX, fileinfo.BaseName()): - return - - for ext in GetHeaderExtensions(): - basefilename = filename[0:len(filename) - len(fileinfo.Extension())] - headerfile = basefilename + '.' + ext - if not os.path.exists(headerfile): - continue - headername = FileInfo(headerfile).RepositoryName() - first_include = None - for section_list in include_state.include_list: - for f in section_list: - if headername in f[0] or f[0] in headername: - return - if not first_include: - first_include = f[1] - - error(filename, first_include, 'build/include', 5, - '%s should include its header file %s' % (fileinfo.RepositoryName(), - headername)) - - -def CheckForBadCharacters(filename, lines, error): - """Logs an error for each line containing bad characters. - - Two kinds of bad characters: - - 1. Unicode replacement characters: These indicate that either the file - contained invalid UTF-8 (likely) or Unicode replacement characters (which - it shouldn't). Note that it's possible for this to throw off line - numbering if the invalid UTF-8 occurred adjacent to a newline. - - 2. NUL bytes. These are problematic for some tools. - - Args: - filename: The name of the current file. - lines: An array of strings, each representing a line of the file. - error: The function to call with any errors found. - """ - for linenum, line in enumerate(lines): - if unicode_escape_decode('\ufffd') in line: - error(filename, linenum, 'readability/utf8', 5, - 'Line contains invalid UTF-8 (or Unicode replacement character).') - if '\0' in line: - error(filename, linenum, 'readability/nul', 5, 'Line contains NUL byte.') - - -def CheckForNewlineAtEOF(filename, lines, error): - """Logs an error if there is no newline char at the end of the file. - - Args: - filename: The name of the current file. - lines: An array of strings, each representing a line of the file. - error: The function to call with any errors found. - """ - - # The array lines() was created by adding two newlines to the - # original file (go figure), then splitting on \n. - # To verify that the file ends in \n, we just have to make sure the - # last-but-two element of lines() exists and is empty. - if len(lines) < 3 or lines[-2]: - error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, - 'Could not find a newline character at the end of the file.') - - -def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): - """Logs an error if we see /* ... */ or "..." that extend past one line. - - /* ... */ comments are legit inside macros, for one line. - Otherwise, we prefer // comments, so it's ok to warn about the - other. Likewise, it's ok for strings to extend across multiple - lines, as long as a line continuation character (backslash) - terminates each line. Although not currently prohibited by the C++ - style guide, it's ugly and unnecessary. We don't do well with either - in this lint program, so we warn about both. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Remove all \\ (escaped backslashes) from the line. They are OK, and the - # second (escaped) slash may trigger later \" detection erroneously. - line = line.replace('\\\\', '') - - if line.count('/*') > line.count('*/'): - error(filename, linenum, 'readability/multiline_comment', 5, - 'Complex multi-line /*...*/-style comment found. ' - 'Lint may give bogus warnings. ' - 'Consider replacing these with //-style comments, ' - 'with #if 0...#endif, ' - 'or with more clearly structured multi-line comments.') - - if (line.count('"') - line.count('\\"')) % 2: - error(filename, linenum, 'readability/multiline_string', 5, - 'Multi-line string ("...") found. This lint script doesn\'t ' - 'do well with such strings, and may give bogus warnings. ' - 'Use C++11 raw strings or concatenation instead.') - - -# (non-threadsafe name, thread-safe alternative, validation pattern) -# -# The validation pattern is used to eliminate false positives such as: -# _rand(); // false positive due to substring match. -# ->rand(); // some member function rand(). -# ACMRandom rand(seed); // some variable named rand. -# ISAACRandom rand(); // another variable named rand. -# -# Basically we require the return value of these functions to be used -# in some expression context on the same line by matching on some -# operator before the function name. This eliminates constructors and -# member function calls. -_UNSAFE_FUNC_PREFIX = r'(?:[-+*/=%^&|(<]\s*|>\s+)' -_THREADING_LIST = ( - ('asctime(', 'asctime_r(', _UNSAFE_FUNC_PREFIX + r'asctime\([^)]+\)'), - ('ctime(', 'ctime_r(', _UNSAFE_FUNC_PREFIX + r'ctime\([^)]+\)'), - ('getgrgid(', 'getgrgid_r(', _UNSAFE_FUNC_PREFIX + r'getgrgid\([^)]+\)'), - ('getgrnam(', 'getgrnam_r(', _UNSAFE_FUNC_PREFIX + r'getgrnam\([^)]+\)'), - ('getlogin(', 'getlogin_r(', _UNSAFE_FUNC_PREFIX + r'getlogin\(\)'), - ('getpwnam(', 'getpwnam_r(', _UNSAFE_FUNC_PREFIX + r'getpwnam\([^)]+\)'), - ('getpwuid(', 'getpwuid_r(', _UNSAFE_FUNC_PREFIX + r'getpwuid\([^)]+\)'), - ('gmtime(', 'gmtime_r(', _UNSAFE_FUNC_PREFIX + r'gmtime\([^)]+\)'), - ('localtime(', 'localtime_r(', _UNSAFE_FUNC_PREFIX + r'localtime\([^)]+\)'), - ('rand(', 'rand_r(', _UNSAFE_FUNC_PREFIX + r'rand\(\)'), - ('strtok(', 'strtok_r(', - _UNSAFE_FUNC_PREFIX + r'strtok\([^)]+\)'), - ('ttyname(', 'ttyname_r(', _UNSAFE_FUNC_PREFIX + r'ttyname\([^)]+\)'), - ) - - -def CheckPosixThreading(filename, clean_lines, linenum, error): - """Checks for calls to thread-unsafe functions. - - Much code has been originally written without consideration of - multi-threading. Also, engineers are relying on their old experience; - they have learned posix before threading extensions were added. These - tests guide the engineers to use thread-safe functions (when using - posix directly). - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - for single_thread_func, multithread_safe_func, pattern in _THREADING_LIST: - # Additional pattern matching check to confirm that this is the - # function we are looking for - if Search(pattern, line): - error(filename, linenum, 'runtime/threadsafe_fn', 2, - 'Consider using ' + multithread_safe_func + - '...) instead of ' + single_thread_func + - '...) for improved thread safety.') - - -def CheckVlogArguments(filename, clean_lines, linenum, error): - """Checks that VLOG() is only used for defining a logging level. - - For example, VLOG(2) is correct. VLOG(INFO), VLOG(WARNING), VLOG(ERROR), and - VLOG(FATAL) are not. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - if Search(r'\bVLOG\((INFO|ERROR|WARNING|DFATAL|FATAL)\)', line): - error(filename, linenum, 'runtime/vlog', 5, - 'VLOG() should be used with numeric verbosity level. ' - 'Use LOG() if you want symbolic severity levels.') - -# Matches invalid increment: *count++, which moves pointer instead of -# incrementing a value. -_RE_PATTERN_INVALID_INCREMENT = re.compile( - r'^\s*\*\w+(\+\+|--);') - - -def CheckInvalidIncrement(filename, clean_lines, linenum, error): - """Checks for invalid increment *count++. - - For example following function: - void increment_counter(int* count) { - *count++; - } - is invalid, because it effectively does count++, moving pointer, and should - be replaced with ++*count, (*count)++ or *count += 1. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - if _RE_PATTERN_INVALID_INCREMENT.match(line): - error(filename, linenum, 'runtime/invalid_increment', 5, - 'Changing pointer instead of value (or unused value of operator*).') - - -def IsMacroDefinition(clean_lines, linenum): - if Search(r'^#define', clean_lines[linenum]): - return True - - if linenum > 0 and Search(r'\\$', clean_lines[linenum - 1]): - return True - - return False - - -def IsForwardClassDeclaration(clean_lines, linenum): - return Match(r'^\s*(\btemplate\b)*.*class\s+\w+;\s*$', clean_lines[linenum]) - - -class _BlockInfo(object): - """Stores information about a generic block of code.""" - - def __init__(self, linenum, seen_open_brace): - self.starting_linenum = linenum - self.seen_open_brace = seen_open_brace - self.open_parentheses = 0 - self.inline_asm = _NO_ASM - self.check_namespace_indentation = False - - def CheckBegin(self, filename, clean_lines, linenum, error): - """Run checks that applies to text up to the opening brace. - - This is mostly for checking the text after the class identifier - and the "{", usually where the base class is specified. For other - blocks, there isn't much to check, so we always pass. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - pass - - def CheckEnd(self, filename, clean_lines, linenum, error): - """Run checks that applies to text after the closing brace. - - This is mostly used for checking end of namespace comments. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - pass - - def IsBlockInfo(self): - """Returns true if this block is a _BlockInfo. - - This is convenient for verifying that an object is an instance of - a _BlockInfo, but not an instance of any of the derived classes. - - Returns: - True for this class, False for derived classes. - """ - return self.__class__ == _BlockInfo - - -class _ExternCInfo(_BlockInfo): - """Stores information about an 'extern "C"' block.""" - - def __init__(self, linenum): - _BlockInfo.__init__(self, linenum, True) - - -class _ClassInfo(_BlockInfo): - """Stores information about a class.""" - - def __init__(self, name, class_or_struct, clean_lines, linenum): - _BlockInfo.__init__(self, linenum, False) - self.name = name - self.is_derived = False - self.check_namespace_indentation = True - if class_or_struct == 'struct': - self.access = 'public' - self.is_struct = True - else: - self.access = 'private' - self.is_struct = False - - # Remember initial indentation level for this class. Using raw_lines here - # instead of elided to account for leading comments. - self.class_indent = GetIndentLevel(clean_lines.raw_lines[linenum]) - - # Try to find the end of the class. This will be confused by things like: - # class A { - # } *x = { ... - # - # But it's still good enough for CheckSectionSpacing. - self.last_line = 0 - depth = 0 - for i in range(linenum, clean_lines.NumLines()): - line = clean_lines.elided[i] - depth += line.count('{') - line.count('}') - if not depth: - self.last_line = i - break - - def CheckBegin(self, filename, clean_lines, linenum, error): - # Look for a bare ':' - if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): - self.is_derived = True - - def CheckEnd(self, filename, clean_lines, linenum, error): - # If there is a DISALLOW macro, it should appear near the end of - # the class. - seen_last_thing_in_class = False - for i in xrange(linenum - 1, self.starting_linenum, -1): - match = Search( - r'\b(DISALLOW_COPY_AND_ASSIGN|DISALLOW_IMPLICIT_CONSTRUCTORS)\(' + - self.name + r'\)', - clean_lines.elided[i]) - if match: - if seen_last_thing_in_class: - error(filename, i, 'readability/constructors', 3, - match.group(1) + ' should be the last thing in the class') - break - - if not Match(r'^\s*$', clean_lines.elided[i]): - seen_last_thing_in_class = True - - # Check that closing brace is aligned with beginning of the class. - # Only do this if the closing brace is indented by only whitespaces. - # This means we will not check single-line class definitions. - indent = Match(r'^( *)\}', clean_lines.elided[linenum]) - if indent and len(indent.group(1)) != self.class_indent: - if self.is_struct: - parent = 'struct ' + self.name - else: - parent = 'class ' + self.name - error(filename, linenum, 'whitespace/indent', 3, - 'Closing brace should be aligned with beginning of %s' % parent) - - -class _NamespaceInfo(_BlockInfo): - """Stores information about a namespace.""" - - def __init__(self, name, linenum): - _BlockInfo.__init__(self, linenum, False) - self.name = name or '' - self.check_namespace_indentation = True - - def CheckEnd(self, filename, clean_lines, linenum, error): - """Check end of namespace comments.""" - line = clean_lines.raw_lines[linenum] - - # Check how many lines is enclosed in this namespace. Don't issue - # warning for missing namespace comments if there aren't enough - # lines. However, do apply checks if there is already an end of - # namespace comment and it's incorrect. - # - # TODO(unknown): We always want to check end of namespace comments - # if a namespace is large, but sometimes we also want to apply the - # check if a short namespace contained nontrivial things (something - # other than forward declarations). There is currently no logic on - # deciding what these nontrivial things are, so this check is - # triggered by namespace size only, which works most of the time. - if (linenum - self.starting_linenum < 10 - and not Match(r'^\s*};*\s*(//|/\*).*\bnamespace\b', line)): - return - - # Look for matching comment at end of namespace. - # - # Note that we accept C style "/* */" comments for terminating - # namespaces, so that code that terminate namespaces inside - # preprocessor macros can be cpplint clean. - # - # We also accept stuff like "// end of namespace ." with the - # period at the end. - # - # Besides these, we don't accept anything else, otherwise we might - # get false negatives when existing comment is a substring of the - # expected namespace. - if self.name: - # Named namespace - if not Match((r'^\s*};*\s*(//|/\*).*\bnamespace\s+' + - re.escape(self.name) + r'[\*/\.\\\s]*$'), - line): - error(filename, linenum, 'readability/namespace', 5, - 'Namespace should be terminated with "// namespace %s"' % - self.name) - else: - # Anonymous namespace - if not Match(r'^\s*};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line): - # If "// namespace anonymous" or "// anonymous namespace (more text)", - # mention "// anonymous namespace" as an acceptable form - if Match(r'^\s*}.*\b(namespace anonymous|anonymous namespace)\b', line): - error(filename, linenum, 'readability/namespace', 5, - 'Anonymous namespace should be terminated with "// namespace"' - ' or "// anonymous namespace"') - else: - error(filename, linenum, 'readability/namespace', 5, - 'Anonymous namespace should be terminated with "// namespace"') - - -class _PreprocessorInfo(object): - """Stores checkpoints of nesting stacks when #if/#else is seen.""" - - def __init__(self, stack_before_if): - # The entire nesting stack before #if - self.stack_before_if = stack_before_if - - # The entire nesting stack up to #else - self.stack_before_else = [] - - # Whether we have already seen #else or #elif - self.seen_else = False - - -class NestingState(object): - """Holds states related to parsing braces.""" - - def __init__(self): - # Stack for tracking all braces. An object is pushed whenever we - # see a "{", and popped when we see a "}". Only 3 types of - # objects are possible: - # - _ClassInfo: a class or struct. - # - _NamespaceInfo: a namespace. - # - _BlockInfo: some other type of block. - self.stack = [] - - # Top of the previous stack before each Update(). - # - # Because the nesting_stack is updated at the end of each line, we - # had to do some convoluted checks to find out what is the current - # scope at the beginning of the line. This check is simplified by - # saving the previous top of nesting stack. - # - # We could save the full stack, but we only need the top. Copying - # the full nesting stack would slow down cpplint by ~10%. - self.previous_stack_top = [] - - # Stack of _PreprocessorInfo objects. - self.pp_stack = [] - - def SeenOpenBrace(self): - """Check if we have seen the opening brace for the innermost block. - - Returns: - True if we have seen the opening brace, False if the innermost - block is still expecting an opening brace. - """ - return (not self.stack) or self.stack[-1].seen_open_brace - - def InNamespaceBody(self): - """Check if we are currently one level inside a namespace body. - - Returns: - True if top of the stack is a namespace block, False otherwise. - """ - return self.stack and isinstance(self.stack[-1], _NamespaceInfo) - - def InExternC(self): - """Check if we are currently one level inside an 'extern "C"' block. - - Returns: - True if top of the stack is an extern block, False otherwise. - """ - return self.stack and isinstance(self.stack[-1], _ExternCInfo) - - def InClassDeclaration(self): - """Check if we are currently one level inside a class or struct declaration. - - Returns: - True if top of the stack is a class/struct, False otherwise. - """ - return self.stack and isinstance(self.stack[-1], _ClassInfo) - - def InAsmBlock(self): - """Check if we are currently one level inside an inline ASM block. - - Returns: - True if the top of the stack is a block containing inline ASM. - """ - return self.stack and self.stack[-1].inline_asm != _NO_ASM - - def InTemplateArgumentList(self, clean_lines, linenum, pos): - """Check if current position is inside template argument list. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - pos: position just after the suspected template argument. - Returns: - True if (linenum, pos) is inside template arguments. - """ - while linenum < clean_lines.NumLines(): - # Find the earliest character that might indicate a template argument - line = clean_lines.elided[linenum] - match = Match(r'^[^{};=\[\]\.<>]*(.)', line[pos:]) - if not match: - linenum += 1 - pos = 0 - continue - token = match.group(1) - pos += len(match.group(0)) - - # These things do not look like template argument list: - # class Suspect { - # class Suspect x; } - if token in ('{', '}', ';'): return False - - # These things look like template argument list: - # template - # template - # template - # template - if token in ('>', '=', '[', ']', '.'): return True - - # Check if token is an unmatched '<'. - # If not, move on to the next character. - if token != '<': - pos += 1 - if pos >= len(line): - linenum += 1 - pos = 0 - continue - - # We can't be sure if we just find a single '<', and need to - # find the matching '>'. - (_, end_line, end_pos) = CloseExpression(clean_lines, linenum, pos - 1) - if end_pos < 0: - # Not sure if template argument list or syntax error in file - return False - linenum = end_line - pos = end_pos - return False - - def UpdatePreprocessor(self, line): - """Update preprocessor stack. - - We need to handle preprocessors due to classes like this: - #ifdef SWIG - struct ResultDetailsPageElementExtensionPoint { - #else - struct ResultDetailsPageElementExtensionPoint : public Extension { - #endif - - We make the following assumptions (good enough for most files): - - Preprocessor condition evaluates to true from #if up to first - #else/#elif/#endif. - - - Preprocessor condition evaluates to false from #else/#elif up - to #endif. We still perform lint checks on these lines, but - these do not affect nesting stack. - - Args: - line: current line to check. - """ - if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): - # Beginning of #if block, save the nesting stack here. The saved - # stack will allow us to restore the parsing state in the #else case. - self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) - elif Match(r'^\s*#\s*(else|elif)\b', line): - # Beginning of #else block - if self.pp_stack: - if not self.pp_stack[-1].seen_else: - # This is the first #else or #elif block. Remember the - # whole nesting stack up to this point. This is what we - # keep after the #endif. - self.pp_stack[-1].seen_else = True - self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack) - - # Restore the stack to how it was before the #if - self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) - else: - # TODO(unknown): unexpected #else, issue warning? - pass - elif Match(r'^\s*#\s*endif\b', line): - # End of #if or #else blocks. - if self.pp_stack: - # If we saw an #else, we will need to restore the nesting - # stack to its former state before the #else, otherwise we - # will just continue from where we left off. - if self.pp_stack[-1].seen_else: - # Here we can just use a shallow copy since we are the last - # reference to it. - self.stack = self.pp_stack[-1].stack_before_else - # Drop the corresponding #if - self.pp_stack.pop() - else: - # TODO(unknown): unexpected #endif, issue warning? - pass - - # TODO(unknown): Update() is too long, but we will refactor later. - def Update(self, filename, clean_lines, linenum, error): - """Update nesting state with current line. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Remember top of the previous nesting stack. - # - # The stack is always pushed/popped and not modified in place, so - # we can just do a shallow copy instead of copy.deepcopy. Using - # deepcopy would slow down cpplint by ~28%. - if self.stack: - self.previous_stack_top = self.stack[-1] - else: - self.previous_stack_top = None - - # Update pp_stack - self.UpdatePreprocessor(line) - - # Count parentheses. This is to avoid adding struct arguments to - # the nesting stack. - if self.stack: - inner_block = self.stack[-1] - depth_change = line.count('(') - line.count(')') - inner_block.open_parentheses += depth_change - - # Also check if we are starting or ending an inline assembly block. - if inner_block.inline_asm in (_NO_ASM, _END_ASM): - if (depth_change != 0 and - inner_block.open_parentheses == 1 and - _MATCH_ASM.match(line)): - # Enter assembly block - inner_block.inline_asm = _INSIDE_ASM - else: - # Not entering assembly block. If previous line was _END_ASM, - # we will now shift to _NO_ASM state. - inner_block.inline_asm = _NO_ASM - elif (inner_block.inline_asm == _INSIDE_ASM and - inner_block.open_parentheses == 0): - # Exit assembly block - inner_block.inline_asm = _END_ASM - - # Consume namespace declaration at the beginning of the line. Do - # this in a loop so that we catch same line declarations like this: - # namespace proto2 { namespace bridge { class MessageSet; } } - while True: - # Match start of namespace. The "\b\s*" below catches namespace - # declarations even if it weren't followed by a whitespace, this - # is so that we don't confuse our namespace checker. The - # missing spaces will be flagged by CheckSpacing. - namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) - if not namespace_decl_match: - break - - new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum) - self.stack.append(new_namespace) - - line = namespace_decl_match.group(2) - if line.find('{') != -1: - new_namespace.seen_open_brace = True - line = line[line.find('{') + 1:] - - # Look for a class declaration in whatever is left of the line - # after parsing namespaces. The regexp accounts for decorated classes - # such as in: - # class LOCKABLE API Object { - # }; - class_decl_match = Match( - r'^(\s*(?:template\s*<[\w\s<>,:=]*>\s*)?' - r'(class|struct)\s+(?:[A-Z_]+\s+)*(\w+(?:::\w+)*))' - r'(.*)$', line) - if (class_decl_match and - (not self.stack or self.stack[-1].open_parentheses == 0)): - # We do not want to accept classes that are actually template arguments: - # template , - # template class Ignore3> - # void Function() {}; - # - # To avoid template argument cases, we scan forward and look for - # an unmatched '>'. If we see one, assume we are inside a - # template argument list. - end_declaration = len(class_decl_match.group(1)) - if not self.InTemplateArgumentList(clean_lines, linenum, end_declaration): - self.stack.append(_ClassInfo( - class_decl_match.group(3), class_decl_match.group(2), - clean_lines, linenum)) - line = class_decl_match.group(4) - - # If we have not yet seen the opening brace for the innermost block, - # run checks here. - if not self.SeenOpenBrace(): - self.stack[-1].CheckBegin(filename, clean_lines, linenum, error) - - # Update access control if we are inside a class/struct - if self.stack and isinstance(self.stack[-1], _ClassInfo): - classinfo = self.stack[-1] - access_match = Match( - r'^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?' - r':(?:[^:]|$)', - line) - if access_match: - classinfo.access = access_match.group(2) - - # Check that access keywords are indented +1 space. Skip this - # check if the keywords are not preceded by whitespaces. - indent = access_match.group(1) - if (len(indent) != classinfo.class_indent + 1 and - Match(r'^\s*$', indent)): - if classinfo.is_struct: - parent = 'struct ' + classinfo.name - else: - parent = 'class ' + classinfo.name - slots = '' - if access_match.group(3): - slots = access_match.group(3) - error(filename, linenum, 'whitespace/indent', 3, - '%s%s: should be indented +1 space inside %s' % ( - access_match.group(2), slots, parent)) - - # Consume braces or semicolons from what's left of the line - while True: - # Match first brace, semicolon, or closed parenthesis. - matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) - if not matched: - break - - token = matched.group(1) - if token == '{': - # If namespace or class hasn't seen a opening brace yet, mark - # namespace/class head as complete. Push a new block onto the - # stack otherwise. - if not self.SeenOpenBrace(): - self.stack[-1].seen_open_brace = True - elif Match(r'^extern\s*"[^"]*"\s*\{', line): - self.stack.append(_ExternCInfo(linenum)) - else: - self.stack.append(_BlockInfo(linenum, True)) - if _MATCH_ASM.match(line): - self.stack[-1].inline_asm = _BLOCK_ASM - - elif token == ';' or token == ')': - # If we haven't seen an opening brace yet, but we already saw - # a semicolon, this is probably a forward declaration. Pop - # the stack for these. - # - # Similarly, if we haven't seen an opening brace yet, but we - # already saw a closing parenthesis, then these are probably - # function arguments with extra "class" or "struct" keywords. - # Also pop these stack for these. - if not self.SeenOpenBrace(): - self.stack.pop() - else: # token == '}' - # Perform end of block checks and pop the stack. - if self.stack: - self.stack[-1].CheckEnd(filename, clean_lines, linenum, error) - self.stack.pop() - line = matched.group(2) - - def InnermostClass(self): - """Get class info on the top of the stack. - - Returns: - A _ClassInfo object if we are inside a class, or None otherwise. - """ - for i in range(len(self.stack), 0, -1): - classinfo = self.stack[i - 1] - if isinstance(classinfo, _ClassInfo): - return classinfo - return None - - def CheckCompletedBlocks(self, filename, error): - """Checks that all classes and namespaces have been completely parsed. - - Call this when all lines in a file have been processed. - Args: - filename: The name of the current file. - error: The function to call with any errors found. - """ - # Note: This test can result in false positives if #ifdef constructs - # get in the way of brace matching. See the testBuildClass test in - # cpplint_unittest.py for an example of this. - for obj in self.stack: - if isinstance(obj, _ClassInfo): - error(filename, obj.starting_linenum, 'build/class', 5, - 'Failed to find complete declaration of class %s' % - obj.name) - elif isinstance(obj, _NamespaceInfo): - error(filename, obj.starting_linenum, 'build/namespaces', 5, - 'Failed to find complete declaration of namespace %s' % - obj.name) - - -def CheckForNonStandardConstructs(filename, clean_lines, linenum, - nesting_state, error): - r"""Logs an error if we see certain non-ANSI constructs ignored by gcc-2. - - Complain about several constructs which gcc-2 accepts, but which are - not standard C++. Warning about these in lint is one way to ease the - transition to new compilers. - - put storage class first (e.g. "static const" instead of "const static"). - - "%lld" instead of %qd" in printf-type functions. - - "%1$d" is non-standard in printf-type functions. - - "\%" is an undefined character escape sequence. - - text after #endif is not allowed. - - invalid inner-style forward declaration. - - >? and ?= and )\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', - line): - error(filename, linenum, 'build/deprecated', 3, - '>? and ))?' - # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;' - error(filename, linenum, 'runtime/member_string_references', 2, - 'const string& members are dangerous. It is much better to use ' - 'alternatives, such as pointers or simple constants.') - - # Everything else in this function operates on class declarations. - # Return early if the top of the nesting stack is not a class, or if - # the class head is not completed yet. - classinfo = nesting_state.InnermostClass() - if not classinfo or not classinfo.seen_open_brace: - return - - # The class may have been declared with namespace or classname qualifiers. - # The constructor and destructor will not have those qualifiers. - base_classname = classinfo.name.split('::')[-1] - - # Look for single-argument constructors that aren't marked explicit. - # Technically a valid construct, but against style. - explicit_constructor_match = Match( - r'\s+(?:inline\s+)?(explicit\s+)?(?:inline\s+)?%s\s*' - r'\(((?:[^()]|\([^()]*\))*)\)' - % re.escape(base_classname), - line) - - if explicit_constructor_match: - is_marked_explicit = explicit_constructor_match.group(1) - - if not explicit_constructor_match.group(2): - constructor_args = [] - else: - constructor_args = explicit_constructor_match.group(2).split(',') - - # collapse arguments so that commas in template parameter lists and function - # argument parameter lists don't split arguments in two - i = 0 - while i < len(constructor_args): - constructor_arg = constructor_args[i] - while (constructor_arg.count('<') > constructor_arg.count('>') or - constructor_arg.count('(') > constructor_arg.count(')')): - constructor_arg += ',' + constructor_args[i + 1] - del constructor_args[i + 1] - constructor_args[i] = constructor_arg - i += 1 - - variadic_args = [arg for arg in constructor_args if '&&...' in arg] - defaulted_args = [arg for arg in constructor_args if '=' in arg] - noarg_constructor = (not constructor_args or # empty arg list - # 'void' arg specifier - (len(constructor_args) == 1 and - constructor_args[0].strip() == 'void')) - onearg_constructor = ((len(constructor_args) == 1 and # exactly one arg - not noarg_constructor) or - # all but at most one arg defaulted - (len(constructor_args) >= 1 and - not noarg_constructor and - len(defaulted_args) >= len(constructor_args) - 1) or - # variadic arguments with zero or one argument - (len(constructor_args) <= 2 and - len(variadic_args) >= 1)) - initializer_list_constructor = bool( - onearg_constructor and - Search(r'\bstd\s*::\s*initializer_list\b', constructor_args[0])) - copy_constructor = bool( - onearg_constructor and - Match(r'(const\s+)?%s(\s*<[^>]*>)?(\s+const)?\s*(?:<\w+>\s*)?&' - % re.escape(base_classname), constructor_args[0].strip())) - - if (not is_marked_explicit and - onearg_constructor and - not initializer_list_constructor and - not copy_constructor): - if defaulted_args or variadic_args: - error(filename, linenum, 'runtime/explicit', 5, - 'Constructors callable with one argument ' - 'should be marked explicit.') - else: - error(filename, linenum, 'runtime/explicit', 5, - 'Single-parameter constructors should be marked explicit.') - elif is_marked_explicit and not onearg_constructor: - if noarg_constructor: - error(filename, linenum, 'runtime/explicit', 5, - 'Zero-parameter constructors should not be marked explicit.') - - -def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error): - """Checks for the correctness of various spacing around function calls. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Since function calls often occur inside if/for/while/switch - # expressions - which have their own, more liberal conventions - we - # first see if we should be looking inside such an expression for a - # function call, to which we can apply more strict standards. - fncall = line # if there's no control flow construct, look at whole line - for pattern in (r'\bif\s*\((.*)\)\s*{', - r'\bfor\s*\((.*)\)\s*{', - r'\bwhile\s*\((.*)\)\s*[{;]', - r'\bswitch\s*\((.*)\)\s*{'): - match = Search(pattern, line) - if match: - fncall = match.group(1) # look inside the parens for function calls - break - - # Except in if/for/while/switch, there should never be space - # immediately inside parens (eg "f( 3, 4 )"). We make an exception - # for nested parens ( (a+b) + c ). Likewise, there should never be - # a space before a ( when it's a function argument. I assume it's a - # function argument when the char before the whitespace is legal in - # a function name (alnum + _) and we're not starting a macro. Also ignore - # pointers and references to arrays and functions coz they're too tricky: - # we use a very simple way to recognize these: - # " (something)(maybe-something)" or - # " (something)(maybe-something," or - # " (something)[something]" - # Note that we assume the contents of [] to be short enough that - # they'll never need to wrap. - if ( # Ignore control structures. - not Search(r'\b(if|for|while|switch|return|new|delete|catch|sizeof)\b', - fncall) and - # Ignore pointers/references to functions. - not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and - # Ignore pointers/references to arrays. - not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): - if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call - error(filename, linenum, 'whitespace/parens', 4, - 'Extra space after ( in function call') - elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): - error(filename, linenum, 'whitespace/parens', 2, - 'Extra space after (') - if (Search(r'\w\s+\(', fncall) and - not Search(r'_{0,2}asm_{0,2}\s+_{0,2}volatile_{0,2}\s+\(', fncall) and - not Search(r'#\s*define|typedef|using\s+\w+\s*=', fncall) and - not Search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall) and - not Search(r'\bcase\s+\(', fncall)): - # TODO(unknown): Space after an operator function seem to be a common - # error, silence those for now by restricting them to highest verbosity. - if Search(r'\boperator_*\b', line): - error(filename, linenum, 'whitespace/parens', 0, - 'Extra space before ( in function call') - else: - error(filename, linenum, 'whitespace/parens', 4, - 'Extra space before ( in function call') - # If the ) is followed only by a newline or a { + newline, assume it's - # part of a control statement (if/while/etc), and don't complain - if Search(r'[^)]\s+\)\s*[^{\s]', fncall): - # If the closing parenthesis is preceded by only whitespaces, - # try to give a more descriptive error message. - if Search(r'^\s+\)', fncall): - error(filename, linenum, 'whitespace/parens', 2, - 'Closing ) should be moved to the previous line') - else: - error(filename, linenum, 'whitespace/parens', 2, - 'Extra space before )') - - -def IsBlankLine(line): - """Returns true if the given line is blank. - - We consider a line to be blank if the line is empty or consists of - only white spaces. - - Args: - line: A line of a string. - - Returns: - True, if the given line is blank. - """ - return not line or line.isspace() - - -def CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, - error): - is_namespace_indent_item = ( - len(nesting_state.stack) > 1 and - nesting_state.stack[-1].check_namespace_indentation and - isinstance(nesting_state.previous_stack_top, _NamespaceInfo) and - nesting_state.previous_stack_top == nesting_state.stack[-2]) - - if ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, - clean_lines.elided, line): - CheckItemIndentationInNamespace(filename, clean_lines.elided, - line, error) - - -def CheckForFunctionLengths(filename, clean_lines, linenum, - function_state, error): - """Reports for long function bodies. - - For an overview why this is done, see: - https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions - - Uses a simplistic algorithm assuming other style guidelines - (especially spacing) are followed. - Only checks unindented functions, so class members are unchecked. - Trivial bodies are unchecked, so constructors with huge initializer lists - may be missed. - Blank/comment lines are not counted so as to avoid encouraging the removal - of vertical space and comments just to get through a lint check. - NOLINT *on the last line of a function* disables this check. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - function_state: Current function name and lines in body so far. - error: The function to call with any errors found. - """ - lines = clean_lines.lines - line = lines[linenum] - joined_line = '' - - starting_func = False - regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... - match_result = Match(regexp, line) - if match_result: - # If the name is all caps and underscores, figure it's a macro and - # ignore it, unless it's TEST or TEST_F. - function_name = match_result.group(1).split()[-1] - if function_name == 'TEST' or function_name == 'TEST_F' or ( - not Match(r'[A-Z_]+$', function_name)): - starting_func = True - - if starting_func: - body_found = False - for start_linenum in range(linenum, clean_lines.NumLines()): - start_line = lines[start_linenum] - joined_line += ' ' + start_line.lstrip() - if Search(r'(;|})', start_line): # Declarations and trivial functions - body_found = True - break # ... ignore - elif Search(r'{', start_line): - body_found = True - function = Search(r'((\w|:)*)\(', line).group(1) - if Match(r'TEST', function): # Handle TEST... macros - parameter_regexp = Search(r'(\(.*\))', joined_line) - if parameter_regexp: # Ignore bad syntax - function += parameter_regexp.group(1) - else: - function += '()' - function_state.Begin(function) - break - if not body_found: - # No body for the function (or evidence of a non-function) was found. - error(filename, linenum, 'readability/fn_size', 5, - 'Lint failed to find start of function body.') - elif Match(r'^\}\s*$', line): # function end - function_state.Check(error, filename, linenum) - function_state.End() - elif not Match(r'^\s*$', line): - function_state.Count() # Count non-blank/non-comment lines. - - -_RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?') - - -def CheckComment(line, filename, linenum, next_line_start, error): - """Checks for common mistakes in comments. - - Args: - line: The line in question. - filename: The name of the current file. - linenum: The number of the line to check. - next_line_start: The first non-whitespace column of the next line. - error: The function to call with any errors found. - """ - commentpos = line.find('//') - if commentpos != -1: - # Check if the // may be in quotes. If so, ignore it - if re.sub(r'\\.', '', line[0:commentpos]).count('"') % 2 == 0: - # Allow one space for new scopes, two spaces otherwise: - if (not (Match(r'^.*{ *//', line) and next_line_start == commentpos) and - ((commentpos >= 1 and - line[commentpos-1] not in string.whitespace) or - (commentpos >= 2 and - line[commentpos-2] not in string.whitespace))): - error(filename, linenum, 'whitespace/comments', 2, - 'At least two spaces is best between code and comments') - - # Checks for common mistakes in TODO comments. - comment = line[commentpos:] - match = _RE_PATTERN_TODO.match(comment) - if match: - # One whitespace is correct; zero whitespace is handled elsewhere. - leading_whitespace = match.group(1) - if len(leading_whitespace) > 1: - error(filename, linenum, 'whitespace/todo', 2, - 'Too many spaces before TODO') - - username = match.group(2) - if not username: - error(filename, linenum, 'readability/todo', 2, - 'Missing username in TODO; it should look like ' - '"// TODO(my_username): Stuff."') - - middle_whitespace = match.group(3) - # Comparisons made explicit for correctness -- pylint: disable=g-explicit-bool-comparison - if middle_whitespace != ' ' and middle_whitespace != '': - error(filename, linenum, 'whitespace/todo', 2, - 'TODO(my_username) should be followed by a space') - - # If the comment contains an alphanumeric character, there - # should be a space somewhere between it and the // unless - # it's a /// or //! Doxygen comment. - if (Match(r'//[^ ]*\w', comment) and - not Match(r'(///|//\!)(\s+|$)', comment)): - error(filename, linenum, 'whitespace/comments', 4, - 'Should have a space between // and comment') - - -def CheckAccess(filename, clean_lines, linenum, nesting_state, error): - """Checks for improper use of DISALLOW* macros. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] # get rid of comments and strings - - matched = Match((r'\s*(DISALLOW_COPY_AND_ASSIGN|' - r'DISALLOW_IMPLICIT_CONSTRUCTORS)'), line) - if not matched: - return - if nesting_state.stack and isinstance(nesting_state.stack[-1], _ClassInfo): - if nesting_state.stack[-1].access != 'private': - error(filename, linenum, 'readability/constructors', 3, - '%s must be in the private: section' % matched.group(1)) - - else: - # Found DISALLOW* macro outside a class declaration, or perhaps it - # was used inside a function when it should have been part of the - # class declaration. We could issue a warning here, but it - # probably resulted in a compiler error already. - pass - - -def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): - """Checks for the correctness of various spacing issues in the code. - - Things we check for: spaces around operators, spaces after - if/for/while/switch, no spaces around parens in function calls, two - spaces between code and comment, don't start a block with a blank - line, don't end a function with a blank line, don't add a blank line - after public/protected/private, don't have too many blank lines in a row. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - - # Don't use "elided" lines here, otherwise we can't check commented lines. - # Don't want to use "raw" either, because we don't want to check inside C++11 - # raw strings, - raw = clean_lines.lines_without_raw_strings - line = raw[linenum] - - # Before nixing comments, check if the line is blank for no good - # reason. This includes the first line after a block is opened, and - # blank lines at the end of a function (ie, right before a line like '}' - # - # Skip all the blank line checks if we are immediately inside a - # namespace body. In other words, don't issue blank line warnings - # for this block: - # namespace { - # - # } - # - # A warning about missing end of namespace comments will be issued instead. - # - # Also skip blank line checks for 'extern "C"' blocks, which are formatted - # like namespaces. - if (IsBlankLine(line) and - not nesting_state.InNamespaceBody() and - not nesting_state.InExternC()): - elided = clean_lines.elided - prev_line = elided[linenum - 1] - prevbrace = prev_line.rfind('{') - # TODO(unknown): Don't complain if line before blank line, and line after, - # both start with alnums and are indented the same amount. - # This ignores whitespace at the start of a namespace block - # because those are not usually indented. - if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1: - # OK, we have a blank line at the start of a code block. Before we - # complain, we check if it is an exception to the rule: The previous - # non-empty line has the parameters of a function header that are indented - # 4 spaces (because they did not fit in a 80 column line when placed on - # the same line as the function name). We also check for the case where - # the previous line is indented 6 spaces, which may happen when the - # initializers of a constructor do not fit into a 80 column line. - exception = False - if Match(r' {6}\w', prev_line): # Initializer list? - # We are looking for the opening column of initializer list, which - # should be indented 4 spaces to cause 6 space indentation afterwards. - search_position = linenum-2 - while (search_position >= 0 - and Match(r' {6}\w', elided[search_position])): - search_position -= 1 - exception = (search_position >= 0 - and elided[search_position][:5] == ' :') - else: - # Search for the function arguments or an initializer list. We use a - # simple heuristic here: If the line is indented 4 spaces; and we have a - # closing paren, without the opening paren, followed by an opening brace - # or colon (for initializer lists) we assume that it is the last line of - # a function header. If we have a colon indented 4 spaces, it is an - # initializer list. - exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', - prev_line) - or Match(r' {4}:', prev_line)) - - if not exception: - error(filename, linenum, 'whitespace/blank_line', 2, - 'Redundant blank line at the start of a code block ' - 'should be deleted.') - # Ignore blank lines at the end of a block in a long if-else - # chain, like this: - # if (condition1) { - # // Something followed by a blank line - # - # } else if (condition2) { - # // Something else - # } - if linenum + 1 < clean_lines.NumLines(): - next_line = raw[linenum + 1] - if (next_line - and Match(r'\s*}', next_line) - and next_line.find('} else ') == -1): - error(filename, linenum, 'whitespace/blank_line', 3, - 'Redundant blank line at the end of a code block ' - 'should be deleted.') - - matched = Match(r'\s*(public|protected|private):', prev_line) - if matched: - error(filename, linenum, 'whitespace/blank_line', 3, - 'Do not leave a blank line after "%s:"' % matched.group(1)) - - # Next, check comments - next_line_start = 0 - if linenum + 1 < clean_lines.NumLines(): - next_line = raw[linenum + 1] - next_line_start = len(next_line) - len(next_line.lstrip()) - CheckComment(line, filename, linenum, next_line_start, error) - - # get rid of comments and strings - line = clean_lines.elided[linenum] - - # You shouldn't have spaces before your brackets, except maybe after - # 'delete []' or 'return []() {};' - if Search(r'\w\s+\[', line) and not Search(r'(?:delete|return)\s+\[', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Extra space before [') - - # In range-based for, we wanted spaces before and after the colon, but - # not around "::" tokens that might appear. - if (Search(r'for *\(.*[^:]:[^: ]', line) or - Search(r'for *\(.*[^: ]:[^:]', line)): - error(filename, linenum, 'whitespace/forcolon', 2, - 'Missing space around colon in range-based for loop') - - -def CheckOperatorSpacing(filename, clean_lines, linenum, error): - """Checks for horizontal spacing around operators. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Don't try to do spacing checks for operator methods. Do this by - # replacing the troublesome characters with something else, - # preserving column position for all other characters. - # - # The replacement is done repeatedly to avoid false positives from - # operators that call operators. - while True: - match = Match(r'^(.*\boperator\b)(\S+)(\s*\(.*)$', line) - if match: - line = match.group(1) + ('_' * len(match.group(2))) + match.group(3) - else: - break - - # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". - # Otherwise not. Note we only check for non-spaces on *both* sides; - # sometimes people put non-spaces on one side when aligning ='s among - # many lines (not that this is behavior that I approve of...) - if ((Search(r'[\w.]=', line) or - Search(r'=[\w.]', line)) - and not Search(r'\b(if|while|for) ', line) - # Operators taken from [lex.operators] in C++11 standard. - and not Search(r'(>=|<=|==|!=|&=|\^=|\|=|\+=|\*=|\/=|\%=)', line) - and not Search(r'operator=', line)): - error(filename, linenum, 'whitespace/operators', 4, - 'Missing spaces around =') - - # It's ok not to have spaces around binary operators like + - * /, but if - # there's too little whitespace, we get concerned. It's hard to tell, - # though, so we punt on this one for now. TODO. - - # You should always have whitespace around binary operators. - # - # Check <= and >= first to avoid false positives with < and >, then - # check non-include lines for spacing around < and >. - # - # If the operator is followed by a comma, assume it's be used in a - # macro context and don't do any checks. This avoids false - # positives. - # - # Note that && is not included here. This is because there are too - # many false positives due to RValue references. - match = Search(r'[^<>=!\s](==|!=|<=|>=|\|\|)[^<>=!\s,;\)]', line) - if match: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around %s' % match.group(1)) - elif not Match(r'#.*include', line): - # Look for < that is not surrounded by spaces. This is only - # triggered if both sides are missing spaces, even though - # technically should should flag if at least one side is missing a - # space. This is done to avoid some false positives with shifts. - match = Match(r'^(.*[^\s<])<[^\s=<,]', line) - if match: - (_, _, end_pos) = CloseExpression( - clean_lines, linenum, len(match.group(1))) - if end_pos <= -1: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around <') - - # Look for > that is not surrounded by spaces. Similar to the - # above, we only trigger if both sides are missing spaces to avoid - # false positives with shifts. - match = Match(r'^(.*[^-\s>])>[^\s=>,]', line) - if match: - (_, _, start_pos) = ReverseCloseExpression( - clean_lines, linenum, len(match.group(1))) - if start_pos <= -1: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around >') - - # We allow no-spaces around << when used like this: 10<<20, but - # not otherwise (particularly, not when used as streams) - # - # We also allow operators following an opening parenthesis, since - # those tend to be macros that deal with operators. - match = Search(r'(operator|[^\s(<])(?:L|UL|LL|ULL|l|ul|ll|ull)?<<([^\s,=<])', line) - if (match and not (match.group(1).isdigit() and match.group(2).isdigit()) and - not (match.group(1) == 'operator' and match.group(2) == ';')): - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around <<') - - # We allow no-spaces around >> for almost anything. This is because - # C++11 allows ">>" to close nested templates, which accounts for - # most cases when ">>" is not followed by a space. - # - # We still warn on ">>" followed by alpha character, because that is - # likely due to ">>" being used for right shifts, e.g.: - # value >> alpha - # - # When ">>" is used to close templates, the alphanumeric letter that - # follows would be part of an identifier, and there should still be - # a space separating the template type and the identifier. - # type> alpha - match = Search(r'>>[a-zA-Z_]', line) - if match: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around >>') - - # There shouldn't be space around unary operators - match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) - if match: - error(filename, linenum, 'whitespace/operators', 4, - 'Extra space for operator %s' % match.group(1)) - - -def CheckParenthesisSpacing(filename, clean_lines, linenum, error): - """Checks for horizontal spacing around parentheses. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # No spaces after an if, while, switch, or for - match = Search(r' (if\(|for\(|while\(|switch\()', line) - if match: - error(filename, linenum, 'whitespace/parens', 5, - 'Missing space before ( in %s' % match.group(1)) - - # For if/for/while/switch, the left and right parens should be - # consistent about how many spaces are inside the parens, and - # there should either be zero or one spaces inside the parens. - # We don't want: "if ( foo)" or "if ( foo )". - # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. - match = Search(r'\b(if|for|while|switch)\s*' - r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', - line) - if match: - if len(match.group(2)) != len(match.group(4)): - if not (match.group(3) == ';' and - len(match.group(2)) == 1 + len(match.group(4)) or - not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): - error(filename, linenum, 'whitespace/parens', 5, - 'Mismatching spaces inside () in %s' % match.group(1)) - if len(match.group(2)) not in [0, 1]: - error(filename, linenum, 'whitespace/parens', 5, - 'Should have zero or one spaces inside ( and ) in %s' % - match.group(1)) - - -def CheckCommaSpacing(filename, clean_lines, linenum, error): - """Checks for horizontal spacing near commas and semicolons. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - raw = clean_lines.lines_without_raw_strings - line = clean_lines.elided[linenum] - - # You should always have a space after a comma (either as fn arg or operator) - # - # This does not apply when the non-space character following the - # comma is another comma, since the only time when that happens is - # for empty macro arguments. - # - # We run this check in two passes: first pass on elided lines to - # verify that lines contain missing whitespaces, second pass on raw - # lines to confirm that those missing whitespaces are not due to - # elided comments. - if (Search(r',[^,\s]', ReplaceAll(r'\boperator\s*,\s*\(', 'F(', line)) and - Search(r',[^,\s]', raw[linenum])): - error(filename, linenum, 'whitespace/comma', 3, - 'Missing space after ,') - - # You should always have a space after a semicolon - # except for few corner cases - # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more - # space after ; - if Search(r';[^\s};\\)/]', line): - error(filename, linenum, 'whitespace/semicolon', 3, - 'Missing space after ;') - - -def _IsType(clean_lines, nesting_state, expr): - """Check if expression looks like a type name, returns true if so. - - Args: - clean_lines: A CleansedLines instance containing the file. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - expr: The expression to check. - Returns: - True, if token looks like a type. - """ - # Keep only the last token in the expression - last_word = Match(r'^.*(\b\S+)$', expr) - if last_word: - token = last_word.group(1) - else: - token = expr - - # Match native types and stdint types - if _TYPES.match(token): - return True - - # Try a bit harder to match templated types. Walk up the nesting - # stack until we find something that resembles a typename - # declaration for what we are looking for. - typename_pattern = (r'\b(?:typename|class|struct)\s+' + re.escape(token) + - r'\b') - block_index = len(nesting_state.stack) - 1 - while block_index >= 0: - if isinstance(nesting_state.stack[block_index], _NamespaceInfo): - return False - - # Found where the opening brace is. We want to scan from this - # line up to the beginning of the function, minus a few lines. - # template - # class C - # : public ... { // start scanning here - last_line = nesting_state.stack[block_index].starting_linenum - - next_block_start = 0 - if block_index > 0: - next_block_start = nesting_state.stack[block_index - 1].starting_linenum - first_line = last_line - while first_line >= next_block_start: - if clean_lines.elided[first_line].find('template') >= 0: - break - first_line -= 1 - if first_line < next_block_start: - # Didn't find any "template" keyword before reaching the next block, - # there are probably no template things to check for this block - block_index -= 1 - continue - - # Look for typename in the specified range - for i in xrange(first_line, last_line + 1, 1): - if Search(typename_pattern, clean_lines.elided[i]): - return True - block_index -= 1 - - return False - - -def CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error): - """Checks for horizontal spacing near commas. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Except after an opening paren, or after another opening brace (in case of - # an initializer list, for instance), you should have spaces before your - # braces when they are delimiting blocks, classes, namespaces etc. - # And since you should never have braces at the beginning of a line, - # this is an easy test. Except that braces used for initialization don't - # follow the same rule; we often don't want spaces before those. - match = Match(r'^(.*[^ ({>]){', line) - - if match: - # Try a bit harder to check for brace initialization. This - # happens in one of the following forms: - # Constructor() : initializer_list_{} { ... } - # Constructor{}.MemberFunction() - # Type variable{}; - # FunctionCall(type{}, ...); - # LastArgument(..., type{}); - # LOG(INFO) << type{} << " ..."; - # map_of_type[{...}] = ...; - # ternary = expr ? new type{} : nullptr; - # OuterTemplate{}> - # - # We check for the character following the closing brace, and - # silence the warning if it's one of those listed above, i.e. - # "{.;,)<>]:". - # - # To account for nested initializer list, we allow any number of - # closing braces up to "{;,)<". We can't simply silence the - # warning on first sight of closing brace, because that would - # cause false negatives for things that are not initializer lists. - # Silence this: But not this: - # Outer{ if (...) { - # Inner{...} if (...){ // Missing space before { - # }; } - # - # There is a false negative with this approach if people inserted - # spurious semicolons, e.g. "if (cond){};", but we will catch the - # spurious semicolon with a separate check. - leading_text = match.group(1) - (endline, endlinenum, endpos) = CloseExpression( - clean_lines, linenum, len(match.group(1))) - trailing_text = '' - if endpos > -1: - trailing_text = endline[endpos:] - for offset in xrange(endlinenum + 1, - min(endlinenum + 3, clean_lines.NumLines() - 1)): - trailing_text += clean_lines.elided[offset] - # We also suppress warnings for `uint64_t{expression}` etc., as the style - # guide recommends brace initialization for integral types to avoid - # overflow/truncation. - if (not Match(r'^[\s}]*[{.;,)<>\]:]', trailing_text) - and not _IsType(clean_lines, nesting_state, leading_text)): - error(filename, linenum, 'whitespace/braces', 5, - 'Missing space before {') - - # Make sure '} else {' has spaces. - if Search(r'}else', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Missing space before else') - - # You shouldn't have a space before a semicolon at the end of the line. - # There's a special case for "for" since the style guide allows space before - # the semicolon there. - if Search(r':\s*;\s*$', line): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Semicolon defining empty statement. Use {} instead.') - elif Search(r'^\s*;\s*$', line): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Line contains only semicolon. If this should be an empty statement, ' - 'use {} instead.') - elif (Search(r'\s+;\s*$', line) and - not Search(r'\bfor\b', line)): - error(filename, linenum, 'whitespace/semicolon', 5, - 'Extra space before last semicolon. If this should be an empty ' - 'statement, use {} instead.') - - -def IsDecltype(clean_lines, linenum, column): - """Check if the token ending on (linenum, column) is decltype(). - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: the number of the line to check. - column: end column of the token to check. - Returns: - True if this token is decltype() expression, False otherwise. - """ - (text, _, start_col) = ReverseCloseExpression(clean_lines, linenum, column) - if start_col < 0: - return False - if Search(r'\bdecltype\s*$', text[0:start_col]): - return True - return False - -def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): - """Checks for additional blank line issues related to sections. - - Currently the only thing checked here is blank line before protected/private. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - class_info: A _ClassInfo objects. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - # Skip checks if the class is small, where small means 25 lines or less. - # 25 lines seems like a good cutoff since that's the usual height of - # terminals, and any class that can't fit in one screen can't really - # be considered "small". - # - # Also skip checks if we are on the first line. This accounts for - # classes that look like - # class Foo { public: ... }; - # - # If we didn't find the end of the class, last_line would be zero, - # and the check will be skipped by the first condition. - if (class_info.last_line - class_info.starting_linenum <= 24 or - linenum <= class_info.starting_linenum): - return - - matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) - if matched: - # Issue warning if the line before public/protected/private was - # not a blank line, but don't do this if the previous line contains - # "class" or "struct". This can happen two ways: - # - We are at the beginning of the class. - # - We are forward-declaring an inner class that is semantically - # private, but needed to be public for implementation reasons. - # Also ignores cases where the previous line ends with a backslash as can be - # common when defining classes in C macros. - prev_line = clean_lines.lines[linenum - 1] - if (not IsBlankLine(prev_line) and - not Search(r'\b(class|struct)\b', prev_line) and - not Search(r'\\$', prev_line)): - # Try a bit harder to find the beginning of the class. This is to - # account for multi-line base-specifier lists, e.g.: - # class Derived - # : public Base { - end_class_head = class_info.starting_linenum - for i in range(class_info.starting_linenum, linenum): - if Search(r'\{\s*$', clean_lines.lines[i]): - end_class_head = i - break - if end_class_head < linenum - 1: - error(filename, linenum, 'whitespace/blank_line', 3, - '"%s:" should be preceded by a blank line' % matched.group(1)) - - -def GetPreviousNonBlankLine(clean_lines, linenum): - """Return the most recent non-blank line and its line number. - - Args: - clean_lines: A CleansedLines instance containing the file contents. - linenum: The number of the line to check. - - Returns: - A tuple with two elements. The first element is the contents of the last - non-blank line before the current line, or the empty string if this is the - first non-blank line. The second is the line number of that line, or -1 - if this is the first non-blank line. - """ - - prevlinenum = linenum - 1 - while prevlinenum >= 0: - prevline = clean_lines.elided[prevlinenum] - if not IsBlankLine(prevline): # if not a blank line... - return (prevline, prevlinenum) - prevlinenum -= 1 - return ('', -1) - - -def CheckBraces(filename, clean_lines, linenum, error): - """Looks for misplaced braces (e.g. at the end of line). - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - line = clean_lines.elided[linenum] # get rid of comments and strings - - if Match(r'\s*{\s*$', line): - # We allow an open brace to start a line in the case where someone is using - # braces in a block to explicitly create a new scope, which is commonly used - # to control the lifetime of stack-allocated variables. Braces are also - # used for brace initializers inside function calls. We don't detect this - # perfectly: we just don't complain if the last non-whitespace character on - # the previous non-blank line is ',', ';', ':', '(', '{', or '}', or if the - # previous line starts a preprocessor block. We also allow a brace on the - # following line if it is part of an array initialization and would not fit - # within the 80 character limit of the preceding line. - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if (not Search(r'[,;:}{(]\s*$', prevline) and - not Match(r'\s*#', prevline) and - not (GetLineWidth(prevline) > _line_length - 2 and '[]' in prevline)): - error(filename, linenum, 'whitespace/braces', 4, - '{ should almost always be at the end of the previous line') - - # An else clause should be on the same line as the preceding closing brace. - if Match(r'\s*else\b\s*(?:if\b|\{|$)', line): - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if Match(r'\s*}\s*$', prevline): - error(filename, linenum, 'whitespace/newline', 4, - 'An else should appear on the same line as the preceding }') - - # If braces come on one side of an else, they should be on both. - # However, we have to worry about "else if" that spans multiple lines! - if Search(r'else if\s*\(', line): # could be multi-line if - brace_on_left = bool(Search(r'}\s*else if\s*\(', line)) - # find the ( after the if - pos = line.find('else if') - pos = line.find('(', pos) - if pos > 0: - (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) - brace_on_right = endline[endpos:].find('{') != -1 - if brace_on_left != brace_on_right: # must be brace after if - error(filename, linenum, 'readability/braces', 5, - 'If an else has a brace on one side, it should have it on both') - elif Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): - error(filename, linenum, 'readability/braces', 5, - 'If an else has a brace on one side, it should have it on both') - - # Likewise, an else should never have the else clause on the same line - if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): - error(filename, linenum, 'whitespace/newline', 4, - 'Else clause should never be on same line as else (use 2 lines)') - - # In the same way, a do/while should never be on one line - if Match(r'\s*do [^\s{]', line): - error(filename, linenum, 'whitespace/newline', 4, - 'do/while clauses should not be on a single line') - - # Check single-line if/else bodies. The style guide says 'curly braces are not - # required for single-line statements'. We additionally allow multi-line, - # single statements, but we reject anything with more than one semicolon in - # it. This means that the first semicolon after the if should be at the end of - # its line, and the line after that should have an indent level equal to or - # lower than the if. We also check for ambiguous if/else nesting without - # braces. - if_else_match = Search(r'\b(if\s*\(|else\b)', line) - if if_else_match and not Match(r'\s*#', line): - if_indent = GetIndentLevel(line) - endline, endlinenum, endpos = line, linenum, if_else_match.end() - if_match = Search(r'\bif\s*\(', line) - if if_match: - # This could be a multiline if condition, so find the end first. - pos = if_match.end() - 1 - (endline, endlinenum, endpos) = CloseExpression(clean_lines, linenum, pos) - # Check for an opening brace, either directly after the if or on the next - # line. If found, this isn't a single-statement conditional. - if (not Match(r'\s*{', endline[endpos:]) - and not (Match(r'\s*$', endline[endpos:]) - and endlinenum < (len(clean_lines.elided) - 1) - and Match(r'\s*{', clean_lines.elided[endlinenum + 1]))): - while (endlinenum < len(clean_lines.elided) - and ';' not in clean_lines.elided[endlinenum][endpos:]): - endlinenum += 1 - endpos = 0 - if endlinenum < len(clean_lines.elided): - endline = clean_lines.elided[endlinenum] - # We allow a mix of whitespace and closing braces (e.g. for one-liner - # methods) and a single \ after the semicolon (for macros) - endpos = endline.find(';') - if not Match(r';[\s}]*(\\?)$', endline[endpos:]): - # Semicolon isn't the last character, there's something trailing. - # Output a warning if the semicolon is not contained inside - # a lambda expression. - if not Match(r'^[^{};]*\[[^\[\]]*\][^{}]*\{[^{}]*\}\s*\)*[;,]\s*$', - endline): - error(filename, linenum, 'readability/braces', 4, - 'If/else bodies with multiple statements require braces') - elif endlinenum < len(clean_lines.elided) - 1: - # Make sure the next line is dedented - next_line = clean_lines.elided[endlinenum + 1] - next_indent = GetIndentLevel(next_line) - # With ambiguous nested if statements, this will error out on the - # if that *doesn't* match the else, regardless of whether it's the - # inner one or outer one. - if (if_match and Match(r'\s*else\b', next_line) - and next_indent != if_indent): - error(filename, linenum, 'readability/braces', 4, - 'Else clause should be indented at the same level as if. ' - 'Ambiguous nested if/else chains require braces.') - elif next_indent > if_indent: - error(filename, linenum, 'readability/braces', 4, - 'If/else bodies with multiple statements require braces') - - -def CheckTrailingSemicolon(filename, clean_lines, linenum, error): - """Looks for redundant trailing semicolon. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - line = clean_lines.elided[linenum] - - # Block bodies should not be followed by a semicolon. Due to C++11 - # brace initialization, there are more places where semicolons are - # required than not, so we use a whitelist approach to check these - # rather than a blacklist. These are the places where "};" should - # be replaced by just "}": - # 1. Some flavor of block following closing parenthesis: - # for (;;) {}; - # while (...) {}; - # switch (...) {}; - # Function(...) {}; - # if (...) {}; - # if (...) else if (...) {}; - # - # 2. else block: - # if (...) else {}; - # - # 3. const member function: - # Function(...) const {}; - # - # 4. Block following some statement: - # x = 42; - # {}; - # - # 5. Block at the beginning of a function: - # Function(...) { - # {}; - # } - # - # Note that naively checking for the preceding "{" will also match - # braces inside multi-dimensional arrays, but this is fine since - # that expression will not contain semicolons. - # - # 6. Block following another block: - # while (true) {} - # {}; - # - # 7. End of namespaces: - # namespace {}; - # - # These semicolons seems far more common than other kinds of - # redundant semicolons, possibly due to people converting classes - # to namespaces. For now we do not warn for this case. - # - # Try matching case 1 first. - match = Match(r'^(.*\)\s*)\{', line) - if match: - # Matched closing parenthesis (case 1). Check the token before the - # matching opening parenthesis, and don't warn if it looks like a - # macro. This avoids these false positives: - # - macro that defines a base class - # - multi-line macro that defines a base class - # - macro that defines the whole class-head - # - # But we still issue warnings for macros that we know are safe to - # warn, specifically: - # - TEST, TEST_F, TEST_P, MATCHER, MATCHER_P - # - TYPED_TEST - # - INTERFACE_DEF - # - EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED, LOCKS_EXCLUDED: - # - # We implement a whitelist of safe macros instead of a blacklist of - # unsafe macros, even though the latter appears less frequently in - # google code and would have been easier to implement. This is because - # the downside for getting the whitelist wrong means some extra - # semicolons, while the downside for getting the blacklist wrong - # would result in compile errors. - # - # In addition to macros, we also don't want to warn on - # - Compound literals - # - Lambdas - # - alignas specifier with anonymous structs - # - decltype - closing_brace_pos = match.group(1).rfind(')') - opening_parenthesis = ReverseCloseExpression( - clean_lines, linenum, closing_brace_pos) - if opening_parenthesis[2] > -1: - line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]] - macro = Search(r'\b([A-Z_][A-Z0-9_]*)\s*$', line_prefix) - func = Match(r'^(.*\])\s*$', line_prefix) - if ((macro and - macro.group(1) not in ( - 'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST', - 'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED', - 'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or - (func and not Search(r'\boperator\s*\[\s*\]', func.group(1))) or - Search(r'\b(?:struct|union)\s+alignas\s*$', line_prefix) or - Search(r'\bdecltype$', line_prefix) or - Search(r'\s+=\s*$', line_prefix)): - match = None - if (match and - opening_parenthesis[1] > 1 and - Search(r'\]\s*$', clean_lines.elided[opening_parenthesis[1] - 1])): - # Multi-line lambda-expression - match = None - - else: - # Try matching cases 2-3. - match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line) - if not match: - # Try matching cases 4-6. These are always matched on separate lines. - # - # Note that we can't simply concatenate the previous line to the - # current line and do a single match, otherwise we may output - # duplicate warnings for the blank line case: - # if (cond) { - # // blank line - # } - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if prevline and Search(r'[;{}]\s*$', prevline): - match = Match(r'^(\s*)\{', line) - - # Check matching closing brace - if match: - (endline, endlinenum, endpos) = CloseExpression( - clean_lines, linenum, len(match.group(1))) - if endpos > -1 and Match(r'^\s*;', endline[endpos:]): - # Current {} pair is eligible for semicolon check, and we have found - # the redundant semicolon, output warning here. - # - # Note: because we are scanning forward for opening braces, and - # outputting warnings for the matching closing brace, if there are - # nested blocks with trailing semicolons, we will get the error - # messages in reversed order. - - # We need to check the line forward for NOLINT - raw_lines = clean_lines.raw_lines - ParseNolintSuppressions(filename, raw_lines[endlinenum-1], endlinenum-1, - error) - ParseNolintSuppressions(filename, raw_lines[endlinenum], endlinenum, - error) - - error(filename, endlinenum, 'readability/braces', 4, - "You don't need a ; after a }") - - -def CheckEmptyBlockBody(filename, clean_lines, linenum, error): - """Look for empty loop/conditional body with only a single semicolon. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - # Search for loop keywords at the beginning of the line. Because only - # whitespaces are allowed before the keywords, this will also ignore most - # do-while-loops, since those lines should start with closing brace. - # - # We also check "if" blocks here, since an empty conditional block - # is likely an error. - line = clean_lines.elided[linenum] - matched = Match(r'\s*(for|while|if)\s*\(', line) - if matched: - # Find the end of the conditional expression. - (end_line, end_linenum, end_pos) = CloseExpression( - clean_lines, linenum, line.find('(')) - - # Output warning if what follows the condition expression is a semicolon. - # No warning for all other cases, including whitespace or newline, since we - # have a separate check for semicolons preceded by whitespace. - if end_pos >= 0 and Match(r';', end_line[end_pos:]): - if matched.group(1) == 'if': - error(filename, end_linenum, 'whitespace/empty_conditional_body', 5, - 'Empty conditional bodies should use {}') - else: - error(filename, end_linenum, 'whitespace/empty_loop_body', 5, - 'Empty loop bodies should use {} or continue') - - # Check for if statements that have completely empty bodies (no comments) - # and no else clauses. - if end_pos >= 0 and matched.group(1) == 'if': - # Find the position of the opening { for the if statement. - # Return without logging an error if it has no brackets. - opening_linenum = end_linenum - opening_line_fragment = end_line[end_pos:] - # Loop until EOF or find anything that's not whitespace or opening {. - while not Search(r'^\s*\{', opening_line_fragment): - if Search(r'^(?!\s*$)', opening_line_fragment): - # Conditional has no brackets. - return - opening_linenum += 1 - if opening_linenum == len(clean_lines.elided): - # Couldn't find conditional's opening { or any code before EOF. - return - opening_line_fragment = clean_lines.elided[opening_linenum] - # Set opening_line (opening_line_fragment may not be entire opening line). - opening_line = clean_lines.elided[opening_linenum] - - # Find the position of the closing }. - opening_pos = opening_line_fragment.find('{') - if opening_linenum == end_linenum: - # We need to make opening_pos relative to the start of the entire line. - opening_pos += end_pos - (closing_line, closing_linenum, closing_pos) = CloseExpression( - clean_lines, opening_linenum, opening_pos) - if closing_pos < 0: - return - - # Now construct the body of the conditional. This consists of the portion - # of the opening line after the {, all lines until the closing line, - # and the portion of the closing line before the }. - if (clean_lines.raw_lines[opening_linenum] != - CleanseComments(clean_lines.raw_lines[opening_linenum])): - # Opening line ends with a comment, so conditional isn't empty. - return - if closing_linenum > opening_linenum: - # Opening line after the {. Ignore comments here since we checked above. - bodylist = list(opening_line[opening_pos+1:]) - # All lines until closing line, excluding closing line, with comments. - bodylist.extend(clean_lines.raw_lines[opening_linenum+1:closing_linenum]) - # Closing line before the }. Won't (and can't) have comments. - bodylist.append(clean_lines.elided[closing_linenum][:closing_pos-1]) - body = '\n'.join(bodylist) - else: - # If statement has brackets and fits on a single line. - body = opening_line[opening_pos+1:closing_pos-1] - - # Check if the body is empty - if not _EMPTY_CONDITIONAL_BODY_PATTERN.search(body): - return - # The body is empty. Now make sure there's not an else clause. - current_linenum = closing_linenum - current_line_fragment = closing_line[closing_pos:] - # Loop until EOF or find anything that's not whitespace or else clause. - while Search(r'^\s*$|^(?=\s*else)', current_line_fragment): - if Search(r'^(?=\s*else)', current_line_fragment): - # Found an else clause, so don't log an error. - return - current_linenum += 1 - if current_linenum == len(clean_lines.elided): - break - current_line_fragment = clean_lines.elided[current_linenum] - - # The body is empty and there's no else clause until EOF or other code. - error(filename, end_linenum, 'whitespace/empty_if_body', 4, - ('If statement had no body and no else clause')) - - -def FindCheckMacro(line): - """Find a replaceable CHECK-like macro. - - Args: - line: line to search on. - Returns: - (macro name, start position), or (None, -1) if no replaceable - macro is found. - """ - for macro in _CHECK_MACROS: - i = line.find(macro) - if i >= 0: - # Find opening parenthesis. Do a regular expression match here - # to make sure that we are matching the expected CHECK macro, as - # opposed to some other macro that happens to contain the CHECK - # substring. - matched = Match(r'^(.*\b' + macro + r'\s*)\(', line) - if not matched: - continue - return (macro, len(matched.group(1))) - return (None, -1) - - -def CheckCheck(filename, clean_lines, linenum, error): - """Checks the use of CHECK and EXPECT macros. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - # Decide the set of replacement macros that should be suggested - lines = clean_lines.elided - (check_macro, start_pos) = FindCheckMacro(lines[linenum]) - if not check_macro: - return - - # Find end of the boolean expression by matching parentheses - (last_line, end_line, end_pos) = CloseExpression( - clean_lines, linenum, start_pos) - if end_pos < 0: - return - - # If the check macro is followed by something other than a - # semicolon, assume users will log their own custom error messages - # and don't suggest any replacements. - if not Match(r'\s*;', last_line[end_pos:]): - return - - if linenum == end_line: - expression = lines[linenum][start_pos + 1:end_pos - 1] - else: - expression = lines[linenum][start_pos + 1:] - for i in xrange(linenum + 1, end_line): - expression += lines[i] - expression += last_line[0:end_pos - 1] - - # Parse expression so that we can take parentheses into account. - # This avoids false positives for inputs like "CHECK((a < 4) == b)", - # which is not replaceable by CHECK_LE. - lhs = '' - rhs = '' - operator = None - while expression: - matched = Match(r'^\s*(<<|<<=|>>|>>=|->\*|->|&&|\|\||' - r'==|!=|>=|>|<=|<|\()(.*)$', expression) - if matched: - token = matched.group(1) - if token == '(': - # Parenthesized operand - expression = matched.group(2) - (end, _) = FindEndOfExpressionInLine(expression, 0, ['(']) - if end < 0: - return # Unmatched parenthesis - lhs += '(' + expression[0:end] - expression = expression[end:] - elif token in ('&&', '||'): - # Logical and/or operators. This means the expression - # contains more than one term, for example: - # CHECK(42 < a && a < b); - # - # These are not replaceable with CHECK_LE, so bail out early. - return - elif token in ('<<', '<<=', '>>', '>>=', '->*', '->'): - # Non-relational operator - lhs += token - expression = matched.group(2) - else: - # Relational operator - operator = token - rhs = matched.group(2) - break - else: - # Unparenthesized operand. Instead of appending to lhs one character - # at a time, we do another regular expression match to consume several - # characters at once if possible. Trivial benchmark shows that this - # is more efficient when the operands are longer than a single - # character, which is generally the case. - matched = Match(r'^([^-=!<>()&|]+)(.*)$', expression) - if not matched: - matched = Match(r'^(\s*\S)(.*)$', expression) - if not matched: - break - lhs += matched.group(1) - expression = matched.group(2) - - # Only apply checks if we got all parts of the boolean expression - if not (lhs and operator and rhs): - return - - # Check that rhs do not contain logical operators. We already know - # that lhs is fine since the loop above parses out && and ||. - if rhs.find('&&') > -1 or rhs.find('||') > -1: - return - - # At least one of the operands must be a constant literal. This is - # to avoid suggesting replacements for unprintable things like - # CHECK(variable != iterator) - # - # The following pattern matches decimal, hex integers, strings, and - # characters (in that order). - lhs = lhs.strip() - rhs = rhs.strip() - match_constant = r'^([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')$' - if Match(match_constant, lhs) or Match(match_constant, rhs): - # Note: since we know both lhs and rhs, we can provide a more - # descriptive error message like: - # Consider using CHECK_EQ(x, 42) instead of CHECK(x == 42) - # Instead of: - # Consider using CHECK_EQ instead of CHECK(a == b) - # - # We are still keeping the less descriptive message because if lhs - # or rhs gets long, the error message might become unreadable. - error(filename, linenum, 'readability/check', 2, - 'Consider using %s instead of %s(a %s b)' % ( - _CHECK_REPLACEMENT[check_macro][operator], - check_macro, operator)) - - -def CheckAltTokens(filename, clean_lines, linenum, error): - """Check alternative keywords being used in boolean expressions. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Avoid preprocessor lines - if Match(r'^\s*#', line): - return - - # Last ditch effort to avoid multi-line comments. This will not help - # if the comment started before the current line or ended after the - # current line, but it catches most of the false positives. At least, - # it provides a way to workaround this warning for people who use - # multi-line comments in preprocessor macros. - # - # TODO(unknown): remove this once cpplint has better support for - # multi-line comments. - if line.find('/*') >= 0 or line.find('*/') >= 0: - return - - for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): - error(filename, linenum, 'readability/alt_tokens', 2, - 'Use operator %s instead of %s' % ( - _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1))) - - -def GetLineWidth(line): - """Determines the width of the line in column positions. - - Args: - line: A string, which may be a Unicode string. - - Returns: - The width of the line in column positions, accounting for Unicode - combining characters and wide characters. - """ - if isinstance(line, unicode): - width = 0 - for uc in unicodedata.normalize('NFC', line): - if unicodedata.east_asian_width(uc) in ('W', 'F'): - width += 2 - elif not unicodedata.combining(uc): - width += 1 - return width - else: - return len(line) - - -def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, - error): - """Checks rules from the 'C++ style rules' section of cppguide.html. - - Most of these rules are hard to test (naming, comment style), but we - do what we can. In particular we check for 2-space indents, line lengths, - tab usage, spaces inside code, etc. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - file_extension: The extension (without the dot) of the filename. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - - # Don't use "elided" lines here, otherwise we can't check commented lines. - # Don't want to use "raw" either, because we don't want to check inside C++11 - # raw strings, - raw_lines = clean_lines.lines_without_raw_strings - line = raw_lines[linenum] - prev = raw_lines[linenum - 1] if linenum > 0 else '' - - if line.find('\t') != -1: - error(filename, linenum, 'whitespace/tab', 1, - 'Tab found; better to use spaces') - - # One or three blank spaces at the beginning of the line is weird; it's - # hard to reconcile that with 2-space indents. - # NOTE: here are the conditions rob pike used for his tests. Mine aren't - # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces - # if(RLENGTH > 20) complain = 0; - # if(match($0, " +(error|private|public|protected):")) complain = 0; - # if(match(prev, "&& *$")) complain = 0; - # if(match(prev, "\\|\\| *$")) complain = 0; - # if(match(prev, "[\",=><] *$")) complain = 0; - # if(match($0, " <<")) complain = 0; - # if(match(prev, " +for \\(")) complain = 0; - # if(prevodd && match(prevprev, " +for \\(")) complain = 0; - scope_or_label_pattern = r'\s*\w+\s*:\s*\\?$' - classinfo = nesting_state.InnermostClass() - initial_spaces = 0 - cleansed_line = clean_lines.elided[linenum] - while initial_spaces < len(line) and line[initial_spaces] == ' ': - initial_spaces += 1 - # There are certain situations we allow one space, notably for - # section labels, and also lines containing multi-line raw strings. - # We also don't check for lines that look like continuation lines - # (of lines ending in double quotes, commas, equals, or angle brackets) - # because the rules for how to indent those are non-trivial. - if (not Search(r'[",=><] *$', prev) and - (initial_spaces == 1 or initial_spaces == 3) and - not Match(scope_or_label_pattern, cleansed_line) and - not (clean_lines.raw_lines[linenum] != line and - Match(r'^\s*""', line))): - error(filename, linenum, 'whitespace/indent', 3, - 'Weird number of spaces at line-start. ' - 'Are you using a 2-space indent?') - - if line and line[-1].isspace(): - error(filename, linenum, 'whitespace/end_of_line', 4, - 'Line ends in whitespace. Consider deleting these extra spaces.') - - # Check if the line is a header guard. - is_header_guard = False - if file_extension in GetHeaderExtensions(): - cppvar = GetHeaderGuardCPPVariable(filename) - if (line.startswith('#ifndef %s' % cppvar) or - line.startswith('#define %s' % cppvar) or - line.startswith('#endif // %s' % cppvar)): - is_header_guard = True - # #include lines and header guards can be long, since there's no clean way to - # split them. - # - # URLs can be long too. It's possible to split these, but it makes them - # harder to cut&paste. - # - # The "$Id:...$" comment may also get very long without it being the - # developers fault. - # - # Doxygen documentation copying can get pretty long when using an overloaded - # function declaration - if (not line.startswith('#include') and not is_header_guard and - not Match(r'^\s*//.*http(s?)://\S*$', line) and - not Match(r'^\s*//\s*[^\s]*$', line) and - not Match(r'^// \$Id:.*#[0-9]+ \$$', line) and - not Match(r'^\s*/// [@\\](copydoc|copydetails|copybrief) .*$', line)): - line_width = GetLineWidth(line) - if line_width > _line_length: - error(filename, linenum, 'whitespace/line_length', 2, - 'Lines should be <= %i characters long' % _line_length) - - if (cleansed_line.count(';') > 1 and - # allow simple single line lambdas - not Match(r'^[^{};]*\[[^\[\]]*\][^{}]*\{[^{}\n\r]*\}', - line) and - # for loops are allowed two ;'s (and may run over two lines). - cleansed_line.find('for') == -1 and - (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or - GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and - # It's ok to have many commands in a switch case that fits in 1 line - not ((cleansed_line.find('case ') != -1 or - cleansed_line.find('default:') != -1) and - cleansed_line.find('break;') != -1)): - error(filename, linenum, 'whitespace/newline', 0, - 'More than one command on the same line') - - # Some more style checks - CheckBraces(filename, clean_lines, linenum, error) - CheckTrailingSemicolon(filename, clean_lines, linenum, error) - CheckEmptyBlockBody(filename, clean_lines, linenum, error) - CheckAccess(filename, clean_lines, linenum, nesting_state, error) - CheckSpacing(filename, clean_lines, linenum, nesting_state, error) - CheckOperatorSpacing(filename, clean_lines, linenum, error) - CheckParenthesisSpacing(filename, clean_lines, linenum, error) - CheckCommaSpacing(filename, clean_lines, linenum, error) - CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error) - CheckSpacingForFunctionCall(filename, clean_lines, linenum, error) - CheckCheck(filename, clean_lines, linenum, error) - CheckAltTokens(filename, clean_lines, linenum, error) - classinfo = nesting_state.InnermostClass() - if classinfo: - CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error) - - -_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') -# Matches the first component of a filename delimited by -s and _s. That is: -# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' -# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' -_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') - - -def _DropCommonSuffixes(filename): - """Drops common suffixes like _test.cc or -inl.h from filename. - - For example: - >>> _DropCommonSuffixes('foo/foo-inl.h') - 'foo/foo' - >>> _DropCommonSuffixes('foo/bar/foo.cc') - 'foo/bar/foo' - >>> _DropCommonSuffixes('foo/foo_internal.h') - 'foo/foo' - >>> _DropCommonSuffixes('foo/foo_unusualinternal.h') - 'foo/foo_unusualinternal' - - Args: - filename: The input filename. - - Returns: - The filename with the common suffix removed. - """ - for suffix in itertools.chain( - ('%s.%s' % (test_suffix.lstrip('_'), ext) - for test_suffix, ext in itertools.product(_test_suffixes, GetNonHeaderExtensions())), - ('%s.%s' % (suffix, ext) - for suffix, ext in itertools.product(['inl', 'imp', 'internal'], GetHeaderExtensions()))): - if (filename.endswith(suffix) and len(filename) > len(suffix) and - filename[-len(suffix) - 1] in ('-', '_')): - return filename[:-len(suffix) - 1] - return os.path.splitext(filename)[0] - - -def _ClassifyInclude(fileinfo, include, is_system): - """Figures out what kind of header 'include' is. - - Args: - fileinfo: The current file cpplint is running over. A FileInfo instance. - include: The path to a #included file. - is_system: True if the #include used <> rather than "". - - Returns: - One of the _XXX_HEADER constants. - - For example: - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True) - _C_SYS_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) - _CPP_SYS_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) - _LIKELY_MY_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), - ... 'bar/foo_other_ext.h', False) - _POSSIBLE_MY_HEADER - >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False) - _OTHER_HEADER - """ - # This is a list of all standard c++ header files, except - # those already checked for above. - is_cpp_h = include in _CPP_HEADERS - - # Headers with C++ extensions shouldn't be considered C system headers - if is_system and os.path.splitext(include)[1] in ['.hpp', '.hxx', '.h++']: - is_system = False - - if is_system: - if is_cpp_h: - return _CPP_SYS_HEADER - else: - return _C_SYS_HEADER - - # If the target file and the include we're checking share a - # basename when we drop common extensions, and the include - # lives in . , then it's likely to be owned by the target file. - target_dir, target_base = ( - os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) - include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) - target_dir_pub = os.path.normpath(target_dir + '/../public') - target_dir_pub = target_dir_pub.replace('\\', '/') - if target_base == include_base and ( - include_dir == target_dir or - include_dir == target_dir_pub): - return _LIKELY_MY_HEADER - - # If the target and include share some initial basename - # component, it's possible the target is implementing the - # include, so it's allowed to be first, but we'll never - # complain if it's not there. - target_first_component = _RE_FIRST_COMPONENT.match(target_base) - include_first_component = _RE_FIRST_COMPONENT.match(include_base) - if (target_first_component and include_first_component and - target_first_component.group(0) == - include_first_component.group(0)): - return _POSSIBLE_MY_HEADER - - return _OTHER_HEADER - - - -def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): - """Check rules that are applicable to #include lines. - - Strings on #include lines are NOT removed from elided line, to make - certain tasks easier. However, to prevent false positives, checks - applicable to #include lines in CheckLanguage must be put here. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - include_state: An _IncludeState instance in which the headers are inserted. - error: The function to call with any errors found. - """ - fileinfo = FileInfo(filename) - line = clean_lines.lines[linenum] - - # "include" should use the new style "foo/bar.h" instead of just "bar.h" - # Only do this check if the included header follows google naming - # conventions. If not, assume that it's a 3rd party API that - # requires special include conventions. - # - # We also make an exception for Lua headers, which follow google - # naming convention but not the include convention. - match = Match(r'#include\s*"([^/]+\.h)"', line) - if match and not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1)): - error(filename, linenum, 'build/include_subdir', 4, - 'Include the directory when naming .h files') - - # we shouldn't include a file more than once. actually, there are a - # handful of instances where doing so is okay, but in general it's - # not. - match = _RE_PATTERN_INCLUDE.search(line) - if match: - include = match.group(2) - is_system = (match.group(1) == '<') - duplicate_line = include_state.FindHeader(include) - if duplicate_line >= 0: - error(filename, linenum, 'build/include', 4, - '"%s" already included at %s:%s' % - (include, filename, duplicate_line)) - return - - for extension in GetNonHeaderExtensions(): - if (include.endswith('.' + extension) and - os.path.dirname(fileinfo.RepositoryName()) != os.path.dirname(include)): - error(filename, linenum, 'build/include', 4, - 'Do not include .' + extension + ' files from other packages') - return - - if not _THIRD_PARTY_HEADERS_PATTERN.match(include): - include_state.include_list[-1].append((include, linenum)) - - # We want to ensure that headers appear in the right order: - # 1) for foo.cc, foo.h (preferred location) - # 2) c system files - # 3) cpp system files - # 4) for foo.cc, foo.h (deprecated location) - # 5) other google headers - # - # We classify each include statement as one of those 5 types - # using a number of techniques. The include_state object keeps - # track of the highest type seen, and complains if we see a - # lower type after that. - error_message = include_state.CheckNextIncludeOrder( - _ClassifyInclude(fileinfo, include, is_system)) - if error_message: - error(filename, linenum, 'build/include_order', 4, - '%s. Should be: %s.h, c system, c++ system, other.' % - (error_message, fileinfo.BaseName())) - canonical_include = include_state.CanonicalizeAlphabeticalOrder(include) - if not include_state.IsInAlphabeticalOrder( - clean_lines, linenum, canonical_include): - error(filename, linenum, 'build/include_alpha', 4, - 'Include "%s" not in alphabetical order' % include) - include_state.SetLastHeader(canonical_include) - - - -def _GetTextInside(text, start_pattern): - r"""Retrieves all the text between matching open and close parentheses. - - Given a string of lines and a regular expression string, retrieve all the text - following the expression and between opening punctuation symbols like - (, [, or {, and the matching close-punctuation symbol. This properly nested - occurrences of the punctuations, so for the text like - printf(a(), b(c())); - a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. - start_pattern must match string having an open punctuation symbol at the end. - - Args: - text: The lines to extract text. Its comments and strings must be elided. - It can be single line and can span multiple lines. - start_pattern: The regexp string indicating where to start extracting - the text. - Returns: - The extracted text. - None if either the opening string or ending punctuation could not be found. - """ - # TODO(unknown): Audit cpplint.py to see what places could be profitably - # rewritten to use _GetTextInside (and use inferior regexp matching today). - - # Give opening punctuations to get the matching close-punctuations. - matching_punctuation = {'(': ')', '{': '}', '[': ']'} - closing_punctuation = set(itervalues(matching_punctuation)) - - # Find the position to start extracting text. - match = re.search(start_pattern, text, re.M) - if not match: # start_pattern not found in text. - return None - start_position = match.end(0) - - assert start_position > 0, ( - 'start_pattern must ends with an opening punctuation.') - assert text[start_position - 1] in matching_punctuation, ( - 'start_pattern must ends with an opening punctuation.') - # Stack of closing punctuations we expect to have in text after position. - punctuation_stack = [matching_punctuation[text[start_position - 1]]] - position = start_position - while punctuation_stack and position < len(text): - if text[position] == punctuation_stack[-1]: - punctuation_stack.pop() - elif text[position] in closing_punctuation: - # A closing punctuation without matching opening punctuations. - return None - elif text[position] in matching_punctuation: - punctuation_stack.append(matching_punctuation[text[position]]) - position += 1 - if punctuation_stack: - # Opening punctuations left without matching close-punctuations. - return None - # punctuations match. - return text[start_position:position - 1] - - -# Patterns for matching call-by-reference parameters. -# -# Supports nested templates up to 2 levels deep using this messy pattern: -# < (?: < (?: < [^<>]* -# > -# | [^<>] )* -# > -# | [^<>] )* -# > -_RE_PATTERN_IDENT = r'[_a-zA-Z]\w*' # =~ [[:alpha:]][[:alnum:]]* -_RE_PATTERN_TYPE = ( - r'(?:const\s+)?(?:typename\s+|class\s+|struct\s+|union\s+|enum\s+)?' - r'(?:\w|' - r'\s*<(?:<(?:<[^<>]*>|[^<>])*>|[^<>])*>|' - r'::)+') -# A call-by-reference parameter ends with '& identifier'. -_RE_PATTERN_REF_PARAM = re.compile( - r'(' + _RE_PATTERN_TYPE + r'(?:\s*(?:\bconst\b|[*]))*\s*' - r'&\s*' + _RE_PATTERN_IDENT + r')\s*(?:=[^,()]+)?[,)]') -# A call-by-const-reference parameter either ends with 'const& identifier' -# or looks like 'const type& identifier' when 'type' is atomic. -_RE_PATTERN_CONST_REF_PARAM = ( - r'(?:.*\s*\bconst\s*&\s*' + _RE_PATTERN_IDENT + - r'|const\s+' + _RE_PATTERN_TYPE + r'\s*&\s*' + _RE_PATTERN_IDENT + r')') -# Stream types. -_RE_PATTERN_REF_STREAM_PARAM = ( - r'(?:.*stream\s*&\s*' + _RE_PATTERN_IDENT + r')') - - -def CheckLanguage(filename, clean_lines, linenum, file_extension, - include_state, nesting_state, error): - """Checks rules from the 'C++ language rules' section of cppguide.html. - - Some of these rules are hard to test (function overloading, using - uint32 inappropriately), but we do the best we can. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - file_extension: The extension (without the dot) of the filename. - include_state: An _IncludeState instance in which the headers are inserted. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - # If the line is empty or consists of entirely a comment, no need to - # check it. - line = clean_lines.elided[linenum] - if not line: - return - - match = _RE_PATTERN_INCLUDE.search(line) - if match: - CheckIncludeLine(filename, clean_lines, linenum, include_state, error) - return - - # Reset include state across preprocessor directives. This is meant - # to silence warnings for conditional includes. - match = Match(r'^\s*#\s*(if|ifdef|ifndef|elif|else|endif)\b', line) - if match: - include_state.ResetSection(match.group(1)) - - - # Perform other checks now that we are sure that this is not an include line - CheckCasts(filename, clean_lines, linenum, error) - CheckGlobalStatic(filename, clean_lines, linenum, error) - CheckPrintf(filename, clean_lines, linenum, error) - - if file_extension in GetHeaderExtensions(): - # TODO(unknown): check that 1-arg constructors are explicit. - # How to tell it's a constructor? - # (handled in CheckForNonStandardConstructs for now) - # TODO(unknown): check that classes declare or disable copy/assign - # (level 1 error) - pass - - # Check if people are using the verboten C basic types. The only exception - # we regularly allow is "unsigned short port" for port. - if Search(r'\bshort port\b', line): - if not Search(r'\bunsigned short port\b', line): - error(filename, linenum, 'runtime/int', 4, - 'Use "unsigned short" for ports, not "short"') - else: - match = Search(r'\b(short|long(?! +double)|long long)\b', line) - if match: - error(filename, linenum, 'runtime/int', 4, - 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) - - # Check if some verboten operator overloading is going on - # TODO(unknown): catch out-of-line unary operator&: - # class X {}; - # int operator&(const X& x) { return 42; } // unary operator& - # The trick is it's hard to tell apart from binary operator&: - # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& - if Search(r'\boperator\s*&\s*\(\s*\)', line): - error(filename, linenum, 'runtime/operator', 4, - 'Unary operator& is dangerous. Do not use it.') - - # Check for suspicious usage of "if" like - # } if (a == b) { - if Search(r'\}\s*if\s*\(', line): - error(filename, linenum, 'readability/braces', 4, - 'Did you mean "else if"? If not, start a new line for "if".') - - # Check for potential format string bugs like printf(foo). - # We constrain the pattern not to pick things like DocidForPrintf(foo). - # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) - # TODO(unknown): Catch the following case. Need to change the calling - # convention of the whole function to process multiple line to handle it. - # printf( - # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); - printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') - if printf_args: - match = Match(r'([\w.\->()]+)$', printf_args) - if match and match.group(1) != '__VA_ARGS__': - function_name = re.search(r'\b((?:string)?printf)\s*\(', - line, re.I).group(1) - error(filename, linenum, 'runtime/printf', 4, - 'Potential format string bug. Do %s("%%s", %s) instead.' - % (function_name, match.group(1))) - - # Check for potential memset bugs like memset(buf, sizeof(buf), 0). - match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) - if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): - error(filename, linenum, 'runtime/memset', 4, - 'Did you mean "memset(%s, 0, %s)"?' - % (match.group(1), match.group(2))) - - if Search(r'\busing namespace\b', line): - if Search(r'\bliterals\b', line): - error(filename, linenum, 'build/namespaces_literals', 5, - 'Do not use namespace using-directives. ' - 'Use using-declarations instead.') - else: - error(filename, linenum, 'build/namespaces', 5, - 'Do not use namespace using-directives. ' - 'Use using-declarations instead.') - - # Detect variable-length arrays. - match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) - if (match and match.group(2) != 'return' and match.group(2) != 'delete' and - match.group(3).find(']') == -1): - # Split the size using space and arithmetic operators as delimiters. - # If any of the resulting tokens are not compile time constants then - # report the error. - tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) - is_const = True - skip_next = False - for tok in tokens: - if skip_next: - skip_next = False - continue - - if Search(r'sizeof\(.+\)', tok): continue - if Search(r'arraysize\(\w+\)', tok): continue - - tok = tok.lstrip('(') - tok = tok.rstrip(')') - if not tok: continue - if Match(r'\d+', tok): continue - if Match(r'0[xX][0-9a-fA-F]+', tok): continue - if Match(r'k[A-Z0-9]\w*', tok): continue - if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue - if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue - # A catch all for tricky sizeof cases, including 'sizeof expression', - # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' - # requires skipping the next token because we split on ' ' and '*'. - if tok.startswith('sizeof'): - skip_next = True - continue - is_const = False - break - if not is_const: - error(filename, linenum, 'runtime/arrays', 1, - 'Do not use variable-length arrays. Use an appropriately named ' - "('k' followed by CamelCase) compile-time constant for the size.") - - # Check for use of unnamed namespaces in header files. Registration - # macros are typically OK, so we allow use of "namespace {" on lines - # that end with backslashes. - if (file_extension in GetHeaderExtensions() - and Search(r'\bnamespace\s*{', line) - and line[-1] != '\\'): - error(filename, linenum, 'build/namespaces', 4, - 'Do not use unnamed namespaces in header files. See ' - 'https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' - ' for more information.') - - -def CheckGlobalStatic(filename, clean_lines, linenum, error): - """Check for unsafe global or static objects. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Match two lines at a time to support multiline declarations - if linenum + 1 < clean_lines.NumLines() and not Search(r'[;({]', line): - line += clean_lines.elided[linenum + 1].strip() - - # Check for people declaring static/global STL strings at the top level. - # This is dangerous because the C++ language does not guarantee that - # globals with constructors are initialized before the first access, and - # also because globals can be destroyed when some threads are still running. - # TODO(unknown): Generalize this to also find static unique_ptr instances. - # TODO(unknown): File bugs for clang-tidy to find these. - match = Match( - r'((?:|static +)(?:|const +))(?::*std::)?string( +const)? +' - r'([a-zA-Z0-9_:]+)\b(.*)', - line) - - # Remove false positives: - # - String pointers (as opposed to values). - # string *pointer - # const string *pointer - # string const *pointer - # string *const pointer - # - # - Functions and template specializations. - # string Function(... - # string Class::Method(... - # - # - Operators. These are matched separately because operator names - # cross non-word boundaries, and trying to match both operators - # and functions at the same time would decrease accuracy of - # matching identifiers. - # string Class::operator*() - if (match and - not Search(r'\bstring\b(\s+const)?\s*[\*\&]\s*(const\s+)?\w', line) and - not Search(r'\boperator\W', line) and - not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)*\s*\(([^"]|$)', match.group(4))): - if Search(r'\bconst\b', line): - error(filename, linenum, 'runtime/string', 4, - 'For a static/global string constant, use a C style string ' - 'instead: "%schar%s %s[]".' % - (match.group(1), match.group(2) or '', match.group(3))) - else: - error(filename, linenum, 'runtime/string', 4, - 'Static/global string variables are not permitted.') - - if (Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line) or - Search(r'\b([A-Za-z0-9_]*_)\(CHECK_NOTNULL\(\1\)\)', line)): - error(filename, linenum, 'runtime/init', 4, - 'You seem to be initializing a member variable with itself.') - - -def CheckPrintf(filename, clean_lines, linenum, error): - """Check for printf related issues. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # When snprintf is used, the second argument shouldn't be a literal. - match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) - if match and match.group(2) != '0': - # If 2nd arg is zero, snprintf is used to calculate size. - error(filename, linenum, 'runtime/printf', 3, - 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' - 'to snprintf.' % (match.group(1), match.group(2))) - - # Check if some verboten C functions are being used. - if Search(r'\bsprintf\s*\(', line): - error(filename, linenum, 'runtime/printf', 5, - 'Never use sprintf. Use snprintf instead.') - match = Search(r'\b(strcpy|strcat)\s*\(', line) - if match: - error(filename, linenum, 'runtime/printf', 4, - 'Almost always, snprintf is better than %s' % match.group(1)) - - -def IsDerivedFunction(clean_lines, linenum): - """Check if current line contains an inherited function. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - Returns: - True if current line contains a function with "override" - virt-specifier. - """ - # Scan back a few lines for start of current function - for i in xrange(linenum, max(-1, linenum - 10), -1): - match = Match(r'^([^()]*\w+)\(', clean_lines.elided[i]) - if match: - # Look for "override" after the matching closing parenthesis - line, _, closing_paren = CloseExpression( - clean_lines, i, len(match.group(1))) - return (closing_paren >= 0 and - Search(r'\boverride\b', line[closing_paren:])) - return False - - -def IsOutOfLineMethodDefinition(clean_lines, linenum): - """Check if current line contains an out-of-line method definition. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - Returns: - True if current line contains an out-of-line method definition. - """ - # Scan back a few lines for start of current function - for i in xrange(linenum, max(-1, linenum - 10), -1): - if Match(r'^([^()]*\w+)\(', clean_lines.elided[i]): - return Match(r'^[^()]*\w+::\w+\(', clean_lines.elided[i]) is not None - return False - - -def IsInitializerList(clean_lines, linenum): - """Check if current line is inside constructor initializer list. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - Returns: - True if current line appears to be inside constructor initializer - list, False otherwise. - """ - for i in xrange(linenum, 1, -1): - line = clean_lines.elided[i] - if i == linenum: - remove_function_body = Match(r'^(.*)\{\s*$', line) - if remove_function_body: - line = remove_function_body.group(1) - - if Search(r'\s:\s*\w+[({]', line): - # A lone colon tend to indicate the start of a constructor - # initializer list. It could also be a ternary operator, which - # also tend to appear in constructor initializer lists as - # opposed to parameter lists. - return True - if Search(r'\}\s*,\s*$', line): - # A closing brace followed by a comma is probably the end of a - # brace-initialized member in constructor initializer list. - return True - if Search(r'[{};]\s*$', line): - # Found one of the following: - # - A closing brace or semicolon, probably the end of the previous - # function. - # - An opening brace, probably the start of current class or namespace. - # - # Current line is probably not inside an initializer list since - # we saw one of those things without seeing the starting colon. - return False - - # Got to the beginning of the file without seeing the start of - # constructor initializer list. - return False - - -def CheckForNonConstReference(filename, clean_lines, linenum, - nesting_state, error): - """Check for non-const references. - - Separate from CheckLanguage since it scans backwards from current - line, instead of scanning forward. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: The function to call with any errors found. - """ - # Do nothing if there is no '&' on current line. - line = clean_lines.elided[linenum] - if '&' not in line: - return - - # If a function is inherited, current function doesn't have much of - # a choice, so any non-const references should not be blamed on - # derived function. - if IsDerivedFunction(clean_lines, linenum): - return - - # Don't warn on out-of-line method definitions, as we would warn on the - # in-line declaration, if it isn't marked with 'override'. - if IsOutOfLineMethodDefinition(clean_lines, linenum): - return - - # Long type names may be broken across multiple lines, usually in one - # of these forms: - # LongType - # ::LongTypeContinued &identifier - # LongType:: - # LongTypeContinued &identifier - # LongType< - # ...>::LongTypeContinued &identifier - # - # If we detected a type split across two lines, join the previous - # line to current line so that we can match const references - # accordingly. - # - # Note that this only scans back one line, since scanning back - # arbitrary number of lines would be expensive. If you have a type - # that spans more than 2 lines, please use a typedef. - if linenum > 1: - previous = None - if Match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line): - # previous_line\n + ::current_line - previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$', - clean_lines.elided[linenum - 1]) - elif Match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line): - # previous_line::\n + current_line - previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$', - clean_lines.elided[linenum - 1]) - if previous: - line = previous.group(1) + line.lstrip() - else: - # Check for templated parameter that is split across multiple lines - endpos = line.rfind('>') - if endpos > -1: - (_, startline, startpos) = ReverseCloseExpression( - clean_lines, linenum, endpos) - if startpos > -1 and startline < linenum: - # Found the matching < on an earlier line, collect all - # pieces up to current line. - line = '' - for i in xrange(startline, linenum + 1): - line += clean_lines.elided[i].strip() - - # Check for non-const references in function parameters. A single '&' may - # found in the following places: - # inside expression: binary & for bitwise AND - # inside expression: unary & for taking the address of something - # inside declarators: reference parameter - # We will exclude the first two cases by checking that we are not inside a - # function body, including one that was just introduced by a trailing '{'. - # TODO(unknown): Doesn't account for 'catch(Exception& e)' [rare]. - if (nesting_state.previous_stack_top and - not (isinstance(nesting_state.previous_stack_top, _ClassInfo) or - isinstance(nesting_state.previous_stack_top, _NamespaceInfo))): - # Not at toplevel, not within a class, and not within a namespace - return - - # Avoid initializer lists. We only need to scan back from the - # current line for something that starts with ':'. - # - # We don't need to check the current line, since the '&' would - # appear inside the second set of parentheses on the current line as - # opposed to the first set. - if linenum > 0: - for i in xrange(linenum - 1, max(0, linenum - 10), -1): - previous_line = clean_lines.elided[i] - if not Search(r'[),]\s*$', previous_line): - break - if Match(r'^\s*:\s+\S', previous_line): - return - - # Avoid preprocessors - if Search(r'\\\s*$', line): - return - - # Avoid constructor initializer lists - if IsInitializerList(clean_lines, linenum): - return - - # We allow non-const references in a few standard places, like functions - # called "swap()" or iostream operators like "<<" or ">>". Do not check - # those function parameters. - # - # We also accept & in static_assert, which looks like a function but - # it's actually a declaration expression. - whitelisted_functions = (r'(?:[sS]wap(?:<\w:+>)?|' - r'operator\s*[<>][<>]|' - r'static_assert|COMPILE_ASSERT' - r')\s*\(') - if Search(whitelisted_functions, line): - return - elif not Search(r'\S+\([^)]*$', line): - # Don't see a whitelisted function on this line. Actually we - # didn't see any function name on this line, so this is likely a - # multi-line parameter list. Try a bit harder to catch this case. - for i in xrange(2): - if (linenum > i and - Search(whitelisted_functions, clean_lines.elided[linenum - i - 1])): - return - - decls = ReplaceAll(r'{[^}]*}', ' ', line) # exclude function body - for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls): - if (not Match(_RE_PATTERN_CONST_REF_PARAM, parameter) and - not Match(_RE_PATTERN_REF_STREAM_PARAM, parameter)): - error(filename, linenum, 'runtime/references', 2, - 'Is this a non-const reference? ' - 'If so, make const or use a pointer: ' + - ReplaceAll(' *<', '<', parameter)) - - -def CheckCasts(filename, clean_lines, linenum, error): - """Various cast related checks. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - # Check to see if they're using an conversion function cast. - # I just try to capture the most common basic types, though there are more. - # Parameterless conversion functions, such as bool(), are allowed as they are - # probably a member operator declaration or default constructor. - match = Search( - r'(\bnew\s+(?:const\s+)?|\S<\s*(?:const\s+)?)?\b' - r'(int|float|double|bool|char|int32|uint32|int64|uint64)' - r'(\([^)].*)', line) - expecting_function = ExpectingFunctionArgs(clean_lines, linenum) - if match and not expecting_function: - matched_type = match.group(2) - - # matched_new_or_template is used to silence two false positives: - # - New operators - # - Template arguments with function types - # - # For template arguments, we match on types immediately following - # an opening bracket without any spaces. This is a fast way to - # silence the common case where the function type is the first - # template argument. False negative with less-than comparison is - # avoided because those operators are usually followed by a space. - # - # function // bracket + no space = false positive - # value < double(42) // bracket + space = true positive - matched_new_or_template = match.group(1) - - # Avoid arrays by looking for brackets that come after the closing - # parenthesis. - if Match(r'\([^()]+\)\s*\[', match.group(3)): - return - - # Other things to ignore: - # - Function pointers - # - Casts to pointer types - # - Placement new - # - Alias declarations - matched_funcptr = match.group(3) - if (matched_new_or_template is None and - not (matched_funcptr and - (Match(r'\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(', - matched_funcptr) or - matched_funcptr.startswith('(*)'))) and - not Match(r'\s*using\s+\S+\s*=\s*' + matched_type, line) and - not Search(r'new\(\S+\)\s*' + matched_type, line)): - error(filename, linenum, 'readability/casting', 4, - 'Using deprecated casting style. ' - 'Use static_cast<%s>(...) instead' % - matched_type) - - if not expecting_function: - CheckCStyleCast(filename, clean_lines, linenum, 'static_cast', - r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) - - # This doesn't catch all cases. Consider (const char * const)"hello". - # - # (char *) "foo" should always be a const_cast (reinterpret_cast won't - # compile). - if CheckCStyleCast(filename, clean_lines, linenum, 'const_cast', - r'\((char\s?\*+\s?)\)\s*"', error): - pass - else: - # Check pointer casts for other than string constants - CheckCStyleCast(filename, clean_lines, linenum, 'reinterpret_cast', - r'\((\w+\s?\*+\s?)\)', error) - - # In addition, we look for people taking the address of a cast. This - # is dangerous -- casts can assign to temporaries, so the pointer doesn't - # point where you think. - # - # Some non-identifier character is required before the '&' for the - # expression to be recognized as a cast. These are casts: - # expression = &static_cast(temporary()); - # function(&(int*)(temporary())); - # - # This is not a cast: - # reference_type&(int* function_param); - match = Search( - r'(?:[^\w]&\(([^)*][^)]*)\)[\w(])|' - r'(?:[^\w]&(static|dynamic|down|reinterpret)_cast\b)', line) - if match: - # Try a better error message when the & is bound to something - # dereferenced by the casted pointer, as opposed to the casted - # pointer itself. - parenthesis_error = False - match = Match(r'^(.*&(?:static|dynamic|down|reinterpret)_cast\b)<', line) - if match: - _, y1, x1 = CloseExpression(clean_lines, linenum, len(match.group(1))) - if x1 >= 0 and clean_lines.elided[y1][x1] == '(': - _, y2, x2 = CloseExpression(clean_lines, y1, x1) - if x2 >= 0: - extended_line = clean_lines.elided[y2][x2:] - if y2 < clean_lines.NumLines() - 1: - extended_line += clean_lines.elided[y2 + 1] - if Match(r'\s*(?:->|\[)', extended_line): - parenthesis_error = True - - if parenthesis_error: - error(filename, linenum, 'readability/casting', 4, - ('Are you taking an address of something dereferenced ' - 'from a cast? Wrapping the dereferenced expression in ' - 'parentheses will make the binding more obvious')) - else: - error(filename, linenum, 'runtime/casting', 4, - ('Are you taking an address of a cast? ' - 'This is dangerous: could be a temp var. ' - 'Take the address before doing the cast, rather than after')) - - -def CheckCStyleCast(filename, clean_lines, linenum, cast_type, pattern, error): - """Checks for a C-style cast by looking for the pattern. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - cast_type: The string for the C++ cast to recommend. This is either - reinterpret_cast, static_cast, or const_cast, depending. - pattern: The regular expression used to find C-style casts. - error: The function to call with any errors found. - - Returns: - True if an error was emitted. - False otherwise. - """ - line = clean_lines.elided[linenum] - match = Search(pattern, line) - if not match: - return False - - # Exclude lines with keywords that tend to look like casts - context = line[0:match.start(1) - 1] - if Match(r'.*\b(?:sizeof|alignof|alignas|[_A-Z][_A-Z0-9]*)\s*$', context): - return False - - # Try expanding current context to see if we one level of - # parentheses inside a macro. - if linenum > 0: - for i in xrange(linenum - 1, max(0, linenum - 5), -1): - context = clean_lines.elided[i] + context - if Match(r'.*\b[_A-Z][_A-Z0-9]*\s*\((?:\([^()]*\)|[^()])*$', context): - return False - - # operator++(int) and operator--(int) - if context.endswith(' operator++') or context.endswith(' operator--'): - return False - - # A single unnamed argument for a function tends to look like old style cast. - # If we see those, don't issue warnings for deprecated casts. - remainder = line[match.end(0):] - if Match(r'^\s*(?:;|const\b|throw\b|final\b|override\b|[=>{),]|->)', - remainder): - return False - - # At this point, all that should be left is actual casts. - error(filename, linenum, 'readability/casting', 4, - 'Using C-style cast. Use %s<%s>(...) instead' % - (cast_type, match.group(1))) - - return True - - -def ExpectingFunctionArgs(clean_lines, linenum): - """Checks whether where function type arguments are expected. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - - Returns: - True if the line at 'linenum' is inside something that expects arguments - of function types. - """ - line = clean_lines.elided[linenum] - return (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or - (linenum >= 2 and - (Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\((?:\S+,)?\s*$', - clean_lines.elided[linenum - 1]) or - Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\(\s*$', - clean_lines.elided[linenum - 2]) or - Search(r'\bstd::m?function\s*\<\s*$', - clean_lines.elided[linenum - 1])))) - - -_HEADERS_CONTAINING_TEMPLATES = ( - ('', ('deque',)), - ('', ('unary_function', 'binary_function', - 'plus', 'minus', 'multiplies', 'divides', 'modulus', - 'negate', - 'equal_to', 'not_equal_to', 'greater', 'less', - 'greater_equal', 'less_equal', - 'logical_and', 'logical_or', 'logical_not', - 'unary_negate', 'not1', 'binary_negate', 'not2', - 'bind1st', 'bind2nd', - 'pointer_to_unary_function', - 'pointer_to_binary_function', - 'ptr_fun', - 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t', - 'mem_fun_ref_t', - 'const_mem_fun_t', 'const_mem_fun1_t', - 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t', - 'mem_fun_ref', - )), - ('', ('numeric_limits',)), - ('', ('list',)), - ('', ('map', 'multimap',)), - ('', ('allocator', 'make_shared', 'make_unique', 'shared_ptr', - 'unique_ptr', 'weak_ptr')), - ('', ('queue', 'priority_queue',)), - ('', ('set', 'multiset',)), - ('', ('stack',)), - ('', ('char_traits', 'basic_string',)), - ('', ('tuple',)), - ('', ('unordered_map', 'unordered_multimap')), - ('', ('unordered_set', 'unordered_multiset')), - ('', ('pair',)), - ('', ('vector',)), - - # gcc extensions. - # Note: std::hash is their hash, ::hash is our hash - ('', ('hash_map', 'hash_multimap',)), - ('', ('hash_set', 'hash_multiset',)), - ('', ('slist',)), - ) - -_HEADERS_MAYBE_TEMPLATES = ( - ('', ('copy', 'max', 'min', 'min_element', 'sort', - 'transform', - )), - ('', ('forward', 'make_pair', 'move', 'swap')), - ) - -_RE_PATTERN_STRING = re.compile(r'\bstring\b') - -_re_pattern_headers_maybe_templates = [] -for _header, _templates in _HEADERS_MAYBE_TEMPLATES: - for _template in _templates: - # Match max(..., ...), max(..., ...), but not foo->max, foo.max or - # type::max(). - _re_pattern_headers_maybe_templates.append( - (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), - _template, - _header)) - -# Other scripts may reach in and modify this pattern. -_re_pattern_templates = [] -for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: - for _template in _templates: - _re_pattern_templates.append( - (re.compile(r'(\<|\b)' + _template + r'\s*\<'), - _template + '<>', - _header)) - - -def FilesBelongToSameModule(filename_cc, filename_h): - """Check if these two filenames belong to the same module. - - The concept of a 'module' here is a as follows: - foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the - same 'module' if they are in the same directory. - some/path/public/xyzzy and some/path/internal/xyzzy are also considered - to belong to the same module here. - - If the filename_cc contains a longer path than the filename_h, for example, - '/absolute/path/to/base/sysinfo.cc', and this file would include - 'base/sysinfo.h', this function also produces the prefix needed to open the - header. This is used by the caller of this function to more robustly open the - header file. We don't have access to the real include paths in this context, - so we need this guesswork here. - - Known bugs: tools/base/bar.cc and base/bar.h belong to the same module - according to this implementation. Because of this, this function gives - some false positives. This should be sufficiently rare in practice. - - Args: - filename_cc: is the path for the source (e.g. .cc) file - filename_h: is the path for the header path - - Returns: - Tuple with a bool and a string: - bool: True if filename_cc and filename_h belong to the same module. - string: the additional prefix needed to open the header file. - """ - fileinfo_cc = FileInfo(filename_cc) - if not fileinfo_cc.Extension().lstrip('.') in GetNonHeaderExtensions(): - return (False, '') - - fileinfo_h = FileInfo(filename_h) - if not fileinfo_h.Extension().lstrip('.') in GetHeaderExtensions(): - return (False, '') - - filename_cc = filename_cc[:-(len(fileinfo_cc.Extension()))] - matched_test_suffix = Search(_TEST_FILE_SUFFIX, fileinfo_cc.BaseName()) - if matched_test_suffix: - filename_cc = filename_cc[:-len(matched_test_suffix.group(1))] - - filename_cc = filename_cc.replace('/public/', '/') - filename_cc = filename_cc.replace('/internal/', '/') - - filename_h = filename_h[:-(len(fileinfo_h.Extension()))] - if filename_h.endswith('-inl'): - filename_h = filename_h[:-len('-inl')] - filename_h = filename_h.replace('/public/', '/') - filename_h = filename_h.replace('/internal/', '/') - - files_belong_to_same_module = filename_cc.endswith(filename_h) - common_path = '' - if files_belong_to_same_module: - common_path = filename_cc[:-len(filename_h)] - return files_belong_to_same_module, common_path - - -def UpdateIncludeState(filename, include_dict, io=codecs): - """Fill up the include_dict with new includes found from the file. - - Args: - filename: the name of the header to read. - include_dict: a dictionary in which the headers are inserted. - io: The io factory to use to read the file. Provided for testability. - - Returns: - True if a header was successfully added. False otherwise. - """ - headerfile = None - try: - headerfile = io.open(filename, 'r', 'utf8', 'replace') - except IOError: - return False - linenum = 0 - for line in headerfile: - linenum += 1 - clean_line = CleanseComments(line) - match = _RE_PATTERN_INCLUDE.search(clean_line) - if match: - include = match.group(2) - include_dict.setdefault(include, linenum) - return True - - -def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, - io=codecs): - """Reports for missing stl includes. - - This function will output warnings to make sure you are including the headers - necessary for the stl containers and functions that you use. We only give one - reason to include a header. For example, if you use both equal_to<> and - less<> in a .h file, only one (the latter in the file) of these will be - reported as a reason to include the . - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - include_state: An _IncludeState instance. - error: The function to call with any errors found. - io: The IO factory to use to read the header file. Provided for unittest - injection. - """ - required = {} # A map of header name to linenumber and the template entity. - # Example of required: { '': (1219, 'less<>') } - - for linenum in range(clean_lines.NumLines()): - line = clean_lines.elided[linenum] - if not line or line[0] == '#': - continue - - # String is special -- it is a non-templatized type in STL. - matched = _RE_PATTERN_STRING.search(line) - if matched: - # Don't warn about strings in non-STL namespaces: - # (We check only the first match per line; good enough.) - prefix = line[:matched.start()] - if prefix.endswith('std::') or not prefix.endswith('::'): - required[''] = (linenum, 'string') - - for pattern, template, header in _re_pattern_headers_maybe_templates: - if pattern.search(line): - required[header] = (linenum, template) - - # The following function is just a speed up, no semantics are changed. - if not '<' in line: # Reduces the cpu time usage by skipping lines. - continue - - for pattern, template, header in _re_pattern_templates: - matched = pattern.search(line) - if matched: - # Don't warn about IWYU in non-STL namespaces: - # (We check only the first match per line; good enough.) - prefix = line[:matched.start()] - if prefix.endswith('std::') or not prefix.endswith('::'): - required[header] = (linenum, template) - - # The policy is that if you #include something in foo.h you don't need to - # include it again in foo.cc. Here, we will look at possible includes. - # Let's flatten the include_state include_list and copy it into a dictionary. - include_dict = dict([item for sublist in include_state.include_list - for item in sublist]) - - # Did we find the header for this file (if any) and successfully load it? - header_found = False - - # Use the absolute path so that matching works properly. - abs_filename = FileInfo(filename).FullName() - - # For Emacs's flymake. - # If cpplint is invoked from Emacs's flymake, a temporary file is generated - # by flymake and that file name might end with '_flymake.cc'. In that case, - # restore original file name here so that the corresponding header file can be - # found. - # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' - # instead of 'foo_flymake.h' - abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) - - # include_dict is modified during iteration, so we iterate over a copy of - # the keys. - header_keys = list(include_dict.keys()) - for header in header_keys: - (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) - fullpath = common_path + header - if same_module and UpdateIncludeState(fullpath, include_dict, io): - header_found = True - - # If we can't find the header file for a .cc, assume it's because we don't - # know where to look. In that case we'll give up as we're not sure they - # didn't include it in the .h file. - # TODO(unknown): Do a better job of finding .h files so we are confident that - # not having the .h file means there isn't one. - if not header_found: - for extension in GetNonHeaderExtensions(): - if filename.endswith('.' + extension): - return - - # All the lines have been processed, report the errors found. - for required_header_unstripped in sorted(required, key=required.__getitem__): - template = required[required_header_unstripped][1] - if required_header_unstripped.strip('<>"') not in include_dict: - error(filename, required[required_header_unstripped][0], - 'build/include_what_you_use', 4, - 'Add #include ' + required_header_unstripped + ' for ' + template) - - -_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') - - -def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error): - """Check that make_pair's template arguments are deduced. - - G++ 4.6 in C++11 mode fails badly if make_pair's template arguments are - specified explicitly, and such use isn't intended in any case. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line) - if match: - error(filename, linenum, 'build/explicit_make_pair', - 4, # 4 = high confidence - 'For C++11-compatibility, omit template arguments from make_pair' - ' OR use pair directly OR if appropriate, construct a pair directly') - - -def CheckRedundantVirtual(filename, clean_lines, linenum, error): - """Check if line contains a redundant "virtual" function-specifier. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - # Look for "virtual" on current line. - line = clean_lines.elided[linenum] - virtual = Match(r'^(.*)(\bvirtual\b)(.*)$', line) - if not virtual: return - - # Ignore "virtual" keywords that are near access-specifiers. These - # are only used in class base-specifier and do not apply to member - # functions. - if (Search(r'\b(public|protected|private)\s+$', virtual.group(1)) or - Match(r'^\s+(public|protected|private)\b', virtual.group(3))): - return - - # Ignore the "virtual" keyword from virtual base classes. Usually - # there is a column on the same line in these cases (virtual base - # classes are rare in google3 because multiple inheritance is rare). - if Match(r'^.*[^:]:[^:].*$', line): return - - # Look for the next opening parenthesis. This is the start of the - # parameter list (possibly on the next line shortly after virtual). - # TODO(unknown): doesn't work if there are virtual functions with - # decltype() or other things that use parentheses, but csearch suggests - # that this is rare. - end_col = -1 - end_line = -1 - start_col = len(virtual.group(2)) - for start_line in xrange(linenum, min(linenum + 3, clean_lines.NumLines())): - line = clean_lines.elided[start_line][start_col:] - parameter_list = Match(r'^([^(]*)\(', line) - if parameter_list: - # Match parentheses to find the end of the parameter list - (_, end_line, end_col) = CloseExpression( - clean_lines, start_line, start_col + len(parameter_list.group(1))) - break - start_col = 0 - - if end_col < 0: - return # Couldn't find end of parameter list, give up - - # Look for "override" or "final" after the parameter list - # (possibly on the next few lines). - for i in xrange(end_line, min(end_line + 3, clean_lines.NumLines())): - line = clean_lines.elided[i][end_col:] - match = Search(r'\b(override|final)\b', line) - if match: - error(filename, linenum, 'readability/inheritance', 4, - ('"virtual" is redundant since function is ' - 'already declared as "%s"' % match.group(1))) - - # Set end_col to check whole lines after we are done with the - # first line. - end_col = 0 - if Search(r'[^\w]\s*$', line): - break - - -def CheckRedundantOverrideOrFinal(filename, clean_lines, linenum, error): - """Check if line contains a redundant "override" or "final" virt-specifier. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - # Look for closing parenthesis nearby. We need one to confirm where - # the declarator ends and where the virt-specifier starts to avoid - # false positives. - line = clean_lines.elided[linenum] - declarator_end = line.rfind(')') - if declarator_end >= 0: - fragment = line[declarator_end:] - else: - if linenum > 1 and clean_lines.elided[linenum - 1].rfind(')') >= 0: - fragment = line - else: - return - - # Check that at most one of "override" or "final" is present, not both - if Search(r'\boverride\b', fragment) and Search(r'\bfinal\b', fragment): - error(filename, linenum, 'readability/inheritance', 4, - ('"override" is redundant since function is ' - 'already declared as "final"')) - - - - -# Returns true if we are at a new block, and it is directly -# inside of a namespace. -def IsBlockInNameSpace(nesting_state, is_forward_declaration): - """Checks that the new block is directly in a namespace. - - Args: - nesting_state: The _NestingState object that contains info about our state. - is_forward_declaration: If the class is a forward declared class. - Returns: - Whether or not the new block is directly in a namespace. - """ - if is_forward_declaration: - return len(nesting_state.stack) >= 1 and ( - isinstance(nesting_state.stack[-1], _NamespaceInfo)) - - - return (len(nesting_state.stack) > 1 and - nesting_state.stack[-1].check_namespace_indentation and - isinstance(nesting_state.stack[-2], _NamespaceInfo)) - - -def ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, - raw_lines_no_comments, linenum): - """This method determines if we should apply our namespace indentation check. - - Args: - nesting_state: The current nesting state. - is_namespace_indent_item: If we just put a new class on the stack, True. - If the top of the stack is not a class, or we did not recently - add the class, False. - raw_lines_no_comments: The lines without the comments. - linenum: The current line number we are processing. - - Returns: - True if we should apply our namespace indentation check. Currently, it - only works for classes and namespaces inside of a namespace. - """ - - is_forward_declaration = IsForwardClassDeclaration(raw_lines_no_comments, - linenum) - - if not (is_namespace_indent_item or is_forward_declaration): - return False - - # If we are in a macro, we do not want to check the namespace indentation. - if IsMacroDefinition(raw_lines_no_comments, linenum): - return False - - return IsBlockInNameSpace(nesting_state, is_forward_declaration) - - -# Call this method if the line is directly inside of a namespace. -# If the line above is blank (excluding comments) or the start of -# an inner namespace, it cannot be indented. -def CheckItemIndentationInNamespace(filename, raw_lines_no_comments, linenum, - error): - line = raw_lines_no_comments[linenum] - if Match(r'^\s+', line): - error(filename, linenum, 'runtime/indentation_namespace', 4, - 'Do not indent within a namespace') - - -def ProcessLine(filename, file_extension, clean_lines, line, - include_state, function_state, nesting_state, error, - extra_check_functions=None): - """Processes a single line in the file. - - Args: - filename: Filename of the file that is being processed. - file_extension: The extension (dot not included) of the file. - clean_lines: An array of strings, each representing a line of the file, - with comments stripped. - line: Number of line being processed. - include_state: An _IncludeState instance in which the headers are inserted. - function_state: A _FunctionState instance which counts function lines, etc. - nesting_state: A NestingState instance which maintains information about - the current stack of nested blocks being parsed. - error: A callable to which errors are reported, which takes 4 arguments: - filename, line number, error level, and message - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - raw_lines = clean_lines.raw_lines - ParseNolintSuppressions(filename, raw_lines[line], line, error) - nesting_state.Update(filename, clean_lines, line, error) - CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, - error) - if nesting_state.InAsmBlock(): return - CheckForFunctionLengths(filename, clean_lines, line, function_state, error) - CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) - CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error) - CheckLanguage(filename, clean_lines, line, file_extension, include_state, - nesting_state, error) - CheckForNonConstReference(filename, clean_lines, line, nesting_state, error) - CheckForNonStandardConstructs(filename, clean_lines, line, - nesting_state, error) - CheckVlogArguments(filename, clean_lines, line, error) - CheckPosixThreading(filename, clean_lines, line, error) - CheckInvalidIncrement(filename, clean_lines, line, error) - CheckMakePairUsesDeduction(filename, clean_lines, line, error) - CheckRedundantVirtual(filename, clean_lines, line, error) - CheckRedundantOverrideOrFinal(filename, clean_lines, line, error) - if extra_check_functions: - for check_fn in extra_check_functions: - check_fn(filename, clean_lines, line, error) - -def FlagCxx11Features(filename, clean_lines, linenum, error): - """Flag those c++11 features that we only allow in certain places. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line) - - # Flag unapproved C++ TR1 headers. - if include and include.group(1).startswith('tr1/'): - error(filename, linenum, 'build/c++tr1', 5, - ('C++ TR1 headers such as <%s> are unapproved.') % include.group(1)) - - # Flag unapproved C++11 headers. - if include and include.group(1) in ('cfenv', - 'condition_variable', - 'fenv.h', - 'future', - 'mutex', - 'thread', - 'chrono', - 'ratio', - 'regex', - 'system_error', - ): - error(filename, linenum, 'build/c++11', 5, - ('<%s> is an unapproved C++11 header.') % include.group(1)) - - # The only place where we need to worry about C++11 keywords and library - # features in preprocessor directives is in macro definitions. - if Match(r'\s*#', line) and not Match(r'\s*#\s*define\b', line): return - - # These are classes and free functions. The classes are always - # mentioned as std::*, but we only catch the free functions if - # they're not found by ADL. They're alphabetical by header. - for top_name in ( - # type_traits - 'alignment_of', - 'aligned_union', - ): - if Search(r'\bstd::%s\b' % top_name, line): - error(filename, linenum, 'build/c++11', 5, - ('std::%s is an unapproved C++11 class or function. Send c-style ' - 'an example of where it would make your code more readable, and ' - 'they may let you use it.') % top_name) - - -def FlagCxx14Features(filename, clean_lines, linenum, error): - """Flag those C++14 features that we restrict. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - line = clean_lines.elided[linenum] - - include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line) - - # Flag unapproved C++14 headers. - if include and include.group(1) in ('scoped_allocator', 'shared_mutex'): - error(filename, linenum, 'build/c++14', 5, - ('<%s> is an unapproved C++14 header.') % include.group(1)) - - -def ProcessFileData(filename, file_extension, lines, error, - extra_check_functions=None): - """Performs lint checks and reports any errors to the given error function. - - Args: - filename: Filename of the file that is being processed. - file_extension: The extension (dot not included) of the file. - lines: An array of strings, each representing a line of the file, with the - last element being empty if the file is terminated with a newline. - error: A callable to which errors are reported, which takes 4 arguments: - filename, line number, error level, and message - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - lines = (['// marker so line numbers and indices both start at 1'] + lines + - ['// marker so line numbers end in a known way']) - - include_state = _IncludeState() - function_state = _FunctionState() - nesting_state = NestingState() - - ResetNolintSuppressions() - - CheckForCopyright(filename, lines, error) - ProcessGlobalSuppresions(lines) - RemoveMultiLineComments(filename, lines, error) - clean_lines = CleansedLines(lines) - - if file_extension in GetHeaderExtensions(): - CheckForHeaderGuard(filename, clean_lines, error) - - for line in range(clean_lines.NumLines()): - ProcessLine(filename, file_extension, clean_lines, line, - include_state, function_state, nesting_state, error, - extra_check_functions) - FlagCxx11Features(filename, clean_lines, line, error) - nesting_state.CheckCompletedBlocks(filename, error) - - CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) - - # Check that the .cc file has included its header if it exists. - if _IsSourceExtension(file_extension): - CheckHeaderFileIncluded(filename, include_state, error) - - # We check here rather than inside ProcessLine so that we see raw - # lines rather than "cleaned" lines. - CheckForBadCharacters(filename, lines, error) - - CheckForNewlineAtEOF(filename, lines, error) - -def ProcessConfigOverrides(filename): - """ Loads the configuration files and processes the config overrides. - - Args: - filename: The name of the file being processed by the linter. - - Returns: - False if the current |filename| should not be processed further. - """ - - abs_filename = os.path.abspath(filename) - cfg_filters = [] - keep_looking = True - while keep_looking: - abs_path, base_name = os.path.split(abs_filename) - if not base_name: - break # Reached the root directory. - - cfg_file = os.path.join(abs_path, "CPPLINT.cfg") - abs_filename = abs_path - if not os.path.isfile(cfg_file): - continue - - try: - with open(cfg_file) as file_handle: - for line in file_handle: - line, _, _ = line.partition('#') # Remove comments. - if not line.strip(): - continue - - name, _, val = line.partition('=') - name = name.strip() - val = val.strip() - if name == 'set noparent': - keep_looking = False - elif name == 'filter': - cfg_filters.append(val) - elif name == 'exclude_files': - # When matching exclude_files pattern, use the base_name of - # the current file name or the directory name we are processing. - # For example, if we are checking for lint errors in /foo/bar/baz.cc - # and we found the .cfg file at /foo/CPPLINT.cfg, then the config - # file's "exclude_files" filter is meant to be checked against "bar" - # and not "baz" nor "bar/baz.cc". - if base_name: - pattern = re.compile(val) - if pattern.match(base_name): - _cpplint_state.PrintInfo('Ignoring "%s": file excluded by ' - '"%s". File path component "%s" matches pattern "%s"\n' % - (filename, cfg_file, base_name, val)) - return False - elif name == 'linelength': - global _line_length - try: - _line_length = int(val) - except ValueError: - _cpplint_state.PrintError('Line length must be numeric.') - elif name == 'extensions': - global _valid_extensions - try: - extensions = [ext.strip() for ext in val.split(',')] - _valid_extensions = set(extensions) - except ValueError: - sys.stderr.write('Extensions should be a comma-separated list of values;' - 'for example: extensions=hpp,cpp\n' - 'This could not be parsed: "%s"' % (val,)) - elif name == 'headers': - global _header_extensions - try: - extensions = [ext.strip() for ext in val.split(',')] - _header_extensions = set(extensions) - except ValueError: - sys.stderr.write('Extensions should be a comma-separated list of values;' - 'for example: extensions=hpp,cpp\n' - 'This could not be parsed: "%s"' % (val,)) - elif name == 'root': - global _root - _root = val - else: - _cpplint_state.PrintError( - 'Invalid configuration option (%s) in file %s\n' % - (name, cfg_file)) - - except IOError: - _cpplint_state.PrintError( - "Skipping config file '%s': Can't open for reading\n" % cfg_file) - keep_looking = False - - # Apply all the accumulated filters in reverse order (top-level directory - # config options having the least priority). - for cfg_filter in reversed(cfg_filters): - _AddFilters(cfg_filter) - - return True - - -def ProcessFile(filename, vlevel, extra_check_functions=None): - """Does google-lint on a single file. - - Args: - filename: The name of the file to parse. - - vlevel: The level of errors to report. Every error of confidence - >= verbose_level will be reported. 0 is a good default. - - extra_check_functions: An array of additional check functions that will be - run on each source line. Each function takes 4 - arguments: filename, clean_lines, line, error - """ - - _SetVerboseLevel(vlevel) - _BackupFilters() - - if not ProcessConfigOverrides(filename): - _RestoreFilters() - return - - lf_lines = [] - crlf_lines = [] - try: - # Support the UNIX convention of using "-" for stdin. Note that - # we are not opening the file with universal newline support - # (which codecs doesn't support anyway), so the resulting lines do - # contain trailing '\r' characters if we are reading a file that - # has CRLF endings. - # If after the split a trailing '\r' is present, it is removed - # below. - if filename == '-': - lines = codecs.StreamReaderWriter(sys.stdin, - codecs.getreader('utf8'), - codecs.getwriter('utf8'), - 'replace').read().split('\n') - else: - lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') - - # Remove trailing '\r'. - # The -1 accounts for the extra trailing blank line we get from split() - for linenum in range(len(lines) - 1): - if lines[linenum].endswith('\r'): - lines[linenum] = lines[linenum].rstrip('\r') - crlf_lines.append(linenum + 1) - else: - lf_lines.append(linenum + 1) - - except IOError: - _cpplint_state.PrintError( - "Skipping input '%s': Can't open for reading\n" % filename) - _RestoreFilters() - return - - # Note, if no dot is found, this will give the entire filename as the ext. - file_extension = filename[filename.rfind('.') + 1:] - - # When reading from stdin, the extension is unknown, so no cpplint tests - # should rely on the extension. - if filename != '-' and file_extension not in GetAllExtensions(): - _cpplint_state.PrintError('Ignoring %s; not a valid file name ' - '(%s)\n' % (filename, ', '.join(GetAllExtensions()))) - else: - ProcessFileData(filename, file_extension, lines, Error, - extra_check_functions) - - # If end-of-line sequences are a mix of LF and CR-LF, issue - # warnings on the lines with CR. - # - # Don't issue any warnings if all lines are uniformly LF or CR-LF, - # since critique can handle these just fine, and the style guide - # doesn't dictate a particular end of line sequence. - # - # We can't depend on os.linesep to determine what the desired - # end-of-line sequence should be, since that will return the - # server-side end-of-line sequence. - if lf_lines and crlf_lines: - # Warn on every line with CR. An alternative approach might be to - # check whether the file is mostly CRLF or just LF, and warn on the - # minority, we bias toward LF here since most tools prefer LF. - for linenum in crlf_lines: - Error(filename, linenum, 'whitespace/newline', 1, - 'Unexpected \\r (^M) found; better to use only \\n') - - _cpplint_state.PrintInfo('Done processing %s\n' % filename) - _RestoreFilters() - - -def PrintUsage(message): - """Prints a brief usage string and exits, optionally with an error message. - - Args: - message: The optional error message. - """ - sys.stderr.write(_USAGE) - - if message: - sys.exit('\nFATAL ERROR: ' + message) - else: - sys.exit(0) - - -def PrintCategories(): - """Prints a list of all the error-categories used by error messages. - - These are the categories used to filter messages via --filter. - """ - sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) - sys.exit(0) - - -def ParseArguments(args): - """Parses the command line arguments. - - This may set the output format and verbosity level as side-effects. - - Args: - args: The command line arguments: - - Returns: - The list of filenames to lint. - """ - try: - (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', - 'counting=', - 'filter=', - 'root=', - 'repository=', - 'linelength=', - 'extensions=', - 'exclude=', - 'headers=', - 'quiet', - 'recursive']) - except getopt.GetoptError: - PrintUsage('Invalid arguments.') - - verbosity = _VerboseLevel() - output_format = _OutputFormat() - filters = '' - counting_style = '' - recursive = False - - for (opt, val) in opts: - if opt == '--help': - PrintUsage(None) - elif opt == '--output': - if val not in ('emacs', 'vs7', 'eclipse', 'junit'): - PrintUsage('The only allowed output formats are emacs, vs7, eclipse ' - 'and junit.') - output_format = val - elif opt == '--verbose': - verbosity = int(val) - elif opt == '--filter': - filters = val - if not filters: - PrintCategories() - elif opt == '--counting': - if val not in ('total', 'toplevel', 'detailed'): - PrintUsage('Valid counting options are total, toplevel, and detailed') - counting_style = val - elif opt == '--root': - global _root - _root = val - elif opt == '--repository': - global _repository - _repository = val - elif opt == '--linelength': - global _line_length - try: - _line_length = int(val) - except ValueError: - PrintUsage('Line length must be digits.') - elif opt == '--exclude': - global _excludes - if not _excludes: - _excludes = set() - _excludes.update(glob.glob(val)) - elif opt == '--extensions': - global _valid_extensions - try: - _valid_extensions = set(val.split(',')) - except ValueError: - PrintUsage('Extensions must be comma separated list.') - elif opt == '--headers': - global _header_extensions - try: - _header_extensions = set(val.split(',')) - except ValueError: - PrintUsage('Extensions must be comma separated list.') - elif opt == '--recursive': - recursive = True - elif opt == '--quiet': - global _quiet - _quiet = True - - if not filenames: - PrintUsage('No files were specified.') - - if recursive: - filenames = _ExpandDirectories(filenames) - - if _excludes: - filenames = _FilterExcludedFiles(filenames) - - _SetOutputFormat(output_format) - _SetVerboseLevel(verbosity) - _SetFilters(filters) - _SetCountingStyle(counting_style) - - return filenames - -def _ExpandDirectories(filenames): - """Searches a list of filenames and replaces directories in the list with - all files descending from those directories. Files with extensions not in - the valid extensions list are excluded. - - Args: - filenames: A list of files or directories - - Returns: - A list of all files that are members of filenames or descended from a - directory in filenames - """ - expanded = set() - for filename in filenames: - if not os.path.isdir(filename): - expanded.add(filename) - continue - - for root, _, files in os.walk(filename): - for loopfile in files: - fullname = os.path.join(root, loopfile) - if fullname.startswith('.' + os.path.sep): - fullname = fullname[len('.' + os.path.sep):] - expanded.add(fullname) - - filtered = [] - for filename in expanded: - if os.path.splitext(filename)[1][1:] in GetAllExtensions(): - filtered.append(filename) - - return filtered - -def _FilterExcludedFiles(filenames): - """Filters out files listed in the --exclude command line switch. File paths - in the switch are evaluated relative to the current working directory - """ - exclude_paths = [os.path.abspath(f) for f in _excludes] - return [f for f in filenames if os.path.abspath(f) not in exclude_paths] - -def main(): - filenames = ParseArguments(sys.argv[1:]) - backup_err = sys.stderr - try: - # Change stderr to write with replacement characters so we don't die - # if we try to print something containing non-ASCII characters. - sys.stderr = codecs.StreamReader(sys.stderr, 'replace') - - _cpplint_state.ResetErrorCounts() - for filename in filenames: - ProcessFile(filename, _cpplint_state.verbose_level) - _cpplint_state.PrintErrorCounts() - - if _cpplint_state.output_format == 'junit': - sys.stderr.write(_cpplint_state.FormatJUnitXML()) - - finally: - sys.stderr = backup_err - - sys.exit(_cpplint_state.error_count > 0) - - -if __name__ == '__main__': - main() - diff --git a/core/build-support/lint_exclusions.txt b/core/build-support/lint_exclusions.txt deleted file mode 100644 index 36a872f912..0000000000 --- a/core/build-support/lint_exclusions.txt +++ /dev/null @@ -1,11 +0,0 @@ -*cmake-build-debug* -*cmake-build-release* -*cmake_build* -*src/index/thirdparty* -*thirdparty* -*easylogging++* -*SqliteMetaImpl.cpp -*src/grpc* -*thirdparty* -*milvus/include* -*unittest/server/test_web.cpp \ No newline at end of file diff --git a/core/build-support/lintutils.py b/core/build-support/lintutils.py deleted file mode 100755 index b4b01da3e9..0000000000 --- a/core/build-support/lintutils.py +++ /dev/null @@ -1,110 +0,0 @@ -# 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 limitations -# under the License. - -import multiprocessing as mp -import os -from fnmatch import fnmatch -from subprocess import Popen - - -def chunk(seq, n): - """ - divide a sequence into equal sized chunks - (the last chunk may be smaller, but won't be empty) - """ - chunks = [] - some = [] - for element in seq: - if len(some) == n: - chunks.append(some) - some = [] - some.append(element) - if len(some) > 0: - chunks.append(some) - return chunks - - -def dechunk(chunks): - "flatten chunks into a single list" - seq = [] - for chunk in chunks: - seq.extend(chunk) - return seq - - -def run_parallel(cmds, **kwargs): - """ - Run each of cmds (with shared **kwargs) using subprocess.Popen - then wait for all of them to complete. - Runs batches of multiprocessing.cpu_count() * 2 from cmds - returns a list of tuples containing each process' - returncode, stdout, stderr - """ - complete = [] - for cmds_batch in chunk(cmds, mp.cpu_count() * 2): - procs_batch = [Popen(cmd, **kwargs) for cmd in cmds_batch] - for proc in procs_batch: - stdout, stderr = proc.communicate() - complete.append((proc.returncode, stdout, stderr)) - return complete - - -_source_extensions = ''' -.h -.cc -.cpp -'''.split() - - -def get_sources(source_dir, exclude_globs=[]): - sources = [] - for directory, subdirs, basenames in os.walk(source_dir): - for path in [os.path.join(directory, basename) - for basename in basenames]: - # filter out non-source files - if os.path.splitext(path)[1] not in _source_extensions: - continue - - path = os.path.abspath(path) - - # filter out files that match the globs in the globs file - if any([fnmatch(path, glob) for glob in exclude_globs]): - continue - - sources.append(path) - return sources - - -def stdout_pathcolonline(completed_process, filenames): - """ - given a completed process which may have reported some files as problematic - by printing the path name followed by ':' then a line number, examine - stdout and return the set of actually reported file names - """ - returncode, stdout, stderr = completed_process - bfilenames = set() - for filename in filenames: - bfilenames.add(filename.encode('utf-8') + b':') - problem_files = set() - for line in stdout.splitlines(): - for filename in bfilenames: - if line.startswith(filename): - problem_files.add(filename.decode('utf-8')) - bfilenames.remove(filename) - break - return problem_files, stdout - diff --git a/core/build-support/run_clang_format.py b/core/build-support/run_clang_format.py deleted file mode 100755 index cb5afaba4a..0000000000 --- a/core/build-support/run_clang_format.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/env python2 -# 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 limitations -# under the License. - -from __future__ import print_function -import lintutils -from subprocess import PIPE -import argparse -import difflib -import multiprocessing as mp -import sys -from functools import partial - - -# examine the output of clang-format and if changes are -# present assemble a (unified)patch of the difference -def _check_one_file(completed_processes, filename): - with open(filename, "rb") as reader: - original = reader.read() - - returncode, stdout, stderr = completed_processes[filename] - formatted = stdout - if formatted != original: - # Run the equivalent of diff -u - diff = list(difflib.unified_diff( - original.decode('utf8').splitlines(True), - formatted.decode('utf8').splitlines(True), - fromfile=filename, - tofile="{} (after clang format)".format( - filename))) - else: - diff = None - - return filename, diff - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="Runs clang-format on all of the source " - "files. If --fix is specified enforce format by " - "modifying in place, otherwise compare the output " - "with the existing file and output any necessary " - "changes as a patch in unified diff format") - parser.add_argument("--clang_format_binary", - required=True, - help="Path to the clang-format binary") - parser.add_argument("--exclude_globs", - help="Filename containing globs for files " - "that should be excluded from the checks") - parser.add_argument("--source_dir", - required=True, - help="Root directory of the source code") - parser.add_argument("--fix", default=False, - action="store_true", - help="If specified, will re-format the source " - "code instead of comparing the re-formatted " - "output, defaults to %(default)s") - parser.add_argument("--quiet", default=False, - action="store_true", - help="If specified, only print errors") - arguments = parser.parse_args() - - exclude_globs = [] - if arguments.exclude_globs: - for line in open(arguments.exclude_globs): - exclude_globs.append(line.strip()) - - formatted_filenames = [] - for path in lintutils.get_sources(arguments.source_dir, exclude_globs): - formatted_filenames.append(str(path)) - - if arguments.fix: - if not arguments.quiet: - print("\n".join(map(lambda x: "Formatting {}".format(x), - formatted_filenames))) - - # Break clang-format invocations into chunks: each invocation formats - # 16 files. Wait for all processes to complete - results = lintutils.run_parallel([ - [arguments.clang_format_binary, "-i"] + some - for some in lintutils.chunk(formatted_filenames, 16) - ]) - for returncode, stdout, stderr in results: - # if any clang-format reported a parse error, bubble it - if returncode != 0: - sys.exit(returncode) - - else: - # run an instance of clang-format for each source file in parallel, - # then wait for all processes to complete - results = lintutils.run_parallel([ - [arguments.clang_format_binary, filename] - for filename in formatted_filenames - ], stdout=PIPE, stderr=PIPE) - for returncode, stdout, stderr in results: - # if any clang-format reported a parse error, bubble it - if returncode != 0: - sys.exit(returncode) - - error = False - checker = partial(_check_one_file, { - filename: result - for filename, result in zip(formatted_filenames, results) - }) - pool = mp.Pool() - try: - # check the output from each invocation of clang-format in parallel - for filename, diff in pool.imap(checker, formatted_filenames): - if not arguments.quiet: - print("Checking {}".format(filename)) - if diff: - print("{} had clang-format style issues".format(filename)) - # Print out the diff to stderr - error = True - # pad with a newline - print(file=sys.stderr) - diff_out = [] - for diff_str in diff: - diff_out.append(diff_str.encode('raw_unicode_escape')) - sys.stderr.writelines(diff_out) - except Exception: - error = True - raise - finally: - pool.terminate() - pool.join() - sys.exit(1 if error else 0) - diff --git a/core/build-support/run_clang_tidy.py b/core/build-support/run_clang_tidy.py deleted file mode 100755 index 6df17d5379..0000000000 --- a/core/build-support/run_clang_tidy.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env python -# 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 limitations -# under the License. - -from __future__ import print_function -import argparse -import multiprocessing as mp -import lintutils -from subprocess import PIPE -import sys -from functools import partial - - -def _get_chunk_key(filenames): - # lists are not hashable so key on the first filename in a chunk - return filenames[0] - - -# clang-tidy outputs complaints in '/path:line_number: complaint' format, -# so we can scan its output to get a list of files to fix -def _check_some_files(completed_processes, filenames): - result = completed_processes[_get_chunk_key(filenames)] - return lintutils.stdout_pathcolonline(result, filenames) - - -def _check_all(cmd, filenames): - # each clang-tidy instance will process 16 files - chunks = lintutils.chunk(filenames, 16) - cmds = [cmd + some for some in chunks] - results = lintutils.run_parallel(cmds, stderr=PIPE, stdout=PIPE) - error = False - # record completed processes (keyed by the first filename in the input - # chunk) for lookup in _check_some_files - completed_processes = { - _get_chunk_key(some): result - for some, result in zip(chunks, results) - } - checker = partial(_check_some_files, completed_processes) - pool = mp.Pool() - try: - # check output of completed clang-tidy invocations in parallel - for problem_files, stdout in pool.imap(checker, chunks): - if problem_files: - msg = "clang-tidy suggested fixes for {}" - print("\n".join(map(msg.format, problem_files))) - print(stdout) - error = True - except Exception: - error = True - raise - finally: - pool.terminate() - pool.join() - - if error: - sys.exit(1) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="Runs clang-tidy on all ") - parser.add_argument("--clang_tidy_binary", - required=True, - help="Path to the clang-tidy binary") - parser.add_argument("--exclude_globs", - help="Filename containing globs for files " - "that should be excluded from the checks") - parser.add_argument("--compile_commands", - required=True, - help="compile_commands.json to pass clang-tidy") - parser.add_argument("--source_dir", - required=True, - help="Root directory of the source code") - parser.add_argument("--fix", default=False, - action="store_true", - help="If specified, will attempt to fix the " - "source code instead of recommending fixes, " - "defaults to %(default)s") - parser.add_argument("--quiet", default=False, - action="store_true", - help="If specified, only print errors") - arguments = parser.parse_args() - - exclude_globs = [] - if arguments.exclude_globs: - for line in open(arguments.exclude_globs): - exclude_globs.append(line.strip()) - - linted_filenames = [] - for path in lintutils.get_sources(arguments.source_dir, exclude_globs): - linted_filenames.append(path) - - if not arguments.quiet: - msg = 'Tidying {}' if arguments.fix else 'Checking {}' - print("\n".join(map(msg.format, linted_filenames))) - - cmd = [ - arguments.clang_tidy_binary, - '-p', - arguments.compile_commands - ] - if arguments.fix: - cmd.append('-fix') - results = lintutils.run_parallel( - [cmd + some for some in lintutils.chunk(linted_filenames, 16)]) - for returncode, stdout, stderr in results: - if returncode != 0: - sys.exit(returncode) - - else: - _check_all(cmd, linted_filenames) - diff --git a/core/build-support/run_cpplint.py b/core/build-support/run_cpplint.py deleted file mode 100755 index 98c4170858..0000000000 --- a/core/build-support/run_cpplint.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env python -# 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 limitations -# under the License. - -from __future__ import print_function -import lintutils -from subprocess import PIPE, STDOUT -import argparse -import multiprocessing as mp -import sys -import platform -from functools import partial - - -# NOTE(wesm): -# -# * readability/casting is disabled as it aggressively warns about functions -# with names like "int32", so "int32(x)", where int32 is a function name, -# warns with -_filters = ''' --whitespace/comments --readability/casting --readability/todo --readability/alt_tokens --build/header_guard --build/c++11 --runtime/references --build/include_order -'''.split() - - -def _get_chunk_key(filenames): - # lists are not hashable so key on the first filename in a chunk - return filenames[0] - - -def _check_some_files(completed_processes, filenames): - # cpplint outputs complaints in '/path:line_number: complaint' format, - # so we can scan its output to get a list of files to fix - result = completed_processes[_get_chunk_key(filenames)] - return lintutils.stdout_pathcolonline(result, filenames) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="Runs cpplint on all of the source files.") - parser.add_argument("--cpplint_binary", - required=True, - help="Path to the cpplint binary") - parser.add_argument("--exclude_globs", - help="Filename containing globs for files " - "that should be excluded from the checks") - parser.add_argument("--source_dir", - required=True, - help="Root directory of the source code") - parser.add_argument("--quiet", default=False, - action="store_true", - help="If specified, only print errors") - arguments = parser.parse_args() - - exclude_globs = [] - if arguments.exclude_globs: - for line in open(arguments.exclude_globs): - exclude_globs.append(line.strip()) - - linted_filenames = [] - for path in lintutils.get_sources(arguments.source_dir, exclude_globs): - linted_filenames.append(str(path)) - - cmd = [ - arguments.cpplint_binary, - '--verbose=2', - '--linelength=120', - '--filter=' + ','.join(_filters) - ] - if (arguments.cpplint_binary.endswith('.py') and - platform.system() == 'Windows'): - # Windows doesn't support executable scripts; execute with - # sys.executable - cmd.insert(0, sys.executable) - if arguments.quiet: - cmd.append('--quiet') - else: - print("\n".join(map(lambda x: "Linting {}".format(x), - linted_filenames))) - - # lint files in chunks: each invocation of cpplint will process 16 files - chunks = lintutils.chunk(linted_filenames, 16) - cmds = [cmd + some for some in chunks] - results = lintutils.run_parallel(cmds, stdout=PIPE, stderr=STDOUT) - - error = False - # record completed processes (keyed by the first filename in the input - # chunk) for lookup in _check_some_files - completed_processes = { - _get_chunk_key(filenames): result - for filenames, result in zip(chunks, results) - } - checker = partial(_check_some_files, completed_processes) - pool = mp.Pool() - try: - # scan the outputs of various cpplint invocations in parallel to - # distill a list of problematic files - for problem_files, stdout in pool.imap(checker, chunks): - if problem_files: - if isinstance(stdout, bytes): - stdout = stdout.decode('utf8') - print(stdout, file=sys.stderr) - error = True - except Exception: - error = True - raise - finally: - pool.terminate() - pool.join() - - sys.exit(1 if error else 0) - diff --git a/core/build.sh b/core/build.sh deleted file mode 100755 index fa19ab5313..0000000000 --- a/core/build.sh +++ /dev/null @@ -1,150 +0,0 @@ -#!/bin/bash - -BUILD_OUTPUT_DIR="cmake_build" -BUILD_TYPE="Debug" -BUILD_UNITTEST="OFF" -INSTALL_PREFIX=$(pwd)/milvus -MAKE_CLEAN="OFF" -BUILD_COVERAGE="OFF" -DB_PATH="/tmp/milvus" -PROFILING="OFF" -RUN_CPPLINT="OFF" -CUDA_COMPILER=/usr/local/cuda/bin/nvcc -GPU_VERSION="OFF" #defaults to CPU version -WITH_MKL="OFF" -WITH_PROMETHEUS="ON" -FIU_ENABLE="OFF" -BUILD_OPENBLAS="ON" - -while getopts "p:d:t:f:ulrcghzmei" arg; do - case $arg in - p) - INSTALL_PREFIX=$OPTARG - ;; - d) - DB_PATH=$OPTARG - ;; - t) - BUILD_TYPE=$OPTARG # BUILD_TYPE - ;; - u) - echo "Build and run unittest cases" - BUILD_UNITTEST="ON" - ;; - l) - RUN_CPPLINT="ON" - ;; - r) - if [[ -d ${BUILD_OUTPUT_DIR} ]]; then - rm ./${BUILD_OUTPUT_DIR} -r - MAKE_CLEAN="ON" - fi - ;; - c) - BUILD_COVERAGE="ON" - ;; - z) - PROFILING="ON" - ;; - g) - GPU_VERSION="ON" - ;; - m) - WITH_MKL="ON" - ;; - e) - WITH_PROMETHEUS="OFF" - ;; - i) - FIU_ENABLE="ON" - ;; - h) # help - echo " - -parameter: --p: install prefix(default: $(pwd)/milvus) --d: db data path(default: /tmp/milvus) --t: build type(default: Debug) --u: building unit test options(default: OFF) --l: run cpplint, clang-format and clang-tidy(default: OFF) --r: remove previous build directory(default: OFF) --c: code coverage(default: OFF) --z: profiling(default: OFF) --g: build GPU version(default: OFF) --m: build with MKL(default: OFF) --e: build without prometheus(default: OFF) --i: build FIU_ENABLE(default: OFF) --h: help - -usage: -./build.sh -p \${INSTALL_PREFIX} -t \${BUILD_TYPE} [-u] [-l] [-r] [-c] [-z] [-g] [-m] [-e] [-h] - " - exit 0 - ;; - ?) - echo "ERROR! unknown argument" - exit 1 - ;; - esac -done - -if [[ ! -d ${BUILD_OUTPUT_DIR} ]]; then - mkdir ${BUILD_OUTPUT_DIR} -fi - -cd ${BUILD_OUTPUT_DIR} - -# remove make cache since build.sh -l use default variables -# force update the variables each time -make rebuild_cache >/dev/null 2>&1 - -CMAKE_CMD="cmake \ --DBUILD_UNIT_TEST=${BUILD_UNITTEST} \ --DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} --DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ --DOpenBLAS_SOURCE=AUTO \ --DCMAKE_CUDA_COMPILER=${CUDA_COMPILER} \ --DBUILD_COVERAGE=${BUILD_COVERAGE} \ --DMILVUS_DB_PATH=${DB_PATH} \ --DENABLE_CPU_PROFILING=${PROFILING} \ --DMILVUS_GPU_VERSION=${GPU_VERSION} \ --DFAISS_WITH_MKL=${WITH_MKL} \ --DMILVUS_WITH_PROMETHEUS=${WITH_PROMETHEUS} \ --DMILVUS_WITH_FIU=${FIU_ENABLE} \ -../" -echo ${CMAKE_CMD} -${CMAKE_CMD} - -if [[ ${MAKE_CLEAN} == "ON" ]]; then - make clean -fi - -if [[ ${RUN_CPPLINT} == "ON" ]]; then - # cpplint check - make lint - if [ $? -ne 0 ]; then - echo "ERROR! cpplint check failed" - exit 1 - fi - echo "cpplint check passed!" - - # clang-format check - make check-clang-format - if [ $? -ne 0 ]; then - echo "ERROR! clang-format check failed" - exit 1 - fi - echo "clang-format check passed!" - -# # clang-tidy check -# make check-clang-tidy -# if [ $? -ne 0 ]; then -# echo "ERROR! clang-tidy check failed" -# exit 1 -# fi -# echo "clang-tidy check passed!" -else - - # compile and build - make -j 8 install || exit 1 -fi diff --git a/core/centos7_build_deps.sh b/core/centos7_build_deps.sh deleted file mode 100755 index 9166aa67d4..0000000000 --- a/core/centos7_build_deps.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -sudo yum install -y epel-release centos-release-scl-rh && sudo yum install -y wget curl which && \ -sudo wget -qO- "https://cmake.org/files/v3.14/cmake-3.14.3-Linux-x86_64.tar.gz" | sudo tar --strip-components=1 -xz -C /usr/local && \ -sudo yum install -y ccache make automake git python3-pip libcurl-devel python3-devel boost-static mysql-devel \ -devtoolset-7-gcc devtoolset-7-gcc-c++ devtoolset-7-gcc-gfortran llvm-toolset-7.0-clang llvm-toolset-7.0-clang-tools-extra lcov \ -lapack-devel openssl-devel - -echo "source scl_source enable devtoolset-7" | sudo tee -a /etc/profile.d/devtoolset-7.sh -echo "source scl_source enable llvm-toolset-7.0" | sudo tee -a /etc/profile.d/llvm-toolset-7.sh -echo "export CLANG_TOOLS_PATH=/opt/rh/llvm-toolset-7.0/root/usr/bin" | sudo tee -a /etc/profile.d/llvm-toolset-7.sh - diff --git a/core/cmake/BuildUtils.cmake b/core/cmake/BuildUtils.cmake deleted file mode 100644 index 6332d29d74..0000000000 --- a/core/cmake/BuildUtils.cmake +++ /dev/null @@ -1,204 +0,0 @@ -# Define a function that check last file modification -function(Check_Last_Modify cache_check_lists_file_path working_dir last_modified_commit_id) - if(EXISTS "${working_dir}") - if(EXISTS "${cache_check_lists_file_path}") - set(GIT_LOG_SKIP_NUM 0) - set(_MATCH_ALL ON CACHE BOOL "Match all") - set(_LOOP_STATUS ON CACHE BOOL "Whether out of loop") - file(STRINGS ${cache_check_lists_file_path} CACHE_IGNORE_TXT) - while(_LOOP_STATUS) - foreach(_IGNORE_ENTRY ${CACHE_IGNORE_TXT}) - if(NOT _IGNORE_ENTRY MATCHES "^[^#]+") - continue() - endif() - - set(_MATCH_ALL OFF) - execute_process(COMMAND git log --no-merges -1 --skip=${GIT_LOG_SKIP_NUM} --name-status --pretty= WORKING_DIRECTORY ${working_dir} OUTPUT_VARIABLE CHANGE_FILES) - if(NOT CHANGE_FILES STREQUAL "") - string(REPLACE "\n" ";" _CHANGE_FILES ${CHANGE_FILES}) - foreach(_FILE_ENTRY ${_CHANGE_FILES}) - string(REGEX MATCH "[^ \t]+$" _FILE_NAME ${_FILE_ENTRY}) - execute_process(COMMAND sh -c "echo ${_FILE_NAME} | grep ${_IGNORE_ENTRY}" RESULT_VARIABLE return_code) - if (return_code EQUAL 0) - execute_process(COMMAND git log --no-merges -1 --skip=${GIT_LOG_SKIP_NUM} --pretty=%H WORKING_DIRECTORY ${working_dir} OUTPUT_VARIABLE LAST_MODIFIED_COMMIT_ID) - set (${last_modified_commit_id} ${LAST_MODIFIED_COMMIT_ID} PARENT_SCOPE) - set(_LOOP_STATUS OFF) - endif() - endforeach() - else() - set(_LOOP_STATUS OFF) - endif() - endforeach() - - if(_MATCH_ALL) - execute_process(COMMAND git log --no-merges -1 --skip=${GIT_LOG_SKIP_NUM} --pretty=%H WORKING_DIRECTORY ${working_dir} OUTPUT_VARIABLE LAST_MODIFIED_COMMIT_ID) - set (${last_modified_commit_id} ${LAST_MODIFIED_COMMIT_ID} PARENT_SCOPE) - set(_LOOP_STATUS OFF) - endif() - - math(EXPR GIT_LOG_SKIP_NUM "${GIT_LOG_SKIP_NUM} + 1") - endwhile(_LOOP_STATUS) - else() - execute_process(COMMAND git log --no-merges -1 --skip=${GIT_LOG_SKIP_NUM} --pretty=%H WORKING_DIRECTORY ${working_dir} OUTPUT_VARIABLE LAST_MODIFIED_COMMIT_ID) - set (${last_modified_commit_id} ${LAST_MODIFIED_COMMIT_ID} PARENT_SCOPE) - endif() - else() - message(FATAL_ERROR "The directory ${working_dir} does not exist") - endif() -endfunction() - -# Define a function that extracts a cached package -function(ExternalProject_Use_Cache project_name package_file install_path) - message(STATUS "Will use cached package file: ${package_file}") - - ExternalProject_Add(${project_name} - DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo - "No download step needed (using cached package)" - CONFIGURE_COMMAND ${CMAKE_COMMAND} -E echo - "No configure step needed (using cached package)" - BUILD_COMMAND ${CMAKE_COMMAND} -E echo - "No build step needed (using cached package)" - INSTALL_COMMAND ${CMAKE_COMMAND} -E echo - "No install step needed (using cached package)" - ) - - # We want our tar files to contain the Install/ prefix (not for any - # very special reason, only for consistency and so that we can identify them - # in the extraction logs) which means that we must extract them in the - # binary (top-level build) directory to have them installed in the right - # place for subsequent ExternalProjects to pick them up. It seems that the - # only way to control the working directory is with Add_Step! - ExternalProject_Add_Step(${project_name} extract - ALWAYS 1 - COMMAND - ${CMAKE_COMMAND} -E echo - "Extracting ${package_file} to ${install_path}" - COMMAND - ${CMAKE_COMMAND} -E tar xzf ${package_file} ${install_path} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - ) - - ExternalProject_Add_StepTargets(${project_name} extract) -endfunction() - -# Define a function that to create a new cached package -function(ExternalProject_Create_Cache project_name package_file install_path cache_username cache_password cache_path) - if(EXISTS ${package_file}) - message(STATUS "Removing existing package file: ${package_file}") - file(REMOVE ${package_file}) - endif() - - string(REGEX REPLACE "(.+)/.+$" "\\1" package_dir ${package_file}) - if(NOT EXISTS ${package_dir}) - file(MAKE_DIRECTORY ${package_dir}) - endif() - - message(STATUS "Will create cached package file: ${package_file}") - - ExternalProject_Add_Step(${project_name} package - DEPENDEES install - BYPRODUCTS ${package_file} - COMMAND ${CMAKE_COMMAND} -E echo "Updating cached package file: ${package_file}" - COMMAND ${CMAKE_COMMAND} -E tar czvf ${package_file} ${install_path} - COMMAND ${CMAKE_COMMAND} -E echo "Uploading package file ${package_file} to ${cache_path}" - COMMAND curl -u${cache_username}:${cache_password} -T ${package_file} ${cache_path} - ) - - ExternalProject_Add_StepTargets(${project_name} package) -endfunction() - -function(ADD_THIRDPARTY_LIB LIB_NAME) - set(options) - set(one_value_args SHARED_LIB STATIC_LIB) - set(multi_value_args DEPS INCLUDE_DIRECTORIES) - cmake_parse_arguments(ARG - "${options}" - "${one_value_args}" - "${multi_value_args}" - ${ARGN}) - if(ARG_UNPARSED_ARGUMENTS) - message(SEND_ERROR "Error: unrecognized arguments: ${ARG_UNPARSED_ARGUMENTS}") - endif() - - if(ARG_STATIC_LIB AND ARG_SHARED_LIB) - if(NOT ARG_STATIC_LIB) - message(FATAL_ERROR "No static or shared library provided for ${LIB_NAME}") - endif() - - set(AUG_LIB_NAME "${LIB_NAME}_static") - add_library(${AUG_LIB_NAME} STATIC IMPORTED) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES IMPORTED_LOCATION "${ARG_STATIC_LIB}") - if(ARG_DEPS) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES INTERFACE_LINK_LIBRARIES "${ARG_DEPS}") - endif() - message(STATUS "Added static library dependency ${AUG_LIB_NAME}: ${ARG_STATIC_LIB}") - if(ARG_INCLUDE_DIRECTORIES) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES INTERFACE_INCLUDE_DIRECTORIES - "${ARG_INCLUDE_DIRECTORIES}") - endif() - - set(AUG_LIB_NAME "${LIB_NAME}_shared") - add_library(${AUG_LIB_NAME} SHARED IMPORTED) - - if(WIN32) - # Mark the ".lib" location as part of a Windows DLL - set_target_properties(${AUG_LIB_NAME} - PROPERTIES IMPORTED_IMPLIB "${ARG_SHARED_LIB}") - else() - set_target_properties(${AUG_LIB_NAME} - PROPERTIES IMPORTED_LOCATION "${ARG_SHARED_LIB}") - endif() - if(ARG_DEPS) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES INTERFACE_LINK_LIBRARIES "${ARG_DEPS}") - endif() - message(STATUS "Added shared library dependency ${AUG_LIB_NAME}: ${ARG_SHARED_LIB}") - if(ARG_INCLUDE_DIRECTORIES) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES INTERFACE_INCLUDE_DIRECTORIES - "${ARG_INCLUDE_DIRECTORIES}") - endif() - elseif(ARG_STATIC_LIB) - set(AUG_LIB_NAME "${LIB_NAME}_static") - add_library(${AUG_LIB_NAME} STATIC IMPORTED) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES IMPORTED_LOCATION "${ARG_STATIC_LIB}") - if(ARG_DEPS) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES INTERFACE_LINK_LIBRARIES "${ARG_DEPS}") - endif() - message(STATUS "Added static library dependency ${AUG_LIB_NAME}: ${ARG_STATIC_LIB}") - if(ARG_INCLUDE_DIRECTORIES) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES INTERFACE_INCLUDE_DIRECTORIES - "${ARG_INCLUDE_DIRECTORIES}") - endif() - elseif(ARG_SHARED_LIB) - set(AUG_LIB_NAME "${LIB_NAME}_shared") - add_library(${AUG_LIB_NAME} SHARED IMPORTED) - - if(WIN32) - # Mark the ".lib" location as part of a Windows DLL - set_target_properties(${AUG_LIB_NAME} - PROPERTIES IMPORTED_IMPLIB "${ARG_SHARED_LIB}") - else() - set_target_properties(${AUG_LIB_NAME} - PROPERTIES IMPORTED_LOCATION "${ARG_SHARED_LIB}") - endif() - message(STATUS "Added shared library dependency ${AUG_LIB_NAME}: ${ARG_SHARED_LIB}") - if(ARG_DEPS) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES INTERFACE_LINK_LIBRARIES "${ARG_DEPS}") - endif() - if(ARG_INCLUDE_DIRECTORIES) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES INTERFACE_INCLUDE_DIRECTORIES - "${ARG_INCLUDE_DIRECTORIES}") - endif() - else() - message(FATAL_ERROR "No static or shared library provided for ${LIB_NAME}") - endif() -endfunction() diff --git a/core/cmake/DefineOptions.cmake b/core/cmake/DefineOptions.cmake deleted file mode 100644 index 1103a9b125..0000000000 --- a/core/cmake/DefineOptions.cmake +++ /dev/null @@ -1,173 +0,0 @@ - -macro(set_option_category name) - set(MILVUS_OPTION_CATEGORY ${name}) - list(APPEND "MILVUS_OPTION_CATEGORIES" ${name}) -endmacro() - -macro(define_option name description default) - option(${name} ${description} ${default}) - list(APPEND "MILVUS_${MILVUS_OPTION_CATEGORY}_OPTION_NAMES" ${name}) - set("${name}_OPTION_DESCRIPTION" ${description}) - set("${name}_OPTION_DEFAULT" ${default}) - set("${name}_OPTION_TYPE" "bool") -endmacro() - -function(list_join lst glue out) - if ("${${lst}}" STREQUAL "") - set(${out} "" PARENT_SCOPE) - return() - endif () - - list(GET ${lst} 0 joined) - list(REMOVE_AT ${lst} 0) - foreach (item ${${lst}}) - set(joined "${joined}${glue}${item}") - endforeach () - set(${out} ${joined} PARENT_SCOPE) -endfunction() - -macro(define_option_string name description default) - set(${name} ${default} CACHE STRING ${description}) - list(APPEND "MILVUS_${MILVUS_OPTION_CATEGORY}_OPTION_NAMES" ${name}) - set("${name}_OPTION_DESCRIPTION" ${description}) - set("${name}_OPTION_DEFAULT" "\"${default}\"") - set("${name}_OPTION_TYPE" "string") - - set("${name}_OPTION_ENUM" ${ARGN}) - list_join("${name}_OPTION_ENUM" "|" "${name}_OPTION_ENUM") - if (NOT ("${${name}_OPTION_ENUM}" STREQUAL "")) - set_property(CACHE ${name} PROPERTY STRINGS ${ARGN}) - endif () -endmacro() - -#---------------------------------------------------------------------- -set_option_category("Milvus Build Option") - -define_option(MILVUS_GPU_VERSION "Build GPU version" OFF) - -#---------------------------------------------------------------------- -set_option_category("Thirdparty") - -set(MILVUS_DEPENDENCY_SOURCE_DEFAULT "BUNDLED") - -define_option_string(MILVUS_DEPENDENCY_SOURCE - "Method to use for acquiring MILVUS's build dependencies" - "${MILVUS_DEPENDENCY_SOURCE_DEFAULT}" - "AUTO" - "BUNDLED" - "SYSTEM") - -define_option(MILVUS_USE_CCACHE "Use ccache when compiling (if available)" ON) - -define_option(MILVUS_VERBOSE_THIRDPARTY_BUILD - "Show output from ExternalProjects rather than just logging to files" ON) - -define_option(MILVUS_WITH_EASYLOGGINGPP "Build with Easylogging++ library" ON) - -define_option(MILVUS_WITH_PROMETHEUS "Build with PROMETHEUS library" ON) - -define_option(MILVUS_WITH_SQLITE "Build with SQLite library" ON) - -define_option(MILVUS_WITH_SQLITE_ORM "Build with SQLite ORM library" ON) - -define_option(MILVUS_WITH_MYSQLPP "Build with MySQL++" ON) - -define_option(MILVUS_WITH_YAMLCPP "Build with yaml-cpp library" ON) - -if (ENABLE_CPU_PROFILING STREQUAL "ON") - define_option(MILVUS_WITH_LIBUNWIND "Build with libunwind" ON) - define_option(MILVUS_WITH_GPERFTOOLS "Build with gperftools" ON) -endif () - -define_option(MILVUS_WITH_GRPC "Build with GRPC" ON) - -define_option(MILVUS_WITH_ZLIB "Build with zlib compression" ON) - -define_option(MILVUS_WITH_OPENTRACING "Build with Opentracing" ON) - -define_option(MILVUS_WITH_FIU "Build with fiu" OFF) - -define_option(MILVUS_WITH_AWS "Build with aws" OFF) - -define_option(MILVUS_WITH_OATPP "Build with oatpp" ON) - -#---------------------------------------------------------------------- -set_option_category("Test and benchmark") - -unset(MILVUS_BUILD_TESTS CACHE) -if (BUILD_UNIT_TEST) - define_option(MILVUS_BUILD_TESTS "Build the MILVUS googletest unit tests" ON) -else () - define_option(MILVUS_BUILD_TESTS "Build the MILVUS googletest unit tests" OFF) -endif (BUILD_UNIT_TEST) - -#---------------------------------------------------------------------- -macro(config_summary) - message(STATUS "---------------------------------------------------------------------") - message(STATUS "MILVUS version: ${MILVUS_VERSION}") - message(STATUS) - message(STATUS "Build configuration summary:") - - message(STATUS " Generator: ${CMAKE_GENERATOR}") - message(STATUS " Build type: ${CMAKE_BUILD_TYPE}") - message(STATUS " Source directory: ${CMAKE_CURRENT_SOURCE_DIR}") - if (${CMAKE_EXPORT_COMPILE_COMMANDS}) - message( - STATUS " Compile commands: ${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json") - endif () - - foreach (category ${MILVUS_OPTION_CATEGORIES}) - - message(STATUS) - message(STATUS "${category} options:") - - set(option_names ${MILVUS_${category}_OPTION_NAMES}) - - set(max_value_length 0) - foreach (name ${option_names}) - string(LENGTH "\"${${name}}\"" value_length) - if (${max_value_length} LESS ${value_length}) - set(max_value_length ${value_length}) - endif () - endforeach () - - foreach (name ${option_names}) - if ("${${name}_OPTION_TYPE}" STREQUAL "string") - set(value "\"${${name}}\"") - else () - set(value "${${name}}") - endif () - - set(default ${${name}_OPTION_DEFAULT}) - set(description ${${name}_OPTION_DESCRIPTION}) - string(LENGTH ${description} description_length) - if (${description_length} LESS 70) - string( - SUBSTRING - " " - ${description_length} -1 description_padding) - else () - set(description_padding " - ") - endif () - - set(comment "[${name}]") - - if ("${value}" STREQUAL "${default}") - set(comment "[default] ${comment}") - endif () - - if (NOT ("${${name}_OPTION_ENUM}" STREQUAL "")) - set(comment "${comment} [${${name}_OPTION_ENUM}]") - endif () - - string( - SUBSTRING "${value} " - 0 ${max_value_length} value) - - message(STATUS " ${description} ${description_padding} ${value} ${comment}") - endforeach () - - endforeach () - -endmacro() diff --git a/core/cmake/FindClangTools.cmake b/core/cmake/FindClangTools.cmake deleted file mode 100644 index 83e71d750b..0000000000 --- a/core/cmake/FindClangTools.cmake +++ /dev/null @@ -1,109 +0,0 @@ -# -# Licensed 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 -# limitations under the License. -# -# Tries to find the clang-tidy and clang-format modules -# -# Usage of this module as follows: -# -# find_package(ClangTools) -# -# Variables used by this module, they can change the default behaviour and need -# to be set before calling find_package: -# -# ClangToolsBin_HOME - -# When set, this path is inspected instead of standard library binary locations -# to find clang-tidy and clang-format -# -# This module defines -# CLANG_TIDY_BIN, The path to the clang tidy binary -# CLANG_TIDY_FOUND, Whether clang tidy was found -# CLANG_FORMAT_BIN, The path to the clang format binary -# CLANG_TIDY_FOUND, Whether clang format was found - -find_program(CLANG_TIDY_BIN - NAMES - clang-tidy-7.0 - clang-tidy-6.0 - clang-tidy-5.0 - clang-tidy-4.0 - clang-tidy-3.9 - clang-tidy-3.8 - clang-tidy-3.7 - clang-tidy-3.6 - clang-tidy - PATHS ${ClangTools_PATH} $ENV{CLANG_TOOLS_PATH} /usr/local/bin /usr/bin - NO_DEFAULT_PATH -) - -if ( "${CLANG_TIDY_BIN}" STREQUAL "CLANG_TIDY_BIN-NOTFOUND" ) - set(CLANG_TIDY_FOUND 0) - message("clang-tidy not found") -else() - set(CLANG_TIDY_FOUND 1) - message("clang-tidy found at ${CLANG_TIDY_BIN}") -endif() - -if (CLANG_FORMAT_VERSION) - find_program(CLANG_FORMAT_BIN - NAMES clang-format-${CLANG_FORMAT_VERSION} - PATHS - ${ClangTools_PATH} - $ENV{CLANG_TOOLS_PATH} - /usr/local/bin /usr/bin - NO_DEFAULT_PATH - ) - - # If not found yet, search alternative locations - if (("${CLANG_FORMAT_BIN}" STREQUAL "CLANG_FORMAT_BIN-NOTFOUND") AND APPLE) - # Homebrew ships older LLVM versions in /usr/local/opt/llvm@version/ - STRING(REGEX REPLACE "^([0-9]+)\\.[0-9]+" "\\1" CLANG_FORMAT_MAJOR_VERSION "${CLANG_FORMAT_VERSION}") - STRING(REGEX REPLACE "^[0-9]+\\.([0-9]+)" "\\1" CLANG_FORMAT_MINOR_VERSION "${CLANG_FORMAT_VERSION}") - if ("${CLANG_FORMAT_MINOR_VERSION}" STREQUAL "0") - find_program(CLANG_FORMAT_BIN - NAMES clang-format - PATHS /usr/local/opt/llvm@${CLANG_FORMAT_MAJOR_VERSION}/bin - NO_DEFAULT_PATH - ) - else() - find_program(CLANG_FORMAT_BIN - NAMES clang-format - PATHS /usr/local/opt/llvm@${CLANG_FORMAT_VERSION}/bin - NO_DEFAULT_PATH - ) - endif() - endif() -else() - find_program(CLANG_FORMAT_BIN - NAMES - clang-format-7.0 - clang-format-6.0 - clang-format-5.0 - clang-format-4.0 - clang-format-3.9 - clang-format-3.8 - clang-format-3.7 - clang-format-3.6 - clang-format - PATHS ${ClangTools_PATH} $ENV{CLANG_TOOLS_PATH} /usr/local/bin /usr/bin - NO_DEFAULT_PATH - ) -endif() - -if ( "${CLANG_FORMAT_BIN}" STREQUAL "CLANG_FORMAT_BIN-NOTFOUND" ) - set(CLANG_FORMAT_FOUND 0) - message("clang-format not found") -else() - set(CLANG_FORMAT_FOUND 1) - message("clang-format found at ${CLANG_FORMAT_BIN}") -endif() - diff --git a/core/cmake/ThirdPartyPackages.cmake b/core/cmake/ThirdPartyPackages.cmake deleted file mode 100644 index 09b8fafd11..0000000000 --- a/core/cmake/ThirdPartyPackages.cmake +++ /dev/null @@ -1,1160 +0,0 @@ -# Copyright (C) 2019-2020 Zilliz. All rights reserved. -# -# Licensed 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 limitations under the License. - -set(MILVUS_THIRDPARTY_DEPENDENCIES - - GTest - MySQLPP - Prometheus - SQLite - SQLite_ORM - yaml-cpp - libunwind - gperftools - GRPC - ZLIB - Opentracing - fiu - AWS - oatpp) - -message(STATUS "Using ${MILVUS_DEPENDENCY_SOURCE} approach to find dependencies") - -# For each dependency, set dependency source to global default, if unset -foreach (DEPENDENCY ${MILVUS_THIRDPARTY_DEPENDENCIES}) - if ("${${DEPENDENCY}_SOURCE}" STREQUAL "") - set(${DEPENDENCY}_SOURCE ${MILVUS_DEPENDENCY_SOURCE}) - endif () -endforeach () - -macro(build_dependency DEPENDENCY_NAME) - if ("${DEPENDENCY_NAME}" STREQUAL "GTest") - build_gtest() - elseif ("${DEPENDENCY_NAME}" STREQUAL "MySQLPP") - build_mysqlpp() - elseif ("${DEPENDENCY_NAME}" STREQUAL "Prometheus") - build_prometheus() - elseif ("${DEPENDENCY_NAME}" STREQUAL "SQLite") - build_sqlite() - elseif ("${DEPENDENCY_NAME}" STREQUAL "SQLite_ORM") - build_sqlite_orm() - elseif ("${DEPENDENCY_NAME}" STREQUAL "yaml-cpp") - build_yamlcpp() - elseif ("${DEPENDENCY_NAME}" STREQUAL "libunwind") - build_libunwind() - elseif ("${DEPENDENCY_NAME}" STREQUAL "gperftools") - build_gperftools() - elseif ("${DEPENDENCY_NAME}" STREQUAL "GRPC") - build_grpc() - elseif ("${DEPENDENCY_NAME}" STREQUAL "ZLIB") - build_zlib() - elseif ("${DEPENDENCY_NAME}" STREQUAL "Opentracing") - build_opentracing() - elseif ("${DEPENDENCY_NAME}" STREQUAL "fiu") - build_fiu() - elseif ("${DEPENDENCY_NAME}" STREQUAL "oatpp") - build_oatpp() - elseif("${DEPENDENCY_NAME}" STREQUAL "AWS") - build_aws() - else () - message(FATAL_ERROR "Unknown thirdparty dependency to build: ${DEPENDENCY_NAME}") - endif () -endmacro() - -# ---------------------------------------------------------------------- -# Identify OS -if (UNIX) - if (APPLE) - set(CMAKE_OS_NAME "osx" CACHE STRING "Operating system name" FORCE) - else (APPLE) - ## Check for Debian GNU/Linux ________________ - find_file(DEBIAN_FOUND debian_version debconf.conf - PATHS /etc - ) - if (DEBIAN_FOUND) - set(CMAKE_OS_NAME "debian" CACHE STRING "Operating system name" FORCE) - endif (DEBIAN_FOUND) - ## Check for Fedora _________________________ - find_file(FEDORA_FOUND fedora-release - PATHS /etc - ) - if (FEDORA_FOUND) - set(CMAKE_OS_NAME "fedora" CACHE STRING "Operating system name" FORCE) - endif (FEDORA_FOUND) - ## Check for RedHat _________________________ - find_file(REDHAT_FOUND redhat-release inittab.RH - PATHS /etc - ) - if (REDHAT_FOUND) - set(CMAKE_OS_NAME "redhat" CACHE STRING "Operating system name" FORCE) - endif (REDHAT_FOUND) - ## Extra check for Ubuntu ____________________ - if (DEBIAN_FOUND) - ## At its core Ubuntu is a Debian system, with - ## a slightly altered configuration; hence from - ## a first superficial inspection a system will - ## be considered as Debian, which signifies an - ## extra check is required. - find_file(UBUNTU_EXTRA legal issue - PATHS /etc - ) - if (UBUNTU_EXTRA) - ## Scan contents of file - file(STRINGS ${UBUNTU_EXTRA} UBUNTU_FOUND - REGEX Ubuntu - ) - ## Check result of string search - if (UBUNTU_FOUND) - set(CMAKE_OS_NAME "ubuntu" CACHE STRING "Operating system name" FORCE) - set(DEBIAN_FOUND FALSE) - - find_program(LSB_RELEASE_EXEC lsb_release) - execute_process(COMMAND ${LSB_RELEASE_EXEC} -rs - OUTPUT_VARIABLE LSB_RELEASE_ID_SHORT - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - STRING(REGEX REPLACE "\\." "_" UBUNTU_VERSION "${LSB_RELEASE_ID_SHORT}") - endif (UBUNTU_FOUND) - endif (UBUNTU_EXTRA) - endif (DEBIAN_FOUND) - endif (APPLE) -endif (UNIX) - -# ---------------------------------------------------------------------- -# thirdparty directory -set(THIRDPARTY_DIR "${MILVUS_SOURCE_DIR}/thirdparty") - -macro(resolve_dependency DEPENDENCY_NAME) - if (${DEPENDENCY_NAME}_SOURCE STREQUAL "AUTO") - find_package(${DEPENDENCY_NAME} MODULE) - if (NOT ${${DEPENDENCY_NAME}_FOUND}) - build_dependency(${DEPENDENCY_NAME}) - endif () - elseif (${DEPENDENCY_NAME}_SOURCE STREQUAL "BUNDLED") - build_dependency(${DEPENDENCY_NAME}) - elseif (${DEPENDENCY_NAME}_SOURCE STREQUAL "SYSTEM") - find_package(${DEPENDENCY_NAME} REQUIRED) - endif () -endmacro() - -# ---------------------------------------------------------------------- -# ExternalProject options - -string(TOUPPER ${CMAKE_BUILD_TYPE} UPPERCASE_BUILD_TYPE) - -set(EP_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${UPPERCASE_BUILD_TYPE}}") -set(EP_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${UPPERCASE_BUILD_TYPE}}") - -# Set -fPIC on all external projects -set(EP_CXX_FLAGS "${EP_CXX_FLAGS} -fPIC") -set(EP_C_FLAGS "${EP_C_FLAGS} -fPIC") - -# CC/CXX environment variables are captured on the first invocation of the -# builder (e.g make or ninja) instead of when CMake is invoked into to build -# directory. This leads to issues if the variables are exported in a subshell -# and the invocation of make/ninja is in distinct subshell without the same -# environment (CC/CXX). -set(EP_COMMON_TOOLCHAIN -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} - -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}) - -if (CMAKE_AR) - set(EP_COMMON_TOOLCHAIN ${EP_COMMON_TOOLCHAIN} -DCMAKE_AR=${CMAKE_AR}) -endif () - -if (CMAKE_RANLIB) - set(EP_COMMON_TOOLCHAIN ${EP_COMMON_TOOLCHAIN} -DCMAKE_RANLIB=${CMAKE_RANLIB}) -endif () - -# External projects are still able to override the following declarations. -# cmake command line will favor the last defined variable when a duplicate is -# encountered. This requires that `EP_COMMON_CMAKE_ARGS` is always the first -# argument. -set(EP_COMMON_CMAKE_ARGS - ${EP_COMMON_TOOLCHAIN} - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} - -DCMAKE_C_FLAGS=${EP_C_FLAGS} - -DCMAKE_C_FLAGS_${UPPERCASE_BUILD_TYPE}=${EP_C_FLAGS} - -DCMAKE_CXX_FLAGS=${EP_CXX_FLAGS} - -DCMAKE_CXX_FLAGS_${UPPERCASE_BUILD_TYPE}=${EP_CXX_FLAGS}) - -if (NOT MILVUS_VERBOSE_THIRDPARTY_BUILD) - set(EP_LOG_OPTIONS LOG_CONFIGURE 1 LOG_BUILD 1 LOG_INSTALL 1 LOG_DOWNLOAD 1) -else () - set(EP_LOG_OPTIONS) -endif () - -# Ensure that a default make is set -if ("${MAKE}" STREQUAL "") - find_program(MAKE make) -endif () - -if (NOT DEFINED MAKE_BUILD_ARGS) - set(MAKE_BUILD_ARGS "-j8") -endif () -message(STATUS "Third Party MAKE_BUILD_ARGS = ${MAKE_BUILD_ARGS}") - -# ---------------------------------------------------------------------- -# Find pthreads - -set(THREADS_PREFER_PTHREAD_FLAG ON) -find_package(Threads REQUIRED) - -# ---------------------------------------------------------------------- -# Versions and URLs for toolchain builds, which also can be used to configure -# offline builds - -# Read toolchain versions from cpp/thirdparty/versions.txt -file(STRINGS "${THIRDPARTY_DIR}/versions.txt" TOOLCHAIN_VERSIONS_TXT) -foreach (_VERSION_ENTRY ${TOOLCHAIN_VERSIONS_TXT}) - # Exclude comments - if (NOT _VERSION_ENTRY MATCHES "^[^#][A-Za-z0-9-_]+_VERSION=") - continue() - endif () - - string(REGEX MATCH "^[^=]*" _LIB_NAME ${_VERSION_ENTRY}) - string(REPLACE "${_LIB_NAME}=" "" _LIB_VERSION ${_VERSION_ENTRY}) - - # Skip blank or malformed lines - if (${_LIB_VERSION} STREQUAL "") - continue() - endif () - - # For debugging - #message(STATUS "${_LIB_NAME}: ${_LIB_VERSION}") - - set(${_LIB_NAME} "${_LIB_VERSION}") -endforeach () - -if (DEFINED ENV{MILVUS_GTEST_URL}) - set(GTEST_SOURCE_URL "$ENV{MILVUS_GTEST_URL}") -else () - set(GTEST_SOURCE_URL - "https://github.com/google/googletest/archive/release-${GTEST_VERSION}.tar.gz" - "https://gitee.com/quicksilver/googletest/repository/archive/release-${GTEST_VERSION}.zip") -endif () - -if (DEFINED ENV{MILVUS_MYSQLPP_URL}) - set(MYSQLPP_SOURCE_URL "$ENV{MILVUS_MYSQLPP_URL}") -else () - set(MYSQLPP_SOURCE_URL "https://tangentsoft.com/mysqlpp/releases/mysql++-${MYSQLPP_VERSION}.tar.gz") -endif () - -if (DEFINED ENV{MILVUS_PROMETHEUS_URL}) - set(PROMETHEUS_SOURCE_URL "$ENV{PROMETHEUS_OPENBLAS_URL}") -else () - set(PROMETHEUS_SOURCE_URL - https://github.com/jupp0r/prometheus-cpp.git) -endif () - -if (DEFINED ENV{MILVUS_SQLITE_URL}) - set(SQLITE_SOURCE_URL "$ENV{MILVUS_SQLITE_URL}") -else () - set(SQLITE_SOURCE_URL - "https://www.sqlite.org/2019/sqlite-autoconf-${SQLITE_VERSION}.tar.gz") -endif () - -if (DEFINED ENV{MILVUS_SQLITE_ORM_URL}) - set(SQLITE_ORM_SOURCE_URLS "$ENV{MILVUS_SQLITE_ORM_URL}") -else () - set(SQLITE_ORM_SOURCE_URLS - "https://github.com/fnc12/sqlite_orm/archive/${SQLITE_ORM_VERSION}.zip" - "https://gitee.com/quicksilver/sqlite_orm/repository/archive/${SQLITE_ORM_VERSION}.zip") -endif () - -if (DEFINED ENV{MILVUS_YAMLCPP_URL}) - set(YAMLCPP_SOURCE_URL "$ENV{MILVUS_YAMLCPP_URL}") -else () - set(YAMLCPP_SOURCE_URL "https://github.com/jbeder/yaml-cpp/archive/yaml-cpp-${YAMLCPP_VERSION}.tar.gz" - "https://gitee.com/quicksilver/yaml-cpp/repository/archive/yaml-cpp-${YAMLCPP_VERSION}.zip") -endif () - -if (DEFINED ENV{MILVUS_LIBUNWIND_URL}) - set(LIBUNWIND_SOURCE_URL "$ENV{MILVUS_LIBUNWIND_URL}") -else () - set(LIBUNWIND_SOURCE_URL - "https://github.com/libunwind/libunwind/releases/download/v${LIBUNWIND_VERSION}/libunwind-${LIBUNWIND_VERSION}.tar.gz") -endif () - -if (DEFINED ENV{MILVUS_GPERFTOOLS_URL}) - set(GPERFTOOLS_SOURCE_URL "$ENV{MILVUS_GPERFTOOLS_URL}") -else () - set(GPERFTOOLS_SOURCE_URL - "https://github.com/gperftools/gperftools/releases/download/gperftools-${GPERFTOOLS_VERSION}/gperftools-${GPERFTOOLS_VERSION}.tar.gz") -endif () - -if (DEFINED ENV{MILVUS_GRPC_URL}) - set(GRPC_SOURCE_URL "$ENV{MILVUS_GRPC_URL}") -else () - set(GRPC_SOURCE_URL - "https://github.com/milvus-io/grpc-milvus/archive/${GRPC_VERSION}.zip" - #"https://github.com/youny626/grpc-milvus/archive/${GRPC_VERSION}.zip" - #"https://gitee.com/quicksilver/grpc-milvus/repository/archive/${GRPC_VERSION}.zip" - ) -endif () - -if (DEFINED ENV{MILVUS_ZLIB_URL}) - set(ZLIB_SOURCE_URL "$ENV{MILVUS_ZLIB_URL}") -else () - set(ZLIB_SOURCE_URL "https://github.com/madler/zlib/archive/${ZLIB_VERSION}.tar.gz" - "https://gitee.com/quicksilver/zlib/repository/archive/${ZLIB_VERSION}.zip") -endif () - -if (DEFINED ENV{MILVUS_OPENTRACING_URL}) - set(OPENTRACING_SOURCE_URL "$ENV{MILVUS_OPENTRACING_URL}") -else () - set(OPENTRACING_SOURCE_URL "https://github.com/opentracing/opentracing-cpp/archive/${OPENTRACING_VERSION}.tar.gz" - "https://gitee.com/quicksilver/opentracing-cpp/repository/archive/${OPENTRACING_VERSION}.zip") -endif () - -if (DEFINED ENV{MILVUS_FIU_URL}) - set(FIU_SOURCE_URL "$ENV{MILVUS_FIU_URL}") -else () - set(FIU_SOURCE_URL "https://github.com/albertito/libfiu/archive/${FIU_VERSION}.tar.gz" - "https://gitee.com/quicksilver/libfiu/repository/archive/${FIU_VERSION}.zip") -endif () - -if (DEFINED ENV{MILVUS_OATPP_URL}) - set(OATPP_SOURCE_URL "$ENV{MILVUS_OATPP_URL}") -else () - # set(OATPP_SOURCE_URL "https://github.com/oatpp/oatpp/archive/${OATPP_VERSION}.tar.gz") - set(OATPP_SOURCE_URL "https://github.com/BossZou/oatpp/archive/${OATPP_VERSION}.zip") -endif () - -if (DEFINED ENV{MILVUS_AWS_URL}) - set(AWS_SOURCE_URL "$ENV{MILVUS_AWS_URL}") -else () - set(AWS_SOURCE_URL "https://github.com/aws/aws-sdk-cpp/archive/${AWS_VERSION}.tar.gz") -endif () - -# ---------------------------------------------------------------------- -# Google gtest - -macro(build_gtest) - message(STATUS "Building gtest-${GTEST_VERSION} from source") - set(GTEST_VENDORED TRUE) - set(GTEST_CMAKE_CXX_FLAGS "${EP_CXX_FLAGS}") - - if (APPLE) - set(GTEST_CMAKE_CXX_FLAGS - ${GTEST_CMAKE_CXX_FLAGS} - -DGTEST_USE_OWN_TR1_TUPLE=1 - -Wno-unused-value - -Wno-ignored-attributes) - endif () - - set(GTEST_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/googletest_ep-prefix/src/googletest_ep") - set(GTEST_INCLUDE_DIR "${GTEST_PREFIX}/include") - set(GTEST_STATIC_LIB - "${GTEST_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(GTEST_MAIN_STATIC_LIB - "${GTEST_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}") - - set(GTEST_CMAKE_ARGS - ${EP_COMMON_CMAKE_ARGS} - "-DCMAKE_INSTALL_PREFIX=${GTEST_PREFIX}" - "-DCMAKE_INSTALL_LIBDIR=lib" - -DCMAKE_CXX_FLAGS=${GTEST_CMAKE_CXX_FLAGS} - -DCMAKE_BUILD_TYPE=Release) - - set(GMOCK_INCLUDE_DIR "${GTEST_PREFIX}/include") - set(GMOCK_STATIC_LIB - "${GTEST_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}gmock${CMAKE_STATIC_LIBRARY_SUFFIX}" - ) - - ExternalProject_Add(googletest_ep - URL - ${GTEST_SOURCE_URL} - BUILD_COMMAND - ${MAKE} - ${MAKE_BUILD_ARGS} - BUILD_BYPRODUCTS - ${GTEST_STATIC_LIB} - ${GTEST_MAIN_STATIC_LIB} - ${GMOCK_STATIC_LIB} - CMAKE_ARGS - ${GTEST_CMAKE_ARGS} - ${EP_LOG_OPTIONS}) - - # The include directory must exist before it is referenced by a target. - file(MAKE_DIRECTORY "${GTEST_INCLUDE_DIR}") - - add_library(gtest STATIC IMPORTED) - set_target_properties(gtest - PROPERTIES IMPORTED_LOCATION "${GTEST_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}") - - add_library(gtest_main STATIC IMPORTED) - set_target_properties(gtest_main - PROPERTIES IMPORTED_LOCATION "${GTEST_MAIN_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}") - - add_library(gmock STATIC IMPORTED) - set_target_properties(gmock - PROPERTIES IMPORTED_LOCATION "${GMOCK_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}") - - add_dependencies(gtest googletest_ep) - add_dependencies(gtest_main googletest_ep) - add_dependencies(gmock googletest_ep) - -endmacro() - -if (MILVUS_BUILD_TESTS) - resolve_dependency(GTest) - - if (NOT GTEST_VENDORED) - endif () - - get_target_property(GTEST_INCLUDE_DIR gtest INTERFACE_INCLUDE_DIRECTORIES) - link_directories(SYSTEM "${GTEST_PREFIX}/lib") - include_directories(SYSTEM ${GTEST_INCLUDE_DIR}) -endif () - -# ---------------------------------------------------------------------- -# MySQL++ - -macro(build_mysqlpp) - message(STATUS "Building MySQL++-${MYSQLPP_VERSION} from source") - set(MYSQLPP_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/mysqlpp_ep-prefix/src/mysqlpp_ep") - set(MYSQLPP_INCLUDE_DIR "${MYSQLPP_PREFIX}/include") - set(MYSQLPP_SHARED_LIB - "${MYSQLPP_PREFIX}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}mysqlpp${CMAKE_SHARED_LIBRARY_SUFFIX}") - - set(MYSQLPP_CONFIGURE_ARGS - "--prefix=${MYSQLPP_PREFIX}" - "--enable-thread-check" - "CFLAGS=${EP_C_FLAGS}" - "CXXFLAGS=${EP_CXX_FLAGS}" - "LDFLAGS=-pthread") - - externalproject_add(mysqlpp_ep - URL - ${MYSQLPP_SOURCE_URL} - ${EP_LOG_OPTIONS} - CONFIGURE_COMMAND - "./configure" - ${MYSQLPP_CONFIGURE_ARGS} - BUILD_COMMAND - ${MAKE} ${MAKE_BUILD_ARGS} - BUILD_IN_SOURCE - 1 - BUILD_BYPRODUCTS - ${MYSQLPP_SHARED_LIB}) - - file(MAKE_DIRECTORY "${MYSQLPP_INCLUDE_DIR}") - add_library(mysqlpp SHARED IMPORTED) - set_target_properties( - mysqlpp - PROPERTIES - IMPORTED_LOCATION "${MYSQLPP_SHARED_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${MYSQLPP_INCLUDE_DIR}") - - add_dependencies(mysqlpp mysqlpp_ep) - -endmacro() - -if (MILVUS_WITH_MYSQLPP) - - resolve_dependency(MySQLPP) - get_target_property(MYSQLPP_INCLUDE_DIR mysqlpp INTERFACE_INCLUDE_DIRECTORIES) - include_directories(SYSTEM "${MYSQLPP_INCLUDE_DIR}") - link_directories(SYSTEM ${MYSQLPP_PREFIX}/lib) -endif () - -# ---------------------------------------------------------------------- -# Prometheus - -macro(build_prometheus) - message(STATUS "Building Prometheus-${PROMETHEUS_VERSION} from source") - set(PROMETHEUS_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/prometheus_ep-prefix/src/prometheus_ep") - set(PROMETHEUS_STATIC_LIB_NAME prometheus-cpp) - set(PROMETHEUS_CORE_STATIC_LIB - "${PROMETHEUS_PREFIX}/core/${CMAKE_STATIC_LIBRARY_PREFIX}${PROMETHEUS_STATIC_LIB_NAME}-core${CMAKE_STATIC_LIBRARY_SUFFIX}" - ) - set(PROMETHEUS_PUSH_STATIC_LIB - "${PROMETHEUS_PREFIX}/push/${CMAKE_STATIC_LIBRARY_PREFIX}${PROMETHEUS_STATIC_LIB_NAME}-push${CMAKE_STATIC_LIBRARY_SUFFIX}" - ) - set(PROMETHEUS_PULL_STATIC_LIB - "${PROMETHEUS_PREFIX}/pull/${CMAKE_STATIC_LIBRARY_PREFIX}${PROMETHEUS_STATIC_LIB_NAME}-pull${CMAKE_STATIC_LIBRARY_SUFFIX}" - ) - - set(PROMETHEUS_CMAKE_ARGS - ${EP_COMMON_CMAKE_ARGS} - -DCMAKE_INSTALL_LIBDIR=lib - -DBUILD_SHARED_LIBS=OFF - "-DCMAKE_INSTALL_PREFIX=${PROMETHEUS_PREFIX}" - -DCMAKE_BUILD_TYPE=Release) - - externalproject_add(prometheus_ep - GIT_REPOSITORY - ${PROMETHEUS_SOURCE_URL} - GIT_TAG - ${PROMETHEUS_VERSION} - GIT_SHALLOW - TRUE - ${EP_LOG_OPTIONS} - CMAKE_ARGS - ${PROMETHEUS_CMAKE_ARGS} - BUILD_COMMAND - ${MAKE} - ${MAKE_BUILD_ARGS} - BUILD_IN_SOURCE - 1 - INSTALL_COMMAND - ${MAKE} - "DESTDIR=${PROMETHEUS_PREFIX}" - install - BUILD_BYPRODUCTS - "${PROMETHEUS_CORE_STATIC_LIB}" - "${PROMETHEUS_PUSH_STATIC_LIB}" - "${PROMETHEUS_PULL_STATIC_LIB}") - - file(MAKE_DIRECTORY "${PROMETHEUS_PREFIX}/push/include") - add_library(prometheus-cpp-push STATIC IMPORTED) - set_target_properties(prometheus-cpp-push - PROPERTIES IMPORTED_LOCATION "${PROMETHEUS_PUSH_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${PROMETHEUS_PREFIX}/push/include") - add_dependencies(prometheus-cpp-push prometheus_ep) - - file(MAKE_DIRECTORY "${PROMETHEUS_PREFIX}/pull/include") - add_library(prometheus-cpp-pull STATIC IMPORTED) - set_target_properties(prometheus-cpp-pull - PROPERTIES IMPORTED_LOCATION "${PROMETHEUS_PULL_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${PROMETHEUS_PREFIX}/pull/include") - add_dependencies(prometheus-cpp-pull prometheus_ep) - - file(MAKE_DIRECTORY "${PROMETHEUS_PREFIX}/core/include") - add_library(prometheus-cpp-core STATIC IMPORTED) - set_target_properties(prometheus-cpp-core - PROPERTIES IMPORTED_LOCATION "${PROMETHEUS_CORE_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${PROMETHEUS_PREFIX}/core/include") - add_dependencies(prometheus-cpp-core prometheus_ep) -endmacro() - -if (MILVUS_WITH_PROMETHEUS) - - resolve_dependency(Prometheus) - - link_directories(SYSTEM ${PROMETHEUS_PREFIX}/push/) - include_directories(SYSTEM ${PROMETHEUS_PREFIX}/push/include) - - link_directories(SYSTEM ${PROMETHEUS_PREFIX}/pull/) - include_directories(SYSTEM ${PROMETHEUS_PREFIX}/pull/include) - - link_directories(SYSTEM ${PROMETHEUS_PREFIX}/core/) - include_directories(SYSTEM ${PROMETHEUS_PREFIX}/core/include) - -endif () - -# ---------------------------------------------------------------------- -# SQLite - -macro(build_sqlite) - message(STATUS "Building SQLITE-${SQLITE_VERSION} from source") - set(SQLITE_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/sqlite_ep-prefix/src/sqlite_ep") - set(SQLITE_INCLUDE_DIR "${SQLITE_PREFIX}/include") - set(SQLITE_STATIC_LIB - "${SQLITE_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}sqlite3${CMAKE_STATIC_LIBRARY_SUFFIX}") - - set(SQLITE_CONFIGURE_ARGS - "--prefix=${SQLITE_PREFIX}" - "CC=${CMAKE_C_COMPILER}" - "CXX=${CMAKE_CXX_COMPILER}" - "CFLAGS=${EP_C_FLAGS}" - "CXXFLAGS=${EP_CXX_FLAGS}") - - externalproject_add(sqlite_ep - URL - ${SQLITE_SOURCE_URL} - ${EP_LOG_OPTIONS} - CONFIGURE_COMMAND - "./configure" - ${SQLITE_CONFIGURE_ARGS} - BUILD_COMMAND - ${MAKE} - ${MAKE_BUILD_ARGS} - BUILD_IN_SOURCE - 1 - BUILD_BYPRODUCTS - "${SQLITE_STATIC_LIB}") - - file(MAKE_DIRECTORY "${SQLITE_INCLUDE_DIR}") - add_library(sqlite STATIC IMPORTED) - set_target_properties( - sqlite - PROPERTIES IMPORTED_LOCATION "${SQLITE_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${SQLITE_INCLUDE_DIR}") - - add_dependencies(sqlite sqlite_ep) -endmacro() - -if (MILVUS_WITH_SQLITE) - resolve_dependency(SQLite) - include_directories(SYSTEM "${SQLITE_INCLUDE_DIR}") - link_directories(SYSTEM ${SQLITE_PREFIX}/lib/) -endif () - -# ---------------------------------------------------------------------- -# SQLite_ORM - -macro(build_sqlite_orm) - message(STATUS "Building SQLITE_ORM-${SQLITE_ORM_VERSION} from source") - - set(SQLITE_ORM_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/sqlite_orm_ep-prefix") - set(SQLITE_ORM_TAR_NAME "${SQLITE_ORM_PREFIX}/sqlite_orm-${SQLITE_ORM_VERSION}.tar.gz") - set(SQLITE_ORM_INCLUDE_DIR "${SQLITE_ORM_PREFIX}/sqlite_orm-${SQLITE_ORM_VERSION}/include/sqlite_orm") - - if (NOT EXISTS ${SQLITE_ORM_INCLUDE_DIR}) - file(MAKE_DIRECTORY ${SQLITE_ORM_PREFIX}) - - set(IS_EXIST_FILE FALSE) - foreach(url ${SQLITE_ORM_SOURCE_URLS}) - file(DOWNLOAD ${url} - ${SQLITE_ORM_TAR_NAME} - TIMEOUT 60 - STATUS status - LOG log) - list(GET status 0 status_code) - list(GET status 1 status_string) - - if(status_code EQUAL 0) - message(STATUS "Downloading SQLITE_ORM ... done") - set(IS_EXIST_FILE TRUE) - break() - else() - string(APPEND logFailedURLs "error: downloading '${url}' failed - status_code: ${status_code} - status_string: ${status_string} - log: - --- LOG BEGIN --- - ${log} - --- LOG END --- - " - ) - endif() - endforeach() - - if(IS_EXIST_FILE STREQUAL "FALSE") - message(FATAL_ERROR "Each download failed! - ${logFailedURLs} - " - ) - endif() - execute_process(COMMAND ${CMAKE_COMMAND} -E tar -xf ${SQLITE_ORM_TAR_NAME} - WORKING_DIRECTORY ${SQLITE_ORM_PREFIX}) - - endif () - -endmacro() - -if (MILVUS_WITH_SQLITE_ORM) - resolve_dependency(SQLite_ORM) - include_directories(SYSTEM "${SQLITE_ORM_INCLUDE_DIR}") -endif () - -# ---------------------------------------------------------------------- -# yaml-cpp - -macro(build_yamlcpp) - message(STATUS "Building yaml-cpp-${YAMLCPP_VERSION} from source") - set(YAMLCPP_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/yaml-cpp_ep-prefix/src/yaml-cpp_ep") - set(YAMLCPP_STATIC_LIB "${YAMLCPP_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}yaml-cpp${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(YAMLCPP_INCLUDE_DIR "${YAMLCPP_PREFIX}/include") - set(YAMLCPP_CMAKE_ARGS - ${EP_COMMON_CMAKE_ARGS} - "-DCMAKE_INSTALL_PREFIX=${YAMLCPP_PREFIX}" - -DCMAKE_INSTALL_LIBDIR=lib - -DYAML_CPP_BUILD_TESTS=OFF - -DYAML_CPP_BUILD_TOOLS=OFF) - - externalproject_add(yaml-cpp_ep - URL - ${YAMLCPP_SOURCE_URL} - ${EP_LOG_OPTIONS} - BUILD_COMMAND - ${MAKE} - ${MAKE_BUILD_ARGS} - BUILD_BYPRODUCTS - "${YAMLCPP_STATIC_LIB}" - CMAKE_ARGS - ${YAMLCPP_CMAKE_ARGS}) - - file(MAKE_DIRECTORY "${YAMLCPP_INCLUDE_DIR}") - add_library(yaml-cpp STATIC IMPORTED) - set_target_properties(yaml-cpp - PROPERTIES IMPORTED_LOCATION "${YAMLCPP_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${YAMLCPP_INCLUDE_DIR}") - - add_dependencies(yaml-cpp yaml-cpp_ep) -endmacro() - -if (MILVUS_WITH_YAMLCPP) - resolve_dependency(yaml-cpp) - - get_target_property(YAMLCPP_INCLUDE_DIR yaml-cpp INTERFACE_INCLUDE_DIRECTORIES) - link_directories(SYSTEM ${YAMLCPP_PREFIX}/lib/) - include_directories(SYSTEM ${YAMLCPP_INCLUDE_DIR}) -endif () - -# ---------------------------------------------------------------------- -# libunwind - -macro(build_libunwind) - message(STATUS "Building libunwind-${LIBUNWIND_VERSION} from source") - set(LIBUNWIND_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/libunwind_ep-prefix/src/libunwind_ep/install") - set(LIBUNWIND_INCLUDE_DIR "${LIBUNWIND_PREFIX}/include") - set(LIBUNWIND_SHARED_LIB "${LIBUNWIND_PREFIX}/lib/libunwind${CMAKE_SHARED_LIBRARY_SUFFIX}") - set(LIBUNWIND_CONFIGURE_ARGS "--prefix=${LIBUNWIND_PREFIX}") - - externalproject_add(libunwind_ep - URL - ${LIBUNWIND_SOURCE_URL} - ${EP_LOG_OPTIONS} - CONFIGURE_COMMAND - "./configure" - ${LIBUNWIND_CONFIGURE_ARGS} - BUILD_COMMAND - ${MAKE} ${MAKE_BUILD_ARGS} - BUILD_IN_SOURCE - 1 - INSTALL_COMMAND - ${MAKE} install - BUILD_BYPRODUCTS - ${LIBUNWIND_SHARED_LIB}) - - file(MAKE_DIRECTORY "${LIBUNWIND_INCLUDE_DIR}") - - add_library(libunwind SHARED IMPORTED) - set_target_properties(libunwind - PROPERTIES IMPORTED_LOCATION "${LIBUNWIND_SHARED_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${LIBUNWIND_INCLUDE_DIR}") - - add_dependencies(libunwind libunwind_ep) -endmacro() - -if (MILVUS_WITH_LIBUNWIND) - resolve_dependency(libunwind) - - get_target_property(LIBUNWIND_INCLUDE_DIR libunwind INTERFACE_INCLUDE_DIRECTORIES) - include_directories(SYSTEM ${LIBUNWIND_INCLUDE_DIR}) -endif () - -# ---------------------------------------------------------------------- -# gperftools - -macro(build_gperftools) - message(STATUS "Building gperftools-${GPERFTOOLS_VERSION} from source") - set(GPERFTOOLS_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/gperftools_ep-prefix/src/gperftools_ep") - set(GPERFTOOLS_INCLUDE_DIR "${GPERFTOOLS_PREFIX}/include") - set(GPERFTOOLS_STATIC_LIB "${GPERFTOOLS_PREFIX}/lib/libprofiler${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(GPERFTOOLS_CONFIGURE_ARGS "--prefix=${GPERFTOOLS_PREFIX}") - - externalproject_add(gperftools_ep - URL - ${GPERFTOOLS_SOURCE_URL} - ${EP_LOG_OPTIONS} - CONFIGURE_COMMAND - "./configure" - ${GPERFTOOLS_CONFIGURE_ARGS} - BUILD_COMMAND - ${MAKE} ${MAKE_BUILD_ARGS} - BUILD_IN_SOURCE - 1 - INSTALL_COMMAND - ${MAKE} install - BUILD_BYPRODUCTS - ${GPERFTOOLS_STATIC_LIB}) - - ExternalProject_Add_StepDependencies(gperftools_ep build libunwind_ep) - - file(MAKE_DIRECTORY "${GPERFTOOLS_INCLUDE_DIR}") - - add_library(gperftools STATIC IMPORTED) - set_target_properties(gperftools - PROPERTIES IMPORTED_LOCATION "${GPERFTOOLS_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${GPERFTOOLS_INCLUDE_DIR}" - INTERFACE_LINK_LIBRARIES libunwind) - - add_dependencies(gperftools gperftools_ep) - add_dependencies(gperftools libunwind_ep) -endmacro() - -if (MILVUS_WITH_GPERFTOOLS) - resolve_dependency(gperftools) - - get_target_property(GPERFTOOLS_INCLUDE_DIR gperftools INTERFACE_INCLUDE_DIRECTORIES) - include_directories(SYSTEM ${GPERFTOOLS_INCLUDE_DIR}) - link_directories(SYSTEM ${GPERFTOOLS_PREFIX}/lib) -endif () - -# ---------------------------------------------------------------------- -# GRPC - -macro(build_grpc) - message(STATUS "Building GRPC-${GRPC_VERSION} from source") - set(GRPC_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/grpc_ep-prefix/src/grpc_ep/install") - set(GRPC_INCLUDE_DIR "${GRPC_PREFIX}/include") - set(GRPC_STATIC_LIB "${GRPC_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}grpc${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(GRPC++_STATIC_LIB "${GRPC_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}grpc++${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(GRPCPP_CHANNELZ_STATIC_LIB "${GRPC_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}grpcpp_channelz${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(GRPC_PROTOBUF_LIB_DIR "${CMAKE_CURRENT_BINARY_DIR}/grpc_ep-prefix/src/grpc_ep/libs/opt/protobuf") - set(GRPC_PROTOBUF_STATIC_LIB "${GRPC_PROTOBUF_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}protobuf${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(GRPC_PROTOC_STATIC_LIB "${GRPC_PROTOBUF_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}protoc${CMAKE_STATIC_LIBRARY_SUFFIX}") - - externalproject_add(grpc_ep - URL - ${GRPC_SOURCE_URL} - ${EP_LOG_OPTIONS} - CONFIGURE_COMMAND - "" - BUILD_IN_SOURCE - 1 - BUILD_COMMAND - ${MAKE} ${MAKE_BUILD_ARGS} prefix=${GRPC_PREFIX} - INSTALL_COMMAND - ${MAKE} install prefix=${GRPC_PREFIX} - BUILD_BYPRODUCTS - ${GRPC_STATIC_LIB} - ${GRPC++_STATIC_LIB} - ${GRPCPP_CHANNELZ_STATIC_LIB} - ${GRPC_PROTOBUF_STATIC_LIB} - ${GRPC_PROTOC_STATIC_LIB}) - - ExternalProject_Add_StepDependencies(grpc_ep build zlib_ep) - - file(MAKE_DIRECTORY "${GRPC_INCLUDE_DIR}") - - add_library(grpc STATIC IMPORTED) - set_target_properties(grpc - PROPERTIES IMPORTED_LOCATION "${GRPC_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${GRPC_INCLUDE_DIR}" - INTERFACE_LINK_LIBRARIES "zlib") - - add_library(grpc++ STATIC IMPORTED) - set_target_properties(grpc++ - PROPERTIES IMPORTED_LOCATION "${GRPC++_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${GRPC_INCLUDE_DIR}" - INTERFACE_LINK_LIBRARIES "zlib") - - add_library(grpcpp_channelz STATIC IMPORTED) - set_target_properties(grpcpp_channelz - PROPERTIES IMPORTED_LOCATION "${GRPCPP_CHANNELZ_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${GRPC_INCLUDE_DIR}" - INTERFACE_LINK_LIBRARIES "zlib") - - add_library(grpc_protobuf STATIC IMPORTED) - set_target_properties(grpc_protobuf - PROPERTIES IMPORTED_LOCATION "${GRPC_PROTOBUF_STATIC_LIB}" - INTERFACE_LINK_LIBRARIES "zlib") - - add_library(grpc_protoc STATIC IMPORTED) - set_target_properties(grpc_protoc - PROPERTIES IMPORTED_LOCATION "${GRPC_PROTOC_STATIC_LIB}" - INTERFACE_LINK_LIBRARIES "zlib") - - add_dependencies(grpc grpc_ep) - add_dependencies(grpc++ grpc_ep) - add_dependencies(grpcpp_channelz grpc_ep) - add_dependencies(grpc_protobuf grpc_ep) - add_dependencies(grpc_protoc grpc_ep) -endmacro() - -if (MILVUS_WITH_GRPC) - resolve_dependency(GRPC) - - get_target_property(GRPC_INCLUDE_DIR grpc INTERFACE_INCLUDE_DIRECTORIES) - include_directories(SYSTEM ${GRPC_INCLUDE_DIR}) - link_directories(SYSTEM ${GRPC_PREFIX}/lib) - - set(GRPC_THIRD_PARTY_DIR ${CMAKE_CURRENT_BINARY_DIR}/grpc_ep-prefix/src/grpc_ep/third_party) - include_directories(SYSTEM ${GRPC_THIRD_PARTY_DIR}/protobuf/src) - link_directories(SYSTEM ${GRPC_PROTOBUF_LIB_DIR}) -endif () - -# ---------------------------------------------------------------------- -# zlib - -macro(build_zlib) - message(STATUS "Building ZLIB-${ZLIB_VERSION} from source") - set(ZLIB_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/zlib_ep-prefix/src/zlib_ep") - set(ZLIB_STATIC_LIB_NAME libz.a) - set(ZLIB_STATIC_LIB "${ZLIB_PREFIX}/lib/${ZLIB_STATIC_LIB_NAME}") - set(ZLIB_INCLUDE_DIR "${ZLIB_PREFIX}/include") - set(ZLIB_CMAKE_ARGS ${EP_COMMON_CMAKE_ARGS} "-DCMAKE_INSTALL_PREFIX=${ZLIB_PREFIX}" - -DBUILD_SHARED_LIBS=OFF) - - externalproject_add(zlib_ep - URL - ${ZLIB_SOURCE_URL} - ${EP_LOG_OPTIONS} - BUILD_COMMAND - ${MAKE} - ${MAKE_BUILD_ARGS} - BUILD_BYPRODUCTS - "${ZLIB_STATIC_LIB}" - CMAKE_ARGS - ${ZLIB_CMAKE_ARGS}) - - file(MAKE_DIRECTORY "${ZLIB_INCLUDE_DIR}") - add_library(zlib STATIC IMPORTED) - set_target_properties(zlib - PROPERTIES IMPORTED_LOCATION "${ZLIB_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INCLUDE_DIR}") - - add_dependencies(zlib zlib_ep) -endmacro() - -if (MILVUS_WITH_ZLIB) - resolve_dependency(ZLIB) - - get_target_property(ZLIB_INCLUDE_DIR zlib INTERFACE_INCLUDE_DIRECTORIES) - include_directories(SYSTEM ${ZLIB_INCLUDE_DIR}) -endif () - -# ---------------------------------------------------------------------- -# opentracing - -macro(build_opentracing) - message(STATUS "Building OPENTRACING-${OPENTRACING_VERSION} from source") - set(OPENTRACING_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/opentracing_ep-prefix/src/opentracing_ep") - set(OPENTRACING_STATIC_LIB "${OPENTRACING_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentracing${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(OPENTRACING_MOCK_TRACER_STATIC_LIB "${OPENTRACING_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}opentracing_mocktracer${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(OPENTRACING_INCLUDE_DIR "${OPENTRACING_PREFIX}/include") - set(OPENTRACING_CMAKE_ARGS - ${EP_COMMON_CMAKE_ARGS} - "-DCMAKE_INSTALL_PREFIX=${OPENTRACING_PREFIX}" - -DBUILD_SHARED_LIBS=OFF) - - externalproject_add(opentracing_ep - URL - ${OPENTRACING_SOURCE_URL} - ${EP_LOG_OPTIONS} - CMAKE_ARGS - ${OPENTRACING_CMAKE_ARGS} - BUILD_COMMAND - ${MAKE} - ${MAKE_BUILD_ARGS} - BUILD_BYPRODUCTS - ${OPENTRACING_STATIC_LIB} - ${OPENTRACING_MOCK_TRACER_STATIC_LIB} - ) - - file(MAKE_DIRECTORY "${OPENTRACING_INCLUDE_DIR}") - add_library(opentracing STATIC IMPORTED) - set_target_properties(opentracing - PROPERTIES IMPORTED_LOCATION "${OPENTRACING_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${OPENTRACING_INCLUDE_DIR}") - - add_library(opentracing_mocktracer STATIC IMPORTED) - set_target_properties(opentracing_mocktracer - PROPERTIES IMPORTED_LOCATION "${OPENTRACING_MOCK_TRACER_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${OPENTRACING_INCLUDE_DIR}") - - add_dependencies(opentracing opentracing_ep) - add_dependencies(opentracing_mocktracer opentracing_ep) -endmacro() - -if (MILVUS_WITH_OPENTRACING) - resolve_dependency(Opentracing) - - get_target_property(OPENTRACING_INCLUDE_DIR opentracing INTERFACE_INCLUDE_DIRECTORIES) - include_directories(SYSTEM ${OPENTRACING_INCLUDE_DIR}) -endif () - -# ---------------------------------------------------------------------- -# fiu -macro(build_fiu) - message(STATUS "Building FIU-${FIU_VERSION} from source") - set(FIU_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/fiu_ep-prefix/src/fiu_ep") - set(FIU_SHARED_LIB "${FIU_PREFIX}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}fiu${CMAKE_SHARED_LIBRARY_SUFFIX}") - set(FIU_INCLUDE_DIR "${FIU_PREFIX}/include") - - externalproject_add(fiu_ep - URL - ${FIU_SOURCE_URL} - ${EP_LOG_OPTIONS} - CONFIGURE_COMMAND - "" - BUILD_IN_SOURCE - 1 - BUILD_COMMAND - ${MAKE} - ${MAKE_BUILD_ARGS} - INSTALL_COMMAND - ${MAKE} - "PREFIX=${FIU_PREFIX}" - install - BUILD_BYPRODUCTS - ${FIU_SHARED_LIB} - ) - - file(MAKE_DIRECTORY "${FIU_INCLUDE_DIR}") - add_library(fiu SHARED IMPORTED) - set_target_properties(fiu - PROPERTIES IMPORTED_LOCATION "${FIU_SHARED_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${FIU_INCLUDE_DIR}") - - add_dependencies(fiu fiu_ep) -endmacro() - -resolve_dependency(fiu) - -get_target_property(FIU_INCLUDE_DIR fiu INTERFACE_INCLUDE_DIRECTORIES) -include_directories(SYSTEM ${FIU_INCLUDE_DIR}) - -# ---------------------------------------------------------------------- -# oatpp -macro(build_oatpp) - message(STATUS "Building oatpp-${OATPP_VERSION} from source") - set(OATPP_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/oatpp_ep-prefix/src/oatpp_ep") - set(OATPP_STATIC_LIB "${OATPP_PREFIX}/lib/oatpp-${OATPP_VERSION}/${CMAKE_STATIC_LIBRARY_PREFIX}oatpp${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(OATPP_INCLUDE_DIR "${OATPP_PREFIX}/include/oatpp-${OATPP_VERSION}/oatpp") - set(OATPP_DIR_SRC "${OATPP_PREFIX}/src") - set(OATPP_DIR_LIB "${OATPP_PREFIX}/lib") - - set(OATPP_CMAKE_ARGS - ${EP_COMMON_CMAKE_ARGS} - "-DCMAKE_INSTALL_PREFIX=${OATPP_PREFIX}" - -DCMAKE_INSTALL_LIBDIR=lib - -DBUILD_SHARED_LIBS=OFF - -DOATPP_BUILD_TESTS=OFF - -DOATPP_DISABLE_LOGV=ON - -DOATPP_DISABLE_LOGD=ON - -DOATPP_DISABLE_LOGI=ON - ) - - - externalproject_add(oatpp_ep - URL - ${OATPP_SOURCE_URL} - ${EP_LOG_OPTIONS} - CMAKE_ARGS - ${OATPP_CMAKE_ARGS} - BUILD_COMMAND - ${MAKE} - ${MAKE_BUILD_ARGS} - BUILD_BYPRODUCTS - ${OATPP_STATIC_LIB} - ) - - file(MAKE_DIRECTORY "${OATPP_INCLUDE_DIR}") - add_library(oatpp STATIC IMPORTED) - set_target_properties(oatpp - PROPERTIES IMPORTED_LOCATION "${OATPP_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${OATPP_INCLUDE_DIR}") - - add_dependencies(oatpp oatpp_ep) -endmacro() - -if (MILVUS_WITH_OATPP) - resolve_dependency(oatpp) - - get_target_property(OATPP_INCLUDE_DIR oatpp INTERFACE_INCLUDE_DIRECTORIES) - include_directories(SYSTEM ${OATPP_INCLUDE_DIR}) -endif () - -# ---------------------------------------------------------------------- -# aws -macro(build_aws) - message(STATUS "Building aws-${AWS_VERSION} from source") - set(AWS_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/aws_ep-prefix/src/aws_ep") - - set(AWS_CMAKE_ARGS - ${EP_COMMON_TOOLCHAIN} - "-DCMAKE_INSTALL_PREFIX=${AWS_PREFIX}" - -DCMAKE_BUILD_TYPE=Release - -DCMAKE_INSTALL_LIBDIR=lib - -DBUILD_ONLY=s3 - -DBUILD_SHARED_LIBS=off - -DENABLE_TESTING=off - -DENABLE_UNITY_BUILD=on - -DNO_ENCRYPTION=off) - - set(AWS_CPP_SDK_CORE_STATIC_LIB - "${AWS_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}aws-cpp-sdk-core${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(AWS_CPP_SDK_S3_STATIC_LIB - "${AWS_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}aws-cpp-sdk-s3${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(AWS_INCLUDE_DIR "${AWS_PREFIX}/include") - set(AWS_CMAKE_ARGS - ${AWS_CMAKE_ARGS} - -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} - -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} - -DCMAKE_C_FLAGS=${EP_C_FLAGS} - -DCMAKE_CXX_FLAGS=${EP_CXX_FLAGS}) - - externalproject_add(aws_ep - ${EP_LOG_OPTIONS} - CMAKE_ARGS - ${AWS_CMAKE_ARGS} - BUILD_COMMAND - ${MAKE} - ${MAKE_BUILD_ARGS} - INSTALL_DIR - ${AWS_PREFIX} - URL - ${AWS_SOURCE_URL} - BUILD_BYPRODUCTS - "${AWS_CPP_SDK_S3_STATIC_LIB}" - "${AWS_CPP_SDK_CORE_STATIC_LIB}") - - file(MAKE_DIRECTORY "${AWS_INCLUDE_DIR}") - add_library(aws-cpp-sdk-s3 STATIC IMPORTED) - add_library(aws-cpp-sdk-core STATIC IMPORTED) - - set_target_properties(aws-cpp-sdk-s3 - PROPERTIES - IMPORTED_LOCATION "${AWS_CPP_SDK_S3_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${AWS_INCLUDE_DIR}" - ) - - set_target_properties(aws-cpp-sdk-core - PROPERTIES - IMPORTED_LOCATION "${AWS_CPP_SDK_CORE_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${AWS_INCLUDE_DIR}" - ) - - if(REDHAT_FOUND) - set_target_properties(aws-cpp-sdk-s3 - PROPERTIES - INTERFACE_LINK_LIBRARIES - "${AWS_PREFIX}/lib64/libaws-c-event-stream.a;${AWS_PREFIX}/lib64/libaws-checksums.a;${AWS_PREFIX}/lib64/libaws-c-common.a") - set_target_properties(aws-cpp-sdk-core - PROPERTIES - INTERFACE_LINK_LIBRARIES - "${AWS_PREFIX}/lib64/libaws-c-event-stream.a;${AWS_PREFIX}/lib64/libaws-checksums.a;${AWS_PREFIX}/lib64/libaws-c-common.a") - else() - set_target_properties(aws-cpp-sdk-s3 - PROPERTIES - INTERFACE_LINK_LIBRARIES - "${AWS_PREFIX}/lib/libaws-c-event-stream.a;${AWS_PREFIX}/lib/libaws-checksums.a;${AWS_PREFIX}/lib/libaws-c-common.a") - set_target_properties(aws-cpp-sdk-core - PROPERTIES - INTERFACE_LINK_LIBRARIES - "${AWS_PREFIX}/lib/libaws-c-event-stream.a;${AWS_PREFIX}/lib/libaws-checksums.a;${AWS_PREFIX}/lib/libaws-c-common.a") - endif() - - add_dependencies(aws-cpp-sdk-s3 aws_ep) - add_dependencies(aws-cpp-sdk-core aws_ep) - -endmacro() - -if(MILVUS_WITH_AWS) - resolve_dependency(AWS) - - link_directories(SYSTEM ${AWS_PREFIX}/lib) - - get_target_property(AWS_CPP_SDK_S3_INCLUDE_DIR aws-cpp-sdk-s3 INTERFACE_INCLUDE_DIRECTORIES) - include_directories(SYSTEM ${AWS_CPP_SDK_S3_INCLUDE_DIR}) - - get_target_property(AWS_CPP_SDK_CORE_INCLUDE_DIR aws-cpp-sdk-core INTERFACE_INCLUDE_DIRECTORIES) - include_directories(SYSTEM ${AWS_CPP_SDK_CORE_INCLUDE_DIR}) - -endif() diff --git a/core/conf/demo/server_config.yaml b/core/conf/demo/server_config.yaml deleted file mode 100644 index 743f15956e..0000000000 --- a/core/conf/demo/server_config.yaml +++ /dev/null @@ -1,189 +0,0 @@ -# Copyright (C) 2019-2020 Zilliz. All rights reserved. -# -# Licensed 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 limitations under the License. - -version: 0.5 - -#----------------------+------------------------------------------------------------+------------+-----------------+ -# Cluster Config | Description | Type | Default | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# enable | If runinng with Mishards, set true, otherwise false. | Boolean | false | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# role | Milvus deployment role: rw / ro | role | rw | -#----------------------+------------------------------------------------------------+------------+-----------------+ -cluster: - enable: false - role: rw - -#----------------------+------------------------------------------------------------+------------+-----------------+ -# General Config | Description | Type | Default | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# time_zone | Use UTC-x or UTC+x to specify a time zone. | Timezone | UTC+8 | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# meta_uri | URI for metadata storage, using SQLite (for single server | URL | sqlite://:@:/ | -# | Milvus) or MySQL (for distributed cluster Milvus). | | | -# | Format: dialect://username:password@host:port/database | | | -# | Keep 'dialect://:@:/', 'dialect' can be either 'sqlite' or | | | -# | 'mysql', replace other texts with real values. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -general: - timezone: UTC+8 - meta_uri: sqlite://:@:/ - -#----------------------+------------------------------------------------------------+------------+-----------------+ -# Network Config | Description | Type | Default | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# bind.address | IP address that Milvus server monitors. | IP | 0.0.0.0 | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# bind.port | Port that Milvus server monitors. Port range (1024, 65535) | Integer | 19530 | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# http.enable | Enable web server or not. | Boolean | true | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# http.port | Port that Milvus web server monitors. | Integer | 19121 | -# | Port range (1024, 65535) | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -network: - bind.address: 0.0.0.0 - bind.port: 19530 - http.enable: true - http.port: 19121 - -#----------------------+------------------------------------------------------------+------------+-----------------+ -# Storage Config | Description | Type | Default | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# path | Path used to save meta data, vector data and index data. | Path | /var/lib/milvus | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# auto_flush_interval | The interval, in seconds, at which Milvus automatically | Integer | 1 (s) | -# | flushes data to disk. | | | -# | 0 means disable the regular flush. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -storage: - path: /var/lib/milvus - auto_flush_interval: 1 - -#----------------------+------------------------------------------------------------+------------+-----------------+ -# WAL Config | Description | Type | Default | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# enable | Whether to enable write-ahead logging (WAL) in Milvus. | Boolean | true | -# | If WAL is enabled, Milvus writes all data changes to log | | | -# | files in advance before implementing data changes. WAL | | | -# | ensures the atomicity and durability for Milvus operations.| | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# recovery_error_ignore| Whether to ignore logs with errors that happens during WAL | Boolean | false | -# | recovery. If true, when Milvus restarts for recovery and | | | -# | there are errors in WAL log files, log files with errors | | | -# | are ignored. If false, Milvus does not restart when there | | | -# | are errors in WAL log files. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# buffer_size | Sum total of the read buffer and the write buffer in MBs. | Integer | 256 (MB) | -# | buffer_size must be in range [64, 4096] (MB). | | | -# | If the value you specified is out of range, Milvus | | | -# | automatically uses the boundary value closest to the | | | -# | specified value. It is recommended you set buffer_size to | | | -# | a value greater than the inserted data size of a single | | | -# | insert operation for better performance. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# path | Location of WAL log files. | String | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -wal: - enable: true - recovery_error_ignore: false - buffer_size: 256MB - path: /var/lib/milvus/wal - -#----------------------+------------------------------------------------------------+------------+-----------------+ -# Cache Config | Description | Type | Default | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# cache_size | The size of CPU memory used for caching data for faster | Integer | 4 (GB) | -# | query. The sum of 'cpu_cache_capacity' and | | | -# | 'insert_buffer_size' must be less than system memory size. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# insert_buffer_size | Buffer size used for data insertion. | Integer | 1 (GB) | -# | The sum of 'insert_buffer_size' and 'cpu_cache_capacity' | | | -# | must be less than system memory size. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# preload_collection | A comma-separated list of collection names that need to | StringList | | -# | be pre-loaded when Milvus server starts up. | | | -# | '*' means preload all existing tables (single-quote or | | | -# | double-quote required). | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -cache: - cache_size: 4GB - insert_buffer_size: 1GB - preload_collection: - -#----------------------+------------------------------------------------------------+------------+-----------------+ -# GPU Config | Description | Type | Default | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# enable | Enable GPU resources or not. | Boolean | false | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# cache_size | The size of GPU memory per card used for cache. | Integer | 1 (GB) | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# gpu_search_threshold | A Milvus performance tuning parameter. This value will be | Integer | 1000 | -# | compared with 'nq' to decide if the search computation will| | | -# | be executed on GPUs only. | | | -# | If nq >= gpu_search_threshold, the search computation will | | | -# | be executed on GPUs only; | | | -# | if nq < gpu_search_threshold, the search computation will | | | -# | be executed on CPUs only. | | | -# | The SQ8H index is special, if nq < gpu_search_threshold, | | | -# | the search will be executed on both CPUs and GPUs. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# search_resources | The list of GPU devices used for search computation. | DeviceList | gpu0 | -# | Must be in format gpux. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# build_index_resources| The list of GPU devices used for index building. | DeviceList | gpu0 | -# | Must be in format gpux. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -gpu: - enable: false - cache_size: 1GB - gpu_search_threshold: 1000 - search_devices: - - gpu0 - build_index_devices: - - gpu0 - -#----------------------+------------------------------------------------------------+------------+-----------------+ -# Logs Config | Description | Type | Default | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# level | Log level in Milvus. Must be one of debug, info, warning, | String | debug | -# | error, fatal | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# trace.enable | Whether to enable trace level logging in Milvus. | Boolean | true | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# path | Absolute path to the folder holding the log files. | String | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# max_log_file_size | The maximum size of each log file, size range [512, 4096] | Integer | 1024 (MB) | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# log_rotate_num | The maximum number of log files that Milvus keeps for each | Integer | 0 | -# | logging level, num range [0, 1024], 0 means unlimited. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -logs: - level: debug - trace.enable: true - path: /var/lib/milvus/logs - max_log_file_size: 1024MB - log_rotate_num: 0 - -#----------------------+------------------------------------------------------------+------------+-----------------+ -# Metric Config | Description | Type | Default | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# enable | Enable monitoring function or not. | Boolean | false | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# address | Pushgateway address | IP | 127.0.0.1 + -#----------------------+------------------------------------------------------------+------------+-----------------+ -# port | Pushgateway port, port range (1024, 65535) | Integer | 9091 | -#----------------------+------------------------------------------------------------+------------+-----------------+ -metric: - enable: false - address: 127.0.0.1 - port: 9091 - diff --git a/core/conf/server_config.template b/core/conf/server_config.template deleted file mode 100644 index 2380088ecc..0000000000 --- a/core/conf/server_config.template +++ /dev/null @@ -1,190 +0,0 @@ -# Copyright (C) 2019-2020 Zilliz. All rights reserved. -# -# Licensed 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 limitations under the License. - -version: 0.5 - -#----------------------+------------------------------------------------------------+------------+-----------------+ -# Cluster Config | Description | Type | Default | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# enable | If running with Mishards, set true, otherwise false. | Boolean | false | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# role | Milvus deployment role: rw / ro | Role | rw | -#----------------------+------------------------------------------------------------+------------+-----------------+ -cluster: - enable: false - role: rw - -#----------------------+------------------------------------------------------------+------------+-----------------+ -# General Config | Description | Type | Default | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# timezone | Use UTC-x or UTC+x to specify a time zone. | Timezone | UTC+8 | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# meta_uri | URI for metadata storage, using SQLite (for single server | URI | sqlite://:@:/ | -# | Milvus) or MySQL (for distributed cluster Milvus). | | | -# | Format: dialect://username:password@host:port/database | | | -# | Keep 'dialect://:@:/', 'dialect' can be either 'sqlite' or | | | -# | 'mysql', replace other texts with real values. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -general: - timezone: UTC+8 - meta_uri: sqlite://:@:/ - -#----------------------+------------------------------------------------------------+------------+-----------------+ -# Network Config | Description | Type | Default | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# bind.address | IP address that Milvus server monitors. | IP | 0.0.0.0 | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# bind.port | Port that Milvus server monitors. Port range (1024, 65535) | Integer | 19530 | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# http.enable | Enable HTTP server or not. | Boolean | true | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# http.port | Port that Milvus HTTP server monitors. | Integer | 19121 | -# | Port range (1024, 65535) | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -network: - bind.address: 0.0.0.0 - bind.port: 19530 - http.enable: true - http.port: 19121 - -#----------------------+------------------------------------------------------------+------------+-----------------+ -# Storage Config | Description | Type | Default | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# path | Path used to save meta data, vector data and index data. | Path | /var/lib/milvus | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# auto_flush_interval | The interval, in seconds, at which Milvus automatically | Integer | 1 (s) | -# | flushes data to disk. | | | -# | 0 means disable the regular flush. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -storage: - path: @MILVUS_DB_PATH@ - auto_flush_interval: 1 - -#----------------------+------------------------------------------------------------+------------+-----------------+ -# WAL Config | Description | Type | Default | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# enable | Whether to enable write-ahead logging (WAL) in Milvus. | Boolean | true | -# | If WAL is enabled, Milvus writes all data changes to log | | | -# | files in advance before implementing data changes. WAL | | | -# | ensures the atomicity and durability for Milvus operations.| | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# recovery_error_ignore| Whether to ignore logs with errors that happens during WAL | Boolean | false | -# | recovery. If true, when Milvus restarts for recovery and | | | -# | there are errors in WAL log files, log files with errors | | | -# | are ignored. If false, Milvus does not restart when there | | | -# | are errors in WAL log files. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# buffer_size | Sum total of the read buffer and the write buffer in Bytes.| String | 256MB | -# | buffer_size must be in range [64MB, 4096MB]. | | | -# | If the value you specified is out of range, Milvus | | | -# | automatically uses the boundary value closest to the | | | -# | specified value. It is recommended you set buffer_size to | | | -# | a value greater than the inserted data size of a single | | | -# | insert operation for better performance. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# path | Location of WAL log files. | String | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -wal: - enable: true - recovery_error_ignore: false - buffer_size: 256MB - path: @MILVUS_DB_PATH@/wal - -#----------------------+------------------------------------------------------------+------------+-----------------+ -# Cache Config | Description | Type | Default | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# cache_size | The size of CPU memory used for caching data for faster | String | 4GB | -# | query. The sum of 'cache_size' and 'insert_buffer_size' | | | -# | must be less than system memory size. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# insert_buffer_size | Buffer size used for data insertion. | String | 1GB | -# | The sum of 'insert_buffer_size' and 'cache_size' | | | -# | must be less than system memory size. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# preload_collection | A comma-separated list of collection names that need to | StringList | | -# | be pre-loaded when Milvus server starts up. | | | -# | '*' means preload all existing tables (single-quote or | | | -# | double-quote required). | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -cache: - cache_size: 4GB - insert_buffer_size: 1GB - preload_collection: - -#----------------------+------------------------------------------------------------+------------+-----------------+ -# GPU Config | Description | Type | Default | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# enable | Use GPU devices or not. | Boolean | false | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# cache_size | The size of GPU memory per card used for cache. | String | 1GB | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# gpu_search_threshold | A Milvus performance tuning parameter. This value will be | Integer | 1000 | -# | compared with 'nq' to decide if the search computation will| | | -# | be executed on GPUs only. | | | -# | If nq >= gpu_search_threshold, the search computation will | | | -# | be executed on GPUs only; | | | -# | if nq < gpu_search_threshold, the search computation will | | | -# | be executed on CPUs only. | | | -# | The SQ8H index is special, if nq < gpu_search_threshold, | | | -# | the search will be executed on both CPUs and GPUs. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# search_devices | The list of GPU devices used for search computation. | DeviceList | gpu0 | -# | Must be in format gpux. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# build_index_devices | The list of GPU devices used for index building. | DeviceList | gpu0 | -# | Must be in format gpux. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -gpu: - enable: @GPU_ENABLE@ - cache_size: 1GB - gpu_search_threshold: 1000 - search_devices: - - gpu0 - build_index_devices: - - gpu0 - -#----------------------+------------------------------------------------------------+------------+-----------------+ -# Logs Config | Description | Type | Default | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# level | Log level in Milvus. Must be one of debug, info, warning, | String | debug | -# | error, fatal | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# trace.enable | Whether to enable trace level logging in Milvus. | Boolean | true | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# path | Absolute path to the folder holding the log files. | String | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# max_log_file_size | The maximum size of each log file, size range | String | 1024MB | -# | [512MB, 4096MB]. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# log_rotate_num | The maximum number of log files that Milvus keeps for each | Integer | 0 | -# | logging level, num range [0, 1024], 0 means unlimited. | | | -#----------------------+------------------------------------------------------------+------------+-----------------+ -logs: - level: debug - trace.enable: true - path: @MILVUS_DB_PATH@/logs - max_log_file_size: 1024MB - log_rotate_num: 0 - -#----------------------+------------------------------------------------------------+------------+-----------------+ -# Metric Config | Description | Type | Default | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# enable | Enable monitoring function or not. | Boolean | false | -#----------------------+------------------------------------------------------------+------------+-----------------+ -# address | Pushgateway address | IP | 127.0.0.1 + -#----------------------+------------------------------------------------------------+------------+-----------------+ -# port | Pushgateway port, port range (1024, 65535) | Integer | 9091 | -#----------------------+------------------------------------------------------------+------------+-----------------+ -metric: - enable: false - address: 127.0.0.1 - port: 9091 - diff --git a/core/conf/tracing_config.json b/core/conf/tracing_config.json deleted file mode 100644 index 3a80614390..0000000000 --- a/core/conf/tracing_config.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "host": "127.0.0.1", - "port": "5666", - "tracer_library": "/path/to/shared_tracing_library", - "tracer_configuration": { - "service_name": "milvus_server", - "sampler": { - "type": "const", - "param": "1" - }, - "disabled": false, - "reporter": { - "localAgentHostPort": "127.0.0.1:6831" - }, - "headers": { - "jaegerDebugHeader": "jaeger_debug_header", - "jaegerBaggageHeader": "jarger_baggage_header", - "TraceContextHeaderName": "trace_context_header_name", - "traceBaggageHeaderPrefix": "trace_baggage_header_prefix" - }, - "baggage_restrictions": { - "denyBaggageOnInitializationFailure": false, - "hostPort": "" - } - } -} diff --git a/core/coverage.sh b/core/coverage.sh deleted file mode 100755 index 833b1b0050..0000000000 --- a/core/coverage.sh +++ /dev/null @@ -1,132 +0,0 @@ -#!/bin/bash - -export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)/milvus/lib - -MYSQL_USER_NAME=root -MYSQL_PASSWORD=123456 -MYSQL_HOST='127.0.0.1' -MYSQL_PORT='3306' - -while getopts "u:p:t:h" arg -do - case $arg in - u) - MYSQL_USER_NAME=$OPTARG - ;; - p) - MYSQL_PASSWORD=$OPTARG - ;; - t) - MYSQL_HOST=$OPTARG - ;; - h) # help - echo " - -parameter: --u: mysql account --p: mysql password --t: mysql host --h: help - -usage: -./coverage.sh -u \${MYSQL_USER} -p \${MYSQL_PASSWORD} -t \${MYSQL_HOST} [-h] - " - exit 0 - ;; - ?) - echo "ERROR! unknown argument" - exit 1 - ;; - esac -done - -LCOV_CMD="lcov" -LCOV_GEN_CMD="genhtml" - -FILE_INFO_BASE="base.info" -FILE_INFO_MILVUS="server.info" -FILE_INFO_OUTPUT="output.info" -FILE_INFO_OUTPUT_NEW="output_new.info" -DIR_LCOV_OUTPUT="lcov_out" - -DIR_GCNO="cmake_build" -DIR_UNITTEST="milvus/unittest" - -# delete old code coverage info files -rm -f FILE_INFO_BASE -rm -f FILE_INFO_MILVUS -rm -f FILE_INFO_OUTPUT -rm -f FILE_INFO_OUTPUT_NEW -rm -rf lcov_out -rm -f FILE_INFO_BASE FILE_INFO_MILVUS FILE_INFO_OUTPUT FILE_INFO_OUTPUT_NEW - -MYSQL_DB_NAME=milvus_`date +%s%N` - -function mysql_exc() -{ - cmd=$1 - mysql -h${MYSQL_HOST} -u${MYSQL_USER_NAME} -p${MYSQL_PASSWORD} -e "${cmd}" - if [ $? -ne 0 ]; then - echo "mysql $cmd run failed" - fi -} - -mysql_exc "CREATE DATABASE IF NOT EXISTS ${MYSQL_DB_NAME};" -mysql_exc "GRANT ALL PRIVILEGES ON ${MYSQL_DB_NAME}.* TO '${MYSQL_USER_NAME}'@'%';" -mysql_exc "FLUSH PRIVILEGES;" -mysql_exc "USE ${MYSQL_DB_NAME};" - -# get baseline -${LCOV_CMD} -c -i -d ${DIR_GCNO} -o "${FILE_INFO_BASE}" -if [ $? -ne 0 ]; then - echo "gen baseline coverage run failed" - exit -1 -fi - -for test in `ls ${DIR_UNITTEST}`; do - echo $test - case ${test} in - test_db) - # set run args for test_db - args="mysql://${MYSQL_USER_NAME}:${MYSQL_PASSWORD}@${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DB_NAME}" - ;; - *test_*) - args="" - ;; - esac - # run unittest - ./${DIR_UNITTEST}/${test} "${args}" - if [ $? -ne 0 ]; then - echo ${args} - echo ${DIR_UNITTEST}/${test} "run failed" - exit -1 - fi -done - -mysql_exc "DROP DATABASE IF EXISTS ${MYSQL_DB_NAME};" - -# gen code coverage -${LCOV_CMD} -d ${DIR_GCNO} -o "${FILE_INFO_MILVUS}" -c -# merge coverage -${LCOV_CMD} -a ${FILE_INFO_BASE} -a ${FILE_INFO_MILVUS} -o "${FILE_INFO_OUTPUT}" - -# remove third party from tracefiles -${LCOV_CMD} -r "${FILE_INFO_OUTPUT}" -o "${FILE_INFO_OUTPUT_NEW}" \ - "/usr/*" \ - "*/boost/*" \ - "*/cmake_build/*_ep-prefix/*" \ - "*/src/index/cmake_build*" \ - "*/src/index/thirdparty*" \ - "*/src/grpc*" \ - "*/src/metrics/MetricBase.h" \ - "*/src/server/Server.cpp" \ - "*/src/server/DBWrapper.cpp" \ - "*/src/server/grpc_impl/GrpcServer.cpp" \ - "*/thirdparty/*" - -if [ $? -ne 0 ]; then - echo "generate ${FILE_INFO_OUTPUT_NEW} failed" - exit -2 -fi -# gen html report -${LCOV_GEN_CMD} "${FILE_INFO_OUTPUT_NEW}" --output-directory ${DIR_LCOV_OUTPUT}/ diff --git a/core/scripts/migration/README.md b/core/scripts/migration/README.md deleted file mode 100644 index 7c318c1393..0000000000 --- a/core/scripts/migration/README.md +++ /dev/null @@ -1,28 +0,0 @@ -## Data Migration - -####0.3.x -legacy data is not migrate-able for later versions - -####0.4.x -legacy data can be reused directly by 0.5.x - -legacy data can be migrated to 0.6.x - -####0.5.x -legacy data can be migrated to 0.6.x - -####0.6.x -how to migrate legacy 0.4.x/0.5.x data - -for sqlite meta: -```shell - $ sqlite3 [parth_to]/meta.sqlite < sqlite_4_to_6.sql -``` - -for mysql meta: -```shell - $ mysql -h127.0.0.1 -uroot -p123456 -Dmilvus < mysql_4_to_6.sql -``` - - - diff --git a/core/scripts/migration/mysql_4_to_6.sql b/core/scripts/migration/mysql_4_to_6.sql deleted file mode 100644 index f8a5b1b70b..0000000000 --- a/core/scripts/migration/mysql_4_to_6.sql +++ /dev/null @@ -1,4 +0,0 @@ -alter table Tables add column owner_table VARCHAR(255) DEFAULT '' NOT NULL; -alter table Tables add column partition_tag VARCHAR(255) DEFAULT '' NOT NULL; -alter table Tables add column version VARCHAR(64) DEFAULT '0.6.0' NOT NULL; -update Tables set version='0.6.0'; diff --git a/core/scripts/migration/mysql_6_to_4.sql b/core/scripts/migration/mysql_6_to_4.sql deleted file mode 100644 index 96c60e0280..0000000000 --- a/core/scripts/migration/mysql_6_to_4.sql +++ /dev/null @@ -1,3 +0,0 @@ -alter table Tables drop column owner_table; -alter table Tables drop column partition_tag; -alter table Tables drop column version; diff --git a/core/scripts/migration/sqlite_4_to_6.sql b/core/scripts/migration/sqlite_4_to_6.sql deleted file mode 100644 index 2069145046..0000000000 --- a/core/scripts/migration/sqlite_4_to_6.sql +++ /dev/null @@ -1,4 +0,0 @@ -alter table Tables add column 'owner_table' TEXT DEFAULT '' NOT NULL; -alter table Tables add column 'partition_tag' TEXT DEFAULT '' NOT NULL; -alter table Tables add column 'version' TEXT DEFAULT '0.6.0' NOT NULL; -update Tables set version='0.6.0'; diff --git a/core/scripts/migration/sqlite_6_to_4.sql b/core/scripts/migration/sqlite_6_to_4.sql deleted file mode 100644 index 686d276f46..0000000000 --- a/core/scripts/migration/sqlite_6_to_4.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE 'TempTables' ( 'id' INTEGER PRIMARY KEY NOT NULL , 'table_id' TEXT UNIQUE NOT NULL , 'state' INTEGER NOT NULL , 'dimension' INTEGER NOT NULL , 'created_on' INTEGER NOT NULL , 'flag' INTEGER DEFAULT 0 NOT NULL , 'index_file_size' INTEGER NOT NULL , 'engine_type' INTEGER NOT NULL , 'nlist' INTEGER NOT NULL , 'metric_type' INTEGER NOT NULL); - -INSERT INTO TempTables SELECT id, table_id, state, dimension, created_on, flag, index_file_size, engine_type, nlist, metric_type FROM Tables; - -DROP TABLE Tables; - -ALTER TABLE TempTables RENAME TO Tables; diff --git a/core/scripts/start_server.sh b/core/scripts/start_server.sh deleted file mode 100755 index 15d2cf0872..0000000000 --- a/core/scripts/start_server.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -../bin/milvus_server -c ../conf/server_config.yaml - diff --git a/core/scripts/stop_server.sh b/core/scripts/stop_server.sh deleted file mode 100755 index f15e48a954..0000000000 --- a/core/scripts/stop_server.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -function kill_progress() -{ - kill -s SIGUSR2 $(pgrep $1) - - sleep 2 -} - -STATUS=$(kill_progress "milvus_server" ) - -if [[ ${STATUS} == "false" ]];then - echo "Milvus server closed abnormally!" -else - echo "Milvus server closed successfully!" -fi diff --git a/core/src/CMakeLists.txt b/core/src/CMakeLists.txt deleted file mode 100644 index 08466ac060..0000000000 --- a/core/src/CMakeLists.txt +++ /dev/null @@ -1,343 +0,0 @@ -#------------------------------------------------------------------------------- -# Copyright (C) 2019-2020 Zilliz. All rights reserved. -# -# Licensed 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 limitations under the License. -#------------------------------------------------------------------------------- - -include_directories(${MILVUS_SOURCE_DIR}) -include_directories(${MILVUS_ENGINE_SRC}) -include_directories(${MILVUS_THIRDPARTY_SRC}) - -include_directories(${MILVUS_ENGINE_SRC}/grpc/gen-status) -include_directories(${MILVUS_ENGINE_SRC}/grpc/gen-milvus) - -set(FOUND_OPENBLAS "unknown") -add_subdirectory(index) -if (FAISS_WITH_MKL) - add_compile_definitions("WITH_MKL") -endif () - -set(INDEX_INCLUDE_DIRS ${INDEX_INCLUDE_DIRS} PARENT_SCOPE) -foreach (dir ${INDEX_INCLUDE_DIRS}) - include_directories(${dir}) -endforeach () - -aux_source_directory(${MILVUS_ENGINE_SRC}/cache cache_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/config config_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/config/handler config_handler_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/metrics metrics_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/metrics/prometheus metrics_prometheus_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/db db_main_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/db/engine db_engine_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/db/insert db_insert_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/db/meta db_meta_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/db/merge db_merge_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/db/wal db_wal_files) - -set(grpc_service_files - ${MILVUS_ENGINE_SRC}/grpc/gen-milvus/milvus.grpc.pb.cc - ${MILVUS_ENGINE_SRC}/grpc/gen-milvus/milvus.pb.cc - ${MILVUS_ENGINE_SRC}/grpc/gen-status/status.grpc.pb.cc - ${MILVUS_ENGINE_SRC}/grpc/gen-status/status.pb.cc - ) - -aux_source_directory(${MILVUS_ENGINE_SRC}/query query_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/context context_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/search search_files) - -aux_source_directory(${MILVUS_ENGINE_SRC}/scheduler scheduler_main_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/scheduler/action scheduler_action_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/scheduler/event scheduler_event_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/scheduler/job scheduler_job_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/scheduler/selector scheduler_selector_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/scheduler/resource scheduler_resource_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/scheduler/task scheduler_task_files) -set(scheduler_files - ${scheduler_main_files} - ${scheduler_action_files} - ${scheduler_event_files} - ${scheduler_job_files} - ${scheduler_selector_files} - ${scheduler_resource_files} - ${scheduler_task_files} - ) - -aux_source_directory(${MILVUS_THIRDPARTY_SRC}/easyloggingpp thirdparty_easyloggingpp_files) -aux_source_directory(${MILVUS_THIRDPARTY_SRC}/nlohmann thirdparty_nlohmann_files) -aux_source_directory(${MILVUS_THIRDPARTY_SRC}/dablooms thirdparty_dablooms_files) -set(thirdparty_files - ${thirdparty_easyloggingpp_files} - ${thirdparty_nlohmann_files} - ${thirdparty_dablooms_files} - ) - -aux_source_directory(${MILVUS_ENGINE_SRC}/server server_service_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/server/init server_init_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/server/delivery/request delivery_request_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/server/delivery/hybrid_request delivery_hybrid_request_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/server/delivery/strategy delivery_strategy_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/server/delivery delivery_files) -set(server_files - ${server_init_files} - ${server_service_files} - ${server_init_files} - ${delivery_request_files} - ${delivery_hybrid_request_files} - ${delivery_strategy_files} - ${delivery_files} - ) - -aux_source_directory(${MILVUS_ENGINE_SRC}/server/grpc_impl grpc_impl_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/server/grpc_impl/interceptor grpc_interceptor_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/server/context server_context_files) -set(grpc_server_files - ${grpc_request_files} - ${grpc_impl_files} - ${grpc_interceptor_files} - ) - -aux_source_directory(${MILVUS_ENGINE_SRC}/server/web_impl/handler web_handler_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/server/web_impl/component web_conponent_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/server/web_impl/controller web_controller_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/server/web_impl/dto web_dto_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/server/web_impl/utils web_utils_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/server/web_impl web_impl_files) -set(web_server_files - ${web_handler_files} - ${web_conponent_files} - ${web_controller_files} - ${web_dto_files} - ${web_utils_files} - ${web_impl_files} - ) - -aux_source_directory(${MILVUS_ENGINE_SRC}/storage storage_main_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/storage/disk storage_disk_files) -#aux_source_directory(${MILVUS_ENGINE_SRC}/storage/s3 storage_s3_files) -set(storage_files - ${storage_main_files} - ${storage_disk_files} -# ${storage_s3_files} - ) - -aux_source_directory(${MILVUS_ENGINE_SRC}/utils utils_files) - -aux_source_directory(${MILVUS_ENGINE_SRC}/index/archive wrapper_files) - -aux_source_directory(${MILVUS_ENGINE_SRC}/tracing tracing_files) - -aux_source_directory(${MILVUS_ENGINE_SRC}/codecs codecs_files) -aux_source_directory(${MILVUS_ENGINE_SRC}/codecs/default codecs_default_files) - -aux_source_directory(${MILVUS_ENGINE_SRC}/segment segment_files) - -set(engine_files - ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp - ${cache_files} - ${db_main_files} - ${db_engine_files} - ${db_insert_files} - ${db_meta_files} - ${db_merge_files} - ${db_wal_files} - ${metrics_files} - ${storage_files} - ${thirdparty_files} - ${utils_files} - ${wrapper_files} - ${codecs_files} - ${codecs_default_files} - ${segment_files} - ) - -if (MILVUS_WITH_PROMETHEUS) - set(engine_files ${engine_files} - ${metrics_prometheus_files}) -endif () - -set(grpc_lib - grpcpp_channelz - grpc++ - grpc - grpc_protobuf - grpc_protoc - ) - -set(prometheus_lib - prometheus-cpp-push - prometheus-cpp-pull - prometheus-cpp-core - curl - ) - -set(boost_lib - libboost_system.a - libboost_filesystem.a - libboost_serialization.a - ) - -set(s3_client_lib - aws-cpp-sdk-s3 - aws-cpp-sdk-core - ) - -set(third_party_libs - sqlite - ${grpc_lib} - yaml-cpp - mysqlpp - zlib - fiu - ${boost_lib} - ) - -if (MILVUS_GPU_VERSION) - include_directories(${CUDA_INCLUDE_DIRS}) - link_directories("${CUDA_TOOLKIT_ROOT_DIR}/lib64") - set(cuda_lib - ${CUDA_TOOLKIT_ROOT_DIR}/lib64/stubs/libnvidia-ml.so - cudart - cublas - ) - set(third_party_libs ${third_party_libs} - ${cuda_lib} - ) - aux_source_directory(${MILVUS_ENGINE_SRC}/wrapper/gpu wrapper_gpu_files) - set(engine_files ${engine_files} - ${wrapper_gpu_files} - ) -endif () - -# cannot be enabled together with ENABLE_CPU_PROFILING -if (ENABLE_MEM_PROFILING) - set(third_party_libs ${third_party_libs} - tcmalloc - ) -endif () - -if (ENABLE_CPU_PROFILING) - set(third_party_libs ${third_party_libs} - gperftools - libunwind - ) -endif () - -if (MILVUS_WITH_PROMETHEUS) - set(third_party_libs ${third_party_libs} - ${prometheus_lib} - ) -endif () - -if (MILVUS_WITH_AWS) - set(third_party_libs ${third_party_libs} - ${s3_client_lib} - curl - crypto - ) -endif () - -set(engine_libs - pthread - libgomp.a - libgfortran.a - dl - ) - -if (NOT ${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64") - set(engine_libs - ${engine_libs} - libquadmath.a - ) -endif () - -add_library(milvus_engine STATIC ${engine_files}) -add_dependencies(milvus_engine fiu) -target_link_libraries(milvus_engine - knowhere - ${third_party_libs} - ${engine_libs} - ) - -if (MILVUS_WITH_PROMETHEUS) - add_library(metrics STATIC ${metrics_files} ${metrics_prometheus_files}) -else () - add_library(metrics STATIC ${metrics_files}) -endif () -add_dependencies(metrics fiu) - -set(metrics_lib - yaml-cpp - ) - -if (MILVUS_WITH_PROMETHEUS) - set(metrics_lib ${metrics_lib} - ${prometheus_lib} - ) -endif () - -target_link_libraries(metrics ${metrics_lib}) - -add_library(tracing STATIC ${tracing_files} ${thirdparty_files}) -add_dependencies(tracing fiu) -set(tracing_lib - opentracing - opentracing_mocktracer - ${grpc_lib} - pthread - z - ) -target_link_libraries(tracing ${tracing_lib}) - -set(server_libs - milvus_engine - metrics - tracing - oatpp - ) - -add_executable(milvus_server - ${config_files} - ${config_handler_files} - ${metrics_files} - ${query_files} - ${search_files} - ${context_files} - ${scheduler_files} - ${server_files} - ${grpc_server_files} - ${grpc_service_files} - ${web_server_files} - ${server_context_files} - ${utils_files} - ${tracing_files} - ) - -target_link_libraries(milvus_server - ${server_libs} - ) - -install(TARGETS milvus_server DESTINATION bin) - -install(FILES - ${CMAKE_BINARY_DIR}/mysqlpp_ep-prefix/src/mysqlpp_ep/lib/${CMAKE_SHARED_LIBRARY_PREFIX}mysqlpp${CMAKE_SHARED_LIBRARY_SUFFIX} - ${CMAKE_BINARY_DIR}/mysqlpp_ep-prefix/src/mysqlpp_ep/lib/${CMAKE_SHARED_LIBRARY_PREFIX}mysqlpp${CMAKE_SHARED_LIBRARY_SUFFIX}.3 - ${CMAKE_BINARY_DIR}/mysqlpp_ep-prefix/src/mysqlpp_ep/lib/${CMAKE_SHARED_LIBRARY_PREFIX}mysqlpp${CMAKE_SHARED_LIBRARY_SUFFIX}.3.2.4 - ${CMAKE_BINARY_DIR}/fiu_ep-prefix/src/fiu_ep/lib/${CMAKE_SHARED_LIBRARY_PREFIX}fiu${CMAKE_SHARED_LIBRARY_SUFFIX} - ${CMAKE_BINARY_DIR}/fiu_ep-prefix/src/fiu_ep/lib/${CMAKE_SHARED_LIBRARY_PREFIX}fiu${CMAKE_SHARED_LIBRARY_SUFFIX}.0 - ${CMAKE_BINARY_DIR}/fiu_ep-prefix/src/fiu_ep/lib/${CMAKE_SHARED_LIBRARY_PREFIX}fiu${CMAKE_SHARED_LIBRARY_SUFFIX}.1.00 - DESTINATION lib) - -if (FOUND_OPENBLAS STREQUAL "false") - install(FILES - ${CMAKE_BINARY_DIR}/src/index/openblas_ep-prefix/src/openblas_ep/lib/${CMAKE_SHARED_LIBRARY_PREFIX}openblas${CMAKE_SHARED_LIBRARY_SUFFIX} - ${CMAKE_BINARY_DIR}/src/index/openblas_ep-prefix/src/openblas_ep/lib/${CMAKE_SHARED_LIBRARY_PREFIX}openblas${CMAKE_SHARED_LIBRARY_SUFFIX}.0 - ${CMAKE_BINARY_DIR}/src/index/openblas_ep-prefix/src/openblas_ep/lib/${CMAKE_SHARED_LIBRARY_PREFIX}openblas${CMAKE_SHARED_LIBRARY_SUFFIX}.0.3 - DESTINATION lib) -endif() diff --git a/core/src/cache/Cache.h b/core/src/cache/Cache.h deleted file mode 100644 index 34594c4573..0000000000 --- a/core/src/cache/Cache.h +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "LRU.h" -#include "utils/Log.h" - -#include -#include -#include -#include - -namespace milvus { -namespace cache { - -template -class Cache { - public: - // mem_capacity, units:GB - Cache(int64_t capacity_gb, int64_t cache_max_count, const std::string& header = ""); - ~Cache() = default; - - int64_t - usage() const { - return usage_; - } - - // unit: BYTE - int64_t - capacity() const { - return capacity_; - } - - // unit: BYTE - void - set_capacity(int64_t capacity); - - double - freemem_percent() const { - return freemem_percent_; - } - - void - set_freemem_percent(double percent) { - freemem_percent_ = percent; - } - - size_t - size() const; - - bool - exists(const std::string& key); - - ItemObj - get(const std::string& key); - - void - insert(const std::string& key, const ItemObj& item); - - void - erase(const std::string& key); - - bool - reserve(const int64_t size); - - void - print(); - - void - clear(); - - private: - void - insert_internal(const std::string& key, const ItemObj& item); - - void - erase_internal(const std::string& key); - - void - free_memory_internal(const int64_t target_size); - - private: - std::string header_; - int64_t usage_; - int64_t capacity_; - double freemem_percent_; - - LRU lru_; - mutable std::mutex mutex_; -}; - -} // namespace cache -} // namespace milvus - -#include "cache/Cache.inl" diff --git a/core/src/cache/Cache.inl b/core/src/cache/Cache.inl deleted file mode 100644 index c371efe515..0000000000 --- a/core/src/cache/Cache.inl +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -namespace milvus { -namespace cache { - -constexpr double DEFAULT_THRESHOLD_PERCENT = 0.7; - -template -Cache::Cache(int64_t capacity, int64_t cache_max_count, const std::string& header) - : header_(header), - usage_(0), - capacity_(capacity), - freemem_percent_(DEFAULT_THRESHOLD_PERCENT), - lru_(cache_max_count) { -} - -template -void -Cache::set_capacity(int64_t capacity) { - std::lock_guard lock(mutex_); - if (capacity > 0) { - capacity_ = capacity; - free_memory_internal(capacity); - } -} - -template -size_t -Cache::size() const { - std::lock_guard lock(mutex_); - return lru_.size(); -} - -template -bool -Cache::exists(const std::string& key) { - std::lock_guard lock(mutex_); - return lru_.exists(key); -} - -template -ItemObj -Cache::get(const std::string& key) { - std::lock_guard lock(mutex_); - if (!lru_.exists(key)) { - return nullptr; - } - return lru_.get(key); -} - -template -void -Cache::insert(const std::string& key, const ItemObj& item) { - std::lock_guard lock(mutex_); - insert_internal(key, item); -} - -template -void -Cache::erase(const std::string& key) { - std::lock_guard lock(mutex_); - erase_internal(key); -} - -template -bool -Cache::reserve(const int64_t item_size) { - std::lock_guard lock(mutex_); - if (item_size > capacity_) { - LOG_SERVER_ERROR_ << header_ << " item size " << (item_size >> 20) << "MB too big to insert into cache capacity" - << (capacity_ >> 20) << "MB"; - return false; - } - if (item_size > capacity_ - usage_) { - free_memory_internal(capacity_ - item_size); - } - return true; -} - -template -void -Cache::clear() { - std::lock_guard lock(mutex_); - lru_.clear(); - usage_ = 0; - LOG_SERVER_DEBUG_ << header_ << " Clear cache !"; -} - - -template -void -Cache::print() { - std::lock_guard lock(mutex_); - size_t cache_count = lru_.size(); - // for (auto it = lru_.begin(); it != lru_.end(); ++it) { - // LOG_SERVER_DEBUG_ << it->first; - // } - LOG_SERVER_DEBUG_ << header_ << " [item count]: " << cache_count << ", [usage] " << (usage_ >> 20) - << "MB, [capacity] " << (capacity_ >> 20) << "MB"; -} - -template -void -Cache::insert_internal(const std::string& key, const ItemObj& item) { - if (item == nullptr) { - return; - } - - size_t item_size = item->Size(); - - // if key already exist, subtract old item size - if (lru_.exists(key)) { - const ItemObj& old_item = lru_.get(key); - usage_ -= old_item->Size(); - } - - // plus new item size - usage_ += item_size; - - // if usage exceed capacity, free some items - if (usage_ > capacity_) { - LOG_SERVER_DEBUG_ << header_ << " Current usage " << (usage_ >> 20) << "MB is too high for capacity " - << (capacity_ >> 20) << "MB, start free memory"; - free_memory_internal(capacity_); - } - - // insert new item - lru_.put(key, item); - LOG_SERVER_DEBUG_ << header_ << " Insert " << key << " size: " << (item_size >> 20) << "MB into cache"; - LOG_SERVER_DEBUG_ << header_ << " Count: " << lru_.size() << ", Usage: " << (usage_ >> 20) << "MB, Capacity: " - << (capacity_ >> 20) << "MB"; -} - -template -void -Cache::erase_internal(const std::string& key) { - if (!lru_.exists(key)) { - return; - } - - const ItemObj& item = lru_.get(key); - size_t item_size = item->Size(); - - lru_.erase(key); - - usage_ -= item_size; - LOG_SERVER_DEBUG_ << header_ << " Erase " << key << " size: " << (item_size >> 20) << "MB from cache"; - LOG_SERVER_DEBUG_ << header_ << " Count: " << lru_.size() << ", Usage: " << (usage_ >> 20) << "MB, Capacity: " - << (capacity_ >> 20) << "MB"; -} - -template -void -Cache::free_memory_internal(const int64_t target_size) { - int64_t threshold = std::min((int64_t)(capacity_ * freemem_percent_), target_size); - int64_t delta_size = usage_ - threshold; - if (delta_size <= 0) { - delta_size = 1; // ensure at least one item erased - } - - std::set key_array; - int64_t released_size = 0; - - auto it = lru_.rbegin(); - while (it != lru_.rend() && released_size < delta_size) { - auto& key = it->first; - auto& obj_ptr = it->second; - - key_array.emplace(key); - released_size += obj_ptr->Size(); - ++it; - } - - LOG_SERVER_DEBUG_ << header_ << " To be released memory size: " << (released_size >> 20) << "MB"; - - for (auto& key : key_array) { - erase_internal(key); - } -} - -} // namespace cache -} // namespace milvus diff --git a/core/src/cache/CacheMgr.h b/core/src/cache/CacheMgr.h deleted file mode 100644 index 114b089c8f..0000000000 --- a/core/src/cache/CacheMgr.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "Cache.h" -#include "metrics/Metrics.h" -#include "utils/Log.h" - -#include -#include - -namespace milvus { -namespace cache { - -template -class CacheMgr { - public: - virtual uint64_t - ItemCount() const; - - virtual bool - ItemExists(const std::string& key); - - virtual ItemObj - GetItem(const std::string& key); - - virtual void - InsertItem(const std::string& key, const ItemObj& data); - - virtual void - EraseItem(const std::string& key); - - virtual bool - Reserve(const int64_t size); - - virtual void - PrintInfo(); - - virtual void - ClearCache(); - - int64_t - CacheUsage() const; - - int64_t - CacheCapacity() const; - - void - SetCapacity(int64_t capacity); - - protected: - CacheMgr(); - - virtual ~CacheMgr(); - - protected: - std::shared_ptr> cache_; -}; - -} // namespace cache -} // namespace milvus - -#include "cache/CacheMgr.inl" diff --git a/core/src/cache/CacheMgr.inl b/core/src/cache/CacheMgr.inl deleted file mode 100644 index 887e2a47f7..0000000000 --- a/core/src/cache/CacheMgr.inl +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -namespace milvus { -namespace cache { - -template -CacheMgr::CacheMgr() { -} - -template -CacheMgr::~CacheMgr() { -} - -template -uint64_t -CacheMgr::ItemCount() const { - if (cache_ == nullptr) { - LOG_SERVER_ERROR_ << "Cache doesn't exist"; - return 0; - } - return (uint64_t)(cache_->size()); -} - -template -bool -CacheMgr::ItemExists(const std::string& key) { - if (cache_ == nullptr) { - LOG_SERVER_ERROR_ << "Cache doesn't exist"; - return false; - } - return cache_->exists(key); -} - -template -ItemObj -CacheMgr::GetItem(const std::string& key) { - if (cache_ == nullptr) { - LOG_SERVER_ERROR_ << "Cache doesn't exist"; - return nullptr; - } - server::Metrics::GetInstance().CacheAccessTotalIncrement(); - return cache_->get(key); -} - -template -void -CacheMgr::InsertItem(const std::string& key, const ItemObj& data) { - if (cache_ == nullptr) { - LOG_SERVER_ERROR_ << "Cache doesn't exist"; - return; - } - cache_->insert(key, data); - server::Metrics::GetInstance().CacheAccessTotalIncrement(); -} - -template -void -CacheMgr::EraseItem(const std::string& key) { - if (cache_ == nullptr) { - LOG_SERVER_ERROR_ << "Cache doesn't exist"; - return; - } - cache_->erase(key); - server::Metrics::GetInstance().CacheAccessTotalIncrement(); -} - -template -bool -CacheMgr::Reserve(const int64_t size) { - if (cache_ == nullptr) { - LOG_SERVER_ERROR_ << "Cache doesn't exist"; - return false; - } - return cache_->reserve(size); -} - -template -void -CacheMgr::PrintInfo() { - if (cache_ == nullptr) { - LOG_SERVER_ERROR_ << "Cache doesn't exist"; - return; - } - cache_->print(); -} - -template -void -CacheMgr::ClearCache() { - if (cache_ == nullptr) { - LOG_SERVER_ERROR_ << "Cache doesn't exist"; - return; - } - cache_->clear(); -} - -template -int64_t -CacheMgr::CacheUsage() const { - if (cache_ == nullptr) { - LOG_SERVER_ERROR_ << "Cache doesn't exist"; - return 0; - } - return cache_->usage(); -} - -template -int64_t -CacheMgr::CacheCapacity() const { - if (cache_ == nullptr) { - LOG_SERVER_ERROR_ << "Cache doesn't exist"; - return 0; - } - return cache_->capacity(); -} - -template -void -CacheMgr::SetCapacity(int64_t capacity) { - if (cache_ == nullptr) { - LOG_SERVER_ERROR_ << "Cache doesn't exist"; - return; - } - cache_->set_capacity(capacity); -} - -} // namespace cache -} // namespace milvus diff --git a/core/src/cache/CpuCacheMgr.cpp b/core/src/cache/CpuCacheMgr.cpp deleted file mode 100644 index 7059bc77ec..0000000000 --- a/core/src/cache/CpuCacheMgr.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "cache/CpuCacheMgr.h" - -#include - -#include - -#include "config/Config.h" -#include "utils/Log.h" - -namespace milvus { -namespace cache { - -namespace { -// constexpr int64_t unit = 1024 * 1024 * 1024; -constexpr int64_t unit = 1; -} // namespace - -CpuCacheMgr::CpuCacheMgr() { - // All config values have been checked in Config::ValidateConfig() - server::Config& config = server::Config::GetInstance(); - - int64_t cpu_cache_cap; - config.GetCacheConfigCpuCacheCapacity(cpu_cache_cap); - int64_t cap = cpu_cache_cap * unit; - LOG_SERVER_DEBUG_ << "cpu cache.size: " << cap; - LOG_SERVER_INFO_ << "cpu cache.size: " << cap; - cache_ = std::make_shared>(cap, 1UL << 32, "[CACHE CPU]"); - - float cpu_cache_threshold; - config.GetCacheConfigCpuCacheThreshold(cpu_cache_threshold); - cache_->set_freemem_percent(cpu_cache_threshold); - - SetIdentity("CpuCacheMgr"); - AddCpuCacheCapacityListener(); -} - -CpuCacheMgr* -CpuCacheMgr::GetInstance() { - static CpuCacheMgr s_mgr; - return &s_mgr; -} - -DataObjPtr -CpuCacheMgr::GetIndex(const std::string& key) { - DataObjPtr obj = GetItem(key); - return obj; -} - -void -CpuCacheMgr::OnCpuCacheCapacityChanged(int64_t value) { - SetCapacity(value * unit); -} - -} // namespace cache -} // namespace milvus diff --git a/core/src/cache/CpuCacheMgr.h b/core/src/cache/CpuCacheMgr.h deleted file mode 100644 index 95bb80ae96..0000000000 --- a/core/src/cache/CpuCacheMgr.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -#include "cache/CacheMgr.h" -#include "cache/DataObj.h" -#include "config/handler/CacheConfigHandler.h" - -namespace milvus { -namespace cache { - -class CpuCacheMgr : public CacheMgr, public server::CacheConfigHandler { - private: - CpuCacheMgr(); - - public: - // TODO(myh): use smart pointer instead - static CpuCacheMgr* - GetInstance(); - - DataObjPtr - GetIndex(const std::string& key); - - protected: - void - OnCpuCacheCapacityChanged(int64_t value) override; -}; - -} // namespace cache -} // namespace milvus diff --git a/core/src/cache/DataObj.h b/core/src/cache/DataObj.h deleted file mode 100644 index 3aea7dea24..0000000000 --- a/core/src/cache/DataObj.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -namespace milvus { -namespace cache { - -class DataObj { - public: - virtual int64_t - Size() = 0; -}; - -using DataObjPtr = std::shared_ptr; - -} // namespace cache -} // namespace milvus diff --git a/core/src/cache/GpuCacheMgr.cpp b/core/src/cache/GpuCacheMgr.cpp deleted file mode 100644 index b371f8f57a..0000000000 --- a/core/src/cache/GpuCacheMgr.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "cache/GpuCacheMgr.h" -#include "config/Config.h" -#include "utils/Log.h" - -#include -#include -#include - -namespace milvus { -namespace cache { - -#ifdef MILVUS_GPU_VERSION -std::mutex GpuCacheMgr::global_mutex_; -std::unordered_map GpuCacheMgr::instance_; - -namespace { -constexpr int64_t G_BYTE = 1024 * 1024 * 1024; -} - -GpuCacheMgr::GpuCacheMgr(int64_t gpu_id) : gpu_id_(gpu_id) { - // All config values have been checked in Config::ValidateConfig() - server::Config& config = server::Config::GetInstance(); - - int64_t gpu_cache_cap; - config.GetGpuResourceConfigCacheCapacity(gpu_cache_cap); - int64_t cap = gpu_cache_cap * G_BYTE; - std::string header = "[CACHE GPU" + std::to_string(gpu_id) + "]"; - cache_ = std::make_shared>(cap, 1UL << 32, header); - - float gpu_mem_threshold; - config.GetGpuResourceConfigCacheThreshold(gpu_mem_threshold); - cache_->set_freemem_percent(gpu_mem_threshold); - - SetIdentity("GpuCacheMgr"); - AddGpuEnableListener(); - AddGpuCacheCapacityListener(); -} - -GpuCacheMgr::~GpuCacheMgr() { - server::Config& config = server::Config::GetInstance(); - config.CancelCallBack(server::CONFIG_GPU_RESOURCE, server::CONFIG_GPU_RESOURCE_ENABLE, identity_); -} - -DataObjPtr -GpuCacheMgr::GetIndex(const std::string& key) { - DataObjPtr obj = GetItem(key); - return obj; -} - -void -GpuCacheMgr::InsertItem(const std::string& key, const milvus::cache::DataObjPtr& data) { - if (gpu_enable_) { - CacheMgr::InsertItem(key, data); - } -} - -bool -GpuCacheMgr::Reserve(const int64_t size) { - return CacheMgr::Reserve(size); -} - -GpuCacheMgrPtr -GpuCacheMgr::GetInstance(int64_t gpu_id) { - if (instance_.find(gpu_id) == instance_.end()) { - std::lock_guard lock(global_mutex_); - if (instance_.find(gpu_id) == instance_.end()) { - instance_[gpu_id] = std::make_shared(gpu_id); - } - } - return instance_[gpu_id]; -} - -void -GpuCacheMgr::OnGpuCacheCapacityChanged(int64_t capacity) { - for (auto& iter : instance_) { - iter.second->SetCapacity(capacity * G_BYTE); - } -} - -#endif - -} // namespace cache -} // namespace milvus diff --git a/core/src/cache/GpuCacheMgr.h b/core/src/cache/GpuCacheMgr.h deleted file mode 100644 index 17a92c1d8e..0000000000 --- a/core/src/cache/GpuCacheMgr.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include -#include -#include -#include - -#include "cache/CacheMgr.h" -#include "cache/DataObj.h" -#include "config/handler/GpuResourceConfigHandler.h" - -namespace milvus { -namespace cache { - -#ifdef MILVUS_GPU_VERSION -class GpuCacheMgr; -using GpuCacheMgrPtr = std::shared_ptr; -using MutexPtr = std::shared_ptr; - -class GpuCacheMgr : public CacheMgr, public server::GpuResourceConfigHandler { - public: - explicit GpuCacheMgr(int64_t gpu_id); - - ~GpuCacheMgr(); - - DataObjPtr - GetIndex(const std::string& key); - - void - InsertItem(const std::string& key, const DataObjPtr& data); - - bool - Reserve(const int64_t size); - - static GpuCacheMgrPtr - GetInstance(int64_t gpu_id); - - protected: - void - OnGpuCacheCapacityChanged(int64_t capacity) override; - - private: - bool gpu_enable_ = true; - int64_t gpu_id_; - static std::mutex global_mutex_; - static std::unordered_map instance_; - std::string identity_; -}; -#endif - -} // namespace cache -} // namespace milvus diff --git a/core/src/cache/LRU.h b/core/src/cache/LRU.h deleted file mode 100644 index e8a853cea0..0000000000 --- a/core/src/cache/LRU.h +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include - -namespace milvus { -namespace cache { - -template -class LRU { - public: - typedef typename std::pair key_value_pair_t; - typedef typename std::list::iterator list_iterator_t; - typedef typename std::list::reverse_iterator reverse_list_iterator_t; - - explicit LRU(size_t max_size) : max_size_(max_size) { - } - - void - put(const key_t& key, const value_t& value) { - auto it = cache_items_map_.find(key); - cache_items_list_.push_front(key_value_pair_t(key, value)); - if (it != cache_items_map_.end()) { - cache_items_list_.erase(it->second); - cache_items_map_.erase(it); - } - cache_items_map_[key] = cache_items_list_.begin(); - - if (cache_items_map_.size() > max_size_) { - auto last = cache_items_list_.end(); - last--; - cache_items_map_.erase(last->first); - cache_items_list_.pop_back(); - } - } - - const value_t& - get(const key_t& key) { - auto it = cache_items_map_.find(key); - if (it == cache_items_map_.end()) { - throw std::range_error("There is no such key in cache"); - } else { - cache_items_list_.splice(cache_items_list_.begin(), cache_items_list_, it->second); - return it->second->second; - } - } - - void - erase(const key_t& key) { - auto it = cache_items_map_.find(key); - if (it != cache_items_map_.end()) { - cache_items_list_.erase(it->second); - cache_items_map_.erase(it); - } - } - - bool - exists(const key_t& key) const { - return cache_items_map_.find(key) != cache_items_map_.end(); - } - - size_t - size() const { - return cache_items_map_.size(); - } - - list_iterator_t - begin() { - iter_ = cache_items_list_.begin(); - return iter_; - } - - list_iterator_t - end() { - return cache_items_list_.end(); - } - - reverse_list_iterator_t - rbegin() { - return cache_items_list_.rbegin(); - } - - reverse_list_iterator_t - rend() { - return cache_items_list_.rend(); - } - - void - clear() { - cache_items_list_.clear(); - cache_items_map_.clear(); - } - - private: - std::list cache_items_list_; - std::unordered_map cache_items_map_; - size_t max_size_; - list_iterator_t iter_; -}; - -} // namespace cache -} // namespace milvus diff --git a/core/src/codecs/AttrsFormat.h b/core/src/codecs/AttrsFormat.h deleted file mode 100644 index 012493f18d..0000000000 --- a/core/src/codecs/AttrsFormat.h +++ /dev/null @@ -1,44 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include - -#include "segment/Attrs.h" -#include "storage/FSHandler.h" - -namespace milvus { -namespace codec { - -class AttrsFormat { - public: - virtual void - read(const storage::FSHandlerPtr& fs_ptr, segment::AttrsPtr& attrs_read) = 0; - - virtual void - write(const storage::FSHandlerPtr& fs_ptr, const segment::AttrsPtr& attr) = 0; - - virtual void - read_uids(const storage::FSHandlerPtr& fs_ptr, std::vector& uids) = 0; -}; - -using AttrsFormatPtr = std::shared_ptr; - -} // namespace codec -} // namespace milvus diff --git a/core/src/codecs/AttrsIndexFormat.h b/core/src/codecs/AttrsIndexFormat.h deleted file mode 100644 index 7ab56d3979..0000000000 --- a/core/src/codecs/AttrsIndexFormat.h +++ /dev/null @@ -1,33 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -namespace milvus { -namespace codec { - -class AttrsIndexFormat { - // public: - // virtual AttrsIndex - // read() = 0; - // - // virtual void - // write(AttrsIndex attrs_index) = 0; -}; - -} // namespace codec -} // namespace milvus diff --git a/core/src/codecs/Codec.h b/core/src/codecs/Codec.h deleted file mode 100644 index 1148b01416..0000000000 --- a/core/src/codecs/Codec.h +++ /dev/null @@ -1,63 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include "AttrsFormat.h" -#include "AttrsIndexFormat.h" -#include "DeletedDocsFormat.h" -#include "IdBloomFilterFormat.h" -#include "IdIndexFormat.h" -#include "VectorIndexFormat.h" -#include "VectorsFormat.h" - -namespace milvus { -namespace codec { - -class Codec { - public: - virtual VectorsFormatPtr - GetVectorsFormat() = 0; - - virtual AttrsFormatPtr - GetAttrsFormat() = 0; - - virtual VectorIndexFormatPtr - GetVectorIndexFormat() = 0; - - virtual DeletedDocsFormatPtr - GetDeletedDocsFormat() = 0; - - virtual IdBloomFilterFormatPtr - GetIdBloomFilterFormat() = 0; - - // TODO(zhiru) - /* - virtual AttrsFormat - GetAttrsFormat() = 0; - - virtual AttrsIndexFormat - GetAttrsIndexFormat() = 0; - - virtual IdIndexFormat - GetIdIndexFormat() = 0; - - */ -}; - -} // namespace codec -} // namespace milvus diff --git a/core/src/codecs/DeletedDocsFormat.h b/core/src/codecs/DeletedDocsFormat.h deleted file mode 100644 index 0c519420cb..0000000000 --- a/core/src/codecs/DeletedDocsFormat.h +++ /dev/null @@ -1,43 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include - -#include "segment/DeletedDocs.h" -#include "storage/FSHandler.h" - -namespace milvus { -namespace codec { - -class DeletedDocsFormat { - public: - virtual void - read(const storage::FSHandlerPtr& fs_ptr, segment::DeletedDocsPtr& deleted_docs) = 0; - - virtual void - write(const storage::FSHandlerPtr& fs_ptr, const segment::DeletedDocsPtr& deleted_docs) = 0; - - virtual void - readSize(const storage::FSHandlerPtr& fs_ptr, size_t& size) = 0; -}; - -using DeletedDocsFormatPtr = std::shared_ptr; - -} // namespace codec -} // namespace milvus diff --git a/core/src/codecs/IdBloomFilterFormat.h b/core/src/codecs/IdBloomFilterFormat.h deleted file mode 100644 index a374dd70cb..0000000000 --- a/core/src/codecs/IdBloomFilterFormat.h +++ /dev/null @@ -1,43 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include - -#include "segment/IdBloomFilter.h" -#include "storage/FSHandler.h" - -namespace milvus { -namespace codec { - -class IdBloomFilterFormat { - public: - virtual void - read(const storage::FSHandlerPtr& fs_ptr, segment::IdBloomFilterPtr& id_bloom_filter_ptr) = 0; - - virtual void - write(const storage::FSHandlerPtr& fs_ptr, const segment::IdBloomFilterPtr& id_bloom_filter_ptr) = 0; - - virtual void - create(const storage::FSHandlerPtr& fs_ptr, segment::IdBloomFilterPtr& id_bloom_filter_ptr) = 0; -}; - -using IdBloomFilterFormatPtr = std::shared_ptr; - -} // namespace codec -} // namespace milvus diff --git a/core/src/codecs/IdIndexFormat.h b/core/src/codecs/IdIndexFormat.h deleted file mode 100644 index 63c71e5ecf..0000000000 --- a/core/src/codecs/IdIndexFormat.h +++ /dev/null @@ -1,33 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -namespace milvus { -namespace codec { - -class IdIndexFormat { - // public: - // virtual IdIndex - // read() = 0; - // - // virtual void - // write(IdIndex id_index) = 0; -}; - -} // namespace codec -} // namespace milvus diff --git a/core/src/codecs/VectorIndexFormat.h b/core/src/codecs/VectorIndexFormat.h deleted file mode 100644 index d25fcd712c..0000000000 --- a/core/src/codecs/VectorIndexFormat.h +++ /dev/null @@ -1,42 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include - -#include "segment/VectorIndex.h" -#include "storage/FSHandler.h" - -namespace milvus { -namespace codec { - -class VectorIndexFormat { - public: - virtual void - read(const storage::FSHandlerPtr& fs_ptr, const std::string& location, segment::VectorIndexPtr& vector_index) = 0; - - virtual void - write(const storage::FSHandlerPtr& fs_ptr, const std::string& location, - const segment::VectorIndexPtr& vector_index) = 0; -}; - -using VectorIndexFormatPtr = std::shared_ptr; - -} // namespace codec -} // namespace milvus diff --git a/core/src/codecs/VectorsFormat.h b/core/src/codecs/VectorsFormat.h deleted file mode 100644 index 5227f9a6a0..0000000000 --- a/core/src/codecs/VectorsFormat.h +++ /dev/null @@ -1,48 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include - -#include "segment/Vectors.h" -#include "storage/FSHandler.h" - -namespace milvus { -namespace codec { - -class VectorsFormat { - public: - virtual void - read(const storage::FSHandlerPtr& fs_ptr, segment::VectorsPtr& vectors_read) = 0; - - virtual void - write(const storage::FSHandlerPtr& fs_ptr, const segment::VectorsPtr& vectors) = 0; - - virtual void - read_uids(const storage::FSHandlerPtr& fs_ptr, std::vector& uids) = 0; - - virtual void - read_vectors(const storage::FSHandlerPtr& fs_ptr, off_t offset, size_t num_bytes, - std::vector& raw_vectors) = 0; -}; - -using VectorsFormatPtr = std::shared_ptr; - -} // namespace codec -} // namespace milvus diff --git a/core/src/codecs/default/DefaultAttrsFormat.cpp b/core/src/codecs/default/DefaultAttrsFormat.cpp deleted file mode 100644 index 7fce91a0a5..0000000000 --- a/core/src/codecs/default/DefaultAttrsFormat.cpp +++ /dev/null @@ -1,222 +0,0 @@ -// 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 limitations -// under the License. - -#include "codecs/default/DefaultAttrsFormat.h" - -#include -#include -#include -#include -#include - -#include - -#include "utils/Exception.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" - -namespace milvus { -namespace codec { - -void -DefaultAttrsFormat::read_attrs_internal(const storage::FSHandlerPtr& fs_ptr, const std::string& file_path, off_t offset, - size_t num, std::vector& raw_attrs, size_t& nbytes) { - auto open_res = fs_ptr->reader_ptr_->open(file_path.c_str()); - fiu_do_on("read_attrs_internal_open_file_fail", open_res = false); - if (!open_res) { - std::string err_msg = "Failed to open file: " + file_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_CANNOT_CREATE_FILE, err_msg); - } - - fs_ptr->reader_ptr_->read(&nbytes, sizeof(size_t)); - - num = std::min(num, nbytes - offset); - - offset += sizeof(size_t); - fs_ptr->reader_ptr_->seekg(offset); - - raw_attrs.resize(num / sizeof(uint8_t)); - fs_ptr->reader_ptr_->read(raw_attrs.data(), num); - - fs_ptr->reader_ptr_->close(); -} - -void -DefaultAttrsFormat::read_uids_internal(const storage::FSHandlerPtr& fs_ptr, const std::string& file_path, - std::vector& uids) { - auto open_res = fs_ptr->reader_ptr_->open(file_path.c_str()); - fiu_do_on("read_uids_internal_open_file_fail", open_res = false); - if (!open_res) { - std::string err_msg = "Failed to open file: " + file_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_CANNOT_CREATE_FILE, err_msg); - } - - size_t num_bytes; - fs_ptr->reader_ptr_->read(&num_bytes, sizeof(size_t)); - - uids.resize(num_bytes / sizeof(int64_t)); - fs_ptr->reader_ptr_->read(uids.data(), num_bytes); - - fs_ptr->reader_ptr_->read(uids.data(), num_bytes); -} - -void -DefaultAttrsFormat::read(const milvus::storage::FSHandlerPtr& fs_ptr, milvus::segment::AttrsPtr& attrs_read) { - const std::lock_guard lock(mutex_); - - std::string dir_path = fs_ptr->operation_ptr_->GetDirectory(); - auto is_directory = boost::filesystem::is_directory(dir_path); - fiu_do_on("read_id_directory_false", is_directory = false); - if (!is_directory) { - std::string err_msg = "Directory: " + dir_path + "does not exist"; - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_INVALID_ARGUMENT, err_msg); - } - - boost::filesystem::path target_path(dir_path); - typedef boost::filesystem::directory_iterator d_it; - d_it it_end; - d_it uid_it(target_path); - std::vector uids; - for (; uid_it != it_end; ++uid_it) { - const auto& path = uid_it->path(); - if (path.extension().string() == user_id_extension_) { - read_uids_internal(fs_ptr, path.string(), uids); - break; - } - } - - d_it it(target_path); - for (; it != it_end; ++it) { - const auto& path = it->path(); - if (path.extension().string() == raw_attr_extension_) { - auto file_name = path.filename().string(); - auto field_name = file_name.substr(0, file_name.size() - 3); - std::vector attr_list; - size_t nbytes; - read_attrs_internal(fs_ptr, path.string(), 0, INT64_MAX, attr_list, nbytes); - milvus::segment::AttrPtr attr = - std::make_shared(attr_list, nbytes, uids, field_name); - attrs_read->attrs.insert(std::pair(field_name, attr)); - } - } -} - -void -DefaultAttrsFormat::write(const milvus::storage::FSHandlerPtr& fs_ptr, const milvus::segment::AttrsPtr& attrs_ptr) { - const std::lock_guard lock(mutex_); - - TimeRecorder rc("write attributes"); - - std::string dir_path = fs_ptr->operation_ptr_->GetDirectory(); - - auto it = attrs_ptr->attrs.begin(); - if (it == attrs_ptr->attrs.end()) { - // std::string err_msg = "Attributes is null"; - // LOG_ENGINE_ERROR_ << err_msg; - return; - } - -#if 0 - const std::string uid_file_path = dir_path + "/" + it->second->GetCollectionId() + user_id_extension_; - - int uid_fd = open(uid_file_path.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 00664); - if (uid_fd == -1) { - std::string err_msg = "Failed to open file: " + uid_file_path + ", error: " + std::strerror(errno); - ENGINE_LOG_ERROR << err_msg; - throw Exception(SERVER_CANNOT_CREATE_FILE, err_msg); - } - size_t uid_num_bytes = it->second->GetUids().size() * sizeof(int64_t); - if (::write(uid_fd, &uid_num_bytes, sizeof(size_t)) == -1) { - std::string err_msg = "Failed to write to file" + uid_file_path + ", error: " + std::strerror(errno); - ENGINE_LOG_ERROR << err_msg; - throw Exception(SERVER_WRITE_ERROR, err_msg); - } - if (::write(uid_fd, it->second->GetUids().data(), uid_num_bytes) == -1) { - std::string err_msg = "Failed to write to file" + uid_file_path + ", error: " + std::strerror(errno); - ENGINE_LOG_ERROR << err_msg; - throw Exception(SERVER_WRITE_ERROR, err_msg); - } - if (::close(uid_fd) == -1) { - std::string err_msg = "Failed to close file: " + uid_file_path + ", error: " + std::strerror(errno); - ENGINE_LOG_ERROR << err_msg; - throw Exception(SERVER_WRITE_ERROR, err_msg); - } - rc.RecordSection("write uids done"); -#endif - - for (; it != attrs_ptr->attrs.end(); it++) { - const std::string ra_file_path = dir_path + "/" + it->second->GetName() + raw_attr_extension_; - - int ra_fd = open(ra_file_path.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 00664); - if (ra_fd == -1) { - std::string err_msg = "Failed to open file: " + ra_file_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_CANNOT_CREATE_FILE, err_msg); - } - - size_t ra_num_bytes = it->second->GetNbytes(); - if (::write(ra_fd, &ra_num_bytes, sizeof(size_t)) == -1) { - std::string err_msg = "Failed to write to file: " + ra_file_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_WRITE_ERROR, err_msg); - } - if (::write(ra_fd, it->second->GetData().data(), ra_num_bytes) == -1) { - std::string err_msg = "Failed to write to file: " + ra_file_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_WRITE_ERROR, err_msg); - } - if (::close(ra_fd) == -1) { - std::string err_msg = "Failed to close file: " + ra_file_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_WRITE_ERROR, err_msg); - } - - rc.RecordSection("write rv done"); - } -} - -void -DefaultAttrsFormat::read_uids(const milvus::storage::FSHandlerPtr& fs_ptr, std::vector& uids) { - const std::lock_guard lock(mutex_); - - std::string dir_path = fs_ptr->operation_ptr_->GetDirectory(); - auto is_directory = boost::filesystem::is_directory(dir_path); - fiu_do_on("is_directory_false", is_directory = false); - if (!is_directory) { - std::string err_msg = "Directory: " + dir_path + "does not exist"; - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_INVALID_ARGUMENT, err_msg); - } - - boost::filesystem::path target_path(dir_path); - typedef boost::filesystem::directory_iterator d_it; - d_it it_end; - d_it it(target_path); - // for (auto& it : boost::filesystem::directory_iterator(dir_path)) { - for (; it != it_end; ++it) { - const auto& path = it->path(); - if (path.extension().string() == user_id_extension_) { - read_uids_internal(fs_ptr, path.string(), uids); - } - } -} - -} // namespace codec -} // namespace milvus diff --git a/core/src/codecs/default/DefaultAttrsFormat.h b/core/src/codecs/default/DefaultAttrsFormat.h deleted file mode 100644 index 9d59e53e02..0000000000 --- a/core/src/codecs/default/DefaultAttrsFormat.h +++ /dev/null @@ -1,68 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include -#include - -#include "codecs/AttrsFormat.h" -#include "segment/Attrs.h" - -namespace milvus { -namespace codec { - -class DefaultAttrsFormat : public AttrsFormat { - public: - DefaultAttrsFormat() = default; - - void - read(const storage::FSHandlerPtr& fs_ptr, segment::AttrsPtr& attrs_read) override; - - void - write(const storage::FSHandlerPtr& fs_ptr, const segment::AttrsPtr& attr) override; - - void - read_uids(const storage::FSHandlerPtr& fs_ptr, std::vector& uids) override; - - // No copy and move - DefaultAttrsFormat(const DefaultAttrsFormat&) = delete; - DefaultAttrsFormat(DefaultAttrsFormat&&) = delete; - - DefaultAttrsFormat& - operator=(const DefaultAttrsFormat&) = delete; - DefaultAttrsFormat& - operator=(DefaultAttrsFormat&&) = delete; - - private: - void - read_attrs_internal(const storage::FSHandlerPtr& fs_ptr, const std::string&, off_t, size_t, std::vector&, - size_t&); - - void - read_uids_internal(const storage::FSHandlerPtr& fs_ptr, const std::string&, std::vector&); - - private: - std::mutex mutex_; - - const std::string raw_attr_extension_ = ".ra"; - const std::string user_id_extension_ = ".uid"; -}; - -} // namespace codec -} // namespace milvus diff --git a/core/src/codecs/default/DefaultCodec.cpp b/core/src/codecs/default/DefaultCodec.cpp deleted file mode 100644 index d5dfd2d63a..0000000000 --- a/core/src/codecs/default/DefaultCodec.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// 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 limitations -// under the License. - -#include "codecs/default/DefaultCodec.h" - -#include - -#include "DefaultAttrsFormat.h" -#include "DefaultDeletedDocsFormat.h" -#include "DefaultIdBloomFilterFormat.h" -#include "DefaultVectorIndexFormat.h" -#include "DefaultVectorsFormat.h" - -namespace milvus { -namespace codec { - -DefaultCodec::DefaultCodec() { - vectors_format_ptr_ = std::make_shared(); - attrs_format_ptr_ = std::make_shared(); - vector_index_format_ptr_ = std::make_shared(); - deleted_docs_format_ptr_ = std::make_shared(); - id_bloom_filter_format_ptr_ = std::make_shared(); -} - -VectorsFormatPtr -DefaultCodec::GetVectorsFormat() { - return vectors_format_ptr_; -} - -AttrsFormatPtr -DefaultCodec::GetAttrsFormat() { - return attrs_format_ptr_; -} - -VectorIndexFormatPtr -DefaultCodec::GetVectorIndexFormat() { - return vector_index_format_ptr_; -} - -DeletedDocsFormatPtr -DefaultCodec::GetDeletedDocsFormat() { - return deleted_docs_format_ptr_; -} - -IdBloomFilterFormatPtr -DefaultCodec::GetIdBloomFilterFormat() { - return id_bloom_filter_format_ptr_; -} - -} // namespace codec -} // namespace milvus diff --git a/core/src/codecs/default/DefaultCodec.h b/core/src/codecs/default/DefaultCodec.h deleted file mode 100644 index c53695de8c..0000000000 --- a/core/src/codecs/default/DefaultCodec.h +++ /dev/null @@ -1,53 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include "codecs/Codec.h" - -namespace milvus { -namespace codec { - -class DefaultCodec : public Codec { - public: - DefaultCodec(); - - VectorsFormatPtr - GetVectorsFormat() override; - - AttrsFormatPtr - GetAttrsFormat() override; - - VectorIndexFormatPtr - GetVectorIndexFormat() override; - - DeletedDocsFormatPtr - GetDeletedDocsFormat() override; - - IdBloomFilterFormatPtr - GetIdBloomFilterFormat() override; - - private: - VectorsFormatPtr vectors_format_ptr_; - AttrsFormatPtr attrs_format_ptr_; - VectorIndexFormatPtr vector_index_format_ptr_; - DeletedDocsFormatPtr deleted_docs_format_ptr_; - IdBloomFilterFormatPtr id_bloom_filter_format_ptr_; -}; - -} // namespace codec -} // namespace milvus diff --git a/core/src/codecs/default/DefaultDeletedDocsFormat.cpp b/core/src/codecs/default/DefaultDeletedDocsFormat.cpp deleted file mode 100644 index 8651a05be7..0000000000 --- a/core/src/codecs/default/DefaultDeletedDocsFormat.cpp +++ /dev/null @@ -1,180 +0,0 @@ -// 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 limitations -// under the License. - -#include "codecs/default/DefaultDeletedDocsFormat.h" - -#include -#include - -#define BOOST_NO_CXX11_SCOPED_ENUMS -#include -#undef BOOST_NO_CXX11_SCOPED_ENUMS -#include -#include -#include - -#include "segment/Types.h" -#include "utils/Exception.h" -#include "utils/Log.h" - -namespace milvus { -namespace codec { - -void -DefaultDeletedDocsFormat::read(const storage::FSHandlerPtr& fs_ptr, segment::DeletedDocsPtr& deleted_docs) { - const std::lock_guard lock(mutex_); - - std::string dir_path = fs_ptr->operation_ptr_->GetDirectory(); - const std::string del_file_path = dir_path + "/" + deleted_docs_filename_; - - int del_fd = open(del_file_path.c_str(), O_RDONLY, 00664); - if (del_fd == -1) { - std::string err_msg = "Failed to open file: " + del_file_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_CANNOT_CREATE_FILE, err_msg); - } - - size_t num_bytes; - if (::read(del_fd, &num_bytes, sizeof(size_t)) == -1) { - std::string err_msg = "Failed to read from file: " + del_file_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_WRITE_ERROR, err_msg); - } - - auto deleted_docs_size = num_bytes / sizeof(segment::offset_t); - std::vector deleted_docs_list; - deleted_docs_list.resize(deleted_docs_size); - - if (::read(del_fd, deleted_docs_list.data(), num_bytes) == -1) { - std::string err_msg = "Failed to read from file: " + del_file_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_WRITE_ERROR, err_msg); - } - - deleted_docs = std::make_shared(deleted_docs_list); - - if (::close(del_fd) == -1) { - std::string err_msg = "Failed to close file: " + del_file_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_WRITE_ERROR, err_msg); - } -} - -void -DefaultDeletedDocsFormat::write(const storage::FSHandlerPtr& fs_ptr, const segment::DeletedDocsPtr& deleted_docs) { - const std::lock_guard lock(mutex_); - - std::string dir_path = fs_ptr->operation_ptr_->GetDirectory(); - const std::string del_file_path = dir_path + "/" + deleted_docs_filename_; - - // Create a temporary file from the existing file - const std::string temp_path = dir_path + "/" + "temp_del"; - bool exists = boost::filesystem::exists(del_file_path); - if (exists) { - boost::filesystem::copy_file(del_file_path, temp_path, boost::filesystem::copy_option::fail_if_exists); - } - - // Write to the temp file, in order to avoid possible race condition with search (concurrent read and write) - int del_fd = open(temp_path.c_str(), O_RDWR | O_CREAT, 00664); - if (del_fd == -1) { - std::string err_msg = "Failed to open file: " + temp_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_CANNOT_CREATE_FILE, err_msg); - } - - size_t old_num_bytes; - if (exists) { - if (::read(del_fd, &old_num_bytes, sizeof(size_t)) == -1) { - std::string err_msg = "Failed to read from file: " + temp_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_WRITE_ERROR, err_msg); - } - } else { - old_num_bytes = 0; - } - - auto deleted_docs_list = deleted_docs->GetDeletedDocs(); - size_t new_num_bytes = old_num_bytes + sizeof(segment::offset_t) * deleted_docs->GetSize(); - - // rewind and overwrite with the new_num_bytes - int off = lseek(del_fd, 0, SEEK_SET); - if (off == -1) { - std::string err_msg = "Failed to seek file: " + temp_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_WRITE_ERROR, err_msg); - } - if (::write(del_fd, &new_num_bytes, sizeof(size_t)) == -1) { - std::string err_msg = "Failed to write to file" + temp_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_WRITE_ERROR, err_msg); - } - - // Move to the end of file and append - off = lseek(del_fd, 0, SEEK_END); - if (off == -1) { - std::string err_msg = "Failed to seek file: " + temp_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_WRITE_ERROR, err_msg); - } - if (::write(del_fd, deleted_docs_list.data(), sizeof(segment::offset_t) * deleted_docs->GetSize()) == -1) { - std::string err_msg = "Failed to write to file" + temp_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_WRITE_ERROR, err_msg); - } - - if (::close(del_fd) == -1) { - std::string err_msg = "Failed to close file: " + temp_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_WRITE_ERROR, err_msg); - } - - // Move temp file to delete file - boost::filesystem::rename(temp_path, del_file_path); -} - -void -DefaultDeletedDocsFormat::readSize(const storage::FSHandlerPtr& fs_ptr, size_t& size) { - const std::lock_guard lock(mutex_); - - std::string dir_path = fs_ptr->operation_ptr_->GetDirectory(); - const std::string del_file_path = dir_path + "/" + deleted_docs_filename_; - - int del_fd = open(del_file_path.c_str(), O_RDONLY, 00664); - if (del_fd == -1) { - std::string err_msg = "Failed to open file: " + del_file_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_CANNOT_CREATE_FILE, err_msg); - } - - size_t num_bytes; - if (::read(del_fd, &num_bytes, sizeof(size_t)) == -1) { - std::string err_msg = "Failed to read from file: " + del_file_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_WRITE_ERROR, err_msg); - } - - size = num_bytes / sizeof(segment::offset_t); - - if (::close(del_fd) == -1) { - std::string err_msg = "Failed to close file: " + del_file_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_WRITE_ERROR, err_msg); - } -} - -} // namespace codec -} // namespace milvus diff --git a/core/src/codecs/default/DefaultDeletedDocsFormat.h b/core/src/codecs/default/DefaultDeletedDocsFormat.h deleted file mode 100644 index 06aff4c563..0000000000 --- a/core/src/codecs/default/DefaultDeletedDocsFormat.h +++ /dev/null @@ -1,57 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include - -#include "codecs/DeletedDocsFormat.h" - -namespace milvus { -namespace codec { - -class DefaultDeletedDocsFormat : public DeletedDocsFormat { - public: - DefaultDeletedDocsFormat() = default; - - void - read(const storage::FSHandlerPtr& fs_ptr, segment::DeletedDocsPtr& deleted_docs) override; - - void - write(const storage::FSHandlerPtr& fs_ptr, const segment::DeletedDocsPtr& deleted_docs) override; - - void - readSize(const storage::FSHandlerPtr& fs_ptr, size_t& size) override; - - // No copy and move - DefaultDeletedDocsFormat(const DefaultDeletedDocsFormat&) = delete; - DefaultDeletedDocsFormat(DefaultDeletedDocsFormat&&) = delete; - - DefaultDeletedDocsFormat& - operator=(const DefaultDeletedDocsFormat&) = delete; - DefaultDeletedDocsFormat& - operator=(DefaultDeletedDocsFormat&&) = delete; - - private: - std::mutex mutex_; - - const std::string deleted_docs_filename_ = "deleted_docs"; -}; - -} // namespace codec -} // namespace milvus diff --git a/core/src/codecs/default/DefaultIdBloomFilterFormat.cpp b/core/src/codecs/default/DefaultIdBloomFilterFormat.cpp deleted file mode 100644 index fb2eb9e535..0000000000 --- a/core/src/codecs/default/DefaultIdBloomFilterFormat.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// 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 limitations -// under the License. - -#include "codecs/default/DefaultIdBloomFilterFormat.h" - -#include -#include -#include - -#include "utils/Exception.h" -#include "utils/Log.h" - -namespace milvus { -namespace codec { - -constexpr unsigned int bloom_filter_capacity = 500000; -constexpr double bloom_filter_error_rate = 0.01; - -void -DefaultIdBloomFilterFormat::read(const storage::FSHandlerPtr& fs_ptr, segment::IdBloomFilterPtr& id_bloom_filter_ptr) { - const std::lock_guard lock(mutex_); - - std::string dir_path = fs_ptr->operation_ptr_->GetDirectory(); - const std::string bloom_filter_file_path = dir_path + "/" + bloom_filter_filename_; - scaling_bloom_t* bloom_filter = - new_scaling_bloom_from_file(bloom_filter_capacity, bloom_filter_error_rate, bloom_filter_file_path.c_str()); - fiu_do_on("bloom_filter_nullptr", bloom_filter = nullptr); - if (bloom_filter == nullptr) { - std::string err_msg = - "Failed to read bloom filter from file: " + bloom_filter_file_path + ". " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_UNEXPECTED_ERROR, err_msg); - } - id_bloom_filter_ptr = std::make_shared(bloom_filter); -} - -void -DefaultIdBloomFilterFormat::write(const storage::FSHandlerPtr& fs_ptr, - const segment::IdBloomFilterPtr& id_bloom_filter_ptr) { - const std::lock_guard lock(mutex_); - - std::string dir_path = fs_ptr->operation_ptr_->GetDirectory(); - const std::string bloom_filter_file_path = dir_path + "/" + bloom_filter_filename_; - if (scaling_bloom_flush(id_bloom_filter_ptr->GetBloomFilter()) == -1) { - std::string err_msg = - "Failed to write bloom filter to file: " + bloom_filter_file_path + ". " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_UNEXPECTED_ERROR, err_msg); - } -} - -void -DefaultIdBloomFilterFormat::create(const storage::FSHandlerPtr& fs_ptr, - segment::IdBloomFilterPtr& id_bloom_filter_ptr) { - std::string dir_path = fs_ptr->operation_ptr_->GetDirectory(); - const std::string bloom_filter_file_path = dir_path + "/" + bloom_filter_filename_; - scaling_bloom_t* bloom_filter = - new_scaling_bloom(bloom_filter_capacity, bloom_filter_error_rate, bloom_filter_file_path.c_str()); - if (bloom_filter == nullptr) { - std::string err_msg = - "Failed to read bloom filter from file: " + bloom_filter_file_path + ". " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_UNEXPECTED_ERROR, err_msg); - } - id_bloom_filter_ptr = std::make_shared(bloom_filter); -} - -} // namespace codec -} // namespace milvus diff --git a/core/src/codecs/default/DefaultIdBloomFilterFormat.h b/core/src/codecs/default/DefaultIdBloomFilterFormat.h deleted file mode 100644 index e35daad9ef..0000000000 --- a/core/src/codecs/default/DefaultIdBloomFilterFormat.h +++ /dev/null @@ -1,59 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include - -#include "codecs/IdBloomFilterFormat.h" -#include "segment/IdBloomFilter.h" -#include "storage/disk/DiskOperation.h" - -namespace milvus { -namespace codec { - -class DefaultIdBloomFilterFormat : public IdBloomFilterFormat { - public: - DefaultIdBloomFilterFormat() = default; - - void - read(const storage::FSHandlerPtr& fs_ptr, segment::IdBloomFilterPtr& id_bloom_filter_ptr) override; - - void - write(const storage::FSHandlerPtr& fs_ptr, const segment::IdBloomFilterPtr& id_bloom_filter_ptr) override; - - void - create(const storage::FSHandlerPtr& fs_ptr, segment::IdBloomFilterPtr& id_bloom_filter_ptr) override; - - // No copy and move - DefaultIdBloomFilterFormat(const DefaultIdBloomFilterFormat&) = delete; - DefaultIdBloomFilterFormat(DefaultIdBloomFilterFormat&&) = delete; - - DefaultIdBloomFilterFormat& - operator=(const DefaultIdBloomFilterFormat&) = delete; - DefaultIdBloomFilterFormat& - operator=(DefaultIdBloomFilterFormat&&) = delete; - - private: - std::mutex mutex_; - - const std::string bloom_filter_filename_ = "bloom_filter"; -}; - -} // namespace codec -} // namespace milvus diff --git a/core/src/codecs/default/DefaultVectorIndexFormat.cpp b/core/src/codecs/default/DefaultVectorIndexFormat.cpp deleted file mode 100644 index e513d2abe6..0000000000 --- a/core/src/codecs/default/DefaultVectorIndexFormat.cpp +++ /dev/null @@ -1,159 +0,0 @@ -// 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 limitations -// under the License. - -#include -#include - -#include "codecs/default/DefaultVectorIndexFormat.h" -#include "knowhere/common/BinarySet.h" -#include "knowhere/index/vector_index/VecIndex.h" -#include "knowhere/index/vector_index/VecIndexFactory.h" -#include "segment/VectorIndex.h" -#include "utils/Exception.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" - -namespace milvus { -namespace codec { - -knowhere::VecIndexPtr -DefaultVectorIndexFormat::read_internal(const storage::FSHandlerPtr& fs_ptr, const std::string& path) { - milvus::TimeRecorder recorder("read_index"); - knowhere::BinarySet load_data_list; - - recorder.RecordSection("Start"); - if (!fs_ptr->reader_ptr_->open(path)) { - LOG_ENGINE_ERROR_ << "Fail to open vector index: " << path; - return nullptr; - } - - int64_t length = fs_ptr->reader_ptr_->length(); - if (length <= 0) { - LOG_ENGINE_ERROR_ << "Invalid vector index length: " << path; - return nullptr; - } - - int64_t rp = 0; - fs_ptr->reader_ptr_->seekg(0); - - int32_t current_type = 0; - fs_ptr->reader_ptr_->read(¤t_type, sizeof(current_type)); - rp += sizeof(current_type); - fs_ptr->reader_ptr_->seekg(rp); - - LOG_ENGINE_DEBUG_ << "Start to read_index(" << path << ") length: " << length << " bytes"; - while (rp < length) { - size_t meta_length; - fs_ptr->reader_ptr_->read(&meta_length, sizeof(meta_length)); - rp += sizeof(meta_length); - fs_ptr->reader_ptr_->seekg(rp); - - auto meta = new char[meta_length]; - fs_ptr->reader_ptr_->read(meta, meta_length); - rp += meta_length; - fs_ptr->reader_ptr_->seekg(rp); - - size_t bin_length; - fs_ptr->reader_ptr_->read(&bin_length, sizeof(bin_length)); - rp += sizeof(bin_length); - fs_ptr->reader_ptr_->seekg(rp); - - auto bin = new uint8_t[bin_length]; - fs_ptr->reader_ptr_->read(bin, bin_length); - rp += bin_length; - fs_ptr->reader_ptr_->seekg(rp); - - std::shared_ptr binptr(bin); - load_data_list.Append(std::string(meta, meta_length), binptr, bin_length); - delete[] meta; - } - fs_ptr->reader_ptr_->close(); - - double span = recorder.RecordSection("End"); - double rate = length * 1000000.0 / span / 1024 / 1024; - LOG_ENGINE_DEBUG_ << "read_index(" << path << ") rate " << rate << "MB/s"; - - knowhere::VecIndexFactory& vec_index_factory = knowhere::VecIndexFactory::GetInstance(); - auto index = - vec_index_factory.CreateVecIndex(knowhere::OldIndexTypeToStr(current_type), knowhere::IndexMode::MODE_CPU); - if (index != nullptr) { - index->Load(load_data_list); - index->UpdateIndexSize(); - LOG_ENGINE_DEBUG_ << "index file size " << length << " index size " << index->IndexSize(); - } else { - LOG_ENGINE_ERROR_ << "Fail to create vector index: " << path; - } - - return index; -} - -void -DefaultVectorIndexFormat::read(const storage::FSHandlerPtr& fs_ptr, const std::string& location, - segment::VectorIndexPtr& vector_index) { - const std::lock_guard lock(mutex_); - - std::string dir_path = fs_ptr->operation_ptr_->GetDirectory(); - if (!boost::filesystem::is_directory(dir_path)) { - std::string err_msg = "Directory: " + dir_path + "does not exist"; - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_INVALID_ARGUMENT, err_msg); - } - - knowhere::VecIndexPtr index = read_internal(fs_ptr, location); - vector_index->SetVectorIndex(index); -} - -void -DefaultVectorIndexFormat::write(const storage::FSHandlerPtr& fs_ptr, const std::string& location, - const segment::VectorIndexPtr& vector_index) { - const std::lock_guard lock(mutex_); - - milvus::TimeRecorder recorder("write_index"); - - knowhere::VecIndexPtr index = vector_index->GetVectorIndex(); - - auto binaryset = index->Serialize(knowhere::Config()); - int32_t index_type = knowhere::StrToOldIndexType(index->index_type()); - - recorder.RecordSection("Start"); - if (!fs_ptr->writer_ptr_->open(location)) { - LOG_ENGINE_ERROR_ << "Fail to open vector index: " << location; - return; - } - - fs_ptr->writer_ptr_->write(&index_type, sizeof(index_type)); - - for (auto& iter : binaryset.binary_map_) { - auto meta = iter.first.c_str(); - size_t meta_length = iter.first.length(); - fs_ptr->writer_ptr_->write(&meta_length, sizeof(meta_length)); - fs_ptr->writer_ptr_->write((void*)meta, meta_length); - - auto binary = iter.second; - int64_t binary_length = binary->size; - fs_ptr->writer_ptr_->write(&binary_length, sizeof(binary_length)); - fs_ptr->writer_ptr_->write((void*)binary->data.get(), binary_length); - } - fs_ptr->writer_ptr_->close(); - - double span = recorder.RecordSection("End"); - double rate = fs_ptr->writer_ptr_->length() * 1000000.0 / span / 1024 / 1024; - LOG_ENGINE_DEBUG_ << "write_index(" << location << ") rate " << rate << "MB/s"; -} - -} // namespace codec -} // namespace milvus diff --git a/core/src/codecs/default/DefaultVectorIndexFormat.h b/core/src/codecs/default/DefaultVectorIndexFormat.h deleted file mode 100644 index 945ff31f45..0000000000 --- a/core/src/codecs/default/DefaultVectorIndexFormat.h +++ /dev/null @@ -1,60 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include - -#include "codecs/VectorIndexFormat.h" - -namespace milvus { -namespace codec { - -class DefaultVectorIndexFormat : public VectorIndexFormat { - public: - DefaultVectorIndexFormat() = default; - - void - read(const storage::FSHandlerPtr& fs_ptr, const std::string& location, - segment::VectorIndexPtr& vector_index) override; - - void - write(const storage::FSHandlerPtr& fs_ptr, const std::string& location, - const segment::VectorIndexPtr& vector_index) override; - - // No copy and move - DefaultVectorIndexFormat(const DefaultVectorIndexFormat&) = delete; - DefaultVectorIndexFormat(DefaultVectorIndexFormat&&) = delete; - - DefaultVectorIndexFormat& - operator=(const DefaultVectorIndexFormat&) = delete; - DefaultVectorIndexFormat& - operator=(DefaultVectorIndexFormat&&) = delete; - - private: - knowhere::VecIndexPtr - read_internal(const storage::FSHandlerPtr& fs_ptr, const std::string& path); - - private: - std::mutex mutex_; - - const std::string vector_index_extension_ = ""; -}; - -} // namespace codec -} // namespace milvus diff --git a/core/src/codecs/default/DefaultVectorsFormat.cpp b/core/src/codecs/default/DefaultVectorsFormat.cpp deleted file mode 100644 index d99dcbfbca..0000000000 --- a/core/src/codecs/default/DefaultVectorsFormat.cpp +++ /dev/null @@ -1,190 +0,0 @@ -// 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 limitations -// under the License. - -#include "codecs/default/DefaultVectorsFormat.h" - -#include -#include -#include - -#include - -#include "utils/Exception.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" - -namespace milvus { -namespace codec { - -void -DefaultVectorsFormat::read_vectors_internal(const storage::FSHandlerPtr& fs_ptr, const std::string& file_path, - off_t offset, size_t num, std::vector& raw_vectors) { - if (!fs_ptr->reader_ptr_->open(file_path.c_str())) { - std::string err_msg = "Failed to open file: " + file_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_CANNOT_OPEN_FILE, err_msg); - } - - size_t num_bytes; - fs_ptr->reader_ptr_->read(&num_bytes, sizeof(size_t)); - - num = std::min(num, num_bytes - offset); - - offset += sizeof(size_t); // Beginning of file is num_bytes - fs_ptr->reader_ptr_->seekg(offset); - - raw_vectors.resize(num / sizeof(uint8_t)); - fs_ptr->reader_ptr_->read(raw_vectors.data(), num); - - fs_ptr->reader_ptr_->close(); -} - -void -DefaultVectorsFormat::read_uids_internal(const storage::FSHandlerPtr& fs_ptr, const std::string& file_path, - std::vector& uids) { - if (!fs_ptr->reader_ptr_->open(file_path.c_str())) { - std::string err_msg = "Failed to open file: " + file_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_CANNOT_OPEN_FILE, err_msg); - } - - size_t num_bytes; - fs_ptr->reader_ptr_->read(&num_bytes, sizeof(size_t)); - - uids.resize(num_bytes / sizeof(segment::doc_id_t)); - fs_ptr->reader_ptr_->read(uids.data(), num_bytes); - - fs_ptr->reader_ptr_->close(); -} - -void -DefaultVectorsFormat::read(const storage::FSHandlerPtr& fs_ptr, segment::VectorsPtr& vectors_read) { - const std::lock_guard lock(mutex_); - - std::string dir_path = fs_ptr->operation_ptr_->GetDirectory(); - if (!boost::filesystem::is_directory(dir_path)) { - std::string err_msg = "Directory: " + dir_path + "does not exist"; - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_INVALID_ARGUMENT, err_msg); - } - - boost::filesystem::path target_path(dir_path); - typedef boost::filesystem::directory_iterator d_it; - d_it it_end; - d_it it(target_path); - // for (auto& it : boost::filesystem::directory_iterator(dir_path)) { - for (; it != it_end; ++it) { - const auto& path = it->path(); - if (path.extension().string() == raw_vector_extension_) { - auto& vector_list = vectors_read->GetMutableData(); - read_vectors_internal(fs_ptr, path.string(), 0, INT64_MAX, vector_list); - vectors_read->SetName(path.stem().string()); - } else if (path.extension().string() == user_id_extension_) { - auto& uids = vectors_read->GetMutableUids(); - read_uids_internal(fs_ptr, path.string(), uids); - } - } -} - -void -DefaultVectorsFormat::write(const storage::FSHandlerPtr& fs_ptr, const segment::VectorsPtr& vectors) { - const std::lock_guard lock(mutex_); - - std::string dir_path = fs_ptr->operation_ptr_->GetDirectory(); - - const std::string rv_file_path = dir_path + "/" + vectors->GetName() + raw_vector_extension_; - const std::string uid_file_path = dir_path + "/" + vectors->GetName() + user_id_extension_; - - TimeRecorder rc("write vectors"); - - if (!fs_ptr->writer_ptr_->open(rv_file_path.c_str())) { - std::string err_msg = "Failed to open file: " + rv_file_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_CANNOT_CREATE_FILE, err_msg); - } - - size_t rv_num_bytes = vectors->GetData().size() * sizeof(uint8_t); - fs_ptr->writer_ptr_->write(&rv_num_bytes, sizeof(size_t)); - fs_ptr->writer_ptr_->write((void*)vectors->GetData().data(), rv_num_bytes); - fs_ptr->writer_ptr_->close(); - - rc.RecordSection("write rv done"); - - if (!fs_ptr->writer_ptr_->open(uid_file_path.c_str())) { - std::string err_msg = "Failed to open file: " + uid_file_path + ", error: " + std::strerror(errno); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_CANNOT_CREATE_FILE, err_msg); - } - size_t uid_num_bytes = vectors->GetUids().size() * sizeof(segment::doc_id_t); - fs_ptr->writer_ptr_->write(&uid_num_bytes, sizeof(size_t)); - fs_ptr->writer_ptr_->write((void*)vectors->GetUids().data(), uid_num_bytes); - fs_ptr->writer_ptr_->close(); - - rc.RecordSection("write uids done"); -} - -void -DefaultVectorsFormat::read_uids(const storage::FSHandlerPtr& fs_ptr, std::vector& uids) { - const std::lock_guard lock(mutex_); - - std::string dir_path = fs_ptr->operation_ptr_->GetDirectory(); - if (!boost::filesystem::is_directory(dir_path)) { - std::string err_msg = "Directory: " + dir_path + "does not exist"; - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_INVALID_ARGUMENT, err_msg); - } - - boost::filesystem::path target_path(dir_path); - typedef boost::filesystem::directory_iterator d_it; - d_it it_end; - d_it it(target_path); - for (; it != it_end; ++it) { - const auto& path = it->path(); - if (path.extension().string() == user_id_extension_) { - read_uids_internal(fs_ptr, path.string(), uids); - break; - } - } -} - -void -DefaultVectorsFormat::read_vectors(const storage::FSHandlerPtr& fs_ptr, off_t offset, size_t num_bytes, - std::vector& raw_vectors) { - const std::lock_guard lock(mutex_); - - std::string dir_path = fs_ptr->operation_ptr_->GetDirectory(); - if (!boost::filesystem::is_directory(dir_path)) { - std::string err_msg = "Directory: " + dir_path + "does not exist"; - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_INVALID_ARGUMENT, err_msg); - } - - boost::filesystem::path target_path(dir_path); - typedef boost::filesystem::directory_iterator d_it; - d_it it_end; - d_it it(target_path); - for (; it != it_end; ++it) { - const auto& path = it->path(); - if (path.extension().string() == raw_vector_extension_) { - read_vectors_internal(fs_ptr, path.string(), offset, num_bytes, raw_vectors); - break; - } - } -} - -} // namespace codec -} // namespace milvus diff --git a/core/src/codecs/default/DefaultVectorsFormat.h b/core/src/codecs/default/DefaultVectorsFormat.h deleted file mode 100644 index ac5fc89a5a..0000000000 --- a/core/src/codecs/default/DefaultVectorsFormat.h +++ /dev/null @@ -1,73 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include -#include - -#include "codecs/VectorsFormat.h" -#include "segment/Vectors.h" - -namespace milvus { -namespace codec { - -class DefaultVectorsFormat : public VectorsFormat { - public: - DefaultVectorsFormat() = default; - - void - read(const storage::FSHandlerPtr& fs_ptr, segment::VectorsPtr& vectors_read) override; - - void - write(const storage::FSHandlerPtr& fs_ptr, const segment::VectorsPtr& vectors) override; - - void - read_uids(const storage::FSHandlerPtr& fs_ptr, std::vector& uids) override; - - void - read_vectors(const storage::FSHandlerPtr& fs_ptr, off_t offset, size_t num_bytes, - std::vector& raw_vectors) override; - - // No copy and move - DefaultVectorsFormat(const DefaultVectorsFormat&) = delete; - DefaultVectorsFormat(DefaultVectorsFormat&&) = delete; - - DefaultVectorsFormat& - operator=(const DefaultVectorsFormat&) = delete; - DefaultVectorsFormat& - operator=(DefaultVectorsFormat&&) = delete; - - private: - void - read_vectors_internal(const storage::FSHandlerPtr& fs_ptr, const std::string& file_path, off_t offset, size_t num, - std::vector& raw_vectors); - - void - read_uids_internal(const storage::FSHandlerPtr& fs_ptr, const std::string& file_path, - std::vector& uids); - - private: - std::mutex mutex_; - - const std::string raw_vector_extension_ = ".rv"; - const std::string user_id_extension_ = ".uid"; -}; - -} // namespace codec -} // namespace milvus diff --git a/core/src/config/Config.cpp b/core/src/config/Config.cpp deleted file mode 100644 index 474aa6e64b..0000000000 --- a/core/src/config/Config.cpp +++ /dev/null @@ -1,2883 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "config/Config.h" -#include "config/Utils.h" -#include "config/YamlConfigMgr.h" -#include "server/DBWrapper.h" -#include "thirdparty/nlohmann/json.hpp" -#include "utils/CommonUtil.h" -#include "utils/Log.h" -#include "utils/StringHelpFunctions.h" -#include "utils/ValidationUtil.h" - -namespace milvus { -namespace server { - -const char* CONFIG_NODE_DELIMITER = "."; -const char* CONFIG_VERSION = "version"; - -/* cluster config */ -const char* CONFIG_CLUSTER = "cluster"; -const char* CONFIG_CLUSTER_ENABLE = "enable"; -const char* CONFIG_CLUSTER_ENABLE_DEFAULT = "true"; -const char* CONFIG_CLUSTER_ROLE = "role"; -const char* CONFIG_CLUSTER_ROLE_DEFAULT = "rw"; - -/* general config */ -const char* CONFIG_GENERAL = "general"; -const char* CONFIG_GENERAL_TIMEZONE = "timezone"; -const char* CONFIG_GENERAL_TIMEZONE_DEFAULT = "UTC+8"; -const char* CONFIG_GENERAL_METAURI = "meta_uri"; -const char* CONFIG_GENERAL_METAURI_DEFAULT = "sqlite://:@:/"; - -/* network config */ -const char* CONFIG_NETWORK = "network"; -const char* CONFIG_NETWORK_BIND_ADDRESS = "bind.address"; -const char* CONFIG_NETWORK_BIND_ADDRESS_DEFAULT = "127.0.0.1"; -const char* CONFIG_NETWORK_BIND_PORT = "bind.port"; -const char* CONFIG_NETWORK_BIND_PORT_DEFAULT = "19530"; -const char* CONFIG_NETWORK_HTTP_ENABLE = "http.enable"; -const char* CONFIG_NETWORK_HTTP_ENABLE_DEFAULT = "true"; -const char* CONFIG_NETWORK_HTTP_PORT = "http.port"; -const char* CONFIG_NETWORK_HTTP_PORT_DEFAULT = "19121"; - -/* db config */ -const char* CONFIG_DB = "db_config"; -const char* CONFIG_DB_ARCHIVE_DISK_THRESHOLD = "archive_disk_threshold"; -const char* CONFIG_DB_ARCHIVE_DISK_THRESHOLD_DEFAULT = "0"; -const char* CONFIG_DB_ARCHIVE_DAYS_THRESHOLD = "archive_days_threshold"; -const char* CONFIG_DB_ARCHIVE_DAYS_THRESHOLD_DEFAULT = "0"; - -/* storage config */ -const char* CONFIG_STORAGE = "storage"; -const char* CONFIG_STORAGE_PATH = "path"; -const char* CONFIG_STORAGE_PATH_DEFAULT = "/tmp/milvus"; -const char* CONFIG_STORAGE_AUTO_FLUSH_INTERVAL = "auto_flush_interval"; -const char* CONFIG_STORAGE_AUTO_FLUSH_INTERVAL_DEFAULT = "1"; -const char* CONFIG_STORAGE_FILE_CLEANUP_TIMEOUT = "file_cleanup_timeout"; -const char* CONFIG_STORAGE_FILE_CLEANUP_TIMEOUT_DEFAULT = "10"; -const int64_t CONFIG_STORAGE_FILE_CLEANUP_TIMEOUT_MIN = 0; -const int64_t CONFIG_STORAGE_FILE_CLEANUP_TIMEOUT_MAX = 3600; - -/* cache config */ -const char* CONFIG_CACHE = "cache"; -const char* CONFIG_CACHE_CPU_CACHE_CAPACITY = "cache_size"; -const char* CONFIG_CACHE_CPU_CACHE_CAPACITY_DEFAULT = "4294967296"; -const char* CONFIG_CACHE_CPU_CACHE_THRESHOLD = "cpu_cache_threshold"; -const char* CONFIG_CACHE_CPU_CACHE_THRESHOLD_DEFAULT = "0.7"; -const char* CONFIG_CACHE_INSERT_BUFFER_SIZE = "insert_buffer_size"; -const char* CONFIG_CACHE_INSERT_BUFFER_SIZE_DEFAULT = "1073741824"; // 1024 * 1024 * 1024 -const char* CONFIG_CACHE_CACHE_INSERT_DATA = "cache_insert_data"; -const char* CONFIG_CACHE_CACHE_INSERT_DATA_DEFAULT = "false"; -const char* CONFIG_CACHE_PRELOAD_COLLECTION = "preload_collection"; -const char* CONFIG_CACHE_PRELOAD_COLLECTION_DEFAULT = ""; - -/* metric config */ -const char* CONFIG_METRIC = "metric"; -const char* CONFIG_METRIC_ENABLE_MONITOR = "enable"; -const char* CONFIG_METRIC_ENABLE_MONITOR_DEFAULT = "false"; -const char* CONFIG_METRIC_ADDRESS = "address"; -const char* CONFIG_METRIC_ADDRESS_DEFAULT = "127.0.0.1"; -const char* CONFIG_METRIC_PORT = "port"; -const char* CONFIG_METRIC_PORT_DEFAULT = "9091"; - -/* engine config */ -const char* CONFIG_ENGINE = "engine_config"; -const char* CONFIG_ENGINE_USE_BLAS_THRESHOLD = "use_blas_threshold"; -const char* CONFIG_ENGINE_USE_BLAS_THRESHOLD_DEFAULT = "1100"; -const char* CONFIG_ENGINE_OMP_THREAD_NUM = "omp_thread_num"; -const char* CONFIG_ENGINE_OMP_THREAD_NUM_DEFAULT = "0"; -const char* CONFIG_ENGINE_SIMD_TYPE = "simd_type"; -const char* CONFIG_ENGINE_SIMD_TYPE_DEFAULT = "auto"; -const char* CONFIG_ENGINE_SEARCH_COMBINE_MAX_NQ = "search_combine_nq"; -const char* CONFIG_ENGINE_SEARCH_COMBINE_MAX_NQ_DEFAULT = "64"; - -/* gpu resource config */ -const char* CONFIG_GPU_RESOURCE = "gpu"; -const char* CONFIG_GPU_RESOURCE_ENABLE = "enable"; -#ifdef MILVUS_GPU_VERSION -const char* CONFIG_GPU_RESOURCE_ENABLE_DEFAULT = "true"; -#else -const char* CONFIG_GPU_RESOURCE_ENABLE_DEFAULT = "false"; -#endif -const char* CONFIG_GPU_RESOURCE_CACHE_ENABLE = "cache.enable"; -const char* CONFIG_GPU_RESOURCE_CACHE_ENABLE_DEFAULT = "false"; -const char* CONFIG_GPU_RESOURCE_CACHE_CAPACITY = "cache_size"; -const char* CONFIG_GPU_RESOURCE_CACHE_CAPACITY_DEFAULT = "1073741824"; // 1024 * 1024 * 1024 -const char* CONFIG_GPU_RESOURCE_CACHE_THRESHOLD = "cache_threshold"; -const char* CONFIG_GPU_RESOURCE_CACHE_THRESHOLD_DEFAULT = "0.7"; -const char* CONFIG_GPU_RESOURCE_GPU_SEARCH_THRESHOLD = "gpu_search_threshold"; -const char* CONFIG_GPU_RESOURCE_GPU_SEARCH_THRESHOLD_DEFAULT = "1000"; -const char* CONFIG_GPU_RESOURCE_DELIMITER = ","; -const char* CONFIG_GPU_RESOURCE_SEARCH_RESOURCES = "search_devices"; -const char* CONFIG_GPU_RESOURCE_SEARCH_RESOURCES_DEFAULT = "gpu0"; -const char* CONFIG_GPU_RESOURCE_BUILD_INDEX_RESOURCES = "build_index_devices"; -const char* CONFIG_GPU_RESOURCE_BUILD_INDEX_RESOURCES_DEFAULT = "gpu0"; - -/* tracing config */ -const char* CONFIG_TRACING = "tracing_config"; -const char* CONFIG_TRACING_JSON_CONFIG_PATH = "json_config_path"; - -/* wal config */ -const char* CONFIG_WAL = "wal"; -const char* CONFIG_WAL_ENABLE = "enable"; -const char* CONFIG_WAL_ENABLE_DEFAULT = "true"; -const char* CONFIG_WAL_RECOVERY_ERROR_IGNORE = "recovery_error_ignore"; -const char* CONFIG_WAL_RECOVERY_ERROR_IGNORE_DEFAULT = "true"; -const char* CONFIG_WAL_BUFFER_SIZE = "buffer_size"; -const char* CONFIG_WAL_BUFFER_SIZE_DEFAULT = "268435456"; -const int64_t CONFIG_WAL_BUFFER_SIZE_MIN = 67108864; -const int64_t CONFIG_WAL_BUFFER_SIZE_MAX = 4294967296; -const char* CONFIG_WAL_WAL_PATH = "path"; -const char* CONFIG_WAL_WAL_PATH_DEFAULT = "/tmp/milvus/wal"; - -/* logs config */ -const char* CONFIG_LOGS = "logs"; -const char* CONFIG_LOGS_LEVEL = "level"; -const char* CONFIG_LOGS_LEVEL_DEFAULT = "debug"; -const char* CONFIG_LOGS_TRACE_ENABLE = "trace.enable"; -const char* CONFIG_LOGS_TRACE_ENABLE_DEFAULT = "true"; -const char* CONFIG_LOGS_PATH = "path"; -const char* CONFIG_LOGS_PATH_DEFAULT = "/tmp/milvus/logs"; -const char* CONFIG_LOGS_MAX_LOG_FILE_SIZE = "max_log_file_size"; -const char* CONFIG_LOGS_MAX_LOG_FILE_SIZE_DEFAULT = "1073741824"; -const int64_t CONFIG_LOGS_MAX_LOG_FILE_SIZE_MIN = 536870912; -const int64_t CONFIG_LOGS_MAX_LOG_FILE_SIZE_MAX = 4294967296; -const char* CONFIG_LOGS_LOG_ROTATE_NUM = "log_rotate_num"; -const char* CONFIG_LOGS_LOG_ROTATE_NUM_DEFAULT = "0"; -const int64_t CONFIG_LOGS_LOG_ROTATE_NUM_MIN = 0; -const int64_t CONFIG_LOGS_LOG_ROTATE_NUM_MAX = 1024; - -constexpr int64_t GB = 1UL << 30; -constexpr int32_t PORT_NUMBER_MIN = 1024; -constexpr int32_t PORT_NUMBER_MAX = 65535; - -static const std::unordered_map milvus_config_version_map({{"0.6.0", "0.1"}, - {"0.7.0", "0.2"}, - {"0.7.1", "0.2"}, - {"0.8.0", "0.3"}, - {"0.9.0", "0.4"}, - {"0.9.1", "0.4"}, - {"0.10.0", "0.5"}, - {"0.10.1", "0.5"}, - {"0.10.2", "0.5"}, - {"0.10.3", "0.5"}}); - -///////////////////////////////////////////////////////////// -Config::Config() { - auto empty_map = std::unordered_map(); - - // cache config - std::string node_cpu_cache_capacity = std::string(CONFIG_CACHE) + "." + CONFIG_CACHE_CPU_CACHE_CAPACITY; - config_callback_[node_cpu_cache_capacity] = empty_map; - - std::string node_insert_buffer_size = std::string(CONFIG_CACHE) + "." + CONFIG_CACHE_INSERT_BUFFER_SIZE; - config_callback_[node_insert_buffer_size] = empty_map; - - std::string node_cache_insert_data = std::string(CONFIG_CACHE) + "." + CONFIG_CACHE_CACHE_INSERT_DATA; - config_callback_[node_cache_insert_data] = empty_map; - - // engine config - std::string node_blas_threshold = std::string(CONFIG_ENGINE) + "." + CONFIG_ENGINE_USE_BLAS_THRESHOLD; - config_callback_[node_blas_threshold] = empty_map; - - std::string node_search_combine = std::string(CONFIG_ENGINE) + "." + CONFIG_ENGINE_SEARCH_COMBINE_MAX_NQ; - config_callback_[node_search_combine] = empty_map; - - // gpu resources config - std::string node_gpu_enable = std::string(CONFIG_GPU_RESOURCE) + "." + CONFIG_GPU_RESOURCE_ENABLE; - config_callback_[node_gpu_enable] = empty_map; - - std::string node_gpu_cache_capacity = std::string(CONFIG_GPU_RESOURCE) + "." + CONFIG_GPU_RESOURCE_CACHE_CAPACITY; - config_callback_[node_gpu_cache_capacity] = empty_map; - - std::string node_gpu_search_threshold = - std::string(CONFIG_GPU_RESOURCE) + "." + CONFIG_GPU_RESOURCE_GPU_SEARCH_THRESHOLD; - config_callback_[node_gpu_search_threshold] = empty_map; - - std::string node_gpu_search_res = std::string(CONFIG_GPU_RESOURCE) + "." + CONFIG_GPU_RESOURCE_SEARCH_RESOURCES; - config_callback_[node_gpu_search_res] = empty_map; - - std::string node_gpu_build_res = std::string(CONFIG_GPU_RESOURCE) + "." + CONFIG_GPU_RESOURCE_BUILD_INDEX_RESOURCES; - config_callback_[node_gpu_build_res] = empty_map; -} - -Config& -Config::GetInstance() { - static Config config_inst; - return config_inst; -} - -Status -Config::LoadConfigFile(const std::string& filename) { - if (filename.empty()) { - return Status(SERVER_UNEXPECTED_ERROR, "No specified config file"); - } - - struct stat file_stat; - if (stat(filename.c_str(), &file_stat) != 0) { - std::string str = "Config file not exist: " + filename; - return Status(SERVER_FILE_NOT_FOUND, str); - } - - ConfigMgr* mgr = YamlConfigMgr::GetInstance(); - STATUS_CHECK(mgr->LoadConfigFile(filename)); - - // store config file path - config_file_ = filename; - - return Status::OK(); -} - -Status -Config::ValidateConfig() { - std::string config_version; - STATUS_CHECK(GetConfigVersion(config_version)); - - /* cluster config */ - bool cluster_enable; - STATUS_CHECK(GetClusterConfigEnable(cluster_enable)); - - std::string cluster_role; - STATUS_CHECK(GetClusterConfigRole(cluster_role)); - - /* general config */ - std::string general_timezone; - STATUS_CHECK(GetGeneralConfigTimezone(general_timezone)); - - std::string general_metauri; - STATUS_CHECK(GetGeneralConfigMetaURI(general_metauri)); - - /* network config */ - std::string bind_address; - STATUS_CHECK(GetNetworkConfigBindAddress(bind_address)); - - std::string bind_port; - STATUS_CHECK(GetNetworkConfigBindPort(bind_port)); - - bool http_enable = false; - STATUS_CHECK(GetNetworkConfigHTTPEnable(http_enable)); - - std::string http_port; - STATUS_CHECK(GetNetworkConfigHTTPPort(http_port)); - - // std::string server_mode; - // STATUS_CHECK(GetServerConfigDeployMode(server_mode)); - - // std::string server_time_zone; - // STATUS_CHECK(GetServerConfigTimeZone(server_time_zone)); - - // bool server_web_enable; - // STATUS_CHECK(GetServerConfigWebEnable(server_web_enable)); - - // std::string server_web_port; - // STATUS_CHECK(GetServerConfigWebPort(server_web_port)); - - /* db config */ - // std::string db_backend_url; - // STATUS_CHECK(GetDBConfigBackendUrl(db_backend_url)); - - int64_t db_archive_disk_threshold; - STATUS_CHECK(GetDBConfigArchiveDiskThreshold(db_archive_disk_threshold)); - - int64_t db_archive_days_threshold; - STATUS_CHECK(GetDBConfigArchiveDaysThreshold(db_archive_days_threshold)); - - /* storage config */ - std::string storage_path; - STATUS_CHECK(GetStorageConfigPath(storage_path)); - - int64_t auto_flush_interval; - STATUS_CHECK(GetStorageConfigAutoFlushInterval(auto_flush_interval)); - - // bool storage_s3_enable; - // STATUS_CHECK(GetStorageConfigS3Enable(storage_s3_enable)); - // // std::cout << "S3 " << (storage_s3_enable ? "ENABLED !" : "DISABLED !") << std::endl; - // - // std::string storage_s3_address; - // STATUS_CHECK(GetStorageConfigS3Address(storage_s3_address)); - // - // std::string storage_s3_port; - // STATUS_CHECK(GetStorageConfigS3Port(storage_s3_port)); - // - // std::string storage_s3_access_key; - // STATUS_CHECK(GetStorageConfigS3AccessKey(storage_s3_access_key)); - // - // std::string storage_s3_secret_key; - // STATUS_CHECK(GetStorageConfigS3SecretKey(storage_s3_secret_key)); - // - // std::string storage_s3_bucket; - // STATUS_CHECK(GetStorageConfigS3Bucket(storage_s3_bucket)); - - /* metric config */ - bool metric_enable_monitor; - STATUS_CHECK(GetMetricConfigEnableMonitor(metric_enable_monitor)); - - std::string metric_address; - STATUS_CHECK(GetMetricConfigAddress(metric_address)); - - std::string metric_port; - STATUS_CHECK(GetMetricConfigPort(metric_port)); - - /* cache config */ - int64_t cache_cpu_cache_capacity; - STATUS_CHECK(GetCacheConfigCpuCacheCapacity(cache_cpu_cache_capacity)); - - float cache_cpu_cache_threshold; - STATUS_CHECK(GetCacheConfigCpuCacheThreshold(cache_cpu_cache_threshold)); - - int64_t cache_insert_buffer_size; - STATUS_CHECK(GetCacheConfigInsertBufferSize(cache_insert_buffer_size)); - - bool cache_insert_data; - STATUS_CHECK(GetCacheConfigCacheInsertData(cache_insert_data)); - - std::string cache_preload_collection; - STATUS_CHECK(GetCacheConfigPreloadCollection(cache_preload_collection)); - - /* engine config */ - int64_t engine_use_blas_threshold; - STATUS_CHECK(GetEngineConfigUseBlasThreshold(engine_use_blas_threshold)); - - int64_t engine_omp_thread_num; - STATUS_CHECK(GetEngineConfigOmpThreadNum(engine_omp_thread_num)); - - std::string engine_simd_type; - STATUS_CHECK(GetEngineConfigSimdType(engine_simd_type)); - - /* gpu resource config */ -#ifdef MILVUS_GPU_VERSION - bool gpu_resource_enable; - STATUS_CHECK(GetGpuResourceConfigEnable(gpu_resource_enable)); - std::cout << "GPU resources " << (gpu_resource_enable ? "ENABLED !" : "DISABLED !") << std::endl; - - if (gpu_resource_enable) { - bool resource_cache_enable; - STATUS_CHECK(GetGpuResourceConfigCacheEnable(resource_cache_enable)); - - int64_t resource_cache_capacity; - STATUS_CHECK(GetGpuResourceConfigCacheCapacity(resource_cache_capacity)); - - float resource_cache_threshold; - STATUS_CHECK(GetGpuResourceConfigCacheThreshold(resource_cache_threshold)); - - int64_t engine_gpu_search_threshold; - STATUS_CHECK(GetGpuResourceConfigGpuSearchThreshold(engine_gpu_search_threshold)); - - std::vector search_resources; - STATUS_CHECK(GetGpuResourceConfigSearchResources(search_resources)); - - std::vector index_build_resources; - STATUS_CHECK(GetGpuResourceConfigBuildIndexResources(index_build_resources)); - } -#endif - - /* tracing config */ - std::string tracing_config_path; - STATUS_CHECK(GetTracingConfigJsonConfigPath(tracing_config_path)); - - /* wal config */ - bool enable; - STATUS_CHECK(GetWalConfigEnable(enable)); - - bool recovery_error_ignore; - STATUS_CHECK(GetWalConfigRecoveryErrorIgnore(recovery_error_ignore)); - - int64_t buffer_size; - STATUS_CHECK(GetWalConfigBufferSize(buffer_size)); - - std::string wal_path; - STATUS_CHECK(GetWalConfigWalPath(wal_path)); - - /* logs config */ - std::string logs_level; - STATUS_CHECK(GetLogsLevel(logs_level)); - - bool trace_enable; - STATUS_CHECK(GetLogsTraceEnable(trace_enable)); - - std::string logs_path; - STATUS_CHECK(GetLogsPath(logs_path)); - - int64_t logs_max_log_file_size; - STATUS_CHECK(GetLogsMaxLogFileSize(logs_max_log_file_size)); - - int64_t logs_log_rotate_num; - STATUS_CHECK(GetLogsLogRotateNum(logs_log_rotate_num)); - - return Status::OK(); -} - -Status -Config::ResetDefaultConfig() { - /* cluster config */ - STATUS_CHECK(SetClusterConfigEnable(CONFIG_CLUSTER_ENABLE_DEFAULT)); - STATUS_CHECK(SetClusterConfigRole(CONFIG_CLUSTER_ROLE_DEFAULT)); - - /* general config */ - STATUS_CHECK(SetGeneralConfigTimezone(CONFIG_GENERAL_TIMEZONE_DEFAULT)); - STATUS_CHECK(SetGeneralConfigMetaURI(CONFIG_GENERAL_METAURI_DEFAULT)); - - /* network config */ - STATUS_CHECK(SetNetworkConfigBindAddress(CONFIG_NETWORK_BIND_ADDRESS_DEFAULT)); - STATUS_CHECK(SetNetworkConfigBindPort(CONFIG_NETWORK_BIND_PORT_DEFAULT)); - STATUS_CHECK(SetNetworkConfigHTTPEnable(CONFIG_NETWORK_HTTP_ENABLE_DEFAULT)); - STATUS_CHECK(SetNetworkConfigHTTPPort(CONFIG_NETWORK_HTTP_PORT_DEFAULT)); - - /* server config */ - // STATUS_CHECK(SetServerConfigAddress(CONFIG_SERVER_ADDRESS_DEFAULT)); - // STATUS_CHECK(SetServerConfigPort(CONFIG_SERVER_PORT_DEFAULT)); - // STATUS_CHECK(SetServerConfigDeployMode(CONFIG_SERVER_DEPLOY_MODE_DEFAULT)); - // STATUS_CHECK(SetServerConfigTimeZone(CONFIG_SERVER_TIME_ZONE_DEFAULT)); - // STATUS_CHECK(SetServerConfigWebEnable(CONFIG_SERVER_WEB_ENABLE_DEFAULT)); - // STATUS_CHECK(SetServerConfigWebPort(CONFIG_SERVER_WEB_PORT_DEFAULT)); - - /* db config */ - // STATUS_CHECK(SetDBConfigBackendUrl(CONFIG_DB_BACKEND_URL_DEFAULT)); - STATUS_CHECK(SetDBConfigArchiveDiskThreshold(CONFIG_DB_ARCHIVE_DISK_THRESHOLD_DEFAULT)); - STATUS_CHECK(SetDBConfigArchiveDaysThreshold(CONFIG_DB_ARCHIVE_DAYS_THRESHOLD_DEFAULT)); - - /* storage config */ - STATUS_CHECK(SetStorageConfigPath(CONFIG_STORAGE_PATH_DEFAULT)); - STATUS_CHECK(SetStorageConfigAutoFlushInterval(CONFIG_STORAGE_AUTO_FLUSH_INTERVAL_DEFAULT)); - STATUS_CHECK(SetStorageConfigFileCleanupTimeout(CONFIG_STORAGE_FILE_CLEANUP_TIMEOUT_DEFAULT)); - // STATUS_CHECK(SetStorageConfigS3Enable(CONFIG_STORAGE_S3_ENABLE_DEFAULT)); - // STATUS_CHECK(SetStorageConfigS3Address(CONFIG_STORAGE_S3_ADDRESS_DEFAULT)); - // STATUS_CHECK(SetStorageConfigS3Port(CONFIG_STORAGE_S3_PORT_DEFAULT)); - // STATUS_CHECK(SetStorageConfigS3AccessKey(CONFIG_STORAGE_S3_ACCESS_KEY_DEFAULT)); - // STATUS_CHECK(SetStorageConfigS3SecretKey(CONFIG_STORAGE_S3_SECRET_KEY_DEFAULT)); - // STATUS_CHECK(SetStorageConfigS3Bucket(CONFIG_STORAGE_S3_BUCKET_DEFAULT)); - - /* metric config */ - STATUS_CHECK(SetMetricConfigEnableMonitor(CONFIG_METRIC_ENABLE_MONITOR_DEFAULT)); - STATUS_CHECK(SetMetricConfigAddress(CONFIG_METRIC_ADDRESS_DEFAULT)); - STATUS_CHECK(SetMetricConfigPort(CONFIG_METRIC_PORT_DEFAULT)); - - /* cache config */ - STATUS_CHECK(SetCacheConfigCpuCacheCapacity(CONFIG_CACHE_CPU_CACHE_CAPACITY_DEFAULT)); - STATUS_CHECK(SetCacheConfigCpuCacheThreshold(CONFIG_CACHE_CPU_CACHE_THRESHOLD_DEFAULT)); - STATUS_CHECK(SetCacheConfigInsertBufferSize(CONFIG_CACHE_INSERT_BUFFER_SIZE_DEFAULT)); - STATUS_CHECK(SetCacheConfigCacheInsertData(CONFIG_CACHE_CACHE_INSERT_DATA_DEFAULT)); - STATUS_CHECK(SetCacheConfigPreloadCollection(CONFIG_CACHE_PRELOAD_COLLECTION_DEFAULT)); - - /* engine config */ - STATUS_CHECK(SetEngineConfigUseBlasThreshold(CONFIG_ENGINE_USE_BLAS_THRESHOLD_DEFAULT)); - STATUS_CHECK(SetEngineConfigOmpThreadNum(CONFIG_ENGINE_OMP_THREAD_NUM_DEFAULT)); - STATUS_CHECK(SetEngineConfigSimdType(CONFIG_ENGINE_SIMD_TYPE_DEFAULT)); - STATUS_CHECK(SetEngineSearchCombineMaxNq(CONFIG_ENGINE_SEARCH_COMBINE_MAX_NQ_DEFAULT)); - - /* gpu resource config */ -#ifdef MILVUS_GPU_VERSION - STATUS_CHECK(SetGpuResourceConfigEnable(CONFIG_GPU_RESOURCE_ENABLE_DEFAULT)); - STATUS_CHECK(SetGpuResourceConfigCacheEnable(CONFIG_GPU_RESOURCE_CACHE_ENABLE_DEFAULT)); - STATUS_CHECK(SetGpuResourceConfigCacheCapacity(CONFIG_GPU_RESOURCE_CACHE_CAPACITY_DEFAULT)); - STATUS_CHECK(SetGpuResourceConfigCacheThreshold(CONFIG_GPU_RESOURCE_CACHE_THRESHOLD_DEFAULT)); - STATUS_CHECK(SetGpuResourceConfigGpuSearchThreshold(CONFIG_GPU_RESOURCE_GPU_SEARCH_THRESHOLD_DEFAULT)); - STATUS_CHECK(SetGpuResourceConfigSearchResources(CONFIG_GPU_RESOURCE_SEARCH_RESOURCES_DEFAULT)); - STATUS_CHECK(SetGpuResourceConfigBuildIndexResources(CONFIG_GPU_RESOURCE_BUILD_INDEX_RESOURCES_DEFAULT)); -#endif - - /* wal config */ - STATUS_CHECK(SetWalConfigEnable(CONFIG_WAL_ENABLE_DEFAULT)); - STATUS_CHECK(SetWalConfigRecoveryErrorIgnore(CONFIG_WAL_RECOVERY_ERROR_IGNORE_DEFAULT)); - STATUS_CHECK(SetWalConfigBufferSize(CONFIG_WAL_BUFFER_SIZE_DEFAULT)); - STATUS_CHECK(SetWalConfigWalPath(CONFIG_WAL_WAL_PATH_DEFAULT)); - - /* logs config */ - STATUS_CHECK(SetLogsLevel(CONFIG_LOGS_LEVEL_DEFAULT)); - STATUS_CHECK(SetLogsTraceEnable(CONFIG_LOGS_TRACE_ENABLE_DEFAULT)); - STATUS_CHECK(SetLogsPath(CONFIG_LOGS_PATH_DEFAULT)); - STATUS_CHECK(SetLogsMaxLogFileSize(CONFIG_LOGS_MAX_LOG_FILE_SIZE_DEFAULT)); - STATUS_CHECK(SetLogsLogRotateNum(CONFIG_LOGS_LOG_ROTATE_NUM_DEFAULT)); - - return Status::OK(); -} - -void -Config::GetConfigJsonStr(std::string& result, int64_t indent) { - nlohmann::json config_json(config_map_); - result = config_json.dump(indent); -} - -Status -Config::GetConfigCli(std::string& value, const std::string& parent_key, const std::string& child_key) { - if (!ConfigNodeValid(parent_key, child_key)) { - std::string str = "Config node invalid: " + parent_key + CONFIG_NODE_DELIMITER + child_key; - return Status(SERVER_UNEXPECTED_ERROR, str); - } - return GetConfigValueInMem(parent_key, child_key, value); -} - -Status -Config::SetConfigCli(const std::string& parent_key, const std::string& child_key, const std::string& value) { - std::string invalid_node_str = "Config node invalid: " + parent_key + CONFIG_NODE_DELIMITER + child_key; - - if (!ConfigNodeValid(parent_key, child_key)) { - return Status(SERVER_UNEXPECTED_ERROR, invalid_node_str); - } - auto status = Status::OK(); - if (parent_key == CONFIG_CLUSTER) { - if (child_key == CONFIG_CLUSTER_ENABLE) { - status = SetClusterConfigEnable(value); - } else if (child_key == CONFIG_CLUSTER_ROLE) { - status = SetClusterConfigRole(value); - } else { - status = Status(SERVER_UNEXPECTED_ERROR, invalid_node_str); - } - } else if (parent_key == CONFIG_GENERAL) { - if (child_key == CONFIG_GENERAL_TIMEZONE) { - status = SetGeneralConfigTimezone(value); - } else if (child_key == CONFIG_GENERAL_METAURI) { - status = SetGeneralConfigMetaURI(value); - } else { - status = Status(SERVER_UNEXPECTED_ERROR, invalid_node_str); - } - } else if (parent_key == CONFIG_NETWORK) { - if (child_key == CONFIG_NETWORK_BIND_ADDRESS) { - status = SetNetworkConfigBindAddress(value); - } else if (child_key == CONFIG_NETWORK_BIND_PORT) { - status = SetNetworkConfigBindPort(value); - } else if (child_key == CONFIG_NETWORK_HTTP_PORT) { - status = SetNetworkConfigHTTPPort(value); - } else { - status = Status(SERVER_UNEXPECTED_ERROR, invalid_node_str); - } - } else if (parent_key == CONFIG_DB) { - // if (child_key == CONFIG_DB_BACKEND_URL) { - // status = SetDBConfigBackendUrl(value); - // } else if (child_key == CONFIG_DB_PRELOAD_COLLECTION) { - // if (child_key == CONFIG_DB_PRELOAD_COLLECTION) { - // status = SetDBConfigPreloadCollection(value); - // } else { - // status = Status(SERVER_UNEXPECTED_ERROR, invalid_node_str); - // } - } else if (parent_key == CONFIG_STORAGE) { - if (child_key == CONFIG_STORAGE_PATH) { - status = SetStorageConfigPath(value); - } else if (child_key == CONFIG_STORAGE_AUTO_FLUSH_INTERVAL) { - status = SetStorageConfigAutoFlushInterval(value); - // } else if (child_key == CONFIG_STORAGE_S3_ENABLE) { - // status = SetStorageConfigS3Enable(value); - // } else if (child_key == CONFIG_STORAGE_S3_ADDRESS) { - // status = SetStorageConfigS3Address(value); - // } else if (child_key == CONFIG_STORAGE_S3_PORT) { - // status = SetStorageConfigS3Port(value); - // } else if (child_key == CONFIG_STORAGE_S3_ACCESS_KEY) { - // status = SetStorageConfigS3AccessKey(value); - // } else if (child_key == CONFIG_STORAGE_S3_SECRET_KEY) { - // status = SetStorageConfigS3SecretKey(value); - // } else if (child_key == CONFIG_STORAGE_S3_BUCKET) { - // status = SetStorageConfigS3Bucket(value); - } else { - status = Status(SERVER_UNEXPECTED_ERROR, invalid_node_str); - } - } else if (parent_key == CONFIG_METRIC) { - if (child_key == CONFIG_METRIC_ENABLE_MONITOR) { - status = SetMetricConfigEnableMonitor(value); - } else if (child_key == CONFIG_METRIC_ADDRESS) { - status = SetMetricConfigAddress(value); - } else if (child_key == CONFIG_METRIC_PORT) { - status = SetMetricConfigPort(value); - } else { - status = Status(SERVER_UNEXPECTED_ERROR, invalid_node_str); - } - } else if (parent_key == CONFIG_CACHE) { - if (child_key == CONFIG_CACHE_CPU_CACHE_CAPACITY) { - status = SetCacheConfigCpuCacheCapacity(value); - } else if (child_key == CONFIG_CACHE_CPU_CACHE_THRESHOLD) { - status = SetCacheConfigCpuCacheThreshold(value); - } else if (child_key == CONFIG_CACHE_CACHE_INSERT_DATA) { - status = SetCacheConfigCacheInsertData(value); - } else if (child_key == CONFIG_CACHE_INSERT_BUFFER_SIZE) { - status = SetCacheConfigInsertBufferSize(value); - } else if (child_key == CONFIG_CACHE_PRELOAD_COLLECTION) { - status = SetCacheConfigPreloadCollection(value); - } else { - status = Status(SERVER_UNEXPECTED_ERROR, invalid_node_str); - } - } else if (parent_key == CONFIG_ENGINE) { - if (child_key == CONFIG_ENGINE_USE_BLAS_THRESHOLD) { - status = SetEngineConfigUseBlasThreshold(value); - } else if (child_key == CONFIG_ENGINE_OMP_THREAD_NUM) { - status = SetEngineConfigOmpThreadNum(value); - } else if (child_key == CONFIG_ENGINE_SIMD_TYPE) { - status = SetEngineConfigSimdType(value); - } else if (child_key == CONFIG_ENGINE_SEARCH_COMBINE_MAX_NQ) { - status = SetEngineSearchCombineMaxNq(value); - } else { - status = Status(SERVER_UNEXPECTED_ERROR, invalid_node_str); - } -#ifdef MILVUS_GPU_VERSION - } else if (parent_key == CONFIG_GPU_RESOURCE) { - if (child_key == CONFIG_GPU_RESOURCE_ENABLE) { - status = SetGpuResourceConfigEnable(value); - } else if (child_key == CONFIG_GPU_RESOURCE_CACHE_ENABLE) { - status = SetGpuResourceConfigCacheEnable(value); - } else if (child_key == CONFIG_GPU_RESOURCE_CACHE_CAPACITY) { - status = SetGpuResourceConfigCacheCapacity(value); - } else if (child_key == CONFIG_GPU_RESOURCE_CACHE_THRESHOLD) { - status = SetGpuResourceConfigCacheThreshold(value); - } else if (child_key == CONFIG_GPU_RESOURCE_GPU_SEARCH_THRESHOLD) { - status = SetGpuResourceConfigGpuSearchThreshold(value); - } else if (child_key == CONFIG_GPU_RESOURCE_SEARCH_RESOURCES) { - status = SetGpuResourceConfigSearchResources(value); - } else if (child_key == CONFIG_GPU_RESOURCE_BUILD_INDEX_RESOURCES) { - status = SetGpuResourceConfigBuildIndexResources(value); - } else { - status = Status(SERVER_UNEXPECTED_ERROR, invalid_node_str); - } -#endif - } else if (parent_key == CONFIG_TRACING) { - if (child_key == CONFIG_TRACING_JSON_CONFIG_PATH) { - status = SetTracingConfigJsonConfigPath(value); - } else { - status = Status(SERVER_UNEXPECTED_ERROR, invalid_node_str); - } - } else if (parent_key == CONFIG_WAL) { - if (child_key == CONFIG_WAL_ENABLE) { - status = SetWalConfigEnable(value); - } else if (child_key == CONFIG_WAL_RECOVERY_ERROR_IGNORE) { - status = SetWalConfigRecoveryErrorIgnore(value); - } else if (child_key == CONFIG_WAL_BUFFER_SIZE) { - status = SetWalConfigBufferSize(value); - } else if (child_key == CONFIG_WAL_WAL_PATH) { - status = SetWalConfigWalPath(value); - } else { - status = Status(SERVER_UNEXPECTED_ERROR, invalid_node_str); - } - } else if (parent_key == CONFIG_LOGS) { - if (child_key == CONFIG_LOGS_LEVEL) { - status = SetLogsLevel(value); - } else if (child_key == CONFIG_LOGS_TRACE_ENABLE) { - status = SetLogsTraceEnable(value); - } else if (child_key == CONFIG_LOGS_PATH) { - status = SetLogsPath(value); - } else if (child_key == CONFIG_LOGS_MAX_LOG_FILE_SIZE) { - status = SetLogsMaxLogFileSize(value); - } else if (child_key == CONFIG_LOGS_LOG_ROTATE_NUM) { - status = SetLogsLogRotateNum(value); - } else { - status = Status(SERVER_UNEXPECTED_ERROR, invalid_node_str); - } - } - - if (status.ok()) { - status = UpdateFileConfigFromMem(parent_key, child_key); - if (status.ok() && - !(parent_key == CONFIG_CACHE || parent_key == CONFIG_ENGINE || parent_key == CONFIG_GPU_RESOURCE)) { - restart_required_ = true; - } - } - - return status; -} - -////////////////////////////////////////////////////////////// -Status -Config::ProcessConfigCli(std::string& result, const std::string& cmd) { - std::vector tokens; - std::vector nodes; - server::StringHelpFunctions::SplitStringByDelimeter(cmd, " ", tokens); - if (tokens[0] == "get_config") { - if (tokens.size() != 2) { - return Status(SERVER_UNEXPECTED_ERROR, "Invalid command: " + cmd); - } - if (tokens[1] == "*") { - GetConfigJsonStr(result); - return Status::OK(); - } else { - server::StringHelpFunctions::SplitStringByDelimeter(tokens[1], CONFIG_NODE_DELIMITER, nodes); - if (nodes.size() < 2) { - return Status(SERVER_UNEXPECTED_ERROR, "Invalid command: " + cmd); - } else if (nodes.size() > 2) { - // to support case likes network.bind.address - std::string result; - std::vector nodes_s(nodes.begin() + 1, nodes.end()); - StringHelpFunctions::MergeStringWithDelimeter(nodes_s, CONFIG_NODE_DELIMITER, result); - nodes[1] = result; - } - // if (nodes.size() != 2) { - // return Status(SERVER_UNEXPECTED_ERROR, "Invalid command: " + cmd); - // } - return GetConfigCli(result, nodes[0], nodes[1]); - } - } else if (tokens[0] == "set_config") { - if (tokens.size() != 3) { - return Status(SERVER_UNEXPECTED_ERROR, "Invalid command: " + cmd); - } - server::StringHelpFunctions::SplitStringByDelimeter(tokens[1], CONFIG_NODE_DELIMITER, nodes); - if (nodes.size() < 2) { - return Status(SERVER_UNEXPECTED_ERROR, "Invalid command: " + cmd); - } else if (nodes.size() > 2) { - // to support case likes network.bind.address - std::string result; - std::vector nodes_s(nodes.begin() + 1, nodes.end()); - StringHelpFunctions::MergeStringWithDelimeter(nodes_s, CONFIG_NODE_DELIMITER, result); - nodes[1] = result; - } - return SetConfigCli(nodes[0], nodes[1], tokens[2]); - } else { - return Status(SERVER_UNEXPECTED_ERROR, "Invalid command: " + cmd); - } -} - -Status -Config::GenUniqueIdentityID(const std::string& identity, std::string& uid) { - std::vector elements; - elements.push_back(identity); - - // get current process id - int64_t pid = getpid(); - elements.push_back(std::to_string(pid)); - - // get current thread id - std::stringstream ss; - ss << std::this_thread::get_id(); - elements.push_back(ss.str()); - - // get current timestamp - auto time_now = std::chrono::system_clock::now(); - auto duration_in_ms = std::chrono::duration_cast(time_now.time_since_epoch()); - elements.push_back(std::to_string(duration_in_ms.count())); - - StringHelpFunctions::MergeStringWithDelimeter(elements, "-", uid); - - return Status::OK(); -} - -Status -Config::UpdateFileConfigFromMem(const std::string& parent_key, const std::string& child_key) { - if (access(config_file_.c_str(), F_OK | R_OK) != 0) { - return Status(SERVER_UNEXPECTED_ERROR, "Cannot find configure file: " + config_file_); - } - - // Store original configure file - std::string ori_file = config_file_ + ".ori"; - if (access(ori_file.c_str(), F_OK) != 0) { - std::fstream fin(config_file_, std::ios::in); - std::ofstream fout(ori_file); - - if (!fin.is_open() || !fout.is_open()) { - return Status(SERVER_UNEXPECTED_ERROR, "Cannot open conf file. Store original conf file failed"); - } - fout << fin.rdbuf(); - fout.flush(); - fout.close(); - fin.close(); - } - - std::string value; - auto status = GetConfigValueInMem(parent_key, child_key, value); - if (!status.ok()) { - return status; - } - - // convert value string to standard string stored in yaml file - std::string value_str; - if (child_key == CONFIG_CACHE_CACHE_INSERT_DATA || - // child_key == CONFIG_STORAGE_S3_ENABLE || - child_key == CONFIG_METRIC_ENABLE_MONITOR || child_key == CONFIG_GPU_RESOURCE_ENABLE || - child_key == CONFIG_WAL_ENABLE || child_key == CONFIG_WAL_RECOVERY_ERROR_IGNORE) { - bool ok = false; - STATUS_CHECK(StringHelpFunctions::ConvertToBoolean(value, ok)); - value_str = ok ? "true" : "false"; - } else if (child_key == CONFIG_GPU_RESOURCE_SEARCH_RESOURCES || - child_key == CONFIG_GPU_RESOURCE_BUILD_INDEX_RESOURCES) { - std::vector vec; - StringHelpFunctions::SplitStringByDelimeter(value, ",", vec); - for (auto& s : vec) { - std::transform(s.begin(), s.end(), s.begin(), ::tolower); - value_str += "\n - " + s; - } - } else { - value_str = value; - } - - std::fstream conf_fin(config_file_, std::ios::in); - if (!conf_fin.is_open()) { - return Status(SERVER_UNEXPECTED_ERROR, "Cannot open conf file: " + config_file_); - } - - bool parent_key_read = false; - std::string conf_str, line; - while (getline(conf_fin, line)) { - if (!parent_key_read) { - conf_str += line + "\n"; - // TODO: danger - if (not(line.empty() || line.find_first_of('#') == 0)) - if (line.find(parent_key) == 0) - parent_key_read = true; - continue; - } - - if (line.find_first_of('#') == 0) { - status = Status(SERVER_UNEXPECTED_ERROR, "Cannot find child key: " + child_key + ", line is " + line); - break; - } - - if (line.find(child_key) != std::string::npos) { - // may loss comments here, need to extract comments from line - conf_str += " " + child_key + ": " + value_str + "\n"; - break; - } - - conf_str += line + "\n"; - } - - // values of gpu resources are sequences, need to remove old here - if (child_key == CONFIG_GPU_RESOURCE_SEARCH_RESOURCES || child_key == CONFIG_GPU_RESOURCE_BUILD_INDEX_RESOURCES) { - while (getline(conf_fin, line)) { - if (line.find("- gpu") != std::string::npos) - continue; - - conf_str += line + "\n"; - if (!line.empty() && line.size() > 2 && isalnum(line.at(2))) { - break; - } - } - } - - if (status.ok()) { - while (getline(conf_fin, line)) { - conf_str += line + "\n"; - } - conf_fin.close(); - - std::fstream fout(config_file_, std::ios::out | std::ios::trunc); - fout << conf_str; - fout.flush(); - fout.close(); - } - - return status; -} - -Status -Config::RegisterCallBack(const std::string& node, const std::string& sub_node, const std::string& key, - ConfigCallBackF& cb) { - std::string cb_node = node + "." + sub_node; - std::lock_guard lock(callback_mutex_); - if (config_callback_.find(cb_node) == config_callback_.end()) { - return Status(SERVER_UNEXPECTED_ERROR, cb_node + " is not supported changed in mem"); - } - - auto& callback_map = config_callback_.at(cb_node); - callback_map[key] = cb; - - return Status::OK(); -} - -Status -Config::CancelCallBack(const std::string& node, const std::string& sub_node, const std::string& key) { - std::lock_guard lock(callback_mutex_); - if (config_callback_.empty() || key.empty()) { - return Status::OK(); - } - - std::string cb_node = node + "." + sub_node; - if (config_callback_.find(cb_node) == config_callback_.end()) { - return Status(SERVER_UNEXPECTED_ERROR, cb_node + " cannot found in callback map"); - } - - auto& cb_map = config_callback_.at(cb_node); - cb_map.erase(key); - - return Status::OK(); -} - -//////////////////////////////////////////////////////////////////////////////// -Status -Config::CheckConfigVersion(const std::string& value) { - if (milvus_config_version_map.find(MILVUS_VERSION) != milvus_config_version_map.end()) { - bool exist_error = milvus_config_version_map.at(MILVUS_VERSION) != value; - fiu_do_on("check_config_version_fail", exist_error = true); - if (exist_error) { - std::string msg = "Invalid config version: " + value + - ". Expected config version: " + milvus_config_version_map.at(MILVUS_VERSION); - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - } - return Status::OK(); -} - -/* cluster config */ -Status -Config::CheckClusterConfigEnable(const std::string& value) { - return ValidationUtil::ValidateStringIsBool(value); -} - -Status -Config::CheckClusterConfigRole(const std::string& value) { - fiu_return_on("check_config_cluster_role_fail", - Status(SERVER_INVALID_ARGUMENT, "cluster.role is not one of rw and ro.")); - - if (value != "rw" && value != "ro") { - return Status(SERVER_INVALID_ARGUMENT, "cluster.role is not one of rw and ro."); - } - return Status::OK(); -} - -/* general config */ -Status -Config::CheckGeneralConfigTimezone(const std::string& value) { - fiu_return_on("check_config_timezone_fail", Status(SERVER_INVALID_ARGUMENT, "Invalid general.timezone: " + value)); - - if (value.length() <= 3) { - return Status(SERVER_INVALID_ARGUMENT, "Invalid general.timezone: " + value); - } else { - if (value.substr(0, 3) != "UTC") { - return Status(SERVER_INVALID_ARGUMENT, "Invalid general.timezone: " + value); - } else { - if (!ValidationUtil::IsNumber(value.substr(4))) { - return Status(SERVER_INVALID_ARGUMENT, "Invalid general.timezone: " + value); - } - } - } - return Status::OK(); -} - -Status -Config::CheckGeneralConfigMetaURI(const std::string& value) { - auto exist_error = !ValidationUtil::ValidateDbURI(value).ok(); - fiu_do_on("check_config_meta_uri_fail", exist_error = true); - - if (exist_error) { - std::string msg = - "Invalid meta uri: " + value + ". Possible reason: general.meta_uri is invalid. " + - "The correct format should be like sqlite://:@:/ or mysql://root:123456@127.0.0.1:3306/milvus."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - return Status::OK(); -} - -/* network config */ -Status -Config::CheckNetworkConfigBindAddress(const std::string& value) { - auto exist_error = !ValidationUtil::ValidateIpAddress(value).ok(); - fiu_do_on("check_config_bind_address_fail", exist_error = true); - - if (exist_error) { - std::string msg = "Invalid server IP address: " + value + ". Possible reason: network.bind.address is invalid."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - return Status::OK(); -} - -Status -Config::CheckNetworkConfigBindPort(const std::string& value) { - auto exist_error = !ValidationUtil::ValidateStringIsNumber(value).ok(); - fiu_do_on("check_config_bind_port_fail", exist_error = true); - - if (exist_error) { - std::string msg = "Invalid server port: " + value + ". Possible reason: network.bind.port is not a number."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } else { - try { - int32_t port = std::stoi(value); - if (!(port > PORT_NUMBER_MIN && port < PORT_NUMBER_MAX)) { - std::string msg = "Invalid server port: " + value + - ". Possible reason: network.bind.port is not in range (1024, 65535)."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - } catch (...) { - return Status(SERVER_INVALID_ARGUMENT, "Invalid network.bind.port: " + value); - } - } - return Status::OK(); -} - -Status -Config::CheckNetworkConfigHTTPEnable(const std::string& value) { - return ValidationUtil::ValidateStringIsBool(value); -} - -Status -Config::CheckNetworkConfigHTTPPort(const std::string& value) { - if (!ValidationUtil::ValidateStringIsNumber(value).ok()) { - std::string msg = "Invalid web server port: " + value + ". Possible reason: network.http.port is not a number."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } else { - try { - int32_t port = std::stoi(value); - if (!(port > PORT_NUMBER_MIN && port < PORT_NUMBER_MAX)) { - std::string msg = "Invalid web server port: " + value + - ". Possible reason: network.http.port is not in range (1024, 65535)."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - } catch (...) { - return Status(SERVER_INVALID_ARGUMENT, "Invalid network.http.port: " + value); - } - } - return Status::OK(); -} - -/* server config */ -// Status -// Config::CheckServerConfigAddress(const std::string& value) { -// auto exist_error = !ValidationUtil::ValidateIpAddress(value).ok(); -// fiu_do_on("check_config_address_fail", exist_error = true); -// -// if (exist_error) { -// std::string msg = -// "Invalid server IP address: " + value + ". Possible reason: server_config.address is invalid."; -// return Status(SERVER_INVALID_ARGUMENT, msg); -// } -// return Status::OK(); -// } - -// Status -// Config::CheckServerConfigPort(const std::string& value) { -// auto exist_error = !ValidationUtil::ValidateStringIsNumber(value).ok(); -// fiu_do_on("check_config_port_fail", exist_error = true); -// -// if (exist_error) { -// std::string msg = "Invalid server port: " + value + ". Possible reason: server_config.port is not a number."; -// return Status(SERVER_INVALID_ARGUMENT, msg); -// } else { -// try { -// int32_t port = std::stoi(value); -// if (!(port > PORT_NUMBER_MIN && port < PORT_NUMBER_MAX)) { -// std::string msg = "Invalid server port: " + value + -// ". Possible reason: server_config.port is not in range (1024, 65535)."; -// return Status(SERVER_INVALID_ARGUMENT, msg); -// } -// } catch (...) { -// return Status(SERVER_INVALID_ARGUMENT, "Invalid server_config.port: " + value); -// } -// } -// return Status::OK(); -// } - -// Status -// Config::CheckServerConfigDeployMode(const std::string& value) { -// fiu_return_on("check_config_deploy_mode_fail", -// Status(SERVER_INVALID_ARGUMENT, -// "server_config.deploy_mode is not one of single, cluster_readonly, and cluster_writable.")); -// -// if (value != "single" && value != "cluster_readonly" && value != "cluster_writable") { -// return Status(SERVER_INVALID_ARGUMENT, -// "server_config.deploy_mode is not one of single, cluster_readonly, and cluster_writable."); -// } -// return Status::OK(); -//} - -// Status -// Config::CheckServerConfigTimeZone(const std::string& value) { -// fiu_return_on("check_config_time_zone_fail", -// Status(SERVER_INVALID_ARGUMENT, "Invalid server_config.time_zone: " + value)); -// -// if (value.length() <= 3) { -// return Status(SERVER_INVALID_ARGUMENT, "Invalid server_config.time_zone: " + value); -// } else { -// if (value.substr(0, 3) != "UTC") { -// return Status(SERVER_INVALID_ARGUMENT, "Invalid server_config.time_zone: " + value); -// } else { -// if (!ValidationUtil::IsNumber(value.substr(4))) { -// return Status(SERVER_INVALID_ARGUMENT, "Invalid server_config.time_zone: " + value); -// } -// } -// } -// return Status::OK(); -// } - -// Status -// Config::CheckServerConfigWebEnable(const std::string& value) { -// return ValidationUtil::ValidateStringIsBool(value); -// } - -// Status -// Config::CheckServerConfigWebPort(const std::string& value) { -// if (!ValidationUtil::ValidateStringIsNumber(value).ok()) { -// std::string msg = -// "Invalid web server port: " + value + ". Possible reason: server_config.web_port is not a number."; -// return Status(SERVER_INVALID_ARGUMENT, msg); -// } else { -// try { -// int32_t port = std::stoi(value); -// if (!(port > PORT_NUMBER_MIN && port < PORT_NUMBER_MAX)) { -// std::string msg = "Invalid web server port: " + value + -// ". Possible reason: server_config.web_port is not in range (1024, 65535)."; -// return Status(SERVER_INVALID_ARGUMENT, msg); -// } -// } catch (...) { -// return Status(SERVER_INVALID_ARGUMENT, "Invalid server_config.web_port: " + value); -// } -// } -// return Status::OK(); -// } - -/* DB config */ -// Status -// Config::CheckDBConfigBackendUrl(const std::string& value) { -// auto exist_error = !ValidationUtil::ValidateDbURI(value).ok(); -// fiu_do_on("check_config_backend_url_fail", exist_error = true); -// -// if (exist_error) { -// std::string msg = -// "Invalid backend url: " + value + ". Possible reason: db_config.db_backend_url is invalid. " + -// "The correct format should be like sqlite://:@:/ or mysql://root:123456@127.0.0.1:3306/milvus."; -// return Status(SERVER_INVALID_ARGUMENT, msg); -// } -// return Status::OK(); -// } - -Status -Config::CheckDBConfigArchiveDiskThreshold(const std::string& value) { - auto exist_error = !ValidationUtil::ValidateStringIsNumber(value).ok(); - fiu_do_on("check_config_archive_disk_threshold_fail", exist_error = true); - - if (exist_error) { - std::string msg = "Invalid archive disk threshold: " + value + - ". Possible reason: db_config.archive_disk_threshold is invalid."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - return Status::OK(); -} - -Status -Config::CheckDBConfigArchiveDaysThreshold(const std::string& value) { - auto exist_error = !ValidationUtil::ValidateStringIsNumber(value).ok(); - fiu_do_on("check_config_archive_days_threshold_fail", exist_error = true); - - if (exist_error) { - std::string msg = "Invalid archive days threshold: " + value + - ". Possible reason: db_config.archive_days_threshold is invalid."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - return Status::OK(); -} - -/* storage config */ -Status -Config::CheckStorageConfigPath(const std::string& value) { - fiu_return_on("check_config_path_fail", Status(SERVER_INVALID_ARGUMENT, "")); - if (value.empty()) { - return Status(SERVER_INVALID_ARGUMENT, "storage.path is empty."); - } - - return ValidationUtil::ValidateStoragePath(value); -} - -Status -Config::CheckStorageConfigAutoFlushInterval(const std::string& value) { - auto exist_error = !ValidationUtil::ValidateStringIsNumber(value).ok(); - fiu_do_on("check_config_auto_flush_interval_fail", exist_error = true); - - if (exist_error) { - std::string msg = "Invalid storage configuration auto_flush_interval: " + value + - ". Possible reason: storage.auto_flush_interval is not a natural number."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - - return Status::OK(); -} - -Status -Config::CheckStorageConfigFileCleanupTimeout(const std::string& value) { - if (!ValidationUtil::ValidateStringIsNumber(value).ok()) { - std::string msg = "Invalid file_cleanup_timeout: " + value + - ". Possible reason: storage.file_cleanup_timeout is not a positive integer."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } else { - int64_t file_cleanup_timeout = std::stoll(value); - if (file_cleanup_timeout < CONFIG_STORAGE_FILE_CLEANUP_TIMEOUT_MIN || - file_cleanup_timeout > CONFIG_STORAGE_FILE_CLEANUP_TIMEOUT_MAX) { - std::string msg = "Invalid file_cleanup_timeout: " + value + - ". Possible reason: storage.file_cleanup_timeout is not in range [" + - std::to_string(CONFIG_STORAGE_FILE_CLEANUP_TIMEOUT_MIN) + ", " + - std::to_string(CONFIG_STORAGE_FILE_CLEANUP_TIMEOUT_MIN) + "]."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - } - - return Status::OK(); -} - -// Status -// Config::CheckStorageConfigS3Enable(const std::string& value) { -// if (!ValidationUtil::ValidateStringIsBool(value).ok()) { -// std::string msg = -// "Invalid storage config: " + value + ". Possible reason: storage_config.s3_enable is not a boolean."; -// return Status(SERVER_INVALID_ARGUMENT, msg); -// } -// return Status::OK(); -// } -// -// Status -// Config::CheckStorageConfigS3Address(const std::string& value) { -// if (!ValidationUtil::ValidateIpAddress(value).ok()) { -// std::string msg = "Invalid s3 address: " + value + ". Possible reason: storage_config.s3_address is invalid."; -// return Status(SERVER_INVALID_ARGUMENT, msg); -// } -// return Status::OK(); -// } -// -// Status -// Config::CheckStorageConfigS3Port(const std::string& value) { -// if (!ValidationUtil::ValidateStringIsNumber(value).ok()) { -// std::string msg = "Invalid s3 port: " + value + ". Possible reason: storage_config.s3_port is not a number."; -// return Status(SERVER_INVALID_ARGUMENT, msg); -// } else { -// try { -// int32_t port = std::stoi(value); -// if (!(port > PORT_NUMBER_MIN && port < PORT_NUMBER_MAX)) { -// std::string msg = "Invalid s3 port: " + value + -// ". Possible reason: storage_config.s3_port is not in range (1024, 65535)."; -// return Status(SERVER_INVALID_ARGUMENT, msg); -// } -// } catch (...) { -// return Status(SERVER_INVALID_ARGUMENT, "Invalid storage_config.s3_port: " + value); -// } -// } -// return Status::OK(); -// } -// -// Status -// Config::CheckStorageConfigS3AccessKey(const std::string& value) { -// if (value.empty()) { -// return Status(SERVER_INVALID_ARGUMENT, "storage_config.s3_access_key is empty."); -// } -// return Status::OK(); -// } -// -// Status -// Config::CheckStorageConfigS3SecretKey(const std::string& value) { -// if (value.empty()) { -// return Status(SERVER_INVALID_ARGUMENT, "storage_config.s3_secret_key is empty."); -// } -// return Status::OK(); -// } -// -// Status -// Config::CheckStorageConfigS3Bucket(const std::string& value) { -// if (value.empty()) { -// return Status(SERVER_INVALID_ARGUMENT, "storage_config.s3_bucket is empty."); -// } -// return Status::OK(); -// } - -/* metric config */ -Status -Config::CheckMetricConfigEnableMonitor(const std::string& value) { - auto exist_error = !ValidationUtil::ValidateStringIsBool(value).ok(); - fiu_do_on("check_config_enable_monitor_fail", exist_error = true); - - if (exist_error) { - std::string msg = "Invalid metric config: " + value + ". Possible reason: metric.enable is not a boolean."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - return Status::OK(); -} - -Status -Config::CheckMetricConfigAddress(const std::string& value) { - if (!ValidationUtil::ValidateIpAddress(value).ok()) { - std::string msg = "Invalid metric ip: " + value + ". Possible reason: metric.ip is invalid."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - return Status::OK(); -} - -Status -Config::CheckMetricConfigPort(const std::string& value) { - if (!ValidationUtil::ValidateStringIsNumber(value).ok()) { - std::string msg = "Invalid metric port: " + value + ". Possible reason: metric.port is not a number."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } else { - try { - int32_t port = std::stoi(value); - if (!(port > PORT_NUMBER_MIN && port < PORT_NUMBER_MAX)) { - std::string msg = - "Invalid metric port: " + value + ". Possible reason: metric.port is not in range (1024, 65535)."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - } catch (...) { - return Status(SERVER_INVALID_ARGUMENT, "Invalid metric.port: " + value); - } - } - return Status::OK(); -} - -/* cache config */ -Status -Config::CheckCacheConfigCpuCacheCapacity(const std::string& value) { - fiu_return_on("check_config_cache_size_fail", Status(SERVER_INVALID_ARGUMENT, "")); - -#if 1 - std::string err; - int64_t cache_size = parse_bytes(value, err); - if (not err.empty()) { - return Status(SERVER_INVALID_ARGUMENT, err); - } else { - if (cache_size <= 0) { - std::string msg = "Invalid cpu cache capacity: " + value + - ". Possible reason: cache.cache_size is not a positive integer."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - - int64_t total_mem = 0, free_mem = 0; - CommonUtil::GetSystemMemInfo(total_mem, free_mem); - - int64_t cgroup_limit_mem = std::numeric_limits::max(); - CommonUtil::GetSysCgroupMemLimit(cgroup_limit_mem); - if (cgroup_limit_mem < total_mem && cache_size >= cgroup_limit_mem) { - std::string msg = "Invalid cpu cache size: " + value + - ". Possible reason: cache.cache_size exceeds system cgroup memory."; - return Status{SERVER_INVALID_ARGUMENT, msg}; - } - - if (cache_size >= total_mem) { - std::string msg = - "Invalid cpu cache size: " + value + ". Possible reason: cache.cache_size exceeds system memory."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } else if (static_cast(cache_size) > static_cast(total_mem * 0.9)) { - std::cerr << "WARNING: cpu cache size value is too big" << std::endl; - } - - std::string str = GetConfigStr(CONFIG_CACHE, CONFIG_CACHE_INSERT_BUFFER_SIZE, "0"); - - int64_t insert_buffer_size = parse_bytes(str, err); - fiu_do_on("Config.CheckCacheConfigCpuCacheCapacity.large_insert_buffer", insert_buffer_size = total_mem + 1); - if (insert_buffer_size + cache_size >= total_mem) { - std::string msg = "Invalid cpu cache size: " + value + - ". Possible reason: sum of cache.cache_size and " - "cache.insert_buffer_size exceeds system memory."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - } -#else - if (!ValidationUtil::ValidateStringIsNumber(value).ok()) { - std::string msg = "Invalid cpu cache capacity: " + value + - ". Possible reason: cache_config.cpu_cache_capacity is not a positive integer."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } else { - int64_t cpu_cache_capacity = std::stoll(value) * GB; - if (cpu_cache_capacity <= 0) { - std::string msg = "Invalid cpu cache capacity: " + value + - ". Possible reason: cache_config.cpu_cache_capacity is not a positive integer."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - - int64_t total_mem = 0, free_mem = 0; - CommonUtil::GetSystemMemInfo(total_mem, free_mem); - if (cpu_cache_capacity >= total_mem) { - std::string msg = "Invalid cpu cache capacity: " + value + - ". Possible reason: cache_config.cpu_cache_capacity exceeds system memory."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } else if (static_cast(cpu_cache_capacity) > static_cast(total_mem * 0.9)) { - std::cerr << "WARNING: cpu cache capacity value is too big" << std::endl; - } - - std::string str = GetConfigStr(CONFIG_CACHE, CONFIG_CACHE_INSERT_BUFFER_SIZE, "0"); - int64_t buffer_value = std::stoll(str); - - int64_t insert_buffer_size = buffer_value * GB; - fiu_do_on("Config.CheckCacheConfigCpuCacheCapacity.large_insert_buffer", insert_buffer_size = total_mem + 1); - if (insert_buffer_size + cpu_cache_capacity >= total_mem) { - std::string msg = "Invalid cpu cache capacity: " + value + - ". Possible reason: sum of cache_config.cpu_cache_capacity and " - "cache_config.insert_buffer_size exceeds system memory."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - } -#endif - return Status::OK(); -} - -Status -Config::CheckCacheConfigCpuCacheThreshold(const std::string& value) { - fiu_return_on("check_config_cpu_cache_threshold_fail", Status(SERVER_INVALID_ARGUMENT, "")); - - if (!ValidationUtil::ValidateStringIsFloat(value).ok()) { - std::string msg = "Invalid cpu cache threshold: " + value + - ". Possible reason: cache_config.cpu_cache_threshold is not in range (0.0, 1.0]."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } else { - float cpu_cache_threshold = std::stof(value); - if (cpu_cache_threshold <= 0.0 || cpu_cache_threshold >= 1.0) { - std::string msg = "Invalid cpu cache threshold: " + value + - ". Possible reason: cache_config.cpu_cache_threshold is not in range (0.0, 1.0]."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - } - return Status::OK(); -} - -Status -Config::CheckCacheConfigInsertBufferSize(const std::string& value) { - fiu_return_on("check_config_insert_buffer_size_fail", Status(SERVER_INVALID_ARGUMENT, "")); - -#if 1 - std::string err; - int64_t buffer_size = parse_bytes(value, err); - if (not err.empty()) { - return Status(SERVER_INVALID_ARGUMENT, err); - } else { - if (buffer_size <= 0) { - std::string msg = "Invalid insert buffer size: " + value + - ". Possible reason: cache.insert_buffer_size is not a positive integer."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - - std::string str = GetConfigStr(CONFIG_CACHE, CONFIG_CACHE_CPU_CACHE_CAPACITY, "0"); - std::string err; - int64_t cache_size = parse_bytes(str, err); - - int64_t total_mem = 0, free_mem = 0; - CommonUtil::GetSystemMemInfo(total_mem, free_mem); - if (buffer_size + cache_size >= total_mem) { - std::string msg = "Invalid insert buffer size: " + value + - ". Possible reason: sum of cache.cache_size and " - "cache.insert_buffer_size exceeds system memory."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - } -#else - if (!ValidationUtil::ValidateStringIsNumber(value).ok()) { - std::string msg = "Invalid insert buffer size: " + value + - ". Possible reason: cache_config.insert_buffer_size is not a positive integer."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } else { - int64_t buffer_size = std::stoll(value) * GB; - if (buffer_size <= 0) { - std::string msg = "Invalid insert buffer size: " + value + - ". Possible reason: cache_config.insert_buffer_size is not a positive integer."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - - std::string str = GetConfigStr(CONFIG_CACHE, CONFIG_CACHE_CPU_CACHE_CAPACITY, "0"); - std::string err; - int64_t cache_size = parse_bytes(str, err); - - int64_t total_mem = 0, free_mem = 0; - CommonUtil::GetSystemMemInfo(total_mem, free_mem); - if (buffer_size + cache_size >= total_mem) { - std::string msg = "Invalid insert buffer size: " + value + - ". Possible reason: cache_config.insert_buffer_size exceeds system memory."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - } -#endif - return Status::OK(); -} - -Status -Config::CheckCacheConfigCacheInsertData(const std::string& value) { - fiu_return_on("check_config_cache_insert_data_fail", Status(SERVER_INVALID_ARGUMENT, "")); - - if (!ValidationUtil::ValidateStringIsBool(value).ok()) { - std::string msg = "Invalid cache insert data option: " + value + - ". Possible reason: cache_config.cache_insert_data is not a boolean."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - return Status::OK(); -} - -Status -Config::CheckCacheConfigPreloadCollection(const std::string& value) { - fiu_return_on("check_config_preload_collection_fail", Status(SERVER_INVALID_ARGUMENT, "")); - - if (value.empty() || value == "*") { - return Status::OK(); - } - - std::vector tables; - StringHelpFunctions::SplitStringByDelimeter(value, ",", tables); - - std::unordered_set table_set; - - for (auto& collection : tables) { - if (!ValidationUtil::ValidateCollectionName(collection).ok()) { - return Status(SERVER_INVALID_ARGUMENT, "Invalid collection name: " + collection); - } - bool exist = false; - auto status = DBWrapper::DB()->HasNativeCollection(collection, exist); - if (!(status.ok() && exist)) { - return Status(SERVER_COLLECTION_NOT_EXIST, "Collection " + collection + " not exist"); - } - table_set.insert(collection); - } - - if (table_set.size() != tables.size()) { - std::string msg = - "Invalid preload tables. " - "Possible reason: cache.preload_collection contains duplicate collection."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - - return Status::OK(); -} - -/* engine config */ -Status -Config::CheckEngineConfigUseBlasThreshold(const std::string& value) { - fiu_return_on("check_config_use_blas_threshold_fail", Status(SERVER_INVALID_ARGUMENT, "")); - - if (!ValidationUtil::ValidateStringIsNumber(value).ok()) { - std::string msg = "Invalid use blas threshold: " + value + - ". Possible reason: engine_config.use_blas_threshold is not a positive integer."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - return Status::OK(); -} - -Status -Config::CheckEngineConfigOmpThreadNum(const std::string& value) { - fiu_return_on("check_config_omp_thread_num_fail", Status(SERVER_INVALID_ARGUMENT, "")); - - if (!ValidationUtil::ValidateStringIsNumber(value).ok()) { - std::string msg = "Invalid omp thread num: " + value + - ". Possible reason: engine_config.omp_thread_num is not a positive integer."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - - int64_t omp_thread = std::stoll(value); - int64_t sys_thread_cnt = 8; - CommonUtil::GetSystemAvailableThreads(sys_thread_cnt); - if (omp_thread > sys_thread_cnt) { - std::string msg = "Invalid omp thread num: " + value + - ". Possible reason: engine_config.omp_thread_num exceeds system cpu cores."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - return Status::OK(); -} - -Status -Config::CheckEngineConfigSimdType(const std::string& value) { - fiu_return_on("check_config_simd_type_fail", - Status(SERVER_INVALID_ARGUMENT, "engine_config.simd_type is not one of avx512, avx2, sse and auto.")); - - if (value != "avx512" && value != "avx2" && value != "sse" && value != "auto") { - return Status(SERVER_INVALID_ARGUMENT, "engine_config.simd_type is not one of avx512, avx2, sse and auto."); - } - return Status::OK(); -} - -Status -Config::CheckEngineSearchCombineMaxNq(const std::string& value) { - fiu_return_on("check_config_search_combine_nq_fail", Status(SERVER_INVALID_ARGUMENT, "")); - - if (!ValidationUtil::ValidateStringIsNumber(value).ok()) { - std::string msg = "Invalid omp thread num: " + value + - ". Possible reason: engine_config.omp_thread_num is not a positive integer."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - return Status::OK(); -} - -#ifdef MILVUS_GPU_VERSION - -/* gpu resource config */ -Status -Config::CheckGpuResourceConfigEnable(const std::string& value) { - fiu_return_on("check_config_gpu_resource_enable_fail", Status(SERVER_INVALID_ARGUMENT, "")); - - if (!ValidationUtil::ValidateStringIsBool(value).ok()) { - std::string msg = "Invalid gpu resource config: " + value + ". Possible reason: gpu.enable is not a boolean."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - return Status::OK(); -} - -Status -Config::CheckGpuResourceConfigCacheEnable(const std::string& value) { - if (!ValidationUtil::ValidateStringIsBool(value).ok()) { - std::string msg = - "Invalid gpu resource config: " + value + ". Possible reason: gpu.cache.enable is not a boolean."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - return Status::OK(); -} - -Status -Config::CheckGpuResourceConfigCacheCapacity(const std::string& value) { - fiu_return_on("check_gpu_cache_size_fail", Status(SERVER_INVALID_ARGUMENT, "")); - -#if 1 - std::string err; - int64_t gpu_cache_size = parse_bytes(value, err); - if (not err.empty()) { - return Status(SERVER_INVALID_ARGUMENT, err); - } else { - if (gpu_cache_size < 0) { - std::string msg = "gpu.cache_size must greater than 0, now is " + std::to_string(gpu_cache_size) + "."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - std::vector gpu_ids; - STATUS_CHECK(GetGpuResourceConfigBuildIndexResources(gpu_ids)); - - for (int64_t gpu_id : gpu_ids) { - int64_t gpu_memory; - if (!ValidationUtil::GetGpuMemory(gpu_id, gpu_memory).ok()) { - std::string msg = "Fail to get GPU memory for GPU device: " + std::to_string(gpu_id); - return Status(SERVER_UNEXPECTED_ERROR, msg); - } else if (gpu_cache_size >= gpu_memory) { - std::string msg = - "Invalid gpu cache capacity: " + value + ". Possible reason: gpu.cache_size exceeds GPU memory."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } else if (gpu_cache_size > (double)gpu_memory * 0.9) { - std::cerr << "Warning: gpu cache size value is too big" << std::endl; - } - } - } -#else - if (!ValidationUtil::ValidateStringIsNumber(value).ok()) { - std::string msg = - "Invalid gpu cache capacity: " + value + ". Possible reason: gpu.cache_capacity is not a positive integer."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } else { - int64_t gpu_cache_capacity = std::stoll(value) * GB; - std::vector gpu_ids; - STATUS_CHECK(GetGpuResourceConfigBuildIndexResources(gpu_ids)); - - for (int64_t gpu_id : gpu_ids) { - int64_t gpu_memory; - if (!ValidationUtil::GetGpuMemory(gpu_id, gpu_memory).ok()) { - std::string msg = "Fail to get GPU memory for GPU device: " + std::to_string(gpu_id); - return Status(SERVER_UNEXPECTED_ERROR, msg); - } else if (gpu_cache_capacity >= gpu_memory) { - std::string msg = "Invalid gpu cache capacity: " + value + - ". Possible reason: gpu.cache_capacity exceeds GPU memory."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } else if (gpu_cache_capacity > (double)gpu_memory * 0.9) { - std::cerr << "Warning: gpu cache capacity value is too big" << std::endl; - } - } - } -#endif - return Status::OK(); -} - -Status -Config::CheckGpuResourceConfigCacheThreshold(const std::string& value) { - fiu_return_on("check_config_gpu_resource_cache_threshold_fail", Status(SERVER_INVALID_ARGUMENT, "")); - - if (!ValidationUtil::ValidateStringIsFloat(value).ok()) { - std::string msg = "Invalid gpu cache threshold: " + value + - ". Possible reason: gpu.cache_threshold is not in range (0.0, 1.0]."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } else { - float gpu_cache_threshold = std::stof(value); - if (gpu_cache_threshold <= 0.0 || gpu_cache_threshold >= 1.0) { - std::string msg = "Invalid gpu cache threshold: " + value + - ". Possible reason: gpu.cache_threshold is not in range (0.0, 1.0]."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - } - return Status::OK(); -} - -Status -Config::CheckGpuResourceConfigGpuSearchThreshold(const std::string& value) { - fiu_return_on("check_config_gpu_search_threshold_fail", Status(SERVER_INVALID_ARGUMENT, "")); - - if (!ValidationUtil::ValidateStringIsNumber(value).ok()) { - std::string msg = "Invalid gpu search threshold: " + value + - ". Possible reason: gpu.gpu_search_threshold is not a positive integer."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - return Status::OK(); -} - -Status -CheckGpuResource(const std::string& value) { - std::string s = value; - std::transform(s.begin(), s.end(), s.begin(), ::tolower); - - const std::regex pat("gpu(\\d+)"); - std::smatch m; - if (!std::regex_match(s, m, pat)) { - std::string msg = - "Invalid gpu resource: " + value + ". Possible reason: gpu is not in the format of cpux or gpux"; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - - if (s.compare(0, 3, "gpu") == 0) { - try { - int32_t gpu_index = std::stoi(s.substr(3)); - if (!ValidationUtil::ValidateGpuIndex(gpu_index).ok()) { - std::string msg = - "Invalid gpu resource: " + value + ". Possible reason: gpu does not match with the hardware."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - } catch (...) { - return Status(SERVER_INVALID_ARGUMENT, "Invalid gpu_resource_config: " + value); - } - } - - return Status::OK(); -} - -Status -Config::CheckGpuResourceConfigSearchResources(const std::vector& value) { - fiu_return_on("check_gpu_search_fail", Status(SERVER_INVALID_ARGUMENT, "")); - - if (value.empty()) { - std::string msg = - "Invalid gpu search resource. " - "Possible reason: gpu.search_resources is empty."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - - std::unordered_set value_set; - for (auto& resource : value) { - STATUS_CHECK(CheckGpuResource(resource)); - value_set.insert(resource); - } - - if (value_set.size() != value.size()) { - std::string msg = - "Invalid gpu build search resource. " - "Possible reason: gpu.gpu_search_resources contains duplicate resources."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - - return Status::OK(); -} - -Status -Config::CheckGpuResourceConfigBuildIndexResources(const std::vector& value) { - fiu_return_on("check_gpu_build_index_fail", Status(SERVER_INVALID_ARGUMENT, "")); - - if (value.empty()) { - std::string msg = - "Invalid gpu build index resource. " - "Possible reason: gpu.build_index_resources is empty."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - - std::unordered_set value_set; - for (auto& resource : value) { - STATUS_CHECK(CheckGpuResource(resource)); - value_set.insert(resource); - } - - if (value_set.size() != value.size()) { - std::string msg = - "Invalid gpu build index resource. " - "Possible reason: gpu.build_index_resources contains duplicate resources."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - - return Status::OK(); -} - -#endif - -/* tracing config */ -Status -Config::CheckTracingConfigJsonConfigPath(const std::string& value) { - std::string msg = "Invalid wal config: " + value + - ". Possible reason: tracing_config.json_config_path is not supported to configure."; - return Status(SERVER_INVALID_ARGUMENT, msg); -} - -/* wal config */ -Status -Config::CheckWalConfigEnable(const std::string& value) { - auto exist_error = !ValidationUtil::ValidateStringIsBool(value).ok(); - fiu_do_on("check_config_wal_enable_fail", exist_error = true); - - if (exist_error) { - std::string msg = "Invalid wal config: " + value + ". Possible reason: wal.enable is not a boolean."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - return Status::OK(); -} - -Status -Config::CheckWalConfigRecoveryErrorIgnore(const std::string& value) { - auto exist_error = !ValidationUtil::ValidateStringIsBool(value).ok(); - fiu_do_on("check_config_wal_recovery_error_ignore_fail", exist_error = true); - - if (exist_error) { - std::string msg = - "Invalid wal config: " + value + ". Possible reason: wal.recovery_error_ignore is not a boolean."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - return Status::OK(); -} - -Status -Config::CheckWalConfigBufferSize(const std::string& value) { - std::string err; - auto buffer_size = parse_bytes(value, err); - auto exist_error = not err.empty(); - fiu_do_on("check_config_wal_buffer_size_fail", exist_error = true); - - if (exist_error || buffer_size < 0) { - std::string msg = - "Invalid wal buffer size: " + value + ". Possible reason: wal.buffer_size is not a positive integer."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - return Status::OK(); -} - -Status -Config::CheckWalConfigWalPath(const std::string& value) { - fiu_return_on("check_wal_path_fail", Status(SERVER_INVALID_ARGUMENT, "")); - if (value.empty()) { - return Status(SERVER_INVALID_ARGUMENT, "wal.path is empty!"); - } - - return ValidationUtil::ValidateStoragePath(value); -} - -/* logs config */ -Status -Config::CheckLogsLevel(const std::string& value) { - fiu_return_on("check_logs_level_fail", Status(SERVER_INVALID_ARGUMENT, "")); - if (value.empty()) { - return Status(SERVER_INVALID_ARGUMENT, "logs.level is empty!"); - } - return ValidationUtil::ValidateLogLevel(value); -} - -Status -Config::CheckLogsTraceEnable(const std::string& value) { - auto exist_error = !ValidationUtil::ValidateStringIsBool(value).ok(); - fiu_do_on("check_logs_trace_enable_fail", exist_error = true); - - if (exist_error) { - std::string msg = "Invalid logs config: " + value + ". Possible reason: logs.trace.enable is not a boolean."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - return Status::OK(); -} - -Status -Config::CheckLogsPath(const std::string& value) { - fiu_return_on("check_logs_path_fail", Status(SERVER_INVALID_ARGUMENT, "")); - if (value.empty()) { - return Status(SERVER_INVALID_ARGUMENT, "logs.path is empty!"); - } - - return ValidationUtil::ValidateStoragePath(value); -} - -Status -Config::CheckLogsMaxLogFileSize(const std::string& value) { - std::string err; - int64_t max_log_file_size = parse_bytes(value, err); - auto exist_error = not err.empty(); - fiu_do_on("check_logs_max_log_file_size_fail", exist_error = true); - - if (exist_error) { - std::string msg = "Invalid max_log_file_size: " + value + - ". Possible reason: logs.max_log_file_size is not a positive integer."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } else { - if (max_log_file_size < CONFIG_LOGS_MAX_LOG_FILE_SIZE_MIN || - max_log_file_size > CONFIG_LOGS_MAX_LOG_FILE_SIZE_MAX) { - std::string msg = "Invalid max_log_file_size: " + value + - ". Possible reason: logs.max_log_file_size is not in range [" + - std::to_string(CONFIG_LOGS_MAX_LOG_FILE_SIZE_MIN) + ", " + - std::to_string(CONFIG_LOGS_MAX_LOG_FILE_SIZE_MAX) + "]."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - } - return Status::OK(); -} - -Status -Config::CheckLogsLogRotateNum(const std::string& value) { - auto exist_error = !ValidationUtil::ValidateStringIsNumber(value).ok(); - fiu_do_on("check_logs_log_rotate_num_fail", exist_error = true); - - if (exist_error) { - std::string msg = - "Invalid log_rotate_num: " + value + ". Possible reason: logs.log_rotate_num is not a positive integer."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } else { - int64_t log_rotate_num = std::stoll(value); - if (log_rotate_num < CONFIG_LOGS_LOG_ROTATE_NUM_MIN || log_rotate_num > CONFIG_LOGS_LOG_ROTATE_NUM_MAX) { - std::string msg = "Invalid log_rotate_num: " + value + - ". Possible reason: logs.log_rotate_num is not in range [" + - std::to_string(CONFIG_LOGS_LOG_ROTATE_NUM_MIN) + ", " + - std::to_string(CONFIG_LOGS_LOG_ROTATE_NUM_MAX) + "]."; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - } - return Status::OK(); -} - -//////////////////////////////////////////////////////////////////////////////// -ConfigNode& -Config::GetConfigRoot() { - ConfigMgr* mgr = YamlConfigMgr::GetInstance(); - return mgr->GetRootNode(); -} - -ConfigNode& -Config::GetConfigNode(const std::string& name) { - return GetConfigRoot().GetChild(name); -} - -bool -Config::ConfigNodeValid(const std::string& parent_key, const std::string& child_key) { - if (config_map_.find(parent_key) == config_map_.end()) { - return false; - } - return config_map_[parent_key].count(child_key) != 0; -} - -Status -Config::GetConfigValueInMem(const std::string& parent_key, const std::string& child_key, std::string& value) { - std::lock_guard lock(mutex_); - if (config_map_.find(parent_key) != config_map_.end() && - config_map_[parent_key].find(child_key) != config_map_[parent_key].end()) { - value = config_map_[parent_key][child_key]; - return Status::OK(); - } - return Status(SERVER_UNEXPECTED_ERROR, "key not exist"); -} - -Status -Config::SetConfigValueInMem(const std::string& parent_key, const std::string& child_key, const std::string& value) { - std::lock_guard lock(mutex_); - config_map_[parent_key][child_key] = value; - return Status::OK(); -} - -//////////////////////////////////////////////////////////////////////////////// -std::string -Config::GetConfigStr(const std::string& parent_key, const std::string& child_key, const std::string& default_value) { - std::string value; - if (!GetConfigValueInMem(parent_key, child_key, value).ok()) { - value = GetConfigNode(parent_key).GetValue(child_key, default_value); - SetConfigValueInMem(parent_key, child_key, value); - } - return value; -} - -std::string -Config::GetConfigSequenceStr(const std::string& parent_key, const std::string& child_key, const std::string& delim, - const std::string& default_value) { - std::string value; - if (!GetConfigValueInMem(parent_key, child_key, value).ok()) { - std::vector sequence = GetConfigNode(parent_key).GetSequence(child_key); - if (sequence.empty()) { - value = default_value; - } else { - server::StringHelpFunctions::MergeStringWithDelimeter(sequence, delim, value); - } - SetConfigValueInMem(parent_key, child_key, value); - } - return value; -} - -Status -Config::GetConfigVersion(std::string& value) { - value = GetConfigRoot().GetValue(CONFIG_VERSION); - return CheckConfigVersion(value); -} - -Status -Config::ExecCallBacks(const std::string& node, const std::string& sub_node, const std::string& value) { - auto status = Status::OK(); - std::lock_guard lock(callback_mutex_); - if (config_callback_.empty()) { - return Status(SERVER_UNEXPECTED_ERROR, "Callback map is empty. Cannot take effect in-service"); - } - - std::string cb_node = node + "." + sub_node; - if (config_callback_.find(cb_node) == config_callback_.end()) { - return Status(SERVER_UNEXPECTED_ERROR, - "Cannot find " + cb_node + " in callback map, cannot take effect in-service"); - } - - auto& cb_map = config_callback_.at(cb_node); - for (auto& cb_kv : cb_map) { - auto& cd = cb_kv.second; - status = cd(value); - if (!status.ok()) { - break; - } - } - - return status; -} - -/* cluster config */ -Status -Config::GetClusterConfigEnable(bool& value) { - std::string str = GetConfigStr(CONFIG_CLUSTER, CONFIG_CLUSTER_ENABLE, CONFIG_CLUSTER_ENABLE_DEFAULT); - STATUS_CHECK(CheckClusterConfigEnable(str)); - return StringHelpFunctions::ConvertToBoolean(str, value); -} - -Status -Config::GetClusterConfigRole(std::string& value) { - value = GetConfigStr(CONFIG_CLUSTER, CONFIG_CLUSTER_ROLE, CONFIG_CLUSTER_ROLE_DEFAULT); - return CheckClusterConfigRole(value); -} - -/* general config */ -Status -Config::GetGeneralConfigTimezone(std::string& value) { - value = GetConfigStr(CONFIG_GENERAL, CONFIG_GENERAL_TIMEZONE, CONFIG_GENERAL_TIMEZONE_DEFAULT); - return CheckGeneralConfigTimezone(value); -} - -Status -Config::GetGeneralConfigMetaURI(std::string& value) { - value = GetConfigStr(CONFIG_GENERAL, CONFIG_GENERAL_METAURI, CONFIG_GENERAL_METAURI_DEFAULT); - return CheckGeneralConfigMetaURI(value); -} - -/* network config */ -Status -Config::GetNetworkConfigBindAddress(std::string& value) { - value = GetConfigStr(CONFIG_NETWORK, CONFIG_NETWORK_BIND_ADDRESS, CONFIG_NETWORK_BIND_ADDRESS_DEFAULT); - return CheckNetworkConfigBindAddress(value); -} - -Status -Config::GetNetworkConfigBindPort(std::string& value) { - value = GetConfigStr(CONFIG_NETWORK, CONFIG_NETWORK_BIND_PORT, CONFIG_NETWORK_BIND_PORT_DEFAULT); - return CheckNetworkConfigBindPort(value); -} - -Status -Config::GetNetworkConfigHTTPEnable(bool& value) { - std::string str = GetConfigStr(CONFIG_NETWORK, CONFIG_NETWORK_HTTP_ENABLE, CONFIG_NETWORK_HTTP_ENABLE_DEFAULT); - STATUS_CHECK(CheckNetworkConfigHTTPEnable(str)); - return StringHelpFunctions::ConvertToBoolean(str, value); -} - -Status -Config::GetNetworkConfigHTTPPort(std::string& value) { - value = GetConfigStr(CONFIG_NETWORK, CONFIG_NETWORK_HTTP_PORT, CONFIG_NETWORK_HTTP_PORT_DEFAULT); - return CheckNetworkConfigHTTPPort(value); -} - -/* server config */ -// Status -// Config::GetServerConfigAddress(std::string& value) { -// value = GetConfigStr(CONFIG_SERVER, CONFIG_SERVER_ADDRESS, CONFIG_SERVER_ADDRESS_DEFAULT); -// return CheckServerConfigAddress(value); -// } - -// Status -// Config::GetServerConfigPort(std::string& value) { -// value = GetConfigStr(CONFIG_SERVER, CONFIG_SERVER_PORT, CONFIG_SERVER_PORT_DEFAULT); -// return CheckServerConfigPort(value); -// } - -// Status -// Config::GetServerConfigDeployMode(std::string& value) { -// value = GetConfigStr(CONFIG_SERVER, CONFIG_SERVER_DEPLOY_MODE, CONFIG_SERVER_DEPLOY_MODE_DEFAULT); -// return CheckServerConfigDeployMode(value); -// } - -// Status -// Config::GetServerConfigTimeZone(std::string& value) { -// value = GetConfigStr(CONFIG_SERVER, CONFIG_SERVER_TIME_ZONE, CONFIG_SERVER_TIME_ZONE_DEFAULT); -// return CheckServerConfigTimeZone(value); -// } - -// Status -// Config::GetServerConfigWebEnable(bool& value) { -// std::string str = GetConfigStr(CONFIG_SERVER, CONFIG_SERVER_WEB_ENABLE, CONFIG_SERVER_WEB_ENABLE_DEFAULT); -// STATUS_CHECK(CheckServerConfigWebEnable(str)); -// return StringHelpFunctions::ConvertToBoolean(str, value); -// } - -// Status -// Config::GetServerConfigWebPort(std::string& value) { -// value = GetConfigStr(CONFIG_SERVER, CONFIG_SERVER_WEB_PORT, CONFIG_SERVER_WEB_PORT_DEFAULT); -// return CheckServerConfigWebPort(value); -// } - -/* DB config */ -// Status -// Config::GetDBConfigBackendUrl(std::string& value) { -// value = GetConfigStr(CONFIG_DB, CONFIG_DB_BACKEND_URL, CONFIG_DB_BACKEND_URL_DEFAULT); -// return CheckDBConfigBackendUrl(value); -// } - -Status -Config::GetDBConfigArchiveDiskThreshold(int64_t& value) { - std::string str = - GetConfigStr(CONFIG_DB, CONFIG_DB_ARCHIVE_DISK_THRESHOLD, CONFIG_DB_ARCHIVE_DISK_THRESHOLD_DEFAULT); - STATUS_CHECK(CheckDBConfigArchiveDiskThreshold(str)); - value = std::stoll(str); - return Status::OK(); -} - -Status -Config::GetDBConfigArchiveDaysThreshold(int64_t& value) { - std::string str = - GetConfigStr(CONFIG_DB, CONFIG_DB_ARCHIVE_DAYS_THRESHOLD, CONFIG_DB_ARCHIVE_DAYS_THRESHOLD_DEFAULT); - STATUS_CHECK(CheckDBConfigArchiveDaysThreshold(str)); - value = std::stoll(str); - return Status::OK(); -} - -/* storage config */ -Status -Config::GetStorageConfigPath(std::string& value) { - value = GetConfigStr(CONFIG_STORAGE, CONFIG_STORAGE_PATH, CONFIG_STORAGE_PATH_DEFAULT); - return CheckStorageConfigPath(value); -} - -Status -Config::GetStorageConfigAutoFlushInterval(int64_t& value) { - std::string str = - GetConfigStr(CONFIG_STORAGE, CONFIG_STORAGE_AUTO_FLUSH_INTERVAL, CONFIG_STORAGE_AUTO_FLUSH_INTERVAL_DEFAULT); - STATUS_CHECK(CheckStorageConfigAutoFlushInterval(str)); - value = std::stoll(str); - return Status::OK(); -} - -Status -Config::GetStorageConfigFileCleanupTimeup(int64_t& value) { - std::string str = - GetConfigStr(CONFIG_STORAGE, CONFIG_STORAGE_FILE_CLEANUP_TIMEOUT, CONFIG_STORAGE_FILE_CLEANUP_TIMEOUT_DEFAULT); - STATUS_CHECK(CheckStorageConfigFileCleanupTimeout(str)); - value = std::stoll(str); - return Status::OK(); -} - -// Status -// Config::GetStorageConfigS3Enable(bool& value) { -// std::string str = GetConfigStr(CONFIG_STORAGE, CONFIG_STORAGE_S3_ENABLE, CONFIG_STORAGE_S3_ENABLE_DEFAULT); -// STATUS_CHECK(CheckStorageConfigS3Enable(str)); -// STATUS_CHECK(StringHelpFunctions::ConvertToBoolean(str, value)); -// return Status::OK(); -// } -// -// Status -// Config::GetStorageConfigS3Address(std::string& value) { -// value = GetConfigStr(CONFIG_STORAGE, CONFIG_STORAGE_S3_ADDRESS, CONFIG_STORAGE_S3_ADDRESS_DEFAULT); -// return CheckStorageConfigS3Address(value); -// } -// -// Status -// Config::GetStorageConfigS3Port(std::string& value) { -// value = GetConfigStr(CONFIG_STORAGE, CONFIG_STORAGE_S3_PORT, CONFIG_STORAGE_S3_PORT_DEFAULT); -// return CheckStorageConfigS3Port(value); -// } -// -// Status -// Config::GetStorageConfigS3AccessKey(std::string& value) { -// value = GetConfigStr(CONFIG_STORAGE, CONFIG_STORAGE_S3_ACCESS_KEY, CONFIG_STORAGE_S3_ACCESS_KEY_DEFAULT); -// return Status::OK(); -// } -// -// Status -// Config::GetStorageConfigS3SecretKey(std::string& value) { -// value = GetConfigStr(CONFIG_STORAGE, CONFIG_STORAGE_S3_SECRET_KEY, CONFIG_STORAGE_S3_SECRET_KEY_DEFAULT); -// return Status::OK(); -// } -// -// Status -// Config::GetStorageConfigS3Bucket(std::string& value) { -// value = GetConfigStr(CONFIG_STORAGE, CONFIG_STORAGE_S3_BUCKET, CONFIG_STORAGE_S3_BUCKET_DEFAULT); -// return Status::OK(); -// } - -/* metric config */ -Status -Config::GetMetricConfigEnableMonitor(bool& value) { - std::string str = GetConfigStr(CONFIG_METRIC, CONFIG_METRIC_ENABLE_MONITOR, CONFIG_METRIC_ENABLE_MONITOR_DEFAULT); - STATUS_CHECK(CheckMetricConfigEnableMonitor(str)); - STATUS_CHECK(StringHelpFunctions::ConvertToBoolean(str, value)); - return Status::OK(); -} - -Status -Config::GetMetricConfigAddress(std::string& value) { - value = GetConfigStr(CONFIG_METRIC, CONFIG_METRIC_ADDRESS, CONFIG_METRIC_ADDRESS_DEFAULT); - return Status::OK(); -} - -Status -Config::GetMetricConfigPort(std::string& value) { - value = GetConfigStr(CONFIG_METRIC, CONFIG_METRIC_PORT, CONFIG_METRIC_PORT_DEFAULT); - return CheckMetricConfigPort(value); -} - -/* cache config */ -Status -Config::GetCacheConfigCpuCacheCapacity(int64_t& value) { - std::string str = - GetConfigStr(CONFIG_CACHE, CONFIG_CACHE_CPU_CACHE_CAPACITY, CONFIG_CACHE_CPU_CACHE_CAPACITY_DEFAULT); - STATUS_CHECK(CheckCacheConfigCpuCacheCapacity(str)); - std::string err; - value = parse_bytes(str, err); - // value = std::stoll(str); - return Status::OK(); -} - -Status -Config::GetCacheConfigCpuCacheThreshold(float& value) { - std::string str = - GetConfigStr(CONFIG_CACHE, CONFIG_CACHE_CPU_CACHE_THRESHOLD, CONFIG_CACHE_CPU_CACHE_THRESHOLD_DEFAULT); - STATUS_CHECK(CheckCacheConfigCpuCacheThreshold(str)); - value = std::stof(str); - return Status::OK(); -} - -Status -Config::GetCacheConfigInsertBufferSize(int64_t& value) { - std::string str = - GetConfigStr(CONFIG_CACHE, CONFIG_CACHE_INSERT_BUFFER_SIZE, CONFIG_CACHE_INSERT_BUFFER_SIZE_DEFAULT); - STATUS_CHECK(CheckCacheConfigInsertBufferSize(str)); - std::string err; - value = parse_bytes(str, err); - // value = std::stoll(str); - return Status::OK(); -} - -Status -Config::GetCacheConfigCacheInsertData(bool& value) { - std::string str = - GetConfigStr(CONFIG_CACHE, CONFIG_CACHE_CACHE_INSERT_DATA, CONFIG_CACHE_CACHE_INSERT_DATA_DEFAULT); - STATUS_CHECK(CheckCacheConfigCacheInsertData(str)); - std::transform(str.begin(), str.end(), str.begin(), ::tolower); - value = (str == "true" || str == "on" || str == "yes" || str == "1"); - return Status::OK(); -} - -Status -Config::GetCacheConfigPreloadCollection(std::string& value) { - value = GetConfigStr(CONFIG_CACHE, CONFIG_CACHE_PRELOAD_COLLECTION); - return Status::OK(); -} - -/* engine config */ -Status -Config::GetEngineConfigUseBlasThreshold(int64_t& value) { - std::string str = - GetConfigStr(CONFIG_ENGINE, CONFIG_ENGINE_USE_BLAS_THRESHOLD, CONFIG_ENGINE_USE_BLAS_THRESHOLD_DEFAULT); - STATUS_CHECK(CheckEngineConfigUseBlasThreshold(str)); - value = std::stoll(str); - return Status::OK(); -} - -Status -Config::GetEngineConfigOmpThreadNum(int64_t& value) { - std::string str = GetConfigStr(CONFIG_ENGINE, CONFIG_ENGINE_OMP_THREAD_NUM, CONFIG_ENGINE_OMP_THREAD_NUM_DEFAULT); - STATUS_CHECK(CheckEngineConfigOmpThreadNum(str)); - value = std::stoll(str); - return Status::OK(); -} - -Status -Config::GetEngineConfigSimdType(std::string& value) { - value = GetConfigStr(CONFIG_ENGINE, CONFIG_ENGINE_SIMD_TYPE, CONFIG_ENGINE_SIMD_TYPE_DEFAULT); - return CheckEngineConfigSimdType(value); -} - -Status -Config::GetEngineSearchCombineMaxNq(int64_t& value) { - std::string str = - GetConfigStr(CONFIG_ENGINE, CONFIG_ENGINE_SEARCH_COMBINE_MAX_NQ, CONFIG_ENGINE_SEARCH_COMBINE_MAX_NQ_DEFAULT); - // STATUS_CHECK(CheckEngineSearchCombineMaxNq(str)); - value = std::stoll(str); - return Status::OK(); -} - -/* gpu resource config */ -#ifdef MILVUS_GPU_VERSION - -Status -Config::GetGpuResourceConfigEnable(bool& value) { - std::string str = GetConfigStr(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_ENABLE, CONFIG_GPU_RESOURCE_ENABLE_DEFAULT); - STATUS_CHECK(CheckGpuResourceConfigEnable(str)); - STATUS_CHECK(StringHelpFunctions::ConvertToBoolean(str, value)); - return Status::OK(); -} - -Status -Config::GetGpuResourceConfigCacheEnable(bool& value) { - std::string str = - GetConfigStr(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_CACHE_ENABLE, CONFIG_GPU_RESOURCE_CACHE_ENABLE_DEFAULT); - STATUS_CHECK(CheckGpuResourceConfigCacheEnable(str)); - STATUS_CHECK(StringHelpFunctions::ConvertToBoolean(str, value)); - return Status::OK(); -} - -Status -Config::GetGpuResourceConfigCacheCapacity(int64_t& value) { - bool gpu_resource_enable = false; - STATUS_CHECK(GetGpuResourceConfigEnable(gpu_resource_enable)); - fiu_do_on("Config.GetGpuResourceConfigCacheCapacity.diable_gpu_resource", gpu_resource_enable = false); - if (!gpu_resource_enable) { - std::string msg = "GPU not supported. Possible reason: gpu.enable is set to false."; - return Status(SERVER_UNSUPPORTED_ERROR, msg); - } - std::string str = GetConfigStr(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_CACHE_CAPACITY, - CONFIG_GPU_RESOURCE_CACHE_CAPACITY_DEFAULT); - STATUS_CHECK(CheckGpuResourceConfigCacheCapacity(str)); - std::string err; - value = parse_bytes(str, err); - // value = std::stoll(str); - return Status::OK(); -} - -Status -Config::GetGpuResourceConfigCacheThreshold(float& value) { - bool gpu_resource_enable = false; - STATUS_CHECK(GetGpuResourceConfigEnable(gpu_resource_enable)); - fiu_do_on("Config.GetGpuResourceConfigCacheThreshold.diable_gpu_resource", gpu_resource_enable = false); - if (!gpu_resource_enable) { - std::string msg = "GPU not supported. Possible reason: gpu.enable is set to false."; - return Status(SERVER_UNSUPPORTED_ERROR, msg); - } - std::string str = GetConfigStr(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_CACHE_THRESHOLD, - CONFIG_GPU_RESOURCE_CACHE_THRESHOLD_DEFAULT); - STATUS_CHECK(CheckGpuResourceConfigCacheThreshold(str)); - value = std::stof(str); - return Status::OK(); -} - -Status -Config::GetGpuResourceConfigGpuSearchThreshold(int64_t& value) { - std::string str = GetConfigStr(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_GPU_SEARCH_THRESHOLD, - CONFIG_GPU_RESOURCE_GPU_SEARCH_THRESHOLD_DEFAULT); - STATUS_CHECK(CheckGpuResourceConfigGpuSearchThreshold(str)); - value = std::stoll(str); - return Status::OK(); -} - -Status -Config::GetGpuResourceConfigSearchResources(std::vector& value) { - bool gpu_resource_enable = false; - STATUS_CHECK(GetGpuResourceConfigEnable(gpu_resource_enable)); - fiu_do_on("get_gpu_config_search_resources.disable_gpu_resource_fail", gpu_resource_enable = false); - if (!gpu_resource_enable) { - std::string msg = "GPU not supported. Possible reason: gpu.enable is set to false."; - return Status(SERVER_UNSUPPORTED_ERROR, msg); - } - std::string str = GetConfigSequenceStr(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_SEARCH_RESOURCES, - CONFIG_GPU_RESOURCE_DELIMITER, CONFIG_GPU_RESOURCE_SEARCH_RESOURCES_DEFAULT); - std::vector res_vec; - server::StringHelpFunctions::SplitStringByDelimeter(str, CONFIG_GPU_RESOURCE_DELIMITER, res_vec); - STATUS_CHECK(CheckGpuResourceConfigSearchResources(res_vec)); - value.clear(); - for (std::string& res : res_vec) { - value.push_back(std::stoll(res.substr(3))); - } - return Status::OK(); -} - -Status -Config::GetGpuResourceConfigBuildIndexResources(std::vector& value) { - bool gpu_resource_enable = false; - STATUS_CHECK(GetGpuResourceConfigEnable(gpu_resource_enable)); - fiu_do_on("get_gpu_config_build_index_resources.disable_gpu_resource_fail", gpu_resource_enable = false); - if (!gpu_resource_enable) { - std::string msg = "GPU not supported. Possible reason: gpu.enable is set to false."; - return Status(SERVER_UNSUPPORTED_ERROR, msg); - } - std::string str = - GetConfigSequenceStr(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_BUILD_INDEX_RESOURCES, - CONFIG_GPU_RESOURCE_DELIMITER, CONFIG_GPU_RESOURCE_BUILD_INDEX_RESOURCES_DEFAULT); - std::vector res_vec; - server::StringHelpFunctions::SplitStringByDelimeter(str, CONFIG_GPU_RESOURCE_DELIMITER, res_vec); - STATUS_CHECK(CheckGpuResourceConfigBuildIndexResources(res_vec)); - value.clear(); - for (std::string& res : res_vec) { - value.push_back(std::stoll(res.substr(3))); - } - return Status::OK(); -} - -#endif - -/* tracing config */ -Status -Config::GetTracingConfigJsonConfigPath(std::string& value) { - value = GetConfigStr(CONFIG_TRACING, CONFIG_TRACING_JSON_CONFIG_PATH, ""); - fiu_do_on("get_config_json_config_path_fail", value = "error_config_json_path"); - if (!value.empty()) { - std::ifstream tracer_config(value); - Status s = tracer_config.good() ? Status::OK() - : Status(SERVER_INVALID_ARGUMENT, "Failed to open tracer config file " + value + - ": " + std::strerror(errno)); - tracer_config.close(); - return s; - } - return Status::OK(); -} - -/* wal config */ -Status -Config::GetWalConfigEnable(bool& wal_enable) { - std::string str = GetConfigStr(CONFIG_WAL, CONFIG_WAL_ENABLE, CONFIG_WAL_ENABLE_DEFAULT); - STATUS_CHECK(CheckWalConfigEnable(str)); - STATUS_CHECK(StringHelpFunctions::ConvertToBoolean(str, wal_enable)); - return Status::OK(); -} - -Status -Config::GetWalConfigRecoveryErrorIgnore(bool& recovery_error_ignore) { - std::string str = - GetConfigStr(CONFIG_WAL, CONFIG_WAL_RECOVERY_ERROR_IGNORE, CONFIG_WAL_RECOVERY_ERROR_IGNORE_DEFAULT); - STATUS_CHECK(CheckWalConfigRecoveryErrorIgnore(str)); - STATUS_CHECK(StringHelpFunctions::ConvertToBoolean(str, recovery_error_ignore)); - return Status::OK(); -} - -Status -Config::GetWalConfigBufferSize(int64_t& buffer_size) { - std::string str = GetConfigStr(CONFIG_WAL, CONFIG_WAL_BUFFER_SIZE, CONFIG_WAL_BUFFER_SIZE_DEFAULT); - STATUS_CHECK(CheckWalConfigBufferSize(str)); - std::string err; - buffer_size = parse_bytes(str, err); - // buffer_size = std::stoll(str); - if (buffer_size > CONFIG_WAL_BUFFER_SIZE_MAX) { - buffer_size = CONFIG_WAL_BUFFER_SIZE_MAX; - } else if (buffer_size < CONFIG_WAL_BUFFER_SIZE_MIN) { - buffer_size = CONFIG_WAL_BUFFER_SIZE_MIN; - } - return Status::OK(); -} - -Status -Config::GetWalConfigWalPath(std::string& wal_path) { - wal_path = GetConfigStr(CONFIG_WAL, CONFIG_WAL_WAL_PATH, CONFIG_WAL_WAL_PATH_DEFAULT); - STATUS_CHECK(CheckWalConfigWalPath(wal_path)); - return Status::OK(); -} - -/* logs config */ -Status -Config::GetLogsLevel(std::string& value) { - value = GetConfigStr(CONFIG_LOGS, CONFIG_LOGS_LEVEL, CONFIG_LOGS_LEVEL_DEFAULT); - STATUS_CHECK(CheckLogsLevel(value)); - return Status::OK(); -} - -Status -Config::GetLogsTraceEnable(bool& value) { - std::string str = GetConfigStr(CONFIG_LOGS, CONFIG_LOGS_TRACE_ENABLE, CONFIG_LOGS_TRACE_ENABLE_DEFAULT); - STATUS_CHECK(CheckLogsTraceEnable(str)); - STATUS_CHECK(StringHelpFunctions::ConvertToBoolean(str, value)); - return Status::OK(); -} - -Status -Config::GetLogsPath(std::string& value) { - value = GetConfigStr(CONFIG_LOGS, CONFIG_LOGS_PATH, CONFIG_LOGS_PATH_DEFAULT); - STATUS_CHECK(CheckLogsPath(value)); - return Status::OK(); -} - -Status -Config::GetLogsMaxLogFileSize(int64_t& value) { - std::string str = GetConfigStr(CONFIG_LOGS, CONFIG_LOGS_MAX_LOG_FILE_SIZE, CONFIG_LOGS_MAX_LOG_FILE_SIZE_DEFAULT); - STATUS_CHECK(CheckLogsMaxLogFileSize(str)); - std::string err; - value = parse_bytes(str, err); - // value = std::stoll(str); - return Status::OK(); -} - -Status -Config::GetLogsLogRotateNum(int64_t& value) { - std::string str = GetConfigStr(CONFIG_LOGS, CONFIG_LOGS_LOG_ROTATE_NUM, CONFIG_LOGS_LOG_ROTATE_NUM_DEFAULT); - STATUS_CHECK(CheckLogsLogRotateNum(str)); - value = std::stoll(str); - return Status::OK(); -} - -Status -Config::GetServerRestartRequired(bool& required) { - required = restart_required_; - return Status::OK(); -} - -/////////////////////////////////////////////////////////////////////////////// -/* cluster config */ -Status -Config::SetClusterConfigEnable(const std::string& value) { - STATUS_CHECK(CheckClusterConfigEnable(value)); - return SetConfigValueInMem(CONFIG_CLUSTER, CONFIG_CLUSTER_ENABLE, value); -} - -Status -Config::SetClusterConfigRole(const std::string& value) { - STATUS_CHECK(CheckClusterConfigRole(value)); - return SetConfigValueInMem(CONFIG_CLUSTER, CONFIG_CLUSTER_ROLE, value); -} - -/* general config */ -Status -Config::SetGeneralConfigTimezone(const std::string& value) { - STATUS_CHECK(CheckGeneralConfigTimezone(value)); - return SetConfigValueInMem(CONFIG_GENERAL, CONFIG_GENERAL_TIMEZONE, value); -} - -Status -Config::SetGeneralConfigMetaURI(const std::string& value) { - STATUS_CHECK(CheckGeneralConfigMetaURI(value)); - return SetConfigValueInMem(CONFIG_GENERAL, CONFIG_GENERAL_METAURI, value); -} - -/* network config */ -Status -Config::SetNetworkConfigBindAddress(const std::string& value) { - STATUS_CHECK(CheckNetworkConfigBindAddress(value)); - return SetConfigValueInMem(CONFIG_NETWORK, CONFIG_NETWORK_BIND_ADDRESS, value); -} - -Status -Config::SetNetworkConfigBindPort(const std::string& value) { - STATUS_CHECK(CheckNetworkConfigBindPort(value)); - return SetConfigValueInMem(CONFIG_NETWORK, CONFIG_NETWORK_BIND_PORT, value); -} - -Status -Config::SetNetworkConfigHTTPEnable(const std::string& value) { - STATUS_CHECK(CheckNetworkConfigHTTPEnable(value)); - return SetConfigValueInMem(CONFIG_NETWORK, CONFIG_NETWORK_HTTP_ENABLE, value); -} - -Status -Config::SetNetworkConfigHTTPPort(const std::string& value) { - STATUS_CHECK(CheckNetworkConfigHTTPPort(value)); - return SetConfigValueInMem(CONFIG_NETWORK, CONFIG_NETWORK_HTTP_PORT, value); -} - -/* server config */ -// Status -// Config::SetServerConfigAddress(const std::string& value) { -// STATUS_CHECK(CheckServerConfigAddress(value)); -// return SetConfigValueInMem(CONFIG_SERVER, CONFIG_SERVER_ADDRESS, value); -// } - -// Status -// Config::SetServerConfigPort(const std::string& value) { -// STATUS_CHECK(CheckServerConfigPort(value)); -// return SetConfigValueInMem(CONFIG_SERVER, CONFIG_SERVER_PORT, value); -// } - -// Status -// Config::SetServerConfigDeployMode(const std::string& value) { -// STATUS_CHECK(CheckServerConfigDeployMode(value)); -// return SetConfigValueInMem(CONFIG_SERVER, CONFIG_SERVER_DEPLOY_MODE, value); -// } - -// Status -// Config::SetServerConfigTimeZone(const std::string& value) { -// STATUS_CHECK(CheckServerConfigTimeZone(value)); -// return SetConfigValueInMem(CONFIG_SERVER, CONFIG_SERVER_TIME_ZONE, value); -// } - -// Status -// Config::SetServerConfigWebEnable(const std::string& value) { -// STATUS_CHECK(CheckServerConfigWebEnable(value)); -// return SetConfigValueInMem(CONFIG_SERVER, CONFIG_SERVER_WEB_ENABLE, value); -// } - -// Status -// Config::SetServerConfigWebPort(const std::string& value) { -// STATUS_CHECK(CheckServerConfigWebPort(value)); -// return SetConfigValueInMem(CONFIG_SERVER, CONFIG_SERVER_WEB_PORT, value); -// } - -/* db config */ -// Status -// Config::SetDBConfigBackendUrl(const std::string& value) { -// STATUS_CHECK(CheckDBConfigBackendUrl(value)); -// return SetConfigValueInMem(CONFIG_DB, CONFIG_DB_BACKEND_URL, value); -// } - -Status -Config::SetDBConfigArchiveDiskThreshold(const std::string& value) { - STATUS_CHECK(CheckDBConfigArchiveDiskThreshold(value)); - return SetConfigValueInMem(CONFIG_DB, CONFIG_DB_ARCHIVE_DISK_THRESHOLD, value); -} - -Status -Config::SetDBConfigArchiveDaysThreshold(const std::string& value) { - STATUS_CHECK(CheckDBConfigArchiveDaysThreshold(value)); - return SetConfigValueInMem(CONFIG_DB, CONFIG_DB_ARCHIVE_DAYS_THRESHOLD, value); -} - -/* storage config */ -Status -Config::SetStorageConfigPath(const std::string& value) { - STATUS_CHECK(CheckStorageConfigPath(value)); - return SetConfigValueInMem(CONFIG_STORAGE, CONFIG_STORAGE_PATH, value); -} - -Status -Config::SetStorageConfigAutoFlushInterval(const std::string& value) { - STATUS_CHECK(CheckStorageConfigAutoFlushInterval(value)); - return SetConfigValueInMem(CONFIG_STORAGE, CONFIG_STORAGE_AUTO_FLUSH_INTERVAL, value); -} - -Status -Config::SetStorageConfigFileCleanupTimeout(const std::string& value) { - STATUS_CHECK(CheckStorageConfigFileCleanupTimeout(value)); - return SetConfigValueInMem(CONFIG_STORAGE, CONFIG_STORAGE_FILE_CLEANUP_TIMEOUT, value); -} - -// Status -// Config::SetStorageConfigS3Enable(const std::string& value) { -// STATUS_CHECK(CheckStorageConfigS3Enable(value)); -// return SetConfigValueInMem(CONFIG_STORAGE, CONFIG_STORAGE_S3_ENABLE, value); -// } -// -// Status -// Config::SetStorageConfigS3Address(const std::string& value) { -// STATUS_CHECK(CheckStorageConfigS3Address(value)); -// return SetConfigValueInMem(CONFIG_STORAGE, CONFIG_STORAGE_S3_ADDRESS, value); -// } -// -// Status -// Config::SetStorageConfigS3Port(const std::string& value) { -// STATUS_CHECK(CheckStorageConfigS3Port(value)); -// return SetConfigValueInMem(CONFIG_STORAGE, CONFIG_STORAGE_S3_PORT, value); -// } -// -// Status -// Config::SetStorageConfigS3AccessKey(const std::string& value) { -// STATUS_CHECK(CheckStorageConfigS3AccessKey(value)); -// return SetConfigValueInMem(CONFIG_STORAGE, CONFIG_STORAGE_S3_ACCESS_KEY, value); -// } -// -// Status -// Config::SetStorageConfigS3SecretKey(const std::string& value) { -// STATUS_CHECK(CheckStorageConfigS3SecretKey(value)); -// return SetConfigValueInMem(CONFIG_STORAGE, CONFIG_STORAGE_S3_SECRET_KEY, value); -// } -// -// Status -// Config::SetStorageConfigS3Bucket(const std::string& value) { -// STATUS_CHECK(CheckStorageConfigS3Bucket(value)); -// return SetConfigValueInMem(CONFIG_STORAGE, CONFIG_STORAGE_S3_BUCKET, value); -// } - -/* metric config */ -Status -Config::SetMetricConfigEnableMonitor(const std::string& value) { - STATUS_CHECK(CheckMetricConfigEnableMonitor(value)); - return SetConfigValueInMem(CONFIG_METRIC, CONFIG_METRIC_ENABLE_MONITOR, value); -} - -Status -Config::SetMetricConfigAddress(const std::string& value) { - STATUS_CHECK(CheckMetricConfigAddress(value)); - return SetConfigValueInMem(CONFIG_METRIC, CONFIG_METRIC_ADDRESS, value); -} - -Status -Config::SetMetricConfigPort(const std::string& value) { - STATUS_CHECK(CheckMetricConfigPort(value)); - return SetConfigValueInMem(CONFIG_METRIC, CONFIG_METRIC_PORT, value); -} - -/* cache config */ -Status -Config::SetCacheConfigCpuCacheCapacity(const std::string& value) { - STATUS_CHECK(CheckCacheConfigCpuCacheCapacity(value)); - STATUS_CHECK(SetConfigValueInMem(CONFIG_CACHE, CONFIG_CACHE_CPU_CACHE_CAPACITY, value)); - return ExecCallBacks(CONFIG_CACHE, CONFIG_CACHE_CPU_CACHE_CAPACITY, value); -} - -Status -Config::SetCacheConfigCpuCacheThreshold(const std::string& value) { - STATUS_CHECK(CheckCacheConfigCpuCacheThreshold(value)); - return SetConfigValueInMem(CONFIG_CACHE, CONFIG_CACHE_CPU_CACHE_THRESHOLD, value); -} - -Status -Config::SetCacheConfigInsertBufferSize(const std::string& value) { - STATUS_CHECK(CheckCacheConfigInsertBufferSize(value)); - STATUS_CHECK(SetConfigValueInMem(CONFIG_CACHE, CONFIG_CACHE_INSERT_BUFFER_SIZE, value)); - return ExecCallBacks(CONFIG_CACHE, CONFIG_CACHE_INSERT_BUFFER_SIZE, value); -} - -Status -Config::SetCacheConfigCacheInsertData(const std::string& value) { - STATUS_CHECK(CheckCacheConfigCacheInsertData(value)); - STATUS_CHECK(SetConfigValueInMem(CONFIG_CACHE, CONFIG_CACHE_CACHE_INSERT_DATA, value)); - return ExecCallBacks(CONFIG_CACHE, CONFIG_CACHE_CACHE_INSERT_DATA, value); -} - -Status -Config::SetCacheConfigPreloadCollection(const std::string& value) { - STATUS_CHECK(CheckCacheConfigPreloadCollection(value)); - std::string cor_value = value == "*" ? "\'*\'" : value; - return SetConfigValueInMem(CONFIG_CACHE, CONFIG_CACHE_PRELOAD_COLLECTION, cor_value); -} - -/* engine config */ -Status -Config::SetEngineConfigUseBlasThreshold(const std::string& value) { - STATUS_CHECK(CheckEngineConfigUseBlasThreshold(value)); - STATUS_CHECK(SetConfigValueInMem(CONFIG_ENGINE, CONFIG_ENGINE_USE_BLAS_THRESHOLD, value)); - return ExecCallBacks(CONFIG_ENGINE, CONFIG_ENGINE_USE_BLAS_THRESHOLD, value); -} - -Status -Config::SetEngineConfigOmpThreadNum(const std::string& value) { - STATUS_CHECK(CheckEngineConfigOmpThreadNum(value)); - return SetConfigValueInMem(CONFIG_ENGINE, CONFIG_ENGINE_OMP_THREAD_NUM, value); -} - -Status -Config::SetEngineConfigSimdType(const std::string& value) { - STATUS_CHECK(CheckEngineConfigSimdType(value)); - return SetConfigValueInMem(CONFIG_ENGINE, CONFIG_ENGINE_SIMD_TYPE, value); -} - -Status -Config::SetEngineSearchCombineMaxNq(const std::string& value) { - STATUS_CHECK(CheckEngineSearchCombineMaxNq(value)); - STATUS_CHECK(SetConfigValueInMem(CONFIG_ENGINE, CONFIG_ENGINE_SEARCH_COMBINE_MAX_NQ, value)); - return ExecCallBacks(CONFIG_ENGINE, CONFIG_ENGINE_SEARCH_COMBINE_MAX_NQ, value); -} - -/* gpu resource config */ -#ifdef MILVUS_GPU_VERSION -Status -Config::SetGpuResourceConfigEnable(const std::string& value) { - STATUS_CHECK(CheckGpuResourceConfigEnable(value)); - STATUS_CHECK(SetConfigValueInMem(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_ENABLE, value)); - return ExecCallBacks(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_ENABLE, value); -} - -Status -Config::SetGpuResourceConfigCacheEnable(const std::string& value) { - STATUS_CHECK(CheckGpuResourceConfigCacheEnable(value)); - return SetConfigValueInMem(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_CACHE_ENABLE, value); -} - -Status -Config::SetGpuResourceConfigCacheCapacity(const std::string& value) { - STATUS_CHECK(CheckGpuResourceConfigCacheCapacity(value)); - STATUS_CHECK(SetConfigValueInMem(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_CACHE_CAPACITY, value)); - return ExecCallBacks(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_CACHE_CAPACITY, value); -} - -Status -Config::SetGpuResourceConfigCacheThreshold(const std::string& value) { - STATUS_CHECK(CheckGpuResourceConfigCacheThreshold(value)); - return SetConfigValueInMem(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_CACHE_THRESHOLD, value); -} - -Status -Config::SetGpuResourceConfigGpuSearchThreshold(const std::string& value) { - STATUS_CHECK(CheckGpuResourceConfigGpuSearchThreshold(value)); - STATUS_CHECK(SetConfigValueInMem(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_GPU_SEARCH_THRESHOLD, value)); - return ExecCallBacks(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_GPU_SEARCH_THRESHOLD, value); -} - -Status -Config::SetGpuResourceConfigSearchResources(const std::string& value) { - std::vector res_vec; - server::StringHelpFunctions::SplitStringByDelimeter(value, CONFIG_GPU_RESOURCE_DELIMITER, res_vec); - STATUS_CHECK(CheckGpuResourceConfigSearchResources(res_vec)); - STATUS_CHECK(SetConfigValueInMem(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_SEARCH_RESOURCES, value)); - return ExecCallBacks(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_SEARCH_RESOURCES, value); -} - -Status -Config::SetGpuResourceConfigBuildIndexResources(const std::string& value) { - std::vector res_vec; - server::StringHelpFunctions::SplitStringByDelimeter(value, CONFIG_GPU_RESOURCE_DELIMITER, res_vec); - STATUS_CHECK(CheckGpuResourceConfigBuildIndexResources(res_vec)); - STATUS_CHECK(SetConfigValueInMem(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_BUILD_INDEX_RESOURCES, value)); - return ExecCallBacks(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_BUILD_INDEX_RESOURCES, value); -} - -#endif - -/* tracing config */ -Status -Config::SetTracingConfigJsonConfigPath(const std::string& value) { - STATUS_CHECK(CheckTracingConfigJsonConfigPath(value)); - return SetConfigValueInMem(CONFIG_TRACING, CONFIG_TRACING_JSON_CONFIG_PATH, value); -} - -/* wal config */ -Status -Config::SetWalConfigEnable(const std::string& value) { - STATUS_CHECK(CheckWalConfigEnable(value)); - return SetConfigValueInMem(CONFIG_WAL, CONFIG_WAL_ENABLE, value); -} - -Status -Config::SetWalConfigRecoveryErrorIgnore(const std::string& value) { - STATUS_CHECK(CheckWalConfigRecoveryErrorIgnore(value)); - return SetConfigValueInMem(CONFIG_WAL, CONFIG_WAL_RECOVERY_ERROR_IGNORE, value); -} - -Status -Config::SetWalConfigBufferSize(const std::string& value) { - STATUS_CHECK(CheckWalConfigBufferSize(value)); - return SetConfigValueInMem(CONFIG_WAL, CONFIG_WAL_BUFFER_SIZE, value); -} - -Status -Config::SetWalConfigWalPath(const std::string& value) { - STATUS_CHECK(CheckWalConfigWalPath(value)); - return SetConfigValueInMem(CONFIG_WAL, CONFIG_WAL_WAL_PATH, value); -} - -/* logs config */ -Status -Config::SetLogsLevel(const std::string& value) { - STATUS_CHECK(CheckLogsLevel(value)); - return SetConfigValueInMem(CONFIG_LOGS, CONFIG_LOGS_LEVEL, value); -} - -Status -Config::SetLogsTraceEnable(const std::string& value) { - STATUS_CHECK(CheckLogsTraceEnable(value)); - return SetConfigValueInMem(CONFIG_LOGS, CONFIG_LOGS_TRACE_ENABLE, value); -} - -Status -Config::SetLogsPath(const std::string& value) { - STATUS_CHECK(CheckLogsPath(value)); - return SetConfigValueInMem(CONFIG_LOGS, CONFIG_LOGS_PATH, value); -} - -Status -Config::SetLogsMaxLogFileSize(const std::string& value) { - STATUS_CHECK(CheckLogsMaxLogFileSize(value)); - return SetConfigValueInMem(CONFIG_LOGS, CONFIG_LOGS_MAX_LOG_FILE_SIZE, value); -} - -Status -Config::SetLogsLogRotateNum(const std::string& value) { - STATUS_CHECK(CheckLogsLogRotateNum(value)); - return SetConfigValueInMem(CONFIG_LOGS, CONFIG_LOGS_LOG_ROTATE_NUM, value); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/config/Config.h b/core/src/config/Config.h deleted file mode 100644 index 77a0b12392..0000000000 --- a/core/src/config/Config.h +++ /dev/null @@ -1,570 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include - -#include "config/ConfigNode.h" -#include "utils/Status.h" - -namespace milvus { -namespace server { - -using ConfigCallBackF = std::function; - -extern const char* CONFIG_NODE_DELIMITER; -extern const char* CONFIG_VERSION; - -/* cluster config */ -extern const char* CONFIG_CLUSTER; -extern const char* CONFIG_CLUSTER_ENABLE; -extern const char* CONFIG_CLUSTER_ENABLE_DEFAULT; -extern const char* CONFIG_CLUSTER_ROLE; -extern const char* CONFIG_CLUSTER_ROLE_DEFAULT; - -/* general config */ -extern const char* CONFIG_GENERAL; -extern const char* CONFIG_GENERAL_TIMEZONE; -extern const char* CONFIG_GENERAL_TIMEZONE_DEFAULT; -extern const char* CONFIG_GENERAL_METAURI; -extern const char* CONFIG_GENERAL_METAURI_DEFAULT; - -/* network config */ -extern const char* CONFIG_NETWORK; -extern const char* CONFIG_NETWORK_BIND_ADDRESS; -extern const char* CONFIG_NETWORK_BIND_ADDRESS_DEFAULT; -extern const char* CONFIG_NETWORK_BIND_PORT; -extern const char* CONFIG_NETWORK_BIND_PORT_DEFAULT; -extern const char* CONFIG_NETWORK_HTTP_ENABLE; -extern const char* CONFIG_NETWORK_HTTP_ENABLE_DEFAULT; -extern const char* CONFIG_NETWORK_HTTP_PORT; -extern const char* CONFIG_NETWORK_HTTP_PORT_DEFAULT; - -/* db config */ -extern const char* CONFIG_DB; -// extern const char* CONFIG_DB_BACKEND_URL; -// extern const char* CONFIG_DB_BACKEND_URL_DEFAULT; -extern const char* CONFIG_DB_ARCHIVE_DISK_THRESHOLD; -extern const char* CONFIG_DB_ARCHIVE_DISK_THRESHOLD_DEFAULT; -extern const char* CONFIG_DB_ARCHIVE_DAYS_THRESHOLD; -extern const char* CONFIG_DB_ARCHIVE_DAYS_THRESHOLD_DEFAULT; - -/* storage config */ -extern const char* CONFIG_STORAGE; -extern const char* CONFIG_STORAGE_PATH; -extern const char* CONFIG_STORAGE_PATH_DEFAULT; -extern const char* CONFIG_STORAGE_AUTO_FLUSH_INTERVAL; -extern const char* CONFIG_STORAGE_AUTO_FLUSH_INTERVAL_DEFAULT; -extern const char* CONFIG_STORAGE_FILE_CLEANUP_TIMEOUT; -extern const int64_t CONFIG_STORAGE_FILE_CLEANUP_TIMEOUT_MIN; -extern const int64_t CONFIG_STORAGE_FILE_CLEANUP_TIMEOUT_MAX; - -/* cache config */ -extern const char* CONFIG_CACHE; -extern const char* CONFIG_CACHE_CPU_CACHE_CAPACITY; -extern const char* CONFIG_CACHE_CPU_CACHE_CAPACITY_DEFAULT; -extern const char* CONFIG_CACHE_CPU_CACHE_THRESHOLD; -extern const char* CONFIG_CACHE_CPU_CACHE_THRESHOLD_DEFAULT; -extern const char* CONFIG_CACHE_INSERT_BUFFER_SIZE; -extern const char* CONFIG_CACHE_INSERT_BUFFER_SIZE_DEFAULT; -extern const char* CONFIG_CACHE_CACHE_INSERT_DATA; -extern const char* CONFIG_CACHE_CACHE_INSERT_DATA_DEFAULT; -extern const char* CONFIG_CACHE_PRELOAD_COLLECTION; -extern const char* CONFIG_CACHE_PRELOAD_COLLECTION_DEFAULT; - -/* metric config */ -extern const char* CONFIG_METRIC; -extern const char* CONFIG_METRIC_ENABLE_MONITOR; -extern const char* CONFIG_METRIC_ENABLE_MONITOR_DEFAULT; -extern const char* CONFIG_METRIC_ADDRESS; -extern const char* CONFIG_METRIC_ADDRESS_DEFAULT; -extern const char* CONFIG_METRIC_PORT; -extern const char* CONFIG_METRIC_PORT_DEFAULT; - -/* engine config */ -extern const char* CONFIG_ENGINE; -extern const char* CONFIG_ENGINE_USE_BLAS_THRESHOLD; -extern const char* CONFIG_ENGINE_USE_BLAS_THRESHOLD_DEFAULT; -extern const char* CONFIG_ENGINE_OMP_THREAD_NUM; -extern const char* CONFIG_ENGINE_OMP_THREAD_NUM_DEFAULT; -extern const char* CONFIG_ENGINE_SIMD_TYPE; -extern const char* CONFIG_ENGINE_SIMD_TYPE_DEFAULT; -extern const char* CONFIG_ENGINE_SEARCH_COMBINE_MAX_NQ; -extern const char* CONFIG_ENGINE_SEARCH_COMBINE_MAX_NQ_DEFAULT; - -/* gpu resource config */ -extern const char* CONFIG_GPU_RESOURCE; -extern const char* CONFIG_GPU_RESOURCE_ENABLE; -extern const char* CONFIG_GPU_RESOURCE_ENABLE_DEFAULT; -extern const char* CONFIG_GPU_RESOURCE_CACHE_CAPACITY; -extern const char* CONFIG_GPU_RESOURCE_CACHE_CAPACITY_DEFAULT; -extern const char* CONFIG_GPU_RESOURCE_CACHE_THRESHOLD; -extern const char* CONFIG_GPU_RESOURCE_CACHE_THRESHOLD_DEFAULT; -extern const char* CONFIG_GPU_RESOURCE_GPU_SEARCH_THRESHOLD; -extern const char* CONFIG_GPU_RESOURCE_GPU_SEARCH_THRESHOLD_DEFAULT; -extern const char* CONFIG_GPU_RESOURCE_DELIMITER; -extern const char* CONFIG_GPU_RESOURCE_SEARCH_RESOURCES; -extern const char* CONFIG_GPU_RESOURCE_SEARCH_RESOURCES_DEFAULT; -extern const char* CONFIG_GPU_RESOURCE_BUILD_INDEX_RESOURCES; -extern const char* CONFIG_GPU_RESOURCE_BUILD_INDEX_RESOURCES_DEFAULT; - -/* tracing config */ -extern const char* CONFIG_TRACING; -extern const char* CONFIG_TRACING_JSON_CONFIG_PATH; - -/* wal config */ -extern const char* CONFIG_WAL; -extern const char* CONFIG_WAL_ENABLE; -extern const char* CONFIG_WAL_ENABLE_DEFAULT; -extern const char* CONFIG_WAL_RECOVERY_ERROR_IGNORE; -extern const char* CONFIG_WAL_RECOVERY_ERROR_IGNORE_DEFAULT; -extern const char* CONFIG_WAL_BUFFER_SIZE; -extern const char* CONFIG_WAL_BUFFER_SIZE_DEFAULT; -extern const int64_t CONFIG_WAL_BUFFER_SIZE_MIN; -extern const int64_t CONFIG_WAL_BUFFER_SIZE_MAX; -extern const char* CONFIG_WAL_WAL_PATH; -extern const char* CONFIG_WAL_WAL_PATH_DEFAULT; - -/* logs config */ -extern const char* CONFIG_LOGS; -extern const char* CONFIG_LOGS_LEVEL; -extern const char* CONFIG_LOGS_LEVEL_DEFAULT; -extern const char* CONFIG_LOGS_TRACE_ENABLE; -extern const char* CONFIG_LOGS_TRACE_ENABLE_DEFAULT; -extern const char* CONFIG_LOGS_PATH; -extern const char* CONFIG_LOGS_MAX_LOG_FILE_SIZE; -extern const char* CONFIG_LOGS_MAX_LOG_FILE_SIZE_DEFAULT; -extern const int64_t CONFIG_LOGS_MAX_LOG_FILE_SIZE_MIN; -extern const int64_t CONFIG_LOGS_MAX_LOG_FILE_SIZE_MAX; -extern const char* CONFIG_LOGS_LOG_ROTATE_NUM; -extern const char* CONFIG_LOGS_LOG_ROTATE_NUM_DEFAULT; -extern const int64_t CONFIG_LOGS_LOG_ROTATE_NUM_MIN; -extern const int64_t CONFIG_LOGS_LOG_ROTATE_NUM_MAX; - -class Config { - private: - Config(); - - public: - static Config& - GetInstance(); - Status - LoadConfigFile(const std::string& filename); - Status - ValidateConfig(); - Status - ResetDefaultConfig(); - void - GetConfigJsonStr(std::string& result, int64_t indent = -1); - Status - ProcessConfigCli(std::string& result, const std::string& cmd); - - Status - GenUniqueIdentityID(const std::string& identity, std::string& uid); - - Status - RegisterCallBack(const std::string& node, const std::string& sub_node, const std::string& key, - ConfigCallBackF& callback); - - Status - CancelCallBack(const std::string& node, const std::string& sub_node, const std::string& key); - - private: - ConfigNode& - GetConfigRoot(); - ConfigNode& - GetConfigNode(const std::string& name); - bool - ConfigNodeValid(const std::string& parent_key, const std::string& child_key); - Status - GetConfigValueInMem(const std::string& parent_key, const std::string& child_key, std::string& value); - Status - SetConfigValueInMem(const std::string& parent_key, const std::string& child_key, const std::string& value); - Status - GetConfigCli(std::string& value, const std::string& parent_key, const std::string& child_key); - Status - SetConfigCli(const std::string& parent_key, const std::string& child_key, const std::string& value); - - Status - UpdateFileConfigFromMem(const std::string& parent_key, const std::string& child_key); - - /////////////////////////////////////////////////////////////////////////// - Status - CheckConfigVersion(const std::string& value); - - /* cluster config */ - Status - CheckClusterConfigEnable(const std::string& value); - Status - CheckClusterConfigRole(const std::string& value); - - /* general config */ - Status - CheckGeneralConfigTimezone(const std::string& value); - Status - CheckGeneralConfigMetaURI(const std::string& value); - - /* network config */ - Status - CheckNetworkConfigBindAddress(const std::string& value); - Status - CheckNetworkConfigBindPort(const std::string& value); - Status - CheckNetworkConfigHTTPEnable(const std::string& value); - Status - CheckNetworkConfigHTTPPort(const std::string& value); - - /* db config */ - // Status - // CheckDBConfigBackendUrl(const std::string& value); - Status - CheckDBConfigArchiveDiskThreshold(const std::string& value); - Status - CheckDBConfigArchiveDaysThreshold(const std::string& value); - - /* storage config */ - Status - CheckStorageConfigPath(const std::string& value); - Status - CheckStorageConfigAutoFlushInterval(const std::string& value); - Status - CheckStorageConfigFileCleanupTimeout(const std::string& value); - - /* metric config */ - Status - CheckMetricConfigEnableMonitor(const std::string& value); - Status - CheckMetricConfigAddress(const std::string& value); - Status - CheckMetricConfigPort(const std::string& value); - - /* cache config */ - Status - CheckCacheConfigCpuCacheCapacity(const std::string& value); - Status - CheckCacheConfigCpuCacheThreshold(const std::string& value); - Status - CheckCacheConfigInsertBufferSize(const std::string& value); - Status - CheckCacheConfigCacheInsertData(const std::string& value); - Status - CheckCacheConfigPreloadCollection(const std::string& value); - - /* engine config */ - Status - CheckEngineConfigUseBlasThreshold(const std::string& value); - Status - CheckEngineConfigOmpThreadNum(const std::string& value); - Status - CheckEngineConfigSimdType(const std::string& value); - Status - CheckEngineSearchCombineMaxNq(const std::string& value); - -#ifdef MILVUS_GPU_VERSION - /* gpu resource config */ - Status - CheckGpuResourceConfigEnable(const std::string& value); - Status - CheckGpuResourceConfigCacheEnable(const std::string& value); - Status - CheckGpuResourceConfigCacheCapacity(const std::string& value); - Status - CheckGpuResourceConfigCacheThreshold(const std::string& value); - Status - CheckGpuResourceConfigGpuSearchThreshold(const std::string& value); - Status - CheckGpuResourceConfigSearchResources(const std::vector& value); - Status - CheckGpuResourceConfigBuildIndexResources(const std::vector& value); -#endif - - /* tracing config */ - Status - CheckTracingConfigJsonConfigPath(const std::string& value); - - /* wal config */ - Status - CheckWalConfigEnable(const std::string& value); - Status - CheckWalConfigRecoveryErrorIgnore(const std::string& value); - Status - CheckWalConfigBufferSize(const std::string& value); - Status - CheckWalConfigWalPath(const std::string& value); - - /* logs config */ - Status - CheckLogsLevel(const std::string& level); - Status - CheckLogsTraceEnable(const std::string& value); - Status - CheckLogsPath(const std::string& value); - Status - CheckLogsMaxLogFileSize(const std::string& value); - Status - CheckLogsLogRotateNum(const std::string& value); - - std::string - GetConfigStr(const std::string& parent_key, const std::string& child_key, const std::string& default_value = ""); - std::string - GetConfigSequenceStr(const std::string& parent_key, const std::string& child_key, const std::string& delim = ",", - const std::string& default_value = ""); - Status - GetConfigVersion(std::string& value); - - Status - ExecCallBacks(const std::string& node, const std::string& sub_node, const std::string& value); - - public: - /* cluster config */ - Status - GetClusterConfigEnable(bool& value); - Status - GetClusterConfigRole(std::string& value); - - /* general config */ - Status - GetGeneralConfigTimezone(std::string& value); - Status - GetGeneralConfigMetaURI(std::string& value); - - /* network config */ - Status - GetNetworkConfigBindAddress(std::string& value); - Status - GetNetworkConfigBindPort(std::string& value); - Status - GetNetworkConfigHTTPEnable(bool& value); - Status - GetNetworkConfigHTTPPort(std::string& value); - - /* db config */ - // Status - // GetDBConfigBackendUrl(std::string& value); - Status - GetDBConfigArchiveDiskThreshold(int64_t& value); - Status - GetDBConfigArchiveDaysThreshold(int64_t& value); - - /* storage config */ - Status - GetStorageConfigPath(std::string& value); - Status - GetStorageConfigAutoFlushInterval(int64_t& value); - Status - GetStorageConfigFileCleanupTimeup(int64_t& value); - - /* metric config */ - Status - GetMetricConfigEnableMonitor(bool& value); - Status - GetMetricConfigAddress(std::string& value); - Status - GetMetricConfigPort(std::string& value); - - /* cache config */ - Status - GetCacheConfigCpuCacheCapacity(int64_t& value); - Status - GetCacheConfigCpuCacheThreshold(float& value); - Status - GetCacheConfigInsertBufferSize(int64_t& value); - Status - GetCacheConfigCacheInsertData(bool& value); - Status - GetCacheConfigPreloadCollection(std::string& value); - - /* engine config */ - Status - GetEngineConfigUseBlasThreshold(int64_t& value); - Status - GetEngineConfigOmpThreadNum(int64_t& value); - Status - GetEngineConfigSimdType(std::string& value); - Status - GetEngineSearchCombineMaxNq(int64_t& value); - -#ifdef MILVUS_GPU_VERSION - /* gpu resource config */ - Status - GetGpuResourceConfigEnable(bool& value); - Status - GetGpuResourceConfigCacheEnable(bool& value); - Status - GetGpuResourceConfigCacheCapacity(int64_t& value); - Status - GetGpuResourceConfigCacheThreshold(float& value); - Status - GetGpuResourceConfigGpuSearchThreshold(int64_t& value); - Status - GetGpuResourceConfigSearchResources(std::vector& value); - Status - GetGpuResourceConfigBuildIndexResources(std::vector& value); -#endif - - /* tracing config */ - Status - GetTracingConfigJsonConfigPath(std::string& value); - - /* wal config */ - Status - GetWalConfigEnable(bool& value); - Status - GetWalConfigRecoveryErrorIgnore(bool& value); - Status - GetWalConfigBufferSize(int64_t& value); - Status - GetWalConfigWalPath(std::string& value); - - /* logs config */ - Status - GetLogsLevel(std::string& value); - Status - GetLogsTraceEnable(bool& value); - Status - GetLogsPath(std::string& value); - Status - GetLogsMaxLogFileSize(int64_t& value); - Status - GetLogsLogRotateNum(int64_t& value); - - Status - GetServerRestartRequired(bool& required); - - public: - /* cluster config */ - Status - SetClusterConfigEnable(const std::string& value); - Status - SetClusterConfigRole(const std::string& value); - - /* general config */ - Status - SetGeneralConfigTimezone(const std::string& value); - Status - SetGeneralConfigMetaURI(const std::string& value); - - /* network config */ - Status - SetNetworkConfigBindAddress(const std::string& value); - Status - SetNetworkConfigBindPort(const std::string& value); - Status - SetNetworkConfigHTTPEnable(const std::string& value); - Status - SetNetworkConfigHTTPPort(const std::string& value); - - /* db config */ - // Status - // SetDBConfigBackendUrl(const std::string& value); - Status - SetDBConfigArchiveDiskThreshold(const std::string& value); - Status - SetDBConfigArchiveDaysThreshold(const std::string& value); - - /* storage config */ - Status - SetStorageConfigPath(const std::string& value); - Status - SetStorageConfigAutoFlushInterval(const std::string& value); - Status - SetStorageConfigFileCleanupTimeout(const std::string& value); - - /* metric config */ - Status - SetMetricConfigEnableMonitor(const std::string& value); - Status - SetMetricConfigAddress(const std::string& value); - Status - SetMetricConfigPort(const std::string& value); - - /* cache config */ - Status - SetCacheConfigCpuCacheCapacity(const std::string& value); - Status - SetCacheConfigCpuCacheThreshold(const std::string& value); - Status - SetCacheConfigInsertBufferSize(const std::string& value); - Status - SetCacheConfigCacheInsertData(const std::string& value); - Status - SetCacheConfigPreloadCollection(const std::string& value); - - /* engine config */ - Status - SetEngineConfigUseBlasThreshold(const std::string& value); - Status - SetEngineConfigOmpThreadNum(const std::string& value); - Status - SetEngineConfigSimdType(const std::string& value); - Status - SetEngineSearchCombineMaxNq(const std::string& value); -#ifdef MILVUS_GPU_VERSION - - /* gpu resource config */ - Status - SetGpuResourceConfigEnable(const std::string& value); - Status - SetGpuResourceConfigCacheEnable(const std::string& value); - Status - SetGpuResourceConfigCacheCapacity(const std::string& value); - Status - SetGpuResourceConfigCacheThreshold(const std::string& value); - Status - SetGpuResourceConfigGpuSearchThreshold(const std::string& value); - Status - SetGpuResourceConfigSearchResources(const std::string& value); - Status - SetGpuResourceConfigBuildIndexResources(const std::string& value); -#endif - - /* tracing config */ - Status - SetTracingConfigJsonConfigPath(const std::string& value); - - /* wal config */ - Status - SetWalConfigEnable(const std::string& value); - Status - SetWalConfigRecoveryErrorIgnore(const std::string& value); - Status - SetWalConfigBufferSize(const std::string& value); - Status - SetWalConfigWalPath(const std::string& value); - - /* logs config */ - Status - SetLogsLevel(const std::string& value); - Status - SetLogsTraceEnable(const std::string& value); - Status - SetLogsPath(const std::string& value); - Status - SetLogsMaxLogFileSize(const std::string& value); - Status - SetLogsLogRotateNum(const std::string& value); - - private: - bool restart_required_ = false; - std::string config_file_; - std::unordered_map> config_map_; - std::unordered_map> config_callback_; - std::mutex callback_mutex_; - std::mutex mutex_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/config/ConfigMgr.h b/core/src/config/ConfigMgr.h deleted file mode 100644 index 07c212b4f5..0000000000 --- a/core/src/config/ConfigMgr.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -#include "ConfigNode.h" -#include "utils/Status.h" - -namespace milvus { -namespace server { - -class ConfigMgr { - public: - virtual Status - LoadConfigFile(const std::string& filename) = 0; - - virtual void - Print() const = 0; // will be deleted - - virtual std::string - DumpString() const = 0; - - virtual const ConfigNode& - GetRootNode() const = 0; - - virtual ConfigNode& - GetRootNode() = 0; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/config/ConfigNode.cpp b/core/src/config/ConfigNode.cpp deleted file mode 100644 index deeccb954c..0000000000 --- a/core/src/config/ConfigNode.cpp +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "config/ConfigNode.h" -#include "utils/Error.h" -#include "utils/Log.h" - -#include -#include -#include -#include - -namespace milvus { -namespace server { - -void -ConfigNode::Combine(const ConfigNode& target) { - const std::map& kv = target.GetConfig(); - for (auto itr = kv.begin(); itr != kv.end(); ++itr) { - config_[itr->first] = itr->second; - } - - const std::map >& sequences = target.GetSequences(); - for (auto itr = sequences.begin(); itr != sequences.end(); ++itr) { - sequences_[itr->first] = itr->second; - } - - const std::map& children = target.GetChildren(); - for (auto itr = children.begin(); itr != children.end(); ++itr) { - children_[itr->first] = itr->second; - } -} - -// key/value pair config -void -ConfigNode::SetValue(const std::string& key, const std::string& value) { - config_[key] = value; -} - -std::string -ConfigNode::GetValue(const std::string& param_key, const std::string& default_val) const { - auto ref = config_.find(param_key); - if (ref != config_.end()) { - return ref->second; - } - - // THROW_UNEXPECTED_ERROR("Can't find parameter key: " + param_key); - return default_val; -} - -bool -ConfigNode::GetBoolValue(const std::string& param_key, bool default_val) const { - std::string val = GetValue(param_key); - if (!val.empty()) { - std::transform(val.begin(), val.end(), val.begin(), ::tolower); - return (val == "true" || val == "on" || val == "yes" || val == "1"); - } else { - return default_val; - } -} - -int32_t -ConfigNode::GetInt32Value(const std::string& param_key, int32_t default_val) const { - std::string val = GetValue(param_key); - if (!val.empty()) { - return (int32_t)std::strtol(val.c_str(), nullptr, 10); - } else { - return default_val; - } -} - -int64_t -ConfigNode::GetInt64Value(const std::string& param_key, int64_t default_val) const { - std::string val = GetValue(param_key); - if (!val.empty()) { - return std::strtol(val.c_str(), nullptr, 10); - } else { - return default_val; - } -} - -float -ConfigNode::GetFloatValue(const std::string& param_key, float default_val) const { - std::string val = GetValue(param_key); - if (!val.empty()) { - return std::strtof(val.c_str(), nullptr); - } else { - return default_val; - } -} - -double -ConfigNode::GetDoubleValue(const std::string& param_key, double default_val) const { - std::string val = GetValue(param_key); - if (!val.empty()) { - return std::strtod(val.c_str(), nullptr); - } else { - return default_val; - } -} - -const std::map& -ConfigNode::GetConfig() const { - return config_; -} - -void -ConfigNode::ClearConfig() { - config_.clear(); -} - -// key/object config -void -ConfigNode::AddChild(const std::string& type_name, const ConfigNode& config) { - children_[type_name] = config; -} - -ConfigNode -ConfigNode::GetChild(const std::string& type_name) const { - auto ref = children_.find(type_name); - if (ref != children_.end()) { - return ref->second; - } - - ConfigNode nc; - return nc; -} - -ConfigNode& -ConfigNode::GetChild(const std::string& type_name) { - return children_[type_name]; -} - -void -ConfigNode::GetChildren(ConfigNodeArr& arr) const { - arr.clear(); - arr.reserve(children_.size()); - transform(children_.begin(), children_.end(), back_inserter(arr), [](auto& ref) { return ref.second; }); -} - -const std::map& -ConfigNode::GetChildren() const { - return children_; -} - -void -ConfigNode::ClearChildren() { - children_.clear(); -} - -// key/sequence config -void -ConfigNode::AddSequenceItem(const std::string& key, const std::string& item) { - sequences_[key].push_back(item); -} - -std::vector -ConfigNode::GetSequence(const std::string& key) const { - auto itr = sequences_.find(key); - if (itr != sequences_.end()) { - return itr->second; - } else { - std::vector temp; - return temp; - } -} - -const std::map >& -ConfigNode::GetSequences() const { - return sequences_; -} - -void -ConfigNode::ClearSequences() { - sequences_.clear(); -} - -void -ConfigNode::PrintAll(const std::string& prefix) const { - for (auto& elem : config_) { - LOG_SERVER_INFO_ << prefix << elem.first + ": " << elem.second; - } - - for (auto& elem : sequences_) { - LOG_SERVER_INFO_ << prefix << elem.first << ": "; - for (auto& str : elem.second) { - LOG_SERVER_INFO_ << prefix << " - " << str; - } - } - - for (auto& elem : children_) { - LOG_SERVER_INFO_ << prefix << elem.first << ": "; - elem.second.PrintAll(prefix + " "); - } -} - -std::string -ConfigNode::DumpString(const std::string& prefix) const { - std::stringstream str_buffer; - const std::string endl = "\n"; - for (auto& elem : config_) { - str_buffer << prefix << elem.first << ": " << elem.second << endl; - } - - for (auto& elem : sequences_) { - str_buffer << prefix << elem.first << ": " << endl; - for (auto& str : elem.second) { - str_buffer << prefix + " - " << str << endl; - } - } - - for (auto& elem : children_) { - str_buffer << prefix << elem.first << ": " << endl; - str_buffer << elem.second.DumpString(prefix + " ") << endl; - } - - return str_buffer.str(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/config/ConfigNode.h b/core/src/config/ConfigNode.h deleted file mode 100644 index 573a19e646..0000000000 --- a/core/src/config/ConfigNode.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include - -namespace milvus { -namespace server { - -class ConfigNode; -typedef std::vector ConfigNodeArr; - -class ConfigNode { - public: - void - Combine(const ConfigNode& target); - - // key/value pair config - void - SetValue(const std::string& key, const std::string& value); - - std::string - GetValue(const std::string& param_key, const std::string& default_val = "") const; - bool - GetBoolValue(const std::string& param_key, bool default_val = false) const; - int32_t - GetInt32Value(const std::string& param_key, int32_t default_val = 0) const; - int64_t - GetInt64Value(const std::string& param_key, int64_t default_val = 0) const; - float - GetFloatValue(const std::string& param_key, float default_val = 0.0) const; - double - GetDoubleValue(const std::string& param_key, double default_val = 0.0) const; - - const std::map& - GetConfig() const; - void - ClearConfig(); - - // key/object config - void - AddChild(const std::string& type_name, const ConfigNode& config); - ConfigNode - GetChild(const std::string& type_name) const; - ConfigNode& - GetChild(const std::string& type_name); - void - GetChildren(ConfigNodeArr& arr) const; - - const std::map& - GetChildren() const; - void - ClearChildren(); - - // key/sequence config - void - AddSequenceItem(const std::string& key, const std::string& item); - std::vector - GetSequence(const std::string& key) const; - - const std::map >& - GetSequences() const; - void - ClearSequences(); - - void - PrintAll(const std::string& prefix = "") const; - std::string - DumpString(const std::string& prefix = "") const; - - private: - std::map config_; - std::map children_; - std::map > sequences_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/config/Utils.cpp b/core/src/config/Utils.cpp deleted file mode 100644 index 64e56fe8b7..0000000000 --- a/core/src/config/Utils.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "config/Utils.h" - -#include -#include -#include - -namespace milvus { -namespace server { - -std::unordered_map BYTE_UNITS = { - {"b", 1}, - {"k", 1024}, - {"m", 1024 * 1024}, - {"g", 1024 * 1024 * 1024}, -}; - -bool -is_number(const std::string& s) { - return !s.empty() && std::find_if(s.begin(), s.end(), [](unsigned char c) { return !std::isdigit(c); }) == s.end(); -} - -bool -is_alpha(const std::string& s) { - return !s.empty() && std::find_if(s.begin(), s.end(), [](unsigned char c) { return !std::isalpha(c); }) == s.end(); -} - -std::string -str_tolower(std::string s) { - std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); }); - return s; -} - -int64_t -parse_bytes(const std::string& str, std::string& err) { - try { - std::string s = str; - if (is_number(s)) - return std::stoll(s); - if (s.length() == 0) - return 0; - - auto last_two = s.substr(s.length() - 2, 2); - auto last_one = s.substr(s.length() - 1); - if (is_alpha(last_two) && is_alpha(last_one)) - if (last_one == "b" or last_one == "B") - s = s.substr(0, s.length() - 1); - auto& units = BYTE_UNITS; - auto suffix = str_tolower(s.substr(s.length() - 1)); - - std::string digits_part; - if (is_number(suffix)) { - digits_part = s; - suffix = 'b'; - } else { - digits_part = s.substr(0, s.length() - 1); - } - - if (units.find(suffix) != units.end() or is_number(suffix)) { - auto digits = std::stoll(digits_part); - return digits * units[suffix]; - } else { - std::stringstream ss; - ss << "The specified value for memory (" << str << ") should specify the units." - << "The postfix should be one of the `b` `k` `m` `g` characters"; - err = ss.str(); - } - } catch (...) { - err = "Unknown error happened on parse bytes."; - } - return 0; -} - -} // namespace server -} // namespace milvus diff --git a/core/src/config/Utils.h b/core/src/config/Utils.h deleted file mode 100644 index 7a39417b64..0000000000 --- a/core/src/config/Utils.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -namespace milvus { -namespace server { - -int64_t -parse_bytes(const std::string& str, std::string& err); - -} // namespace server -} // namespace milvus diff --git a/core/src/config/YamlConfigMgr.cpp b/core/src/config/YamlConfigMgr.cpp deleted file mode 100644 index 4c4eee639b..0000000000 --- a/core/src/config/YamlConfigMgr.cpp +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "config/YamlConfigMgr.h" -#include "utils/Log.h" - -namespace milvus { -namespace server { - -Status -YamlConfigMgr::LoadConfigFile(const std::string& filename) { - try { - node_ = YAML::LoadFile(filename); - LoadConfigNode(node_, config_); - } catch (YAML::Exception& e) { - std::string str = "Exception: load config file fail: " + std::string(e.what()); - return Status(SERVER_UNEXPECTED_ERROR, str); - } - - return Status::OK(); -} - -void -YamlConfigMgr::Print() const { - LOG_SERVER_INFO_ << "System config content:"; - config_.PrintAll(); -} - -std::string -YamlConfigMgr::DumpString() const { - return config_.DumpString(""); -} - -const ConfigNode& -YamlConfigMgr::GetRootNode() const { - return config_; -} - -ConfigNode& -YamlConfigMgr::GetRootNode() { - return config_; -} - -bool -YamlConfigMgr::SetConfigValue(const YAML::Node& node, const std::string& key, ConfigNode& config) { - if (node[key].IsDefined()) { - config.SetValue(key, node[key].as()); - return true; - } - return false; -} - -bool -YamlConfigMgr::SetChildConfig(const YAML::Node& node, const std::string& child_name, ConfigNode& config) { - if (node[child_name].IsDefined()) { - ConfigNode sub_config; - LoadConfigNode(node[child_name], sub_config); - config.AddChild(child_name, sub_config); - return true; - } - return false; -} - -bool -YamlConfigMgr::SetSequence(const YAML::Node& node, const std::string& child_name, ConfigNode& config) { - if (node[child_name].IsDefined()) { - size_t cnt = node[child_name].size(); - for (size_t i = 0; i < cnt; ++i) { - config.AddSequenceItem(child_name, node[child_name][i].as()); - } - return true; - } - return false; -} - -void -YamlConfigMgr::LoadConfigNode(const YAML::Node& node, ConfigNode& config) { - std::string key; - for (YAML::const_iterator it = node.begin(); it != node.end(); ++it) { - if (!it->first.IsNull()) { - key = it->first.as(); - } - if (node[key].IsScalar()) { - SetConfigValue(node, key, config); - } else if (node[key].IsMap()) { - SetChildConfig(node, key, config); - } else if (node[key].IsSequence()) { - SetSequence(node, key, config); - } - } -} - -} // namespace server -} // namespace milvus diff --git a/core/src/config/YamlConfigMgr.h b/core/src/config/YamlConfigMgr.h deleted file mode 100644 index a8a9bc43da..0000000000 --- a/core/src/config/YamlConfigMgr.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -#include "ConfigMgr.h" -#include "utils/Status.h" - -namespace milvus { -namespace server { - -class YamlConfigMgr : public ConfigMgr { - public: - static ConfigMgr* - GetInstance() { - static YamlConfigMgr mgr; - return &mgr; - } - - virtual Status - LoadConfigFile(const std::string& filename); - - virtual void - Print() const; - - virtual std::string - DumpString() const; - - virtual const ConfigNode& - GetRootNode() const; - - virtual ConfigNode& - GetRootNode(); - - private: - bool - SetConfigValue(const YAML::Node& node, const std::string& key, ConfigNode& config); - - bool - SetChildConfig(const YAML::Node& node, const std::string& child_name, ConfigNode& config); - - bool - SetSequence(const YAML::Node& node, const std::string& child_name, ConfigNode& config); - - void - LoadConfigNode(const YAML::Node& node, ConfigNode& config); - - private: - YAML::Node node_; - ConfigNode config_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/config/handler/CacheConfigHandler.cpp b/core/src/config/handler/CacheConfigHandler.cpp deleted file mode 100644 index 6136017ef1..0000000000 --- a/core/src/config/handler/CacheConfigHandler.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "config/handler/CacheConfigHandler.h" -#include "config/Config.h" - -namespace milvus { -namespace server { - -CacheConfigHandler::CacheConfigHandler() { - auto& config = Config::GetInstance(); - config.GetCacheConfigCpuCacheCapacity(cpu_cache_capacity_); - config.GetCacheConfigInsertBufferSize(insert_buffer_size_); - config.GetCacheConfigCacheInsertData(cache_insert_data_); -} - -CacheConfigHandler::~CacheConfigHandler() { - RemoveCpuCacheCapacityListener(); - RemoveInsertBufferSizeListener(); - RemoveCacheInsertDataListener(); -} - -//////////////////////////// Listener methods ////////////////////////////////// -void -CacheConfigHandler::AddCpuCacheCapacityListener() { - ConfigCallBackF lambda = [this](const std::string& value) -> Status { - auto& config = Config::GetInstance(); - auto status = config.GetCacheConfigCpuCacheCapacity(cpu_cache_capacity_); - if (status.ok()) { - OnCpuCacheCapacityChanged(cpu_cache_capacity_); - } - - return status; - }; - - auto& config = Config::GetInstance(); - config.RegisterCallBack(CONFIG_CACHE, CONFIG_CACHE_CPU_CACHE_CAPACITY, identity_, lambda); -} - -void -CacheConfigHandler::AddInsertBufferSizeListener() { - ConfigCallBackF lambda = [this](const std::string& value) -> Status { - auto& config = Config::GetInstance(); - auto status = config.GetCacheConfigInsertBufferSize(insert_buffer_size_); - if (status.ok()) { - OnInsertBufferSizeChanged(insert_buffer_size_); - } - return status; - }; - - auto& config = Config::GetInstance(); - config.RegisterCallBack(CONFIG_CACHE, CONFIG_CACHE_INSERT_BUFFER_SIZE, identity_, lambda); -} - -void -CacheConfigHandler::AddCacheInsertDataListener() { - server::ConfigCallBackF lambda = [this](const std::string& value) -> Status { - auto& config = Config::GetInstance(); - auto status = config.GetCacheConfigCacheInsertData(cache_insert_data_); - if (status.ok()) { - OnCacheInsertDataChanged(cache_insert_data_); - } - return status; - }; - - auto& config = Config::GetInstance(); - config.RegisterCallBack(CONFIG_CACHE, CONFIG_CACHE_CACHE_INSERT_DATA, identity_, lambda); -} - -void -CacheConfigHandler::RemoveCpuCacheCapacityListener() { - auto& config = Config::GetInstance(); - config.CancelCallBack(CONFIG_CACHE, CONFIG_CACHE_CPU_CACHE_CAPACITY, identity_); -} - -void -CacheConfigHandler::RemoveInsertBufferSizeListener() { - auto& config = Config::GetInstance(); - config.CancelCallBack(CONFIG_CACHE, CONFIG_CACHE_INSERT_BUFFER_SIZE, identity_); -} - -void -CacheConfigHandler::RemoveCacheInsertDataListener() { - auto& config = Config::GetInstance(); - config.CancelCallBack(server::CONFIG_CACHE, server::CONFIG_CACHE_CACHE_INSERT_DATA, identity_); -} -} // namespace server -} // namespace milvus diff --git a/core/src/config/handler/CacheConfigHandler.h b/core/src/config/handler/CacheConfigHandler.h deleted file mode 100644 index 0ddab20f79..0000000000 --- a/core/src/config/handler/CacheConfigHandler.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#pragma once - -#include - -#include "config/handler/ConfigHandler.h" - -namespace milvus { -namespace server { - -class CacheConfigHandler : virtual public ConfigHandler { - public: - CacheConfigHandler(); - virtual ~CacheConfigHandler(); - - protected: - virtual void - OnCpuCacheCapacityChanged(int64_t value) { - } - - virtual void - OnInsertBufferSizeChanged(int64_t value) { - } - - virtual void - OnCacheInsertDataChanged(bool value) { - } - - protected: - void - AddCpuCacheCapacityListener(); - - void - AddInsertBufferSizeListener(); - - void - AddCacheInsertDataListener(); - - void - RemoveCpuCacheCapacityListener(); - - void - RemoveInsertBufferSizeListener(); - - void - RemoveCacheInsertDataListener(); - - private: - int64_t cpu_cache_capacity_ = std::stoll(CONFIG_CACHE_CPU_CACHE_CAPACITY_DEFAULT) /*GiB*/; - int64_t insert_buffer_size_ = std::stoll(CONFIG_CACHE_INSERT_BUFFER_SIZE_DEFAULT) /*GiB*/; - bool cache_insert_data_ = false; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/config/handler/ConfigHandler.h b/core/src/config/handler/ConfigHandler.h deleted file mode 100644 index aa7862b9c5..0000000000 --- a/core/src/config/handler/ConfigHandler.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -#include "config/Config.h" -#include "utils/Log.h" - -namespace milvus { -namespace server { - -class ConfigHandler { - public: - ConfigHandler() = default; - virtual ~ConfigHandler() = default; - - protected: - void - SetIdentity(const std::string& identity) { - auto& config = server::Config::GetInstance(); - config.GenUniqueIdentityID(identity, identity_); - } - - protected: - std::string identity_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/config/handler/EngineConfigHandler.cpp b/core/src/config/handler/EngineConfigHandler.cpp deleted file mode 100644 index e838bc0773..0000000000 --- a/core/src/config/handler/EngineConfigHandler.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "config/handler/EngineConfigHandler.h" - -#include - -namespace milvus { -namespace server { - -EngineConfigHandler::EngineConfigHandler() { - auto& config = Config::GetInstance(); - config.GetEngineConfigUseBlasThreshold(use_blas_threshold_); - config.GetEngineSearchCombineMaxNq(search_combine_nq_); -} - -EngineConfigHandler::~EngineConfigHandler() { - RemoveUseBlasThresholdListener(); - RemoveSearchCombineMaxNqListener(); -} - -//////////////////////////// Listener methods ////////////////////////////////// -void -EngineConfigHandler::AddUseBlasThresholdListener() { - ConfigCallBackF lambda = [this](const std::string& value) -> Status { - auto& config = server::Config::GetInstance(); - auto status = config.GetEngineConfigUseBlasThreshold(use_blas_threshold_); - if (status.ok()) { - OnUseBlasThresholdChanged(use_blas_threshold_); - } - - return status; - }; - - auto& config = Config::GetInstance(); - config.RegisterCallBack(CONFIG_ENGINE, CONFIG_ENGINE_USE_BLAS_THRESHOLD, identity_, lambda); -} - -void -EngineConfigHandler::RemoveUseBlasThresholdListener() { - auto& config = Config::GetInstance(); - config.CancelCallBack(CONFIG_ENGINE, CONFIG_ENGINE_USE_BLAS_THRESHOLD, identity_); -} - -void -EngineConfigHandler::AddSearchCombineMaxNqListener() { - ConfigCallBackF lambda = [this](const std::string& value) -> Status { - auto& config = server::Config::GetInstance(); - auto status = config.GetEngineSearchCombineMaxNq(search_combine_nq_); - if (status.ok()) { - OnSearchCombineMaxNqChanged(search_combine_nq_); - } - - return status; - }; - - auto& config = Config::GetInstance(); - config.RegisterCallBack(CONFIG_ENGINE, CONFIG_ENGINE_SEARCH_COMBINE_MAX_NQ, identity_, lambda); -} - -void -EngineConfigHandler::RemoveSearchCombineMaxNqListener() { - auto& config = Config::GetInstance(); - config.CancelCallBack(CONFIG_ENGINE, CONFIG_ENGINE_SEARCH_COMBINE_MAX_NQ, identity_); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/config/handler/EngineConfigHandler.h b/core/src/config/handler/EngineConfigHandler.h deleted file mode 100644 index 3fed6c9847..0000000000 --- a/core/src/config/handler/EngineConfigHandler.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "config/Config.h" -#include "config/handler/ConfigHandler.h" - -namespace milvus { -namespace server { - -class EngineConfigHandler : virtual public ConfigHandler { - public: - EngineConfigHandler(); - - virtual ~EngineConfigHandler(); - - protected: - virtual void - OnUseBlasThresholdChanged(int64_t threshold) { - } - - virtual void - OnSearchCombineMaxNqChanged(int64_t nq) { - search_combine_nq_ = nq; - } - - protected: - void - AddUseBlasThresholdListener(); - - void - RemoveUseBlasThresholdListener(); - - void - AddSearchCombineMaxNqListener(); - - void - RemoveSearchCombineMaxNqListener(); - - protected: - int64_t use_blas_threshold_ = std::stoll(CONFIG_ENGINE_USE_BLAS_THRESHOLD_DEFAULT); - int64_t search_combine_nq_ = std::stoll(CONFIG_ENGINE_SEARCH_COMBINE_MAX_NQ_DEFAULT); -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/config/handler/GpuResourceConfigHandler.cpp b/core/src/config/handler/GpuResourceConfigHandler.cpp deleted file mode 100644 index 08d98f8d1a..0000000000 --- a/core/src/config/handler/GpuResourceConfigHandler.cpp +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#ifdef MILVUS_GPU_VERSION -#include "config/handler/GpuResourceConfigHandler.h" - -namespace milvus { -namespace server { - -GpuResourceConfigHandler::GpuResourceConfigHandler() { - auto& config = Config::GetInstance(); - config.GetGpuResourceConfigEnable(gpu_enable_); -} - -GpuResourceConfigHandler::~GpuResourceConfigHandler() { - RemoveGpuEnableListener(); - RemoveGpuCacheCapacityListener(); - RemoveGpuBuildResourcesListener(); - RemoveGpuSearchThresholdListener(); - RemoveGpuSearchResourcesListener(); -} - -//////////////////////////// Listener methods ////////////////////////////////// -void -GpuResourceConfigHandler::AddGpuEnableListener() { - auto& config = Config::GetInstance(); - - ConfigCallBackF lambda = [this](const std::string& value) -> Status { - auto& config = Config::GetInstance(); - auto status = config.GetGpuResourceConfigEnable(gpu_enable_); - if (status.ok()) { - OnGpuEnableChanged(gpu_enable_); - } - - return status; - }; - config.RegisterCallBack(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_ENABLE, identity_, lambda); -} - -void -GpuResourceConfigHandler::AddGpuCacheCapacityListener() { - ConfigCallBackF lambda = [this](const std::string& value) -> Status { - if (!gpu_enable_) { - std::string msg = - std::string("GPU resources is disable. Cannot set config ") + CONFIG_GPU_RESOURCE_CACHE_CAPACITY; - return Status(SERVER_UNEXPECTED_ERROR, msg); - } - - auto& config = Config::GetInstance(); - auto status = config.GetGpuResourceConfigCacheCapacity(gpu_cache_capacity_); - if (status.ok()) { - OnGpuCacheCapacityChanged(gpu_cache_capacity_); - } - - return status; - }; - - auto& config = Config::GetInstance(); - config.RegisterCallBack(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_CACHE_CAPACITY, identity_, lambda); -} - -void -GpuResourceConfigHandler::AddGpuBuildResourcesListener() { - auto& config = Config::GetInstance(); - ConfigCallBackF lambda = [this](const std::string& value) -> Status { - if (!gpu_enable_) { - std::string msg = - std::string("GPU resources is disable. Cannot set config ") + CONFIG_GPU_RESOURCE_BUILD_INDEX_RESOURCES; - return Status(SERVER_UNEXPECTED_ERROR, msg); - } - - auto& config = Config::GetInstance(); - auto status = config.GetGpuResourceConfigSearchResources(build_gpus_); - if (status.ok()) { - OnGpuBuildResChanged(build_gpus_); - } - - return status; - }; - config.RegisterCallBack(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_BUILD_INDEX_RESOURCES, identity_, lambda); -} - -void -GpuResourceConfigHandler::AddGpuSearchThresholdListener() { - auto& config = Config::GetInstance(); - - ConfigCallBackF lambda_gpu_threshold = [this](const std::string& value) -> Status { - if (!gpu_enable_) { - std::string msg = - std::string("GPU resources is disabled. Cannot set config ") + CONFIG_GPU_RESOURCE_GPU_SEARCH_THRESHOLD; - return Status(SERVER_UNEXPECTED_ERROR, msg); - } - - auto& config = Config::GetInstance(); - auto status = config.GetGpuResourceConfigGpuSearchThreshold(threshold_); - if (status.ok()) { - OnGpuSearchThresholdChanged(threshold_); - } - - return status; - }; - config.RegisterCallBack(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_GPU_SEARCH_THRESHOLD, identity_, - lambda_gpu_threshold); -} - -void -GpuResourceConfigHandler::AddGpuSearchResourcesListener() { - auto& config = Config::GetInstance(); - - ConfigCallBackF lambda = [this](const std::string& value) -> Status { - if (!gpu_enable_) { - std::string msg = - std::string("GPU resources is disable. Cannot set config ") + CONFIG_GPU_RESOURCE_SEARCH_RESOURCES; - return Status(SERVER_UNEXPECTED_ERROR, msg); - } - - auto& config = Config::GetInstance(); - auto status = config.GetGpuResourceConfigSearchResources(search_gpus_); - if (status.ok()) { - OnGpuSearchResChanged(search_gpus_); - } - - return status; - }; - config.RegisterCallBack(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_SEARCH_RESOURCES, identity_, lambda); -} - -void -GpuResourceConfigHandler::RemoveGpuEnableListener() { - auto& config = Config::GetInstance(); - config.CancelCallBack(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_ENABLE, identity_); -} - -void -GpuResourceConfigHandler::RemoveGpuCacheCapacityListener() { - auto& config = Config::GetInstance(); - config.CancelCallBack(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_CACHE_CAPACITY, identity_); -} - -void -GpuResourceConfigHandler::RemoveGpuBuildResourcesListener() { - auto& config = Config::GetInstance(); - config.CancelCallBack(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_BUILD_INDEX_RESOURCES, identity_); -} - -void -GpuResourceConfigHandler::RemoveGpuSearchThresholdListener() { - auto& config = Config::GetInstance(); - config.CancelCallBack(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_GPU_SEARCH_THRESHOLD, identity_); -} - -void -GpuResourceConfigHandler::RemoveGpuSearchResourcesListener() { - auto& config = Config::GetInstance(); - config.CancelCallBack(CONFIG_GPU_RESOURCE, CONFIG_GPU_RESOURCE_SEARCH_RESOURCES, identity_); -} - -} // namespace server -} // namespace milvus -#endif diff --git a/core/src/config/handler/GpuResourceConfigHandler.h b/core/src/config/handler/GpuResourceConfigHandler.h deleted file mode 100644 index 8d282bc46d..0000000000 --- a/core/src/config/handler/GpuResourceConfigHandler.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#ifdef MILVUS_GPU_VERSION -#pragma once - -#include -#include -#include -#include - -#include "config/Config.h" -#include "config/handler/ConfigHandler.h" - -namespace milvus { -namespace server { - -class GpuResourceConfigHandler : virtual public ConfigHandler { - public: - GpuResourceConfigHandler(); - - virtual ~GpuResourceConfigHandler(); - - protected: - virtual void - OnGpuEnableChanged(bool enable) { - } - - virtual void - OnGpuCacheCapacityChanged(int64_t capacity) { - } - - virtual void - OnGpuBuildResChanged(const std::vector& gpus) { - } - - virtual void - OnGpuSearchThresholdChanged(int64_t threshold) { - } - - virtual void - OnGpuSearchResChanged(const std::vector& gpus) { - } - - protected: - void - AddGpuEnableListener(); - - void - AddGpuCacheCapacityListener(); - - void - AddGpuBuildResourcesListener(); - - void - AddGpuSearchThresholdListener(); - - void - AddGpuSearchResourcesListener(); - - protected: - void - RemoveGpuEnableListener(); - - void - RemoveGpuCacheCapacityListener(); - - void - RemoveGpuBuildResourcesListener(); - - void - RemoveGpuSearchThresholdListener(); - - void - RemoveGpuSearchResourcesListener(); - - protected: - bool gpu_enable_ = true; - int64_t gpu_cache_capacity_ = std::stoll(CONFIG_GPU_RESOURCE_CACHE_CAPACITY_DEFAULT) /* GiB */; - int64_t threshold_ = std::stoll(CONFIG_ENGINE_USE_BLAS_THRESHOLD_DEFAULT); - std::vector build_gpus_; - std::vector search_gpus_; -}; - -} // namespace server -} // namespace milvus -#endif diff --git a/core/src/context/HybridSearchContext.h b/core/src/context/HybridSearchContext.h deleted file mode 100644 index 976af1adc4..0000000000 --- a/core/src/context/HybridSearchContext.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include - -#include "query/BinaryQuery.h" -#include "search/Task.h" - -namespace milvus { -namespace search { -class Task; - -using TaskPtr = std::shared_ptr; -} // namespace search - -namespace context { - -struct HybridSearchContext { - query::GeneralQueryPtr general_query_; - std::vector<::milvus::search::TaskPtr> tasks_; -}; - -using HybridSearchContextPtr = std::shared_ptr; - -} // namespace context -} // namespace milvus diff --git a/core/src/db/Constants.h b/core/src/db/Constants.h deleted file mode 100644 index c8afeed1cd..0000000000 --- a/core/src/db/Constants.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -namespace milvus { -namespace engine { - -constexpr int64_t KB = 1LL << 10; -constexpr int64_t MB = 1LL << 20; -constexpr int64_t GB = 1LL << 30; -constexpr int64_t TB = 1LL << 40; - -constexpr int64_t MAX_TABLE_FILE_MEM = 128 * MB; - -constexpr int FLOAT_TYPE_SIZE = sizeof(float); - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/DB.h b/core/src/db/DB.h deleted file mode 100644 index d5bca1f7b2..0000000000 --- a/core/src/db/DB.h +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include - -#include "Options.h" -#include "Types.h" -#include "context/HybridSearchContext.h" -#include "meta/Meta.h" -#include "query/GeneralQuery.h" -#include "server/context/Context.h" -#include "utils/Status.h" - -namespace milvus { -namespace engine { - -class Env; - -class DB { - public: - DB() = default; - - DB(const DB&) = delete; - - DB& - operator=(const DB&) = delete; - - virtual ~DB() = default; - - virtual Status - Start() = 0; - - virtual Status - Stop() = 0; - - virtual Status - CreateCollection(meta::CollectionSchema& table_schema_) = 0; - - virtual Status - DropCollection(const std::string& collection_id) = 0; - - virtual Status - DescribeCollection(meta::CollectionSchema& table_schema_) = 0; - - virtual Status - HasCollection(const std::string& collection_id, bool& has_or_not) = 0; - - virtual Status - HasNativeCollection(const std::string& collection_id, bool& has_or_not) = 0; - - virtual Status - AllCollections(std::vector& table_schema_array) = 0; - - virtual Status - GetCollectionInfo(const std::string& collection_id, std::string& collection_info) = 0; - - virtual Status - GetCollectionRowCount(const std::string& collection_id, uint64_t& row_count) = 0; - - virtual Status - PreloadCollection(const std::shared_ptr& context, const std::string& collection_id, - bool force = false) = 0; - - virtual Status - ReLoadSegmentsDeletedDocs(const std::string& collection_id, const std::vector& segment_ids) = 0; - - virtual Status - UpdateCollectionFlag(const std::string& collection_id, int64_t flag) = 0; - - virtual Status - CreatePartition(const std::string& collection_id, const std::string& partition_name, - const std::string& partition_tag) = 0; - - virtual Status - HasPartition(const std::string& collection_id, const std::string& tag, bool& has_or_not) = 0; - - virtual Status - DropPartition(const std::string& partition_name) = 0; - - virtual Status - DropPartitionByTag(const std::string& collection_id, const std::string& partition_tag) = 0; - - virtual Status - ShowPartitions(const std::string& collection_id, std::vector& partition_schema_array) = 0; - - virtual Status - InsertVectors(const std::string& collection_id, const std::string& partition_tag, VectorsData& vectors) = 0; - - virtual Status - DeleteVector(const std::string& collection_id, IDNumber vector_id) = 0; - - virtual Status - DeleteVectors(const std::string& collection_id, IDNumbers vector_ids) = 0; - - virtual Status - Flush(const std::string& collection_id) = 0; - - virtual Status - Flush() = 0; - - virtual Status - Compact(const std::shared_ptr& context, const std::string& collection_id, - double threshold = 0.0) = 0; - - virtual Status - GetVectorsByID(const engine::meta::CollectionSchema& collection, const IDNumbers& id_array, - std::vector& vectors) = 0; - - virtual Status - GetVectorIDs(const std::string& collection_id, const std::string& segment_id, IDNumbers& vector_ids) = 0; - - // virtual Status - // Merge(const std::set& table_ids) = 0; - - virtual Status - QueryByIDs(const std::shared_ptr& context, const std::string& collection_id, - const std::vector& partition_tags, uint64_t k, const milvus::json& extra_params, - const IDNumbers& id_array, ResultIds& result_ids, ResultDistances& result_distances) = 0; - - virtual Status - Query(const std::shared_ptr& context, const std::string& collection_id, - const std::vector& partition_tags, uint64_t k, const milvus::json& extra_params, - const VectorsData& vectors, ResultIds& result_ids, ResultDistances& result_distances) = 0; - - virtual Status - QueryByFileID(const std::shared_ptr& context, const std::vector& file_ids, uint64_t k, - const milvus::json& extra_params, const VectorsData& vectors, ResultIds& result_ids, - ResultDistances& result_distances) = 0; - - virtual Status - Size(uint64_t& result) = 0; - - virtual Status - CreateIndex(const std::shared_ptr& context, const std::string& collection_id, - const CollectionIndex& index) = 0; - - virtual Status - DescribeIndex(const std::string& collection_id, CollectionIndex& index) = 0; - - virtual Status - DropIndex(const std::string& collection_id) = 0; - - virtual Status - DropAll() = 0; - - virtual Status - CreateHybridCollection(meta::CollectionSchema& collection_schema, meta::hybrid::FieldsSchema& fields_schema) = 0; - - virtual Status - DescribeHybridCollection(meta::CollectionSchema& collection_schema, meta::hybrid::FieldsSchema& fields_schema) = 0; - - virtual Status - InsertEntities(const std::string& collection_id, const std::string& partition_tag, - const std::vector& field_names, Entity& entity, - std::unordered_map& field_types) = 0; - - virtual Status - HybridQuery(const std::shared_ptr& context, const std::string& collection_id, - const std::vector& partition_tags, context::HybridSearchContextPtr hybrid_search_context, - query::GeneralQueryPtr general_query, - std::unordered_map& attr_type, uint64_t& nq, - engine::ResultIds& result_ids, engine::ResultDistances& result_distances) = 0; -}; // DB - -using DBPtr = std::shared_ptr; - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/DBFactory.cpp b/core/src/db/DBFactory.cpp deleted file mode 100644 index 7af8f87786..0000000000 --- a/core/src/db/DBFactory.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/DBFactory.h" -#include "DBImpl.h" -#include "meta/MetaFactory.h" -#include "meta/MySQLMetaImpl.h" -#include "meta/SqliteMetaImpl.h" -#include "utils/Exception.h" - -#include -#include -#include -#include -#include - -namespace milvus { -namespace engine { - -DBOptions -DBFactory::BuildOption() { - auto meta = MetaFactory::BuildOption(); - DBOptions options; - options.meta_ = meta; - return options; -} - -DBPtr -DBFactory::Build(const DBOptions& options) { - return std::make_shared(options); -} - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/DBFactory.h b/core/src/db/DBFactory.h deleted file mode 100644 index 88374d07f8..0000000000 --- a/core/src/db/DBFactory.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "DB.h" -#include "Options.h" - -#include -#include - -namespace milvus { -namespace engine { - -class DBFactory { - public: - static DBOptions - BuildOption(); - - static DBPtr - Build(const DBOptions& options); -}; - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/DBImpl.cpp b/core/src/db/DBImpl.cpp deleted file mode 100644 index 4495900266..0000000000 --- a/core/src/db/DBImpl.cpp +++ /dev/null @@ -1,2845 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/DBImpl.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Utils.h" -#include "cache/CpuCacheMgr.h" -#include "cache/GpuCacheMgr.h" -#include "db/IDGenerator.h" -#include "db/merge/MergeManagerFactory.h" -#include "engine/EngineFactory.h" -#include "index/knowhere/knowhere/index/vector_index/helpers/BuilderSuspend.h" -#include "index/knowhere/knowhere/index/vector_index/helpers/FaissIO.h" -#include "index/thirdparty/faiss/utils/distances.h" -#include "insert/MemManagerFactory.h" -#include "meta/MetaConsts.h" -#include "meta/MetaFactory.h" -#include "meta/SqliteMetaImpl.h" -#include "metrics/Metrics.h" -#include "scheduler/Definition.h" -#include "scheduler/SchedInst.h" -#include "scheduler/job/BuildIndexJob.h" -#include "scheduler/job/DeleteJob.h" -#include "scheduler/job/SearchJob.h" -#include "segment/SegmentReader.h" -#include "segment/SegmentWriter.h" -#include "utils/Exception.h" -#include "utils/Log.h" -#include "utils/StringHelpFunctions.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" -#include "wal/WalDefinations.h" - -#include "search/TaskInst.h" - -namespace milvus { -namespace engine { - -namespace { -constexpr uint64_t BACKGROUND_METRIC_INTERVAL = 1; -constexpr uint64_t BACKGROUND_INDEX_INTERVAL = 1; -constexpr uint64_t WAIT_BUILD_INDEX_INTERVAL = 5; - -constexpr const char* JSON_ROW_COUNT = "row_count"; -constexpr const char* JSON_PARTITIONS = "partitions"; -constexpr const char* JSON_PARTITION_TAG = "tag"; -constexpr const char* JSON_SEGMENTS = "segments"; -constexpr const char* JSON_SEGMENT_NAME = "name"; -constexpr const char* JSON_INDEX_NAME = "index_name"; -constexpr const char* JSON_DATA_SIZE = "data_size"; - -static const Status SHUTDOWN_ERROR = Status(DB_ERROR, "Milvus server is shutdown!"); - -} // namespace - -DBImpl::DBImpl(const DBOptions& options) - : options_(options), initialized_(false), merge_thread_pool_(1, 1), index_thread_pool_(1, 1) { - meta_ptr_ = MetaFactory::Build(options.meta_, options.mode_); - mem_mgr_ = MemManagerFactory::Build(meta_ptr_, options_); - merge_mgr_ptr_ = MergeManagerFactory::Build(meta_ptr_, options_); - - if (options_.wal_enable_) { - wal::MXLogConfiguration mxlog_config; - mxlog_config.recovery_error_ignore = options_.recovery_error_ignore_; - // 2 buffers in the WAL - mxlog_config.buffer_size = options_.buffer_size_ / 2; - mxlog_config.mxlog_path = options_.mxlog_path_; - wal_mgr_ = std::make_shared(mxlog_config); - } - - SetIdentity("DBImpl"); - AddCacheInsertDataListener(); - AddUseBlasThresholdListener(); - knowhere::enable_faiss_logging(); - - Start(); -} - -DBImpl::~DBImpl() { - Stop(); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// external api -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -Status -DBImpl::Start() { - if (initialized_.load(std::memory_order_acquire)) { - return Status::OK(); - } - - // LOG_ENGINE_TRACE_ << "DB service start"; - initialized_.store(true, std::memory_order_release); - - // server may be closed unexpected, these un-merge files need to be merged when server restart - // and soft-delete files need to be deleted when server restart - std::set merge_collection_ids; - std::vector collection_schema_array; - meta_ptr_->AllCollections(collection_schema_array); - for (auto& schema : collection_schema_array) { - merge_collection_ids.insert(schema.collection_id_); - } - StartMergeTask(merge_collection_ids, true); - - // wal - if (options_.wal_enable_) { - auto error_code = DB_ERROR; - if (wal_mgr_ != nullptr) { - error_code = wal_mgr_->Init(meta_ptr_); - } - if (error_code != WAL_SUCCESS) { - throw Exception(error_code, "Wal init error!"); - } - - // recovery - while (1) { - wal::MXLogRecord record; - auto error_code = wal_mgr_->GetNextRecovery(record); - if (error_code != WAL_SUCCESS) { - throw Exception(error_code, "Wal recovery error!"); - } - if (record.type == wal::MXLogType::None) { - break; - } - ExecWalRecord(record); - } - - // for distribute version, some nodes are read only - if (options_.mode_ != DBOptions::MODE::CLUSTER_READONLY) { - // background wal thread - bg_wal_thread_ = std::thread(&DBImpl::BackgroundWalThread, this); - } - } else { - // for distribute version, some nodes are read only - if (options_.mode_ != DBOptions::MODE::CLUSTER_READONLY) { - // background flush thread - bg_flush_thread_ = std::thread(&DBImpl::BackgroundFlushThread, this); - } - } - - // for distribute version, some nodes are read only - if (options_.mode_ != DBOptions::MODE::CLUSTER_READONLY) { - // background build index thread - bg_index_thread_ = std::thread(&DBImpl::BackgroundIndexThread, this); - } - - // background metric thread - fiu_do_on("options_metric_enable", options_.metric_enable_ = true); - if (options_.metric_enable_) { - bg_metric_thread_ = std::thread(&DBImpl::BackgroundMetricThread, this); - } - - return Status::OK(); -} - -Status -DBImpl::Stop() { - if (!initialized_.load(std::memory_order_acquire)) { - return Status::OK(); - } - - initialized_.store(false, std::memory_order_release); - - if (options_.mode_ != DBOptions::MODE::CLUSTER_READONLY) { - if (options_.wal_enable_) { - // wait wal thread finish - swn_wal_.Notify(); - bg_wal_thread_.join(); - } else { - // flush all without merge - wal::MXLogRecord record; - record.type = wal::MXLogType::Flush; - ExecWalRecord(record); - - // wait flush thread finish - swn_flush_.Notify(); - bg_flush_thread_.join(); - } - - WaitMergeFileFinish(); - - swn_index_.Notify(); - bg_index_thread_.join(); - - meta_ptr_->CleanUpShadowFiles(); - } - - // wait metric thread exit - if (options_.metric_enable_) { - swn_metric_.Notify(); - bg_metric_thread_.join(); - } - - // LOG_ENGINE_TRACE_ << "DB service stop"; - return Status::OK(); -} - -Status -DBImpl::DropAll() { - return meta_ptr_->DropAll(); -} - -Status -DBImpl::CreateCollection(meta::CollectionSchema& collection_schema) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - meta::CollectionSchema temp_schema = collection_schema; - temp_schema.index_file_size_ *= MB; // store as MB - if (options_.wal_enable_) { - temp_schema.flush_lsn_ = wal_mgr_->CreateCollection(collection_schema.collection_id_); - } - - return meta_ptr_->CreateCollection(temp_schema); -} - -Status -DBImpl::CreateHybridCollection(meta::CollectionSchema& collection_schema, meta::hybrid::FieldsSchema& fields_schema) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - meta::CollectionSchema temp_schema = collection_schema; - temp_schema.index_file_size_ *= MB; - if (options_.wal_enable_) { - temp_schema.flush_lsn_ = wal_mgr_->CreateHybridCollection(collection_schema.collection_id_); - } - - return meta_ptr_->CreateHybridCollection(temp_schema, fields_schema); -} - -Status -DBImpl::DescribeHybridCollection(meta::CollectionSchema& collection_schema, - milvus::engine::meta::hybrid::FieldsSchema& fields_schema) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - auto stat = meta_ptr_->DescribeHybridCollection(collection_schema, fields_schema); - return stat; -} - -Status -DBImpl::DropCollection(const std::string& collection_id) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - // dates partly delete files of the collection but currently we don't support - LOG_ENGINE_DEBUG_ << "Prepare to delete collection " << collection_id; - - Status status; - if (options_.wal_enable_) { - wal_mgr_->DropCollection(collection_id); - } - - status = mem_mgr_->EraseMemVector(collection_id); // not allow insert - status = meta_ptr_->DropCollections({collection_id}); // soft delete collection - index_failed_checker_.CleanFailedIndexFileOfCollection(collection_id); - - std::vector partition_array; - status = meta_ptr_->ShowPartitions(collection_id, partition_array); - std::vector partition_id_array; - for (auto& schema : partition_array) { - if (options_.wal_enable_) { - wal_mgr_->DropCollection(schema.collection_id_); - } - status = mem_mgr_->EraseMemVector(schema.collection_id_); - index_failed_checker_.CleanFailedIndexFileOfCollection(schema.collection_id_); - partition_id_array.push_back(schema.collection_id_); - } - - status = meta_ptr_->DropCollections(partition_id_array); - fiu_do_on("DBImpl.DropCollection.failed", status = Status(DB_ERROR, "")); - if (!status.ok()) { - return status; - } - - return Status::OK(); -} - -Status -DBImpl::DescribeCollection(meta::CollectionSchema& collection_schema) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - auto stat = meta_ptr_->DescribeCollection(collection_schema); - collection_schema.index_file_size_ /= MB; // return as MB - return stat; -} - -Status -DBImpl::HasCollection(const std::string& collection_id, bool& has_or_not) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - return meta_ptr_->HasCollection(collection_id, has_or_not, false); -} - -Status -DBImpl::HasNativeCollection(const std::string& collection_id, bool& has_or_not) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - return meta_ptr_->HasCollection(collection_id, has_or_not, true); -} - -Status -DBImpl::AllCollections(std::vector& collection_schema_array) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - std::vector all_collections; - auto status = meta_ptr_->AllCollections(all_collections); - - // only return real collections, dont return partition collections - collection_schema_array.clear(); - for (auto& schema : all_collections) { - if (schema.owner_collection_.empty()) { - collection_schema_array.push_back(schema); - } - } - - return status; -} - -Status -DBImpl::GetCollectionInfo(const std::string& collection_id, std::string& collection_info) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - // step1: get all partition ids - std::vector partition_array; - auto status = meta_ptr_->ShowPartitions(collection_id, partition_array); - - std::vector file_types{meta::SegmentSchema::FILE_TYPE::RAW, meta::SegmentSchema::FILE_TYPE::TO_INDEX, - meta::SegmentSchema::FILE_TYPE::INDEX}; - - milvus::json json_info; - milvus::json json_partitions; - size_t total_row_count = 0; - - auto get_info = [&](const std::string& col_id, const std::string& tag) { - meta::FilesHolder files_holder; - status = meta_ptr_->FilesByType(col_id, file_types, files_holder); - if (!status.ok()) { - std::string err_msg = "Failed to get collection info: " + status.ToString(); - LOG_ENGINE_ERROR_ << err_msg; - return Status(DB_ERROR, err_msg); - } - - milvus::json json_partition; - json_partition[JSON_PARTITION_TAG] = tag; - - milvus::json json_segments; - size_t row_count = 0; - milvus::engine::meta::SegmentsSchema& collection_files = files_holder.HoldFiles(); - for (auto& file : collection_files) { - milvus::json json_segment; - json_segment[JSON_SEGMENT_NAME] = file.segment_id_; - json_segment[JSON_ROW_COUNT] = file.row_count_; - json_segment[JSON_INDEX_NAME] = utils::GetIndexName(file.engine_type_); - json_segment[JSON_DATA_SIZE] = (int64_t)file.file_size_; - json_segments.push_back(json_segment); - - row_count += file.row_count_; - total_row_count += file.row_count_; - } - - json_partition[JSON_ROW_COUNT] = row_count; - json_partition[JSON_SEGMENTS] = json_segments; - - json_partitions.push_back(json_partition); - - return Status::OK(); - }; - - // step2: get default partition info - status = get_info(collection_id, milvus::engine::DEFAULT_PARTITON_TAG); - if (!status.ok()) { - return status; - } - - // step3: get partitions info - for (auto& schema : partition_array) { - status = get_info(schema.collection_id_, schema.partition_tag_); - if (!status.ok()) { - return status; - } - } - - json_info[JSON_ROW_COUNT] = total_row_count; - json_info[JSON_PARTITIONS] = json_partitions; - - collection_info = json_info.dump(); - - return Status::OK(); -} - -Status -DBImpl::PreloadCollection(const std::shared_ptr& context, const std::string& collection_id, - bool force) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - // step 1: get all collection files from parent collection - meta::FilesHolder files_holder; -#if 0 - auto status = meta_ptr_->FilesToSearch(collection_id, files_holder); - if (!status.ok()) { - return status; - } - - // step 2: get files from partition collections - std::vector partition_array; - status = meta_ptr_->ShowPartitions(collection_id, partition_array); - for (auto& schema : partition_array) { - status = meta_ptr_->FilesToSearch(schema.collection_id_, files_holder); - } -#else - auto status = meta_ptr_->FilesToSearch(collection_id, files_holder); - if (!status.ok()) { - return status; - } - - std::vector partition_array; - status = meta_ptr_->ShowPartitions(collection_id, partition_array); - - std::set partition_ids; - for (auto& schema : partition_array) { - partition_ids.insert(schema.collection_id_); - } - - status = meta_ptr_->FilesToSearchEx(collection_id, partition_ids, files_holder); - if (!status.ok()) { - return status; - } -#endif - - int64_t size = 0; - int64_t cache_total = cache::CpuCacheMgr::GetInstance()->CacheCapacity(); - int64_t cache_usage = cache::CpuCacheMgr::GetInstance()->CacheUsage(); - int64_t available_size = cache_total - cache_usage; - - // step 3: load file one by one - milvus::engine::meta::SegmentsSchema& files_array = files_holder.HoldFiles(); - LOG_ENGINE_DEBUG_ << "Begin pre-load collection:" + collection_id + ", totally " << files_array.size() - << " files need to be pre-loaded"; - TimeRecorderAuto rc("Pre-load collection:" + collection_id); - for (auto& file : files_array) { - // client break the connection, no need to continue - if (context && context->IsConnectionBroken()) { - LOG_ENGINE_DEBUG_ << "Client connection broken, stop load collection"; - break; - } - - EngineType engine_type; - if (file.file_type_ == meta::SegmentSchema::FILE_TYPE::RAW || - file.file_type_ == meta::SegmentSchema::FILE_TYPE::TO_INDEX || - file.file_type_ == meta::SegmentSchema::FILE_TYPE::BACKUP) { - engine_type = - utils::IsBinaryMetricType(file.metric_type_) ? EngineType::FAISS_BIN_IDMAP : EngineType::FAISS_IDMAP; - } else { - engine_type = (EngineType)file.engine_type_; - } - - auto json = milvus::json::parse(file.index_params_); - ExecutionEnginePtr engine = - EngineFactory::Build(file.dimension_, file.location_, engine_type, (MetricType)file.metric_type_, json); - fiu_do_on("DBImpl.PreloadCollection.null_engine", engine = nullptr); - if (engine == nullptr) { - LOG_ENGINE_ERROR_ << "Invalid engine type"; - return Status(DB_ERROR, "Invalid engine type"); - } - - fiu_do_on("DBImpl.PreloadCollection.exceed_cache", size = available_size + 1); - - try { - fiu_do_on("DBImpl.PreloadCollection.engine_throw_exception", throw std::exception()); - std::string msg = "Pre-loaded file: " + file.file_id_ + " size: " + std::to_string(file.file_size_); - TimeRecorderAuto rc_1(msg); - status = engine->Load(true); - if (!status.ok()) { - return status; - } - - size += engine->Size(); - if (!force && size > available_size) { - LOG_ENGINE_DEBUG_ << "Pre-load cancelled since cache is almost full"; - return Status(SERVER_CACHE_FULL, "Cache is full"); - } - } catch (std::exception& ex) { - std::string msg = "Pre-load collection encounter exception: " + std::string(ex.what()); - LOG_ENGINE_ERROR_ << msg; - return Status(DB_ERROR, msg); - } - } - - return Status::OK(); -} - -Status -DBImpl::ReLoadSegmentsDeletedDocs(const std::string& collection_id, const std::vector& segment_ids) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - meta::FilesHolder files_holder; - std::vector file_ids; - for (auto& id : segment_ids) { - file_ids.emplace_back(id); - } - - auto status = meta_ptr_->FilesByID(file_ids, files_holder); - if (!status.ok()) { - std::string err_msg = "Failed get file holders by ids: " + status.ToString(); - LOG_ENGINE_ERROR_ << err_msg; - return Status(DB_ERROR, err_msg); - } - - milvus::engine::meta::SegmentsSchema hold_files = files_holder.HoldFiles(); - - for (auto& file : hold_files) { - std::string segment_dir; - utils::GetParentPath(file.location_, segment_dir); - - auto data_obj_ptr = cache::CpuCacheMgr::GetInstance()->GetIndex(file.location_); - auto index = std::static_pointer_cast(data_obj_ptr); - if (nullptr == index) { - LOG_ENGINE_WARNING_ << "Index " << file.location_ << " not found"; - continue; - } - - segment::SegmentReader segment_reader(segment_dir); - - segment::DeletedDocsPtr delete_docs = std::make_shared(); - segment_reader.LoadDeletedDocs(delete_docs); - auto& docs_offsets = delete_docs->GetDeletedDocs(); - - faiss::ConcurrentBitsetPtr blacklist = index->GetBlacklist(); - if (nullptr == blacklist) { - LOG_ENGINE_WARNING_ << "Index " << file.location_ << " is empty"; - faiss::ConcurrentBitsetPtr concurrent_bitset_ptr = - std::make_shared(index->Count()); - index->SetBlacklist(concurrent_bitset_ptr); - blacklist = concurrent_bitset_ptr; - } - - for (auto& i : docs_offsets) { - if (!blacklist->test(i)) { - blacklist->set(i); - } - } - } - - return Status::OK(); -} - -Status -DBImpl::UpdateCollectionFlag(const std::string& collection_id, int64_t flag) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - return meta_ptr_->UpdateCollectionFlag(collection_id, flag); -} - -Status -DBImpl::GetCollectionRowCount(const std::string& collection_id, uint64_t& row_count) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - return GetCollectionRowCountRecursively(collection_id, row_count); -} - -Status -DBImpl::CreatePartition(const std::string& collection_id, const std::string& partition_name, - const std::string& partition_tag) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - uint64_t lsn = 0; - if (options_.wal_enable_) { - lsn = wal_mgr_->CreatePartition(collection_id, partition_tag); - } else { - meta_ptr_->GetCollectionFlushLSN(collection_id, lsn); - } - return meta_ptr_->CreatePartition(collection_id, partition_name, partition_tag, lsn); -} - -Status -DBImpl::HasPartition(const std::string& collection_id, const std::string& tag, bool& has_or_not) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - // trim side-blank of tag, only compare valid characters - // for example: " ab cd " is treated as "ab cd" - std::string valid_tag = tag; - server::StringHelpFunctions::TrimStringBlank(valid_tag); - - if (valid_tag == milvus::engine::DEFAULT_PARTITON_TAG) { - has_or_not = true; - return Status::OK(); - } - - return meta_ptr_->HasPartition(collection_id, valid_tag, has_or_not); -} - -Status -DBImpl::DropPartition(const std::string& partition_name) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - mem_mgr_->EraseMemVector(partition_name); // not allow insert - auto status = meta_ptr_->DropPartition(partition_name); // soft delete collection - if (!status.ok()) { - LOG_ENGINE_ERROR_ << status.message(); - return status; - } - - // scheduler will determine when to delete collection files - auto nres = scheduler::ResMgrInst::GetInstance()->GetNumOfComputeResource(); - scheduler::DeleteJobPtr job = std::make_shared(partition_name, meta_ptr_, nres); - scheduler::JobMgrInst::GetInstance()->Put(job); - job->WaitAndDelete(); - - return Status::OK(); -} - -Status -DBImpl::DropPartitionByTag(const std::string& collection_id, const std::string& partition_tag) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - std::string partition_name; - auto status = meta_ptr_->GetPartitionName(collection_id, partition_tag, partition_name); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << status.message(); - return status; - } - - if (options_.wal_enable_) { - wal_mgr_->DropPartition(collection_id, partition_tag); - } - - return DropPartition(partition_name); -} - -Status -DBImpl::ShowPartitions(const std::string& collection_id, std::vector& partition_schema_array) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - return meta_ptr_->ShowPartitions(collection_id, partition_schema_array); -} - -Status -DBImpl::InsertVectors(const std::string& collection_id, const std::string& partition_tag, VectorsData& vectors) { - // LOG_ENGINE_DEBUG_ << "Insert " << n << " vectors to cache"; - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - // insert vectors into target collection - // (zhiru): generate ids - if (vectors.id_array_.empty()) { - SafeIDGenerator& id_generator = SafeIDGenerator::GetInstance(); - Status status = id_generator.GetNextIDNumbers(vectors.vector_count_, vectors.id_array_); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << LogOut("[%s][%ld] Get next id number fail: %s", "insert", 0, status.message().c_str()); - return status; - } - } - - Status status; - if (options_.wal_enable_) { - std::string target_collection_name; - status = GetPartitionByTag(collection_id, partition_tag, target_collection_name); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << LogOut("[%s][%ld] Get partition fail: %s", "insert", 0, status.message().c_str()); - return status; - } - - if (!vectors.float_data_.empty()) { - wal_mgr_->Insert(collection_id, partition_tag, vectors.id_array_, vectors.float_data_); - } else if (!vectors.binary_data_.empty()) { - wal_mgr_->Insert(collection_id, partition_tag, vectors.id_array_, vectors.binary_data_); - } - swn_wal_.Notify(); - } else { - wal::MXLogRecord record; - record.lsn = 0; // need to get from meta ? - record.collection_id = collection_id; - record.partition_tag = partition_tag; - record.ids = vectors.id_array_.data(); - record.length = vectors.vector_count_; - if (vectors.binary_data_.empty()) { - record.type = wal::MXLogType::InsertVector; - record.data = vectors.float_data_.data(); - record.data_size = vectors.float_data_.size() * sizeof(float); - } else { - record.type = wal::MXLogType::InsertBinary; - record.ids = vectors.id_array_.data(); - record.length = vectors.vector_count_; - record.data = vectors.binary_data_.data(); - record.data_size = vectors.binary_data_.size() * sizeof(uint8_t); - } - - status = ExecWalRecord(record); - } - - return status; -} - -Status -CopyToAttr(std::vector& record, uint64_t row_num, const std::vector& field_names, - std::unordered_map& attr_types, - std::unordered_map>& attr_datas, - std::unordered_map& attr_nbytes, - std::unordered_map& attr_data_size) { - uint64_t offset = 0; - for (auto name : field_names) { - switch (attr_types.at(name)) { - case meta::hybrid::DataType::INT8: { - std::vector data; - data.resize(row_num * sizeof(int8_t)); - - std::vector attr_value(row_num, 0); - memcpy(attr_value.data(), record.data() + offset, row_num * sizeof(int64_t)); - - std::vector raw_value(row_num, 0); - for (uint64_t i = 0; i < row_num; ++i) { - raw_value[i] = attr_value[i]; - } - - memcpy(data.data(), raw_value.data(), row_num * sizeof(int8_t)); - attr_datas.insert(std::make_pair(name, data)); - - attr_nbytes.insert(std::make_pair(name, sizeof(int8_t))); - attr_data_size.insert(std::make_pair(name, row_num * sizeof(int8_t))); - offset += row_num * sizeof(int64_t); - break; - } - case meta::hybrid::DataType::INT16: { - std::vector data; - data.resize(row_num * sizeof(int16_t)); - - std::vector attr_value(row_num, 0); - memcpy(attr_value.data(), record.data() + offset, row_num * sizeof(int64_t)); - - std::vector raw_value(row_num, 0); - for (uint64_t i = 0; i < row_num; ++i) { - raw_value[i] = attr_value[i]; - } - - memcpy(data.data(), raw_value.data(), row_num * sizeof(int16_t)); - attr_datas.insert(std::make_pair(name, data)); - - attr_nbytes.insert(std::make_pair(name, sizeof(int16_t))); - attr_data_size.insert(std::make_pair(name, row_num * sizeof(int16_t))); - offset += row_num * sizeof(int64_t); - break; - } - case meta::hybrid::DataType::INT32: { - std::vector data; - data.resize(row_num * sizeof(int32_t)); - - std::vector attr_value(row_num, 0); - memcpy(attr_value.data(), record.data() + offset, row_num * sizeof(int64_t)); - - std::vector raw_value(row_num, 0); - for (uint64_t i = 0; i < row_num; ++i) { - raw_value[i] = attr_value[i]; - } - - memcpy(data.data(), raw_value.data(), row_num * sizeof(int32_t)); - attr_datas.insert(std::make_pair(name, data)); - - attr_nbytes.insert(std::make_pair(name, sizeof(int32_t))); - attr_data_size.insert(std::make_pair(name, row_num * sizeof(int32_t))); - offset += row_num * sizeof(int64_t); - break; - } - case meta::hybrid::DataType::INT64: { - std::vector data; - data.resize(row_num * sizeof(int64_t)); - memcpy(data.data(), record.data() + offset, row_num * sizeof(int64_t)); - attr_datas.insert(std::make_pair(name, data)); - - std::vector test_data(row_num); - memcpy(test_data.data(), record.data(), row_num * sizeof(int64_t)); - - attr_nbytes.insert(std::make_pair(name, sizeof(int64_t))); - attr_data_size.insert(std::make_pair(name, row_num * sizeof(int64_t))); - offset += row_num * sizeof(int64_t); - break; - } - case meta::hybrid::DataType::FLOAT: { - std::vector data; - data.resize(row_num * sizeof(float)); - - std::vector attr_value(row_num, 0); - memcpy(attr_value.data(), record.data() + offset, row_num * sizeof(double)); - - std::vector raw_value(row_num, 0); - for (uint64_t i = 0; i < row_num; ++i) { - raw_value[i] = attr_value[i]; - } - - memcpy(data.data(), raw_value.data(), row_num * sizeof(float)); - attr_datas.insert(std::make_pair(name, data)); - - attr_nbytes.insert(std::make_pair(name, sizeof(float))); - attr_data_size.insert(std::make_pair(name, row_num * sizeof(float))); - offset += row_num * sizeof(double); - break; - } - case meta::hybrid::DataType::DOUBLE: { - std::vector data; - data.resize(row_num * sizeof(double)); - memcpy(data.data(), record.data() + offset, row_num * sizeof(double)); - attr_datas.insert(std::make_pair(name, data)); - - attr_nbytes.insert(std::make_pair(name, sizeof(double))); - attr_data_size.insert(std::make_pair(name, row_num * sizeof(double))); - offset += row_num * sizeof(double); - break; - } - default: - break; - } - } - return Status::OK(); -} - -Status -DBImpl::InsertEntities(const std::string& collection_id, const std::string& partition_tag, - const std::vector& field_names, Entity& entity, - std::unordered_map& attr_types) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - // Generate id - if (entity.id_array_.empty()) { - SafeIDGenerator& id_generator = SafeIDGenerator::GetInstance(); - Status status = id_generator.GetNextIDNumbers(entity.entity_count_, entity.id_array_); - if (!status.ok()) { - return status; - } - } - - Status status; - std::unordered_map> attr_data; - std::unordered_map attr_nbytes; - std::unordered_map attr_data_size; - status = CopyToAttr(entity.attr_value_, entity.entity_count_, field_names, attr_types, attr_data, attr_nbytes, - attr_data_size); - if (!status.ok()) { - return status; - } - - wal::MXLogRecord record; - record.lsn = 0; - record.collection_id = collection_id; - record.partition_tag = partition_tag; - record.ids = entity.id_array_.data(); - record.length = entity.entity_count_; - - auto vector_it = entity.vector_data_.begin(); - if (vector_it->second.binary_data_.empty()) { - record.type = wal::MXLogType::Entity; - record.data = vector_it->second.float_data_.data(); - record.data_size = vector_it->second.float_data_.size() * sizeof(float); - record.attr_data = attr_data; - record.attr_nbytes = attr_nbytes; - record.attr_data_size = attr_data_size; - } else { - // record.type = wal::MXLogType::InsertBinary; - // record.data = entities.vector_data_[0].binary_data_.data(); - // record.length = entities.vector_data_[0].binary_data_.size() * sizeof(uint8_t); - } - - status = ExecWalRecord(record); - -#if 0 - if (options_.wal_enable_) { - std::string target_collection_name; - status = GetPartitionByTag(collection_id, partition_tag, target_collection_name); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << LogOut("[%s][%ld] Get partition fail: %s", "insert", 0, status.message().c_str()); - return status; - } - - auto vector_it = entity.vector_data_.begin(); - if (!vector_it->second.binary_data_.empty()) { - wal_mgr_->InsertEntities(collection_id, partition_tag, entity.id_array_, vector_it->second.binary_data_, - attr_nbytes, attr_data); - } else if (!vector_it->second.float_data_.empty()) { - wal_mgr_->InsertEntities(collection_id, partition_tag, entity.id_array_, vector_it->second.float_data_, - attr_nbytes, attr_data); - } - swn_wal_.Notify(); - } else { - // insert entities: collection_name is field id - wal::MXLogRecord record; - record.lsn = 0; - record.collection_id = collection_id; - record.partition_tag = partition_tag; - record.ids = entity.id_array_.data(); - record.length = entity.entity_count_; - - auto vector_it = entity.vector_data_.begin(); - if (vector_it->second.binary_data_.empty()) { - record.type = wal::MXLogType::Entity; - record.data = vector_it->second.float_data_.data(); - record.data_size = vector_it->second.float_data_.size() * sizeof(float); - record.attr_data = attr_data; - record.attr_nbytes = attr_nbytes; - record.attr_data_size = attr_data_size; - } else { - // record.type = wal::MXLogType::InsertBinary; - // record.data = entities.vector_data_[0].binary_data_.data(); - // record.length = entities.vector_data_[0].binary_data_.size() * sizeof(uint8_t); - } - - status = ExecWalRecord(record); - } -#endif - - return status; -} - -Status -DBImpl::DeleteVector(const std::string& collection_id, IDNumber vector_id) { - IDNumbers ids; - ids.push_back(vector_id); - return DeleteVectors(collection_id, ids); -} - -Status -DBImpl::DeleteVectors(const std::string& collection_id, IDNumbers vector_ids) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - Status status; - if (options_.wal_enable_) { - wal_mgr_->DeleteById(collection_id, vector_ids); - swn_wal_.Notify(); - } else { - wal::MXLogRecord record; - record.lsn = 0; // need to get from meta ? - record.type = wal::MXLogType::Delete; - record.collection_id = collection_id; - record.ids = vector_ids.data(); - record.length = vector_ids.size(); - - status = ExecWalRecord(record); - } - - return status; -} - -Status -DBImpl::Flush(const std::string& collection_id) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - Status status; - bool has_collection; - status = HasCollection(collection_id, has_collection); - if (!status.ok()) { - return status; - } - if (!has_collection) { - LOG_ENGINE_ERROR_ << "Collection to flush does not exist: " << collection_id; - return Status(DB_NOT_FOUND, "Collection to flush does not exist"); - } - - LOG_ENGINE_DEBUG_ << "Begin flush collection: " << collection_id; - - if (options_.wal_enable_) { - LOG_ENGINE_DEBUG_ << "WAL flush"; - auto lsn = wal_mgr_->Flush(collection_id); - if (lsn != 0) { - swn_wal_.Notify(); - flush_req_swn_.Wait(); - } else { - // no collection flushed, call merge task to cleanup files - std::set merge_collection_ids; - StartMergeTask(merge_collection_ids); - } - } else { - LOG_ENGINE_DEBUG_ << "MemTable flush"; - InternalFlush(collection_id); - } - - LOG_ENGINE_DEBUG_ << "End flush collection: " << collection_id; - - return status; -} - -Status -DBImpl::Flush() { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - LOG_ENGINE_DEBUG_ << "Begin flush all collections"; - - Status status; - fiu_do_on("options_wal_enable_false", options_.wal_enable_ = false); - if (options_.wal_enable_) { - LOG_ENGINE_DEBUG_ << "WAL flush"; - auto lsn = wal_mgr_->Flush(); - if (lsn != 0) { - swn_wal_.Notify(); - flush_req_swn_.Wait(); - } else { - // no collection flushed, call merge task to cleanup files - std::set merge_collection_ids; - StartMergeTask(merge_collection_ids); - } - } else { - LOG_ENGINE_DEBUG_ << "MemTable flush"; - InternalFlush(); - } - - LOG_ENGINE_DEBUG_ << "End flush all collections"; - - return status; -} - -Status -DBImpl::Compact(const std::shared_ptr& context, const std::string& collection_id, double threshold) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_id; - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - LOG_ENGINE_ERROR_ << "Collection to compact does not exist: " << collection_id; - return Status(DB_NOT_FOUND, "Collection to compact does not exist"); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - LOG_ENGINE_ERROR_ << "Collection to compact does not exist: " << collection_id; - return Status(DB_NOT_FOUND, "Collection to compact does not exist"); - } - } - - LOG_ENGINE_DEBUG_ << "Before compacting, wait for build index thread to finish..."; - - std::vector collection_array; - status = meta_ptr_->ShowPartitions(collection_id, collection_array); - collection_array.push_back(collection_schema); - - const std::lock_guard index_lock(build_index_mutex_); - const std::lock_guard merge_lock(flush_merge_compact_mutex_); - - LOG_ENGINE_DEBUG_ << "Compacting collection: " << collection_id; - - // Get files to compact from meta. - std::vector file_types{meta::SegmentSchema::FILE_TYPE::RAW, meta::SegmentSchema::FILE_TYPE::TO_INDEX, - meta::SegmentSchema::FILE_TYPE::BACKUP}; - meta::FilesHolder files_holder; - status = meta_ptr_->FilesByTypeEx(collection_array, file_types, files_holder); - if (!status.ok()) { - std::string err_msg = "Failed to get files to compact: " + status.message(); - LOG_ENGINE_ERROR_ << err_msg; - return Status(DB_ERROR, err_msg); - } - - LOG_ENGINE_DEBUG_ << "Found " << files_holder.HoldFiles().size() << " segment to compact"; - - Status compact_status; - // attention: here is a copy, not reference, since files_holder.UnmarkFile will change the array internal - milvus::engine::meta::SegmentsSchema files_to_compact = files_holder.HoldFiles(); - for (auto iter = files_to_compact.begin(); iter != files_to_compact.end();) { - // client break the connection, no need to continue - if (context && context->IsConnectionBroken()) { - LOG_ENGINE_DEBUG_ << "Client connection broken, stop compact operation"; - break; - } - - meta::SegmentSchema file = *iter; - iter = files_to_compact.erase(iter); - - // Check if the segment needs compacting - std::string segment_dir; - utils::GetParentPath(file.location_, segment_dir); - - segment::SegmentReader segment_reader(segment_dir); - size_t deleted_docs_size; - status = segment_reader.ReadDeletedDocsSize(deleted_docs_size); - if (!status.ok()) { - files_holder.UnmarkFile(file); - continue; // skip this file and try compact next one - } - - meta::SegmentsSchema files_to_update; - if (deleted_docs_size != 0) { - compact_status = CompactFile(file, threshold, files_to_update); - - if (!compact_status.ok()) { - LOG_ENGINE_ERROR_ << "Compact failed for segment " << file.segment_id_ << ": " - << compact_status.message(); - files_holder.UnmarkFile(file); - continue; // skip this file and try compact next one - } - } else { - files_holder.UnmarkFile(file); - LOG_ENGINE_DEBUG_ << "Segment " << file.segment_id_ << " has no deleted data. No need to compact"; - continue; // skip this file and try compact next one - } - - LOG_ENGINE_DEBUG_ << "Updating meta after compaction..."; - status = meta_ptr_->UpdateCollectionFiles(files_to_update); - files_holder.UnmarkFile(file); - if (!status.ok()) { - compact_status = status; - break; // meta error, could not go on - } - } - - if (compact_status.ok()) { - LOG_ENGINE_DEBUG_ << "Finished compacting collection: " << collection_id; - } - - return compact_status; -} - -Status -DBImpl::CompactFile(const meta::SegmentSchema& file, double threshold, meta::SegmentsSchema& files_to_update) { - LOG_ENGINE_DEBUG_ << "Compacting segment " << file.segment_id_ << " for collection: " << file.collection_id_; - - std::string segment_dir_to_merge; - utils::GetParentPath(file.location_, segment_dir_to_merge); - - // no need to compact if deleted vectors are too few(less than threashold) - if (file.row_count_ > 0 && threshold > 0.0) { - segment::SegmentReader segment_reader_to_merge(segment_dir_to_merge); - segment::DeletedDocsPtr deleted_docs_ptr; - auto status = segment_reader_to_merge.LoadDeletedDocs(deleted_docs_ptr); - if (status.ok()) { - auto delete_items = deleted_docs_ptr->GetDeletedDocs(); - double delete_rate = (double)delete_items.size() / (double)(delete_items.size() + file.row_count_); - if (delete_rate < threshold) { - LOG_ENGINE_DEBUG_ << "Delete rate less than " << threshold << ", no need to compact for" - << segment_dir_to_merge; - return Status::OK(); - } - } - } - - // Create new collection file - meta::SegmentSchema compacted_file; - compacted_file.collection_id_ = file.collection_id_; - compacted_file.file_type_ = meta::SegmentSchema::NEW_MERGE; // TODO: use NEW_MERGE for now - auto status = meta_ptr_->CreateCollectionFile(compacted_file); - - if (!status.ok()) { - LOG_ENGINE_ERROR_ << "Failed to create collection file: " << status.message(); - return status; - } - - // Compact (merge) file to the newly created collection file - std::string new_segment_dir; - utils::GetParentPath(compacted_file.location_, new_segment_dir); - auto segment_writer_ptr = std::make_shared(new_segment_dir); - - LOG_ENGINE_DEBUG_ << "Compacting begin..."; - segment_writer_ptr->Merge(segment_dir_to_merge, compacted_file.file_id_); - - // Serialize - LOG_ENGINE_DEBUG_ << "Serializing compacted segment..."; - status = segment_writer_ptr->Serialize(); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << "Failed to serialize compacted segment: " << status.message(); - compacted_file.file_type_ = meta::SegmentSchema::TO_DELETE; - auto mark_status = meta_ptr_->UpdateCollectionFile(compacted_file); - if (mark_status.ok()) { - LOG_ENGINE_DEBUG_ << "Mark file: " << compacted_file.file_id_ << " to to_delete"; - } - - return status; - } - - // Update compacted file state, if origin file is backup or to_index, set compacted file to to_index - compacted_file.file_size_ = segment_writer_ptr->Size(); - compacted_file.row_count_ = segment_writer_ptr->VectorCount(); - if ((file.file_type_ == (int32_t)meta::SegmentSchema::BACKUP || - file.file_type_ == (int32_t)meta::SegmentSchema::TO_INDEX) && - (compacted_file.row_count_ > meta::BUILD_INDEX_THRESHOLD)) { - compacted_file.file_type_ = meta::SegmentSchema::TO_INDEX; - } else { - compacted_file.file_type_ = meta::SegmentSchema::RAW; - } - - if (compacted_file.row_count_ == 0) { - LOG_ENGINE_DEBUG_ << "Compacted segment is empty. Mark it as TO_DELETE"; - compacted_file.file_type_ = meta::SegmentSchema::TO_DELETE; - } - - files_to_update.emplace_back(compacted_file); - - // Set all files in segment to TO_DELETE - auto& segment_id = file.segment_id_; - meta::FilesHolder files_holder; - status = meta_ptr_->GetCollectionFilesBySegmentId(segment_id, files_holder); - if (!status.ok()) { - return status; - } - - milvus::engine::meta::SegmentsSchema& segment_files = files_holder.HoldFiles(); - for (auto& f : segment_files) { - f.file_type_ = meta::SegmentSchema::FILE_TYPE::TO_DELETE; - files_to_update.emplace_back(f); - } - files_holder.ReleaseFiles(); - - LOG_ENGINE_DEBUG_ << "Compacted segment " << compacted_file.segment_id_ << " from " - << std::to_string(file.file_size_) << " bytes to " << std::to_string(compacted_file.file_size_) - << " bytes"; - - if (options_.insert_cache_immediately_) { - segment_writer_ptr->Cache(); - } - - return status; -} - -Status -DBImpl::GetVectorsByID(const engine::meta::CollectionSchema& collection, const IDNumbers& id_array, - std::vector& vectors) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - meta::FilesHolder files_holder; - std::vector file_types{meta::SegmentSchema::FILE_TYPE::RAW, meta::SegmentSchema::FILE_TYPE::TO_INDEX, - meta::SegmentSchema::FILE_TYPE::BACKUP}; - - std::vector collection_array; - auto status = meta_ptr_->ShowPartitions(collection.collection_id_, collection_array); - - collection_array.push_back(collection); - status = meta_ptr_->FilesByTypeEx(collection_array, file_types, files_holder); - if (!status.ok()) { - std::string err_msg = "Failed to get files for GetVectorByID: " + status.message(); - LOG_ENGINE_ERROR_ << err_msg; - return status; - } - - if (files_holder.HoldFiles().empty()) { - LOG_ENGINE_DEBUG_ << "No files to get vector by id from"; - return Status(DB_NOT_FOUND, "Collection is empty"); - } - - cache::CpuCacheMgr::GetInstance()->PrintInfo(); - status = GetVectorsByIdHelper(id_array, vectors, files_holder); - cache::CpuCacheMgr::GetInstance()->PrintInfo(); - - if (vectors.empty()) { - std::string msg = "Vectors not found in collection " + collection.collection_id_; - LOG_ENGINE_DEBUG_ << msg; - } - - return status; -} - -Status -DBImpl::GetVectorIDs(const std::string& collection_id, const std::string& segment_id, IDNumbers& vector_ids) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - // step 1: check collection existence - bool has_collection; - auto status = HasCollection(collection_id, has_collection); - if (!has_collection) { - LOG_ENGINE_ERROR_ << "Collection " << collection_id << " does not exist: "; - return Status(DB_NOT_FOUND, "Collection does not exist"); - } - if (!status.ok()) { - return status; - } - - // step 2: find segment - meta::FilesHolder files_holder; - status = meta_ptr_->GetCollectionFilesBySegmentId(segment_id, files_holder); - if (!status.ok()) { - return status; - } - - milvus::engine::meta::SegmentsSchema& collection_files = files_holder.HoldFiles(); - if (collection_files.empty()) { - return Status(DB_NOT_FOUND, "Segment does not exist"); - } - - // check the segment is belong to this collection - if (collection_files[0].collection_id_ != collection_id) { - // the segment could be in a partition under this collection - meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_files[0].collection_id_; - status = DescribeCollection(collection_schema); - if (collection_schema.owner_collection_ != collection_id) { - return Status(DB_NOT_FOUND, "Segment does not belong to this collection"); - } - } - - // step 3: load segment ids and delete offset - std::string segment_dir; - engine::utils::GetParentPath(collection_files[0].location_, segment_dir); - segment::SegmentReader segment_reader(segment_dir); - - std::vector uids; - status = segment_reader.LoadUids(uids); - if (!status.ok()) { - return status; - } - - segment::DeletedDocsPtr deleted_docs_ptr; - status = segment_reader.LoadDeletedDocs(deleted_docs_ptr); - if (!status.ok()) { - return status; - } - - // step 4: construct id array - // avoid duplicate offset and erase from max offset to min offset - auto& deleted_offset = deleted_docs_ptr->GetDeletedDocs(); - std::set> ordered_offset; - for (segment::offset_t offset : deleted_offset) { - ordered_offset.insert(offset); - } - for (segment::offset_t offset : ordered_offset) { - uids.erase(uids.begin() + offset); - } - vector_ids.swap(uids); - - return status; -} - -Status -DBImpl::GetVectorsByIdHelper(const IDNumbers& id_array, std::vector& vectors, - meta::FilesHolder& files_holder) { - // attention: this is a copy, not a reference, since the files_holder.UnMarkFile will change the array internal - milvus::engine::meta::SegmentsSchema files = files_holder.HoldFiles(); - LOG_ENGINE_DEBUG_ << "Getting vector by id in " << files.size() << " files, id count = " << id_array.size(); - - // sometimes not all of id_array can be found, we need to return empty vector for id not found - // for example: - // id_array = [1, -1, 2, -1, 3] - // vectors should return [valid_vector, empty_vector, valid_vector, empty_vector, valid_vector] - // the ID2RAW is to ensure returned vector sequence is consist with id_array - using ID2VECTOR = std::map; - ID2VECTOR map_id2vector; - - vectors.clear(); - - IDNumbers temp_ids = id_array; - for (auto& file : files) { - if (temp_ids.empty()) { - break; // all vectors found, no need to continue - } - // Load bloom filter - std::string segment_dir; - engine::utils::GetParentPath(file.location_, segment_dir); - segment::SegmentReader segment_reader(segment_dir); - segment::IdBloomFilterPtr id_bloom_filter_ptr; - auto status = segment_reader.LoadBloomFilter(id_bloom_filter_ptr); - if (!status.ok()) { - return status; - } - - for (IDNumbers::iterator it = temp_ids.begin(); it != temp_ids.end();) { - int64_t vector_id = *it; - // each id must has a VectorsData - // if vector not found for an id, its VectorsData's vector_count = 0, else 1 - VectorsData& vector_ref = map_id2vector[vector_id]; - - // Check if the id is present in bloom filter. - if (id_bloom_filter_ptr->Check(vector_id)) { - // Load uids and check if the id is indeed present. If yes, find its offset. - std::vector uids; - auto status = segment_reader.LoadUids(uids); - if (!status.ok()) { - return status; - } - - auto found = std::find(uids.begin(), uids.end(), vector_id); - if (found != uids.end()) { - auto offset = std::distance(uids.begin(), found); - - // Check whether the id has been deleted - segment::DeletedDocsPtr deleted_docs_ptr; - status = segment_reader.LoadDeletedDocs(deleted_docs_ptr); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << status.message(); - return status; - } - auto& deleted_docs = deleted_docs_ptr->GetDeletedDocs(); - - auto deleted = std::find(deleted_docs.begin(), deleted_docs.end(), offset); - if (deleted == deleted_docs.end()) { - // Load raw vector - bool is_binary = utils::IsBinaryMetricType(file.metric_type_); - size_t single_vector_bytes = is_binary ? file.dimension_ / 8 : file.dimension_ * sizeof(float); - std::vector raw_vector; - status = - segment_reader.LoadVectors(offset * single_vector_bytes, single_vector_bytes, raw_vector); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << status.message(); - return status; - } - - vector_ref.vector_count_ = 1; - if (is_binary) { - vector_ref.binary_data_.swap(raw_vector); - } else { - std::vector float_vector; - float_vector.resize(file.dimension_); - memcpy(float_vector.data(), raw_vector.data(), single_vector_bytes); - vector_ref.float_data_.swap(float_vector); - } - temp_ids.erase(it); - continue; - } - } - } - - it++; - } - - // unmark file, allow the file to be deleted - files_holder.UnmarkFile(file); - } - - for (auto id : id_array) { - VectorsData& vector_ref = map_id2vector[id]; - - VectorsData data; - data.vector_count_ = vector_ref.vector_count_; - if (data.vector_count_ > 0) { - data.float_data_ = vector_ref.float_data_; // copy data since there could be duplicated id - data.binary_data_ = vector_ref.binary_data_; // copy data since there could be duplicated id - } - vectors.emplace_back(data); - } - - return Status::OK(); -} - -Status -DBImpl::CreateIndex(const std::shared_ptr& context, const std::string& collection_id, - const CollectionIndex& index) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - // step 1: wait merge file thread finished to avoid duplicate data bug - auto status = Flush(); - WaitMergeFileFinish(); // let merge file thread finish - - // merge all files for this collection, including its partitions - std::set merge_collection_ids = {collection_id}; - std::vector partition_array; - status = meta_ptr_->ShowPartitions(collection_id, partition_array); - for (auto& schema : partition_array) { - merge_collection_ids.insert(schema.collection_id_); - } - StartMergeTask(merge_collection_ids, true); // start force-merge task - WaitMergeFileFinish(); // let force-merge file thread finish - - { - std::unique_lock lock(build_index_mutex_); - - // step 2: check index difference - CollectionIndex old_index; - status = DescribeIndex(collection_id, old_index); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << "Failed to get collection index info for collection: " << collection_id; - return status; - } - - // step 3: update index info - CollectionIndex new_index = index; - new_index.metric_type_ = old_index.metric_type_; // dont change metric type, it was defined by CreateCollection - if (!utils::IsSameIndex(old_index, new_index)) { - status = UpdateCollectionIndexRecursively(collection_id, new_index); - if (!status.ok()) { - return status; - } - } - } - - // step 4: wait and build index - status = index_failed_checker_.CleanFailedIndexFileOfCollection(collection_id); - status = WaitCollectionIndexRecursively(context, collection_id, index); - - return status; -} - -Status -DBImpl::DescribeIndex(const std::string& collection_id, CollectionIndex& index) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - return meta_ptr_->DescribeCollectionIndex(collection_id, index); -} - -Status -DBImpl::DropIndex(const std::string& collection_id) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - LOG_ENGINE_DEBUG_ << "Drop index for collection: " << collection_id; - auto status = DropCollectionIndexRecursively(collection_id); - std::set merge_collection_ids = {collection_id}; - StartMergeTask(merge_collection_ids, true); // merge small files after drop index - return status; -} - -Status -DBImpl::QueryByIDs(const std::shared_ptr& context, const std::string& collection_id, - const std::vector& partition_tags, uint64_t k, const milvus::json& extra_params, - const IDNumbers& id_array, ResultIds& result_ids, ResultDistances& result_distances) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - if (id_array.empty()) { - return Status(DB_ERROR, "Empty id array during query by id"); - } - - TimeRecorder rc("Query by id in collection:" + collection_id); - - // get collection schema - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_id; - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - std::string msg = "Collection to search does not exist: " + collection_id; - LOG_ENGINE_ERROR_ << msg; - return Status(DB_NOT_FOUND, msg); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - std::string msg = "Collection to search does not exist: " + collection_id; - LOG_ENGINE_ERROR_ << msg; - return Status(DB_NOT_FOUND, msg); - } - } - - rc.RecordSection("get collection schema"); - - // get target vectors data - std::vector vectors; - status = GetVectorsByID(collection_schema, id_array, vectors); - if (!status.ok()) { - std::string msg = "Failed to get vector data for collection: " + collection_id; - LOG_ENGINE_ERROR_ << msg; - return status; - } - - // some vectors could not be found, no need to search them - uint64_t valid_count = 0; - bool is_binary = utils::IsBinaryMetricType(collection_schema.metric_type_); - for (auto& vector : vectors) { - if (vector.vector_count_ > 0) { - valid_count++; - } - } - - // copy valid vectors data for search input - uint64_t dimension = collection_schema.dimension_; - VectorsData valid_vectors; - valid_vectors.vector_count_ = valid_count; - if (is_binary) { - valid_vectors.binary_data_.resize(valid_count * dimension / 8); - } else { - valid_vectors.float_data_.resize(valid_count * dimension * sizeof(float)); - } - - int64_t valid_index = 0; - for (size_t i = 0; i < vectors.size(); i++) { - if (vectors[i].vector_count_ == 0) { - continue; - } - if (is_binary) { - memcpy(valid_vectors.binary_data_.data() + valid_index * dimension / 8, vectors[i].binary_data_.data(), - vectors[i].binary_data_.size()); - } else { - memcpy(valid_vectors.float_data_.data() + valid_index * dimension, vectors[i].float_data_.data(), - vectors[i].float_data_.size() * sizeof(float)); - } - valid_index++; - } - - rc.RecordSection("construct query input"); - - // search valid vectors - ResultIds valid_result_ids; - ResultDistances valid_result_distances; - status = Query(context, collection_id, partition_tags, k, extra_params, valid_vectors, valid_result_ids, - valid_result_distances); - if (!status.ok()) { - std::string msg = "Failed to query by id in collection " + collection_id + ", error: " + status.message(); - LOG_ENGINE_ERROR_ << msg; - return status; - } - - if (valid_result_ids.size() != valid_count * k || valid_result_distances.size() != valid_count * k) { - std::string msg = "Failed to query by id in collection " + collection_id + ", result doesn't match id count"; - return Status(DB_ERROR, msg); - } - - rc.RecordSection("query vealid vectors"); - - // construct result - if (valid_count == id_array.size()) { - result_ids.swap(valid_result_ids); - result_distances.swap(valid_result_distances); - } else { - result_ids.resize(vectors.size() * k); - result_distances.resize(vectors.size() * k); - int64_t valid_index = 0; - for (uint64_t i = 0; i < vectors.size(); i++) { - if (vectors[i].vector_count_ > 0) { - memcpy(result_ids.data() + i * k, valid_result_ids.data() + valid_index * k, k * sizeof(int64_t)); - memcpy(result_distances.data() + i * k, valid_result_distances.data() + valid_index * k, - k * sizeof(float)); - valid_index++; - } else { - memset(result_ids.data() + i * k, -1, k * sizeof(int64_t)); - for (uint64_t j = i * k; j < i * k + k; j++) { - result_distances[j] = std::numeric_limits::max(); - } - } - } - } - - rc.RecordSection("construct result"); - - return status; -} - -Status -DBImpl::HybridQuery(const std::shared_ptr& context, const std::string& collection_id, - const std::vector& partition_tags, - context::HybridSearchContextPtr hybrid_search_context, query::GeneralQueryPtr general_query, - std::unordered_map& attr_type, uint64_t& nq, - ResultIds& result_ids, ResultDistances& result_distances) { - auto query_ctx = context->Child("Query"); - - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - Status status; - meta::FilesHolder files_holder; - if (partition_tags.empty()) { - // no partition tag specified, means search in whole table - // get all table files from parent table - status = meta_ptr_->FilesToSearch(collection_id, files_holder); - if (!status.ok()) { - return status; - } - - std::vector partition_array; - status = meta_ptr_->ShowPartitions(collection_id, partition_array); - if (!status.ok()) { - return status; - } - for (auto& schema : partition_array) { - status = meta_ptr_->FilesToSearch(schema.collection_id_, files_holder); - if (!status.ok()) { - return Status(DB_ERROR, "get files to search failed in HybridQuery"); - } - } - - if (files_holder.HoldFiles().empty()) { - return Status::OK(); // no files to search - } - } else { - // get files from specified partitions - std::set partition_name_array; - GetPartitionsByTags(collection_id, partition_tags, partition_name_array); - - for (auto& partition_name : partition_name_array) { - status = meta_ptr_->FilesToSearch(partition_name, files_holder); - if (!status.ok()) { - return Status(DB_ERROR, "get files to search failed in HybridQuery"); - } - } - - if (files_holder.HoldFiles().empty()) { - return Status::OK(); - } - } - - cache::CpuCacheMgr::GetInstance()->PrintInfo(); // print cache info before query - status = HybridQueryAsync(query_ctx, collection_id, files_holder, hybrid_search_context, general_query, attr_type, - nq, result_ids, result_distances); - if (!status.ok()) { - return status; - } - cache::CpuCacheMgr::GetInstance()->PrintInfo(); // print cache info after query - - query_ctx->GetTraceContext()->GetSpan()->Finish(); - - return status; -} - -Status -DBImpl::Query(const std::shared_ptr& context, const std::string& collection_id, - const std::vector& partition_tags, uint64_t k, const milvus::json& extra_params, - const VectorsData& vectors, ResultIds& result_ids, ResultDistances& result_distances) { - milvus::server::ContextChild tracer(context, "Query"); - - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - Status status; - meta::FilesHolder files_holder; - if (partition_tags.empty()) { -#if 0 - // no partition tag specified, means search in whole collection - // get all collection files from parent collection - status = meta_ptr_->FilesToSearch(collection_id, files_holder); - if (!status.ok()) { - return status; - } - - std::vector partition_array; - status = meta_ptr_->ShowPartitions(collection_id, partition_array); - for (auto& schema : partition_array) { - status = meta_ptr_->FilesToSearch(schema.collection_id_, files_holder); - } -#else - // no partition tag specified, means search in whole collection - // get files from root collection - status = meta_ptr_->FilesToSearch(collection_id, files_holder); - if (!status.ok()) { - return status; - } - - // get files from partitions - std::set partition_ids; - std::vector partition_array; - status = meta_ptr_->ShowPartitions(collection_id, partition_array); - for (auto& id : partition_array) { - partition_ids.insert(id.collection_id_); - } - - status = meta_ptr_->FilesToSearchEx(collection_id, partition_ids, files_holder); - if (!status.ok()) { - return status; - } -#endif - - if (files_holder.HoldFiles().empty()) { - return Status::OK(); // no files to search - } - } else { -#if 0 - // get files from specified partitions - std::set partition_name_array; - status = GetPartitionsByTags(collection_id, partition_tags, partition_name_array); - if (!status.ok()) { - return status; // didn't match any partition. - } - - for (auto& partition_name : partition_name_array) { - status = meta_ptr_->FilesToSearch(partition_name, files_holder); - } -#else - std::set partition_name_array; - status = GetPartitionsByTags(collection_id, partition_tags, partition_name_array); - if (!status.ok()) { - return status; // didn't match any partition. - } - - std::set partition_ids; - for (auto& partition_name : partition_name_array) { - partition_ids.insert(partition_name); - } - - status = meta_ptr_->FilesToSearchEx(collection_id, partition_ids, files_holder); -#endif - if (files_holder.HoldFiles().empty()) { - return Status::OK(); // no files to search - } - } - - cache::CpuCacheMgr::GetInstance()->PrintInfo(); // print cache info before query - status = QueryAsync(tracer.Context(), files_holder, k, extra_params, vectors, result_ids, result_distances); - cache::CpuCacheMgr::GetInstance()->PrintInfo(); // print cache info after query - - return status; -} - -Status -DBImpl::QueryByFileID(const std::shared_ptr& context, const std::vector& file_ids, - uint64_t k, const milvus::json& extra_params, const VectorsData& vectors, ResultIds& result_ids, - ResultDistances& result_distances) { - milvus::server::ContextChild tracer(context, "Query by file id"); - - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - // get specified files - std::vector ids; - for (auto& id : file_ids) { - std::string::size_type sz; - ids.push_back(std::stoul(id, &sz)); - } - - meta::FilesHolder files_holder; - auto status = meta_ptr_->FilesByID(ids, files_holder); - if (!status.ok()) { - return status; - } - - milvus::engine::meta::SegmentsSchema& search_files = files_holder.HoldFiles(); - if (search_files.empty()) { - return Status(DB_ERROR, "Invalid file id"); - } - - cache::CpuCacheMgr::GetInstance()->PrintInfo(); // print cache info before query - status = QueryAsync(tracer.Context(), files_holder, k, extra_params, vectors, result_ids, result_distances); - cache::CpuCacheMgr::GetInstance()->PrintInfo(); // print cache info after query - - return status; -} - -Status -DBImpl::Size(uint64_t& result) { - if (!initialized_.load(std::memory_order_acquire)) { - return SHUTDOWN_ERROR; - } - - return meta_ptr_->Size(result); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// internal methods -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -Status -DBImpl::QueryAsync(const std::shared_ptr& context, meta::FilesHolder& files_holder, uint64_t k, - const milvus::json& extra_params, const VectorsData& vectors, ResultIds& result_ids, - ResultDistances& result_distances) { - milvus::server::ContextChild tracer(context, "Query Async"); - server::CollectQueryMetrics metrics(vectors.vector_count_); - - milvus::engine::meta::SegmentsSchema& files = files_holder.HoldFiles(); - if (files.size() > milvus::scheduler::TASK_TABLE_MAX_COUNT) { - std::string msg = - "Search files count exceed scheduler limit: " + std::to_string(milvus::scheduler::TASK_TABLE_MAX_COUNT); - LOG_ENGINE_ERROR_ << msg; - return Status(DB_ERROR, msg); - } - - TimeRecorder rc(""); - - // step 1: construct search job - LOG_ENGINE_DEBUG_ << LogOut("Engine query begin, index file count: %ld", files.size()); - scheduler::SearchJobPtr job = std::make_shared(tracer.Context(), k, extra_params, vectors); - for (auto& file : files) { - scheduler::SegmentSchemaPtr file_ptr = std::make_shared(file); - job->AddIndexFile(file_ptr); - } - - // Suspend builder - SuspendIfFirst(); - - // step 2: put search job to scheduler and wait result - scheduler::JobMgrInst::GetInstance()->Put(job); - job->WaitResult(); - - // Resume builder - ResumeIfLast(); - - files_holder.ReleaseFiles(); - if (!job->GetStatus().ok()) { - return job->GetStatus(); - } - - // step 3: construct results - result_ids = job->GetResultIds(); - result_distances = job->GetResultDistances(); - rc.ElapseFromBegin("Engine query totally cost"); - - return Status::OK(); -} - -Status -DBImpl::HybridQueryAsync(const std::shared_ptr& context, const std::string& collection_id, - meta::FilesHolder& files_holder, context::HybridSearchContextPtr hybrid_search_context, - query::GeneralQueryPtr general_query, - std::unordered_map& attr_type, uint64_t& nq, - ResultIds& result_ids, ResultDistances& result_distances) { - auto query_async_ctx = context->Child("Query Async"); - -#if 0 - // Construct tasks - for (auto file : files) { - std::unordered_map types; - auto it = attr_type.begin(); - for (; it != attr_type.end(); it++) { - types.insert(std::make_pair(it->first, (engine::DataType)it->second)); - } - - auto file_ptr = std::make_shared(file); - search::TaskPtr - task = std::make_shared(context, file_ptr, general_query, types, hybrid_search_context); - search::TaskInst::GetInstance().load_queue().push(task); - search::TaskInst::GetInstance().load_cv().notify_one(); - hybrid_search_context->tasks_.emplace_back(task); - } - -#endif - - //#if 0 - TimeRecorder rc(""); - - // step 1: construct search job - VectorsData vectors; - milvus::engine::meta::SegmentsSchema& files = files_holder.HoldFiles(); - LOG_ENGINE_DEBUG_ << LogOut("Engine query begin, index file count: %ld", files_holder.HoldFiles().size()); - scheduler::SearchJobPtr job = - std::make_shared(query_async_ctx, general_query, attr_type, vectors); - for (auto& file : files) { - scheduler::SegmentSchemaPtr file_ptr = std::make_shared(file); - job->AddIndexFile(file_ptr); - } - - // step 2: put search job to scheduler and wait result - scheduler::JobMgrInst::GetInstance()->Put(job); - job->WaitResult(); - - files_holder.ReleaseFiles(); - if (!job->GetStatus().ok()) { - return job->GetStatus(); - } - - // step 3: construct results - nq = job->vector_count(); - result_ids = job->GetResultIds(); - result_distances = job->GetResultDistances(); - rc.ElapseFromBegin("Engine query totally cost"); - - query_async_ctx->GetTraceContext()->GetSpan()->Finish(); - //#endif - - return Status::OK(); -} - -void -DBImpl::BackgroundIndexThread() { - SetThreadName("index_thread"); - server::SystemInfo::GetInstance().Init(); - while (true) { - if (!initialized_.load(std::memory_order_acquire)) { - WaitMergeFileFinish(); - WaitBuildIndexFinish(); - - LOG_ENGINE_DEBUG_ << "DB background thread exit"; - break; - } - - swn_index_.Wait_For(std::chrono::seconds(BACKGROUND_INDEX_INTERVAL)); - - WaitMergeFileFinish(); - StartBuildIndexTask(); - } -} - -void -DBImpl::WaitMergeFileFinish() { - // LOG_ENGINE_DEBUG_ << "Begin WaitMergeFileFinish"; - std::lock_guard lck(merge_result_mutex_); - for (auto& iter : merge_thread_results_) { - iter.wait(); - } - // LOG_ENGINE_DEBUG_ << "End WaitMergeFileFinish"; -} - -void -DBImpl::WaitBuildIndexFinish() { - // LOG_ENGINE_DEBUG_ << "Begin WaitBuildIndexFinish"; - std::lock_guard lck(index_result_mutex_); - for (auto& iter : index_thread_results_) { - iter.wait(); - } - // LOG_ENGINE_DEBUG_ << "End WaitBuildIndexFinish"; -} - -void -DBImpl::StartMetricTask() { - server::Metrics::GetInstance().KeepingAliveCounterIncrement(BACKGROUND_METRIC_INTERVAL); - int64_t cache_usage = cache::CpuCacheMgr::GetInstance()->CacheUsage(); - int64_t cache_total = cache::CpuCacheMgr::GetInstance()->CacheCapacity(); - fiu_do_on("DBImpl.StartMetricTask.InvalidTotalCache", cache_total = 0); - - if (cache_total > 0) { - double cache_usage_double = cache_usage; - server::Metrics::GetInstance().CpuCacheUsageGaugeSet(cache_usage_double * 100 / cache_total); - } else { - server::Metrics::GetInstance().CpuCacheUsageGaugeSet(0); - } - - server::Metrics::GetInstance().GpuCacheUsageGaugeSet(); - uint64_t size; - Size(size); - server::Metrics::GetInstance().DataFileSizeGaugeSet(size); - server::Metrics::GetInstance().CPUUsagePercentSet(); - server::Metrics::GetInstance().RAMUsagePercentSet(); - server::Metrics::GetInstance().GPUPercentGaugeSet(); - server::Metrics::GetInstance().GPUMemoryUsageGaugeSet(); - server::Metrics::GetInstance().OctetsSet(); - - server::Metrics::GetInstance().CPUCoreUsagePercentSet(); - server::Metrics::GetInstance().GPUTemperature(); - server::Metrics::GetInstance().CPUTemperature(); - server::Metrics::GetInstance().PushToGateway(); -} - -void -DBImpl::StartMergeTask(const std::set& merge_collection_ids, bool force_merge_all) { - // LOG_ENGINE_DEBUG_ << "Begin StartMergeTask"; - // merge task has been finished? - { - std::lock_guard lck(merge_result_mutex_); - if (!merge_thread_results_.empty()) { - std::chrono::milliseconds span(10); - if (merge_thread_results_.back().wait_for(span) == std::future_status::ready) { - merge_thread_results_.pop_back(); - } - } - } - - // add new merge task - { - std::lock_guard lck(merge_result_mutex_); - if (merge_thread_results_.empty()) { - // start merge file thread - merge_thread_results_.push_back( - merge_thread_pool_.enqueue(&DBImpl::BackgroundMerge, this, merge_collection_ids, force_merge_all)); - } - } - - // LOG_ENGINE_DEBUG_ << "End StartMergeTask"; -} - -// Status -// DBImpl::MergeHybridFiles(const std::string& collection_id, meta::FilesHolder& files_holder) { -// // const std::lock_guard lock(flush_merge_compact_mutex_); -// -// LOG_ENGINE_DEBUG_ << "Merge files for collection: " << collection_id; -// -// // step 1: create table file -// meta::SegmentSchema table_file; -// table_file.collection_id_ = collection_id; -// table_file.file_type_ = meta::SegmentSchema::NEW_MERGE; -// Status status = meta_ptr_->CreateHybridCollectionFile(table_file); -// -// if (!status.ok()) { -// LOG_ENGINE_ERROR_ << "Failed to create collection: " << status.ToString(); -// return status; -// } -// -// // step 2: merge files -// /* -// ExecutionEnginePtr index = -// EngineFactory::Build(table_file.dimension_, table_file.location_, (EngineType)table_file.engine_type_, -// (MetricType)table_file.metric_type_, table_file.nlist_); -//*/ -// meta::SegmentsSchema updated; -// -// std::string new_segment_dir; -// utils::GetParentPath(table_file.location_, new_segment_dir); -// auto segment_writer_ptr = std::make_shared(new_segment_dir); -// -// // attention: here is a copy, not reference, since files_holder.UnmarkFile will change the array internal -// milvus::engine::meta::SegmentsSchema files = files_holder.HoldFiles(); -// for (auto& file : files) { -// server::CollectMergeFilesMetrics metrics; -// std::string segment_dir_to_merge; -// utils::GetParentPath(file.location_, segment_dir_to_merge); -// segment_writer_ptr->Merge(segment_dir_to_merge, table_file.file_id_); -// -// files_holder.UnmarkFile(file); -// -// auto file_schema = file; -// file_schema.file_type_ = meta::SegmentSchema::TO_DELETE; -// updated.push_back(file_schema); -// int64_t size = segment_writer_ptr->Size(); -// if (size >= file_schema.index_file_size_) { -// break; -// } -// } -// -// // step 3: serialize to disk -// try { -// status = segment_writer_ptr->Serialize(); -// fiu_do_on("DBImpl.MergeFiles.Serialize_ThrowException", throw std::exception()); -// fiu_do_on("DBImpl.MergeFiles.Serialize_ErrorStatus", status = Status(DB_ERROR, "")); -// } catch (std::exception& ex) { -// std::string msg = "Serialize merged index encounter exception: " + std::string(ex.what()); -// LOG_ENGINE_ERROR_ << msg; -// status = Status(DB_ERROR, msg); -// } -// -// if (!status.ok()) { -// LOG_ENGINE_ERROR_ << "Failed to persist merged segment: " << new_segment_dir << ". Error: " << -// status.message(); -// -// // if failed to serialize merge file to disk -// // typical error: out of disk space, out of memory or permission denied -// table_file.file_type_ = meta::SegmentSchema::TO_DELETE; -// status = meta_ptr_->UpdateCollectionFile(table_file); -// LOG_ENGINE_DEBUG_ << "Failed to update file to index, mark file: " << table_file.file_id_ << " to to_delete"; -// -// return status; -// } -// -// // step 4: update table files state -// // if index type isn't IDMAP, set file type to TO_INDEX if file size exceed index_file_size -// // else set file type to RAW, no need to build index -// if (!utils::IsRawIndexType(table_file.engine_type_)) { -// table_file.file_type_ = (segment_writer_ptr->Size() >= (size_t)(table_file.index_file_size_)) -// ? meta::SegmentSchema::TO_INDEX -// : meta::SegmentSchema::RAW; -// } else { -// table_file.file_type_ = meta::SegmentSchema::RAW; -// } -// table_file.file_size_ = segment_writer_ptr->Size(); -// table_file.row_count_ = segment_writer_ptr->VectorCount(); -// updated.push_back(table_file); -// status = meta_ptr_->UpdateCollectionFiles(updated); -// LOG_ENGINE_DEBUG_ << "New merged segment " << table_file.segment_id_ << " of size " << segment_writer_ptr->Size() -// << " bytes"; -// -// if (options_.insert_cache_immediately_) { -// segment_writer_ptr->Cache(); -// } -// -// return status; -//} - -void -DBImpl::BackgroundMerge(std::set collection_ids, bool force_merge_all) { - // LOG_ENGINE_TRACE_ << " Background merge thread start"; - - Status status; - for (auto& collection_id : collection_ids) { - const std::lock_guard lock(flush_merge_compact_mutex_); - - auto old_strategy = merge_mgr_ptr_->Strategy(); - if (force_merge_all) { - merge_mgr_ptr_->UseStrategy(MergeStrategyType::ADAPTIVE); - } - - auto status = merge_mgr_ptr_->MergeFiles(collection_id); - merge_mgr_ptr_->UseStrategy(old_strategy); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << "Failed to get merge files for collection: " << collection_id - << " reason:" << status.message(); - } - - if (!initialized_.load(std::memory_order_acquire)) { - LOG_ENGINE_DEBUG_ << "Server will shutdown, skip merge action for collection: " << collection_id; - break; - } - } - - // meta_ptr_->Archive(); - - { - uint64_t timeout = (options_.file_cleanup_timeout_ >= 0) ? options_.file_cleanup_timeout_ : 10; - uint64_t ttl = timeout * meta::SECOND; // default: file will be hard-deleted few seconds after soft-deleted - meta_ptr_->CleanUpFilesWithTTL(ttl); - } - - // LOG_ENGINE_TRACE_ << " Background merge thread exit"; -} - -void -DBImpl::StartBuildIndexTask() { - // build index has been finished? - { - std::lock_guard lck(index_result_mutex_); - if (!index_thread_results_.empty()) { - std::chrono::milliseconds span(10); - if (index_thread_results_.back().wait_for(span) == std::future_status::ready) { - index_thread_results_.pop_back(); - } - } - } - - // add new build index task - { - std::lock_guard lck(index_result_mutex_); - if (index_thread_results_.empty()) { - index_thread_results_.push_back(index_thread_pool_.enqueue(&DBImpl::BackgroundBuildIndex, this)); - } - } -} - -void -DBImpl::BackgroundBuildIndex() { - std::unique_lock lock(build_index_mutex_); - meta::FilesHolder files_holder; - meta_ptr_->FilesToIndex(files_holder); - - milvus::engine::meta::SegmentsSchema to_index_files = files_holder.HoldFiles(); - Status status = index_failed_checker_.IgnoreFailedIndexFiles(to_index_files); - - if (!to_index_files.empty()) { - LOG_ENGINE_DEBUG_ << "Background build index thread begin " << to_index_files.size() << " files"; - - // step 2: put build index task to scheduler - std::vector> job2file_map; - for (auto& file : to_index_files) { - scheduler::BuildIndexJobPtr job = std::make_shared(meta_ptr_, options_); - scheduler::SegmentSchemaPtr file_ptr = std::make_shared(file); - job->AddToIndexFiles(file_ptr); - scheduler::JobMgrInst::GetInstance()->Put(job); - job2file_map.push_back(std::make_pair(job, file_ptr)); - } - - // step 3: wait build index finished and mark failed files - int64_t completed = 0; - for (auto iter = job2file_map.begin(); iter != job2file_map.end(); ++iter) { - scheduler::BuildIndexJobPtr job = iter->first; - meta::SegmentSchema& file_schema = *(iter->second.get()); - job->WaitBuildIndexFinish(); - LOG_ENGINE_INFO_ << "Build Index Progress: " << ++completed << " of " << job2file_map.size(); - if (!job->GetStatus().ok()) { - Status status = job->GetStatus(); - LOG_ENGINE_ERROR_ << "Building index job " << job->id() << " failed: " << status.ToString(); - - index_failed_checker_.MarkFailedIndexFile(file_schema, status.message()); - } else { - LOG_ENGINE_DEBUG_ << "Building index job " << job->id() << " succeed."; - - index_failed_checker_.MarkSucceedIndexFile(file_schema); - } - status = files_holder.UnmarkFile(file_schema); - LOG_ENGINE_DEBUG_ << "Finish build index file " << file_schema.file_id_; - } - - LOG_ENGINE_DEBUG_ << "Background build index thread finished"; - index_req_swn_.Notify(); // notify CreateIndex check circle - } -} - -Status -DBImpl::GetFilesToBuildIndex(const std::string& collection_id, const std::vector& file_types, - meta::FilesHolder& files_holder) { - files_holder.ReleaseFiles(); - auto status = meta_ptr_->FilesByType(collection_id, file_types, files_holder); - - // attention: here is a copy, not reference, since files_holder.UnmarkFile will change the array internal - milvus::engine::meta::SegmentsSchema files = files_holder.HoldFiles(); - for (const milvus::engine::meta::SegmentSchema& file : files) { - if (file.file_type_ == static_cast(meta::SegmentSchema::RAW) && - file.row_count_ < meta::BUILD_INDEX_THRESHOLD) { - // skip build index for files that row count less than certain threshold - files_holder.UnmarkFile(file); - } else if (index_failed_checker_.IsFailedIndexFile(file)) { - // skip build index for files that failed before - files_holder.UnmarkFile(file); - } - } - - return Status::OK(); -} - -Status -DBImpl::GetPartitionByTag(const std::string& collection_id, const std::string& partition_tag, - std::string& partition_name) { - Status status; - - if (partition_tag.empty()) { - partition_name = collection_id; - - } else { - // trim side-blank of tag, only compare valid characters - // for example: " ab cd " is treated as "ab cd" - std::string valid_tag = partition_tag; - server::StringHelpFunctions::TrimStringBlank(valid_tag); - - if (valid_tag == milvus::engine::DEFAULT_PARTITON_TAG) { - partition_name = collection_id; - return status; - } - - status = meta_ptr_->GetPartitionName(collection_id, partition_tag, partition_name); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << status.message(); - } - } - - return status; -} - -Status -DBImpl::GetPartitionsByTags(const std::string& collection_id, const std::vector& partition_tags, - std::set& partition_name_array) { - std::vector partition_array; - auto status = meta_ptr_->ShowPartitions(collection_id, partition_array); - - for (auto& tag : partition_tags) { - // trim side-blank of tag, only compare valid characters - // for example: " ab cd " is treated as "ab cd" - std::string valid_tag = tag; - server::StringHelpFunctions::TrimStringBlank(valid_tag); - - if (valid_tag == milvus::engine::DEFAULT_PARTITON_TAG) { - partition_name_array.insert(collection_id); - return status; - } - - for (auto& schema : partition_array) { - if (server::StringHelpFunctions::IsRegexMatch(schema.partition_tag_, valid_tag)) { - partition_name_array.insert(schema.collection_id_); - } - } - } - - if (partition_name_array.empty()) { - return Status(DB_PARTITION_NOT_FOUND, "The specified partiton does not exist"); - } - - return Status::OK(); -} - -Status -DBImpl::UpdateCollectionIndexRecursively(const std::string& collection_id, const CollectionIndex& index) { - DropIndex(collection_id); - WaitMergeFileFinish(); // DropIndex called StartMergeTask, need to wait merge thread finish - auto status = meta_ptr_->UpdateCollectionIndex(collection_id, index); - fiu_do_on("DBImpl.UpdateCollectionIndexRecursively.fail_update_collection_index", - status = Status(DB_META_TRANSACTION_FAILED, "")); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << "Failed to update collection index info for collection: " << collection_id; - return status; - } - - std::vector partition_array; - status = meta_ptr_->ShowPartitions(collection_id, partition_array); - if (!status.ok()) { - return status; - } - for (auto& schema : partition_array) { - status = UpdateCollectionIndexRecursively(schema.collection_id_, index); - if (!status.ok()) { - return status; - } - } - - return Status::OK(); -} - -Status -DBImpl::WaitCollectionIndexRecursively(const std::shared_ptr& context, - const std::string& collection_id, const CollectionIndex& index) { - // for IDMAP type, only wait all NEW file converted to RAW file - // for other type, wait NEW/RAW/NEW_MERGE/NEW_INDEX/TO_INDEX files converted to INDEX files - std::vector file_types; - if (utils::IsRawIndexType(index.engine_type_)) { - file_types = { - static_cast(meta::SegmentSchema::NEW), - static_cast(meta::SegmentSchema::NEW_MERGE), - }; - } else { - file_types = { - static_cast(meta::SegmentSchema::RAW), static_cast(meta::SegmentSchema::NEW), - static_cast(meta::SegmentSchema::NEW_MERGE), static_cast(meta::SegmentSchema::NEW_INDEX), - static_cast(meta::SegmentSchema::TO_INDEX), - }; - } - - // get files to build index - { - meta::FilesHolder files_holder; - auto status = GetFilesToBuildIndex(collection_id, file_types, files_holder); - int times = 1; - uint64_t repeat = 0; - while (!files_holder.HoldFiles().empty()) { - if (repeat % WAIT_BUILD_INDEX_INTERVAL == 0) { - LOG_ENGINE_DEBUG_ << files_holder.HoldFiles().size() << " non-index files detected! Will build index " - << times; - if (!utils::IsRawIndexType(index.engine_type_)) { - status = meta_ptr_->UpdateCollectionFilesToIndex(collection_id); - } - } - - auto ret = index_req_swn_.Wait_For(std::chrono::seconds(1)); - // client break the connection, no need to block, check every 1 second - if (context && context->IsConnectionBroken()) { - LOG_ENGINE_DEBUG_ << "Client connection broken, build index in background"; - break; // just break, not return, continue to update partitions files to to_index - } - - // check to_index files every 5 seconds or background index thread finished - repeat++; - if (repeat % WAIT_BUILD_INDEX_INTERVAL == 0) { - GetFilesToBuildIndex(collection_id, file_types, files_holder); - ++times; - } - } - } - - // build index for partition - std::vector partition_array; - auto status = meta_ptr_->ShowPartitions(collection_id, partition_array); - for (auto& schema : partition_array) { - status = WaitCollectionIndexRecursively(context, schema.collection_id_, index); - fiu_do_on("DBImpl.WaitCollectionIndexRecursively.fail_build_collection_Index_for_partition", - status = Status(DB_ERROR, "")); - if (!status.ok()) { - return status; - } - } - - // failed to build index for some files, return error - std::string err_msg; - index_failed_checker_.GetErrMsgForCollection(collection_id, err_msg); - fiu_do_on("DBImpl.WaitCollectionIndexRecursively.not_empty_err_msg", err_msg.append("fiu")); - if (!err_msg.empty()) { - return Status(DB_ERROR, err_msg); - } - - LOG_ENGINE_DEBUG_ << "WaitCollectionIndexRecursively finished"; - - return Status::OK(); -} - -Status -DBImpl::DropCollectionIndexRecursively(const std::string& collection_id) { - LOG_ENGINE_DEBUG_ << "Drop index for collection: " << collection_id; - index_failed_checker_.CleanFailedIndexFileOfCollection(collection_id); - auto status = meta_ptr_->DropCollectionIndex(collection_id); - if (!status.ok()) { - return status; - } - - // drop partition index - std::vector partition_array; - status = meta_ptr_->ShowPartitions(collection_id, partition_array); - for (auto& schema : partition_array) { - status = DropCollectionIndexRecursively(schema.collection_id_); - fiu_do_on("DBImpl.DropCollectionIndexRecursively.fail_drop_collection_Index_for_partition", - status = Status(DB_ERROR, "")); - if (!status.ok()) { - return status; - } - } - - return Status::OK(); -} - -Status -DBImpl::GetCollectionRowCountRecursively(const std::string& collection_id, uint64_t& row_count) { - row_count = 0; - auto status = meta_ptr_->Count(collection_id, row_count); - if (!status.ok()) { - return status; - } - - // get partition row count - std::vector partition_array; - status = meta_ptr_->ShowPartitions(collection_id, partition_array); - for (auto& schema : partition_array) { - uint64_t partition_row_count = 0; - status = GetCollectionRowCountRecursively(schema.collection_id_, partition_row_count); - fiu_do_on("DBImpl.GetCollectionRowCountRecursively.fail_get_collection_rowcount_for_partition", - status = Status(DB_ERROR, "")); - if (!status.ok()) { - return status; - } - - row_count += partition_row_count; - } - - return Status::OK(); -} - -Status -DBImpl::ExecWalRecord(const wal::MXLogRecord& record) { - fiu_return_on("DBImpl.ExexWalRecord.return", Status();); - - auto collections_flushed = [&](const std::string collection_id, - const std::set& target_collection_names) -> uint64_t { - uint64_t max_lsn = 0; - if (options_.wal_enable_ && !target_collection_names.empty()) { - uint64_t lsn = 0; - for (auto& collection : target_collection_names) { - meta_ptr_->GetCollectionFlushLSN(collection, lsn); - if (lsn > max_lsn) { - max_lsn = lsn; - } - } - wal_mgr_->CollectionFlushed(collection_id, lsn); - } - - std::set merge_collection_ids; - for (auto& collection : target_collection_names) { - merge_collection_ids.insert(collection); - } - StartMergeTask(merge_collection_ids); - return max_lsn; - }; - - auto force_flush_if_mem_full = [&]() -> uint64_t { - if (mem_mgr_->GetCurrentMem() > options_.insert_buffer_size_) { - LOG_ENGINE_DEBUG_ << LogOut("[%s][%ld] ", "insert", 0) << "Insert buffer size exceeds limit. Force flush"; - InternalFlush(); - } - }; - - Status status; - - switch (record.type) { - case wal::MXLogType::Entity: { - std::string target_collection_name; - status = GetPartitionByTag(record.collection_id, record.partition_tag, target_collection_name); - if (!status.ok()) { - LOG_WAL_ERROR_ << LogOut("[%s][%ld] ", "insert", 0) << "Get partition fail: " << status.message(); - return status; - } - - status = mem_mgr_->InsertEntities( - target_collection_name, record.length, record.ids, (record.data_size / record.length / sizeof(float)), - (const float*)record.data, record.attr_nbytes, record.attr_data_size, record.attr_data, record.lsn); - force_flush_if_mem_full(); - - // metrics - milvus::server::CollectInsertMetrics metrics(record.length, status); - break; - } - case wal::MXLogType::InsertBinary: { - std::string target_collection_name; - status = GetPartitionByTag(record.collection_id, record.partition_tag, target_collection_name); - if (!status.ok()) { - LOG_WAL_ERROR_ << LogOut("[%s][%ld] ", "insert", 0) << "Get partition fail: " << status.message(); - return status; - } - - status = mem_mgr_->InsertVectors(target_collection_name, record.length, record.ids, - (record.data_size / record.length / sizeof(uint8_t)), - (const u_int8_t*)record.data, record.lsn); - force_flush_if_mem_full(); - - // metrics - milvus::server::CollectInsertMetrics metrics(record.length, status); - break; - } - - case wal::MXLogType::InsertVector: { - std::string target_collection_name; - status = GetPartitionByTag(record.collection_id, record.partition_tag, target_collection_name); - if (!status.ok()) { - LOG_WAL_ERROR_ << LogOut("[%s][%ld] ", "insert", 0) << "Get partition fail: " << status.message(); - return status; - } - - status = mem_mgr_->InsertVectors(target_collection_name, record.length, record.ids, - (record.data_size / record.length / sizeof(float)), - (const float*)record.data, record.lsn); - force_flush_if_mem_full(); - - // metrics - milvus::server::CollectInsertMetrics metrics(record.length, status); - break; - } - - case wal::MXLogType::Delete: { - std::vector partition_array; - status = meta_ptr_->ShowPartitions(record.collection_id, partition_array); - if (!status.ok()) { - return status; - } - - std::vector collection_ids{record.collection_id}; - for (auto& partition : partition_array) { - auto& partition_collection_id = partition.collection_id_; - collection_ids.emplace_back(partition_collection_id); - } - - if (record.length == 1) { - for (auto& collection_id : collection_ids) { - status = mem_mgr_->DeleteVector(collection_id, *record.ids, record.lsn); - if (!status.ok()) { - return status; - } - } - } else { - for (auto& collection_id : collection_ids) { - status = mem_mgr_->DeleteVectors(collection_id, record.length, record.ids, record.lsn); - if (!status.ok()) { - return status; - } - } - } - break; - } - - case wal::MXLogType::Flush: { - if (!record.collection_id.empty()) { - // flush one collection - std::vector partition_array; - status = meta_ptr_->ShowPartitions(record.collection_id, partition_array); - if (!status.ok()) { - return status; - } - - std::vector collection_ids{record.collection_id}; - for (auto& partition : partition_array) { - auto& partition_collection_id = partition.collection_id_; - collection_ids.emplace_back(partition_collection_id); - } - - std::set flushed_collections; - for (auto& collection_id : collection_ids) { - const std::lock_guard lock(flush_merge_compact_mutex_); - status = mem_mgr_->Flush(collection_id); - if (!status.ok()) { - break; - } - flushed_collections.insert(collection_id); - } - - collections_flushed(record.collection_id, flushed_collections); - - } else { - // flush all collections - std::set collection_ids; - { - const std::lock_guard lock(flush_merge_compact_mutex_); - status = mem_mgr_->Flush(collection_ids); - } - - uint64_t lsn = collections_flushed("", collection_ids); - if (options_.wal_enable_) { - wal_mgr_->RemoveOldFiles(lsn); - } - } - break; - } - - default: - break; - } - - return status; -} - -void -DBImpl::InternalFlush(const std::string& collection_id) { - wal::MXLogRecord record; - record.type = wal::MXLogType::Flush; - record.collection_id = collection_id; - ExecWalRecord(record); -} - -void -DBImpl::BackgroundWalThread() { - SetThreadName("wal_thread"); - server::SystemInfo::GetInstance().Init(); - - std::chrono::system_clock::time_point next_auto_flush_time; - auto get_next_auto_flush_time = [&]() { - return std::chrono::system_clock::now() + std::chrono::seconds(options_.auto_flush_interval_); - }; - if (options_.auto_flush_interval_ > 0) { - next_auto_flush_time = get_next_auto_flush_time(); - } - InternalFlush(); - while (true) { - if (options_.auto_flush_interval_ > 0) { - if (std::chrono::system_clock::now() >= next_auto_flush_time) { - InternalFlush(); - next_auto_flush_time = get_next_auto_flush_time(); - } - } - - wal::MXLogRecord record; - auto error_code = wal_mgr_->GetNextRecord(record); - if (error_code != WAL_SUCCESS) { - LOG_ENGINE_ERROR_ << "WAL background GetNextRecord error"; - break; - } - - if (record.type != wal::MXLogType::None) { - ExecWalRecord(record); - if (record.type == wal::MXLogType::Flush) { - // notify flush request to return - flush_req_swn_.Notify(); - - // if user flush all manually, update auto flush also - if (record.collection_id.empty() && options_.auto_flush_interval_ > 0) { - next_auto_flush_time = get_next_auto_flush_time(); - } - } - - } else { - if (!initialized_.load(std::memory_order_acquire)) { - InternalFlush(); - flush_req_swn_.Notify(); - WaitMergeFileFinish(); - WaitBuildIndexFinish(); - LOG_ENGINE_DEBUG_ << "WAL background thread exit"; - break; - } - - if (options_.auto_flush_interval_ > 0) { - swn_wal_.Wait_Until(next_auto_flush_time); - } else { - swn_wal_.Wait(); - } - } - } -} - -void -DBImpl::BackgroundFlushThread() { - SetThreadName("flush_thread"); - server::SystemInfo::GetInstance().Init(); - while (true) { - if (!initialized_.load(std::memory_order_acquire)) { - LOG_ENGINE_DEBUG_ << "DB background flush thread exit"; - break; - } - - InternalFlush(); - if (options_.auto_flush_interval_ > 0) { - swn_flush_.Wait_For(std::chrono::seconds(options_.auto_flush_interval_)); - } else { - swn_flush_.Wait(); - } - } -} - -void -DBImpl::BackgroundMetricThread() { - SetThreadName("metric_thread"); - server::SystemInfo::GetInstance().Init(); - while (true) { - if (!initialized_.load(std::memory_order_acquire)) { - LOG_ENGINE_DEBUG_ << "DB background metric thread exit"; - break; - } - - swn_metric_.Wait_For(std::chrono::seconds(BACKGROUND_METRIC_INTERVAL)); - StartMetricTask(); - meta::FilesHolder::PrintInfo(); - } -} - -void -DBImpl::OnCacheInsertDataChanged(bool value) { - options_.insert_cache_immediately_ = value; -} - -void -DBImpl::OnUseBlasThresholdChanged(int64_t threshold) { - faiss::distance_compute_blas_threshold = threshold; -} - -void -DBImpl::SuspendIfFirst() { - std::lock_guard lock(suspend_build_mutex_); - if (++live_search_num_ == 1) { - LOG_ENGINE_TRACE_ << "live_search_num_: " << live_search_num_; - knowhere::BuilderSuspend(); - } -} - -void -DBImpl::ResumeIfLast() { - std::lock_guard lock(suspend_build_mutex_); - if (--live_search_num_ == 0) { - LOG_ENGINE_TRACE_ << "live_search_num_: " << live_search_num_; - knowhere::BuildResume(); - } -} - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/DBImpl.h b/core/src/db/DBImpl.h deleted file mode 100644 index dc6c8b5f69..0000000000 --- a/core/src/db/DBImpl.h +++ /dev/null @@ -1,374 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config/handler/CacheConfigHandler.h" -#include "config/handler/EngineConfigHandler.h" -#include "db/DB.h" -#include "db/IndexFailedChecker.h" -#include "db/Types.h" -#include "db/insert/MemManager.h" -#include "db/merge/MergeManager.h" -#include "db/meta/FilesHolder.h" -#include "utils/ThreadPool.h" -#include "wal/WalManager.h" - -namespace milvus { -namespace engine { - -namespace meta { -class Meta; -} - -class DBImpl : public DB, public server::CacheConfigHandler, public server::EngineConfigHandler { - public: - explicit DBImpl(const DBOptions& options); - - ~DBImpl(); - - Status - Start() override; - - Status - Stop() override; - - Status - DropAll() override; - - Status - CreateCollection(meta::CollectionSchema& collection_schema) override; - - Status - DropCollection(const std::string& collection_id) override; - - Status - DescribeCollection(meta::CollectionSchema& collection_schema) override; - - Status - HasCollection(const std::string& collection_id, bool& has_or_not) override; - - Status - HasNativeCollection(const std::string& collection_id, bool& has_or_not_) override; - - Status - AllCollections(std::vector& collection_schema_array) override; - - Status - GetCollectionInfo(const std::string& collection_id, std::string& collection_info) override; - - Status - PreloadCollection(const std::shared_ptr& context, const std::string& collection_id, - bool force = false) override; - - Status - ReLoadSegmentsDeletedDocs(const std::string& collection_id, const std::vector& segment_ids) override; - - Status - UpdateCollectionFlag(const std::string& collection_id, int64_t flag) override; - - Status - GetCollectionRowCount(const std::string& collection_id, uint64_t& row_count) override; - - Status - CreatePartition(const std::string& collection_id, const std::string& partition_name, - const std::string& partition_tag) override; - - Status - HasPartition(const std::string& collection_id, const std::string& tag, bool& has_or_not) override; - - Status - DropPartition(const std::string& partition_name) override; - - Status - DropPartitionByTag(const std::string& collection_id, const std::string& partition_tag) override; - - Status - ShowPartitions(const std::string& collection_id, - std::vector& partition_schema_array) override; - - Status - InsertVectors(const std::string& collection_id, const std::string& partition_tag, VectorsData& vectors) override; - - Status - DeleteVector(const std::string& collection_id, IDNumber vector_id) override; - - Status - DeleteVectors(const std::string& collection_id, IDNumbers vector_ids) override; - - Status - Flush(const std::string& collection_id) override; - - Status - Flush() override; - - Status - Compact(const std::shared_ptr& context, const std::string& collection_id, - double threshold = 0.0) override; - - Status - GetVectorsByID(const engine::meta::CollectionSchema& collection, const IDNumbers& id_array, - std::vector& vectors) override; - - Status - GetVectorIDs(const std::string& collection_id, const std::string& segment_id, IDNumbers& vector_ids) override; - - // Status - // Merge(const std::set& collection_ids) override; - - Status - CreateIndex(const std::shared_ptr& context, const std::string& collection_id, - const CollectionIndex& index) override; - - Status - DescribeIndex(const std::string& collection_id, CollectionIndex& index) override; - - Status - DropIndex(const std::string& collection_id) override; - - Status - CreateHybridCollection(meta::CollectionSchema& collection_schema, - meta::hybrid::FieldsSchema& fields_schema) override; - - Status - DescribeHybridCollection(meta::CollectionSchema& collection_schema, - meta::hybrid::FieldsSchema& fields_schema) override; - - Status - InsertEntities(const std::string& collection_name, const std::string& partition_tag, - const std::vector& field_names, engine::Entity& entity, - std::unordered_map& field_types) override; - - Status - HybridQuery(const std::shared_ptr& context, const std::string& collection_id, - const std::vector& partition_tags, context::HybridSearchContextPtr hybrid_search_context, - query::GeneralQueryPtr general_query, - std::unordered_map& attr_type, uint64_t& nq, - ResultIds& result_ids, ResultDistances& result_distances) override; - - Status - QueryByIDs(const std::shared_ptr& context, const std::string& collection_id, - const std::vector& partition_tags, uint64_t k, const milvus::json& extra_params, - const IDNumbers& id_array, ResultIds& result_ids, ResultDistances& result_distances) override; - - Status - Query(const std::shared_ptr& context, const std::string& collection_id, - const std::vector& partition_tags, uint64_t k, const milvus::json& extra_params, - const VectorsData& vectors, ResultIds& result_ids, ResultDistances& result_distances) override; - - Status - QueryByFileID(const std::shared_ptr& context, const std::vector& file_ids, uint64_t k, - const milvus::json& extra_params, const VectorsData& vectors, ResultIds& result_ids, - ResultDistances& result_distances) override; - - Status - Size(uint64_t& result) override; - - protected: - void - OnCacheInsertDataChanged(bool value) override; - - void - OnUseBlasThresholdChanged(int64_t threshold) override; - - private: - Status - QueryAsync(const std::shared_ptr& context, meta::FilesHolder& files_holder, uint64_t k, - const milvus::json& extra_params, const VectorsData& vectors, ResultIds& result_ids, - ResultDistances& result_distances); - - Status - HybridQueryAsync(const std::shared_ptr& context, const std::string& collection_id, - meta::FilesHolder& files_holder, context::HybridSearchContextPtr hybrid_search_context, - query::GeneralQueryPtr general_query, - std::unordered_map& attr_type, uint64_t& nq, - ResultIds& result_ids, ResultDistances& result_distances); - - Status - GetVectorsByIdHelper(const IDNumbers& id_array, std::vector& vectors, - meta::FilesHolder& files_holder); - - void - InternalFlush(const std::string& collection_id = ""); - - void - BackgroundWalThread(); - - void - BackgroundFlushThread(); - - void - BackgroundMetricThread(); - - void - BackgroundIndexThread(); - - void - WaitMergeFileFinish(); - - void - WaitBuildIndexFinish(); - - void - StartMetricTask(); - - void - StartMergeTask(const std::set& merge_collection_ids, bool force_merge_all = false); - - void - BackgroundMerge(std::set collection_ids, bool force_merge_all); - - // Status - // MergeHybridFiles(const std::string& table_id, meta::FilesHolder& files_holder); - - void - StartBuildIndexTask(); - - void - BackgroundBuildIndex(); - - Status - CompactFile(const meta::SegmentSchema& file, double threshold, meta::SegmentsSchema& files_to_update); - - Status - GetFilesToBuildIndex(const std::string& collection_id, const std::vector& file_types, - meta::FilesHolder& files_holder); - - Status - GetPartitionByTag(const std::string& collection_id, const std::string& partition_tag, std::string& partition_name); - - Status - GetPartitionsByTags(const std::string& collection_id, const std::vector& partition_tags, - std::set& partition_name_array); - - Status - UpdateCollectionIndexRecursively(const std::string& collection_id, const CollectionIndex& index); - - Status - WaitCollectionIndexRecursively(const std::shared_ptr& context, const std::string& collection_id, - const CollectionIndex& index); - - Status - DropCollectionIndexRecursively(const std::string& collection_id); - - Status - GetCollectionRowCountRecursively(const std::string& collection_id, uint64_t& row_count); - - Status - ExecWalRecord(const wal::MXLogRecord& record); - - void - SuspendIfFirst(); - - void - ResumeIfLast(); - - private: - DBOptions options_; - - std::atomic initialized_; - - meta::MetaPtr meta_ptr_; - MemManagerPtr mem_mgr_; - MergeManagerPtr merge_mgr_ptr_; - - std::shared_ptr wal_mgr_; - std::thread bg_wal_thread_; - - std::thread bg_flush_thread_; - std::thread bg_metric_thread_; - std::thread bg_index_thread_; - - struct SimpleWaitNotify { - bool notified_ = false; - std::mutex mutex_; - std::condition_variable cv_; - - void - Wait() { - std::unique_lock lck(mutex_); - if (!notified_) { - cv_.wait(lck); - } - notified_ = false; - } - - std::cv_status - Wait_Until(const std::chrono::system_clock::time_point& tm_pint) { - std::unique_lock lck(mutex_); - std::cv_status ret = std::cv_status::timeout; - if (!notified_) { - ret = cv_.wait_until(lck, tm_pint); - } - notified_ = false; - return ret; - } - - std::cv_status - Wait_For(const std::chrono::system_clock::duration& tm_dur) { - std::unique_lock lck(mutex_); - std::cv_status ret = std::cv_status::timeout; - if (!notified_) { - ret = cv_.wait_for(lck, tm_dur); - } - notified_ = false; - return ret; - } - - void - Notify() { - std::unique_lock lck(mutex_); - notified_ = true; - lck.unlock(); - cv_.notify_one(); - } - }; - - SimpleWaitNotify swn_wal_; - SimpleWaitNotify swn_flush_; - SimpleWaitNotify swn_metric_; - SimpleWaitNotify swn_index_; - - SimpleWaitNotify flush_req_swn_; - SimpleWaitNotify index_req_swn_; - - ThreadPool merge_thread_pool_; - std::mutex merge_result_mutex_; - std::list> merge_thread_results_; - - ThreadPool index_thread_pool_; - std::mutex index_result_mutex_; - std::list> index_thread_results_; - - std::mutex build_index_mutex_; - - IndexFailedChecker index_failed_checker_; - - std::mutex flush_merge_compact_mutex_; - - int64_t live_search_num_ = 0; - std::mutex suspend_build_mutex_; -}; // DBImpl - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/IDGenerator.cpp b/core/src/db/IDGenerator.cpp deleted file mode 100644 index 46f751ce40..0000000000 --- a/core/src/db/IDGenerator.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/IDGenerator.h" -#include "utils/Log.h" - -#include -#include -#include -#include -#include - -namespace milvus { -namespace engine { - -IDGenerator::~IDGenerator() = default; - -constexpr size_t SimpleIDGenerator::MAX_IDS_PER_MICRO; - -IDNumber -SimpleIDGenerator::GetNextIDNumber() { - auto now = std::chrono::system_clock::now(); - auto micros = std::chrono::duration_cast(now.time_since_epoch()).count(); - return micros * MAX_IDS_PER_MICRO; -} - -Status -SimpleIDGenerator::NextIDNumbers(size_t n, IDNumbers& ids) { - if (n > MAX_IDS_PER_MICRO) { - NextIDNumbers(n - MAX_IDS_PER_MICRO, ids); - NextIDNumbers(MAX_IDS_PER_MICRO, ids); - return Status::OK(); - } - if (n <= 0) { - return Status::OK(); - } - - auto now = std::chrono::system_clock::now(); - auto micros = std::chrono::duration_cast(now.time_since_epoch()).count(); - micros *= MAX_IDS_PER_MICRO; - - for (size_t pos = 0; pos < n; ++pos) { - ids.push_back(micros + pos); - } - return Status::OK(); -} - -Status -SimpleIDGenerator::GetNextIDNumbers(size_t n, IDNumbers& ids) { - ids.clear(); - NextIDNumbers(n, ids); - - return Status::OK(); -} - -IDNumber -SafeIDGenerator::GetNextIDNumber() { - auto now = std::chrono::system_clock::now(); - auto micros = std::chrono::duration_cast(now.time_since_epoch()).count(); - std::lock_guard lock(mtx_); - if (micros <= time_stamp_ms_) { - time_stamp_ms_ += 1; - } else { - time_stamp_ms_ = micros; - } - return time_stamp_ms_ * MAX_IDS_PER_MICRO; -} - -Status -SafeIDGenerator::GetNextIDNumbers(size_t n, IDNumbers& ids) { - ids.clear(); - std::lock_guard lock(mtx_); - while (n > 0) { - if (n > MAX_IDS_PER_MICRO) { - Status status = NextIDNumbers(MAX_IDS_PER_MICRO, ids); - if (!status.ok()) { - return status; - } - n -= MAX_IDS_PER_MICRO; - } else { - Status status = NextIDNumbers(n, ids); - if (!status.ok()) { - return status; - } - break; - } - } - return Status::OK(); -} - -Status -SafeIDGenerator::NextIDNumbers(size_t n, IDNumbers& ids) { - if (n <= 0 || n > MAX_IDS_PER_MICRO) { - std::string msg = "Invalid ID number: " + std::to_string(n); - LOG_ENGINE_ERROR_ << msg; - return Status(SERVER_UNEXPECTED_ERROR, msg); - } - - auto now = std::chrono::system_clock::now(); - int64_t micros = std::chrono::duration_cast(now.time_since_epoch()).count(); - if (micros <= time_stamp_ms_) { - time_stamp_ms_ += 1; - } else { - time_stamp_ms_ = micros; - } - - int64_t ID_high_part = time_stamp_ms_ * MAX_IDS_PER_MICRO; - - for (size_t pos = 0; pos < n; ++pos) { - ids.push_back(ID_high_part + pos); - } - - return Status::OK(); -} - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/IDGenerator.h b/core/src/db/IDGenerator.h deleted file mode 100644 index fd59fc338f..0000000000 --- a/core/src/db/IDGenerator.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "Types.h" -#include "utils/Status.h" - -#include -#include - -namespace milvus { -namespace engine { - -class IDGenerator { - public: - virtual IDNumber - GetNextIDNumber() = 0; - - virtual Status - GetNextIDNumbers(size_t n, IDNumbers& ids) = 0; - - virtual ~IDGenerator() = 0; -}; // IDGenerator - -class SimpleIDGenerator : public IDGenerator { - public: - ~SimpleIDGenerator() override = default; - - IDNumber - GetNextIDNumber() override; - - Status - GetNextIDNumbers(size_t n, IDNumbers& ids) override; - - private: - Status - NextIDNumbers(size_t n, IDNumbers& ids); - - static constexpr size_t MAX_IDS_PER_MICRO = 1000; -}; // SimpleIDGenerator - -class SafeIDGenerator : public IDGenerator { - public: - static SafeIDGenerator& - GetInstance() { - static SafeIDGenerator instance; - return instance; - } - - ~SafeIDGenerator() override = default; - - IDNumber - GetNextIDNumber() override; - - Status - GetNextIDNumbers(size_t n, IDNumbers& ids) override; - - private: - SafeIDGenerator() = default; - - Status - NextIDNumbers(size_t n, IDNumbers& ids); - - static constexpr size_t MAX_IDS_PER_MICRO = 1000; - - std::mutex mtx_; - int64_t time_stamp_ms_ = 0; -}; - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/IndexFailedChecker.cpp b/core/src/db/IndexFailedChecker.cpp deleted file mode 100644 index 016e6399c1..0000000000 --- a/core/src/db/IndexFailedChecker.cpp +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include - -#include "db/IndexFailedChecker.h" - -namespace milvus { -namespace engine { - -constexpr uint64_t INDEX_FAILED_RETRY_TIME = 1; - -Status -IndexFailedChecker::CleanFailedIndexFileOfCollection(const std::string& collection_id) { - std::lock_guard lck(mutex_); - index_failed_files_.erase(collection_id); // rebuild failed index files for this collection - - return Status::OK(); -} - -Status -IndexFailedChecker::GetErrMsgForCollection(const std::string& collection_id, std::string& err_msg) { - std::lock_guard lck(mutex_); - auto iter = index_failed_files_.find(collection_id); - if (iter != index_failed_files_.end()) { - err_msg = iter->second.begin()->second[0]; - } - - return Status::OK(); -} - -Status -IndexFailedChecker::MarkFailedIndexFile(const meta::SegmentSchema& file, const std::string& err_msg) { - std::lock_guard lck(mutex_); - - auto iter = index_failed_files_.find(file.collection_id_); - if (iter == index_failed_files_.end()) { - File2ErrArray failed_files; - failed_files.insert(std::make_pair(file.file_id_, std::vector(1, err_msg))); - index_failed_files_.insert(std::make_pair(file.collection_id_, failed_files)); - } else { - auto it_failed_files = iter->second.find(file.file_id_); - if (it_failed_files != iter->second.end()) { - it_failed_files->second.push_back(err_msg); - } else { - iter->second.insert(std::make_pair(file.file_id_, std::vector(1, err_msg))); - } - } - - return Status::OK(); -} - -Status -IndexFailedChecker::MarkSucceedIndexFile(const meta::SegmentSchema& file) { - std::lock_guard lck(mutex_); - - auto iter = index_failed_files_.find(file.collection_id_); - if (iter != index_failed_files_.end()) { - iter->second.erase(file.file_id_); - if (iter->second.empty()) { - index_failed_files_.erase(file.collection_id_); - } - } - - return Status::OK(); -} - -bool -IndexFailedChecker::IsFailedIndexFile(const meta::SegmentSchema& file) { - std::lock_guard lck(mutex_); - - auto it_failed_files = index_failed_files_.find(file.collection_id_); - if (it_failed_files != index_failed_files_.end()) { - auto it_failed_file = it_failed_files->second.find(file.file_id_); - if (it_failed_file != it_failed_files->second.end()) { - if (it_failed_file->second.size() >= INDEX_FAILED_RETRY_TIME) { - return true; - } - } - } - - return false; -} - -Status -IndexFailedChecker::IgnoreFailedIndexFiles(meta::SegmentsSchema& table_files) { - std::lock_guard lck(mutex_); - - // there could be some failed files belong to different collection. - // some files may has failed for several times, no need to build index for these files. - // thus we can avoid dead circle for build index operation - for (auto it_file = table_files.begin(); it_file != table_files.end();) { - auto it_failed_files = index_failed_files_.find((*it_file).collection_id_); - if (it_failed_files != index_failed_files_.end()) { - auto it_failed_file = it_failed_files->second.find((*it_file).file_id_); - if (it_failed_file != it_failed_files->second.end()) { - if (it_failed_file->second.size() >= INDEX_FAILED_RETRY_TIME) { - it_file = table_files.erase(it_file); - continue; - } - } - } - - ++it_file; - } - - return Status::OK(); -} - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/IndexFailedChecker.h b/core/src/db/IndexFailedChecker.h deleted file mode 100644 index 87e8a12085..0000000000 --- a/core/src/db/IndexFailedChecker.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "db/Types.h" -#include "meta/Meta.h" -#include "utils/Status.h" - -#include -#include -#include - -namespace milvus { -namespace engine { - -class IndexFailedChecker { - public: - Status - CleanFailedIndexFileOfCollection(const std::string& collection_id); - - Status - GetErrMsgForCollection(const std::string& collection_id, std::string& err_msg); - - Status - MarkFailedIndexFile(const meta::SegmentSchema& file, const std::string& err_msg); - - Status - MarkSucceedIndexFile(const meta::SegmentSchema& file); - - bool - IsFailedIndexFile(const meta::SegmentSchema& file); - - Status - IgnoreFailedIndexFiles(meta::SegmentsSchema& table_files); - - private: - std::mutex mutex_; - Table2FileErr index_failed_files_; // collection id mapping to (file id mapping to failed times) -}; - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/Options.cpp b/core/src/db/Options.cpp deleted file mode 100644 index 9e82772fad..0000000000 --- a/core/src/db/Options.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/Options.h" -#include -#include -#include "utils/Exception.h" -#include "utils/Log.h" - -#include -#include -#include - -namespace milvus { -namespace engine { - -const char* ARCHIVE_CONF_DISK = "disk"; -const char* ARCHIVE_CONF_DAYS = "days"; -const char* DEFAULT_PARTITON_TAG = "_default"; - -ArchiveConf::ArchiveConf(const std::string& type, const std::string& criterias) { - ParseType(type); - ParseCritirias(criterias); -} - -void -ArchiveConf::SetCriterias(const ArchiveConf::CriteriaT& criterial) { - for (auto& pair : criterial) { - criterias_[pair.first] = pair.second; - } -} - -void -ArchiveConf::ParseCritirias(const std::string& criterias) { - std::stringstream ss(criterias); - std::vector tokens; - - boost::algorithm::split(tokens, criterias, boost::is_any_of(";")); - - fiu_do_on("ArchiveConf.ParseCritirias.empty_tokens", tokens.clear()); - if (tokens.size() == 0) { - return; - } - - for (auto& token : tokens) { - if (token.empty()) { - continue; - } - - std::vector kv; - boost::algorithm::split(kv, token, boost::is_any_of(":")); - if (kv.size() != 2) { - LOG_ENGINE_WARNING_ << "Invalid ArchiveConf Criterias: " << token << " Ignore!"; - continue; - } - if (kv[0] != "disk" && kv[0] != "days") { - LOG_ENGINE_WARNING_ << "Invalid ArchiveConf Criterias: " << token << " Ignore!"; - continue; - } - try { - fiu_do_on("ArchiveConf.ParseCritirias.OptionsParseCritiriasOutOfRange", - kv[1] = std::to_string(std::numeric_limits::max() + 1UL)); - auto value = std::stoi(kv[1]); - criterias_[kv[0]] = value; - } catch (std::out_of_range&) { - std::string msg = "Out of range: '" + kv[1] + "'"; - LOG_ENGINE_ERROR_ << msg; - throw InvalidArgumentException(msg); - } catch (...) { - std::string msg = "Invalid argument: '" + kv[1] + "'"; - LOG_ENGINE_ERROR_ << msg; - throw InvalidArgumentException(msg); - } - } -} - -void -ArchiveConf::ParseType(const std::string& type) { - if (type != "delete" && type != "swap") { - std::string msg = "Invalid argument: type='" + type + "'"; - throw InvalidArgumentException(msg); - } - type_ = type; -} - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/Options.h b/core/src/db/Options.h deleted file mode 100644 index 91e7f91b11..0000000000 --- a/core/src/db/Options.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "Constants.h" - -#include -#include -#include -#include - -namespace milvus { -namespace engine { - -class Env; - -extern const char* ARCHIVE_CONF_DISK; -extern const char* ARCHIVE_CONF_DAYS; -extern const char* DEFAULT_PARTITON_TAG; - -struct ArchiveConf { - using CriteriaT = std::map; - - explicit ArchiveConf(const std::string& type, const std::string& criterias = std::string()); - - const std::string& - GetType() const { - return type_; - } - - const CriteriaT - GetCriterias() const { - return criterias_; - } - - void - SetCriterias(const ArchiveConf::CriteriaT& criterial); - - private: - void - ParseCritirias(const std::string& criterias); - void - ParseType(const std::string& type); - - std::string type_; - CriteriaT criterias_; -}; - -struct DBMetaOptions { - std::string path_; - std::vector slave_paths_; - std::string backend_uri_; - ArchiveConf archive_conf_ = ArchiveConf("delete"); -}; // DBMetaOptions - -struct DBOptions { - typedef enum { SINGLE = 0, CLUSTER_READONLY, CLUSTER_WRITABLE } MODE; - - uint16_t merge_trigger_number_ = 2; - DBMetaOptions meta_; - int mode_ = MODE::SINGLE; - - size_t insert_buffer_size_ = 4 * GB; - bool insert_cache_immediately_ = false; - - int64_t auto_flush_interval_ = 1; - int64_t file_cleanup_timeout_ = 10; - - bool metric_enable_ = false; - - // wal relative configurations - bool wal_enable_ = true; - bool recovery_error_ignore_ = true; - int64_t buffer_size_ = 256; - std::string mxlog_path_ = "/tmp/milvus/wal/"; -}; // Options - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/Types.h b/core/src/db/Types.h deleted file mode 100644 index c162353017..0000000000 --- a/core/src/db/Types.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "db/engine/ExecutionEngine.h" -#include "segment/Types.h" -#include "utils/Json.h" - -namespace milvus { -namespace engine { - -typedef segment::doc_id_t IDNumber; -typedef IDNumber* IDNumberPtr; -typedef std::vector IDNumbers; - -typedef std::vector ResultIds; -typedef std::vector ResultDistances; - -struct CollectionIndex { - int32_t engine_type_ = (int)EngineType::FAISS_IDMAP; - int32_t metric_type_ = (int)MetricType::L2; - milvus::json extra_params_ = {{"nlist", 2048}}; -}; - -struct VectorsData { - uint64_t vector_count_ = 0; - std::vector float_data_; - std::vector binary_data_; - IDNumbers id_array_; -}; - -struct Entity { - uint64_t entity_count_ = 0; - std::vector attr_value_; - std::unordered_map> attr_data_; - std::unordered_map vector_data_; - IDNumbers id_array_; -}; - -using File2ErrArray = std::map>; -using Table2FileErr = std::map; - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/Utils.cpp b/core/src/db/Utils.cpp deleted file mode 100644 index d37a6f83fc..0000000000 --- a/core/src/db/Utils.cpp +++ /dev/null @@ -1,336 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/Utils.h" - -#include - -#include -#include -#include -#include -#include -#include - -#include "config/Config.h" -//#include "storage/s3/S3ClientWrapper.h" -#include "utils/CommonUtil.h" -#include "utils/Log.h" - -#include - -namespace milvus { -namespace engine { -namespace utils { - -namespace { - -const char* TABLES_FOLDER = "/tables/"; - -uint64_t index_file_counter = 0; -std::mutex index_file_counter_mutex; - -static std::string -ConstructParentFolder(const std::string& db_path, const meta::SegmentSchema& table_file) { - std::string table_path = db_path + TABLES_FOLDER + table_file.collection_id_; - std::string partition_path = table_path + "/" + table_file.segment_id_; - return partition_path; -} - -static std::string -GetCollectionFileParentFolder(const DBMetaOptions& options, const meta::SegmentSchema& table_file) { - uint64_t path_count = options.slave_paths_.size() + 1; - std::string target_path = options.path_; - uint64_t index = 0; - - if (meta::SegmentSchema::NEW_INDEX == table_file.file_type_) { - // index file is large file and to be persisted permanently - // we need to distribute index files to each db_path averagely - // round robin according to a file counter - std::lock_guard lock(index_file_counter_mutex); - index = index_file_counter % path_count; - ++index_file_counter; - } else { - // for other type files, they could be merged or deleted - // so we round robin according to their file id - index = table_file.id_ % path_count; - } - - if (index > 0) { - target_path = options.slave_paths_[index - 1]; - } - - return ConstructParentFolder(target_path, table_file); -} - -} // namespace - -int64_t -GetMicroSecTimeStamp() { - auto now = std::chrono::system_clock::now(); - auto micros = std::chrono::duration_cast(now.time_since_epoch()).count(); - - return micros; -} - -Status -CreateCollectionPath(const DBMetaOptions& options, const std::string& collection_id) { - std::string db_path = options.path_; - std::string table_path = db_path + TABLES_FOLDER + collection_id; - auto status = server::CommonUtil::CreateDirectory(table_path); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << status.message(); - return status; - } - - for (auto& path : options.slave_paths_) { - table_path = path + TABLES_FOLDER + collection_id; - status = server::CommonUtil::CreateDirectory(table_path); - fiu_do_on("CreateCollectionPath.creat_slave_path", status = Status(DB_INVALID_PATH, "")); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << status.message(); - return status; - } - } - - return Status::OK(); -} - -Status -DeleteCollectionPath(const DBMetaOptions& options, const std::string& collection_id, bool force) { - std::vector paths = options.slave_paths_; - paths.push_back(options.path_); - - for (auto& path : paths) { - std::string table_path = path + TABLES_FOLDER + collection_id; - if (force) { - boost::filesystem::remove_all(table_path); - LOG_ENGINE_DEBUG_ << "Remove collection folder: " << table_path; - } else if (boost::filesystem::exists(table_path) && boost::filesystem::is_empty(table_path)) { - boost::filesystem::remove_all(table_path); - LOG_ENGINE_DEBUG_ << "Remove collection folder: " << table_path; - } - } - - // bool s3_enable = false; - // server::Config& config = server::Config::GetInstance(); - // config.GetStorageConfigS3Enable(s3_enable); - - // if (s3_enable) { - // std::string table_path = options.path_ + TABLES_FOLDER + collection_id; - - // auto& storage_inst = milvus::storage::S3ClientWrapper::GetInstance(); - // Status stat = storage_inst.DeleteObjects(table_path); - // if (!stat.ok()) { - // return stat; - // } - // } - - return Status::OK(); -} - -Status -CreateCollectionFilePath(const DBMetaOptions& options, meta::SegmentSchema& table_file) { - std::string parent_path = GetCollectionFileParentFolder(options, table_file); - - auto status = server::CommonUtil::CreateDirectory(parent_path); - fiu_do_on("CreateCollectionFilePath.fail_create", status = Status(DB_INVALID_PATH, "")); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << status.message(); - return status; - } - - table_file.location_ = parent_path + "/" + table_file.file_id_; - - return Status::OK(); -} - -Status -GetCollectionFilePath(const DBMetaOptions& options, meta::SegmentSchema& table_file) { - std::string parent_path = ConstructParentFolder(options.path_, table_file); - std::string file_path = parent_path + "/" + table_file.file_id_; - - // bool s3_enable = false; - // server::Config& config = server::Config::GetInstance(); - // config.GetStorageConfigS3Enable(s3_enable); - // fiu_do_on("GetCollectionFilePath.enable_s3", s3_enable = true); - // if (s3_enable) { - // /* need not check file existence */ - // table_file.location_ = file_path; - // return Status::OK(); - // } - - if (boost::filesystem::exists(parent_path)) { - table_file.location_ = file_path; - return Status::OK(); - } - - for (auto& path : options.slave_paths_) { - parent_path = ConstructParentFolder(path, table_file); - file_path = parent_path + "/" + table_file.file_id_; - if (boost::filesystem::exists(parent_path)) { - table_file.location_ = file_path; - return Status::OK(); - } - } - - std::string msg = "Collection file doesn't exist: " + file_path; - if (table_file.file_size_ > 0) { // no need to pop error for empty file - LOG_ENGINE_ERROR_ << msg << " in path: " << options.path_ << " for collection: " << table_file.collection_id_; - } - - return Status(DB_ERROR, msg); -} - -Status -DeleteCollectionFilePath(const DBMetaOptions& options, meta::SegmentSchema& table_file) { - utils::GetCollectionFilePath(options, table_file); - boost::filesystem::remove(table_file.location_); - return Status::OK(); -} - -Status -DeleteSegment(const DBMetaOptions& options, meta::SegmentSchema& table_file) { - utils::GetCollectionFilePath(options, table_file); - std::string segment_dir; - GetParentPath(table_file.location_, segment_dir); - boost::filesystem::remove_all(segment_dir); - return Status::OK(); -} - -Status -GetParentPath(const std::string& path, std::string& parent_path) { - boost::filesystem::path p(path); - parent_path = p.parent_path().string(); - return Status::OK(); -} - -bool -IsSameIndex(const CollectionIndex& index1, const CollectionIndex& index2) { - return index1.engine_type_ == index2.engine_type_ && index1.extra_params_ == index2.extra_params_ && - index1.metric_type_ == index2.metric_type_; -} - -bool -IsRawIndexType(int32_t type) { - return (type == (int32_t)EngineType::FAISS_IDMAP) || (type == (int32_t)EngineType::FAISS_BIN_IDMAP); -} - -bool -IsBinaryMetricType(int32_t metric_type) { - return (metric_type == (int32_t)engine::MetricType::HAMMING) || - (metric_type == (int32_t)engine::MetricType::JACCARD) || - (metric_type == (int32_t)engine::MetricType::SUBSTRUCTURE) || - (metric_type == (int32_t)engine::MetricType::SUPERSTRUCTURE) || - (metric_type == (int32_t)engine::MetricType::TANIMOTO); -} - -meta::DateT -GetDate(const std::time_t& t, int day_delta) { - struct tm ltm; - localtime_r(&t, <m); - if (day_delta > 0) { - do { - ++ltm.tm_mday; - --day_delta; - } while (day_delta > 0); - mktime(<m); - } else if (day_delta < 0) { - do { - --ltm.tm_mday; - ++day_delta; - } while (day_delta < 0); - mktime(<m); - } - return ltm.tm_year * 10000 + ltm.tm_mon * 100 + ltm.tm_mday; -} - -meta::DateT -GetDateWithDelta(int day_delta) { - return GetDate(std::time(nullptr), day_delta); -} - -meta::DateT -GetDate() { - return GetDate(std::time(nullptr), 0); -} - -// URI format: dialect://username:password@host:port/database -Status -ParseMetaUri(const std::string& uri, MetaUriInfo& info) { - std::string dialect_regex = "(.*)"; - std::string username_tegex = "(.*)"; - std::string password_regex = "(.*)"; - std::string host_regex = "(.*)"; - std::string port_regex = "(.*)"; - std::string db_name_regex = "(.*)"; - std::string uri_regex_str = dialect_regex + "\\:\\/\\/" + username_tegex + "\\:" + password_regex + "\\@" + - host_regex + "\\:" + port_regex + "\\/" + db_name_regex; - - std::regex uri_regex(uri_regex_str); - std::smatch pieces_match; - - if (std::regex_match(uri, pieces_match, uri_regex)) { - info.dialect_ = pieces_match[1].str(); - info.username_ = pieces_match[2].str(); - info.password_ = pieces_match[3].str(); - info.host_ = pieces_match[4].str(); - info.port_ = pieces_match[5].str(); - info.db_name_ = pieces_match[6].str(); - - // TODO(myh): verify host, port... - } else { - return Status(DB_INVALID_META_URI, "Invalid meta uri: " + uri); - } - - return Status::OK(); -} - -std::string -GetIndexName(int32_t index_type) { - static std::map index_type_name = { - {(int32_t)engine::EngineType::FAISS_IDMAP, "IDMAP"}, - {(int32_t)engine::EngineType::FAISS_IVFFLAT, "IVFFLAT"}, - {(int32_t)engine::EngineType::FAISS_IVFSQ8, "IVFSQ8"}, - {(int32_t)engine::EngineType::NSG_MIX, "NSG"}, - {(int32_t)engine::EngineType::FAISS_IVFSQ8H, "IVFSQ8H"}, - {(int32_t)engine::EngineType::FAISS_PQ, "PQ"}, - {(int32_t)engine::EngineType::SPTAG_KDT, "KDT"}, - {(int32_t)engine::EngineType::SPTAG_BKT, "BKT"}, - {(int32_t)engine::EngineType::FAISS_BIN_IDMAP, "IDMAP"}, - {(int32_t)engine::EngineType::FAISS_BIN_IVFFLAT, "IVFFLAT"}, - {(int32_t)engine::EngineType::HNSW, "HNSW"}, - {(int32_t)engine::EngineType::ANNOY, "ANNOY"}}; - - if (index_type_name.find(index_type) == index_type_name.end()) { - return "Unknow"; - } - - return index_type_name[index_type]; -} - -void -SendExitSignal() { - LOG_SERVER_INFO_ << "Send SIGUSR2 signal to exit"; - pid_t pid = getpid(); - kill(pid, SIGUSR2); -} - -void -ExitOnWriteError(Status& status) { - if (status.code() == SERVER_WRITE_ERROR) { - utils::SendExitSignal(); - } -} - -} // namespace utils -} // namespace engine -} // namespace milvus diff --git a/core/src/db/Utils.h b/core/src/db/Utils.h deleted file mode 100644 index b701ad41eb..0000000000 --- a/core/src/db/Utils.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -#include "Options.h" -#include "db/Types.h" -#include "db/meta/MetaTypes.h" - -namespace milvus { -namespace engine { -namespace utils { - -int64_t -GetMicroSecTimeStamp(); - -Status -CreateCollectionPath(const DBMetaOptions& options, const std::string& collection_id); -Status -DeleteCollectionPath(const DBMetaOptions& options, const std::string& collection_id, bool force = true); - -Status -CreateCollectionFilePath(const DBMetaOptions& options, meta::SegmentSchema& table_file); -Status -GetCollectionFilePath(const DBMetaOptions& options, meta::SegmentSchema& table_file); -Status -DeleteCollectionFilePath(const DBMetaOptions& options, meta::SegmentSchema& table_file); -Status -DeleteSegment(const DBMetaOptions& options, meta::SegmentSchema& table_file); - -Status -GetParentPath(const std::string& path, std::string& parent_path); - -bool -IsSameIndex(const CollectionIndex& index1, const CollectionIndex& index2); - -bool -IsRawIndexType(int32_t type); - -bool -IsBinaryMetricType(int32_t metric_type); - -meta::DateT -GetDate(const std::time_t& t, int day_delta = 0); -meta::DateT -GetDate(); -meta::DateT -GetDateWithDelta(int day_delta); - -struct MetaUriInfo { - std::string dialect_; - std::string username_; - std::string password_; - std::string host_; - std::string port_; - std::string db_name_; -}; - -Status -ParseMetaUri(const std::string& uri, MetaUriInfo& info); - -std::string -GetIndexName(int32_t index_type); - -void -SendExitSignal(); - -void -ExitOnWriteError(Status& status); - -} // namespace utils -} // namespace engine -} // namespace milvus diff --git a/core/src/db/engine/EngineFactory.cpp b/core/src/db/engine/EngineFactory.cpp deleted file mode 100644 index f275439d55..0000000000 --- a/core/src/db/engine/EngineFactory.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/engine/EngineFactory.h" -#include "db/engine/ExecutionEngineImpl.h" -#include "utils/Log.h" - -#include - -namespace milvus { -namespace engine { - -ExecutionEnginePtr -EngineFactory::Build(uint16_t dimension, const std::string& location, EngineType index_type, MetricType metric_type, - const milvus::json& index_params) { - if (index_type == EngineType::INVALID) { - LOG_ENGINE_ERROR_ << "Unsupported engine type"; - return nullptr; - } - - LOG_ENGINE_DEBUG_ << "EngineFactory index type: " << (int)index_type; - ExecutionEnginePtr execution_engine_ptr = - std::make_shared(dimension, location, index_type, metric_type, index_params); - - execution_engine_ptr->Init(); - return execution_engine_ptr; -} - -// ExecutionEnginePtr -// EngineFactory::Build(uint16_t dimension, -// const std::string& location, -// EngineType index_type, -// MetricType metric_type, -// std::unordered_map& attr_type, -// const milvus::json& index_params) { -// -// if (index_type == EngineType::INVALID) { -// ENGINE_LOG_ERROR << "Unsupported engine type"; -// return nullptr; -// } -// -// ENGINE_LOG_DEBUG << "EngineFactory index type: " << (int)index_type; -// ExecutionEnginePtr execution_engine_ptr = -// std::make_shared(dimension, location, index_type, metric_type, attr_type, index_params); -// -// execution_engine_ptr->Init(); -// return execution_engine_ptr; -//} - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/engine/EngineFactory.h b/core/src/db/engine/EngineFactory.h deleted file mode 100644 index 14334c1522..0000000000 --- a/core/src/db/engine/EngineFactory.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "ExecutionEngine.h" -#include "utils/Json.h" -#include "utils/Status.h" - -#include - -namespace milvus { -namespace engine { - -class EngineFactory { - public: - static ExecutionEnginePtr - Build(uint16_t dimension, const std::string& location, EngineType index_type, MetricType metric_type, - const milvus::json& index_params); - - // static ExecutionEnginePtr - // Build(uint16_t dimension, - // const std::string& location, - // EngineType index_type, - // MetricType metric_type, - // std::unordered_map& attr_type, - // const milvus::json& index_params); -}; - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/engine/ExecutionEngine.h b/core/src/db/engine/ExecutionEngine.h deleted file mode 100644 index 1bc2a555f6..0000000000 --- a/core/src/db/engine/ExecutionEngine.h +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include - -#include - -#include "query/GeneralQuery.h" -#include "utils/Json.h" -#include "utils/Status.h" - -namespace milvus { -namespace engine { - -// TODO(linxj): replace with VecIndex::IndexType -enum class EngineType { - INVALID = 0, - FAISS_IDMAP = 1, - FAISS_IVFFLAT, - FAISS_IVFSQ8, - NSG_MIX, - FAISS_IVFSQ8H, - FAISS_PQ, - SPTAG_KDT, - SPTAG_BKT, - FAISS_BIN_IDMAP, - FAISS_BIN_IVFFLAT, - HNSW, - ANNOY, - MAX_VALUE = ANNOY, -}; - -enum class MetricType { - L2 = 1, // Euclidean Distance - IP = 2, // Cosine Similarity - HAMMING = 3, // Hamming Distance - JACCARD = 4, // Jaccard Distance - TANIMOTO = 5, // Tanimoto Distance - SUBSTRUCTURE = 6, // Substructure Distance - SUPERSTRUCTURE = 7, // Superstructure Distance - MAX_VALUE = SUPERSTRUCTURE -}; - -enum class DataType { - INT8 = 1, - INT16 = 2, - INT32 = 3, - INT64 = 4, - - STRING = 20, - - BOOL = 30, - - FLOAT = 40, - DOUBLE = 41, - - VECTOR = 100, - UNKNOWN = 9999, -}; - -class ExecutionEngine { - public: - virtual Status - AddWithIds(int64_t n, const float* xdata, const int64_t* xids) = 0; - - virtual Status - AddWithIds(int64_t n, const uint8_t* xdata, const int64_t* xids) = 0; - - virtual size_t - Count() const = 0; - - virtual size_t - Dimension() const = 0; - - virtual size_t - Size() const = 0; - - virtual Status - Serialize() = 0; - - virtual Status - Load(bool to_cache = true) = 0; - - virtual Status - CopyToGpu(uint64_t device_id, bool hybrid) = 0; - - virtual Status - CopyToIndexFileToGpu(uint64_t device_id) = 0; - - virtual Status - CopyToCpu() = 0; - - // virtual std::shared_ptr - // Clone() = 0; - - // virtual Status - // Merge(const std::string& location) = 0; - -#if 0 - virtual Status - GetVectorByID(const int64_t id, float* vector, bool hybrid) = 0; - - virtual Status - GetVectorByID(const int64_t id, uint8_t* vector, bool hybrid) = 0; -#endif - - virtual Status - ExecBinaryQuery(query::GeneralQueryPtr general_query, faiss::ConcurrentBitsetPtr bitset, - std::unordered_map& attr_type, uint64_t& nq, uint64_t& topk, - std::vector& distances, std::vector& labels) = 0; - - virtual Status - Search(int64_t n, const float* data, int64_t k, const milvus::json& extra_params, float* distances, int64_t* labels, - bool hybrid) = 0; - - virtual Status - Search(int64_t n, const uint8_t* data, int64_t k, const milvus::json& extra_params, float* distances, - int64_t* labels, bool hybrid) = 0; - - virtual std::shared_ptr - BuildIndex(const std::string& location, EngineType engine_type) = 0; - - virtual Status - Cache() = 0; - - virtual Status - Init() = 0; - - virtual EngineType - IndexEngineType() const = 0; - - virtual MetricType - IndexMetricType() const = 0; - - virtual std::string - GetLocation() const = 0; -}; - -using ExecutionEnginePtr = std::shared_ptr; - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/engine/ExecutionEngineImpl.cpp b/core/src/db/engine/ExecutionEngineImpl.cpp deleted file mode 100644 index fee486c532..0000000000 --- a/core/src/db/engine/ExecutionEngineImpl.cpp +++ /dev/null @@ -1,1320 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/engine/ExecutionEngineImpl.h" - -#include -#include - -#include -#include -#include -#include - -#include "cache/CpuCacheMgr.h" -#include "cache/GpuCacheMgr.h" -#include "config/Config.h" -#include "db/Utils.h" -#include "knowhere/common/Config.h" -#include "knowhere/index/vector_index/ConfAdapter.h" -#include "knowhere/index/vector_index/ConfAdapterMgr.h" -#include "knowhere/index/vector_index/IndexBinaryIDMAP.h" -#include "knowhere/index/vector_index/IndexIDMAP.h" -#include "knowhere/index/vector_index/VecIndex.h" -#include "knowhere/index/vector_index/VecIndexFactory.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" -#ifdef MILVUS_GPU_VERSION -#include "knowhere/index/vector_index/gpu/GPUIndex.h" -#include "knowhere/index/vector_index/gpu/IndexIVFSQHybrid.h" -#include "knowhere/index/vector_index/gpu/Quantizer.h" -#include "knowhere/index/vector_index/helpers/Cloner.h" -#endif -#include "knowhere/index/vector_index/helpers/IndexParameter.h" -#include "metrics/Metrics.h" -#include "scheduler/Utils.h" -#include "segment/SegmentReader.h" -#include "segment/SegmentWriter.h" -#include "utils/CommonUtil.h" -#include "utils/Error.h" -#include "utils/Exception.h" -#include "utils/Log.h" -#include "utils/Status.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -namespace milvus { -namespace engine { - -namespace { - -Status -MappingMetricType(MetricType metric_type, milvus::json& conf) { - switch (metric_type) { - case MetricType::IP: - conf[knowhere::Metric::TYPE] = knowhere::Metric::IP; - break; - case MetricType::L2: - conf[knowhere::Metric::TYPE] = knowhere::Metric::L2; - break; - case MetricType::HAMMING: - conf[knowhere::Metric::TYPE] = knowhere::Metric::HAMMING; - break; - case MetricType::JACCARD: - conf[knowhere::Metric::TYPE] = knowhere::Metric::JACCARD; - break; - case MetricType::TANIMOTO: - conf[knowhere::Metric::TYPE] = knowhere::Metric::TANIMOTO; - break; - case MetricType::SUBSTRUCTURE: - conf[knowhere::Metric::TYPE] = knowhere::Metric::SUBSTRUCTURE; - break; - case MetricType::SUPERSTRUCTURE: - conf[knowhere::Metric::TYPE] = knowhere::Metric::SUPERSTRUCTURE; - break; - default: - return Status(DB_ERROR, "Unsupported metric type"); - } - - return Status::OK(); -} - -bool -IsBinaryIndexType(knowhere::IndexType type) { - return type == knowhere::IndexEnum::INDEX_FAISS_BIN_IDMAP || type == knowhere::IndexEnum::INDEX_FAISS_BIN_IVFFLAT; -} - -} // namespace - -#ifdef MILVUS_GPU_VERSION -class CachedQuantizer : public cache::DataObj { - public: - explicit CachedQuantizer(knowhere::QuantizerPtr data) : data_(std::move(data)) { - } - - knowhere::QuantizerPtr - Data() { - return data_; - } - - int64_t - Size() override { - return data_->size; - } - - private: - knowhere::QuantizerPtr data_; -}; -#endif - -ExecutionEngineImpl::ExecutionEngineImpl(uint16_t dimension, const std::string& location, EngineType index_type, - MetricType metric_type, const milvus::json& index_params) - : location_(location), - dim_(dimension), - index_type_(index_type), - metric_type_(metric_type), - index_params_(index_params) { - EngineType tmp_index_type = - utils::IsBinaryMetricType((int32_t)metric_type) ? EngineType::FAISS_BIN_IDMAP : EngineType::FAISS_IDMAP; - index_ = CreatetVecIndex(tmp_index_type); - if (!index_) { - throw Exception(DB_ERROR, "Unsupported index type"); - } - - milvus::json conf = index_params; - conf[knowhere::meta::DEVICEID] = gpu_num_; - conf[knowhere::meta::DIM] = dimension; - MappingMetricType(metric_type, conf); - LOG_ENGINE_DEBUG_ << "Index params: " << conf.dump(); - auto adapter = knowhere::AdapterMgr::GetInstance().GetAdapter(index_->index_type()); - if (!adapter->CheckTrain(conf, index_->index_mode())) { - throw Exception(DB_ERROR, "Illegal index params"); - } - - fiu_do_on("ExecutionEngineImpl.throw_exception", throw Exception(DB_ERROR, "")); - if (auto bf_index = std::dynamic_pointer_cast(index_)) { - bf_index->Train(knowhere::DatasetPtr(), conf); - } else if (auto bf_bin_index = std::dynamic_pointer_cast(index_)) { - bf_bin_index->Train(knowhere::DatasetPtr(), conf); - } -} - -ExecutionEngineImpl::ExecutionEngineImpl(knowhere::VecIndexPtr index, const std::string& location, - EngineType index_type, MetricType metric_type, - const milvus::json& index_params) - : index_(std::move(index)), - location_(location), - index_type_(index_type), - metric_type_(metric_type), - index_params_(index_params) { -} - -knowhere::VecIndexPtr -ExecutionEngineImpl::CreatetVecIndex(EngineType type) { - knowhere::VecIndexFactory& vec_index_factory = knowhere::VecIndexFactory::GetInstance(); - knowhere::IndexMode mode = knowhere::IndexMode::MODE_CPU; -#ifdef MILVUS_GPU_VERSION - server::Config& config = server::Config::GetInstance(); - bool gpu_resource_enable = true; - config.GetGpuResourceConfigEnable(gpu_resource_enable); - fiu_do_on("ExecutionEngineImpl.CreatetVecIndex.gpu_res_disabled", gpu_resource_enable = false); - if (gpu_resource_enable) { - mode = knowhere::IndexMode::MODE_GPU; - } -#endif - - fiu_do_on("ExecutionEngineImpl.CreateVecIndex.invalid_type", type = EngineType::INVALID); - knowhere::VecIndexPtr index = nullptr; - switch (type) { - case EngineType::FAISS_IDMAP: { - index = vec_index_factory.CreateVecIndex(knowhere::IndexEnum::INDEX_FAISS_IDMAP, mode); - break; - } - case EngineType::FAISS_IVFFLAT: { - index = vec_index_factory.CreateVecIndex(knowhere::IndexEnum::INDEX_FAISS_IVFFLAT, mode); - break; - } - case EngineType::FAISS_PQ: { - index = vec_index_factory.CreateVecIndex(knowhere::IndexEnum::INDEX_FAISS_IVFPQ, mode); - break; - } - case EngineType::FAISS_IVFSQ8: { - index = vec_index_factory.CreateVecIndex(knowhere::IndexEnum::INDEX_FAISS_IVFSQ8, mode); - break; - } -#ifdef MILVUS_GPU_VERSION - case EngineType::FAISS_IVFSQ8H: { - index = vec_index_factory.CreateVecIndex(knowhere::IndexEnum::INDEX_FAISS_IVFSQ8H, mode); - break; - } -#endif - case EngineType::FAISS_BIN_IDMAP: { - index = vec_index_factory.CreateVecIndex(knowhere::IndexEnum::INDEX_FAISS_BIN_IDMAP, mode); - break; - } - case EngineType::FAISS_BIN_IVFFLAT: { - index = vec_index_factory.CreateVecIndex(knowhere::IndexEnum::INDEX_FAISS_BIN_IVFFLAT, mode); - break; - } - case EngineType::NSG_MIX: { - index = vec_index_factory.CreateVecIndex(knowhere::IndexEnum::INDEX_NSG, mode); - break; - } - case EngineType::SPTAG_KDT: { - index = vec_index_factory.CreateVecIndex(knowhere::IndexEnum::INDEX_SPTAG_KDT_RNT, mode); - break; - } - case EngineType::SPTAG_BKT: { - index = vec_index_factory.CreateVecIndex(knowhere::IndexEnum::INDEX_SPTAG_BKT_RNT, mode); - break; - } - case EngineType::HNSW: { - index = vec_index_factory.CreateVecIndex(knowhere::IndexEnum::INDEX_HNSW, mode); - break; - } - case EngineType::ANNOY: { - index = vec_index_factory.CreateVecIndex(knowhere::IndexEnum::INDEX_ANNOY, mode); - break; - } - default: { - LOG_ENGINE_ERROR_ << "Unsupported index type " << (int)type; - return nullptr; - } - } - if (index == nullptr) { - std::string err_msg = "Invalid index type " + std::to_string((int)type) + " mod " + std::to_string((int)mode); - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(DB_ERROR, err_msg); - } - return index; -} - -void -ExecutionEngineImpl::HybridLoad() const { -#ifdef MILVUS_GPU_VERSION - auto hybrid_index = std::dynamic_pointer_cast(index_); - if (hybrid_index == nullptr) { - LOG_ENGINE_WARNING_ << "HybridLoad only support with IVFSQHybrid"; - return; - } - - const std::string key = location_ + ".quantizer"; - - server::Config& config = server::Config::GetInstance(); - std::vector gpus; - Status s = config.GetGpuResourceConfigSearchResources(gpus); - if (!s.ok()) { - LOG_ENGINE_ERROR_ << s.message(); - return; - } - - // cache hit - { - const int64_t NOT_FOUND = -1; - int64_t device_id = NOT_FOUND; - knowhere::QuantizerPtr quantizer = nullptr; - - for (auto& gpu : gpus) { - auto cache = cache::GpuCacheMgr::GetInstance(gpu); - if (auto cached_quantizer = cache->GetIndex(key)) { - device_id = gpu; - quantizer = std::static_pointer_cast(cached_quantizer)->Data(); - } - } - - if (device_id != NOT_FOUND) { - hybrid_index->SetQuantizer(quantizer); - return; - } - } - - // cache miss - { - std::vector all_free_mem; - for (auto& gpu : gpus) { - auto cache = cache::GpuCacheMgr::GetInstance(gpu); - auto free_mem = cache->CacheCapacity() - cache->CacheUsage(); - all_free_mem.push_back(free_mem); - } - - auto max_e = std::max_element(all_free_mem.begin(), all_free_mem.end()); - auto best_index = std::distance(all_free_mem.begin(), max_e); - auto best_device_id = gpus[best_index]; - - milvus::json quantizer_conf{{knowhere::meta::DEVICEID, best_device_id}, {"mode", 1}}; - auto quantizer = hybrid_index->LoadQuantizer(quantizer_conf); - LOG_ENGINE_DEBUG_ << "Quantizer params: " << quantizer_conf.dump(); - if (quantizer == nullptr) { - LOG_ENGINE_ERROR_ << "quantizer is nullptr"; - } - hybrid_index->SetQuantizer(quantizer); - auto cache_quantizer = std::make_shared(quantizer); - cache::GpuCacheMgr::GetInstance(best_device_id)->InsertItem(key, cache_quantizer); - } -#endif -} - -void -ExecutionEngineImpl::HybridUnset() const { -#ifdef MILVUS_GPU_VERSION - auto hybrid_index = std::dynamic_pointer_cast(index_); - if (hybrid_index == nullptr) { - return; - } - hybrid_index->UnsetQuantizer(); -#endif -} - -Status -ExecutionEngineImpl::AddWithIds(int64_t n, const float* xdata, const int64_t* xids) { - auto dataset = knowhere::GenDatasetWithIds(n, index_->Dim(), xdata, xids); - index_->Add(dataset, knowhere::Config()); - return Status::OK(); -} - -Status -ExecutionEngineImpl::AddWithIds(int64_t n, const uint8_t* xdata, const int64_t* xids) { - auto dataset = knowhere::GenDatasetWithIds(n, index_->Dim(), xdata, xids); - index_->Add(dataset, knowhere::Config()); - return Status::OK(); -} - -size_t -ExecutionEngineImpl::Count() const { - if (index_ == nullptr) { - LOG_ENGINE_ERROR_ << "ExecutionEngineImpl: index is null, return count 0"; - return 0; - } - return index_->Count(); -} - -size_t -ExecutionEngineImpl::Dimension() const { - if (index_ == nullptr) { - LOG_ENGINE_ERROR_ << "ExecutionEngineImpl: index is null, return dimension " << dim_; - return dim_; - } - return index_->Dim(); -} - -size_t -ExecutionEngineImpl::Size() const { - if (index_ == nullptr) { - LOG_ENGINE_ERROR_ << "ExecutionEngineImpl: index is null, return size 0"; - return 0; - } - return index_->Size(); -} - -Status -ExecutionEngineImpl::Serialize() { - std::string segment_dir; - utils::GetParentPath(location_, segment_dir); - auto segment_writer_ptr = std::make_shared(segment_dir); - segment_writer_ptr->SetVectorIndex(index_); - auto status = segment_writer_ptr->WriteVectorIndex(location_); - - if (!status.ok()) { - return status; - } - - // here we reset index size by file size, - // since some index type(such as SQ8) data size become smaller after serialized - index_->UpdateIndexSize(); - LOG_ENGINE_DEBUG_ << "Finish serialize index file: " << location_ << " size: " << index_->Size(); - - if (index_->Size() == 0) { - std::string msg = "Failed to serialize file: " + location_ + " reason: out of disk space or memory"; - return Status(DB_ERROR, msg); - } - - return Status::OK(); -} - -Status -ExecutionEngineImpl::Load(bool to_cache) { - index_ = std::static_pointer_cast(cache::CpuCacheMgr::GetInstance()->GetIndex(location_)); - bool already_in_cache = (index_ != nullptr); - if (!already_in_cache) { - std::string segment_dir; - utils::GetParentPath(location_, segment_dir); - auto segment_reader_ptr = std::make_shared(segment_dir); - knowhere::VecIndexFactory& vec_index_factory = knowhere::VecIndexFactory::GetInstance(); - - if (utils::IsRawIndexType((int32_t)index_type_)) { - if (index_type_ == EngineType::FAISS_IDMAP) { - index_ = vec_index_factory.CreateVecIndex(knowhere::IndexEnum::INDEX_FAISS_IDMAP); - } else { - index_ = vec_index_factory.CreateVecIndex(knowhere::IndexEnum::INDEX_FAISS_BIN_IDMAP); - } - milvus::json conf{{knowhere::meta::DEVICEID, gpu_num_}, {knowhere::meta::DIM, dim_}}; - MappingMetricType(metric_type_, conf); - auto adapter = knowhere::AdapterMgr::GetInstance().GetAdapter(index_->index_type()); - LOG_ENGINE_DEBUG_ << "Index params: " << conf.dump(); - if (!adapter->CheckTrain(conf, index_->index_mode())) { - throw Exception(DB_ERROR, "Illegal index params"); - } - - auto status = segment_reader_ptr->Load(); - if (!status.ok()) { - std::string msg = "Failed to load segment from " + location_; - LOG_ENGINE_ERROR_ << msg; - return Status(DB_ERROR, msg); - } - - segment::SegmentPtr segment_ptr; - segment_reader_ptr->GetSegment(segment_ptr); - auto& vectors = segment_ptr->vectors_ptr_; - auto& deleted_docs = segment_ptr->deleted_docs_ptr_->GetDeletedDocs(); - - auto& vectors_uids = vectors->GetMutableUids(); - auto count = vectors_uids.size(); - index_->SetUids(vectors_uids); - LOG_ENGINE_DEBUG_ << "set uids " << index_->GetUids().size() << " for index " << location_; - - auto& vectors_data = vectors->GetData(); - - auto attrs = segment_ptr->attrs_ptr_; - - auto attrs_it = attrs->attrs.begin(); - for (; attrs_it != attrs->attrs.end(); ++attrs_it) { - attr_data_.insert(std::pair(attrs_it->first, attrs_it->second->GetData())); - attr_size_.insert(std::pair(attrs_it->first, attrs_it->second->GetNbytes())); - } - - vector_count_ = count; - - faiss::ConcurrentBitsetPtr concurrent_bitset_ptr = std::make_shared(count); - for (auto& offset : deleted_docs) { - concurrent_bitset_ptr->set(offset); - } - - auto dataset = knowhere::GenDataset(count, this->dim_, vectors_data.data()); - if (index_type_ == EngineType::FAISS_IDMAP) { - auto bf_index = std::static_pointer_cast(index_); - bf_index->Train(knowhere::DatasetPtr(), conf); - bf_index->AddWithoutIds(dataset, conf); - bf_index->SetBlacklist(concurrent_bitset_ptr); - } else if (index_type_ == EngineType::FAISS_BIN_IDMAP) { - auto bin_bf_index = std::static_pointer_cast(index_); - bin_bf_index->Train(knowhere::DatasetPtr(), conf); - bin_bf_index->AddWithoutIds(dataset, conf); - bin_bf_index->SetBlacklist(concurrent_bitset_ptr); - } - - LOG_ENGINE_DEBUG_ << "Finished loading raw data from segment " << segment_dir; - } else { - try { - segment::SegmentPtr segment_ptr; - segment_reader_ptr->GetSegment(segment_ptr); - auto status = segment_reader_ptr->LoadVectorIndex(location_, segment_ptr->vector_index_ptr_); - index_ = segment_ptr->vector_index_ptr_->GetVectorIndex(); - - if (index_ == nullptr) { - std::string msg = "Failed to load index from " + location_; - LOG_ENGINE_ERROR_ << msg; - return Status(DB_ERROR, msg); - } else { - bool gpu_enable = false; -#ifdef MILVUS_GPU_VERSION - server::Config& config = server::Config::GetInstance(); - STATUS_CHECK(config.GetGpuResourceConfigEnable(gpu_enable)); -#endif - if (!gpu_enable && index_->index_mode() == knowhere::IndexMode::MODE_GPU) { - std::string err_msg = "Index with type " + index_->index_type() + " must be used in GPU mode"; - LOG_ENGINE_ERROR_ << err_msg; - return Status(DB_ERROR, err_msg); - } - segment::DeletedDocsPtr deleted_docs_ptr; - auto status = segment_reader_ptr->LoadDeletedDocs(deleted_docs_ptr); - if (!status.ok()) { - std::string msg = "Failed to load deleted docs from " + location_; - LOG_ENGINE_ERROR_ << msg; - return Status(DB_ERROR, msg); - } - auto& deleted_docs = deleted_docs_ptr->GetDeletedDocs(); - - faiss::ConcurrentBitsetPtr concurrent_bitset_ptr = - std::make_shared(index_->Count()); - for (auto& offset : deleted_docs) { - if (!concurrent_bitset_ptr->test(offset)) { - concurrent_bitset_ptr->set(offset); - } - } - - index_->SetBlacklist(concurrent_bitset_ptr); - - std::vector uids; - segment_reader_ptr->LoadUids(uids); - index_->SetUids(uids); - LOG_ENGINE_DEBUG_ << "set uids " << index_->GetUids().size() << " for index " << location_; - - LOG_ENGINE_DEBUG_ << "Finished loading index file from segment " << segment_dir; - } - } catch (std::exception& e) { - LOG_ENGINE_ERROR_ << e.what(); - return Status(DB_ERROR, e.what()); - } - } - } - - if (!already_in_cache && to_cache) { - Cache(); - } - return Status::OK(); -} // namespace engine - -Status -ExecutionEngineImpl::CopyToGpu(uint64_t device_id, bool hybrid) { -#if 0 - if (hybrid) { - const std::string key = location_ + ".quantizer"; - std::vector gpus{device_id}; - - const int64_t NOT_FOUND = -1; - int64_t device_id = NOT_FOUND; - - // cache hit - { - knowhere::QuantizerPtr quantizer = nullptr; - - for (auto& gpu : gpus) { - auto cache = cache::GpuCacheMgr::GetInstance(gpu); - if (auto cached_quantizer = cache->GetIndex(key)) { - device_id = gpu; - quantizer = std::static_pointer_cast(cached_quantizer)->Data(); - } - } - - if (device_id != NOT_FOUND) { - // cache hit - milvus::json quantizer_conf{{knowhere::meta::DEVICEID : device_id}, {"mode" : 2}}; - auto new_index = index_->LoadData(quantizer, config); - index_ = new_index; - } - } - - if (device_id == NOT_FOUND) { - // cache miss - std::vector all_free_mem; - for (auto& gpu : gpus) { - auto cache = cache::GpuCacheMgr::GetInstance(gpu); - auto free_mem = cache->CacheCapacity() - cache->CacheUsage(); - all_free_mem.push_back(free_mem); - } - - auto max_e = std::max_element(all_free_mem.begin(), all_free_mem.end()); - auto best_index = std::distance(all_free_mem.begin(), max_e); - device_id = gpus[best_index]; - - auto pair = index_->CopyToGpuWithQuantizer(device_id); - index_ = pair.first; - - // cache - auto cached_quantizer = std::make_shared(pair.second); - cache::GpuCacheMgr::GetInstance(device_id)->InsertItem(key, cached_quantizer); - } - return Status::OK(); - } -#endif - -#ifdef MILVUS_GPU_VERSION - auto data_obj_ptr = cache::GpuCacheMgr::GetInstance(device_id)->GetIndex(location_); - auto index = std::static_pointer_cast(data_obj_ptr); - bool already_in_cache = (index != nullptr); - if (already_in_cache) { - index_ = index; - } else { - if (index_ == nullptr) { - LOG_ENGINE_ERROR_ << "ExecutionEngineImpl: index is null, failed to copy to gpu"; - return Status(DB_ERROR, "index is null"); - } - - try { - /* Index data is copied to GPU first, then added into GPU cache. - * Add lock here to avoid multiple INDEX are copied to one GPU card at same time. - * And reserve space to avoid GPU out of memory issue. - */ - LOG_ENGINE_DEBUG_ << "CPU to GPU" << device_id << " start"; - auto gpu_cache_mgr = cache::GpuCacheMgr::GetInstance(device_id); - - bool gpu_cache_enable = false; - STATUS_CHECK(server::Config::GetInstance().GetGpuResourceConfigCacheEnable(gpu_cache_enable)); - - /* CopyCpuToGpu() is an asynchronous method. - * It should be make sure that the CPU index is always valid. - * Therefore, we reserve its shared pointer. - */ - index_reserve_ = index_; - if (gpu_cache_enable) { - gpu_cache_mgr->Reserve(index_->Size()); - index_ = knowhere::cloner::CopyCpuToGpu(index_, device_id, knowhere::Config()); - gpu_cache_mgr->InsertItem(location_, std::static_pointer_cast(index_)); - } else { - index_ = knowhere::cloner::CopyCpuToGpu(index_, device_id, knowhere::Config()); - } - LOG_ENGINE_DEBUG_ << "CPU to GPU" << device_id << " finished"; - } catch (std::exception& e) { - LOG_ENGINE_ERROR_ << e.what(); - return Status(DB_ERROR, e.what()); - } - } -#endif - - return Status::OK(); -} - -Status -ExecutionEngineImpl::CopyToIndexFileToGpu(uint64_t device_id) { -#ifdef MILVUS_GPU_VERSION - // the ToIndexData is only a placeholder, cpu-copy-to-gpu action is performed in - if (index_) { - auto gpu_cache_mgr = milvus::cache::GpuCacheMgr::GetInstance(device_id); - gpu_num_ = device_id; - gpu_cache_mgr->Reserve(index_->Size()); - } -#endif - return Status::OK(); -} - -Status -ExecutionEngineImpl::CopyToCpu() { -#ifdef MILVUS_GPU_VERSION - auto index = std::static_pointer_cast(cache::CpuCacheMgr::GetInstance()->GetIndex(location_)); - bool already_in_cache = (index != nullptr); - if (already_in_cache) { - index_ = index; - } else { - if (index_ == nullptr) { - LOG_ENGINE_ERROR_ << "ExecutionEngineImpl: index is null, failed to copy to cpu"; - return Status(DB_ERROR, "index is null"); - } - - try { - index_ = knowhere::cloner::CopyGpuToCpu(index_, knowhere::Config()); - LOG_ENGINE_DEBUG_ << "GPU to CPU"; - } catch (std::exception& e) { - LOG_ENGINE_ERROR_ << e.what(); - return Status(DB_ERROR, e.what()); - } - } - - if (!already_in_cache) { - Cache(); - } - return Status::OK(); -#else - LOG_ENGINE_ERROR_ << "Calling ExecutionEngineImpl::CopyToCpu when using CPU version"; - return Status(DB_ERROR, "Calling ExecutionEngineImpl::CopyToCpu when using CPU version"); -#endif -} - -ExecutionEnginePtr -ExecutionEngineImpl::BuildIndex(const std::string& location, EngineType engine_type) { - LOG_ENGINE_DEBUG_ << "Build index file: " << location << " from: " << location_; - - auto from_index = std::dynamic_pointer_cast(index_); - auto bin_from_index = std::dynamic_pointer_cast(index_); - if (from_index == nullptr && bin_from_index == nullptr) { - LOG_ENGINE_ERROR_ << "ExecutionEngineImpl: from_index is null, failed to build index"; - return nullptr; - } - - auto to_index = CreatetVecIndex(engine_type); - if (!to_index) { - throw Exception(DB_ERROR, "Unsupported index type"); - } - - milvus::json conf = index_params_; - conf[knowhere::meta::DIM] = Dimension(); - conf[knowhere::meta::ROWS] = Count(); - conf[knowhere::meta::DEVICEID] = gpu_num_; - MappingMetricType(metric_type_, conf); - LOG_ENGINE_DEBUG_ << "Index params: " << conf.dump(); - auto adapter = knowhere::AdapterMgr::GetInstance().GetAdapter(to_index->index_type()); - if (!adapter->CheckTrain(conf, to_index->index_mode())) { - throw Exception(DB_ERROR, "Illegal index params"); - } - LOG_ENGINE_DEBUG_ << "Index config: " << conf.dump(); - - std::vector uids; - faiss::ConcurrentBitsetPtr blacklist; - if (from_index) { - auto dataset = - knowhere::GenDatasetWithIds(Count(), Dimension(), from_index->GetRawVectors(), from_index->GetRawIds()); - to_index->BuildAll(dataset, conf); - uids = from_index->GetUids(); - blacklist = from_index->GetBlacklist(); - } else if (bin_from_index) { - auto dataset = knowhere::GenDatasetWithIds(Count(), Dimension(), bin_from_index->GetRawVectors(), - bin_from_index->GetRawIds()); - to_index->BuildAll(dataset, conf); - uids = bin_from_index->GetUids(); - blacklist = bin_from_index->GetBlacklist(); - } - -#ifdef MILVUS_GPU_VERSION - /* for GPU index, need copy back to CPU */ - if (to_index->index_mode() == knowhere::IndexMode::MODE_GPU) { - auto device_index = std::dynamic_pointer_cast(to_index); - to_index = device_index->CopyGpuToCpu(conf); - } -#endif - - to_index->SetUids(uids); - LOG_ENGINE_DEBUG_ << "Set " << to_index->GetUids().size() << "uids for " << location; - if (blacklist != nullptr) { - to_index->SetBlacklist(blacklist); - LOG_ENGINE_DEBUG_ << "Set blacklist for index " << location; - } - - LOG_ENGINE_DEBUG_ << "Finish build index: " << location; - return std::make_shared(to_index, location, engine_type, metric_type_, index_params_); -} - -void -MapAndCopyResult(const knowhere::DatasetPtr& dataset, const std::vector& uids, int64_t nq, - int64_t k, float* distances, int64_t* labels) { - int64_t* res_ids = dataset->Get(knowhere::meta::IDS); - float* res_dist = dataset->Get(knowhere::meta::DISTANCE); - - memcpy(distances, res_dist, sizeof(float) * nq * k); - - /* map offsets to ids */ - int64_t num = nq * k; - for (int64_t i = 0; i < num; ++i) { - int64_t offset = res_ids[i]; - if (offset != -1) { - labels[i] = uids[offset]; - } else { - labels[i] = -1; - } - } - - free(res_ids); - free(res_dist); -} - -template -void -ProcessRangeQuery(std::vector data, T value, query::CompareOperator type, faiss::ConcurrentBitsetPtr& bitset) { - switch (type) { - case query::CompareOperator::LT: { - for (uint64_t i = 0; i < data.size(); ++i) { - if (data[i] >= value) { - if (!bitset->test(i)) { - bitset->set(i); - } - } - } - break; - } - case query::CompareOperator::LTE: { - for (uint64_t i = 0; i < data.size(); ++i) { - if (data[i] > value) { - if (!bitset->test(i)) { - bitset->set(i); - } - } - } - break; - } - case query::CompareOperator::GT: { - for (uint64_t i = 0; i < data.size(); ++i) { - if (data[i] <= value) { - if (!bitset->test(i)) { - bitset->set(i); - } - } - } - break; - } - case query::CompareOperator::GTE: { - for (uint64_t i = 0; i < data.size(); ++i) { - if (data[i] < value) { - if (!bitset->test(i)) { - bitset->set(i); - } - } - } - break; - } - case query::CompareOperator::EQ: { - for (uint64_t i = 0; i < data.size(); ++i) { - if (data[i] != value) { - if (!bitset->test(i)) { - bitset->set(i); - } - } - } - } - case query::CompareOperator::NE: { - for (uint64_t i = 0; i < data.size(); ++i) { - if (data[i] == value) { - if (!bitset->test(i)) { - bitset->set(i); - } - } - } - break; - } - } -} - -Status -ExecutionEngineImpl::ExecBinaryQuery(milvus::query::GeneralQueryPtr general_query, faiss::ConcurrentBitsetPtr bitset, - std::unordered_map& attr_type, uint64_t& nq, uint64_t& topk, - std::vector& distances, std::vector& labels) { - if (bitset == nullptr) { - bitset = std::make_shared(vector_count_); - } - - if (general_query->leaf == nullptr) { - Status status = Status::OK(); - if (general_query->bin->left_query != nullptr) { - status = ExecBinaryQuery(general_query->bin->left_query, bitset, attr_type, nq, topk, distances, labels); - } - if (general_query->bin->right_query != nullptr) { - status = ExecBinaryQuery(general_query->bin->right_query, bitset, attr_type, nq, topk, distances, labels); - } - return status; - } else { - if (general_query->leaf->term_query != nullptr) { - // process attrs_data - auto field_name = general_query->leaf->term_query->field_name; - auto type = attr_type.at(field_name); - auto size = attr_size_.at(field_name); - switch (type) { - case DataType::INT8: { - std::vector data; - data.resize(size / sizeof(int8_t)); - memcpy(data.data(), attr_data_.at(field_name).data(), size); - - std::vector term_value; - auto term_size = - general_query->leaf->term_query->field_value.size() * (sizeof(int8_t)) / sizeof(int8_t); - term_value.resize(term_size); - memcpy(term_value.data(), general_query->leaf->term_query->field_value.data(), - term_size * sizeof(int8_t)); - - for (uint64_t i = 0; i < data.size(); ++i) { - bool value_in_term = false; - for (auto query_value : term_value) { - if (data[i] == query_value) { - value_in_term = true; - break; - } - } - if (!value_in_term) { - if (!bitset->test(i)) { - bitset->set(i); - } - } - } - break; - } - case DataType::INT16: { - std::vector data; - data.resize(size / sizeof(int16_t)); - memcpy(data.data(), attr_data_.at(field_name).data(), size); - std::vector term_value; - auto term_size = - general_query->leaf->term_query->field_value.size() * (sizeof(int8_t)) / sizeof(int16_t); - term_value.resize(term_size); - memcpy(term_value.data(), general_query->leaf->term_query->field_value.data(), - term_size * sizeof(int16_t)); - - for (uint64_t i = 0; i < data.size(); ++i) { - bool value_in_term = false; - for (auto query_value : term_value) { - if (data[i] == query_value) { - value_in_term = true; - break; - } - } - if (!value_in_term) { - if (!bitset->test(i)) { - bitset->set(i); - } - } - } - break; - } - case DataType::INT32: { - std::vector data; - data.resize(size / sizeof(int32_t)); - memcpy(data.data(), attr_data_.at(field_name).data(), size); - - std::vector term_value; - auto term_size = - general_query->leaf->term_query->field_value.size() * (sizeof(int8_t)) / sizeof(int32_t); - term_value.resize(term_size); - memcpy(term_value.data(), general_query->leaf->term_query->field_value.data(), - term_size * sizeof(int32_t)); - - for (uint64_t i = 0; i < data.size(); ++i) { - bool value_in_term = false; - for (auto query_value : term_value) { - if (data[i] == query_value) { - value_in_term = true; - break; - } - } - if (!value_in_term) { - if (!bitset->test(i)) { - bitset->set(i); - } - } - } - break; - } - case DataType::INT64: { - std::vector data; - data.resize(size / sizeof(int64_t)); - memcpy(data.data(), attr_data_.at(field_name).data(), size); - - std::vector term_value; - auto term_size = - general_query->leaf->term_query->field_value.size() * (sizeof(int8_t)) / sizeof(int64_t); - term_value.resize(term_size); - memcpy(term_value.data(), general_query->leaf->term_query->field_value.data(), - term_size * sizeof(int64_t)); - - for (uint64_t i = 0; i < data.size(); ++i) { - bool value_in_term = false; - for (auto query_value : term_value) { - if (data[i] == query_value) { - value_in_term = true; - break; - } - } - if (!value_in_term) { - if (!bitset->test(i)) { - bitset->set(i); - } - } - } - break; - } - case DataType::FLOAT: { - std::vector data; - data.resize(size / sizeof(float)); - memcpy(data.data(), attr_data_.at(field_name).data(), size); - - std::vector term_value; - auto term_size = - general_query->leaf->term_query->field_value.size() * (sizeof(int8_t)) / sizeof(float); - term_value.resize(term_size); - memcpy(term_value.data(), general_query->leaf->term_query->field_value.data(), - term_size * sizeof(int64_t)); - - for (uint64_t i = 0; i < data.size(); ++i) { - bool value_in_term = false; - for (auto query_value : term_value) { - if (data[i] == query_value) { - value_in_term = true; - break; - } - } - if (!value_in_term) { - if (!bitset->test(i)) { - bitset->set(i); - } - } - } - break; - } - case DataType::DOUBLE: { - std::vector data; - data.resize(size / sizeof(double)); - memcpy(data.data(), attr_data_.at(field_name).data(), size); - - std::vector term_value; - auto term_size = - general_query->leaf->term_query->field_value.size() * (sizeof(int8_t)) / sizeof(double); - term_value.resize(term_size); - memcpy(term_value.data(), general_query->leaf->term_query->field_value.data(), - term_size * sizeof(double)); - - for (uint64_t i = 0; i < data.size(); ++i) { - bool value_in_term = false; - for (auto query_value : term_value) { - if (data[i] == query_value) { - value_in_term = true; - break; - } - } - if (!value_in_term) { - if (!bitset->test(i)) { - bitset->set(i); - } - } - } - break; - } - default: - break; - } - return Status::OK(); - } - if (general_query->leaf->range_query != nullptr) { - auto field_name = general_query->leaf->range_query->field_name; - auto com_expr = general_query->leaf->range_query->compare_expr; - auto type = attr_type.at(field_name); - auto size = attr_size_.at(field_name); - for (uint64_t j = 0; j < com_expr.size(); ++j) { - auto operand = com_expr[j].operand; - switch (type) { - case DataType::INT8: { - std::vector data; - data.resize(size / sizeof(int8_t)); - memcpy(data.data(), attr_data_.at(field_name).data(), size); - int8_t value = atoi(operand.c_str()); - ProcessRangeQuery(data, value, com_expr[j].compare_operator, bitset); - break; - } - case DataType::INT16: { - std::vector data; - data.resize(size / sizeof(int16_t)); - memcpy(data.data(), attr_data_.at(field_name).data(), size); - int16_t value = atoi(operand.c_str()); - ProcessRangeQuery(data, value, com_expr[j].compare_operator, bitset); - break; - } - case DataType::INT32: { - std::vector data; - data.resize(size / sizeof(int32_t)); - memcpy(data.data(), attr_data_.at(field_name).data(), size); - int32_t value = atoi(operand.c_str()); - ProcessRangeQuery(data, value, com_expr[j].compare_operator, bitset); - break; - } - case DataType::INT64: { - std::vector data; - data.resize(size / sizeof(int64_t)); - memcpy(data.data(), attr_data_.at(field_name).data(), size); - int64_t value = atoi(operand.c_str()); - ProcessRangeQuery(data, value, com_expr[j].compare_operator, bitset); - break; - } - case DataType::FLOAT: { - std::vector data; - data.resize(size / sizeof(float)); - memcpy(data.data(), attr_data_.at(field_name).data(), size); - std::istringstream iss(operand); - double value; - iss >> value; - ProcessRangeQuery(data, value, com_expr[j].compare_operator, bitset); - break; - } - case DataType::DOUBLE: { - std::vector data; - data.resize(size / sizeof(double)); - memcpy(data.data(), attr_data_.at(field_name).data(), size); - std::istringstream iss(operand); - double value; - iss >> value; - ProcessRangeQuery(data, value, com_expr[j].compare_operator, bitset); - break; - } - default: - break; - } - } - return Status::OK(); - } - if (general_query->leaf->vector_query != nullptr) { - // Do search - faiss::ConcurrentBitsetPtr list; - list = index_->GetBlacklist(); - // Do OR - for (int64_t i = 0; i < vector_count_; ++i) { - if (list->test(i) || bitset->test(i)) { - bitset->set(i); - } - } - index_->SetBlacklist(bitset); - auto vector_query = general_query->leaf->vector_query; - topk = vector_query->topk; - nq = vector_query->query_vector.float_data.size() / dim_; - - distances.resize(nq * topk); - labels.resize(nq * topk); - - return Search(nq, vector_query->query_vector.float_data.data(), topk, vector_query->extra_params, - distances.data(), labels.data()); - } - } - return Status::OK(); -} - -Status -ExecutionEngineImpl::Search(int64_t n, const float* data, int64_t k, const milvus::json& extra_params, float* distances, - int64_t* labels, bool hybrid) { -#if 0 - if (index_type_ == EngineType::FAISS_IVFSQ8H) { - if (!hybrid) { - const std::string key = location_ + ".quantizer"; - std::vector gpus = scheduler::get_gpu_pool(); - - const int64_t NOT_FOUND = -1; - int64_t device_id = NOT_FOUND; - - // cache hit - { - knowhere::QuantizerPtr quantizer = nullptr; - - for (auto& gpu : gpus) { - auto cache = cache::GpuCacheMgr::GetInstance(gpu); - if (auto cached_quantizer = cache->GetIndex(key)) { - device_id = gpu; - quantizer = std::static_pointer_cast(cached_quantizer)->Data(); - } - } - - if (device_id != NOT_FOUND) { - // cache hit - milvus::json quantizer_conf{{knowhere::meta::DEVICEID : device_id}, {"mode" : 2}}; - auto new_index = index_->LoadData(quantizer, config); - index_ = new_index; - } - } - - if (device_id == NOT_FOUND) { - // cache miss - std::vector all_free_mem; - for (auto& gpu : gpus) { - auto cache = cache::GpuCacheMgr::GetInstance(gpu); - auto free_mem = cache->CacheCapacity() - cache->CacheUsage(); - all_free_mem.push_back(free_mem); - } - - auto max_e = std::max_element(all_free_mem.begin(), all_free_mem.end()); - auto best_index = std::distance(all_free_mem.begin(), max_e); - device_id = gpus[best_index]; - - auto pair = index_->CopyToGpuWithQuantizer(device_id); - index_ = pair.first; - - // cache - auto cached_quantizer = std::make_shared(pair.second); - cache::GpuCacheMgr::GetInstance(device_id)->InsertItem(key, cached_quantizer); - } - } - } -#endif - TimeRecorder rc(LogOut("[%s][%ld] ExecutionEngineImpl::Search float", "search", 0)); - - if (index_ == nullptr) { - LOG_ENGINE_ERROR_ << LogOut("[%s][%ld] ExecutionEngineImpl: index is null, failed to search", "search", 0); - return Status(DB_ERROR, "index is null"); - } - - milvus::json conf = extra_params; - if (conf.contains(knowhere::Metric::TYPE)) - MappingMetricType(conf[knowhere::Metric::TYPE], conf); - conf[knowhere::meta::TOPK] = k; - - auto adapter = knowhere::AdapterMgr::GetInstance().GetAdapter(index_->index_type()); - if (!adapter->CheckSearch(conf, index_->index_type(), index_->index_mode())) { - LOG_ENGINE_ERROR_ << LogOut("[%s][%ld] Illegal search params", "search", 0); - throw Exception(DB_ERROR, "Illegal search params"); - } - - if (hybrid) { - HybridLoad(); - } - - rc.RecordSection("query prepare"); - auto dataset = knowhere::GenDataset(n, index_->Dim(), data); - auto result = index_->Query(dataset, conf); - rc.RecordSection("query done"); - - LOG_ENGINE_DEBUG_ << LogOut("[%s][%ld] get %ld uids from index %s", "search", 0, index_->GetUids().size(), - location_.c_str()); - MapAndCopyResult(result, index_->GetUids(), n, k, distances, labels); - rc.RecordSection("map uids " + std::to_string(n * k)); - - if (hybrid) { - HybridUnset(); - } - - return Status::OK(); -} - -Status -ExecutionEngineImpl::Search(int64_t n, const uint8_t* data, int64_t k, const milvus::json& extra_params, - float* distances, int64_t* labels, bool hybrid) { - TimeRecorder rc(LogOut("[%s][%ld] ExecutionEngineImpl::Search uint8", "search", 0)); - - if (index_ == nullptr) { - LOG_ENGINE_ERROR_ << LogOut("[%s][%ld] ExecutionEngineImpl: index is null, failed to search", "search", 0); - return Status(DB_ERROR, "index is null"); - } - - milvus::json conf = extra_params; - if (conf.contains(knowhere::Metric::TYPE)) - MappingMetricType(conf[knowhere::Metric::TYPE], conf); - conf[knowhere::meta::TOPK] = k; - auto adapter = knowhere::AdapterMgr::GetInstance().GetAdapter(index_->index_type()); - if (!adapter->CheckSearch(conf, index_->index_type(), index_->index_mode())) { - LOG_ENGINE_ERROR_ << LogOut("[%s][%ld] Illegal search params", "search", 0); - throw Exception(DB_ERROR, "Illegal search params"); - } - - if (hybrid) { - HybridLoad(); - } - - rc.RecordSection("query prepare"); - auto dataset = knowhere::GenDataset(n, index_->Dim(), data); - auto result = index_->Query(dataset, conf); - rc.RecordSection("query done"); - - LOG_ENGINE_DEBUG_ << LogOut("[%s][%ld] get %ld uids from index %s", "search", 0, index_->GetUids().size(), - location_.c_str()); - MapAndCopyResult(result, index_->GetUids(), n, k, distances, labels); - rc.RecordSection("map uids " + std::to_string(n * k)); - - if (hybrid) { - HybridUnset(); - } - - return Status::OK(); -} - -#if 0 -Status -ExecutionEngineImpl::GetVectorByID(const int64_t id, float* vector, bool hybrid) { - if (index_ == nullptr) { - LOG_ENGINE_ERROR_ << "ExecutionEngineImpl: index is null, failed to search"; - return Status(DB_ERROR, "index is null"); - } - - if (hybrid) { - HybridLoad(); - } - - // Only one id for now - std::vector ids{id}; - auto dataset = knowhere::GenDatasetWithIds(1, index_->Dim(), nullptr, ids.data()); - auto result = index_->GetVectorById(dataset, knowhere::Config()); - float* res_vec = (float*)(result->Get(knowhere::meta::TENSOR)); - memcpy(vector, res_vec, sizeof(float) * 1 * index_->Dim()); - - if (hybrid) { - HybridUnset(); - } - - return Status::OK(); -} - -Status -ExecutionEngineImpl::GetVectorByID(const int64_t id, uint8_t* vector, bool hybrid) { - if (index_ == nullptr) { - LOG_ENGINE_ERROR_ << "ExecutionEngineImpl: index is null, failed to search"; - return Status(DB_ERROR, "index is null"); - } - - LOG_ENGINE_DEBUG_ << "Get binary vector by id: " << id; - - if (hybrid) { - HybridLoad(); - } - - // Only one id for now - std::vector ids{id}; - auto dataset = knowhere::GenDatasetWithIds(1, index_->Dim(), nullptr, ids.data()); - auto result = index_->GetVectorById(dataset, knowhere::Config()); - uint8_t* res_vec = (uint8_t*)(result->Get(knowhere::meta::TENSOR)); - memcpy(vector, res_vec, sizeof(uint8_t) * 1 * index_->Dim()); - - if (hybrid) { - HybridUnset(); - } - - return Status::OK(); -} -#endif - -Status -ExecutionEngineImpl::Cache() { - auto cpu_cache_mgr = milvus::cache::CpuCacheMgr::GetInstance(); - cache::DataObjPtr obj = std::static_pointer_cast(index_); - cpu_cache_mgr->InsertItem(location_, obj); - return Status::OK(); -} - -// TODO(linxj): remove. -Status -ExecutionEngineImpl::Init() { -#ifdef MILVUS_GPU_VERSION - server::Config& config = server::Config::GetInstance(); - std::vector gpu_ids; - Status s = config.GetGpuResourceConfigBuildIndexResources(gpu_ids); - if (!s.ok()) { - gpu_num_ = -1; - return s; - } - for (auto id : gpu_ids) { - if (gpu_num_ == id) { - return Status::OK(); - } - } - - std::string msg = "Invalid gpu_num"; - return Status(SERVER_INVALID_ARGUMENT, msg); -#else - return Status::OK(); -#endif -} - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/engine/ExecutionEngineImpl.h b/core/src/db/engine/ExecutionEngineImpl.h deleted file mode 100644 index 30eba674cd..0000000000 --- a/core/src/db/engine/ExecutionEngineImpl.h +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "segment/SegmentReader.h" -#include "utils/Json.h" - -#include -#include -#include -#include - -#include "ExecutionEngine.h" -#include "knowhere/index/vector_index/VecIndex.h" - -namespace milvus { -namespace engine { - -class ExecutionEngineImpl : public ExecutionEngine { - public: - ExecutionEngineImpl(uint16_t dimension, const std::string& location, EngineType index_type, MetricType metric_type, - const milvus::json& index_params); - - ExecutionEngineImpl(knowhere::VecIndexPtr index, const std::string& location, EngineType index_type, - MetricType metric_type, const milvus::json& index_params); - - Status - AddWithIds(int64_t n, const float* xdata, const int64_t* xids) override; - - Status - AddWithIds(int64_t n, const uint8_t* xdata, const int64_t* xids) override; - - size_t - Count() const override; - - size_t - Dimension() const override; - - size_t - Size() const override; - - Status - Serialize() override; - - Status - Load(bool to_cache) override; - - Status - CopyToGpu(uint64_t device_id, bool hybrid = false) override; - - Status - CopyToIndexFileToGpu(uint64_t device_id) override; - - Status - CopyToCpu() override; - -#if 0 - Status - GetVectorByID(const int64_t id, float* vector, bool hybrid) override; - - Status - GetVectorByID(const int64_t id, uint8_t* vector, bool hybrid) override; -#endif - - Status - ExecBinaryQuery(query::GeneralQueryPtr general_query, faiss::ConcurrentBitsetPtr bitset, - std::unordered_map& attr_type, uint64_t& nq, uint64_t& topk, - std::vector& distances, std::vector& labels) override; - - Status - Search(int64_t n, const float* data, int64_t k, const milvus::json& extra_params, float* distances, int64_t* labels, - bool hybrid = false) override; - - Status - Search(int64_t n, const uint8_t* data, int64_t k, const milvus::json& extra_params, float* distances, - int64_t* labels, bool hybrid = false) override; - - ExecutionEnginePtr - BuildIndex(const std::string& location, EngineType engine_type) override; - - Status - Cache() override; - - Status - Init() override; - - EngineType - IndexEngineType() const override { - return index_type_; - } - - MetricType - IndexMetricType() const override { - return metric_type_; - } - - std::string - GetLocation() const override { - return location_; - } - - private: - knowhere::VecIndexPtr - CreatetVecIndex(EngineType type); - - knowhere::VecIndexPtr - Load(const std::string& location); - - void - HybridLoad() const; - - void - HybridUnset() const; - - protected: - knowhere::VecIndexPtr index_ = nullptr; -#ifdef MILVUS_GPU_VERSION - knowhere::VecIndexPtr index_reserve_ = nullptr; // reserve the cpu index before copying it to gpu -#endif - std::string location_; - int64_t dim_; - EngineType index_type_; - MetricType metric_type_; - - std::unordered_map attr_types_; - std::unordered_map> attr_data_; - std::unordered_map attr_size_; - query::BinaryQueryPtr binary_query_; - int64_t vector_count_; - - milvus::json index_params_; - int64_t gpu_num_ = 0; - - bool gpu_cache_enable_ = false; -}; - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/insert/MemManager.h b/core/src/db/insert/MemManager.h deleted file mode 100644 index 53389654ca..0000000000 --- a/core/src/db/insert/MemManager.h +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include - -#include "db/Types.h" -#include "utils/Status.h" - -namespace milvus { -namespace engine { - -class MemManager { - public: - virtual Status - InsertVectors(const std::string& collection_id, int64_t length, const IDNumber* vector_ids, int64_t dim, - const float* vectors, uint64_t lsn) = 0; - - virtual Status - InsertVectors(const std::string& collection_id, int64_t length, const IDNumber* vector_ids, int64_t dim, - const uint8_t* vectors, uint64_t lsn) = 0; - - virtual Status - InsertEntities(const std::string& collection_id, int64_t length, const IDNumber* vector_ids, int64_t dim, - const float* vectors, const std::unordered_map& attr_nbytes, - const std::unordered_map& attr_size, - const std::unordered_map>& attr_data, uint64_t lsn) = 0; - - virtual Status - DeleteVector(const std::string& collection_id, IDNumber vector_id, uint64_t lsn) = 0; - - virtual Status - DeleteVectors(const std::string& collection_id, int64_t length, const IDNumber* vector_ids, uint64_t lsn) = 0; - - virtual Status - Flush(const std::string& collection_id) = 0; - - virtual Status - - Flush(std::set& collection_ids) = 0; - - // virtual Status - // Serialize(std::set& table_ids) = 0; - - virtual Status - EraseMemVector(const std::string& collection_id) = 0; - - virtual size_t - GetCurrentMutableMem() = 0; - - virtual size_t - GetCurrentImmutableMem() = 0; - - virtual size_t - GetCurrentMem() = 0; -}; // MemManagerAbstract - -using MemManagerPtr = std::shared_ptr; - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/insert/MemManagerFactory.cpp b/core/src/db/insert/MemManagerFactory.cpp deleted file mode 100644 index b75f3605a5..0000000000 --- a/core/src/db/insert/MemManagerFactory.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/insert/MemManagerFactory.h" -#include "MemManagerImpl.h" -#include "utils/Exception.h" -#include "utils/Log.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace milvus { -namespace engine { - -MemManagerPtr -MemManagerFactory::Build(const std::shared_ptr& meta, const DBOptions& options) { - return std::make_shared(meta, options); -} - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/insert/MemManagerFactory.h b/core/src/db/insert/MemManagerFactory.h deleted file mode 100644 index f77dc6e575..0000000000 --- a/core/src/db/insert/MemManagerFactory.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "MemManager.h" -#include "db/meta/Meta.h" - -#include - -namespace milvus { -namespace engine { - -class MemManagerFactory { - public: - static MemManagerPtr - Build(const std::shared_ptr& meta, const DBOptions& options); -}; - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/insert/MemManagerImpl.cpp b/core/src/db/insert/MemManagerImpl.cpp deleted file mode 100644 index 437a031300..0000000000 --- a/core/src/db/insert/MemManagerImpl.cpp +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/insert/MemManagerImpl.h" - -#include -#include - -#include "VectorSource.h" -#include "db/Constants.h" -#include "utils/Log.h" - -namespace milvus { -namespace engine { - -MemTablePtr -MemManagerImpl::GetMemByTable(const std::string& collection_id) { - auto memIt = mem_id_map_.find(collection_id); - if (memIt != mem_id_map_.end()) { - return memIt->second; - } - - mem_id_map_[collection_id] = std::make_shared(collection_id, meta_, options_); - return mem_id_map_[collection_id]; -} - -Status -MemManagerImpl::InsertVectors(const std::string& collection_id, int64_t length, const IDNumber* vector_ids, int64_t dim, - const float* vectors, uint64_t lsn) { - VectorsData vectors_data; - vectors_data.vector_count_ = length; - vectors_data.float_data_.resize(length * dim); - memcpy(vectors_data.float_data_.data(), vectors, length * dim * sizeof(float)); - vectors_data.id_array_.resize(length); - memcpy(vectors_data.id_array_.data(), vector_ids, length * sizeof(IDNumber)); - VectorSourcePtr source = std::make_shared(vectors_data); - - std::unique_lock lock(mutex_); - - return InsertVectorsNoLock(collection_id, source, lsn); -} - -Status -MemManagerImpl::InsertVectors(const std::string& collection_id, int64_t length, const IDNumber* vector_ids, int64_t dim, - const uint8_t* vectors, uint64_t lsn) { - VectorsData vectors_data; - vectors_data.vector_count_ = length; - vectors_data.binary_data_.resize(length * dim); - memcpy(vectors_data.binary_data_.data(), vectors, length * dim * sizeof(uint8_t)); - vectors_data.id_array_.resize(length); - memcpy(vectors_data.id_array_.data(), vector_ids, length * sizeof(IDNumber)); - VectorSourcePtr source = std::make_shared(vectors_data); - - std::unique_lock lock(mutex_); - - return InsertVectorsNoLock(collection_id, source, lsn); -} - -Status -MemManagerImpl::InsertEntities(const std::string& collection_id, int64_t length, const IDNumber* vector_ids, - int64_t dim, const float* vectors, - const std::unordered_map& attr_nbytes, - const std::unordered_map& attr_size, - const std::unordered_map>& attr_data, uint64_t lsn) { - VectorsData vectors_data; - vectors_data.vector_count_ = length; - vectors_data.float_data_.resize(length * dim); - memcpy(vectors_data.float_data_.data(), vectors, length * dim * sizeof(float)); - vectors_data.id_array_.resize(length); - memcpy(vectors_data.id_array_.data(), vector_ids, length * sizeof(IDNumber)); - - VectorSourcePtr source = std::make_shared(vectors_data, attr_nbytes, attr_size, attr_data); - - std::unique_lock lock(mutex_); - - return InsertEntitiesNoLock(collection_id, source, lsn); -} - -Status -MemManagerImpl::InsertVectorsNoLock(const std::string& collection_id, const VectorSourcePtr& source, uint64_t lsn) { - MemTablePtr mem = GetMemByTable(collection_id); - mem->SetLSN(lsn); - - auto status = mem->Add(source); - return status; -} - -Status -MemManagerImpl::InsertEntitiesNoLock(const std::string& collection_id, const milvus::engine::VectorSourcePtr& source, - uint64_t lsn) { - MemTablePtr mem = GetMemByTable(collection_id); - mem->SetLSN(lsn); - - auto status = mem->AddEntities(source); - return status; -} - -Status -MemManagerImpl::DeleteVector(const std::string& collection_id, IDNumber vector_id, uint64_t lsn) { - std::unique_lock lock(mutex_); - MemTablePtr mem = GetMemByTable(collection_id); - mem->SetLSN(lsn); - auto status = mem->Delete(vector_id); - return status; -} - -Status -MemManagerImpl::DeleteVectors(const std::string& collection_id, int64_t length, const IDNumber* vector_ids, - uint64_t lsn) { - std::unique_lock lock(mutex_); - MemTablePtr mem = GetMemByTable(collection_id); - mem->SetLSN(lsn); - - IDNumbers ids; - ids.resize(length); - memcpy(ids.data(), vector_ids, length * sizeof(IDNumber)); - - auto status = mem->Delete(ids); - if (!status.ok()) { - return status; - } - - // // TODO(zhiru): loop for now - // for (auto& id : ids) { - // auto status = mem->Delete(id); - // if (!status.ok()) { - // return status; - // } - // } - - return Status::OK(); -} - -Status -MemManagerImpl::Flush(const std::string& collection_id) { - ToImmutable(collection_id); - // TODO: There is actually only one memTable in the immutable list - MemList temp_immutable_list; - { - std::unique_lock lock(mutex_); - immu_mem_list_.swap(temp_immutable_list); - } - - std::unique_lock lock(serialization_mtx_); - auto max_lsn = GetMaxLSN(temp_immutable_list); - for (auto& mem : temp_immutable_list) { - LOG_ENGINE_DEBUG_ << "Flushing collection: " << mem->GetTableId(); - auto status = mem->Serialize(max_lsn, true); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << "Flush collection " << mem->GetTableId() << " failed"; - return status; - } - LOG_ENGINE_DEBUG_ << "Flushed collection: " << mem->GetTableId(); - } - - return Status::OK(); -} - -Status -MemManagerImpl::Flush(std::set& collection_ids) { - ToImmutable(); - - MemList temp_immutable_list; - { - std::unique_lock lock(mutex_); - immu_mem_list_.swap(temp_immutable_list); - } - - std::unique_lock lock(serialization_mtx_); - collection_ids.clear(); - auto max_lsn = GetMaxLSN(temp_immutable_list); - for (auto& mem : temp_immutable_list) { - LOG_ENGINE_DEBUG_ << "Flushing collection: " << mem->GetTableId(); - auto status = mem->Serialize(max_lsn, true); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << "Flush collection " << mem->GetTableId() << " failed"; - return status; - } - collection_ids.insert(mem->GetTableId()); - LOG_ENGINE_DEBUG_ << "Flushed collection: " << mem->GetTableId(); - } - - meta_->SetGlobalLastLSN(max_lsn); - - return Status::OK(); -} - -Status -MemManagerImpl::ToImmutable(const std::string& collection_id) { - std::unique_lock lock(mutex_); - auto memIt = mem_id_map_.find(collection_id); - if (memIt != mem_id_map_.end()) { - if (!memIt->second->Empty()) { - immu_mem_list_.push_back(memIt->second); - mem_id_map_.erase(memIt); - } - // std::string err_msg = "Could not find collection = " + collection_id + " to flush"; - // LOG_ENGINE_ERROR_ << err_msg; - // return Status(DB_NOT_FOUND, err_msg); - } - - return Status::OK(); -} - -Status -MemManagerImpl::ToImmutable() { - std::unique_lock lock(mutex_); - MemIdMap temp_map; - for (auto& kv : mem_id_map_) { - if (kv.second->Empty()) { - // empty collection without any deletes, no need to serialize - temp_map.insert(kv); - } else { - immu_mem_list_.push_back(kv.second); - } - } - - mem_id_map_.swap(temp_map); - return Status::OK(); -} - -Status -MemManagerImpl::EraseMemVector(const std::string& collection_id) { - { // erase MemVector from rapid-insert cache - std::unique_lock lock(mutex_); - mem_id_map_.erase(collection_id); - } - - { // erase MemVector from serialize cache - std::unique_lock lock(serialization_mtx_); - MemList temp_list; - for (auto& mem : immu_mem_list_) { - if (mem->GetTableId() != collection_id) { - temp_list.push_back(mem); - } - } - immu_mem_list_.swap(temp_list); - } - - return Status::OK(); -} - -size_t -MemManagerImpl::GetCurrentMutableMem() { - size_t total_mem = 0; - std::unique_lock lock(mutex_); - for (auto& kv : mem_id_map_) { - auto memTable = kv.second; - total_mem += memTable->GetCurrentMem(); - } - return total_mem; -} - -size_t -MemManagerImpl::GetCurrentImmutableMem() { - size_t total_mem = 0; - std::unique_lock lock(serialization_mtx_); - for (auto& mem_table : immu_mem_list_) { - total_mem += mem_table->GetCurrentMem(); - } - return total_mem; -} - -size_t -MemManagerImpl::GetCurrentMem() { - return GetCurrentMutableMem() + GetCurrentImmutableMem(); -} - -uint64_t -MemManagerImpl::GetMaxLSN(const MemList& tables) { - uint64_t max_lsn = 0; - for (auto& collection : tables) { - auto cur_lsn = collection->GetLSN(); - if (collection->GetLSN() > max_lsn) { - max_lsn = cur_lsn; - } - } - return max_lsn; -} - -void -MemManagerImpl::OnInsertBufferSizeChanged(int64_t value) { - options_.insert_buffer_size_ = value * GB; -} - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/insert/MemManagerImpl.h b/core/src/db/insert/MemManagerImpl.h deleted file mode 100644 index 0eb957f437..0000000000 --- a/core/src/db/insert/MemManagerImpl.h +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config/Config.h" -#include "config/handler/CacheConfigHandler.h" -#include "db/insert/MemManager.h" -#include "db/insert/MemTable.h" -#include "db/meta/Meta.h" -#include "utils/Status.h" - -namespace milvus { -namespace engine { - -class MemManagerImpl : public MemManager, public server::CacheConfigHandler { - public: - using Ptr = std::shared_ptr; - using MemIdMap = std::map; - using MemList = std::vector; - - MemManagerImpl(const meta::MetaPtr& meta, const DBOptions& options) : meta_(meta), options_(options) { - SetIdentity("MemManagerImpl"); - AddInsertBufferSizeListener(); - } - - Status - InsertVectors(const std::string& collection_id, int64_t length, const IDNumber* vector_ids, int64_t dim, - const float* vectors, uint64_t lsn) override; - - Status - InsertVectors(const std::string& collection_id, int64_t length, const IDNumber* vector_ids, int64_t dim, - const uint8_t* vectors, uint64_t lsn) override; - - Status - InsertEntities(const std::string& collection_id, int64_t length, const IDNumber* vector_ids, int64_t dim, - const float* vectors, const std::unordered_map& attr_nbytes, - const std::unordered_map& attr_size, - const std::unordered_map>& attr_data, uint64_t lsn) override; - - Status - DeleteVector(const std::string& collection_id, IDNumber vector_id, uint64_t lsn) override; - - Status - DeleteVectors(const std::string& collection_id, int64_t length, const IDNumber* vector_ids, uint64_t lsn) override; - - Status - Flush(const std::string& collection_id) override; - - Status - Flush(std::set& collection_ids) override; - - // Status - // Serialize(std::set& table_ids) override; - - Status - EraseMemVector(const std::string& collection_id) override; - - size_t - GetCurrentMutableMem() override; - - size_t - GetCurrentImmutableMem() override; - - size_t - GetCurrentMem() override; - - protected: - void - OnInsertBufferSizeChanged(int64_t value) override; - - private: - MemTablePtr - GetMemByTable(const std::string& collection_id); - - Status - InsertVectorsNoLock(const std::string& collection_id, const VectorSourcePtr& source, uint64_t lsn); - - Status - InsertEntitiesNoLock(const std::string& collection_id, const VectorSourcePtr& source, uint64_t lsn); - - Status - ToImmutable(); - - Status - ToImmutable(const std::string& collection_id); - - uint64_t - GetMaxLSN(const MemList& tables); - - MemIdMap mem_id_map_; - MemList immu_mem_list_; - meta::MetaPtr meta_; - DBOptions options_; - std::mutex mutex_; - std::mutex serialization_mtx_; -}; // NewMemManager - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/insert/MemTable.cpp b/core/src/db/insert/MemTable.cpp deleted file mode 100644 index 280ede58e9..0000000000 --- a/core/src/db/insert/MemTable.cpp +++ /dev/null @@ -1,416 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include -#include -#include -#include - -#include "cache/CpuCacheMgr.h" -#include "db/Utils.h" -#include "db/insert/MemTable.h" -#include "db/meta/FilesHolder.h" -#include "knowhere/index/vector_index/VecIndex.h" -#include "segment/SegmentReader.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" - -namespace milvus { -namespace engine { - -MemTable::MemTable(const std::string& collection_id, const meta::MetaPtr& meta, const DBOptions& options) - : collection_id_(collection_id), meta_(meta), options_(options) { - SetIdentity("MemTable"); - AddCacheInsertDataListener(); -} - -Status -MemTable::Add(const VectorSourcePtr& source) { - while (!source->AllAdded()) { - MemTableFilePtr current_mem_table_file; - if (!mem_table_file_list_.empty()) { - current_mem_table_file = mem_table_file_list_.back(); - } - - Status status; - if (mem_table_file_list_.empty() || current_mem_table_file->IsFull()) { - MemTableFilePtr new_mem_table_file = std::make_shared(collection_id_, meta_, options_); - status = new_mem_table_file->Add(source); - if (status.ok()) { - mem_table_file_list_.emplace_back(new_mem_table_file); - } - } else { - status = current_mem_table_file->Add(source); - } - - if (!status.ok()) { - std::string err_msg = "Insert failed: " + status.ToString(); - LOG_ENGINE_ERROR_ << LogOut("[%s][%ld] ", "insert", 0) << err_msg; - return Status(DB_ERROR, err_msg); - } - } - return Status::OK(); -} - -Status -MemTable::AddEntities(const milvus::engine::VectorSourcePtr& source) { - while (!source->AllAdded()) { - MemTableFilePtr current_mem_table_file; - if (!mem_table_file_list_.empty()) { - current_mem_table_file = mem_table_file_list_.back(); - } - - Status status; - if (mem_table_file_list_.empty() || current_mem_table_file->IsFull()) { - MemTableFilePtr new_mem_table_file = std::make_shared(collection_id_, meta_, options_); - status = new_mem_table_file->AddEntities(source); - if (status.ok()) { - mem_table_file_list_.emplace_back(new_mem_table_file); - } - } else { - status = current_mem_table_file->AddEntities(source); - } - - if (!status.ok()) { - std::string err_msg = "Insert failed: " + status.ToString(); - LOG_ENGINE_ERROR_ << LogOut("[%s][%ld] ", "insert", 0) << err_msg; - return Status(DB_ERROR, err_msg); - } - } - return Status::OK(); -} - -Status -MemTable::Delete(segment::doc_id_t doc_id) { - // Locate which collection file the doc id lands in - for (auto& table_file : mem_table_file_list_) { - table_file->Delete(doc_id); - } - // Add the id to delete list so it can be applied to other segments on disk during the next flush - doc_ids_to_delete_.insert(doc_id); - - return Status::OK(); -} - -Status -MemTable::Delete(const std::vector& doc_ids) { - // Locate which collection file the doc id lands in - for (auto& table_file : mem_table_file_list_) { - table_file->Delete(doc_ids); - } - // Add the id to delete list so it can be applied to other segments on disk during the next flush - for (auto& id : doc_ids) { - doc_ids_to_delete_.insert(id); - } - - return Status::OK(); -} - -void -MemTable::GetCurrentMemTableFile(MemTableFilePtr& mem_table_file) { - mem_table_file = mem_table_file_list_.back(); -} - -size_t -MemTable::GetTableFileCount() { - return mem_table_file_list_.size(); -} - -Status -MemTable::Serialize(uint64_t wal_lsn, bool apply_delete) { - TimeRecorder recorder("MemTable::Serialize collection " + collection_id_); - - if (!doc_ids_to_delete_.empty() && apply_delete) { - auto status = ApplyDeletes(); - if (!status.ok()) { - return Status(DB_ERROR, status.message()); - } - } - - meta::SegmentsSchema update_files; - for (auto mem_table_file = mem_table_file_list_.begin(); mem_table_file != mem_table_file_list_.end();) { - auto status = (*mem_table_file)->Serialize(wal_lsn); - update_files.push_back((*mem_table_file)->GetSegmentSchema()); - if (!status.ok()) { - return status; - } - - LOG_ENGINE_DEBUG_ << "Flushed segment " << (*mem_table_file)->GetSegmentId(); - - { - std::lock_guard lock(mutex_); - mem_table_file = mem_table_file_list_.erase(mem_table_file); - } - } - - // Update meta files and flush lsn - auto status = meta_->UpdateCollectionFiles(update_files); - if (!status.ok()) { - return status; - } - - status = meta_->UpdateCollectionFlushLSN(collection_id_, wal_lsn); - if (!status.ok()) { - std::string err_msg = "Failed to write flush lsn to meta: " + status.ToString(); - LOG_ENGINE_ERROR_ << err_msg; - return Status(DB_ERROR, err_msg); - } - - recorder.RecordSection("Finished flushing"); - - return Status::OK(); -} - -bool -MemTable::Empty() { - return mem_table_file_list_.empty() && doc_ids_to_delete_.empty(); -} - -const std::string& -MemTable::GetTableId() const { - return collection_id_; -} - -size_t -MemTable::GetCurrentMem() { - std::lock_guard lock(mutex_); - size_t total_mem = 0; - for (auto& mem_table_file : mem_table_file_list_) { - total_mem += mem_table_file->GetCurrentMem(); - } - return total_mem; -} - -Status -MemTable::ApplyDeletes() { - // Applying deletes to other segments on disk and their corresponding cache: - // For each segment in collection: - // Load its bloom filter - // For each id in delete list: - // If present, add the uid to segment's uid list - // For each segment - // Get its cache if exists - // Load its uids file. - // Scan the uids, if any uid in segment's uid list exists: - // add its offset to deletedDoc - // remove the id from bloom filter - // set black list in cache - // Serialize segment's deletedDoc TODO(zhiru): append directly to previous file for now, may have duplicates - // Serialize bloom filter - - LOG_ENGINE_DEBUG_ << "Applying " << doc_ids_to_delete_.size() << " deletes in collection: " << collection_id_; - - TimeRecorder recorder("MemTable::ApplyDeletes for collection " + collection_id_); - - std::vector file_types{meta::SegmentSchema::FILE_TYPE::RAW, meta::SegmentSchema::FILE_TYPE::TO_INDEX, - meta::SegmentSchema::FILE_TYPE::BACKUP}; - meta::FilesHolder files_holder; - auto status = meta_->FilesByType(collection_id_, file_types, files_holder); - if (!status.ok()) { - std::string err_msg = "Failed to apply deletes: " + status.ToString(); - LOG_ENGINE_ERROR_ << err_msg; - return Status(DB_ERROR, err_msg); - } - - // attention: here is a copy, not reference, since files_holder.UnmarkFile will change the array internal - milvus::engine::meta::SegmentsSchema files = files_holder.HoldFiles(); - - // which file need to be apply delete - std::unordered_map> ids_to_check_map; // file id mapping to delete ids - for (auto& file : files) { - std::string segment_dir; - utils::GetParentPath(file.location_, segment_dir); - - segment::SegmentReader segment_reader(segment_dir); - segment::IdBloomFilterPtr id_bloom_filter_ptr; - segment_reader.LoadBloomFilter(id_bloom_filter_ptr); - - for (auto& id : doc_ids_to_delete_) { - if (id_bloom_filter_ptr->Check(id)) { - ids_to_check_map[file.id_].emplace_back(id); - } - } - } - - // release unused files - for (auto& file : files) { - if (ids_to_check_map.find(file.id_) == ids_to_check_map.end()) { - files_holder.UnmarkFile(file); - } - } - - // attention: here is a copy, not reference, since files_holder.UnmarkFile will change the array internal - milvus::engine::meta::SegmentsSchema hold_files = files_holder.HoldFiles(); - recorder.RecordSection("Found " + std::to_string(hold_files.size()) + " segment to apply deletes"); - - meta::SegmentsSchema files_to_update; - for (auto& file : hold_files) { - LOG_ENGINE_DEBUG_ << "Applying deletes in segment: " << file.segment_id_; - - TimeRecorder rec("handle segment " + file.segment_id_); - - std::string segment_dir; - utils::GetParentPath(file.location_, segment_dir); - segment::SegmentReader segment_reader(segment_dir); - - auto& segment_id = file.segment_id_; - meta::FilesHolder segment_holder; - status = meta_->GetCollectionFilesBySegmentId(segment_id, segment_holder); - if (!status.ok()) { - break; - } - - // Get all index that contains blacklist in cache - std::vector indexes; - std::vector blacklists; - milvus::engine::meta::SegmentsSchema& segment_files = segment_holder.HoldFiles(); - for (auto& segment_file : segment_files) { - auto data_obj_ptr = cache::CpuCacheMgr::GetInstance()->GetIndex(segment_file.location_); - auto index = std::static_pointer_cast(data_obj_ptr); - if (index != nullptr) { - faiss::ConcurrentBitsetPtr blacklist = index->GetBlacklist(); - if (blacklist != nullptr) { - indexes.emplace_back(index); - blacklists.emplace_back(blacklist); - } - } - } - - std::vector uids; - status = segment_reader.LoadUids(uids); - if (!status.ok()) { - break; - } - segment::IdBloomFilterPtr id_bloom_filter_ptr; - status = segment_reader.LoadBloomFilter(id_bloom_filter_ptr); - if (!status.ok()) { - break; - } - - auto& ids_to_check = ids_to_check_map[file.id_]; - - segment::DeletedDocsPtr deleted_docs = std::make_shared(); - - rec.RecordSection("Loading uids and deleted docs"); - - std::sort(ids_to_check.begin(), ids_to_check.end()); - - rec.RecordSection("Sorting " + std::to_string(ids_to_check.size()) + " ids"); - - size_t delete_count = 0; - auto find_diff = std::chrono::duration::zero(); - auto set_diff = std::chrono::duration::zero(); - - for (size_t i = 0; i < uids.size(); ++i) { - auto find_start = std::chrono::high_resolution_clock::now(); - - auto found = std::binary_search(ids_to_check.begin(), ids_to_check.end(), uids[i]); - - auto find_end = std::chrono::high_resolution_clock::now(); - find_diff += (find_end - find_start); - - if (found) { - auto set_start = std::chrono::high_resolution_clock::now(); - - delete_count++; - - deleted_docs->AddDeletedDoc(i); - - if (id_bloom_filter_ptr->Check(uids[i])) { - id_bloom_filter_ptr->Remove(uids[i]); - } - - for (auto& blacklist : blacklists) { - if (!blacklist->test(i)) { - blacklist->set(i); - } - } - - auto set_end = std::chrono::high_resolution_clock::now(); - set_diff += (set_end - set_start); - } - } - - LOG_ENGINE_DEBUG_ << "Finding " << ids_to_check.size() << " uids in " << uids.size() << " uids took " - << find_diff.count() << " s in total"; - LOG_ENGINE_DEBUG_ << "Setting deleted docs and bloom filter took " << set_diff.count() << " s in total"; - - rec.RecordSection("Find uids and set deleted docs and bloom filter"); - - for (size_t i = 0; i < indexes.size(); ++i) { - indexes[i]->SetBlacklist(blacklists[i]); - } - - segment::Segment tmp_segment; - segment::SegmentWriter segment_writer(segment_dir); - status = segment_writer.WriteDeletedDocs(deleted_docs); - if (!status.ok()) { - break; - } - - rec.RecordSection("Appended " + std::to_string(deleted_docs->GetSize()) + " offsets to deleted docs"); - - status = segment_writer.WriteBloomFilter(id_bloom_filter_ptr); - if (!status.ok()) { - break; - } - - rec.RecordSection("Updated bloom filter"); - - // Update collection file row count - for (auto& segment_file : segment_files) { - if (segment_file.file_type_ == meta::SegmentSchema::RAW || - segment_file.file_type_ == meta::SegmentSchema::TO_INDEX || - segment_file.file_type_ == meta::SegmentSchema::INDEX || - segment_file.file_type_ == meta::SegmentSchema::BACKUP) { - segment_file.row_count_ -= delete_count; - files_to_update.emplace_back(segment_file); - } - } - rec.RecordSection("Update collection file row count in vector"); - } - - recorder.RecordSection("Finished " + std::to_string(ids_to_check_map.size()) + " segment to apply deletes"); - - status = meta_->UpdateCollectionFilesRowCount(files_to_update); - - if (!status.ok()) { - std::string err_msg = "Failed to apply deletes: " + status.ToString(); - LOG_ENGINE_ERROR_ << err_msg; - return Status(DB_ERROR, err_msg); - } - - doc_ids_to_delete_.clear(); - - recorder.RecordSection("Update deletes to meta"); - recorder.ElapseFromBegin("Finished deletes"); - - return Status::OK(); -} - -uint64_t -MemTable::GetLSN() { - return lsn_; -} - -void -MemTable::SetLSN(uint64_t lsn) { - lsn_ = lsn; -} - -void -MemTable::OnCacheInsertDataChanged(bool value) { - options_.insert_cache_immediately_ = value; -} - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/insert/MemTable.h b/core/src/db/insert/MemTable.h deleted file mode 100644 index 8940e8b495..0000000000 --- a/core/src/db/insert/MemTable.h +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "config/handler/CacheConfigHandler.h" -#include "db/insert/MemTableFile.h" -#include "db/insert/VectorSource.h" -#include "utils/Status.h" - -namespace milvus { -namespace engine { - -class MemTable : public server::CacheConfigHandler { - public: - using MemTableFileList = std::vector; - - MemTable(const std::string& collection_id, const meta::MetaPtr& meta, const DBOptions& options); - - Status - Add(const VectorSourcePtr& source); - - Status - AddEntities(const VectorSourcePtr& source); - - Status - Delete(segment::doc_id_t doc_id); - - Status - Delete(const std::vector& doc_ids); - - void - GetCurrentMemTableFile(MemTableFilePtr& mem_table_file); - - size_t - GetTableFileCount(); - - Status - Serialize(uint64_t wal_lsn, bool apply_delete = true); - - bool - Empty(); - - const std::string& - GetTableId() const; - - size_t - GetCurrentMem(); - - uint64_t - GetLSN(); - - void - SetLSN(uint64_t lsn); - - protected: - void - OnCacheInsertDataChanged(bool value) override; - - private: - Status - ApplyDeletes(); - - private: - const std::string collection_id_; - - MemTableFileList mem_table_file_list_; - - meta::MetaPtr meta_; - - DBOptions options_; - - std::mutex mutex_; - - std::set doc_ids_to_delete_; - - std::atomic lsn_; -}; // MemTable - -using MemTablePtr = std::shared_ptr; - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/insert/MemTableFile.cpp b/core/src/db/insert/MemTableFile.cpp deleted file mode 100644 index 23b47606e0..0000000000 --- a/core/src/db/insert/MemTableFile.cpp +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/insert/MemTableFile.h" - -#include -#include -#include -#include -#include - -#include "db/Constants.h" -#include "db/Utils.h" -#include "db/engine/EngineFactory.h" -#include "metrics/Metrics.h" -#include "segment/SegmentReader.h" -#include "utils/Log.h" -#include "utils/ValidationUtil.h" - -namespace milvus { -namespace engine { - -MemTableFile::MemTableFile(const std::string& collection_id, const meta::MetaPtr& meta, const DBOptions& options) - : collection_id_(collection_id), meta_(meta), options_(options) { - current_mem_ = 0; - auto status = CreateCollectionFile(); - if (status.ok()) { - /*execution_engine_ = EngineFactory::Build( - table_file_schema_.dimension_, table_file_schema_.location_, (EngineType)table_file_schema_.engine_type_, - (MetricType)table_file_schema_.metric_type_, table_file_schema_.nlist_);*/ - std::string directory; - utils::GetParentPath(table_file_schema_.location_, directory); - segment_writer_ptr_ = std::make_shared(directory); - } - - SetIdentity("MemTableFile"); - AddCacheInsertDataListener(); -} - -Status -MemTableFile::CreateCollectionFile() { - meta::SegmentSchema table_file_schema; - table_file_schema.collection_id_ = collection_id_; - auto status = meta_->CreateCollectionFile(table_file_schema); - if (status.ok()) { - table_file_schema_ = table_file_schema; - } else { - std::string err_msg = "MemTableFile::CreateCollectionFile failed: " + status.ToString(); - LOG_ENGINE_ERROR_ << err_msg; - } - return status; -} - -Status -MemTableFile::Add(const VectorSourcePtr& source) { - if (table_file_schema_.dimension_ <= 0) { - std::string err_msg = - "MemTableFile::Add: table_file_schema dimension = " + std::to_string(table_file_schema_.dimension_) + - ", collection_id = " + table_file_schema_.collection_id_; - LOG_ENGINE_ERROR_ << LogOut("[%s][%ld]", "insert", 0) << err_msg; - return Status(DB_ERROR, "Not able to create collection file"); - } - - size_t single_vector_mem_size = source->SingleVectorSize(table_file_schema_.dimension_); - size_t mem_left = GetMemLeft(); - if (mem_left >= single_vector_mem_size) { - size_t num_vectors_to_add = std::ceil(mem_left / single_vector_mem_size); - size_t num_vectors_added; - - auto status = source->Add(/*execution_engine_,*/ segment_writer_ptr_, table_file_schema_, num_vectors_to_add, - num_vectors_added); - if (status.ok()) { - current_mem_ += (num_vectors_added * single_vector_mem_size); - } - return status; - } - return Status::OK(); -} - -Status -MemTableFile::AddEntities(const VectorSourcePtr& source) { - if (table_file_schema_.dimension_ <= 0) { - std::string err_msg = - "MemTableFile::Add: table_file_schema dimension = " + std::to_string(table_file_schema_.dimension_) + - ", collection_id = " + table_file_schema_.collection_id_; - LOG_ENGINE_ERROR_ << LogOut("[%s][%ld]", "insert", 0) << err_msg; - return Status(DB_ERROR, "Not able to create collection file"); - } - - size_t single_entity_mem_size = source->SingleEntitySize(table_file_schema_.dimension_); - size_t mem_left = GetMemLeft(); - if (mem_left >= single_entity_mem_size) { - size_t num_entities_to_add = std::ceil(mem_left / single_entity_mem_size); - size_t num_entities_added; - - auto status = - source->AddEntities(segment_writer_ptr_, table_file_schema_, num_entities_to_add, num_entities_added); - - if (status.ok()) { - current_mem_ += (num_entities_added * single_entity_mem_size); - } - return status; - } - return Status::OK(); -} - -Status -MemTableFile::Delete(segment::doc_id_t doc_id) { - segment::SegmentPtr segment_ptr; - segment_writer_ptr_->GetSegment(segment_ptr); - // Check wither the doc_id is present, if yes, delete it's corresponding buffer - auto uids = segment_ptr->vectors_ptr_->GetUids(); - auto found = std::find(uids.begin(), uids.end(), doc_id); - if (found != uids.end()) { - auto offset = std::distance(uids.begin(), found); - segment_ptr->vectors_ptr_->Erase(offset); - } - - return Status::OK(); -} - -Status -MemTableFile::Delete(const std::vector& doc_ids) { - segment::SegmentPtr segment_ptr; - segment_writer_ptr_->GetSegment(segment_ptr); - - // Check wither the doc_id is present, if yes, delete it's corresponding buffer - - std::vector temp; - temp.resize(doc_ids.size()); - memcpy(temp.data(), doc_ids.data(), doc_ids.size() * sizeof(segment::doc_id_t)); - - std::sort(temp.begin(), temp.end()); - - auto uids = segment_ptr->vectors_ptr_->GetUids(); - - size_t deleted = 0; - size_t loop = uids.size(); - for (size_t i = 0; i < loop; ++i) { - if (std::binary_search(temp.begin(), temp.end(), uids[i])) { - segment_ptr->vectors_ptr_->Erase(i - deleted); - ++deleted; - } - } - /* - for (auto& doc_id : doc_ids) { - auto found = std::find(uids.begin(), uids.end(), doc_id); - if (found != uids.end()) { - auto offset = std::distance(uids.begin(), found); - segment_ptr->vectors_ptr_->Erase(offset); - uids = segment_ptr->vectors_ptr_->GetUids(); - } - } - */ - - return Status::OK(); -} - -size_t -MemTableFile::GetCurrentMem() { - return current_mem_; -} - -size_t -MemTableFile::GetMemLeft() { - return (MAX_TABLE_FILE_MEM - current_mem_); -} - -bool -MemTableFile::IsFull() { - size_t single_vector_mem_size = table_file_schema_.dimension_ * FLOAT_TYPE_SIZE; - return (GetMemLeft() < single_vector_mem_size); -} - -Status -MemTableFile::Serialize(uint64_t wal_lsn) { - int64_t size = GetCurrentMem(); - server::CollectSerializeMetrics metrics(size); - - auto status = segment_writer_ptr_->Serialize(); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << "Failed to serialize segment: " << table_file_schema_.segment_id_; - - /* Can't mark it as to_delete because data is stored in this mem collection file. Any further flush - * will try to serialize the same mem collection file and it won't be able to find the directory - * to write to or update the associated collection file in meta. - * - table_file_schema_.file_type_ = meta::SegmentSchema::TO_DELETE; - meta_->UpdateCollectionFile(table_file_schema_); - LOG_ENGINE_DEBUG_ << "Failed to serialize segment, mark file: " << table_file_schema_.file_id_ - << " to to_delete"; - */ - return status; - } - - // execution_engine_->Serialize(); - - // TODO(zhiru): - // table_file_schema_.file_size_ = execution_engine_->PhysicalSize(); - // table_file_schema_.row_count_ = execution_engine_->Count(); - table_file_schema_.file_size_ = segment_writer_ptr_->Size(); - table_file_schema_.row_count_ = segment_writer_ptr_->VectorCount(); - - // if index type isn't IDMAP, set file type to TO_INDEX if file size exceed index_file_size - // else set file type to RAW, no need to build index - if (table_file_schema_.engine_type_ != (int)EngineType::FAISS_IDMAP && - table_file_schema_.engine_type_ != (int)EngineType::FAISS_BIN_IDMAP) { - table_file_schema_.file_type_ = - (size >= table_file_schema_.index_file_size_) ? meta::SegmentSchema::TO_INDEX : meta::SegmentSchema::RAW; - } else { - table_file_schema_.file_type_ = meta::SegmentSchema::RAW; - } - - LOG_ENGINE_DEBUG_ << "New " << ((table_file_schema_.file_type_ == meta::SegmentSchema::RAW) ? "raw" : "to_index") - << " file " << table_file_schema_.file_id_ << " of size " << size << " bytes, lsn = " << wal_lsn; - - // TODO(zhiru): cache - /* - if (options_.insert_cache_immediately_) { - execution_engine_->Cache(); - } - */ - if (options_.insert_cache_immediately_) { - segment_writer_ptr_->Cache(); - } - - return status; -} - -const std::string& -MemTableFile::GetSegmentId() const { - return table_file_schema_.segment_id_; -} - -meta::SegmentSchema -MemTableFile::GetSegmentSchema() const { - return table_file_schema_; -} - -void -MemTableFile::OnCacheInsertDataChanged(bool value) { - options_.insert_cache_immediately_ = value; -} - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/insert/MemTableFile.h b/core/src/db/insert/MemTableFile.h deleted file mode 100644 index 4f24202840..0000000000 --- a/core/src/db/insert/MemTableFile.h +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include - -#include "config/handler/CacheConfigHandler.h" -#include "db/engine/ExecutionEngine.h" -#include "db/insert/VectorSource.h" -#include "db/meta/Meta.h" -#include "segment/SegmentWriter.h" -#include "utils/Status.h" - -namespace milvus { -namespace engine { - -class MemTableFile : public server::CacheConfigHandler { - public: - MemTableFile(const std::string& collection_id, const meta::MetaPtr& meta, const DBOptions& options); - - ~MemTableFile() = default; - - public: - Status - Add(const VectorSourcePtr& source); - - Status - AddEntities(const VectorSourcePtr& source); - - Status - Delete(segment::doc_id_t doc_id); - - Status - Delete(const std::vector& doc_ids); - - size_t - GetCurrentMem(); - - size_t - GetMemLeft(); - - bool - IsFull(); - - Status - Serialize(uint64_t wal_lsn); - - const std::string& - GetSegmentId() const; - - meta::SegmentSchema - GetSegmentSchema() const; - - protected: - void - OnCacheInsertDataChanged(bool value) override; - - private: - Status - CreateCollectionFile(); - - private: - const std::string collection_id_; - meta::SegmentSchema table_file_schema_; - meta::MetaPtr meta_; - DBOptions options_; - size_t current_mem_; - - // ExecutionEnginePtr execution_engine_; - segment::SegmentWriterPtr segment_writer_ptr_; -}; // MemTableFile - -using MemTableFilePtr = std::shared_ptr; - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/insert/VectorSource.cpp b/core/src/db/insert/VectorSource.cpp deleted file mode 100644 index ad659c19e7..0000000000 --- a/core/src/db/insert/VectorSource.cpp +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/insert/VectorSource.h" - -#include -#include - -#include "db/engine/EngineFactory.h" -#include "db/engine/ExecutionEngine.h" -#include "metrics/Metrics.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" - -namespace milvus { -namespace engine { - -VectorSource::VectorSource(VectorsData vectors) : vectors_(std::move(vectors)) { - current_num_vectors_added = 0; -} - -VectorSource::VectorSource(milvus::engine::VectorsData vectors, - const std::unordered_map& attr_nbytes, - const std::unordered_map& attr_size, - const std::unordered_map>& attr_data) - : vectors_(std::move(vectors)), attr_nbytes_(attr_nbytes), attr_size_(attr_size), attr_data_(attr_data) { - current_num_vectors_added = 0; - current_num_attrs_added = 0; -} - -Status -VectorSource::Add(const segment::SegmentWriterPtr& segment_writer_ptr, const meta::SegmentSchema& table_file_schema, - const size_t& num_vectors_to_add, size_t& num_vectors_added) { - uint64_t n = vectors_.vector_count_; - server::CollectAddMetrics metrics(n, table_file_schema.dimension_); - - num_vectors_added = - current_num_vectors_added + num_vectors_to_add <= n ? num_vectors_to_add : n - current_num_vectors_added; - IDNumbers vector_ids_to_add; - if (vectors_.id_array_.empty()) { - SafeIDGenerator& id_generator = SafeIDGenerator::GetInstance(); - Status status = id_generator.GetNextIDNumbers(num_vectors_added, vector_ids_to_add); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << LogOut("[%s][%ld]", "insert", 0) << "Generate ids fail: " << status.message(); - return status; - } - } else { - vector_ids_to_add.resize(num_vectors_added); - for (size_t pos = current_num_vectors_added; pos < current_num_vectors_added + num_vectors_added; pos++) { - vector_ids_to_add[pos - current_num_vectors_added] = vectors_.id_array_[pos]; - } - } - - Status status; - if (!vectors_.float_data_.empty()) { - LOG_ENGINE_DEBUG_ << LogOut("[%s][%ld]", "insert", 0) << "Insert float data into segment"; - auto size = num_vectors_added * table_file_schema.dimension_ * sizeof(float); - float* ptr = vectors_.float_data_.data() + current_num_vectors_added * table_file_schema.dimension_; - status = segment_writer_ptr->AddVectors(table_file_schema.file_id_, (uint8_t*)ptr, size, vector_ids_to_add); - } else if (!vectors_.binary_data_.empty()) { - LOG_ENGINE_DEBUG_ << LogOut("[%s][%ld]", "insert", 0) << "Insert binary data into segment"; - std::vector vectors; - auto size = num_vectors_added * SingleVectorSize(table_file_schema.dimension_) * sizeof(uint8_t); - uint8_t* ptr = - vectors_.binary_data_.data() + current_num_vectors_added * SingleVectorSize(table_file_schema.dimension_); - status = segment_writer_ptr->AddVectors(table_file_schema.file_id_, ptr, size, vector_ids_to_add); - } - - // Clear vector data - if (status.ok()) { - current_num_vectors_added += num_vectors_added; - // TODO(zhiru): remove - vector_ids_.insert(vector_ids_.end(), std::make_move_iterator(vector_ids_to_add.begin()), - std::make_move_iterator(vector_ids_to_add.end())); - } else { - LOG_ENGINE_ERROR_ << LogOut("[%s][%ld]", "insert", 0) << "VectorSource::Add failed: " + status.ToString(); - } - - return status; -} - -Status -VectorSource::AddEntities(const milvus::segment::SegmentWriterPtr& segment_writer_ptr, - const milvus::engine::meta::SegmentSchema& collection_file_schema, - const size_t& num_entities_to_add, size_t& num_entities_added) { - // TODO: n = vectors_.vector_count_;??? - uint64_t n = vectors_.vector_count_; - num_entities_added = - current_num_attrs_added + num_entities_to_add <= n ? num_entities_to_add : n - current_num_attrs_added; - IDNumbers vector_ids_to_add; - if (vectors_.id_array_.empty()) { - SafeIDGenerator& id_generator = SafeIDGenerator::GetInstance(); - Status status = id_generator.GetNextIDNumbers(num_entities_added, vector_ids_to_add); - if (!status.ok()) { - return status; - } - } else { - vector_ids_to_add.resize(num_entities_added); - for (size_t pos = current_num_attrs_added; pos < current_num_attrs_added + num_entities_added; pos++) { - vector_ids_to_add[pos - current_num_attrs_added] = vectors_.id_array_[pos]; - } - } - - Status status; - status = - segment_writer_ptr->AddAttrs(collection_file_schema.collection_id_, attr_size_, attr_data_, vector_ids_to_add); - - if (status.ok()) { - current_num_attrs_added += num_entities_added; - } else { - LOG_ENGINE_ERROR_ << LogOut("[%s][%ld]", "insert", 0) << "Generate ids fail: " << status.message(); - return status; - } - - std::vector vectors; - auto size = num_entities_added * collection_file_schema.dimension_ * sizeof(float); - vectors.resize(size); - memcpy(vectors.data(), vectors_.float_data_.data() + current_num_vectors_added * collection_file_schema.dimension_, - size); - LOG_ENGINE_DEBUG_ << LogOut("[%s][%ld]", "insert", 0) << "Insert into segment"; - status = segment_writer_ptr->AddVectors(collection_file_schema.file_id_, vectors, vector_ids_to_add); - if (status.ok()) { - current_num_vectors_added += num_entities_added; - vector_ids_.insert(vector_ids_.end(), std::make_move_iterator(vector_ids_to_add.begin()), - std::make_move_iterator(vector_ids_to_add.end())); - } - - // don't need to add current_num_attrs_added again - if (!status.ok()) { - LOG_ENGINE_ERROR_ << LogOut("[%s][%ld]", "insert", 0) << "VectorSource::Add failed: " + status.ToString(); - return status; - } - - return status; -} - -size_t -VectorSource::GetNumVectorsAdded() { - return current_num_vectors_added; -} - -size_t -VectorSource::SingleVectorSize(uint16_t dimension) { - if (!vectors_.float_data_.empty()) { - return dimension * FLOAT_TYPE_SIZE; - } else if (!vectors_.binary_data_.empty()) { - return dimension / 8; - } - - return 0; -} - -size_t -VectorSource::SingleEntitySize(uint16_t dimension) { - // TODO(yukun) add entity type and size compute - size_t size = 0; - size += dimension * FLOAT_TYPE_SIZE; - auto nbyte_it = attr_nbytes_.begin(); - for (; nbyte_it != attr_nbytes_.end(); ++nbyte_it) { - size += nbyte_it->second; - } - return size; -} - -bool -VectorSource::AllAdded() { - return (current_num_vectors_added == vectors_.vector_count_); -} - -IDNumbers -VectorSource::GetVectorIds() { - return vector_ids_; -} - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/insert/VectorSource.h b/core/src/db/insert/VectorSource.h deleted file mode 100644 index 46aa249b33..0000000000 --- a/core/src/db/insert/VectorSource.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include - -#include "db/IDGenerator.h" -#include "db/engine/ExecutionEngine.h" -#include "db/meta/Meta.h" -#include "segment/SegmentWriter.h" -#include "utils/Status.h" - -namespace milvus { -namespace engine { - -// TODO(zhiru): this class needs to be refactored once attributes are added - -class VectorSource { - public: - explicit VectorSource(VectorsData vectors); - - VectorSource(VectorsData vectors, const std::unordered_map& attr_nbytes, - const std::unordered_map& attr_size, - const std::unordered_map>& attr_data); - - Status - Add(const segment::SegmentWriterPtr& segment_writer_ptr, const meta::SegmentSchema& table_file_schema, - const size_t& num_vectors_to_add, size_t& num_vectors_added); - - Status - AddEntities(const segment::SegmentWriterPtr& segment_writer_ptr, const meta::SegmentSchema& collection_file_schema, - const size_t& num_attrs_to_add, size_t& num_attrs_added); - - size_t - GetNumVectorsAdded(); - - size_t - SingleVectorSize(uint16_t dimension); - - size_t - SingleEntitySize(uint16_t dimension); - - bool - AllAdded(); - - IDNumbers - GetVectorIds(); - - private: - VectorsData vectors_; - IDNumbers vector_ids_; - const std::unordered_map attr_nbytes_; - std::unordered_map attr_size_; - std::unordered_map> attr_data_; - - size_t current_num_vectors_added; - size_t current_num_attrs_added; -}; // VectorSource - -using VectorSourcePtr = std::shared_ptr; - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/merge/MergeAdaptiveStrategy.cpp b/core/src/db/merge/MergeAdaptiveStrategy.cpp deleted file mode 100644 index 6b948a8737..0000000000 --- a/core/src/db/merge/MergeAdaptiveStrategy.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/merge/MergeAdaptiveStrategy.h" -#include "utils/Log.h" - -#include -#include - -namespace milvus { -namespace engine { - -Status -MergeAdaptiveStrategy::RegroupFiles(meta::FilesHolder& files_holder, MergeFilesGroups& files_groups) { - meta::SegmentsSchema sort_files, ignore_files; - meta::SegmentsSchema& files = files_holder.HoldFiles(); - for (meta::SegmentsSchema::reverse_iterator iter = files.rbegin(); iter != files.rend(); ++iter) { - meta::SegmentSchema& file = *iter; - if (file.index_file_size_ > 0 && (int64_t)file.file_size_ > file.index_file_size_) { - // file that no need to merge - ignore_files.push_back(file); - continue; - } - sort_files.push_back(file); - } - - files_holder.UnmarkFiles(ignore_files); - - // no need to merge single file - if (sort_files.size() < 2) { - return Status::OK(); - } - - // two files, simply merge them - if (sort_files.size() == 2) { - files_groups.emplace_back(sort_files); - return Status::OK(); - } - - // arrange files by file size in descending order - std::sort(sort_files.begin(), sort_files.end(), - [](const meta::SegmentSchema& left, const meta::SegmentSchema& right) { - return left.file_size_ > right.file_size_; - }); - - // pick files to merge - int64_t index_file_size = sort_files[0].index_file_size_; - while (true) { - meta::SegmentsSchema temp_group; - int64_t sum_size = 0; - for (auto iter = sort_files.begin(); iter != sort_files.end();) { - meta::SegmentSchema& file = *iter; - if (sum_size + (int64_t)(file.file_size_) <= index_file_size) { - temp_group.push_back(file); - sum_size += file.file_size_; - iter = sort_files.erase(iter); - } else { - if ((iter + 1 == sort_files.end()) && sum_size < index_file_size) { - temp_group.push_back(file); - sort_files.erase(iter); - break; - } else { - ++iter; - } - } - } - - if (!temp_group.empty()) { - files_groups.emplace_back(temp_group); - } - - if (sort_files.empty()) { - break; - } - } - - return Status::OK(); -} - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/merge/MergeAdaptiveStrategy.h b/core/src/db/merge/MergeAdaptiveStrategy.h deleted file mode 100644 index 79e4ef5741..0000000000 --- a/core/src/db/merge/MergeAdaptiveStrategy.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -#include "db/merge/MergeStrategy.h" -#include "utils/Status.h" - -namespace milvus { -namespace engine { - -class MergeAdaptiveStrategy : public MergeStrategy { - public: - Status - RegroupFiles(meta::FilesHolder& files_holder, MergeFilesGroups& files_groups) override; -}; // MergeSimpleStrategy - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/merge/MergeLayeredStrategy.cpp b/core/src/db/merge/MergeLayeredStrategy.cpp deleted file mode 100644 index 298c9c3491..0000000000 --- a/core/src/db/merge/MergeLayeredStrategy.cpp +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/merge/MergeLayeredStrategy.h" -#include "db/Utils.h" -#include "db/meta/MetaConsts.h" -#include "utils/Log.h" - -#include -#include -#include - -namespace milvus { -namespace engine { - -const int64_t FORCE_MERGE_THREASHOLD = 30; // force merge files older this time(in second) - -Status -MergeLayeredStrategy::RegroupFiles(meta::FilesHolder& files_holder, MergeFilesGroups& files_groups) { - using LayerGroups = std::map; - // distribute files to groups according to file size(in byte) - LayerGroups layers = { - {1UL << 22, meta::SegmentsSchema()}, // 4MB - {1UL << 24, meta::SegmentsSchema()}, // 16MB - {1UL << 26, meta::SegmentsSchema()}, // 64MB - {1UL << 28, meta::SegmentsSchema()}, // 256MB - {1UL << 30, meta::SegmentsSchema()}, // 1GB - }; - - meta::SegmentsSchema sort_files = files_holder.HoldFiles(); - // no need to merge single file - if (sort_files.size() < 2) { - return Status::OK(); - } - - // arrange files by file size in descending order - std::sort(sort_files.begin(), sort_files.end(), - [](const meta::SegmentSchema& left, const meta::SegmentSchema& right) { - return left.file_size_ > right.file_size_; - }); - - // priority pick files that merge size greater than index_file_size - // to avoid big files such as index_file_size = 1024, merged file size = 1280 - int64_t index_file_size = sort_files[0].index_file_size_; - size_t biggest_size = sort_files[0].file_size_; - for (auto iter = sort_files.end() - 1; iter != sort_files.begin() + 1; --iter) { - if ((*iter).file_size_ + biggest_size > index_file_size) { - meta::SegmentsSchema temp_group = {*sort_files.begin(), *iter}; - files_groups.emplace_back(temp_group); - sort_files.erase(iter); - sort_files.erase(sort_files.begin()); - break; - } - } - - meta::SegmentsSchema huge_files; - // put files to layers - for (meta::SegmentsSchema::reverse_iterator iter = sort_files.rbegin(); iter != sort_files.rend(); ++iter) { - meta::SegmentSchema& file = *iter; - if (file.index_file_size_ > 0 && file.file_size_ > (size_t)(file.index_file_size_)) { - // file that no need to merge - files_holder.UnmarkFile(file); - continue; - } - - bool match = false; - for (auto& pair : layers) { - if ((*iter).file_size_ < pair.first) { - pair.second.push_back(file); - match = true; - break; - } - } - - if (!match) { - huge_files.push_back(file); - } - } - - auto now = utils::GetMicroSecTimeStamp(); - meta::SegmentsSchema force_merge_file; - for (auto& pair : layers) { - // skip empty layer - if (pair.second.empty()) { - continue; - } - - // layer has multiple files, merge along with the force_merge_file - if (!force_merge_file.empty()) { - for (auto& file : force_merge_file) { - pair.second.push_back(file); - } - force_merge_file.clear(); - } - - // layer only has one file, if the file is too old, force merge it, else no need to merge it - if (pair.second.size() == 1) { - if (now - pair.second[0].created_on_ > (int64_t)(FORCE_MERGE_THREASHOLD * meta::US_PS)) { - force_merge_file.push_back(pair.second[0]); - pair.second.clear(); - } - } - } - - // if force_merge_file is not allocated by any layer, combine it to huge_files - if (!force_merge_file.empty() && !huge_files.empty()) { - for (auto& file : force_merge_file) { - huge_files.push_back(file); - } - force_merge_file.clear(); - } - - // return result - for (auto& pair : layers) { - if (pair.second.size() == 1) { - // release file that no need to merge - files_holder.UnmarkFile(pair.second[0]); - } else if (pair.second.size() > 1) { - // create group - meta::SegmentsSchema temp_files; - temp_files.swap(pair.second); - files_groups.emplace_back(temp_files); - } - } - - if (huge_files.size() >= 1) { - meta::SegmentsSchema temp_files; - temp_files.swap(huge_files); - for (auto& file : force_merge_file) { - temp_files.push_back(file); - } - - if (temp_files.size() >= 2) { - // create group - files_groups.emplace_back(temp_files); - } else { - for (auto& file : huge_files) { - // release file that no need to merge - files_holder.UnmarkFile(file); - } - for (auto& file : force_merge_file) { - // release file that no need to merge - files_holder.UnmarkFile(file); - } - } - } else { - for (auto& file : force_merge_file) { - // release file that no need to merge - files_holder.UnmarkFile(file); - } - } - - return Status::OK(); -} - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/merge/MergeLayeredStrategy.h b/core/src/db/merge/MergeLayeredStrategy.h deleted file mode 100644 index 4442d6a359..0000000000 --- a/core/src/db/merge/MergeLayeredStrategy.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -#include "db/merge/MergeStrategy.h" -#include "utils/Status.h" - -namespace milvus { -namespace engine { - -class MergeLayeredStrategy : public MergeStrategy { - public: - Status - RegroupFiles(meta::FilesHolder& files_holder, MergeFilesGroups& files_groups) override; -}; // MergeLayeredStrategy - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/merge/MergeManager.h b/core/src/db/merge/MergeManager.h deleted file mode 100644 index ddc24aba47..0000000000 --- a/core/src/db/merge/MergeManager.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include - -#include "db/Types.h" -#include "db/meta/FilesHolder.h" -#include "utils/Status.h" - -namespace milvus { -namespace engine { - -// 1. SIMPLE -// merge in old way, merge files one by one, stop merge until file size exceed index_file_size -// 2. LAYERED -// distribute files to several groups according to file size -// firstly, define layers by file size: 4MB, 16MB, 64MB, 256MB, 1024MB -// if file size between 0MB~4MB, put it into layer "4" -// if file size between 4MB~16MB, put it into layer "16" -// if file size between 16MB~64MB, put it into layer "64" -// if file size between 64MB~256MB, put it into layer "256" -// if file size between 256MB~1024MB, put it into layer "1024" -// secondly, merge files for each group -// third, if some file's create time is 30 seconds ago, and it still un-merged, force merge with upper layer files -// 3. ADAPTIVE -// Pick files that sum of size is close to index_file_size, merge them -enum class MergeStrategyType { - SIMPLE = 1, - LAYERED = 2, - ADAPTIVE = 3, -}; - -class MergeManager { - public: - virtual MergeStrategyType - Strategy() const = 0; - - virtual Status - UseStrategy(MergeStrategyType type) = 0; - - virtual Status - MergeFiles(const std::string& collection_id) = 0; -}; // MergeManager - -using MergeManagerPtr = std::shared_ptr; - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/merge/MergeManagerFactory.cpp b/core/src/db/merge/MergeManagerFactory.cpp deleted file mode 100644 index 4f15281e16..0000000000 --- a/core/src/db/merge/MergeManagerFactory.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/merge/MergeManagerFactory.h" -#include "db/merge/MergeManagerImpl.h" -#include "utils/Exception.h" -#include "utils/Log.h" - -namespace milvus { -namespace engine { - -MergeManagerPtr -MergeManagerFactory::Build(const meta::MetaPtr& meta_ptr, const DBOptions& options) { - return std::make_shared(meta_ptr, options, MergeStrategyType::LAYERED); -} - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/merge/MergeManagerFactory.h b/core/src/db/merge/MergeManagerFactory.h deleted file mode 100644 index 533a321161..0000000000 --- a/core/src/db/merge/MergeManagerFactory.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "MergeManager.h" -#include "db/Options.h" - -#include - -namespace milvus { -namespace engine { - -class MergeManagerFactory { - public: - static MergeManagerPtr - Build(const meta::MetaPtr& meta_ptr, const DBOptions& options); -}; - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/merge/MergeManagerImpl.cpp b/core/src/db/merge/MergeManagerImpl.cpp deleted file mode 100644 index 963c75693a..0000000000 --- a/core/src/db/merge/MergeManagerImpl.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/merge/MergeManagerImpl.h" -#include "db/merge/MergeAdaptiveStrategy.h" -#include "db/merge/MergeLayeredStrategy.h" -#include "db/merge/MergeSimpleStrategy.h" -#include "db/merge/MergeStrategy.h" -#include "db/merge/MergeTask.h" -#include "utils/Exception.h" -#include "utils/Log.h" - -namespace milvus { -namespace engine { - -MergeManagerImpl::MergeManagerImpl(const meta::MetaPtr& meta_ptr, const DBOptions& options, MergeStrategyType type) - : meta_ptr_(meta_ptr), options_(options), strategy_type_(type) { - UseStrategy(type); -} - -Status -MergeManagerImpl::UseStrategy(MergeStrategyType type) { - switch (type) { - case MergeStrategyType::SIMPLE: { - strategy_ = std::make_shared(); - break; - } - case MergeStrategyType::LAYERED: { - strategy_ = std::make_shared(); - break; - } - case MergeStrategyType::ADAPTIVE: { - strategy_ = std::make_shared(); - break; - } - default: { - std::string msg = "Unsupported merge strategy type: " + std::to_string((int32_t)type); - LOG_ENGINE_ERROR_ << msg; - throw Exception(DB_ERROR, msg); - } - } - strategy_type_ = type; - - return Status::OK(); -} - -Status -MergeManagerImpl::MergeFiles(const std::string& collection_id) { - if (strategy_ == nullptr) { - std::string msg = "No merge strategy specified"; - LOG_ENGINE_ERROR_ << msg; - return Status(DB_ERROR, msg); - } - - meta::FilesHolder files_holder; - auto status = meta_ptr_->FilesToMerge(collection_id, files_holder); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << "Failed to get merge files for collection: " << collection_id; - return status; - } - - if (files_holder.HoldFiles().size() < 2) { - return Status::OK(); - } - - MergeFilesGroups files_groups; - status = strategy_->RegroupFiles(files_holder, files_groups); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << "Failed to regroup files for: " << collection_id - << ", continue to merge all files into one"; - - MergeTask task(meta_ptr_, options_, files_holder.HoldFiles()); - return task.Execute(); - } - - for (auto& group : files_groups) { - MergeTask task(meta_ptr_, options_, group); - status = task.Execute(); - - files_holder.UnmarkFiles(group); - } - - return status; -} - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/merge/MergeManagerImpl.h b/core/src/db/merge/MergeManagerImpl.h deleted file mode 100644 index 7772bc0fea..0000000000 --- a/core/src/db/merge/MergeManagerImpl.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "db/merge/MergeManager.h" -#include "db/merge/MergeStrategy.h" -#include "utils/Status.h" - -namespace milvus { -namespace engine { - -class MergeManagerImpl : public MergeManager { - public: - MergeManagerImpl(const meta::MetaPtr& meta_ptr, const DBOptions& options, MergeStrategyType type); - - MergeStrategyType - Strategy() const override { - return strategy_type_; - } - - Status - UseStrategy(MergeStrategyType type) override; - - Status - MergeFiles(const std::string& collection_id) override; - - private: - meta::MetaPtr meta_ptr_; - DBOptions options_; - - MergeStrategyType strategy_type_ = MergeStrategyType::SIMPLE; - MergeStrategyPtr strategy_; -}; // MergeManagerImpl - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/merge/MergeSimpleStrategy.cpp b/core/src/db/merge/MergeSimpleStrategy.cpp deleted file mode 100644 index 825d0d4bbe..0000000000 --- a/core/src/db/merge/MergeSimpleStrategy.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/merge/MergeSimpleStrategy.h" -#include "utils/Log.h" - -namespace milvus { -namespace engine { - -Status -MergeSimpleStrategy::RegroupFiles(meta::FilesHolder& files_holder, MergeFilesGroups& files_groups) { - files_groups.push_back(files_holder.HoldFiles()); - return Status::OK(); -} - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/merge/MergeSimpleStrategy.h b/core/src/db/merge/MergeSimpleStrategy.h deleted file mode 100644 index 3d6406ca29..0000000000 --- a/core/src/db/merge/MergeSimpleStrategy.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -#include "db/merge/MergeStrategy.h" -#include "utils/Status.h" - -namespace milvus { -namespace engine { - -class MergeSimpleStrategy : public MergeStrategy { - public: - Status - RegroupFiles(meta::FilesHolder& files_holder, MergeFilesGroups& files_groups) override; -}; // MergeSimpleStrategy - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/merge/MergeStrategy.h b/core/src/db/merge/MergeStrategy.h deleted file mode 100644 index cb00babb25..0000000000 --- a/core/src/db/merge/MergeStrategy.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include - -#include "db/Types.h" -#include "db/meta/FilesHolder.h" -#include "utils/Status.h" - -namespace milvus { -namespace engine { - -using MergeFilesGroups = std::vector; - -class MergeStrategy { - public: - virtual Status - RegroupFiles(meta::FilesHolder& files_holder, MergeFilesGroups& files_groups) = 0; -}; // MergeStrategy - -using MergeStrategyPtr = std::shared_ptr; - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/merge/MergeTask.cpp b/core/src/db/merge/MergeTask.cpp deleted file mode 100644 index df80d8ad3f..0000000000 --- a/core/src/db/merge/MergeTask.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/merge/MergeTask.h" -#include "db/Utils.h" -#include "metrics/Metrics.h" -#include "segment/SegmentReader.h" -#include "segment/SegmentWriter.h" -#include "utils/Log.h" - -#include -#include - -namespace milvus { -namespace engine { - -MergeTask::MergeTask(const meta::MetaPtr& meta_ptr, const DBOptions& options, meta::SegmentsSchema& files) - : meta_ptr_(meta_ptr), options_(options), files_(files) { -} - -Status -MergeTask::Execute() { - if (files_.empty()) { - return Status::OK(); - } - - // check input - std::string collection_id = files_.front().collection_id_; - for (auto& file : files_) { - if (file.collection_id_ != collection_id) { - return Status(DB_ERROR, "Cannot merge files across collections"); - } - } - - // step 1: create collection file - meta::SegmentSchema collection_file; - collection_file.collection_id_ = collection_id; - collection_file.file_type_ = meta::SegmentSchema::NEW_MERGE; - Status status = meta_ptr_->CreateCollectionFile(collection_file); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << "Failed to create collection: " << status.ToString(); - return status; - } - - // step 2: merge files - meta::SegmentsSchema updated; - - std::string new_segment_dir; - utils::GetParentPath(collection_file.location_, new_segment_dir); - auto segment_writer_ptr = std::make_shared(new_segment_dir); - - // attention: here is a copy, not reference, since files_holder.UnmarkFile will change the array internal - std::string info = "Merge task files size info:"; - for (auto& file : files_) { - info += std::to_string(file.file_size_); - info += ", "; - - server::CollectMergeFilesMetrics metrics; - std::string segment_dir_to_merge; - utils::GetParentPath(file.location_, segment_dir_to_merge); - segment_writer_ptr->Merge(segment_dir_to_merge, collection_file.file_id_); - - auto file_schema = file; - file_schema.file_type_ = meta::SegmentSchema::TO_DELETE; - updated.push_back(file_schema); - int64_t size = segment_writer_ptr->Size(); - if (size >= file_schema.index_file_size_) { - break; - } - } - LOG_ENGINE_DEBUG_ << info; - - // step 3: serialize to disk - try { - status = segment_writer_ptr->Serialize(); - } catch (std::exception& ex) { - std::string msg = "Serialize merged index encounter exception: " + std::string(ex.what()); - LOG_ENGINE_ERROR_ << msg; - status = Status(DB_ERROR, msg); - } - - if (!status.ok()) { - LOG_ENGINE_ERROR_ << "Failed to persist merged segment: " << new_segment_dir << ". Error: " << status.message(); - - // if failed to serialize merge file to disk - // typical error: out of disk space, out of memory or permission denied - collection_file.file_type_ = meta::SegmentSchema::TO_DELETE; - status = meta_ptr_->UpdateCollectionFile(collection_file); - LOG_ENGINE_DEBUG_ << "Failed to update file to index, mark file: " << collection_file.file_id_ - << " to to_delete"; - - return status; - } - - // step 4: update collection files state - // if index type isn't IDMAP, set file type to TO_INDEX if file size exceed index_file_size - // else set file type to RAW, no need to build index - if (!utils::IsRawIndexType(collection_file.engine_type_)) { - collection_file.file_type_ = (segment_writer_ptr->Size() >= (size_t)(collection_file.index_file_size_)) - ? meta::SegmentSchema::TO_INDEX - : meta::SegmentSchema::RAW; - } else { - collection_file.file_type_ = meta::SegmentSchema::RAW; - } - collection_file.file_size_ = segment_writer_ptr->Size(); - collection_file.row_count_ = segment_writer_ptr->VectorCount(); - updated.push_back(collection_file); - status = meta_ptr_->UpdateCollectionFiles(updated); - LOG_ENGINE_DEBUG_ << "New merged segment " << collection_file.segment_id_ << " of size " - << segment_writer_ptr->Size() << " bytes"; - - if (options_.insert_cache_immediately_) { - segment_writer_ptr->Cache(); - } - - return status; -} - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/merge/MergeTask.h b/core/src/db/merge/MergeTask.h deleted file mode 100644 index af0933a665..0000000000 --- a/core/src/db/merge/MergeTask.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "db/merge/MergeManager.h" -#include "db/meta/MetaTypes.h" -#include "utils/Status.h" - -namespace milvus { -namespace engine { - -class MergeTask { - public: - MergeTask(const meta::MetaPtr& meta, const DBOptions& options, meta::SegmentsSchema& files); - - Status - Execute(); - - private: - meta::MetaPtr meta_ptr_; - DBOptions options_; - - meta::SegmentsSchema files_; -}; // MergeTask - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/meta/FilesHolder.cpp b/core/src/db/meta/FilesHolder.cpp deleted file mode 100644 index 80b0f764bc..0000000000 --- a/core/src/db/meta/FilesHolder.cpp +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/meta/FilesHolder.h" -#include "utils/Log.h" - -#include - -namespace milvus { -namespace engine { -namespace meta { - -////////////////////////////////////////////////////////////////////////////////////////////////////////// -FilesHolder::OngoingFileChecker& -FilesHolder::OngoingFileChecker::GetInstance() { - static OngoingFileChecker instance; - return instance; -} - -Status -FilesHolder::OngoingFileChecker::MarkOngoingFile(const meta::SegmentSchema& table_file) { - std::lock_guard lck(mutex_); - return MarkOngoingFileNoLock(table_file); -} - -Status -FilesHolder::OngoingFileChecker::MarkOngoingFiles(const meta::SegmentsSchema& table_files) { - std::lock_guard lck(mutex_); - - for (auto& table_file : table_files) { - MarkOngoingFileNoLock(table_file); - } - - return Status::OK(); -} - -Status -FilesHolder::OngoingFileChecker::UnmarkOngoingFile(const meta::SegmentSchema& table_file) { - std::lock_guard lck(mutex_); - return UnmarkOngoingFileNoLock(table_file); -} - -Status -FilesHolder::OngoingFileChecker::UnmarkOngoingFiles(const meta::SegmentsSchema& table_files) { - std::lock_guard lck(mutex_); - - for (auto& table_file : table_files) { - UnmarkOngoingFileNoLock(table_file); - } - - return Status::OK(); -} - -bool -FilesHolder::OngoingFileChecker::CanBeDeleted(const meta::SegmentSchema& schema) { - std::lock_guard lck(mutex_); - - auto iter = ongoing_files_.find(schema.collection_id_); - if (iter == ongoing_files_.end()) { - return true; - } else { - auto it_file = iter->second.find(schema.id_); - if (it_file == iter->second.end()) { - return true; - } else { - return (it_file->second > 0) ? false : true; - } - } -} - -void -FilesHolder::OngoingFileChecker::PrintInfo() { - std::lock_guard lck(mutex_); - if (!ongoing_files_.empty()) { - LOG_ENGINE_DEBUG_ << "File reference information:"; - for (meta::Table2FileRef::iterator iter = ongoing_files_.begin(); iter != ongoing_files_.end(); ++iter) { - LOG_ENGINE_DEBUG_ << "\t" << iter->first << ": " << iter->second.size() << " files in use"; - } - } -} - -Status -FilesHolder::OngoingFileChecker::MarkOngoingFileNoLock(const meta::SegmentSchema& table_file) { - if (table_file.collection_id_.empty() || table_file.file_id_.empty()) { - return Status(DB_ERROR, "Invalid collection files"); - } - - auto iter = ongoing_files_.find(table_file.collection_id_); - if (iter == ongoing_files_.end()) { - File2RefCount files_refcount; - files_refcount.insert(std::make_pair(table_file.id_, 1)); - ongoing_files_.insert(std::make_pair(table_file.collection_id_, files_refcount)); - } else { - auto it_file = iter->second.find(table_file.id_); - if (it_file == iter->second.end()) { - iter->second[table_file.id_] = 1; - } else { - it_file->second++; - } - } - - LOG_ENGINE_DEBUG_ << "Mark ongoing file:" << table_file.file_id_ - << " refcount:" << ongoing_files_[table_file.collection_id_][table_file.id_]; - - return Status::OK(); -} - -Status -FilesHolder::OngoingFileChecker::UnmarkOngoingFileNoLock(const meta::SegmentSchema& table_file) { - if (table_file.collection_id_.empty() || table_file.file_id_.empty()) { - return Status(DB_ERROR, "Invalid collection files"); - } - - auto iter = ongoing_files_.find(table_file.collection_id_); - if (iter != ongoing_files_.end()) { - auto it_file = iter->second.find(table_file.id_); - if (it_file != iter->second.end()) { - it_file->second--; - - LOG_ENGINE_DEBUG_ << "Unmark ongoing file:" << table_file.file_id_ << " refcount:" << it_file->second; - - if (it_file->second <= 0) { - iter->second.erase(table_file.id_); - if (iter->second.empty()) { - ongoing_files_.erase(table_file.collection_id_); - } - } - } - } - - return Status::OK(); -} - -////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -FilesHolder::FilesHolder() { -} - -FilesHolder::~FilesHolder() { - ReleaseFiles(); -} - -Status -FilesHolder::MarkFile(const meta::SegmentSchema& file) { - std::lock_guard lck(mutex_); - return MarkFileInternal(file); -} - -Status -FilesHolder::MarkFiles(const meta::SegmentsSchema& files) { - std::lock_guard lck(mutex_); - for (auto& file : files) { - MarkFileInternal(file); - } - - return Status::OK(); -} - -Status -FilesHolder::UnmarkFile(const meta::SegmentSchema& file) { - std::lock_guard lck(mutex_); - return UnmarkFileInternal(file); -} - -Status -FilesHolder::UnmarkFiles(const meta::SegmentsSchema& files) { - std::lock_guard lck(mutex_); - for (auto& file : files) { - UnmarkFileInternal(file); - } - - return Status::OK(); -} - -void -FilesHolder::ReleaseFiles() { - std::lock_guard lck(mutex_); - OngoingFileChecker::GetInstance().UnmarkOngoingFiles(hold_files_); - hold_files_.clear(); - unique_ids_.clear(); -} - -bool -FilesHolder::CanBeDeleted(const meta::SegmentSchema& file) { - return OngoingFileChecker::GetInstance().CanBeDeleted(file); -} - -void -FilesHolder::PrintInfo() { - return OngoingFileChecker::GetInstance().PrintInfo(); -} - -Status -FilesHolder::MarkFileInternal(const meta::SegmentSchema& file) { - if (unique_ids_.find(file.id_) != unique_ids_.end()) { - return Status::OK(); // already marked - } - - auto status = OngoingFileChecker::GetInstance().MarkOngoingFile(file); - if (status.ok()) { - unique_ids_.insert(file.id_); - hold_files_.push_back(file); - } - - return status; -} - -Status -FilesHolder::UnmarkFileInternal(const meta::SegmentSchema& file) { - if (unique_ids_.find(file.id_) == unique_ids_.end()) { - return Status::OK(); // no such file - } - - auto status = OngoingFileChecker::GetInstance().UnmarkOngoingFile(file); - if (status.ok()) { - for (auto iter = hold_files_.begin(); iter != hold_files_.end(); ++iter) { - if (file.id_ == (*iter).id_) { - hold_files_.erase(iter); - break; - } - } - - unique_ids_.erase(file.id_); - } - return status; -} - -} // namespace meta -} // namespace engine -} // namespace milvus diff --git a/core/src/db/meta/FilesHolder.h b/core/src/db/meta/FilesHolder.h deleted file mode 100644 index 1c9afbbb16..0000000000 --- a/core/src/db/meta/FilesHolder.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "db/meta/Meta.h" -#include "utils/Status.h" - -#include -#include -#include -#include - -namespace milvus { -namespace engine { -namespace meta { - -class FilesHolder { - public: - FilesHolder(); - virtual ~FilesHolder(); - - Status - MarkFile(const meta::SegmentSchema& file); - - Status - MarkFiles(const meta::SegmentsSchema& files); - - Status - UnmarkFile(const meta::SegmentSchema& file); - - Status - UnmarkFiles(const meta::SegmentsSchema& files); - - const milvus::engine::meta::SegmentsSchema& - HoldFiles() const { - return hold_files_; - } - - milvus::engine::meta::SegmentsSchema& - HoldFiles() { - return hold_files_; - } - - void - ReleaseFiles(); - - static bool - CanBeDeleted(const meta::SegmentSchema& file); - - static void - PrintInfo(); - - private: - class OngoingFileChecker { - public: - static OngoingFileChecker& - GetInstance(); - - Status - MarkOngoingFile(const meta::SegmentSchema& file); - - Status - MarkOngoingFiles(const meta::SegmentsSchema& files); - - Status - UnmarkOngoingFile(const meta::SegmentSchema& file); - - Status - UnmarkOngoingFiles(const meta::SegmentsSchema& files); - - bool - CanBeDeleted(const meta::SegmentSchema& file); - - void - PrintInfo(); - - private: - Status - MarkOngoingFileNoLock(const meta::SegmentSchema& file); - - Status - UnmarkOngoingFileNoLock(const meta::SegmentSchema& file); - - private: - std::mutex mutex_; - meta::Table2FileRef ongoing_files_; // collection id mapping to (file id mapping to ongoing ref-count) - }; - - private: - Status - MarkFileInternal(const meta::SegmentSchema& file); - - Status - UnmarkFileInternal(const meta::SegmentSchema& file); - - private: - std::mutex mutex_; - milvus::engine::meta::SegmentsSchema hold_files_; - std::set unique_ids_; -}; - -} // namespace meta -} // namespace engine -} // namespace milvus diff --git a/core/src/db/meta/Meta.cpp b/core/src/db/meta/Meta.cpp deleted file mode 100644 index 252e4fa4e1..0000000000 --- a/core/src/db/meta/Meta.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/meta/Meta.h" - -namespace milvus { -namespace engine { -namespace meta { - -const char* META_ENVIRONMENT = "Environment"; -const char* META_TABLES = "Tables"; -const char* META_TABLEFILES = "TableFiles"; -const char* META_COLLECTIONS = "Collections"; -const char* META_FIELDS = "Fields"; -const char* META_COLLECTIONFILES = "CollectionFiles"; - -} // namespace meta -} // namespace engine -} // namespace milvus diff --git a/core/src/db/meta/Meta.h b/core/src/db/meta/Meta.h deleted file mode 100644 index 7261341a6a..0000000000 --- a/core/src/db/meta/Meta.h +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include - -#include "MetaTypes.h" -#include "db/Options.h" -#include "db/Types.h" -#include "db/meta/FilesHolder.h" -#include "utils/Status.h" - -namespace milvus { -namespace engine { -namespace meta { - -extern const char* META_ENVIRONMENT; -extern const char* META_TABLES; -extern const char* META_TABLEFILES; -extern const char* META_COLLECTIONS; -extern const char* META_FIELDS; -extern const char* META_COLLECTIONFILES; - -class FilesHolder; - -class Meta { - /* - public: - class CleanUpFilter { - public: - virtual bool - IsIgnored(const SegmentSchema& schema) = 0; - }; -*/ - - public: - virtual ~Meta() = default; - - virtual Status - CreateCollection(CollectionSchema& table_schema) = 0; - - virtual Status - DescribeCollection(CollectionSchema& table_schema) = 0; - - virtual Status - HasCollection(const std::string& collection_id, bool& has_or_not, bool is_root = false) = 0; - - virtual Status - AllCollections(std::vector& table_schema_array, bool is_root = false) = 0; - - virtual Status - UpdateCollectionFlag(const std::string& collection_id, int64_t flag) = 0; - - virtual Status - UpdateCollectionFlushLSN(const std::string& collection_id, uint64_t flush_lsn) = 0; - - virtual Status - GetCollectionFlushLSN(const std::string& collection_id, uint64_t& flush_lsn) = 0; - - virtual Status - DropCollections(const std::vector& collection_id_array) = 0; - - virtual Status - DeleteCollectionFiles(const std::vector& collection_id_array) = 0; - - virtual Status - CreateCollectionFile(SegmentSchema& file_schema) = 0; - - virtual Status - GetCollectionFiles(const std::string& collection_id, const std::vector& ids, FilesHolder& files_holder) = 0; - - virtual Status - GetCollectionFilesBySegmentId(const std::string& segment_id, FilesHolder& files_holder) = 0; - - virtual Status - UpdateCollectionFile(SegmentSchema& file_schema) = 0; - - virtual Status - UpdateCollectionFiles(SegmentsSchema& files) = 0; - - virtual Status - UpdateCollectionFilesRowCount(SegmentsSchema& files) = 0; - - virtual Status - UpdateCollectionIndex(const std::string& collection_id, const CollectionIndex& index) = 0; - - virtual Status - UpdateCollectionFilesToIndex(const std::string& collection_id) = 0; - - virtual Status - DescribeCollectionIndex(const std::string& collection_id, CollectionIndex& index) = 0; - - virtual Status - DropCollectionIndex(const std::string& collection_id) = 0; - - virtual Status - CreatePartition(const std::string& collection_name, const std::string& partition_name, const std::string& tag, - uint64_t lsn) = 0; - - virtual Status - HasPartition(const std::string& collection_id, const std::string& tag, bool& has_or_not) = 0; - - virtual Status - DropPartition(const std::string& partition_name) = 0; - - virtual Status - ShowPartitions(const std::string& collection_name, std::vector& partition_schema_array) = 0; - - virtual Status - GetPartitionName(const std::string& collection_name, const std::string& tag, std::string& partition_name) = 0; - - virtual Status - FilesToSearch(const std::string& collection_id, FilesHolder& files_holder) = 0; - - virtual Status - FilesToSearchEx(const std::string& root_collection, const std::set& partition_id_array, - FilesHolder& files_holder) = 0; - - virtual Status - FilesToMerge(const std::string& collection_id, FilesHolder& files_holder) = 0; - - virtual Status - FilesToIndex(FilesHolder& files_holder) = 0; - - virtual Status - FilesByType(const std::string& collection_id, const std::vector& file_types, FilesHolder& files_holder) = 0; - - virtual Status - FilesByTypeEx(const std::vector& collections, const std::vector& file_types, - FilesHolder& files_holder) = 0; - - virtual Status - FilesByID(const std::vector& ids, FilesHolder& files_holder) = 0; - - virtual Status - Size(uint64_t& result) = 0; - - virtual Status - Archive() = 0; - - virtual Status - CleanUpShadowFiles() = 0; - - virtual Status - CleanUpFilesWithTTL(uint64_t seconds /*, CleanUpFilter* filter = nullptr*/) = 0; - - virtual Status - DropAll() = 0; - - virtual Status - Count(const std::string& collection_id, uint64_t& result) = 0; - - virtual Status - SetGlobalLastLSN(uint64_t lsn) = 0; - - virtual Status - GetGlobalLastLSN(uint64_t& lsn) = 0; - - virtual Status - CreateHybridCollection(CollectionSchema& collection_schema, hybrid::FieldsSchema& fields_schema) = 0; - - virtual Status - DescribeHybridCollection(CollectionSchema& collection_schema, hybrid::FieldsSchema& fields_schema) = 0; -}; // MetaData - -using MetaPtr = std::shared_ptr; - -} // namespace meta -} // namespace engine -} // namespace milvus diff --git a/core/src/db/meta/MetaConsts.h b/core/src/db/meta/MetaConsts.h deleted file mode 100644 index 4ae31ca52d..0000000000 --- a/core/src/db/meta/MetaConsts.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -namespace milvus { -namespace engine { -namespace meta { - -const size_t S_PS = 1UL; -const size_t MS_PS = 1000 * S_PS; -const size_t US_PS = 1000 * MS_PS; -const size_t NS_PS = 1000 * US_PS; - -const size_t SECOND = 1UL; -const size_t MINUTE = 60 * SECOND; -const size_t HOUR = 60 * MINUTE; -const size_t DAY = 24 * HOUR; -const size_t WEEK = 7 * DAY; - -// This value is to ignore small raw files when building index. -// The reason is: -// 1. The performance of brute-search for small raw files could be better than small index file. -// 2. And small raw files can be merged to larger files, thus reduce fragmented files count. -// We decide the value based on a testing for small size raw/index files. -const size_t BUILD_INDEX_THRESHOLD = 4096; - -} // namespace meta -} // namespace engine -} // namespace milvus diff --git a/core/src/db/meta/MetaFactory.cpp b/core/src/db/meta/MetaFactory.cpp deleted file mode 100644 index c4cf585d5c..0000000000 --- a/core/src/db/meta/MetaFactory.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/meta/MetaFactory.h" -#include "MySQLMetaImpl.h" -#include "SqliteMetaImpl.h" -#include "db/Utils.h" -#include "utils/Exception.h" -#include "utils/Log.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace milvus { -namespace engine { - -DBMetaOptions -MetaFactory::BuildOption(const std::string& path) { - auto p = path; - if (p == "") { - srand(time(nullptr)); - std::stringstream ss; - uint32_t seed = 1; - ss << "/tmp/" << rand_r(&seed); - p = ss.str(); - } - - DBMetaOptions meta; - meta.path_ = p; - return meta; -} - -meta::MetaPtr -MetaFactory::Build(const DBMetaOptions& meta_options, const int& mode) { - std::string uri = meta_options.backend_uri_; - - utils::MetaUriInfo uri_info; - LOG_ENGINE_DEBUG_ << "MetaUri: " << uri << std::endl; - auto status = utils::ParseMetaUri(uri, uri_info); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << "Wrong URI format: URI = " << uri; - throw InvalidArgumentException("Wrong URI format "); - } - - if (strcasecmp(uri_info.dialect_.c_str(), "mysql") == 0) { - LOG_ENGINE_INFO_ << "Using MySQL"; - return std::make_shared(meta_options, mode); - } else if (strcasecmp(uri_info.dialect_.c_str(), "sqlite") == 0) { - LOG_ENGINE_INFO_ << "Using SQLite"; - return std::make_shared(meta_options); - } else { - LOG_ENGINE_ERROR_ << "Invalid dialect in URI: dialect = " << uri_info.dialect_; - throw InvalidArgumentException("URI dialect is not mysql / sqlite"); - } -} - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/meta/MetaFactory.h b/core/src/db/meta/MetaFactory.h deleted file mode 100644 index ffe7add176..0000000000 --- a/core/src/db/meta/MetaFactory.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "Meta.h" -#include "db/Options.h" - -#include - -namespace milvus { -namespace engine { - -class MetaFactory { - public: - static DBMetaOptions - BuildOption(const std::string& path = ""); - - static meta::MetaPtr - Build(const DBMetaOptions& meta_options, const int& mode); -}; - -} // namespace engine -} // namespace milvus diff --git a/core/src/db/meta/MetaTypes.h b/core/src/db/meta/MetaTypes.h deleted file mode 100644 index e988769030..0000000000 --- a/core/src/db/meta/MetaTypes.h +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include - -#include "db/Constants.h" -#include "db/engine/ExecutionEngine.h" -#include "src/version.h" - -namespace milvus { -namespace engine { -namespace meta { - -constexpr int32_t DEFAULT_ENGINE_TYPE = (int)EngineType::FAISS_IDMAP; -constexpr int32_t DEFAULT_METRIC_TYPE = (int)MetricType::L2; -constexpr int32_t DEFAULT_INDEX_FILE_SIZE = GB; -constexpr char CURRENT_VERSION[] = MILVUS_VERSION; - -constexpr int64_t FLAG_MASK_NO_USERID = 0x1; -constexpr int64_t FLAG_MASK_HAS_USERID = 0x1 << 1; - -using DateT = int; -const DateT EmptyDate = -1; - -struct EnvironmentSchema { - uint64_t global_lsn_ = 0; -}; // EnvironmentSchema - -struct CollectionSchema { - typedef enum { - NORMAL, - TO_DELETE, - } TABLE_STATE; - - size_t id_ = 0; - std::string collection_id_; - int32_t state_ = (int)NORMAL; - uint16_t dimension_ = 0; - int64_t created_on_ = 0; - int64_t flag_ = 0; - int64_t index_file_size_ = DEFAULT_INDEX_FILE_SIZE; - int32_t engine_type_ = DEFAULT_ENGINE_TYPE; - std::string index_params_ = "{}"; - int32_t metric_type_ = DEFAULT_METRIC_TYPE; - std::string owner_collection_; - std::string partition_tag_; - std::string version_ = CURRENT_VERSION; - uint64_t flush_lsn_ = 0; -}; // CollectionSchema - -struct SegmentSchema { - typedef enum { - NEW, - RAW, - TO_INDEX, - INDEX, - TO_DELETE, - NEW_MERGE, - NEW_INDEX, - BACKUP, - } FILE_TYPE; - - size_t id_ = 0; - std::string collection_id_; - std::string segment_id_; - std::string file_id_; - int32_t file_type_ = NEW; - size_t file_size_ = 0; - size_t row_count_ = 0; - DateT date_ = EmptyDate; - uint16_t dimension_ = 0; - // TODO(zhiru) - std::string location_; - int64_t updated_time_ = 0; - int64_t created_on_ = 0; - int64_t index_file_size_ = DEFAULT_INDEX_FILE_SIZE; // not persist to meta - int32_t engine_type_ = DEFAULT_ENGINE_TYPE; - std::string index_params_; // not persist to meta - int32_t metric_type_ = DEFAULT_METRIC_TYPE; // not persist to meta - uint64_t flush_lsn_ = 0; -}; // SegmentSchema - -using SegmentSchemaPtr = std::shared_ptr; -using SegmentsSchema = std::vector; - -using File2RefCount = std::map; -using Table2FileRef = std::map; - -namespace hybrid { - -enum class DataType { - INT8 = 1, - INT16 = 2, - INT32 = 3, - INT64 = 4, - - STRING = 20, - - BOOL = 30, - - FLOAT = 40, - DOUBLE = 41, - - VECTOR = 100, - UNKNOWN = 9999, -}; - -struct VectorFieldSchema { - std::string vector_id_; - int64_t dimension; - int64_t index_file_size_ = DEFAULT_INDEX_FILE_SIZE; - int32_t engine_type_ = DEFAULT_ENGINE_TYPE; - std::string index_params_ = "{}"; - int32_t metric_type_ = DEFAULT_METRIC_TYPE; -}; - -struct VectorFieldsSchema { - std::vector vector_fields_; -}; -using VectorFieldSchemaPtr = std::shared_ptr; - -struct FieldSchema { - typedef enum { - INT8 = 1, - INT16 = 2, - INT32 = 3, - INT64 = 4, - - STRING = 20, - - BOOL = 30, - - FLOAT = 40, - DOUBLE = 41, - - VECTOR = 100, - UNKNOWN = 9999, - } FIELD_TYPE; - - // TODO(yukun): need field_id? - std::string collection_id_; - std::string field_name_; - int32_t field_type_ = (int)INT8; - std::string field_params_; -}; - -struct FieldsSchema { - std::vector fields_schema_; -}; - -using FieldSchemaPtr = std::shared_ptr; - -struct VectorFileSchema { - std::string field_name_; - int64_t index_file_size_ = DEFAULT_INDEX_FILE_SIZE; // not persist to meta - int32_t engine_type_ = DEFAULT_ENGINE_TYPE; - std::string index_params_ = "{}"; // not persist to meta - int32_t metric_type_ = DEFAULT_METRIC_TYPE; // not persist to meta -}; - -using VectorFileSchemaPtr = std::shared_ptr; - -struct CollectionFileSchema { - typedef enum { - NEW, - RAW, - TO_INDEX, - INDEX, - TO_DELETE, - NEW_MERGE, - NEW_INDEX, - BACKUP, - } FILE_TYPE; - - size_t id_ = 0; - std::string collection_id_; - std::string segment_id_; - std::string file_id_; - int32_t file_type_ = NEW; - size_t file_size_ = 0; - size_t row_count_ = 0; - DateT date_ = EmptyDate; - std::string location_; - int64_t updated_time_ = 0; - int64_t created_on_ = 0; - uint64_t flush_lsn_ = 0; -}; - -using CollectionFileSchemaPtr = std::shared_ptr; -} // namespace hybrid - -} // namespace meta -} // namespace engine -} // namespace milvus diff --git a/core/src/db/meta/MySQLConnectionPool.cpp b/core/src/db/meta/MySQLConnectionPool.cpp deleted file mode 100644 index 104e6584fd..0000000000 --- a/core/src/db/meta/MySQLConnectionPool.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/meta/MySQLConnectionPool.h" -#include -#include - -namespace milvus::engine::meta { - -// Do a simple form of in-use connection limiting: wait to return -// a connection until there are a reasonably low number in use -// already. Can't do this in create() because we're interested in -// connections actually in use, not those created. Also note that -// we keep our own count; ConnectionPool::size() isn't the same! -mysqlpp::Connection* -MySQLConnectionPool::grab() { - while (conns_in_use_ > max_pool_size_) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - - ++conns_in_use_; - return mysqlpp::ConnectionPool::grab(); -} - -// Other half of in-use conn count limit -void -MySQLConnectionPool::release(const mysqlpp::Connection* pc) { - mysqlpp::ConnectionPool::release(pc); - if (conns_in_use_ <= 0) { - LOG_ENGINE_WARNING_ << "MySQLConnetionPool::release: conns_in_use_ is less than zero. conns_in_use_ = " - << conns_in_use_; - } else { - --conns_in_use_; - } -} - -// int MySQLConnectionPool::getConnectionsInUse() { -// return conns_in_use_; -// } -// -// void MySQLConnectionPool::set_max_idle_time(int max_idle) { -// max_idle_time_ = max_idle; -// } - -// Superclass overrides -mysqlpp::Connection* -MySQLConnectionPool::create() { - try { - fiu_do_on("MySQLConnectionPool.create.throw_exception", throw mysqlpp::ConnectionFailed()); - - // Create connection using the parameters we were passed upon - // creation. - auto conn = new mysqlpp::Connection(); - conn->set_option(new mysqlpp::ReconnectOption(true)); - conn->connect(db_name_.empty() ? 0 : db_name_.c_str(), server_.empty() ? 0 : server_.c_str(), - user_.empty() ? 0 : user_.c_str(), password_.empty() ? 0 : password_.c_str(), port_); - return conn; - } catch (const mysqlpp::ConnectionFailed& er) { - LOG_ENGINE_ERROR_ << "Failed to connect to database server" - << ": " << er.what(); - return nullptr; - } -} - -void -MySQLConnectionPool::destroy(mysqlpp::Connection* cp) { - // Our superclass can't know how we created the Connection, so - // it delegates destruction to us, to be safe. - delete cp; -} - -unsigned int -MySQLConnectionPool::max_idle_time() { - return max_idle_time_; -} - -} // namespace milvus::engine::meta diff --git a/core/src/db/meta/MySQLConnectionPool.h b/core/src/db/meta/MySQLConnectionPool.h deleted file mode 100644 index 523ffe00eb..0000000000 --- a/core/src/db/meta/MySQLConnectionPool.h +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include - -#include -#include -#include - -#include "utils/Log.h" - -namespace milvus { -namespace engine { -namespace meta { - -class MySQLConnectionPool : public mysqlpp::ConnectionPool { - public: - // The object's only constructor - MySQLConnectionPool(const std::string& dbName, const std::string& userName, const std::string& passWord, - const std::string& serverIp, int port = 0, int maxPoolSize = 8) - : db_name_(dbName), - user_(userName), - password_(passWord), - server_(serverIp), - port_(port), - max_pool_size_(maxPoolSize) { - } - - // The destructor. We _must_ call ConnectionPool::clear() here, - // because our superclass can't do it for us. - ~MySQLConnectionPool() override { - clear(); - } - - mysqlpp::Connection* - grab() override; - - // Other half of in-use conn count limit - void - release(const mysqlpp::Connection* pc) override; - - const std::string& - db_name() const { - return db_name_; - } - - protected: - // Superclass overrides - mysqlpp::Connection* - create() override; - - void - destroy(mysqlpp::Connection* cp) override; - - unsigned int - max_idle_time() override; - - private: - // Number of connections currently in use - std::atomic conns_in_use_ = 0; - - // Our connection parameters - std::string db_name_, user_, password_, server_; - int port_; - - int max_pool_size_; - - unsigned int max_idle_time_ = 10; // 10 seconds -}; - -} // namespace meta -} // namespace engine -} // namespace milvus diff --git a/core/src/db/meta/MySQLMetaImpl.cpp b/core/src/db/meta/MySQLMetaImpl.cpp deleted file mode 100644 index a5b296dce1..0000000000 --- a/core/src/db/meta/MySQLMetaImpl.cpp +++ /dev/null @@ -1,3209 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/meta/MySQLMetaImpl.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "MetaConsts.h" -#include "db/IDGenerator.h" -#include "db/Utils.h" -#include "metrics/Metrics.h" -#include "utils/CommonUtil.h" -#include "utils/Exception.h" -#include "utils/Log.h" -#include "utils/StringHelpFunctions.h" -#include "utils/ValidationUtil.h" - -namespace milvus { -namespace engine { -namespace meta { - -namespace { - -constexpr uint64_t SQL_BATCH_SIZE = 50; - -template -void -DistributeBatch(const T& id_array, std::vector>& id_groups) { - std::vector temp_group; - // constexpr uint64_t SQL_BATCH_SIZE = 50; // duplicate variable - for (auto& id : id_array) { - temp_group.push_back(id); - if (temp_group.size() >= SQL_BATCH_SIZE) { - id_groups.emplace_back(temp_group); - temp_group.clear(); - } - } - - if (!temp_group.empty()) { - id_groups.emplace_back(temp_group); - } -} - -Status -HandleException(const std::string& desc, const char* what = nullptr) { - if (what == nullptr) { - LOG_ENGINE_ERROR_ << desc; - return Status(DB_META_TRANSACTION_FAILED, desc); - } - - std::string msg = desc + ":" + what; - LOG_ENGINE_ERROR_ << msg; - return Status(DB_META_TRANSACTION_FAILED, msg); -} - -class MetaField { - public: - MetaField(const std::string& name, const std::string& type, const std::string& setting) - : name_(name), type_(type), setting_(setting) { - } - - std::string - name() const { - return name_; - } - - std::string - ToString() const { - return name_ + " " + type_ + " " + setting_; - } - - // mysql field type has additional information. for instance, a filed type is defined as 'BIGINT' - // we get the type from sql is 'bigint(20)', so we need to ignore the '(20)' - bool - IsEqual(const MetaField& field) const { - size_t name_len_min = field.name_.length() > name_.length() ? name_.length() : field.name_.length(); - size_t type_len_min = field.type_.length() > type_.length() ? type_.length() : field.type_.length(); - - // only check field type, don't check field width, for example: VARCHAR(255) and VARCHAR(100) is equal - std::vector type_split; - milvus::server::StringHelpFunctions::SplitStringByDelimeter(type_, "(", type_split); - if (!type_split.empty()) { - type_len_min = type_split[0].length() > type_len_min ? type_len_min : type_split[0].length(); - } - - // field name must be equal, ignore type width - return strncasecmp(field.name_.c_str(), name_.c_str(), name_len_min) == 0 && - strncasecmp(field.type_.c_str(), type_.c_str(), type_len_min) == 0; - } - - private: - std::string name_; - std::string type_; - std::string setting_; -}; - -using MetaFields = std::vector; - -class MetaSchema { - public: - MetaSchema(const std::string& name, const MetaFields& fields) : name_(name), fields_(fields) { - } - - std::string - name() const { - return name_; - } - - std::string - ToString() const { - std::string result; - for (auto& field : fields_) { - if (!result.empty()) { - result += ","; - } - result += field.ToString(); - } - return result; - } - - // if the outer fields contains all this MetaSchema fields, return true - // otherwise return false - bool - IsEqual(const MetaFields& fields) const { - std::vector found_field; - for (const auto& this_field : fields_) { - for (const auto& outer_field : fields) { - if (this_field.IsEqual(outer_field)) { - found_field.push_back(this_field.name()); - break; - } - } - } - - return found_field.size() == fields_.size(); - } - - private: - std::string name_; - MetaFields fields_; -}; - -// Environment schema -static const MetaSchema ENVIRONMENT_SCHEMA(META_ENVIRONMENT, { - MetaField("global_lsn", "BIGINT", "NOT NULL"), - }); - -// Tables schema -static const MetaSchema TABLES_SCHEMA(META_TABLES, { - MetaField("id", "BIGINT", "PRIMARY KEY AUTO_INCREMENT"), - MetaField("table_id", "VARCHAR(255)", "UNIQUE NOT NULL"), - MetaField("state", "INT", "NOT NULL"), - MetaField("dimension", "SMALLINT", "NOT NULL"), - MetaField("created_on", "BIGINT", "NOT NULL"), - MetaField("flag", "BIGINT", "DEFAULT 0 NOT NULL"), - MetaField("index_file_size", "BIGINT", "DEFAULT 1024 NOT NULL"), - MetaField("engine_type", "INT", "DEFAULT 1 NOT NULL"), - MetaField("index_params", "VARCHAR(512)", "NOT NULL"), - MetaField("metric_type", "INT", "DEFAULT 1 NOT NULL"), - MetaField("owner_table", "VARCHAR(255)", "NOT NULL"), - MetaField("partition_tag", "VARCHAR(255)", "NOT NULL"), - MetaField("version", "VARCHAR(64)", - std::string("DEFAULT '") + CURRENT_VERSION + "'"), - MetaField("flush_lsn", "BIGINT", "DEFAULT 0 NOT NULL"), - }); - -// TableFiles schema -static const MetaSchema TABLEFILES_SCHEMA(META_TABLEFILES, { - MetaField("id", "BIGINT", "PRIMARY KEY AUTO_INCREMENT"), - MetaField("table_id", "VARCHAR(255)", "NOT NULL"), - MetaField("segment_id", "VARCHAR(255)", "NOT NULL"), - MetaField("engine_type", "INT", "DEFAULT 1 NOT NULL"), - MetaField("file_id", "VARCHAR(255)", "NOT NULL"), - MetaField("file_type", "INT", "DEFAULT 0 NOT NULL"), - MetaField("file_size", "BIGINT", "DEFAULT 0 NOT NULL"), - MetaField("row_count", "BIGINT", "DEFAULT 0 NOT NULL"), - MetaField("updated_time", "BIGINT", "NOT NULL"), - MetaField("created_on", "BIGINT", "NOT NULL"), - MetaField("date", "INT", "DEFAULT -1 NOT NULL"), - MetaField("flush_lsn", "BIGINT", "DEFAULT 0 NOT NULL"), - }); - -// Fields schema -static const MetaSchema FIELDS_SCHEMA(META_FIELDS, { - MetaField("collection_id", "VARCHAR(255)", "NOT NULL"), - MetaField("field_name", "VARCHAR(255)", "NOT NULL"), - MetaField("field_type", "INT", "DEFAULT 0 NOT NULL"), - MetaField("field_params", "VARCHAR(255)", "NOT NULL"), - }); - -} // namespace - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -MySQLMetaImpl::MySQLMetaImpl(const DBMetaOptions& options, const int& mode) : options_(options), mode_(mode) { - Initialize(); -} - -MySQLMetaImpl::~MySQLMetaImpl() { -} - -Status -MySQLMetaImpl::NextCollectionId(std::string& collection_id) { - std::lock_guard lock(genid_mutex_); // avoid duplicated id - std::stringstream ss; - SafeIDGenerator& id_generator = SafeIDGenerator::GetInstance(); - ss << id_generator.GetNextIDNumber(); - collection_id = ss.str(); - return Status::OK(); -} - -Status -MySQLMetaImpl::NextFileId(std::string& file_id) { - std::lock_guard lock(genid_mutex_); // avoid duplicated id - std::stringstream ss; - - SafeIDGenerator& id_generator = SafeIDGenerator::GetInstance(); - ss << id_generator.GetNextIDNumber(); - file_id = ss.str(); - return Status::OK(); -} - -void -MySQLMetaImpl::ValidateMetaSchema() { - if (mysql_connection_pool_ == nullptr) { - throw Exception(DB_ERROR, "MySQL connection pool is invalid"); - return; - } - - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - if (connectionPtr == nullptr) { - throw Exception(DB_ERROR, "Can't construct MySQL connection"); - return; - } - - auto validate_func = [&](const MetaSchema& schema) { - fiu_return_on("MySQLMetaImpl.ValidateMetaSchema.fail_validate", false); - mysqlpp::Query query_statement = connectionPtr->query(); - query_statement << "DESC " << schema.name() << ";"; - - MetaFields exist_fields; - - try { - mysqlpp::StoreQueryResult res = query_statement.store(); - for (size_t i = 0; i < res.num_rows(); ++i) { - const mysqlpp::Row& row = res[i]; - std::string name, type; - row["Field"].to_string(name); - row["Type"].to_string(type); - - exist_fields.push_back(MetaField(name, type, "")); - } - } catch (std::exception& e) { - LOG_ENGINE_DEBUG_ << "Meta collection '" << schema.name() << "' not exist and will be created"; - } - - if (exist_fields.empty()) { - return true; - } - - return schema.IsEqual(exist_fields); - }; - - // verify Environment - if (!validate_func(ENVIRONMENT_SCHEMA)) { - throw Exception(DB_INCOMPATIB_META, "Meta Environment schema is created by Milvus old version"); - } - - // verify Tables - if (!validate_func(TABLES_SCHEMA)) { - throw Exception(DB_INCOMPATIB_META, "Meta Tables schema is created by Milvus old version"); - } - - // verify TableFiles - if (!validate_func(TABLEFILES_SCHEMA)) { - throw Exception(DB_INCOMPATIB_META, "Meta TableFiles schema is created by Milvus old version"); - } - - // verify Fields - if (!validate_func(FIELDS_SCHEMA)) { - throw Exception(DB_INCOMPATIB_META, "Meta Fields schema is created by milvus old version"); - } -} - -Status -MySQLMetaImpl::Initialize() { - // step 1: create db root path - if (!boost::filesystem::is_directory(options_.path_)) { - auto ret = boost::filesystem::create_directory(options_.path_); - fiu_do_on("MySQLMetaImpl.Initialize.fail_create_directory", ret = false); - if (!ret) { - std::string msg = "Failed to create db directory " + options_.path_; - LOG_ENGINE_ERROR_ << msg; - throw Exception(DB_META_TRANSACTION_FAILED, msg); - } - } - - std::string uri = options_.backend_uri_; - - // step 2: parse and check meta uri - utils::MetaUriInfo uri_info; - auto status = utils::ParseMetaUri(uri, uri_info); - if (!status.ok()) { - std::string msg = "Wrong URI format: " + uri; - LOG_ENGINE_ERROR_ << msg; - throw Exception(DB_INVALID_META_URI, msg); - } - - if (strcasecmp(uri_info.dialect_.c_str(), "mysql") != 0) { - std::string msg = "URI's dialect is not MySQL"; - LOG_ENGINE_ERROR_ << msg; - throw Exception(DB_INVALID_META_URI, msg); - } - - // step 3: connect mysql - unsigned int thread_hint = std::thread::hardware_concurrency(); - int max_pool_size = (thread_hint > 8) ? static_cast(thread_hint) : 8; - int port = 0; - if (!uri_info.port_.empty()) { - port = std::stoi(uri_info.port_); - } - - mysql_connection_pool_ = std::make_shared( - uri_info.db_name_, uri_info.username_, uri_info.password_, uri_info.host_, port, max_pool_size); - LOG_ENGINE_DEBUG_ << "MySQL connection pool: maximum pool size = " << std::to_string(max_pool_size); - - // step 4: validate to avoid open old version schema - ValidateMetaSchema(); - - // step 5: clean shadow files - if (mode_ != DBOptions::MODE::CLUSTER_READONLY) { - CleanUpShadowFiles(); - } - - // step 6: try connect mysql server - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - if (connectionPtr == nullptr) { - std::string msg = "Failed to connect MySQL meta server: " + uri; - LOG_ENGINE_ERROR_ << msg; - throw Exception(DB_INVALID_META_URI, msg); - } - - bool is_thread_aware = connectionPtr->thread_aware(); - fiu_do_on("MySQLMetaImpl.Initialize.is_thread_aware", is_thread_aware = false); - if (!is_thread_aware) { - std::string msg = - "Failed to initialize MySQL meta backend: MySQL client component wasn't built with thread awareness"; - LOG_ENGINE_ERROR_ << msg; - throw Exception(DB_INVALID_META_URI, msg); - } - - // step 7: create meta collection Tables - mysqlpp::Query InitializeQuery = connectionPtr->query(); - - InitializeQuery << "CREATE TABLE IF NOT EXISTS " << TABLES_SCHEMA.name() << " (" << TABLES_SCHEMA.ToString() + ");"; - - LOG_ENGINE_DEBUG_ << "Initialize: " << InitializeQuery.str(); - - bool initialize_query_exec = InitializeQuery.exec(); - fiu_do_on("MySQLMetaImpl.Initialize.fail_create_collection_scheme", initialize_query_exec = false); - if (!initialize_query_exec) { - std::string msg = "Failed to create meta collection 'Tables' in MySQL"; - LOG_ENGINE_ERROR_ << msg; - throw Exception(DB_META_TRANSACTION_FAILED, msg); - } - - // step 8: create meta collection TableFiles - InitializeQuery << "CREATE TABLE IF NOT EXISTS " << TABLEFILES_SCHEMA.name() << " (" - << TABLEFILES_SCHEMA.ToString() + ");"; - - LOG_ENGINE_DEBUG_ << "Initialize: " << InitializeQuery.str(); - - initialize_query_exec = InitializeQuery.exec(); - fiu_do_on("MySQLMetaImpl.Initialize.fail_create_collection_files", initialize_query_exec = false); - if (!initialize_query_exec) { - std::string msg = "Failed to create meta collection 'TableFiles' in MySQL"; - LOG_ENGINE_ERROR_ << msg; - throw Exception(DB_META_TRANSACTION_FAILED, msg); - } - - // step 9: create meta table Environment - InitializeQuery << "CREATE TABLE IF NOT EXISTS " << ENVIRONMENT_SCHEMA.name() << " (" - << ENVIRONMENT_SCHEMA.ToString() + ");"; - - LOG_ENGINE_DEBUG_ << "Initialize: " << InitializeQuery.str(); - - initialize_query_exec = InitializeQuery.exec(); - if (!initialize_query_exec) { - std::string msg = "Failed to create meta table 'Environment' in MySQL"; - LOG_ENGINE_ERROR_ << msg; - throw Exception(DB_META_TRANSACTION_FAILED, msg); - } - - // step 10: create meta table Field - InitializeQuery << "CREATE TABLE IF NOT EXISTS " << FIELDS_SCHEMA.name() << " (" << FIELDS_SCHEMA.ToString() + ");"; - - LOG_ENGINE_DEBUG_ << "Initialize: " << InitializeQuery.str(); - - initialize_query_exec = InitializeQuery.exec(); - if (!initialize_query_exec) { - std::string msg = "Failed to create meta table 'Fields' in MySQL"; - LOG_ENGINE_ERROR_ << msg; - throw Exception(DB_META_TRANSACTION_FAILED, msg); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::CreateCollection(CollectionSchema& collection_schema) { - try { - server::MetricCollector metric; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.CreateCollection.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.CreateCollection.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - - if (collection_schema.collection_id_.empty()) { - NextCollectionId(collection_schema.collection_id_); - } else { - statement << "SELECT state FROM " << META_TABLES << " WHERE table_id = " << mysqlpp::quote - << collection_schema.collection_id_ << ";"; - - LOG_ENGINE_DEBUG_ << "CreateCollection: " << statement.str(); - - mysqlpp::StoreQueryResult res = statement.store(); - - if (res.num_rows() == 1) { - int state = res[0]["state"]; - fiu_do_on("MySQLMetaImpl.CreateCollection.schema_TO_DELETE", state = CollectionSchema::TO_DELETE); - if (CollectionSchema::TO_DELETE == state) { - return Status(DB_ERROR, - "Collection already exists and it is in delete state, please wait a second"); - } else { - return Status(DB_ALREADY_EXIST, "Collection already exists"); - } - } - } - - collection_schema.id_ = -1; - collection_schema.created_on_ = utils::GetMicroSecTimeStamp(); - - std::string id = "NULL"; // auto-increment - std::string& collection_id = collection_schema.collection_id_; - std::string state = std::to_string(collection_schema.state_); - std::string dimension = std::to_string(collection_schema.dimension_); - std::string created_on = std::to_string(collection_schema.created_on_); - std::string flag = std::to_string(collection_schema.flag_); - std::string index_file_size = std::to_string(collection_schema.index_file_size_); - std::string engine_type = std::to_string(collection_schema.engine_type_); - std::string& index_params = collection_schema.index_params_; - std::string metric_type = std::to_string(collection_schema.metric_type_); - std::string& owner_collection = collection_schema.owner_collection_; - std::string& partition_tag = collection_schema.partition_tag_; - std::string& version = collection_schema.version_; - std::string flush_lsn = std::to_string(collection_schema.flush_lsn_); - - statement << "INSERT INTO " << META_TABLES << " VALUES(" << id << ", " << mysqlpp::quote << collection_id - << ", " << state << ", " << dimension << ", " << created_on << ", " << flag << ", " - << index_file_size << ", " << engine_type << ", " << mysqlpp::quote << index_params << ", " - << metric_type << ", " << mysqlpp::quote << owner_collection << ", " << mysqlpp::quote - << partition_tag << ", " << mysqlpp::quote << version << ", " << flush_lsn << ");"; - - LOG_ENGINE_DEBUG_ << "CreateCollection: " << statement.str(); - - if (mysqlpp::SimpleResult res = statement.execute()) { - collection_schema.id_ = res.insert_id(); // Might need to use SELECT LAST_INSERT_ID()? - - // Consume all results to avoid "Commands out of sync" error - } else { - return HandleException("Failed to create collection", statement.error()); - } - } // Scoped Connection - - LOG_ENGINE_DEBUG_ << "Successfully create collection: " << collection_schema.collection_id_; - return utils::CreateCollectionPath(options_, collection_schema.collection_id_); - } catch (std::exception& e) { - return HandleException("Failed to create collection", e.what()); - } -} - -Status -MySQLMetaImpl::DescribeCollection(CollectionSchema& collection_schema) { - try { - server::MetricCollector metric; - mysqlpp::StoreQueryResult res; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.DescribeCollection.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.DescribeCollection.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT id, state, dimension, created_on, flag, index_file_size, engine_type, index_params" - << " , metric_type ,owner_table, partition_tag, version, flush_lsn" - << " FROM " << META_TABLES << " WHERE table_id = " << mysqlpp::quote - << collection_schema.collection_id_ << " AND state <> " - << std::to_string(CollectionSchema::TO_DELETE) << ";"; - - LOG_ENGINE_DEBUG_ << "DescribeCollection: " << statement.str(); - - res = statement.store(); - } // Scoped Connection - - if (res.num_rows() == 1) { - const mysqlpp::Row& resRow = res[0]; - collection_schema.id_ = resRow["id"]; // implicit conversion - collection_schema.state_ = resRow["state"]; - collection_schema.dimension_ = resRow["dimension"]; - collection_schema.created_on_ = resRow["created_on"]; - collection_schema.flag_ = resRow["flag"]; - collection_schema.index_file_size_ = resRow["index_file_size"]; - collection_schema.engine_type_ = resRow["engine_type"]; - resRow["index_params"].to_string(collection_schema.index_params_); - collection_schema.metric_type_ = resRow["metric_type"]; - resRow["owner_table"].to_string(collection_schema.owner_collection_); - resRow["partition_tag"].to_string(collection_schema.partition_tag_); - resRow["version"].to_string(collection_schema.version_); - collection_schema.flush_lsn_ = resRow["flush_lsn"]; - } else { - return Status(DB_NOT_FOUND, "Collection " + collection_schema.collection_id_ + " not found"); - } - } catch (std::exception& e) { - return HandleException("Failed to describe collection", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::HasCollection(const std::string& collection_id, bool& has_or_not, bool is_root) { - try { - server::MetricCollector metric; - mysqlpp::StoreQueryResult res; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.HasCollection.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.HasCollection.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query HasCollectionQuery = connectionPtr->query(); - // since collection_id is a unique column we just need to check whether it exists or not - if (is_root) { - HasCollectionQuery << "SELECT id FROM " << META_TABLES << " WHERE table_id = " << mysqlpp::quote - << collection_id << " AND state <> " << std::to_string(CollectionSchema::TO_DELETE) - << " AND owner_table = " << mysqlpp::quote << "" - << ";"; - } else { - HasCollectionQuery << "SELECT id FROM " << META_TABLES << " WHERE table_id = " << mysqlpp::quote - << collection_id << " AND state <> " << std::to_string(CollectionSchema::TO_DELETE) - << ";"; - } - - LOG_ENGINE_DEBUG_ << "HasCollection: " << HasCollectionQuery.str(); - - res = HasCollectionQuery.store(); - } // Scoped Connection - - has_or_not = (res.num_rows() > 0); - } catch (std::exception& e) { - return HandleException("Failed to check collection existence", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::AllCollections(std::vector& collection_schema_array, bool is_root) { - try { - server::MetricCollector metric; - mysqlpp::StoreQueryResult res; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.AllCollection.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.AllCollection.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT id, table_id, dimension, engine_type, index_params, index_file_size, metric_type" - << " ,owner_table, partition_tag, version, flush_lsn" - << " FROM " << META_TABLES << " WHERE state <> " << std::to_string(CollectionSchema::TO_DELETE); - if (is_root) { - statement << " AND owner_table = \"\";"; - } else { - statement << ";"; - } - - LOG_ENGINE_DEBUG_ << "AllCollections: " << statement.str(); - - res = statement.store(); - } // Scoped Connection - - for (auto& resRow : res) { - CollectionSchema collection_schema; - collection_schema.id_ = resRow["id"]; // implicit conversion - resRow["table_id"].to_string(collection_schema.collection_id_); - collection_schema.dimension_ = resRow["dimension"]; - collection_schema.index_file_size_ = resRow["index_file_size"]; - collection_schema.engine_type_ = resRow["engine_type"]; - resRow["index_params"].to_string(collection_schema.index_params_); - collection_schema.metric_type_ = resRow["metric_type"]; - resRow["owner_table"].to_string(collection_schema.owner_collection_); - resRow["partition_tag"].to_string(collection_schema.partition_tag_); - resRow["version"].to_string(collection_schema.version_); - collection_schema.flush_lsn_ = resRow["flush_lsn"]; - - collection_schema_array.emplace_back(collection_schema); - } - } catch (std::exception& e) { - return HandleException("Failed to get all collections", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::DropCollections(const std::vector& collection_id_array) { - try { - // distribute id array to batches - std::vector> id_groups; - DistributeBatch(collection_id_array, id_groups); - - server::MetricCollector metric; - - for (auto group : id_groups) { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.DropCollection.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.DropCollection.throw_exception", throw std::exception();); - - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - // to ensure UpdateCollectionFiles to be a atomic operation - std::lock_guard meta_lock(meta_mutex_); - - // soft delete collection - mysqlpp::Query statement = connectionPtr->query(); - // - statement << "UPDATE " << META_TABLES << " SET state = " << std::to_string(CollectionSchema::TO_DELETE) - << " WHERE table_id in("; - for (size_t i = 0; i < group.size(); i++) { - statement << mysqlpp::quote << group[i]; - if (i != group.size() - 1) { - statement << ","; - } - } - statement << ")"; - - LOG_ENGINE_DEBUG_ << "DropCollections: " << statement.str(); - - if (!statement.exec()) { - return HandleException("Failed to drop collections", statement.error()); - } - } // Scoped Connection - - auto status = DeleteCollectionFiles(collection_id_array); - LOG_ENGINE_DEBUG_ << "Successfully delete collections"; - return status; - } catch (std::exception& e) { - return HandleException("Failed to drop collection", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::DeleteCollectionFiles(const std::vector& collection_id_array) { - try { - // distribute id array to batches - std::vector> id_groups; - DistributeBatch(collection_id_array, id_groups); - - server::MetricCollector metric; - for (auto group : id_groups) { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.DeleteCollectionFiles.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.DeleteCollectionFiles.throw_exception", throw std::exception();); - - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - // to ensure UpdateCollectionFiles to be a atomic operation - std::lock_guard meta_lock(meta_mutex_); - - // soft delete collection files - mysqlpp::Query statement = connectionPtr->query(); - // - statement << "UPDATE " << META_TABLEFILES << " SET file_type = " << std::to_string(SegmentSchema::TO_DELETE) - << " ,updated_time = " << std::to_string(utils::GetMicroSecTimeStamp()) << " WHERE table_id in ("; - for (size_t i = 0; i < group.size(); i++) { - statement << mysqlpp::quote << group[i]; - if (i != group.size() - 1) { - statement << ","; - } - } - statement << ") AND file_type <> " << std::to_string(SegmentSchema::TO_DELETE) << ";"; - - LOG_ENGINE_DEBUG_ << "DeleteCollectionFiles: " << statement.str(); - - if (!statement.exec()) { - return HandleException("Failed to delete colletion files", statement.error()); - } - } // Scoped Connection - - LOG_ENGINE_DEBUG_ << "Successfully delete collection files"; - } catch (std::exception& e) { - return HandleException("Failed to delete colletion files", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::CreateCollectionFile(SegmentSchema& file_schema) { - if (file_schema.date_ == EmptyDate) { - file_schema.date_ = utils::GetDate(); - } - CollectionSchema collection_schema; - collection_schema.collection_id_ = file_schema.collection_id_; - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - return status; - } - - try { - server::MetricCollector metric; - - NextFileId(file_schema.file_id_); - if (file_schema.segment_id_.empty()) { - file_schema.segment_id_ = file_schema.file_id_; - } - file_schema.dimension_ = collection_schema.dimension_; - file_schema.file_size_ = 0; - file_schema.row_count_ = 0; - file_schema.created_on_ = utils::GetMicroSecTimeStamp(); - file_schema.updated_time_ = file_schema.created_on_; - file_schema.index_file_size_ = collection_schema.index_file_size_; - file_schema.index_params_ = collection_schema.index_params_; - file_schema.engine_type_ = collection_schema.engine_type_; - file_schema.metric_type_ = collection_schema.metric_type_; - - std::string id = "NULL"; // auto-increment - std::string collection_id = file_schema.collection_id_; - std::string segment_id = file_schema.segment_id_; - std::string engine_type = std::to_string(file_schema.engine_type_); - std::string file_id = file_schema.file_id_; - std::string file_type = std::to_string(file_schema.file_type_); - std::string file_size = std::to_string(file_schema.file_size_); - std::string row_count = std::to_string(file_schema.row_count_); - std::string updated_time = std::to_string(file_schema.updated_time_); - std::string created_on = std::to_string(file_schema.created_on_); - std::string date = std::to_string(file_schema.date_); - std::string flush_lsn = std::to_string(file_schema.flush_lsn_); - - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.CreateCollectionFiles.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.CreateCollectionFiles.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - // to ensure UpdateCollectionFiles to be a atomic operation - std::lock_guard meta_lock(meta_mutex_); - - mysqlpp::Query statement = connectionPtr->query(); - - statement << "INSERT INTO " << META_TABLEFILES << " VALUES(" << id << ", " << mysqlpp::quote - << collection_id << ", " << mysqlpp::quote << segment_id << ", " << engine_type << ", " - << mysqlpp::quote << file_id << ", " << file_type << ", " << file_size << ", " << row_count - << ", " << updated_time << ", " << created_on << ", " << date << ", " << flush_lsn << ");"; - - LOG_ENGINE_DEBUG_ << "CreateCollectionFile: " << statement.str(); - - if (mysqlpp::SimpleResult res = statement.execute()) { - file_schema.id_ = res.insert_id(); // Might need to use SELECT LAST_INSERT_ID()? - - // Consume all results to avoid "Commands out of sync" error - } else { - return HandleException("Failed to create collection file", statement.error()); - } - } // Scoped Connection - - LOG_ENGINE_DEBUG_ << "Successfully create collection file, file id = " << file_schema.file_id_; - return utils::CreateCollectionFilePath(options_, file_schema); - } catch (std::exception& e) { - return HandleException("Failed to create collection file", e.what()); - } -} - -Status -MySQLMetaImpl::GetCollectionFiles(const std::string& collection_id, const std::vector& ids, - FilesHolder& files_holder) { - if (ids.empty()) { - return Status::OK(); - } - - std::stringstream idSS; - for (auto& id : ids) { - idSS << "id = " << std::to_string(id) << " OR "; - } - std::string idStr = idSS.str(); - idStr = idStr.substr(0, idStr.size() - 4); // remove the last " OR " - - try { - mysqlpp::StoreQueryResult res; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.GetCollectionFiles.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.GetCollectionFiles.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - // to ensure UpdateCollectionFiles to be a atomic operation - std::lock_guard meta_lock(meta_mutex_); - - mysqlpp::Query statement = connectionPtr->query(); - statement - << "SELECT id, segment_id, engine_type, file_id, file_type, file_size, row_count, date, created_on" - << " FROM " << META_TABLEFILES << " WHERE table_id = " << mysqlpp::quote << collection_id << " AND (" - << idStr << ")" - << " AND file_type <> " << std::to_string(SegmentSchema::TO_DELETE) << ";"; - - LOG_ENGINE_DEBUG_ << "GetCollectionFiles: " << statement.str(); - - res = statement.store(); - } // Scoped Connection - - CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_id; - DescribeCollection(collection_schema); - - Status ret; - for (auto& resRow : res) { - SegmentSchema file_schema; - file_schema.id_ = resRow["id"]; - file_schema.collection_id_ = collection_id; - resRow["segment_id"].to_string(file_schema.segment_id_); - file_schema.index_file_size_ = collection_schema.index_file_size_; - file_schema.engine_type_ = resRow["engine_type"]; - file_schema.index_params_ = collection_schema.index_params_; - file_schema.metric_type_ = collection_schema.metric_type_; - resRow["file_id"].to_string(file_schema.file_id_); - file_schema.file_type_ = resRow["file_type"]; - file_schema.file_size_ = resRow["file_size"]; - file_schema.row_count_ = resRow["row_count"]; - file_schema.date_ = resRow["date"]; - file_schema.created_on_ = resRow["created_on"]; - file_schema.dimension_ = collection_schema.dimension_; - - utils::GetCollectionFilePath(options_, file_schema); - files_holder.MarkFile(file_schema); - } - - LOG_ENGINE_DEBUG_ << "Get " << res.size() << " files by id from collection " << collection_id; - return ret; - } catch (std::exception& e) { - return HandleException("Failed to get collection files", e.what()); - } -} - -Status -MySQLMetaImpl::GetCollectionFilesBySegmentId(const std::string& segment_id, FilesHolder& files_holder) { - try { - mysqlpp::StoreQueryResult res; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - if (connectionPtr == nullptr) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - // to ensure UpdateCollectionFiles to be a atomic operation - std::lock_guard meta_lock(meta_mutex_); - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT id, table_id, segment_id, engine_type, file_id, file_type, file_size, " - << "row_count, date, created_on" - << " FROM " << META_TABLEFILES << " WHERE segment_id = " << mysqlpp::quote << segment_id - << " AND file_type <> " << std::to_string(SegmentSchema::TO_DELETE) << ";"; - - LOG_ENGINE_DEBUG_ << "GetCollectionFilesBySegmentId: " << statement.str(); - - res = statement.store(); - } // Scoped Connection - - if (!res.empty()) { - CollectionSchema collection_schema; - res[0]["table_id"].to_string(collection_schema.collection_id_); - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - return status; - } - - for (auto& resRow : res) { - SegmentSchema file_schema; - file_schema.id_ = resRow["id"]; - file_schema.collection_id_ = collection_schema.collection_id_; - resRow["segment_id"].to_string(file_schema.segment_id_); - file_schema.index_file_size_ = collection_schema.index_file_size_; - file_schema.engine_type_ = resRow["engine_type"]; - file_schema.index_params_ = collection_schema.index_params_; - file_schema.metric_type_ = collection_schema.metric_type_; - resRow["file_id"].to_string(file_schema.file_id_); - file_schema.file_type_ = resRow["file_type"]; - file_schema.file_size_ = resRow["file_size"]; - file_schema.row_count_ = resRow["row_count"]; - file_schema.date_ = resRow["date"]; - file_schema.created_on_ = resRow["created_on"]; - file_schema.dimension_ = collection_schema.dimension_; - - utils::GetCollectionFilePath(options_, file_schema); - files_holder.MarkFile(file_schema); - } - } - - LOG_ENGINE_DEBUG_ << "Get " << res.size() << " files by segment id " << segment_id; - return Status::OK(); - } catch (std::exception& e) { - return HandleException("Failed to get collection files by segment id", e.what()); - } -} - -Status -MySQLMetaImpl::UpdateCollectionIndex(const std::string& collection_id, const CollectionIndex& index) { - try { - server::MetricCollector metric; - - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.UpdateCollectionIndex.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.UpdateCollectionIndex.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT id, state, dimension, created_on" - << " FROM " << META_TABLES << " WHERE table_id = " << mysqlpp::quote << collection_id - << " AND state <> " << std::to_string(CollectionSchema::TO_DELETE) << ";"; - - LOG_ENGINE_DEBUG_ << "UpdateCollectionIndex: " << statement.str(); - - mysqlpp::StoreQueryResult res = statement.store(); - - if (res.num_rows() == 1) { - const mysqlpp::Row& resRow = res[0]; - - size_t id = resRow["id"]; - int32_t state = resRow["state"]; - uint16_t dimension = resRow["dimension"]; - int64_t created_on = resRow["created_on"]; - - statement << "UPDATE " << META_TABLES << " SET id = " << id << " ,state = " << state - << " ,dimension = " << dimension << " ,created_on = " << created_on - << " ,engine_type = " << index.engine_type_ << " ,index_params = " << mysqlpp::quote - << index.extra_params_.dump() << " ,metric_type = " << index.metric_type_ - << " WHERE table_id = " << mysqlpp::quote << collection_id << ";"; - - LOG_ENGINE_DEBUG_ << "UpdateCollectionIndex: " << statement.str(); - - if (!statement.exec()) { - return HandleException("Failed to update collection index", statement.error()); - } - } else { - return Status(DB_NOT_FOUND, "Collection " + collection_id + " not found"); - } - } // Scoped Connection - - LOG_ENGINE_DEBUG_ << "Successfully update collection index for " << collection_id; - } catch (std::exception& e) { - return HandleException("Failed to update collection index", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::UpdateCollectionFlag(const std::string& collection_id, int64_t flag) { - try { - server::MetricCollector metric; - - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.UpdateCollectionFlag.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.UpdateCollectionFlag.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - statement << "UPDATE " << META_TABLES << " SET flag = " << flag << " WHERE table_id = " << mysqlpp::quote - << collection_id << ";"; - - LOG_ENGINE_DEBUG_ << "UpdateCollectionFlag: " << statement.str(); - - if (!statement.exec()) { - return HandleException("Failed to update collection flag", statement.error()); - } - } // Scoped Connection - - LOG_ENGINE_DEBUG_ << "Successfully update collection flag for " << collection_id; - } catch (std::exception& e) { - return HandleException("Failed to update collection flag", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::UpdateCollectionFlushLSN(const std::string& collection_id, uint64_t flush_lsn) { - try { - server::MetricCollector metric; - - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - if (connectionPtr == nullptr) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - statement << "UPDATE " << META_TABLES << " SET flush_lsn = " << flush_lsn - << " WHERE table_id = " << mysqlpp::quote << collection_id << ";"; - - LOG_ENGINE_DEBUG_ << "UpdateCollectionFlushLSN: " << statement.str(); - - if (!statement.exec()) { - return HandleException("Failed to update collection lsn", statement.error()); - } - } // Scoped Connection - - LOG_ENGINE_DEBUG_ << "Successfully update collection flush_lsn for " << collection_id; - } catch (std::exception& e) { - return HandleException("Failed to update collection lsn", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::GetCollectionFlushLSN(const std::string& collection_id, uint64_t& flush_lsn) { - try { - server::MetricCollector metric; - - mysqlpp::StoreQueryResult res; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - if (connectionPtr == nullptr) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT flush_lsn FROM " << META_TABLES << " WHERE table_id = " << mysqlpp::quote - << collection_id << ";"; - - LOG_ENGINE_DEBUG_ << "GetCollectionFlushLSN: " << statement.str(); - - res = statement.store(); - } // Scoped Connection - - if (!res.empty()) { - flush_lsn = res[0]["flush_lsn"]; - } - } catch (std::exception& e) { - return HandleException("Failed to get collection lsn", e.what()); - } - - return Status::OK(); -} - -// ZR: this function assumes all fields in file_schema have value -Status -MySQLMetaImpl::UpdateCollectionFile(SegmentSchema& file_schema) { - file_schema.updated_time_ = utils::GetMicroSecTimeStamp(); - - try { - server::MetricCollector metric; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.UpdateCollectionFile.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.UpdateCollectionFile.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - // to ensure UpdateCollectionFiles to be a atomic operation - std::lock_guard meta_lock(meta_mutex_); - - mysqlpp::Query statement = connectionPtr->query(); - - // if the collection has been deleted, just mark the collection file as TO_DELETE - // clean thread will delete the file later - statement << "SELECT state FROM " << META_TABLES << " WHERE table_id = " << mysqlpp::quote - << file_schema.collection_id_ << ";"; - - LOG_ENGINE_DEBUG_ << "UpdateCollectionFile: " << statement.str(); - - mysqlpp::StoreQueryResult res = statement.store(); - - if (res.num_rows() == 1) { - int state = res[0]["state"]; - if (state == CollectionSchema::TO_DELETE) { - file_schema.file_type_ = SegmentSchema::TO_DELETE; - } - } else { - file_schema.file_type_ = SegmentSchema::TO_DELETE; - } - - std::string id = std::to_string(file_schema.id_); - std::string collection_id = file_schema.collection_id_; - std::string engine_type = std::to_string(file_schema.engine_type_); - std::string file_id = file_schema.file_id_; - std::string file_type = std::to_string(file_schema.file_type_); - std::string file_size = std::to_string(file_schema.file_size_); - std::string row_count = std::to_string(file_schema.row_count_); - std::string updated_time = std::to_string(file_schema.updated_time_); - std::string created_on = std::to_string(file_schema.created_on_); - std::string date = std::to_string(file_schema.date_); - - statement << "UPDATE " << META_TABLEFILES << " SET table_id = " << mysqlpp::quote << collection_id - << " ,engine_type = " << engine_type << " ,file_id = " << mysqlpp::quote << file_id - << " ,file_type = " << file_type << " ,file_size = " << file_size << " ,row_count = " << row_count - << " ,updated_time = " << updated_time << " ,created_on = " << created_on << " ,date = " << date - << " WHERE id = " << id << ";"; - - LOG_ENGINE_DEBUG_ << "UpdateCollectionFile: " << statement.str(); - - if (!statement.exec()) { - LOG_ENGINE_DEBUG_ << "collection_id= " << file_schema.collection_id_ - << " file_id=" << file_schema.file_id_; - return HandleException("Failed to update collection file", statement.error()); - } - } // Scoped Connection - - LOG_ENGINE_DEBUG_ << "Update single collection file, file id: " << file_schema.file_id_; - } catch (std::exception& e) { - return HandleException("Failed to update collection file", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::UpdateCollectionFilesToIndex(const std::string& collection_id) { - try { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.UpdateCollectionFilesToIndex.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.UpdateCollectionFilesToIndex.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - // to ensure UpdateCollectionFiles to be a atomic operation - std::lock_guard meta_lock(meta_mutex_); - - mysqlpp::Query statement = connectionPtr->query(); - - statement << "UPDATE " << META_TABLEFILES << " SET file_type = " << std::to_string(SegmentSchema::TO_INDEX) - << " WHERE table_id = " << mysqlpp::quote << collection_id - << " AND row_count >= " << std::to_string(meta::BUILD_INDEX_THRESHOLD) - << " AND file_type = " << std::to_string(SegmentSchema::RAW) << ";"; - - LOG_ENGINE_DEBUG_ << "UpdateCollectionFilesToIndex: " << statement.str(); - - if (!statement.exec()) { - return HandleException("Failed to update collection files to index", statement.error()); - } - - LOG_ENGINE_DEBUG_ << "Update files to to_index for " << collection_id; - } catch (std::exception& e) { - return HandleException("Failed to update collection files to index", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::UpdateCollectionFiles(SegmentsSchema& files) { - try { - server::MetricCollector metric; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.UpdateCollectionFiles.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.UpdateCollectionFiles.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - // to ensure UpdateCollectionFiles to be a atomic operation - std::lock_guard meta_lock(meta_mutex_); - - mysqlpp::Query statement = connectionPtr->query(); - - std::map has_collections; - for (auto& file_schema : files) { - if (has_collections.find(file_schema.collection_id_) != has_collections.end()) { - continue; - } - - statement << "SELECT EXISTS" - << " (SELECT 1 FROM " << META_TABLES << " WHERE table_id = " << mysqlpp::quote - << file_schema.collection_id_ << " AND state <> " - << std::to_string(CollectionSchema::TO_DELETE) << ")" - << " AS " << mysqlpp::quote << "check" - << ";"; - - LOG_ENGINE_DEBUG_ << "UpdateCollectionFiles: " << statement.str(); - - mysqlpp::StoreQueryResult res = statement.store(); - - int check = res[0]["check"]; - has_collections[file_schema.collection_id_] = (check == 1); - } - - for (auto& file_schema : files) { - if (!has_collections[file_schema.collection_id_]) { - file_schema.file_type_ = SegmentSchema::TO_DELETE; - } - file_schema.updated_time_ = utils::GetMicroSecTimeStamp(); - - std::string id = std::to_string(file_schema.id_); - std::string& collection_id = file_schema.collection_id_; - std::string engine_type = std::to_string(file_schema.engine_type_); - std::string& file_id = file_schema.file_id_; - std::string file_type = std::to_string(file_schema.file_type_); - std::string file_size = std::to_string(file_schema.file_size_); - std::string row_count = std::to_string(file_schema.row_count_); - std::string updated_time = std::to_string(file_schema.updated_time_); - std::string created_on = std::to_string(file_schema.created_on_); - std::string date = std::to_string(file_schema.date_); - - statement << "UPDATE " << META_TABLEFILES << " SET table_id = " << mysqlpp::quote << collection_id - << " ,engine_type = " << engine_type << " ,file_id = " << mysqlpp::quote << file_id - << " ,file_type = " << file_type << " ,file_size = " << file_size - << " ,row_count = " << row_count << " ,updated_time = " << updated_time - << " ,created_on = " << created_on << " ,date = " << date << " WHERE id = " << id << ";"; - - LOG_ENGINE_DEBUG_ << "UpdateCollectionFiles: " << statement.str(); - - if (!statement.exec()) { - return HandleException("Failed to update collection files", statement.error()); - } - } - } // Scoped Connection - - LOG_ENGINE_DEBUG_ << "Update " << files.size() << " collection files"; - } catch (std::exception& e) { - return HandleException("Failed to update collection files", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::UpdateCollectionFilesRowCount(SegmentsSchema& files) { - try { - server::MetricCollector metric; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - // to ensure UpdateCollectionFiles to be a atomic operation - std::lock_guard meta_lock(meta_mutex_); - - mysqlpp::Query statement = connectionPtr->query(); - - for (auto& file : files) { - std::string row_count = std::to_string(file.row_count_); - std::string updated_time = std::to_string(utils::GetMicroSecTimeStamp()); - - statement << "UPDATE " << META_TABLEFILES << " SET row_count = " << row_count - << " , updated_time = " << updated_time << " WHERE file_id = " << file.file_id_ << ";"; - - LOG_ENGINE_DEBUG_ << "UpdateCollectionFilesRowCount: " << statement.str(); - - if (!statement.exec()) { - return HandleException("Failed to update collection files row count", statement.error()); - } - - LOG_ENGINE_DEBUG_ << "Update file " << file.file_id_ << " row count to " << file.row_count_; - } - } // Scoped Connection - - LOG_ENGINE_DEBUG_ << "Update " << files.size() << " collection files"; - } catch (std::exception& e) { - return HandleException("Failed to update collection files row count", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::DescribeCollectionIndex(const std::string& collection_id, CollectionIndex& index) { - try { - server::MetricCollector metric; - - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.DescribeCollectionIndex.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.DescribeCollectionIndex.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT engine_type, index_params, index_file_size, metric_type" - << " FROM " << META_TABLES << " WHERE table_id = " << mysqlpp::quote << collection_id - << " AND state <> " << std::to_string(CollectionSchema::TO_DELETE) << ";"; - - LOG_ENGINE_DEBUG_ << "DescribeCollectionIndex: " << statement.str(); - - mysqlpp::StoreQueryResult res = statement.store(); - - if (res.num_rows() == 1) { - const mysqlpp::Row& resRow = res[0]; - - index.engine_type_ = resRow["engine_type"]; - std::string str_index_params; - resRow["index_params"].to_string(str_index_params); - index.extra_params_ = milvus::json::parse(str_index_params); - index.metric_type_ = resRow["metric_type"]; - } else { - return Status(DB_NOT_FOUND, "Collection " + collection_id + " not found"); - } - } // Scoped Connection - } catch (std::exception& e) { - return HandleException("Failed to describe collection index", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::DropCollectionIndex(const std::string& collection_id) { - try { - server::MetricCollector metric; - - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.DropCollectionIndex.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.DropCollectionIndex.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - - // soft delete index files - statement << "UPDATE " << META_TABLEFILES << " SET file_type = " << std::to_string(SegmentSchema::TO_DELETE) - << " ,updated_time = " << utils::GetMicroSecTimeStamp() << " WHERE table_id = " << mysqlpp::quote - << collection_id << " AND file_type = " << std::to_string(SegmentSchema::INDEX) << ";"; - - LOG_ENGINE_DEBUG_ << "DropCollectionIndex: " << statement.str(); - - if (!statement.exec()) { - return HandleException("Failed to drop collection index", statement.error()); - } - - // set all backup file to raw - statement << "UPDATE " << META_TABLEFILES << " SET file_type = " << std::to_string(SegmentSchema::RAW) - << " ,updated_time = " << utils::GetMicroSecTimeStamp() << " WHERE table_id = " << mysqlpp::quote - << collection_id << " AND file_type = " << std::to_string(SegmentSchema::BACKUP) << ";"; - - LOG_ENGINE_DEBUG_ << "DropCollectionIndex: " << statement.str(); - - if (!statement.exec()) { - return HandleException("Failed to drop collection index", statement.error()); - } - - // set collection index type to raw - statement << "UPDATE " << META_TABLES << " SET engine_type = " - << " (CASE" - << " WHEN metric_type in (" << (int32_t)MetricType::HAMMING << " ," - << (int32_t)MetricType::JACCARD << " ," << (int32_t)MetricType::TANIMOTO << ")" - << " THEN " << (int32_t)EngineType::FAISS_BIN_IDMAP << " ELSE " - << (int32_t)EngineType::FAISS_IDMAP << " END)" - << " , index_params = '{}'" - << " WHERE table_id = " << mysqlpp::quote << collection_id << ";"; - - LOG_ENGINE_DEBUG_ << "DropCollectionIndex: " << statement.str(); - - if (!statement.exec()) { - return HandleException("Failed to drop collection index", statement.error()); - } - } // Scoped Connection - - LOG_ENGINE_DEBUG_ << "Successfully drop collection index for " << collection_id; - } catch (std::exception& e) { - return HandleException("Failed to drop collection index", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::CreatePartition(const std::string& collection_id, const std::string& partition_name, - const std::string& tag, uint64_t lsn) { - server::MetricCollector metric; - - CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_id; - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - return status; - } - - // not allow create partition under partition - if (!collection_schema.owner_collection_.empty()) { - return Status(DB_ERROR, "Nested partition is not allowed"); - } - - // trim side-blank of tag, only compare valid characters - // for example: " ab cd " is treated as "ab cd" - std::string valid_tag = tag; - server::StringHelpFunctions::TrimStringBlank(valid_tag); - - // not allow duplicated partition - std::string exist_partition; - GetPartitionName(collection_id, valid_tag, exist_partition); - if (!exist_partition.empty()) { - return Status(DB_ERROR, "Duplicate partition is not allowed"); - } - - if (partition_name == "") { - // generate unique partition name - NextCollectionId(collection_schema.collection_id_); - } else { - collection_schema.collection_id_ = partition_name; - } - - collection_schema.id_ = -1; - collection_schema.flag_ = 0; - collection_schema.created_on_ = utils::GetMicroSecTimeStamp(); - collection_schema.owner_collection_ = collection_id; - collection_schema.partition_tag_ = valid_tag; - collection_schema.flush_lsn_ = lsn; - - status = CreateCollection(collection_schema); - fiu_do_on("MySQLMetaImpl.CreatePartition.aleady_exist", status = Status(DB_ALREADY_EXIST, "")); - if (status.code() == DB_ALREADY_EXIST) { - return Status(DB_ALREADY_EXIST, "Partition already exists"); - } - - return status; -} - -Status -MySQLMetaImpl::HasPartition(const std::string& collection_id, const std::string& tag, bool& has_or_not) { - try { - server::MetricCollector metric; - mysqlpp::StoreQueryResult res; - - // trim side-blank of tag, only compare valid characters - // for example: " ab cd " is treated as "ab cd" - std::string valid_tag = tag; - server::StringHelpFunctions::TrimStringBlank(valid_tag); - - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT table_id FROM " << META_TABLES << " WHERE owner_table = " << mysqlpp::quote - << collection_id << " AND partition_tag = " << mysqlpp::quote << valid_tag << " AND state <> " - << std::to_string(CollectionSchema::TO_DELETE) << ";"; - - LOG_ENGINE_DEBUG_ << "HasPartition: " << statement.str(); - - res = statement.store(); - } // Scoped Connection - - if (res.num_rows() > 0) { - has_or_not = true; - } else { - has_or_not = false; - } - } catch (std::exception& e) { - return HandleException("Failed to lookup partition", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::DropPartition(const std::string& partition_name) { - return DropCollections({partition_name}); -} - -Status -MySQLMetaImpl::ShowPartitions(const std::string& collection_id, - std::vector& partition_schema_array) { - try { - server::MetricCollector metric; - mysqlpp::StoreQueryResult res; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.ShowPartitions.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.ShowPartitions.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT table_id, id, state, dimension, created_on, flag, index_file_size," - << " engine_type, index_params, metric_type, partition_tag, version, flush_lsn FROM " - << META_TABLES << " WHERE owner_table = " << mysqlpp::quote << collection_id << " AND state <> " - << std::to_string(CollectionSchema::TO_DELETE) << ";"; - - LOG_ENGINE_DEBUG_ << "ShowPartitions: " << statement.str(); - - res = statement.store(); - } // Scoped Connection - - for (auto& resRow : res) { - meta::CollectionSchema partition_schema; - resRow["table_id"].to_string(partition_schema.collection_id_); - partition_schema.id_ = resRow["id"]; // implicit conversion - partition_schema.state_ = resRow["state"]; - partition_schema.dimension_ = resRow["dimension"]; - partition_schema.created_on_ = resRow["created_on"]; - partition_schema.flag_ = resRow["flag"]; - partition_schema.index_file_size_ = resRow["index_file_size"]; - partition_schema.engine_type_ = resRow["engine_type"]; - resRow["index_params"].to_string(partition_schema.index_params_); - partition_schema.metric_type_ = resRow["metric_type"]; - partition_schema.owner_collection_ = collection_id; - resRow["partition_tag"].to_string(partition_schema.partition_tag_); - resRow["version"].to_string(partition_schema.version_); - partition_schema.flush_lsn_ = resRow["flush_lsn"]; - - partition_schema_array.emplace_back(partition_schema); - } - } catch (std::exception& e) { - return HandleException("Failed to show partitions", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::GetPartitionName(const std::string& collection_id, const std::string& tag, std::string& partition_name) { - try { - server::MetricCollector metric; - mysqlpp::StoreQueryResult res; - - // trim side-blank of tag, only compare valid characters - // for example: " ab cd " is treated as "ab cd" - std::string valid_tag = tag; - server::StringHelpFunctions::TrimStringBlank(valid_tag); - - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.GetPartitionName.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.GetPartitionName.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT table_id FROM " << META_TABLES << " WHERE owner_table = " << mysqlpp::quote - << collection_id << " AND partition_tag = " << mysqlpp::quote << valid_tag << " AND state <> " - << std::to_string(CollectionSchema::TO_DELETE) << ";"; - - LOG_ENGINE_DEBUG_ << "GetPartitionName: " << statement.str(); - - res = statement.store(); - } // Scoped Connection - - if (res.num_rows() > 0) { - const mysqlpp::Row& resRow = res[0]; - resRow["table_id"].to_string(partition_name); - } else { - return Status(DB_NOT_FOUND, "Partition " + valid_tag + " of collection " + collection_id + " not found"); - } - } catch (std::exception& e) { - return HandleException("Failed to get partition name", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::FilesToSearch(const std::string& collection_id, FilesHolder& files_holder) { - try { - server::MetricCollector metric; - mysqlpp::StoreQueryResult res; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.FilesToSearch.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.FilesToSearch.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - // to ensure UpdateCollectionFiles to be a atomic operation - std::lock_guard meta_lock(meta_mutex_); - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT id, table_id, segment_id, file_id, file_type, file_size, row_count, date," - << " engine_type, created_on, updated_time" - << " FROM " << META_TABLEFILES << " WHERE table_id = " << mysqlpp::quote << collection_id; - - // End - statement << " AND" - << " (file_type = " << std::to_string(SegmentSchema::RAW) - << " OR file_type = " << std::to_string(SegmentSchema::TO_INDEX) - << " OR file_type = " << std::to_string(SegmentSchema::INDEX) << ");"; - - LOG_ENGINE_DEBUG_ << "FilesToSearch: " << statement.str(); - - res = statement.store(); - } // Scoped Connection - - CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_id; - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - return status; - } - - Status ret; - int64_t files_count = 0; - for (auto& resRow : res) { - SegmentSchema collection_file; - collection_file.id_ = resRow["id"]; // implicit conversion - resRow["table_id"].to_string(collection_file.collection_id_); - resRow["segment_id"].to_string(collection_file.segment_id_); - resRow["file_id"].to_string(collection_file.file_id_); - collection_file.file_type_ = resRow["file_type"]; - collection_file.file_size_ = resRow["file_size"]; - collection_file.row_count_ = resRow["row_count"]; - collection_file.date_ = resRow["date"]; - collection_file.engine_type_ = resRow["engine_type"]; - collection_file.created_on_ = resRow["created_on"]; - collection_file.updated_time_ = resRow["updated_time"]; - - collection_file.dimension_ = collection_schema.dimension_; - collection_file.index_file_size_ = collection_schema.index_file_size_; - collection_file.index_params_ = collection_schema.index_params_; - collection_file.metric_type_ = collection_schema.metric_type_; - - auto status = utils::GetCollectionFilePath(options_, collection_file); - if (!status.ok()) { - ret = status; - continue; - } - - files_holder.MarkFile(collection_file); - files_count++; - } - - if (files_count == 0) { - LOG_ENGINE_DEBUG_ << "No file to search for collection: " << collection_id; - } else { - LOG_ENGINE_DEBUG_ << "Collect " << files_count << " to-search files in collection " << collection_id; - } - return ret; - } catch (std::exception& e) { - return HandleException("Failed to get files to search", e.what()); - } -} - -Status -MySQLMetaImpl::FilesToSearchEx(const std::string& root_collection, const std::set& partition_id_array, - FilesHolder& files_holder) { - try { - server::MetricCollector metric; - - // get root collection information - CollectionSchema collection_schema; - collection_schema.collection_id_ = root_collection; - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - return status; - } - - // distribute id array to batches - std::vector> id_groups; - DistributeBatch(partition_id_array, id_groups); - - // perform query batch by batch - int64_t files_count = 0; - Status ret; - for (auto group : id_groups) { - mysqlpp::StoreQueryResult res; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.FilesToSearch.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.FilesToSearch.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - // to ensure UpdateCollectionFiles to be a atomic operation - std::lock_guard meta_lock(meta_mutex_); - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT id, table_id, segment_id, file_id, file_type, file_size, row_count, date," - << " engine_type, created_on, updated_time" - << " FROM " << META_TABLEFILES << " WHERE table_id in ("; - for (size_t i = 0; i < group.size(); i++) { - statement << mysqlpp::quote << group[i]; - if (i != group.size() - 1) { - statement << ","; - } - } - statement << ")"; - - // End - statement << " AND" - << " (file_type = " << std::to_string(SegmentSchema::RAW) - << " OR file_type = " << std::to_string(SegmentSchema::TO_INDEX) - << " OR file_type = " << std::to_string(SegmentSchema::INDEX) << ");"; - - LOG_ENGINE_DEBUG_ << "FilesToSearch: " << statement.str(); - - res = statement.store(); - } // Scoped Connection - - for (auto& resRow : res) { - SegmentSchema collection_file; - collection_file.id_ = resRow["id"]; // implicit conversion - resRow["table_id"].to_string(collection_file.collection_id_); - resRow["segment_id"].to_string(collection_file.segment_id_); - resRow["file_id"].to_string(collection_file.file_id_); - collection_file.file_type_ = resRow["file_type"]; - collection_file.file_size_ = resRow["file_size"]; - collection_file.row_count_ = resRow["row_count"]; - collection_file.date_ = resRow["date"]; - collection_file.engine_type_ = resRow["engine_type"]; - collection_file.created_on_ = resRow["created_on"]; - collection_file.updated_time_ = resRow["updated_time"]; - - collection_file.dimension_ = collection_schema.dimension_; - collection_file.index_file_size_ = collection_schema.index_file_size_; - collection_file.index_params_ = collection_schema.index_params_; - collection_file.metric_type_ = collection_schema.metric_type_; - - auto status = utils::GetCollectionFilePath(options_, collection_file); - if (!status.ok()) { - ret = status; - continue; - } - - files_holder.MarkFile(collection_file); - files_count++; - } - } - - if (files_count == 0) { - LOG_ENGINE_DEBUG_ << "No file to search for collection: " << root_collection; - } else { - LOG_ENGINE_DEBUG_ << "Collect " << files_count << " to-search files in collection " << root_collection; - } - return ret; - } catch (std::exception& e) { - return HandleException("Failed to get files to search", e.what()); - } -} - -Status -MySQLMetaImpl::FilesToMerge(const std::string& collection_id, FilesHolder& files_holder) { - try { - server::MetricCollector metric; - - // check collection existence - CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_id; - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - return status; - } - - mysqlpp::StoreQueryResult res; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.FilesToMerge.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.FilesToMerge.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - // to ensure UpdateCollectionFiles to be a atomic operation - std::lock_guard meta_lock(meta_mutex_); - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT id, table_id, segment_id, file_id, file_type, file_size, row_count, date," - " engine_type, created_on, updated_time" - << " FROM " << META_TABLEFILES << " WHERE table_id = " << mysqlpp::quote << collection_id - << " AND file_type = " << std::to_string(SegmentSchema::RAW) << " ORDER BY row_count DESC;"; - - LOG_ENGINE_DEBUG_ << "FilesToMerge: " << statement.str(); - - res = statement.store(); - } // Scoped Connection - - Status ret; - SegmentsSchema files; - for (auto& resRow : res) { - SegmentSchema collection_file; - collection_file.file_size_ = resRow["file_size"]; - if ((int64_t)(collection_file.file_size_) >= collection_schema.index_file_size_) { - continue; // skip large file - } - - collection_file.id_ = resRow["id"]; // implicit conversion - resRow["table_id"].to_string(collection_file.collection_id_); - resRow["segment_id"].to_string(collection_file.segment_id_); - resRow["file_id"].to_string(collection_file.file_id_); - collection_file.file_type_ = resRow["file_type"]; - collection_file.file_size_ = resRow["file_size"]; - collection_file.row_count_ = resRow["row_count"]; - collection_file.date_ = resRow["date"]; - collection_file.engine_type_ = resRow["engine_type"]; - collection_file.created_on_ = resRow["created_on"]; - collection_file.updated_time_ = resRow["updated_time"]; - - collection_file.dimension_ = collection_schema.dimension_; - collection_file.index_file_size_ = collection_schema.index_file_size_; - collection_file.index_params_ = collection_schema.index_params_; - collection_file.metric_type_ = collection_schema.metric_type_; - - auto status = utils::GetCollectionFilePath(options_, collection_file); - if (!status.ok()) { - ret = status; - continue; - } - - files.emplace_back(collection_file); - } - - // no need to merge if files count less than 2 - if (files.size() > 1) { - LOG_ENGINE_DEBUG_ << "Collect " << files.size() << " to-merge files in collection " << collection_id; - for (auto& file : files) { - files_holder.MarkFile(file); - } - } - return ret; - } catch (std::exception& e) { - return HandleException("Failed to get files to merge", e.what()); - } -} - -Status -MySQLMetaImpl::FilesToIndex(FilesHolder& files_holder) { - try { - server::MetricCollector metric; - mysqlpp::StoreQueryResult res; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.FilesToIndex.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.FilesToIndex.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - // to ensure UpdateCollectionFiles to be a atomic operation - std::lock_guard meta_lock(meta_mutex_); - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT id, table_id, segment_id, file_id, file_type, file_size, row_count, date," - << " engine_type, created_on, updated_time" - << " FROM " << META_TABLEFILES << " WHERE file_type = " << std::to_string(SegmentSchema::TO_INDEX) - << ";"; - - // LOG_ENGINE_DEBUG_ << "FilesToIndex: " << statement.str(); - - res = statement.store(); - } // Scoped Connection - - Status ret; - int64_t files_count = 0; - std::map groups; - for (auto& resRow : res) { - SegmentSchema collection_file; - collection_file.id_ = resRow["id"]; // implicit conversion - resRow["table_id"].to_string(collection_file.collection_id_); - resRow["segment_id"].to_string(collection_file.segment_id_); - resRow["file_id"].to_string(collection_file.file_id_); - collection_file.file_type_ = resRow["file_type"]; - collection_file.file_size_ = resRow["file_size"]; - collection_file.row_count_ = resRow["row_count"]; - collection_file.date_ = resRow["date"]; - collection_file.engine_type_ = resRow["engine_type"]; - collection_file.created_on_ = resRow["created_on"]; - collection_file.updated_time_ = resRow["updated_time"]; - - auto groupItr = groups.find(collection_file.collection_id_); - if (groupItr == groups.end()) { - CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_file.collection_id_; - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - return status; - } - groups[collection_file.collection_id_] = collection_schema; - } - collection_file.dimension_ = groups[collection_file.collection_id_].dimension_; - collection_file.index_file_size_ = groups[collection_file.collection_id_].index_file_size_; - collection_file.index_params_ = groups[collection_file.collection_id_].index_params_; - collection_file.metric_type_ = groups[collection_file.collection_id_].metric_type_; - - auto status = utils::GetCollectionFilePath(options_, collection_file); - if (!status.ok()) { - ret = status; - } - - files_holder.MarkFile(collection_file); - files_count++; - } - - if (files_count > 0) { - LOG_ENGINE_DEBUG_ << "Collect " << files_count << " to-index files"; - } - return ret; - } catch (std::exception& e) { - return HandleException("Failed to get files to index", e.what()); - } -} - -Status -MySQLMetaImpl::FilesByType(const std::string& collection_id, const std::vector& file_types, - FilesHolder& files_holder) { - if (file_types.empty()) { - return Status(DB_ERROR, "file types array is empty"); - } - - Status ret = Status::OK(); - try { - mysqlpp::StoreQueryResult res; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.FilesByType.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.FilesByType.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - std::string types; - for (auto type : file_types) { - if (!types.empty()) { - types += ","; - } - types += std::to_string(type); - } - - // to ensure UpdateCollectionFiles to be a atomic operation - std::lock_guard meta_lock(meta_mutex_); - - mysqlpp::Query statement = connectionPtr->query(); - // since collection_id is a unique column we just need to check whether it exists or not - statement << "SELECT id, table_id, segment_id, file_id, file_type, file_size, row_count, date," - << " engine_type, created_on, updated_time" - << " FROM " << META_TABLEFILES << " WHERE table_id = " << mysqlpp::quote << collection_id - << " AND file_type in (" << types << ");"; - - LOG_ENGINE_DEBUG_ << "FilesByType: " << statement.str(); - - res = statement.store(); - } // Scoped Connection - - CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_id; - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - return status; - } - - if (res.num_rows() > 0) { - int raw_count = 0, new_count = 0, new_merge_count = 0, new_index_count = 0; - int to_index_count = 0, index_count = 0, backup_count = 0; - for (auto& resRow : res) { - SegmentSchema file_schema; - file_schema.id_ = resRow["id"]; - file_schema.collection_id_ = collection_id; - resRow["segment_id"].to_string(file_schema.segment_id_); - resRow["file_id"].to_string(file_schema.file_id_); - file_schema.file_type_ = resRow["file_type"]; - file_schema.file_size_ = resRow["file_size"]; - file_schema.row_count_ = resRow["row_count"]; - file_schema.date_ = resRow["date"]; - file_schema.engine_type_ = resRow["engine_type"]; - file_schema.created_on_ = resRow["created_on"]; - file_schema.updated_time_ = resRow["updated_time"]; - - file_schema.index_file_size_ = collection_schema.index_file_size_; - file_schema.index_params_ = collection_schema.index_params_; - file_schema.metric_type_ = collection_schema.metric_type_; - file_schema.dimension_ = collection_schema.dimension_; - - auto status = utils::GetCollectionFilePath(options_, file_schema); - if (!status.ok()) { - ret = status; - } - - files_holder.MarkFile(file_schema); - - int32_t file_type = resRow["file_type"]; - switch (file_type) { - case (int)SegmentSchema::RAW: - ++raw_count; - break; - case (int)SegmentSchema::NEW: - ++new_count; - break; - case (int)SegmentSchema::NEW_MERGE: - ++new_merge_count; - break; - case (int)SegmentSchema::NEW_INDEX: - ++new_index_count; - break; - case (int)SegmentSchema::TO_INDEX: - ++to_index_count; - break; - case (int)SegmentSchema::INDEX: - ++index_count; - break; - case (int)SegmentSchema::BACKUP: - ++backup_count; - break; - default: - break; - } - } - - std::string msg = "Get collection files by type."; - for (int file_type : file_types) { - switch (file_type) { - case (int)SegmentSchema::RAW: - msg = msg + " raw files:" + std::to_string(raw_count); - break; - case (int)SegmentSchema::NEW: - msg = msg + " new files:" + std::to_string(new_count); - break; - case (int)SegmentSchema::NEW_MERGE: - msg = msg + " new_merge files:" + std::to_string(new_merge_count); - break; - case (int)SegmentSchema::NEW_INDEX: - msg = msg + " new_index files:" + std::to_string(new_index_count); - break; - case (int)SegmentSchema::TO_INDEX: - msg = msg + " to_index files:" + std::to_string(to_index_count); - break; - case (int)SegmentSchema::INDEX: - msg = msg + " index files:" + std::to_string(index_count); - break; - case (int)SegmentSchema::BACKUP: - msg = msg + " backup files:" + std::to_string(backup_count); - break; - default: - break; - } - } - LOG_ENGINE_DEBUG_ << msg; - } - } catch (std::exception& e) { - return HandleException("Failed to get files by type", e.what()); - } - - return ret; -} - -Status -MySQLMetaImpl::FilesByTypeEx(const std::vector& collections, const std::vector& file_types, - FilesHolder& files_holder) { - try { - server::MetricCollector metric; - - // distribute id array to batches - std::vector> id_groups; - std::vector temp_group; - std::unordered_map map_collections; - for (auto& collection : collections) { - map_collections.insert(std::make_pair(collection.collection_id_, collection)); - temp_group.push_back(collection.collection_id_); - if (temp_group.size() >= SQL_BATCH_SIZE) { - id_groups.emplace_back(temp_group); - temp_group.clear(); - } - } - - if (!temp_group.empty()) { - id_groups.emplace_back(temp_group); - } - - // perform query batch by batch - Status ret; - int raw_count = 0, new_count = 0, new_merge_count = 0, new_index_count = 0; - int to_index_count = 0, index_count = 0, backup_count = 0; - for (auto group : id_groups) { - mysqlpp::StoreQueryResult res; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.FilesByTypeEx.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.FilesByTypeEx.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - std::string types; - for (auto type : file_types) { - if (!types.empty()) { - types += ","; - } - types += std::to_string(type); - } - - // to ensure UpdateCollectionFiles to be a atomic operation - std::lock_guard meta_lock(meta_mutex_); - - mysqlpp::Query statement = connectionPtr->query(); - // since collection_id is a unique column we just need to check whether it exists or not - statement << "SELECT id, table_id, segment_id, file_id, file_type, file_size, row_count, date," - << " engine_type, created_on, updated_time" - << " FROM " << META_TABLEFILES << " WHERE table_id in ("; - for (size_t i = 0; i < group.size(); i++) { - statement << mysqlpp::quote << group[i]; - if (i != group.size() - 1) { - statement << ","; - } - } - statement << ") AND file_type in (" << types << ");"; - - LOG_ENGINE_DEBUG_ << "FilesByType: " << statement.str(); - - res = statement.store(); - } // Scoped Connection - - for (auto& resRow : res) { - SegmentSchema file_schema; - file_schema.id_ = resRow["id"]; // implicit conversion - resRow["table_id"].to_string(file_schema.collection_id_); - resRow["segment_id"].to_string(file_schema.segment_id_); - resRow["file_id"].to_string(file_schema.file_id_); - file_schema.file_type_ = resRow["file_type"]; - file_schema.file_size_ = resRow["file_size"]; - file_schema.row_count_ = resRow["row_count"]; - file_schema.date_ = resRow["date"]; - file_schema.engine_type_ = resRow["engine_type"]; - file_schema.created_on_ = resRow["created_on"]; - file_schema.updated_time_ = resRow["updated_time"]; - - auto& collection_schema = map_collections[file_schema.collection_id_]; - file_schema.dimension_ = collection_schema.dimension_; - file_schema.index_file_size_ = collection_schema.index_file_size_; - file_schema.index_params_ = collection_schema.index_params_; - file_schema.metric_type_ = collection_schema.metric_type_; - - auto status = utils::GetCollectionFilePath(options_, file_schema); - if (!status.ok()) { - ret = status; - continue; - } - - files_holder.MarkFile(file_schema); - - int32_t file_type = resRow["file_type"]; - switch (file_type) { - case (int)SegmentSchema::RAW: - ++raw_count; - break; - case (int)SegmentSchema::NEW: - ++new_count; - break; - case (int)SegmentSchema::NEW_MERGE: - ++new_merge_count; - break; - case (int)SegmentSchema::NEW_INDEX: - ++new_index_count; - break; - case (int)SegmentSchema::TO_INDEX: - ++to_index_count; - break; - case (int)SegmentSchema::INDEX: - ++index_count; - break; - case (int)SegmentSchema::BACKUP: - ++backup_count; - break; - default: - break; - } - } - } - - std::string msg = "Get collection files by type."; - for (int file_type : file_types) { - switch (file_type) { - case (int)SegmentSchema::RAW: - msg = msg + " raw files:" + std::to_string(raw_count); - break; - case (int)SegmentSchema::NEW: - msg = msg + " new files:" + std::to_string(new_count); - break; - case (int)SegmentSchema::NEW_MERGE: - msg = msg + " new_merge files:" + std::to_string(new_merge_count); - break; - case (int)SegmentSchema::NEW_INDEX: - msg = msg + " new_index files:" + std::to_string(new_index_count); - break; - case (int)SegmentSchema::TO_INDEX: - msg = msg + " to_index files:" + std::to_string(to_index_count); - break; - case (int)SegmentSchema::INDEX: - msg = msg + " index files:" + std::to_string(index_count); - break; - case (int)SegmentSchema::BACKUP: - msg = msg + " backup files:" + std::to_string(backup_count); - break; - default: - break; - } - } - LOG_ENGINE_DEBUG_ << msg; - return ret; - } catch (std::exception& e) { - return HandleException("Failed to get files by type", e.what()); - } -} - -Status -MySQLMetaImpl::FilesByID(const std::vector& ids, FilesHolder& files_holder) { - if (ids.empty()) { - return Status::OK(); - } - - try { - server::MetricCollector metric; - mysqlpp::StoreQueryResult res; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.FilesByID.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.FilesByID.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - // to ensure UpdateCollectionFiles to be a atomic operation - std::lock_guard meta_lock(meta_mutex_); - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT id, table_id, segment_id, file_id, file_type, file_size, row_count, date," - << " engine_type, created_on, updated_time" - << " FROM " << META_TABLEFILES; - - std::stringstream idSS; - for (auto& id : ids) { - idSS << "id = " << std::to_string(id) << " OR "; - } - std::string idStr = idSS.str(); - idStr = idStr.substr(0, idStr.size() - 4); // remove the last " OR " - - statement << " WHERE (" << idStr << ")"; - - LOG_ENGINE_DEBUG_ << "FilesToSearch: " << statement.str(); - - res = statement.store(); - } // Scoped Connection - - std::map collections; - Status ret; - int64_t files_count = 0; - for (auto& resRow : res) { - SegmentSchema collection_file; - collection_file.id_ = resRow["id"]; // implicit conversion - resRow["table_id"].to_string(collection_file.collection_id_); - resRow["segment_id"].to_string(collection_file.segment_id_); - resRow["file_id"].to_string(collection_file.file_id_); - collection_file.file_type_ = resRow["file_type"]; - collection_file.file_size_ = resRow["file_size"]; - collection_file.row_count_ = resRow["row_count"]; - collection_file.date_ = resRow["date"]; - collection_file.engine_type_ = resRow["engine_type"]; - collection_file.created_on_ = resRow["created_on"]; - collection_file.updated_time_ = resRow["updated_time"]; - - if (collections.find(collection_file.collection_id_) == collections.end()) { - CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_file.collection_id_; - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - return status; - } - collections.insert(std::make_pair(collection_file.collection_id_, collection_schema)); - } - - auto status = utils::GetCollectionFilePath(options_, collection_file); - if (!status.ok()) { - ret = status; - continue; - } - - files_holder.MarkFile(collection_file); - files_count++; - } - - milvus::engine::meta::SegmentsSchema& files = files_holder.HoldFiles(); - for (auto& collection_file : files) { - CollectionSchema& collection_schema = collections[collection_file.collection_id_]; - collection_file.dimension_ = collection_schema.dimension_; - collection_file.index_file_size_ = collection_schema.index_file_size_; - collection_file.index_params_ = collection_schema.index_params_; - collection_file.metric_type_ = collection_schema.metric_type_; - } - - if (files_count == 0) { - LOG_ENGINE_ERROR_ << "No file to search in file id list"; - } else { - LOG_ENGINE_DEBUG_ << "Collect " << files_count << " files by id"; - } - - return ret; - } catch (std::exception& e) { - return HandleException("Failed to get files by id", e.what()); - } -} - -// TODO(myh): Support swap to cloud storage -Status -MySQLMetaImpl::Archive() { - auto& criterias = options_.archive_conf_.GetCriterias(); - if (criterias.empty()) { - return Status::OK(); - } - - for (auto& kv : criterias) { - auto& criteria = kv.first; - auto& limit = kv.second; - if (criteria == engine::ARCHIVE_CONF_DAYS) { - size_t usecs = limit * DAY * US_PS; - int64_t now = utils::GetMicroSecTimeStamp(); - - try { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.Archive.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.Archive.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - statement << "UPDATE " << META_TABLEFILES - << " SET file_type = " << std::to_string(SegmentSchema::TO_DELETE) << " WHERE created_on < " - << std::to_string(now - usecs) << " AND file_type <> " - << std::to_string(SegmentSchema::TO_DELETE) << ";"; - - LOG_ENGINE_DEBUG_ << "Archive: " << statement.str(); - - if (!statement.exec()) { - return HandleException("Failed to archive", statement.error()); - } - - LOG_ENGINE_DEBUG_ << "Archive old files"; - } catch (std::exception& e) { - return HandleException("Failed to archive", e.what()); - } - } - if (criteria == engine::ARCHIVE_CONF_DISK) { - uint64_t sum = 0; - Size(sum); - - auto to_delete = (sum - limit * GB); - DiscardFiles(to_delete); - - LOG_ENGINE_DEBUG_ << "Archive files to free disk"; - } - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::Size(uint64_t& result) { - result = 0; - - try { - mysqlpp::StoreQueryResult res; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.Size.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.Size.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - // to ensure UpdateCollectionFiles to be a atomic operation - std::lock_guard meta_lock(meta_mutex_); - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT IFNULL(SUM(file_size),0) AS sum" - << " FROM " << META_TABLEFILES << " WHERE file_type <> " - << std::to_string(SegmentSchema::TO_DELETE) << ";"; - - LOG_ENGINE_DEBUG_ << "Size: " << statement.str(); - - res = statement.store(); - } // Scoped Connection - - if (res.empty()) { - result = 0; - } else { - result = res[0]["sum"]; - } - } catch (std::exception& e) { - return HandleException("Failed to get total files size", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::CleanUpShadowFiles() { - try { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.CleanUpShadowFiles.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.CleanUpShadowFiles.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT table_name" - << " FROM information_schema.tables" - << " WHERE table_schema = " << mysqlpp::quote << mysql_connection_pool_->db_name() - << " AND table_name = " << mysqlpp::quote << META_TABLEFILES << ";"; - - LOG_ENGINE_DEBUG_ << "CleanUp: " << statement.str(); - - mysqlpp::StoreQueryResult res = statement.store(); - - if (!res.empty()) { - LOG_ENGINE_DEBUG_ << "Remove collection file type as NEW"; - statement << "DELETE FROM " << META_TABLEFILES << " WHERE file_type IN (" - << std::to_string(SegmentSchema::NEW) << "," << std::to_string(SegmentSchema::NEW_MERGE) << "," - << std::to_string(SegmentSchema::NEW_INDEX) << ");"; - - LOG_ENGINE_DEBUG_ << "CleanUp: " << statement.str(); - - if (!statement.exec()) { - return HandleException("Failed to clean shadow files", statement.error()); - } - } - - if (res.size() > 0) { - LOG_ENGINE_DEBUG_ << "Clean " << res.size() << " files"; - } - } catch (std::exception& e) { - return HandleException("Failed to clean shadow files", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::CleanUpFilesWithTTL(uint64_t seconds /*, CleanUpFilter* filter*/) { - auto now = utils::GetMicroSecTimeStamp(); - std::set collection_ids; - std::map segment_ids; - - // remove to_delete files - try { - server::MetricCollector metric; - - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.CleanUpFilesWithTTL.RomoveToDeleteFiles_NullConnection", - is_null_connection = true); - fiu_do_on("MySQLMetaImpl.CleanUpFilesWithTTL.RomoveToDeleteFiles_ThrowException", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - mysqlpp::StoreQueryResult res; - { - // to ensure UpdateCollectionFiles to be a atomic operation - std::lock_guard meta_lock(meta_mutex_); - - statement << "SELECT id, table_id, segment_id, engine_type, file_id, file_type, date" - << " FROM " << META_TABLEFILES << " WHERE file_type IN (" - << std::to_string(SegmentSchema::TO_DELETE) << "," << std::to_string(SegmentSchema::BACKUP) - << ")" - << " AND updated_time < " << std::to_string(now - seconds * US_PS) << ";"; - - // LOG_ENGINE_DEBUG_ << "CleanUpFilesWithTTL: " << statement.str(); - - res = statement.store(); - } - - SegmentSchema collection_file; - std::vector delete_ids; - - int64_t clean_files = 0; - for (auto& resRow : res) { - collection_file.id_ = resRow["id"]; // implicit conversion - resRow["table_id"].to_string(collection_file.collection_id_); - resRow["segment_id"].to_string(collection_file.segment_id_); - collection_file.engine_type_ = resRow["engine_type"]; - resRow["file_id"].to_string(collection_file.file_id_); - collection_file.date_ = resRow["date"]; - collection_file.file_type_ = resRow["file_type"]; - - // check if the file can be deleted - if (!FilesHolder::CanBeDeleted(collection_file)) { - LOG_ENGINE_DEBUG_ << "File:" << collection_file.file_id_ - << " currently is in use, not able to delete now"; - continue; // ignore this file, don't delete it - } - - // erase file data from cache - // because GetCollectionFilePath won't able to generate file path after the file is deleted - utils::GetCollectionFilePath(options_, collection_file); - server::CommonUtil::EraseFromCache(collection_file.location_); - - if (collection_file.file_type_ == (int)SegmentSchema::TO_DELETE) { - // delete file from disk storage - utils::DeleteCollectionFilePath(options_, collection_file); - LOG_ENGINE_DEBUG_ << "Remove file id:" << collection_file.id_ - << " location:" << collection_file.location_; - - delete_ids.emplace_back(std::to_string(collection_file.id_)); - collection_ids.insert(collection_file.collection_id_); - segment_ids.insert(std::make_pair(collection_file.segment_id_, collection_file)); - - clean_files++; - } - } - - // delete file from meta - if (!delete_ids.empty()) { - std::stringstream idsToDeleteSS; - for (auto& id : delete_ids) { - idsToDeleteSS << "id = " << id << " OR "; - } - - std::string idsToDeleteStr = idsToDeleteSS.str(); - idsToDeleteStr = idsToDeleteStr.substr(0, idsToDeleteStr.size() - 4); // remove the last " OR " - statement << "DELETE FROM " << META_TABLEFILES << " WHERE " << idsToDeleteStr << ";"; - - LOG_ENGINE_DEBUG_ << "CleanUpFilesWithTTL: " << statement.str(); - - if (!statement.exec()) { - return HandleException("Failed to clean up with ttl", statement.error()); - } - } - - if (clean_files > 0) { - LOG_ENGINE_DEBUG_ << "Clean " << clean_files << " files expired in " << seconds << " seconds"; - } - } // Scoped Connection - } catch (std::exception& e) { - return HandleException("Failed to clean up with ttl", e.what()); - } - - // remove to_delete collections - try { - server::MetricCollector metric; - - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.CleanUpFilesWithTTL.RemoveToDeleteCollections_NUllConnection", - is_null_connection = true); - fiu_do_on("MySQLMetaImpl.CleanUpFilesWithTTL.RemoveToDeleteCollections_ThrowException", - throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT id, table_id" - << " FROM " << META_TABLES << " WHERE state = " << std::to_string(CollectionSchema::TO_DELETE) - << ";"; - - // LOG_ENGINE_DEBUG_ << "CleanUpFilesWithTTL: " << statement.str(); - - mysqlpp::StoreQueryResult res = statement.store(); - - int64_t remove_collections = 0; - if (!res.empty()) { - std::stringstream idsToDeleteSS; - for (auto& resRow : res) { - size_t id = resRow["id"]; - std::string collection_id; - resRow["table_id"].to_string(collection_id); - - utils::DeleteCollectionPath(options_, collection_id, false); // only delete empty folder - ++remove_collections; - idsToDeleteSS << "id = " << std::to_string(id) << " OR "; - } - std::string idsToDeleteStr = idsToDeleteSS.str(); - idsToDeleteStr = idsToDeleteStr.substr(0, idsToDeleteStr.size() - 4); // remove the last " OR " - statement << "DELETE FROM " << META_TABLES << " WHERE " << idsToDeleteStr << ";"; - - LOG_ENGINE_DEBUG_ << "CleanUpFilesWithTTL: " << statement.str(); - - if (!statement.exec()) { - return HandleException("Failed to clean up with ttl", statement.error()); - } - } - - if (remove_collections > 0) { - LOG_ENGINE_DEBUG_ << "Remove " << remove_collections << " collections from meta"; - } - } // Scoped Connection - } catch (std::exception& e) { - return HandleException("Failed to clean up with ttl", e.what()); - } - - // remove deleted collection folder - // don't remove collection folder until all its files has been deleted - try { - server::MetricCollector metric; - - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.CleanUpFilesWithTTL.RemoveDeletedCollectionFolder_NUllConnection", - is_null_connection = true); - fiu_do_on("MySQLMetaImpl.CleanUpFilesWithTTL.RemoveDeletedCollectionFolder_ThrowException", - throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - for (auto& collection_id : collection_ids) { - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT file_id" - << " FROM " << META_TABLEFILES << " WHERE table_id = " << mysqlpp::quote << collection_id - << ";"; - - // LOG_ENGINE_DEBUG_ << "CleanUpFilesWithTTL: " << statement.str(); - - mysqlpp::StoreQueryResult res = statement.store(); - - if (res.empty()) { - utils::DeleteCollectionPath(options_, collection_id); - } - } - - if (collection_ids.size() > 0) { - LOG_ENGINE_DEBUG_ << "Remove " << collection_ids.size() << " collections folder"; - } - } - } catch (std::exception& e) { - return HandleException("Failed to clean up with ttl", e.what()); - } - - // remove deleted segment folder - // don't remove segment folder until all its files has been deleted - try { - server::MetricCollector metric; - - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.CleanUpFilesWithTTL.RemoveDeletedSegmentFolder_NUllConnection", - is_null_connection = true); - fiu_do_on("MySQLMetaImpl.CleanUpFilesWithTTL.RemoveDeletedSegmentFolder_ThrowException", - throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - int64_t remove_segments = 0; - for (auto& segment_id : segment_ids) { - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT id" - << " FROM " << META_TABLEFILES << " WHERE segment_id = " << mysqlpp::quote << segment_id.first - << ";"; - - LOG_ENGINE_DEBUG_ << "CleanUpFilesWithTTL: " << statement.str(); - - mysqlpp::StoreQueryResult res = statement.store(); - - if (res.empty()) { - utils::DeleteSegment(options_, segment_id.second); - std::string segment_dir; - utils::GetParentPath(segment_id.second.location_, segment_dir); - LOG_ENGINE_DEBUG_ << "Remove segment directory: " << segment_dir; - ++remove_segments; - } - } - - if (remove_segments > 0) { - LOG_ENGINE_DEBUG_ << "Remove " << remove_segments << " segments folder"; - } - } - } catch (std::exception& e) { - return HandleException("Failed to clean up with ttl", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::Count(const std::string& collection_id, uint64_t& result) { - try { - server::MetricCollector metric; - - CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_id; - auto status = DescribeCollection(collection_schema); - - if (!status.ok()) { - return status; - } - - mysqlpp::StoreQueryResult res; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.Count.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.Count.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - // to ensure UpdateCollectionFiles to be a atomic operation - std::lock_guard meta_lock(meta_mutex_); - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT row_count" - << " FROM " << META_TABLEFILES << " WHERE table_id = " << mysqlpp::quote << collection_id - << " AND (file_type = " << std::to_string(SegmentSchema::RAW) - << " OR file_type = " << std::to_string(SegmentSchema::TO_INDEX) - << " OR file_type = " << std::to_string(SegmentSchema::INDEX) << ");"; - - LOG_ENGINE_DEBUG_ << "Count: " << statement.str(); - - res = statement.store(); - } // Scoped Connection - - result = 0; - for (auto& resRow : res) { - size_t size = resRow["row_count"]; - result += size; - } - } catch (std::exception& e) { - return HandleException("Failed to clean up with ttl", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::DropAll() { - try { - LOG_ENGINE_DEBUG_ << "Drop all mysql meta"; - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.DropAll.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.DropAll.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - statement << "DROP TABLE IF EXISTS " << TABLES_SCHEMA.name() << ", " << TABLEFILES_SCHEMA.name() << ", " - << ENVIRONMENT_SCHEMA.name() << ", " << FIELDS_SCHEMA.name() << ";"; - - LOG_ENGINE_DEBUG_ << "DropAll: " << statement.str(); - - if (statement.exec()) { - return Status::OK(); - } - return HandleException("Failed to drop all", statement.error()); - } catch (std::exception& e) { - return HandleException("Failed to drop all", e.what()); - } -} - -Status -MySQLMetaImpl::DiscardFiles(int64_t to_discard_size) { - if (to_discard_size <= 0) { - return Status::OK(); - } - LOG_ENGINE_DEBUG_ << "About to discard size=" << to_discard_size; - - try { - server::MetricCollector metric; - bool status; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.DiscardFiles.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.DiscardFiles.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT id, file_size" - << " FROM " << META_TABLEFILES << " WHERE file_type <> " - << std::to_string(SegmentSchema::TO_DELETE) << " ORDER BY id ASC " - << " LIMIT 10;"; - - LOG_ENGINE_DEBUG_ << "DiscardFiles: " << statement.str(); - - mysqlpp::StoreQueryResult res = statement.store(); - if (res.num_rows() == 0) { - return Status::OK(); - } - - SegmentSchema collection_file; - std::stringstream idsToDiscardSS; - for (auto& resRow : res) { - if (to_discard_size <= 0) { - break; - } - collection_file.id_ = resRow["id"]; - collection_file.file_size_ = resRow["file_size"]; - idsToDiscardSS << "id = " << std::to_string(collection_file.id_) << " OR "; - LOG_ENGINE_DEBUG_ << "Discard file id=" << collection_file.file_id_ - << " file size=" << collection_file.file_size_; - to_discard_size -= collection_file.file_size_; - } - - std::string idsToDiscardStr = idsToDiscardSS.str(); - idsToDiscardStr = idsToDiscardStr.substr(0, idsToDiscardStr.size() - 4); // remove the last " OR " - - statement << "UPDATE " << META_TABLEFILES << " SET file_type = " << std::to_string(SegmentSchema::TO_DELETE) - << " ,updated_time = " << std::to_string(utils::GetMicroSecTimeStamp()) << " WHERE " - << idsToDiscardStr << ";"; - - LOG_ENGINE_DEBUG_ << "DiscardFiles: " << statement.str(); - - status = statement.exec(); - if (!status) { - return HandleException("Failed to discard files", statement.error()); - } - } // Scoped Connection - - return DiscardFiles(to_discard_size); - } catch (std::exception& e) { - return HandleException("Failed to discard files", e.what()); - } -} - -Status -MySQLMetaImpl::SetGlobalLastLSN(uint64_t lsn) { - try { - server::MetricCollector metric; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - if (connectionPtr == nullptr) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - bool first_create = false; - uint64_t last_lsn = 0; - { - mysqlpp::StoreQueryResult res; - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT global_lsn FROM " << META_ENVIRONMENT << ";"; - res = statement.store(); - if (res.num_rows() == 0) { - first_create = true; - } else { - last_lsn = res[0]["global_lsn"]; - } - } - - if (first_create) { // first time to get global lsn - mysqlpp::Query statement = connectionPtr->query(); - statement << "INSERT INTO " << META_ENVIRONMENT << " VALUES(" << lsn << ");"; - LOG_ENGINE_DEBUG_ << "SetGlobalLastLSN: " << statement.str(); - - if (!statement.exec()) { - return HandleException("QUERY ERROR WHEN SET GLOBAL LSN", statement.error()); - } - } else if (lsn > last_lsn) { - mysqlpp::Query statement = connectionPtr->query(); - statement << "UPDATE " << META_ENVIRONMENT << " SET global_lsn = " << lsn << ";"; - LOG_ENGINE_DEBUG_ << "SetGlobalLastLSN: " << statement.str(); - - if (!statement.exec()) { - return HandleException("Failed to set global lsn", statement.error()); - } - } - } // Scoped Connection - } catch (std::exception& e) { - return HandleException("Failed to set global lsn", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::GetGlobalLastLSN(uint64_t& lsn) { - try { - server::MetricCollector metric; - - mysqlpp::StoreQueryResult res; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - if (connectionPtr == nullptr) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT global_lsn FROM " << META_ENVIRONMENT << ";"; - - LOG_ENGINE_DEBUG_ << "GetGlobalLastLSN: " << statement.str(); - - res = statement.store(); - } // Scoped Connection - - if (!res.empty()) { - lsn = res[0]["global_lsn"]; - } - } catch (std::exception& e) { - return HandleException("Failed to update global lsn", e.what()); - } - - return Status::OK(); -} - -Status -MySQLMetaImpl::CreateHybridCollection(CollectionSchema& collection_schema, hybrid::FieldsSchema& fields_schema) { - try { - server::MetricCollector metric; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.CreateCollection.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.CreateCollection.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - - if (collection_schema.collection_id_.empty()) { - NextCollectionId(collection_schema.collection_id_); - } else { - statement << "SELECT state FROM " << META_TABLES << " WHERE table_id = " << mysqlpp::quote - << collection_schema.collection_id_ << ";"; - - LOG_ENGINE_DEBUG_ << "CreateCollection: " << statement.str(); - - mysqlpp::StoreQueryResult res = statement.store(); - - if (res.num_rows() == 1) { - int state = res[0]["state"]; - fiu_do_on("MySQLMetaImpl.CreateCollection.schema_TO_DELETE", state = CollectionSchema::TO_DELETE); - if (CollectionSchema::TO_DELETE == state) { - return Status(DB_ERROR, - "Collection already exists and it is in delete state, please wait a second"); - } else { - return Status(DB_ALREADY_EXIST, "Collection already exists"); - } - } - } - - collection_schema.id_ = -1; - collection_schema.created_on_ = utils::GetMicroSecTimeStamp(); - - std::string id = "NULL"; // auto-increment - std::string& collection_id = collection_schema.collection_id_; - std::string state = std::to_string(collection_schema.state_); - std::string dimension = std::to_string(collection_schema.dimension_); - std::string created_on = std::to_string(collection_schema.created_on_); - std::string flag = std::to_string(collection_schema.flag_); - std::string index_file_size = std::to_string(collection_schema.index_file_size_); - std::string engine_type = std::to_string(collection_schema.engine_type_); - std::string& index_params = collection_schema.index_params_; - std::string metric_type = std::to_string(collection_schema.metric_type_); - std::string& owner_collection = collection_schema.owner_collection_; - std::string& partition_tag = collection_schema.partition_tag_; - std::string& version = collection_schema.version_; - std::string flush_lsn = std::to_string(collection_schema.flush_lsn_); - - statement << "INSERT INTO " << META_TABLES << " VALUES(" << id << ", " << mysqlpp::quote << collection_id - << ", " << state << ", " << dimension << ", " << created_on << ", " << flag << ", " - << index_file_size << ", " << engine_type << ", " << mysqlpp::quote << index_params << ", " - << metric_type << ", " << mysqlpp::quote << owner_collection << ", " << mysqlpp::quote - << partition_tag << ", " << mysqlpp::quote << version << ", " << flush_lsn << ");"; - - LOG_ENGINE_DEBUG_ << "CreateHybridCollection: " << statement.str(); - - if (mysqlpp::SimpleResult res = statement.execute()) { - collection_schema.id_ = res.insert_id(); // Might need to use SELECT LAST_INSERT_ID()? - - // Consume all results to avoid "Commands out of sync" error - } else { - return HandleException("Failed to create collection", statement.error()); - } - - for (auto schema : fields_schema.fields_schema_) { - std::string id = "NULL"; - std::string collection_id = schema.collection_id_; - std::string field_name = schema.field_name_; - std::string field_type = std::to_string(schema.field_type_); - std::string field_params = schema.field_params_; - - statement << "INSERT INTO " << META_FIELDS << " VALUES(" << mysqlpp::quote << collection_id << ", " - << mysqlpp::quote << field_name << ", " << field_type << ", " << mysqlpp::quote << ", " - << field_params << ");"; - - LOG_ENGINE_DEBUG_ << "Create field: " << statement.str(); - - if (mysqlpp::SimpleResult field_res = statement.execute()) { - // TODO(yukun): need field id? - - } else { - return HandleException("Failed to create field table", statement.error()); - } - } - } // Scoped Connection - - LOG_ENGINE_DEBUG_ << "Successfully create hybrid collection: " << collection_schema.collection_id_; - std::cout << collection_schema.collection_id_; - return utils::CreateCollectionPath(options_, collection_schema.collection_id_); - } catch (std::exception& e) { - return HandleException("Failed to create collection", e.what()); - } -} - -Status -MySQLMetaImpl::DescribeHybridCollection(CollectionSchema& collection_schema, hybrid::FieldsSchema& fields_schema) { - try { - server::MetricCollector metric; - mysqlpp::StoreQueryResult res, field_res; - { - mysqlpp::ScopedConnection connectionPtr(*mysql_connection_pool_, safe_grab_); - - bool is_null_connection = (connectionPtr == nullptr); - fiu_do_on("MySQLMetaImpl.DescribeCollection.null_connection", is_null_connection = true); - fiu_do_on("MySQLMetaImpl.DescribeCollection.throw_exception", throw std::exception();); - if (is_null_connection) { - return Status(DB_ERROR, "Failed to connect to meta server(mysql)"); - } - - mysqlpp::Query statement = connectionPtr->query(); - statement << "SELECT id, state, dimension, created_on, flag, index_file_size, engine_type, index_params" - << " , metric_type ,owner_table, partition_tag, version, flush_lsn" - << " FROM " << META_TABLES << " WHERE table_id = " << mysqlpp::quote - << collection_schema.collection_id_ << " AND state <> " - << std::to_string(CollectionSchema::TO_DELETE) << ";"; - - LOG_ENGINE_DEBUG_ << "DescribeHybridCollection: " << statement.str(); - - res = statement.store(); - - mysqlpp::Query field_statement = connectionPtr->query(); - field_statement << "SELECT collection_id, field_name, field_type, field_params" - << " FROM " << META_FIELDS << " WHERE collection_id = " << mysqlpp::quote - << collection_schema.collection_id_ << ";"; - - LOG_ENGINE_DEBUG_ << "Describe Collection Fields: " << field_statement.str(); - - field_res = field_statement.store(); - } // Scoped Connection - - if (res.num_rows() == 1) { - const mysqlpp::Row& resRow = res[0]; - collection_schema.id_ = resRow["id"]; // implicit conversion - collection_schema.state_ = resRow["state"]; - collection_schema.dimension_ = resRow["dimension"]; - collection_schema.created_on_ = resRow["created_on"]; - collection_schema.flag_ = resRow["flag"]; - collection_schema.index_file_size_ = resRow["index_file_size"]; - collection_schema.engine_type_ = resRow["engine_type"]; - resRow["index_params"].to_string(collection_schema.index_params_); - collection_schema.metric_type_ = resRow["metric_type"]; - resRow["owner_table"].to_string(collection_schema.owner_collection_); - resRow["partition_tag"].to_string(collection_schema.partition_tag_); - resRow["version"].to_string(collection_schema.version_); - collection_schema.flush_lsn_ = resRow["flush_lsn"]; - } else { - return Status(DB_NOT_FOUND, "Collection " + collection_schema.collection_id_ + " not found"); - } - - auto num_row = field_res.num_rows(); - if (num_row >= 1) { - fields_schema.fields_schema_.resize(num_row); - for (uint64_t i = 0; i < num_row; ++i) { - const mysqlpp::Row& resRow = field_res[i]; - resRow["collection_id"].to_string(fields_schema.fields_schema_[i].collection_id_); - resRow["field_name"].to_string(fields_schema.fields_schema_[i].field_name_); - fields_schema.fields_schema_[i].field_type_ = resRow["field_type"]; - resRow["field_params"].to_string(fields_schema.fields_schema_[i].field_params_); - } - } else { - return Status(DB_NOT_FOUND, "Fields of " + collection_schema.collection_id_ + " not found"); - } - } catch (std::exception& e) { - return HandleException("Failed to describe collection", e.what()); - } - - return Status::OK(); -} - -} // namespace meta -} // namespace engine -} // namespace milvus diff --git a/core/src/db/meta/MySQLMetaImpl.h b/core/src/db/meta/MySQLMetaImpl.h deleted file mode 100644 index 44251de119..0000000000 --- a/core/src/db/meta/MySQLMetaImpl.h +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -#include -#include -#include -#include -#include - -#include "Meta.h" -#include "MySQLConnectionPool.h" -#include "db/Options.h" - -namespace milvus { -namespace engine { -namespace meta { - -class MySQLMetaImpl : public Meta { - public: - MySQLMetaImpl(const DBMetaOptions& options, const int& mode); - ~MySQLMetaImpl(); - - Status - CreateCollection(CollectionSchema& collection_schema) override; - - Status - DescribeCollection(CollectionSchema& collection_schema) override; - - Status - HasCollection(const std::string& collection_id, bool& has_or_not, bool is_root = false) override; - - Status - AllCollections(std::vector& collection_schema_array, bool is_root = false) override; - - Status - DropCollections(const std::vector& collection_id_array) override; - - Status - DeleteCollectionFiles(const std::vector& collection_id_array) override; - - Status - CreateCollectionFile(SegmentSchema& file_schema) override; - - Status - GetCollectionFiles(const std::string& collection_id, const std::vector& ids, - FilesHolder& files_holder) override; - - Status - GetCollectionFilesBySegmentId(const std::string& segment_id, FilesHolder& files_holder) override; - - Status - UpdateCollectionIndex(const std::string& collection_id, const CollectionIndex& index) override; - - Status - UpdateCollectionFlag(const std::string& collection_id, int64_t flag) override; - - Status - UpdateCollectionFlushLSN(const std::string& collection_id, uint64_t flush_lsn) override; - - Status - GetCollectionFlushLSN(const std::string& collection_id, uint64_t& flush_lsn) override; - - Status - UpdateCollectionFile(SegmentSchema& file_schema) override; - - Status - UpdateCollectionFilesToIndex(const std::string& collection_id) override; - - Status - UpdateCollectionFiles(SegmentsSchema& files) override; - - Status - UpdateCollectionFilesRowCount(SegmentsSchema& files) override; - - Status - DescribeCollectionIndex(const std::string& collection_id, CollectionIndex& index) override; - - Status - DropCollectionIndex(const std::string& collection_id) override; - - Status - CreatePartition(const std::string& collection_id, const std::string& partition_name, const std::string& tag, - uint64_t lsn) override; - - Status - HasPartition(const std::string& collection_id, const std::string& tag, bool& has_or_not) override; - - Status - DropPartition(const std::string& partition_name) override; - - Status - ShowPartitions(const std::string& collection_id, - std::vector& partition_schema_array) override; - - Status - GetPartitionName(const std::string& collection_id, const std::string& tag, std::string& partition_name) override; - - Status - FilesToSearch(const std::string& collection_id, FilesHolder& files_holder) override; - - Status - FilesToSearchEx(const std::string& root_collection, const std::set& partition_id_array, - FilesHolder& files_holder) override; - - Status - FilesToMerge(const std::string& collection_id, FilesHolder& files_holder) override; - - Status - FilesToIndex(FilesHolder& files_holder) override; - - Status - FilesByType(const std::string& collection_id, const std::vector& file_types, - FilesHolder& files_holder) override; - - Status - FilesByTypeEx(const std::vector& collections, const std::vector& file_types, - FilesHolder& files_holder) override; - - Status - FilesByID(const std::vector& ids, FilesHolder& files_holder) override; - - Status - Archive() override; - - Status - Size(uint64_t& result) override; - - Status - CleanUpShadowFiles() override; - - Status - CleanUpFilesWithTTL(uint64_t seconds /*, CleanUpFilter* filter = nullptr*/) override; - - Status - DropAll() override; - - Status - Count(const std::string& collection_id, uint64_t& result) override; - - Status - SetGlobalLastLSN(uint64_t lsn) override; - - Status - GetGlobalLastLSN(uint64_t& lsn) override; - - Status - CreateHybridCollection(CollectionSchema& collection_schema, hybrid::FieldsSchema& fields_schema) override; - - Status - DescribeHybridCollection(CollectionSchema& collection_schema, hybrid::FieldsSchema& fields_schema) override; - - private: - Status - NextFileId(std::string& file_id); - Status - NextCollectionId(std::string& collection_id); - Status - DiscardFiles(int64_t to_discard_size); - - void - ValidateMetaSchema(); - Status - Initialize(); - - private: - const DBMetaOptions options_; - const int mode_; - - std::shared_ptr mysql_connection_pool_; - bool safe_grab_ = false; // Safely graps a connection from mysql pool - - std::mutex meta_mutex_; - std::mutex genid_mutex_; - // std::mutex connectionMutex_; -}; // DBMetaImpl - -} // namespace meta -} // namespace engine -} // namespace milvus diff --git a/core/src/db/meta/SqliteMetaImpl.cpp b/core/src/db/meta/SqliteMetaImpl.cpp deleted file mode 100644 index d53162a901..0000000000 --- a/core/src/db/meta/SqliteMetaImpl.cpp +++ /dev/null @@ -1,2265 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/meta/SqliteMetaImpl.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "MetaConsts.h" -#include "db/IDGenerator.h" -#include "db/Utils.h" -#include "metrics/Metrics.h" -#include "utils/CommonUtil.h" -#include "utils/Exception.h" -#include "utils/Log.h" -#include "utils/StringHelpFunctions.h" -#include "utils/ValidationUtil.h" - -#define USING_SQLITE_WARNING \ - LOG_ENGINE_WARNING_ << "You are using SQLite as the meta data management, which can't be used in production. " \ - "Please change it to MySQL!"; - -namespace milvus { -namespace engine { -namespace meta { - -using namespace sqlite_orm; - -namespace { - -constexpr uint64_t SQL_BATCH_SIZE = 50; - -template -void -DistributeBatch(const T& id_array, std::vector>& id_groups) { - std::vector temp_group; - constexpr uint64_t SQL_BATCH_SIZE = 50; - for (auto& id : id_array) { - temp_group.push_back(id); - if (temp_group.size() >= SQL_BATCH_SIZE) { - id_groups.emplace_back(temp_group); - temp_group.clear(); - } - } - - if (!temp_group.empty()) { - id_groups.emplace_back(temp_group); - } -} - -Status -HandleException(const std::string& desc, const char* what = nullptr) { - if (what == nullptr) { - LOG_ENGINE_ERROR_ << desc; - return Status(DB_META_TRANSACTION_FAILED, desc); - } else { - std::string msg = desc + ":" + what; - LOG_ENGINE_ERROR_ << msg; - return Status(DB_META_TRANSACTION_FAILED, msg); - } -} - -} // namespace - -inline auto -StoragePrototype(const std::string& path) { - return make_storage( - path, - make_table(META_ENVIRONMENT, make_column("global_lsn", &EnvironmentSchema::global_lsn_, default_value(0))), - make_table(META_TABLES, make_column("id", &CollectionSchema::id_, primary_key()), - make_column("table_id", &CollectionSchema::collection_id_, unique()), - make_column("state", &CollectionSchema::state_), - make_column("dimension", &CollectionSchema::dimension_), - make_column("created_on", &CollectionSchema::created_on_), - make_column("flag", &CollectionSchema::flag_, default_value(0)), - make_column("index_file_size", &CollectionSchema::index_file_size_), - make_column("engine_type", &CollectionSchema::engine_type_), - make_column("index_params", &CollectionSchema::index_params_), - make_column("metric_type", &CollectionSchema::metric_type_), - make_column("owner_table", &CollectionSchema::owner_collection_, default_value("")), - make_column("partition_tag", &CollectionSchema::partition_tag_, default_value("")), - make_column("version", &CollectionSchema::version_, default_value(CURRENT_VERSION)), - make_column("flush_lsn", &CollectionSchema::flush_lsn_)), - make_table(META_FIELDS, make_column("collection_id", &hybrid::FieldSchema::collection_id_), - make_column("field_name", &hybrid::FieldSchema::field_name_), - make_column("field_type", &hybrid::FieldSchema::field_type_), - make_column("field_params", &hybrid::FieldSchema::field_params_)), - make_table(META_TABLEFILES, make_column("id", &SegmentSchema::id_, primary_key()), - make_column("table_id", &SegmentSchema::collection_id_), - make_column("segment_id", &SegmentSchema::segment_id_, default_value("")), - make_column("engine_type", &SegmentSchema::engine_type_), - make_column("file_id", &SegmentSchema::file_id_), - make_column("file_type", &SegmentSchema::file_type_), - make_column("file_size", &SegmentSchema::file_size_, default_value(0)), - make_column("row_count", &SegmentSchema::row_count_, default_value(0)), - make_column("updated_time", &SegmentSchema::updated_time_), - make_column("created_on", &SegmentSchema::created_on_), make_column("date", &SegmentSchema::date_), - make_column("flush_lsn", &SegmentSchema::flush_lsn_))); -} - -using ConnectorT = decltype(StoragePrototype("table")); -static std::unique_ptr ConnectorPtr; - -SqliteMetaImpl::SqliteMetaImpl(const DBMetaOptions& options) : options_(options) { - Initialize(); -} - -SqliteMetaImpl::~SqliteMetaImpl() { -} - -Status -SqliteMetaImpl::NextCollectionId(std::string& collection_id) { - std::lock_guard lock(genid_mutex_); // avoid duplicated id - std::stringstream ss; - SafeIDGenerator& id_generator = SafeIDGenerator::GetInstance(); - ss << id_generator.GetNextIDNumber(); - collection_id = ss.str(); - return Status::OK(); -} - -Status -SqliteMetaImpl::NextFileId(std::string& file_id) { - std::lock_guard lock(genid_mutex_); // avoid duplicated id - std::stringstream ss; - SafeIDGenerator& id_generator = SafeIDGenerator::GetInstance(); - ss << id_generator.GetNextIDNumber(); - file_id = ss.str(); - return Status::OK(); -} - -void -SqliteMetaImpl::ValidateMetaSchema() { - bool is_null_connector{ConnectorPtr == nullptr}; - fiu_do_on("SqliteMetaImpl.ValidateMetaSchema.NullConnection", is_null_connector = true); - if (is_null_connector) { - throw Exception(DB_ERROR, "Connector is null pointer"); - } - - // old meta could be recreated since schema changed, throw exception if meta schema is not compatible - auto ret = ConnectorPtr->sync_schema_simulate(); - if (ret.find(META_TABLES) != ret.end() && - sqlite_orm::sync_schema_result::dropped_and_recreated == ret[META_TABLES]) { - throw Exception(DB_INCOMPATIB_META, "Meta Tables schema is created by Milvus old version"); - } - if (ret.find(META_FIELDS) != ret.end() && - sqlite_orm::sync_schema_result::dropped_and_recreated == ret[META_FIELDS]) { - throw Exception(DB_INCOMPATIB_META, "Meta Tables schema is created by Milvus old version"); - } - if (ret.find(META_TABLEFILES) != ret.end() && - sqlite_orm::sync_schema_result::dropped_and_recreated == ret[META_TABLEFILES]) { - throw Exception(DB_INCOMPATIB_META, "Meta TableFiles schema is created by Milvus old version"); - } -} - -Status -SqliteMetaImpl::Initialize() { - if (!boost::filesystem::is_directory(options_.path_)) { - auto ret = boost::filesystem::create_directory(options_.path_); - fiu_do_on("SqliteMetaImpl.Initialize.fail_create_directory", ret = false); - if (!ret) { - std::string msg = "Failed to create db directory " + options_.path_; - LOG_ENGINE_ERROR_ << msg; - throw Exception(DB_INVALID_PATH, msg); - } - } - - ConnectorPtr = std::make_unique(StoragePrototype(options_.path_ + "/meta.sqlite")); - - ValidateMetaSchema(); - - ConnectorPtr->sync_schema(); - ConnectorPtr->open_forever(); // thread safe option - ConnectorPtr->pragma.journal_mode(journal_mode::WAL); // WAL => write ahead log - - CleanUpShadowFiles(); - - return Status::OK(); -} - -Status -SqliteMetaImpl::CreateCollection(CollectionSchema& collection_schema) { - USING_SQLITE_WARNING - try { - server::MetricCollector metric; - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - if (collection_schema.collection_id_ == "") { - NextCollectionId(collection_schema.collection_id_); - } else { - fiu_do_on("SqliteMetaImpl.CreateCollection.throw_exception", throw std::exception()); - auto collection = - ConnectorPtr->select(columns(&CollectionSchema::state_), - where(c(&CollectionSchema::collection_id_) == collection_schema.collection_id_)); - if (collection.size() == 1) { - if (CollectionSchema::TO_DELETE == std::get<0>(collection[0])) { - return Status(DB_ERROR, - "Collection already exists and it is in delete state, please wait a second"); - } else { - // Change from no error to already exist. - return Status(DB_ALREADY_EXIST, "Collection already exists"); - } - } - } - - collection_schema.id_ = -1; - collection_schema.created_on_ = utils::GetMicroSecTimeStamp(); - - try { - fiu_do_on("SqliteMetaImpl.CreateCollection.insert_throw_exception", throw std::exception()); - auto id = ConnectorPtr->insert(collection_schema); - collection_schema.id_ = id; - } catch (std::exception& e) { - return HandleException("Encounter exception when create collection", e.what()); - } - - LOG_ENGINE_DEBUG_ << "Successfully create collection: " << collection_schema.collection_id_; - - return utils::CreateCollectionPath(options_, collection_schema.collection_id_); - } catch (std::exception& e) { - return HandleException("Encounter exception when create collection", e.what()); - } -} - -Status -SqliteMetaImpl::DescribeCollection(CollectionSchema& collection_schema) { - try { - server::MetricCollector metric; - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - fiu_do_on("SqliteMetaImpl.DescribeCollection.throw_exception", throw std::exception()); - auto groups = ConnectorPtr->select( - columns(&CollectionSchema::id_, &CollectionSchema::state_, &CollectionSchema::dimension_, - &CollectionSchema::created_on_, &CollectionSchema::flag_, &CollectionSchema::index_file_size_, - &CollectionSchema::engine_type_, &CollectionSchema::index_params_, &CollectionSchema::metric_type_, - &CollectionSchema::owner_collection_, &CollectionSchema::partition_tag_, - &CollectionSchema::version_, &CollectionSchema::flush_lsn_), - where(c(&CollectionSchema::collection_id_) == collection_schema.collection_id_ and - c(&CollectionSchema::state_) != (int)CollectionSchema::TO_DELETE)); - - if (groups.size() == 1) { - collection_schema.id_ = std::get<0>(groups[0]); - collection_schema.state_ = std::get<1>(groups[0]); - collection_schema.dimension_ = std::get<2>(groups[0]); - collection_schema.created_on_ = std::get<3>(groups[0]); - collection_schema.flag_ = std::get<4>(groups[0]); - collection_schema.index_file_size_ = std::get<5>(groups[0]); - collection_schema.engine_type_ = std::get<6>(groups[0]); - collection_schema.index_params_ = std::get<7>(groups[0]); - collection_schema.metric_type_ = std::get<8>(groups[0]); - collection_schema.owner_collection_ = std::get<9>(groups[0]); - collection_schema.partition_tag_ = std::get<10>(groups[0]); - collection_schema.version_ = std::get<11>(groups[0]); - collection_schema.flush_lsn_ = std::get<12>(groups[0]); - } else { - return Status(DB_NOT_FOUND, "Collection " + collection_schema.collection_id_ + " not found"); - } - } catch (std::exception& e) { - return HandleException("Encounter exception when describe collection", e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::HasCollection(const std::string& collection_id, bool& has_or_not, bool is_root) { - has_or_not = false; - - try { - fiu_do_on("SqliteMetaImpl.HasCollection.throw_exception", throw std::exception()); - server::MetricCollector metric; - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - auto select_columns = columns(&CollectionSchema::id_, &CollectionSchema::owner_collection_); - decltype(ConnectorPtr->select(select_columns)) selected; - if (is_root) { - selected = ConnectorPtr->select(select_columns, - where(c(&CollectionSchema::collection_id_) == collection_id and - c(&CollectionSchema::state_) != (int)CollectionSchema::TO_DELETE and - c(&CollectionSchema::owner_collection_) == "")); - } else { - selected = ConnectorPtr->select(select_columns, - where(c(&CollectionSchema::collection_id_) == collection_id and - c(&CollectionSchema::state_) != (int)CollectionSchema::TO_DELETE)); - } - - if (selected.size() == 1) { - has_or_not = true; - } else { - has_or_not = false; - } - } catch (std::exception& e) { - return HandleException("Encounter exception when lookup collection", e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::AllCollections(std::vector& collection_schema_array, bool is_root) { - try { - fiu_do_on("SqliteMetaImpl.AllCollections.throw_exception", throw std::exception()); - server::MetricCollector metric; - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - auto select_columns = - columns(&CollectionSchema::id_, &CollectionSchema::collection_id_, &CollectionSchema::dimension_, - &CollectionSchema::created_on_, &CollectionSchema::flag_, &CollectionSchema::index_file_size_, - &CollectionSchema::engine_type_, &CollectionSchema::index_params_, &CollectionSchema::metric_type_, - &CollectionSchema::owner_collection_, &CollectionSchema::partition_tag_, - &CollectionSchema::version_, &CollectionSchema::flush_lsn_); - decltype(ConnectorPtr->select(select_columns)) selected; - - if (is_root) { - selected = ConnectorPtr->select(select_columns, - where(c(&CollectionSchema::state_) != (int)CollectionSchema::TO_DELETE and - c(&CollectionSchema::owner_collection_) == "")); - } else { - selected = ConnectorPtr->select(select_columns, - where(c(&CollectionSchema::state_) != (int)CollectionSchema::TO_DELETE)); - } - - for (auto& collection : selected) { - CollectionSchema schema; - schema.id_ = std::get<0>(collection); - schema.collection_id_ = std::get<1>(collection); - schema.dimension_ = std::get<2>(collection); - schema.created_on_ = std::get<3>(collection); - schema.flag_ = std::get<4>(collection); - schema.index_file_size_ = std::get<5>(collection); - schema.engine_type_ = std::get<6>(collection); - schema.index_params_ = std::get<7>(collection); - schema.metric_type_ = std::get<8>(collection); - schema.owner_collection_ = std::get<9>(collection); - schema.partition_tag_ = std::get<10>(collection); - schema.version_ = std::get<11>(collection); - schema.flush_lsn_ = std::get<12>(collection); - - collection_schema_array.emplace_back(schema); - } - } catch (std::exception& e) { - return HandleException("Encounter exception when lookup all collections", e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::DropCollections(const std::vector& collection_id_array) { - try { - fiu_do_on("SqliteMetaImpl.DropCollection.throw_exception", throw std::exception()); - - // distribute id array to batches - std::vector> id_groups; - DistributeBatch(collection_id_array, id_groups); - - server::MetricCollector metric; - - { - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - for (auto group : id_groups) { - // soft delete collection - ConnectorPtr->update_all(set(c(&CollectionSchema::state_) = (int)CollectionSchema::TO_DELETE), - where(in(&CollectionSchema::collection_id_, group) and - c(&CollectionSchema::state_) != (int)CollectionSchema::TO_DELETE)); - } - } - - auto status = DeleteCollectionFiles(collection_id_array); - LOG_ENGINE_DEBUG_ << "Successfully delete collections"; - return status; - } catch (std::exception& e) { - return HandleException("Encounter exception when delete collection", e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::DeleteCollectionFiles(const std::vector& collection_id_array) { - try { - fiu_do_on("SqliteMetaImpl.DeleteCollectionFiles.throw_exception", throw std::exception()); - - // distribute id array to batches - std::vector> id_groups; - DistributeBatch(collection_id_array, id_groups); - - server::MetricCollector metric; - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - for (auto group : id_groups) { - // soft delete collection files - ConnectorPtr->update_all(set(c(&SegmentSchema::file_type_) = (int)SegmentSchema::TO_DELETE, - c(&SegmentSchema::updated_time_) = utils::GetMicroSecTimeStamp()), - where(in(&SegmentSchema::collection_id_, group) and - c(&SegmentSchema::file_type_) != (int)SegmentSchema::TO_DELETE)); - } - - LOG_ENGINE_DEBUG_ << "Successfully delete collection files"; - } catch (std::exception& e) { - return HandleException("Encounter exception when delete collection files", e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::CreateCollectionFile(SegmentSchema& file_schema) { - USING_SQLITE_WARNING - if (file_schema.date_ == EmptyDate) { - file_schema.date_ = utils::GetDate(); - } - CollectionSchema collection_schema; - collection_schema.collection_id_ = file_schema.collection_id_; - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - return status; - } - - try { - fiu_do_on("SqliteMetaImpl.CreateCollectionFile.throw_exception", throw std::exception()); - server::MetricCollector metric; - - NextFileId(file_schema.file_id_); - if (file_schema.segment_id_.empty()) { - file_schema.segment_id_ = file_schema.file_id_; - } - file_schema.dimension_ = collection_schema.dimension_; - file_schema.file_size_ = 0; - file_schema.row_count_ = 0; - file_schema.created_on_ = utils::GetMicroSecTimeStamp(); - file_schema.updated_time_ = file_schema.created_on_; - file_schema.index_file_size_ = collection_schema.index_file_size_; - file_schema.index_params_ = collection_schema.index_params_; - file_schema.engine_type_ = collection_schema.engine_type_; - file_schema.metric_type_ = collection_schema.metric_type_; - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - auto id = ConnectorPtr->insert(file_schema); - file_schema.id_ = id; - - LOG_ENGINE_DEBUG_ << "Successfully create collection file, file id = " << file_schema.file_id_; - return utils::CreateCollectionFilePath(options_, file_schema); - } catch (std::exception& e) { - return HandleException("Encounter exception when create collection file", e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::GetCollectionFiles(const std::string& collection_id, const std::vector& ids, - FilesHolder& files_holder) { - try { - fiu_do_on("SqliteMetaImpl.GetCollectionFiles.throw_exception", throw std::exception()); - - CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_id; - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - return status; - } - - auto select_columns = - columns(&SegmentSchema::id_, &SegmentSchema::segment_id_, &SegmentSchema::file_id_, - &SegmentSchema::file_type_, &SegmentSchema::file_size_, &SegmentSchema::row_count_, - &SegmentSchema::date_, &SegmentSchema::engine_type_, &SegmentSchema::created_on_); - decltype(ConnectorPtr->select(select_columns)) selected; - { - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - selected = ConnectorPtr->select( - select_columns, - where(c(&SegmentSchema::collection_id_) == collection_id and in(&SegmentSchema::id_, ids) and - c(&SegmentSchema::file_type_) != (int)SegmentSchema::TO_DELETE)); - } - - Status result; - for (auto& file : selected) { - SegmentSchema file_schema; - file_schema.collection_id_ = collection_id; - file_schema.id_ = std::get<0>(file); - file_schema.segment_id_ = std::get<1>(file); - file_schema.file_id_ = std::get<2>(file); - file_schema.file_type_ = std::get<3>(file); - file_schema.file_size_ = std::get<4>(file); - file_schema.row_count_ = std::get<5>(file); - file_schema.date_ = std::get<6>(file); - file_schema.engine_type_ = std::get<7>(file); - file_schema.created_on_ = std::get<8>(file); - file_schema.dimension_ = collection_schema.dimension_; - file_schema.index_file_size_ = collection_schema.index_file_size_; - file_schema.index_params_ = collection_schema.index_params_; - file_schema.metric_type_ = collection_schema.metric_type_; - - utils::GetCollectionFilePath(options_, file_schema); - - files_holder.MarkFile(file_schema); - } - - LOG_ENGINE_DEBUG_ << "Get " << selected.size() << " files by id from collection " << collection_id; - return result; - } catch (std::exception& e) { - return HandleException("Encounter exception when lookup collection files", e.what()); - } -} - -Status -SqliteMetaImpl::GetCollectionFilesBySegmentId(const std::string& segment_id, FilesHolder& files_holder) { - try { - auto select_columns = columns(&SegmentSchema::id_, &SegmentSchema::collection_id_, &SegmentSchema::segment_id_, - &SegmentSchema::file_id_, &SegmentSchema::file_type_, &SegmentSchema::file_size_, - &SegmentSchema::row_count_, &SegmentSchema::date_, &SegmentSchema::engine_type_, - &SegmentSchema::created_on_); - decltype(ConnectorPtr->select(select_columns)) selected; - { - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - selected = ConnectorPtr->select(select_columns, - where(c(&SegmentSchema::segment_id_) == segment_id and - c(&SegmentSchema::file_type_) != (int)SegmentSchema::TO_DELETE)); - } - - if (!selected.empty()) { - CollectionSchema collection_schema; - collection_schema.collection_id_ = std::get<1>(selected[0]); - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - return status; - } - - for (auto& file : selected) { - SegmentSchema file_schema; - file_schema.collection_id_ = collection_schema.collection_id_; - file_schema.id_ = std::get<0>(file); - file_schema.segment_id_ = std::get<2>(file); - file_schema.file_id_ = std::get<3>(file); - file_schema.file_type_ = std::get<4>(file); - file_schema.file_size_ = std::get<5>(file); - file_schema.row_count_ = std::get<6>(file); - file_schema.date_ = std::get<7>(file); - file_schema.engine_type_ = std::get<8>(file); - file_schema.created_on_ = std::get<9>(file); - file_schema.dimension_ = collection_schema.dimension_; - file_schema.index_file_size_ = collection_schema.index_file_size_; - file_schema.index_params_ = collection_schema.index_params_; - file_schema.metric_type_ = collection_schema.metric_type_; - - utils::GetCollectionFilePath(options_, file_schema); - files_holder.MarkFile(file_schema); - } - } - - LOG_ENGINE_DEBUG_ << "Get " << selected.size() << " files by segment id" << segment_id; - return Status::OK(); - } catch (std::exception& e) { - return HandleException("Encounter exception when lookup collection files by segment id", e.what()); - } -} - -Status -SqliteMetaImpl::UpdateCollectionFlag(const std::string& collection_id, int64_t flag) { - try { - server::MetricCollector metric; - fiu_do_on("SqliteMetaImpl.UpdateCollectionFlag.throw_exception", throw std::exception()); - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - // set all backup file to raw - ConnectorPtr->update_all(set(c(&CollectionSchema::flag_) = flag), - where(c(&CollectionSchema::collection_id_) == collection_id)); - LOG_ENGINE_DEBUG_ << "Successfully update collection flag, collection id = " << collection_id; - } catch (std::exception& e) { - std::string msg = "Encounter exception when update collection flag: collection_id = " + collection_id; - return HandleException(msg, e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::UpdateCollectionFlushLSN(const std::string& collection_id, uint64_t flush_lsn) { - try { - server::MetricCollector metric; - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - ConnectorPtr->update_all(set(c(&CollectionSchema::flush_lsn_) = flush_lsn), - where(c(&CollectionSchema::collection_id_) == collection_id)); - LOG_ENGINE_DEBUG_ << "Successfully update collection flush_lsn, collection id = " << collection_id - << " flush_lsn = " << flush_lsn; - } catch (std::exception& e) { - std::string msg = "Encounter exception when update collection lsn: collection_id = " + collection_id; - return HandleException(msg, e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::GetCollectionFlushLSN(const std::string& collection_id, uint64_t& flush_lsn) { - try { - server::MetricCollector metric; - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - auto selected = ConnectorPtr->select(columns(&CollectionSchema::flush_lsn_), - where(c(&CollectionSchema::collection_id_) == collection_id)); - - if (selected.size() > 0) { - flush_lsn = std::get<0>(selected[0]); - } else { - return Status(DB_NOT_FOUND, "Collection " + collection_id + " not found"); - } - - } catch (std::exception& e) { - return HandleException("Encounter exception when getting collection files by flush_lsn", e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::UpdateCollectionFile(SegmentSchema& file_schema) { - file_schema.updated_time_ = utils::GetMicroSecTimeStamp(); - try { - server::MetricCollector metric; - fiu_do_on("SqliteMetaImpl.UpdateCollectionFile.throw_exception", throw std::exception()); - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - auto collections = - ConnectorPtr->select(columns(&CollectionSchema::state_), - where(c(&CollectionSchema::collection_id_) == file_schema.collection_id_)); - - // if the collection has been deleted, just mark the collection file as TO_DELETE - // clean thread will delete the file later - if (collections.size() < 1 || std::get<0>(collections[0]) == (int)CollectionSchema::TO_DELETE) { - file_schema.file_type_ = SegmentSchema::TO_DELETE; - } - - ConnectorPtr->update(file_schema); - - LOG_ENGINE_DEBUG_ << "Update single collection file, file id = " << file_schema.file_id_; - } catch (std::exception& e) { - std::string msg = "Exception update collection file: collection_id = " + file_schema.collection_id_ + - " file_id = " + file_schema.file_id_; - return HandleException(msg, e.what()); - } - return Status::OK(); -} - -Status -SqliteMetaImpl::UpdateCollectionFiles(SegmentsSchema& files) { - try { - server::MetricCollector metric; - fiu_do_on("SqliteMetaImpl.UpdateCollectionFiles.throw_exception", throw std::exception()); - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - std::map has_collections; - for (auto& file : files) { - if (has_collections.find(file.collection_id_) != has_collections.end()) { - continue; - } - auto collections = - ConnectorPtr->select(columns(&CollectionSchema::id_), - where(c(&CollectionSchema::collection_id_) == file.collection_id_ and - c(&CollectionSchema::state_) != (int)CollectionSchema::TO_DELETE)); - if (collections.size() >= 1) { - has_collections[file.collection_id_] = true; - } else { - has_collections[file.collection_id_] = false; - } - } - - auto commited = ConnectorPtr->transaction([&]() mutable { - for (auto& file : files) { - if (!has_collections[file.collection_id_]) { - file.file_type_ = SegmentSchema::TO_DELETE; - } - - file.updated_time_ = utils::GetMicroSecTimeStamp(); - ConnectorPtr->update(file); - } - return true; - }); - fiu_do_on("SqliteMetaImpl.UpdateCollectionFiles.fail_commited", commited = false); - - if (!commited) { - return HandleException("UpdateCollectionFiles error: sqlite transaction failed"); - } - - LOG_ENGINE_DEBUG_ << "Update " << files.size() << " collection files"; - } catch (std::exception& e) { - return HandleException("Encounter exception when update collection files", e.what()); - } - return Status::OK(); -} - -Status -SqliteMetaImpl::UpdateCollectionFilesRowCount(SegmentsSchema& files) { - try { - server::MetricCollector metric; - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - for (auto& file : files) { - ConnectorPtr->update_all(set(c(&SegmentSchema::row_count_) = file.row_count_, - c(&SegmentSchema::updated_time_) = utils::GetMicroSecTimeStamp()), - where(c(&SegmentSchema::file_id_) == file.file_id_)); - LOG_ENGINE_DEBUG_ << "Update file " << file.file_id_ << " row count to " << file.row_count_; - } - } catch (std::exception& e) { - return HandleException("Encounter exception when update collection files row count", e.what()); - } - return Status::OK(); -} - -Status -SqliteMetaImpl::UpdateCollectionIndex(const std::string& collection_id, const CollectionIndex& index) { - try { - server::MetricCollector metric; - fiu_do_on("SqliteMetaImpl.UpdateCollectionIndex.throw_exception", throw std::exception()); - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - auto collections = ConnectorPtr->select( - - columns(&CollectionSchema::id_, &CollectionSchema::state_, &CollectionSchema::dimension_, - &CollectionSchema::created_on_, &CollectionSchema::flag_, &CollectionSchema::index_file_size_, - &CollectionSchema::owner_collection_, &CollectionSchema::partition_tag_, - &CollectionSchema::version_, &CollectionSchema::flush_lsn_), - where(c(&CollectionSchema::collection_id_) == collection_id and - c(&CollectionSchema::state_) != (int)CollectionSchema::TO_DELETE)); - - if (collections.size() > 0) { - meta::CollectionSchema collection_schema; - collection_schema.id_ = std::get<0>(collections[0]); - collection_schema.collection_id_ = collection_id; - collection_schema.state_ = std::get<1>(collections[0]); - collection_schema.dimension_ = std::get<2>(collections[0]); - collection_schema.created_on_ = std::get<3>(collections[0]); - collection_schema.flag_ = std::get<4>(collections[0]); - collection_schema.index_file_size_ = std::get<5>(collections[0]); - collection_schema.owner_collection_ = std::get<6>(collections[0]); - collection_schema.partition_tag_ = std::get<7>(collections[0]); - collection_schema.version_ = std::get<8>(collections[0]); - collection_schema.flush_lsn_ = std::get<9>(collections[0]); - collection_schema.engine_type_ = index.engine_type_; - collection_schema.index_params_ = index.extra_params_.dump(); - collection_schema.metric_type_ = index.metric_type_; - - ConnectorPtr->update(collection_schema); - } else { - return Status(DB_NOT_FOUND, "Collection " + collection_id + " not found"); - } - - // set all backup file to raw - ConnectorPtr->update_all(set(c(&SegmentSchema::file_type_) = (int)SegmentSchema::RAW, - c(&SegmentSchema::updated_time_) = utils::GetMicroSecTimeStamp()), - where(c(&SegmentSchema::collection_id_) == collection_id and - c(&SegmentSchema::file_type_) == (int)SegmentSchema::BACKUP)); - - LOG_ENGINE_DEBUG_ << "Successfully update collection index, collection id = " << collection_id; - } catch (std::exception& e) { - std::string msg = "Encounter exception when update collection index: collection_id = " + collection_id; - return HandleException(msg, e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::UpdateCollectionFilesToIndex(const std::string& collection_id) { - try { - server::MetricCollector metric; - fiu_do_on("SqliteMetaImpl.UpdateCollectionFilesToIndex.throw_exception", throw std::exception()); - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - ConnectorPtr->update_all(set(c(&SegmentSchema::file_type_) = (int)SegmentSchema::TO_INDEX), - where(c(&SegmentSchema::collection_id_) == collection_id and - c(&SegmentSchema::row_count_) >= meta::BUILD_INDEX_THRESHOLD and - c(&SegmentSchema::file_type_) == (int)SegmentSchema::RAW)); - - LOG_ENGINE_DEBUG_ << "Update files to to_index, collection id = " << collection_id; - } catch (std::exception& e) { - return HandleException("Encounter exception when update collection files to to_index", e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::DescribeCollectionIndex(const std::string& collection_id, CollectionIndex& index) { - try { - server::MetricCollector metric; - fiu_do_on("SqliteMetaImpl.DescribeCollectionIndex.throw_exception", throw std::exception()); - - auto groups = ConnectorPtr->select( - columns(&CollectionSchema::engine_type_, &CollectionSchema::index_params_, &CollectionSchema::metric_type_), - where(c(&CollectionSchema::collection_id_) == collection_id and - c(&CollectionSchema::state_) != (int)CollectionSchema::TO_DELETE)); - - if (groups.size() == 1) { - index.engine_type_ = std::get<0>(groups[0]); - index.extra_params_ = milvus::json::parse(std::get<1>(groups[0])); - index.metric_type_ = std::get<2>(groups[0]); - } else { - return Status(DB_NOT_FOUND, "Collection " + collection_id + " not found"); - } - } catch (std::exception& e) { - return HandleException("Encounter exception when describe index", e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::DropCollectionIndex(const std::string& collection_id) { - try { - server::MetricCollector metric; - fiu_do_on("SqliteMetaImpl.DropCollectionIndex.throw_exception", throw std::exception()); - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - // soft delete index files - ConnectorPtr->update_all(set(c(&SegmentSchema::file_type_) = (int)SegmentSchema::TO_DELETE, - c(&SegmentSchema::updated_time_) = utils::GetMicroSecTimeStamp()), - where(c(&SegmentSchema::collection_id_) == collection_id and - c(&SegmentSchema::file_type_) == (int)SegmentSchema::INDEX)); - - // set all backup file to raw - ConnectorPtr->update_all(set(c(&SegmentSchema::file_type_) = (int)SegmentSchema::RAW, - c(&SegmentSchema::updated_time_) = utils::GetMicroSecTimeStamp()), - where(c(&SegmentSchema::collection_id_) == collection_id and - c(&SegmentSchema::file_type_) == (int)SegmentSchema::BACKUP)); - - // set collection index type to raw - auto groups = ConnectorPtr->select(columns(&CollectionSchema::metric_type_), - where(c(&CollectionSchema::collection_id_) == collection_id)); - - int32_t raw_engine_type = DEFAULT_ENGINE_TYPE; - if (groups.size() == 1) { - int32_t metric_type_ = std::get<0>(groups[0]); - if (engine::utils::IsBinaryMetricType(metric_type_)) { - raw_engine_type = (int32_t)EngineType::FAISS_BIN_IDMAP; - } - } - ConnectorPtr->update_all( - set(c(&CollectionSchema::engine_type_) = raw_engine_type, c(&CollectionSchema::index_params_) = "{}"), - where(c(&CollectionSchema::collection_id_) == collection_id)); - - LOG_ENGINE_DEBUG_ << "Successfully drop collection index, collection id = " << collection_id; - } catch (std::exception& e) { - return HandleException("Encounter exception when delete collection index files", e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::CreatePartition(const std::string& collection_id, const std::string& partition_name, - const std::string& tag, uint64_t lsn) { - USING_SQLITE_WARNING - server::MetricCollector metric; - - CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_id; - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - return status; - } - - // not allow create partition under partition - if (!collection_schema.owner_collection_.empty()) { - return Status(DB_ERROR, "Nested partition is not allowed"); - } - - // trim side-blank of tag, only compare valid characters - // for example: " ab cd " is treated as "ab cd" - std::string valid_tag = tag; - server::StringHelpFunctions::TrimStringBlank(valid_tag); - - // not allow duplicated partition - std::string exist_partition; - GetPartitionName(collection_id, valid_tag, exist_partition); - if (!exist_partition.empty()) { - return Status(DB_ERROR, "Duplicate partition is not allowed"); - } - - if (partition_name == "") { - // generate unique partition name - NextCollectionId(collection_schema.collection_id_); - } else { - collection_schema.collection_id_ = partition_name; - } - - collection_schema.id_ = -1; - collection_schema.flag_ = 0; - collection_schema.created_on_ = utils::GetMicroSecTimeStamp(); - collection_schema.owner_collection_ = collection_id; - collection_schema.partition_tag_ = valid_tag; - collection_schema.flush_lsn_ = lsn; - - status = CreateCollection(collection_schema); - if (status.code() == DB_ALREADY_EXIST) { - return Status(DB_ALREADY_EXIST, "Partition already exists"); - } - - return status; -} - -Status -SqliteMetaImpl::HasPartition(const std::string& collection_id, const std::string& tag, bool& has_or_not) { - try { - server::MetricCollector metric; - - // trim side-blank of tag, only compare valid characters - // for example: " ab cd " is treated as "ab cd" - std::string valid_tag = tag; - server::StringHelpFunctions::TrimStringBlank(valid_tag); - - auto name = ConnectorPtr->select(columns(&CollectionSchema::collection_id_), - where(c(&CollectionSchema::owner_collection_) == collection_id and - c(&CollectionSchema::partition_tag_) == valid_tag and - c(&CollectionSchema::state_) != (int)CollectionSchema::TO_DELETE)); - if (name.size() > 0) { - has_or_not = true; - } else { - has_or_not = false; - } - } catch (std::exception& e) { - return HandleException("Encounter exception when lookup partition", e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::DropPartition(const std::string& partition_name) { - return DropCollections({partition_name}); -} - -Status -SqliteMetaImpl::ShowPartitions(const std::string& collection_id, - std::vector& partition_schema_array) { - try { - server::MetricCollector metric; - fiu_do_on("SqliteMetaImpl.ShowPartitions.throw_exception", throw std::exception()); - - auto partitions = ConnectorPtr->select( - columns(&CollectionSchema::id_, &CollectionSchema::state_, &CollectionSchema::dimension_, - &CollectionSchema::created_on_, &CollectionSchema::flag_, &CollectionSchema::index_file_size_, - &CollectionSchema::engine_type_, &CollectionSchema::index_params_, &CollectionSchema::metric_type_, - &CollectionSchema::partition_tag_, &CollectionSchema::version_, &CollectionSchema::collection_id_, - &CollectionSchema::flush_lsn_), - where(c(&CollectionSchema::owner_collection_) == collection_id and - c(&CollectionSchema::state_) != (int)CollectionSchema::TO_DELETE)); - - for (size_t i = 0; i < partitions.size(); i++) { - meta::CollectionSchema partition_schema; - partition_schema.id_ = std::get<0>(partitions[i]); - partition_schema.state_ = std::get<1>(partitions[i]); - partition_schema.dimension_ = std::get<2>(partitions[i]); - partition_schema.created_on_ = std::get<3>(partitions[i]); - partition_schema.flag_ = std::get<4>(partitions[i]); - partition_schema.index_file_size_ = std::get<5>(partitions[i]); - partition_schema.engine_type_ = std::get<6>(partitions[i]); - partition_schema.index_params_ = std::get<7>(partitions[i]); - partition_schema.metric_type_ = std::get<8>(partitions[i]); - partition_schema.owner_collection_ = collection_id; - partition_schema.partition_tag_ = std::get<9>(partitions[i]); - partition_schema.version_ = std::get<10>(partitions[i]); - partition_schema.collection_id_ = std::get<11>(partitions[i]); - partition_schema.flush_lsn_ = std::get<12>(partitions[i]); - partition_schema_array.emplace_back(partition_schema); - } - } catch (std::exception& e) { - return HandleException("Encounter exception when show partitions", e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::GetPartitionName(const std::string& collection_id, const std::string& tag, - std::string& partition_name) { - try { - server::MetricCollector metric; - fiu_do_on("SqliteMetaImpl.GetPartitionName.throw_exception", throw std::exception()); - - // trim side-blank of tag, only compare valid characters - // for example: " ab cd " is treated as "ab cd" - std::string valid_tag = tag; - server::StringHelpFunctions::TrimStringBlank(valid_tag); - - auto name = ConnectorPtr->select(columns(&CollectionSchema::collection_id_), - where(c(&CollectionSchema::owner_collection_) == collection_id and - c(&CollectionSchema::partition_tag_) == valid_tag and - c(&CollectionSchema::state_) != (int)CollectionSchema::TO_DELETE)); - if (name.size() > 0) { - partition_name = std::get<0>(name[0]); - } else { - return Status(DB_NOT_FOUND, "Collection " + collection_id + "'s partition " + valid_tag + " not found"); - } - } catch (std::exception& e) { - return HandleException("Encounter exception when get partition name", e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::FilesToSearch(const std::string& collection_id, FilesHolder& files_holder) { - try { - server::MetricCollector metric; - fiu_do_on("SqliteMetaImpl.FilesToSearch.throw_exception", throw std::exception()); - - CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_id; - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - return status; - } - - // perform query - auto select_columns = columns(&SegmentSchema::id_, &SegmentSchema::collection_id_, &SegmentSchema::segment_id_, - &SegmentSchema::file_id_, &SegmentSchema::file_type_, &SegmentSchema::file_size_, - &SegmentSchema::row_count_, &SegmentSchema::date_, &SegmentSchema::engine_type_, - &SegmentSchema::created_on_, &SegmentSchema::updated_time_); - - auto match_collectionid = c(&SegmentSchema::collection_id_) == collection_id; - - std::vector file_types = {(int)SegmentSchema::RAW, (int)SegmentSchema::TO_INDEX, - (int)SegmentSchema::INDEX}; - auto match_type = in(&SegmentSchema::file_type_, file_types); - decltype(ConnectorPtr->select(select_columns)) selected; - { - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - auto filter = where(match_collectionid and match_type); - selected = ConnectorPtr->select(select_columns, filter); - } - - Status ret; - int64_t files_count = 0; - for (auto& file : selected) { - SegmentSchema collection_file; - collection_file.id_ = std::get<0>(file); - collection_file.collection_id_ = std::get<1>(file); - collection_file.segment_id_ = std::get<2>(file); - collection_file.file_id_ = std::get<3>(file); - collection_file.file_type_ = std::get<4>(file); - collection_file.file_size_ = std::get<5>(file); - collection_file.row_count_ = std::get<6>(file); - collection_file.date_ = std::get<7>(file); - collection_file.engine_type_ = std::get<8>(file); - collection_file.created_on_ = std::get<9>(file); - collection_file.updated_time_ = std::get<10>(file); - - collection_file.dimension_ = collection_schema.dimension_; - collection_file.index_file_size_ = collection_schema.index_file_size_; - collection_file.index_params_ = collection_schema.index_params_; - collection_file.metric_type_ = collection_schema.metric_type_; - - auto status = utils::GetCollectionFilePath(options_, collection_file); - if (!status.ok()) { - ret = status; - continue; - } - - files_holder.MarkFile(collection_file); - files_count++; - } - if (files_count == 0) { - LOG_ENGINE_DEBUG_ << "No file to search for collection: " << collection_id; - } else { - LOG_ENGINE_DEBUG_ << "Collect " << files_count << " to-search files in collection " << collection_id; - } - return ret; - } catch (std::exception& e) { - return HandleException("Encounter exception when iterate index files", e.what()); - } -} - -Status -SqliteMetaImpl::FilesToSearchEx(const std::string& root_collection, const std::set& partition_id_array, - FilesHolder& files_holder) { - try { - server::MetricCollector metric; - fiu_do_on("SqliteMetaImpl.FilesToSearch.throw_exception", throw std::exception()); - - // get root collection information - CollectionSchema collection_schema; - collection_schema.collection_id_ = root_collection; - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - return status; - } - - // distribute id array to batches - const uint64_t batch_size = 50; - std::vector> id_groups; - std::vector temp_group; - for (auto& id : partition_id_array) { - temp_group.push_back(id); - if (temp_group.size() >= batch_size) { - id_groups.emplace_back(temp_group); - temp_group.clear(); - } - } - - if (!temp_group.empty()) { - id_groups.emplace_back(temp_group); - } - - // perform query batch by batch - int64_t files_count = 0; - Status ret; - for (auto group : id_groups) { - auto select_columns = - columns(&SegmentSchema::id_, &SegmentSchema::collection_id_, &SegmentSchema::segment_id_, - &SegmentSchema::file_id_, &SegmentSchema::file_type_, &SegmentSchema::file_size_, - &SegmentSchema::row_count_, &SegmentSchema::date_, &SegmentSchema::engine_type_, - &SegmentSchema::created_on_, &SegmentSchema::updated_time_); - - auto match_collectionid = in(&SegmentSchema::collection_id_, group); - - std::vector file_types = {(int)SegmentSchema::RAW, (int)SegmentSchema::TO_INDEX, - (int)SegmentSchema::INDEX}; - auto match_type = in(&SegmentSchema::file_type_, file_types); - decltype(ConnectorPtr->select(select_columns)) selected; - { - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - auto filter = where(match_collectionid and match_type); - selected = ConnectorPtr->select(select_columns, filter); - } - - for (auto& file : selected) { - SegmentSchema collection_file; - collection_file.id_ = std::get<0>(file); - collection_file.collection_id_ = std::get<1>(file); - collection_file.segment_id_ = std::get<2>(file); - collection_file.file_id_ = std::get<3>(file); - collection_file.file_type_ = std::get<4>(file); - collection_file.file_size_ = std::get<5>(file); - collection_file.row_count_ = std::get<6>(file); - collection_file.date_ = std::get<7>(file); - collection_file.engine_type_ = std::get<8>(file); - collection_file.created_on_ = std::get<9>(file); - collection_file.updated_time_ = std::get<10>(file); - collection_file.dimension_ = collection_schema.dimension_; - collection_file.index_file_size_ = collection_schema.index_file_size_; - collection_file.index_params_ = collection_schema.index_params_; - collection_file.metric_type_ = collection_schema.metric_type_; - - auto status = utils::GetCollectionFilePath(options_, collection_file); - if (!status.ok()) { - ret = status; - continue; - } - - files_holder.MarkFile(collection_file); - files_count++; - } - } - if (files_count == 0) { - LOG_ENGINE_DEBUG_ << "No file to search for collection: " << root_collection; - } else { - LOG_ENGINE_DEBUG_ << "Collect " << files_count << " to-search files in collection " << root_collection; - } - return ret; - } catch (std::exception& e) { - return HandleException("Encounter exception when iterate index files", e.what()); - } -} - -Status -SqliteMetaImpl::FilesToMerge(const std::string& collection_id, FilesHolder& files_holder) { - try { - fiu_do_on("SqliteMetaImpl.FilesToMerge.throw_exception", throw std::exception()); - - server::MetricCollector metric; - - // check collection existence - CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_id; - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - return status; - } - - // get files to merge - auto select_columns = columns(&SegmentSchema::id_, &SegmentSchema::collection_id_, &SegmentSchema::segment_id_, - &SegmentSchema::file_id_, &SegmentSchema::file_type_, &SegmentSchema::file_size_, - &SegmentSchema::row_count_, &SegmentSchema::date_, &SegmentSchema::engine_type_, - &SegmentSchema::created_on_, &SegmentSchema::updated_time_); - decltype(ConnectorPtr->select(select_columns)) selected; - { - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - selected = ConnectorPtr->select(select_columns, - where(c(&SegmentSchema::file_type_) == (int)SegmentSchema::RAW and - c(&SegmentSchema::collection_id_) == collection_id), - order_by(&SegmentSchema::file_size_).desc()); - } - - Status result; - SegmentsSchema files; - for (auto& file : selected) { - SegmentSchema collection_file; - collection_file.file_size_ = std::get<5>(file); - if (collection_file.file_size_ >= (size_t)(collection_schema.index_file_size_)) { - continue; // skip large file - } - - collection_file.id_ = std::get<0>(file); - collection_file.collection_id_ = std::get<1>(file); - collection_file.segment_id_ = std::get<2>(file); - collection_file.file_id_ = std::get<3>(file); - collection_file.file_type_ = std::get<4>(file); - collection_file.row_count_ = std::get<6>(file); - collection_file.date_ = std::get<7>(file); - collection_file.engine_type_ = std::get<8>(file); - collection_file.created_on_ = std::get<9>(file); - collection_file.updated_time_ = std::get<10>(file); - - collection_file.dimension_ = collection_schema.dimension_; - collection_file.index_file_size_ = collection_schema.index_file_size_; - collection_file.index_params_ = collection_schema.index_params_; - collection_file.metric_type_ = collection_schema.metric_type_; - - auto status = utils::GetCollectionFilePath(options_, collection_file); - if (!status.ok()) { - result = status; - } - - files.emplace_back(collection_file); - } - - // no need to merge if files count less than 2 - if (files.size() > 1) { - LOG_ENGINE_DEBUG_ << "Collect " << files.size() << " to-merge files in collection " << collection_id; - for (auto& file : files) { - files_holder.MarkFile(file); - } - } - return result; - } catch (std::exception& e) { - return HandleException("Encounter exception when iterate merge files", e.what()); - } -} - -Status -SqliteMetaImpl::FilesToIndex(FilesHolder& files_holder) { - try { - fiu_do_on("SqliteMetaImpl.FilesToIndex.throw_exception", throw std::exception()); - - server::MetricCollector metric; - - auto select_columns = columns(&SegmentSchema::id_, &SegmentSchema::collection_id_, &SegmentSchema::segment_id_, - &SegmentSchema::file_id_, &SegmentSchema::file_type_, &SegmentSchema::file_size_, - &SegmentSchema::row_count_, &SegmentSchema::date_, &SegmentSchema::engine_type_, - &SegmentSchema::created_on_, &SegmentSchema::updated_time_); - decltype(ConnectorPtr->select(select_columns)) selected; - { - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - selected = ConnectorPtr->select(select_columns, - where(c(&SegmentSchema::file_type_) == (int)SegmentSchema::TO_INDEX)); - } - - Status ret; - int64_t files_count = 0; - std::map groups; - for (auto& file : selected) { - SegmentSchema collection_file; - collection_file.id_ = std::get<0>(file); - collection_file.collection_id_ = std::get<1>(file); - collection_file.segment_id_ = std::get<2>(file); - collection_file.file_id_ = std::get<3>(file); - collection_file.file_type_ = std::get<4>(file); - collection_file.file_size_ = std::get<5>(file); - collection_file.row_count_ = std::get<6>(file); - collection_file.date_ = std::get<7>(file); - collection_file.engine_type_ = std::get<8>(file); - collection_file.created_on_ = std::get<9>(file); - collection_file.updated_time_ = std::get<10>(file); - - auto status = utils::GetCollectionFilePath(options_, collection_file); - if (!status.ok()) { - ret = status; - } - auto groupItr = groups.find(collection_file.collection_id_); - if (groupItr == groups.end()) { - CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_file.collection_id_; - auto status = DescribeCollection(collection_schema); - fiu_do_on("SqliteMetaImpl_FilesToIndex_CollectionNotFound", - status = Status(DB_NOT_FOUND, "collection not found")); - if (!status.ok()) { - return status; - } - groups[collection_file.collection_id_] = collection_schema; - } - collection_file.dimension_ = groups[collection_file.collection_id_].dimension_; - collection_file.index_file_size_ = groups[collection_file.collection_id_].index_file_size_; - collection_file.index_params_ = groups[collection_file.collection_id_].index_params_; - collection_file.metric_type_ = groups[collection_file.collection_id_].metric_type_; - files_holder.MarkFile(collection_file); - - files_count++; - } - - if (files_count > 0) { - LOG_ENGINE_DEBUG_ << "Collect " << files_count << " to-index files"; - } - return ret; - } catch (std::exception& e) { - return HandleException("Encounter exception when iterate raw files", e.what()); - } -} - -Status -SqliteMetaImpl::FilesByType(const std::string& collection_id, const std::vector& file_types, - FilesHolder& files_holder) { - if (file_types.empty()) { - return Status(DB_ERROR, "file types array is empty"); - } - - Status ret = Status::OK(); - - try { - fiu_do_on("SqliteMetaImpl.FilesByType.throw_exception", throw std::exception()); - - CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_id; - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - return status; - } - - // get files by type - auto select_columns = columns(&SegmentSchema::id_, &SegmentSchema::segment_id_, &SegmentSchema::file_id_, - &SegmentSchema::file_type_, &SegmentSchema::file_size_, - &SegmentSchema::row_count_, &SegmentSchema::date_, &SegmentSchema::engine_type_, - &SegmentSchema::created_on_, &SegmentSchema::updated_time_); - decltype(ConnectorPtr->select(select_columns)) selected; - { - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - selected = ConnectorPtr->select(select_columns, where(in(&SegmentSchema::file_type_, file_types) and - c(&SegmentSchema::collection_id_) == collection_id)); - } - - if (selected.size() >= 1) { - int raw_count = 0, new_count = 0, new_merge_count = 0, new_index_count = 0; - int to_index_count = 0, index_count = 0, backup_count = 0; - for (auto& file : selected) { - SegmentSchema file_schema; - file_schema.collection_id_ = collection_id; - file_schema.id_ = std::get<0>(file); - file_schema.segment_id_ = std::get<1>(file); - file_schema.file_id_ = std::get<2>(file); - file_schema.file_type_ = std::get<3>(file); - file_schema.file_size_ = std::get<4>(file); - file_schema.row_count_ = std::get<5>(file); - file_schema.date_ = std::get<6>(file); - file_schema.engine_type_ = std::get<7>(file); - file_schema.created_on_ = std::get<8>(file); - file_schema.updated_time_ = std::get<9>(file); - - file_schema.dimension_ = collection_schema.dimension_; - file_schema.index_file_size_ = collection_schema.index_file_size_; - file_schema.index_params_ = collection_schema.index_params_; - file_schema.metric_type_ = collection_schema.metric_type_; - - switch (file_schema.file_type_) { - case (int)SegmentSchema::RAW: - ++raw_count; - break; - case (int)SegmentSchema::NEW: - ++new_count; - break; - case (int)SegmentSchema::NEW_MERGE: - ++new_merge_count; - break; - case (int)SegmentSchema::NEW_INDEX: - ++new_index_count; - break; - case (int)SegmentSchema::TO_INDEX: - ++to_index_count; - break; - case (int)SegmentSchema::INDEX: - ++index_count; - break; - case (int)SegmentSchema::BACKUP: - ++backup_count; - break; - default: - break; - } - - auto status = utils::GetCollectionFilePath(options_, file_schema); - if (!status.ok()) { - ret = status; - } - - files_holder.MarkFile(file_schema); - } - - std::string msg = "Get collection files by type."; - for (int file_type : file_types) { - switch (file_type) { - case (int)SegmentSchema::RAW: - msg = msg + " raw files:" + std::to_string(raw_count); - break; - case (int)SegmentSchema::NEW: - msg = msg + " new files:" + std::to_string(new_count); - break; - case (int)SegmentSchema::NEW_MERGE: - msg = msg + " new_merge files:" + std::to_string(new_merge_count); - break; - case (int)SegmentSchema::NEW_INDEX: - msg = msg + " new_index files:" + std::to_string(new_index_count); - break; - case (int)SegmentSchema::TO_INDEX: - msg = msg + " to_index files:" + std::to_string(to_index_count); - break; - case (int)SegmentSchema::INDEX: - msg = msg + " index files:" + std::to_string(index_count); - break; - case (int)SegmentSchema::BACKUP: - msg = msg + " backup files:" + std::to_string(backup_count); - break; - default: - break; - } - } - LOG_ENGINE_DEBUG_ << msg; - } - } catch (std::exception& e) { - return HandleException("Encounter exception when check non index files", e.what()); - } - - return ret; -} - -Status -SqliteMetaImpl::FilesByTypeEx(const std::vector& collections, - const std::vector& file_types, FilesHolder& files_holder) { - if (file_types.empty()) { - return Status(DB_ERROR, "file types array is empty"); - } - - Status ret = Status::OK(); - - try { - fiu_do_on("SqliteMetaImpl.FilesByTypeEx.throw_exception", throw std::exception()); - - // distribute id array to batches - const uint64_t batch_size = 50; - std::vector> id_groups; - std::vector temp_group; - std::unordered_map map_collections; - for (auto& collection : collections) { - map_collections.insert(std::make_pair(collection.collection_id_, collection)); - temp_group.push_back(collection.collection_id_); - if (temp_group.size() >= batch_size) { - id_groups.emplace_back(temp_group); - temp_group.clear(); - } - } - - if (!temp_group.empty()) { - id_groups.emplace_back(temp_group); - } - - // perform query batch by batch - Status ret; - int raw_count = 0, new_count = 0, new_merge_count = 0, new_index_count = 0; - int to_index_count = 0, index_count = 0, backup_count = 0; - for (auto group : id_groups) { - auto select_columns = - columns(&SegmentSchema::id_, &SegmentSchema::collection_id_, &SegmentSchema::segment_id_, - &SegmentSchema::file_id_, &SegmentSchema::file_type_, &SegmentSchema::file_size_, - &SegmentSchema::row_count_, &SegmentSchema::date_, &SegmentSchema::engine_type_, - &SegmentSchema::created_on_, &SegmentSchema::updated_time_); - decltype(ConnectorPtr->select(select_columns)) selected; - - auto match_collectionid = in(&SegmentSchema::collection_id_, group); - - std::vector file_types = {(int)SegmentSchema::RAW, (int)SegmentSchema::TO_INDEX, - (int)SegmentSchema::INDEX}; - auto match_type = in(&SegmentSchema::file_type_, file_types); - { - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - auto filter = where(match_collectionid and match_type); - selected = ConnectorPtr->select(select_columns, filter); - } - - for (auto& file : selected) { - SegmentSchema file_schema; - file_schema.id_ = std::get<0>(file); - file_schema.collection_id_ = std::get<1>(file); - file_schema.segment_id_ = std::get<2>(file); - file_schema.file_id_ = std::get<3>(file); - file_schema.file_type_ = std::get<4>(file); - file_schema.file_size_ = std::get<5>(file); - file_schema.row_count_ = std::get<6>(file); - file_schema.date_ = std::get<7>(file); - file_schema.engine_type_ = std::get<8>(file); - file_schema.created_on_ = std::get<9>(file); - file_schema.updated_time_ = std::get<10>(file); - - auto& collection_schema = map_collections[file_schema.collection_id_]; - file_schema.dimension_ = collection_schema.dimension_; - file_schema.index_file_size_ = collection_schema.index_file_size_; - file_schema.index_params_ = collection_schema.index_params_; - file_schema.metric_type_ = collection_schema.metric_type_; - - switch (file_schema.file_type_) { - case (int)SegmentSchema::RAW: - ++raw_count; - break; - case (int)SegmentSchema::NEW: - ++new_count; - break; - case (int)SegmentSchema::NEW_MERGE: - ++new_merge_count; - break; - case (int)SegmentSchema::NEW_INDEX: - ++new_index_count; - break; - case (int)SegmentSchema::TO_INDEX: - ++to_index_count; - break; - case (int)SegmentSchema::INDEX: - ++index_count; - break; - case (int)SegmentSchema::BACKUP: - ++backup_count; - break; - default: - break; - } - - auto status = utils::GetCollectionFilePath(options_, file_schema); - if (!status.ok()) { - ret = status; - } - - files_holder.MarkFile(file_schema); - } - } - - std::string msg = "Get collection files by type."; - for (int file_type : file_types) { - switch (file_type) { - case (int)SegmentSchema::RAW: - msg = msg + " raw files:" + std::to_string(raw_count); - break; - case (int)SegmentSchema::NEW: - msg = msg + " new files:" + std::to_string(new_count); - break; - case (int)SegmentSchema::NEW_MERGE: - msg = msg + " new_merge files:" + std::to_string(new_merge_count); - break; - case (int)SegmentSchema::NEW_INDEX: - msg = msg + " new_index files:" + std::to_string(new_index_count); - break; - case (int)SegmentSchema::TO_INDEX: - msg = msg + " to_index files:" + std::to_string(to_index_count); - break; - case (int)SegmentSchema::INDEX: - msg = msg + " index files:" + std::to_string(index_count); - break; - case (int)SegmentSchema::BACKUP: - msg = msg + " backup files:" + std::to_string(backup_count); - break; - default: - break; - } - } - LOG_ENGINE_DEBUG_ << msg; - } catch (std::exception& e) { - return HandleException("Encounter exception when check non index files", e.what()); - } - - return ret; -} - -Status -SqliteMetaImpl::FilesByID(const std::vector& ids, FilesHolder& files_holder) { - if (ids.empty()) { - return Status::OK(); - } - - try { - server::MetricCollector metric; - fiu_do_on("SqliteMetaImpl.FilesByID.throw_exception", throw std::exception()); - - auto select_columns = columns(&SegmentSchema::id_, &SegmentSchema::collection_id_, &SegmentSchema::segment_id_, - &SegmentSchema::file_id_, &SegmentSchema::file_type_, &SegmentSchema::file_size_, - &SegmentSchema::row_count_, &SegmentSchema::date_, &SegmentSchema::engine_type_, - &SegmentSchema::created_on_, &SegmentSchema::updated_time_); - - // perform query - decltype(ConnectorPtr->select(select_columns)) selected; - auto match_fileid = in(&SegmentSchema::id_, ids); - auto filter = where(match_fileid); - { - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - selected = ConnectorPtr->select(select_columns, filter); - } - - std::map collections; - Status ret; - int64_t files_count = 0; - for (auto& file : selected) { - SegmentSchema collection_file; - collection_file.id_ = std::get<0>(file); - collection_file.collection_id_ = std::get<1>(file); - collection_file.segment_id_ = std::get<2>(file); - collection_file.file_id_ = std::get<3>(file); - collection_file.file_type_ = std::get<4>(file); - collection_file.file_size_ = std::get<5>(file); - collection_file.row_count_ = std::get<6>(file); - collection_file.date_ = std::get<7>(file); - collection_file.engine_type_ = std::get<8>(file); - collection_file.created_on_ = std::get<9>(file); - collection_file.updated_time_ = std::get<10>(file); - - if (collections.find(collection_file.collection_id_) == collections.end()) { - CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_file.collection_id_; - auto status = DescribeCollection(collection_schema); - if (!status.ok()) { - return status; - } - collections.insert(std::make_pair(collection_file.collection_id_, collection_schema)); - } - - auto status = utils::GetCollectionFilePath(options_, collection_file); - if (!status.ok()) { - ret = status; - } - - files_holder.MarkFile(collection_file); - files_count++; - } - - milvus::engine::meta::SegmentsSchema& files = files_holder.HoldFiles(); - for (auto& collection_file : files) { - CollectionSchema& collection_schema = collections[collection_file.collection_id_]; - collection_file.dimension_ = collection_schema.dimension_; - collection_file.index_file_size_ = collection_schema.index_file_size_; - collection_file.index_params_ = collection_schema.index_params_; - collection_file.metric_type_ = collection_schema.metric_type_; - } - - if (files_count == 0) { - LOG_ENGINE_ERROR_ << "No file to search in file id list"; - } else { - LOG_ENGINE_DEBUG_ << "Collect " << files_count << " files by id"; - } - - return ret; - } catch (std::exception& e) { - return HandleException("Encounter exception when iterate index files", e.what()); - } - return Status::OK(); -} - -// TODO(myh): Support swap to cloud storage -Status -SqliteMetaImpl::Archive() { - auto& criterias = options_.archive_conf_.GetCriterias(); - if (criterias.size() == 0) { - return Status::OK(); - } - - for (auto kv : criterias) { - auto& criteria = kv.first; - auto& limit = kv.second; - if (criteria == engine::ARCHIVE_CONF_DAYS) { - int64_t usecs = limit * DAY * US_PS; - int64_t now = utils::GetMicroSecTimeStamp(); - try { - fiu_do_on("SqliteMetaImpl.Archive.throw_exception", throw std::exception()); - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - ConnectorPtr->update_all(set(c(&SegmentSchema::file_type_) = (int)SegmentSchema::TO_DELETE), - where(c(&SegmentSchema::created_on_) < (int64_t)(now - usecs) and - c(&SegmentSchema::file_type_) != (int)SegmentSchema::TO_DELETE)); - } catch (std::exception& e) { - return HandleException("Encounter exception when update collection files", e.what()); - } - - LOG_ENGINE_DEBUG_ << "Archive old files"; - } - if (criteria == engine::ARCHIVE_CONF_DISK) { - uint64_t sum = 0; - Size(sum); - - int64_t to_delete = (int64_t)sum - limit * GB; - DiscardFiles(to_delete); - - LOG_ENGINE_DEBUG_ << "Archive files to free disk"; - } - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::Size(uint64_t& result) { - result = 0; - try { - fiu_do_on("SqliteMetaImpl.Size.throw_exception", throw std::exception()); - - auto selected = ConnectorPtr->select(columns(sum(&SegmentSchema::file_size_)), - where(c(&SegmentSchema::file_type_) != (int)SegmentSchema::TO_DELETE)); - for (auto& total_size : selected) { - if (!std::get<0>(total_size)) { - continue; - } - result += (uint64_t)(*std::get<0>(total_size)); - } - } catch (std::exception& e) { - return HandleException("Encounter exception when calculate db size", e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::CleanUpShadowFiles() { - try { - server::MetricCollector metric; - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - std::vector file_types = {(int)SegmentSchema::NEW, (int)SegmentSchema::NEW_INDEX, - (int)SegmentSchema::NEW_MERGE}; - auto files = - ConnectorPtr->select(columns(&SegmentSchema::id_), where(in(&SegmentSchema::file_type_, file_types))); - - auto commited = ConnectorPtr->transaction([&]() mutable { - for (auto& file : files) { - LOG_ENGINE_DEBUG_ << "Remove collection file type as NEW"; - ConnectorPtr->remove(std::get<0>(file)); - } - return true; - }); - - fiu_do_on("SqliteMetaImpl.CleanUpShadowFiles.fail_commited", commited = false); - fiu_do_on("SqliteMetaImpl.CleanUpShadowFiles.throw_exception", throw std::exception()); - if (!commited) { - return HandleException("CleanUp error: sqlite transaction failed"); - } - - if (files.size() > 0) { - LOG_ENGINE_DEBUG_ << "Clean " << files.size() << " files"; - } - } catch (std::exception& e) { - return HandleException("Encounter exception when clean collection file", e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::CleanUpFilesWithTTL(uint64_t seconds /*, CleanUpFilter* filter*/) { - auto now = utils::GetMicroSecTimeStamp(); - std::set collection_ids; - std::map segment_ids; - - // remove to_delete files - try { - fiu_do_on("SqliteMetaImpl.CleanUpFilesWithTTL.RemoveFile_ThrowException", throw std::exception()); - - server::MetricCollector metric; - - std::vector file_types = { - (int)SegmentSchema::TO_DELETE, - (int)SegmentSchema::BACKUP, - }; - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - // collect files to be deleted - auto files = - ConnectorPtr->select(columns(&SegmentSchema::id_, &SegmentSchema::collection_id_, - &SegmentSchema::segment_id_, &SegmentSchema::engine_type_, - &SegmentSchema::file_id_, &SegmentSchema::file_type_, &SegmentSchema::date_), - where(in(&SegmentSchema::file_type_, file_types) and - c(&SegmentSchema::updated_time_) < now - seconds * US_PS)); - - int64_t clean_files = 0; - auto commited = ConnectorPtr->transaction([&]() mutable { - SegmentSchema collection_file; - for (auto& file : files) { - collection_file.id_ = std::get<0>(file); - collection_file.collection_id_ = std::get<1>(file); - collection_file.segment_id_ = std::get<2>(file); - collection_file.engine_type_ = std::get<3>(file); - collection_file.file_id_ = std::get<4>(file); - collection_file.file_type_ = std::get<5>(file); - collection_file.date_ = std::get<6>(file); - - // check if the file can be deleted - if (!FilesHolder::CanBeDeleted(collection_file)) { - LOG_ENGINE_DEBUG_ << "File:" << collection_file.file_id_ - << " currently is in use, not able to delete now"; - continue; // ignore this file, don't delete it - } - - // erase from cache, must do this before file deleted, - // because GetCollectionFilePath won't able to generate file path after the file is deleted - // TODO(zhiru): clean up - utils::GetCollectionFilePath(options_, collection_file); - server::CommonUtil::EraseFromCache(collection_file.location_); - - if (collection_file.file_type_ == (int)SegmentSchema::TO_DELETE) { - // delete file from meta - ConnectorPtr->remove(collection_file.id_); - - // delete file from disk storage - utils::DeleteCollectionFilePath(options_, collection_file); - - LOG_ENGINE_DEBUG_ << "Remove file id:" << collection_file.file_id_ - << " location:" << collection_file.location_; - collection_ids.insert(collection_file.collection_id_); - segment_ids.insert(std::make_pair(collection_file.segment_id_, collection_file)); - - ++clean_files; - } - } - return true; - }); - fiu_do_on("SqliteMetaImpl.CleanUpFilesWithTTL.RemoveFile_FailCommited", commited = false); - - if (!commited) { - return HandleException("CleanUpFilesWithTTL error: sqlite transaction failed"); - } - - if (clean_files > 0) { - LOG_ENGINE_DEBUG_ << "Clean " << clean_files << " files expired in " << seconds << " seconds"; - } - } catch (std::exception& e) { - return HandleException("Encounter exception when clean collection files", e.what()); - } - - // remove to_delete collections - try { - fiu_do_on("SqliteMetaImpl.CleanUpFilesWithTTL.RemoveCollection_ThrowException", throw std::exception()); - server::MetricCollector metric; - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - auto collections = - ConnectorPtr->select(columns(&CollectionSchema::id_, &CollectionSchema::collection_id_), - where(c(&CollectionSchema::state_) == (int)CollectionSchema::TO_DELETE)); - - auto commited = ConnectorPtr->transaction([&]() mutable { - for (auto& collection : collections) { - utils::DeleteCollectionPath(options_, std::get<1>(collection), false); // only delete empty folder - ConnectorPtr->remove(std::get<0>(collection)); - } - - return true; - }); - fiu_do_on("SqliteMetaImpl.CleanUpFilesWithTTL.RemoveCollection_Failcommited", commited = false); - - if (!commited) { - return HandleException("CleanUpFilesWithTTL error: sqlite transaction failed"); - } - - if (collections.size() > 0) { - LOG_ENGINE_DEBUG_ << "Remove " << collections.size() << " collections from meta"; - } - } catch (std::exception& e) { - return HandleException("Encounter exception when clean collection files", e.what()); - } - - // remove deleted collection folder - // don't remove collection folder until all its files has been deleted - try { - fiu_do_on("SqliteMetaImpl.CleanUpFilesWithTTL.RemoveCollectionFolder_ThrowException", throw std::exception()); - server::MetricCollector metric; - - int64_t remove_collections = 0; - for (auto& collection_id : collection_ids) { - auto selected = ConnectorPtr->select(columns(&SegmentSchema::file_id_), - where(c(&SegmentSchema::collection_id_) == collection_id)); - if (selected.size() == 0) { - utils::DeleteCollectionPath(options_, collection_id); - ++remove_collections; - } - } - - if (remove_collections) { - LOG_ENGINE_DEBUG_ << "Remove " << remove_collections << " collections folder"; - } - } catch (std::exception& e) { - return HandleException("Encounter exception when delete collection folder", e.what()); - } - - // remove deleted segment folder - // don't remove segment folder until all its files has been deleted - try { - fiu_do_on("SqliteMetaImpl.CleanUpFilesWithTTL.RemoveSegmentFolder_ThrowException", throw std::exception()); - server::MetricCollector metric; - - int64_t remove_segments = 0; - for (auto& segment_id : segment_ids) { - auto selected = ConnectorPtr->select(columns(&SegmentSchema::id_), - where(c(&SegmentSchema::segment_id_) == segment_id.first)); - if (selected.size() == 0) { - utils::DeleteSegment(options_, segment_id.second); - std::string segment_dir; - utils::GetParentPath(segment_id.second.location_, segment_dir); - LOG_ENGINE_DEBUG_ << "Remove segment directory: " << segment_dir; - ++remove_segments; - } - } - - if (remove_segments > 0) { - LOG_ENGINE_DEBUG_ << "Remove " << remove_segments << " segments folder"; - } - } catch (std::exception& e) { - return HandleException("Encounter exception when delete collection folder", e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::Count(const std::string& collection_id, uint64_t& result) { - try { - fiu_do_on("SqliteMetaImpl.Count.throw_exception", throw std::exception()); - - server::MetricCollector metric; - - std::vector file_types = {(int)SegmentSchema::RAW, (int)SegmentSchema::TO_INDEX, - (int)SegmentSchema::INDEX}; - auto select_columns = columns(&SegmentSchema::row_count_); - decltype(ConnectorPtr->select(select_columns)) selected; - { - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - selected = ConnectorPtr->select(select_columns, where(in(&SegmentSchema::file_type_, file_types) and - c(&SegmentSchema::collection_id_) == collection_id)); - } - - CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_id; - auto status = DescribeCollection(collection_schema); - - if (!status.ok()) { - return status; - } - - result = 0; - for (auto& file : selected) { - result += std::get<0>(file); - } - } catch (std::exception& e) { - return HandleException("Encounter exception when calculate collection file size", e.what()); - } - return Status::OK(); -} - -Status -SqliteMetaImpl::DropAll() { - LOG_ENGINE_DEBUG_ << "Drop all sqlite meta"; - - try { - ConnectorPtr->drop_table(META_TABLES); - ConnectorPtr->drop_table(META_TABLEFILES); - ConnectorPtr->drop_table(META_ENVIRONMENT); - ConnectorPtr->drop_table(META_FIELDS); - } catch (std::exception& e) { - return HandleException("Encounter exception when drop all meta", e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::DiscardFiles(int64_t to_discard_size) { - if (to_discard_size <= 0) { - return Status::OK(); - } - - LOG_ENGINE_DEBUG_ << "About to discard size=" << to_discard_size; - - try { - fiu_do_on("SqliteMetaImpl.DiscardFiles.throw_exception", throw std::exception()); - - server::MetricCollector metric; - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - auto commited = ConnectorPtr->transaction([&]() mutable { - auto selected = ConnectorPtr->select(columns(&SegmentSchema::id_, &SegmentSchema::file_size_), - where(c(&SegmentSchema::file_type_) != (int)SegmentSchema::TO_DELETE), - order_by(&SegmentSchema::id_), limit(10)); - - std::vector ids; - SegmentSchema collection_file; - - for (auto& file : selected) { - if (to_discard_size <= 0) - break; - collection_file.id_ = std::get<0>(file); - collection_file.file_size_ = std::get<1>(file); - ids.push_back(collection_file.id_); - LOG_ENGINE_DEBUG_ << "Discard file id=" << collection_file.file_id_ - << " file size=" << collection_file.file_size_; - to_discard_size -= collection_file.file_size_; - } - - if (ids.size() == 0) { - return true; - } - - ConnectorPtr->update_all(set(c(&SegmentSchema::file_type_) = (int)SegmentSchema::TO_DELETE, - c(&SegmentSchema::updated_time_) = utils::GetMicroSecTimeStamp()), - where(in(&SegmentSchema::id_, ids))); - - return true; - }); - fiu_do_on("SqliteMetaImpl.DiscardFiles.fail_commited", commited = false); - if (!commited) { - return HandleException("DiscardFiles error: sqlite transaction failed"); - } - } catch (std::exception& e) { - return HandleException("Encounter exception when discard collection file", e.what()); - } - - return DiscardFiles(to_discard_size); -} - -Status -SqliteMetaImpl::SetGlobalLastLSN(uint64_t lsn) { - try { - server::MetricCollector metric; - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - auto selected = ConnectorPtr->select(columns(&EnvironmentSchema::global_lsn_)); - if (selected.size() == 0) { - EnvironmentSchema env; - env.global_lsn_ = lsn; - ConnectorPtr->insert(env); - } else { - uint64_t last_lsn = std::get<0>(selected[0]); - if (lsn <= last_lsn) { - return Status::OK(); - } - - ConnectorPtr->update_all(set(c(&EnvironmentSchema::global_lsn_) = lsn)); - } - - LOG_ENGINE_DEBUG_ << "Update global lsn = " << lsn; - } catch (std::exception& e) { - std::string msg = "Exception update global lsn = " + lsn; - return HandleException(msg, e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::GetGlobalLastLSN(uint64_t& lsn) { - try { - server::MetricCollector metric; - - auto selected = ConnectorPtr->select(columns(&EnvironmentSchema::global_lsn_)); - if (selected.size() == 0) { - lsn = 0; - } else { - lsn = std::get<0>(selected[0]); - } - } catch (std::exception& e) { - return HandleException("Encounter exception when delete collection folder", e.what()); - } - - return Status::OK(); -} - -Status -SqliteMetaImpl::CreateHybridCollection(meta::CollectionSchema& collection_schema, - meta::hybrid::FieldsSchema& fields_schema) { - USING_SQLITE_WARNING - try { - server::MetricCollector metric; - - // multi-threads call sqlite update may get exception('bad logic', etc), so we add a lock here - std::lock_guard meta_lock(meta_mutex_); - - if (collection_schema.collection_id_ == "") { - NextCollectionId(collection_schema.collection_id_); - } else { - fiu_do_on("SqliteMetaImpl.CreateCollection.throw_exception", throw std::exception()); - auto collection = - ConnectorPtr->select(columns(&CollectionSchema::state_), - where(c(&CollectionSchema::collection_id_) == collection_schema.collection_id_)); - if (collection.size() == 1) { - if (CollectionSchema::TO_DELETE == std::get<0>(collection[0])) { - return Status(DB_ERROR, - "Collection already exists and it is in delete state, please wait a second"); - } else { - // Change from no error to already exist. - return Status(DB_ALREADY_EXIST, "Collection already exists"); - } - } - } - - collection_schema.id_ = -1; - collection_schema.created_on_ = utils::GetMicroSecTimeStamp(); - - try { - fiu_do_on("SqliteMetaImpl.CreateHybridCollection.insert_throw_exception", throw std::exception()); - auto id = ConnectorPtr->insert(collection_schema); - collection_schema.id_ = id; - } catch (std::exception& e) { - return HandleException("Encounter exception when create collection", e.what()); - } - - LOG_ENGINE_DEBUG_ << "Successfully create collection collection: " << collection_schema.collection_id_; - - Status status = utils::CreateCollectionPath(options_, collection_schema.collection_id_); - if (!status.ok()) { - return status; - } - - try { - for (auto schema : fields_schema.fields_schema_) { - auto field_id = ConnectorPtr->insert(schema); - LOG_ENGINE_DEBUG_ << "Successfully create collection field" << field_id; - } - } catch (std::exception& e) { - return HandleException("Encounter exception when create collection field", e.what()); - } - - return status; - } catch (std::exception& e) { - return HandleException("Encounter exception when create collection", e.what()); - } -} - -Status -SqliteMetaImpl::DescribeHybridCollection(milvus::engine::meta::CollectionSchema& collection_schema, - milvus::engine::meta::hybrid::FieldsSchema& fields_schema) { - try { - server::MetricCollector metric; - fiu_do_on("SqliteMetaImpl.DescriCollection.throw_exception", throw std::exception()); - auto groups = ConnectorPtr->select( - columns(&CollectionSchema::id_, &CollectionSchema::state_, &CollectionSchema::dimension_, - &CollectionSchema::created_on_, &CollectionSchema::flag_, &CollectionSchema::index_file_size_, - &CollectionSchema::engine_type_, &CollectionSchema::index_params_, &CollectionSchema::metric_type_, - &CollectionSchema::owner_collection_, &CollectionSchema::partition_tag_, - &CollectionSchema::version_, &CollectionSchema::flush_lsn_), - where(c(&CollectionSchema::collection_id_) == collection_schema.collection_id_ and - c(&CollectionSchema::state_) != (int)CollectionSchema::TO_DELETE)); - - if (groups.size() == 1) { - collection_schema.id_ = std::get<0>(groups[0]); - collection_schema.state_ = std::get<1>(groups[0]); - collection_schema.dimension_ = std::get<2>(groups[0]); - collection_schema.created_on_ = std::get<3>(groups[0]); - collection_schema.flag_ = std::get<4>(groups[0]); - collection_schema.index_file_size_ = std::get<5>(groups[0]); - collection_schema.engine_type_ = std::get<6>(groups[0]); - collection_schema.index_params_ = std::get<7>(groups[0]); - collection_schema.metric_type_ = std::get<8>(groups[0]); - collection_schema.owner_collection_ = std::get<9>(groups[0]); - collection_schema.partition_tag_ = std::get<10>(groups[0]); - collection_schema.version_ = std::get<11>(groups[0]); - collection_schema.flush_lsn_ = std::get<12>(groups[0]); - } else { - return Status(DB_NOT_FOUND, "Collection " + collection_schema.collection_id_ + " not found"); - } - - auto field_groups = - ConnectorPtr->select(columns(&hybrid::FieldSchema::collection_id_, &hybrid::FieldSchema::field_name_, - &hybrid::FieldSchema::field_type_, &hybrid::FieldSchema::field_params_), - where(c(&hybrid::FieldSchema::collection_id_) == collection_schema.collection_id_)); - - if (field_groups.size() >= 1) { - fields_schema.fields_schema_.resize(field_groups.size()); - for (uint64_t i = 0; i < field_groups.size(); ++i) { - fields_schema.fields_schema_[i].collection_id_ = std::get<0>(field_groups[i]); - fields_schema.fields_schema_[i].field_name_ = std::get<1>(field_groups[i]); - fields_schema.fields_schema_[i].field_type_ = std::get<2>(field_groups[i]); - fields_schema.fields_schema_[i].field_params_ = std::get<3>(field_groups[i]); - } - } else { - return Status(DB_NOT_FOUND, "Collection " + collection_schema.collection_id_ + " fields not found"); - } - - } catch (std::exception& e) { - return HandleException("Encounter exception when describe collection", e.what()); - } - - return Status::OK(); -} - -} // namespace meta -} // namespace engine -} // namespace milvus diff --git a/core/src/db/meta/SqliteMetaImpl.h b/core/src/db/meta/SqliteMetaImpl.h deleted file mode 100644 index 5b3c04314a..0000000000 --- a/core/src/db/meta/SqliteMetaImpl.h +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include - -#include "Meta.h" -#include "db/Options.h" - -namespace milvus { -namespace engine { -namespace meta { - -auto -StoragePrototype(const std::string& path); - -auto -CollectionPrototype(const std::string& path); - -class SqliteMetaImpl : public Meta { - public: - explicit SqliteMetaImpl(const DBMetaOptions& options); - ~SqliteMetaImpl(); - - Status - CreateCollection(CollectionSchema& collection_schema) override; - - Status - DescribeCollection(CollectionSchema& collection_schema) override; - - Status - HasCollection(const std::string& collection_id, bool& has_or_not, bool is_root = false) override; - - Status - AllCollections(std::vector& collection_schema_array, bool is_root = false) override; - - Status - DropCollections(const std::vector& collection_id_array) override; - - Status - DeleteCollectionFiles(const std::vector& collection_id_array) override; - - Status - CreateCollectionFile(SegmentSchema& file_schema) override; - - Status - GetCollectionFiles(const std::string& collection_id, const std::vector& ids, - FilesHolder& files_holder) override; - - Status - GetCollectionFilesBySegmentId(const std::string& segment_id, FilesHolder& files_holder) override; - - Status - UpdateCollectionIndex(const std::string& collection_id, const CollectionIndex& index) override; - - Status - UpdateCollectionFlag(const std::string& collection_id, int64_t flag) override; - - Status - UpdateCollectionFlushLSN(const std::string& collection_id, uint64_t flush_lsn) override; - - Status - GetCollectionFlushLSN(const std::string& collection_id, uint64_t& flush_lsn) override; - - Status - UpdateCollectionFile(SegmentSchema& file_schema) override; - - Status - UpdateCollectionFilesToIndex(const std::string& collection_id) override; - - Status - UpdateCollectionFiles(SegmentsSchema& files) override; - - Status - UpdateCollectionFilesRowCount(SegmentsSchema& files) override; - - Status - DescribeCollectionIndex(const std::string& collection_id, CollectionIndex& index) override; - - Status - DropCollectionIndex(const std::string& collection_id) override; - - Status - CreatePartition(const std::string& collection_id, const std::string& partition_name, const std::string& tag, - uint64_t lsn) override; - - Status - HasPartition(const std::string& collection_id, const std::string& tag, bool& has_or_not) override; - - Status - DropPartition(const std::string& partition_name) override; - - Status - ShowPartitions(const std::string& collection_id, - std::vector& partition_schema_array) override; - - Status - GetPartitionName(const std::string& collection_id, const std::string& tag, std::string& partition_name) override; - - Status - FilesToSearch(const std::string& collection_id, FilesHolder& files_holder) override; - - Status - FilesToSearchEx(const std::string& root_collection, const std::set& partition_id_array, - FilesHolder& files_holder) override; - - Status - FilesToMerge(const std::string& collection_id, FilesHolder& files_holder) override; - - Status - FilesToIndex(FilesHolder& files_holder) override; - - Status - FilesByType(const std::string& collection_id, const std::vector& file_types, - FilesHolder& files_holder) override; - - Status - FilesByTypeEx(const std::vector& collections, const std::vector& file_types, - FilesHolder& files_holder) override; - - Status - FilesByID(const std::vector& ids, FilesHolder& files_holder) override; - - Status - Size(uint64_t& result) override; - - Status - Archive() override; - - Status - CleanUpShadowFiles() override; - - Status - CleanUpFilesWithTTL(uint64_t seconds /*, CleanUpFilter* filter = nullptr*/) override; - - Status - DropAll() override; - - Status - Count(const std::string& collection_id, uint64_t& result) override; - - Status - SetGlobalLastLSN(uint64_t lsn) override; - - Status - GetGlobalLastLSN(uint64_t& lsn) override; - - Status - CreateHybridCollection(CollectionSchema& collection_schema, hybrid::FieldsSchema& fields_schema) override; - - Status - DescribeHybridCollection(CollectionSchema& collection_schema, hybrid::FieldsSchema& fields_schema) override; - - private: - Status - NextFileId(std::string& file_id); - Status - NextCollectionId(std::string& collection_id); - Status - DiscardFiles(int64_t to_discard_size); - - void - ValidateMetaSchema(); - Status - Initialize(); - - private: - const DBMetaOptions options_; - std::mutex meta_mutex_; - std::mutex genid_mutex_; -}; // DBMetaImpl - -} // namespace meta -} // namespace engine -} // namespace milvus diff --git a/core/src/db/wal/WalBuffer.cpp b/core/src/db/wal/WalBuffer.cpp deleted file mode 100644 index 027d38e651..0000000000 --- a/core/src/db/wal/WalBuffer.cpp +++ /dev/null @@ -1,679 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/wal/WalBuffer.h" - -#include -#include -#include - -#include "db/wal/WalDefinations.h" -#include "utils/Log.h" - -namespace milvus { -namespace engine { -namespace wal { - -inline std::string -ToFileName(int32_t file_no) { - return std::to_string(file_no) + ".wal"; -} - -inline void -BuildLsn(uint32_t file_no, uint32_t offset, uint64_t& lsn) { - lsn = (uint64_t)file_no << 32 | offset; -} - -inline void -ParserLsn(uint64_t lsn, uint32_t& file_no, uint32_t& offset) { - file_no = uint32_t(lsn >> 32); - offset = uint32_t(lsn & LSN_OFFSET_MASK); -} - -MXLogBuffer::MXLogBuffer(const std::string& mxlog_path, const uint32_t buffer_size) - : mxlog_buffer_size_(buffer_size * UNIT_MB), mxlog_writer_(mxlog_path) { -} - -MXLogBuffer::~MXLogBuffer() { -} - -/** - * alloc space for buffers - * @param buffer_size - * @return - */ -bool -MXLogBuffer::Init(uint64_t start_lsn, uint64_t end_lsn) { - LOG_WAL_DEBUG_ << "start_lsn " << start_lsn << " end_lsn " << end_lsn; - - ParserLsn(start_lsn, mxlog_buffer_reader_.file_no, mxlog_buffer_reader_.buf_offset); - ParserLsn(end_lsn, mxlog_buffer_writer_.file_no, mxlog_buffer_writer_.buf_offset); - - if (start_lsn == end_lsn) { - // no data need recovery, start a new file_no - if (mxlog_buffer_writer_.buf_offset != 0) { - mxlog_buffer_writer_.file_no++; - mxlog_buffer_writer_.buf_offset = 0; - mxlog_buffer_reader_.file_no++; - mxlog_buffer_reader_.buf_offset = 0; - } - } else { - // to check whether buffer_size is enough - MXLogFileHandler file_handler(mxlog_writer_.GetFilePath()); - - uint32_t buffer_size_need = 0; - for (auto i = mxlog_buffer_reader_.file_no; i < mxlog_buffer_writer_.file_no; i++) { - file_handler.SetFileName(ToFileName(i)); - auto file_size = file_handler.GetFileSize(); - if (file_size == 0) { - LOG_WAL_ERROR_ << "bad wal file " << i; - return false; - } - if (file_size > buffer_size_need) { - buffer_size_need = file_size; - } - } - if (mxlog_buffer_writer_.buf_offset > buffer_size_need) { - buffer_size_need = mxlog_buffer_writer_.buf_offset; - } - - if (buffer_size_need > mxlog_buffer_size_) { - mxlog_buffer_size_ = buffer_size_need; - LOG_WAL_INFO_ << "recovery will need more buffer, buffer size changed " << mxlog_buffer_size_; - } - } - - buf_[0] = BufferPtr(new char[mxlog_buffer_size_]); - buf_[1] = BufferPtr(new char[mxlog_buffer_size_]); - - if (mxlog_buffer_reader_.file_no == mxlog_buffer_writer_.file_no) { - // read-write buffer - mxlog_buffer_reader_.buf_idx = 0; - mxlog_buffer_writer_.buf_idx = 0; - - mxlog_writer_.SetFileName(ToFileName(mxlog_buffer_writer_.file_no)); - if (mxlog_buffer_writer_.buf_offset == 0) { - mxlog_writer_.SetFileOpenMode("w"); - - } else { - mxlog_writer_.SetFileOpenMode("r+"); - if (!mxlog_writer_.FileExists()) { - LOG_WAL_ERROR_ << "wal file not exist " << mxlog_buffer_writer_.file_no; - return false; - } - - auto read_offset = mxlog_buffer_reader_.buf_offset; - auto read_size = mxlog_buffer_writer_.buf_offset - mxlog_buffer_reader_.buf_offset; - if (!mxlog_writer_.Load(buf_[0].get() + read_offset, read_offset, read_size)) { - LOG_WAL_ERROR_ << "load wal file error " << read_offset << " " << read_size; - return false; - } - } - - } else { - // read buffer - mxlog_buffer_reader_.buf_idx = 0; - - MXLogFileHandler file_handler(mxlog_writer_.GetFilePath()); - file_handler.SetFileName(ToFileName(mxlog_buffer_reader_.file_no)); - file_handler.SetFileOpenMode("r"); - - auto read_offset = mxlog_buffer_reader_.buf_offset; - auto read_size = file_handler.Load(buf_[0].get() + read_offset, read_offset); - mxlog_buffer_reader_.max_offset = read_size + read_offset; - file_handler.CloseFile(); - - // write buffer - mxlog_buffer_writer_.buf_idx = 1; - - mxlog_writer_.SetFileName(ToFileName(mxlog_buffer_writer_.file_no)); - mxlog_writer_.SetFileOpenMode("r+"); - if (!mxlog_writer_.FileExists()) { - LOG_WAL_ERROR_ << "wal file not exist " << mxlog_buffer_writer_.file_no; - return false; - } - if (!mxlog_writer_.Load(buf_[1].get(), 0, mxlog_buffer_writer_.buf_offset)) { - LOG_WAL_ERROR_ << "load wal file error " << mxlog_buffer_writer_.file_no; - return false; - } - } - - SetFileNoFrom(mxlog_buffer_reader_.file_no); - - return true; -} - -void -MXLogBuffer::Reset(uint64_t lsn) { - LOG_WAL_DEBUG_ << "reset lsn " << lsn; - - buf_[0] = BufferPtr(new char[mxlog_buffer_size_]); - buf_[1] = BufferPtr(new char[mxlog_buffer_size_]); - - ParserLsn(lsn, mxlog_buffer_writer_.file_no, mxlog_buffer_writer_.buf_offset); - if (mxlog_buffer_writer_.buf_offset != 0) { - mxlog_buffer_writer_.file_no++; - mxlog_buffer_writer_.buf_offset = 0; - } - mxlog_buffer_writer_.buf_idx = 0; - - memcpy(&mxlog_buffer_reader_, &mxlog_buffer_writer_, sizeof(MXLogBufferHandler)); - - mxlog_writer_.CloseFile(); - mxlog_writer_.SetFileName(ToFileName(mxlog_buffer_writer_.file_no)); - mxlog_writer_.SetFileOpenMode("w"); - - SetFileNoFrom(mxlog_buffer_reader_.file_no); -} - -uint32_t -MXLogBuffer::GetBufferSize() { - return mxlog_buffer_size_; -} - -// buffer writer cares about surplus space of buffer -uint32_t -MXLogBuffer::SurplusSpace() { - return mxlog_buffer_size_ - mxlog_buffer_writer_.buf_offset; -} - -uint32_t -MXLogBuffer::RecordSize(const MXLogRecord& record) { - return SizeOfMXLogRecordHeader + (uint32_t)record.collection_id.size() + (uint32_t)record.partition_tag.size() + - record.length * (uint32_t)sizeof(IDNumber) + record.data_size; -} - -uint32_t -MXLogBuffer::EntityRecordSize(const milvus::engine::wal::MXLogRecord& record, uint32_t attr_num, - std::vector& field_name_size) { - uint32_t attr_header_size = 0; - attr_header_size += sizeof(uint32_t); - attr_header_size += attr_num * sizeof(uint64_t) * 3; - - uint32_t name_sizes = 0; - for (auto field_name : record.field_names) { - field_name_size.emplace_back(field_name.size()); - name_sizes += field_name.size(); - } - - uint64_t attr_size = 0; - auto attr_it = record.attr_data_size.begin(); - for (; attr_it != record.attr_data_size.end(); attr_it++) { - attr_size += attr_it->second; - } - - return RecordSize(record) + name_sizes + attr_size + attr_header_size; -} - -ErrorCode -MXLogBuffer::Append(MXLogRecord& record) { - uint32_t record_size = RecordSize(record); - if (SurplusSpace() < record_size) { - // writer buffer has no space, switch wal file and write to a new buffer - std::unique_lock lck(mutex_); - if (mxlog_buffer_writer_.buf_idx == mxlog_buffer_reader_.buf_idx) { - // swith writer buffer - mxlog_buffer_reader_.max_offset = mxlog_buffer_writer_.buf_offset; - mxlog_buffer_writer_.buf_idx ^= 1; - } - mxlog_buffer_writer_.file_no++; - mxlog_buffer_writer_.buf_offset = 0; - lck.unlock(); - - // Reborn means close old wal file and open new wal file - if (!mxlog_writer_.ReBorn(ToFileName(mxlog_buffer_writer_.file_no), "w")) { - LOG_WAL_ERROR_ << "ReBorn wal file error " << mxlog_buffer_writer_.file_no; - return WAL_FILE_ERROR; - } - } - - // point to the offset of current record in wal file - char* current_write_buf = buf_[mxlog_buffer_writer_.buf_idx].get(); - uint32_t current_write_offset = mxlog_buffer_writer_.buf_offset; - - MXLogRecordHeader head; - BuildLsn(mxlog_buffer_writer_.file_no, mxlog_buffer_writer_.buf_offset + (uint32_t)record_size, head.mxl_lsn); - head.mxl_type = (uint8_t)record.type; - head.collection_id_size = (uint16_t)record.collection_id.size(); - head.partition_tag_size = (uint16_t)record.partition_tag.size(); - head.vector_num = record.length; - head.data_size = record.data_size; - - memcpy(current_write_buf + current_write_offset, &head, SizeOfMXLogRecordHeader); - current_write_offset += SizeOfMXLogRecordHeader; - - if (!record.collection_id.empty()) { - memcpy(current_write_buf + current_write_offset, record.collection_id.data(), record.collection_id.size()); - current_write_offset += record.collection_id.size(); - } - - if (!record.partition_tag.empty()) { - memcpy(current_write_buf + current_write_offset, record.partition_tag.data(), record.partition_tag.size()); - current_write_offset += record.partition_tag.size(); - } - if (record.ids != nullptr && record.length > 0) { - memcpy(current_write_buf + current_write_offset, record.ids, record.length * sizeof(IDNumber)); - current_write_offset += record.length * sizeof(IDNumber); - } - - if (record.data != nullptr && record.data_size > 0) { - memcpy(current_write_buf + current_write_offset, record.data, record.data_size); - current_write_offset += record.data_size; - } - - bool write_rst = mxlog_writer_.Write(current_write_buf + mxlog_buffer_writer_.buf_offset, record_size); - if (!write_rst) { - LOG_WAL_ERROR_ << "write wal file error"; - return WAL_FILE_ERROR; - } - - mxlog_buffer_writer_.buf_offset = current_write_offset; - - record.lsn = head.mxl_lsn; - return WAL_SUCCESS; -} - -ErrorCode -MXLogBuffer::AppendEntity(milvus::engine::wal::MXLogRecord& record) { - std::vector field_name_size; - MXLogAttrRecordHeader attr_header; - attr_header.attr_num = 0; - for (auto name : record.field_names) { - attr_header.attr_num++; - attr_header.field_name_size.emplace_back(name.size()); - attr_header.attr_size.emplace_back(record.attr_data_size.at(name)); - attr_header.attr_nbytes.emplace_back(record.attr_nbytes.at(name)); - } - - uint32_t record_size = EntityRecordSize(record, attr_header.attr_num, field_name_size); - if (SurplusSpace() < record_size) { - // writer buffer has no space, switch wal file and write to a new buffer - std::unique_lock lck(mutex_); - if (mxlog_buffer_writer_.buf_idx == mxlog_buffer_reader_.buf_idx) { - // swith writer buffer - mxlog_buffer_reader_.max_offset = mxlog_buffer_writer_.buf_offset; - mxlog_buffer_writer_.buf_idx ^= 1; - } - mxlog_buffer_writer_.file_no++; - mxlog_buffer_writer_.buf_offset = 0; - lck.unlock(); - - // Reborn means close old wal file and open new wal file - if (!mxlog_writer_.ReBorn(ToFileName(mxlog_buffer_writer_.file_no), "w")) { - LOG_WAL_ERROR_ << "ReBorn wal file error " << mxlog_buffer_writer_.file_no; - return WAL_FILE_ERROR; - } - } - - // point to the offset of current record in wal file - char* current_write_buf = buf_[mxlog_buffer_writer_.buf_idx].get(); - uint32_t current_write_offset = mxlog_buffer_writer_.buf_offset; - - MXLogRecordHeader head; - BuildLsn(mxlog_buffer_writer_.file_no, mxlog_buffer_writer_.buf_offset + (uint32_t)record_size, head.mxl_lsn); - head.mxl_type = (uint8_t)record.type; - head.collection_id_size = (uint16_t)record.collection_id.size(); - head.partition_tag_size = (uint16_t)record.partition_tag.size(); - head.vector_num = record.length; - head.data_size = record.data_size; - - memcpy(current_write_buf + current_write_offset, &head, SizeOfMXLogRecordHeader); - current_write_offset += SizeOfMXLogRecordHeader; - - memcpy(current_write_buf + current_write_offset, &attr_header.attr_num, sizeof(int32_t)); - current_write_offset += sizeof(int32_t); - - memcpy(current_write_buf + current_write_offset, attr_header.field_name_size.data(), - sizeof(int64_t) * attr_header.attr_num); - current_write_offset += sizeof(int64_t) * attr_header.attr_num; - - memcpy(current_write_buf + current_write_offset, attr_header.attr_size.data(), - sizeof(int64_t) * attr_header.attr_num); - current_write_offset += sizeof(int64_t) * attr_header.attr_num; - - memcpy(current_write_buf + current_write_offset, attr_header.attr_nbytes.data(), - sizeof(int64_t) * attr_header.attr_num); - current_write_offset += sizeof(int64_t) * attr_header.attr_num; - - if (!record.collection_id.empty()) { - memcpy(current_write_buf + current_write_offset, record.collection_id.data(), record.collection_id.size()); - current_write_offset += record.collection_id.size(); - } - - if (!record.partition_tag.empty()) { - memcpy(current_write_buf + current_write_offset, record.partition_tag.data(), record.partition_tag.size()); - current_write_offset += record.partition_tag.size(); - } - if (record.ids != nullptr && record.length > 0) { - memcpy(current_write_buf + current_write_offset, record.ids, record.length * sizeof(IDNumber)); - current_write_offset += record.length * sizeof(IDNumber); - } - - if (record.data != nullptr && record.data_size > 0) { - memcpy(current_write_buf + current_write_offset, record.data, record.data_size); - current_write_offset += record.data_size; - } - - // Assign attr names - for (auto name : record.field_names) { - if (name.size() > 0) { - memcpy(current_write_buf + current_write_offset, name.data(), name.size()); - current_write_offset += name.size(); - } - } - - // Assign attr values - for (auto name : record.field_names) { - if (record.attr_data_size.at(name) != 0) { - memcpy(current_write_buf + current_write_offset, record.attr_data.at(name).data(), - record.attr_data_size.at(name)); - current_write_offset += record.attr_data_size.at(name); - } - } - - bool write_rst = mxlog_writer_.Write(current_write_buf + mxlog_buffer_writer_.buf_offset, record_size); - if (!write_rst) { - LOG_WAL_ERROR_ << "write wal file error"; - return WAL_FILE_ERROR; - } - - mxlog_buffer_writer_.buf_offset = current_write_offset; - - record.lsn = head.mxl_lsn; - return WAL_SUCCESS; -} - -ErrorCode -MXLogBuffer::Next(const uint64_t last_applied_lsn, MXLogRecord& record) { - // init output - record.type = MXLogType::None; - - // reader catch up to writer, no next record, read fail - if (GetReadLsn() >= last_applied_lsn) { - return WAL_SUCCESS; - } - - // otherwise, it means there must exists next record, in buffer or wal log - bool need_load_new = false; - std::unique_lock lck(mutex_); - if (mxlog_buffer_reader_.file_no != mxlog_buffer_writer_.file_no) { - if (mxlog_buffer_reader_.buf_offset == mxlog_buffer_reader_.max_offset) { // last record - mxlog_buffer_reader_.file_no++; - mxlog_buffer_reader_.buf_offset = 0; - need_load_new = (mxlog_buffer_reader_.file_no != mxlog_buffer_writer_.file_no); - if (!need_load_new) { - // read reach write buffer - mxlog_buffer_reader_.buf_idx = mxlog_buffer_writer_.buf_idx; - } - } - } - lck.unlock(); - - if (need_load_new) { - MXLogFileHandler mxlog_reader(mxlog_writer_.GetFilePath()); - mxlog_reader.SetFileName(ToFileName(mxlog_buffer_reader_.file_no)); - mxlog_reader.SetFileOpenMode("r"); - uint32_t file_size = mxlog_reader.Load(buf_[mxlog_buffer_reader_.buf_idx].get(), 0); - if (file_size == 0) { - LOG_WAL_ERROR_ << "load wal file error " << mxlog_buffer_reader_.file_no; - return WAL_FILE_ERROR; - } - mxlog_buffer_reader_.max_offset = file_size; - } - - char* current_read_buf = buf_[mxlog_buffer_reader_.buf_idx].get(); - uint64_t current_read_offset = mxlog_buffer_reader_.buf_offset; - - MXLogRecordHeader* head = (MXLogRecordHeader*)(current_read_buf + current_read_offset); - record.type = (MXLogType)head->mxl_type; - record.lsn = head->mxl_lsn; - record.length = head->vector_num; - record.data_size = head->data_size; - - current_read_offset += SizeOfMXLogRecordHeader; - - if (head->collection_id_size != 0) { - record.collection_id.assign(current_read_buf + current_read_offset, head->collection_id_size); - current_read_offset += head->collection_id_size; - } else { - record.collection_id = ""; - } - - if (head->partition_tag_size != 0) { - record.partition_tag.assign(current_read_buf + current_read_offset, head->partition_tag_size); - current_read_offset += head->partition_tag_size; - } else { - record.partition_tag = ""; - } - - if (head->vector_num != 0) { - record.ids = (IDNumber*)(current_read_buf + current_read_offset); - current_read_offset += head->vector_num * sizeof(IDNumber); - } else { - record.ids = nullptr; - } - - if (record.data_size != 0) { - record.data = current_read_buf + current_read_offset; - } else { - record.data = nullptr; - } - - mxlog_buffer_reader_.buf_offset = uint32_t(head->mxl_lsn & LSN_OFFSET_MASK); - return WAL_SUCCESS; -} - -ErrorCode -MXLogBuffer::NextEntity(const uint64_t last_applied_lsn, milvus::engine::wal::MXLogRecord& record) { - // init output - record.type = MXLogType::None; - - // reader catch up to writer, no next record, read fail - if (GetReadLsn() >= last_applied_lsn) { - return WAL_SUCCESS; - } - - // otherwise, it means there must exists next record, in buffer or wal log - bool need_load_new = false; - std::unique_lock lck(mutex_); - if (mxlog_buffer_reader_.file_no != mxlog_buffer_writer_.file_no) { - if (mxlog_buffer_reader_.buf_offset == mxlog_buffer_reader_.max_offset) { // last record - mxlog_buffer_reader_.file_no++; - mxlog_buffer_reader_.buf_offset = 0; - need_load_new = (mxlog_buffer_reader_.file_no != mxlog_buffer_writer_.file_no); - if (!need_load_new) { - // read reach write buffer - mxlog_buffer_reader_.buf_idx = mxlog_buffer_writer_.buf_idx; - } - } - } - lck.unlock(); - - if (need_load_new) { - MXLogFileHandler mxlog_reader(mxlog_writer_.GetFilePath()); - mxlog_reader.SetFileName(ToFileName(mxlog_buffer_reader_.file_no)); - mxlog_reader.SetFileOpenMode("r"); - uint32_t file_size = mxlog_reader.Load(buf_[mxlog_buffer_reader_.buf_idx].get(), 0); - if (file_size == 0) { - LOG_WAL_ERROR_ << "load wal file error " << mxlog_buffer_reader_.file_no; - return WAL_FILE_ERROR; - } - mxlog_buffer_reader_.max_offset = file_size; - } - - char* current_read_buf = buf_[mxlog_buffer_reader_.buf_idx].get(); - uint64_t current_read_offset = mxlog_buffer_reader_.buf_offset; - - MXLogRecordHeader* head = (MXLogRecordHeader*)(current_read_buf + current_read_offset); - - record.type = (MXLogType)head->mxl_type; - record.lsn = head->mxl_lsn; - record.length = head->vector_num; - record.data_size = head->data_size; - - current_read_offset += SizeOfMXLogRecordHeader; - - MXLogAttrRecordHeader attr_head; - - memcpy(&attr_head.attr_num, current_read_buf + current_read_offset, sizeof(uint32_t)); - current_read_offset += sizeof(uint32_t); - - attr_head.attr_size.resize(attr_head.attr_num); - attr_head.field_name_size.resize(attr_head.attr_num); - attr_head.attr_nbytes.resize(attr_head.attr_num); - memcpy(attr_head.field_name_size.data(), current_read_buf + current_read_offset, - sizeof(uint64_t) * attr_head.attr_num); - current_read_offset += sizeof(uint64_t) * attr_head.attr_num; - - memcpy(attr_head.attr_size.data(), current_read_buf + current_read_offset, sizeof(uint64_t) * attr_head.attr_num); - current_read_offset += sizeof(uint64_t) * attr_head.attr_num; - - memcpy(attr_head.attr_nbytes.data(), current_read_buf + current_read_offset, sizeof(uint64_t) * attr_head.attr_num); - current_read_offset += sizeof(uint64_t) * attr_head.attr_num; - - if (head->collection_id_size != 0) { - record.collection_id.assign(current_read_buf + current_read_offset, head->collection_id_size); - current_read_offset += head->collection_id_size; - } else { - record.collection_id = ""; - } - - if (head->partition_tag_size != 0) { - record.partition_tag.assign(current_read_buf + current_read_offset, head->partition_tag_size); - current_read_offset += head->partition_tag_size; - } else { - record.partition_tag = ""; - } - - if (head->vector_num != 0) { - record.ids = (IDNumber*)(current_read_buf + current_read_offset); - current_read_offset += head->vector_num * sizeof(IDNumber); - } else { - record.ids = nullptr; - } - - if (record.data_size != 0) { - record.data = current_read_buf + current_read_offset; - current_read_offset += record.data_size; - } else { - record.data = nullptr; - } - - // Read field names - auto attr_num = attr_head.attr_num; - record.field_names.clear(); - if (attr_num > 0) { - for (auto size : attr_head.field_name_size) { - if (size != 0) { - std::string name; - name.assign(current_read_buf + current_read_offset, size); - record.field_names.emplace_back(name); - current_read_offset += size; - } else { - record.field_names.emplace_back(""); - } - } - } - - // Read attributes data - record.attr_data.clear(); - record.attr_data_size.clear(); - record.attr_nbytes.clear(); - if (attr_num > 0) { - for (uint64_t i = 0; i < attr_num; ++i) { - auto attr_size = attr_head.attr_size[i]; - record.attr_data_size.insert(std::make_pair(record.field_names[i], attr_size)); - record.attr_nbytes.insert(std::make_pair(record.field_names[i], attr_head.attr_nbytes[i])); - std::vector data(attr_size); - memcpy(data.data(), current_read_buf + current_read_offset, attr_size); - record.attr_data.insert(std::make_pair(record.field_names[i], data)); - current_read_offset += attr_size; - } - } - - mxlog_buffer_reader_.buf_offset = uint32_t(head->mxl_lsn & LSN_OFFSET_MASK); - return WAL_SUCCESS; -} - -uint64_t -MXLogBuffer::GetReadLsn() { - uint64_t read_lsn; - BuildLsn(mxlog_buffer_reader_.file_no, mxlog_buffer_reader_.buf_offset, read_lsn); - return read_lsn; -} - -bool -MXLogBuffer::ResetWriteLsn(uint64_t lsn) { - LOG_WAL_INFO_ << "reset write lsn " << lsn; - - uint32_t old_file_no = mxlog_buffer_writer_.file_no; - ParserLsn(lsn, mxlog_buffer_writer_.file_no, mxlog_buffer_writer_.buf_offset); - if (old_file_no == mxlog_buffer_writer_.file_no) { - LOG_WAL_DEBUG_ << "file No. is not changed"; - return true; - } - - std::unique_lock lck(mutex_); - if (mxlog_buffer_writer_.file_no == mxlog_buffer_reader_.file_no) { - mxlog_buffer_writer_.buf_idx = mxlog_buffer_reader_.buf_idx; - LOG_WAL_DEBUG_ << "file No. is the same as reader"; - return true; - } - lck.unlock(); - - if (!mxlog_writer_.ReBorn(ToFileName(mxlog_buffer_writer_.file_no), "r+")) { - LOG_WAL_ERROR_ << "reborn file error " << mxlog_buffer_writer_.file_no; - return false; - } - if (!mxlog_writer_.Load(buf_[mxlog_buffer_writer_.buf_idx].get(), 0, mxlog_buffer_writer_.buf_offset)) { - LOG_WAL_ERROR_ << "load file error"; - return false; - } - - return true; -} - -void -MXLogBuffer::SetFileNoFrom(uint32_t file_no) { - file_no_from_ = file_no; - - if (file_no > 0) { - // remove the files whose No. are less than file_no - MXLogFileHandler file_handler(mxlog_writer_.GetFilePath()); - do { - file_handler.SetFileName(ToFileName(--file_no)); - if (!file_handler.FileExists()) { - break; - } - LOG_WAL_INFO_ << "Delete wal file " << file_no; - file_handler.DeleteFile(); - } while (file_no > 0); - } -} - -void -MXLogBuffer::RemoveOldFiles(uint64_t flushed_lsn) { - uint32_t file_no; - uint32_t offset; - ParserLsn(flushed_lsn, file_no, offset); - if (file_no_from_ < file_no) { - MXLogFileHandler file_handler(mxlog_writer_.GetFilePath()); - do { - file_handler.SetFileName(ToFileName(file_no_from_)); - LOG_WAL_INFO_ << "Delete wal file " << file_no_from_; - file_handler.DeleteFile(); - } while (++file_no_from_ < file_no); - } -} - -} // namespace wal -} // namespace engine -} // namespace milvus diff --git a/core/src/db/wal/WalBuffer.h b/core/src/db/wal/WalBuffer.h deleted file mode 100644 index 9e421b84a2..0000000000 --- a/core/src/db/wal/WalBuffer.h +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include - -#include "WalDefinations.h" -#include "WalFileHandler.h" -#include "WalMetaHandler.h" -#include "utils/Error.h" - -namespace milvus { -namespace engine { -namespace wal { - -#pragma pack(push) -#pragma pack(1) - -struct MXLogRecordHeader { - uint64_t mxl_lsn; // log sequence number (high 32 bits: file No. inc by 1, low 32 bits: offset in file, max 4GB) - uint8_t mxl_type; // record type, insert/delete/update/flush... - uint16_t collection_id_size; - uint16_t partition_tag_size; - uint32_t vector_num; - uint32_t data_size; -}; - -const uint32_t SizeOfMXLogRecordHeader = sizeof(MXLogRecordHeader); - -struct MXLogAttrRecordHeader { - uint32_t attr_num; - std::vector field_name_size; - std::vector attr_size; - std::vector attr_nbytes; -}; - -#pragma pack(pop) - -struct MXLogBufferHandler { - uint32_t max_offset; - uint32_t file_no; - uint32_t buf_offset; - uint8_t buf_idx; -}; - -using BufferPtr = std::shared_ptr; - -class MXLogBuffer { - public: - MXLogBuffer(const std::string& mxlog_path, const uint32_t buffer_size); - ~MXLogBuffer(); - - bool - Init(uint64_t read_lsn, uint64_t write_lsn); - - // ignore all old wal file - void - Reset(uint64_t lsn); - - // Note: record.lsn will be set inner - ErrorCode - Append(MXLogRecord& record); - - ErrorCode - AppendEntity(MXLogRecord& record); - - ErrorCode - Next(const uint64_t last_applied_lsn, MXLogRecord& record); - - ErrorCode - NextEntity(const uint64_t last_applied_lsn, MXLogRecord& record); - - uint64_t - GetReadLsn(); - - bool - ResetWriteLsn(uint64_t lsn); - - void - SetFileNoFrom(uint32_t file_no); - - void - RemoveOldFiles(uint64_t flushed_lsn); - - uint32_t - GetBufferSize(); - - uint32_t - SurplusSpace(); - - private: - uint32_t - RecordSize(const MXLogRecord& record); - - uint32_t - EntityRecordSize(const milvus::engine::wal::MXLogRecord& record, uint32_t attr_num, - std::vector& field_name_size); - - private: - uint32_t mxlog_buffer_size_; // from config - BufferPtr buf_[2]; - std::mutex mutex_; - uint32_t file_no_from_; - MXLogBufferHandler mxlog_buffer_reader_; - MXLogBufferHandler mxlog_buffer_writer_; - MXLogFileHandler mxlog_writer_; -}; - -using MXLogBufferPtr = std::shared_ptr; - -} // namespace wal -} // namespace engine -} // namespace milvus diff --git a/core/src/db/wal/WalDefinations.h b/core/src/db/wal/WalDefinations.h deleted file mode 100644 index 6808e90224..0000000000 --- a/core/src/db/wal/WalDefinations.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include - -#include "db/Types.h" -#include "db/meta/MetaTypes.h" - -namespace milvus { -namespace engine { -namespace wal { - -using TableSchemaPtr = std::shared_ptr; -using TableMetaPtr = std::shared_ptr>; - -#define UNIT_MB (1024 * 1024) -#define UNIT_B 1 -#define LSN_OFFSET_MASK 0x00000000ffffffff - -enum class MXLogType { None, InsertBinary, InsertVector, Delete, Update, Flush, Entity }; - -struct MXLogRecord { - uint64_t lsn; - MXLogType type; - std::string collection_id; - std::string partition_tag; - uint32_t length; - const IDNumber* ids; - uint32_t data_size; - const void* data; - std::vector field_names; - // std::vector attrs_size; - // std::vector attrs_data; - std::unordered_map attr_nbytes; - std::unordered_map attr_data_size; - std::unordered_map> attr_data; -}; - -struct MXLogConfiguration { - bool recovery_error_ignore; - uint32_t buffer_size; - std::string mxlog_path; -}; - -} // namespace wal -} // namespace engine -} // namespace milvus diff --git a/core/src/db/wal/WalFileHandler.cpp b/core/src/db/wal/WalFileHandler.cpp deleted file mode 100644 index c5719cc248..0000000000 --- a/core/src/db/wal/WalFileHandler.cpp +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/wal/WalFileHandler.h" - -#include -#include - -namespace milvus { -namespace engine { -namespace wal { - -MXLogFileHandler::MXLogFileHandler(const std::string& mxlog_path) : file_path_(mxlog_path), p_file_(nullptr) { -} - -MXLogFileHandler::~MXLogFileHandler() { - CloseFile(); -} - -bool -MXLogFileHandler::OpenFile() { - if (p_file_ == nullptr) { - p_file_ = fopen((file_path_ + file_name_).c_str(), file_mode_.c_str()); - } - return (p_file_ != nullptr); -} - -uint32_t -MXLogFileHandler::Load(char* buf, uint32_t data_offset) { - uint32_t read_size = 0; - if (OpenFile()) { - uint32_t file_size = GetFileSize(); - if (file_size > data_offset) { - read_size = file_size - data_offset; - fseek(p_file_, data_offset, SEEK_SET); - auto ret = fread(buf, 1, read_size, p_file_); - __glibcxx_assert(ret == read_size); - } - } - return read_size; -} - -bool -MXLogFileHandler::Load(char* buf, uint32_t data_offset, uint32_t data_size) { - if (OpenFile() && data_size != 0) { - auto file_size = GetFileSize(); - if ((file_size < data_offset) || (file_size - data_offset < data_size)) { - return false; - } - - fseek(p_file_, data_offset, SEEK_SET); - auto ret = fread(buf, 1, data_size, p_file_); - __glibcxx_assert(ret == data_size); - } - return true; -} - -bool -MXLogFileHandler::Write(char* buf, uint32_t data_size, bool is_sync) { - uint32_t written_size = 0; - if (OpenFile() && data_size != 0) { - written_size = fwrite(buf, 1, data_size, p_file_); - fflush(p_file_); - } - return (written_size == data_size); -} - -bool -MXLogFileHandler::ReBorn(const std::string& file_name, const std::string& open_mode) { - CloseFile(); - SetFileName(file_name); - SetFileOpenMode(open_mode); - return OpenFile(); -} - -bool -MXLogFileHandler::CloseFile() { - if (p_file_ != nullptr) { - fclose(p_file_); - p_file_ = nullptr; - } - return true; -} - -std::string -MXLogFileHandler::GetFilePath() { - return file_path_; -} - -std::string -MXLogFileHandler::GetFileName() { - return file_name_; -} - -uint32_t -MXLogFileHandler::GetFileSize() { - struct stat statbuf; - if (0 == stat((file_path_ + file_name_).c_str(), &statbuf)) { - return (uint32_t)statbuf.st_size; - } - - return 0; -} - -void -MXLogFileHandler::DeleteFile() { - remove((file_path_ + file_name_).c_str()); - file_name_ = ""; -} - -bool -MXLogFileHandler::FileExists() { - return access((file_path_ + file_name_).c_str(), 0) != -1; -} - -void -MXLogFileHandler::SetFileOpenMode(const std::string& open_mode) { - file_mode_ = open_mode; -} - -void -MXLogFileHandler::SetFileName(const std::string& file_name) { - file_name_ = file_name; -} - -void -MXLogFileHandler::SetFilePath(const std::string& file_path) { - file_path_ = file_path; -} - -} // namespace wal -} // namespace engine -} // namespace milvus diff --git a/core/src/db/wal/WalFileHandler.h b/core/src/db/wal/WalFileHandler.h deleted file mode 100644 index 5eb2add2e7..0000000000 --- a/core/src/db/wal/WalFileHandler.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -#include "WalDefinations.h" - -namespace milvus { -namespace engine { -namespace wal { - -class MXLogFileHandler { - public: - explicit MXLogFileHandler(const std::string& mxlog_path); - ~MXLogFileHandler(); - - std::string - GetFilePath(); - std::string - GetFileName(); - bool - OpenFile(); - bool - CloseFile(); - uint32_t - Load(char* buf, uint32_t data_offset); - bool - Load(char* buf, uint32_t data_offset, uint32_t data_size); - bool - Write(char* buf, uint32_t data_size, bool is_sync = false); - bool - ReBorn(const std::string& file_name, const std::string& open_mode); - uint32_t - GetFileSize(); - void - SetFileOpenMode(const std::string& open_mode); - void - SetFilePath(const std::string& file_path); - void - SetFileName(const std::string& file_name); - void - DeleteFile(); - bool - FileExists(); - - private: - std::string file_path_; - std::string file_name_; - std::string file_mode_; - FILE* p_file_; -}; - -} // namespace wal -} // namespace engine -} // namespace milvus diff --git a/core/src/db/wal/WalManager.cpp b/core/src/db/wal/WalManager.cpp deleted file mode 100644 index 6d32b66837..0000000000 --- a/core/src/db/wal/WalManager.cpp +++ /dev/null @@ -1,696 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/wal/WalManager.h" - -#include - -#include -#include -#include - -#include "config/Config.h" -#include "utils/CommonUtil.h" -#include "utils/Exception.h" -#include "utils/Log.h" - -namespace milvus { -namespace engine { -namespace wal { - -WalManager::WalManager(const MXLogConfiguration& config) { - __glibcxx_assert(config.buffer_size <= milvus::server::CONFIG_WAL_BUFFER_SIZE_MAX / 2); - __glibcxx_assert(config.buffer_size >= milvus::server::CONFIG_WAL_BUFFER_SIZE_MIN / 2); - - mxlog_config_.recovery_error_ignore = config.recovery_error_ignore; - mxlog_config_.buffer_size = config.buffer_size; - mxlog_config_.mxlog_path = config.mxlog_path; - - // check the path end with '/' - if (mxlog_config_.mxlog_path.back() != '/') { - mxlog_config_.mxlog_path += '/'; - } - // check path exist - auto status = server::CommonUtil::CreateDirectory(mxlog_config_.mxlog_path); - if (!status.ok()) { - std::string msg = "failed to create wal directory " + mxlog_config_.mxlog_path; - LOG_ENGINE_ERROR_ << msg; - throw Exception(WAL_PATH_ERROR, msg); - } -} - -WalManager::~WalManager() { -} - -ErrorCode -WalManager::Init(const meta::MetaPtr& meta) { - uint64_t applied_lsn = 0; - p_meta_handler_ = std::make_shared(mxlog_config_.mxlog_path); - if (p_meta_handler_ != nullptr) { - p_meta_handler_->GetMXLogInternalMeta(applied_lsn); - } - - uint64_t recovery_start = 0; - if (meta != nullptr) { - meta->GetGlobalLastLSN(recovery_start); - - std::vector collention_schema_array; - auto status = meta->AllCollections(collention_schema_array); - if (!status.ok()) { - return WAL_META_ERROR; - } - - if (!collention_schema_array.empty()) { - u_int64_t min_flushed_lsn = ~(u_int64_t)0; - u_int64_t max_flushed_lsn = 0; - auto update_limit_lsn = [&](u_int64_t lsn) { - if (min_flushed_lsn > lsn) { - min_flushed_lsn = lsn; - } - if (max_flushed_lsn < lsn) { - max_flushed_lsn = lsn; - } - }; - - for (auto& col_schema : collention_schema_array) { - auto& collection = collections_[col_schema.collection_id_]; - auto& default_part = collection[""]; - default_part.flush_lsn = col_schema.flush_lsn_; - update_limit_lsn(default_part.flush_lsn); - - std::vector partition_schema_array; - status = meta->ShowPartitions(col_schema.collection_id_, partition_schema_array); - if (!status.ok()) { - return WAL_META_ERROR; - } - for (auto& par_schema : partition_schema_array) { - auto& partition = collection[par_schema.partition_tag_]; - partition.flush_lsn = par_schema.flush_lsn_; - update_limit_lsn(partition.flush_lsn); - } - } - - if (applied_lsn < max_flushed_lsn) { - // a new WAL folder? - applied_lsn = max_flushed_lsn; - } - if (recovery_start < min_flushed_lsn) { - // not flush all yet - recovery_start = min_flushed_lsn; - } - - for (auto& col : collections_) { - for (auto& part : col.second) { - part.second.wal_lsn = applied_lsn; - } - } - } - } - - // all tables are droped and a new wal path? - if (applied_lsn < recovery_start) { - applied_lsn = recovery_start; - } - - ErrorCode error_code = WAL_ERROR; - p_buffer_ = std::make_shared(mxlog_config_.mxlog_path, mxlog_config_.buffer_size); - if (p_buffer_ != nullptr) { - if (p_buffer_->Init(recovery_start, applied_lsn)) { - error_code = WAL_SUCCESS; - } else if (mxlog_config_.recovery_error_ignore) { - p_buffer_->Reset(applied_lsn); - error_code = WAL_SUCCESS; - } else { - error_code = WAL_FILE_ERROR; - } - } - - // buffer size may changed - mxlog_config_.buffer_size = p_buffer_->GetBufferSize(); - - last_applied_lsn_ = applied_lsn; - return error_code; -} - -ErrorCode -WalManager::GetNextRecovery(MXLogRecord& record) { - ErrorCode error_code = WAL_SUCCESS; - while (true) { - error_code = p_buffer_->Next(last_applied_lsn_, record); - if (error_code != WAL_SUCCESS) { - if (mxlog_config_.recovery_error_ignore) { - // reset and break recovery - p_buffer_->Reset(last_applied_lsn_); - - record.type = MXLogType::None; - error_code = WAL_SUCCESS; - } - break; - } - if (record.type == MXLogType::None) { - break; - } - - // background thread has not started. - // so, needn't lock here. - auto it_col = collections_.find(record.collection_id); - if (it_col != collections_.end()) { - auto it_part = it_col->second.find(record.partition_tag); - if (it_part->second.flush_lsn < record.lsn) { - break; - } - } - } - - // print the log only when record.type != MXLogType::None - if (record.type != MXLogType::None) { - LOG_WAL_INFO_ << "record type " << (int32_t)record.type << " record lsn " << record.lsn << " error code " - << error_code; - } - - return error_code; -} - -ErrorCode -WalManager::GetNextEntityRecovery(milvus::engine::wal::MXLogRecord& record) { - ErrorCode error_code = WAL_SUCCESS; - while (true) { - error_code = p_buffer_->NextEntity(last_applied_lsn_, record); - if (error_code != WAL_SUCCESS) { - if (mxlog_config_.recovery_error_ignore) { - // reset and break recovery - p_buffer_->Reset(last_applied_lsn_); - - record.type = MXLogType::None; - error_code = WAL_SUCCESS; - } - break; - } - if (record.type == MXLogType::None) { - break; - } - - // background thread has not started. - // so, needn't lock here. - auto it_col = collections_.find(record.collection_id); - if (it_col != collections_.end()) { - auto it_part = it_col->second.find(record.partition_tag); - if (it_part->second.flush_lsn < record.lsn) { - break; - } - } - } - - // print the log only when record.type != MXLogType::None - if (record.type != MXLogType::None) { - LOG_WAL_INFO_ << "record type " << (int32_t)record.type << " record lsn " << record.lsn << " error code " - << error_code; - } - - return error_code; -} - -ErrorCode -WalManager::GetNextRecord(MXLogRecord& record) { - auto check_flush = [&]() -> bool { - std::lock_guard lck(mutex_); - if (flush_info_.IsValid()) { - if (p_buffer_->GetReadLsn() >= flush_info_.lsn_) { - // can exec flush requirement - record.type = MXLogType::Flush; - record.collection_id = flush_info_.collection_id_; - record.lsn = flush_info_.lsn_; - flush_info_.Clear(); - - LOG_WAL_INFO_ << "record flush collection " << record.collection_id << " lsn " << record.lsn; - return true; - } - } - return false; - }; - - if (check_flush()) { - return WAL_SUCCESS; - } - - ErrorCode error_code = WAL_SUCCESS; - while (WAL_SUCCESS == p_buffer_->Next(last_applied_lsn_, record)) { - if (record.type == MXLogType::None) { - if (check_flush()) { - return WAL_SUCCESS; - } - break; - } - - std::lock_guard lck(mutex_); - auto it_col = collections_.find(record.collection_id); - if (it_col != collections_.end()) { - auto it_part = it_col->second.find(record.partition_tag); - if (it_part->second.flush_lsn < record.lsn) { - break; - } - } - } - - if (record.type != MXLogType::None) { - LOG_WAL_INFO_ << "record type " << (int32_t)record.type << " collection " << record.collection_id << " lsn " - << record.lsn; - } - return error_code; -} - -ErrorCode -WalManager::GetNextEntityRecord(milvus::engine::wal::MXLogRecord& record) { - auto check_flush = [&]() -> bool { - std::lock_guard lck(mutex_); - if (flush_info_.IsValid()) { - if (p_buffer_->GetReadLsn() >= flush_info_.lsn_) { - // can exec flush requirement - record.type = MXLogType::Flush; - record.collection_id = flush_info_.collection_id_; - record.lsn = flush_info_.lsn_; - flush_info_.Clear(); - - LOG_WAL_INFO_ << "record flush collection " << record.collection_id << " lsn " << record.lsn; - return true; - } - } - return false; - }; - - if (check_flush()) { - return WAL_SUCCESS; - } - - ErrorCode error_code = WAL_SUCCESS; - while (WAL_SUCCESS == p_buffer_->NextEntity(last_applied_lsn_, record)) { - if (record.type == MXLogType::None) { - if (check_flush()) { - return WAL_SUCCESS; - } - break; - } - - std::lock_guard lck(mutex_); - auto it_col = collections_.find(record.collection_id); - if (it_col != collections_.end()) { - auto it_part = it_col->second.find(record.partition_tag); - if (it_part->second.flush_lsn < record.lsn) { - break; - } - } - } - - LOG_WAL_INFO_ << "record type " << (int32_t)record.type << " collection " << record.collection_id << " lsn " - << record.lsn; - return error_code; -} - -uint64_t -WalManager::CreateCollection(const std::string& collection_id) { - LOG_WAL_INFO_ << "create collection " << collection_id << " " << last_applied_lsn_; - std::lock_guard lck(mutex_); - uint64_t applied_lsn = last_applied_lsn_; - collections_[collection_id][""] = {applied_lsn, applied_lsn}; - return applied_lsn; -} - -uint64_t -WalManager::CreatePartition(const std::string& collection_id, const std::string& partition_tag) { - LOG_WAL_INFO_ << "create collection " << collection_id << " " << partition_tag << " " << last_applied_lsn_; - std::lock_guard lck(mutex_); - uint64_t applied_lsn = last_applied_lsn_; - collections_[collection_id][partition_tag] = {applied_lsn, applied_lsn}; - return applied_lsn; -} - -uint64_t -WalManager::CreateHybridCollection(const std::string& collection_id) { - LOG_WAL_INFO_ << "create hybrid collection " << collection_id << " " << last_applied_lsn_; - std::lock_guard lck(mutex_); - uint64_t applied_lsn = last_applied_lsn_; - collections_[collection_id][""] = {applied_lsn, applied_lsn}; - return applied_lsn; -} - -void -WalManager::DropCollection(const std::string& collection_id) { - LOG_WAL_INFO_ << "drop collection " << collection_id; - std::lock_guard lck(mutex_); - collections_.erase(collection_id); -} - -void -WalManager::DropPartition(const std::string& collection_id, const std::string& partition_tag) { - LOG_WAL_INFO_ << collection_id << " drop partition " << partition_tag; - std::lock_guard lck(mutex_); - auto it = collections_.find(collection_id); - if (it != collections_.end()) { - it->second.erase(partition_tag); - } -} - -void -WalManager::CollectionFlushed(const std::string& collection_id, uint64_t lsn) { - std::unique_lock lck(mutex_); - if (collection_id.empty()) { - // all collections - for (auto& col : collections_) { - for (auto& part : col.second) { - part.second.flush_lsn = lsn; - } - } - - } else { - // one collection - auto it_col = collections_.find(collection_id); - if (it_col != collections_.end()) { - for (auto& part : it_col->second) { - part.second.flush_lsn = lsn; - } - } - } - lck.unlock(); - - LOG_WAL_INFO_ << collection_id << " is flushed by lsn " << lsn; -} - -void -WalManager::PartitionFlushed(const std::string& collection_id, const std::string& partition_tag, uint64_t lsn) { - std::unique_lock lck(mutex_); - auto it_col = collections_.find(collection_id); - if (it_col != collections_.end()) { - auto it_part = it_col->second.find(partition_tag); - if (it_part != it_col->second.end()) { - it_part->second.flush_lsn = lsn; - } - } - lck.unlock(); - - LOG_WAL_INFO_ << collection_id << " " << partition_tag << " is flushed by lsn " << lsn; -} - -void -WalManager::CollectionUpdated(const std::string& collection_id, uint64_t lsn) { - std::unique_lock lck(mutex_); - auto it_col = collections_.find(collection_id); - if (it_col != collections_.end()) { - for (auto& part : it_col->second) { - part.second.wal_lsn = lsn; - } - } - lck.unlock(); -} - -void -WalManager::PartitionUpdated(const std::string& collection_id, const std::string& partition_tag, uint64_t lsn) { - std::unique_lock lck(mutex_); - auto it_col = collections_.find(collection_id); - if (it_col != collections_.end()) { - auto it_part = it_col->second.find(partition_tag); - if (it_part != it_col->second.end()) { - it_part->second.wal_lsn = lsn; - } - } - lck.unlock(); -} - -template -bool -WalManager::Insert(const std::string& collection_id, const std::string& partition_tag, const IDNumbers& vector_ids, - const std::vector& vectors) { - MXLogType log_type; - if (std::is_same::value) { - log_type = MXLogType::InsertVector; - } else if (std::is_same::value) { - log_type = MXLogType::InsertBinary; - } else { - return false; - } - - size_t vector_num = vector_ids.size(); - if (vector_num == 0) { - LOG_WAL_ERROR_ << LogOut("[%s][%ld] The ids is empty.", "insert", 0); - return false; - } - size_t dim = vectors.size() / vector_num; - size_t unit_size = dim * sizeof(T) + sizeof(IDNumber); - size_t head_size = SizeOfMXLogRecordHeader + collection_id.length() + partition_tag.length(); - - MXLogRecord record; - record.type = log_type; - record.collection_id = collection_id; - record.partition_tag = partition_tag; - - uint64_t new_lsn = 0; - for (size_t i = 0; i < vector_num; i += record.length) { - size_t surplus_space = p_buffer_->SurplusSpace(); - size_t max_rcd_num = 0; - if (surplus_space >= head_size + unit_size) { - max_rcd_num = (surplus_space - head_size) / unit_size; - } else { - max_rcd_num = (mxlog_config_.buffer_size - head_size) / unit_size; - } - if (max_rcd_num == 0) { - LOG_WAL_ERROR_ << LogOut("[%s][%ld]", "insert", 0) << "Wal buffer size is too small " - << mxlog_config_.buffer_size << " unit " << unit_size; - return false; - } - - record.length = std::min(vector_num - i, max_rcd_num); - record.ids = vector_ids.data() + i; - record.data_size = record.length * dim * sizeof(T); - record.data = vectors.data() + i * dim; - - auto error_code = p_buffer_->Append(record); - if (error_code != WAL_SUCCESS) { - p_buffer_->ResetWriteLsn(last_applied_lsn_); - return false; - } - new_lsn = record.lsn; - } - - last_applied_lsn_ = new_lsn; - PartitionUpdated(collection_id, partition_tag, new_lsn); - - LOG_WAL_INFO_ << LogOut("[%s][%ld]", "insert", 0) << collection_id << " insert in part " << partition_tag - << " with lsn " << new_lsn; - - return p_meta_handler_->SetMXLogInternalMeta(new_lsn); -} - -template -bool -WalManager::InsertEntities(const std::string& collection_id, const std::string& partition_tag, - const milvus::engine::IDNumbers& entity_ids, const std::vector& vectors, - const std::unordered_map& attr_nbytes, - const std::unordered_map>& attrs) { - MXLogType log_type; - if (std::is_same::value) { - log_type = MXLogType::Entity; - } else { - return false; - } - - size_t entity_num = entity_ids.size(); - if (entity_num == 0) { - LOG_WAL_ERROR_ << LogOut("[%s][%ld] The ids is empty.", "insert", 0); - return false; - } - size_t dim = vectors.size() / entity_num; - - MXLogRecord record; - - size_t attr_unit_size = 0; - auto attr_it = attr_nbytes.begin(); - for (; attr_it != attr_nbytes.end(); attr_it++) { - record.field_names.emplace_back(attr_it->first); - attr_unit_size += attr_it->second; - } - - size_t unit_size = dim * sizeof(T) + sizeof(IDNumber) + attr_unit_size; - size_t head_size = SizeOfMXLogRecordHeader + collection_id.length() + partition_tag.length(); - - // TODO(yukun): field_name put into MXLogRecord??? - - record.type = log_type; - record.collection_id = collection_id; - record.partition_tag = partition_tag; - record.attr_nbytes = attr_nbytes; - - uint64_t new_lsn = 0; - for (size_t i = 0; i < entity_num; i += record.length) { - size_t surplus_space = p_buffer_->SurplusSpace(); - size_t max_rcd_num = 0; - if (surplus_space >= head_size + unit_size) { - max_rcd_num = (surplus_space - head_size) / unit_size; - } else { - max_rcd_num = (mxlog_config_.buffer_size - head_size) / unit_size; - } - if (max_rcd_num == 0) { - LOG_WAL_ERROR_ << LogOut("[%s][%ld]", "insert", 0) << "Wal buffer size is too small " - << mxlog_config_.buffer_size << " unit " << unit_size; - return false; - } - - size_t length = std::min(entity_num - i, max_rcd_num); - record.length = length; - record.ids = entity_ids.data() + i; - record.data_size = record.length * dim * sizeof(T); - record.data = vectors.data() + i * dim; - - record.attr_data.clear(); - record.attr_data_size.clear(); - for (auto field_name : record.field_names) { - size_t attr_size = length * attr_nbytes.at(field_name); - record.attr_data_size.insert(std::make_pair(field_name, attr_size)); - std::vector attr_data(attr_size, 0); - memcpy(attr_data.data(), attrs.at(field_name).data() + i * attr_nbytes.at(field_name), attr_size); - record.attr_data.insert(std::make_pair(field_name, attr_data)); - } - - auto error_code = p_buffer_->AppendEntity(record); - if (error_code != WAL_SUCCESS) { - p_buffer_->ResetWriteLsn(last_applied_lsn_); - return false; - } - new_lsn = record.lsn; - } - - last_applied_lsn_ = new_lsn; - PartitionUpdated(collection_id, partition_tag, new_lsn); - - LOG_WAL_INFO_ << LogOut("[%s][%ld]", "insert", 0) << collection_id << " insert in part " << partition_tag - << " with lsn " << new_lsn; - - return p_meta_handler_->SetMXLogInternalMeta(new_lsn); -} - -bool -WalManager::DeleteById(const std::string& collection_id, const IDNumbers& vector_ids) { - size_t vector_num = vector_ids.size(); - if (vector_num == 0) { - LOG_WAL_ERROR_ << "The ids is empty."; - return false; - } - - size_t unit_size = sizeof(IDNumber); - size_t head_size = SizeOfMXLogRecordHeader + collection_id.length(); - - MXLogRecord record; - record.type = MXLogType::Delete; - record.collection_id = collection_id; - record.partition_tag = ""; - - uint64_t new_lsn = 0; - for (size_t i = 0; i < vector_num; i += record.length) { - size_t surplus_space = p_buffer_->SurplusSpace(); - size_t max_rcd_num = 0; - if (surplus_space >= head_size + unit_size) { - max_rcd_num = (surplus_space - head_size) / unit_size; - } else { - max_rcd_num = (mxlog_config_.buffer_size - head_size) / unit_size; - } - - record.length = std::min(vector_num - i, max_rcd_num); - record.ids = vector_ids.data() + i; - record.data_size = 0; - record.data = nullptr; - - auto error_code = p_buffer_->Append(record); - if (error_code != WAL_SUCCESS) { - p_buffer_->ResetWriteLsn(last_applied_lsn_); - return false; - } - new_lsn = record.lsn; - } - - last_applied_lsn_ = new_lsn; - CollectionUpdated(collection_id, new_lsn); - - LOG_WAL_INFO_ << collection_id << " delete rows by id, lsn " << new_lsn; - - return p_meta_handler_->SetMXLogInternalMeta(new_lsn); -} - -uint64_t -WalManager::Flush(const std::string& collection_id) { - std::lock_guard lck(mutex_); - // At most one flush requirement is waiting at any time. - // Otherwise, flush_info_ should be modified to a list. - __glibcxx_assert(!flush_info_.IsValid()); - - uint64_t lsn = 0; - if (collection_id.empty()) { - // flush all tables - for (auto& col : collections_) { - for (auto& part : col.second) { - if (part.second.wal_lsn > part.second.flush_lsn) { - lsn = last_applied_lsn_; - break; - } - } - } - - } else { - // flush one collection - auto it_col = collections_.find(collection_id); - if (it_col != collections_.end()) { - for (auto& part : it_col->second) { - auto wal_lsn = part.second.wal_lsn; - auto flush_lsn = part.second.flush_lsn; - if (wal_lsn > flush_lsn && wal_lsn > lsn) { - lsn = wal_lsn; - } - } - } - } - - if (lsn != 0) { - flush_info_.collection_id_ = collection_id; - flush_info_.lsn_ = lsn; - } - - LOG_WAL_INFO_ << collection_id << " want to be flush, lsn " << lsn; - - return lsn; -} - -void -WalManager::RemoveOldFiles(uint64_t flushed_lsn) { - if (p_buffer_ != nullptr) { - p_buffer_->RemoveOldFiles(flushed_lsn); - } -} - -template bool -WalManager::Insert(const std::string& collection_id, const std::string& partition_tag, - const IDNumbers& vector_ids, const std::vector& vectors); - -template bool -WalManager::Insert(const std::string& collection_id, const std::string& partition_tag, - const IDNumbers& vector_ids, const std::vector& vectors); - -template bool -WalManager::InsertEntities(const std::string& collection_id, const std::string& partition_tag, - const milvus::engine::IDNumbers& entity_ids, const std::vector& vectors, - const std::unordered_map& attr_nbytes, - const std::unordered_map>& attrs); - -template bool -WalManager::InsertEntities(const std::string& collection_id, const std::string& partition_tag, - const milvus::engine::IDNumbers& entity_ids, const std::vector& vectors, - const std::unordered_map& attr_nbytes, - const std::unordered_map>& attrs); - -} // namespace wal -} // namespace engine -} // namespace milvus diff --git a/core/src/db/wal/WalManager.h b/core/src/db/wal/WalManager.h deleted file mode 100644 index 04fd849575..0000000000 --- a/core/src/db/wal/WalManager.h +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "WalBuffer.h" -#include "WalDefinations.h" -#include "WalFileHandler.h" -#include "WalMetaHandler.h" -#include "utils/Error.h" - -namespace milvus { -namespace engine { -namespace wal { - -class WalManager { - public: - explicit WalManager(const MXLogConfiguration& config); - ~WalManager(); - - /* - * init - * @param meta - * @retval error_code - */ - ErrorCode - Init(const meta::MetaPtr& meta); - - /* - * Get next recovery - * @param record[out]: record - * @retval error_code - */ - ErrorCode - GetNextRecovery(MXLogRecord& record); - - ErrorCode - GetNextEntityRecovery(MXLogRecord& record); - - /* - * Get next record - * @param record[out]: record - * @retval error_code - */ - ErrorCode - GetNextRecord(MXLogRecord& record); - - ErrorCode - GetNextEntityRecord(MXLogRecord& record); - - /* - * Create collection - * @param collection_id: collection id - * @retval lsn - */ - uint64_t - CreateCollection(const std::string& collection_id); - - /* - * Create partition - * @param collection_id: collection id - * @param partition_tag: partition tag - * @retval lsn - */ - uint64_t - CreatePartition(const std::string& collection_id, const std::string& partition_tag); - - /* - * Create hybrid collection - * @param collection_id: collection id - * @retval lsn - */ - uint64_t - CreateHybridCollection(const std::string& collection_id); - - /* - * Drop collection - * @param collection_id: collection id - * @retval none - */ - void - DropCollection(const std::string& collection_id); - - /* - * Drop partition - * @param collection_id: collection id - * @param partition_tag: partition tag - * @retval none - */ - void - DropPartition(const std::string& collection_id, const std::string& partition_tag); - - /* - * Collection is flushed (update flushed_lsn) - * @param collection_id: collection id - * @param lsn: flushed lsn - */ - void - CollectionFlushed(const std::string& collection_id, uint64_t lsn); - - /* - * Partition is flushed (update flushed_lsn) - * @param collection_id: collection id - * @param partition_tag: partition_tag - * @param lsn: flushed lsn - */ - void - PartitionFlushed(const std::string& collection_id, const std::string& partition_tag, uint64_t lsn); - - /* - * Collection is updated (update wal_lsn) - * @param collection_id: collection id - * @param partition_tag: partition_tag - * @param lsn: flushed lsn - */ - void - CollectionUpdated(const std::string& collection_id, uint64_t lsn); - - /* - * Partition is updated (update wal_lsn) - * @param collection_id: collection id - * @param partition_tag: partition_tag - * @param lsn: flushed lsn - */ - void - PartitionUpdated(const std::string& collection_id, const std::string& partition_tag, uint64_t lsn); - - /* - * Insert - * @param collection_id: collection id - * @param collection_id: partition tag - * @param vector_ids: vector ids - * @param vectors: vectors - */ - template - bool - Insert(const std::string& collection_id, const std::string& partition_tag, const IDNumbers& vector_ids, - const std::vector& vectors); - - /* - * Insert - * @param collection_id: collection id - * @param partition_tag: partition tag - * @param vector_ids: vector ids - * @param vectors: vectors - * @param attrs: attributes - */ - template - bool - InsertEntities(const std::string& collection_id, const std::string& partition_tag, - const milvus::engine::IDNumbers& entity_ids, const std::vector& vectors, - const std::unordered_map& attr_nbytes, - const std::unordered_map>& attrs); - - /* - * Insert - * @param collection_id: collection id - * @param vector_ids: vector ids - */ - bool - DeleteById(const std::string& collection_id, const IDNumbers& vector_ids); - - /* - * Get flush lsn - * @param collection_id: collection id (empty means all tables) - * @retval if there is something not flushed, return lsn; - * else, return 0 - */ - uint64_t - Flush(const std::string& collection_id = ""); - - void - RemoveOldFiles(uint64_t flushed_lsn); - - private: - WalManager - operator=(WalManager&); - - MXLogConfiguration mxlog_config_; - - MXLogBufferPtr p_buffer_; - MXLogMetaHandlerPtr p_meta_handler_; - - struct TableLsn { - uint64_t flush_lsn; - uint64_t wal_lsn; - }; - std::mutex mutex_; - std::map> collections_; - std::atomic last_applied_lsn_; - - // if multi-thread call Flush(), use list - struct FlushInfo { - std::string collection_id_; - uint64_t lsn_ = 0; - - bool - IsValid() { - return (lsn_ != 0); - } - void - Clear() { - lsn_ = 0; - } - }; - FlushInfo flush_info_; -}; - -extern template bool -WalManager::Insert(const std::string& collection_id, const std::string& partition_tag, - const IDNumbers& vector_ids, const std::vector& vectors); - -extern template bool -WalManager::Insert(const std::string& collection_id, const std::string& partition_tag, - const IDNumbers& vector_ids, const std::vector& vectors); - -} // namespace wal -} // namespace engine -} // namespace milvus diff --git a/core/src/db/wal/WalMetaHandler.cpp b/core/src/db/wal/WalMetaHandler.cpp deleted file mode 100644 index d6a3483c77..0000000000 --- a/core/src/db/wal/WalMetaHandler.cpp +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "db/wal/WalMetaHandler.h" - -#include - -namespace milvus { -namespace engine { -namespace wal { - -const char* WAL_META_FILE_NAME = "mxlog.meta"; - -MXLogMetaHandler::MXLogMetaHandler(const std::string& internal_meta_file_path) { - std::string file_full_path = internal_meta_file_path + WAL_META_FILE_NAME; - - wal_meta_fp_ = fopen(file_full_path.c_str(), "r+"); - if (wal_meta_fp_ == nullptr) { - wal_meta_fp_ = fopen(file_full_path.c_str(), "w"); - - } else { - uint64_t all_wal_lsn[3] = {0, 0, 0}; - auto rt_val = fread(&all_wal_lsn, sizeof(all_wal_lsn), 1, wal_meta_fp_); - if (rt_val == 1) { - if (all_wal_lsn[2] == all_wal_lsn[1]) { - latest_wal_lsn_ = all_wal_lsn[2]; - } else { - latest_wal_lsn_ = all_wal_lsn[0]; - } - } - } -} - -MXLogMetaHandler::~MXLogMetaHandler() { - if (wal_meta_fp_ != nullptr) { - fclose(wal_meta_fp_); - wal_meta_fp_ = nullptr; - } -} - -bool -MXLogMetaHandler::GetMXLogInternalMeta(uint64_t& wal_lsn) { - wal_lsn = latest_wal_lsn_; - return true; -} - -bool -MXLogMetaHandler::SetMXLogInternalMeta(uint64_t wal_lsn) { - if (wal_meta_fp_ != nullptr) { - uint64_t all_wal_lsn[3] = {latest_wal_lsn_, wal_lsn, wal_lsn}; - fseek(wal_meta_fp_, 0, SEEK_SET); - auto rt_val = fwrite(&all_wal_lsn, sizeof(all_wal_lsn), 1, wal_meta_fp_); - if (rt_val == 1) { - fflush(wal_meta_fp_); - latest_wal_lsn_ = wal_lsn; - return true; - } - } - return false; -} - -} // namespace wal -} // namespace engine -} // namespace milvus diff --git a/core/src/db/wal/WalMetaHandler.h b/core/src/db/wal/WalMetaHandler.h deleted file mode 100644 index a43d6cd4c5..0000000000 --- a/core/src/db/wal/WalMetaHandler.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include - -#include "db/meta/Meta.h" -#include "db/meta/MetaFactory.h" -#include "db/meta/MetaTypes.h" -#include "db/wal/WalDefinations.h" -#include "db/wal/WalFileHandler.h" - -namespace milvus { -namespace engine { -namespace wal { - -extern const char* WAL_META_FILE_NAME; - -class MXLogMetaHandler { - public: - explicit MXLogMetaHandler(const std::string& internal_meta_file_path); - ~MXLogMetaHandler(); - - bool - GetMXLogInternalMeta(uint64_t& wal_lsn); - - bool - SetMXLogInternalMeta(uint64_t wal_lsn); - - private: - FILE* wal_meta_fp_; - uint64_t latest_wal_lsn_ = 0; -}; - -using MXLogMetaHandlerPtr = std::shared_ptr; - -} // namespace wal -} // namespace engine -} // namespace milvus diff --git a/core/src/grpc/cpp_gen.sh b/core/src/grpc/cpp_gen.sh deleted file mode 100755 index 62b9d95728..0000000000 --- a/core/src/grpc/cpp_gen.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -../../cmake-build-debug/grpc_ep-prefix/src/grpc_ep/bins/opt/protobuf/protoc -I . --grpc_out=./gen-status --plugin=protoc-gen-grpc="../../cmake-build-debug/grpc_ep-prefix/src/grpc_ep/bins/opt/grpc_cpp_plugin" status.proto - -../../cmake-build-debug/grpc_ep-prefix/src/grpc_ep/bins/opt/protobuf/protoc -I . --cpp_out=./gen-status status.proto - -../../cmake-build-debug/grpc_ep-prefix/src/grpc_ep/bins/opt/protobuf/protoc -I . --grpc_out=./gen-milvus --plugin=protoc-gen-grpc="../../cmake-build-debug/grpc_ep-prefix/src/grpc_ep/bins/opt/grpc_cpp_plugin" milvus.proto - -../../cmake-build-debug/grpc_ep-prefix/src/grpc_ep/bins/opt/protobuf/protoc -I . --cpp_out=./gen-milvus milvus.proto \ No newline at end of file diff --git a/core/src/grpc/gen-milvus/milvus.grpc.pb.cc b/core/src/grpc/gen-milvus/milvus.grpc.pb.cc deleted file mode 100644 index 81b5a99b98..0000000000 --- a/core/src/grpc/gen-milvus/milvus.grpc.pb.cc +++ /dev/null @@ -1,1723 +0,0 @@ -// Generated by the gRPC C++ plugin. -// If you make any local change, they will be lost. -// source: milvus.proto - -#include "milvus.pb.h" -#include "milvus.grpc.pb.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -namespace milvus { -namespace grpc { - -static const char* MilvusService_method_names[] = { - "/milvus.grpc.MilvusService/CreateCollection", - "/milvus.grpc.MilvusService/HasCollection", - "/milvus.grpc.MilvusService/DescribeCollection", - "/milvus.grpc.MilvusService/CountCollection", - "/milvus.grpc.MilvusService/ShowCollections", - "/milvus.grpc.MilvusService/ShowCollectionInfo", - "/milvus.grpc.MilvusService/DropCollection", - "/milvus.grpc.MilvusService/CreateIndex", - "/milvus.grpc.MilvusService/DescribeIndex", - "/milvus.grpc.MilvusService/DropIndex", - "/milvus.grpc.MilvusService/CreatePartition", - "/milvus.grpc.MilvusService/HasPartition", - "/milvus.grpc.MilvusService/ShowPartitions", - "/milvus.grpc.MilvusService/DropPartition", - "/milvus.grpc.MilvusService/Insert", - "/milvus.grpc.MilvusService/GetVectorsByID", - "/milvus.grpc.MilvusService/GetVectorIDs", - "/milvus.grpc.MilvusService/Search", - "/milvus.grpc.MilvusService/SearchByID", - "/milvus.grpc.MilvusService/SearchInFiles", - "/milvus.grpc.MilvusService/Cmd", - "/milvus.grpc.MilvusService/DeleteByID", - "/milvus.grpc.MilvusService/PreloadCollection", - "/milvus.grpc.MilvusService/ReloadSegments", - "/milvus.grpc.MilvusService/Flush", - "/milvus.grpc.MilvusService/Compact", - "/milvus.grpc.MilvusService/CreateHybridCollection", - "/milvus.grpc.MilvusService/HasHybridCollection", - "/milvus.grpc.MilvusService/DropHybridCollection", - "/milvus.grpc.MilvusService/DescribeHybridCollection", - "/milvus.grpc.MilvusService/CountHybridCollection", - "/milvus.grpc.MilvusService/ShowHybridCollections", - "/milvus.grpc.MilvusService/ShowHybridCollectionInfo", - "/milvus.grpc.MilvusService/PreloadHybridCollection", - "/milvus.grpc.MilvusService/InsertEntity", - "/milvus.grpc.MilvusService/HybridSearch", - "/milvus.grpc.MilvusService/HybridSearchInSegments", - "/milvus.grpc.MilvusService/GetEntityByID", - "/milvus.grpc.MilvusService/GetEntityIDs", - "/milvus.grpc.MilvusService/DeleteEntitiesByID", -}; - -std::unique_ptr< MilvusService::Stub> MilvusService::NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options) { - (void)options; - std::unique_ptr< MilvusService::Stub> stub(new MilvusService::Stub(channel)); - return stub; -} - -MilvusService::Stub::Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel) - : channel_(channel), rpcmethod_CreateCollection_(MilvusService_method_names[0], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_HasCollection_(MilvusService_method_names[1], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_DescribeCollection_(MilvusService_method_names[2], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_CountCollection_(MilvusService_method_names[3], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_ShowCollections_(MilvusService_method_names[4], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_ShowCollectionInfo_(MilvusService_method_names[5], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_DropCollection_(MilvusService_method_names[6], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_CreateIndex_(MilvusService_method_names[7], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_DescribeIndex_(MilvusService_method_names[8], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_DropIndex_(MilvusService_method_names[9], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_CreatePartition_(MilvusService_method_names[10], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_HasPartition_(MilvusService_method_names[11], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_ShowPartitions_(MilvusService_method_names[12], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_DropPartition_(MilvusService_method_names[13], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_Insert_(MilvusService_method_names[14], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_GetVectorsByID_(MilvusService_method_names[15], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_GetVectorIDs_(MilvusService_method_names[16], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_Search_(MilvusService_method_names[17], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_SearchByID_(MilvusService_method_names[18], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_SearchInFiles_(MilvusService_method_names[19], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_Cmd_(MilvusService_method_names[20], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_DeleteByID_(MilvusService_method_names[21], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_PreloadCollection_(MilvusService_method_names[22], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_ReloadSegments_(MilvusService_method_names[23], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_Flush_(MilvusService_method_names[24], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_Compact_(MilvusService_method_names[25], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_CreateHybridCollection_(MilvusService_method_names[26], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_HasHybridCollection_(MilvusService_method_names[27], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_DropHybridCollection_(MilvusService_method_names[28], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_DescribeHybridCollection_(MilvusService_method_names[29], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_CountHybridCollection_(MilvusService_method_names[30], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_ShowHybridCollections_(MilvusService_method_names[31], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_ShowHybridCollectionInfo_(MilvusService_method_names[32], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_PreloadHybridCollection_(MilvusService_method_names[33], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_InsertEntity_(MilvusService_method_names[34], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_HybridSearch_(MilvusService_method_names[35], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_HybridSearchInSegments_(MilvusService_method_names[36], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_GetEntityByID_(MilvusService_method_names[37], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_GetEntityIDs_(MilvusService_method_names[38], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - , rpcmethod_DeleteEntitiesByID_(MilvusService_method_names[39], ::grpc::internal::RpcMethod::NORMAL_RPC, channel) - {} - -::grpc::Status MilvusService::Stub::CreateCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionSchema& request, ::milvus::grpc::Status* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_CreateCollection_, context, request, response); -} - -void MilvusService::Stub::experimental_async::CreateCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionSchema* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_CreateCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::CreateCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_CreateCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::CreateCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionSchema* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_CreateCollection_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::CreateCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_CreateCollection_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::AsyncCreateCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionSchema& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_CreateCollection_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::PrepareAsyncCreateCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionSchema& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_CreateCollection_, context, request, false); -} - -::grpc::Status MilvusService::Stub::HasCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::BoolReply* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_HasCollection_, context, request, response); -} - -void MilvusService::Stub::experimental_async::HasCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::BoolReply* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_HasCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::HasCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::BoolReply* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_HasCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::HasCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::BoolReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_HasCollection_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::HasCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::BoolReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_HasCollection_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>* MilvusService::Stub::AsyncHasCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::BoolReply>::Create(channel_.get(), cq, rpcmethod_HasCollection_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>* MilvusService::Stub::PrepareAsyncHasCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::BoolReply>::Create(channel_.get(), cq, rpcmethod_HasCollection_, context, request, false); -} - -::grpc::Status MilvusService::Stub::DescribeCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::CollectionSchema* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_DescribeCollection_, context, request, response); -} - -void MilvusService::Stub::experimental_async::DescribeCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionSchema* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_DescribeCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::DescribeCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionSchema* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_DescribeCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::DescribeCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionSchema* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_DescribeCollection_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::DescribeCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionSchema* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_DescribeCollection_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionSchema>* MilvusService::Stub::AsyncDescribeCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::CollectionSchema>::Create(channel_.get(), cq, rpcmethod_DescribeCollection_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionSchema>* MilvusService::Stub::PrepareAsyncDescribeCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::CollectionSchema>::Create(channel_.get(), cq, rpcmethod_DescribeCollection_, context, request, false); -} - -::grpc::Status MilvusService::Stub::CountCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::CollectionRowCount* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_CountCollection_, context, request, response); -} - -void MilvusService::Stub::experimental_async::CountCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionRowCount* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_CountCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::CountCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionRowCount* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_CountCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::CountCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionRowCount* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_CountCollection_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::CountCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionRowCount* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_CountCollection_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionRowCount>* MilvusService::Stub::AsyncCountCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::CollectionRowCount>::Create(channel_.get(), cq, rpcmethod_CountCollection_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionRowCount>* MilvusService::Stub::PrepareAsyncCountCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::CollectionRowCount>::Create(channel_.get(), cq, rpcmethod_CountCollection_, context, request, false); -} - -::grpc::Status MilvusService::Stub::ShowCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::milvus::grpc::CollectionNameList* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_ShowCollections_, context, request, response); -} - -void MilvusService::Stub::experimental_async::ShowCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::CollectionNameList* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_ShowCollections_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::ShowCollections(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionNameList* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_ShowCollections_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::ShowCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::CollectionNameList* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_ShowCollections_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::ShowCollections(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionNameList* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_ShowCollections_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionNameList>* MilvusService::Stub::AsyncShowCollectionsRaw(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::CollectionNameList>::Create(channel_.get(), cq, rpcmethod_ShowCollections_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionNameList>* MilvusService::Stub::PrepareAsyncShowCollectionsRaw(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::CollectionNameList>::Create(channel_.get(), cq, rpcmethod_ShowCollections_, context, request, false); -} - -::grpc::Status MilvusService::Stub::ShowCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::CollectionInfo* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_ShowCollectionInfo_, context, request, response); -} - -void MilvusService::Stub::experimental_async::ShowCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionInfo* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_ShowCollectionInfo_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::ShowCollectionInfo(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionInfo* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_ShowCollectionInfo_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::ShowCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionInfo* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_ShowCollectionInfo_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::ShowCollectionInfo(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionInfo* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_ShowCollectionInfo_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionInfo>* MilvusService::Stub::AsyncShowCollectionInfoRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::CollectionInfo>::Create(channel_.get(), cq, rpcmethod_ShowCollectionInfo_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionInfo>* MilvusService::Stub::PrepareAsyncShowCollectionInfoRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::CollectionInfo>::Create(channel_.get(), cq, rpcmethod_ShowCollectionInfo_, context, request, false); -} - -::grpc::Status MilvusService::Stub::DropCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Status* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_DropCollection_, context, request, response); -} - -void MilvusService::Stub::experimental_async::DropCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_DropCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::DropCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_DropCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::DropCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_DropCollection_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::DropCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_DropCollection_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::AsyncDropCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_DropCollection_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::PrepareAsyncDropCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_DropCollection_, context, request, false); -} - -::grpc::Status MilvusService::Stub::CreateIndex(::grpc::ClientContext* context, const ::milvus::grpc::IndexParam& request, ::milvus::grpc::Status* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_CreateIndex_, context, request, response); -} - -void MilvusService::Stub::experimental_async::CreateIndex(::grpc::ClientContext* context, const ::milvus::grpc::IndexParam* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_CreateIndex_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::CreateIndex(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_CreateIndex_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::CreateIndex(::grpc::ClientContext* context, const ::milvus::grpc::IndexParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_CreateIndex_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::CreateIndex(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_CreateIndex_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::AsyncCreateIndexRaw(::grpc::ClientContext* context, const ::milvus::grpc::IndexParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_CreateIndex_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::PrepareAsyncCreateIndexRaw(::grpc::ClientContext* context, const ::milvus::grpc::IndexParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_CreateIndex_, context, request, false); -} - -::grpc::Status MilvusService::Stub::DescribeIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::IndexParam* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_DescribeIndex_, context, request, response); -} - -void MilvusService::Stub::experimental_async::DescribeIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::IndexParam* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_DescribeIndex_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::DescribeIndex(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::IndexParam* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_DescribeIndex_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::DescribeIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::IndexParam* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_DescribeIndex_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::DescribeIndex(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::IndexParam* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_DescribeIndex_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::IndexParam>* MilvusService::Stub::AsyncDescribeIndexRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::IndexParam>::Create(channel_.get(), cq, rpcmethod_DescribeIndex_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::IndexParam>* MilvusService::Stub::PrepareAsyncDescribeIndexRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::IndexParam>::Create(channel_.get(), cq, rpcmethod_DescribeIndex_, context, request, false); -} - -::grpc::Status MilvusService::Stub::DropIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Status* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_DropIndex_, context, request, response); -} - -void MilvusService::Stub::experimental_async::DropIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_DropIndex_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::DropIndex(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_DropIndex_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::DropIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_DropIndex_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::DropIndex(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_DropIndex_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::AsyncDropIndexRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_DropIndex_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::PrepareAsyncDropIndexRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_DropIndex_, context, request, false); -} - -::grpc::Status MilvusService::Stub::CreatePartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::milvus::grpc::Status* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_CreatePartition_, context, request, response); -} - -void MilvusService::Stub::experimental_async::CreatePartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_CreatePartition_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::CreatePartition(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_CreatePartition_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::CreatePartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_CreatePartition_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::CreatePartition(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_CreatePartition_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::AsyncCreatePartitionRaw(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_CreatePartition_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::PrepareAsyncCreatePartitionRaw(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_CreatePartition_, context, request, false); -} - -::grpc::Status MilvusService::Stub::HasPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::milvus::grpc::BoolReply* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_HasPartition_, context, request, response); -} - -void MilvusService::Stub::experimental_async::HasPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::BoolReply* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_HasPartition_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::HasPartition(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::BoolReply* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_HasPartition_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::HasPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::BoolReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_HasPartition_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::HasPartition(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::BoolReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_HasPartition_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>* MilvusService::Stub::AsyncHasPartitionRaw(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::BoolReply>::Create(channel_.get(), cq, rpcmethod_HasPartition_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>* MilvusService::Stub::PrepareAsyncHasPartitionRaw(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::BoolReply>::Create(channel_.get(), cq, rpcmethod_HasPartition_, context, request, false); -} - -::grpc::Status MilvusService::Stub::ShowPartitions(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::PartitionList* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_ShowPartitions_, context, request, response); -} - -void MilvusService::Stub::experimental_async::ShowPartitions(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::PartitionList* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_ShowPartitions_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::ShowPartitions(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::PartitionList* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_ShowPartitions_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::ShowPartitions(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::PartitionList* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_ShowPartitions_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::ShowPartitions(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::PartitionList* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_ShowPartitions_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::PartitionList>* MilvusService::Stub::AsyncShowPartitionsRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::PartitionList>::Create(channel_.get(), cq, rpcmethod_ShowPartitions_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::PartitionList>* MilvusService::Stub::PrepareAsyncShowPartitionsRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::PartitionList>::Create(channel_.get(), cq, rpcmethod_ShowPartitions_, context, request, false); -} - -::grpc::Status MilvusService::Stub::DropPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::milvus::grpc::Status* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_DropPartition_, context, request, response); -} - -void MilvusService::Stub::experimental_async::DropPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_DropPartition_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::DropPartition(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_DropPartition_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::DropPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_DropPartition_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::DropPartition(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_DropPartition_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::AsyncDropPartitionRaw(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_DropPartition_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::PrepareAsyncDropPartitionRaw(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_DropPartition_, context, request, false); -} - -::grpc::Status MilvusService::Stub::Insert(::grpc::ClientContext* context, const ::milvus::grpc::InsertParam& request, ::milvus::grpc::VectorIds* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_Insert_, context, request, response); -} - -void MilvusService::Stub::experimental_async::Insert(::grpc::ClientContext* context, const ::milvus::grpc::InsertParam* request, ::milvus::grpc::VectorIds* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_Insert_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::Insert(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::VectorIds* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_Insert_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::Insert(::grpc::ClientContext* context, const ::milvus::grpc::InsertParam* request, ::milvus::grpc::VectorIds* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_Insert_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::Insert(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::VectorIds* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_Insert_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorIds>* MilvusService::Stub::AsyncInsertRaw(::grpc::ClientContext* context, const ::milvus::grpc::InsertParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::VectorIds>::Create(channel_.get(), cq, rpcmethod_Insert_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorIds>* MilvusService::Stub::PrepareAsyncInsertRaw(::grpc::ClientContext* context, const ::milvus::grpc::InsertParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::VectorIds>::Create(channel_.get(), cq, rpcmethod_Insert_, context, request, false); -} - -::grpc::Status MilvusService::Stub::GetVectorsByID(::grpc::ClientContext* context, const ::milvus::grpc::VectorsIdentity& request, ::milvus::grpc::VectorsData* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_GetVectorsByID_, context, request, response); -} - -void MilvusService::Stub::experimental_async::GetVectorsByID(::grpc::ClientContext* context, const ::milvus::grpc::VectorsIdentity* request, ::milvus::grpc::VectorsData* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_GetVectorsByID_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::GetVectorsByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::VectorsData* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_GetVectorsByID_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::GetVectorsByID(::grpc::ClientContext* context, const ::milvus::grpc::VectorsIdentity* request, ::milvus::grpc::VectorsData* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_GetVectorsByID_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::GetVectorsByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::VectorsData* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_GetVectorsByID_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorsData>* MilvusService::Stub::AsyncGetVectorsByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::VectorsIdentity& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::VectorsData>::Create(channel_.get(), cq, rpcmethod_GetVectorsByID_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorsData>* MilvusService::Stub::PrepareAsyncGetVectorsByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::VectorsIdentity& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::VectorsData>::Create(channel_.get(), cq, rpcmethod_GetVectorsByID_, context, request, false); -} - -::grpc::Status MilvusService::Stub::GetVectorIDs(::grpc::ClientContext* context, const ::milvus::grpc::GetVectorIDsParam& request, ::milvus::grpc::VectorIds* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_GetVectorIDs_, context, request, response); -} - -void MilvusService::Stub::experimental_async::GetVectorIDs(::grpc::ClientContext* context, const ::milvus::grpc::GetVectorIDsParam* request, ::milvus::grpc::VectorIds* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_GetVectorIDs_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::GetVectorIDs(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::VectorIds* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_GetVectorIDs_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::GetVectorIDs(::grpc::ClientContext* context, const ::milvus::grpc::GetVectorIDsParam* request, ::milvus::grpc::VectorIds* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_GetVectorIDs_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::GetVectorIDs(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::VectorIds* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_GetVectorIDs_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorIds>* MilvusService::Stub::AsyncGetVectorIDsRaw(::grpc::ClientContext* context, const ::milvus::grpc::GetVectorIDsParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::VectorIds>::Create(channel_.get(), cq, rpcmethod_GetVectorIDs_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorIds>* MilvusService::Stub::PrepareAsyncGetVectorIDsRaw(::grpc::ClientContext* context, const ::milvus::grpc::GetVectorIDsParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::VectorIds>::Create(channel_.get(), cq, rpcmethod_GetVectorIDs_, context, request, false); -} - -::grpc::Status MilvusService::Stub::Search(::grpc::ClientContext* context, const ::milvus::grpc::SearchParam& request, ::milvus::grpc::TopKQueryResult* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_Search_, context, request, response); -} - -void MilvusService::Stub::experimental_async::Search(::grpc::ClientContext* context, const ::milvus::grpc::SearchParam* request, ::milvus::grpc::TopKQueryResult* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_Search_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::Search(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_Search_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::Search(::grpc::ClientContext* context, const ::milvus::grpc::SearchParam* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_Search_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::Search(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_Search_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* MilvusService::Stub::AsyncSearchRaw(::grpc::ClientContext* context, const ::milvus::grpc::SearchParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::TopKQueryResult>::Create(channel_.get(), cq, rpcmethod_Search_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* MilvusService::Stub::PrepareAsyncSearchRaw(::grpc::ClientContext* context, const ::milvus::grpc::SearchParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::TopKQueryResult>::Create(channel_.get(), cq, rpcmethod_Search_, context, request, false); -} - -::grpc::Status MilvusService::Stub::SearchByID(::grpc::ClientContext* context, const ::milvus::grpc::SearchByIDParam& request, ::milvus::grpc::TopKQueryResult* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_SearchByID_, context, request, response); -} - -void MilvusService::Stub::experimental_async::SearchByID(::grpc::ClientContext* context, const ::milvus::grpc::SearchByIDParam* request, ::milvus::grpc::TopKQueryResult* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_SearchByID_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::SearchByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_SearchByID_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::SearchByID(::grpc::ClientContext* context, const ::milvus::grpc::SearchByIDParam* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_SearchByID_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::SearchByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_SearchByID_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* MilvusService::Stub::AsyncSearchByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::SearchByIDParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::TopKQueryResult>::Create(channel_.get(), cq, rpcmethod_SearchByID_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* MilvusService::Stub::PrepareAsyncSearchByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::SearchByIDParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::TopKQueryResult>::Create(channel_.get(), cq, rpcmethod_SearchByID_, context, request, false); -} - -::grpc::Status MilvusService::Stub::SearchInFiles(::grpc::ClientContext* context, const ::milvus::grpc::SearchInFilesParam& request, ::milvus::grpc::TopKQueryResult* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_SearchInFiles_, context, request, response); -} - -void MilvusService::Stub::experimental_async::SearchInFiles(::grpc::ClientContext* context, const ::milvus::grpc::SearchInFilesParam* request, ::milvus::grpc::TopKQueryResult* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_SearchInFiles_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::SearchInFiles(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_SearchInFiles_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::SearchInFiles(::grpc::ClientContext* context, const ::milvus::grpc::SearchInFilesParam* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_SearchInFiles_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::SearchInFiles(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_SearchInFiles_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* MilvusService::Stub::AsyncSearchInFilesRaw(::grpc::ClientContext* context, const ::milvus::grpc::SearchInFilesParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::TopKQueryResult>::Create(channel_.get(), cq, rpcmethod_SearchInFiles_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* MilvusService::Stub::PrepareAsyncSearchInFilesRaw(::grpc::ClientContext* context, const ::milvus::grpc::SearchInFilesParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::TopKQueryResult>::Create(channel_.get(), cq, rpcmethod_SearchInFiles_, context, request, false); -} - -::grpc::Status MilvusService::Stub::Cmd(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::milvus::grpc::StringReply* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_Cmd_, context, request, response); -} - -void MilvusService::Stub::experimental_async::Cmd(::grpc::ClientContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::StringReply* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_Cmd_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::Cmd(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::StringReply* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_Cmd_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::Cmd(::grpc::ClientContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::StringReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_Cmd_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::Cmd(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::StringReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_Cmd_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::StringReply>* MilvusService::Stub::AsyncCmdRaw(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::StringReply>::Create(channel_.get(), cq, rpcmethod_Cmd_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::StringReply>* MilvusService::Stub::PrepareAsyncCmdRaw(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::StringReply>::Create(channel_.get(), cq, rpcmethod_Cmd_, context, request, false); -} - -::grpc::Status MilvusService::Stub::DeleteByID(::grpc::ClientContext* context, const ::milvus::grpc::DeleteByIDParam& request, ::milvus::grpc::Status* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_DeleteByID_, context, request, response); -} - -void MilvusService::Stub::experimental_async::DeleteByID(::grpc::ClientContext* context, const ::milvus::grpc::DeleteByIDParam* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_DeleteByID_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::DeleteByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_DeleteByID_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::DeleteByID(::grpc::ClientContext* context, const ::milvus::grpc::DeleteByIDParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_DeleteByID_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::DeleteByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_DeleteByID_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::AsyncDeleteByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::DeleteByIDParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_DeleteByID_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::PrepareAsyncDeleteByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::DeleteByIDParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_DeleteByID_, context, request, false); -} - -::grpc::Status MilvusService::Stub::PreloadCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Status* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_PreloadCollection_, context, request, response); -} - -void MilvusService::Stub::experimental_async::PreloadCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_PreloadCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::PreloadCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_PreloadCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::PreloadCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_PreloadCollection_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::PreloadCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_PreloadCollection_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::AsyncPreloadCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_PreloadCollection_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::PrepareAsyncPreloadCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_PreloadCollection_, context, request, false); -} - -::grpc::Status MilvusService::Stub::ReloadSegments(::grpc::ClientContext* context, const ::milvus::grpc::ReLoadSegmentsParam& request, ::milvus::grpc::Status* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_ReloadSegments_, context, request, response); -} - -void MilvusService::Stub::experimental_async::ReloadSegments(::grpc::ClientContext* context, const ::milvus::grpc::ReLoadSegmentsParam* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_ReloadSegments_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::ReloadSegments(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_ReloadSegments_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::ReloadSegments(::grpc::ClientContext* context, const ::milvus::grpc::ReLoadSegmentsParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_ReloadSegments_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::ReloadSegments(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_ReloadSegments_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::AsyncReloadSegmentsRaw(::grpc::ClientContext* context, const ::milvus::grpc::ReLoadSegmentsParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_ReloadSegments_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::PrepareAsyncReloadSegmentsRaw(::grpc::ClientContext* context, const ::milvus::grpc::ReLoadSegmentsParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_ReloadSegments_, context, request, false); -} - -::grpc::Status MilvusService::Stub::Flush(::grpc::ClientContext* context, const ::milvus::grpc::FlushParam& request, ::milvus::grpc::Status* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_Flush_, context, request, response); -} - -void MilvusService::Stub::experimental_async::Flush(::grpc::ClientContext* context, const ::milvus::grpc::FlushParam* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_Flush_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::Flush(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_Flush_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::Flush(::grpc::ClientContext* context, const ::milvus::grpc::FlushParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_Flush_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::Flush(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_Flush_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::AsyncFlushRaw(::grpc::ClientContext* context, const ::milvus::grpc::FlushParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_Flush_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::PrepareAsyncFlushRaw(::grpc::ClientContext* context, const ::milvus::grpc::FlushParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_Flush_, context, request, false); -} - -::grpc::Status MilvusService::Stub::Compact(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Status* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_Compact_, context, request, response); -} - -void MilvusService::Stub::experimental_async::Compact(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_Compact_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::Compact(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_Compact_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::Compact(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_Compact_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::Compact(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_Compact_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::AsyncCompactRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_Compact_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::PrepareAsyncCompactRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_Compact_, context, request, false); -} - -::grpc::Status MilvusService::Stub::CreateHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::Mapping& request, ::milvus::grpc::Status* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_CreateHybridCollection_, context, request, response); -} - -void MilvusService::Stub::experimental_async::CreateHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::Mapping* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_CreateHybridCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::CreateHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_CreateHybridCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::CreateHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::Mapping* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_CreateHybridCollection_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::CreateHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_CreateHybridCollection_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::AsyncCreateHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::Mapping& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_CreateHybridCollection_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::PrepareAsyncCreateHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::Mapping& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_CreateHybridCollection_, context, request, false); -} - -::grpc::Status MilvusService::Stub::HasHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::BoolReply* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_HasHybridCollection_, context, request, response); -} - -void MilvusService::Stub::experimental_async::HasHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::BoolReply* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_HasHybridCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::HasHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::BoolReply* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_HasHybridCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::HasHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::BoolReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_HasHybridCollection_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::HasHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::BoolReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_HasHybridCollection_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>* MilvusService::Stub::AsyncHasHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::BoolReply>::Create(channel_.get(), cq, rpcmethod_HasHybridCollection_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>* MilvusService::Stub::PrepareAsyncHasHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::BoolReply>::Create(channel_.get(), cq, rpcmethod_HasHybridCollection_, context, request, false); -} - -::grpc::Status MilvusService::Stub::DropHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Status* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_DropHybridCollection_, context, request, response); -} - -void MilvusService::Stub::experimental_async::DropHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_DropHybridCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::DropHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_DropHybridCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::DropHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_DropHybridCollection_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::DropHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_DropHybridCollection_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::AsyncDropHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_DropHybridCollection_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::PrepareAsyncDropHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_DropHybridCollection_, context, request, false); -} - -::grpc::Status MilvusService::Stub::DescribeHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Mapping* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_DescribeHybridCollection_, context, request, response); -} - -void MilvusService::Stub::experimental_async::DescribeHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Mapping* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_DescribeHybridCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::DescribeHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Mapping* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_DescribeHybridCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::DescribeHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Mapping* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_DescribeHybridCollection_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::DescribeHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Mapping* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_DescribeHybridCollection_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Mapping>* MilvusService::Stub::AsyncDescribeHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Mapping>::Create(channel_.get(), cq, rpcmethod_DescribeHybridCollection_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Mapping>* MilvusService::Stub::PrepareAsyncDescribeHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Mapping>::Create(channel_.get(), cq, rpcmethod_DescribeHybridCollection_, context, request, false); -} - -::grpc::Status MilvusService::Stub::CountHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::CollectionRowCount* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_CountHybridCollection_, context, request, response); -} - -void MilvusService::Stub::experimental_async::CountHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionRowCount* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_CountHybridCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::CountHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionRowCount* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_CountHybridCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::CountHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionRowCount* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_CountHybridCollection_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::CountHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionRowCount* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_CountHybridCollection_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionRowCount>* MilvusService::Stub::AsyncCountHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::CollectionRowCount>::Create(channel_.get(), cq, rpcmethod_CountHybridCollection_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionRowCount>* MilvusService::Stub::PrepareAsyncCountHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::CollectionRowCount>::Create(channel_.get(), cq, rpcmethod_CountHybridCollection_, context, request, false); -} - -::grpc::Status MilvusService::Stub::ShowHybridCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::milvus::grpc::MappingList* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_ShowHybridCollections_, context, request, response); -} - -void MilvusService::Stub::experimental_async::ShowHybridCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::MappingList* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_ShowHybridCollections_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::ShowHybridCollections(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::MappingList* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_ShowHybridCollections_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::ShowHybridCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::MappingList* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_ShowHybridCollections_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::ShowHybridCollections(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::MappingList* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_ShowHybridCollections_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::MappingList>* MilvusService::Stub::AsyncShowHybridCollectionsRaw(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::MappingList>::Create(channel_.get(), cq, rpcmethod_ShowHybridCollections_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::MappingList>* MilvusService::Stub::PrepareAsyncShowHybridCollectionsRaw(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::MappingList>::Create(channel_.get(), cq, rpcmethod_ShowHybridCollections_, context, request, false); -} - -::grpc::Status MilvusService::Stub::ShowHybridCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::CollectionInfo* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_ShowHybridCollectionInfo_, context, request, response); -} - -void MilvusService::Stub::experimental_async::ShowHybridCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionInfo* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_ShowHybridCollectionInfo_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::ShowHybridCollectionInfo(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionInfo* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_ShowHybridCollectionInfo_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::ShowHybridCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionInfo* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_ShowHybridCollectionInfo_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::ShowHybridCollectionInfo(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionInfo* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_ShowHybridCollectionInfo_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionInfo>* MilvusService::Stub::AsyncShowHybridCollectionInfoRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::CollectionInfo>::Create(channel_.get(), cq, rpcmethod_ShowHybridCollectionInfo_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionInfo>* MilvusService::Stub::PrepareAsyncShowHybridCollectionInfoRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::CollectionInfo>::Create(channel_.get(), cq, rpcmethod_ShowHybridCollectionInfo_, context, request, false); -} - -::grpc::Status MilvusService::Stub::PreloadHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Status* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_PreloadHybridCollection_, context, request, response); -} - -void MilvusService::Stub::experimental_async::PreloadHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_PreloadHybridCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::PreloadHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_PreloadHybridCollection_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::PreloadHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_PreloadHybridCollection_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::PreloadHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_PreloadHybridCollection_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::AsyncPreloadHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_PreloadHybridCollection_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::PrepareAsyncPreloadHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_PreloadHybridCollection_, context, request, false); -} - -::grpc::Status MilvusService::Stub::InsertEntity(::grpc::ClientContext* context, const ::milvus::grpc::HInsertParam& request, ::milvus::grpc::HEntityIDs* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_InsertEntity_, context, request, response); -} - -void MilvusService::Stub::experimental_async::InsertEntity(::grpc::ClientContext* context, const ::milvus::grpc::HInsertParam* request, ::milvus::grpc::HEntityIDs* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_InsertEntity_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::InsertEntity(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::HEntityIDs* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_InsertEntity_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::InsertEntity(::grpc::ClientContext* context, const ::milvus::grpc::HInsertParam* request, ::milvus::grpc::HEntityIDs* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_InsertEntity_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::InsertEntity(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::HEntityIDs* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_InsertEntity_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntityIDs>* MilvusService::Stub::AsyncInsertEntityRaw(::grpc::ClientContext* context, const ::milvus::grpc::HInsertParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::HEntityIDs>::Create(channel_.get(), cq, rpcmethod_InsertEntity_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntityIDs>* MilvusService::Stub::PrepareAsyncInsertEntityRaw(::grpc::ClientContext* context, const ::milvus::grpc::HInsertParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::HEntityIDs>::Create(channel_.get(), cq, rpcmethod_InsertEntity_, context, request, false); -} - -::grpc::Status MilvusService::Stub::HybridSearch(::grpc::ClientContext* context, const ::milvus::grpc::HSearchParam& request, ::milvus::grpc::TopKQueryResult* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_HybridSearch_, context, request, response); -} - -void MilvusService::Stub::experimental_async::HybridSearch(::grpc::ClientContext* context, const ::milvus::grpc::HSearchParam* request, ::milvus::grpc::TopKQueryResult* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_HybridSearch_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::HybridSearch(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_HybridSearch_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::HybridSearch(::grpc::ClientContext* context, const ::milvus::grpc::HSearchParam* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_HybridSearch_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::HybridSearch(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_HybridSearch_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* MilvusService::Stub::AsyncHybridSearchRaw(::grpc::ClientContext* context, const ::milvus::grpc::HSearchParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::TopKQueryResult>::Create(channel_.get(), cq, rpcmethod_HybridSearch_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* MilvusService::Stub::PrepareAsyncHybridSearchRaw(::grpc::ClientContext* context, const ::milvus::grpc::HSearchParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::TopKQueryResult>::Create(channel_.get(), cq, rpcmethod_HybridSearch_, context, request, false); -} - -::grpc::Status MilvusService::Stub::HybridSearchInSegments(::grpc::ClientContext* context, const ::milvus::grpc::HSearchInSegmentsParam& request, ::milvus::grpc::TopKQueryResult* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_HybridSearchInSegments_, context, request, response); -} - -void MilvusService::Stub::experimental_async::HybridSearchInSegments(::grpc::ClientContext* context, const ::milvus::grpc::HSearchInSegmentsParam* request, ::milvus::grpc::TopKQueryResult* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_HybridSearchInSegments_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::HybridSearchInSegments(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_HybridSearchInSegments_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::HybridSearchInSegments(::grpc::ClientContext* context, const ::milvus::grpc::HSearchInSegmentsParam* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_HybridSearchInSegments_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::HybridSearchInSegments(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_HybridSearchInSegments_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* MilvusService::Stub::AsyncHybridSearchInSegmentsRaw(::grpc::ClientContext* context, const ::milvus::grpc::HSearchInSegmentsParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::TopKQueryResult>::Create(channel_.get(), cq, rpcmethod_HybridSearchInSegments_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* MilvusService::Stub::PrepareAsyncHybridSearchInSegmentsRaw(::grpc::ClientContext* context, const ::milvus::grpc::HSearchInSegmentsParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::TopKQueryResult>::Create(channel_.get(), cq, rpcmethod_HybridSearchInSegments_, context, request, false); -} - -::grpc::Status MilvusService::Stub::GetEntityByID(::grpc::ClientContext* context, const ::milvus::grpc::HEntityIdentity& request, ::milvus::grpc::HEntity* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_GetEntityByID_, context, request, response); -} - -void MilvusService::Stub::experimental_async::GetEntityByID(::grpc::ClientContext* context, const ::milvus::grpc::HEntityIdentity* request, ::milvus::grpc::HEntity* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_GetEntityByID_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::GetEntityByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::HEntity* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_GetEntityByID_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::GetEntityByID(::grpc::ClientContext* context, const ::milvus::grpc::HEntityIdentity* request, ::milvus::grpc::HEntity* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_GetEntityByID_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::GetEntityByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::HEntity* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_GetEntityByID_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntity>* MilvusService::Stub::AsyncGetEntityByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::HEntityIdentity& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::HEntity>::Create(channel_.get(), cq, rpcmethod_GetEntityByID_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntity>* MilvusService::Stub::PrepareAsyncGetEntityByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::HEntityIdentity& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::HEntity>::Create(channel_.get(), cq, rpcmethod_GetEntityByID_, context, request, false); -} - -::grpc::Status MilvusService::Stub::GetEntityIDs(::grpc::ClientContext* context, const ::milvus::grpc::HGetEntityIDsParam& request, ::milvus::grpc::HEntityIDs* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_GetEntityIDs_, context, request, response); -} - -void MilvusService::Stub::experimental_async::GetEntityIDs(::grpc::ClientContext* context, const ::milvus::grpc::HGetEntityIDsParam* request, ::milvus::grpc::HEntityIDs* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_GetEntityIDs_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::GetEntityIDs(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::HEntityIDs* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_GetEntityIDs_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::GetEntityIDs(::grpc::ClientContext* context, const ::milvus::grpc::HGetEntityIDsParam* request, ::milvus::grpc::HEntityIDs* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_GetEntityIDs_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::GetEntityIDs(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::HEntityIDs* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_GetEntityIDs_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntityIDs>* MilvusService::Stub::AsyncGetEntityIDsRaw(::grpc::ClientContext* context, const ::milvus::grpc::HGetEntityIDsParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::HEntityIDs>::Create(channel_.get(), cq, rpcmethod_GetEntityIDs_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntityIDs>* MilvusService::Stub::PrepareAsyncGetEntityIDsRaw(::grpc::ClientContext* context, const ::milvus::grpc::HGetEntityIDsParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::HEntityIDs>::Create(channel_.get(), cq, rpcmethod_GetEntityIDs_, context, request, false); -} - -::grpc::Status MilvusService::Stub::DeleteEntitiesByID(::grpc::ClientContext* context, const ::milvus::grpc::HDeleteByIDParam& request, ::milvus::grpc::Status* response) { - return ::grpc::internal::BlockingUnaryCall(channel_.get(), rpcmethod_DeleteEntitiesByID_, context, request, response); -} - -void MilvusService::Stub::experimental_async::DeleteEntitiesByID(::grpc::ClientContext* context, const ::milvus::grpc::HDeleteByIDParam* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_DeleteEntitiesByID_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::DeleteEntitiesByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function f) { - ::grpc_impl::internal::CallbackUnaryCall(stub_->channel_.get(), stub_->rpcmethod_DeleteEntitiesByID_, context, request, response, std::move(f)); -} - -void MilvusService::Stub::experimental_async::DeleteEntitiesByID(::grpc::ClientContext* context, const ::milvus::grpc::HDeleteByIDParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_DeleteEntitiesByID_, context, request, response, reactor); -} - -void MilvusService::Stub::experimental_async::DeleteEntitiesByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) { - ::grpc_impl::internal::ClientCallbackUnaryFactory::Create(stub_->channel_.get(), stub_->rpcmethod_DeleteEntitiesByID_, context, request, response, reactor); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::AsyncDeleteEntitiesByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::HDeleteByIDParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_DeleteEntitiesByID_, context, request, true); -} - -::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* MilvusService::Stub::PrepareAsyncDeleteEntitiesByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::HDeleteByIDParam& request, ::grpc::CompletionQueue* cq) { - return ::grpc_impl::internal::ClientAsyncResponseReaderFactory< ::milvus::grpc::Status>::Create(channel_.get(), cq, rpcmethod_DeleteEntitiesByID_, context, request, false); -} - -MilvusService::Service::Service() { - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[0], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::CollectionSchema, ::milvus::grpc::Status>( - std::mem_fn(&MilvusService::Service::CreateCollection), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[1], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::CollectionName, ::milvus::grpc::BoolReply>( - std::mem_fn(&MilvusService::Service::HasCollection), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[2], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionSchema>( - std::mem_fn(&MilvusService::Service::DescribeCollection), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[3], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionRowCount>( - std::mem_fn(&MilvusService::Service::CountCollection), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[4], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::Command, ::milvus::grpc::CollectionNameList>( - std::mem_fn(&MilvusService::Service::ShowCollections), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[5], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionInfo>( - std::mem_fn(&MilvusService::Service::ShowCollectionInfo), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[6], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::CollectionName, ::milvus::grpc::Status>( - std::mem_fn(&MilvusService::Service::DropCollection), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[7], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::IndexParam, ::milvus::grpc::Status>( - std::mem_fn(&MilvusService::Service::CreateIndex), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[8], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::CollectionName, ::milvus::grpc::IndexParam>( - std::mem_fn(&MilvusService::Service::DescribeIndex), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[9], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::CollectionName, ::milvus::grpc::Status>( - std::mem_fn(&MilvusService::Service::DropIndex), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[10], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::PartitionParam, ::milvus::grpc::Status>( - std::mem_fn(&MilvusService::Service::CreatePartition), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[11], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::PartitionParam, ::milvus::grpc::BoolReply>( - std::mem_fn(&MilvusService::Service::HasPartition), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[12], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::CollectionName, ::milvus::grpc::PartitionList>( - std::mem_fn(&MilvusService::Service::ShowPartitions), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[13], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::PartitionParam, ::milvus::grpc::Status>( - std::mem_fn(&MilvusService::Service::DropPartition), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[14], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::InsertParam, ::milvus::grpc::VectorIds>( - std::mem_fn(&MilvusService::Service::Insert), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[15], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::VectorsIdentity, ::milvus::grpc::VectorsData>( - std::mem_fn(&MilvusService::Service::GetVectorsByID), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[16], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::GetVectorIDsParam, ::milvus::grpc::VectorIds>( - std::mem_fn(&MilvusService::Service::GetVectorIDs), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[17], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::SearchParam, ::milvus::grpc::TopKQueryResult>( - std::mem_fn(&MilvusService::Service::Search), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[18], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::SearchByIDParam, ::milvus::grpc::TopKQueryResult>( - std::mem_fn(&MilvusService::Service::SearchByID), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[19], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::SearchInFilesParam, ::milvus::grpc::TopKQueryResult>( - std::mem_fn(&MilvusService::Service::SearchInFiles), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[20], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::Command, ::milvus::grpc::StringReply>( - std::mem_fn(&MilvusService::Service::Cmd), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[21], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::DeleteByIDParam, ::milvus::grpc::Status>( - std::mem_fn(&MilvusService::Service::DeleteByID), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[22], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::CollectionName, ::milvus::grpc::Status>( - std::mem_fn(&MilvusService::Service::PreloadCollection), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[23], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::ReLoadSegmentsParam, ::milvus::grpc::Status>( - std::mem_fn(&MilvusService::Service::ReloadSegments), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[24], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::FlushParam, ::milvus::grpc::Status>( - std::mem_fn(&MilvusService::Service::Flush), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[25], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::CollectionName, ::milvus::grpc::Status>( - std::mem_fn(&MilvusService::Service::Compact), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[26], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::Mapping, ::milvus::grpc::Status>( - std::mem_fn(&MilvusService::Service::CreateHybridCollection), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[27], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::CollectionName, ::milvus::grpc::BoolReply>( - std::mem_fn(&MilvusService::Service::HasHybridCollection), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[28], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::CollectionName, ::milvus::grpc::Status>( - std::mem_fn(&MilvusService::Service::DropHybridCollection), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[29], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::CollectionName, ::milvus::grpc::Mapping>( - std::mem_fn(&MilvusService::Service::DescribeHybridCollection), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[30], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionRowCount>( - std::mem_fn(&MilvusService::Service::CountHybridCollection), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[31], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::Command, ::milvus::grpc::MappingList>( - std::mem_fn(&MilvusService::Service::ShowHybridCollections), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[32], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionInfo>( - std::mem_fn(&MilvusService::Service::ShowHybridCollectionInfo), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[33], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::CollectionName, ::milvus::grpc::Status>( - std::mem_fn(&MilvusService::Service::PreloadHybridCollection), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[34], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::HInsertParam, ::milvus::grpc::HEntityIDs>( - std::mem_fn(&MilvusService::Service::InsertEntity), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[35], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::HSearchParam, ::milvus::grpc::TopKQueryResult>( - std::mem_fn(&MilvusService::Service::HybridSearch), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[36], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::HSearchInSegmentsParam, ::milvus::grpc::TopKQueryResult>( - std::mem_fn(&MilvusService::Service::HybridSearchInSegments), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[37], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::HEntityIdentity, ::milvus::grpc::HEntity>( - std::mem_fn(&MilvusService::Service::GetEntityByID), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[38], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::HGetEntityIDsParam, ::milvus::grpc::HEntityIDs>( - std::mem_fn(&MilvusService::Service::GetEntityIDs), this))); - AddMethod(new ::grpc::internal::RpcServiceMethod( - MilvusService_method_names[39], - ::grpc::internal::RpcMethod::NORMAL_RPC, - new ::grpc::internal::RpcMethodHandler< MilvusService::Service, ::milvus::grpc::HDeleteByIDParam, ::milvus::grpc::Status>( - std::mem_fn(&MilvusService::Service::DeleteEntitiesByID), this))); -} - -MilvusService::Service::~Service() { -} - -::grpc::Status MilvusService::Service::CreateCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionSchema* request, ::milvus::grpc::Status* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::HasCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::BoolReply* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::DescribeCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionSchema* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::CountCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionRowCount* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::ShowCollections(::grpc::ServerContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::CollectionNameList* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::ShowCollectionInfo(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionInfo* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::DropCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::CreateIndex(::grpc::ServerContext* context, const ::milvus::grpc::IndexParam* request, ::milvus::grpc::Status* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::DescribeIndex(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::IndexParam* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::DropIndex(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::CreatePartition(::grpc::ServerContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::Status* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::HasPartition(::grpc::ServerContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::BoolReply* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::ShowPartitions(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::PartitionList* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::DropPartition(::grpc::ServerContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::Status* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::Insert(::grpc::ServerContext* context, const ::milvus::grpc::InsertParam* request, ::milvus::grpc::VectorIds* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::GetVectorsByID(::grpc::ServerContext* context, const ::milvus::grpc::VectorsIdentity* request, ::milvus::grpc::VectorsData* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::GetVectorIDs(::grpc::ServerContext* context, const ::milvus::grpc::GetVectorIDsParam* request, ::milvus::grpc::VectorIds* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::Search(::grpc::ServerContext* context, const ::milvus::grpc::SearchParam* request, ::milvus::grpc::TopKQueryResult* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::SearchByID(::grpc::ServerContext* context, const ::milvus::grpc::SearchByIDParam* request, ::milvus::grpc::TopKQueryResult* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::SearchInFiles(::grpc::ServerContext* context, const ::milvus::grpc::SearchInFilesParam* request, ::milvus::grpc::TopKQueryResult* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::Cmd(::grpc::ServerContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::StringReply* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::DeleteByID(::grpc::ServerContext* context, const ::milvus::grpc::DeleteByIDParam* request, ::milvus::grpc::Status* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::PreloadCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::ReloadSegments(::grpc::ServerContext* context, const ::milvus::grpc::ReLoadSegmentsParam* request, ::milvus::grpc::Status* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::Flush(::grpc::ServerContext* context, const ::milvus::grpc::FlushParam* request, ::milvus::grpc::Status* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::Compact(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::CreateHybridCollection(::grpc::ServerContext* context, const ::milvus::grpc::Mapping* request, ::milvus::grpc::Status* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::HasHybridCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::BoolReply* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::DropHybridCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::DescribeHybridCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Mapping* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::CountHybridCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionRowCount* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::ShowHybridCollections(::grpc::ServerContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::MappingList* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::ShowHybridCollectionInfo(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionInfo* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::PreloadHybridCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::InsertEntity(::grpc::ServerContext* context, const ::milvus::grpc::HInsertParam* request, ::milvus::grpc::HEntityIDs* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::HybridSearch(::grpc::ServerContext* context, const ::milvus::grpc::HSearchParam* request, ::milvus::grpc::TopKQueryResult* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::HybridSearchInSegments(::grpc::ServerContext* context, const ::milvus::grpc::HSearchInSegmentsParam* request, ::milvus::grpc::TopKQueryResult* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::GetEntityByID(::grpc::ServerContext* context, const ::milvus::grpc::HEntityIdentity* request, ::milvus::grpc::HEntity* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::GetEntityIDs(::grpc::ServerContext* context, const ::milvus::grpc::HGetEntityIDsParam* request, ::milvus::grpc::HEntityIDs* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - -::grpc::Status MilvusService::Service::DeleteEntitiesByID(::grpc::ServerContext* context, const ::milvus::grpc::HDeleteByIDParam* request, ::milvus::grpc::Status* response) { - (void) context; - (void) request; - (void) response; - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); -} - - -} // namespace milvus -} // namespace grpc - diff --git a/core/src/grpc/gen-milvus/milvus.grpc.pb.h b/core/src/grpc/gen-milvus/milvus.grpc.pb.h deleted file mode 100644 index 96bc956102..0000000000 --- a/core/src/grpc/gen-milvus/milvus.grpc.pb.h +++ /dev/null @@ -1,7040 +0,0 @@ -// Generated by the gRPC C++ plugin. -// If you make any local change, they will be lost. -// source: milvus.proto -#ifndef GRPC_milvus_2eproto__INCLUDED -#define GRPC_milvus_2eproto__INCLUDED - -#include "milvus.pb.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace grpc_impl { -class CompletionQueue; -class ServerCompletionQueue; -class ServerContext; -} // namespace grpc_impl - -namespace grpc { -namespace experimental { -template -class MessageAllocator; -} // namespace experimental -} // namespace grpc - -namespace milvus { -namespace grpc { - -class MilvusService final { - public: - static constexpr char const* service_full_name() { - return "milvus.grpc.MilvusService"; - } - class StubInterface { - public: - virtual ~StubInterface() {} - // * - // @brief This method is used to create collection - // - // @param CollectionSchema, use to provide collection information to be created. - // - // @return Status - virtual ::grpc::Status CreateCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionSchema& request, ::milvus::grpc::Status* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> AsyncCreateCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionSchema& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(AsyncCreateCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> PrepareAsyncCreateCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionSchema& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(PrepareAsyncCreateCollectionRaw(context, request, cq)); - } - // * - // @brief This method is used to test collection existence. - // - // @param CollectionName, collection name is going to be tested. - // - // @return BoolReply - virtual ::grpc::Status HasCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::BoolReply* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::BoolReply>> AsyncHasCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::BoolReply>>(AsyncHasCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::BoolReply>> PrepareAsyncHasCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::BoolReply>>(PrepareAsyncHasCollectionRaw(context, request, cq)); - } - // * - // @brief This method is used to get collection schema. - // - // @param CollectionName, target collection name. - // - // @return CollectionSchema - virtual ::grpc::Status DescribeCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::CollectionSchema* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionSchema>> AsyncDescribeCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionSchema>>(AsyncDescribeCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionSchema>> PrepareAsyncDescribeCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionSchema>>(PrepareAsyncDescribeCollectionRaw(context, request, cq)); - } - // * - // @brief This method is used to get collection schema. - // - // @param CollectionName, target collection name. - // - // @return CollectionRowCount - virtual ::grpc::Status CountCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::CollectionRowCount* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionRowCount>> AsyncCountCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionRowCount>>(AsyncCountCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionRowCount>> PrepareAsyncCountCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionRowCount>>(PrepareAsyncCountCollectionRaw(context, request, cq)); - } - // * - // @brief This method is used to list all collections. - // - // @param Command, dummy parameter. - // - // @return CollectionNameList - virtual ::grpc::Status ShowCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::milvus::grpc::CollectionNameList* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionNameList>> AsyncShowCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionNameList>>(AsyncShowCollectionsRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionNameList>> PrepareAsyncShowCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionNameList>>(PrepareAsyncShowCollectionsRaw(context, request, cq)); - } - // * - // @brief This method is used to get collection detail information. - // - // @param CollectionName, target collection name. - // - // @return CollectionInfo - virtual ::grpc::Status ShowCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::CollectionInfo* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionInfo>> AsyncShowCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionInfo>>(AsyncShowCollectionInfoRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionInfo>> PrepareAsyncShowCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionInfo>>(PrepareAsyncShowCollectionInfoRaw(context, request, cq)); - } - // * - // @brief This method is used to delete collection. - // - // @param CollectionName, collection name is going to be deleted. - // - // @return CollectionNameList - virtual ::grpc::Status DropCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Status* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> AsyncDropCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(AsyncDropCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> PrepareAsyncDropCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(PrepareAsyncDropCollectionRaw(context, request, cq)); - } - // * - // @brief This method is used to build index by collection in sync mode. - // - // @param IndexParam, index paramters. - // - // @return Status - virtual ::grpc::Status CreateIndex(::grpc::ClientContext* context, const ::milvus::grpc::IndexParam& request, ::milvus::grpc::Status* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> AsyncCreateIndex(::grpc::ClientContext* context, const ::milvus::grpc::IndexParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(AsyncCreateIndexRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> PrepareAsyncCreateIndex(::grpc::ClientContext* context, const ::milvus::grpc::IndexParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(PrepareAsyncCreateIndexRaw(context, request, cq)); - } - // * - // @brief This method is used to describe index - // - // @param CollectionName, target collection name. - // - // @return IndexParam - virtual ::grpc::Status DescribeIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::IndexParam* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::IndexParam>> AsyncDescribeIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::IndexParam>>(AsyncDescribeIndexRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::IndexParam>> PrepareAsyncDescribeIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::IndexParam>>(PrepareAsyncDescribeIndexRaw(context, request, cq)); - } - // * - // @brief This method is used to drop index - // - // @param CollectionName, target collection name. - // - // @return Status - virtual ::grpc::Status DropIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Status* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> AsyncDropIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(AsyncDropIndexRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> PrepareAsyncDropIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(PrepareAsyncDropIndexRaw(context, request, cq)); - } - // * - // @brief This method is used to create partition - // - // @param PartitionParam, partition parameters. - // - // @return Status - virtual ::grpc::Status CreatePartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::milvus::grpc::Status* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> AsyncCreatePartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(AsyncCreatePartitionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> PrepareAsyncCreatePartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(PrepareAsyncCreatePartitionRaw(context, request, cq)); - } - // * - // @brief This method is used to test partition existence. - // - // @param PartitionParam, target partition. - // - // @return BoolReply - virtual ::grpc::Status HasPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::milvus::grpc::BoolReply* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::BoolReply>> AsyncHasPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::BoolReply>>(AsyncHasPartitionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::BoolReply>> PrepareAsyncHasPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::BoolReply>>(PrepareAsyncHasPartitionRaw(context, request, cq)); - } - // * - // @brief This method is used to show partition information - // - // @param CollectionName, target collection name. - // - // @return PartitionList - virtual ::grpc::Status ShowPartitions(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::PartitionList* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::PartitionList>> AsyncShowPartitions(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::PartitionList>>(AsyncShowPartitionsRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::PartitionList>> PrepareAsyncShowPartitions(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::PartitionList>>(PrepareAsyncShowPartitionsRaw(context, request, cq)); - } - // * - // @brief This method is used to drop partition - // - // @param PartitionParam, target partition. - // - // @return Status - virtual ::grpc::Status DropPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::milvus::grpc::Status* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> AsyncDropPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(AsyncDropPartitionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> PrepareAsyncDropPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(PrepareAsyncDropPartitionRaw(context, request, cq)); - } - // * - // @brief This method is used to add vector array to collection. - // - // @param InsertParam, insert parameters. - // - // @return VectorIds - virtual ::grpc::Status Insert(::grpc::ClientContext* context, const ::milvus::grpc::InsertParam& request, ::milvus::grpc::VectorIds* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::VectorIds>> AsyncInsert(::grpc::ClientContext* context, const ::milvus::grpc::InsertParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::VectorIds>>(AsyncInsertRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::VectorIds>> PrepareAsyncInsert(::grpc::ClientContext* context, const ::milvus::grpc::InsertParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::VectorIds>>(PrepareAsyncInsertRaw(context, request, cq)); - } - // * - // @brief This method is used to get vectors data by id array. - // - // @param VectorsIdentity, target vector id array. - // - // @return VectorsData - virtual ::grpc::Status GetVectorsByID(::grpc::ClientContext* context, const ::milvus::grpc::VectorsIdentity& request, ::milvus::grpc::VectorsData* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::VectorsData>> AsyncGetVectorsByID(::grpc::ClientContext* context, const ::milvus::grpc::VectorsIdentity& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::VectorsData>>(AsyncGetVectorsByIDRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::VectorsData>> PrepareAsyncGetVectorsByID(::grpc::ClientContext* context, const ::milvus::grpc::VectorsIdentity& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::VectorsData>>(PrepareAsyncGetVectorsByIDRaw(context, request, cq)); - } - // * - // @brief This method is used to get vector ids from a segment - // - // @param GetVectorIDsParam, target collection and segment - // - // @return VectorIds - virtual ::grpc::Status GetVectorIDs(::grpc::ClientContext* context, const ::milvus::grpc::GetVectorIDsParam& request, ::milvus::grpc::VectorIds* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::VectorIds>> AsyncGetVectorIDs(::grpc::ClientContext* context, const ::milvus::grpc::GetVectorIDsParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::VectorIds>>(AsyncGetVectorIDsRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::VectorIds>> PrepareAsyncGetVectorIDs(::grpc::ClientContext* context, const ::milvus::grpc::GetVectorIDsParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::VectorIds>>(PrepareAsyncGetVectorIDsRaw(context, request, cq)); - } - // * - // @brief This method is used to query vector in collection. - // - // @param SearchParam, search parameters. - // - // @return TopKQueryResult - virtual ::grpc::Status Search(::grpc::ClientContext* context, const ::milvus::grpc::SearchParam& request, ::milvus::grpc::TopKQueryResult* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>> AsyncSearch(::grpc::ClientContext* context, const ::milvus::grpc::SearchParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>>(AsyncSearchRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>> PrepareAsyncSearch(::grpc::ClientContext* context, const ::milvus::grpc::SearchParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>>(PrepareAsyncSearchRaw(context, request, cq)); - } - // * - // @brief This method is used to query vector by id. - // - // @param SearchByIDParam, search parameters. - // - // @return TopKQueryResult - virtual ::grpc::Status SearchByID(::grpc::ClientContext* context, const ::milvus::grpc::SearchByIDParam& request, ::milvus::grpc::TopKQueryResult* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>> AsyncSearchByID(::grpc::ClientContext* context, const ::milvus::grpc::SearchByIDParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>>(AsyncSearchByIDRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>> PrepareAsyncSearchByID(::grpc::ClientContext* context, const ::milvus::grpc::SearchByIDParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>>(PrepareAsyncSearchByIDRaw(context, request, cq)); - } - // * - // @brief This method is used to query vector in specified files. - // - // @param SearchInFilesParam, search in files paremeters. - // - // @return TopKQueryResult - virtual ::grpc::Status SearchInFiles(::grpc::ClientContext* context, const ::milvus::grpc::SearchInFilesParam& request, ::milvus::grpc::TopKQueryResult* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>> AsyncSearchInFiles(::grpc::ClientContext* context, const ::milvus::grpc::SearchInFilesParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>>(AsyncSearchInFilesRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>> PrepareAsyncSearchInFiles(::grpc::ClientContext* context, const ::milvus::grpc::SearchInFilesParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>>(PrepareAsyncSearchInFilesRaw(context, request, cq)); - } - // * - // @brief This method is used to give the server status. - // - // @param Command, command string - // - // @return StringReply - virtual ::grpc::Status Cmd(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::milvus::grpc::StringReply* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::StringReply>> AsyncCmd(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::StringReply>>(AsyncCmdRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::StringReply>> PrepareAsyncCmd(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::StringReply>>(PrepareAsyncCmdRaw(context, request, cq)); - } - // * - // @brief This method is used to delete vector by id - // - // @param DeleteByIDParam, delete parameters. - // - // @return status - virtual ::grpc::Status DeleteByID(::grpc::ClientContext* context, const ::milvus::grpc::DeleteByIDParam& request, ::milvus::grpc::Status* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> AsyncDeleteByID(::grpc::ClientContext* context, const ::milvus::grpc::DeleteByIDParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(AsyncDeleteByIDRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> PrepareAsyncDeleteByID(::grpc::ClientContext* context, const ::milvus::grpc::DeleteByIDParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(PrepareAsyncDeleteByIDRaw(context, request, cq)); - } - // * - // @brief This method is used to preload collection - // - // @param CollectionName, target collection name. - // - // @return Status - virtual ::grpc::Status PreloadCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Status* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> AsyncPreloadCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(AsyncPreloadCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> PrepareAsyncPreloadCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(PrepareAsyncPreloadCollectionRaw(context, request, cq)); - } - // * - // @brief This method is used to reload collection segments - // - // @param ReLoadSegmentsParam, target segments information. - // - // @return Status - virtual ::grpc::Status ReloadSegments(::grpc::ClientContext* context, const ::milvus::grpc::ReLoadSegmentsParam& request, ::milvus::grpc::Status* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> AsyncReloadSegments(::grpc::ClientContext* context, const ::milvus::grpc::ReLoadSegmentsParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(AsyncReloadSegmentsRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> PrepareAsyncReloadSegments(::grpc::ClientContext* context, const ::milvus::grpc::ReLoadSegmentsParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(PrepareAsyncReloadSegmentsRaw(context, request, cq)); - } - // * - // @brief This method is used to flush buffer into storage. - // - // @param FlushParam, flush parameters - // - // @return Status - virtual ::grpc::Status Flush(::grpc::ClientContext* context, const ::milvus::grpc::FlushParam& request, ::milvus::grpc::Status* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> AsyncFlush(::grpc::ClientContext* context, const ::milvus::grpc::FlushParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(AsyncFlushRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> PrepareAsyncFlush(::grpc::ClientContext* context, const ::milvus::grpc::FlushParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(PrepareAsyncFlushRaw(context, request, cq)); - } - // * - // @brief This method is used to compact collection - // - // @param CollectionName, target collection name. - // - // @return Status - virtual ::grpc::Status Compact(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Status* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> AsyncCompact(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(AsyncCompactRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> PrepareAsyncCompact(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(PrepareAsyncCompactRaw(context, request, cq)); - } - // *******************************New Interface******************************************* - // - virtual ::grpc::Status CreateHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::Mapping& request, ::milvus::grpc::Status* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> AsyncCreateHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::Mapping& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(AsyncCreateHybridCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> PrepareAsyncCreateHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::Mapping& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(PrepareAsyncCreateHybridCollectionRaw(context, request, cq)); - } - virtual ::grpc::Status HasHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::BoolReply* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::BoolReply>> AsyncHasHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::BoolReply>>(AsyncHasHybridCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::BoolReply>> PrepareAsyncHasHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::BoolReply>>(PrepareAsyncHasHybridCollectionRaw(context, request, cq)); - } - virtual ::grpc::Status DropHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Status* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> AsyncDropHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(AsyncDropHybridCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> PrepareAsyncDropHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(PrepareAsyncDropHybridCollectionRaw(context, request, cq)); - } - virtual ::grpc::Status DescribeHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Mapping* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Mapping>> AsyncDescribeHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Mapping>>(AsyncDescribeHybridCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Mapping>> PrepareAsyncDescribeHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Mapping>>(PrepareAsyncDescribeHybridCollectionRaw(context, request, cq)); - } - virtual ::grpc::Status CountHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::CollectionRowCount* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionRowCount>> AsyncCountHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionRowCount>>(AsyncCountHybridCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionRowCount>> PrepareAsyncCountHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionRowCount>>(PrepareAsyncCountHybridCollectionRaw(context, request, cq)); - } - virtual ::grpc::Status ShowHybridCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::milvus::grpc::MappingList* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::MappingList>> AsyncShowHybridCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::MappingList>>(AsyncShowHybridCollectionsRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::MappingList>> PrepareAsyncShowHybridCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::MappingList>>(PrepareAsyncShowHybridCollectionsRaw(context, request, cq)); - } - virtual ::grpc::Status ShowHybridCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::CollectionInfo* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionInfo>> AsyncShowHybridCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionInfo>>(AsyncShowHybridCollectionInfoRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionInfo>> PrepareAsyncShowHybridCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionInfo>>(PrepareAsyncShowHybridCollectionInfoRaw(context, request, cq)); - } - virtual ::grpc::Status PreloadHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Status* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> AsyncPreloadHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(AsyncPreloadHybridCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> PrepareAsyncPreloadHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(PrepareAsyncPreloadHybridCollectionRaw(context, request, cq)); - } - // ///////////////////////////////////////////////////////////////// - // - // rpc CreateIndex(IndexParam) returns (Status) {} - // - // rpc DescribeIndex(CollectionName) returns (IndexParam) {} - // - // rpc DropIndex(CollectionName) returns (Status) {} - // - // ///////////////////////////////////////////////////////////////// - // - virtual ::grpc::Status InsertEntity(::grpc::ClientContext* context, const ::milvus::grpc::HInsertParam& request, ::milvus::grpc::HEntityIDs* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::HEntityIDs>> AsyncInsertEntity(::grpc::ClientContext* context, const ::milvus::grpc::HInsertParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::HEntityIDs>>(AsyncInsertEntityRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::HEntityIDs>> PrepareAsyncInsertEntity(::grpc::ClientContext* context, const ::milvus::grpc::HInsertParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::HEntityIDs>>(PrepareAsyncInsertEntityRaw(context, request, cq)); - } - // TODO(yukun): will change to HQueryResult - virtual ::grpc::Status HybridSearch(::grpc::ClientContext* context, const ::milvus::grpc::HSearchParam& request, ::milvus::grpc::TopKQueryResult* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>> AsyncHybridSearch(::grpc::ClientContext* context, const ::milvus::grpc::HSearchParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>>(AsyncHybridSearchRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>> PrepareAsyncHybridSearch(::grpc::ClientContext* context, const ::milvus::grpc::HSearchParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>>(PrepareAsyncHybridSearchRaw(context, request, cq)); - } - virtual ::grpc::Status HybridSearchInSegments(::grpc::ClientContext* context, const ::milvus::grpc::HSearchInSegmentsParam& request, ::milvus::grpc::TopKQueryResult* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>> AsyncHybridSearchInSegments(::grpc::ClientContext* context, const ::milvus::grpc::HSearchInSegmentsParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>>(AsyncHybridSearchInSegmentsRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>> PrepareAsyncHybridSearchInSegments(::grpc::ClientContext* context, const ::milvus::grpc::HSearchInSegmentsParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>>(PrepareAsyncHybridSearchInSegmentsRaw(context, request, cq)); - } - virtual ::grpc::Status GetEntityByID(::grpc::ClientContext* context, const ::milvus::grpc::HEntityIdentity& request, ::milvus::grpc::HEntity* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::HEntity>> AsyncGetEntityByID(::grpc::ClientContext* context, const ::milvus::grpc::HEntityIdentity& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::HEntity>>(AsyncGetEntityByIDRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::HEntity>> PrepareAsyncGetEntityByID(::grpc::ClientContext* context, const ::milvus::grpc::HEntityIdentity& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::HEntity>>(PrepareAsyncGetEntityByIDRaw(context, request, cq)); - } - virtual ::grpc::Status GetEntityIDs(::grpc::ClientContext* context, const ::milvus::grpc::HGetEntityIDsParam& request, ::milvus::grpc::HEntityIDs* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::HEntityIDs>> AsyncGetEntityIDs(::grpc::ClientContext* context, const ::milvus::grpc::HGetEntityIDsParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::HEntityIDs>>(AsyncGetEntityIDsRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::HEntityIDs>> PrepareAsyncGetEntityIDs(::grpc::ClientContext* context, const ::milvus::grpc::HGetEntityIDsParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::HEntityIDs>>(PrepareAsyncGetEntityIDsRaw(context, request, cq)); - } - virtual ::grpc::Status DeleteEntitiesByID(::grpc::ClientContext* context, const ::milvus::grpc::HDeleteByIDParam& request, ::milvus::grpc::Status* response) = 0; - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> AsyncDeleteEntitiesByID(::grpc::ClientContext* context, const ::milvus::grpc::HDeleteByIDParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(AsyncDeleteEntitiesByIDRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>> PrepareAsyncDeleteEntitiesByID(::grpc::ClientContext* context, const ::milvus::grpc::HDeleteByIDParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>>(PrepareAsyncDeleteEntitiesByIDRaw(context, request, cq)); - } - class experimental_async_interface { - public: - virtual ~experimental_async_interface() {} - // * - // @brief This method is used to create collection - // - // @param CollectionSchema, use to provide collection information to be created. - // - // @return Status - virtual void CreateCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionSchema* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void CreateCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void CreateCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionSchema* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void CreateCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to test collection existence. - // - // @param CollectionName, collection name is going to be tested. - // - // @return BoolReply - virtual void HasCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::BoolReply* response, std::function) = 0; - virtual void HasCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::BoolReply* response, std::function) = 0; - virtual void HasCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::BoolReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void HasCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::BoolReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to get collection schema. - // - // @param CollectionName, target collection name. - // - // @return CollectionSchema - virtual void DescribeCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionSchema* response, std::function) = 0; - virtual void DescribeCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionSchema* response, std::function) = 0; - virtual void DescribeCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionSchema* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void DescribeCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionSchema* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to get collection schema. - // - // @param CollectionName, target collection name. - // - // @return CollectionRowCount - virtual void CountCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionRowCount* response, std::function) = 0; - virtual void CountCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionRowCount* response, std::function) = 0; - virtual void CountCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionRowCount* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void CountCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionRowCount* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to list all collections. - // - // @param Command, dummy parameter. - // - // @return CollectionNameList - virtual void ShowCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::CollectionNameList* response, std::function) = 0; - virtual void ShowCollections(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionNameList* response, std::function) = 0; - virtual void ShowCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::CollectionNameList* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void ShowCollections(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionNameList* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to get collection detail information. - // - // @param CollectionName, target collection name. - // - // @return CollectionInfo - virtual void ShowCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionInfo* response, std::function) = 0; - virtual void ShowCollectionInfo(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionInfo* response, std::function) = 0; - virtual void ShowCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionInfo* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void ShowCollectionInfo(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionInfo* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to delete collection. - // - // @param CollectionName, collection name is going to be deleted. - // - // @return CollectionNameList - virtual void DropCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void DropCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void DropCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void DropCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to build index by collection in sync mode. - // - // @param IndexParam, index paramters. - // - // @return Status - virtual void CreateIndex(::grpc::ClientContext* context, const ::milvus::grpc::IndexParam* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void CreateIndex(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void CreateIndex(::grpc::ClientContext* context, const ::milvus::grpc::IndexParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void CreateIndex(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to describe index - // - // @param CollectionName, target collection name. - // - // @return IndexParam - virtual void DescribeIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::IndexParam* response, std::function) = 0; - virtual void DescribeIndex(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::IndexParam* response, std::function) = 0; - virtual void DescribeIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::IndexParam* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void DescribeIndex(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::IndexParam* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to drop index - // - // @param CollectionName, target collection name. - // - // @return Status - virtual void DropIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void DropIndex(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void DropIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void DropIndex(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to create partition - // - // @param PartitionParam, partition parameters. - // - // @return Status - virtual void CreatePartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void CreatePartition(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void CreatePartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void CreatePartition(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to test partition existence. - // - // @param PartitionParam, target partition. - // - // @return BoolReply - virtual void HasPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::BoolReply* response, std::function) = 0; - virtual void HasPartition(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::BoolReply* response, std::function) = 0; - virtual void HasPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::BoolReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void HasPartition(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::BoolReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to show partition information - // - // @param CollectionName, target collection name. - // - // @return PartitionList - virtual void ShowPartitions(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::PartitionList* response, std::function) = 0; - virtual void ShowPartitions(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::PartitionList* response, std::function) = 0; - virtual void ShowPartitions(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::PartitionList* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void ShowPartitions(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::PartitionList* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to drop partition - // - // @param PartitionParam, target partition. - // - // @return Status - virtual void DropPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void DropPartition(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void DropPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void DropPartition(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to add vector array to collection. - // - // @param InsertParam, insert parameters. - // - // @return VectorIds - virtual void Insert(::grpc::ClientContext* context, const ::milvus::grpc::InsertParam* request, ::milvus::grpc::VectorIds* response, std::function) = 0; - virtual void Insert(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::VectorIds* response, std::function) = 0; - virtual void Insert(::grpc::ClientContext* context, const ::milvus::grpc::InsertParam* request, ::milvus::grpc::VectorIds* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void Insert(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::VectorIds* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to get vectors data by id array. - // - // @param VectorsIdentity, target vector id array. - // - // @return VectorsData - virtual void GetVectorsByID(::grpc::ClientContext* context, const ::milvus::grpc::VectorsIdentity* request, ::milvus::grpc::VectorsData* response, std::function) = 0; - virtual void GetVectorsByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::VectorsData* response, std::function) = 0; - virtual void GetVectorsByID(::grpc::ClientContext* context, const ::milvus::grpc::VectorsIdentity* request, ::milvus::grpc::VectorsData* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void GetVectorsByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::VectorsData* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to get vector ids from a segment - // - // @param GetVectorIDsParam, target collection and segment - // - // @return VectorIds - virtual void GetVectorIDs(::grpc::ClientContext* context, const ::milvus::grpc::GetVectorIDsParam* request, ::milvus::grpc::VectorIds* response, std::function) = 0; - virtual void GetVectorIDs(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::VectorIds* response, std::function) = 0; - virtual void GetVectorIDs(::grpc::ClientContext* context, const ::milvus::grpc::GetVectorIDsParam* request, ::milvus::grpc::VectorIds* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void GetVectorIDs(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::VectorIds* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to query vector in collection. - // - // @param SearchParam, search parameters. - // - // @return TopKQueryResult - virtual void Search(::grpc::ClientContext* context, const ::milvus::grpc::SearchParam* request, ::milvus::grpc::TopKQueryResult* response, std::function) = 0; - virtual void Search(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, std::function) = 0; - virtual void Search(::grpc::ClientContext* context, const ::milvus::grpc::SearchParam* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void Search(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to query vector by id. - // - // @param SearchByIDParam, search parameters. - // - // @return TopKQueryResult - virtual void SearchByID(::grpc::ClientContext* context, const ::milvus::grpc::SearchByIDParam* request, ::milvus::grpc::TopKQueryResult* response, std::function) = 0; - virtual void SearchByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, std::function) = 0; - virtual void SearchByID(::grpc::ClientContext* context, const ::milvus::grpc::SearchByIDParam* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void SearchByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to query vector in specified files. - // - // @param SearchInFilesParam, search in files paremeters. - // - // @return TopKQueryResult - virtual void SearchInFiles(::grpc::ClientContext* context, const ::milvus::grpc::SearchInFilesParam* request, ::milvus::grpc::TopKQueryResult* response, std::function) = 0; - virtual void SearchInFiles(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, std::function) = 0; - virtual void SearchInFiles(::grpc::ClientContext* context, const ::milvus::grpc::SearchInFilesParam* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void SearchInFiles(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to give the server status. - // - // @param Command, command string - // - // @return StringReply - virtual void Cmd(::grpc::ClientContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::StringReply* response, std::function) = 0; - virtual void Cmd(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::StringReply* response, std::function) = 0; - virtual void Cmd(::grpc::ClientContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::StringReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void Cmd(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::StringReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to delete vector by id - // - // @param DeleteByIDParam, delete parameters. - // - // @return status - virtual void DeleteByID(::grpc::ClientContext* context, const ::milvus::grpc::DeleteByIDParam* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void DeleteByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void DeleteByID(::grpc::ClientContext* context, const ::milvus::grpc::DeleteByIDParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void DeleteByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to preload collection - // - // @param CollectionName, target collection name. - // - // @return Status - virtual void PreloadCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void PreloadCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void PreloadCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void PreloadCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to reload collection segments - // - // @param ReLoadSegmentsParam, target segments information. - // - // @return Status - virtual void ReloadSegments(::grpc::ClientContext* context, const ::milvus::grpc::ReLoadSegmentsParam* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void ReloadSegments(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void ReloadSegments(::grpc::ClientContext* context, const ::milvus::grpc::ReLoadSegmentsParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void ReloadSegments(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to flush buffer into storage. - // - // @param FlushParam, flush parameters - // - // @return Status - virtual void Flush(::grpc::ClientContext* context, const ::milvus::grpc::FlushParam* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void Flush(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void Flush(::grpc::ClientContext* context, const ::milvus::grpc::FlushParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void Flush(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // * - // @brief This method is used to compact collection - // - // @param CollectionName, target collection name. - // - // @return Status - virtual void Compact(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void Compact(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void Compact(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void Compact(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // *******************************New Interface******************************************* - // - virtual void CreateHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::Mapping* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void CreateHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void CreateHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::Mapping* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void CreateHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void HasHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::BoolReply* response, std::function) = 0; - virtual void HasHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::BoolReply* response, std::function) = 0; - virtual void HasHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::BoolReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void HasHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::BoolReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void DropHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void DropHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void DropHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void DropHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void DescribeHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Mapping* response, std::function) = 0; - virtual void DescribeHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Mapping* response, std::function) = 0; - virtual void DescribeHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Mapping* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void DescribeHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Mapping* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void CountHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionRowCount* response, std::function) = 0; - virtual void CountHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionRowCount* response, std::function) = 0; - virtual void CountHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionRowCount* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void CountHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionRowCount* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void ShowHybridCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::MappingList* response, std::function) = 0; - virtual void ShowHybridCollections(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::MappingList* response, std::function) = 0; - virtual void ShowHybridCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::MappingList* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void ShowHybridCollections(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::MappingList* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void ShowHybridCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionInfo* response, std::function) = 0; - virtual void ShowHybridCollectionInfo(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionInfo* response, std::function) = 0; - virtual void ShowHybridCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionInfo* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void ShowHybridCollectionInfo(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionInfo* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void PreloadHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void PreloadHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void PreloadHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void PreloadHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // ///////////////////////////////////////////////////////////////// - // - // rpc CreateIndex(IndexParam) returns (Status) {} - // - // rpc DescribeIndex(CollectionName) returns (IndexParam) {} - // - // rpc DropIndex(CollectionName) returns (Status) {} - // - // ///////////////////////////////////////////////////////////////// - // - virtual void InsertEntity(::grpc::ClientContext* context, const ::milvus::grpc::HInsertParam* request, ::milvus::grpc::HEntityIDs* response, std::function) = 0; - virtual void InsertEntity(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::HEntityIDs* response, std::function) = 0; - virtual void InsertEntity(::grpc::ClientContext* context, const ::milvus::grpc::HInsertParam* request, ::milvus::grpc::HEntityIDs* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void InsertEntity(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::HEntityIDs* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - // TODO(yukun): will change to HQueryResult - virtual void HybridSearch(::grpc::ClientContext* context, const ::milvus::grpc::HSearchParam* request, ::milvus::grpc::TopKQueryResult* response, std::function) = 0; - virtual void HybridSearch(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, std::function) = 0; - virtual void HybridSearch(::grpc::ClientContext* context, const ::milvus::grpc::HSearchParam* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void HybridSearch(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void HybridSearchInSegments(::grpc::ClientContext* context, const ::milvus::grpc::HSearchInSegmentsParam* request, ::milvus::grpc::TopKQueryResult* response, std::function) = 0; - virtual void HybridSearchInSegments(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, std::function) = 0; - virtual void HybridSearchInSegments(::grpc::ClientContext* context, const ::milvus::grpc::HSearchInSegmentsParam* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void HybridSearchInSegments(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void GetEntityByID(::grpc::ClientContext* context, const ::milvus::grpc::HEntityIdentity* request, ::milvus::grpc::HEntity* response, std::function) = 0; - virtual void GetEntityByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::HEntity* response, std::function) = 0; - virtual void GetEntityByID(::grpc::ClientContext* context, const ::milvus::grpc::HEntityIdentity* request, ::milvus::grpc::HEntity* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void GetEntityByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::HEntity* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void GetEntityIDs(::grpc::ClientContext* context, const ::milvus::grpc::HGetEntityIDsParam* request, ::milvus::grpc::HEntityIDs* response, std::function) = 0; - virtual void GetEntityIDs(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::HEntityIDs* response, std::function) = 0; - virtual void GetEntityIDs(::grpc::ClientContext* context, const ::milvus::grpc::HGetEntityIDsParam* request, ::milvus::grpc::HEntityIDs* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void GetEntityIDs(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::HEntityIDs* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void DeleteEntitiesByID(::grpc::ClientContext* context, const ::milvus::grpc::HDeleteByIDParam* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void DeleteEntitiesByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) = 0; - virtual void DeleteEntitiesByID(::grpc::ClientContext* context, const ::milvus::grpc::HDeleteByIDParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - virtual void DeleteEntitiesByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) = 0; - }; - virtual class experimental_async_interface* experimental_async() { return nullptr; } - private: - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* AsyncCreateCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionSchema& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* PrepareAsyncCreateCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionSchema& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::BoolReply>* AsyncHasCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::BoolReply>* PrepareAsyncHasCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionSchema>* AsyncDescribeCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionSchema>* PrepareAsyncDescribeCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionRowCount>* AsyncCountCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionRowCount>* PrepareAsyncCountCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionNameList>* AsyncShowCollectionsRaw(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionNameList>* PrepareAsyncShowCollectionsRaw(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionInfo>* AsyncShowCollectionInfoRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionInfo>* PrepareAsyncShowCollectionInfoRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* AsyncDropCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* PrepareAsyncDropCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* AsyncCreateIndexRaw(::grpc::ClientContext* context, const ::milvus::grpc::IndexParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* PrepareAsyncCreateIndexRaw(::grpc::ClientContext* context, const ::milvus::grpc::IndexParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::IndexParam>* AsyncDescribeIndexRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::IndexParam>* PrepareAsyncDescribeIndexRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* AsyncDropIndexRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* PrepareAsyncDropIndexRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* AsyncCreatePartitionRaw(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* PrepareAsyncCreatePartitionRaw(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::BoolReply>* AsyncHasPartitionRaw(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::BoolReply>* PrepareAsyncHasPartitionRaw(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::PartitionList>* AsyncShowPartitionsRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::PartitionList>* PrepareAsyncShowPartitionsRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* AsyncDropPartitionRaw(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* PrepareAsyncDropPartitionRaw(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::VectorIds>* AsyncInsertRaw(::grpc::ClientContext* context, const ::milvus::grpc::InsertParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::VectorIds>* PrepareAsyncInsertRaw(::grpc::ClientContext* context, const ::milvus::grpc::InsertParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::VectorsData>* AsyncGetVectorsByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::VectorsIdentity& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::VectorsData>* PrepareAsyncGetVectorsByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::VectorsIdentity& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::VectorIds>* AsyncGetVectorIDsRaw(::grpc::ClientContext* context, const ::milvus::grpc::GetVectorIDsParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::VectorIds>* PrepareAsyncGetVectorIDsRaw(::grpc::ClientContext* context, const ::milvus::grpc::GetVectorIDsParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>* AsyncSearchRaw(::grpc::ClientContext* context, const ::milvus::grpc::SearchParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>* PrepareAsyncSearchRaw(::grpc::ClientContext* context, const ::milvus::grpc::SearchParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>* AsyncSearchByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::SearchByIDParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>* PrepareAsyncSearchByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::SearchByIDParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>* AsyncSearchInFilesRaw(::grpc::ClientContext* context, const ::milvus::grpc::SearchInFilesParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>* PrepareAsyncSearchInFilesRaw(::grpc::ClientContext* context, const ::milvus::grpc::SearchInFilesParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::StringReply>* AsyncCmdRaw(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::StringReply>* PrepareAsyncCmdRaw(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* AsyncDeleteByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::DeleteByIDParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* PrepareAsyncDeleteByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::DeleteByIDParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* AsyncPreloadCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* PrepareAsyncPreloadCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* AsyncReloadSegmentsRaw(::grpc::ClientContext* context, const ::milvus::grpc::ReLoadSegmentsParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* PrepareAsyncReloadSegmentsRaw(::grpc::ClientContext* context, const ::milvus::grpc::ReLoadSegmentsParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* AsyncFlushRaw(::grpc::ClientContext* context, const ::milvus::grpc::FlushParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* PrepareAsyncFlushRaw(::grpc::ClientContext* context, const ::milvus::grpc::FlushParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* AsyncCompactRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* PrepareAsyncCompactRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* AsyncCreateHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::Mapping& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* PrepareAsyncCreateHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::Mapping& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::BoolReply>* AsyncHasHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::BoolReply>* PrepareAsyncHasHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* AsyncDropHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* PrepareAsyncDropHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Mapping>* AsyncDescribeHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Mapping>* PrepareAsyncDescribeHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionRowCount>* AsyncCountHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionRowCount>* PrepareAsyncCountHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::MappingList>* AsyncShowHybridCollectionsRaw(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::MappingList>* PrepareAsyncShowHybridCollectionsRaw(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionInfo>* AsyncShowHybridCollectionInfoRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::CollectionInfo>* PrepareAsyncShowHybridCollectionInfoRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* AsyncPreloadHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* PrepareAsyncPreloadHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::HEntityIDs>* AsyncInsertEntityRaw(::grpc::ClientContext* context, const ::milvus::grpc::HInsertParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::HEntityIDs>* PrepareAsyncInsertEntityRaw(::grpc::ClientContext* context, const ::milvus::grpc::HInsertParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>* AsyncHybridSearchRaw(::grpc::ClientContext* context, const ::milvus::grpc::HSearchParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>* PrepareAsyncHybridSearchRaw(::grpc::ClientContext* context, const ::milvus::grpc::HSearchParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>* AsyncHybridSearchInSegmentsRaw(::grpc::ClientContext* context, const ::milvus::grpc::HSearchInSegmentsParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::TopKQueryResult>* PrepareAsyncHybridSearchInSegmentsRaw(::grpc::ClientContext* context, const ::milvus::grpc::HSearchInSegmentsParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::HEntity>* AsyncGetEntityByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::HEntityIdentity& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::HEntity>* PrepareAsyncGetEntityByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::HEntityIdentity& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::HEntityIDs>* AsyncGetEntityIDsRaw(::grpc::ClientContext* context, const ::milvus::grpc::HGetEntityIDsParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::HEntityIDs>* PrepareAsyncGetEntityIDsRaw(::grpc::ClientContext* context, const ::milvus::grpc::HGetEntityIDsParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* AsyncDeleteEntitiesByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::HDeleteByIDParam& request, ::grpc::CompletionQueue* cq) = 0; - virtual ::grpc::ClientAsyncResponseReaderInterface< ::milvus::grpc::Status>* PrepareAsyncDeleteEntitiesByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::HDeleteByIDParam& request, ::grpc::CompletionQueue* cq) = 0; - }; - class Stub final : public StubInterface { - public: - Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel); - ::grpc::Status CreateCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionSchema& request, ::milvus::grpc::Status* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> AsyncCreateCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionSchema& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(AsyncCreateCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> PrepareAsyncCreateCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionSchema& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(PrepareAsyncCreateCollectionRaw(context, request, cq)); - } - ::grpc::Status HasCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::BoolReply* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>> AsyncHasCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>>(AsyncHasCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>> PrepareAsyncHasCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>>(PrepareAsyncHasCollectionRaw(context, request, cq)); - } - ::grpc::Status DescribeCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::CollectionSchema* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionSchema>> AsyncDescribeCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionSchema>>(AsyncDescribeCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionSchema>> PrepareAsyncDescribeCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionSchema>>(PrepareAsyncDescribeCollectionRaw(context, request, cq)); - } - ::grpc::Status CountCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::CollectionRowCount* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionRowCount>> AsyncCountCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionRowCount>>(AsyncCountCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionRowCount>> PrepareAsyncCountCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionRowCount>>(PrepareAsyncCountCollectionRaw(context, request, cq)); - } - ::grpc::Status ShowCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::milvus::grpc::CollectionNameList* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionNameList>> AsyncShowCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionNameList>>(AsyncShowCollectionsRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionNameList>> PrepareAsyncShowCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionNameList>>(PrepareAsyncShowCollectionsRaw(context, request, cq)); - } - ::grpc::Status ShowCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::CollectionInfo* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionInfo>> AsyncShowCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionInfo>>(AsyncShowCollectionInfoRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionInfo>> PrepareAsyncShowCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionInfo>>(PrepareAsyncShowCollectionInfoRaw(context, request, cq)); - } - ::grpc::Status DropCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Status* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> AsyncDropCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(AsyncDropCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> PrepareAsyncDropCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(PrepareAsyncDropCollectionRaw(context, request, cq)); - } - ::grpc::Status CreateIndex(::grpc::ClientContext* context, const ::milvus::grpc::IndexParam& request, ::milvus::grpc::Status* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> AsyncCreateIndex(::grpc::ClientContext* context, const ::milvus::grpc::IndexParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(AsyncCreateIndexRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> PrepareAsyncCreateIndex(::grpc::ClientContext* context, const ::milvus::grpc::IndexParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(PrepareAsyncCreateIndexRaw(context, request, cq)); - } - ::grpc::Status DescribeIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::IndexParam* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::IndexParam>> AsyncDescribeIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::IndexParam>>(AsyncDescribeIndexRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::IndexParam>> PrepareAsyncDescribeIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::IndexParam>>(PrepareAsyncDescribeIndexRaw(context, request, cq)); - } - ::grpc::Status DropIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Status* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> AsyncDropIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(AsyncDropIndexRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> PrepareAsyncDropIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(PrepareAsyncDropIndexRaw(context, request, cq)); - } - ::grpc::Status CreatePartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::milvus::grpc::Status* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> AsyncCreatePartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(AsyncCreatePartitionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> PrepareAsyncCreatePartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(PrepareAsyncCreatePartitionRaw(context, request, cq)); - } - ::grpc::Status HasPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::milvus::grpc::BoolReply* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>> AsyncHasPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>>(AsyncHasPartitionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>> PrepareAsyncHasPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>>(PrepareAsyncHasPartitionRaw(context, request, cq)); - } - ::grpc::Status ShowPartitions(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::PartitionList* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::PartitionList>> AsyncShowPartitions(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::PartitionList>>(AsyncShowPartitionsRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::PartitionList>> PrepareAsyncShowPartitions(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::PartitionList>>(PrepareAsyncShowPartitionsRaw(context, request, cq)); - } - ::grpc::Status DropPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::milvus::grpc::Status* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> AsyncDropPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(AsyncDropPartitionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> PrepareAsyncDropPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(PrepareAsyncDropPartitionRaw(context, request, cq)); - } - ::grpc::Status Insert(::grpc::ClientContext* context, const ::milvus::grpc::InsertParam& request, ::milvus::grpc::VectorIds* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorIds>> AsyncInsert(::grpc::ClientContext* context, const ::milvus::grpc::InsertParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorIds>>(AsyncInsertRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorIds>> PrepareAsyncInsert(::grpc::ClientContext* context, const ::milvus::grpc::InsertParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorIds>>(PrepareAsyncInsertRaw(context, request, cq)); - } - ::grpc::Status GetVectorsByID(::grpc::ClientContext* context, const ::milvus::grpc::VectorsIdentity& request, ::milvus::grpc::VectorsData* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorsData>> AsyncGetVectorsByID(::grpc::ClientContext* context, const ::milvus::grpc::VectorsIdentity& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorsData>>(AsyncGetVectorsByIDRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorsData>> PrepareAsyncGetVectorsByID(::grpc::ClientContext* context, const ::milvus::grpc::VectorsIdentity& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorsData>>(PrepareAsyncGetVectorsByIDRaw(context, request, cq)); - } - ::grpc::Status GetVectorIDs(::grpc::ClientContext* context, const ::milvus::grpc::GetVectorIDsParam& request, ::milvus::grpc::VectorIds* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorIds>> AsyncGetVectorIDs(::grpc::ClientContext* context, const ::milvus::grpc::GetVectorIDsParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorIds>>(AsyncGetVectorIDsRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorIds>> PrepareAsyncGetVectorIDs(::grpc::ClientContext* context, const ::milvus::grpc::GetVectorIDsParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorIds>>(PrepareAsyncGetVectorIDsRaw(context, request, cq)); - } - ::grpc::Status Search(::grpc::ClientContext* context, const ::milvus::grpc::SearchParam& request, ::milvus::grpc::TopKQueryResult* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>> AsyncSearch(::grpc::ClientContext* context, const ::milvus::grpc::SearchParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>>(AsyncSearchRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>> PrepareAsyncSearch(::grpc::ClientContext* context, const ::milvus::grpc::SearchParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>>(PrepareAsyncSearchRaw(context, request, cq)); - } - ::grpc::Status SearchByID(::grpc::ClientContext* context, const ::milvus::grpc::SearchByIDParam& request, ::milvus::grpc::TopKQueryResult* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>> AsyncSearchByID(::grpc::ClientContext* context, const ::milvus::grpc::SearchByIDParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>>(AsyncSearchByIDRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>> PrepareAsyncSearchByID(::grpc::ClientContext* context, const ::milvus::grpc::SearchByIDParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>>(PrepareAsyncSearchByIDRaw(context, request, cq)); - } - ::grpc::Status SearchInFiles(::grpc::ClientContext* context, const ::milvus::grpc::SearchInFilesParam& request, ::milvus::grpc::TopKQueryResult* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>> AsyncSearchInFiles(::grpc::ClientContext* context, const ::milvus::grpc::SearchInFilesParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>>(AsyncSearchInFilesRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>> PrepareAsyncSearchInFiles(::grpc::ClientContext* context, const ::milvus::grpc::SearchInFilesParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>>(PrepareAsyncSearchInFilesRaw(context, request, cq)); - } - ::grpc::Status Cmd(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::milvus::grpc::StringReply* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::StringReply>> AsyncCmd(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::StringReply>>(AsyncCmdRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::StringReply>> PrepareAsyncCmd(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::StringReply>>(PrepareAsyncCmdRaw(context, request, cq)); - } - ::grpc::Status DeleteByID(::grpc::ClientContext* context, const ::milvus::grpc::DeleteByIDParam& request, ::milvus::grpc::Status* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> AsyncDeleteByID(::grpc::ClientContext* context, const ::milvus::grpc::DeleteByIDParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(AsyncDeleteByIDRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> PrepareAsyncDeleteByID(::grpc::ClientContext* context, const ::milvus::grpc::DeleteByIDParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(PrepareAsyncDeleteByIDRaw(context, request, cq)); - } - ::grpc::Status PreloadCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Status* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> AsyncPreloadCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(AsyncPreloadCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> PrepareAsyncPreloadCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(PrepareAsyncPreloadCollectionRaw(context, request, cq)); - } - ::grpc::Status ReloadSegments(::grpc::ClientContext* context, const ::milvus::grpc::ReLoadSegmentsParam& request, ::milvus::grpc::Status* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> AsyncReloadSegments(::grpc::ClientContext* context, const ::milvus::grpc::ReLoadSegmentsParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(AsyncReloadSegmentsRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> PrepareAsyncReloadSegments(::grpc::ClientContext* context, const ::milvus::grpc::ReLoadSegmentsParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(PrepareAsyncReloadSegmentsRaw(context, request, cq)); - } - ::grpc::Status Flush(::grpc::ClientContext* context, const ::milvus::grpc::FlushParam& request, ::milvus::grpc::Status* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> AsyncFlush(::grpc::ClientContext* context, const ::milvus::grpc::FlushParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(AsyncFlushRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> PrepareAsyncFlush(::grpc::ClientContext* context, const ::milvus::grpc::FlushParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(PrepareAsyncFlushRaw(context, request, cq)); - } - ::grpc::Status Compact(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Status* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> AsyncCompact(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(AsyncCompactRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> PrepareAsyncCompact(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(PrepareAsyncCompactRaw(context, request, cq)); - } - ::grpc::Status CreateHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::Mapping& request, ::milvus::grpc::Status* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> AsyncCreateHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::Mapping& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(AsyncCreateHybridCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> PrepareAsyncCreateHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::Mapping& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(PrepareAsyncCreateHybridCollectionRaw(context, request, cq)); - } - ::grpc::Status HasHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::BoolReply* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>> AsyncHasHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>>(AsyncHasHybridCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>> PrepareAsyncHasHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>>(PrepareAsyncHasHybridCollectionRaw(context, request, cq)); - } - ::grpc::Status DropHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Status* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> AsyncDropHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(AsyncDropHybridCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> PrepareAsyncDropHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(PrepareAsyncDropHybridCollectionRaw(context, request, cq)); - } - ::grpc::Status DescribeHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Mapping* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Mapping>> AsyncDescribeHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Mapping>>(AsyncDescribeHybridCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Mapping>> PrepareAsyncDescribeHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Mapping>>(PrepareAsyncDescribeHybridCollectionRaw(context, request, cq)); - } - ::grpc::Status CountHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::CollectionRowCount* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionRowCount>> AsyncCountHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionRowCount>>(AsyncCountHybridCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionRowCount>> PrepareAsyncCountHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionRowCount>>(PrepareAsyncCountHybridCollectionRaw(context, request, cq)); - } - ::grpc::Status ShowHybridCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::milvus::grpc::MappingList* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::MappingList>> AsyncShowHybridCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::MappingList>>(AsyncShowHybridCollectionsRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::MappingList>> PrepareAsyncShowHybridCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::MappingList>>(PrepareAsyncShowHybridCollectionsRaw(context, request, cq)); - } - ::grpc::Status ShowHybridCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::CollectionInfo* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionInfo>> AsyncShowHybridCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionInfo>>(AsyncShowHybridCollectionInfoRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionInfo>> PrepareAsyncShowHybridCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionInfo>>(PrepareAsyncShowHybridCollectionInfoRaw(context, request, cq)); - } - ::grpc::Status PreloadHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::milvus::grpc::Status* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> AsyncPreloadHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(AsyncPreloadHybridCollectionRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> PrepareAsyncPreloadHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(PrepareAsyncPreloadHybridCollectionRaw(context, request, cq)); - } - ::grpc::Status InsertEntity(::grpc::ClientContext* context, const ::milvus::grpc::HInsertParam& request, ::milvus::grpc::HEntityIDs* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntityIDs>> AsyncInsertEntity(::grpc::ClientContext* context, const ::milvus::grpc::HInsertParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntityIDs>>(AsyncInsertEntityRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntityIDs>> PrepareAsyncInsertEntity(::grpc::ClientContext* context, const ::milvus::grpc::HInsertParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntityIDs>>(PrepareAsyncInsertEntityRaw(context, request, cq)); - } - ::grpc::Status HybridSearch(::grpc::ClientContext* context, const ::milvus::grpc::HSearchParam& request, ::milvus::grpc::TopKQueryResult* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>> AsyncHybridSearch(::grpc::ClientContext* context, const ::milvus::grpc::HSearchParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>>(AsyncHybridSearchRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>> PrepareAsyncHybridSearch(::grpc::ClientContext* context, const ::milvus::grpc::HSearchParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>>(PrepareAsyncHybridSearchRaw(context, request, cq)); - } - ::grpc::Status HybridSearchInSegments(::grpc::ClientContext* context, const ::milvus::grpc::HSearchInSegmentsParam& request, ::milvus::grpc::TopKQueryResult* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>> AsyncHybridSearchInSegments(::grpc::ClientContext* context, const ::milvus::grpc::HSearchInSegmentsParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>>(AsyncHybridSearchInSegmentsRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>> PrepareAsyncHybridSearchInSegments(::grpc::ClientContext* context, const ::milvus::grpc::HSearchInSegmentsParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>>(PrepareAsyncHybridSearchInSegmentsRaw(context, request, cq)); - } - ::grpc::Status GetEntityByID(::grpc::ClientContext* context, const ::milvus::grpc::HEntityIdentity& request, ::milvus::grpc::HEntity* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntity>> AsyncGetEntityByID(::grpc::ClientContext* context, const ::milvus::grpc::HEntityIdentity& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntity>>(AsyncGetEntityByIDRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntity>> PrepareAsyncGetEntityByID(::grpc::ClientContext* context, const ::milvus::grpc::HEntityIdentity& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntity>>(PrepareAsyncGetEntityByIDRaw(context, request, cq)); - } - ::grpc::Status GetEntityIDs(::grpc::ClientContext* context, const ::milvus::grpc::HGetEntityIDsParam& request, ::milvus::grpc::HEntityIDs* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntityIDs>> AsyncGetEntityIDs(::grpc::ClientContext* context, const ::milvus::grpc::HGetEntityIDsParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntityIDs>>(AsyncGetEntityIDsRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntityIDs>> PrepareAsyncGetEntityIDs(::grpc::ClientContext* context, const ::milvus::grpc::HGetEntityIDsParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntityIDs>>(PrepareAsyncGetEntityIDsRaw(context, request, cq)); - } - ::grpc::Status DeleteEntitiesByID(::grpc::ClientContext* context, const ::milvus::grpc::HDeleteByIDParam& request, ::milvus::grpc::Status* response) override; - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> AsyncDeleteEntitiesByID(::grpc::ClientContext* context, const ::milvus::grpc::HDeleteByIDParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(AsyncDeleteEntitiesByIDRaw(context, request, cq)); - } - std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>> PrepareAsyncDeleteEntitiesByID(::grpc::ClientContext* context, const ::milvus::grpc::HDeleteByIDParam& request, ::grpc::CompletionQueue* cq) { - return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>>(PrepareAsyncDeleteEntitiesByIDRaw(context, request, cq)); - } - class experimental_async final : - public StubInterface::experimental_async_interface { - public: - void CreateCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionSchema* request, ::milvus::grpc::Status* response, std::function) override; - void CreateCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) override; - void CreateCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionSchema* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void CreateCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void HasCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::BoolReply* response, std::function) override; - void HasCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::BoolReply* response, std::function) override; - void HasCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::BoolReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void HasCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::BoolReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void DescribeCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionSchema* response, std::function) override; - void DescribeCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionSchema* response, std::function) override; - void DescribeCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionSchema* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void DescribeCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionSchema* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void CountCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionRowCount* response, std::function) override; - void CountCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionRowCount* response, std::function) override; - void CountCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionRowCount* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void CountCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionRowCount* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void ShowCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::CollectionNameList* response, std::function) override; - void ShowCollections(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionNameList* response, std::function) override; - void ShowCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::CollectionNameList* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void ShowCollections(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionNameList* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void ShowCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionInfo* response, std::function) override; - void ShowCollectionInfo(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionInfo* response, std::function) override; - void ShowCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionInfo* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void ShowCollectionInfo(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionInfo* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void DropCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, std::function) override; - void DropCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) override; - void DropCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void DropCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void CreateIndex(::grpc::ClientContext* context, const ::milvus::grpc::IndexParam* request, ::milvus::grpc::Status* response, std::function) override; - void CreateIndex(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) override; - void CreateIndex(::grpc::ClientContext* context, const ::milvus::grpc::IndexParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void CreateIndex(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void DescribeIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::IndexParam* response, std::function) override; - void DescribeIndex(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::IndexParam* response, std::function) override; - void DescribeIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::IndexParam* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void DescribeIndex(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::IndexParam* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void DropIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, std::function) override; - void DropIndex(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) override; - void DropIndex(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void DropIndex(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void CreatePartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::Status* response, std::function) override; - void CreatePartition(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) override; - void CreatePartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void CreatePartition(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void HasPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::BoolReply* response, std::function) override; - void HasPartition(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::BoolReply* response, std::function) override; - void HasPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::BoolReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void HasPartition(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::BoolReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void ShowPartitions(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::PartitionList* response, std::function) override; - void ShowPartitions(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::PartitionList* response, std::function) override; - void ShowPartitions(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::PartitionList* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void ShowPartitions(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::PartitionList* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void DropPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::Status* response, std::function) override; - void DropPartition(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) override; - void DropPartition(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void DropPartition(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void Insert(::grpc::ClientContext* context, const ::milvus::grpc::InsertParam* request, ::milvus::grpc::VectorIds* response, std::function) override; - void Insert(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::VectorIds* response, std::function) override; - void Insert(::grpc::ClientContext* context, const ::milvus::grpc::InsertParam* request, ::milvus::grpc::VectorIds* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void Insert(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::VectorIds* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void GetVectorsByID(::grpc::ClientContext* context, const ::milvus::grpc::VectorsIdentity* request, ::milvus::grpc::VectorsData* response, std::function) override; - void GetVectorsByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::VectorsData* response, std::function) override; - void GetVectorsByID(::grpc::ClientContext* context, const ::milvus::grpc::VectorsIdentity* request, ::milvus::grpc::VectorsData* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void GetVectorsByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::VectorsData* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void GetVectorIDs(::grpc::ClientContext* context, const ::milvus::grpc::GetVectorIDsParam* request, ::milvus::grpc::VectorIds* response, std::function) override; - void GetVectorIDs(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::VectorIds* response, std::function) override; - void GetVectorIDs(::grpc::ClientContext* context, const ::milvus::grpc::GetVectorIDsParam* request, ::milvus::grpc::VectorIds* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void GetVectorIDs(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::VectorIds* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void Search(::grpc::ClientContext* context, const ::milvus::grpc::SearchParam* request, ::milvus::grpc::TopKQueryResult* response, std::function) override; - void Search(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, std::function) override; - void Search(::grpc::ClientContext* context, const ::milvus::grpc::SearchParam* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void Search(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void SearchByID(::grpc::ClientContext* context, const ::milvus::grpc::SearchByIDParam* request, ::milvus::grpc::TopKQueryResult* response, std::function) override; - void SearchByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, std::function) override; - void SearchByID(::grpc::ClientContext* context, const ::milvus::grpc::SearchByIDParam* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void SearchByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void SearchInFiles(::grpc::ClientContext* context, const ::milvus::grpc::SearchInFilesParam* request, ::milvus::grpc::TopKQueryResult* response, std::function) override; - void SearchInFiles(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, std::function) override; - void SearchInFiles(::grpc::ClientContext* context, const ::milvus::grpc::SearchInFilesParam* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void SearchInFiles(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void Cmd(::grpc::ClientContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::StringReply* response, std::function) override; - void Cmd(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::StringReply* response, std::function) override; - void Cmd(::grpc::ClientContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::StringReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void Cmd(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::StringReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void DeleteByID(::grpc::ClientContext* context, const ::milvus::grpc::DeleteByIDParam* request, ::milvus::grpc::Status* response, std::function) override; - void DeleteByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) override; - void DeleteByID(::grpc::ClientContext* context, const ::milvus::grpc::DeleteByIDParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void DeleteByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void PreloadCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, std::function) override; - void PreloadCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) override; - void PreloadCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void PreloadCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void ReloadSegments(::grpc::ClientContext* context, const ::milvus::grpc::ReLoadSegmentsParam* request, ::milvus::grpc::Status* response, std::function) override; - void ReloadSegments(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) override; - void ReloadSegments(::grpc::ClientContext* context, const ::milvus::grpc::ReLoadSegmentsParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void ReloadSegments(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void Flush(::grpc::ClientContext* context, const ::milvus::grpc::FlushParam* request, ::milvus::grpc::Status* response, std::function) override; - void Flush(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) override; - void Flush(::grpc::ClientContext* context, const ::milvus::grpc::FlushParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void Flush(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void Compact(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, std::function) override; - void Compact(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) override; - void Compact(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void Compact(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void CreateHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::Mapping* request, ::milvus::grpc::Status* response, std::function) override; - void CreateHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) override; - void CreateHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::Mapping* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void CreateHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void HasHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::BoolReply* response, std::function) override; - void HasHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::BoolReply* response, std::function) override; - void HasHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::BoolReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void HasHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::BoolReply* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void DropHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, std::function) override; - void DropHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) override; - void DropHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void DropHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void DescribeHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Mapping* response, std::function) override; - void DescribeHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Mapping* response, std::function) override; - void DescribeHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Mapping* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void DescribeHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Mapping* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void CountHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionRowCount* response, std::function) override; - void CountHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionRowCount* response, std::function) override; - void CountHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionRowCount* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void CountHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionRowCount* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void ShowHybridCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::MappingList* response, std::function) override; - void ShowHybridCollections(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::MappingList* response, std::function) override; - void ShowHybridCollections(::grpc::ClientContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::MappingList* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void ShowHybridCollections(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::MappingList* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void ShowHybridCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionInfo* response, std::function) override; - void ShowHybridCollectionInfo(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionInfo* response, std::function) override; - void ShowHybridCollectionInfo(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionInfo* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void ShowHybridCollectionInfo(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::CollectionInfo* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void PreloadHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, std::function) override; - void PreloadHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) override; - void PreloadHybridCollection(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void PreloadHybridCollection(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void InsertEntity(::grpc::ClientContext* context, const ::milvus::grpc::HInsertParam* request, ::milvus::grpc::HEntityIDs* response, std::function) override; - void InsertEntity(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::HEntityIDs* response, std::function) override; - void InsertEntity(::grpc::ClientContext* context, const ::milvus::grpc::HInsertParam* request, ::milvus::grpc::HEntityIDs* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void InsertEntity(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::HEntityIDs* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void HybridSearch(::grpc::ClientContext* context, const ::milvus::grpc::HSearchParam* request, ::milvus::grpc::TopKQueryResult* response, std::function) override; - void HybridSearch(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, std::function) override; - void HybridSearch(::grpc::ClientContext* context, const ::milvus::grpc::HSearchParam* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void HybridSearch(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void HybridSearchInSegments(::grpc::ClientContext* context, const ::milvus::grpc::HSearchInSegmentsParam* request, ::milvus::grpc::TopKQueryResult* response, std::function) override; - void HybridSearchInSegments(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, std::function) override; - void HybridSearchInSegments(::grpc::ClientContext* context, const ::milvus::grpc::HSearchInSegmentsParam* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void HybridSearchInSegments(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::TopKQueryResult* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void GetEntityByID(::grpc::ClientContext* context, const ::milvus::grpc::HEntityIdentity* request, ::milvus::grpc::HEntity* response, std::function) override; - void GetEntityByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::HEntity* response, std::function) override; - void GetEntityByID(::grpc::ClientContext* context, const ::milvus::grpc::HEntityIdentity* request, ::milvus::grpc::HEntity* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void GetEntityByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::HEntity* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void GetEntityIDs(::grpc::ClientContext* context, const ::milvus::grpc::HGetEntityIDsParam* request, ::milvus::grpc::HEntityIDs* response, std::function) override; - void GetEntityIDs(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::HEntityIDs* response, std::function) override; - void GetEntityIDs(::grpc::ClientContext* context, const ::milvus::grpc::HGetEntityIDsParam* request, ::milvus::grpc::HEntityIDs* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void GetEntityIDs(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::HEntityIDs* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void DeleteEntitiesByID(::grpc::ClientContext* context, const ::milvus::grpc::HDeleteByIDParam* request, ::milvus::grpc::Status* response, std::function) override; - void DeleteEntitiesByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, std::function) override; - void DeleteEntitiesByID(::grpc::ClientContext* context, const ::milvus::grpc::HDeleteByIDParam* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - void DeleteEntitiesByID(::grpc::ClientContext* context, const ::grpc::ByteBuffer* request, ::milvus::grpc::Status* response, ::grpc::experimental::ClientUnaryReactor* reactor) override; - private: - friend class Stub; - explicit experimental_async(Stub* stub): stub_(stub) { } - Stub* stub() { return stub_; } - Stub* stub_; - }; - class experimental_async_interface* experimental_async() override { return &async_stub_; } - - private: - std::shared_ptr< ::grpc::ChannelInterface> channel_; - class experimental_async async_stub_{this}; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* AsyncCreateCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionSchema& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* PrepareAsyncCreateCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionSchema& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>* AsyncHasCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>* PrepareAsyncHasCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionSchema>* AsyncDescribeCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionSchema>* PrepareAsyncDescribeCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionRowCount>* AsyncCountCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionRowCount>* PrepareAsyncCountCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionNameList>* AsyncShowCollectionsRaw(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionNameList>* PrepareAsyncShowCollectionsRaw(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionInfo>* AsyncShowCollectionInfoRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionInfo>* PrepareAsyncShowCollectionInfoRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* AsyncDropCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* PrepareAsyncDropCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* AsyncCreateIndexRaw(::grpc::ClientContext* context, const ::milvus::grpc::IndexParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* PrepareAsyncCreateIndexRaw(::grpc::ClientContext* context, const ::milvus::grpc::IndexParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::IndexParam>* AsyncDescribeIndexRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::IndexParam>* PrepareAsyncDescribeIndexRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* AsyncDropIndexRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* PrepareAsyncDropIndexRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* AsyncCreatePartitionRaw(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* PrepareAsyncCreatePartitionRaw(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>* AsyncHasPartitionRaw(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>* PrepareAsyncHasPartitionRaw(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::PartitionList>* AsyncShowPartitionsRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::PartitionList>* PrepareAsyncShowPartitionsRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* AsyncDropPartitionRaw(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* PrepareAsyncDropPartitionRaw(::grpc::ClientContext* context, const ::milvus::grpc::PartitionParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorIds>* AsyncInsertRaw(::grpc::ClientContext* context, const ::milvus::grpc::InsertParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorIds>* PrepareAsyncInsertRaw(::grpc::ClientContext* context, const ::milvus::grpc::InsertParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorsData>* AsyncGetVectorsByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::VectorsIdentity& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorsData>* PrepareAsyncGetVectorsByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::VectorsIdentity& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorIds>* AsyncGetVectorIDsRaw(::grpc::ClientContext* context, const ::milvus::grpc::GetVectorIDsParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::VectorIds>* PrepareAsyncGetVectorIDsRaw(::grpc::ClientContext* context, const ::milvus::grpc::GetVectorIDsParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* AsyncSearchRaw(::grpc::ClientContext* context, const ::milvus::grpc::SearchParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* PrepareAsyncSearchRaw(::grpc::ClientContext* context, const ::milvus::grpc::SearchParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* AsyncSearchByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::SearchByIDParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* PrepareAsyncSearchByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::SearchByIDParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* AsyncSearchInFilesRaw(::grpc::ClientContext* context, const ::milvus::grpc::SearchInFilesParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* PrepareAsyncSearchInFilesRaw(::grpc::ClientContext* context, const ::milvus::grpc::SearchInFilesParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::StringReply>* AsyncCmdRaw(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::StringReply>* PrepareAsyncCmdRaw(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* AsyncDeleteByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::DeleteByIDParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* PrepareAsyncDeleteByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::DeleteByIDParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* AsyncPreloadCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* PrepareAsyncPreloadCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* AsyncReloadSegmentsRaw(::grpc::ClientContext* context, const ::milvus::grpc::ReLoadSegmentsParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* PrepareAsyncReloadSegmentsRaw(::grpc::ClientContext* context, const ::milvus::grpc::ReLoadSegmentsParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* AsyncFlushRaw(::grpc::ClientContext* context, const ::milvus::grpc::FlushParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* PrepareAsyncFlushRaw(::grpc::ClientContext* context, const ::milvus::grpc::FlushParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* AsyncCompactRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* PrepareAsyncCompactRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* AsyncCreateHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::Mapping& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* PrepareAsyncCreateHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::Mapping& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>* AsyncHasHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::BoolReply>* PrepareAsyncHasHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* AsyncDropHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* PrepareAsyncDropHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Mapping>* AsyncDescribeHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Mapping>* PrepareAsyncDescribeHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionRowCount>* AsyncCountHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionRowCount>* PrepareAsyncCountHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::MappingList>* AsyncShowHybridCollectionsRaw(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::MappingList>* PrepareAsyncShowHybridCollectionsRaw(::grpc::ClientContext* context, const ::milvus::grpc::Command& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionInfo>* AsyncShowHybridCollectionInfoRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::CollectionInfo>* PrepareAsyncShowHybridCollectionInfoRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* AsyncPreloadHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* PrepareAsyncPreloadHybridCollectionRaw(::grpc::ClientContext* context, const ::milvus::grpc::CollectionName& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntityIDs>* AsyncInsertEntityRaw(::grpc::ClientContext* context, const ::milvus::grpc::HInsertParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntityIDs>* PrepareAsyncInsertEntityRaw(::grpc::ClientContext* context, const ::milvus::grpc::HInsertParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* AsyncHybridSearchRaw(::grpc::ClientContext* context, const ::milvus::grpc::HSearchParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* PrepareAsyncHybridSearchRaw(::grpc::ClientContext* context, const ::milvus::grpc::HSearchParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* AsyncHybridSearchInSegmentsRaw(::grpc::ClientContext* context, const ::milvus::grpc::HSearchInSegmentsParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::TopKQueryResult>* PrepareAsyncHybridSearchInSegmentsRaw(::grpc::ClientContext* context, const ::milvus::grpc::HSearchInSegmentsParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntity>* AsyncGetEntityByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::HEntityIdentity& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntity>* PrepareAsyncGetEntityByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::HEntityIdentity& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntityIDs>* AsyncGetEntityIDsRaw(::grpc::ClientContext* context, const ::milvus::grpc::HGetEntityIDsParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::HEntityIDs>* PrepareAsyncGetEntityIDsRaw(::grpc::ClientContext* context, const ::milvus::grpc::HGetEntityIDsParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* AsyncDeleteEntitiesByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::HDeleteByIDParam& request, ::grpc::CompletionQueue* cq) override; - ::grpc::ClientAsyncResponseReader< ::milvus::grpc::Status>* PrepareAsyncDeleteEntitiesByIDRaw(::grpc::ClientContext* context, const ::milvus::grpc::HDeleteByIDParam& request, ::grpc::CompletionQueue* cq) override; - const ::grpc::internal::RpcMethod rpcmethod_CreateCollection_; - const ::grpc::internal::RpcMethod rpcmethod_HasCollection_; - const ::grpc::internal::RpcMethod rpcmethod_DescribeCollection_; - const ::grpc::internal::RpcMethod rpcmethod_CountCollection_; - const ::grpc::internal::RpcMethod rpcmethod_ShowCollections_; - const ::grpc::internal::RpcMethod rpcmethod_ShowCollectionInfo_; - const ::grpc::internal::RpcMethod rpcmethod_DropCollection_; - const ::grpc::internal::RpcMethod rpcmethod_CreateIndex_; - const ::grpc::internal::RpcMethod rpcmethod_DescribeIndex_; - const ::grpc::internal::RpcMethod rpcmethod_DropIndex_; - const ::grpc::internal::RpcMethod rpcmethod_CreatePartition_; - const ::grpc::internal::RpcMethod rpcmethod_HasPartition_; - const ::grpc::internal::RpcMethod rpcmethod_ShowPartitions_; - const ::grpc::internal::RpcMethod rpcmethod_DropPartition_; - const ::grpc::internal::RpcMethod rpcmethod_Insert_; - const ::grpc::internal::RpcMethod rpcmethod_GetVectorsByID_; - const ::grpc::internal::RpcMethod rpcmethod_GetVectorIDs_; - const ::grpc::internal::RpcMethod rpcmethod_Search_; - const ::grpc::internal::RpcMethod rpcmethod_SearchByID_; - const ::grpc::internal::RpcMethod rpcmethod_SearchInFiles_; - const ::grpc::internal::RpcMethod rpcmethod_Cmd_; - const ::grpc::internal::RpcMethod rpcmethod_DeleteByID_; - const ::grpc::internal::RpcMethod rpcmethod_PreloadCollection_; - const ::grpc::internal::RpcMethod rpcmethod_ReloadSegments_; - const ::grpc::internal::RpcMethod rpcmethod_Flush_; - const ::grpc::internal::RpcMethod rpcmethod_Compact_; - const ::grpc::internal::RpcMethod rpcmethod_CreateHybridCollection_; - const ::grpc::internal::RpcMethod rpcmethod_HasHybridCollection_; - const ::grpc::internal::RpcMethod rpcmethod_DropHybridCollection_; - const ::grpc::internal::RpcMethod rpcmethod_DescribeHybridCollection_; - const ::grpc::internal::RpcMethod rpcmethod_CountHybridCollection_; - const ::grpc::internal::RpcMethod rpcmethod_ShowHybridCollections_; - const ::grpc::internal::RpcMethod rpcmethod_ShowHybridCollectionInfo_; - const ::grpc::internal::RpcMethod rpcmethod_PreloadHybridCollection_; - const ::grpc::internal::RpcMethod rpcmethod_InsertEntity_; - const ::grpc::internal::RpcMethod rpcmethod_HybridSearch_; - const ::grpc::internal::RpcMethod rpcmethod_HybridSearchInSegments_; - const ::grpc::internal::RpcMethod rpcmethod_GetEntityByID_; - const ::grpc::internal::RpcMethod rpcmethod_GetEntityIDs_; - const ::grpc::internal::RpcMethod rpcmethod_DeleteEntitiesByID_; - }; - static std::unique_ptr NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options = ::grpc::StubOptions()); - - class Service : public ::grpc::Service { - public: - Service(); - virtual ~Service(); - // * - // @brief This method is used to create collection - // - // @param CollectionSchema, use to provide collection information to be created. - // - // @return Status - virtual ::grpc::Status CreateCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionSchema* request, ::milvus::grpc::Status* response); - // * - // @brief This method is used to test collection existence. - // - // @param CollectionName, collection name is going to be tested. - // - // @return BoolReply - virtual ::grpc::Status HasCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::BoolReply* response); - // * - // @brief This method is used to get collection schema. - // - // @param CollectionName, target collection name. - // - // @return CollectionSchema - virtual ::grpc::Status DescribeCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionSchema* response); - // * - // @brief This method is used to get collection schema. - // - // @param CollectionName, target collection name. - // - // @return CollectionRowCount - virtual ::grpc::Status CountCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionRowCount* response); - // * - // @brief This method is used to list all collections. - // - // @param Command, dummy parameter. - // - // @return CollectionNameList - virtual ::grpc::Status ShowCollections(::grpc::ServerContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::CollectionNameList* response); - // * - // @brief This method is used to get collection detail information. - // - // @param CollectionName, target collection name. - // - // @return CollectionInfo - virtual ::grpc::Status ShowCollectionInfo(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionInfo* response); - // * - // @brief This method is used to delete collection. - // - // @param CollectionName, collection name is going to be deleted. - // - // @return CollectionNameList - virtual ::grpc::Status DropCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response); - // * - // @brief This method is used to build index by collection in sync mode. - // - // @param IndexParam, index paramters. - // - // @return Status - virtual ::grpc::Status CreateIndex(::grpc::ServerContext* context, const ::milvus::grpc::IndexParam* request, ::milvus::grpc::Status* response); - // * - // @brief This method is used to describe index - // - // @param CollectionName, target collection name. - // - // @return IndexParam - virtual ::grpc::Status DescribeIndex(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::IndexParam* response); - // * - // @brief This method is used to drop index - // - // @param CollectionName, target collection name. - // - // @return Status - virtual ::grpc::Status DropIndex(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response); - // * - // @brief This method is used to create partition - // - // @param PartitionParam, partition parameters. - // - // @return Status - virtual ::grpc::Status CreatePartition(::grpc::ServerContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::Status* response); - // * - // @brief This method is used to test partition existence. - // - // @param PartitionParam, target partition. - // - // @return BoolReply - virtual ::grpc::Status HasPartition(::grpc::ServerContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::BoolReply* response); - // * - // @brief This method is used to show partition information - // - // @param CollectionName, target collection name. - // - // @return PartitionList - virtual ::grpc::Status ShowPartitions(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::PartitionList* response); - // * - // @brief This method is used to drop partition - // - // @param PartitionParam, target partition. - // - // @return Status - virtual ::grpc::Status DropPartition(::grpc::ServerContext* context, const ::milvus::grpc::PartitionParam* request, ::milvus::grpc::Status* response); - // * - // @brief This method is used to add vector array to collection. - // - // @param InsertParam, insert parameters. - // - // @return VectorIds - virtual ::grpc::Status Insert(::grpc::ServerContext* context, const ::milvus::grpc::InsertParam* request, ::milvus::grpc::VectorIds* response); - // * - // @brief This method is used to get vectors data by id array. - // - // @param VectorsIdentity, target vector id array. - // - // @return VectorsData - virtual ::grpc::Status GetVectorsByID(::grpc::ServerContext* context, const ::milvus::grpc::VectorsIdentity* request, ::milvus::grpc::VectorsData* response); - // * - // @brief This method is used to get vector ids from a segment - // - // @param GetVectorIDsParam, target collection and segment - // - // @return VectorIds - virtual ::grpc::Status GetVectorIDs(::grpc::ServerContext* context, const ::milvus::grpc::GetVectorIDsParam* request, ::milvus::grpc::VectorIds* response); - // * - // @brief This method is used to query vector in collection. - // - // @param SearchParam, search parameters. - // - // @return TopKQueryResult - virtual ::grpc::Status Search(::grpc::ServerContext* context, const ::milvus::grpc::SearchParam* request, ::milvus::grpc::TopKQueryResult* response); - // * - // @brief This method is used to query vector by id. - // - // @param SearchByIDParam, search parameters. - // - // @return TopKQueryResult - virtual ::grpc::Status SearchByID(::grpc::ServerContext* context, const ::milvus::grpc::SearchByIDParam* request, ::milvus::grpc::TopKQueryResult* response); - // * - // @brief This method is used to query vector in specified files. - // - // @param SearchInFilesParam, search in files paremeters. - // - // @return TopKQueryResult - virtual ::grpc::Status SearchInFiles(::grpc::ServerContext* context, const ::milvus::grpc::SearchInFilesParam* request, ::milvus::grpc::TopKQueryResult* response); - // * - // @brief This method is used to give the server status. - // - // @param Command, command string - // - // @return StringReply - virtual ::grpc::Status Cmd(::grpc::ServerContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::StringReply* response); - // * - // @brief This method is used to delete vector by id - // - // @param DeleteByIDParam, delete parameters. - // - // @return status - virtual ::grpc::Status DeleteByID(::grpc::ServerContext* context, const ::milvus::grpc::DeleteByIDParam* request, ::milvus::grpc::Status* response); - // * - // @brief This method is used to preload collection - // - // @param CollectionName, target collection name. - // - // @return Status - virtual ::grpc::Status PreloadCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response); - // * - // @brief This method is used to reload collection segments - // - // @param ReLoadSegmentsParam, target segments information. - // - // @return Status - virtual ::grpc::Status ReloadSegments(::grpc::ServerContext* context, const ::milvus::grpc::ReLoadSegmentsParam* request, ::milvus::grpc::Status* response); - // * - // @brief This method is used to flush buffer into storage. - // - // @param FlushParam, flush parameters - // - // @return Status - virtual ::grpc::Status Flush(::grpc::ServerContext* context, const ::milvus::grpc::FlushParam* request, ::milvus::grpc::Status* response); - // * - // @brief This method is used to compact collection - // - // @param CollectionName, target collection name. - // - // @return Status - virtual ::grpc::Status Compact(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response); - // *******************************New Interface******************************************* - // - virtual ::grpc::Status CreateHybridCollection(::grpc::ServerContext* context, const ::milvus::grpc::Mapping* request, ::milvus::grpc::Status* response); - virtual ::grpc::Status HasHybridCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::BoolReply* response); - virtual ::grpc::Status DropHybridCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response); - virtual ::grpc::Status DescribeHybridCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Mapping* response); - virtual ::grpc::Status CountHybridCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionRowCount* response); - virtual ::grpc::Status ShowHybridCollections(::grpc::ServerContext* context, const ::milvus::grpc::Command* request, ::milvus::grpc::MappingList* response); - virtual ::grpc::Status ShowHybridCollectionInfo(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::CollectionInfo* response); - virtual ::grpc::Status PreloadHybridCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, ::milvus::grpc::Status* response); - // ///////////////////////////////////////////////////////////////// - // - // rpc CreateIndex(IndexParam) returns (Status) {} - // - // rpc DescribeIndex(CollectionName) returns (IndexParam) {} - // - // rpc DropIndex(CollectionName) returns (Status) {} - // - // ///////////////////////////////////////////////////////////////// - // - virtual ::grpc::Status InsertEntity(::grpc::ServerContext* context, const ::milvus::grpc::HInsertParam* request, ::milvus::grpc::HEntityIDs* response); - // TODO(yukun): will change to HQueryResult - virtual ::grpc::Status HybridSearch(::grpc::ServerContext* context, const ::milvus::grpc::HSearchParam* request, ::milvus::grpc::TopKQueryResult* response); - virtual ::grpc::Status HybridSearchInSegments(::grpc::ServerContext* context, const ::milvus::grpc::HSearchInSegmentsParam* request, ::milvus::grpc::TopKQueryResult* response); - virtual ::grpc::Status GetEntityByID(::grpc::ServerContext* context, const ::milvus::grpc::HEntityIdentity* request, ::milvus::grpc::HEntity* response); - virtual ::grpc::Status GetEntityIDs(::grpc::ServerContext* context, const ::milvus::grpc::HGetEntityIDsParam* request, ::milvus::grpc::HEntityIDs* response); - virtual ::grpc::Status DeleteEntitiesByID(::grpc::ServerContext* context, const ::milvus::grpc::HDeleteByIDParam* request, ::milvus::grpc::Status* response); - }; - template - class WithAsyncMethod_CreateCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_CreateCollection() { - ::grpc::Service::MarkMethodAsync(0); - } - ~WithAsyncMethod_CreateCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreateCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionSchema* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestCreateCollection(::grpc::ServerContext* context, ::milvus::grpc::CollectionSchema* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::Status>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(0, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_HasCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_HasCollection() { - ::grpc::Service::MarkMethodAsync(1); - } - ~WithAsyncMethod_HasCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HasCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::BoolReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestHasCollection(::grpc::ServerContext* context, ::milvus::grpc::CollectionName* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::BoolReply>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(1, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_DescribeCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_DescribeCollection() { - ::grpc::Service::MarkMethodAsync(2); - } - ~WithAsyncMethod_DescribeCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DescribeCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionSchema* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestDescribeCollection(::grpc::ServerContext* context, ::milvus::grpc::CollectionName* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::CollectionSchema>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(2, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_CountCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_CountCollection() { - ::grpc::Service::MarkMethodAsync(3); - } - ~WithAsyncMethod_CountCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CountCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionRowCount* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestCountCollection(::grpc::ServerContext* context, ::milvus::grpc::CollectionName* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::CollectionRowCount>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(3, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_ShowCollections : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_ShowCollections() { - ::grpc::Service::MarkMethodAsync(4); - } - ~WithAsyncMethod_ShowCollections() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowCollections(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::CollectionNameList* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestShowCollections(::grpc::ServerContext* context, ::milvus::grpc::Command* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::CollectionNameList>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(4, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_ShowCollectionInfo : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_ShowCollectionInfo() { - ::grpc::Service::MarkMethodAsync(5); - } - ~WithAsyncMethod_ShowCollectionInfo() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowCollectionInfo(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionInfo* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestShowCollectionInfo(::grpc::ServerContext* context, ::milvus::grpc::CollectionName* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::CollectionInfo>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(5, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_DropCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_DropCollection() { - ::grpc::Service::MarkMethodAsync(6); - } - ~WithAsyncMethod_DropCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestDropCollection(::grpc::ServerContext* context, ::milvus::grpc::CollectionName* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::Status>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(6, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_CreateIndex : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_CreateIndex() { - ::grpc::Service::MarkMethodAsync(7); - } - ~WithAsyncMethod_CreateIndex() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreateIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::IndexParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestCreateIndex(::grpc::ServerContext* context, ::milvus::grpc::IndexParam* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::Status>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(7, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_DescribeIndex : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_DescribeIndex() { - ::grpc::Service::MarkMethodAsync(8); - } - ~WithAsyncMethod_DescribeIndex() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DescribeIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::IndexParam* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestDescribeIndex(::grpc::ServerContext* context, ::milvus::grpc::CollectionName* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::IndexParam>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(8, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_DropIndex : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_DropIndex() { - ::grpc::Service::MarkMethodAsync(9); - } - ~WithAsyncMethod_DropIndex() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestDropIndex(::grpc::ServerContext* context, ::milvus::grpc::CollectionName* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::Status>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(9, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_CreatePartition : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_CreatePartition() { - ::grpc::Service::MarkMethodAsync(10); - } - ~WithAsyncMethod_CreatePartition() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreatePartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestCreatePartition(::grpc::ServerContext* context, ::milvus::grpc::PartitionParam* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::Status>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(10, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_HasPartition : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_HasPartition() { - ::grpc::Service::MarkMethodAsync(11); - } - ~WithAsyncMethod_HasPartition() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HasPartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::BoolReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestHasPartition(::grpc::ServerContext* context, ::milvus::grpc::PartitionParam* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::BoolReply>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(11, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_ShowPartitions : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_ShowPartitions() { - ::grpc::Service::MarkMethodAsync(12); - } - ~WithAsyncMethod_ShowPartitions() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowPartitions(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::PartitionList* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestShowPartitions(::grpc::ServerContext* context, ::milvus::grpc::CollectionName* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::PartitionList>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(12, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_DropPartition : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_DropPartition() { - ::grpc::Service::MarkMethodAsync(13); - } - ~WithAsyncMethod_DropPartition() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropPartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestDropPartition(::grpc::ServerContext* context, ::milvus::grpc::PartitionParam* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::Status>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(13, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_Insert : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_Insert() { - ::grpc::Service::MarkMethodAsync(14); - } - ~WithAsyncMethod_Insert() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Insert(::grpc::ServerContext* /*context*/, const ::milvus::grpc::InsertParam* /*request*/, ::milvus::grpc::VectorIds* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestInsert(::grpc::ServerContext* context, ::milvus::grpc::InsertParam* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::VectorIds>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(14, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_GetVectorsByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_GetVectorsByID() { - ::grpc::Service::MarkMethodAsync(15); - } - ~WithAsyncMethod_GetVectorsByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetVectorsByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::VectorsIdentity* /*request*/, ::milvus::grpc::VectorsData* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestGetVectorsByID(::grpc::ServerContext* context, ::milvus::grpc::VectorsIdentity* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::VectorsData>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(15, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_GetVectorIDs : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_GetVectorIDs() { - ::grpc::Service::MarkMethodAsync(16); - } - ~WithAsyncMethod_GetVectorIDs() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetVectorIDs(::grpc::ServerContext* /*context*/, const ::milvus::grpc::GetVectorIDsParam* /*request*/, ::milvus::grpc::VectorIds* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestGetVectorIDs(::grpc::ServerContext* context, ::milvus::grpc::GetVectorIDsParam* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::VectorIds>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(16, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_Search : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_Search() { - ::grpc::Service::MarkMethodAsync(17); - } - ~WithAsyncMethod_Search() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Search(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestSearch(::grpc::ServerContext* context, ::milvus::grpc::SearchParam* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::TopKQueryResult>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(17, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_SearchByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_SearchByID() { - ::grpc::Service::MarkMethodAsync(18); - } - ~WithAsyncMethod_SearchByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status SearchByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchByIDParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestSearchByID(::grpc::ServerContext* context, ::milvus::grpc::SearchByIDParam* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::TopKQueryResult>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(18, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_SearchInFiles : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_SearchInFiles() { - ::grpc::Service::MarkMethodAsync(19); - } - ~WithAsyncMethod_SearchInFiles() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status SearchInFiles(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchInFilesParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestSearchInFiles(::grpc::ServerContext* context, ::milvus::grpc::SearchInFilesParam* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::TopKQueryResult>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(19, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_Cmd : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_Cmd() { - ::grpc::Service::MarkMethodAsync(20); - } - ~WithAsyncMethod_Cmd() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Cmd(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::StringReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestCmd(::grpc::ServerContext* context, ::milvus::grpc::Command* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::StringReply>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(20, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_DeleteByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_DeleteByID() { - ::grpc::Service::MarkMethodAsync(21); - } - ~WithAsyncMethod_DeleteByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DeleteByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::DeleteByIDParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestDeleteByID(::grpc::ServerContext* context, ::milvus::grpc::DeleteByIDParam* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::Status>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(21, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_PreloadCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_PreloadCollection() { - ::grpc::Service::MarkMethodAsync(22); - } - ~WithAsyncMethod_PreloadCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status PreloadCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestPreloadCollection(::grpc::ServerContext* context, ::milvus::grpc::CollectionName* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::Status>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(22, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_ReloadSegments : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_ReloadSegments() { - ::grpc::Service::MarkMethodAsync(23); - } - ~WithAsyncMethod_ReloadSegments() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ReloadSegments(::grpc::ServerContext* /*context*/, const ::milvus::grpc::ReLoadSegmentsParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestReloadSegments(::grpc::ServerContext* context, ::milvus::grpc::ReLoadSegmentsParam* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::Status>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(23, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_Flush : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_Flush() { - ::grpc::Service::MarkMethodAsync(24); - } - ~WithAsyncMethod_Flush() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Flush(::grpc::ServerContext* /*context*/, const ::milvus::grpc::FlushParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestFlush(::grpc::ServerContext* context, ::milvus::grpc::FlushParam* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::Status>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(24, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_Compact : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_Compact() { - ::grpc::Service::MarkMethodAsync(25); - } - ~WithAsyncMethod_Compact() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Compact(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestCompact(::grpc::ServerContext* context, ::milvus::grpc::CollectionName* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::Status>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(25, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_CreateHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_CreateHybridCollection() { - ::grpc::Service::MarkMethodAsync(26); - } - ~WithAsyncMethod_CreateHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreateHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Mapping* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestCreateHybridCollection(::grpc::ServerContext* context, ::milvus::grpc::Mapping* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::Status>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(26, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_HasHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_HasHybridCollection() { - ::grpc::Service::MarkMethodAsync(27); - } - ~WithAsyncMethod_HasHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HasHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::BoolReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestHasHybridCollection(::grpc::ServerContext* context, ::milvus::grpc::CollectionName* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::BoolReply>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(27, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_DropHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_DropHybridCollection() { - ::grpc::Service::MarkMethodAsync(28); - } - ~WithAsyncMethod_DropHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestDropHybridCollection(::grpc::ServerContext* context, ::milvus::grpc::CollectionName* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::Status>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(28, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_DescribeHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_DescribeHybridCollection() { - ::grpc::Service::MarkMethodAsync(29); - } - ~WithAsyncMethod_DescribeHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DescribeHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Mapping* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestDescribeHybridCollection(::grpc::ServerContext* context, ::milvus::grpc::CollectionName* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::Mapping>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(29, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_CountHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_CountHybridCollection() { - ::grpc::Service::MarkMethodAsync(30); - } - ~WithAsyncMethod_CountHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CountHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionRowCount* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestCountHybridCollection(::grpc::ServerContext* context, ::milvus::grpc::CollectionName* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::CollectionRowCount>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(30, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_ShowHybridCollections : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_ShowHybridCollections() { - ::grpc::Service::MarkMethodAsync(31); - } - ~WithAsyncMethod_ShowHybridCollections() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowHybridCollections(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::MappingList* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestShowHybridCollections(::grpc::ServerContext* context, ::milvus::grpc::Command* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::MappingList>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(31, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_ShowHybridCollectionInfo : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_ShowHybridCollectionInfo() { - ::grpc::Service::MarkMethodAsync(32); - } - ~WithAsyncMethod_ShowHybridCollectionInfo() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowHybridCollectionInfo(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionInfo* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestShowHybridCollectionInfo(::grpc::ServerContext* context, ::milvus::grpc::CollectionName* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::CollectionInfo>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(32, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_PreloadHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_PreloadHybridCollection() { - ::grpc::Service::MarkMethodAsync(33); - } - ~WithAsyncMethod_PreloadHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status PreloadHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestPreloadHybridCollection(::grpc::ServerContext* context, ::milvus::grpc::CollectionName* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::Status>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(33, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_InsertEntity : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_InsertEntity() { - ::grpc::Service::MarkMethodAsync(34); - } - ~WithAsyncMethod_InsertEntity() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status InsertEntity(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HInsertParam* /*request*/, ::milvus::grpc::HEntityIDs* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestInsertEntity(::grpc::ServerContext* context, ::milvus::grpc::HInsertParam* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::HEntityIDs>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(34, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_HybridSearch : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_HybridSearch() { - ::grpc::Service::MarkMethodAsync(35); - } - ~WithAsyncMethod_HybridSearch() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HybridSearch(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HSearchParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestHybridSearch(::grpc::ServerContext* context, ::milvus::grpc::HSearchParam* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::TopKQueryResult>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(35, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_HybridSearchInSegments : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_HybridSearchInSegments() { - ::grpc::Service::MarkMethodAsync(36); - } - ~WithAsyncMethod_HybridSearchInSegments() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HybridSearchInSegments(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HSearchInSegmentsParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestHybridSearchInSegments(::grpc::ServerContext* context, ::milvus::grpc::HSearchInSegmentsParam* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::TopKQueryResult>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(36, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_GetEntityByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_GetEntityByID() { - ::grpc::Service::MarkMethodAsync(37); - } - ~WithAsyncMethod_GetEntityByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetEntityByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HEntityIdentity* /*request*/, ::milvus::grpc::HEntity* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestGetEntityByID(::grpc::ServerContext* context, ::milvus::grpc::HEntityIdentity* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::HEntity>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(37, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_GetEntityIDs : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_GetEntityIDs() { - ::grpc::Service::MarkMethodAsync(38); - } - ~WithAsyncMethod_GetEntityIDs() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetEntityIDs(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HGetEntityIDsParam* /*request*/, ::milvus::grpc::HEntityIDs* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestGetEntityIDs(::grpc::ServerContext* context, ::milvus::grpc::HGetEntityIDsParam* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::HEntityIDs>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(38, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithAsyncMethod_DeleteEntitiesByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithAsyncMethod_DeleteEntitiesByID() { - ::grpc::Service::MarkMethodAsync(39); - } - ~WithAsyncMethod_DeleteEntitiesByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DeleteEntitiesByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HDeleteByIDParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestDeleteEntitiesByID(::grpc::ServerContext* context, ::milvus::grpc::HDeleteByIDParam* request, ::grpc::ServerAsyncResponseWriter< ::milvus::grpc::Status>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(39, context, request, response, new_call_cq, notification_cq, tag); - } - }; - typedef WithAsyncMethod_CreateCollection > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > AsyncService; - template - class ExperimentalWithCallbackMethod_CreateCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_CreateCollection() { - ::grpc::Service::experimental().MarkMethodCallback(0, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionSchema, ::milvus::grpc::Status>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::CollectionSchema* request, - ::milvus::grpc::Status* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->CreateCollection(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_CreateCollection( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::CollectionSchema, ::milvus::grpc::Status>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionSchema, ::milvus::grpc::Status>*>( - ::grpc::Service::experimental().GetHandler(0)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_CreateCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreateCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionSchema* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void CreateCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionSchema* /*request*/, ::milvus::grpc::Status* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_HasCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_HasCollection() { - ::grpc::Service::experimental().MarkMethodCallback(1, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::BoolReply>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::BoolReply* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->HasCollection(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_HasCollection( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::CollectionName, ::milvus::grpc::BoolReply>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::BoolReply>*>( - ::grpc::Service::experimental().GetHandler(1)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_HasCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HasCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::BoolReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void HasCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::BoolReply* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_DescribeCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_DescribeCollection() { - ::grpc::Service::experimental().MarkMethodCallback(2, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionSchema>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::CollectionSchema* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->DescribeCollection(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_DescribeCollection( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionSchema>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionSchema>*>( - ::grpc::Service::experimental().GetHandler(2)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_DescribeCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DescribeCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionSchema* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void DescribeCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionSchema* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_CountCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_CountCollection() { - ::grpc::Service::experimental().MarkMethodCallback(3, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionRowCount>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::CollectionRowCount* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->CountCollection(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_CountCollection( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionRowCount>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionRowCount>*>( - ::grpc::Service::experimental().GetHandler(3)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_CountCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CountCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionRowCount* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void CountCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionRowCount* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_ShowCollections : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_ShowCollections() { - ::grpc::Service::experimental().MarkMethodCallback(4, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::Command, ::milvus::grpc::CollectionNameList>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::Command* request, - ::milvus::grpc::CollectionNameList* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->ShowCollections(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_ShowCollections( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::Command, ::milvus::grpc::CollectionNameList>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::Command, ::milvus::grpc::CollectionNameList>*>( - ::grpc::Service::experimental().GetHandler(4)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_ShowCollections() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowCollections(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::CollectionNameList* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void ShowCollections(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::CollectionNameList* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_ShowCollectionInfo : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_ShowCollectionInfo() { - ::grpc::Service::experimental().MarkMethodCallback(5, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionInfo>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::CollectionInfo* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->ShowCollectionInfo(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_ShowCollectionInfo( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionInfo>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionInfo>*>( - ::grpc::Service::experimental().GetHandler(5)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_ShowCollectionInfo() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowCollectionInfo(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionInfo* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void ShowCollectionInfo(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionInfo* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_DropCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_DropCollection() { - ::grpc::Service::experimental().MarkMethodCallback(6, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::Status* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->DropCollection(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_DropCollection( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>*>( - ::grpc::Service::experimental().GetHandler(6)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_DropCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void DropCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_CreateIndex : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_CreateIndex() { - ::grpc::Service::experimental().MarkMethodCallback(7, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::IndexParam, ::milvus::grpc::Status>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::IndexParam* request, - ::milvus::grpc::Status* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->CreateIndex(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_CreateIndex( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::IndexParam, ::milvus::grpc::Status>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::IndexParam, ::milvus::grpc::Status>*>( - ::grpc::Service::experimental().GetHandler(7)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_CreateIndex() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreateIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::IndexParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void CreateIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::IndexParam* /*request*/, ::milvus::grpc::Status* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_DescribeIndex : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_DescribeIndex() { - ::grpc::Service::experimental().MarkMethodCallback(8, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::IndexParam>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::IndexParam* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->DescribeIndex(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_DescribeIndex( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::CollectionName, ::milvus::grpc::IndexParam>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::IndexParam>*>( - ::grpc::Service::experimental().GetHandler(8)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_DescribeIndex() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DescribeIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::IndexParam* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void DescribeIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::IndexParam* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_DropIndex : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_DropIndex() { - ::grpc::Service::experimental().MarkMethodCallback(9, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::Status* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->DropIndex(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_DropIndex( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>*>( - ::grpc::Service::experimental().GetHandler(9)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_DropIndex() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void DropIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_CreatePartition : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_CreatePartition() { - ::grpc::Service::experimental().MarkMethodCallback(10, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::PartitionParam, ::milvus::grpc::Status>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::PartitionParam* request, - ::milvus::grpc::Status* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->CreatePartition(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_CreatePartition( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::PartitionParam, ::milvus::grpc::Status>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::PartitionParam, ::milvus::grpc::Status>*>( - ::grpc::Service::experimental().GetHandler(10)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_CreatePartition() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreatePartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void CreatePartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::Status* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_HasPartition : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_HasPartition() { - ::grpc::Service::experimental().MarkMethodCallback(11, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::PartitionParam, ::milvus::grpc::BoolReply>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::PartitionParam* request, - ::milvus::grpc::BoolReply* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->HasPartition(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_HasPartition( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::PartitionParam, ::milvus::grpc::BoolReply>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::PartitionParam, ::milvus::grpc::BoolReply>*>( - ::grpc::Service::experimental().GetHandler(11)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_HasPartition() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HasPartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::BoolReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void HasPartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::BoolReply* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_ShowPartitions : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_ShowPartitions() { - ::grpc::Service::experimental().MarkMethodCallback(12, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::PartitionList>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::PartitionList* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->ShowPartitions(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_ShowPartitions( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::CollectionName, ::milvus::grpc::PartitionList>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::PartitionList>*>( - ::grpc::Service::experimental().GetHandler(12)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_ShowPartitions() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowPartitions(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::PartitionList* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void ShowPartitions(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::PartitionList* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_DropPartition : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_DropPartition() { - ::grpc::Service::experimental().MarkMethodCallback(13, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::PartitionParam, ::milvus::grpc::Status>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::PartitionParam* request, - ::milvus::grpc::Status* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->DropPartition(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_DropPartition( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::PartitionParam, ::milvus::grpc::Status>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::PartitionParam, ::milvus::grpc::Status>*>( - ::grpc::Service::experimental().GetHandler(13)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_DropPartition() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropPartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void DropPartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::Status* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_Insert : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_Insert() { - ::grpc::Service::experimental().MarkMethodCallback(14, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::InsertParam, ::milvus::grpc::VectorIds>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::InsertParam* request, - ::milvus::grpc::VectorIds* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->Insert(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_Insert( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::InsertParam, ::milvus::grpc::VectorIds>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::InsertParam, ::milvus::grpc::VectorIds>*>( - ::grpc::Service::experimental().GetHandler(14)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_Insert() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Insert(::grpc::ServerContext* /*context*/, const ::milvus::grpc::InsertParam* /*request*/, ::milvus::grpc::VectorIds* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void Insert(::grpc::ServerContext* /*context*/, const ::milvus::grpc::InsertParam* /*request*/, ::milvus::grpc::VectorIds* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_GetVectorsByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_GetVectorsByID() { - ::grpc::Service::experimental().MarkMethodCallback(15, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::VectorsIdentity, ::milvus::grpc::VectorsData>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::VectorsIdentity* request, - ::milvus::grpc::VectorsData* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->GetVectorsByID(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_GetVectorsByID( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::VectorsIdentity, ::milvus::grpc::VectorsData>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::VectorsIdentity, ::milvus::grpc::VectorsData>*>( - ::grpc::Service::experimental().GetHandler(15)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_GetVectorsByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetVectorsByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::VectorsIdentity* /*request*/, ::milvus::grpc::VectorsData* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void GetVectorsByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::VectorsIdentity* /*request*/, ::milvus::grpc::VectorsData* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_GetVectorIDs : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_GetVectorIDs() { - ::grpc::Service::experimental().MarkMethodCallback(16, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::GetVectorIDsParam, ::milvus::grpc::VectorIds>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::GetVectorIDsParam* request, - ::milvus::grpc::VectorIds* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->GetVectorIDs(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_GetVectorIDs( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::GetVectorIDsParam, ::milvus::grpc::VectorIds>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::GetVectorIDsParam, ::milvus::grpc::VectorIds>*>( - ::grpc::Service::experimental().GetHandler(16)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_GetVectorIDs() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetVectorIDs(::grpc::ServerContext* /*context*/, const ::milvus::grpc::GetVectorIDsParam* /*request*/, ::milvus::grpc::VectorIds* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void GetVectorIDs(::grpc::ServerContext* /*context*/, const ::milvus::grpc::GetVectorIDsParam* /*request*/, ::milvus::grpc::VectorIds* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_Search : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_Search() { - ::grpc::Service::experimental().MarkMethodCallback(17, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::SearchParam, ::milvus::grpc::TopKQueryResult>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::SearchParam* request, - ::milvus::grpc::TopKQueryResult* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->Search(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_Search( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::SearchParam, ::milvus::grpc::TopKQueryResult>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::SearchParam, ::milvus::grpc::TopKQueryResult>*>( - ::grpc::Service::experimental().GetHandler(17)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_Search() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Search(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void Search(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_SearchByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_SearchByID() { - ::grpc::Service::experimental().MarkMethodCallback(18, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::SearchByIDParam, ::milvus::grpc::TopKQueryResult>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::SearchByIDParam* request, - ::milvus::grpc::TopKQueryResult* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->SearchByID(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_SearchByID( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::SearchByIDParam, ::milvus::grpc::TopKQueryResult>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::SearchByIDParam, ::milvus::grpc::TopKQueryResult>*>( - ::grpc::Service::experimental().GetHandler(18)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_SearchByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status SearchByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchByIDParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void SearchByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchByIDParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_SearchInFiles : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_SearchInFiles() { - ::grpc::Service::experimental().MarkMethodCallback(19, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::SearchInFilesParam, ::milvus::grpc::TopKQueryResult>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::SearchInFilesParam* request, - ::milvus::grpc::TopKQueryResult* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->SearchInFiles(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_SearchInFiles( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::SearchInFilesParam, ::milvus::grpc::TopKQueryResult>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::SearchInFilesParam, ::milvus::grpc::TopKQueryResult>*>( - ::grpc::Service::experimental().GetHandler(19)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_SearchInFiles() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status SearchInFiles(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchInFilesParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void SearchInFiles(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchInFilesParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_Cmd : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_Cmd() { - ::grpc::Service::experimental().MarkMethodCallback(20, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::Command, ::milvus::grpc::StringReply>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::Command* request, - ::milvus::grpc::StringReply* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->Cmd(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_Cmd( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::Command, ::milvus::grpc::StringReply>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::Command, ::milvus::grpc::StringReply>*>( - ::grpc::Service::experimental().GetHandler(20)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_Cmd() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Cmd(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::StringReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void Cmd(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::StringReply* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_DeleteByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_DeleteByID() { - ::grpc::Service::experimental().MarkMethodCallback(21, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::DeleteByIDParam, ::milvus::grpc::Status>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::DeleteByIDParam* request, - ::milvus::grpc::Status* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->DeleteByID(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_DeleteByID( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::DeleteByIDParam, ::milvus::grpc::Status>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::DeleteByIDParam, ::milvus::grpc::Status>*>( - ::grpc::Service::experimental().GetHandler(21)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_DeleteByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DeleteByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::DeleteByIDParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void DeleteByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::DeleteByIDParam* /*request*/, ::milvus::grpc::Status* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_PreloadCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_PreloadCollection() { - ::grpc::Service::experimental().MarkMethodCallback(22, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::Status* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->PreloadCollection(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_PreloadCollection( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>*>( - ::grpc::Service::experimental().GetHandler(22)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_PreloadCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status PreloadCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void PreloadCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_ReloadSegments : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_ReloadSegments() { - ::grpc::Service::experimental().MarkMethodCallback(23, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::ReLoadSegmentsParam, ::milvus::grpc::Status>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::ReLoadSegmentsParam* request, - ::milvus::grpc::Status* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->ReloadSegments(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_ReloadSegments( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::ReLoadSegmentsParam, ::milvus::grpc::Status>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::ReLoadSegmentsParam, ::milvus::grpc::Status>*>( - ::grpc::Service::experimental().GetHandler(23)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_ReloadSegments() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ReloadSegments(::grpc::ServerContext* /*context*/, const ::milvus::grpc::ReLoadSegmentsParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void ReloadSegments(::grpc::ServerContext* /*context*/, const ::milvus::grpc::ReLoadSegmentsParam* /*request*/, ::milvus::grpc::Status* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_Flush : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_Flush() { - ::grpc::Service::experimental().MarkMethodCallback(24, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::FlushParam, ::milvus::grpc::Status>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::FlushParam* request, - ::milvus::grpc::Status* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->Flush(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_Flush( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::FlushParam, ::milvus::grpc::Status>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::FlushParam, ::milvus::grpc::Status>*>( - ::grpc::Service::experimental().GetHandler(24)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_Flush() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Flush(::grpc::ServerContext* /*context*/, const ::milvus::grpc::FlushParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void Flush(::grpc::ServerContext* /*context*/, const ::milvus::grpc::FlushParam* /*request*/, ::milvus::grpc::Status* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_Compact : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_Compact() { - ::grpc::Service::experimental().MarkMethodCallback(25, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::Status* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->Compact(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_Compact( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>*>( - ::grpc::Service::experimental().GetHandler(25)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_Compact() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Compact(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void Compact(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_CreateHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_CreateHybridCollection() { - ::grpc::Service::experimental().MarkMethodCallback(26, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::Mapping, ::milvus::grpc::Status>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::Mapping* request, - ::milvus::grpc::Status* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->CreateHybridCollection(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_CreateHybridCollection( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::Mapping, ::milvus::grpc::Status>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::Mapping, ::milvus::grpc::Status>*>( - ::grpc::Service::experimental().GetHandler(26)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_CreateHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreateHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Mapping* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void CreateHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Mapping* /*request*/, ::milvus::grpc::Status* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_HasHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_HasHybridCollection() { - ::grpc::Service::experimental().MarkMethodCallback(27, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::BoolReply>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::BoolReply* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->HasHybridCollection(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_HasHybridCollection( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::CollectionName, ::milvus::grpc::BoolReply>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::BoolReply>*>( - ::grpc::Service::experimental().GetHandler(27)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_HasHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HasHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::BoolReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void HasHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::BoolReply* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_DropHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_DropHybridCollection() { - ::grpc::Service::experimental().MarkMethodCallback(28, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::Status* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->DropHybridCollection(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_DropHybridCollection( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>*>( - ::grpc::Service::experimental().GetHandler(28)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_DropHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void DropHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_DescribeHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_DescribeHybridCollection() { - ::grpc::Service::experimental().MarkMethodCallback(29, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Mapping>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::Mapping* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->DescribeHybridCollection(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_DescribeHybridCollection( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::CollectionName, ::milvus::grpc::Mapping>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Mapping>*>( - ::grpc::Service::experimental().GetHandler(29)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_DescribeHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DescribeHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Mapping* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void DescribeHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Mapping* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_CountHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_CountHybridCollection() { - ::grpc::Service::experimental().MarkMethodCallback(30, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionRowCount>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::CollectionRowCount* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->CountHybridCollection(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_CountHybridCollection( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionRowCount>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionRowCount>*>( - ::grpc::Service::experimental().GetHandler(30)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_CountHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CountHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionRowCount* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void CountHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionRowCount* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_ShowHybridCollections : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_ShowHybridCollections() { - ::grpc::Service::experimental().MarkMethodCallback(31, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::Command, ::milvus::grpc::MappingList>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::Command* request, - ::milvus::grpc::MappingList* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->ShowHybridCollections(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_ShowHybridCollections( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::Command, ::milvus::grpc::MappingList>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::Command, ::milvus::grpc::MappingList>*>( - ::grpc::Service::experimental().GetHandler(31)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_ShowHybridCollections() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowHybridCollections(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::MappingList* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void ShowHybridCollections(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::MappingList* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_ShowHybridCollectionInfo : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_ShowHybridCollectionInfo() { - ::grpc::Service::experimental().MarkMethodCallback(32, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionInfo>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::CollectionInfo* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->ShowHybridCollectionInfo(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_ShowHybridCollectionInfo( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionInfo>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionInfo>*>( - ::grpc::Service::experimental().GetHandler(32)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_ShowHybridCollectionInfo() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowHybridCollectionInfo(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionInfo* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void ShowHybridCollectionInfo(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionInfo* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_PreloadHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_PreloadHybridCollection() { - ::grpc::Service::experimental().MarkMethodCallback(33, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::Status* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->PreloadHybridCollection(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_PreloadHybridCollection( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>*>( - ::grpc::Service::experimental().GetHandler(33)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_PreloadHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status PreloadHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void PreloadHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_InsertEntity : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_InsertEntity() { - ::grpc::Service::experimental().MarkMethodCallback(34, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::HInsertParam, ::milvus::grpc::HEntityIDs>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::HInsertParam* request, - ::milvus::grpc::HEntityIDs* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->InsertEntity(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_InsertEntity( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::HInsertParam, ::milvus::grpc::HEntityIDs>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::HInsertParam, ::milvus::grpc::HEntityIDs>*>( - ::grpc::Service::experimental().GetHandler(34)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_InsertEntity() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status InsertEntity(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HInsertParam* /*request*/, ::milvus::grpc::HEntityIDs* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void InsertEntity(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HInsertParam* /*request*/, ::milvus::grpc::HEntityIDs* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_HybridSearch : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_HybridSearch() { - ::grpc::Service::experimental().MarkMethodCallback(35, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::HSearchParam, ::milvus::grpc::TopKQueryResult>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::HSearchParam* request, - ::milvus::grpc::TopKQueryResult* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->HybridSearch(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_HybridSearch( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::HSearchParam, ::milvus::grpc::TopKQueryResult>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::HSearchParam, ::milvus::grpc::TopKQueryResult>*>( - ::grpc::Service::experimental().GetHandler(35)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_HybridSearch() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HybridSearch(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HSearchParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void HybridSearch(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HSearchParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_HybridSearchInSegments : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_HybridSearchInSegments() { - ::grpc::Service::experimental().MarkMethodCallback(36, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::HSearchInSegmentsParam, ::milvus::grpc::TopKQueryResult>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::HSearchInSegmentsParam* request, - ::milvus::grpc::TopKQueryResult* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->HybridSearchInSegments(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_HybridSearchInSegments( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::HSearchInSegmentsParam, ::milvus::grpc::TopKQueryResult>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::HSearchInSegmentsParam, ::milvus::grpc::TopKQueryResult>*>( - ::grpc::Service::experimental().GetHandler(36)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_HybridSearchInSegments() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HybridSearchInSegments(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HSearchInSegmentsParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void HybridSearchInSegments(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HSearchInSegmentsParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_GetEntityByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_GetEntityByID() { - ::grpc::Service::experimental().MarkMethodCallback(37, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::HEntityIdentity, ::milvus::grpc::HEntity>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::HEntityIdentity* request, - ::milvus::grpc::HEntity* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->GetEntityByID(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_GetEntityByID( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::HEntityIdentity, ::milvus::grpc::HEntity>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::HEntityIdentity, ::milvus::grpc::HEntity>*>( - ::grpc::Service::experimental().GetHandler(37)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_GetEntityByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetEntityByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HEntityIdentity* /*request*/, ::milvus::grpc::HEntity* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void GetEntityByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HEntityIdentity* /*request*/, ::milvus::grpc::HEntity* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_GetEntityIDs : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_GetEntityIDs() { - ::grpc::Service::experimental().MarkMethodCallback(38, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::HGetEntityIDsParam, ::milvus::grpc::HEntityIDs>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::HGetEntityIDsParam* request, - ::milvus::grpc::HEntityIDs* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->GetEntityIDs(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_GetEntityIDs( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::HGetEntityIDsParam, ::milvus::grpc::HEntityIDs>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::HGetEntityIDsParam, ::milvus::grpc::HEntityIDs>*>( - ::grpc::Service::experimental().GetHandler(38)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_GetEntityIDs() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetEntityIDs(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HGetEntityIDsParam* /*request*/, ::milvus::grpc::HEntityIDs* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void GetEntityIDs(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HGetEntityIDsParam* /*request*/, ::milvus::grpc::HEntityIDs* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithCallbackMethod_DeleteEntitiesByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithCallbackMethod_DeleteEntitiesByID() { - ::grpc::Service::experimental().MarkMethodCallback(39, - new ::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::HDeleteByIDParam, ::milvus::grpc::Status>( - [this](::grpc::ServerContext* context, - const ::milvus::grpc::HDeleteByIDParam* request, - ::milvus::grpc::Status* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - return this->DeleteEntitiesByID(context, request, response, controller); - })); - } - void SetMessageAllocatorFor_DeleteEntitiesByID( - ::grpc::experimental::MessageAllocator< ::milvus::grpc::HDeleteByIDParam, ::milvus::grpc::Status>* allocator) { - static_cast<::grpc_impl::internal::CallbackUnaryHandler< ::milvus::grpc::HDeleteByIDParam, ::milvus::grpc::Status>*>( - ::grpc::Service::experimental().GetHandler(39)) - ->SetMessageAllocator(allocator); - } - ~ExperimentalWithCallbackMethod_DeleteEntitiesByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DeleteEntitiesByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HDeleteByIDParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void DeleteEntitiesByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HDeleteByIDParam* /*request*/, ::milvus::grpc::Status* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - typedef ExperimentalWithCallbackMethod_CreateCollection > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > ExperimentalCallbackService; - template - class WithGenericMethod_CreateCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_CreateCollection() { - ::grpc::Service::MarkMethodGeneric(0); - } - ~WithGenericMethod_CreateCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreateCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionSchema* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_HasCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_HasCollection() { - ::grpc::Service::MarkMethodGeneric(1); - } - ~WithGenericMethod_HasCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HasCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::BoolReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_DescribeCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_DescribeCollection() { - ::grpc::Service::MarkMethodGeneric(2); - } - ~WithGenericMethod_DescribeCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DescribeCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionSchema* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_CountCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_CountCollection() { - ::grpc::Service::MarkMethodGeneric(3); - } - ~WithGenericMethod_CountCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CountCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionRowCount* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_ShowCollections : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_ShowCollections() { - ::grpc::Service::MarkMethodGeneric(4); - } - ~WithGenericMethod_ShowCollections() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowCollections(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::CollectionNameList* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_ShowCollectionInfo : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_ShowCollectionInfo() { - ::grpc::Service::MarkMethodGeneric(5); - } - ~WithGenericMethod_ShowCollectionInfo() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowCollectionInfo(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionInfo* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_DropCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_DropCollection() { - ::grpc::Service::MarkMethodGeneric(6); - } - ~WithGenericMethod_DropCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_CreateIndex : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_CreateIndex() { - ::grpc::Service::MarkMethodGeneric(7); - } - ~WithGenericMethod_CreateIndex() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreateIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::IndexParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_DescribeIndex : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_DescribeIndex() { - ::grpc::Service::MarkMethodGeneric(8); - } - ~WithGenericMethod_DescribeIndex() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DescribeIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::IndexParam* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_DropIndex : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_DropIndex() { - ::grpc::Service::MarkMethodGeneric(9); - } - ~WithGenericMethod_DropIndex() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_CreatePartition : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_CreatePartition() { - ::grpc::Service::MarkMethodGeneric(10); - } - ~WithGenericMethod_CreatePartition() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreatePartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_HasPartition : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_HasPartition() { - ::grpc::Service::MarkMethodGeneric(11); - } - ~WithGenericMethod_HasPartition() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HasPartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::BoolReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_ShowPartitions : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_ShowPartitions() { - ::grpc::Service::MarkMethodGeneric(12); - } - ~WithGenericMethod_ShowPartitions() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowPartitions(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::PartitionList* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_DropPartition : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_DropPartition() { - ::grpc::Service::MarkMethodGeneric(13); - } - ~WithGenericMethod_DropPartition() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropPartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_Insert : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_Insert() { - ::grpc::Service::MarkMethodGeneric(14); - } - ~WithGenericMethod_Insert() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Insert(::grpc::ServerContext* /*context*/, const ::milvus::grpc::InsertParam* /*request*/, ::milvus::grpc::VectorIds* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_GetVectorsByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_GetVectorsByID() { - ::grpc::Service::MarkMethodGeneric(15); - } - ~WithGenericMethod_GetVectorsByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetVectorsByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::VectorsIdentity* /*request*/, ::milvus::grpc::VectorsData* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_GetVectorIDs : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_GetVectorIDs() { - ::grpc::Service::MarkMethodGeneric(16); - } - ~WithGenericMethod_GetVectorIDs() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetVectorIDs(::grpc::ServerContext* /*context*/, const ::milvus::grpc::GetVectorIDsParam* /*request*/, ::milvus::grpc::VectorIds* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_Search : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_Search() { - ::grpc::Service::MarkMethodGeneric(17); - } - ~WithGenericMethod_Search() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Search(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_SearchByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_SearchByID() { - ::grpc::Service::MarkMethodGeneric(18); - } - ~WithGenericMethod_SearchByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status SearchByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchByIDParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_SearchInFiles : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_SearchInFiles() { - ::grpc::Service::MarkMethodGeneric(19); - } - ~WithGenericMethod_SearchInFiles() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status SearchInFiles(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchInFilesParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_Cmd : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_Cmd() { - ::grpc::Service::MarkMethodGeneric(20); - } - ~WithGenericMethod_Cmd() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Cmd(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::StringReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_DeleteByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_DeleteByID() { - ::grpc::Service::MarkMethodGeneric(21); - } - ~WithGenericMethod_DeleteByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DeleteByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::DeleteByIDParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_PreloadCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_PreloadCollection() { - ::grpc::Service::MarkMethodGeneric(22); - } - ~WithGenericMethod_PreloadCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status PreloadCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_ReloadSegments : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_ReloadSegments() { - ::grpc::Service::MarkMethodGeneric(23); - } - ~WithGenericMethod_ReloadSegments() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ReloadSegments(::grpc::ServerContext* /*context*/, const ::milvus::grpc::ReLoadSegmentsParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_Flush : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_Flush() { - ::grpc::Service::MarkMethodGeneric(24); - } - ~WithGenericMethod_Flush() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Flush(::grpc::ServerContext* /*context*/, const ::milvus::grpc::FlushParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_Compact : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_Compact() { - ::grpc::Service::MarkMethodGeneric(25); - } - ~WithGenericMethod_Compact() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Compact(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_CreateHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_CreateHybridCollection() { - ::grpc::Service::MarkMethodGeneric(26); - } - ~WithGenericMethod_CreateHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreateHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Mapping* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_HasHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_HasHybridCollection() { - ::grpc::Service::MarkMethodGeneric(27); - } - ~WithGenericMethod_HasHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HasHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::BoolReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_DropHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_DropHybridCollection() { - ::grpc::Service::MarkMethodGeneric(28); - } - ~WithGenericMethod_DropHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_DescribeHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_DescribeHybridCollection() { - ::grpc::Service::MarkMethodGeneric(29); - } - ~WithGenericMethod_DescribeHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DescribeHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Mapping* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_CountHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_CountHybridCollection() { - ::grpc::Service::MarkMethodGeneric(30); - } - ~WithGenericMethod_CountHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CountHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionRowCount* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_ShowHybridCollections : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_ShowHybridCollections() { - ::grpc::Service::MarkMethodGeneric(31); - } - ~WithGenericMethod_ShowHybridCollections() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowHybridCollections(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::MappingList* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_ShowHybridCollectionInfo : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_ShowHybridCollectionInfo() { - ::grpc::Service::MarkMethodGeneric(32); - } - ~WithGenericMethod_ShowHybridCollectionInfo() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowHybridCollectionInfo(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionInfo* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_PreloadHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_PreloadHybridCollection() { - ::grpc::Service::MarkMethodGeneric(33); - } - ~WithGenericMethod_PreloadHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status PreloadHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_InsertEntity : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_InsertEntity() { - ::grpc::Service::MarkMethodGeneric(34); - } - ~WithGenericMethod_InsertEntity() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status InsertEntity(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HInsertParam* /*request*/, ::milvus::grpc::HEntityIDs* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_HybridSearch : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_HybridSearch() { - ::grpc::Service::MarkMethodGeneric(35); - } - ~WithGenericMethod_HybridSearch() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HybridSearch(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HSearchParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_HybridSearchInSegments : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_HybridSearchInSegments() { - ::grpc::Service::MarkMethodGeneric(36); - } - ~WithGenericMethod_HybridSearchInSegments() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HybridSearchInSegments(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HSearchInSegmentsParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_GetEntityByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_GetEntityByID() { - ::grpc::Service::MarkMethodGeneric(37); - } - ~WithGenericMethod_GetEntityByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetEntityByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HEntityIdentity* /*request*/, ::milvus::grpc::HEntity* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_GetEntityIDs : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_GetEntityIDs() { - ::grpc::Service::MarkMethodGeneric(38); - } - ~WithGenericMethod_GetEntityIDs() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetEntityIDs(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HGetEntityIDsParam* /*request*/, ::milvus::grpc::HEntityIDs* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithGenericMethod_DeleteEntitiesByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithGenericMethod_DeleteEntitiesByID() { - ::grpc::Service::MarkMethodGeneric(39); - } - ~WithGenericMethod_DeleteEntitiesByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DeleteEntitiesByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HDeleteByIDParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - }; - template - class WithRawMethod_CreateCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_CreateCollection() { - ::grpc::Service::MarkMethodRaw(0); - } - ~WithRawMethod_CreateCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreateCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionSchema* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestCreateCollection(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(0, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_HasCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_HasCollection() { - ::grpc::Service::MarkMethodRaw(1); - } - ~WithRawMethod_HasCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HasCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::BoolReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestHasCollection(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(1, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_DescribeCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_DescribeCollection() { - ::grpc::Service::MarkMethodRaw(2); - } - ~WithRawMethod_DescribeCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DescribeCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionSchema* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestDescribeCollection(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(2, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_CountCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_CountCollection() { - ::grpc::Service::MarkMethodRaw(3); - } - ~WithRawMethod_CountCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CountCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionRowCount* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestCountCollection(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(3, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_ShowCollections : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_ShowCollections() { - ::grpc::Service::MarkMethodRaw(4); - } - ~WithRawMethod_ShowCollections() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowCollections(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::CollectionNameList* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestShowCollections(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(4, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_ShowCollectionInfo : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_ShowCollectionInfo() { - ::grpc::Service::MarkMethodRaw(5); - } - ~WithRawMethod_ShowCollectionInfo() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowCollectionInfo(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionInfo* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestShowCollectionInfo(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(5, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_DropCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_DropCollection() { - ::grpc::Service::MarkMethodRaw(6); - } - ~WithRawMethod_DropCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestDropCollection(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(6, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_CreateIndex : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_CreateIndex() { - ::grpc::Service::MarkMethodRaw(7); - } - ~WithRawMethod_CreateIndex() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreateIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::IndexParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestCreateIndex(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(7, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_DescribeIndex : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_DescribeIndex() { - ::grpc::Service::MarkMethodRaw(8); - } - ~WithRawMethod_DescribeIndex() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DescribeIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::IndexParam* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestDescribeIndex(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(8, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_DropIndex : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_DropIndex() { - ::grpc::Service::MarkMethodRaw(9); - } - ~WithRawMethod_DropIndex() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestDropIndex(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(9, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_CreatePartition : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_CreatePartition() { - ::grpc::Service::MarkMethodRaw(10); - } - ~WithRawMethod_CreatePartition() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreatePartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestCreatePartition(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(10, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_HasPartition : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_HasPartition() { - ::grpc::Service::MarkMethodRaw(11); - } - ~WithRawMethod_HasPartition() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HasPartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::BoolReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestHasPartition(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(11, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_ShowPartitions : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_ShowPartitions() { - ::grpc::Service::MarkMethodRaw(12); - } - ~WithRawMethod_ShowPartitions() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowPartitions(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::PartitionList* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestShowPartitions(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(12, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_DropPartition : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_DropPartition() { - ::grpc::Service::MarkMethodRaw(13); - } - ~WithRawMethod_DropPartition() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropPartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestDropPartition(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(13, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_Insert : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_Insert() { - ::grpc::Service::MarkMethodRaw(14); - } - ~WithRawMethod_Insert() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Insert(::grpc::ServerContext* /*context*/, const ::milvus::grpc::InsertParam* /*request*/, ::milvus::grpc::VectorIds* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestInsert(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(14, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_GetVectorsByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_GetVectorsByID() { - ::grpc::Service::MarkMethodRaw(15); - } - ~WithRawMethod_GetVectorsByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetVectorsByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::VectorsIdentity* /*request*/, ::milvus::grpc::VectorsData* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestGetVectorsByID(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(15, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_GetVectorIDs : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_GetVectorIDs() { - ::grpc::Service::MarkMethodRaw(16); - } - ~WithRawMethod_GetVectorIDs() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetVectorIDs(::grpc::ServerContext* /*context*/, const ::milvus::grpc::GetVectorIDsParam* /*request*/, ::milvus::grpc::VectorIds* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestGetVectorIDs(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(16, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_Search : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_Search() { - ::grpc::Service::MarkMethodRaw(17); - } - ~WithRawMethod_Search() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Search(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestSearch(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(17, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_SearchByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_SearchByID() { - ::grpc::Service::MarkMethodRaw(18); - } - ~WithRawMethod_SearchByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status SearchByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchByIDParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestSearchByID(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(18, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_SearchInFiles : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_SearchInFiles() { - ::grpc::Service::MarkMethodRaw(19); - } - ~WithRawMethod_SearchInFiles() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status SearchInFiles(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchInFilesParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestSearchInFiles(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(19, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_Cmd : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_Cmd() { - ::grpc::Service::MarkMethodRaw(20); - } - ~WithRawMethod_Cmd() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Cmd(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::StringReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestCmd(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(20, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_DeleteByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_DeleteByID() { - ::grpc::Service::MarkMethodRaw(21); - } - ~WithRawMethod_DeleteByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DeleteByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::DeleteByIDParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestDeleteByID(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(21, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_PreloadCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_PreloadCollection() { - ::grpc::Service::MarkMethodRaw(22); - } - ~WithRawMethod_PreloadCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status PreloadCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestPreloadCollection(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(22, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_ReloadSegments : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_ReloadSegments() { - ::grpc::Service::MarkMethodRaw(23); - } - ~WithRawMethod_ReloadSegments() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ReloadSegments(::grpc::ServerContext* /*context*/, const ::milvus::grpc::ReLoadSegmentsParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestReloadSegments(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(23, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_Flush : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_Flush() { - ::grpc::Service::MarkMethodRaw(24); - } - ~WithRawMethod_Flush() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Flush(::grpc::ServerContext* /*context*/, const ::milvus::grpc::FlushParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestFlush(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(24, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_Compact : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_Compact() { - ::grpc::Service::MarkMethodRaw(25); - } - ~WithRawMethod_Compact() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Compact(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestCompact(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(25, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_CreateHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_CreateHybridCollection() { - ::grpc::Service::MarkMethodRaw(26); - } - ~WithRawMethod_CreateHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreateHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Mapping* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestCreateHybridCollection(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(26, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_HasHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_HasHybridCollection() { - ::grpc::Service::MarkMethodRaw(27); - } - ~WithRawMethod_HasHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HasHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::BoolReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestHasHybridCollection(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(27, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_DropHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_DropHybridCollection() { - ::grpc::Service::MarkMethodRaw(28); - } - ~WithRawMethod_DropHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestDropHybridCollection(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(28, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_DescribeHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_DescribeHybridCollection() { - ::grpc::Service::MarkMethodRaw(29); - } - ~WithRawMethod_DescribeHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DescribeHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Mapping* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestDescribeHybridCollection(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(29, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_CountHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_CountHybridCollection() { - ::grpc::Service::MarkMethodRaw(30); - } - ~WithRawMethod_CountHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CountHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionRowCount* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestCountHybridCollection(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(30, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_ShowHybridCollections : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_ShowHybridCollections() { - ::grpc::Service::MarkMethodRaw(31); - } - ~WithRawMethod_ShowHybridCollections() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowHybridCollections(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::MappingList* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestShowHybridCollections(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(31, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_ShowHybridCollectionInfo : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_ShowHybridCollectionInfo() { - ::grpc::Service::MarkMethodRaw(32); - } - ~WithRawMethod_ShowHybridCollectionInfo() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowHybridCollectionInfo(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionInfo* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestShowHybridCollectionInfo(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(32, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_PreloadHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_PreloadHybridCollection() { - ::grpc::Service::MarkMethodRaw(33); - } - ~WithRawMethod_PreloadHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status PreloadHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestPreloadHybridCollection(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(33, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_InsertEntity : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_InsertEntity() { - ::grpc::Service::MarkMethodRaw(34); - } - ~WithRawMethod_InsertEntity() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status InsertEntity(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HInsertParam* /*request*/, ::milvus::grpc::HEntityIDs* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestInsertEntity(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(34, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_HybridSearch : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_HybridSearch() { - ::grpc::Service::MarkMethodRaw(35); - } - ~WithRawMethod_HybridSearch() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HybridSearch(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HSearchParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestHybridSearch(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(35, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_HybridSearchInSegments : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_HybridSearchInSegments() { - ::grpc::Service::MarkMethodRaw(36); - } - ~WithRawMethod_HybridSearchInSegments() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HybridSearchInSegments(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HSearchInSegmentsParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestHybridSearchInSegments(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(36, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_GetEntityByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_GetEntityByID() { - ::grpc::Service::MarkMethodRaw(37); - } - ~WithRawMethod_GetEntityByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetEntityByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HEntityIdentity* /*request*/, ::milvus::grpc::HEntity* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestGetEntityByID(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(37, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_GetEntityIDs : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_GetEntityIDs() { - ::grpc::Service::MarkMethodRaw(38); - } - ~WithRawMethod_GetEntityIDs() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetEntityIDs(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HGetEntityIDsParam* /*request*/, ::milvus::grpc::HEntityIDs* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestGetEntityIDs(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(38, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class WithRawMethod_DeleteEntitiesByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithRawMethod_DeleteEntitiesByID() { - ::grpc::Service::MarkMethodRaw(39); - } - ~WithRawMethod_DeleteEntitiesByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DeleteEntitiesByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HDeleteByIDParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - void RequestDeleteEntitiesByID(::grpc::ServerContext* context, ::grpc::ByteBuffer* request, ::grpc::ServerAsyncResponseWriter< ::grpc::ByteBuffer>* response, ::grpc::CompletionQueue* new_call_cq, ::grpc::ServerCompletionQueue* notification_cq, void *tag) { - ::grpc::Service::RequestAsyncUnary(39, context, request, response, new_call_cq, notification_cq, tag); - } - }; - template - class ExperimentalWithRawCallbackMethod_CreateCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_CreateCollection() { - ::grpc::Service::experimental().MarkMethodRawCallback(0, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->CreateCollection(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_CreateCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreateCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionSchema* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void CreateCollection(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_HasCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_HasCollection() { - ::grpc::Service::experimental().MarkMethodRawCallback(1, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->HasCollection(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_HasCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HasCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::BoolReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void HasCollection(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_DescribeCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_DescribeCollection() { - ::grpc::Service::experimental().MarkMethodRawCallback(2, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->DescribeCollection(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_DescribeCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DescribeCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionSchema* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void DescribeCollection(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_CountCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_CountCollection() { - ::grpc::Service::experimental().MarkMethodRawCallback(3, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->CountCollection(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_CountCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CountCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionRowCount* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void CountCollection(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_ShowCollections : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_ShowCollections() { - ::grpc::Service::experimental().MarkMethodRawCallback(4, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->ShowCollections(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_ShowCollections() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowCollections(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::CollectionNameList* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void ShowCollections(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_ShowCollectionInfo : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_ShowCollectionInfo() { - ::grpc::Service::experimental().MarkMethodRawCallback(5, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->ShowCollectionInfo(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_ShowCollectionInfo() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowCollectionInfo(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionInfo* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void ShowCollectionInfo(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_DropCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_DropCollection() { - ::grpc::Service::experimental().MarkMethodRawCallback(6, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->DropCollection(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_DropCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void DropCollection(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_CreateIndex : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_CreateIndex() { - ::grpc::Service::experimental().MarkMethodRawCallback(7, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->CreateIndex(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_CreateIndex() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreateIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::IndexParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void CreateIndex(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_DescribeIndex : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_DescribeIndex() { - ::grpc::Service::experimental().MarkMethodRawCallback(8, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->DescribeIndex(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_DescribeIndex() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DescribeIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::IndexParam* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void DescribeIndex(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_DropIndex : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_DropIndex() { - ::grpc::Service::experimental().MarkMethodRawCallback(9, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->DropIndex(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_DropIndex() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void DropIndex(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_CreatePartition : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_CreatePartition() { - ::grpc::Service::experimental().MarkMethodRawCallback(10, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->CreatePartition(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_CreatePartition() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreatePartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void CreatePartition(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_HasPartition : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_HasPartition() { - ::grpc::Service::experimental().MarkMethodRawCallback(11, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->HasPartition(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_HasPartition() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HasPartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::BoolReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void HasPartition(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_ShowPartitions : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_ShowPartitions() { - ::grpc::Service::experimental().MarkMethodRawCallback(12, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->ShowPartitions(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_ShowPartitions() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowPartitions(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::PartitionList* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void ShowPartitions(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_DropPartition : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_DropPartition() { - ::grpc::Service::experimental().MarkMethodRawCallback(13, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->DropPartition(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_DropPartition() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropPartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void DropPartition(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_Insert : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_Insert() { - ::grpc::Service::experimental().MarkMethodRawCallback(14, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->Insert(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_Insert() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Insert(::grpc::ServerContext* /*context*/, const ::milvus::grpc::InsertParam* /*request*/, ::milvus::grpc::VectorIds* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void Insert(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_GetVectorsByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_GetVectorsByID() { - ::grpc::Service::experimental().MarkMethodRawCallback(15, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->GetVectorsByID(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_GetVectorsByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetVectorsByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::VectorsIdentity* /*request*/, ::milvus::grpc::VectorsData* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void GetVectorsByID(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_GetVectorIDs : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_GetVectorIDs() { - ::grpc::Service::experimental().MarkMethodRawCallback(16, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->GetVectorIDs(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_GetVectorIDs() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetVectorIDs(::grpc::ServerContext* /*context*/, const ::milvus::grpc::GetVectorIDsParam* /*request*/, ::milvus::grpc::VectorIds* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void GetVectorIDs(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_Search : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_Search() { - ::grpc::Service::experimental().MarkMethodRawCallback(17, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->Search(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_Search() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Search(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void Search(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_SearchByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_SearchByID() { - ::grpc::Service::experimental().MarkMethodRawCallback(18, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->SearchByID(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_SearchByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status SearchByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchByIDParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void SearchByID(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_SearchInFiles : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_SearchInFiles() { - ::grpc::Service::experimental().MarkMethodRawCallback(19, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->SearchInFiles(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_SearchInFiles() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status SearchInFiles(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchInFilesParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void SearchInFiles(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_Cmd : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_Cmd() { - ::grpc::Service::experimental().MarkMethodRawCallback(20, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->Cmd(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_Cmd() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Cmd(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::StringReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void Cmd(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_DeleteByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_DeleteByID() { - ::grpc::Service::experimental().MarkMethodRawCallback(21, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->DeleteByID(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_DeleteByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DeleteByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::DeleteByIDParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void DeleteByID(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_PreloadCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_PreloadCollection() { - ::grpc::Service::experimental().MarkMethodRawCallback(22, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->PreloadCollection(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_PreloadCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status PreloadCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void PreloadCollection(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_ReloadSegments : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_ReloadSegments() { - ::grpc::Service::experimental().MarkMethodRawCallback(23, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->ReloadSegments(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_ReloadSegments() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ReloadSegments(::grpc::ServerContext* /*context*/, const ::milvus::grpc::ReLoadSegmentsParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void ReloadSegments(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_Flush : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_Flush() { - ::grpc::Service::experimental().MarkMethodRawCallback(24, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->Flush(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_Flush() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Flush(::grpc::ServerContext* /*context*/, const ::milvus::grpc::FlushParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void Flush(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_Compact : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_Compact() { - ::grpc::Service::experimental().MarkMethodRawCallback(25, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->Compact(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_Compact() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status Compact(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void Compact(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_CreateHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_CreateHybridCollection() { - ::grpc::Service::experimental().MarkMethodRawCallback(26, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->CreateHybridCollection(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_CreateHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CreateHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Mapping* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void CreateHybridCollection(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_HasHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_HasHybridCollection() { - ::grpc::Service::experimental().MarkMethodRawCallback(27, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->HasHybridCollection(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_HasHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HasHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::BoolReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void HasHybridCollection(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_DropHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_DropHybridCollection() { - ::grpc::Service::experimental().MarkMethodRawCallback(28, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->DropHybridCollection(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_DropHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DropHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void DropHybridCollection(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_DescribeHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_DescribeHybridCollection() { - ::grpc::Service::experimental().MarkMethodRawCallback(29, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->DescribeHybridCollection(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_DescribeHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DescribeHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Mapping* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void DescribeHybridCollection(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_CountHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_CountHybridCollection() { - ::grpc::Service::experimental().MarkMethodRawCallback(30, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->CountHybridCollection(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_CountHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status CountHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionRowCount* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void CountHybridCollection(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_ShowHybridCollections : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_ShowHybridCollections() { - ::grpc::Service::experimental().MarkMethodRawCallback(31, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->ShowHybridCollections(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_ShowHybridCollections() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowHybridCollections(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::MappingList* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void ShowHybridCollections(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_ShowHybridCollectionInfo : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_ShowHybridCollectionInfo() { - ::grpc::Service::experimental().MarkMethodRawCallback(32, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->ShowHybridCollectionInfo(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_ShowHybridCollectionInfo() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status ShowHybridCollectionInfo(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionInfo* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void ShowHybridCollectionInfo(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_PreloadHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_PreloadHybridCollection() { - ::grpc::Service::experimental().MarkMethodRawCallback(33, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->PreloadHybridCollection(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_PreloadHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status PreloadHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void PreloadHybridCollection(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_InsertEntity : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_InsertEntity() { - ::grpc::Service::experimental().MarkMethodRawCallback(34, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->InsertEntity(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_InsertEntity() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status InsertEntity(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HInsertParam* /*request*/, ::milvus::grpc::HEntityIDs* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void InsertEntity(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_HybridSearch : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_HybridSearch() { - ::grpc::Service::experimental().MarkMethodRawCallback(35, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->HybridSearch(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_HybridSearch() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HybridSearch(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HSearchParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void HybridSearch(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_HybridSearchInSegments : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_HybridSearchInSegments() { - ::grpc::Service::experimental().MarkMethodRawCallback(36, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->HybridSearchInSegments(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_HybridSearchInSegments() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status HybridSearchInSegments(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HSearchInSegmentsParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void HybridSearchInSegments(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_GetEntityByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_GetEntityByID() { - ::grpc::Service::experimental().MarkMethodRawCallback(37, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->GetEntityByID(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_GetEntityByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetEntityByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HEntityIdentity* /*request*/, ::milvus::grpc::HEntity* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void GetEntityByID(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_GetEntityIDs : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_GetEntityIDs() { - ::grpc::Service::experimental().MarkMethodRawCallback(38, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->GetEntityIDs(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_GetEntityIDs() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status GetEntityIDs(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HGetEntityIDsParam* /*request*/, ::milvus::grpc::HEntityIDs* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void GetEntityIDs(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class ExperimentalWithRawCallbackMethod_DeleteEntitiesByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - ExperimentalWithRawCallbackMethod_DeleteEntitiesByID() { - ::grpc::Service::experimental().MarkMethodRawCallback(39, - new ::grpc_impl::internal::CallbackUnaryHandler< ::grpc::ByteBuffer, ::grpc::ByteBuffer>( - [this](::grpc::ServerContext* context, - const ::grpc::ByteBuffer* request, - ::grpc::ByteBuffer* response, - ::grpc::experimental::ServerCallbackRpcController* controller) { - this->DeleteEntitiesByID(context, request, response, controller); - })); - } - ~ExperimentalWithRawCallbackMethod_DeleteEntitiesByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable synchronous version of this method - ::grpc::Status DeleteEntitiesByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HDeleteByIDParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - virtual void DeleteEntitiesByID(::grpc::ServerContext* /*context*/, const ::grpc::ByteBuffer* /*request*/, ::grpc::ByteBuffer* /*response*/, ::grpc::experimental::ServerCallbackRpcController* controller) { controller->Finish(::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "")); } - }; - template - class WithStreamedUnaryMethod_CreateCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_CreateCollection() { - ::grpc::Service::MarkMethodStreamed(0, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::CollectionSchema, ::milvus::grpc::Status>(std::bind(&WithStreamedUnaryMethod_CreateCollection::StreamedCreateCollection, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_CreateCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status CreateCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionSchema* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedCreateCollection(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::CollectionSchema,::milvus::grpc::Status>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_HasCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_HasCollection() { - ::grpc::Service::MarkMethodStreamed(1, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::BoolReply>(std::bind(&WithStreamedUnaryMethod_HasCollection::StreamedHasCollection, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_HasCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status HasCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::BoolReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedHasCollection(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::CollectionName,::milvus::grpc::BoolReply>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_DescribeCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_DescribeCollection() { - ::grpc::Service::MarkMethodStreamed(2, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionSchema>(std::bind(&WithStreamedUnaryMethod_DescribeCollection::StreamedDescribeCollection, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_DescribeCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status DescribeCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionSchema* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedDescribeCollection(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::CollectionName,::milvus::grpc::CollectionSchema>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_CountCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_CountCollection() { - ::grpc::Service::MarkMethodStreamed(3, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionRowCount>(std::bind(&WithStreamedUnaryMethod_CountCollection::StreamedCountCollection, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_CountCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status CountCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionRowCount* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedCountCollection(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::CollectionName,::milvus::grpc::CollectionRowCount>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_ShowCollections : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_ShowCollections() { - ::grpc::Service::MarkMethodStreamed(4, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::Command, ::milvus::grpc::CollectionNameList>(std::bind(&WithStreamedUnaryMethod_ShowCollections::StreamedShowCollections, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_ShowCollections() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status ShowCollections(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::CollectionNameList* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedShowCollections(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::Command,::milvus::grpc::CollectionNameList>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_ShowCollectionInfo : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_ShowCollectionInfo() { - ::grpc::Service::MarkMethodStreamed(5, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionInfo>(std::bind(&WithStreamedUnaryMethod_ShowCollectionInfo::StreamedShowCollectionInfo, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_ShowCollectionInfo() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status ShowCollectionInfo(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionInfo* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedShowCollectionInfo(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::CollectionName,::milvus::grpc::CollectionInfo>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_DropCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_DropCollection() { - ::grpc::Service::MarkMethodStreamed(6, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>(std::bind(&WithStreamedUnaryMethod_DropCollection::StreamedDropCollection, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_DropCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status DropCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedDropCollection(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::CollectionName,::milvus::grpc::Status>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_CreateIndex : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_CreateIndex() { - ::grpc::Service::MarkMethodStreamed(7, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::IndexParam, ::milvus::grpc::Status>(std::bind(&WithStreamedUnaryMethod_CreateIndex::StreamedCreateIndex, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_CreateIndex() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status CreateIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::IndexParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedCreateIndex(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::IndexParam,::milvus::grpc::Status>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_DescribeIndex : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_DescribeIndex() { - ::grpc::Service::MarkMethodStreamed(8, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::IndexParam>(std::bind(&WithStreamedUnaryMethod_DescribeIndex::StreamedDescribeIndex, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_DescribeIndex() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status DescribeIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::IndexParam* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedDescribeIndex(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::CollectionName,::milvus::grpc::IndexParam>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_DropIndex : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_DropIndex() { - ::grpc::Service::MarkMethodStreamed(9, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>(std::bind(&WithStreamedUnaryMethod_DropIndex::StreamedDropIndex, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_DropIndex() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status DropIndex(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedDropIndex(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::CollectionName,::milvus::grpc::Status>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_CreatePartition : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_CreatePartition() { - ::grpc::Service::MarkMethodStreamed(10, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::PartitionParam, ::milvus::grpc::Status>(std::bind(&WithStreamedUnaryMethod_CreatePartition::StreamedCreatePartition, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_CreatePartition() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status CreatePartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedCreatePartition(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::PartitionParam,::milvus::grpc::Status>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_HasPartition : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_HasPartition() { - ::grpc::Service::MarkMethodStreamed(11, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::PartitionParam, ::milvus::grpc::BoolReply>(std::bind(&WithStreamedUnaryMethod_HasPartition::StreamedHasPartition, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_HasPartition() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status HasPartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::BoolReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedHasPartition(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::PartitionParam,::milvus::grpc::BoolReply>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_ShowPartitions : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_ShowPartitions() { - ::grpc::Service::MarkMethodStreamed(12, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::PartitionList>(std::bind(&WithStreamedUnaryMethod_ShowPartitions::StreamedShowPartitions, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_ShowPartitions() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status ShowPartitions(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::PartitionList* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedShowPartitions(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::CollectionName,::milvus::grpc::PartitionList>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_DropPartition : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_DropPartition() { - ::grpc::Service::MarkMethodStreamed(13, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::PartitionParam, ::milvus::grpc::Status>(std::bind(&WithStreamedUnaryMethod_DropPartition::StreamedDropPartition, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_DropPartition() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status DropPartition(::grpc::ServerContext* /*context*/, const ::milvus::grpc::PartitionParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedDropPartition(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::PartitionParam,::milvus::grpc::Status>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_Insert : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_Insert() { - ::grpc::Service::MarkMethodStreamed(14, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::InsertParam, ::milvus::grpc::VectorIds>(std::bind(&WithStreamedUnaryMethod_Insert::StreamedInsert, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_Insert() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status Insert(::grpc::ServerContext* /*context*/, const ::milvus::grpc::InsertParam* /*request*/, ::milvus::grpc::VectorIds* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedInsert(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::InsertParam,::milvus::grpc::VectorIds>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_GetVectorsByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_GetVectorsByID() { - ::grpc::Service::MarkMethodStreamed(15, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::VectorsIdentity, ::milvus::grpc::VectorsData>(std::bind(&WithStreamedUnaryMethod_GetVectorsByID::StreamedGetVectorsByID, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_GetVectorsByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status GetVectorsByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::VectorsIdentity* /*request*/, ::milvus::grpc::VectorsData* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedGetVectorsByID(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::VectorsIdentity,::milvus::grpc::VectorsData>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_GetVectorIDs : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_GetVectorIDs() { - ::grpc::Service::MarkMethodStreamed(16, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::GetVectorIDsParam, ::milvus::grpc::VectorIds>(std::bind(&WithStreamedUnaryMethod_GetVectorIDs::StreamedGetVectorIDs, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_GetVectorIDs() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status GetVectorIDs(::grpc::ServerContext* /*context*/, const ::milvus::grpc::GetVectorIDsParam* /*request*/, ::milvus::grpc::VectorIds* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedGetVectorIDs(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::GetVectorIDsParam,::milvus::grpc::VectorIds>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_Search : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_Search() { - ::grpc::Service::MarkMethodStreamed(17, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::SearchParam, ::milvus::grpc::TopKQueryResult>(std::bind(&WithStreamedUnaryMethod_Search::StreamedSearch, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_Search() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status Search(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedSearch(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::SearchParam,::milvus::grpc::TopKQueryResult>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_SearchByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_SearchByID() { - ::grpc::Service::MarkMethodStreamed(18, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::SearchByIDParam, ::milvus::grpc::TopKQueryResult>(std::bind(&WithStreamedUnaryMethod_SearchByID::StreamedSearchByID, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_SearchByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status SearchByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchByIDParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedSearchByID(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::SearchByIDParam,::milvus::grpc::TopKQueryResult>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_SearchInFiles : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_SearchInFiles() { - ::grpc::Service::MarkMethodStreamed(19, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::SearchInFilesParam, ::milvus::grpc::TopKQueryResult>(std::bind(&WithStreamedUnaryMethod_SearchInFiles::StreamedSearchInFiles, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_SearchInFiles() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status SearchInFiles(::grpc::ServerContext* /*context*/, const ::milvus::grpc::SearchInFilesParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedSearchInFiles(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::SearchInFilesParam,::milvus::grpc::TopKQueryResult>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_Cmd : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_Cmd() { - ::grpc::Service::MarkMethodStreamed(20, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::Command, ::milvus::grpc::StringReply>(std::bind(&WithStreamedUnaryMethod_Cmd::StreamedCmd, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_Cmd() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status Cmd(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::StringReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedCmd(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::Command,::milvus::grpc::StringReply>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_DeleteByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_DeleteByID() { - ::grpc::Service::MarkMethodStreamed(21, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::DeleteByIDParam, ::milvus::grpc::Status>(std::bind(&WithStreamedUnaryMethod_DeleteByID::StreamedDeleteByID, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_DeleteByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status DeleteByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::DeleteByIDParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedDeleteByID(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::DeleteByIDParam,::milvus::grpc::Status>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_PreloadCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_PreloadCollection() { - ::grpc::Service::MarkMethodStreamed(22, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>(std::bind(&WithStreamedUnaryMethod_PreloadCollection::StreamedPreloadCollection, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_PreloadCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status PreloadCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedPreloadCollection(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::CollectionName,::milvus::grpc::Status>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_ReloadSegments : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_ReloadSegments() { - ::grpc::Service::MarkMethodStreamed(23, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::ReLoadSegmentsParam, ::milvus::grpc::Status>(std::bind(&WithStreamedUnaryMethod_ReloadSegments::StreamedReloadSegments, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_ReloadSegments() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status ReloadSegments(::grpc::ServerContext* /*context*/, const ::milvus::grpc::ReLoadSegmentsParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedReloadSegments(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::ReLoadSegmentsParam,::milvus::grpc::Status>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_Flush : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_Flush() { - ::grpc::Service::MarkMethodStreamed(24, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::FlushParam, ::milvus::grpc::Status>(std::bind(&WithStreamedUnaryMethod_Flush::StreamedFlush, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_Flush() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status Flush(::grpc::ServerContext* /*context*/, const ::milvus::grpc::FlushParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedFlush(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::FlushParam,::milvus::grpc::Status>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_Compact : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_Compact() { - ::grpc::Service::MarkMethodStreamed(25, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>(std::bind(&WithStreamedUnaryMethod_Compact::StreamedCompact, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_Compact() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status Compact(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedCompact(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::CollectionName,::milvus::grpc::Status>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_CreateHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_CreateHybridCollection() { - ::grpc::Service::MarkMethodStreamed(26, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::Mapping, ::milvus::grpc::Status>(std::bind(&WithStreamedUnaryMethod_CreateHybridCollection::StreamedCreateHybridCollection, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_CreateHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status CreateHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Mapping* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedCreateHybridCollection(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::Mapping,::milvus::grpc::Status>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_HasHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_HasHybridCollection() { - ::grpc::Service::MarkMethodStreamed(27, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::BoolReply>(std::bind(&WithStreamedUnaryMethod_HasHybridCollection::StreamedHasHybridCollection, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_HasHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status HasHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::BoolReply* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedHasHybridCollection(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::CollectionName,::milvus::grpc::BoolReply>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_DropHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_DropHybridCollection() { - ::grpc::Service::MarkMethodStreamed(28, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>(std::bind(&WithStreamedUnaryMethod_DropHybridCollection::StreamedDropHybridCollection, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_DropHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status DropHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedDropHybridCollection(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::CollectionName,::milvus::grpc::Status>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_DescribeHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_DescribeHybridCollection() { - ::grpc::Service::MarkMethodStreamed(29, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Mapping>(std::bind(&WithStreamedUnaryMethod_DescribeHybridCollection::StreamedDescribeHybridCollection, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_DescribeHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status DescribeHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Mapping* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedDescribeHybridCollection(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::CollectionName,::milvus::grpc::Mapping>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_CountHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_CountHybridCollection() { - ::grpc::Service::MarkMethodStreamed(30, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionRowCount>(std::bind(&WithStreamedUnaryMethod_CountHybridCollection::StreamedCountHybridCollection, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_CountHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status CountHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionRowCount* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedCountHybridCollection(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::CollectionName,::milvus::grpc::CollectionRowCount>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_ShowHybridCollections : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_ShowHybridCollections() { - ::grpc::Service::MarkMethodStreamed(31, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::Command, ::milvus::grpc::MappingList>(std::bind(&WithStreamedUnaryMethod_ShowHybridCollections::StreamedShowHybridCollections, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_ShowHybridCollections() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status ShowHybridCollections(::grpc::ServerContext* /*context*/, const ::milvus::grpc::Command* /*request*/, ::milvus::grpc::MappingList* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedShowHybridCollections(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::Command,::milvus::grpc::MappingList>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_ShowHybridCollectionInfo : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_ShowHybridCollectionInfo() { - ::grpc::Service::MarkMethodStreamed(32, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::CollectionInfo>(std::bind(&WithStreamedUnaryMethod_ShowHybridCollectionInfo::StreamedShowHybridCollectionInfo, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_ShowHybridCollectionInfo() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status ShowHybridCollectionInfo(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::CollectionInfo* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedShowHybridCollectionInfo(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::CollectionName,::milvus::grpc::CollectionInfo>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_PreloadHybridCollection : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_PreloadHybridCollection() { - ::grpc::Service::MarkMethodStreamed(33, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::CollectionName, ::milvus::grpc::Status>(std::bind(&WithStreamedUnaryMethod_PreloadHybridCollection::StreamedPreloadHybridCollection, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_PreloadHybridCollection() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status PreloadHybridCollection(::grpc::ServerContext* /*context*/, const ::milvus::grpc::CollectionName* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedPreloadHybridCollection(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::CollectionName,::milvus::grpc::Status>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_InsertEntity : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_InsertEntity() { - ::grpc::Service::MarkMethodStreamed(34, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::HInsertParam, ::milvus::grpc::HEntityIDs>(std::bind(&WithStreamedUnaryMethod_InsertEntity::StreamedInsertEntity, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_InsertEntity() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status InsertEntity(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HInsertParam* /*request*/, ::milvus::grpc::HEntityIDs* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedInsertEntity(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::HInsertParam,::milvus::grpc::HEntityIDs>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_HybridSearch : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_HybridSearch() { - ::grpc::Service::MarkMethodStreamed(35, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::HSearchParam, ::milvus::grpc::TopKQueryResult>(std::bind(&WithStreamedUnaryMethod_HybridSearch::StreamedHybridSearch, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_HybridSearch() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status HybridSearch(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HSearchParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedHybridSearch(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::HSearchParam,::milvus::grpc::TopKQueryResult>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_HybridSearchInSegments : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_HybridSearchInSegments() { - ::grpc::Service::MarkMethodStreamed(36, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::HSearchInSegmentsParam, ::milvus::grpc::TopKQueryResult>(std::bind(&WithStreamedUnaryMethod_HybridSearchInSegments::StreamedHybridSearchInSegments, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_HybridSearchInSegments() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status HybridSearchInSegments(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HSearchInSegmentsParam* /*request*/, ::milvus::grpc::TopKQueryResult* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedHybridSearchInSegments(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::HSearchInSegmentsParam,::milvus::grpc::TopKQueryResult>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_GetEntityByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_GetEntityByID() { - ::grpc::Service::MarkMethodStreamed(37, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::HEntityIdentity, ::milvus::grpc::HEntity>(std::bind(&WithStreamedUnaryMethod_GetEntityByID::StreamedGetEntityByID, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_GetEntityByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status GetEntityByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HEntityIdentity* /*request*/, ::milvus::grpc::HEntity* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedGetEntityByID(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::HEntityIdentity,::milvus::grpc::HEntity>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_GetEntityIDs : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_GetEntityIDs() { - ::grpc::Service::MarkMethodStreamed(38, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::HGetEntityIDsParam, ::milvus::grpc::HEntityIDs>(std::bind(&WithStreamedUnaryMethod_GetEntityIDs::StreamedGetEntityIDs, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_GetEntityIDs() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status GetEntityIDs(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HGetEntityIDsParam* /*request*/, ::milvus::grpc::HEntityIDs* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedGetEntityIDs(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::HGetEntityIDsParam,::milvus::grpc::HEntityIDs>* server_unary_streamer) = 0; - }; - template - class WithStreamedUnaryMethod_DeleteEntitiesByID : public BaseClass { - private: - void BaseClassMustBeDerivedFromService(const Service* /*service*/) {} - public: - WithStreamedUnaryMethod_DeleteEntitiesByID() { - ::grpc::Service::MarkMethodStreamed(39, - new ::grpc::internal::StreamedUnaryHandler< ::milvus::grpc::HDeleteByIDParam, ::milvus::grpc::Status>(std::bind(&WithStreamedUnaryMethod_DeleteEntitiesByID::StreamedDeleteEntitiesByID, this, std::placeholders::_1, std::placeholders::_2))); - } - ~WithStreamedUnaryMethod_DeleteEntitiesByID() override { - BaseClassMustBeDerivedFromService(this); - } - // disable regular version of this method - ::grpc::Status DeleteEntitiesByID(::grpc::ServerContext* /*context*/, const ::milvus::grpc::HDeleteByIDParam* /*request*/, ::milvus::grpc::Status* /*response*/) override { - abort(); - return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, ""); - } - // replace default version of method with streamed unary - virtual ::grpc::Status StreamedDeleteEntitiesByID(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::milvus::grpc::HDeleteByIDParam,::milvus::grpc::Status>* server_unary_streamer) = 0; - }; - typedef WithStreamedUnaryMethod_CreateCollection > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > StreamedUnaryService; - typedef Service SplitStreamedService; - typedef WithStreamedUnaryMethod_CreateCollection > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > StreamedService; -}; - -} // namespace grpc -} // namespace milvus - - -#endif // GRPC_milvus_2eproto__INCLUDED diff --git a/core/src/grpc/gen-milvus/milvus.pb.cc b/core/src/grpc/gen-milvus/milvus.pb.cc deleted file mode 100644 index 2339336b86..0000000000 --- a/core/src/grpc/gen-milvus/milvus.pb.cc +++ /dev/null @@ -1,20489 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: milvus.proto - -#include "milvus.pb.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -// @@protoc_insertion_point(includes) -#include -extern PROTOBUF_INTERNAL_EXPORT_milvus_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<3> scc_info_BooleanQuery_milvus_2eproto; -extern PROTOBUF_INTERNAL_EXPORT_milvus_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_CompareExpr_milvus_2eproto; -extern PROTOBUF_INTERNAL_EXPORT_milvus_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_FieldParam_milvus_2eproto; -extern PROTOBUF_INTERNAL_EXPORT_milvus_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_FieldType_milvus_2eproto; -extern PROTOBUF_INTERNAL_EXPORT_milvus_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_FieldValue_milvus_2eproto; -extern PROTOBUF_INTERNAL_EXPORT_milvus_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_HEntity_milvus_2eproto; -extern PROTOBUF_INTERNAL_EXPORT_milvus_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_HSearchParam_milvus_2eproto; -extern PROTOBUF_INTERNAL_EXPORT_milvus_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_KeyValuePair_milvus_2eproto; -extern PROTOBUF_INTERNAL_EXPORT_milvus_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_Mapping_milvus_2eproto; -extern PROTOBUF_INTERNAL_EXPORT_milvus_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_RangeQuery_milvus_2eproto; -extern PROTOBUF_INTERNAL_EXPORT_milvus_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_RowRecord_milvus_2eproto; -extern PROTOBUF_INTERNAL_EXPORT_milvus_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_SearchParam_milvus_2eproto; -extern PROTOBUF_INTERNAL_EXPORT_status_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Status_status_2eproto; -extern PROTOBUF_INTERNAL_EXPORT_milvus_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_TermQuery_milvus_2eproto; -extern PROTOBUF_INTERNAL_EXPORT_milvus_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_VectorFieldParam_milvus_2eproto; -extern PROTOBUF_INTERNAL_EXPORT_milvus_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_VectorFieldValue_milvus_2eproto; -extern PROTOBUF_INTERNAL_EXPORT_milvus_2eproto ::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_VectorQuery_milvus_2eproto; -namespace milvus { -namespace grpc { -class KeyValuePairDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _KeyValuePair_default_instance_; -class CollectionNameDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _CollectionName_default_instance_; -class CollectionNameListDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _CollectionNameList_default_instance_; -class CollectionSchemaDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _CollectionSchema_default_instance_; -class PartitionParamDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _PartitionParam_default_instance_; -class PartitionListDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _PartitionList_default_instance_; -class RowRecordDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _RowRecord_default_instance_; -class InsertParamDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _InsertParam_default_instance_; -class VectorIdsDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _VectorIds_default_instance_; -class SearchParamDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _SearchParam_default_instance_; -class SearchInFilesParamDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _SearchInFilesParam_default_instance_; -class SearchByIDParamDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _SearchByIDParam_default_instance_; -class ReLoadSegmentsParamDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _ReLoadSegmentsParam_default_instance_; -class TopKQueryResultDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _TopKQueryResult_default_instance_; -class StringReplyDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _StringReply_default_instance_; -class BoolReplyDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _BoolReply_default_instance_; -class CollectionRowCountDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _CollectionRowCount_default_instance_; -class CommandDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _Command_default_instance_; -class IndexParamDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _IndexParam_default_instance_; -class FlushParamDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _FlushParam_default_instance_; -class DeleteByIDParamDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _DeleteByIDParam_default_instance_; -class CollectionInfoDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _CollectionInfo_default_instance_; -class VectorsIdentityDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _VectorsIdentity_default_instance_; -class VectorsDataDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _VectorsData_default_instance_; -class GetVectorIDsParamDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _GetVectorIDsParam_default_instance_; -class VectorFieldParamDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _VectorFieldParam_default_instance_; -class FieldTypeDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; - int data_type_; - const ::milvus::grpc::VectorFieldParam* vector_param_; -} _FieldType_default_instance_; -class FieldParamDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _FieldParam_default_instance_; -class VectorFieldValueDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _VectorFieldValue_default_instance_; -class FieldValueDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; - ::PROTOBUF_NAMESPACE_ID::int32 int32_value_; - ::PROTOBUF_NAMESPACE_ID::int64 int64_value_; - float float_value_; - double double_value_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr string_value_; - bool bool_value_; - const ::milvus::grpc::VectorFieldValue* vector_value_; -} _FieldValue_default_instance_; -class MappingDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _Mapping_default_instance_; -class MappingListDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _MappingList_default_instance_; -class TermQueryDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _TermQuery_default_instance_; -class CompareExprDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _CompareExpr_default_instance_; -class RangeQueryDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _RangeQuery_default_instance_; -class VectorQueryDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _VectorQuery_default_instance_; -class BooleanQueryDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _BooleanQuery_default_instance_; -class GeneralQueryDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; - const ::milvus::grpc::BooleanQuery* boolean_query_; - const ::milvus::grpc::TermQuery* term_query_; - const ::milvus::grpc::RangeQuery* range_query_; - const ::milvus::grpc::VectorQuery* vector_query_; -} _GeneralQuery_default_instance_; -class HSearchParamDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _HSearchParam_default_instance_; -class HSearchInSegmentsParamDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _HSearchInSegmentsParam_default_instance_; -class AttrRecordDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _AttrRecord_default_instance_; -class HEntityDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _HEntity_default_instance_; -class HQueryResultDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _HQueryResult_default_instance_; -class HInsertParamDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _HInsertParam_default_instance_; -class HEntityIdentityDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _HEntityIdentity_default_instance_; -class HEntityIDsDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _HEntityIDs_default_instance_; -class HGetEntityIDsParamDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _HGetEntityIDsParam_default_instance_; -class HDeleteByIDParamDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _HDeleteByIDParam_default_instance_; -class HIndexParamDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _HIndexParam_default_instance_; -} // namespace grpc -} // namespace milvus -static void InitDefaultsscc_info_AttrRecord_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_AttrRecord_default_instance_; - new (ptr) ::milvus::grpc::AttrRecord(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::AttrRecord::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_AttrRecord_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, InitDefaultsscc_info_AttrRecord_milvus_2eproto}, {}}; - -static void InitDefaultsscc_info_BoolReply_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_BoolReply_default_instance_; - new (ptr) ::milvus::grpc::BoolReply(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::BoolReply::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_BoolReply_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, InitDefaultsscc_info_BoolReply_milvus_2eproto}, { - &scc_info_Status_status_2eproto.base,}}; - -static void InitDefaultsscc_info_BooleanQuery_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_BooleanQuery_default_instance_; - new (ptr) ::milvus::grpc::BooleanQuery(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - { - void* ptr = &::milvus::grpc::_GeneralQuery_default_instance_; - new (ptr) ::milvus::grpc::GeneralQuery(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::BooleanQuery::InitAsDefaultInstance(); - ::milvus::grpc::GeneralQuery::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<3> scc_info_BooleanQuery_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 3, InitDefaultsscc_info_BooleanQuery_milvus_2eproto}, { - &scc_info_TermQuery_milvus_2eproto.base, - &scc_info_RangeQuery_milvus_2eproto.base, - &scc_info_VectorQuery_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_CollectionInfo_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_CollectionInfo_default_instance_; - new (ptr) ::milvus::grpc::CollectionInfo(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::CollectionInfo::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_CollectionInfo_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, InitDefaultsscc_info_CollectionInfo_milvus_2eproto}, { - &scc_info_Status_status_2eproto.base,}}; - -static void InitDefaultsscc_info_CollectionName_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_CollectionName_default_instance_; - new (ptr) ::milvus::grpc::CollectionName(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::CollectionName::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_CollectionName_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, InitDefaultsscc_info_CollectionName_milvus_2eproto}, {}}; - -static void InitDefaultsscc_info_CollectionNameList_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_CollectionNameList_default_instance_; - new (ptr) ::milvus::grpc::CollectionNameList(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::CollectionNameList::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_CollectionNameList_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, InitDefaultsscc_info_CollectionNameList_milvus_2eproto}, { - &scc_info_Status_status_2eproto.base,}}; - -static void InitDefaultsscc_info_CollectionRowCount_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_CollectionRowCount_default_instance_; - new (ptr) ::milvus::grpc::CollectionRowCount(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::CollectionRowCount::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_CollectionRowCount_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, InitDefaultsscc_info_CollectionRowCount_milvus_2eproto}, { - &scc_info_Status_status_2eproto.base,}}; - -static void InitDefaultsscc_info_CollectionSchema_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_CollectionSchema_default_instance_; - new (ptr) ::milvus::grpc::CollectionSchema(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::CollectionSchema::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_CollectionSchema_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 2, InitDefaultsscc_info_CollectionSchema_milvus_2eproto}, { - &scc_info_Status_status_2eproto.base, - &scc_info_KeyValuePair_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_Command_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_Command_default_instance_; - new (ptr) ::milvus::grpc::Command(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::Command::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Command_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, InitDefaultsscc_info_Command_milvus_2eproto}, {}}; - -static void InitDefaultsscc_info_CompareExpr_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_CompareExpr_default_instance_; - new (ptr) ::milvus::grpc::CompareExpr(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::CompareExpr::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_CompareExpr_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, InitDefaultsscc_info_CompareExpr_milvus_2eproto}, {}}; - -static void InitDefaultsscc_info_DeleteByIDParam_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_DeleteByIDParam_default_instance_; - new (ptr) ::milvus::grpc::DeleteByIDParam(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::DeleteByIDParam::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_DeleteByIDParam_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, InitDefaultsscc_info_DeleteByIDParam_milvus_2eproto}, {}}; - -static void InitDefaultsscc_info_FieldParam_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_FieldParam_default_instance_; - new (ptr) ::milvus::grpc::FieldParam(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::FieldParam::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_FieldParam_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 2, InitDefaultsscc_info_FieldParam_milvus_2eproto}, { - &scc_info_FieldType_milvus_2eproto.base, - &scc_info_KeyValuePair_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_FieldType_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_FieldType_default_instance_; - new (ptr) ::milvus::grpc::FieldType(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::FieldType::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_FieldType_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, InitDefaultsscc_info_FieldType_milvus_2eproto}, { - &scc_info_VectorFieldParam_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_FieldValue_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_FieldValue_default_instance_; - new (ptr) ::milvus::grpc::FieldValue(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::FieldValue::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_FieldValue_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, InitDefaultsscc_info_FieldValue_milvus_2eproto}, { - &scc_info_VectorFieldValue_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_FlushParam_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_FlushParam_default_instance_; - new (ptr) ::milvus::grpc::FlushParam(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::FlushParam::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_FlushParam_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, InitDefaultsscc_info_FlushParam_milvus_2eproto}, {}}; - -static void InitDefaultsscc_info_GetVectorIDsParam_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_GetVectorIDsParam_default_instance_; - new (ptr) ::milvus::grpc::GetVectorIDsParam(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::GetVectorIDsParam::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_GetVectorIDsParam_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, InitDefaultsscc_info_GetVectorIDsParam_milvus_2eproto}, {}}; - -static void InitDefaultsscc_info_HDeleteByIDParam_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_HDeleteByIDParam_default_instance_; - new (ptr) ::milvus::grpc::HDeleteByIDParam(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::HDeleteByIDParam::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_HDeleteByIDParam_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, InitDefaultsscc_info_HDeleteByIDParam_milvus_2eproto}, {}}; - -static void InitDefaultsscc_info_HEntity_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_HEntity_default_instance_; - new (ptr) ::milvus::grpc::HEntity(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::HEntity::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_HEntity_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 2, InitDefaultsscc_info_HEntity_milvus_2eproto}, { - &scc_info_Status_status_2eproto.base, - &scc_info_FieldValue_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_HEntityIDs_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_HEntityIDs_default_instance_; - new (ptr) ::milvus::grpc::HEntityIDs(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::HEntityIDs::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_HEntityIDs_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, InitDefaultsscc_info_HEntityIDs_milvus_2eproto}, { - &scc_info_Status_status_2eproto.base,}}; - -static void InitDefaultsscc_info_HEntityIdentity_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_HEntityIdentity_default_instance_; - new (ptr) ::milvus::grpc::HEntityIdentity(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::HEntityIdentity::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_HEntityIdentity_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, InitDefaultsscc_info_HEntityIdentity_milvus_2eproto}, {}}; - -static void InitDefaultsscc_info_HGetEntityIDsParam_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_HGetEntityIDsParam_default_instance_; - new (ptr) ::milvus::grpc::HGetEntityIDsParam(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::HGetEntityIDsParam::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_HGetEntityIDsParam_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, InitDefaultsscc_info_HGetEntityIDsParam_milvus_2eproto}, {}}; - -static void InitDefaultsscc_info_HIndexParam_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_HIndexParam_default_instance_; - new (ptr) ::milvus::grpc::HIndexParam(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::HIndexParam::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_HIndexParam_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 2, InitDefaultsscc_info_HIndexParam_milvus_2eproto}, { - &scc_info_Status_status_2eproto.base, - &scc_info_KeyValuePair_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_HInsertParam_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_HInsertParam_default_instance_; - new (ptr) ::milvus::grpc::HInsertParam(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::HInsertParam::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_HInsertParam_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 2, InitDefaultsscc_info_HInsertParam_milvus_2eproto}, { - &scc_info_HEntity_milvus_2eproto.base, - &scc_info_KeyValuePair_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_HQueryResult_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_HQueryResult_default_instance_; - new (ptr) ::milvus::grpc::HQueryResult(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::HQueryResult::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_HQueryResult_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 2, InitDefaultsscc_info_HQueryResult_milvus_2eproto}, { - &scc_info_Status_status_2eproto.base, - &scc_info_HEntity_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_HSearchInSegmentsParam_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_HSearchInSegmentsParam_default_instance_; - new (ptr) ::milvus::grpc::HSearchInSegmentsParam(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::HSearchInSegmentsParam::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_HSearchInSegmentsParam_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, InitDefaultsscc_info_HSearchInSegmentsParam_milvus_2eproto}, { - &scc_info_HSearchParam_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_HSearchParam_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_HSearchParam_default_instance_; - new (ptr) ::milvus::grpc::HSearchParam(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::HSearchParam::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_HSearchParam_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 2, InitDefaultsscc_info_HSearchParam_milvus_2eproto}, { - &scc_info_BooleanQuery_milvus_2eproto.base, - &scc_info_KeyValuePair_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_IndexParam_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_IndexParam_default_instance_; - new (ptr) ::milvus::grpc::IndexParam(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::IndexParam::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_IndexParam_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 2, InitDefaultsscc_info_IndexParam_milvus_2eproto}, { - &scc_info_Status_status_2eproto.base, - &scc_info_KeyValuePair_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_InsertParam_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_InsertParam_default_instance_; - new (ptr) ::milvus::grpc::InsertParam(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::InsertParam::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_InsertParam_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 2, InitDefaultsscc_info_InsertParam_milvus_2eproto}, { - &scc_info_RowRecord_milvus_2eproto.base, - &scc_info_KeyValuePair_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_KeyValuePair_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_KeyValuePair_default_instance_; - new (ptr) ::milvus::grpc::KeyValuePair(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::KeyValuePair::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_KeyValuePair_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, InitDefaultsscc_info_KeyValuePair_milvus_2eproto}, {}}; - -static void InitDefaultsscc_info_Mapping_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_Mapping_default_instance_; - new (ptr) ::milvus::grpc::Mapping(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::Mapping::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_Mapping_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 2, InitDefaultsscc_info_Mapping_milvus_2eproto}, { - &scc_info_Status_status_2eproto.base, - &scc_info_FieldParam_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_MappingList_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_MappingList_default_instance_; - new (ptr) ::milvus::grpc::MappingList(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::MappingList::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_MappingList_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 2, InitDefaultsscc_info_MappingList_milvus_2eproto}, { - &scc_info_Status_status_2eproto.base, - &scc_info_Mapping_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_PartitionList_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_PartitionList_default_instance_; - new (ptr) ::milvus::grpc::PartitionList(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::PartitionList::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_PartitionList_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, InitDefaultsscc_info_PartitionList_milvus_2eproto}, { - &scc_info_Status_status_2eproto.base,}}; - -static void InitDefaultsscc_info_PartitionParam_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_PartitionParam_default_instance_; - new (ptr) ::milvus::grpc::PartitionParam(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::PartitionParam::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_PartitionParam_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, InitDefaultsscc_info_PartitionParam_milvus_2eproto}, {}}; - -static void InitDefaultsscc_info_RangeQuery_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_RangeQuery_default_instance_; - new (ptr) ::milvus::grpc::RangeQuery(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::RangeQuery::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_RangeQuery_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 2, InitDefaultsscc_info_RangeQuery_milvus_2eproto}, { - &scc_info_CompareExpr_milvus_2eproto.base, - &scc_info_KeyValuePair_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_ReLoadSegmentsParam_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_ReLoadSegmentsParam_default_instance_; - new (ptr) ::milvus::grpc::ReLoadSegmentsParam(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::ReLoadSegmentsParam::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_ReLoadSegmentsParam_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, InitDefaultsscc_info_ReLoadSegmentsParam_milvus_2eproto}, {}}; - -static void InitDefaultsscc_info_RowRecord_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_RowRecord_default_instance_; - new (ptr) ::milvus::grpc::RowRecord(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::RowRecord::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_RowRecord_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, InitDefaultsscc_info_RowRecord_milvus_2eproto}, {}}; - -static void InitDefaultsscc_info_SearchByIDParam_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_SearchByIDParam_default_instance_; - new (ptr) ::milvus::grpc::SearchByIDParam(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::SearchByIDParam::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_SearchByIDParam_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, InitDefaultsscc_info_SearchByIDParam_milvus_2eproto}, { - &scc_info_KeyValuePair_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_SearchInFilesParam_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_SearchInFilesParam_default_instance_; - new (ptr) ::milvus::grpc::SearchInFilesParam(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::SearchInFilesParam::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_SearchInFilesParam_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, InitDefaultsscc_info_SearchInFilesParam_milvus_2eproto}, { - &scc_info_SearchParam_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_SearchParam_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_SearchParam_default_instance_; - new (ptr) ::milvus::grpc::SearchParam(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::SearchParam::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_SearchParam_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 2, InitDefaultsscc_info_SearchParam_milvus_2eproto}, { - &scc_info_RowRecord_milvus_2eproto.base, - &scc_info_KeyValuePair_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_StringReply_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_StringReply_default_instance_; - new (ptr) ::milvus::grpc::StringReply(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::StringReply::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_StringReply_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, InitDefaultsscc_info_StringReply_milvus_2eproto}, { - &scc_info_Status_status_2eproto.base,}}; - -static void InitDefaultsscc_info_TermQuery_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_TermQuery_default_instance_; - new (ptr) ::milvus::grpc::TermQuery(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::TermQuery::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_TermQuery_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, InitDefaultsscc_info_TermQuery_milvus_2eproto}, { - &scc_info_KeyValuePair_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_TopKQueryResult_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_TopKQueryResult_default_instance_; - new (ptr) ::milvus::grpc::TopKQueryResult(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::TopKQueryResult::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_TopKQueryResult_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, InitDefaultsscc_info_TopKQueryResult_milvus_2eproto}, { - &scc_info_Status_status_2eproto.base,}}; - -static void InitDefaultsscc_info_VectorFieldParam_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_VectorFieldParam_default_instance_; - new (ptr) ::milvus::grpc::VectorFieldParam(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::VectorFieldParam::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_VectorFieldParam_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, InitDefaultsscc_info_VectorFieldParam_milvus_2eproto}, {}}; - -static void InitDefaultsscc_info_VectorFieldValue_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_VectorFieldValue_default_instance_; - new (ptr) ::milvus::grpc::VectorFieldValue(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::VectorFieldValue::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_VectorFieldValue_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, InitDefaultsscc_info_VectorFieldValue_milvus_2eproto}, { - &scc_info_RowRecord_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_VectorIds_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_VectorIds_default_instance_; - new (ptr) ::milvus::grpc::VectorIds(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::VectorIds::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<1> scc_info_VectorIds_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 1, InitDefaultsscc_info_VectorIds_milvus_2eproto}, { - &scc_info_Status_status_2eproto.base,}}; - -static void InitDefaultsscc_info_VectorQuery_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_VectorQuery_default_instance_; - new (ptr) ::milvus::grpc::VectorQuery(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::VectorQuery::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_VectorQuery_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 2, InitDefaultsscc_info_VectorQuery_milvus_2eproto}, { - &scc_info_RowRecord_milvus_2eproto.base, - &scc_info_KeyValuePair_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_VectorsData_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_VectorsData_default_instance_; - new (ptr) ::milvus::grpc::VectorsData(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::VectorsData::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<2> scc_info_VectorsData_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 2, InitDefaultsscc_info_VectorsData_milvus_2eproto}, { - &scc_info_Status_status_2eproto.base, - &scc_info_RowRecord_milvus_2eproto.base,}}; - -static void InitDefaultsscc_info_VectorsIdentity_milvus_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_VectorsIdentity_default_instance_; - new (ptr) ::milvus::grpc::VectorsIdentity(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::VectorsIdentity::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_VectorsIdentity_milvus_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, InitDefaultsscc_info_VectorsIdentity_milvus_2eproto}, {}}; - -static ::PROTOBUF_NAMESPACE_ID::Metadata file_level_metadata_milvus_2eproto[49]; -static const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* file_level_enum_descriptors_milvus_2eproto[3]; -static constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_milvus_2eproto = nullptr; - -const ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_milvus_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::KeyValuePair, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::KeyValuePair, key_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::KeyValuePair, value_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CollectionName, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CollectionName, collection_name_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CollectionNameList, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CollectionNameList, status_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CollectionNameList, collection_names_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CollectionSchema, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CollectionSchema, status_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CollectionSchema, collection_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CollectionSchema, dimension_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CollectionSchema, index_file_size_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CollectionSchema, metric_type_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CollectionSchema, extra_params_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::PartitionParam, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::PartitionParam, collection_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::PartitionParam, tag_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::PartitionList, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::PartitionList, status_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::PartitionList, partition_tag_array_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::RowRecord, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::RowRecord, float_data_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::RowRecord, binary_data_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::InsertParam, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::InsertParam, collection_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::InsertParam, row_record_array_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::InsertParam, row_id_array_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::InsertParam, partition_tag_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::InsertParam, extra_params_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::VectorIds, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::VectorIds, status_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::VectorIds, vector_id_array_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::SearchParam, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::SearchParam, collection_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::SearchParam, partition_tag_array_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::SearchParam, query_record_array_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::SearchParam, topk_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::SearchParam, extra_params_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::SearchInFilesParam, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::SearchInFilesParam, file_id_array_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::SearchInFilesParam, search_param_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::SearchByIDParam, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::SearchByIDParam, collection_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::SearchByIDParam, partition_tag_array_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::SearchByIDParam, id_array_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::SearchByIDParam, topk_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::SearchByIDParam, extra_params_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::ReLoadSegmentsParam, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::ReLoadSegmentsParam, collection_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::ReLoadSegmentsParam, segment_id_array_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::TopKQueryResult, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::TopKQueryResult, status_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::TopKQueryResult, row_num_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::TopKQueryResult, ids_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::TopKQueryResult, distances_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::StringReply, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::StringReply, status_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::StringReply, string_reply_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::BoolReply, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::BoolReply, status_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::BoolReply, bool_reply_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CollectionRowCount, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CollectionRowCount, status_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CollectionRowCount, collection_row_count_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::Command, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::Command, cmd_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::IndexParam, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::IndexParam, status_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::IndexParam, collection_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::IndexParam, index_type_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::IndexParam, extra_params_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::FlushParam, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::FlushParam, collection_name_array_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::DeleteByIDParam, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::DeleteByIDParam, collection_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::DeleteByIDParam, id_array_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CollectionInfo, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CollectionInfo, status_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CollectionInfo, json_info_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::VectorsIdentity, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::VectorsIdentity, collection_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::VectorsIdentity, id_array_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::VectorsData, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::VectorsData, status_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::VectorsData, vectors_data_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::GetVectorIDsParam, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::GetVectorIDsParam, collection_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::GetVectorIDsParam, segment_name_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::VectorFieldParam, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::VectorFieldParam, dimension_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::FieldType, _internal_metadata_), - ~0u, // no _extensions_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::FieldType, _oneof_case_[0]), - ~0u, // no _weak_field_map_ - offsetof(::milvus::grpc::FieldTypeDefaultTypeInternal, data_type_), - offsetof(::milvus::grpc::FieldTypeDefaultTypeInternal, vector_param_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::FieldType, value_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::FieldParam, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::FieldParam, id_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::FieldParam, name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::FieldParam, type_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::FieldParam, extra_params_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::VectorFieldValue, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::VectorFieldValue, value_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::FieldValue, _internal_metadata_), - ~0u, // no _extensions_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::FieldValue, _oneof_case_[0]), - ~0u, // no _weak_field_map_ - offsetof(::milvus::grpc::FieldValueDefaultTypeInternal, int32_value_), - offsetof(::milvus::grpc::FieldValueDefaultTypeInternal, int64_value_), - offsetof(::milvus::grpc::FieldValueDefaultTypeInternal, float_value_), - offsetof(::milvus::grpc::FieldValueDefaultTypeInternal, double_value_), - offsetof(::milvus::grpc::FieldValueDefaultTypeInternal, string_value_), - offsetof(::milvus::grpc::FieldValueDefaultTypeInternal, bool_value_), - offsetof(::milvus::grpc::FieldValueDefaultTypeInternal, vector_value_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::FieldValue, value_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::Mapping, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::Mapping, status_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::Mapping, collection_id_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::Mapping, collection_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::Mapping, fields_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::MappingList, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::MappingList, status_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::MappingList, mapping_list_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::TermQuery, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::TermQuery, field_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::TermQuery, values_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::TermQuery, value_num_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::TermQuery, boost_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::TermQuery, extra_params_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CompareExpr, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CompareExpr, operator__), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::CompareExpr, operand_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::RangeQuery, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::RangeQuery, field_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::RangeQuery, operand_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::RangeQuery, boost_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::RangeQuery, extra_params_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::VectorQuery, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::VectorQuery, field_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::VectorQuery, query_boost_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::VectorQuery, records_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::VectorQuery, topk_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::VectorQuery, extra_params_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::BooleanQuery, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::BooleanQuery, occur_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::BooleanQuery, general_query_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::GeneralQuery, _internal_metadata_), - ~0u, // no _extensions_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::GeneralQuery, _oneof_case_[0]), - ~0u, // no _weak_field_map_ - offsetof(::milvus::grpc::GeneralQueryDefaultTypeInternal, boolean_query_), - offsetof(::milvus::grpc::GeneralQueryDefaultTypeInternal, term_query_), - offsetof(::milvus::grpc::GeneralQueryDefaultTypeInternal, range_query_), - offsetof(::milvus::grpc::GeneralQueryDefaultTypeInternal, vector_query_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::GeneralQuery, query_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HSearchParam, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HSearchParam, collection_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HSearchParam, partition_tag_array_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HSearchParam, general_query_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HSearchParam, extra_params_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HSearchInSegmentsParam, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HSearchInSegmentsParam, segment_id_array_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HSearchInSegmentsParam, search_param_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::AttrRecord, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::AttrRecord, value_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HEntity, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HEntity, status_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HEntity, entity_id_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HEntity, field_names_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HEntity, attr_records_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HEntity, row_num_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HEntity, result_values_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HQueryResult, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HQueryResult, status_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HQueryResult, entities_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HQueryResult, row_num_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HQueryResult, score_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HQueryResult, distance_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HInsertParam, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HInsertParam, collection_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HInsertParam, partition_tag_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HInsertParam, entities_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HInsertParam, entity_id_array_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HInsertParam, extra_params_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HEntityIdentity, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HEntityIdentity, collection_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HEntityIdentity, id_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HEntityIDs, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HEntityIDs, status_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HEntityIDs, entity_id_array_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HGetEntityIDsParam, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HGetEntityIDsParam, collection_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HGetEntityIDsParam, segment_name_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HDeleteByIDParam, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HDeleteByIDParam, collection_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HDeleteByIDParam, id_array_), - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HIndexParam, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HIndexParam, status_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HIndexParam, collection_name_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HIndexParam, index_type_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::HIndexParam, extra_params_), -}; -static const ::PROTOBUF_NAMESPACE_ID::internal::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { - { 0, -1, sizeof(::milvus::grpc::KeyValuePair)}, - { 7, -1, sizeof(::milvus::grpc::CollectionName)}, - { 13, -1, sizeof(::milvus::grpc::CollectionNameList)}, - { 20, -1, sizeof(::milvus::grpc::CollectionSchema)}, - { 31, -1, sizeof(::milvus::grpc::PartitionParam)}, - { 38, -1, sizeof(::milvus::grpc::PartitionList)}, - { 45, -1, sizeof(::milvus::grpc::RowRecord)}, - { 52, -1, sizeof(::milvus::grpc::InsertParam)}, - { 62, -1, sizeof(::milvus::grpc::VectorIds)}, - { 69, -1, sizeof(::milvus::grpc::SearchParam)}, - { 79, -1, sizeof(::milvus::grpc::SearchInFilesParam)}, - { 86, -1, sizeof(::milvus::grpc::SearchByIDParam)}, - { 96, -1, sizeof(::milvus::grpc::ReLoadSegmentsParam)}, - { 103, -1, sizeof(::milvus::grpc::TopKQueryResult)}, - { 112, -1, sizeof(::milvus::grpc::StringReply)}, - { 119, -1, sizeof(::milvus::grpc::BoolReply)}, - { 126, -1, sizeof(::milvus::grpc::CollectionRowCount)}, - { 133, -1, sizeof(::milvus::grpc::Command)}, - { 139, -1, sizeof(::milvus::grpc::IndexParam)}, - { 148, -1, sizeof(::milvus::grpc::FlushParam)}, - { 154, -1, sizeof(::milvus::grpc::DeleteByIDParam)}, - { 161, -1, sizeof(::milvus::grpc::CollectionInfo)}, - { 168, -1, sizeof(::milvus::grpc::VectorsIdentity)}, - { 175, -1, sizeof(::milvus::grpc::VectorsData)}, - { 182, -1, sizeof(::milvus::grpc::GetVectorIDsParam)}, - { 189, -1, sizeof(::milvus::grpc::VectorFieldParam)}, - { 195, -1, sizeof(::milvus::grpc::FieldType)}, - { 203, -1, sizeof(::milvus::grpc::FieldParam)}, - { 212, -1, sizeof(::milvus::grpc::VectorFieldValue)}, - { 218, -1, sizeof(::milvus::grpc::FieldValue)}, - { 231, -1, sizeof(::milvus::grpc::Mapping)}, - { 240, -1, sizeof(::milvus::grpc::MappingList)}, - { 247, -1, sizeof(::milvus::grpc::TermQuery)}, - { 257, -1, sizeof(::milvus::grpc::CompareExpr)}, - { 264, -1, sizeof(::milvus::grpc::RangeQuery)}, - { 273, -1, sizeof(::milvus::grpc::VectorQuery)}, - { 283, -1, sizeof(::milvus::grpc::BooleanQuery)}, - { 290, -1, sizeof(::milvus::grpc::GeneralQuery)}, - { 300, -1, sizeof(::milvus::grpc::HSearchParam)}, - { 309, -1, sizeof(::milvus::grpc::HSearchInSegmentsParam)}, - { 316, -1, sizeof(::milvus::grpc::AttrRecord)}, - { 322, -1, sizeof(::milvus::grpc::HEntity)}, - { 333, -1, sizeof(::milvus::grpc::HQueryResult)}, - { 343, -1, sizeof(::milvus::grpc::HInsertParam)}, - { 353, -1, sizeof(::milvus::grpc::HEntityIdentity)}, - { 360, -1, sizeof(::milvus::grpc::HEntityIDs)}, - { 367, -1, sizeof(::milvus::grpc::HGetEntityIDsParam)}, - { 374, -1, sizeof(::milvus::grpc::HDeleteByIDParam)}, - { 381, -1, sizeof(::milvus::grpc::HIndexParam)}, -}; - -static ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = { - reinterpret_cast(&::milvus::grpc::_KeyValuePair_default_instance_), - reinterpret_cast(&::milvus::grpc::_CollectionName_default_instance_), - reinterpret_cast(&::milvus::grpc::_CollectionNameList_default_instance_), - reinterpret_cast(&::milvus::grpc::_CollectionSchema_default_instance_), - reinterpret_cast(&::milvus::grpc::_PartitionParam_default_instance_), - reinterpret_cast(&::milvus::grpc::_PartitionList_default_instance_), - reinterpret_cast(&::milvus::grpc::_RowRecord_default_instance_), - reinterpret_cast(&::milvus::grpc::_InsertParam_default_instance_), - reinterpret_cast(&::milvus::grpc::_VectorIds_default_instance_), - reinterpret_cast(&::milvus::grpc::_SearchParam_default_instance_), - reinterpret_cast(&::milvus::grpc::_SearchInFilesParam_default_instance_), - reinterpret_cast(&::milvus::grpc::_SearchByIDParam_default_instance_), - reinterpret_cast(&::milvus::grpc::_ReLoadSegmentsParam_default_instance_), - reinterpret_cast(&::milvus::grpc::_TopKQueryResult_default_instance_), - reinterpret_cast(&::milvus::grpc::_StringReply_default_instance_), - reinterpret_cast(&::milvus::grpc::_BoolReply_default_instance_), - reinterpret_cast(&::milvus::grpc::_CollectionRowCount_default_instance_), - reinterpret_cast(&::milvus::grpc::_Command_default_instance_), - reinterpret_cast(&::milvus::grpc::_IndexParam_default_instance_), - reinterpret_cast(&::milvus::grpc::_FlushParam_default_instance_), - reinterpret_cast(&::milvus::grpc::_DeleteByIDParam_default_instance_), - reinterpret_cast(&::milvus::grpc::_CollectionInfo_default_instance_), - reinterpret_cast(&::milvus::grpc::_VectorsIdentity_default_instance_), - reinterpret_cast(&::milvus::grpc::_VectorsData_default_instance_), - reinterpret_cast(&::milvus::grpc::_GetVectorIDsParam_default_instance_), - reinterpret_cast(&::milvus::grpc::_VectorFieldParam_default_instance_), - reinterpret_cast(&::milvus::grpc::_FieldType_default_instance_), - reinterpret_cast(&::milvus::grpc::_FieldParam_default_instance_), - reinterpret_cast(&::milvus::grpc::_VectorFieldValue_default_instance_), - reinterpret_cast(&::milvus::grpc::_FieldValue_default_instance_), - reinterpret_cast(&::milvus::grpc::_Mapping_default_instance_), - reinterpret_cast(&::milvus::grpc::_MappingList_default_instance_), - reinterpret_cast(&::milvus::grpc::_TermQuery_default_instance_), - reinterpret_cast(&::milvus::grpc::_CompareExpr_default_instance_), - reinterpret_cast(&::milvus::grpc::_RangeQuery_default_instance_), - reinterpret_cast(&::milvus::grpc::_VectorQuery_default_instance_), - reinterpret_cast(&::milvus::grpc::_BooleanQuery_default_instance_), - reinterpret_cast(&::milvus::grpc::_GeneralQuery_default_instance_), - reinterpret_cast(&::milvus::grpc::_HSearchParam_default_instance_), - reinterpret_cast(&::milvus::grpc::_HSearchInSegmentsParam_default_instance_), - reinterpret_cast(&::milvus::grpc::_AttrRecord_default_instance_), - reinterpret_cast(&::milvus::grpc::_HEntity_default_instance_), - reinterpret_cast(&::milvus::grpc::_HQueryResult_default_instance_), - reinterpret_cast(&::milvus::grpc::_HInsertParam_default_instance_), - reinterpret_cast(&::milvus::grpc::_HEntityIdentity_default_instance_), - reinterpret_cast(&::milvus::grpc::_HEntityIDs_default_instance_), - reinterpret_cast(&::milvus::grpc::_HGetEntityIDsParam_default_instance_), - reinterpret_cast(&::milvus::grpc::_HDeleteByIDParam_default_instance_), - reinterpret_cast(&::milvus::grpc::_HIndexParam_default_instance_), -}; - -const char descriptor_table_protodef_milvus_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = - "\n\014milvus.proto\022\013milvus.grpc\032\014status.prot" - "o\"*\n\014KeyValuePair\022\013\n\003key\030\001 \001(\t\022\r\n\005value\030" - "\002 \001(\t\")\n\016CollectionName\022\027\n\017collection_na" - "me\030\001 \001(\t\"S\n\022CollectionNameList\022#\n\006status" - "\030\001 \001(\0132\023.milvus.grpc.Status\022\030\n\020collectio" - "n_names\030\002 \003(\t\"\302\001\n\020CollectionSchema\022#\n\006st" - "atus\030\001 \001(\0132\023.milvus.grpc.Status\022\027\n\017colle" - "ction_name\030\002 \001(\t\022\021\n\tdimension\030\003 \001(\003\022\027\n\017i" - "ndex_file_size\030\004 \001(\003\022\023\n\013metric_type\030\005 \001(" - "\005\022/\n\014extra_params\030\006 \003(\0132\031.milvus.grpc.Ke" - "yValuePair\"6\n\016PartitionParam\022\027\n\017collecti" - "on_name\030\001 \001(\t\022\013\n\003tag\030\002 \001(\t\"Q\n\rPartitionL" - "ist\022#\n\006status\030\001 \001(\0132\023.milvus.grpc.Status" - "\022\033\n\023partition_tag_array\030\002 \003(\t\"4\n\tRowReco" - "rd\022\022\n\nfloat_data\030\001 \003(\002\022\023\n\013binary_data\030\002 " - "\001(\014\"\266\001\n\013InsertParam\022\027\n\017collection_name\030\001" - " \001(\t\0220\n\020row_record_array\030\002 \003(\0132\026.milvus." - "grpc.RowRecord\022\024\n\014row_id_array\030\003 \003(\003\022\025\n\r" - "partition_tag\030\004 \001(\t\022/\n\014extra_params\030\005 \003(" - "\0132\031.milvus.grpc.KeyValuePair\"I\n\tVectorId" - "s\022#\n\006status\030\001 \001(\0132\023.milvus.grpc.Status\022\027" - "\n\017vector_id_array\030\002 \003(\003\"\266\001\n\013SearchParam\022" - "\027\n\017collection_name\030\001 \001(\t\022\033\n\023partition_ta" - "g_array\030\002 \003(\t\0222\n\022query_record_array\030\003 \003(" - "\0132\026.milvus.grpc.RowRecord\022\014\n\004topk\030\004 \001(\003\022" - "/\n\014extra_params\030\005 \003(\0132\031.milvus.grpc.KeyV" - "aluePair\"[\n\022SearchInFilesParam\022\025\n\rfile_i" - "d_array\030\001 \003(\t\022.\n\014search_param\030\002 \001(\0132\030.mi" - "lvus.grpc.SearchParam\"\230\001\n\017SearchByIDPara" - "m\022\027\n\017collection_name\030\001 \001(\t\022\033\n\023partition_" - "tag_array\030\002 \003(\t\022\020\n\010id_array\030\003 \003(\003\022\014\n\004top" - "k\030\004 \001(\003\022/\n\014extra_params\030\005 \003(\0132\031.milvus.g" - "rpc.KeyValuePair\"H\n\023ReLoadSegmentsParam\022" - "\027\n\017collection_name\030\001 \001(\t\022\030\n\020segment_id_a" - "rray\030\002 \003(\t\"g\n\017TopKQueryResult\022#\n\006status\030" - "\001 \001(\0132\023.milvus.grpc.Status\022\017\n\007row_num\030\002 " - "\001(\003\022\013\n\003ids\030\003 \003(\003\022\021\n\tdistances\030\004 \003(\002\"H\n\013S" - "tringReply\022#\n\006status\030\001 \001(\0132\023.milvus.grpc" - ".Status\022\024\n\014string_reply\030\002 \001(\t\"D\n\tBoolRep" - "ly\022#\n\006status\030\001 \001(\0132\023.milvus.grpc.Status\022" - "\022\n\nbool_reply\030\002 \001(\010\"W\n\022CollectionRowCoun" - "t\022#\n\006status\030\001 \001(\0132\023.milvus.grpc.Status\022\034" - "\n\024collection_row_count\030\002 \001(\003\"\026\n\007Command\022" - "\013\n\003cmd\030\001 \001(\t\"\217\001\n\nIndexParam\022#\n\006status\030\001 " - "\001(\0132\023.milvus.grpc.Status\022\027\n\017collection_n" - "ame\030\002 \001(\t\022\022\n\nindex_type\030\003 \001(\005\022/\n\014extra_p" - "arams\030\004 \003(\0132\031.milvus.grpc.KeyValuePair\"+" - "\n\nFlushParam\022\035\n\025collection_name_array\030\001 " - "\003(\t\"<\n\017DeleteByIDParam\022\027\n\017collection_nam" - "e\030\001 \001(\t\022\020\n\010id_array\030\002 \003(\003\"H\n\016CollectionI" - "nfo\022#\n\006status\030\001 \001(\0132\023.milvus.grpc.Status" - "\022\021\n\tjson_info\030\002 \001(\t\"<\n\017VectorsIdentity\022\027" - "\n\017collection_name\030\001 \001(\t\022\020\n\010id_array\030\002 \003(" - "\003\"`\n\013VectorsData\022#\n\006status\030\001 \001(\0132\023.milvu" - "s.grpc.Status\022,\n\014vectors_data\030\002 \003(\0132\026.mi" - "lvus.grpc.RowRecord\"B\n\021GetVectorIDsParam" - "\022\027\n\017collection_name\030\001 \001(\t\022\024\n\014segment_nam" - "e\030\002 \001(\t\"%\n\020VectorFieldParam\022\021\n\tdimension" - "\030\001 \001(\003\"w\n\tFieldType\022*\n\tdata_type\030\001 \001(\0162\025" - ".milvus.grpc.DataTypeH\000\0225\n\014vector_param\030" - "\002 \001(\0132\035.milvus.grpc.VectorFieldParamH\000B\007" - "\n\005value\"}\n\nFieldParam\022\n\n\002id\030\001 \001(\004\022\014\n\004nam" - "e\030\002 \001(\t\022$\n\004type\030\003 \001(\0132\026.milvus.grpc.Fiel" - "dType\022/\n\014extra_params\030\004 \003(\0132\031.milvus.grp" - "c.KeyValuePair\"9\n\020VectorFieldValue\022%\n\005va" - "lue\030\001 \003(\0132\026.milvus.grpc.RowRecord\"\327\001\n\nFi" - "eldValue\022\025\n\013int32_value\030\001 \001(\005H\000\022\025\n\013int64" - "_value\030\002 \001(\003H\000\022\025\n\013float_value\030\003 \001(\002H\000\022\026\n" - "\014double_value\030\004 \001(\001H\000\022\026\n\014string_value\030\005 " - "\001(\tH\000\022\024\n\nbool_value\030\006 \001(\010H\000\0225\n\014vector_va" - "lue\030\007 \001(\0132\035.milvus.grpc.VectorFieldValue" - "H\000B\007\n\005value\"\207\001\n\007Mapping\022#\n\006status\030\001 \001(\0132" - "\023.milvus.grpc.Status\022\025\n\rcollection_id\030\002 " - "\001(\004\022\027\n\017collection_name\030\003 \001(\t\022\'\n\006fields\030\004" - " \003(\0132\027.milvus.grpc.FieldParam\"^\n\013Mapping" - "List\022#\n\006status\030\001 \001(\0132\023.milvus.grpc.Statu" - "s\022*\n\014mapping_list\030\002 \003(\0132\024.milvus.grpc.Ma" - "pping\"\202\001\n\tTermQuery\022\022\n\nfield_name\030\001 \001(\t\022" - "\016\n\006values\030\002 \001(\014\022\021\n\tvalue_num\030\003 \001(\003\022\r\n\005bo" - "ost\030\004 \001(\002\022/\n\014extra_params\030\005 \003(\0132\031.milvus" - ".grpc.KeyValuePair\"N\n\013CompareExpr\022.\n\010ope" - "rator\030\001 \001(\0162\034.milvus.grpc.CompareOperato" - "r\022\017\n\007operand\030\002 \001(\t\"\213\001\n\nRangeQuery\022\022\n\nfie" - "ld_name\030\001 \001(\t\022)\n\007operand\030\002 \003(\0132\030.milvus." - "grpc.CompareExpr\022\r\n\005boost\030\003 \001(\002\022/\n\014extra" - "_params\030\004 \003(\0132\031.milvus.grpc.KeyValuePair" - "\"\236\001\n\013VectorQuery\022\022\n\nfield_name\030\001 \001(\t\022\023\n\013" - "query_boost\030\002 \001(\002\022\'\n\007records\030\003 \003(\0132\026.mil" - "vus.grpc.RowRecord\022\014\n\004topk\030\004 \001(\003\022/\n\014extr" - "a_params\030\005 \003(\0132\031.milvus.grpc.KeyValuePai" - "r\"c\n\014BooleanQuery\022!\n\005occur\030\001 \001(\0162\022.milvu" - "s.grpc.Occur\0220\n\rgeneral_query\030\002 \003(\0132\031.mi" - "lvus.grpc.GeneralQuery\"\333\001\n\014GeneralQuery\022" - "2\n\rboolean_query\030\001 \001(\0132\031.milvus.grpc.Boo" - "leanQueryH\000\022,\n\nterm_query\030\002 \001(\0132\026.milvus" - ".grpc.TermQueryH\000\022.\n\013range_query\030\003 \001(\0132\027" - ".milvus.grpc.RangeQueryH\000\0220\n\014vector_quer" - "y\030\004 \001(\0132\030.milvus.grpc.VectorQueryH\000B\007\n\005q" - "uery\"\247\001\n\014HSearchParam\022\027\n\017collection_name" - "\030\001 \001(\t\022\033\n\023partition_tag_array\030\002 \003(\t\0220\n\rg" - "eneral_query\030\003 \001(\0132\031.milvus.grpc.General" - "Query\022/\n\014extra_params\030\004 \003(\0132\031.milvus.grp" - "c.KeyValuePair\"c\n\026HSearchInSegmentsParam" - "\022\030\n\020segment_id_array\030\001 \003(\t\022/\n\014search_par" - "am\030\002 \001(\0132\031.milvus.grpc.HSearchParam\"\033\n\nA" - "ttrRecord\022\r\n\005value\030\001 \003(\t\"\255\001\n\007HEntity\022#\n\006" - "status\030\001 \001(\0132\023.milvus.grpc.Status\022\021\n\tent" - "ity_id\030\002 \001(\003\022\023\n\013field_names\030\003 \003(\t\022\024\n\014att" - "r_records\030\004 \001(\014\022\017\n\007row_num\030\005 \001(\003\022.\n\rresu" - "lt_values\030\006 \003(\0132\027.milvus.grpc.FieldValue" - "\"\215\001\n\014HQueryResult\022#\n\006status\030\001 \001(\0132\023.milv" - "us.grpc.Status\022&\n\010entities\030\002 \003(\0132\024.milvu" - "s.grpc.HEntity\022\017\n\007row_num\030\003 \001(\003\022\r\n\005score" - "\030\004 \003(\002\022\020\n\010distance\030\005 \003(\002\"\260\001\n\014HInsertPara" - "m\022\027\n\017collection_name\030\001 \001(\t\022\025\n\rpartition_" - "tag\030\002 \001(\t\022&\n\010entities\030\003 \001(\0132\024.milvus.grp" - "c.HEntity\022\027\n\017entity_id_array\030\004 \003(\003\022/\n\014ex" - "tra_params\030\005 \003(\0132\031.milvus.grpc.KeyValueP" - "air\"6\n\017HEntityIdentity\022\027\n\017collection_nam" - "e\030\001 \001(\t\022\n\n\002id\030\002 \001(\003\"J\n\nHEntityIDs\022#\n\006sta" - "tus\030\001 \001(\0132\023.milvus.grpc.Status\022\027\n\017entity" - "_id_array\030\002 \003(\003\"C\n\022HGetEntityIDsParam\022\027\n" - "\017collection_name\030\001 \001(\t\022\024\n\014segment_name\030\002" - " \001(\t\"=\n\020HDeleteByIDParam\022\027\n\017collection_n" - "ame\030\001 \001(\t\022\020\n\010id_array\030\002 \003(\003\"\220\001\n\013HIndexPa" - "ram\022#\n\006status\030\001 \001(\0132\023.milvus.grpc.Status" - "\022\027\n\017collection_name\030\002 \001(\t\022\022\n\nindex_type\030" - "\003 \001(\005\022/\n\014extra_params\030\004 \003(\0132\031.milvus.grp" - "c.KeyValuePair*\206\001\n\010DataType\022\010\n\004NULL\020\000\022\010\n" - "\004INT8\020\001\022\t\n\005INT16\020\002\022\t\n\005INT32\020\003\022\t\n\005INT64\020\004" - "\022\n\n\006STRING\020\024\022\010\n\004BOOL\020\036\022\t\n\005FLOAT\020(\022\n\n\006DOU" - "BLE\020)\022\n\n\006VECTOR\020d\022\014\n\007UNKNOWN\020\217N*C\n\017Compa" - "reOperator\022\006\n\002LT\020\000\022\007\n\003LTE\020\001\022\006\n\002EQ\020\002\022\006\n\002G" - "T\020\003\022\007\n\003GTE\020\004\022\006\n\002NE\020\005*8\n\005Occur\022\013\n\007INVALID" - "\020\000\022\010\n\004MUST\020\001\022\n\n\006SHOULD\020\002\022\014\n\010MUST_NOT\020\0032\237" - "\027\n\rMilvusService\022H\n\020CreateCollection\022\035.m" - "ilvus.grpc.CollectionSchema\032\023.milvus.grp" - "c.Status\"\000\022F\n\rHasCollection\022\033.milvus.grp" - "c.CollectionName\032\026.milvus.grpc.BoolReply" - "\"\000\022R\n\022DescribeCollection\022\033.milvus.grpc.C" - "ollectionName\032\035.milvus.grpc.CollectionSc" - "hema\"\000\022Q\n\017CountCollection\022\033.milvus.grpc." - "CollectionName\032\037.milvus.grpc.CollectionR" - "owCount\"\000\022J\n\017ShowCollections\022\024.milvus.gr" - "pc.Command\032\037.milvus.grpc.CollectionNameL" - "ist\"\000\022P\n\022ShowCollectionInfo\022\033.milvus.grp" - "c.CollectionName\032\033.milvus.grpc.Collectio" - "nInfo\"\000\022D\n\016DropCollection\022\033.milvus.grpc." - "CollectionName\032\023.milvus.grpc.Status\"\000\022=\n" - "\013CreateIndex\022\027.milvus.grpc.IndexParam\032\023." - "milvus.grpc.Status\"\000\022G\n\rDescribeIndex\022\033." - "milvus.grpc.CollectionName\032\027.milvus.grpc" - ".IndexParam\"\000\022\?\n\tDropIndex\022\033.milvus.grpc" - ".CollectionName\032\023.milvus.grpc.Status\"\000\022E" - "\n\017CreatePartition\022\033.milvus.grpc.Partitio" - "nParam\032\023.milvus.grpc.Status\"\000\022E\n\014HasPart" - "ition\022\033.milvus.grpc.PartitionParam\032\026.mil" - "vus.grpc.BoolReply\"\000\022K\n\016ShowPartitions\022\033" - ".milvus.grpc.CollectionName\032\032.milvus.grp" - "c.PartitionList\"\000\022C\n\rDropPartition\022\033.mil" - "vus.grpc.PartitionParam\032\023.milvus.grpc.St" - "atus\"\000\022<\n\006Insert\022\030.milvus.grpc.InsertPar" - "am\032\026.milvus.grpc.VectorIds\"\000\022J\n\016GetVecto" - "rsByID\022\034.milvus.grpc.VectorsIdentity\032\030.m" - "ilvus.grpc.VectorsData\"\000\022H\n\014GetVectorIDs" - "\022\036.milvus.grpc.GetVectorIDsParam\032\026.milvu" - "s.grpc.VectorIds\"\000\022B\n\006Search\022\030.milvus.gr" - "pc.SearchParam\032\034.milvus.grpc.TopKQueryRe" - "sult\"\000\022J\n\nSearchByID\022\034.milvus.grpc.Searc" - "hByIDParam\032\034.milvus.grpc.TopKQueryResult" - "\"\000\022P\n\rSearchInFiles\022\037.milvus.grpc.Search" - "InFilesParam\032\034.milvus.grpc.TopKQueryResu" - "lt\"\000\0227\n\003Cmd\022\024.milvus.grpc.Command\032\030.milv" - "us.grpc.StringReply\"\000\022A\n\nDeleteByID\022\034.mi" - "lvus.grpc.DeleteByIDParam\032\023.milvus.grpc." - "Status\"\000\022G\n\021PreloadCollection\022\033.milvus.g" - "rpc.CollectionName\032\023.milvus.grpc.Status\"" - "\000\022I\n\016ReloadSegments\022 .milvus.grpc.ReLoad" - "SegmentsParam\032\023.milvus.grpc.Status\"\000\0227\n\005" - "Flush\022\027.milvus.grpc.FlushParam\032\023.milvus." - "grpc.Status\"\000\022=\n\007Compact\022\033.milvus.grpc.C" - "ollectionName\032\023.milvus.grpc.Status\"\000\022E\n\026" - "CreateHybridCollection\022\024.milvus.grpc.Map" - "ping\032\023.milvus.grpc.Status\"\000\022L\n\023HasHybrid" - "Collection\022\033.milvus.grpc.CollectionName\032" - "\026.milvus.grpc.BoolReply\"\000\022J\n\024DropHybridC" - "ollection\022\033.milvus.grpc.CollectionName\032\023" - ".milvus.grpc.Status\"\000\022O\n\030DescribeHybridC" - "ollection\022\033.milvus.grpc.CollectionName\032\024" - ".milvus.grpc.Mapping\"\000\022W\n\025CountHybridCol" - "lection\022\033.milvus.grpc.CollectionName\032\037.m" - "ilvus.grpc.CollectionRowCount\"\000\022I\n\025ShowH" - "ybridCollections\022\024.milvus.grpc.Command\032\030" - ".milvus.grpc.MappingList\"\000\022V\n\030ShowHybrid" - "CollectionInfo\022\033.milvus.grpc.CollectionN" - "ame\032\033.milvus.grpc.CollectionInfo\"\000\022M\n\027Pr" - "eloadHybridCollection\022\033.milvus.grpc.Coll" - "ectionName\032\023.milvus.grpc.Status\"\000\022D\n\014Ins" - "ertEntity\022\031.milvus.grpc.HInsertParam\032\027.m" - "ilvus.grpc.HEntityIDs\"\000\022I\n\014HybridSearch\022" - "\031.milvus.grpc.HSearchParam\032\034.milvus.grpc" - ".TopKQueryResult\"\000\022]\n\026HybridSearchInSegm" - "ents\022#.milvus.grpc.HSearchInSegmentsPara" - "m\032\034.milvus.grpc.TopKQueryResult\"\000\022E\n\rGet" - "EntityByID\022\034.milvus.grpc.HEntityIdentity" - "\032\024.milvus.grpc.HEntity\"\000\022J\n\014GetEntityIDs" - "\022\037.milvus.grpc.HGetEntityIDsParam\032\027.milv" - "us.grpc.HEntityIDs\"\000\022J\n\022DeleteEntitiesBy" - "ID\022\035.milvus.grpc.HDeleteByIDParam\032\023.milv" - "us.grpc.Status\"\000b\006proto3" - ; -static const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_milvus_2eproto_deps[1] = { - &::descriptor_table_status_2eproto, -}; -static ::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase*const descriptor_table_milvus_2eproto_sccs[48] = { - &scc_info_AttrRecord_milvus_2eproto.base, - &scc_info_BoolReply_milvus_2eproto.base, - &scc_info_BooleanQuery_milvus_2eproto.base, - &scc_info_CollectionInfo_milvus_2eproto.base, - &scc_info_CollectionName_milvus_2eproto.base, - &scc_info_CollectionNameList_milvus_2eproto.base, - &scc_info_CollectionRowCount_milvus_2eproto.base, - &scc_info_CollectionSchema_milvus_2eproto.base, - &scc_info_Command_milvus_2eproto.base, - &scc_info_CompareExpr_milvus_2eproto.base, - &scc_info_DeleteByIDParam_milvus_2eproto.base, - &scc_info_FieldParam_milvus_2eproto.base, - &scc_info_FieldType_milvus_2eproto.base, - &scc_info_FieldValue_milvus_2eproto.base, - &scc_info_FlushParam_milvus_2eproto.base, - &scc_info_GetVectorIDsParam_milvus_2eproto.base, - &scc_info_HDeleteByIDParam_milvus_2eproto.base, - &scc_info_HEntity_milvus_2eproto.base, - &scc_info_HEntityIDs_milvus_2eproto.base, - &scc_info_HEntityIdentity_milvus_2eproto.base, - &scc_info_HGetEntityIDsParam_milvus_2eproto.base, - &scc_info_HIndexParam_milvus_2eproto.base, - &scc_info_HInsertParam_milvus_2eproto.base, - &scc_info_HQueryResult_milvus_2eproto.base, - &scc_info_HSearchInSegmentsParam_milvus_2eproto.base, - &scc_info_HSearchParam_milvus_2eproto.base, - &scc_info_IndexParam_milvus_2eproto.base, - &scc_info_InsertParam_milvus_2eproto.base, - &scc_info_KeyValuePair_milvus_2eproto.base, - &scc_info_Mapping_milvus_2eproto.base, - &scc_info_MappingList_milvus_2eproto.base, - &scc_info_PartitionList_milvus_2eproto.base, - &scc_info_PartitionParam_milvus_2eproto.base, - &scc_info_RangeQuery_milvus_2eproto.base, - &scc_info_ReLoadSegmentsParam_milvus_2eproto.base, - &scc_info_RowRecord_milvus_2eproto.base, - &scc_info_SearchByIDParam_milvus_2eproto.base, - &scc_info_SearchInFilesParam_milvus_2eproto.base, - &scc_info_SearchParam_milvus_2eproto.base, - &scc_info_StringReply_milvus_2eproto.base, - &scc_info_TermQuery_milvus_2eproto.base, - &scc_info_TopKQueryResult_milvus_2eproto.base, - &scc_info_VectorFieldParam_milvus_2eproto.base, - &scc_info_VectorFieldValue_milvus_2eproto.base, - &scc_info_VectorIds_milvus_2eproto.base, - &scc_info_VectorQuery_milvus_2eproto.base, - &scc_info_VectorsData_milvus_2eproto.base, - &scc_info_VectorsIdentity_milvus_2eproto.base, -}; -static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_milvus_2eproto_once; -static bool descriptor_table_milvus_2eproto_initialized = false; -const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_milvus_2eproto = { - &descriptor_table_milvus_2eproto_initialized, descriptor_table_protodef_milvus_2eproto, "milvus.proto", 8384, - &descriptor_table_milvus_2eproto_once, descriptor_table_milvus_2eproto_sccs, descriptor_table_milvus_2eproto_deps, 48, 1, - schemas, file_default_instances, TableStruct_milvus_2eproto::offsets, - file_level_metadata_milvus_2eproto, 49, file_level_enum_descriptors_milvus_2eproto, file_level_service_descriptors_milvus_2eproto, -}; - -// Force running AddDescriptors() at dynamic initialization time. -static bool dynamic_init_dummy_milvus_2eproto = ( ::PROTOBUF_NAMESPACE_ID::internal::AddDescriptors(&descriptor_table_milvus_2eproto), true); -namespace milvus { -namespace grpc { -const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* DataType_descriptor() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_milvus_2eproto); - return file_level_enum_descriptors_milvus_2eproto[0]; -} -bool DataType_IsValid(int value) { - switch (value) { - case 0: - case 1: - case 2: - case 3: - case 4: - case 20: - case 30: - case 40: - case 41: - case 100: - case 9999: - return true; - default: - return false; - } -} - -const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* CompareOperator_descriptor() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_milvus_2eproto); - return file_level_enum_descriptors_milvus_2eproto[1]; -} -bool CompareOperator_IsValid(int value) { - switch (value) { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - return true; - default: - return false; - } -} - -const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* Occur_descriptor() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_milvus_2eproto); - return file_level_enum_descriptors_milvus_2eproto[2]; -} -bool Occur_IsValid(int value) { - switch (value) { - case 0: - case 1: - case 2: - case 3: - return true; - default: - return false; - } -} - - -// =================================================================== - -void KeyValuePair::InitAsDefaultInstance() { -} -class KeyValuePair::_Internal { - public: -}; - -KeyValuePair::KeyValuePair() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.KeyValuePair) -} -KeyValuePair::KeyValuePair(const KeyValuePair& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - key_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.key().empty()) { - key_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.key_); - } - value_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.value().empty()) { - value_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.value_); - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.KeyValuePair) -} - -void KeyValuePair::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_KeyValuePair_milvus_2eproto.base); - key_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - value_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -KeyValuePair::~KeyValuePair() { - // @@protoc_insertion_point(destructor:milvus.grpc.KeyValuePair) - SharedDtor(); -} - -void KeyValuePair::SharedDtor() { - key_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - value_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void KeyValuePair::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const KeyValuePair& KeyValuePair::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_KeyValuePair_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void KeyValuePair::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.KeyValuePair) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - key_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - value_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* KeyValuePair::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // string key = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_key(), ptr, ctx, "milvus.grpc.KeyValuePair.key"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // string value = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_value(), ptr, ctx, "milvus.grpc.KeyValuePair.value"); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool KeyValuePair::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.KeyValuePair) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // string key = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_key())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->key().data(), static_cast(this->key().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.KeyValuePair.key")); - } else { - goto handle_unusual; - } - break; - } - - // string value = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_value())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->value().data(), static_cast(this->value().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.KeyValuePair.value")); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.KeyValuePair) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.KeyValuePair) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void KeyValuePair::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.KeyValuePair) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string key = 1; - if (this->key().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->key().data(), static_cast(this->key().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.KeyValuePair.key"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 1, this->key(), output); - } - - // string value = 2; - if (this->value().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->value().data(), static_cast(this->value().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.KeyValuePair.value"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 2, this->value(), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.KeyValuePair) -} - -::PROTOBUF_NAMESPACE_ID::uint8* KeyValuePair::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.KeyValuePair) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string key = 1; - if (this->key().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->key().data(), static_cast(this->key().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.KeyValuePair.key"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 1, this->key(), target); - } - - // string value = 2; - if (this->value().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->value().data(), static_cast(this->value().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.KeyValuePair.value"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 2, this->value(), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.KeyValuePair) - return target; -} - -size_t KeyValuePair::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.KeyValuePair) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // string key = 1; - if (this->key().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->key()); - } - - // string value = 2; - if (this->value().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->value()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void KeyValuePair::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.KeyValuePair) - GOOGLE_DCHECK_NE(&from, this); - const KeyValuePair* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.KeyValuePair) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.KeyValuePair) - MergeFrom(*source); - } -} - -void KeyValuePair::MergeFrom(const KeyValuePair& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.KeyValuePair) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - if (from.key().size() > 0) { - - key_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.key_); - } - if (from.value().size() > 0) { - - value_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.value_); - } -} - -void KeyValuePair::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.KeyValuePair) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void KeyValuePair::CopyFrom(const KeyValuePair& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.KeyValuePair) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool KeyValuePair::IsInitialized() const { - return true; -} - -void KeyValuePair::InternalSwap(KeyValuePair* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - key_.Swap(&other->key_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - value_.Swap(&other->value_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); -} - -::PROTOBUF_NAMESPACE_ID::Metadata KeyValuePair::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void CollectionName::InitAsDefaultInstance() { -} -class CollectionName::_Internal { - public: -}; - -CollectionName::CollectionName() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.CollectionName) -} -CollectionName::CollectionName(const CollectionName& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.collection_name().empty()) { - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.CollectionName) -} - -void CollectionName::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_CollectionName_milvus_2eproto.base); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -CollectionName::~CollectionName() { - // @@protoc_insertion_point(destructor:milvus.grpc.CollectionName) - SharedDtor(); -} - -void CollectionName::SharedDtor() { - collection_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void CollectionName::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const CollectionName& CollectionName::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_CollectionName_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void CollectionName::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.CollectionName) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* CollectionName::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // string collection_name = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_collection_name(), ptr, ctx, "milvus.grpc.CollectionName.collection_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool CollectionName::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.CollectionName) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // string collection_name = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_collection_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.CollectionName.collection_name")); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.CollectionName) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.CollectionName) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void CollectionName::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.CollectionName) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.CollectionName.collection_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 1, this->collection_name(), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.CollectionName) -} - -::PROTOBUF_NAMESPACE_ID::uint8* CollectionName::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.CollectionName) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.CollectionName.collection_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 1, this->collection_name(), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.CollectionName) - return target; -} - -size_t CollectionName::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.CollectionName) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_name()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void CollectionName::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.CollectionName) - GOOGLE_DCHECK_NE(&from, this); - const CollectionName* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.CollectionName) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.CollectionName) - MergeFrom(*source); - } -} - -void CollectionName::MergeFrom(const CollectionName& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.CollectionName) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - if (from.collection_name().size() > 0) { - - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } -} - -void CollectionName::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.CollectionName) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void CollectionName::CopyFrom(const CollectionName& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.CollectionName) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool CollectionName::IsInitialized() const { - return true; -} - -void CollectionName::InternalSwap(CollectionName* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - collection_name_.Swap(&other->collection_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); -} - -::PROTOBUF_NAMESPACE_ID::Metadata CollectionName::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void CollectionNameList::InitAsDefaultInstance() { - ::milvus::grpc::_CollectionNameList_default_instance_._instance.get_mutable()->status_ = const_cast< ::milvus::grpc::Status*>( - ::milvus::grpc::Status::internal_default_instance()); -} -class CollectionNameList::_Internal { - public: - static const ::milvus::grpc::Status& status(const CollectionNameList* msg); -}; - -const ::milvus::grpc::Status& -CollectionNameList::_Internal::status(const CollectionNameList* msg) { - return *msg->status_; -} -void CollectionNameList::clear_status() { - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; -} -CollectionNameList::CollectionNameList() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.CollectionNameList) -} -CollectionNameList::CollectionNameList(const CollectionNameList& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - collection_names_(from.collection_names_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - if (from.has_status()) { - status_ = new ::milvus::grpc::Status(*from.status_); - } else { - status_ = nullptr; - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.CollectionNameList) -} - -void CollectionNameList::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_CollectionNameList_milvus_2eproto.base); - status_ = nullptr; -} - -CollectionNameList::~CollectionNameList() { - // @@protoc_insertion_point(destructor:milvus.grpc.CollectionNameList) - SharedDtor(); -} - -void CollectionNameList::SharedDtor() { - if (this != internal_default_instance()) delete status_; -} - -void CollectionNameList::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const CollectionNameList& CollectionNameList::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_CollectionNameList_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void CollectionNameList::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.CollectionNameList) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - collection_names_.Clear(); - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* CollectionNameList::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.Status status = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ctx->ParseMessage(mutable_status(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated string collection_names = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr -= 1; - do { - ptr += 1; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(add_collection_names(), ptr, ctx, "milvus.grpc.CollectionNameList.collection_names"); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 18); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool CollectionNameList::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.CollectionNameList) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.Status status = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_status())); - } else { - goto handle_unusual; - } - break; - } - - // repeated string collection_names = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->add_collection_names())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_names(this->collection_names_size() - 1).data(), - static_cast(this->collection_names(this->collection_names_size() - 1).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.CollectionNameList.collection_names")); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.CollectionNameList) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.CollectionNameList) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void CollectionNameList::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.CollectionNameList) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 1, _Internal::status(this), output); - } - - // repeated string collection_names = 2; - for (int i = 0, n = this->collection_names_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_names(i).data(), static_cast(this->collection_names(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.CollectionNameList.collection_names"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteString( - 2, this->collection_names(i), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.CollectionNameList) -} - -::PROTOBUF_NAMESPACE_ID::uint8* CollectionNameList::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.CollectionNameList) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 1, _Internal::status(this), target); - } - - // repeated string collection_names = 2; - for (int i = 0, n = this->collection_names_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_names(i).data(), static_cast(this->collection_names(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.CollectionNameList.collection_names"); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteStringToArray(2, this->collection_names(i), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.CollectionNameList) - return target; -} - -size_t CollectionNameList::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.CollectionNameList) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated string collection_names = 2; - total_size += 1 * - ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(this->collection_names_size()); - for (int i = 0, n = this->collection_names_size(); i < n; i++) { - total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_names(i)); - } - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *status_); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void CollectionNameList::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.CollectionNameList) - GOOGLE_DCHECK_NE(&from, this); - const CollectionNameList* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.CollectionNameList) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.CollectionNameList) - MergeFrom(*source); - } -} - -void CollectionNameList::MergeFrom(const CollectionNameList& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.CollectionNameList) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - collection_names_.MergeFrom(from.collection_names_); - if (from.has_status()) { - mutable_status()->::milvus::grpc::Status::MergeFrom(from.status()); - } -} - -void CollectionNameList::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.CollectionNameList) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void CollectionNameList::CopyFrom(const CollectionNameList& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.CollectionNameList) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool CollectionNameList::IsInitialized() const { - return true; -} - -void CollectionNameList::InternalSwap(CollectionNameList* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - collection_names_.InternalSwap(CastToBase(&other->collection_names_)); - swap(status_, other->status_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata CollectionNameList::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void CollectionSchema::InitAsDefaultInstance() { - ::milvus::grpc::_CollectionSchema_default_instance_._instance.get_mutable()->status_ = const_cast< ::milvus::grpc::Status*>( - ::milvus::grpc::Status::internal_default_instance()); -} -class CollectionSchema::_Internal { - public: - static const ::milvus::grpc::Status& status(const CollectionSchema* msg); -}; - -const ::milvus::grpc::Status& -CollectionSchema::_Internal::status(const CollectionSchema* msg) { - return *msg->status_; -} -void CollectionSchema::clear_status() { - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; -} -CollectionSchema::CollectionSchema() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.CollectionSchema) -} -CollectionSchema::CollectionSchema(const CollectionSchema& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - extra_params_(from.extra_params_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.collection_name().empty()) { - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - if (from.has_status()) { - status_ = new ::milvus::grpc::Status(*from.status_); - } else { - status_ = nullptr; - } - ::memcpy(&dimension_, &from.dimension_, - static_cast(reinterpret_cast(&metric_type_) - - reinterpret_cast(&dimension_)) + sizeof(metric_type_)); - // @@protoc_insertion_point(copy_constructor:milvus.grpc.CollectionSchema) -} - -void CollectionSchema::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_CollectionSchema_milvus_2eproto.base); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - ::memset(&status_, 0, static_cast( - reinterpret_cast(&metric_type_) - - reinterpret_cast(&status_)) + sizeof(metric_type_)); -} - -CollectionSchema::~CollectionSchema() { - // @@protoc_insertion_point(destructor:milvus.grpc.CollectionSchema) - SharedDtor(); -} - -void CollectionSchema::SharedDtor() { - collection_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (this != internal_default_instance()) delete status_; -} - -void CollectionSchema::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const CollectionSchema& CollectionSchema::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_CollectionSchema_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void CollectionSchema::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.CollectionSchema) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - extra_params_.Clear(); - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; - ::memset(&dimension_, 0, static_cast( - reinterpret_cast(&metric_type_) - - reinterpret_cast(&dimension_)) + sizeof(metric_type_)); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* CollectionSchema::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.Status status = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ctx->ParseMessage(mutable_status(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // string collection_name = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_collection_name(), ptr, ctx, "milvus.grpc.CollectionSchema.collection_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // int64 dimension = 3; - case 3: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 24)) { - dimension_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // int64 index_file_size = 4; - case 4: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 32)) { - index_file_size_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // int32 metric_type = 5; - case 5: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 40)) { - metric_type_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.KeyValuePair extra_params = 6; - case 6: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 50)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_extra_params(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 50); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool CollectionSchema::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.CollectionSchema) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.Status status = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_status())); - } else { - goto handle_unusual; - } - break; - } - - // string collection_name = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_collection_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.CollectionSchema.collection_name")); - } else { - goto handle_unusual; - } - break; - } - - // int64 dimension = 3; - case 3: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (24 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, &dimension_))); - } else { - goto handle_unusual; - } - break; - } - - // int64 index_file_size = 4; - case 4: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (32 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, &index_file_size_))); - } else { - goto handle_unusual; - } - break; - } - - // int32 metric_type = 5; - case 5: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (40 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::int32, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT32>( - input, &metric_type_))); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 6; - case 6: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (50 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_extra_params())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.CollectionSchema) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.CollectionSchema) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void CollectionSchema::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.CollectionSchema) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 1, _Internal::status(this), output); - } - - // string collection_name = 2; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.CollectionSchema.collection_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 2, this->collection_name(), output); - } - - // int64 dimension = 3; - if (this->dimension() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64(3, this->dimension(), output); - } - - // int64 index_file_size = 4; - if (this->index_file_size() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64(4, this->index_file_size(), output); - } - - // int32 metric_type = 5; - if (this->metric_type() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32(5, this->metric_type(), output); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 6; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 6, - this->extra_params(static_cast(i)), - output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.CollectionSchema) -} - -::PROTOBUF_NAMESPACE_ID::uint8* CollectionSchema::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.CollectionSchema) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 1, _Internal::status(this), target); - } - - // string collection_name = 2; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.CollectionSchema.collection_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 2, this->collection_name(), target); - } - - // int64 dimension = 3; - if (this->dimension() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64ToArray(3, this->dimension(), target); - } - - // int64 index_file_size = 4; - if (this->index_file_size() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64ToArray(4, this->index_file_size(), target); - } - - // int32 metric_type = 5; - if (this->metric_type() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(5, this->metric_type(), target); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 6; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 6, this->extra_params(static_cast(i)), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.CollectionSchema) - return target; -} - -size_t CollectionSchema::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.CollectionSchema) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated .milvus.grpc.KeyValuePair extra_params = 6; - { - unsigned int count = static_cast(this->extra_params_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->extra_params(static_cast(i))); - } - } - - // string collection_name = 2; - if (this->collection_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_name()); - } - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *status_); - } - - // int64 dimension = 3; - if (this->dimension() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int64Size( - this->dimension()); - } - - // int64 index_file_size = 4; - if (this->index_file_size() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int64Size( - this->index_file_size()); - } - - // int32 metric_type = 5; - if (this->metric_type() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( - this->metric_type()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void CollectionSchema::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.CollectionSchema) - GOOGLE_DCHECK_NE(&from, this); - const CollectionSchema* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.CollectionSchema) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.CollectionSchema) - MergeFrom(*source); - } -} - -void CollectionSchema::MergeFrom(const CollectionSchema& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.CollectionSchema) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - extra_params_.MergeFrom(from.extra_params_); - if (from.collection_name().size() > 0) { - - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - if (from.has_status()) { - mutable_status()->::milvus::grpc::Status::MergeFrom(from.status()); - } - if (from.dimension() != 0) { - set_dimension(from.dimension()); - } - if (from.index_file_size() != 0) { - set_index_file_size(from.index_file_size()); - } - if (from.metric_type() != 0) { - set_metric_type(from.metric_type()); - } -} - -void CollectionSchema::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.CollectionSchema) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void CollectionSchema::CopyFrom(const CollectionSchema& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.CollectionSchema) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool CollectionSchema::IsInitialized() const { - return true; -} - -void CollectionSchema::InternalSwap(CollectionSchema* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - CastToBase(&extra_params_)->InternalSwap(CastToBase(&other->extra_params_)); - collection_name_.Swap(&other->collection_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - swap(status_, other->status_); - swap(dimension_, other->dimension_); - swap(index_file_size_, other->index_file_size_); - swap(metric_type_, other->metric_type_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata CollectionSchema::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void PartitionParam::InitAsDefaultInstance() { -} -class PartitionParam::_Internal { - public: -}; - -PartitionParam::PartitionParam() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.PartitionParam) -} -PartitionParam::PartitionParam(const PartitionParam& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.collection_name().empty()) { - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - tag_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.tag().empty()) { - tag_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.tag_); - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.PartitionParam) -} - -void PartitionParam::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_PartitionParam_milvus_2eproto.base); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - tag_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -PartitionParam::~PartitionParam() { - // @@protoc_insertion_point(destructor:milvus.grpc.PartitionParam) - SharedDtor(); -} - -void PartitionParam::SharedDtor() { - collection_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - tag_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void PartitionParam::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const PartitionParam& PartitionParam::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_PartitionParam_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void PartitionParam::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.PartitionParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - tag_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* PartitionParam::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // string collection_name = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_collection_name(), ptr, ctx, "milvus.grpc.PartitionParam.collection_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // string tag = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_tag(), ptr, ctx, "milvus.grpc.PartitionParam.tag"); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool PartitionParam::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.PartitionParam) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // string collection_name = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_collection_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.PartitionParam.collection_name")); - } else { - goto handle_unusual; - } - break; - } - - // string tag = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_tag())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->tag().data(), static_cast(this->tag().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.PartitionParam.tag")); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.PartitionParam) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.PartitionParam) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void PartitionParam::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.PartitionParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.PartitionParam.collection_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 1, this->collection_name(), output); - } - - // string tag = 2; - if (this->tag().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->tag().data(), static_cast(this->tag().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.PartitionParam.tag"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 2, this->tag(), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.PartitionParam) -} - -::PROTOBUF_NAMESPACE_ID::uint8* PartitionParam::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.PartitionParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.PartitionParam.collection_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 1, this->collection_name(), target); - } - - // string tag = 2; - if (this->tag().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->tag().data(), static_cast(this->tag().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.PartitionParam.tag"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 2, this->tag(), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.PartitionParam) - return target; -} - -size_t PartitionParam::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.PartitionParam) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_name()); - } - - // string tag = 2; - if (this->tag().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->tag()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void PartitionParam::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.PartitionParam) - GOOGLE_DCHECK_NE(&from, this); - const PartitionParam* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.PartitionParam) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.PartitionParam) - MergeFrom(*source); - } -} - -void PartitionParam::MergeFrom(const PartitionParam& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.PartitionParam) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - if (from.collection_name().size() > 0) { - - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - if (from.tag().size() > 0) { - - tag_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.tag_); - } -} - -void PartitionParam::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.PartitionParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void PartitionParam::CopyFrom(const PartitionParam& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.PartitionParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool PartitionParam::IsInitialized() const { - return true; -} - -void PartitionParam::InternalSwap(PartitionParam* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - collection_name_.Swap(&other->collection_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - tag_.Swap(&other->tag_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); -} - -::PROTOBUF_NAMESPACE_ID::Metadata PartitionParam::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void PartitionList::InitAsDefaultInstance() { - ::milvus::grpc::_PartitionList_default_instance_._instance.get_mutable()->status_ = const_cast< ::milvus::grpc::Status*>( - ::milvus::grpc::Status::internal_default_instance()); -} -class PartitionList::_Internal { - public: - static const ::milvus::grpc::Status& status(const PartitionList* msg); -}; - -const ::milvus::grpc::Status& -PartitionList::_Internal::status(const PartitionList* msg) { - return *msg->status_; -} -void PartitionList::clear_status() { - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; -} -PartitionList::PartitionList() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.PartitionList) -} -PartitionList::PartitionList(const PartitionList& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - partition_tag_array_(from.partition_tag_array_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - if (from.has_status()) { - status_ = new ::milvus::grpc::Status(*from.status_); - } else { - status_ = nullptr; - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.PartitionList) -} - -void PartitionList::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_PartitionList_milvus_2eproto.base); - status_ = nullptr; -} - -PartitionList::~PartitionList() { - // @@protoc_insertion_point(destructor:milvus.grpc.PartitionList) - SharedDtor(); -} - -void PartitionList::SharedDtor() { - if (this != internal_default_instance()) delete status_; -} - -void PartitionList::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const PartitionList& PartitionList::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_PartitionList_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void PartitionList::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.PartitionList) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - partition_tag_array_.Clear(); - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* PartitionList::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.Status status = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ctx->ParseMessage(mutable_status(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated string partition_tag_array = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr -= 1; - do { - ptr += 1; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(add_partition_tag_array(), ptr, ctx, "milvus.grpc.PartitionList.partition_tag_array"); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 18); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool PartitionList::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.PartitionList) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.Status status = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_status())); - } else { - goto handle_unusual; - } - break; - } - - // repeated string partition_tag_array = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->add_partition_tag_array())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->partition_tag_array(this->partition_tag_array_size() - 1).data(), - static_cast(this->partition_tag_array(this->partition_tag_array_size() - 1).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.PartitionList.partition_tag_array")); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.PartitionList) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.PartitionList) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void PartitionList::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.PartitionList) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 1, _Internal::status(this), output); - } - - // repeated string partition_tag_array = 2; - for (int i = 0, n = this->partition_tag_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->partition_tag_array(i).data(), static_cast(this->partition_tag_array(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.PartitionList.partition_tag_array"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteString( - 2, this->partition_tag_array(i), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.PartitionList) -} - -::PROTOBUF_NAMESPACE_ID::uint8* PartitionList::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.PartitionList) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 1, _Internal::status(this), target); - } - - // repeated string partition_tag_array = 2; - for (int i = 0, n = this->partition_tag_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->partition_tag_array(i).data(), static_cast(this->partition_tag_array(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.PartitionList.partition_tag_array"); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteStringToArray(2, this->partition_tag_array(i), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.PartitionList) - return target; -} - -size_t PartitionList::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.PartitionList) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated string partition_tag_array = 2; - total_size += 1 * - ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(this->partition_tag_array_size()); - for (int i = 0, n = this->partition_tag_array_size(); i < n; i++) { - total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->partition_tag_array(i)); - } - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *status_); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void PartitionList::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.PartitionList) - GOOGLE_DCHECK_NE(&from, this); - const PartitionList* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.PartitionList) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.PartitionList) - MergeFrom(*source); - } -} - -void PartitionList::MergeFrom(const PartitionList& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.PartitionList) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - partition_tag_array_.MergeFrom(from.partition_tag_array_); - if (from.has_status()) { - mutable_status()->::milvus::grpc::Status::MergeFrom(from.status()); - } -} - -void PartitionList::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.PartitionList) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void PartitionList::CopyFrom(const PartitionList& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.PartitionList) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool PartitionList::IsInitialized() const { - return true; -} - -void PartitionList::InternalSwap(PartitionList* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - partition_tag_array_.InternalSwap(CastToBase(&other->partition_tag_array_)); - swap(status_, other->status_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata PartitionList::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void RowRecord::InitAsDefaultInstance() { -} -class RowRecord::_Internal { - public: -}; - -RowRecord::RowRecord() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.RowRecord) -} -RowRecord::RowRecord(const RowRecord& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - float_data_(from.float_data_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - binary_data_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.binary_data().empty()) { - binary_data_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.binary_data_); - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.RowRecord) -} - -void RowRecord::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_RowRecord_milvus_2eproto.base); - binary_data_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -RowRecord::~RowRecord() { - // @@protoc_insertion_point(destructor:milvus.grpc.RowRecord) - SharedDtor(); -} - -void RowRecord::SharedDtor() { - binary_data_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void RowRecord::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const RowRecord& RowRecord::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_RowRecord_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void RowRecord::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.RowRecord) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - float_data_.Clear(); - binary_data_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* RowRecord::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // repeated float float_data = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedFloatParser(mutable_float_data(), ptr, ctx); - CHK_(ptr); - } else if (static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 13) { - add_float_data(::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr)); - ptr += sizeof(float); - } else goto handle_unusual; - continue; - // bytes binary_data = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(mutable_binary_data(), ptr, ctx); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool RowRecord::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.RowRecord) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // repeated float float_data = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPackedPrimitive< - float, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_FLOAT>( - input, this->mutable_float_data()))); - } else if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (13 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadRepeatedPrimitiveNoInline< - float, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_FLOAT>( - 1, 10u, input, this->mutable_float_data()))); - } else { - goto handle_unusual; - } - break; - } - - // bytes binary_data = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadBytes( - input, this->mutable_binary_data())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.RowRecord) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.RowRecord) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void RowRecord::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.RowRecord) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // repeated float float_data = 1; - if (this->float_data_size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTag(1, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, output); - output->WriteVarint32(_float_data_cached_byte_size_.load( - std::memory_order_relaxed)); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteFloatArray( - this->float_data().data(), this->float_data_size(), output); - } - - // bytes binary_data = 2; - if (this->binary_data().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBytesMaybeAliased( - 2, this->binary_data(), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.RowRecord) -} - -::PROTOBUF_NAMESPACE_ID::uint8* RowRecord::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.RowRecord) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // repeated float float_data = 1; - if (this->float_data_size() > 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTagToArray( - 1, - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, - target); - target = ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream::WriteVarint32ToArray( - _float_data_cached_byte_size_.load(std::memory_order_relaxed), - target); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteFloatNoTagToArray(this->float_data_, target); - } - - // bytes binary_data = 2; - if (this->binary_data().size() > 0) { - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBytesToArray( - 2, this->binary_data(), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.RowRecord) - return target; -} - -size_t RowRecord::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.RowRecord) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated float float_data = 1; - { - unsigned int count = static_cast(this->float_data_size()); - size_t data_size = 4UL * count; - if (data_size > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( - static_cast<::PROTOBUF_NAMESPACE_ID::int32>(data_size)); - } - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); - _float_data_cached_byte_size_.store(cached_size, - std::memory_order_relaxed); - total_size += data_size; - } - - // bytes binary_data = 2; - if (this->binary_data().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize( - this->binary_data()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void RowRecord::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.RowRecord) - GOOGLE_DCHECK_NE(&from, this); - const RowRecord* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.RowRecord) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.RowRecord) - MergeFrom(*source); - } -} - -void RowRecord::MergeFrom(const RowRecord& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.RowRecord) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - float_data_.MergeFrom(from.float_data_); - if (from.binary_data().size() > 0) { - - binary_data_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.binary_data_); - } -} - -void RowRecord::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.RowRecord) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void RowRecord::CopyFrom(const RowRecord& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.RowRecord) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool RowRecord::IsInitialized() const { - return true; -} - -void RowRecord::InternalSwap(RowRecord* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - float_data_.InternalSwap(&other->float_data_); - binary_data_.Swap(&other->binary_data_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); -} - -::PROTOBUF_NAMESPACE_ID::Metadata RowRecord::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void InsertParam::InitAsDefaultInstance() { -} -class InsertParam::_Internal { - public: -}; - -InsertParam::InsertParam() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.InsertParam) -} -InsertParam::InsertParam(const InsertParam& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - row_record_array_(from.row_record_array_), - row_id_array_(from.row_id_array_), - extra_params_(from.extra_params_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.collection_name().empty()) { - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - partition_tag_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.partition_tag().empty()) { - partition_tag_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.partition_tag_); - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.InsertParam) -} - -void InsertParam::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_InsertParam_milvus_2eproto.base); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - partition_tag_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -InsertParam::~InsertParam() { - // @@protoc_insertion_point(destructor:milvus.grpc.InsertParam) - SharedDtor(); -} - -void InsertParam::SharedDtor() { - collection_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - partition_tag_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void InsertParam::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const InsertParam& InsertParam::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_InsertParam_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void InsertParam::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.InsertParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - row_record_array_.Clear(); - row_id_array_.Clear(); - extra_params_.Clear(); - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - partition_tag_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* InsertParam::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // string collection_name = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_collection_name(), ptr, ctx, "milvus.grpc.InsertParam.collection_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.RowRecord row_record_array = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_row_record_array(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 18); - } else goto handle_unusual; - continue; - // repeated int64 row_id_array = 3; - case 3: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt64Parser(mutable_row_id_array(), ptr, ctx); - CHK_(ptr); - } else if (static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 24) { - add_row_id_array(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr)); - CHK_(ptr); - } else goto handle_unusual; - continue; - // string partition_tag = 4; - case 4: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 34)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_partition_tag(), ptr, ctx, "milvus.grpc.InsertParam.partition_tag"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - case 5: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 42)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_extra_params(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 42); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool InsertParam::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.InsertParam) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // string collection_name = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_collection_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.InsertParam.collection_name")); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.RowRecord row_record_array = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_row_record_array())); - } else { - goto handle_unusual; - } - break; - } - - // repeated int64 row_id_array = 3; - case 3: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (26 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPackedPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, this->mutable_row_id_array()))); - } else if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (24 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadRepeatedPrimitiveNoInline< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - 1, 26u, input, this->mutable_row_id_array()))); - } else { - goto handle_unusual; - } - break; - } - - // string partition_tag = 4; - case 4: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (34 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_partition_tag())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->partition_tag().data(), static_cast(this->partition_tag().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.InsertParam.partition_tag")); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - case 5: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (42 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_extra_params())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.InsertParam) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.InsertParam) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void InsertParam::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.InsertParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.InsertParam.collection_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 1, this->collection_name(), output); - } - - // repeated .milvus.grpc.RowRecord row_record_array = 2; - for (unsigned int i = 0, - n = static_cast(this->row_record_array_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 2, - this->row_record_array(static_cast(i)), - output); - } - - // repeated int64 row_id_array = 3; - if (this->row_id_array_size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTag(3, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, output); - output->WriteVarint32(_row_id_array_cached_byte_size_.load( - std::memory_order_relaxed)); - } - for (int i = 0, n = this->row_id_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64NoTag( - this->row_id_array(i), output); - } - - // string partition_tag = 4; - if (this->partition_tag().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->partition_tag().data(), static_cast(this->partition_tag().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.InsertParam.partition_tag"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 4, this->partition_tag(), output); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 5, - this->extra_params(static_cast(i)), - output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.InsertParam) -} - -::PROTOBUF_NAMESPACE_ID::uint8* InsertParam::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.InsertParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.InsertParam.collection_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 1, this->collection_name(), target); - } - - // repeated .milvus.grpc.RowRecord row_record_array = 2; - for (unsigned int i = 0, - n = static_cast(this->row_record_array_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 2, this->row_record_array(static_cast(i)), target); - } - - // repeated int64 row_id_array = 3; - if (this->row_id_array_size() > 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTagToArray( - 3, - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, - target); - target = ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream::WriteVarint32ToArray( - _row_id_array_cached_byte_size_.load(std::memory_order_relaxed), - target); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteInt64NoTagToArray(this->row_id_array_, target); - } - - // string partition_tag = 4; - if (this->partition_tag().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->partition_tag().data(), static_cast(this->partition_tag().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.InsertParam.partition_tag"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 4, this->partition_tag(), target); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 5, this->extra_params(static_cast(i)), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.InsertParam) - return target; -} - -size_t InsertParam::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.InsertParam) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated .milvus.grpc.RowRecord row_record_array = 2; - { - unsigned int count = static_cast(this->row_record_array_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->row_record_array(static_cast(i))); - } - } - - // repeated int64 row_id_array = 3; - { - size_t data_size = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - Int64Size(this->row_id_array_); - if (data_size > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( - static_cast<::PROTOBUF_NAMESPACE_ID::int32>(data_size)); - } - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); - _row_id_array_cached_byte_size_.store(cached_size, - std::memory_order_relaxed); - total_size += data_size; - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - { - unsigned int count = static_cast(this->extra_params_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->extra_params(static_cast(i))); - } - } - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_name()); - } - - // string partition_tag = 4; - if (this->partition_tag().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->partition_tag()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void InsertParam::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.InsertParam) - GOOGLE_DCHECK_NE(&from, this); - const InsertParam* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.InsertParam) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.InsertParam) - MergeFrom(*source); - } -} - -void InsertParam::MergeFrom(const InsertParam& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.InsertParam) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - row_record_array_.MergeFrom(from.row_record_array_); - row_id_array_.MergeFrom(from.row_id_array_); - extra_params_.MergeFrom(from.extra_params_); - if (from.collection_name().size() > 0) { - - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - if (from.partition_tag().size() > 0) { - - partition_tag_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.partition_tag_); - } -} - -void InsertParam::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.InsertParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void InsertParam::CopyFrom(const InsertParam& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.InsertParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool InsertParam::IsInitialized() const { - return true; -} - -void InsertParam::InternalSwap(InsertParam* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - CastToBase(&row_record_array_)->InternalSwap(CastToBase(&other->row_record_array_)); - row_id_array_.InternalSwap(&other->row_id_array_); - CastToBase(&extra_params_)->InternalSwap(CastToBase(&other->extra_params_)); - collection_name_.Swap(&other->collection_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - partition_tag_.Swap(&other->partition_tag_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); -} - -::PROTOBUF_NAMESPACE_ID::Metadata InsertParam::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void VectorIds::InitAsDefaultInstance() { - ::milvus::grpc::_VectorIds_default_instance_._instance.get_mutable()->status_ = const_cast< ::milvus::grpc::Status*>( - ::milvus::grpc::Status::internal_default_instance()); -} -class VectorIds::_Internal { - public: - static const ::milvus::grpc::Status& status(const VectorIds* msg); -}; - -const ::milvus::grpc::Status& -VectorIds::_Internal::status(const VectorIds* msg) { - return *msg->status_; -} -void VectorIds::clear_status() { - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; -} -VectorIds::VectorIds() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.VectorIds) -} -VectorIds::VectorIds(const VectorIds& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - vector_id_array_(from.vector_id_array_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - if (from.has_status()) { - status_ = new ::milvus::grpc::Status(*from.status_); - } else { - status_ = nullptr; - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.VectorIds) -} - -void VectorIds::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_VectorIds_milvus_2eproto.base); - status_ = nullptr; -} - -VectorIds::~VectorIds() { - // @@protoc_insertion_point(destructor:milvus.grpc.VectorIds) - SharedDtor(); -} - -void VectorIds::SharedDtor() { - if (this != internal_default_instance()) delete status_; -} - -void VectorIds::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const VectorIds& VectorIds::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_VectorIds_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void VectorIds::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.VectorIds) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - vector_id_array_.Clear(); - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* VectorIds::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.Status status = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ctx->ParseMessage(mutable_status(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated int64 vector_id_array = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt64Parser(mutable_vector_id_array(), ptr, ctx); - CHK_(ptr); - } else if (static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16) { - add_vector_id_array(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr)); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool VectorIds::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.VectorIds) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.Status status = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_status())); - } else { - goto handle_unusual; - } - break; - } - - // repeated int64 vector_id_array = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPackedPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, this->mutable_vector_id_array()))); - } else if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (16 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadRepeatedPrimitiveNoInline< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - 1, 18u, input, this->mutable_vector_id_array()))); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.VectorIds) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.VectorIds) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void VectorIds::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.VectorIds) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 1, _Internal::status(this), output); - } - - // repeated int64 vector_id_array = 2; - if (this->vector_id_array_size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTag(2, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, output); - output->WriteVarint32(_vector_id_array_cached_byte_size_.load( - std::memory_order_relaxed)); - } - for (int i = 0, n = this->vector_id_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64NoTag( - this->vector_id_array(i), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.VectorIds) -} - -::PROTOBUF_NAMESPACE_ID::uint8* VectorIds::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.VectorIds) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 1, _Internal::status(this), target); - } - - // repeated int64 vector_id_array = 2; - if (this->vector_id_array_size() > 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTagToArray( - 2, - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, - target); - target = ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream::WriteVarint32ToArray( - _vector_id_array_cached_byte_size_.load(std::memory_order_relaxed), - target); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteInt64NoTagToArray(this->vector_id_array_, target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.VectorIds) - return target; -} - -size_t VectorIds::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.VectorIds) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated int64 vector_id_array = 2; - { - size_t data_size = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - Int64Size(this->vector_id_array_); - if (data_size > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( - static_cast<::PROTOBUF_NAMESPACE_ID::int32>(data_size)); - } - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); - _vector_id_array_cached_byte_size_.store(cached_size, - std::memory_order_relaxed); - total_size += data_size; - } - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *status_); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void VectorIds::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.VectorIds) - GOOGLE_DCHECK_NE(&from, this); - const VectorIds* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.VectorIds) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.VectorIds) - MergeFrom(*source); - } -} - -void VectorIds::MergeFrom(const VectorIds& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.VectorIds) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - vector_id_array_.MergeFrom(from.vector_id_array_); - if (from.has_status()) { - mutable_status()->::milvus::grpc::Status::MergeFrom(from.status()); - } -} - -void VectorIds::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.VectorIds) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void VectorIds::CopyFrom(const VectorIds& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.VectorIds) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool VectorIds::IsInitialized() const { - return true; -} - -void VectorIds::InternalSwap(VectorIds* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - vector_id_array_.InternalSwap(&other->vector_id_array_); - swap(status_, other->status_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata VectorIds::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void SearchParam::InitAsDefaultInstance() { -} -class SearchParam::_Internal { - public: -}; - -SearchParam::SearchParam() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.SearchParam) -} -SearchParam::SearchParam(const SearchParam& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - partition_tag_array_(from.partition_tag_array_), - query_record_array_(from.query_record_array_), - extra_params_(from.extra_params_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.collection_name().empty()) { - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - topk_ = from.topk_; - // @@protoc_insertion_point(copy_constructor:milvus.grpc.SearchParam) -} - -void SearchParam::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_SearchParam_milvus_2eproto.base); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - topk_ = PROTOBUF_LONGLONG(0); -} - -SearchParam::~SearchParam() { - // @@protoc_insertion_point(destructor:milvus.grpc.SearchParam) - SharedDtor(); -} - -void SearchParam::SharedDtor() { - collection_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void SearchParam::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const SearchParam& SearchParam::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_SearchParam_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void SearchParam::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.SearchParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - partition_tag_array_.Clear(); - query_record_array_.Clear(); - extra_params_.Clear(); - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - topk_ = PROTOBUF_LONGLONG(0); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* SearchParam::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // string collection_name = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_collection_name(), ptr, ctx, "milvus.grpc.SearchParam.collection_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated string partition_tag_array = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr -= 1; - do { - ptr += 1; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(add_partition_tag_array(), ptr, ctx, "milvus.grpc.SearchParam.partition_tag_array"); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 18); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.RowRecord query_record_array = 3; - case 3: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_query_record_array(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 26); - } else goto handle_unusual; - continue; - // int64 topk = 4; - case 4: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 32)) { - topk_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - case 5: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 42)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_extra_params(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 42); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool SearchParam::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.SearchParam) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // string collection_name = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_collection_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.SearchParam.collection_name")); - } else { - goto handle_unusual; - } - break; - } - - // repeated string partition_tag_array = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->add_partition_tag_array())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->partition_tag_array(this->partition_tag_array_size() - 1).data(), - static_cast(this->partition_tag_array(this->partition_tag_array_size() - 1).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.SearchParam.partition_tag_array")); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.RowRecord query_record_array = 3; - case 3: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (26 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_query_record_array())); - } else { - goto handle_unusual; - } - break; - } - - // int64 topk = 4; - case 4: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (32 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, &topk_))); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - case 5: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (42 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_extra_params())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.SearchParam) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.SearchParam) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void SearchParam::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.SearchParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.SearchParam.collection_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 1, this->collection_name(), output); - } - - // repeated string partition_tag_array = 2; - for (int i = 0, n = this->partition_tag_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->partition_tag_array(i).data(), static_cast(this->partition_tag_array(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.SearchParam.partition_tag_array"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteString( - 2, this->partition_tag_array(i), output); - } - - // repeated .milvus.grpc.RowRecord query_record_array = 3; - for (unsigned int i = 0, - n = static_cast(this->query_record_array_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 3, - this->query_record_array(static_cast(i)), - output); - } - - // int64 topk = 4; - if (this->topk() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64(4, this->topk(), output); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 5, - this->extra_params(static_cast(i)), - output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.SearchParam) -} - -::PROTOBUF_NAMESPACE_ID::uint8* SearchParam::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.SearchParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.SearchParam.collection_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 1, this->collection_name(), target); - } - - // repeated string partition_tag_array = 2; - for (int i = 0, n = this->partition_tag_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->partition_tag_array(i).data(), static_cast(this->partition_tag_array(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.SearchParam.partition_tag_array"); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteStringToArray(2, this->partition_tag_array(i), target); - } - - // repeated .milvus.grpc.RowRecord query_record_array = 3; - for (unsigned int i = 0, - n = static_cast(this->query_record_array_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 3, this->query_record_array(static_cast(i)), target); - } - - // int64 topk = 4; - if (this->topk() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64ToArray(4, this->topk(), target); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 5, this->extra_params(static_cast(i)), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.SearchParam) - return target; -} - -size_t SearchParam::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.SearchParam) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated string partition_tag_array = 2; - total_size += 1 * - ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(this->partition_tag_array_size()); - for (int i = 0, n = this->partition_tag_array_size(); i < n; i++) { - total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->partition_tag_array(i)); - } - - // repeated .milvus.grpc.RowRecord query_record_array = 3; - { - unsigned int count = static_cast(this->query_record_array_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->query_record_array(static_cast(i))); - } - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - { - unsigned int count = static_cast(this->extra_params_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->extra_params(static_cast(i))); - } - } - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_name()); - } - - // int64 topk = 4; - if (this->topk() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int64Size( - this->topk()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void SearchParam::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.SearchParam) - GOOGLE_DCHECK_NE(&from, this); - const SearchParam* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.SearchParam) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.SearchParam) - MergeFrom(*source); - } -} - -void SearchParam::MergeFrom(const SearchParam& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.SearchParam) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - partition_tag_array_.MergeFrom(from.partition_tag_array_); - query_record_array_.MergeFrom(from.query_record_array_); - extra_params_.MergeFrom(from.extra_params_); - if (from.collection_name().size() > 0) { - - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - if (from.topk() != 0) { - set_topk(from.topk()); - } -} - -void SearchParam::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.SearchParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void SearchParam::CopyFrom(const SearchParam& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.SearchParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool SearchParam::IsInitialized() const { - return true; -} - -void SearchParam::InternalSwap(SearchParam* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - partition_tag_array_.InternalSwap(CastToBase(&other->partition_tag_array_)); - CastToBase(&query_record_array_)->InternalSwap(CastToBase(&other->query_record_array_)); - CastToBase(&extra_params_)->InternalSwap(CastToBase(&other->extra_params_)); - collection_name_.Swap(&other->collection_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - swap(topk_, other->topk_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata SearchParam::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void SearchInFilesParam::InitAsDefaultInstance() { - ::milvus::grpc::_SearchInFilesParam_default_instance_._instance.get_mutable()->search_param_ = const_cast< ::milvus::grpc::SearchParam*>( - ::milvus::grpc::SearchParam::internal_default_instance()); -} -class SearchInFilesParam::_Internal { - public: - static const ::milvus::grpc::SearchParam& search_param(const SearchInFilesParam* msg); -}; - -const ::milvus::grpc::SearchParam& -SearchInFilesParam::_Internal::search_param(const SearchInFilesParam* msg) { - return *msg->search_param_; -} -SearchInFilesParam::SearchInFilesParam() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.SearchInFilesParam) -} -SearchInFilesParam::SearchInFilesParam(const SearchInFilesParam& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - file_id_array_(from.file_id_array_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - if (from.has_search_param()) { - search_param_ = new ::milvus::grpc::SearchParam(*from.search_param_); - } else { - search_param_ = nullptr; - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.SearchInFilesParam) -} - -void SearchInFilesParam::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_SearchInFilesParam_milvus_2eproto.base); - search_param_ = nullptr; -} - -SearchInFilesParam::~SearchInFilesParam() { - // @@protoc_insertion_point(destructor:milvus.grpc.SearchInFilesParam) - SharedDtor(); -} - -void SearchInFilesParam::SharedDtor() { - if (this != internal_default_instance()) delete search_param_; -} - -void SearchInFilesParam::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const SearchInFilesParam& SearchInFilesParam::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_SearchInFilesParam_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void SearchInFilesParam::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.SearchInFilesParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - file_id_array_.Clear(); - if (GetArenaNoVirtual() == nullptr && search_param_ != nullptr) { - delete search_param_; - } - search_param_ = nullptr; - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* SearchInFilesParam::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // repeated string file_id_array = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr -= 1; - do { - ptr += 1; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(add_file_id_array(), ptr, ctx, "milvus.grpc.SearchInFilesParam.file_id_array"); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 10); - } else goto handle_unusual; - continue; - // .milvus.grpc.SearchParam search_param = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ctx->ParseMessage(mutable_search_param(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool SearchInFilesParam::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.SearchInFilesParam) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // repeated string file_id_array = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->add_file_id_array())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->file_id_array(this->file_id_array_size() - 1).data(), - static_cast(this->file_id_array(this->file_id_array_size() - 1).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.SearchInFilesParam.file_id_array")); - } else { - goto handle_unusual; - } - break; - } - - // .milvus.grpc.SearchParam search_param = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_search_param())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.SearchInFilesParam) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.SearchInFilesParam) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void SearchInFilesParam::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.SearchInFilesParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // repeated string file_id_array = 1; - for (int i = 0, n = this->file_id_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->file_id_array(i).data(), static_cast(this->file_id_array(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.SearchInFilesParam.file_id_array"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteString( - 1, this->file_id_array(i), output); - } - - // .milvus.grpc.SearchParam search_param = 2; - if (this->has_search_param()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 2, _Internal::search_param(this), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.SearchInFilesParam) -} - -::PROTOBUF_NAMESPACE_ID::uint8* SearchInFilesParam::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.SearchInFilesParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // repeated string file_id_array = 1; - for (int i = 0, n = this->file_id_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->file_id_array(i).data(), static_cast(this->file_id_array(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.SearchInFilesParam.file_id_array"); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteStringToArray(1, this->file_id_array(i), target); - } - - // .milvus.grpc.SearchParam search_param = 2; - if (this->has_search_param()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 2, _Internal::search_param(this), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.SearchInFilesParam) - return target; -} - -size_t SearchInFilesParam::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.SearchInFilesParam) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated string file_id_array = 1; - total_size += 1 * - ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(this->file_id_array_size()); - for (int i = 0, n = this->file_id_array_size(); i < n; i++) { - total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->file_id_array(i)); - } - - // .milvus.grpc.SearchParam search_param = 2; - if (this->has_search_param()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *search_param_); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void SearchInFilesParam::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.SearchInFilesParam) - GOOGLE_DCHECK_NE(&from, this); - const SearchInFilesParam* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.SearchInFilesParam) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.SearchInFilesParam) - MergeFrom(*source); - } -} - -void SearchInFilesParam::MergeFrom(const SearchInFilesParam& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.SearchInFilesParam) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - file_id_array_.MergeFrom(from.file_id_array_); - if (from.has_search_param()) { - mutable_search_param()->::milvus::grpc::SearchParam::MergeFrom(from.search_param()); - } -} - -void SearchInFilesParam::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.SearchInFilesParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void SearchInFilesParam::CopyFrom(const SearchInFilesParam& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.SearchInFilesParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool SearchInFilesParam::IsInitialized() const { - return true; -} - -void SearchInFilesParam::InternalSwap(SearchInFilesParam* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - file_id_array_.InternalSwap(CastToBase(&other->file_id_array_)); - swap(search_param_, other->search_param_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata SearchInFilesParam::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void SearchByIDParam::InitAsDefaultInstance() { -} -class SearchByIDParam::_Internal { - public: -}; - -SearchByIDParam::SearchByIDParam() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.SearchByIDParam) -} -SearchByIDParam::SearchByIDParam(const SearchByIDParam& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - partition_tag_array_(from.partition_tag_array_), - id_array_(from.id_array_), - extra_params_(from.extra_params_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.collection_name().empty()) { - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - topk_ = from.topk_; - // @@protoc_insertion_point(copy_constructor:milvus.grpc.SearchByIDParam) -} - -void SearchByIDParam::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_SearchByIDParam_milvus_2eproto.base); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - topk_ = PROTOBUF_LONGLONG(0); -} - -SearchByIDParam::~SearchByIDParam() { - // @@protoc_insertion_point(destructor:milvus.grpc.SearchByIDParam) - SharedDtor(); -} - -void SearchByIDParam::SharedDtor() { - collection_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void SearchByIDParam::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const SearchByIDParam& SearchByIDParam::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_SearchByIDParam_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void SearchByIDParam::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.SearchByIDParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - partition_tag_array_.Clear(); - id_array_.Clear(); - extra_params_.Clear(); - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - topk_ = PROTOBUF_LONGLONG(0); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* SearchByIDParam::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // string collection_name = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_collection_name(), ptr, ctx, "milvus.grpc.SearchByIDParam.collection_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated string partition_tag_array = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr -= 1; - do { - ptr += 1; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(add_partition_tag_array(), ptr, ctx, "milvus.grpc.SearchByIDParam.partition_tag_array"); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 18); - } else goto handle_unusual; - continue; - // repeated int64 id_array = 3; - case 3: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt64Parser(mutable_id_array(), ptr, ctx); - CHK_(ptr); - } else if (static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 24) { - add_id_array(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr)); - CHK_(ptr); - } else goto handle_unusual; - continue; - // int64 topk = 4; - case 4: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 32)) { - topk_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - case 5: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 42)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_extra_params(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 42); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool SearchByIDParam::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.SearchByIDParam) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // string collection_name = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_collection_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.SearchByIDParam.collection_name")); - } else { - goto handle_unusual; - } - break; - } - - // repeated string partition_tag_array = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->add_partition_tag_array())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->partition_tag_array(this->partition_tag_array_size() - 1).data(), - static_cast(this->partition_tag_array(this->partition_tag_array_size() - 1).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.SearchByIDParam.partition_tag_array")); - } else { - goto handle_unusual; - } - break; - } - - // repeated int64 id_array = 3; - case 3: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (26 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPackedPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, this->mutable_id_array()))); - } else if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (24 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadRepeatedPrimitiveNoInline< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - 1, 26u, input, this->mutable_id_array()))); - } else { - goto handle_unusual; - } - break; - } - - // int64 topk = 4; - case 4: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (32 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, &topk_))); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - case 5: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (42 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_extra_params())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.SearchByIDParam) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.SearchByIDParam) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void SearchByIDParam::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.SearchByIDParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.SearchByIDParam.collection_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 1, this->collection_name(), output); - } - - // repeated string partition_tag_array = 2; - for (int i = 0, n = this->partition_tag_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->partition_tag_array(i).data(), static_cast(this->partition_tag_array(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.SearchByIDParam.partition_tag_array"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteString( - 2, this->partition_tag_array(i), output); - } - - // repeated int64 id_array = 3; - if (this->id_array_size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTag(3, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, output); - output->WriteVarint32(_id_array_cached_byte_size_.load( - std::memory_order_relaxed)); - } - for (int i = 0, n = this->id_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64NoTag( - this->id_array(i), output); - } - - // int64 topk = 4; - if (this->topk() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64(4, this->topk(), output); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 5, - this->extra_params(static_cast(i)), - output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.SearchByIDParam) -} - -::PROTOBUF_NAMESPACE_ID::uint8* SearchByIDParam::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.SearchByIDParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.SearchByIDParam.collection_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 1, this->collection_name(), target); - } - - // repeated string partition_tag_array = 2; - for (int i = 0, n = this->partition_tag_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->partition_tag_array(i).data(), static_cast(this->partition_tag_array(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.SearchByIDParam.partition_tag_array"); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteStringToArray(2, this->partition_tag_array(i), target); - } - - // repeated int64 id_array = 3; - if (this->id_array_size() > 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTagToArray( - 3, - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, - target); - target = ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream::WriteVarint32ToArray( - _id_array_cached_byte_size_.load(std::memory_order_relaxed), - target); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteInt64NoTagToArray(this->id_array_, target); - } - - // int64 topk = 4; - if (this->topk() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64ToArray(4, this->topk(), target); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 5, this->extra_params(static_cast(i)), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.SearchByIDParam) - return target; -} - -size_t SearchByIDParam::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.SearchByIDParam) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated string partition_tag_array = 2; - total_size += 1 * - ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(this->partition_tag_array_size()); - for (int i = 0, n = this->partition_tag_array_size(); i < n; i++) { - total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->partition_tag_array(i)); - } - - // repeated int64 id_array = 3; - { - size_t data_size = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - Int64Size(this->id_array_); - if (data_size > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( - static_cast<::PROTOBUF_NAMESPACE_ID::int32>(data_size)); - } - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); - _id_array_cached_byte_size_.store(cached_size, - std::memory_order_relaxed); - total_size += data_size; - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - { - unsigned int count = static_cast(this->extra_params_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->extra_params(static_cast(i))); - } - } - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_name()); - } - - // int64 topk = 4; - if (this->topk() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int64Size( - this->topk()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void SearchByIDParam::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.SearchByIDParam) - GOOGLE_DCHECK_NE(&from, this); - const SearchByIDParam* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.SearchByIDParam) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.SearchByIDParam) - MergeFrom(*source); - } -} - -void SearchByIDParam::MergeFrom(const SearchByIDParam& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.SearchByIDParam) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - partition_tag_array_.MergeFrom(from.partition_tag_array_); - id_array_.MergeFrom(from.id_array_); - extra_params_.MergeFrom(from.extra_params_); - if (from.collection_name().size() > 0) { - - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - if (from.topk() != 0) { - set_topk(from.topk()); - } -} - -void SearchByIDParam::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.SearchByIDParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void SearchByIDParam::CopyFrom(const SearchByIDParam& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.SearchByIDParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool SearchByIDParam::IsInitialized() const { - return true; -} - -void SearchByIDParam::InternalSwap(SearchByIDParam* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - partition_tag_array_.InternalSwap(CastToBase(&other->partition_tag_array_)); - id_array_.InternalSwap(&other->id_array_); - CastToBase(&extra_params_)->InternalSwap(CastToBase(&other->extra_params_)); - collection_name_.Swap(&other->collection_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - swap(topk_, other->topk_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata SearchByIDParam::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void ReLoadSegmentsParam::InitAsDefaultInstance() { -} -class ReLoadSegmentsParam::_Internal { - public: -}; - -ReLoadSegmentsParam::ReLoadSegmentsParam() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.ReLoadSegmentsParam) -} -ReLoadSegmentsParam::ReLoadSegmentsParam(const ReLoadSegmentsParam& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - segment_id_array_(from.segment_id_array_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.collection_name().empty()) { - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.ReLoadSegmentsParam) -} - -void ReLoadSegmentsParam::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_ReLoadSegmentsParam_milvus_2eproto.base); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -ReLoadSegmentsParam::~ReLoadSegmentsParam() { - // @@protoc_insertion_point(destructor:milvus.grpc.ReLoadSegmentsParam) - SharedDtor(); -} - -void ReLoadSegmentsParam::SharedDtor() { - collection_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void ReLoadSegmentsParam::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const ReLoadSegmentsParam& ReLoadSegmentsParam::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_ReLoadSegmentsParam_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void ReLoadSegmentsParam::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.ReLoadSegmentsParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - segment_id_array_.Clear(); - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* ReLoadSegmentsParam::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // string collection_name = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_collection_name(), ptr, ctx, "milvus.grpc.ReLoadSegmentsParam.collection_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated string segment_id_array = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr -= 1; - do { - ptr += 1; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(add_segment_id_array(), ptr, ctx, "milvus.grpc.ReLoadSegmentsParam.segment_id_array"); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 18); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool ReLoadSegmentsParam::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.ReLoadSegmentsParam) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // string collection_name = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_collection_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.ReLoadSegmentsParam.collection_name")); - } else { - goto handle_unusual; - } - break; - } - - // repeated string segment_id_array = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->add_segment_id_array())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->segment_id_array(this->segment_id_array_size() - 1).data(), - static_cast(this->segment_id_array(this->segment_id_array_size() - 1).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.ReLoadSegmentsParam.segment_id_array")); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.ReLoadSegmentsParam) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.ReLoadSegmentsParam) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void ReLoadSegmentsParam::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.ReLoadSegmentsParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.ReLoadSegmentsParam.collection_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 1, this->collection_name(), output); - } - - // repeated string segment_id_array = 2; - for (int i = 0, n = this->segment_id_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->segment_id_array(i).data(), static_cast(this->segment_id_array(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.ReLoadSegmentsParam.segment_id_array"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteString( - 2, this->segment_id_array(i), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.ReLoadSegmentsParam) -} - -::PROTOBUF_NAMESPACE_ID::uint8* ReLoadSegmentsParam::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.ReLoadSegmentsParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.ReLoadSegmentsParam.collection_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 1, this->collection_name(), target); - } - - // repeated string segment_id_array = 2; - for (int i = 0, n = this->segment_id_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->segment_id_array(i).data(), static_cast(this->segment_id_array(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.ReLoadSegmentsParam.segment_id_array"); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteStringToArray(2, this->segment_id_array(i), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.ReLoadSegmentsParam) - return target; -} - -size_t ReLoadSegmentsParam::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.ReLoadSegmentsParam) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated string segment_id_array = 2; - total_size += 1 * - ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(this->segment_id_array_size()); - for (int i = 0, n = this->segment_id_array_size(); i < n; i++) { - total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->segment_id_array(i)); - } - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_name()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void ReLoadSegmentsParam::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.ReLoadSegmentsParam) - GOOGLE_DCHECK_NE(&from, this); - const ReLoadSegmentsParam* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.ReLoadSegmentsParam) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.ReLoadSegmentsParam) - MergeFrom(*source); - } -} - -void ReLoadSegmentsParam::MergeFrom(const ReLoadSegmentsParam& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.ReLoadSegmentsParam) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - segment_id_array_.MergeFrom(from.segment_id_array_); - if (from.collection_name().size() > 0) { - - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } -} - -void ReLoadSegmentsParam::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.ReLoadSegmentsParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void ReLoadSegmentsParam::CopyFrom(const ReLoadSegmentsParam& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.ReLoadSegmentsParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool ReLoadSegmentsParam::IsInitialized() const { - return true; -} - -void ReLoadSegmentsParam::InternalSwap(ReLoadSegmentsParam* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - segment_id_array_.InternalSwap(CastToBase(&other->segment_id_array_)); - collection_name_.Swap(&other->collection_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); -} - -::PROTOBUF_NAMESPACE_ID::Metadata ReLoadSegmentsParam::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void TopKQueryResult::InitAsDefaultInstance() { - ::milvus::grpc::_TopKQueryResult_default_instance_._instance.get_mutable()->status_ = const_cast< ::milvus::grpc::Status*>( - ::milvus::grpc::Status::internal_default_instance()); -} -class TopKQueryResult::_Internal { - public: - static const ::milvus::grpc::Status& status(const TopKQueryResult* msg); -}; - -const ::milvus::grpc::Status& -TopKQueryResult::_Internal::status(const TopKQueryResult* msg) { - return *msg->status_; -} -void TopKQueryResult::clear_status() { - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; -} -TopKQueryResult::TopKQueryResult() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.TopKQueryResult) -} -TopKQueryResult::TopKQueryResult(const TopKQueryResult& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - ids_(from.ids_), - distances_(from.distances_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - if (from.has_status()) { - status_ = new ::milvus::grpc::Status(*from.status_); - } else { - status_ = nullptr; - } - row_num_ = from.row_num_; - // @@protoc_insertion_point(copy_constructor:milvus.grpc.TopKQueryResult) -} - -void TopKQueryResult::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_TopKQueryResult_milvus_2eproto.base); - ::memset(&status_, 0, static_cast( - reinterpret_cast(&row_num_) - - reinterpret_cast(&status_)) + sizeof(row_num_)); -} - -TopKQueryResult::~TopKQueryResult() { - // @@protoc_insertion_point(destructor:milvus.grpc.TopKQueryResult) - SharedDtor(); -} - -void TopKQueryResult::SharedDtor() { - if (this != internal_default_instance()) delete status_; -} - -void TopKQueryResult::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const TopKQueryResult& TopKQueryResult::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_TopKQueryResult_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void TopKQueryResult::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.TopKQueryResult) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - ids_.Clear(); - distances_.Clear(); - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; - row_num_ = PROTOBUF_LONGLONG(0); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* TopKQueryResult::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.Status status = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ctx->ParseMessage(mutable_status(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // int64 row_num = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) { - row_num_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated int64 ids = 3; - case 3: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt64Parser(mutable_ids(), ptr, ctx); - CHK_(ptr); - } else if (static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 24) { - add_ids(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr)); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated float distances = 4; - case 4: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 34)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedFloatParser(mutable_distances(), ptr, ctx); - CHK_(ptr); - } else if (static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 37) { - add_distances(::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr)); - ptr += sizeof(float); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool TopKQueryResult::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.TopKQueryResult) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.Status status = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_status())); - } else { - goto handle_unusual; - } - break; - } - - // int64 row_num = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (16 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, &row_num_))); - } else { - goto handle_unusual; - } - break; - } - - // repeated int64 ids = 3; - case 3: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (26 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPackedPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, this->mutable_ids()))); - } else if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (24 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadRepeatedPrimitiveNoInline< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - 1, 26u, input, this->mutable_ids()))); - } else { - goto handle_unusual; - } - break; - } - - // repeated float distances = 4; - case 4: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (34 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPackedPrimitive< - float, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_FLOAT>( - input, this->mutable_distances()))); - } else if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (37 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadRepeatedPrimitiveNoInline< - float, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_FLOAT>( - 1, 34u, input, this->mutable_distances()))); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.TopKQueryResult) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.TopKQueryResult) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void TopKQueryResult::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.TopKQueryResult) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 1, _Internal::status(this), output); - } - - // int64 row_num = 2; - if (this->row_num() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64(2, this->row_num(), output); - } - - // repeated int64 ids = 3; - if (this->ids_size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTag(3, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, output); - output->WriteVarint32(_ids_cached_byte_size_.load( - std::memory_order_relaxed)); - } - for (int i = 0, n = this->ids_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64NoTag( - this->ids(i), output); - } - - // repeated float distances = 4; - if (this->distances_size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTag(4, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, output); - output->WriteVarint32(_distances_cached_byte_size_.load( - std::memory_order_relaxed)); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteFloatArray( - this->distances().data(), this->distances_size(), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.TopKQueryResult) -} - -::PROTOBUF_NAMESPACE_ID::uint8* TopKQueryResult::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.TopKQueryResult) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 1, _Internal::status(this), target); - } - - // int64 row_num = 2; - if (this->row_num() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64ToArray(2, this->row_num(), target); - } - - // repeated int64 ids = 3; - if (this->ids_size() > 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTagToArray( - 3, - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, - target); - target = ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream::WriteVarint32ToArray( - _ids_cached_byte_size_.load(std::memory_order_relaxed), - target); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteInt64NoTagToArray(this->ids_, target); - } - - // repeated float distances = 4; - if (this->distances_size() > 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTagToArray( - 4, - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, - target); - target = ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream::WriteVarint32ToArray( - _distances_cached_byte_size_.load(std::memory_order_relaxed), - target); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteFloatNoTagToArray(this->distances_, target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.TopKQueryResult) - return target; -} - -size_t TopKQueryResult::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.TopKQueryResult) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated int64 ids = 3; - { - size_t data_size = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - Int64Size(this->ids_); - if (data_size > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( - static_cast<::PROTOBUF_NAMESPACE_ID::int32>(data_size)); - } - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); - _ids_cached_byte_size_.store(cached_size, - std::memory_order_relaxed); - total_size += data_size; - } - - // repeated float distances = 4; - { - unsigned int count = static_cast(this->distances_size()); - size_t data_size = 4UL * count; - if (data_size > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( - static_cast<::PROTOBUF_NAMESPACE_ID::int32>(data_size)); - } - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); - _distances_cached_byte_size_.store(cached_size, - std::memory_order_relaxed); - total_size += data_size; - } - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *status_); - } - - // int64 row_num = 2; - if (this->row_num() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int64Size( - this->row_num()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void TopKQueryResult::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.TopKQueryResult) - GOOGLE_DCHECK_NE(&from, this); - const TopKQueryResult* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.TopKQueryResult) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.TopKQueryResult) - MergeFrom(*source); - } -} - -void TopKQueryResult::MergeFrom(const TopKQueryResult& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.TopKQueryResult) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - ids_.MergeFrom(from.ids_); - distances_.MergeFrom(from.distances_); - if (from.has_status()) { - mutable_status()->::milvus::grpc::Status::MergeFrom(from.status()); - } - if (from.row_num() != 0) { - set_row_num(from.row_num()); - } -} - -void TopKQueryResult::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.TopKQueryResult) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void TopKQueryResult::CopyFrom(const TopKQueryResult& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.TopKQueryResult) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool TopKQueryResult::IsInitialized() const { - return true; -} - -void TopKQueryResult::InternalSwap(TopKQueryResult* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - ids_.InternalSwap(&other->ids_); - distances_.InternalSwap(&other->distances_); - swap(status_, other->status_); - swap(row_num_, other->row_num_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata TopKQueryResult::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void StringReply::InitAsDefaultInstance() { - ::milvus::grpc::_StringReply_default_instance_._instance.get_mutable()->status_ = const_cast< ::milvus::grpc::Status*>( - ::milvus::grpc::Status::internal_default_instance()); -} -class StringReply::_Internal { - public: - static const ::milvus::grpc::Status& status(const StringReply* msg); -}; - -const ::milvus::grpc::Status& -StringReply::_Internal::status(const StringReply* msg) { - return *msg->status_; -} -void StringReply::clear_status() { - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; -} -StringReply::StringReply() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.StringReply) -} -StringReply::StringReply(const StringReply& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - string_reply_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.string_reply().empty()) { - string_reply_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.string_reply_); - } - if (from.has_status()) { - status_ = new ::milvus::grpc::Status(*from.status_); - } else { - status_ = nullptr; - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.StringReply) -} - -void StringReply::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_StringReply_milvus_2eproto.base); - string_reply_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - status_ = nullptr; -} - -StringReply::~StringReply() { - // @@protoc_insertion_point(destructor:milvus.grpc.StringReply) - SharedDtor(); -} - -void StringReply::SharedDtor() { - string_reply_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (this != internal_default_instance()) delete status_; -} - -void StringReply::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const StringReply& StringReply::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_StringReply_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void StringReply::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.StringReply) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - string_reply_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* StringReply::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.Status status = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ctx->ParseMessage(mutable_status(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // string string_reply = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_string_reply(), ptr, ctx, "milvus.grpc.StringReply.string_reply"); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool StringReply::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.StringReply) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.Status status = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_status())); - } else { - goto handle_unusual; - } - break; - } - - // string string_reply = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_string_reply())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->string_reply().data(), static_cast(this->string_reply().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.StringReply.string_reply")); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.StringReply) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.StringReply) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void StringReply::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.StringReply) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 1, _Internal::status(this), output); - } - - // string string_reply = 2; - if (this->string_reply().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->string_reply().data(), static_cast(this->string_reply().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.StringReply.string_reply"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 2, this->string_reply(), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.StringReply) -} - -::PROTOBUF_NAMESPACE_ID::uint8* StringReply::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.StringReply) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 1, _Internal::status(this), target); - } - - // string string_reply = 2; - if (this->string_reply().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->string_reply().data(), static_cast(this->string_reply().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.StringReply.string_reply"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 2, this->string_reply(), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.StringReply) - return target; -} - -size_t StringReply::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.StringReply) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // string string_reply = 2; - if (this->string_reply().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->string_reply()); - } - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *status_); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void StringReply::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.StringReply) - GOOGLE_DCHECK_NE(&from, this); - const StringReply* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.StringReply) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.StringReply) - MergeFrom(*source); - } -} - -void StringReply::MergeFrom(const StringReply& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.StringReply) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - if (from.string_reply().size() > 0) { - - string_reply_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.string_reply_); - } - if (from.has_status()) { - mutable_status()->::milvus::grpc::Status::MergeFrom(from.status()); - } -} - -void StringReply::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.StringReply) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void StringReply::CopyFrom(const StringReply& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.StringReply) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool StringReply::IsInitialized() const { - return true; -} - -void StringReply::InternalSwap(StringReply* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - string_reply_.Swap(&other->string_reply_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - swap(status_, other->status_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata StringReply::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void BoolReply::InitAsDefaultInstance() { - ::milvus::grpc::_BoolReply_default_instance_._instance.get_mutable()->status_ = const_cast< ::milvus::grpc::Status*>( - ::milvus::grpc::Status::internal_default_instance()); -} -class BoolReply::_Internal { - public: - static const ::milvus::grpc::Status& status(const BoolReply* msg); -}; - -const ::milvus::grpc::Status& -BoolReply::_Internal::status(const BoolReply* msg) { - return *msg->status_; -} -void BoolReply::clear_status() { - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; -} -BoolReply::BoolReply() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.BoolReply) -} -BoolReply::BoolReply(const BoolReply& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - if (from.has_status()) { - status_ = new ::milvus::grpc::Status(*from.status_); - } else { - status_ = nullptr; - } - bool_reply_ = from.bool_reply_; - // @@protoc_insertion_point(copy_constructor:milvus.grpc.BoolReply) -} - -void BoolReply::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_BoolReply_milvus_2eproto.base); - ::memset(&status_, 0, static_cast( - reinterpret_cast(&bool_reply_) - - reinterpret_cast(&status_)) + sizeof(bool_reply_)); -} - -BoolReply::~BoolReply() { - // @@protoc_insertion_point(destructor:milvus.grpc.BoolReply) - SharedDtor(); -} - -void BoolReply::SharedDtor() { - if (this != internal_default_instance()) delete status_; -} - -void BoolReply::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const BoolReply& BoolReply::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_BoolReply_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void BoolReply::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.BoolReply) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; - bool_reply_ = false; - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* BoolReply::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.Status status = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ctx->ParseMessage(mutable_status(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // bool bool_reply = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) { - bool_reply_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool BoolReply::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.BoolReply) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.Status status = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_status())); - } else { - goto handle_unusual; - } - break; - } - - // bool bool_reply = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (16 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - bool, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_BOOL>( - input, &bool_reply_))); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.BoolReply) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.BoolReply) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void BoolReply::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.BoolReply) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 1, _Internal::status(this), output); - } - - // bool bool_reply = 2; - if (this->bool_reply() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBool(2, this->bool_reply(), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.BoolReply) -} - -::PROTOBUF_NAMESPACE_ID::uint8* BoolReply::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.BoolReply) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 1, _Internal::status(this), target); - } - - // bool bool_reply = 2; - if (this->bool_reply() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(2, this->bool_reply(), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.BoolReply) - return target; -} - -size_t BoolReply::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.BoolReply) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *status_); - } - - // bool bool_reply = 2; - if (this->bool_reply() != 0) { - total_size += 1 + 1; - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void BoolReply::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.BoolReply) - GOOGLE_DCHECK_NE(&from, this); - const BoolReply* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.BoolReply) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.BoolReply) - MergeFrom(*source); - } -} - -void BoolReply::MergeFrom(const BoolReply& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.BoolReply) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - if (from.has_status()) { - mutable_status()->::milvus::grpc::Status::MergeFrom(from.status()); - } - if (from.bool_reply() != 0) { - set_bool_reply(from.bool_reply()); - } -} - -void BoolReply::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.BoolReply) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void BoolReply::CopyFrom(const BoolReply& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.BoolReply) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool BoolReply::IsInitialized() const { - return true; -} - -void BoolReply::InternalSwap(BoolReply* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - swap(status_, other->status_); - swap(bool_reply_, other->bool_reply_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata BoolReply::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void CollectionRowCount::InitAsDefaultInstance() { - ::milvus::grpc::_CollectionRowCount_default_instance_._instance.get_mutable()->status_ = const_cast< ::milvus::grpc::Status*>( - ::milvus::grpc::Status::internal_default_instance()); -} -class CollectionRowCount::_Internal { - public: - static const ::milvus::grpc::Status& status(const CollectionRowCount* msg); -}; - -const ::milvus::grpc::Status& -CollectionRowCount::_Internal::status(const CollectionRowCount* msg) { - return *msg->status_; -} -void CollectionRowCount::clear_status() { - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; -} -CollectionRowCount::CollectionRowCount() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.CollectionRowCount) -} -CollectionRowCount::CollectionRowCount(const CollectionRowCount& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - if (from.has_status()) { - status_ = new ::milvus::grpc::Status(*from.status_); - } else { - status_ = nullptr; - } - collection_row_count_ = from.collection_row_count_; - // @@protoc_insertion_point(copy_constructor:milvus.grpc.CollectionRowCount) -} - -void CollectionRowCount::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_CollectionRowCount_milvus_2eproto.base); - ::memset(&status_, 0, static_cast( - reinterpret_cast(&collection_row_count_) - - reinterpret_cast(&status_)) + sizeof(collection_row_count_)); -} - -CollectionRowCount::~CollectionRowCount() { - // @@protoc_insertion_point(destructor:milvus.grpc.CollectionRowCount) - SharedDtor(); -} - -void CollectionRowCount::SharedDtor() { - if (this != internal_default_instance()) delete status_; -} - -void CollectionRowCount::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const CollectionRowCount& CollectionRowCount::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_CollectionRowCount_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void CollectionRowCount::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.CollectionRowCount) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; - collection_row_count_ = PROTOBUF_LONGLONG(0); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* CollectionRowCount::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.Status status = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ctx->ParseMessage(mutable_status(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // int64 collection_row_count = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) { - collection_row_count_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool CollectionRowCount::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.CollectionRowCount) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.Status status = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_status())); - } else { - goto handle_unusual; - } - break; - } - - // int64 collection_row_count = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (16 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, &collection_row_count_))); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.CollectionRowCount) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.CollectionRowCount) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void CollectionRowCount::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.CollectionRowCount) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 1, _Internal::status(this), output); - } - - // int64 collection_row_count = 2; - if (this->collection_row_count() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64(2, this->collection_row_count(), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.CollectionRowCount) -} - -::PROTOBUF_NAMESPACE_ID::uint8* CollectionRowCount::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.CollectionRowCount) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 1, _Internal::status(this), target); - } - - // int64 collection_row_count = 2; - if (this->collection_row_count() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64ToArray(2, this->collection_row_count(), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.CollectionRowCount) - return target; -} - -size_t CollectionRowCount::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.CollectionRowCount) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *status_); - } - - // int64 collection_row_count = 2; - if (this->collection_row_count() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int64Size( - this->collection_row_count()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void CollectionRowCount::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.CollectionRowCount) - GOOGLE_DCHECK_NE(&from, this); - const CollectionRowCount* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.CollectionRowCount) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.CollectionRowCount) - MergeFrom(*source); - } -} - -void CollectionRowCount::MergeFrom(const CollectionRowCount& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.CollectionRowCount) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - if (from.has_status()) { - mutable_status()->::milvus::grpc::Status::MergeFrom(from.status()); - } - if (from.collection_row_count() != 0) { - set_collection_row_count(from.collection_row_count()); - } -} - -void CollectionRowCount::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.CollectionRowCount) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void CollectionRowCount::CopyFrom(const CollectionRowCount& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.CollectionRowCount) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool CollectionRowCount::IsInitialized() const { - return true; -} - -void CollectionRowCount::InternalSwap(CollectionRowCount* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - swap(status_, other->status_); - swap(collection_row_count_, other->collection_row_count_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata CollectionRowCount::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void Command::InitAsDefaultInstance() { -} -class Command::_Internal { - public: -}; - -Command::Command() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.Command) -} -Command::Command(const Command& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - cmd_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.cmd().empty()) { - cmd_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.cmd_); - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.Command) -} - -void Command::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_Command_milvus_2eproto.base); - cmd_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -Command::~Command() { - // @@protoc_insertion_point(destructor:milvus.grpc.Command) - SharedDtor(); -} - -void Command::SharedDtor() { - cmd_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void Command::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const Command& Command::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_Command_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void Command::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.Command) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - cmd_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* Command::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // string cmd = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_cmd(), ptr, ctx, "milvus.grpc.Command.cmd"); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool Command::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.Command) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // string cmd = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_cmd())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->cmd().data(), static_cast(this->cmd().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.Command.cmd")); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.Command) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.Command) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void Command::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.Command) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string cmd = 1; - if (this->cmd().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->cmd().data(), static_cast(this->cmd().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.Command.cmd"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 1, this->cmd(), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.Command) -} - -::PROTOBUF_NAMESPACE_ID::uint8* Command::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.Command) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string cmd = 1; - if (this->cmd().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->cmd().data(), static_cast(this->cmd().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.Command.cmd"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 1, this->cmd(), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.Command) - return target; -} - -size_t Command::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.Command) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // string cmd = 1; - if (this->cmd().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->cmd()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void Command::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.Command) - GOOGLE_DCHECK_NE(&from, this); - const Command* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.Command) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.Command) - MergeFrom(*source); - } -} - -void Command::MergeFrom(const Command& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.Command) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - if (from.cmd().size() > 0) { - - cmd_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.cmd_); - } -} - -void Command::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.Command) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void Command::CopyFrom(const Command& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.Command) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool Command::IsInitialized() const { - return true; -} - -void Command::InternalSwap(Command* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - cmd_.Swap(&other->cmd_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); -} - -::PROTOBUF_NAMESPACE_ID::Metadata Command::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void IndexParam::InitAsDefaultInstance() { - ::milvus::grpc::_IndexParam_default_instance_._instance.get_mutable()->status_ = const_cast< ::milvus::grpc::Status*>( - ::milvus::grpc::Status::internal_default_instance()); -} -class IndexParam::_Internal { - public: - static const ::milvus::grpc::Status& status(const IndexParam* msg); -}; - -const ::milvus::grpc::Status& -IndexParam::_Internal::status(const IndexParam* msg) { - return *msg->status_; -} -void IndexParam::clear_status() { - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; -} -IndexParam::IndexParam() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.IndexParam) -} -IndexParam::IndexParam(const IndexParam& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - extra_params_(from.extra_params_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.collection_name().empty()) { - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - if (from.has_status()) { - status_ = new ::milvus::grpc::Status(*from.status_); - } else { - status_ = nullptr; - } - index_type_ = from.index_type_; - // @@protoc_insertion_point(copy_constructor:milvus.grpc.IndexParam) -} - -void IndexParam::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_IndexParam_milvus_2eproto.base); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - ::memset(&status_, 0, static_cast( - reinterpret_cast(&index_type_) - - reinterpret_cast(&status_)) + sizeof(index_type_)); -} - -IndexParam::~IndexParam() { - // @@protoc_insertion_point(destructor:milvus.grpc.IndexParam) - SharedDtor(); -} - -void IndexParam::SharedDtor() { - collection_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (this != internal_default_instance()) delete status_; -} - -void IndexParam::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const IndexParam& IndexParam::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_IndexParam_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void IndexParam::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.IndexParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - extra_params_.Clear(); - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; - index_type_ = 0; - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* IndexParam::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.Status status = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ctx->ParseMessage(mutable_status(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // string collection_name = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_collection_name(), ptr, ctx, "milvus.grpc.IndexParam.collection_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // int32 index_type = 3; - case 3: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 24)) { - index_type_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - case 4: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 34)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_extra_params(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 34); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool IndexParam::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.IndexParam) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.Status status = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_status())); - } else { - goto handle_unusual; - } - break; - } - - // string collection_name = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_collection_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.IndexParam.collection_name")); - } else { - goto handle_unusual; - } - break; - } - - // int32 index_type = 3; - case 3: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (24 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::int32, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT32>( - input, &index_type_))); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - case 4: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (34 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_extra_params())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.IndexParam) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.IndexParam) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void IndexParam::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.IndexParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 1, _Internal::status(this), output); - } - - // string collection_name = 2; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.IndexParam.collection_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 2, this->collection_name(), output); - } - - // int32 index_type = 3; - if (this->index_type() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32(3, this->index_type(), output); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 4, - this->extra_params(static_cast(i)), - output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.IndexParam) -} - -::PROTOBUF_NAMESPACE_ID::uint8* IndexParam::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.IndexParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 1, _Internal::status(this), target); - } - - // string collection_name = 2; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.IndexParam.collection_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 2, this->collection_name(), target); - } - - // int32 index_type = 3; - if (this->index_type() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(3, this->index_type(), target); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 4, this->extra_params(static_cast(i)), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.IndexParam) - return target; -} - -size_t IndexParam::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.IndexParam) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - { - unsigned int count = static_cast(this->extra_params_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->extra_params(static_cast(i))); - } - } - - // string collection_name = 2; - if (this->collection_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_name()); - } - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *status_); - } - - // int32 index_type = 3; - if (this->index_type() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( - this->index_type()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void IndexParam::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.IndexParam) - GOOGLE_DCHECK_NE(&from, this); - const IndexParam* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.IndexParam) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.IndexParam) - MergeFrom(*source); - } -} - -void IndexParam::MergeFrom(const IndexParam& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.IndexParam) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - extra_params_.MergeFrom(from.extra_params_); - if (from.collection_name().size() > 0) { - - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - if (from.has_status()) { - mutable_status()->::milvus::grpc::Status::MergeFrom(from.status()); - } - if (from.index_type() != 0) { - set_index_type(from.index_type()); - } -} - -void IndexParam::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.IndexParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void IndexParam::CopyFrom(const IndexParam& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.IndexParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool IndexParam::IsInitialized() const { - return true; -} - -void IndexParam::InternalSwap(IndexParam* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - CastToBase(&extra_params_)->InternalSwap(CastToBase(&other->extra_params_)); - collection_name_.Swap(&other->collection_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - swap(status_, other->status_); - swap(index_type_, other->index_type_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata IndexParam::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void FlushParam::InitAsDefaultInstance() { -} -class FlushParam::_Internal { - public: -}; - -FlushParam::FlushParam() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.FlushParam) -} -FlushParam::FlushParam(const FlushParam& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - collection_name_array_(from.collection_name_array_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - // @@protoc_insertion_point(copy_constructor:milvus.grpc.FlushParam) -} - -void FlushParam::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_FlushParam_milvus_2eproto.base); -} - -FlushParam::~FlushParam() { - // @@protoc_insertion_point(destructor:milvus.grpc.FlushParam) - SharedDtor(); -} - -void FlushParam::SharedDtor() { -} - -void FlushParam::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const FlushParam& FlushParam::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_FlushParam_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void FlushParam::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.FlushParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - collection_name_array_.Clear(); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* FlushParam::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // repeated string collection_name_array = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr -= 1; - do { - ptr += 1; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(add_collection_name_array(), ptr, ctx, "milvus.grpc.FlushParam.collection_name_array"); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 10); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool FlushParam::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.FlushParam) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // repeated string collection_name_array = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->add_collection_name_array())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name_array(this->collection_name_array_size() - 1).data(), - static_cast(this->collection_name_array(this->collection_name_array_size() - 1).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.FlushParam.collection_name_array")); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.FlushParam) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.FlushParam) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void FlushParam::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.FlushParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // repeated string collection_name_array = 1; - for (int i = 0, n = this->collection_name_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name_array(i).data(), static_cast(this->collection_name_array(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.FlushParam.collection_name_array"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteString( - 1, this->collection_name_array(i), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.FlushParam) -} - -::PROTOBUF_NAMESPACE_ID::uint8* FlushParam::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.FlushParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // repeated string collection_name_array = 1; - for (int i = 0, n = this->collection_name_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name_array(i).data(), static_cast(this->collection_name_array(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.FlushParam.collection_name_array"); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteStringToArray(1, this->collection_name_array(i), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.FlushParam) - return target; -} - -size_t FlushParam::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.FlushParam) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated string collection_name_array = 1; - total_size += 1 * - ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(this->collection_name_array_size()); - for (int i = 0, n = this->collection_name_array_size(); i < n; i++) { - total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_name_array(i)); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void FlushParam::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.FlushParam) - GOOGLE_DCHECK_NE(&from, this); - const FlushParam* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.FlushParam) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.FlushParam) - MergeFrom(*source); - } -} - -void FlushParam::MergeFrom(const FlushParam& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.FlushParam) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - collection_name_array_.MergeFrom(from.collection_name_array_); -} - -void FlushParam::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.FlushParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void FlushParam::CopyFrom(const FlushParam& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.FlushParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool FlushParam::IsInitialized() const { - return true; -} - -void FlushParam::InternalSwap(FlushParam* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - collection_name_array_.InternalSwap(CastToBase(&other->collection_name_array_)); -} - -::PROTOBUF_NAMESPACE_ID::Metadata FlushParam::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void DeleteByIDParam::InitAsDefaultInstance() { -} -class DeleteByIDParam::_Internal { - public: -}; - -DeleteByIDParam::DeleteByIDParam() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.DeleteByIDParam) -} -DeleteByIDParam::DeleteByIDParam(const DeleteByIDParam& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - id_array_(from.id_array_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.collection_name().empty()) { - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.DeleteByIDParam) -} - -void DeleteByIDParam::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_DeleteByIDParam_milvus_2eproto.base); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -DeleteByIDParam::~DeleteByIDParam() { - // @@protoc_insertion_point(destructor:milvus.grpc.DeleteByIDParam) - SharedDtor(); -} - -void DeleteByIDParam::SharedDtor() { - collection_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void DeleteByIDParam::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const DeleteByIDParam& DeleteByIDParam::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_DeleteByIDParam_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void DeleteByIDParam::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.DeleteByIDParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - id_array_.Clear(); - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* DeleteByIDParam::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // string collection_name = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_collection_name(), ptr, ctx, "milvus.grpc.DeleteByIDParam.collection_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated int64 id_array = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt64Parser(mutable_id_array(), ptr, ctx); - CHK_(ptr); - } else if (static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16) { - add_id_array(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr)); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool DeleteByIDParam::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.DeleteByIDParam) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // string collection_name = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_collection_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.DeleteByIDParam.collection_name")); - } else { - goto handle_unusual; - } - break; - } - - // repeated int64 id_array = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPackedPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, this->mutable_id_array()))); - } else if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (16 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadRepeatedPrimitiveNoInline< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - 1, 18u, input, this->mutable_id_array()))); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.DeleteByIDParam) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.DeleteByIDParam) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void DeleteByIDParam::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.DeleteByIDParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.DeleteByIDParam.collection_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 1, this->collection_name(), output); - } - - // repeated int64 id_array = 2; - if (this->id_array_size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTag(2, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, output); - output->WriteVarint32(_id_array_cached_byte_size_.load( - std::memory_order_relaxed)); - } - for (int i = 0, n = this->id_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64NoTag( - this->id_array(i), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.DeleteByIDParam) -} - -::PROTOBUF_NAMESPACE_ID::uint8* DeleteByIDParam::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.DeleteByIDParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.DeleteByIDParam.collection_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 1, this->collection_name(), target); - } - - // repeated int64 id_array = 2; - if (this->id_array_size() > 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTagToArray( - 2, - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, - target); - target = ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream::WriteVarint32ToArray( - _id_array_cached_byte_size_.load(std::memory_order_relaxed), - target); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteInt64NoTagToArray(this->id_array_, target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.DeleteByIDParam) - return target; -} - -size_t DeleteByIDParam::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.DeleteByIDParam) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated int64 id_array = 2; - { - size_t data_size = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - Int64Size(this->id_array_); - if (data_size > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( - static_cast<::PROTOBUF_NAMESPACE_ID::int32>(data_size)); - } - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); - _id_array_cached_byte_size_.store(cached_size, - std::memory_order_relaxed); - total_size += data_size; - } - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_name()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void DeleteByIDParam::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.DeleteByIDParam) - GOOGLE_DCHECK_NE(&from, this); - const DeleteByIDParam* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.DeleteByIDParam) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.DeleteByIDParam) - MergeFrom(*source); - } -} - -void DeleteByIDParam::MergeFrom(const DeleteByIDParam& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.DeleteByIDParam) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - id_array_.MergeFrom(from.id_array_); - if (from.collection_name().size() > 0) { - - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } -} - -void DeleteByIDParam::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.DeleteByIDParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void DeleteByIDParam::CopyFrom(const DeleteByIDParam& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.DeleteByIDParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool DeleteByIDParam::IsInitialized() const { - return true; -} - -void DeleteByIDParam::InternalSwap(DeleteByIDParam* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - id_array_.InternalSwap(&other->id_array_); - collection_name_.Swap(&other->collection_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); -} - -::PROTOBUF_NAMESPACE_ID::Metadata DeleteByIDParam::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void CollectionInfo::InitAsDefaultInstance() { - ::milvus::grpc::_CollectionInfo_default_instance_._instance.get_mutable()->status_ = const_cast< ::milvus::grpc::Status*>( - ::milvus::grpc::Status::internal_default_instance()); -} -class CollectionInfo::_Internal { - public: - static const ::milvus::grpc::Status& status(const CollectionInfo* msg); -}; - -const ::milvus::grpc::Status& -CollectionInfo::_Internal::status(const CollectionInfo* msg) { - return *msg->status_; -} -void CollectionInfo::clear_status() { - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; -} -CollectionInfo::CollectionInfo() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.CollectionInfo) -} -CollectionInfo::CollectionInfo(const CollectionInfo& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - json_info_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.json_info().empty()) { - json_info_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.json_info_); - } - if (from.has_status()) { - status_ = new ::milvus::grpc::Status(*from.status_); - } else { - status_ = nullptr; - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.CollectionInfo) -} - -void CollectionInfo::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_CollectionInfo_milvus_2eproto.base); - json_info_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - status_ = nullptr; -} - -CollectionInfo::~CollectionInfo() { - // @@protoc_insertion_point(destructor:milvus.grpc.CollectionInfo) - SharedDtor(); -} - -void CollectionInfo::SharedDtor() { - json_info_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (this != internal_default_instance()) delete status_; -} - -void CollectionInfo::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const CollectionInfo& CollectionInfo::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_CollectionInfo_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void CollectionInfo::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.CollectionInfo) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - json_info_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* CollectionInfo::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.Status status = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ctx->ParseMessage(mutable_status(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // string json_info = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_json_info(), ptr, ctx, "milvus.grpc.CollectionInfo.json_info"); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool CollectionInfo::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.CollectionInfo) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.Status status = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_status())); - } else { - goto handle_unusual; - } - break; - } - - // string json_info = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_json_info())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->json_info().data(), static_cast(this->json_info().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.CollectionInfo.json_info")); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.CollectionInfo) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.CollectionInfo) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void CollectionInfo::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.CollectionInfo) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 1, _Internal::status(this), output); - } - - // string json_info = 2; - if (this->json_info().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->json_info().data(), static_cast(this->json_info().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.CollectionInfo.json_info"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 2, this->json_info(), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.CollectionInfo) -} - -::PROTOBUF_NAMESPACE_ID::uint8* CollectionInfo::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.CollectionInfo) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 1, _Internal::status(this), target); - } - - // string json_info = 2; - if (this->json_info().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->json_info().data(), static_cast(this->json_info().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.CollectionInfo.json_info"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 2, this->json_info(), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.CollectionInfo) - return target; -} - -size_t CollectionInfo::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.CollectionInfo) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // string json_info = 2; - if (this->json_info().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->json_info()); - } - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *status_); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void CollectionInfo::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.CollectionInfo) - GOOGLE_DCHECK_NE(&from, this); - const CollectionInfo* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.CollectionInfo) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.CollectionInfo) - MergeFrom(*source); - } -} - -void CollectionInfo::MergeFrom(const CollectionInfo& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.CollectionInfo) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - if (from.json_info().size() > 0) { - - json_info_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.json_info_); - } - if (from.has_status()) { - mutable_status()->::milvus::grpc::Status::MergeFrom(from.status()); - } -} - -void CollectionInfo::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.CollectionInfo) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void CollectionInfo::CopyFrom(const CollectionInfo& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.CollectionInfo) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool CollectionInfo::IsInitialized() const { - return true; -} - -void CollectionInfo::InternalSwap(CollectionInfo* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - json_info_.Swap(&other->json_info_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - swap(status_, other->status_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata CollectionInfo::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void VectorsIdentity::InitAsDefaultInstance() { -} -class VectorsIdentity::_Internal { - public: -}; - -VectorsIdentity::VectorsIdentity() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.VectorsIdentity) -} -VectorsIdentity::VectorsIdentity(const VectorsIdentity& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - id_array_(from.id_array_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.collection_name().empty()) { - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.VectorsIdentity) -} - -void VectorsIdentity::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_VectorsIdentity_milvus_2eproto.base); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -VectorsIdentity::~VectorsIdentity() { - // @@protoc_insertion_point(destructor:milvus.grpc.VectorsIdentity) - SharedDtor(); -} - -void VectorsIdentity::SharedDtor() { - collection_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void VectorsIdentity::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const VectorsIdentity& VectorsIdentity::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_VectorsIdentity_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void VectorsIdentity::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.VectorsIdentity) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - id_array_.Clear(); - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* VectorsIdentity::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // string collection_name = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_collection_name(), ptr, ctx, "milvus.grpc.VectorsIdentity.collection_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated int64 id_array = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt64Parser(mutable_id_array(), ptr, ctx); - CHK_(ptr); - } else if (static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16) { - add_id_array(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr)); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool VectorsIdentity::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.VectorsIdentity) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // string collection_name = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_collection_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.VectorsIdentity.collection_name")); - } else { - goto handle_unusual; - } - break; - } - - // repeated int64 id_array = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPackedPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, this->mutable_id_array()))); - } else if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (16 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadRepeatedPrimitiveNoInline< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - 1, 18u, input, this->mutable_id_array()))); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.VectorsIdentity) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.VectorsIdentity) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void VectorsIdentity::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.VectorsIdentity) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.VectorsIdentity.collection_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 1, this->collection_name(), output); - } - - // repeated int64 id_array = 2; - if (this->id_array_size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTag(2, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, output); - output->WriteVarint32(_id_array_cached_byte_size_.load( - std::memory_order_relaxed)); - } - for (int i = 0, n = this->id_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64NoTag( - this->id_array(i), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.VectorsIdentity) -} - -::PROTOBUF_NAMESPACE_ID::uint8* VectorsIdentity::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.VectorsIdentity) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.VectorsIdentity.collection_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 1, this->collection_name(), target); - } - - // repeated int64 id_array = 2; - if (this->id_array_size() > 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTagToArray( - 2, - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, - target); - target = ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream::WriteVarint32ToArray( - _id_array_cached_byte_size_.load(std::memory_order_relaxed), - target); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteInt64NoTagToArray(this->id_array_, target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.VectorsIdentity) - return target; -} - -size_t VectorsIdentity::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.VectorsIdentity) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated int64 id_array = 2; - { - size_t data_size = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - Int64Size(this->id_array_); - if (data_size > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( - static_cast<::PROTOBUF_NAMESPACE_ID::int32>(data_size)); - } - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); - _id_array_cached_byte_size_.store(cached_size, - std::memory_order_relaxed); - total_size += data_size; - } - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_name()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void VectorsIdentity::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.VectorsIdentity) - GOOGLE_DCHECK_NE(&from, this); - const VectorsIdentity* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.VectorsIdentity) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.VectorsIdentity) - MergeFrom(*source); - } -} - -void VectorsIdentity::MergeFrom(const VectorsIdentity& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.VectorsIdentity) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - id_array_.MergeFrom(from.id_array_); - if (from.collection_name().size() > 0) { - - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } -} - -void VectorsIdentity::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.VectorsIdentity) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void VectorsIdentity::CopyFrom(const VectorsIdentity& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.VectorsIdentity) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool VectorsIdentity::IsInitialized() const { - return true; -} - -void VectorsIdentity::InternalSwap(VectorsIdentity* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - id_array_.InternalSwap(&other->id_array_); - collection_name_.Swap(&other->collection_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); -} - -::PROTOBUF_NAMESPACE_ID::Metadata VectorsIdentity::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void VectorsData::InitAsDefaultInstance() { - ::milvus::grpc::_VectorsData_default_instance_._instance.get_mutable()->status_ = const_cast< ::milvus::grpc::Status*>( - ::milvus::grpc::Status::internal_default_instance()); -} -class VectorsData::_Internal { - public: - static const ::milvus::grpc::Status& status(const VectorsData* msg); -}; - -const ::milvus::grpc::Status& -VectorsData::_Internal::status(const VectorsData* msg) { - return *msg->status_; -} -void VectorsData::clear_status() { - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; -} -VectorsData::VectorsData() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.VectorsData) -} -VectorsData::VectorsData(const VectorsData& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - vectors_data_(from.vectors_data_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - if (from.has_status()) { - status_ = new ::milvus::grpc::Status(*from.status_); - } else { - status_ = nullptr; - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.VectorsData) -} - -void VectorsData::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_VectorsData_milvus_2eproto.base); - status_ = nullptr; -} - -VectorsData::~VectorsData() { - // @@protoc_insertion_point(destructor:milvus.grpc.VectorsData) - SharedDtor(); -} - -void VectorsData::SharedDtor() { - if (this != internal_default_instance()) delete status_; -} - -void VectorsData::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const VectorsData& VectorsData::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_VectorsData_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void VectorsData::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.VectorsData) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - vectors_data_.Clear(); - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* VectorsData::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.Status status = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ctx->ParseMessage(mutable_status(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.RowRecord vectors_data = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_vectors_data(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 18); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool VectorsData::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.VectorsData) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.Status status = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_status())); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.RowRecord vectors_data = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_vectors_data())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.VectorsData) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.VectorsData) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void VectorsData::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.VectorsData) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 1, _Internal::status(this), output); - } - - // repeated .milvus.grpc.RowRecord vectors_data = 2; - for (unsigned int i = 0, - n = static_cast(this->vectors_data_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 2, - this->vectors_data(static_cast(i)), - output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.VectorsData) -} - -::PROTOBUF_NAMESPACE_ID::uint8* VectorsData::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.VectorsData) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 1, _Internal::status(this), target); - } - - // repeated .milvus.grpc.RowRecord vectors_data = 2; - for (unsigned int i = 0, - n = static_cast(this->vectors_data_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 2, this->vectors_data(static_cast(i)), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.VectorsData) - return target; -} - -size_t VectorsData::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.VectorsData) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated .milvus.grpc.RowRecord vectors_data = 2; - { - unsigned int count = static_cast(this->vectors_data_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->vectors_data(static_cast(i))); - } - } - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *status_); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void VectorsData::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.VectorsData) - GOOGLE_DCHECK_NE(&from, this); - const VectorsData* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.VectorsData) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.VectorsData) - MergeFrom(*source); - } -} - -void VectorsData::MergeFrom(const VectorsData& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.VectorsData) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - vectors_data_.MergeFrom(from.vectors_data_); - if (from.has_status()) { - mutable_status()->::milvus::grpc::Status::MergeFrom(from.status()); - } -} - -void VectorsData::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.VectorsData) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void VectorsData::CopyFrom(const VectorsData& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.VectorsData) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool VectorsData::IsInitialized() const { - return true; -} - -void VectorsData::InternalSwap(VectorsData* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - CastToBase(&vectors_data_)->InternalSwap(CastToBase(&other->vectors_data_)); - swap(status_, other->status_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata VectorsData::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void GetVectorIDsParam::InitAsDefaultInstance() { -} -class GetVectorIDsParam::_Internal { - public: -}; - -GetVectorIDsParam::GetVectorIDsParam() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.GetVectorIDsParam) -} -GetVectorIDsParam::GetVectorIDsParam(const GetVectorIDsParam& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.collection_name().empty()) { - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - segment_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.segment_name().empty()) { - segment_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.segment_name_); - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.GetVectorIDsParam) -} - -void GetVectorIDsParam::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_GetVectorIDsParam_milvus_2eproto.base); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - segment_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -GetVectorIDsParam::~GetVectorIDsParam() { - // @@protoc_insertion_point(destructor:milvus.grpc.GetVectorIDsParam) - SharedDtor(); -} - -void GetVectorIDsParam::SharedDtor() { - collection_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - segment_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void GetVectorIDsParam::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const GetVectorIDsParam& GetVectorIDsParam::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_GetVectorIDsParam_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void GetVectorIDsParam::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.GetVectorIDsParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - segment_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* GetVectorIDsParam::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // string collection_name = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_collection_name(), ptr, ctx, "milvus.grpc.GetVectorIDsParam.collection_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // string segment_name = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_segment_name(), ptr, ctx, "milvus.grpc.GetVectorIDsParam.segment_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool GetVectorIDsParam::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.GetVectorIDsParam) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // string collection_name = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_collection_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.GetVectorIDsParam.collection_name")); - } else { - goto handle_unusual; - } - break; - } - - // string segment_name = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_segment_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->segment_name().data(), static_cast(this->segment_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.GetVectorIDsParam.segment_name")); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.GetVectorIDsParam) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.GetVectorIDsParam) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void GetVectorIDsParam::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.GetVectorIDsParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.GetVectorIDsParam.collection_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 1, this->collection_name(), output); - } - - // string segment_name = 2; - if (this->segment_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->segment_name().data(), static_cast(this->segment_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.GetVectorIDsParam.segment_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 2, this->segment_name(), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.GetVectorIDsParam) -} - -::PROTOBUF_NAMESPACE_ID::uint8* GetVectorIDsParam::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.GetVectorIDsParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.GetVectorIDsParam.collection_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 1, this->collection_name(), target); - } - - // string segment_name = 2; - if (this->segment_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->segment_name().data(), static_cast(this->segment_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.GetVectorIDsParam.segment_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 2, this->segment_name(), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.GetVectorIDsParam) - return target; -} - -size_t GetVectorIDsParam::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.GetVectorIDsParam) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_name()); - } - - // string segment_name = 2; - if (this->segment_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->segment_name()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void GetVectorIDsParam::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.GetVectorIDsParam) - GOOGLE_DCHECK_NE(&from, this); - const GetVectorIDsParam* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.GetVectorIDsParam) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.GetVectorIDsParam) - MergeFrom(*source); - } -} - -void GetVectorIDsParam::MergeFrom(const GetVectorIDsParam& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.GetVectorIDsParam) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - if (from.collection_name().size() > 0) { - - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - if (from.segment_name().size() > 0) { - - segment_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.segment_name_); - } -} - -void GetVectorIDsParam::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.GetVectorIDsParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void GetVectorIDsParam::CopyFrom(const GetVectorIDsParam& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.GetVectorIDsParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool GetVectorIDsParam::IsInitialized() const { - return true; -} - -void GetVectorIDsParam::InternalSwap(GetVectorIDsParam* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - collection_name_.Swap(&other->collection_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - segment_name_.Swap(&other->segment_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); -} - -::PROTOBUF_NAMESPACE_ID::Metadata GetVectorIDsParam::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void VectorFieldParam::InitAsDefaultInstance() { -} -class VectorFieldParam::_Internal { - public: -}; - -VectorFieldParam::VectorFieldParam() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.VectorFieldParam) -} -VectorFieldParam::VectorFieldParam(const VectorFieldParam& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - dimension_ = from.dimension_; - // @@protoc_insertion_point(copy_constructor:milvus.grpc.VectorFieldParam) -} - -void VectorFieldParam::SharedCtor() { - dimension_ = PROTOBUF_LONGLONG(0); -} - -VectorFieldParam::~VectorFieldParam() { - // @@protoc_insertion_point(destructor:milvus.grpc.VectorFieldParam) - SharedDtor(); -} - -void VectorFieldParam::SharedDtor() { -} - -void VectorFieldParam::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const VectorFieldParam& VectorFieldParam::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_VectorFieldParam_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void VectorFieldParam::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.VectorFieldParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - dimension_ = PROTOBUF_LONGLONG(0); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* VectorFieldParam::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // int64 dimension = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 8)) { - dimension_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool VectorFieldParam::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.VectorFieldParam) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // int64 dimension = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (8 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, &dimension_))); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.VectorFieldParam) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.VectorFieldParam) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void VectorFieldParam::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.VectorFieldParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // int64 dimension = 1; - if (this->dimension() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64(1, this->dimension(), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.VectorFieldParam) -} - -::PROTOBUF_NAMESPACE_ID::uint8* VectorFieldParam::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.VectorFieldParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // int64 dimension = 1; - if (this->dimension() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64ToArray(1, this->dimension(), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.VectorFieldParam) - return target; -} - -size_t VectorFieldParam::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.VectorFieldParam) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // int64 dimension = 1; - if (this->dimension() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int64Size( - this->dimension()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void VectorFieldParam::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.VectorFieldParam) - GOOGLE_DCHECK_NE(&from, this); - const VectorFieldParam* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.VectorFieldParam) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.VectorFieldParam) - MergeFrom(*source); - } -} - -void VectorFieldParam::MergeFrom(const VectorFieldParam& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.VectorFieldParam) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - if (from.dimension() != 0) { - set_dimension(from.dimension()); - } -} - -void VectorFieldParam::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.VectorFieldParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void VectorFieldParam::CopyFrom(const VectorFieldParam& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.VectorFieldParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool VectorFieldParam::IsInitialized() const { - return true; -} - -void VectorFieldParam::InternalSwap(VectorFieldParam* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - swap(dimension_, other->dimension_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata VectorFieldParam::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void FieldType::InitAsDefaultInstance() { - ::milvus::grpc::_FieldType_default_instance_.data_type_ = 0; - ::milvus::grpc::_FieldType_default_instance_.vector_param_ = const_cast< ::milvus::grpc::VectorFieldParam*>( - ::milvus::grpc::VectorFieldParam::internal_default_instance()); -} -class FieldType::_Internal { - public: - static const ::milvus::grpc::VectorFieldParam& vector_param(const FieldType* msg); -}; - -const ::milvus::grpc::VectorFieldParam& -FieldType::_Internal::vector_param(const FieldType* msg) { - return *msg->value_.vector_param_; -} -void FieldType::set_allocated_vector_param(::milvus::grpc::VectorFieldParam* vector_param) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - clear_value(); - if (vector_param) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - vector_param = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, vector_param, submessage_arena); - } - set_has_vector_param(); - value_.vector_param_ = vector_param; - } - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.FieldType.vector_param) -} -FieldType::FieldType() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.FieldType) -} -FieldType::FieldType(const FieldType& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - clear_has_value(); - switch (from.value_case()) { - case kDataType: { - set_data_type(from.data_type()); - break; - } - case kVectorParam: { - mutable_vector_param()->::milvus::grpc::VectorFieldParam::MergeFrom(from.vector_param()); - break; - } - case VALUE_NOT_SET: { - break; - } - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.FieldType) -} - -void FieldType::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_FieldType_milvus_2eproto.base); - clear_has_value(); -} - -FieldType::~FieldType() { - // @@protoc_insertion_point(destructor:milvus.grpc.FieldType) - SharedDtor(); -} - -void FieldType::SharedDtor() { - if (has_value()) { - clear_value(); - } -} - -void FieldType::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const FieldType& FieldType::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_FieldType_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void FieldType::clear_value() { -// @@protoc_insertion_point(one_of_clear_start:milvus.grpc.FieldType) - switch (value_case()) { - case kDataType: { - // No need to clear - break; - } - case kVectorParam: { - delete value_.vector_param_; - break; - } - case VALUE_NOT_SET: { - break; - } - } - _oneof_case_[0] = VALUE_NOT_SET; -} - - -void FieldType::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.FieldType) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - clear_value(); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* FieldType::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.DataType data_type = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 8)) { - ::PROTOBUF_NAMESPACE_ID::uint64 val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - set_data_type(static_cast<::milvus::grpc::DataType>(val)); - } else goto handle_unusual; - continue; - // .milvus.grpc.VectorFieldParam vector_param = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ctx->ParseMessage(mutable_vector_param(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool FieldType::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.FieldType) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.DataType data_type = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (8 & 0xFF)) { - int value = 0; - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - int, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_ENUM>( - input, &value))); - set_data_type(static_cast< ::milvus::grpc::DataType >(value)); - } else { - goto handle_unusual; - } - break; - } - - // .milvus.grpc.VectorFieldParam vector_param = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_vector_param())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.FieldType) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.FieldType) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void FieldType::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.FieldType) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.DataType data_type = 1; - if (has_data_type()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnum( - 1, this->data_type(), output); - } - - // .milvus.grpc.VectorFieldParam vector_param = 2; - if (has_vector_param()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 2, _Internal::vector_param(this), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.FieldType) -} - -::PROTOBUF_NAMESPACE_ID::uint8* FieldType::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.FieldType) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.DataType data_type = 1; - if (has_data_type()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray( - 1, this->data_type(), target); - } - - // .milvus.grpc.VectorFieldParam vector_param = 2; - if (has_vector_param()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 2, _Internal::vector_param(this), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.FieldType) - return target; -} - -size_t FieldType::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.FieldType) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - switch (value_case()) { - // .milvus.grpc.DataType data_type = 1; - case kDataType: { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->data_type()); - break; - } - // .milvus.grpc.VectorFieldParam vector_param = 2; - case kVectorParam: { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *value_.vector_param_); - break; - } - case VALUE_NOT_SET: { - break; - } - } - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void FieldType::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.FieldType) - GOOGLE_DCHECK_NE(&from, this); - const FieldType* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.FieldType) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.FieldType) - MergeFrom(*source); - } -} - -void FieldType::MergeFrom(const FieldType& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.FieldType) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - switch (from.value_case()) { - case kDataType: { - set_data_type(from.data_type()); - break; - } - case kVectorParam: { - mutable_vector_param()->::milvus::grpc::VectorFieldParam::MergeFrom(from.vector_param()); - break; - } - case VALUE_NOT_SET: { - break; - } - } -} - -void FieldType::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.FieldType) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void FieldType::CopyFrom(const FieldType& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.FieldType) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool FieldType::IsInitialized() const { - return true; -} - -void FieldType::InternalSwap(FieldType* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - swap(value_, other->value_); - swap(_oneof_case_[0], other->_oneof_case_[0]); -} - -::PROTOBUF_NAMESPACE_ID::Metadata FieldType::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void FieldParam::InitAsDefaultInstance() { - ::milvus::grpc::_FieldParam_default_instance_._instance.get_mutable()->type_ = const_cast< ::milvus::grpc::FieldType*>( - ::milvus::grpc::FieldType::internal_default_instance()); -} -class FieldParam::_Internal { - public: - static const ::milvus::grpc::FieldType& type(const FieldParam* msg); -}; - -const ::milvus::grpc::FieldType& -FieldParam::_Internal::type(const FieldParam* msg) { - return *msg->type_; -} -FieldParam::FieldParam() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.FieldParam) -} -FieldParam::FieldParam(const FieldParam& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - extra_params_(from.extra_params_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.name().empty()) { - name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.name_); - } - if (from.has_type()) { - type_ = new ::milvus::grpc::FieldType(*from.type_); - } else { - type_ = nullptr; - } - id_ = from.id_; - // @@protoc_insertion_point(copy_constructor:milvus.grpc.FieldParam) -} - -void FieldParam::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_FieldParam_milvus_2eproto.base); - name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - ::memset(&type_, 0, static_cast( - reinterpret_cast(&id_) - - reinterpret_cast(&type_)) + sizeof(id_)); -} - -FieldParam::~FieldParam() { - // @@protoc_insertion_point(destructor:milvus.grpc.FieldParam) - SharedDtor(); -} - -void FieldParam::SharedDtor() { - name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (this != internal_default_instance()) delete type_; -} - -void FieldParam::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const FieldParam& FieldParam::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_FieldParam_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void FieldParam::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.FieldParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - extra_params_.Clear(); - name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (GetArenaNoVirtual() == nullptr && type_ != nullptr) { - delete type_; - } - type_ = nullptr; - id_ = PROTOBUF_ULONGLONG(0); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* FieldParam::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // uint64 id = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 8)) { - id_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // string name = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_name(), ptr, ctx, "milvus.grpc.FieldParam.name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // .milvus.grpc.FieldType type = 3; - case 3: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) { - ptr = ctx->ParseMessage(mutable_type(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - case 4: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 34)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_extra_params(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 34); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool FieldParam::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.FieldParam) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // uint64 id = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (8 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::uint64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_UINT64>( - input, &id_))); - } else { - goto handle_unusual; - } - break; - } - - // string name = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->name().data(), static_cast(this->name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.FieldParam.name")); - } else { - goto handle_unusual; - } - break; - } - - // .milvus.grpc.FieldType type = 3; - case 3: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (26 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_type())); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - case 4: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (34 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_extra_params())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.FieldParam) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.FieldParam) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void FieldParam::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.FieldParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // uint64 id = 1; - if (this->id() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt64(1, this->id(), output); - } - - // string name = 2; - if (this->name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->name().data(), static_cast(this->name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.FieldParam.name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 2, this->name(), output); - } - - // .milvus.grpc.FieldType type = 3; - if (this->has_type()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 3, _Internal::type(this), output); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 4, - this->extra_params(static_cast(i)), - output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.FieldParam) -} - -::PROTOBUF_NAMESPACE_ID::uint8* FieldParam::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.FieldParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // uint64 id = 1; - if (this->id() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt64ToArray(1, this->id(), target); - } - - // string name = 2; - if (this->name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->name().data(), static_cast(this->name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.FieldParam.name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 2, this->name(), target); - } - - // .milvus.grpc.FieldType type = 3; - if (this->has_type()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 3, _Internal::type(this), target); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 4, this->extra_params(static_cast(i)), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.FieldParam) - return target; -} - -size_t FieldParam::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.FieldParam) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - { - unsigned int count = static_cast(this->extra_params_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->extra_params(static_cast(i))); - } - } - - // string name = 2; - if (this->name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->name()); - } - - // .milvus.grpc.FieldType type = 3; - if (this->has_type()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *type_); - } - - // uint64 id = 1; - if (this->id() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::UInt64Size( - this->id()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void FieldParam::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.FieldParam) - GOOGLE_DCHECK_NE(&from, this); - const FieldParam* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.FieldParam) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.FieldParam) - MergeFrom(*source); - } -} - -void FieldParam::MergeFrom(const FieldParam& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.FieldParam) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - extra_params_.MergeFrom(from.extra_params_); - if (from.name().size() > 0) { - - name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.name_); - } - if (from.has_type()) { - mutable_type()->::milvus::grpc::FieldType::MergeFrom(from.type()); - } - if (from.id() != 0) { - set_id(from.id()); - } -} - -void FieldParam::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.FieldParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void FieldParam::CopyFrom(const FieldParam& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.FieldParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool FieldParam::IsInitialized() const { - return true; -} - -void FieldParam::InternalSwap(FieldParam* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - CastToBase(&extra_params_)->InternalSwap(CastToBase(&other->extra_params_)); - name_.Swap(&other->name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - swap(type_, other->type_); - swap(id_, other->id_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata FieldParam::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void VectorFieldValue::InitAsDefaultInstance() { -} -class VectorFieldValue::_Internal { - public: -}; - -VectorFieldValue::VectorFieldValue() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.VectorFieldValue) -} -VectorFieldValue::VectorFieldValue(const VectorFieldValue& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - value_(from.value_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - // @@protoc_insertion_point(copy_constructor:milvus.grpc.VectorFieldValue) -} - -void VectorFieldValue::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_VectorFieldValue_milvus_2eproto.base); -} - -VectorFieldValue::~VectorFieldValue() { - // @@protoc_insertion_point(destructor:milvus.grpc.VectorFieldValue) - SharedDtor(); -} - -void VectorFieldValue::SharedDtor() { -} - -void VectorFieldValue::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const VectorFieldValue& VectorFieldValue::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_VectorFieldValue_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void VectorFieldValue::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.VectorFieldValue) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - value_.Clear(); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* VectorFieldValue::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // repeated .milvus.grpc.RowRecord value = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_value(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 10); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool VectorFieldValue::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.VectorFieldValue) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // repeated .milvus.grpc.RowRecord value = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_value())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.VectorFieldValue) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.VectorFieldValue) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void VectorFieldValue::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.VectorFieldValue) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // repeated .milvus.grpc.RowRecord value = 1; - for (unsigned int i = 0, - n = static_cast(this->value_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 1, - this->value(static_cast(i)), - output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.VectorFieldValue) -} - -::PROTOBUF_NAMESPACE_ID::uint8* VectorFieldValue::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.VectorFieldValue) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // repeated .milvus.grpc.RowRecord value = 1; - for (unsigned int i = 0, - n = static_cast(this->value_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 1, this->value(static_cast(i)), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.VectorFieldValue) - return target; -} - -size_t VectorFieldValue::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.VectorFieldValue) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated .milvus.grpc.RowRecord value = 1; - { - unsigned int count = static_cast(this->value_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->value(static_cast(i))); - } - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void VectorFieldValue::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.VectorFieldValue) - GOOGLE_DCHECK_NE(&from, this); - const VectorFieldValue* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.VectorFieldValue) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.VectorFieldValue) - MergeFrom(*source); - } -} - -void VectorFieldValue::MergeFrom(const VectorFieldValue& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.VectorFieldValue) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - value_.MergeFrom(from.value_); -} - -void VectorFieldValue::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.VectorFieldValue) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void VectorFieldValue::CopyFrom(const VectorFieldValue& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.VectorFieldValue) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool VectorFieldValue::IsInitialized() const { - return true; -} - -void VectorFieldValue::InternalSwap(VectorFieldValue* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - CastToBase(&value_)->InternalSwap(CastToBase(&other->value_)); -} - -::PROTOBUF_NAMESPACE_ID::Metadata VectorFieldValue::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void FieldValue::InitAsDefaultInstance() { - ::milvus::grpc::_FieldValue_default_instance_.int32_value_ = 0; - ::milvus::grpc::_FieldValue_default_instance_.int64_value_ = PROTOBUF_LONGLONG(0); - ::milvus::grpc::_FieldValue_default_instance_.float_value_ = 0; - ::milvus::grpc::_FieldValue_default_instance_.double_value_ = 0; - ::milvus::grpc::_FieldValue_default_instance_.string_value_.UnsafeSetDefault( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - ::milvus::grpc::_FieldValue_default_instance_.bool_value_ = false; - ::milvus::grpc::_FieldValue_default_instance_.vector_value_ = const_cast< ::milvus::grpc::VectorFieldValue*>( - ::milvus::grpc::VectorFieldValue::internal_default_instance()); -} -class FieldValue::_Internal { - public: - static const ::milvus::grpc::VectorFieldValue& vector_value(const FieldValue* msg); -}; - -const ::milvus::grpc::VectorFieldValue& -FieldValue::_Internal::vector_value(const FieldValue* msg) { - return *msg->value_.vector_value_; -} -void FieldValue::set_allocated_vector_value(::milvus::grpc::VectorFieldValue* vector_value) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - clear_value(); - if (vector_value) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - vector_value = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, vector_value, submessage_arena); - } - set_has_vector_value(); - value_.vector_value_ = vector_value; - } - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.FieldValue.vector_value) -} -FieldValue::FieldValue() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.FieldValue) -} -FieldValue::FieldValue(const FieldValue& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - clear_has_value(); - switch (from.value_case()) { - case kInt32Value: { - set_int32_value(from.int32_value()); - break; - } - case kInt64Value: { - set_int64_value(from.int64_value()); - break; - } - case kFloatValue: { - set_float_value(from.float_value()); - break; - } - case kDoubleValue: { - set_double_value(from.double_value()); - break; - } - case kStringValue: { - set_string_value(from.string_value()); - break; - } - case kBoolValue: { - set_bool_value(from.bool_value()); - break; - } - case kVectorValue: { - mutable_vector_value()->::milvus::grpc::VectorFieldValue::MergeFrom(from.vector_value()); - break; - } - case VALUE_NOT_SET: { - break; - } - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.FieldValue) -} - -void FieldValue::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_FieldValue_milvus_2eproto.base); - clear_has_value(); -} - -FieldValue::~FieldValue() { - // @@protoc_insertion_point(destructor:milvus.grpc.FieldValue) - SharedDtor(); -} - -void FieldValue::SharedDtor() { - if (has_value()) { - clear_value(); - } -} - -void FieldValue::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const FieldValue& FieldValue::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_FieldValue_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void FieldValue::clear_value() { -// @@protoc_insertion_point(one_of_clear_start:milvus.grpc.FieldValue) - switch (value_case()) { - case kInt32Value: { - // No need to clear - break; - } - case kInt64Value: { - // No need to clear - break; - } - case kFloatValue: { - // No need to clear - break; - } - case kDoubleValue: { - // No need to clear - break; - } - case kStringValue: { - value_.string_value_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - break; - } - case kBoolValue: { - // No need to clear - break; - } - case kVectorValue: { - delete value_.vector_value_; - break; - } - case VALUE_NOT_SET: { - break; - } - } - _oneof_case_[0] = VALUE_NOT_SET; -} - - -void FieldValue::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.FieldValue) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - clear_value(); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* FieldValue::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // int32 int32_value = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 8)) { - set_int32_value(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr)); - CHK_(ptr); - } else goto handle_unusual; - continue; - // int64 int64_value = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) { - set_int64_value(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr)); - CHK_(ptr); - } else goto handle_unusual; - continue; - // float float_value = 3; - case 3: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 29)) { - set_float_value(::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr)); - ptr += sizeof(float); - } else goto handle_unusual; - continue; - // double double_value = 4; - case 4: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 33)) { - set_double_value(::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr)); - ptr += sizeof(double); - } else goto handle_unusual; - continue; - // string string_value = 5; - case 5: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 42)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_string_value(), ptr, ctx, "milvus.grpc.FieldValue.string_value"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // bool bool_value = 6; - case 6: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 48)) { - set_bool_value(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr)); - CHK_(ptr); - } else goto handle_unusual; - continue; - // .milvus.grpc.VectorFieldValue vector_value = 7; - case 7: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 58)) { - ptr = ctx->ParseMessage(mutable_vector_value(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool FieldValue::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.FieldValue) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // int32 int32_value = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (8 & 0xFF)) { - clear_value(); - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::int32, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT32>( - input, &value_.int32_value_))); - set_has_int32_value(); - } else { - goto handle_unusual; - } - break; - } - - // int64 int64_value = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (16 & 0xFF)) { - clear_value(); - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, &value_.int64_value_))); - set_has_int64_value(); - } else { - goto handle_unusual; - } - break; - } - - // float float_value = 3; - case 3: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (29 & 0xFF)) { - clear_value(); - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - float, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_FLOAT>( - input, &value_.float_value_))); - set_has_float_value(); - } else { - goto handle_unusual; - } - break; - } - - // double double_value = 4; - case 4: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (33 & 0xFF)) { - clear_value(); - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - double, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_DOUBLE>( - input, &value_.double_value_))); - set_has_double_value(); - } else { - goto handle_unusual; - } - break; - } - - // string string_value = 5; - case 5: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (42 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_string_value())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->string_value().data(), static_cast(this->string_value().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.FieldValue.string_value")); - } else { - goto handle_unusual; - } - break; - } - - // bool bool_value = 6; - case 6: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (48 & 0xFF)) { - clear_value(); - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - bool, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_BOOL>( - input, &value_.bool_value_))); - set_has_bool_value(); - } else { - goto handle_unusual; - } - break; - } - - // .milvus.grpc.VectorFieldValue vector_value = 7; - case 7: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (58 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_vector_value())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.FieldValue) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.FieldValue) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void FieldValue::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.FieldValue) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // int32 int32_value = 1; - if (has_int32_value()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32(1, this->int32_value(), output); - } - - // int64 int64_value = 2; - if (has_int64_value()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64(2, this->int64_value(), output); - } - - // float float_value = 3; - if (has_float_value()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteFloat(3, this->float_value(), output); - } - - // double double_value = 4; - if (has_double_value()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteDouble(4, this->double_value(), output); - } - - // string string_value = 5; - if (has_string_value()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->string_value().data(), static_cast(this->string_value().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.FieldValue.string_value"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 5, this->string_value(), output); - } - - // bool bool_value = 6; - if (has_bool_value()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBool(6, this->bool_value(), output); - } - - // .milvus.grpc.VectorFieldValue vector_value = 7; - if (has_vector_value()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 7, _Internal::vector_value(this), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.FieldValue) -} - -::PROTOBUF_NAMESPACE_ID::uint8* FieldValue::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.FieldValue) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // int32 int32_value = 1; - if (has_int32_value()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(1, this->int32_value(), target); - } - - // int64 int64_value = 2; - if (has_int64_value()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64ToArray(2, this->int64_value(), target); - } - - // float float_value = 3; - if (has_float_value()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteFloatToArray(3, this->float_value(), target); - } - - // double double_value = 4; - if (has_double_value()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteDoubleToArray(4, this->double_value(), target); - } - - // string string_value = 5; - if (has_string_value()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->string_value().data(), static_cast(this->string_value().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.FieldValue.string_value"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 5, this->string_value(), target); - } - - // bool bool_value = 6; - if (has_bool_value()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(6, this->bool_value(), target); - } - - // .milvus.grpc.VectorFieldValue vector_value = 7; - if (has_vector_value()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 7, _Internal::vector_value(this), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.FieldValue) - return target; -} - -size_t FieldValue::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.FieldValue) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - switch (value_case()) { - // int32 int32_value = 1; - case kInt32Value: { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( - this->int32_value()); - break; - } - // int64 int64_value = 2; - case kInt64Value: { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int64Size( - this->int64_value()); - break; - } - // float float_value = 3; - case kFloatValue: { - total_size += 1 + 4; - break; - } - // double double_value = 4; - case kDoubleValue: { - total_size += 1 + 8; - break; - } - // string string_value = 5; - case kStringValue: { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->string_value()); - break; - } - // bool bool_value = 6; - case kBoolValue: { - total_size += 1 + 1; - break; - } - // .milvus.grpc.VectorFieldValue vector_value = 7; - case kVectorValue: { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *value_.vector_value_); - break; - } - case VALUE_NOT_SET: { - break; - } - } - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void FieldValue::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.FieldValue) - GOOGLE_DCHECK_NE(&from, this); - const FieldValue* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.FieldValue) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.FieldValue) - MergeFrom(*source); - } -} - -void FieldValue::MergeFrom(const FieldValue& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.FieldValue) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - switch (from.value_case()) { - case kInt32Value: { - set_int32_value(from.int32_value()); - break; - } - case kInt64Value: { - set_int64_value(from.int64_value()); - break; - } - case kFloatValue: { - set_float_value(from.float_value()); - break; - } - case kDoubleValue: { - set_double_value(from.double_value()); - break; - } - case kStringValue: { - set_string_value(from.string_value()); - break; - } - case kBoolValue: { - set_bool_value(from.bool_value()); - break; - } - case kVectorValue: { - mutable_vector_value()->::milvus::grpc::VectorFieldValue::MergeFrom(from.vector_value()); - break; - } - case VALUE_NOT_SET: { - break; - } - } -} - -void FieldValue::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.FieldValue) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void FieldValue::CopyFrom(const FieldValue& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.FieldValue) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool FieldValue::IsInitialized() const { - return true; -} - -void FieldValue::InternalSwap(FieldValue* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - swap(value_, other->value_); - swap(_oneof_case_[0], other->_oneof_case_[0]); -} - -::PROTOBUF_NAMESPACE_ID::Metadata FieldValue::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void Mapping::InitAsDefaultInstance() { - ::milvus::grpc::_Mapping_default_instance_._instance.get_mutable()->status_ = const_cast< ::milvus::grpc::Status*>( - ::milvus::grpc::Status::internal_default_instance()); -} -class Mapping::_Internal { - public: - static const ::milvus::grpc::Status& status(const Mapping* msg); -}; - -const ::milvus::grpc::Status& -Mapping::_Internal::status(const Mapping* msg) { - return *msg->status_; -} -void Mapping::clear_status() { - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; -} -Mapping::Mapping() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.Mapping) -} -Mapping::Mapping(const Mapping& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - fields_(from.fields_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.collection_name().empty()) { - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - if (from.has_status()) { - status_ = new ::milvus::grpc::Status(*from.status_); - } else { - status_ = nullptr; - } - collection_id_ = from.collection_id_; - // @@protoc_insertion_point(copy_constructor:milvus.grpc.Mapping) -} - -void Mapping::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_Mapping_milvus_2eproto.base); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - ::memset(&status_, 0, static_cast( - reinterpret_cast(&collection_id_) - - reinterpret_cast(&status_)) + sizeof(collection_id_)); -} - -Mapping::~Mapping() { - // @@protoc_insertion_point(destructor:milvus.grpc.Mapping) - SharedDtor(); -} - -void Mapping::SharedDtor() { - collection_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (this != internal_default_instance()) delete status_; -} - -void Mapping::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const Mapping& Mapping::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_Mapping_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void Mapping::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.Mapping) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - fields_.Clear(); - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; - collection_id_ = PROTOBUF_ULONGLONG(0); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* Mapping::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.Status status = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ctx->ParseMessage(mutable_status(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // uint64 collection_id = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) { - collection_id_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // string collection_name = 3; - case 3: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_collection_name(), ptr, ctx, "milvus.grpc.Mapping.collection_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.FieldParam fields = 4; - case 4: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 34)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_fields(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 34); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool Mapping::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.Mapping) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.Status status = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_status())); - } else { - goto handle_unusual; - } - break; - } - - // uint64 collection_id = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (16 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::uint64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_UINT64>( - input, &collection_id_))); - } else { - goto handle_unusual; - } - break; - } - - // string collection_name = 3; - case 3: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (26 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_collection_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.Mapping.collection_name")); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.FieldParam fields = 4; - case 4: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (34 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_fields())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.Mapping) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.Mapping) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void Mapping::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.Mapping) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 1, _Internal::status(this), output); - } - - // uint64 collection_id = 2; - if (this->collection_id() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt64(2, this->collection_id(), output); - } - - // string collection_name = 3; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.Mapping.collection_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 3, this->collection_name(), output); - } - - // repeated .milvus.grpc.FieldParam fields = 4; - for (unsigned int i = 0, - n = static_cast(this->fields_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 4, - this->fields(static_cast(i)), - output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.Mapping) -} - -::PROTOBUF_NAMESPACE_ID::uint8* Mapping::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.Mapping) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 1, _Internal::status(this), target); - } - - // uint64 collection_id = 2; - if (this->collection_id() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt64ToArray(2, this->collection_id(), target); - } - - // string collection_name = 3; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.Mapping.collection_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 3, this->collection_name(), target); - } - - // repeated .milvus.grpc.FieldParam fields = 4; - for (unsigned int i = 0, - n = static_cast(this->fields_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 4, this->fields(static_cast(i)), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.Mapping) - return target; -} - -size_t Mapping::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.Mapping) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated .milvus.grpc.FieldParam fields = 4; - { - unsigned int count = static_cast(this->fields_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->fields(static_cast(i))); - } - } - - // string collection_name = 3; - if (this->collection_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_name()); - } - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *status_); - } - - // uint64 collection_id = 2; - if (this->collection_id() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::UInt64Size( - this->collection_id()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void Mapping::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.Mapping) - GOOGLE_DCHECK_NE(&from, this); - const Mapping* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.Mapping) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.Mapping) - MergeFrom(*source); - } -} - -void Mapping::MergeFrom(const Mapping& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.Mapping) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - fields_.MergeFrom(from.fields_); - if (from.collection_name().size() > 0) { - - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - if (from.has_status()) { - mutable_status()->::milvus::grpc::Status::MergeFrom(from.status()); - } - if (from.collection_id() != 0) { - set_collection_id(from.collection_id()); - } -} - -void Mapping::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.Mapping) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void Mapping::CopyFrom(const Mapping& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.Mapping) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool Mapping::IsInitialized() const { - return true; -} - -void Mapping::InternalSwap(Mapping* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - CastToBase(&fields_)->InternalSwap(CastToBase(&other->fields_)); - collection_name_.Swap(&other->collection_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - swap(status_, other->status_); - swap(collection_id_, other->collection_id_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata Mapping::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void MappingList::InitAsDefaultInstance() { - ::milvus::grpc::_MappingList_default_instance_._instance.get_mutable()->status_ = const_cast< ::milvus::grpc::Status*>( - ::milvus::grpc::Status::internal_default_instance()); -} -class MappingList::_Internal { - public: - static const ::milvus::grpc::Status& status(const MappingList* msg); -}; - -const ::milvus::grpc::Status& -MappingList::_Internal::status(const MappingList* msg) { - return *msg->status_; -} -void MappingList::clear_status() { - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; -} -MappingList::MappingList() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.MappingList) -} -MappingList::MappingList(const MappingList& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - mapping_list_(from.mapping_list_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - if (from.has_status()) { - status_ = new ::milvus::grpc::Status(*from.status_); - } else { - status_ = nullptr; - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.MappingList) -} - -void MappingList::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_MappingList_milvus_2eproto.base); - status_ = nullptr; -} - -MappingList::~MappingList() { - // @@protoc_insertion_point(destructor:milvus.grpc.MappingList) - SharedDtor(); -} - -void MappingList::SharedDtor() { - if (this != internal_default_instance()) delete status_; -} - -void MappingList::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const MappingList& MappingList::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_MappingList_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void MappingList::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.MappingList) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - mapping_list_.Clear(); - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* MappingList::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.Status status = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ctx->ParseMessage(mutable_status(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.Mapping mapping_list = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_mapping_list(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 18); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool MappingList::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.MappingList) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.Status status = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_status())); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.Mapping mapping_list = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_mapping_list())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.MappingList) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.MappingList) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void MappingList::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.MappingList) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 1, _Internal::status(this), output); - } - - // repeated .milvus.grpc.Mapping mapping_list = 2; - for (unsigned int i = 0, - n = static_cast(this->mapping_list_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 2, - this->mapping_list(static_cast(i)), - output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.MappingList) -} - -::PROTOBUF_NAMESPACE_ID::uint8* MappingList::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.MappingList) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 1, _Internal::status(this), target); - } - - // repeated .milvus.grpc.Mapping mapping_list = 2; - for (unsigned int i = 0, - n = static_cast(this->mapping_list_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 2, this->mapping_list(static_cast(i)), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.MappingList) - return target; -} - -size_t MappingList::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.MappingList) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated .milvus.grpc.Mapping mapping_list = 2; - { - unsigned int count = static_cast(this->mapping_list_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->mapping_list(static_cast(i))); - } - } - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *status_); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void MappingList::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.MappingList) - GOOGLE_DCHECK_NE(&from, this); - const MappingList* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.MappingList) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.MappingList) - MergeFrom(*source); - } -} - -void MappingList::MergeFrom(const MappingList& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.MappingList) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - mapping_list_.MergeFrom(from.mapping_list_); - if (from.has_status()) { - mutable_status()->::milvus::grpc::Status::MergeFrom(from.status()); - } -} - -void MappingList::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.MappingList) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void MappingList::CopyFrom(const MappingList& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.MappingList) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool MappingList::IsInitialized() const { - return true; -} - -void MappingList::InternalSwap(MappingList* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - CastToBase(&mapping_list_)->InternalSwap(CastToBase(&other->mapping_list_)); - swap(status_, other->status_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata MappingList::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void TermQuery::InitAsDefaultInstance() { -} -class TermQuery::_Internal { - public: -}; - -TermQuery::TermQuery() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.TermQuery) -} -TermQuery::TermQuery(const TermQuery& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - extra_params_(from.extra_params_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - field_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.field_name().empty()) { - field_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.field_name_); - } - values_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.values().empty()) { - values_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.values_); - } - ::memcpy(&value_num_, &from.value_num_, - static_cast(reinterpret_cast(&boost_) - - reinterpret_cast(&value_num_)) + sizeof(boost_)); - // @@protoc_insertion_point(copy_constructor:milvus.grpc.TermQuery) -} - -void TermQuery::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_TermQuery_milvus_2eproto.base); - field_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - values_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - ::memset(&value_num_, 0, static_cast( - reinterpret_cast(&boost_) - - reinterpret_cast(&value_num_)) + sizeof(boost_)); -} - -TermQuery::~TermQuery() { - // @@protoc_insertion_point(destructor:milvus.grpc.TermQuery) - SharedDtor(); -} - -void TermQuery::SharedDtor() { - field_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - values_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void TermQuery::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const TermQuery& TermQuery::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_TermQuery_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void TermQuery::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.TermQuery) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - extra_params_.Clear(); - field_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - values_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - ::memset(&value_num_, 0, static_cast( - reinterpret_cast(&boost_) - - reinterpret_cast(&value_num_)) + sizeof(boost_)); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* TermQuery::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // string field_name = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_field_name(), ptr, ctx, "milvus.grpc.TermQuery.field_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // bytes values = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(mutable_values(), ptr, ctx); - CHK_(ptr); - } else goto handle_unusual; - continue; - // int64 value_num = 3; - case 3: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 24)) { - value_num_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // float boost = 4; - case 4: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 37)) { - boost_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr); - ptr += sizeof(float); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - case 5: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 42)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_extra_params(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 42); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool TermQuery::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.TermQuery) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // string field_name = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_field_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->field_name().data(), static_cast(this->field_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.TermQuery.field_name")); - } else { - goto handle_unusual; - } - break; - } - - // bytes values = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadBytes( - input, this->mutable_values())); - } else { - goto handle_unusual; - } - break; - } - - // int64 value_num = 3; - case 3: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (24 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, &value_num_))); - } else { - goto handle_unusual; - } - break; - } - - // float boost = 4; - case 4: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (37 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - float, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_FLOAT>( - input, &boost_))); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - case 5: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (42 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_extra_params())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.TermQuery) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.TermQuery) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void TermQuery::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.TermQuery) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string field_name = 1; - if (this->field_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->field_name().data(), static_cast(this->field_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.TermQuery.field_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 1, this->field_name(), output); - } - - // bytes values = 2; - if (this->values().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBytesMaybeAliased( - 2, this->values(), output); - } - - // int64 value_num = 3; - if (this->value_num() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64(3, this->value_num(), output); - } - - // float boost = 4; - if (!(this->boost() <= 0 && this->boost() >= 0)) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteFloat(4, this->boost(), output); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 5, - this->extra_params(static_cast(i)), - output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.TermQuery) -} - -::PROTOBUF_NAMESPACE_ID::uint8* TermQuery::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.TermQuery) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string field_name = 1; - if (this->field_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->field_name().data(), static_cast(this->field_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.TermQuery.field_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 1, this->field_name(), target); - } - - // bytes values = 2; - if (this->values().size() > 0) { - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBytesToArray( - 2, this->values(), target); - } - - // int64 value_num = 3; - if (this->value_num() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64ToArray(3, this->value_num(), target); - } - - // float boost = 4; - if (!(this->boost() <= 0 && this->boost() >= 0)) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteFloatToArray(4, this->boost(), target); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 5, this->extra_params(static_cast(i)), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.TermQuery) - return target; -} - -size_t TermQuery::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.TermQuery) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - { - unsigned int count = static_cast(this->extra_params_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->extra_params(static_cast(i))); - } - } - - // string field_name = 1; - if (this->field_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->field_name()); - } - - // bytes values = 2; - if (this->values().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize( - this->values()); - } - - // int64 value_num = 3; - if (this->value_num() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int64Size( - this->value_num()); - } - - // float boost = 4; - if (!(this->boost() <= 0 && this->boost() >= 0)) { - total_size += 1 + 4; - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void TermQuery::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.TermQuery) - GOOGLE_DCHECK_NE(&from, this); - const TermQuery* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.TermQuery) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.TermQuery) - MergeFrom(*source); - } -} - -void TermQuery::MergeFrom(const TermQuery& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.TermQuery) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - extra_params_.MergeFrom(from.extra_params_); - if (from.field_name().size() > 0) { - - field_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.field_name_); - } - if (from.values().size() > 0) { - - values_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.values_); - } - if (from.value_num() != 0) { - set_value_num(from.value_num()); - } - if (!(from.boost() <= 0 && from.boost() >= 0)) { - set_boost(from.boost()); - } -} - -void TermQuery::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.TermQuery) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void TermQuery::CopyFrom(const TermQuery& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.TermQuery) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool TermQuery::IsInitialized() const { - return true; -} - -void TermQuery::InternalSwap(TermQuery* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - CastToBase(&extra_params_)->InternalSwap(CastToBase(&other->extra_params_)); - field_name_.Swap(&other->field_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - values_.Swap(&other->values_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - swap(value_num_, other->value_num_); - swap(boost_, other->boost_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata TermQuery::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void CompareExpr::InitAsDefaultInstance() { -} -class CompareExpr::_Internal { - public: -}; - -CompareExpr::CompareExpr() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.CompareExpr) -} -CompareExpr::CompareExpr(const CompareExpr& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - operand_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.operand().empty()) { - operand_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.operand_); - } - operator__ = from.operator__; - // @@protoc_insertion_point(copy_constructor:milvus.grpc.CompareExpr) -} - -void CompareExpr::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_CompareExpr_milvus_2eproto.base); - operand_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - operator__ = 0; -} - -CompareExpr::~CompareExpr() { - // @@protoc_insertion_point(destructor:milvus.grpc.CompareExpr) - SharedDtor(); -} - -void CompareExpr::SharedDtor() { - operand_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void CompareExpr::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const CompareExpr& CompareExpr::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_CompareExpr_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void CompareExpr::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.CompareExpr) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - operand_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - operator__ = 0; - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* CompareExpr::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.CompareOperator operator = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 8)) { - ::PROTOBUF_NAMESPACE_ID::uint64 val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - set_operator_(static_cast<::milvus::grpc::CompareOperator>(val)); - } else goto handle_unusual; - continue; - // string operand = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_operand(), ptr, ctx, "milvus.grpc.CompareExpr.operand"); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool CompareExpr::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.CompareExpr) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.CompareOperator operator = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (8 & 0xFF)) { - int value = 0; - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - int, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_ENUM>( - input, &value))); - set_operator_(static_cast< ::milvus::grpc::CompareOperator >(value)); - } else { - goto handle_unusual; - } - break; - } - - // string operand = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_operand())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->operand().data(), static_cast(this->operand().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.CompareExpr.operand")); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.CompareExpr) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.CompareExpr) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void CompareExpr::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.CompareExpr) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.CompareOperator operator = 1; - if (this->operator_() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnum( - 1, this->operator_(), output); - } - - // string operand = 2; - if (this->operand().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->operand().data(), static_cast(this->operand().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.CompareExpr.operand"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 2, this->operand(), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.CompareExpr) -} - -::PROTOBUF_NAMESPACE_ID::uint8* CompareExpr::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.CompareExpr) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.CompareOperator operator = 1; - if (this->operator_() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray( - 1, this->operator_(), target); - } - - // string operand = 2; - if (this->operand().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->operand().data(), static_cast(this->operand().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.CompareExpr.operand"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 2, this->operand(), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.CompareExpr) - return target; -} - -size_t CompareExpr::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.CompareExpr) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // string operand = 2; - if (this->operand().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->operand()); - } - - // .milvus.grpc.CompareOperator operator = 1; - if (this->operator_() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->operator_()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void CompareExpr::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.CompareExpr) - GOOGLE_DCHECK_NE(&from, this); - const CompareExpr* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.CompareExpr) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.CompareExpr) - MergeFrom(*source); - } -} - -void CompareExpr::MergeFrom(const CompareExpr& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.CompareExpr) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - if (from.operand().size() > 0) { - - operand_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.operand_); - } - if (from.operator_() != 0) { - set_operator_(from.operator_()); - } -} - -void CompareExpr::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.CompareExpr) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void CompareExpr::CopyFrom(const CompareExpr& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.CompareExpr) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool CompareExpr::IsInitialized() const { - return true; -} - -void CompareExpr::InternalSwap(CompareExpr* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - operand_.Swap(&other->operand_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - swap(operator__, other->operator__); -} - -::PROTOBUF_NAMESPACE_ID::Metadata CompareExpr::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void RangeQuery::InitAsDefaultInstance() { -} -class RangeQuery::_Internal { - public: -}; - -RangeQuery::RangeQuery() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.RangeQuery) -} -RangeQuery::RangeQuery(const RangeQuery& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - operand_(from.operand_), - extra_params_(from.extra_params_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - field_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.field_name().empty()) { - field_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.field_name_); - } - boost_ = from.boost_; - // @@protoc_insertion_point(copy_constructor:milvus.grpc.RangeQuery) -} - -void RangeQuery::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_RangeQuery_milvus_2eproto.base); - field_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - boost_ = 0; -} - -RangeQuery::~RangeQuery() { - // @@protoc_insertion_point(destructor:milvus.grpc.RangeQuery) - SharedDtor(); -} - -void RangeQuery::SharedDtor() { - field_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void RangeQuery::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const RangeQuery& RangeQuery::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_RangeQuery_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void RangeQuery::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.RangeQuery) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - operand_.Clear(); - extra_params_.Clear(); - field_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - boost_ = 0; - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* RangeQuery::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // string field_name = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_field_name(), ptr, ctx, "milvus.grpc.RangeQuery.field_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.CompareExpr operand = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_operand(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 18); - } else goto handle_unusual; - continue; - // float boost = 3; - case 3: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 29)) { - boost_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr); - ptr += sizeof(float); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - case 4: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 34)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_extra_params(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 34); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool RangeQuery::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.RangeQuery) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // string field_name = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_field_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->field_name().data(), static_cast(this->field_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.RangeQuery.field_name")); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.CompareExpr operand = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_operand())); - } else { - goto handle_unusual; - } - break; - } - - // float boost = 3; - case 3: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (29 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - float, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_FLOAT>( - input, &boost_))); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - case 4: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (34 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_extra_params())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.RangeQuery) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.RangeQuery) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void RangeQuery::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.RangeQuery) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string field_name = 1; - if (this->field_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->field_name().data(), static_cast(this->field_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.RangeQuery.field_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 1, this->field_name(), output); - } - - // repeated .milvus.grpc.CompareExpr operand = 2; - for (unsigned int i = 0, - n = static_cast(this->operand_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 2, - this->operand(static_cast(i)), - output); - } - - // float boost = 3; - if (!(this->boost() <= 0 && this->boost() >= 0)) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteFloat(3, this->boost(), output); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 4, - this->extra_params(static_cast(i)), - output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.RangeQuery) -} - -::PROTOBUF_NAMESPACE_ID::uint8* RangeQuery::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.RangeQuery) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string field_name = 1; - if (this->field_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->field_name().data(), static_cast(this->field_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.RangeQuery.field_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 1, this->field_name(), target); - } - - // repeated .milvus.grpc.CompareExpr operand = 2; - for (unsigned int i = 0, - n = static_cast(this->operand_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 2, this->operand(static_cast(i)), target); - } - - // float boost = 3; - if (!(this->boost() <= 0 && this->boost() >= 0)) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteFloatToArray(3, this->boost(), target); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 4, this->extra_params(static_cast(i)), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.RangeQuery) - return target; -} - -size_t RangeQuery::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.RangeQuery) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated .milvus.grpc.CompareExpr operand = 2; - { - unsigned int count = static_cast(this->operand_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->operand(static_cast(i))); - } - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - { - unsigned int count = static_cast(this->extra_params_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->extra_params(static_cast(i))); - } - } - - // string field_name = 1; - if (this->field_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->field_name()); - } - - // float boost = 3; - if (!(this->boost() <= 0 && this->boost() >= 0)) { - total_size += 1 + 4; - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void RangeQuery::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.RangeQuery) - GOOGLE_DCHECK_NE(&from, this); - const RangeQuery* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.RangeQuery) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.RangeQuery) - MergeFrom(*source); - } -} - -void RangeQuery::MergeFrom(const RangeQuery& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.RangeQuery) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - operand_.MergeFrom(from.operand_); - extra_params_.MergeFrom(from.extra_params_); - if (from.field_name().size() > 0) { - - field_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.field_name_); - } - if (!(from.boost() <= 0 && from.boost() >= 0)) { - set_boost(from.boost()); - } -} - -void RangeQuery::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.RangeQuery) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void RangeQuery::CopyFrom(const RangeQuery& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.RangeQuery) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool RangeQuery::IsInitialized() const { - return true; -} - -void RangeQuery::InternalSwap(RangeQuery* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - CastToBase(&operand_)->InternalSwap(CastToBase(&other->operand_)); - CastToBase(&extra_params_)->InternalSwap(CastToBase(&other->extra_params_)); - field_name_.Swap(&other->field_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - swap(boost_, other->boost_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata RangeQuery::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void VectorQuery::InitAsDefaultInstance() { -} -class VectorQuery::_Internal { - public: -}; - -VectorQuery::VectorQuery() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.VectorQuery) -} -VectorQuery::VectorQuery(const VectorQuery& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - records_(from.records_), - extra_params_(from.extra_params_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - field_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.field_name().empty()) { - field_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.field_name_); - } - ::memcpy(&topk_, &from.topk_, - static_cast(reinterpret_cast(&query_boost_) - - reinterpret_cast(&topk_)) + sizeof(query_boost_)); - // @@protoc_insertion_point(copy_constructor:milvus.grpc.VectorQuery) -} - -void VectorQuery::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_VectorQuery_milvus_2eproto.base); - field_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - ::memset(&topk_, 0, static_cast( - reinterpret_cast(&query_boost_) - - reinterpret_cast(&topk_)) + sizeof(query_boost_)); -} - -VectorQuery::~VectorQuery() { - // @@protoc_insertion_point(destructor:milvus.grpc.VectorQuery) - SharedDtor(); -} - -void VectorQuery::SharedDtor() { - field_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void VectorQuery::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const VectorQuery& VectorQuery::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_VectorQuery_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void VectorQuery::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.VectorQuery) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - records_.Clear(); - extra_params_.Clear(); - field_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - ::memset(&topk_, 0, static_cast( - reinterpret_cast(&query_boost_) - - reinterpret_cast(&topk_)) + sizeof(query_boost_)); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* VectorQuery::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // string field_name = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_field_name(), ptr, ctx, "milvus.grpc.VectorQuery.field_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // float query_boost = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 21)) { - query_boost_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr); - ptr += sizeof(float); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.RowRecord records = 3; - case 3: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_records(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 26); - } else goto handle_unusual; - continue; - // int64 topk = 4; - case 4: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 32)) { - topk_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - case 5: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 42)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_extra_params(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 42); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool VectorQuery::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.VectorQuery) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // string field_name = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_field_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->field_name().data(), static_cast(this->field_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.VectorQuery.field_name")); - } else { - goto handle_unusual; - } - break; - } - - // float query_boost = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (21 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - float, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_FLOAT>( - input, &query_boost_))); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.RowRecord records = 3; - case 3: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (26 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_records())); - } else { - goto handle_unusual; - } - break; - } - - // int64 topk = 4; - case 4: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (32 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, &topk_))); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - case 5: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (42 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_extra_params())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.VectorQuery) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.VectorQuery) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void VectorQuery::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.VectorQuery) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string field_name = 1; - if (this->field_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->field_name().data(), static_cast(this->field_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.VectorQuery.field_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 1, this->field_name(), output); - } - - // float query_boost = 2; - if (!(this->query_boost() <= 0 && this->query_boost() >= 0)) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteFloat(2, this->query_boost(), output); - } - - // repeated .milvus.grpc.RowRecord records = 3; - for (unsigned int i = 0, - n = static_cast(this->records_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 3, - this->records(static_cast(i)), - output); - } - - // int64 topk = 4; - if (this->topk() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64(4, this->topk(), output); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 5, - this->extra_params(static_cast(i)), - output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.VectorQuery) -} - -::PROTOBUF_NAMESPACE_ID::uint8* VectorQuery::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.VectorQuery) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string field_name = 1; - if (this->field_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->field_name().data(), static_cast(this->field_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.VectorQuery.field_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 1, this->field_name(), target); - } - - // float query_boost = 2; - if (!(this->query_boost() <= 0 && this->query_boost() >= 0)) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteFloatToArray(2, this->query_boost(), target); - } - - // repeated .milvus.grpc.RowRecord records = 3; - for (unsigned int i = 0, - n = static_cast(this->records_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 3, this->records(static_cast(i)), target); - } - - // int64 topk = 4; - if (this->topk() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64ToArray(4, this->topk(), target); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 5, this->extra_params(static_cast(i)), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.VectorQuery) - return target; -} - -size_t VectorQuery::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.VectorQuery) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated .milvus.grpc.RowRecord records = 3; - { - unsigned int count = static_cast(this->records_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->records(static_cast(i))); - } - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - { - unsigned int count = static_cast(this->extra_params_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->extra_params(static_cast(i))); - } - } - - // string field_name = 1; - if (this->field_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->field_name()); - } - - // int64 topk = 4; - if (this->topk() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int64Size( - this->topk()); - } - - // float query_boost = 2; - if (!(this->query_boost() <= 0 && this->query_boost() >= 0)) { - total_size += 1 + 4; - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void VectorQuery::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.VectorQuery) - GOOGLE_DCHECK_NE(&from, this); - const VectorQuery* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.VectorQuery) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.VectorQuery) - MergeFrom(*source); - } -} - -void VectorQuery::MergeFrom(const VectorQuery& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.VectorQuery) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - records_.MergeFrom(from.records_); - extra_params_.MergeFrom(from.extra_params_); - if (from.field_name().size() > 0) { - - field_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.field_name_); - } - if (from.topk() != 0) { - set_topk(from.topk()); - } - if (!(from.query_boost() <= 0 && from.query_boost() >= 0)) { - set_query_boost(from.query_boost()); - } -} - -void VectorQuery::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.VectorQuery) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void VectorQuery::CopyFrom(const VectorQuery& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.VectorQuery) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool VectorQuery::IsInitialized() const { - return true; -} - -void VectorQuery::InternalSwap(VectorQuery* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - CastToBase(&records_)->InternalSwap(CastToBase(&other->records_)); - CastToBase(&extra_params_)->InternalSwap(CastToBase(&other->extra_params_)); - field_name_.Swap(&other->field_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - swap(topk_, other->topk_); - swap(query_boost_, other->query_boost_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata VectorQuery::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void BooleanQuery::InitAsDefaultInstance() { -} -class BooleanQuery::_Internal { - public: -}; - -BooleanQuery::BooleanQuery() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.BooleanQuery) -} -BooleanQuery::BooleanQuery(const BooleanQuery& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - general_query_(from.general_query_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - occur_ = from.occur_; - // @@protoc_insertion_point(copy_constructor:milvus.grpc.BooleanQuery) -} - -void BooleanQuery::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_BooleanQuery_milvus_2eproto.base); - occur_ = 0; -} - -BooleanQuery::~BooleanQuery() { - // @@protoc_insertion_point(destructor:milvus.grpc.BooleanQuery) - SharedDtor(); -} - -void BooleanQuery::SharedDtor() { -} - -void BooleanQuery::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const BooleanQuery& BooleanQuery::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_BooleanQuery_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void BooleanQuery::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.BooleanQuery) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - general_query_.Clear(); - occur_ = 0; - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* BooleanQuery::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.Occur occur = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 8)) { - ::PROTOBUF_NAMESPACE_ID::uint64 val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - set_occur(static_cast<::milvus::grpc::Occur>(val)); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.GeneralQuery general_query = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_general_query(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 18); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool BooleanQuery::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.BooleanQuery) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.Occur occur = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (8 & 0xFF)) { - int value = 0; - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - int, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_ENUM>( - input, &value))); - set_occur(static_cast< ::milvus::grpc::Occur >(value)); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.GeneralQuery general_query = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_general_query())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.BooleanQuery) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.BooleanQuery) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void BooleanQuery::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.BooleanQuery) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Occur occur = 1; - if (this->occur() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnum( - 1, this->occur(), output); - } - - // repeated .milvus.grpc.GeneralQuery general_query = 2; - for (unsigned int i = 0, - n = static_cast(this->general_query_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 2, - this->general_query(static_cast(i)), - output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.BooleanQuery) -} - -::PROTOBUF_NAMESPACE_ID::uint8* BooleanQuery::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.BooleanQuery) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Occur occur = 1; - if (this->occur() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray( - 1, this->occur(), target); - } - - // repeated .milvus.grpc.GeneralQuery general_query = 2; - for (unsigned int i = 0, - n = static_cast(this->general_query_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 2, this->general_query(static_cast(i)), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.BooleanQuery) - return target; -} - -size_t BooleanQuery::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.BooleanQuery) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated .milvus.grpc.GeneralQuery general_query = 2; - { - unsigned int count = static_cast(this->general_query_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->general_query(static_cast(i))); - } - } - - // .milvus.grpc.Occur occur = 1; - if (this->occur() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->occur()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void BooleanQuery::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.BooleanQuery) - GOOGLE_DCHECK_NE(&from, this); - const BooleanQuery* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.BooleanQuery) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.BooleanQuery) - MergeFrom(*source); - } -} - -void BooleanQuery::MergeFrom(const BooleanQuery& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.BooleanQuery) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - general_query_.MergeFrom(from.general_query_); - if (from.occur() != 0) { - set_occur(from.occur()); - } -} - -void BooleanQuery::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.BooleanQuery) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void BooleanQuery::CopyFrom(const BooleanQuery& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.BooleanQuery) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool BooleanQuery::IsInitialized() const { - return true; -} - -void BooleanQuery::InternalSwap(BooleanQuery* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - CastToBase(&general_query_)->InternalSwap(CastToBase(&other->general_query_)); - swap(occur_, other->occur_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata BooleanQuery::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void GeneralQuery::InitAsDefaultInstance() { - ::milvus::grpc::_GeneralQuery_default_instance_.boolean_query_ = const_cast< ::milvus::grpc::BooleanQuery*>( - ::milvus::grpc::BooleanQuery::internal_default_instance()); - ::milvus::grpc::_GeneralQuery_default_instance_.term_query_ = const_cast< ::milvus::grpc::TermQuery*>( - ::milvus::grpc::TermQuery::internal_default_instance()); - ::milvus::grpc::_GeneralQuery_default_instance_.range_query_ = const_cast< ::milvus::grpc::RangeQuery*>( - ::milvus::grpc::RangeQuery::internal_default_instance()); - ::milvus::grpc::_GeneralQuery_default_instance_.vector_query_ = const_cast< ::milvus::grpc::VectorQuery*>( - ::milvus::grpc::VectorQuery::internal_default_instance()); -} -class GeneralQuery::_Internal { - public: - static const ::milvus::grpc::BooleanQuery& boolean_query(const GeneralQuery* msg); - static const ::milvus::grpc::TermQuery& term_query(const GeneralQuery* msg); - static const ::milvus::grpc::RangeQuery& range_query(const GeneralQuery* msg); - static const ::milvus::grpc::VectorQuery& vector_query(const GeneralQuery* msg); -}; - -const ::milvus::grpc::BooleanQuery& -GeneralQuery::_Internal::boolean_query(const GeneralQuery* msg) { - return *msg->query_.boolean_query_; -} -const ::milvus::grpc::TermQuery& -GeneralQuery::_Internal::term_query(const GeneralQuery* msg) { - return *msg->query_.term_query_; -} -const ::milvus::grpc::RangeQuery& -GeneralQuery::_Internal::range_query(const GeneralQuery* msg) { - return *msg->query_.range_query_; -} -const ::milvus::grpc::VectorQuery& -GeneralQuery::_Internal::vector_query(const GeneralQuery* msg) { - return *msg->query_.vector_query_; -} -void GeneralQuery::set_allocated_boolean_query(::milvus::grpc::BooleanQuery* boolean_query) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - clear_query(); - if (boolean_query) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - boolean_query = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, boolean_query, submessage_arena); - } - set_has_boolean_query(); - query_.boolean_query_ = boolean_query; - } - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.GeneralQuery.boolean_query) -} -void GeneralQuery::set_allocated_term_query(::milvus::grpc::TermQuery* term_query) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - clear_query(); - if (term_query) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - term_query = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, term_query, submessage_arena); - } - set_has_term_query(); - query_.term_query_ = term_query; - } - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.GeneralQuery.term_query) -} -void GeneralQuery::set_allocated_range_query(::milvus::grpc::RangeQuery* range_query) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - clear_query(); - if (range_query) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - range_query = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, range_query, submessage_arena); - } - set_has_range_query(); - query_.range_query_ = range_query; - } - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.GeneralQuery.range_query) -} -void GeneralQuery::set_allocated_vector_query(::milvus::grpc::VectorQuery* vector_query) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - clear_query(); - if (vector_query) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - vector_query = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, vector_query, submessage_arena); - } - set_has_vector_query(); - query_.vector_query_ = vector_query; - } - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.GeneralQuery.vector_query) -} -GeneralQuery::GeneralQuery() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.GeneralQuery) -} -GeneralQuery::GeneralQuery(const GeneralQuery& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - clear_has_query(); - switch (from.query_case()) { - case kBooleanQuery: { - mutable_boolean_query()->::milvus::grpc::BooleanQuery::MergeFrom(from.boolean_query()); - break; - } - case kTermQuery: { - mutable_term_query()->::milvus::grpc::TermQuery::MergeFrom(from.term_query()); - break; - } - case kRangeQuery: { - mutable_range_query()->::milvus::grpc::RangeQuery::MergeFrom(from.range_query()); - break; - } - case kVectorQuery: { - mutable_vector_query()->::milvus::grpc::VectorQuery::MergeFrom(from.vector_query()); - break; - } - case QUERY_NOT_SET: { - break; - } - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.GeneralQuery) -} - -void GeneralQuery::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_BooleanQuery_milvus_2eproto.base); - clear_has_query(); -} - -GeneralQuery::~GeneralQuery() { - // @@protoc_insertion_point(destructor:milvus.grpc.GeneralQuery) - SharedDtor(); -} - -void GeneralQuery::SharedDtor() { - if (has_query()) { - clear_query(); - } -} - -void GeneralQuery::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const GeneralQuery& GeneralQuery::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_BooleanQuery_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void GeneralQuery::clear_query() { -// @@protoc_insertion_point(one_of_clear_start:milvus.grpc.GeneralQuery) - switch (query_case()) { - case kBooleanQuery: { - delete query_.boolean_query_; - break; - } - case kTermQuery: { - delete query_.term_query_; - break; - } - case kRangeQuery: { - delete query_.range_query_; - break; - } - case kVectorQuery: { - delete query_.vector_query_; - break; - } - case QUERY_NOT_SET: { - break; - } - } - _oneof_case_[0] = QUERY_NOT_SET; -} - - -void GeneralQuery::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.GeneralQuery) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - clear_query(); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* GeneralQuery::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.BooleanQuery boolean_query = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ctx->ParseMessage(mutable_boolean_query(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // .milvus.grpc.TermQuery term_query = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ctx->ParseMessage(mutable_term_query(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // .milvus.grpc.RangeQuery range_query = 3; - case 3: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) { - ptr = ctx->ParseMessage(mutable_range_query(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // .milvus.grpc.VectorQuery vector_query = 4; - case 4: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 34)) { - ptr = ctx->ParseMessage(mutable_vector_query(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool GeneralQuery::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.GeneralQuery) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.BooleanQuery boolean_query = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_boolean_query())); - } else { - goto handle_unusual; - } - break; - } - - // .milvus.grpc.TermQuery term_query = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_term_query())); - } else { - goto handle_unusual; - } - break; - } - - // .milvus.grpc.RangeQuery range_query = 3; - case 3: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (26 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_range_query())); - } else { - goto handle_unusual; - } - break; - } - - // .milvus.grpc.VectorQuery vector_query = 4; - case 4: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (34 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_vector_query())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.GeneralQuery) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.GeneralQuery) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void GeneralQuery::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.GeneralQuery) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.BooleanQuery boolean_query = 1; - if (has_boolean_query()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 1, _Internal::boolean_query(this), output); - } - - // .milvus.grpc.TermQuery term_query = 2; - if (has_term_query()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 2, _Internal::term_query(this), output); - } - - // .milvus.grpc.RangeQuery range_query = 3; - if (has_range_query()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 3, _Internal::range_query(this), output); - } - - // .milvus.grpc.VectorQuery vector_query = 4; - if (has_vector_query()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 4, _Internal::vector_query(this), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.GeneralQuery) -} - -::PROTOBUF_NAMESPACE_ID::uint8* GeneralQuery::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.GeneralQuery) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.BooleanQuery boolean_query = 1; - if (has_boolean_query()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 1, _Internal::boolean_query(this), target); - } - - // .milvus.grpc.TermQuery term_query = 2; - if (has_term_query()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 2, _Internal::term_query(this), target); - } - - // .milvus.grpc.RangeQuery range_query = 3; - if (has_range_query()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 3, _Internal::range_query(this), target); - } - - // .milvus.grpc.VectorQuery vector_query = 4; - if (has_vector_query()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 4, _Internal::vector_query(this), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.GeneralQuery) - return target; -} - -size_t GeneralQuery::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.GeneralQuery) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - switch (query_case()) { - // .milvus.grpc.BooleanQuery boolean_query = 1; - case kBooleanQuery: { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *query_.boolean_query_); - break; - } - // .milvus.grpc.TermQuery term_query = 2; - case kTermQuery: { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *query_.term_query_); - break; - } - // .milvus.grpc.RangeQuery range_query = 3; - case kRangeQuery: { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *query_.range_query_); - break; - } - // .milvus.grpc.VectorQuery vector_query = 4; - case kVectorQuery: { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *query_.vector_query_); - break; - } - case QUERY_NOT_SET: { - break; - } - } - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void GeneralQuery::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.GeneralQuery) - GOOGLE_DCHECK_NE(&from, this); - const GeneralQuery* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.GeneralQuery) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.GeneralQuery) - MergeFrom(*source); - } -} - -void GeneralQuery::MergeFrom(const GeneralQuery& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.GeneralQuery) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - switch (from.query_case()) { - case kBooleanQuery: { - mutable_boolean_query()->::milvus::grpc::BooleanQuery::MergeFrom(from.boolean_query()); - break; - } - case kTermQuery: { - mutable_term_query()->::milvus::grpc::TermQuery::MergeFrom(from.term_query()); - break; - } - case kRangeQuery: { - mutable_range_query()->::milvus::grpc::RangeQuery::MergeFrom(from.range_query()); - break; - } - case kVectorQuery: { - mutable_vector_query()->::milvus::grpc::VectorQuery::MergeFrom(from.vector_query()); - break; - } - case QUERY_NOT_SET: { - break; - } - } -} - -void GeneralQuery::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.GeneralQuery) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void GeneralQuery::CopyFrom(const GeneralQuery& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.GeneralQuery) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool GeneralQuery::IsInitialized() const { - return true; -} - -void GeneralQuery::InternalSwap(GeneralQuery* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - swap(query_, other->query_); - swap(_oneof_case_[0], other->_oneof_case_[0]); -} - -::PROTOBUF_NAMESPACE_ID::Metadata GeneralQuery::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void HSearchParam::InitAsDefaultInstance() { - ::milvus::grpc::_HSearchParam_default_instance_._instance.get_mutable()->general_query_ = const_cast< ::milvus::grpc::GeneralQuery*>( - ::milvus::grpc::GeneralQuery::internal_default_instance()); -} -class HSearchParam::_Internal { - public: - static const ::milvus::grpc::GeneralQuery& general_query(const HSearchParam* msg); -}; - -const ::milvus::grpc::GeneralQuery& -HSearchParam::_Internal::general_query(const HSearchParam* msg) { - return *msg->general_query_; -} -HSearchParam::HSearchParam() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.HSearchParam) -} -HSearchParam::HSearchParam(const HSearchParam& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - partition_tag_array_(from.partition_tag_array_), - extra_params_(from.extra_params_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.collection_name().empty()) { - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - if (from.has_general_query()) { - general_query_ = new ::milvus::grpc::GeneralQuery(*from.general_query_); - } else { - general_query_ = nullptr; - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.HSearchParam) -} - -void HSearchParam::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_HSearchParam_milvus_2eproto.base); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - general_query_ = nullptr; -} - -HSearchParam::~HSearchParam() { - // @@protoc_insertion_point(destructor:milvus.grpc.HSearchParam) - SharedDtor(); -} - -void HSearchParam::SharedDtor() { - collection_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (this != internal_default_instance()) delete general_query_; -} - -void HSearchParam::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const HSearchParam& HSearchParam::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_HSearchParam_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void HSearchParam::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.HSearchParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - partition_tag_array_.Clear(); - extra_params_.Clear(); - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (GetArenaNoVirtual() == nullptr && general_query_ != nullptr) { - delete general_query_; - } - general_query_ = nullptr; - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* HSearchParam::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // string collection_name = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_collection_name(), ptr, ctx, "milvus.grpc.HSearchParam.collection_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated string partition_tag_array = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr -= 1; - do { - ptr += 1; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(add_partition_tag_array(), ptr, ctx, "milvus.grpc.HSearchParam.partition_tag_array"); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 18); - } else goto handle_unusual; - continue; - // .milvus.grpc.GeneralQuery general_query = 3; - case 3: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) { - ptr = ctx->ParseMessage(mutable_general_query(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - case 4: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 34)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_extra_params(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 34); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool HSearchParam::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.HSearchParam) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // string collection_name = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_collection_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.HSearchParam.collection_name")); - } else { - goto handle_unusual; - } - break; - } - - // repeated string partition_tag_array = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->add_partition_tag_array())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->partition_tag_array(this->partition_tag_array_size() - 1).data(), - static_cast(this->partition_tag_array(this->partition_tag_array_size() - 1).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.HSearchParam.partition_tag_array")); - } else { - goto handle_unusual; - } - break; - } - - // .milvus.grpc.GeneralQuery general_query = 3; - case 3: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (26 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_general_query())); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - case 4: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (34 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_extra_params())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.HSearchParam) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.HSearchParam) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void HSearchParam::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.HSearchParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HSearchParam.collection_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 1, this->collection_name(), output); - } - - // repeated string partition_tag_array = 2; - for (int i = 0, n = this->partition_tag_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->partition_tag_array(i).data(), static_cast(this->partition_tag_array(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HSearchParam.partition_tag_array"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteString( - 2, this->partition_tag_array(i), output); - } - - // .milvus.grpc.GeneralQuery general_query = 3; - if (this->has_general_query()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 3, _Internal::general_query(this), output); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 4, - this->extra_params(static_cast(i)), - output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.HSearchParam) -} - -::PROTOBUF_NAMESPACE_ID::uint8* HSearchParam::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.HSearchParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HSearchParam.collection_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 1, this->collection_name(), target); - } - - // repeated string partition_tag_array = 2; - for (int i = 0, n = this->partition_tag_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->partition_tag_array(i).data(), static_cast(this->partition_tag_array(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HSearchParam.partition_tag_array"); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteStringToArray(2, this->partition_tag_array(i), target); - } - - // .milvus.grpc.GeneralQuery general_query = 3; - if (this->has_general_query()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 3, _Internal::general_query(this), target); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 4, this->extra_params(static_cast(i)), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.HSearchParam) - return target; -} - -size_t HSearchParam::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.HSearchParam) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated string partition_tag_array = 2; - total_size += 1 * - ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(this->partition_tag_array_size()); - for (int i = 0, n = this->partition_tag_array_size(); i < n; i++) { - total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->partition_tag_array(i)); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - { - unsigned int count = static_cast(this->extra_params_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->extra_params(static_cast(i))); - } - } - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_name()); - } - - // .milvus.grpc.GeneralQuery general_query = 3; - if (this->has_general_query()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *general_query_); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void HSearchParam::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.HSearchParam) - GOOGLE_DCHECK_NE(&from, this); - const HSearchParam* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.HSearchParam) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.HSearchParam) - MergeFrom(*source); - } -} - -void HSearchParam::MergeFrom(const HSearchParam& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.HSearchParam) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - partition_tag_array_.MergeFrom(from.partition_tag_array_); - extra_params_.MergeFrom(from.extra_params_); - if (from.collection_name().size() > 0) { - - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - if (from.has_general_query()) { - mutable_general_query()->::milvus::grpc::GeneralQuery::MergeFrom(from.general_query()); - } -} - -void HSearchParam::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.HSearchParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void HSearchParam::CopyFrom(const HSearchParam& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.HSearchParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool HSearchParam::IsInitialized() const { - return true; -} - -void HSearchParam::InternalSwap(HSearchParam* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - partition_tag_array_.InternalSwap(CastToBase(&other->partition_tag_array_)); - CastToBase(&extra_params_)->InternalSwap(CastToBase(&other->extra_params_)); - collection_name_.Swap(&other->collection_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - swap(general_query_, other->general_query_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata HSearchParam::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void HSearchInSegmentsParam::InitAsDefaultInstance() { - ::milvus::grpc::_HSearchInSegmentsParam_default_instance_._instance.get_mutable()->search_param_ = const_cast< ::milvus::grpc::HSearchParam*>( - ::milvus::grpc::HSearchParam::internal_default_instance()); -} -class HSearchInSegmentsParam::_Internal { - public: - static const ::milvus::grpc::HSearchParam& search_param(const HSearchInSegmentsParam* msg); -}; - -const ::milvus::grpc::HSearchParam& -HSearchInSegmentsParam::_Internal::search_param(const HSearchInSegmentsParam* msg) { - return *msg->search_param_; -} -HSearchInSegmentsParam::HSearchInSegmentsParam() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.HSearchInSegmentsParam) -} -HSearchInSegmentsParam::HSearchInSegmentsParam(const HSearchInSegmentsParam& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - segment_id_array_(from.segment_id_array_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - if (from.has_search_param()) { - search_param_ = new ::milvus::grpc::HSearchParam(*from.search_param_); - } else { - search_param_ = nullptr; - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.HSearchInSegmentsParam) -} - -void HSearchInSegmentsParam::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_HSearchInSegmentsParam_milvus_2eproto.base); - search_param_ = nullptr; -} - -HSearchInSegmentsParam::~HSearchInSegmentsParam() { - // @@protoc_insertion_point(destructor:milvus.grpc.HSearchInSegmentsParam) - SharedDtor(); -} - -void HSearchInSegmentsParam::SharedDtor() { - if (this != internal_default_instance()) delete search_param_; -} - -void HSearchInSegmentsParam::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const HSearchInSegmentsParam& HSearchInSegmentsParam::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_HSearchInSegmentsParam_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void HSearchInSegmentsParam::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.HSearchInSegmentsParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - segment_id_array_.Clear(); - if (GetArenaNoVirtual() == nullptr && search_param_ != nullptr) { - delete search_param_; - } - search_param_ = nullptr; - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* HSearchInSegmentsParam::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // repeated string segment_id_array = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr -= 1; - do { - ptr += 1; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(add_segment_id_array(), ptr, ctx, "milvus.grpc.HSearchInSegmentsParam.segment_id_array"); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 10); - } else goto handle_unusual; - continue; - // .milvus.grpc.HSearchParam search_param = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ctx->ParseMessage(mutable_search_param(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool HSearchInSegmentsParam::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.HSearchInSegmentsParam) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // repeated string segment_id_array = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->add_segment_id_array())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->segment_id_array(this->segment_id_array_size() - 1).data(), - static_cast(this->segment_id_array(this->segment_id_array_size() - 1).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.HSearchInSegmentsParam.segment_id_array")); - } else { - goto handle_unusual; - } - break; - } - - // .milvus.grpc.HSearchParam search_param = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_search_param())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.HSearchInSegmentsParam) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.HSearchInSegmentsParam) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void HSearchInSegmentsParam::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.HSearchInSegmentsParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // repeated string segment_id_array = 1; - for (int i = 0, n = this->segment_id_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->segment_id_array(i).data(), static_cast(this->segment_id_array(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HSearchInSegmentsParam.segment_id_array"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteString( - 1, this->segment_id_array(i), output); - } - - // .milvus.grpc.HSearchParam search_param = 2; - if (this->has_search_param()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 2, _Internal::search_param(this), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.HSearchInSegmentsParam) -} - -::PROTOBUF_NAMESPACE_ID::uint8* HSearchInSegmentsParam::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.HSearchInSegmentsParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // repeated string segment_id_array = 1; - for (int i = 0, n = this->segment_id_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->segment_id_array(i).data(), static_cast(this->segment_id_array(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HSearchInSegmentsParam.segment_id_array"); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteStringToArray(1, this->segment_id_array(i), target); - } - - // .milvus.grpc.HSearchParam search_param = 2; - if (this->has_search_param()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 2, _Internal::search_param(this), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.HSearchInSegmentsParam) - return target; -} - -size_t HSearchInSegmentsParam::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.HSearchInSegmentsParam) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated string segment_id_array = 1; - total_size += 1 * - ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(this->segment_id_array_size()); - for (int i = 0, n = this->segment_id_array_size(); i < n; i++) { - total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->segment_id_array(i)); - } - - // .milvus.grpc.HSearchParam search_param = 2; - if (this->has_search_param()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *search_param_); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void HSearchInSegmentsParam::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.HSearchInSegmentsParam) - GOOGLE_DCHECK_NE(&from, this); - const HSearchInSegmentsParam* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.HSearchInSegmentsParam) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.HSearchInSegmentsParam) - MergeFrom(*source); - } -} - -void HSearchInSegmentsParam::MergeFrom(const HSearchInSegmentsParam& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.HSearchInSegmentsParam) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - segment_id_array_.MergeFrom(from.segment_id_array_); - if (from.has_search_param()) { - mutable_search_param()->::milvus::grpc::HSearchParam::MergeFrom(from.search_param()); - } -} - -void HSearchInSegmentsParam::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.HSearchInSegmentsParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void HSearchInSegmentsParam::CopyFrom(const HSearchInSegmentsParam& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.HSearchInSegmentsParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool HSearchInSegmentsParam::IsInitialized() const { - return true; -} - -void HSearchInSegmentsParam::InternalSwap(HSearchInSegmentsParam* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - segment_id_array_.InternalSwap(CastToBase(&other->segment_id_array_)); - swap(search_param_, other->search_param_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata HSearchInSegmentsParam::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void AttrRecord::InitAsDefaultInstance() { -} -class AttrRecord::_Internal { - public: -}; - -AttrRecord::AttrRecord() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.AttrRecord) -} -AttrRecord::AttrRecord(const AttrRecord& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - value_(from.value_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - // @@protoc_insertion_point(copy_constructor:milvus.grpc.AttrRecord) -} - -void AttrRecord::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_AttrRecord_milvus_2eproto.base); -} - -AttrRecord::~AttrRecord() { - // @@protoc_insertion_point(destructor:milvus.grpc.AttrRecord) - SharedDtor(); -} - -void AttrRecord::SharedDtor() { -} - -void AttrRecord::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const AttrRecord& AttrRecord::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_AttrRecord_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void AttrRecord::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.AttrRecord) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - value_.Clear(); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* AttrRecord::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // repeated string value = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr -= 1; - do { - ptr += 1; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(add_value(), ptr, ctx, "milvus.grpc.AttrRecord.value"); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 10); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool AttrRecord::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.AttrRecord) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // repeated string value = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->add_value())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->value(this->value_size() - 1).data(), - static_cast(this->value(this->value_size() - 1).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.AttrRecord.value")); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.AttrRecord) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.AttrRecord) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void AttrRecord::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.AttrRecord) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // repeated string value = 1; - for (int i = 0, n = this->value_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->value(i).data(), static_cast(this->value(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.AttrRecord.value"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteString( - 1, this->value(i), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.AttrRecord) -} - -::PROTOBUF_NAMESPACE_ID::uint8* AttrRecord::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.AttrRecord) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // repeated string value = 1; - for (int i = 0, n = this->value_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->value(i).data(), static_cast(this->value(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.AttrRecord.value"); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteStringToArray(1, this->value(i), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.AttrRecord) - return target; -} - -size_t AttrRecord::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.AttrRecord) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated string value = 1; - total_size += 1 * - ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(this->value_size()); - for (int i = 0, n = this->value_size(); i < n; i++) { - total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->value(i)); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void AttrRecord::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.AttrRecord) - GOOGLE_DCHECK_NE(&from, this); - const AttrRecord* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.AttrRecord) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.AttrRecord) - MergeFrom(*source); - } -} - -void AttrRecord::MergeFrom(const AttrRecord& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.AttrRecord) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - value_.MergeFrom(from.value_); -} - -void AttrRecord::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.AttrRecord) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void AttrRecord::CopyFrom(const AttrRecord& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.AttrRecord) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool AttrRecord::IsInitialized() const { - return true; -} - -void AttrRecord::InternalSwap(AttrRecord* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - value_.InternalSwap(CastToBase(&other->value_)); -} - -::PROTOBUF_NAMESPACE_ID::Metadata AttrRecord::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void HEntity::InitAsDefaultInstance() { - ::milvus::grpc::_HEntity_default_instance_._instance.get_mutable()->status_ = const_cast< ::milvus::grpc::Status*>( - ::milvus::grpc::Status::internal_default_instance()); -} -class HEntity::_Internal { - public: - static const ::milvus::grpc::Status& status(const HEntity* msg); -}; - -const ::milvus::grpc::Status& -HEntity::_Internal::status(const HEntity* msg) { - return *msg->status_; -} -void HEntity::clear_status() { - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; -} -HEntity::HEntity() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.HEntity) -} -HEntity::HEntity(const HEntity& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - field_names_(from.field_names_), - result_values_(from.result_values_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - attr_records_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.attr_records().empty()) { - attr_records_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.attr_records_); - } - if (from.has_status()) { - status_ = new ::milvus::grpc::Status(*from.status_); - } else { - status_ = nullptr; - } - ::memcpy(&entity_id_, &from.entity_id_, - static_cast(reinterpret_cast(&row_num_) - - reinterpret_cast(&entity_id_)) + sizeof(row_num_)); - // @@protoc_insertion_point(copy_constructor:milvus.grpc.HEntity) -} - -void HEntity::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_HEntity_milvus_2eproto.base); - attr_records_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - ::memset(&status_, 0, static_cast( - reinterpret_cast(&row_num_) - - reinterpret_cast(&status_)) + sizeof(row_num_)); -} - -HEntity::~HEntity() { - // @@protoc_insertion_point(destructor:milvus.grpc.HEntity) - SharedDtor(); -} - -void HEntity::SharedDtor() { - attr_records_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (this != internal_default_instance()) delete status_; -} - -void HEntity::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const HEntity& HEntity::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_HEntity_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void HEntity::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.HEntity) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - field_names_.Clear(); - result_values_.Clear(); - attr_records_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; - ::memset(&entity_id_, 0, static_cast( - reinterpret_cast(&row_num_) - - reinterpret_cast(&entity_id_)) + sizeof(row_num_)); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* HEntity::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.Status status = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ctx->ParseMessage(mutable_status(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // int64 entity_id = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) { - entity_id_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated string field_names = 3; - case 3: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) { - ptr -= 1; - do { - ptr += 1; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(add_field_names(), ptr, ctx, "milvus.grpc.HEntity.field_names"); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 26); - } else goto handle_unusual; - continue; - // bytes attr_records = 4; - case 4: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 34)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(mutable_attr_records(), ptr, ctx); - CHK_(ptr); - } else goto handle_unusual; - continue; - // int64 row_num = 5; - case 5: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 40)) { - row_num_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.FieldValue result_values = 6; - case 6: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 50)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_result_values(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 50); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool HEntity::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.HEntity) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.Status status = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_status())); - } else { - goto handle_unusual; - } - break; - } - - // int64 entity_id = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (16 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, &entity_id_))); - } else { - goto handle_unusual; - } - break; - } - - // repeated string field_names = 3; - case 3: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (26 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->add_field_names())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->field_names(this->field_names_size() - 1).data(), - static_cast(this->field_names(this->field_names_size() - 1).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.HEntity.field_names")); - } else { - goto handle_unusual; - } - break; - } - - // bytes attr_records = 4; - case 4: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (34 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadBytes( - input, this->mutable_attr_records())); - } else { - goto handle_unusual; - } - break; - } - - // int64 row_num = 5; - case 5: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (40 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, &row_num_))); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.FieldValue result_values = 6; - case 6: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (50 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_result_values())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.HEntity) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.HEntity) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void HEntity::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.HEntity) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 1, _Internal::status(this), output); - } - - // int64 entity_id = 2; - if (this->entity_id() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64(2, this->entity_id(), output); - } - - // repeated string field_names = 3; - for (int i = 0, n = this->field_names_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->field_names(i).data(), static_cast(this->field_names(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HEntity.field_names"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteString( - 3, this->field_names(i), output); - } - - // bytes attr_records = 4; - if (this->attr_records().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBytesMaybeAliased( - 4, this->attr_records(), output); - } - - // int64 row_num = 5; - if (this->row_num() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64(5, this->row_num(), output); - } - - // repeated .milvus.grpc.FieldValue result_values = 6; - for (unsigned int i = 0, - n = static_cast(this->result_values_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 6, - this->result_values(static_cast(i)), - output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.HEntity) -} - -::PROTOBUF_NAMESPACE_ID::uint8* HEntity::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.HEntity) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 1, _Internal::status(this), target); - } - - // int64 entity_id = 2; - if (this->entity_id() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64ToArray(2, this->entity_id(), target); - } - - // repeated string field_names = 3; - for (int i = 0, n = this->field_names_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->field_names(i).data(), static_cast(this->field_names(i).length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HEntity.field_names"); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteStringToArray(3, this->field_names(i), target); - } - - // bytes attr_records = 4; - if (this->attr_records().size() > 0) { - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBytesToArray( - 4, this->attr_records(), target); - } - - // int64 row_num = 5; - if (this->row_num() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64ToArray(5, this->row_num(), target); - } - - // repeated .milvus.grpc.FieldValue result_values = 6; - for (unsigned int i = 0, - n = static_cast(this->result_values_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 6, this->result_values(static_cast(i)), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.HEntity) - return target; -} - -size_t HEntity::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.HEntity) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated string field_names = 3; - total_size += 1 * - ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(this->field_names_size()); - for (int i = 0, n = this->field_names_size(); i < n; i++) { - total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->field_names(i)); - } - - // repeated .milvus.grpc.FieldValue result_values = 6; - { - unsigned int count = static_cast(this->result_values_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->result_values(static_cast(i))); - } - } - - // bytes attr_records = 4; - if (this->attr_records().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize( - this->attr_records()); - } - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *status_); - } - - // int64 entity_id = 2; - if (this->entity_id() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int64Size( - this->entity_id()); - } - - // int64 row_num = 5; - if (this->row_num() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int64Size( - this->row_num()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void HEntity::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.HEntity) - GOOGLE_DCHECK_NE(&from, this); - const HEntity* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.HEntity) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.HEntity) - MergeFrom(*source); - } -} - -void HEntity::MergeFrom(const HEntity& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.HEntity) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - field_names_.MergeFrom(from.field_names_); - result_values_.MergeFrom(from.result_values_); - if (from.attr_records().size() > 0) { - - attr_records_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.attr_records_); - } - if (from.has_status()) { - mutable_status()->::milvus::grpc::Status::MergeFrom(from.status()); - } - if (from.entity_id() != 0) { - set_entity_id(from.entity_id()); - } - if (from.row_num() != 0) { - set_row_num(from.row_num()); - } -} - -void HEntity::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.HEntity) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void HEntity::CopyFrom(const HEntity& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.HEntity) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool HEntity::IsInitialized() const { - return true; -} - -void HEntity::InternalSwap(HEntity* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - field_names_.InternalSwap(CastToBase(&other->field_names_)); - CastToBase(&result_values_)->InternalSwap(CastToBase(&other->result_values_)); - attr_records_.Swap(&other->attr_records_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - swap(status_, other->status_); - swap(entity_id_, other->entity_id_); - swap(row_num_, other->row_num_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata HEntity::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void HQueryResult::InitAsDefaultInstance() { - ::milvus::grpc::_HQueryResult_default_instance_._instance.get_mutable()->status_ = const_cast< ::milvus::grpc::Status*>( - ::milvus::grpc::Status::internal_default_instance()); -} -class HQueryResult::_Internal { - public: - static const ::milvus::grpc::Status& status(const HQueryResult* msg); -}; - -const ::milvus::grpc::Status& -HQueryResult::_Internal::status(const HQueryResult* msg) { - return *msg->status_; -} -void HQueryResult::clear_status() { - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; -} -HQueryResult::HQueryResult() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.HQueryResult) -} -HQueryResult::HQueryResult(const HQueryResult& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - entities_(from.entities_), - score_(from.score_), - distance_(from.distance_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - if (from.has_status()) { - status_ = new ::milvus::grpc::Status(*from.status_); - } else { - status_ = nullptr; - } - row_num_ = from.row_num_; - // @@protoc_insertion_point(copy_constructor:milvus.grpc.HQueryResult) -} - -void HQueryResult::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_HQueryResult_milvus_2eproto.base); - ::memset(&status_, 0, static_cast( - reinterpret_cast(&row_num_) - - reinterpret_cast(&status_)) + sizeof(row_num_)); -} - -HQueryResult::~HQueryResult() { - // @@protoc_insertion_point(destructor:milvus.grpc.HQueryResult) - SharedDtor(); -} - -void HQueryResult::SharedDtor() { - if (this != internal_default_instance()) delete status_; -} - -void HQueryResult::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const HQueryResult& HQueryResult::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_HQueryResult_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void HQueryResult::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.HQueryResult) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - entities_.Clear(); - score_.Clear(); - distance_.Clear(); - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; - row_num_ = PROTOBUF_LONGLONG(0); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* HQueryResult::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.Status status = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ctx->ParseMessage(mutable_status(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.HEntity entities = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_entities(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 18); - } else goto handle_unusual; - continue; - // int64 row_num = 3; - case 3: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 24)) { - row_num_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated float score = 4; - case 4: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 34)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedFloatParser(mutable_score(), ptr, ctx); - CHK_(ptr); - } else if (static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 37) { - add_score(::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr)); - ptr += sizeof(float); - } else goto handle_unusual; - continue; - // repeated float distance = 5; - case 5: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 42)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedFloatParser(mutable_distance(), ptr, ctx); - CHK_(ptr); - } else if (static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 45) { - add_distance(::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr)); - ptr += sizeof(float); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool HQueryResult::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.HQueryResult) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.Status status = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_status())); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.HEntity entities = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_entities())); - } else { - goto handle_unusual; - } - break; - } - - // int64 row_num = 3; - case 3: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (24 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, &row_num_))); - } else { - goto handle_unusual; - } - break; - } - - // repeated float score = 4; - case 4: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (34 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPackedPrimitive< - float, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_FLOAT>( - input, this->mutable_score()))); - } else if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (37 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadRepeatedPrimitiveNoInline< - float, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_FLOAT>( - 1, 34u, input, this->mutable_score()))); - } else { - goto handle_unusual; - } - break; - } - - // repeated float distance = 5; - case 5: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (42 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPackedPrimitive< - float, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_FLOAT>( - input, this->mutable_distance()))); - } else if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (45 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadRepeatedPrimitiveNoInline< - float, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_FLOAT>( - 1, 42u, input, this->mutable_distance()))); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.HQueryResult) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.HQueryResult) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void HQueryResult::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.HQueryResult) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 1, _Internal::status(this), output); - } - - // repeated .milvus.grpc.HEntity entities = 2; - for (unsigned int i = 0, - n = static_cast(this->entities_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 2, - this->entities(static_cast(i)), - output); - } - - // int64 row_num = 3; - if (this->row_num() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64(3, this->row_num(), output); - } - - // repeated float score = 4; - if (this->score_size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTag(4, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, output); - output->WriteVarint32(_score_cached_byte_size_.load( - std::memory_order_relaxed)); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteFloatArray( - this->score().data(), this->score_size(), output); - } - - // repeated float distance = 5; - if (this->distance_size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTag(5, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, output); - output->WriteVarint32(_distance_cached_byte_size_.load( - std::memory_order_relaxed)); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteFloatArray( - this->distance().data(), this->distance_size(), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.HQueryResult) -} - -::PROTOBUF_NAMESPACE_ID::uint8* HQueryResult::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.HQueryResult) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 1, _Internal::status(this), target); - } - - // repeated .milvus.grpc.HEntity entities = 2; - for (unsigned int i = 0, - n = static_cast(this->entities_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 2, this->entities(static_cast(i)), target); - } - - // int64 row_num = 3; - if (this->row_num() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64ToArray(3, this->row_num(), target); - } - - // repeated float score = 4; - if (this->score_size() > 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTagToArray( - 4, - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, - target); - target = ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream::WriteVarint32ToArray( - _score_cached_byte_size_.load(std::memory_order_relaxed), - target); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteFloatNoTagToArray(this->score_, target); - } - - // repeated float distance = 5; - if (this->distance_size() > 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTagToArray( - 5, - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, - target); - target = ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream::WriteVarint32ToArray( - _distance_cached_byte_size_.load(std::memory_order_relaxed), - target); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteFloatNoTagToArray(this->distance_, target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.HQueryResult) - return target; -} - -size_t HQueryResult::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.HQueryResult) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated .milvus.grpc.HEntity entities = 2; - { - unsigned int count = static_cast(this->entities_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->entities(static_cast(i))); - } - } - - // repeated float score = 4; - { - unsigned int count = static_cast(this->score_size()); - size_t data_size = 4UL * count; - if (data_size > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( - static_cast<::PROTOBUF_NAMESPACE_ID::int32>(data_size)); - } - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); - _score_cached_byte_size_.store(cached_size, - std::memory_order_relaxed); - total_size += data_size; - } - - // repeated float distance = 5; - { - unsigned int count = static_cast(this->distance_size()); - size_t data_size = 4UL * count; - if (data_size > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( - static_cast<::PROTOBUF_NAMESPACE_ID::int32>(data_size)); - } - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); - _distance_cached_byte_size_.store(cached_size, - std::memory_order_relaxed); - total_size += data_size; - } - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *status_); - } - - // int64 row_num = 3; - if (this->row_num() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int64Size( - this->row_num()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void HQueryResult::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.HQueryResult) - GOOGLE_DCHECK_NE(&from, this); - const HQueryResult* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.HQueryResult) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.HQueryResult) - MergeFrom(*source); - } -} - -void HQueryResult::MergeFrom(const HQueryResult& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.HQueryResult) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - entities_.MergeFrom(from.entities_); - score_.MergeFrom(from.score_); - distance_.MergeFrom(from.distance_); - if (from.has_status()) { - mutable_status()->::milvus::grpc::Status::MergeFrom(from.status()); - } - if (from.row_num() != 0) { - set_row_num(from.row_num()); - } -} - -void HQueryResult::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.HQueryResult) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void HQueryResult::CopyFrom(const HQueryResult& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.HQueryResult) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool HQueryResult::IsInitialized() const { - return true; -} - -void HQueryResult::InternalSwap(HQueryResult* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - CastToBase(&entities_)->InternalSwap(CastToBase(&other->entities_)); - score_.InternalSwap(&other->score_); - distance_.InternalSwap(&other->distance_); - swap(status_, other->status_); - swap(row_num_, other->row_num_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata HQueryResult::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void HInsertParam::InitAsDefaultInstance() { - ::milvus::grpc::_HInsertParam_default_instance_._instance.get_mutable()->entities_ = const_cast< ::milvus::grpc::HEntity*>( - ::milvus::grpc::HEntity::internal_default_instance()); -} -class HInsertParam::_Internal { - public: - static const ::milvus::grpc::HEntity& entities(const HInsertParam* msg); -}; - -const ::milvus::grpc::HEntity& -HInsertParam::_Internal::entities(const HInsertParam* msg) { - return *msg->entities_; -} -HInsertParam::HInsertParam() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.HInsertParam) -} -HInsertParam::HInsertParam(const HInsertParam& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - entity_id_array_(from.entity_id_array_), - extra_params_(from.extra_params_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.collection_name().empty()) { - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - partition_tag_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.partition_tag().empty()) { - partition_tag_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.partition_tag_); - } - if (from.has_entities()) { - entities_ = new ::milvus::grpc::HEntity(*from.entities_); - } else { - entities_ = nullptr; - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.HInsertParam) -} - -void HInsertParam::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_HInsertParam_milvus_2eproto.base); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - partition_tag_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - entities_ = nullptr; -} - -HInsertParam::~HInsertParam() { - // @@protoc_insertion_point(destructor:milvus.grpc.HInsertParam) - SharedDtor(); -} - -void HInsertParam::SharedDtor() { - collection_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - partition_tag_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (this != internal_default_instance()) delete entities_; -} - -void HInsertParam::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const HInsertParam& HInsertParam::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_HInsertParam_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void HInsertParam::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.HInsertParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - entity_id_array_.Clear(); - extra_params_.Clear(); - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - partition_tag_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (GetArenaNoVirtual() == nullptr && entities_ != nullptr) { - delete entities_; - } - entities_ = nullptr; - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* HInsertParam::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // string collection_name = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_collection_name(), ptr, ctx, "milvus.grpc.HInsertParam.collection_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // string partition_tag = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_partition_tag(), ptr, ctx, "milvus.grpc.HInsertParam.partition_tag"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // .milvus.grpc.HEntity entities = 3; - case 3: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 26)) { - ptr = ctx->ParseMessage(mutable_entities(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated int64 entity_id_array = 4; - case 4: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 34)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt64Parser(mutable_entity_id_array(), ptr, ctx); - CHK_(ptr); - } else if (static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 32) { - add_entity_id_array(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr)); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - case 5: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 42)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_extra_params(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 42); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool HInsertParam::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.HInsertParam) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // string collection_name = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_collection_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.HInsertParam.collection_name")); - } else { - goto handle_unusual; - } - break; - } - - // string partition_tag = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_partition_tag())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->partition_tag().data(), static_cast(this->partition_tag().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.HInsertParam.partition_tag")); - } else { - goto handle_unusual; - } - break; - } - - // .milvus.grpc.HEntity entities = 3; - case 3: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (26 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_entities())); - } else { - goto handle_unusual; - } - break; - } - - // repeated int64 entity_id_array = 4; - case 4: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (34 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPackedPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, this->mutable_entity_id_array()))); - } else if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (32 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadRepeatedPrimitiveNoInline< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - 1, 34u, input, this->mutable_entity_id_array()))); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - case 5: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (42 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_extra_params())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.HInsertParam) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.HInsertParam) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void HInsertParam::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.HInsertParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HInsertParam.collection_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 1, this->collection_name(), output); - } - - // string partition_tag = 2; - if (this->partition_tag().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->partition_tag().data(), static_cast(this->partition_tag().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HInsertParam.partition_tag"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 2, this->partition_tag(), output); - } - - // .milvus.grpc.HEntity entities = 3; - if (this->has_entities()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 3, _Internal::entities(this), output); - } - - // repeated int64 entity_id_array = 4; - if (this->entity_id_array_size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTag(4, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, output); - output->WriteVarint32(_entity_id_array_cached_byte_size_.load( - std::memory_order_relaxed)); - } - for (int i = 0, n = this->entity_id_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64NoTag( - this->entity_id_array(i), output); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 5, - this->extra_params(static_cast(i)), - output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.HInsertParam) -} - -::PROTOBUF_NAMESPACE_ID::uint8* HInsertParam::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.HInsertParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HInsertParam.collection_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 1, this->collection_name(), target); - } - - // string partition_tag = 2; - if (this->partition_tag().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->partition_tag().data(), static_cast(this->partition_tag().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HInsertParam.partition_tag"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 2, this->partition_tag(), target); - } - - // .milvus.grpc.HEntity entities = 3; - if (this->has_entities()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 3, _Internal::entities(this), target); - } - - // repeated int64 entity_id_array = 4; - if (this->entity_id_array_size() > 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTagToArray( - 4, - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, - target); - target = ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream::WriteVarint32ToArray( - _entity_id_array_cached_byte_size_.load(std::memory_order_relaxed), - target); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteInt64NoTagToArray(this->entity_id_array_, target); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 5, this->extra_params(static_cast(i)), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.HInsertParam) - return target; -} - -size_t HInsertParam::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.HInsertParam) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated int64 entity_id_array = 4; - { - size_t data_size = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - Int64Size(this->entity_id_array_); - if (data_size > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( - static_cast<::PROTOBUF_NAMESPACE_ID::int32>(data_size)); - } - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); - _entity_id_array_cached_byte_size_.store(cached_size, - std::memory_order_relaxed); - total_size += data_size; - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - { - unsigned int count = static_cast(this->extra_params_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->extra_params(static_cast(i))); - } - } - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_name()); - } - - // string partition_tag = 2; - if (this->partition_tag().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->partition_tag()); - } - - // .milvus.grpc.HEntity entities = 3; - if (this->has_entities()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *entities_); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void HInsertParam::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.HInsertParam) - GOOGLE_DCHECK_NE(&from, this); - const HInsertParam* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.HInsertParam) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.HInsertParam) - MergeFrom(*source); - } -} - -void HInsertParam::MergeFrom(const HInsertParam& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.HInsertParam) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - entity_id_array_.MergeFrom(from.entity_id_array_); - extra_params_.MergeFrom(from.extra_params_); - if (from.collection_name().size() > 0) { - - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - if (from.partition_tag().size() > 0) { - - partition_tag_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.partition_tag_); - } - if (from.has_entities()) { - mutable_entities()->::milvus::grpc::HEntity::MergeFrom(from.entities()); - } -} - -void HInsertParam::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.HInsertParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void HInsertParam::CopyFrom(const HInsertParam& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.HInsertParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool HInsertParam::IsInitialized() const { - return true; -} - -void HInsertParam::InternalSwap(HInsertParam* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - entity_id_array_.InternalSwap(&other->entity_id_array_); - CastToBase(&extra_params_)->InternalSwap(CastToBase(&other->extra_params_)); - collection_name_.Swap(&other->collection_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - partition_tag_.Swap(&other->partition_tag_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - swap(entities_, other->entities_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata HInsertParam::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void HEntityIdentity::InitAsDefaultInstance() { -} -class HEntityIdentity::_Internal { - public: -}; - -HEntityIdentity::HEntityIdentity() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.HEntityIdentity) -} -HEntityIdentity::HEntityIdentity(const HEntityIdentity& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.collection_name().empty()) { - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - id_ = from.id_; - // @@protoc_insertion_point(copy_constructor:milvus.grpc.HEntityIdentity) -} - -void HEntityIdentity::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_HEntityIdentity_milvus_2eproto.base); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - id_ = PROTOBUF_LONGLONG(0); -} - -HEntityIdentity::~HEntityIdentity() { - // @@protoc_insertion_point(destructor:milvus.grpc.HEntityIdentity) - SharedDtor(); -} - -void HEntityIdentity::SharedDtor() { - collection_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void HEntityIdentity::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const HEntityIdentity& HEntityIdentity::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_HEntityIdentity_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void HEntityIdentity::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.HEntityIdentity) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - id_ = PROTOBUF_LONGLONG(0); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* HEntityIdentity::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // string collection_name = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_collection_name(), ptr, ctx, "milvus.grpc.HEntityIdentity.collection_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // int64 id = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) { - id_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool HEntityIdentity::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.HEntityIdentity) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // string collection_name = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_collection_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.HEntityIdentity.collection_name")); - } else { - goto handle_unusual; - } - break; - } - - // int64 id = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (16 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, &id_))); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.HEntityIdentity) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.HEntityIdentity) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void HEntityIdentity::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.HEntityIdentity) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HEntityIdentity.collection_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 1, this->collection_name(), output); - } - - // int64 id = 2; - if (this->id() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64(2, this->id(), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.HEntityIdentity) -} - -::PROTOBUF_NAMESPACE_ID::uint8* HEntityIdentity::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.HEntityIdentity) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HEntityIdentity.collection_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 1, this->collection_name(), target); - } - - // int64 id = 2; - if (this->id() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64ToArray(2, this->id(), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.HEntityIdentity) - return target; -} - -size_t HEntityIdentity::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.HEntityIdentity) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_name()); - } - - // int64 id = 2; - if (this->id() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int64Size( - this->id()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void HEntityIdentity::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.HEntityIdentity) - GOOGLE_DCHECK_NE(&from, this); - const HEntityIdentity* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.HEntityIdentity) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.HEntityIdentity) - MergeFrom(*source); - } -} - -void HEntityIdentity::MergeFrom(const HEntityIdentity& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.HEntityIdentity) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - if (from.collection_name().size() > 0) { - - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - if (from.id() != 0) { - set_id(from.id()); - } -} - -void HEntityIdentity::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.HEntityIdentity) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void HEntityIdentity::CopyFrom(const HEntityIdentity& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.HEntityIdentity) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool HEntityIdentity::IsInitialized() const { - return true; -} - -void HEntityIdentity::InternalSwap(HEntityIdentity* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - collection_name_.Swap(&other->collection_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - swap(id_, other->id_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata HEntityIdentity::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void HEntityIDs::InitAsDefaultInstance() { - ::milvus::grpc::_HEntityIDs_default_instance_._instance.get_mutable()->status_ = const_cast< ::milvus::grpc::Status*>( - ::milvus::grpc::Status::internal_default_instance()); -} -class HEntityIDs::_Internal { - public: - static const ::milvus::grpc::Status& status(const HEntityIDs* msg); -}; - -const ::milvus::grpc::Status& -HEntityIDs::_Internal::status(const HEntityIDs* msg) { - return *msg->status_; -} -void HEntityIDs::clear_status() { - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; -} -HEntityIDs::HEntityIDs() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.HEntityIDs) -} -HEntityIDs::HEntityIDs(const HEntityIDs& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - entity_id_array_(from.entity_id_array_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - if (from.has_status()) { - status_ = new ::milvus::grpc::Status(*from.status_); - } else { - status_ = nullptr; - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.HEntityIDs) -} - -void HEntityIDs::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_HEntityIDs_milvus_2eproto.base); - status_ = nullptr; -} - -HEntityIDs::~HEntityIDs() { - // @@protoc_insertion_point(destructor:milvus.grpc.HEntityIDs) - SharedDtor(); -} - -void HEntityIDs::SharedDtor() { - if (this != internal_default_instance()) delete status_; -} - -void HEntityIDs::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const HEntityIDs& HEntityIDs::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_HEntityIDs_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void HEntityIDs::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.HEntityIDs) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - entity_id_array_.Clear(); - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* HEntityIDs::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.Status status = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ctx->ParseMessage(mutable_status(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated int64 entity_id_array = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt64Parser(mutable_entity_id_array(), ptr, ctx); - CHK_(ptr); - } else if (static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16) { - add_entity_id_array(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr)); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool HEntityIDs::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.HEntityIDs) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.Status status = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_status())); - } else { - goto handle_unusual; - } - break; - } - - // repeated int64 entity_id_array = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPackedPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, this->mutable_entity_id_array()))); - } else if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (16 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadRepeatedPrimitiveNoInline< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - 1, 18u, input, this->mutable_entity_id_array()))); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.HEntityIDs) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.HEntityIDs) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void HEntityIDs::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.HEntityIDs) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 1, _Internal::status(this), output); - } - - // repeated int64 entity_id_array = 2; - if (this->entity_id_array_size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTag(2, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, output); - output->WriteVarint32(_entity_id_array_cached_byte_size_.load( - std::memory_order_relaxed)); - } - for (int i = 0, n = this->entity_id_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64NoTag( - this->entity_id_array(i), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.HEntityIDs) -} - -::PROTOBUF_NAMESPACE_ID::uint8* HEntityIDs::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.HEntityIDs) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 1, _Internal::status(this), target); - } - - // repeated int64 entity_id_array = 2; - if (this->entity_id_array_size() > 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTagToArray( - 2, - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, - target); - target = ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream::WriteVarint32ToArray( - _entity_id_array_cached_byte_size_.load(std::memory_order_relaxed), - target); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteInt64NoTagToArray(this->entity_id_array_, target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.HEntityIDs) - return target; -} - -size_t HEntityIDs::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.HEntityIDs) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated int64 entity_id_array = 2; - { - size_t data_size = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - Int64Size(this->entity_id_array_); - if (data_size > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( - static_cast<::PROTOBUF_NAMESPACE_ID::int32>(data_size)); - } - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); - _entity_id_array_cached_byte_size_.store(cached_size, - std::memory_order_relaxed); - total_size += data_size; - } - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *status_); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void HEntityIDs::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.HEntityIDs) - GOOGLE_DCHECK_NE(&from, this); - const HEntityIDs* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.HEntityIDs) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.HEntityIDs) - MergeFrom(*source); - } -} - -void HEntityIDs::MergeFrom(const HEntityIDs& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.HEntityIDs) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - entity_id_array_.MergeFrom(from.entity_id_array_); - if (from.has_status()) { - mutable_status()->::milvus::grpc::Status::MergeFrom(from.status()); - } -} - -void HEntityIDs::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.HEntityIDs) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void HEntityIDs::CopyFrom(const HEntityIDs& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.HEntityIDs) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool HEntityIDs::IsInitialized() const { - return true; -} - -void HEntityIDs::InternalSwap(HEntityIDs* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - entity_id_array_.InternalSwap(&other->entity_id_array_); - swap(status_, other->status_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata HEntityIDs::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void HGetEntityIDsParam::InitAsDefaultInstance() { -} -class HGetEntityIDsParam::_Internal { - public: -}; - -HGetEntityIDsParam::HGetEntityIDsParam() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.HGetEntityIDsParam) -} -HGetEntityIDsParam::HGetEntityIDsParam(const HGetEntityIDsParam& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.collection_name().empty()) { - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - segment_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.segment_name().empty()) { - segment_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.segment_name_); - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.HGetEntityIDsParam) -} - -void HGetEntityIDsParam::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_HGetEntityIDsParam_milvus_2eproto.base); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - segment_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -HGetEntityIDsParam::~HGetEntityIDsParam() { - // @@protoc_insertion_point(destructor:milvus.grpc.HGetEntityIDsParam) - SharedDtor(); -} - -void HGetEntityIDsParam::SharedDtor() { - collection_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - segment_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void HGetEntityIDsParam::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const HGetEntityIDsParam& HGetEntityIDsParam::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_HGetEntityIDsParam_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void HGetEntityIDsParam::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.HGetEntityIDsParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - segment_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* HGetEntityIDsParam::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // string collection_name = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_collection_name(), ptr, ctx, "milvus.grpc.HGetEntityIDsParam.collection_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // string segment_name = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_segment_name(), ptr, ctx, "milvus.grpc.HGetEntityIDsParam.segment_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool HGetEntityIDsParam::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.HGetEntityIDsParam) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // string collection_name = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_collection_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.HGetEntityIDsParam.collection_name")); - } else { - goto handle_unusual; - } - break; - } - - // string segment_name = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_segment_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->segment_name().data(), static_cast(this->segment_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.HGetEntityIDsParam.segment_name")); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.HGetEntityIDsParam) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.HGetEntityIDsParam) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void HGetEntityIDsParam::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.HGetEntityIDsParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HGetEntityIDsParam.collection_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 1, this->collection_name(), output); - } - - // string segment_name = 2; - if (this->segment_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->segment_name().data(), static_cast(this->segment_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HGetEntityIDsParam.segment_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 2, this->segment_name(), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.HGetEntityIDsParam) -} - -::PROTOBUF_NAMESPACE_ID::uint8* HGetEntityIDsParam::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.HGetEntityIDsParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HGetEntityIDsParam.collection_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 1, this->collection_name(), target); - } - - // string segment_name = 2; - if (this->segment_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->segment_name().data(), static_cast(this->segment_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HGetEntityIDsParam.segment_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 2, this->segment_name(), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.HGetEntityIDsParam) - return target; -} - -size_t HGetEntityIDsParam::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.HGetEntityIDsParam) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_name()); - } - - // string segment_name = 2; - if (this->segment_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->segment_name()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void HGetEntityIDsParam::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.HGetEntityIDsParam) - GOOGLE_DCHECK_NE(&from, this); - const HGetEntityIDsParam* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.HGetEntityIDsParam) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.HGetEntityIDsParam) - MergeFrom(*source); - } -} - -void HGetEntityIDsParam::MergeFrom(const HGetEntityIDsParam& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.HGetEntityIDsParam) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - if (from.collection_name().size() > 0) { - - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - if (from.segment_name().size() > 0) { - - segment_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.segment_name_); - } -} - -void HGetEntityIDsParam::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.HGetEntityIDsParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void HGetEntityIDsParam::CopyFrom(const HGetEntityIDsParam& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.HGetEntityIDsParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool HGetEntityIDsParam::IsInitialized() const { - return true; -} - -void HGetEntityIDsParam::InternalSwap(HGetEntityIDsParam* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - collection_name_.Swap(&other->collection_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - segment_name_.Swap(&other->segment_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); -} - -::PROTOBUF_NAMESPACE_ID::Metadata HGetEntityIDsParam::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void HDeleteByIDParam::InitAsDefaultInstance() { -} -class HDeleteByIDParam::_Internal { - public: -}; - -HDeleteByIDParam::HDeleteByIDParam() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.HDeleteByIDParam) -} -HDeleteByIDParam::HDeleteByIDParam(const HDeleteByIDParam& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - id_array_(from.id_array_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.collection_name().empty()) { - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - // @@protoc_insertion_point(copy_constructor:milvus.grpc.HDeleteByIDParam) -} - -void HDeleteByIDParam::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_HDeleteByIDParam_milvus_2eproto.base); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -HDeleteByIDParam::~HDeleteByIDParam() { - // @@protoc_insertion_point(destructor:milvus.grpc.HDeleteByIDParam) - SharedDtor(); -} - -void HDeleteByIDParam::SharedDtor() { - collection_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void HDeleteByIDParam::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const HDeleteByIDParam& HDeleteByIDParam::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_HDeleteByIDParam_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void HDeleteByIDParam::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.HDeleteByIDParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - id_array_.Clear(); - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* HDeleteByIDParam::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // string collection_name = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_collection_name(), ptr, ctx, "milvus.grpc.HDeleteByIDParam.collection_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated int64 id_array = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt64Parser(mutable_id_array(), ptr, ctx); - CHK_(ptr); - } else if (static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16) { - add_id_array(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr)); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool HDeleteByIDParam::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.HDeleteByIDParam) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // string collection_name = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_collection_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.HDeleteByIDParam.collection_name")); - } else { - goto handle_unusual; - } - break; - } - - // repeated int64 id_array = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPackedPrimitive< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - input, this->mutable_id_array()))); - } else if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (16 & 0xFF)) { - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadRepeatedPrimitiveNoInline< - ::PROTOBUF_NAMESPACE_ID::int64, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT64>( - 1, 18u, input, this->mutable_id_array()))); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.HDeleteByIDParam) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.HDeleteByIDParam) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void HDeleteByIDParam::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.HDeleteByIDParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HDeleteByIDParam.collection_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 1, this->collection_name(), output); - } - - // repeated int64 id_array = 2; - if (this->id_array_size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTag(2, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, output); - output->WriteVarint32(_id_array_cached_byte_size_.load( - std::memory_order_relaxed)); - } - for (int i = 0, n = this->id_array_size(); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64NoTag( - this->id_array(i), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.HDeleteByIDParam) -} - -::PROTOBUF_NAMESPACE_ID::uint8* HDeleteByIDParam::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.HDeleteByIDParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HDeleteByIDParam.collection_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 1, this->collection_name(), target); - } - - // repeated int64 id_array = 2; - if (this->id_array_size() > 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteTagToArray( - 2, - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED, - target); - target = ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream::WriteVarint32ToArray( - _id_array_cached_byte_size_.load(std::memory_order_relaxed), - target); - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - WriteInt64NoTagToArray(this->id_array_, target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.HDeleteByIDParam) - return target; -} - -size_t HDeleteByIDParam::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.HDeleteByIDParam) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated int64 id_array = 2; - { - size_t data_size = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - Int64Size(this->id_array_); - if (data_size > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( - static_cast<::PROTOBUF_NAMESPACE_ID::int32>(data_size)); - } - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); - _id_array_cached_byte_size_.store(cached_size, - std::memory_order_relaxed); - total_size += data_size; - } - - // string collection_name = 1; - if (this->collection_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_name()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void HDeleteByIDParam::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.HDeleteByIDParam) - GOOGLE_DCHECK_NE(&from, this); - const HDeleteByIDParam* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.HDeleteByIDParam) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.HDeleteByIDParam) - MergeFrom(*source); - } -} - -void HDeleteByIDParam::MergeFrom(const HDeleteByIDParam& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.HDeleteByIDParam) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - id_array_.MergeFrom(from.id_array_); - if (from.collection_name().size() > 0) { - - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } -} - -void HDeleteByIDParam::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.HDeleteByIDParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void HDeleteByIDParam::CopyFrom(const HDeleteByIDParam& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.HDeleteByIDParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool HDeleteByIDParam::IsInitialized() const { - return true; -} - -void HDeleteByIDParam::InternalSwap(HDeleteByIDParam* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - id_array_.InternalSwap(&other->id_array_); - collection_name_.Swap(&other->collection_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); -} - -::PROTOBUF_NAMESPACE_ID::Metadata HDeleteByIDParam::GetMetadata() const { - return GetMetadataStatic(); -} - - -// =================================================================== - -void HIndexParam::InitAsDefaultInstance() { - ::milvus::grpc::_HIndexParam_default_instance_._instance.get_mutable()->status_ = const_cast< ::milvus::grpc::Status*>( - ::milvus::grpc::Status::internal_default_instance()); -} -class HIndexParam::_Internal { - public: - static const ::milvus::grpc::Status& status(const HIndexParam* msg); -}; - -const ::milvus::grpc::Status& -HIndexParam::_Internal::status(const HIndexParam* msg) { - return *msg->status_; -} -void HIndexParam::clear_status() { - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; -} -HIndexParam::HIndexParam() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.HIndexParam) -} -HIndexParam::HIndexParam(const HIndexParam& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr), - extra_params_(from.extra_params_) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.collection_name().empty()) { - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - if (from.has_status()) { - status_ = new ::milvus::grpc::Status(*from.status_); - } else { - status_ = nullptr; - } - index_type_ = from.index_type_; - // @@protoc_insertion_point(copy_constructor:milvus.grpc.HIndexParam) -} - -void HIndexParam::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_HIndexParam_milvus_2eproto.base); - collection_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - ::memset(&status_, 0, static_cast( - reinterpret_cast(&index_type_) - - reinterpret_cast(&status_)) + sizeof(index_type_)); -} - -HIndexParam::~HIndexParam() { - // @@protoc_insertion_point(destructor:milvus.grpc.HIndexParam) - SharedDtor(); -} - -void HIndexParam::SharedDtor() { - collection_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (this != internal_default_instance()) delete status_; -} - -void HIndexParam::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const HIndexParam& HIndexParam::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_HIndexParam_milvus_2eproto.base); - return *internal_default_instance(); -} - - -void HIndexParam::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.HIndexParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - extra_params_.Clear(); - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (GetArenaNoVirtual() == nullptr && status_ != nullptr) { - delete status_; - } - status_ = nullptr; - index_type_ = 0; - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* HIndexParam::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.Status status = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { - ptr = ctx->ParseMessage(mutable_status(), ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // string collection_name = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_collection_name(), ptr, ctx, "milvus.grpc.HIndexParam.collection_name"); - CHK_(ptr); - } else goto handle_unusual; - continue; - // int32 index_type = 3; - case 3: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 24)) { - index_type_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - } else goto handle_unusual; - continue; - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - case 4: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 34)) { - ptr -= 1; - do { - ptr += 1; - ptr = ctx->ParseMessage(add_extra_params(), ptr); - CHK_(ptr); - if (!ctx->DataAvailable(ptr)) break; - } while (::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad<::PROTOBUF_NAMESPACE_ID::uint8>(ptr) == 34); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool HIndexParam::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.HIndexParam) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.Status status = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (10 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, mutable_status())); - } else { - goto handle_unusual; - } - break; - } - - // string collection_name = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_collection_name())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.HIndexParam.collection_name")); - } else { - goto handle_unusual; - } - break; - } - - // int32 index_type = 3; - case 3: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (24 & 0xFF)) { - - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - ::PROTOBUF_NAMESPACE_ID::int32, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_INT32>( - input, &index_type_))); - } else { - goto handle_unusual; - } - break; - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - case 4: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (34 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadMessage( - input, add_extra_params())); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.HIndexParam) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.HIndexParam) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void HIndexParam::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.HIndexParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 1, _Internal::status(this), output); - } - - // string collection_name = 2; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HIndexParam.collection_name"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 2, this->collection_name(), output); - } - - // int32 index_type = 3; - if (this->index_type() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32(3, this->index_type(), output); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteMessageMaybeToArray( - 4, - this->extra_params(static_cast(i)), - output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.HIndexParam) -} - -::PROTOBUF_NAMESPACE_ID::uint8* HIndexParam::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.HIndexParam) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 1, _Internal::status(this), target); - } - - // string collection_name = 2; - if (this->collection_name().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->collection_name().data(), static_cast(this->collection_name().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.HIndexParam.collection_name"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 2, this->collection_name(), target); - } - - // int32 index_type = 3; - if (this->index_type() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(3, this->index_type(), target); - } - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - for (unsigned int i = 0, - n = static_cast(this->extra_params_size()); i < n; i++) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: - InternalWriteMessageToArray( - 4, this->extra_params(static_cast(i)), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.HIndexParam) - return target; -} - -size_t HIndexParam::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.HIndexParam) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - { - unsigned int count = static_cast(this->extra_params_size()); - total_size += 1UL * count; - for (unsigned int i = 0; i < count; i++) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - this->extra_params(static_cast(i))); - } - } - - // string collection_name = 2; - if (this->collection_name().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->collection_name()); - } - - // .milvus.grpc.Status status = 1; - if (this->has_status()) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( - *status_); - } - - // int32 index_type = 3; - if (this->index_type() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( - this->index_type()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void HIndexParam::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.HIndexParam) - GOOGLE_DCHECK_NE(&from, this); - const HIndexParam* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.HIndexParam) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.HIndexParam) - MergeFrom(*source); - } -} - -void HIndexParam::MergeFrom(const HIndexParam& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.HIndexParam) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - extra_params_.MergeFrom(from.extra_params_); - if (from.collection_name().size() > 0) { - - collection_name_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.collection_name_); - } - if (from.has_status()) { - mutable_status()->::milvus::grpc::Status::MergeFrom(from.status()); - } - if (from.index_type() != 0) { - set_index_type(from.index_type()); - } -} - -void HIndexParam::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.HIndexParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void HIndexParam::CopyFrom(const HIndexParam& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.HIndexParam) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool HIndexParam::IsInitialized() const { - return true; -} - -void HIndexParam::InternalSwap(HIndexParam* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - CastToBase(&extra_params_)->InternalSwap(CastToBase(&other->extra_params_)); - collection_name_.Swap(&other->collection_name_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - swap(status_, other->status_); - swap(index_type_, other->index_type_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata HIndexParam::GetMetadata() const { - return GetMetadataStatic(); -} - - -// @@protoc_insertion_point(namespace_scope) -} // namespace grpc -} // namespace milvus -PROTOBUF_NAMESPACE_OPEN -template<> PROTOBUF_NOINLINE ::milvus::grpc::KeyValuePair* Arena::CreateMaybeMessage< ::milvus::grpc::KeyValuePair >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::KeyValuePair >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::CollectionName* Arena::CreateMaybeMessage< ::milvus::grpc::CollectionName >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::CollectionName >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::CollectionNameList* Arena::CreateMaybeMessage< ::milvus::grpc::CollectionNameList >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::CollectionNameList >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::CollectionSchema* Arena::CreateMaybeMessage< ::milvus::grpc::CollectionSchema >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::CollectionSchema >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::PartitionParam* Arena::CreateMaybeMessage< ::milvus::grpc::PartitionParam >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::PartitionParam >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::PartitionList* Arena::CreateMaybeMessage< ::milvus::grpc::PartitionList >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::PartitionList >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::RowRecord* Arena::CreateMaybeMessage< ::milvus::grpc::RowRecord >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::RowRecord >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::InsertParam* Arena::CreateMaybeMessage< ::milvus::grpc::InsertParam >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::InsertParam >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::VectorIds* Arena::CreateMaybeMessage< ::milvus::grpc::VectorIds >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::VectorIds >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::SearchParam* Arena::CreateMaybeMessage< ::milvus::grpc::SearchParam >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::SearchParam >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::SearchInFilesParam* Arena::CreateMaybeMessage< ::milvus::grpc::SearchInFilesParam >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::SearchInFilesParam >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::SearchByIDParam* Arena::CreateMaybeMessage< ::milvus::grpc::SearchByIDParam >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::SearchByIDParam >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::ReLoadSegmentsParam* Arena::CreateMaybeMessage< ::milvus::grpc::ReLoadSegmentsParam >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::ReLoadSegmentsParam >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::TopKQueryResult* Arena::CreateMaybeMessage< ::milvus::grpc::TopKQueryResult >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::TopKQueryResult >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::StringReply* Arena::CreateMaybeMessage< ::milvus::grpc::StringReply >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::StringReply >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::BoolReply* Arena::CreateMaybeMessage< ::milvus::grpc::BoolReply >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::BoolReply >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::CollectionRowCount* Arena::CreateMaybeMessage< ::milvus::grpc::CollectionRowCount >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::CollectionRowCount >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::Command* Arena::CreateMaybeMessage< ::milvus::grpc::Command >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::Command >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::IndexParam* Arena::CreateMaybeMessage< ::milvus::grpc::IndexParam >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::IndexParam >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::FlushParam* Arena::CreateMaybeMessage< ::milvus::grpc::FlushParam >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::FlushParam >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::DeleteByIDParam* Arena::CreateMaybeMessage< ::milvus::grpc::DeleteByIDParam >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::DeleteByIDParam >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::CollectionInfo* Arena::CreateMaybeMessage< ::milvus::grpc::CollectionInfo >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::CollectionInfo >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::VectorsIdentity* Arena::CreateMaybeMessage< ::milvus::grpc::VectorsIdentity >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::VectorsIdentity >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::VectorsData* Arena::CreateMaybeMessage< ::milvus::grpc::VectorsData >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::VectorsData >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::GetVectorIDsParam* Arena::CreateMaybeMessage< ::milvus::grpc::GetVectorIDsParam >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::GetVectorIDsParam >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::VectorFieldParam* Arena::CreateMaybeMessage< ::milvus::grpc::VectorFieldParam >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::VectorFieldParam >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::FieldType* Arena::CreateMaybeMessage< ::milvus::grpc::FieldType >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::FieldType >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::FieldParam* Arena::CreateMaybeMessage< ::milvus::grpc::FieldParam >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::FieldParam >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::VectorFieldValue* Arena::CreateMaybeMessage< ::milvus::grpc::VectorFieldValue >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::VectorFieldValue >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::FieldValue* Arena::CreateMaybeMessage< ::milvus::grpc::FieldValue >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::FieldValue >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::Mapping* Arena::CreateMaybeMessage< ::milvus::grpc::Mapping >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::Mapping >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::MappingList* Arena::CreateMaybeMessage< ::milvus::grpc::MappingList >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::MappingList >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::TermQuery* Arena::CreateMaybeMessage< ::milvus::grpc::TermQuery >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::TermQuery >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::CompareExpr* Arena::CreateMaybeMessage< ::milvus::grpc::CompareExpr >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::CompareExpr >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::RangeQuery* Arena::CreateMaybeMessage< ::milvus::grpc::RangeQuery >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::RangeQuery >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::VectorQuery* Arena::CreateMaybeMessage< ::milvus::grpc::VectorQuery >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::VectorQuery >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::BooleanQuery* Arena::CreateMaybeMessage< ::milvus::grpc::BooleanQuery >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::BooleanQuery >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::GeneralQuery* Arena::CreateMaybeMessage< ::milvus::grpc::GeneralQuery >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::GeneralQuery >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::HSearchParam* Arena::CreateMaybeMessage< ::milvus::grpc::HSearchParam >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::HSearchParam >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::HSearchInSegmentsParam* Arena::CreateMaybeMessage< ::milvus::grpc::HSearchInSegmentsParam >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::HSearchInSegmentsParam >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::AttrRecord* Arena::CreateMaybeMessage< ::milvus::grpc::AttrRecord >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::AttrRecord >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::HEntity* Arena::CreateMaybeMessage< ::milvus::grpc::HEntity >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::HEntity >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::HQueryResult* Arena::CreateMaybeMessage< ::milvus::grpc::HQueryResult >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::HQueryResult >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::HInsertParam* Arena::CreateMaybeMessage< ::milvus::grpc::HInsertParam >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::HInsertParam >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::HEntityIdentity* Arena::CreateMaybeMessage< ::milvus::grpc::HEntityIdentity >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::HEntityIdentity >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::HEntityIDs* Arena::CreateMaybeMessage< ::milvus::grpc::HEntityIDs >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::HEntityIDs >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::HGetEntityIDsParam* Arena::CreateMaybeMessage< ::milvus::grpc::HGetEntityIDsParam >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::HGetEntityIDsParam >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::HDeleteByIDParam* Arena::CreateMaybeMessage< ::milvus::grpc::HDeleteByIDParam >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::HDeleteByIDParam >(arena); -} -template<> PROTOBUF_NOINLINE ::milvus::grpc::HIndexParam* Arena::CreateMaybeMessage< ::milvus::grpc::HIndexParam >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::HIndexParam >(arena); -} -PROTOBUF_NAMESPACE_CLOSE - -// @@protoc_insertion_point(global_scope) -#include diff --git a/core/src/grpc/gen-milvus/milvus.pb.h b/core/src/grpc/gen-milvus/milvus.pb.h deleted file mode 100644 index 76ff7b9402..0000000000 --- a/core/src/grpc/gen-milvus/milvus.pb.h +++ /dev/null @@ -1,14035 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: milvus.proto - -#ifndef GOOGLE_PROTOBUF_INCLUDED_milvus_2eproto -#define GOOGLE_PROTOBUF_INCLUDED_milvus_2eproto - -#include -#include - -#include -#if PROTOBUF_VERSION < 3009000 -#error This file was generated by a newer version of protoc which is -#error incompatible with your Protocol Buffer headers. Please update -#error your headers. -#endif -#if 3009000 < PROTOBUF_MIN_PROTOC_VERSION -#error This file was generated by an older version of protoc which is -#error incompatible with your Protocol Buffer headers. Please -#error regenerate this file with a newer version of protoc. -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // IWYU pragma: export -#include // IWYU pragma: export -#include -#include -#include "status.pb.h" -// @@protoc_insertion_point(includes) -#include -#define PROTOBUF_INTERNAL_EXPORT_milvus_2eproto -PROTOBUF_NAMESPACE_OPEN -namespace internal { -class AnyMetadata; -} // namespace internal -PROTOBUF_NAMESPACE_CLOSE - -// Internal implementation detail -- do not use these members. -struct TableStruct_milvus_2eproto { - static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[] - PROTOBUF_SECTION_VARIABLE(protodesc_cold); - static const ::PROTOBUF_NAMESPACE_ID::internal::AuxillaryParseTableField aux[] - PROTOBUF_SECTION_VARIABLE(protodesc_cold); - static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[49] - PROTOBUF_SECTION_VARIABLE(protodesc_cold); - static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[]; - static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[]; - static const ::PROTOBUF_NAMESPACE_ID::uint32 offsets[]; -}; -extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_milvus_2eproto; -namespace milvus { -namespace grpc { -class AttrRecord; -class AttrRecordDefaultTypeInternal; -extern AttrRecordDefaultTypeInternal _AttrRecord_default_instance_; -class BoolReply; -class BoolReplyDefaultTypeInternal; -extern BoolReplyDefaultTypeInternal _BoolReply_default_instance_; -class BooleanQuery; -class BooleanQueryDefaultTypeInternal; -extern BooleanQueryDefaultTypeInternal _BooleanQuery_default_instance_; -class CollectionInfo; -class CollectionInfoDefaultTypeInternal; -extern CollectionInfoDefaultTypeInternal _CollectionInfo_default_instance_; -class CollectionName; -class CollectionNameDefaultTypeInternal; -extern CollectionNameDefaultTypeInternal _CollectionName_default_instance_; -class CollectionNameList; -class CollectionNameListDefaultTypeInternal; -extern CollectionNameListDefaultTypeInternal _CollectionNameList_default_instance_; -class CollectionRowCount; -class CollectionRowCountDefaultTypeInternal; -extern CollectionRowCountDefaultTypeInternal _CollectionRowCount_default_instance_; -class CollectionSchema; -class CollectionSchemaDefaultTypeInternal; -extern CollectionSchemaDefaultTypeInternal _CollectionSchema_default_instance_; -class Command; -class CommandDefaultTypeInternal; -extern CommandDefaultTypeInternal _Command_default_instance_; -class CompareExpr; -class CompareExprDefaultTypeInternal; -extern CompareExprDefaultTypeInternal _CompareExpr_default_instance_; -class DeleteByIDParam; -class DeleteByIDParamDefaultTypeInternal; -extern DeleteByIDParamDefaultTypeInternal _DeleteByIDParam_default_instance_; -class FieldParam; -class FieldParamDefaultTypeInternal; -extern FieldParamDefaultTypeInternal _FieldParam_default_instance_; -class FieldType; -class FieldTypeDefaultTypeInternal; -extern FieldTypeDefaultTypeInternal _FieldType_default_instance_; -class FieldValue; -class FieldValueDefaultTypeInternal; -extern FieldValueDefaultTypeInternal _FieldValue_default_instance_; -class FlushParam; -class FlushParamDefaultTypeInternal; -extern FlushParamDefaultTypeInternal _FlushParam_default_instance_; -class GeneralQuery; -class GeneralQueryDefaultTypeInternal; -extern GeneralQueryDefaultTypeInternal _GeneralQuery_default_instance_; -class GetVectorIDsParam; -class GetVectorIDsParamDefaultTypeInternal; -extern GetVectorIDsParamDefaultTypeInternal _GetVectorIDsParam_default_instance_; -class HDeleteByIDParam; -class HDeleteByIDParamDefaultTypeInternal; -extern HDeleteByIDParamDefaultTypeInternal _HDeleteByIDParam_default_instance_; -class HEntity; -class HEntityDefaultTypeInternal; -extern HEntityDefaultTypeInternal _HEntity_default_instance_; -class HEntityIDs; -class HEntityIDsDefaultTypeInternal; -extern HEntityIDsDefaultTypeInternal _HEntityIDs_default_instance_; -class HEntityIdentity; -class HEntityIdentityDefaultTypeInternal; -extern HEntityIdentityDefaultTypeInternal _HEntityIdentity_default_instance_; -class HGetEntityIDsParam; -class HGetEntityIDsParamDefaultTypeInternal; -extern HGetEntityIDsParamDefaultTypeInternal _HGetEntityIDsParam_default_instance_; -class HIndexParam; -class HIndexParamDefaultTypeInternal; -extern HIndexParamDefaultTypeInternal _HIndexParam_default_instance_; -class HInsertParam; -class HInsertParamDefaultTypeInternal; -extern HInsertParamDefaultTypeInternal _HInsertParam_default_instance_; -class HQueryResult; -class HQueryResultDefaultTypeInternal; -extern HQueryResultDefaultTypeInternal _HQueryResult_default_instance_; -class HSearchInSegmentsParam; -class HSearchInSegmentsParamDefaultTypeInternal; -extern HSearchInSegmentsParamDefaultTypeInternal _HSearchInSegmentsParam_default_instance_; -class HSearchParam; -class HSearchParamDefaultTypeInternal; -extern HSearchParamDefaultTypeInternal _HSearchParam_default_instance_; -class IndexParam; -class IndexParamDefaultTypeInternal; -extern IndexParamDefaultTypeInternal _IndexParam_default_instance_; -class InsertParam; -class InsertParamDefaultTypeInternal; -extern InsertParamDefaultTypeInternal _InsertParam_default_instance_; -class KeyValuePair; -class KeyValuePairDefaultTypeInternal; -extern KeyValuePairDefaultTypeInternal _KeyValuePair_default_instance_; -class Mapping; -class MappingDefaultTypeInternal; -extern MappingDefaultTypeInternal _Mapping_default_instance_; -class MappingList; -class MappingListDefaultTypeInternal; -extern MappingListDefaultTypeInternal _MappingList_default_instance_; -class PartitionList; -class PartitionListDefaultTypeInternal; -extern PartitionListDefaultTypeInternal _PartitionList_default_instance_; -class PartitionParam; -class PartitionParamDefaultTypeInternal; -extern PartitionParamDefaultTypeInternal _PartitionParam_default_instance_; -class RangeQuery; -class RangeQueryDefaultTypeInternal; -extern RangeQueryDefaultTypeInternal _RangeQuery_default_instance_; -class ReLoadSegmentsParam; -class ReLoadSegmentsParamDefaultTypeInternal; -extern ReLoadSegmentsParamDefaultTypeInternal _ReLoadSegmentsParam_default_instance_; -class RowRecord; -class RowRecordDefaultTypeInternal; -extern RowRecordDefaultTypeInternal _RowRecord_default_instance_; -class SearchByIDParam; -class SearchByIDParamDefaultTypeInternal; -extern SearchByIDParamDefaultTypeInternal _SearchByIDParam_default_instance_; -class SearchInFilesParam; -class SearchInFilesParamDefaultTypeInternal; -extern SearchInFilesParamDefaultTypeInternal _SearchInFilesParam_default_instance_; -class SearchParam; -class SearchParamDefaultTypeInternal; -extern SearchParamDefaultTypeInternal _SearchParam_default_instance_; -class StringReply; -class StringReplyDefaultTypeInternal; -extern StringReplyDefaultTypeInternal _StringReply_default_instance_; -class TermQuery; -class TermQueryDefaultTypeInternal; -extern TermQueryDefaultTypeInternal _TermQuery_default_instance_; -class TopKQueryResult; -class TopKQueryResultDefaultTypeInternal; -extern TopKQueryResultDefaultTypeInternal _TopKQueryResult_default_instance_; -class VectorFieldParam; -class VectorFieldParamDefaultTypeInternal; -extern VectorFieldParamDefaultTypeInternal _VectorFieldParam_default_instance_; -class VectorFieldValue; -class VectorFieldValueDefaultTypeInternal; -extern VectorFieldValueDefaultTypeInternal _VectorFieldValue_default_instance_; -class VectorIds; -class VectorIdsDefaultTypeInternal; -extern VectorIdsDefaultTypeInternal _VectorIds_default_instance_; -class VectorQuery; -class VectorQueryDefaultTypeInternal; -extern VectorQueryDefaultTypeInternal _VectorQuery_default_instance_; -class VectorsData; -class VectorsDataDefaultTypeInternal; -extern VectorsDataDefaultTypeInternal _VectorsData_default_instance_; -class VectorsIdentity; -class VectorsIdentityDefaultTypeInternal; -extern VectorsIdentityDefaultTypeInternal _VectorsIdentity_default_instance_; -} // namespace grpc -} // namespace milvus -PROTOBUF_NAMESPACE_OPEN -template<> ::milvus::grpc::AttrRecord* Arena::CreateMaybeMessage<::milvus::grpc::AttrRecord>(Arena*); -template<> ::milvus::grpc::BoolReply* Arena::CreateMaybeMessage<::milvus::grpc::BoolReply>(Arena*); -template<> ::milvus::grpc::BooleanQuery* Arena::CreateMaybeMessage<::milvus::grpc::BooleanQuery>(Arena*); -template<> ::milvus::grpc::CollectionInfo* Arena::CreateMaybeMessage<::milvus::grpc::CollectionInfo>(Arena*); -template<> ::milvus::grpc::CollectionName* Arena::CreateMaybeMessage<::milvus::grpc::CollectionName>(Arena*); -template<> ::milvus::grpc::CollectionNameList* Arena::CreateMaybeMessage<::milvus::grpc::CollectionNameList>(Arena*); -template<> ::milvus::grpc::CollectionRowCount* Arena::CreateMaybeMessage<::milvus::grpc::CollectionRowCount>(Arena*); -template<> ::milvus::grpc::CollectionSchema* Arena::CreateMaybeMessage<::milvus::grpc::CollectionSchema>(Arena*); -template<> ::milvus::grpc::Command* Arena::CreateMaybeMessage<::milvus::grpc::Command>(Arena*); -template<> ::milvus::grpc::CompareExpr* Arena::CreateMaybeMessage<::milvus::grpc::CompareExpr>(Arena*); -template<> ::milvus::grpc::DeleteByIDParam* Arena::CreateMaybeMessage<::milvus::grpc::DeleteByIDParam>(Arena*); -template<> ::milvus::grpc::FieldParam* Arena::CreateMaybeMessage<::milvus::grpc::FieldParam>(Arena*); -template<> ::milvus::grpc::FieldType* Arena::CreateMaybeMessage<::milvus::grpc::FieldType>(Arena*); -template<> ::milvus::grpc::FieldValue* Arena::CreateMaybeMessage<::milvus::grpc::FieldValue>(Arena*); -template<> ::milvus::grpc::FlushParam* Arena::CreateMaybeMessage<::milvus::grpc::FlushParam>(Arena*); -template<> ::milvus::grpc::GeneralQuery* Arena::CreateMaybeMessage<::milvus::grpc::GeneralQuery>(Arena*); -template<> ::milvus::grpc::GetVectorIDsParam* Arena::CreateMaybeMessage<::milvus::grpc::GetVectorIDsParam>(Arena*); -template<> ::milvus::grpc::HDeleteByIDParam* Arena::CreateMaybeMessage<::milvus::grpc::HDeleteByIDParam>(Arena*); -template<> ::milvus::grpc::HEntity* Arena::CreateMaybeMessage<::milvus::grpc::HEntity>(Arena*); -template<> ::milvus::grpc::HEntityIDs* Arena::CreateMaybeMessage<::milvus::grpc::HEntityIDs>(Arena*); -template<> ::milvus::grpc::HEntityIdentity* Arena::CreateMaybeMessage<::milvus::grpc::HEntityIdentity>(Arena*); -template<> ::milvus::grpc::HGetEntityIDsParam* Arena::CreateMaybeMessage<::milvus::grpc::HGetEntityIDsParam>(Arena*); -template<> ::milvus::grpc::HIndexParam* Arena::CreateMaybeMessage<::milvus::grpc::HIndexParam>(Arena*); -template<> ::milvus::grpc::HInsertParam* Arena::CreateMaybeMessage<::milvus::grpc::HInsertParam>(Arena*); -template<> ::milvus::grpc::HQueryResult* Arena::CreateMaybeMessage<::milvus::grpc::HQueryResult>(Arena*); -template<> ::milvus::grpc::HSearchInSegmentsParam* Arena::CreateMaybeMessage<::milvus::grpc::HSearchInSegmentsParam>(Arena*); -template<> ::milvus::grpc::HSearchParam* Arena::CreateMaybeMessage<::milvus::grpc::HSearchParam>(Arena*); -template<> ::milvus::grpc::IndexParam* Arena::CreateMaybeMessage<::milvus::grpc::IndexParam>(Arena*); -template<> ::milvus::grpc::InsertParam* Arena::CreateMaybeMessage<::milvus::grpc::InsertParam>(Arena*); -template<> ::milvus::grpc::KeyValuePair* Arena::CreateMaybeMessage<::milvus::grpc::KeyValuePair>(Arena*); -template<> ::milvus::grpc::Mapping* Arena::CreateMaybeMessage<::milvus::grpc::Mapping>(Arena*); -template<> ::milvus::grpc::MappingList* Arena::CreateMaybeMessage<::milvus::grpc::MappingList>(Arena*); -template<> ::milvus::grpc::PartitionList* Arena::CreateMaybeMessage<::milvus::grpc::PartitionList>(Arena*); -template<> ::milvus::grpc::PartitionParam* Arena::CreateMaybeMessage<::milvus::grpc::PartitionParam>(Arena*); -template<> ::milvus::grpc::RangeQuery* Arena::CreateMaybeMessage<::milvus::grpc::RangeQuery>(Arena*); -template<> ::milvus::grpc::ReLoadSegmentsParam* Arena::CreateMaybeMessage<::milvus::grpc::ReLoadSegmentsParam>(Arena*); -template<> ::milvus::grpc::RowRecord* Arena::CreateMaybeMessage<::milvus::grpc::RowRecord>(Arena*); -template<> ::milvus::grpc::SearchByIDParam* Arena::CreateMaybeMessage<::milvus::grpc::SearchByIDParam>(Arena*); -template<> ::milvus::grpc::SearchInFilesParam* Arena::CreateMaybeMessage<::milvus::grpc::SearchInFilesParam>(Arena*); -template<> ::milvus::grpc::SearchParam* Arena::CreateMaybeMessage<::milvus::grpc::SearchParam>(Arena*); -template<> ::milvus::grpc::StringReply* Arena::CreateMaybeMessage<::milvus::grpc::StringReply>(Arena*); -template<> ::milvus::grpc::TermQuery* Arena::CreateMaybeMessage<::milvus::grpc::TermQuery>(Arena*); -template<> ::milvus::grpc::TopKQueryResult* Arena::CreateMaybeMessage<::milvus::grpc::TopKQueryResult>(Arena*); -template<> ::milvus::grpc::VectorFieldParam* Arena::CreateMaybeMessage<::milvus::grpc::VectorFieldParam>(Arena*); -template<> ::milvus::grpc::VectorFieldValue* Arena::CreateMaybeMessage<::milvus::grpc::VectorFieldValue>(Arena*); -template<> ::milvus::grpc::VectorIds* Arena::CreateMaybeMessage<::milvus::grpc::VectorIds>(Arena*); -template<> ::milvus::grpc::VectorQuery* Arena::CreateMaybeMessage<::milvus::grpc::VectorQuery>(Arena*); -template<> ::milvus::grpc::VectorsData* Arena::CreateMaybeMessage<::milvus::grpc::VectorsData>(Arena*); -template<> ::milvus::grpc::VectorsIdentity* Arena::CreateMaybeMessage<::milvus::grpc::VectorsIdentity>(Arena*); -PROTOBUF_NAMESPACE_CLOSE -namespace milvus { -namespace grpc { - -enum DataType : int { - NULL_ = 0, - INT8 = 1, - INT16 = 2, - INT32 = 3, - INT64 = 4, - STRING = 20, - BOOL = 30, - FLOAT = 40, - DOUBLE = 41, - VECTOR = 100, - UNKNOWN = 9999, - DataType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::min(), - DataType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::max() -}; -bool DataType_IsValid(int value); -constexpr DataType DataType_MIN = NULL_; -constexpr DataType DataType_MAX = UNKNOWN; -constexpr int DataType_ARRAYSIZE = DataType_MAX + 1; - -const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* DataType_descriptor(); -template -inline const std::string& DataType_Name(T enum_t_value) { - static_assert(::std::is_same::value || - ::std::is_integral::value, - "Incorrect type passed to function DataType_Name."); - return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum( - DataType_descriptor(), enum_t_value); -} -inline bool DataType_Parse( - const std::string& name, DataType* value) { - return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum( - DataType_descriptor(), name, value); -} -enum CompareOperator : int { - LT = 0, - LTE = 1, - EQ = 2, - GT = 3, - GTE = 4, - NE = 5, - CompareOperator_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::min(), - CompareOperator_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::max() -}; -bool CompareOperator_IsValid(int value); -constexpr CompareOperator CompareOperator_MIN = LT; -constexpr CompareOperator CompareOperator_MAX = NE; -constexpr int CompareOperator_ARRAYSIZE = CompareOperator_MAX + 1; - -const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* CompareOperator_descriptor(); -template -inline const std::string& CompareOperator_Name(T enum_t_value) { - static_assert(::std::is_same::value || - ::std::is_integral::value, - "Incorrect type passed to function CompareOperator_Name."); - return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum( - CompareOperator_descriptor(), enum_t_value); -} -inline bool CompareOperator_Parse( - const std::string& name, CompareOperator* value) { - return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum( - CompareOperator_descriptor(), name, value); -} -enum Occur : int { - INVALID = 0, - MUST = 1, - SHOULD = 2, - MUST_NOT = 3, - Occur_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::min(), - Occur_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::max() -}; -bool Occur_IsValid(int value); -constexpr Occur Occur_MIN = INVALID; -constexpr Occur Occur_MAX = MUST_NOT; -constexpr int Occur_ARRAYSIZE = Occur_MAX + 1; - -const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* Occur_descriptor(); -template -inline const std::string& Occur_Name(T enum_t_value) { - static_assert(::std::is_same::value || - ::std::is_integral::value, - "Incorrect type passed to function Occur_Name."); - return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum( - Occur_descriptor(), enum_t_value); -} -inline bool Occur_Parse( - const std::string& name, Occur* value) { - return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum( - Occur_descriptor(), name, value); -} -// =================================================================== - -class KeyValuePair : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.KeyValuePair) */ { - public: - KeyValuePair(); - virtual ~KeyValuePair(); - - KeyValuePair(const KeyValuePair& from); - KeyValuePair(KeyValuePair&& from) noexcept - : KeyValuePair() { - *this = ::std::move(from); - } - - inline KeyValuePair& operator=(const KeyValuePair& from) { - CopyFrom(from); - return *this; - } - inline KeyValuePair& operator=(KeyValuePair&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const KeyValuePair& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const KeyValuePair* internal_default_instance() { - return reinterpret_cast( - &_KeyValuePair_default_instance_); - } - static constexpr int kIndexInFileMessages = - 0; - - friend void swap(KeyValuePair& a, KeyValuePair& b) { - a.Swap(&b); - } - inline void Swap(KeyValuePair* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline KeyValuePair* New() const final { - return CreateMaybeMessage(nullptr); - } - - KeyValuePair* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const KeyValuePair& from); - void MergeFrom(const KeyValuePair& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(KeyValuePair* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.KeyValuePair"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kKeyFieldNumber = 1, - kValueFieldNumber = 2, - }; - // string key = 1; - void clear_key(); - const std::string& key() const; - void set_key(const std::string& value); - void set_key(std::string&& value); - void set_key(const char* value); - void set_key(const char* value, size_t size); - std::string* mutable_key(); - std::string* release_key(); - void set_allocated_key(std::string* key); - - // string value = 2; - void clear_value(); - const std::string& value() const; - void set_value(const std::string& value); - void set_value(std::string&& value); - void set_value(const char* value); - void set_value(const char* value, size_t size); - std::string* mutable_value(); - std::string* release_value(); - void set_allocated_value(std::string* value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.KeyValuePair) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr key_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr value_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class CollectionName : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.CollectionName) */ { - public: - CollectionName(); - virtual ~CollectionName(); - - CollectionName(const CollectionName& from); - CollectionName(CollectionName&& from) noexcept - : CollectionName() { - *this = ::std::move(from); - } - - inline CollectionName& operator=(const CollectionName& from) { - CopyFrom(from); - return *this; - } - inline CollectionName& operator=(CollectionName&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const CollectionName& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const CollectionName* internal_default_instance() { - return reinterpret_cast( - &_CollectionName_default_instance_); - } - static constexpr int kIndexInFileMessages = - 1; - - friend void swap(CollectionName& a, CollectionName& b) { - a.Swap(&b); - } - inline void Swap(CollectionName* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline CollectionName* New() const final { - return CreateMaybeMessage(nullptr); - } - - CollectionName* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const CollectionName& from); - void MergeFrom(const CollectionName& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(CollectionName* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.CollectionName"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kCollectionNameFieldNumber = 1, - }; - // string collection_name = 1; - void clear_collection_name(); - const std::string& collection_name() const; - void set_collection_name(const std::string& value); - void set_collection_name(std::string&& value); - void set_collection_name(const char* value); - void set_collection_name(const char* value, size_t size); - std::string* mutable_collection_name(); - std::string* release_collection_name(); - void set_allocated_collection_name(std::string* collection_name); - - // @@protoc_insertion_point(class_scope:milvus.grpc.CollectionName) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr collection_name_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class CollectionNameList : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.CollectionNameList) */ { - public: - CollectionNameList(); - virtual ~CollectionNameList(); - - CollectionNameList(const CollectionNameList& from); - CollectionNameList(CollectionNameList&& from) noexcept - : CollectionNameList() { - *this = ::std::move(from); - } - - inline CollectionNameList& operator=(const CollectionNameList& from) { - CopyFrom(from); - return *this; - } - inline CollectionNameList& operator=(CollectionNameList&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const CollectionNameList& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const CollectionNameList* internal_default_instance() { - return reinterpret_cast( - &_CollectionNameList_default_instance_); - } - static constexpr int kIndexInFileMessages = - 2; - - friend void swap(CollectionNameList& a, CollectionNameList& b) { - a.Swap(&b); - } - inline void Swap(CollectionNameList* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline CollectionNameList* New() const final { - return CreateMaybeMessage(nullptr); - } - - CollectionNameList* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const CollectionNameList& from); - void MergeFrom(const CollectionNameList& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(CollectionNameList* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.CollectionNameList"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kCollectionNamesFieldNumber = 2, - kStatusFieldNumber = 1, - }; - // repeated string collection_names = 2; - int collection_names_size() const; - void clear_collection_names(); - const std::string& collection_names(int index) const; - std::string* mutable_collection_names(int index); - void set_collection_names(int index, const std::string& value); - void set_collection_names(int index, std::string&& value); - void set_collection_names(int index, const char* value); - void set_collection_names(int index, const char* value, size_t size); - std::string* add_collection_names(); - void add_collection_names(const std::string& value); - void add_collection_names(std::string&& value); - void add_collection_names(const char* value); - void add_collection_names(const char* value, size_t size); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& collection_names() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* mutable_collection_names(); - - // .milvus.grpc.Status status = 1; - bool has_status() const; - void clear_status(); - const ::milvus::grpc::Status& status() const; - ::milvus::grpc::Status* release_status(); - ::milvus::grpc::Status* mutable_status(); - void set_allocated_status(::milvus::grpc::Status* status); - - // @@protoc_insertion_point(class_scope:milvus.grpc.CollectionNameList) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField collection_names_; - ::milvus::grpc::Status* status_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class CollectionSchema : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.CollectionSchema) */ { - public: - CollectionSchema(); - virtual ~CollectionSchema(); - - CollectionSchema(const CollectionSchema& from); - CollectionSchema(CollectionSchema&& from) noexcept - : CollectionSchema() { - *this = ::std::move(from); - } - - inline CollectionSchema& operator=(const CollectionSchema& from) { - CopyFrom(from); - return *this; - } - inline CollectionSchema& operator=(CollectionSchema&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const CollectionSchema& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const CollectionSchema* internal_default_instance() { - return reinterpret_cast( - &_CollectionSchema_default_instance_); - } - static constexpr int kIndexInFileMessages = - 3; - - friend void swap(CollectionSchema& a, CollectionSchema& b) { - a.Swap(&b); - } - inline void Swap(CollectionSchema* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline CollectionSchema* New() const final { - return CreateMaybeMessage(nullptr); - } - - CollectionSchema* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const CollectionSchema& from); - void MergeFrom(const CollectionSchema& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(CollectionSchema* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.CollectionSchema"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kExtraParamsFieldNumber = 6, - kCollectionNameFieldNumber = 2, - kStatusFieldNumber = 1, - kDimensionFieldNumber = 3, - kIndexFileSizeFieldNumber = 4, - kMetricTypeFieldNumber = 5, - }; - // repeated .milvus.grpc.KeyValuePair extra_params = 6; - int extra_params_size() const; - void clear_extra_params(); - ::milvus::grpc::KeyValuePair* mutable_extra_params(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* - mutable_extra_params(); - const ::milvus::grpc::KeyValuePair& extra_params(int index) const; - ::milvus::grpc::KeyValuePair* add_extra_params(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& - extra_params() const; - - // string collection_name = 2; - void clear_collection_name(); - const std::string& collection_name() const; - void set_collection_name(const std::string& value); - void set_collection_name(std::string&& value); - void set_collection_name(const char* value); - void set_collection_name(const char* value, size_t size); - std::string* mutable_collection_name(); - std::string* release_collection_name(); - void set_allocated_collection_name(std::string* collection_name); - - // .milvus.grpc.Status status = 1; - bool has_status() const; - void clear_status(); - const ::milvus::grpc::Status& status() const; - ::milvus::grpc::Status* release_status(); - ::milvus::grpc::Status* mutable_status(); - void set_allocated_status(::milvus::grpc::Status* status); - - // int64 dimension = 3; - void clear_dimension(); - ::PROTOBUF_NAMESPACE_ID::int64 dimension() const; - void set_dimension(::PROTOBUF_NAMESPACE_ID::int64 value); - - // int64 index_file_size = 4; - void clear_index_file_size(); - ::PROTOBUF_NAMESPACE_ID::int64 index_file_size() const; - void set_index_file_size(::PROTOBUF_NAMESPACE_ID::int64 value); - - // int32 metric_type = 5; - void clear_metric_type(); - ::PROTOBUF_NAMESPACE_ID::int32 metric_type() const; - void set_metric_type(::PROTOBUF_NAMESPACE_ID::int32 value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.CollectionSchema) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair > extra_params_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr collection_name_; - ::milvus::grpc::Status* status_; - ::PROTOBUF_NAMESPACE_ID::int64 dimension_; - ::PROTOBUF_NAMESPACE_ID::int64 index_file_size_; - ::PROTOBUF_NAMESPACE_ID::int32 metric_type_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class PartitionParam : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.PartitionParam) */ { - public: - PartitionParam(); - virtual ~PartitionParam(); - - PartitionParam(const PartitionParam& from); - PartitionParam(PartitionParam&& from) noexcept - : PartitionParam() { - *this = ::std::move(from); - } - - inline PartitionParam& operator=(const PartitionParam& from) { - CopyFrom(from); - return *this; - } - inline PartitionParam& operator=(PartitionParam&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const PartitionParam& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const PartitionParam* internal_default_instance() { - return reinterpret_cast( - &_PartitionParam_default_instance_); - } - static constexpr int kIndexInFileMessages = - 4; - - friend void swap(PartitionParam& a, PartitionParam& b) { - a.Swap(&b); - } - inline void Swap(PartitionParam* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline PartitionParam* New() const final { - return CreateMaybeMessage(nullptr); - } - - PartitionParam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const PartitionParam& from); - void MergeFrom(const PartitionParam& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(PartitionParam* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.PartitionParam"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kCollectionNameFieldNumber = 1, - kTagFieldNumber = 2, - }; - // string collection_name = 1; - void clear_collection_name(); - const std::string& collection_name() const; - void set_collection_name(const std::string& value); - void set_collection_name(std::string&& value); - void set_collection_name(const char* value); - void set_collection_name(const char* value, size_t size); - std::string* mutable_collection_name(); - std::string* release_collection_name(); - void set_allocated_collection_name(std::string* collection_name); - - // string tag = 2; - void clear_tag(); - const std::string& tag() const; - void set_tag(const std::string& value); - void set_tag(std::string&& value); - void set_tag(const char* value); - void set_tag(const char* value, size_t size); - std::string* mutable_tag(); - std::string* release_tag(); - void set_allocated_tag(std::string* tag); - - // @@protoc_insertion_point(class_scope:milvus.grpc.PartitionParam) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr collection_name_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr tag_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class PartitionList : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.PartitionList) */ { - public: - PartitionList(); - virtual ~PartitionList(); - - PartitionList(const PartitionList& from); - PartitionList(PartitionList&& from) noexcept - : PartitionList() { - *this = ::std::move(from); - } - - inline PartitionList& operator=(const PartitionList& from) { - CopyFrom(from); - return *this; - } - inline PartitionList& operator=(PartitionList&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const PartitionList& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const PartitionList* internal_default_instance() { - return reinterpret_cast( - &_PartitionList_default_instance_); - } - static constexpr int kIndexInFileMessages = - 5; - - friend void swap(PartitionList& a, PartitionList& b) { - a.Swap(&b); - } - inline void Swap(PartitionList* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline PartitionList* New() const final { - return CreateMaybeMessage(nullptr); - } - - PartitionList* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const PartitionList& from); - void MergeFrom(const PartitionList& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(PartitionList* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.PartitionList"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kPartitionTagArrayFieldNumber = 2, - kStatusFieldNumber = 1, - }; - // repeated string partition_tag_array = 2; - int partition_tag_array_size() const; - void clear_partition_tag_array(); - const std::string& partition_tag_array(int index) const; - std::string* mutable_partition_tag_array(int index); - void set_partition_tag_array(int index, const std::string& value); - void set_partition_tag_array(int index, std::string&& value); - void set_partition_tag_array(int index, const char* value); - void set_partition_tag_array(int index, const char* value, size_t size); - std::string* add_partition_tag_array(); - void add_partition_tag_array(const std::string& value); - void add_partition_tag_array(std::string&& value); - void add_partition_tag_array(const char* value); - void add_partition_tag_array(const char* value, size_t size); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& partition_tag_array() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* mutable_partition_tag_array(); - - // .milvus.grpc.Status status = 1; - bool has_status() const; - void clear_status(); - const ::milvus::grpc::Status& status() const; - ::milvus::grpc::Status* release_status(); - ::milvus::grpc::Status* mutable_status(); - void set_allocated_status(::milvus::grpc::Status* status); - - // @@protoc_insertion_point(class_scope:milvus.grpc.PartitionList) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField partition_tag_array_; - ::milvus::grpc::Status* status_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class RowRecord : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.RowRecord) */ { - public: - RowRecord(); - virtual ~RowRecord(); - - RowRecord(const RowRecord& from); - RowRecord(RowRecord&& from) noexcept - : RowRecord() { - *this = ::std::move(from); - } - - inline RowRecord& operator=(const RowRecord& from) { - CopyFrom(from); - return *this; - } - inline RowRecord& operator=(RowRecord&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const RowRecord& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const RowRecord* internal_default_instance() { - return reinterpret_cast( - &_RowRecord_default_instance_); - } - static constexpr int kIndexInFileMessages = - 6; - - friend void swap(RowRecord& a, RowRecord& b) { - a.Swap(&b); - } - inline void Swap(RowRecord* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline RowRecord* New() const final { - return CreateMaybeMessage(nullptr); - } - - RowRecord* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const RowRecord& from); - void MergeFrom(const RowRecord& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(RowRecord* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.RowRecord"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kFloatDataFieldNumber = 1, - kBinaryDataFieldNumber = 2, - }; - // repeated float float_data = 1; - int float_data_size() const; - void clear_float_data(); - float float_data(int index) const; - void set_float_data(int index, float value); - void add_float_data(float value); - const ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >& - float_data() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >* - mutable_float_data(); - - // bytes binary_data = 2; - void clear_binary_data(); - const std::string& binary_data() const; - void set_binary_data(const std::string& value); - void set_binary_data(std::string&& value); - void set_binary_data(const char* value); - void set_binary_data(const void* value, size_t size); - std::string* mutable_binary_data(); - std::string* release_binary_data(); - void set_allocated_binary_data(std::string* binary_data); - - // @@protoc_insertion_point(class_scope:milvus.grpc.RowRecord) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< float > float_data_; - mutable std::atomic _float_data_cached_byte_size_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr binary_data_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class InsertParam : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.InsertParam) */ { - public: - InsertParam(); - virtual ~InsertParam(); - - InsertParam(const InsertParam& from); - InsertParam(InsertParam&& from) noexcept - : InsertParam() { - *this = ::std::move(from); - } - - inline InsertParam& operator=(const InsertParam& from) { - CopyFrom(from); - return *this; - } - inline InsertParam& operator=(InsertParam&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const InsertParam& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const InsertParam* internal_default_instance() { - return reinterpret_cast( - &_InsertParam_default_instance_); - } - static constexpr int kIndexInFileMessages = - 7; - - friend void swap(InsertParam& a, InsertParam& b) { - a.Swap(&b); - } - inline void Swap(InsertParam* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline InsertParam* New() const final { - return CreateMaybeMessage(nullptr); - } - - InsertParam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const InsertParam& from); - void MergeFrom(const InsertParam& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(InsertParam* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.InsertParam"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kRowRecordArrayFieldNumber = 2, - kRowIdArrayFieldNumber = 3, - kExtraParamsFieldNumber = 5, - kCollectionNameFieldNumber = 1, - kPartitionTagFieldNumber = 4, - }; - // repeated .milvus.grpc.RowRecord row_record_array = 2; - int row_record_array_size() const; - void clear_row_record_array(); - ::milvus::grpc::RowRecord* mutable_row_record_array(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >* - mutable_row_record_array(); - const ::milvus::grpc::RowRecord& row_record_array(int index) const; - ::milvus::grpc::RowRecord* add_row_record_array(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >& - row_record_array() const; - - // repeated int64 row_id_array = 3; - int row_id_array_size() const; - void clear_row_id_array(); - ::PROTOBUF_NAMESPACE_ID::int64 row_id_array(int index) const; - void set_row_id_array(int index, ::PROTOBUF_NAMESPACE_ID::int64 value); - void add_row_id_array(::PROTOBUF_NAMESPACE_ID::int64 value); - const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >& - row_id_array() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >* - mutable_row_id_array(); - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - int extra_params_size() const; - void clear_extra_params(); - ::milvus::grpc::KeyValuePair* mutable_extra_params(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* - mutable_extra_params(); - const ::milvus::grpc::KeyValuePair& extra_params(int index) const; - ::milvus::grpc::KeyValuePair* add_extra_params(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& - extra_params() const; - - // string collection_name = 1; - void clear_collection_name(); - const std::string& collection_name() const; - void set_collection_name(const std::string& value); - void set_collection_name(std::string&& value); - void set_collection_name(const char* value); - void set_collection_name(const char* value, size_t size); - std::string* mutable_collection_name(); - std::string* release_collection_name(); - void set_allocated_collection_name(std::string* collection_name); - - // string partition_tag = 4; - void clear_partition_tag(); - const std::string& partition_tag() const; - void set_partition_tag(const std::string& value); - void set_partition_tag(std::string&& value); - void set_partition_tag(const char* value); - void set_partition_tag(const char* value, size_t size); - std::string* mutable_partition_tag(); - std::string* release_partition_tag(); - void set_allocated_partition_tag(std::string* partition_tag); - - // @@protoc_insertion_point(class_scope:milvus.grpc.InsertParam) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord > row_record_array_; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 > row_id_array_; - mutable std::atomic _row_id_array_cached_byte_size_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair > extra_params_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr collection_name_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr partition_tag_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class VectorIds : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.VectorIds) */ { - public: - VectorIds(); - virtual ~VectorIds(); - - VectorIds(const VectorIds& from); - VectorIds(VectorIds&& from) noexcept - : VectorIds() { - *this = ::std::move(from); - } - - inline VectorIds& operator=(const VectorIds& from) { - CopyFrom(from); - return *this; - } - inline VectorIds& operator=(VectorIds&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const VectorIds& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const VectorIds* internal_default_instance() { - return reinterpret_cast( - &_VectorIds_default_instance_); - } - static constexpr int kIndexInFileMessages = - 8; - - friend void swap(VectorIds& a, VectorIds& b) { - a.Swap(&b); - } - inline void Swap(VectorIds* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline VectorIds* New() const final { - return CreateMaybeMessage(nullptr); - } - - VectorIds* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const VectorIds& from); - void MergeFrom(const VectorIds& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(VectorIds* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.VectorIds"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kVectorIdArrayFieldNumber = 2, - kStatusFieldNumber = 1, - }; - // repeated int64 vector_id_array = 2; - int vector_id_array_size() const; - void clear_vector_id_array(); - ::PROTOBUF_NAMESPACE_ID::int64 vector_id_array(int index) const; - void set_vector_id_array(int index, ::PROTOBUF_NAMESPACE_ID::int64 value); - void add_vector_id_array(::PROTOBUF_NAMESPACE_ID::int64 value); - const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >& - vector_id_array() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >* - mutable_vector_id_array(); - - // .milvus.grpc.Status status = 1; - bool has_status() const; - void clear_status(); - const ::milvus::grpc::Status& status() const; - ::milvus::grpc::Status* release_status(); - ::milvus::grpc::Status* mutable_status(); - void set_allocated_status(::milvus::grpc::Status* status); - - // @@protoc_insertion_point(class_scope:milvus.grpc.VectorIds) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 > vector_id_array_; - mutable std::atomic _vector_id_array_cached_byte_size_; - ::milvus::grpc::Status* status_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class SearchParam : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.SearchParam) */ { - public: - SearchParam(); - virtual ~SearchParam(); - - SearchParam(const SearchParam& from); - SearchParam(SearchParam&& from) noexcept - : SearchParam() { - *this = ::std::move(from); - } - - inline SearchParam& operator=(const SearchParam& from) { - CopyFrom(from); - return *this; - } - inline SearchParam& operator=(SearchParam&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const SearchParam& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const SearchParam* internal_default_instance() { - return reinterpret_cast( - &_SearchParam_default_instance_); - } - static constexpr int kIndexInFileMessages = - 9; - - friend void swap(SearchParam& a, SearchParam& b) { - a.Swap(&b); - } - inline void Swap(SearchParam* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline SearchParam* New() const final { - return CreateMaybeMessage(nullptr); - } - - SearchParam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const SearchParam& from); - void MergeFrom(const SearchParam& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(SearchParam* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.SearchParam"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kPartitionTagArrayFieldNumber = 2, - kQueryRecordArrayFieldNumber = 3, - kExtraParamsFieldNumber = 5, - kCollectionNameFieldNumber = 1, - kTopkFieldNumber = 4, - }; - // repeated string partition_tag_array = 2; - int partition_tag_array_size() const; - void clear_partition_tag_array(); - const std::string& partition_tag_array(int index) const; - std::string* mutable_partition_tag_array(int index); - void set_partition_tag_array(int index, const std::string& value); - void set_partition_tag_array(int index, std::string&& value); - void set_partition_tag_array(int index, const char* value); - void set_partition_tag_array(int index, const char* value, size_t size); - std::string* add_partition_tag_array(); - void add_partition_tag_array(const std::string& value); - void add_partition_tag_array(std::string&& value); - void add_partition_tag_array(const char* value); - void add_partition_tag_array(const char* value, size_t size); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& partition_tag_array() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* mutable_partition_tag_array(); - - // repeated .milvus.grpc.RowRecord query_record_array = 3; - int query_record_array_size() const; - void clear_query_record_array(); - ::milvus::grpc::RowRecord* mutable_query_record_array(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >* - mutable_query_record_array(); - const ::milvus::grpc::RowRecord& query_record_array(int index) const; - ::milvus::grpc::RowRecord* add_query_record_array(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >& - query_record_array() const; - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - int extra_params_size() const; - void clear_extra_params(); - ::milvus::grpc::KeyValuePair* mutable_extra_params(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* - mutable_extra_params(); - const ::milvus::grpc::KeyValuePair& extra_params(int index) const; - ::milvus::grpc::KeyValuePair* add_extra_params(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& - extra_params() const; - - // string collection_name = 1; - void clear_collection_name(); - const std::string& collection_name() const; - void set_collection_name(const std::string& value); - void set_collection_name(std::string&& value); - void set_collection_name(const char* value); - void set_collection_name(const char* value, size_t size); - std::string* mutable_collection_name(); - std::string* release_collection_name(); - void set_allocated_collection_name(std::string* collection_name); - - // int64 topk = 4; - void clear_topk(); - ::PROTOBUF_NAMESPACE_ID::int64 topk() const; - void set_topk(::PROTOBUF_NAMESPACE_ID::int64 value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.SearchParam) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField partition_tag_array_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord > query_record_array_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair > extra_params_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr collection_name_; - ::PROTOBUF_NAMESPACE_ID::int64 topk_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class SearchInFilesParam : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.SearchInFilesParam) */ { - public: - SearchInFilesParam(); - virtual ~SearchInFilesParam(); - - SearchInFilesParam(const SearchInFilesParam& from); - SearchInFilesParam(SearchInFilesParam&& from) noexcept - : SearchInFilesParam() { - *this = ::std::move(from); - } - - inline SearchInFilesParam& operator=(const SearchInFilesParam& from) { - CopyFrom(from); - return *this; - } - inline SearchInFilesParam& operator=(SearchInFilesParam&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const SearchInFilesParam& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const SearchInFilesParam* internal_default_instance() { - return reinterpret_cast( - &_SearchInFilesParam_default_instance_); - } - static constexpr int kIndexInFileMessages = - 10; - - friend void swap(SearchInFilesParam& a, SearchInFilesParam& b) { - a.Swap(&b); - } - inline void Swap(SearchInFilesParam* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline SearchInFilesParam* New() const final { - return CreateMaybeMessage(nullptr); - } - - SearchInFilesParam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const SearchInFilesParam& from); - void MergeFrom(const SearchInFilesParam& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(SearchInFilesParam* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.SearchInFilesParam"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kFileIdArrayFieldNumber = 1, - kSearchParamFieldNumber = 2, - }; - // repeated string file_id_array = 1; - int file_id_array_size() const; - void clear_file_id_array(); - const std::string& file_id_array(int index) const; - std::string* mutable_file_id_array(int index); - void set_file_id_array(int index, const std::string& value); - void set_file_id_array(int index, std::string&& value); - void set_file_id_array(int index, const char* value); - void set_file_id_array(int index, const char* value, size_t size); - std::string* add_file_id_array(); - void add_file_id_array(const std::string& value); - void add_file_id_array(std::string&& value); - void add_file_id_array(const char* value); - void add_file_id_array(const char* value, size_t size); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& file_id_array() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* mutable_file_id_array(); - - // .milvus.grpc.SearchParam search_param = 2; - bool has_search_param() const; - void clear_search_param(); - const ::milvus::grpc::SearchParam& search_param() const; - ::milvus::grpc::SearchParam* release_search_param(); - ::milvus::grpc::SearchParam* mutable_search_param(); - void set_allocated_search_param(::milvus::grpc::SearchParam* search_param); - - // @@protoc_insertion_point(class_scope:milvus.grpc.SearchInFilesParam) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField file_id_array_; - ::milvus::grpc::SearchParam* search_param_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class SearchByIDParam : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.SearchByIDParam) */ { - public: - SearchByIDParam(); - virtual ~SearchByIDParam(); - - SearchByIDParam(const SearchByIDParam& from); - SearchByIDParam(SearchByIDParam&& from) noexcept - : SearchByIDParam() { - *this = ::std::move(from); - } - - inline SearchByIDParam& operator=(const SearchByIDParam& from) { - CopyFrom(from); - return *this; - } - inline SearchByIDParam& operator=(SearchByIDParam&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const SearchByIDParam& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const SearchByIDParam* internal_default_instance() { - return reinterpret_cast( - &_SearchByIDParam_default_instance_); - } - static constexpr int kIndexInFileMessages = - 11; - - friend void swap(SearchByIDParam& a, SearchByIDParam& b) { - a.Swap(&b); - } - inline void Swap(SearchByIDParam* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline SearchByIDParam* New() const final { - return CreateMaybeMessage(nullptr); - } - - SearchByIDParam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const SearchByIDParam& from); - void MergeFrom(const SearchByIDParam& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(SearchByIDParam* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.SearchByIDParam"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kPartitionTagArrayFieldNumber = 2, - kIdArrayFieldNumber = 3, - kExtraParamsFieldNumber = 5, - kCollectionNameFieldNumber = 1, - kTopkFieldNumber = 4, - }; - // repeated string partition_tag_array = 2; - int partition_tag_array_size() const; - void clear_partition_tag_array(); - const std::string& partition_tag_array(int index) const; - std::string* mutable_partition_tag_array(int index); - void set_partition_tag_array(int index, const std::string& value); - void set_partition_tag_array(int index, std::string&& value); - void set_partition_tag_array(int index, const char* value); - void set_partition_tag_array(int index, const char* value, size_t size); - std::string* add_partition_tag_array(); - void add_partition_tag_array(const std::string& value); - void add_partition_tag_array(std::string&& value); - void add_partition_tag_array(const char* value); - void add_partition_tag_array(const char* value, size_t size); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& partition_tag_array() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* mutable_partition_tag_array(); - - // repeated int64 id_array = 3; - int id_array_size() const; - void clear_id_array(); - ::PROTOBUF_NAMESPACE_ID::int64 id_array(int index) const; - void set_id_array(int index, ::PROTOBUF_NAMESPACE_ID::int64 value); - void add_id_array(::PROTOBUF_NAMESPACE_ID::int64 value); - const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >& - id_array() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >* - mutable_id_array(); - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - int extra_params_size() const; - void clear_extra_params(); - ::milvus::grpc::KeyValuePair* mutable_extra_params(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* - mutable_extra_params(); - const ::milvus::grpc::KeyValuePair& extra_params(int index) const; - ::milvus::grpc::KeyValuePair* add_extra_params(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& - extra_params() const; - - // string collection_name = 1; - void clear_collection_name(); - const std::string& collection_name() const; - void set_collection_name(const std::string& value); - void set_collection_name(std::string&& value); - void set_collection_name(const char* value); - void set_collection_name(const char* value, size_t size); - std::string* mutable_collection_name(); - std::string* release_collection_name(); - void set_allocated_collection_name(std::string* collection_name); - - // int64 topk = 4; - void clear_topk(); - ::PROTOBUF_NAMESPACE_ID::int64 topk() const; - void set_topk(::PROTOBUF_NAMESPACE_ID::int64 value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.SearchByIDParam) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField partition_tag_array_; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 > id_array_; - mutable std::atomic _id_array_cached_byte_size_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair > extra_params_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr collection_name_; - ::PROTOBUF_NAMESPACE_ID::int64 topk_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class ReLoadSegmentsParam : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.ReLoadSegmentsParam) */ { - public: - ReLoadSegmentsParam(); - virtual ~ReLoadSegmentsParam(); - - ReLoadSegmentsParam(const ReLoadSegmentsParam& from); - ReLoadSegmentsParam(ReLoadSegmentsParam&& from) noexcept - : ReLoadSegmentsParam() { - *this = ::std::move(from); - } - - inline ReLoadSegmentsParam& operator=(const ReLoadSegmentsParam& from) { - CopyFrom(from); - return *this; - } - inline ReLoadSegmentsParam& operator=(ReLoadSegmentsParam&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const ReLoadSegmentsParam& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const ReLoadSegmentsParam* internal_default_instance() { - return reinterpret_cast( - &_ReLoadSegmentsParam_default_instance_); - } - static constexpr int kIndexInFileMessages = - 12; - - friend void swap(ReLoadSegmentsParam& a, ReLoadSegmentsParam& b) { - a.Swap(&b); - } - inline void Swap(ReLoadSegmentsParam* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline ReLoadSegmentsParam* New() const final { - return CreateMaybeMessage(nullptr); - } - - ReLoadSegmentsParam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const ReLoadSegmentsParam& from); - void MergeFrom(const ReLoadSegmentsParam& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(ReLoadSegmentsParam* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.ReLoadSegmentsParam"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kSegmentIdArrayFieldNumber = 2, - kCollectionNameFieldNumber = 1, - }; - // repeated string segment_id_array = 2; - int segment_id_array_size() const; - void clear_segment_id_array(); - const std::string& segment_id_array(int index) const; - std::string* mutable_segment_id_array(int index); - void set_segment_id_array(int index, const std::string& value); - void set_segment_id_array(int index, std::string&& value); - void set_segment_id_array(int index, const char* value); - void set_segment_id_array(int index, const char* value, size_t size); - std::string* add_segment_id_array(); - void add_segment_id_array(const std::string& value); - void add_segment_id_array(std::string&& value); - void add_segment_id_array(const char* value); - void add_segment_id_array(const char* value, size_t size); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& segment_id_array() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* mutable_segment_id_array(); - - // string collection_name = 1; - void clear_collection_name(); - const std::string& collection_name() const; - void set_collection_name(const std::string& value); - void set_collection_name(std::string&& value); - void set_collection_name(const char* value); - void set_collection_name(const char* value, size_t size); - std::string* mutable_collection_name(); - std::string* release_collection_name(); - void set_allocated_collection_name(std::string* collection_name); - - // @@protoc_insertion_point(class_scope:milvus.grpc.ReLoadSegmentsParam) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField segment_id_array_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr collection_name_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class TopKQueryResult : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.TopKQueryResult) */ { - public: - TopKQueryResult(); - virtual ~TopKQueryResult(); - - TopKQueryResult(const TopKQueryResult& from); - TopKQueryResult(TopKQueryResult&& from) noexcept - : TopKQueryResult() { - *this = ::std::move(from); - } - - inline TopKQueryResult& operator=(const TopKQueryResult& from) { - CopyFrom(from); - return *this; - } - inline TopKQueryResult& operator=(TopKQueryResult&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const TopKQueryResult& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const TopKQueryResult* internal_default_instance() { - return reinterpret_cast( - &_TopKQueryResult_default_instance_); - } - static constexpr int kIndexInFileMessages = - 13; - - friend void swap(TopKQueryResult& a, TopKQueryResult& b) { - a.Swap(&b); - } - inline void Swap(TopKQueryResult* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline TopKQueryResult* New() const final { - return CreateMaybeMessage(nullptr); - } - - TopKQueryResult* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const TopKQueryResult& from); - void MergeFrom(const TopKQueryResult& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(TopKQueryResult* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.TopKQueryResult"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kIdsFieldNumber = 3, - kDistancesFieldNumber = 4, - kStatusFieldNumber = 1, - kRowNumFieldNumber = 2, - }; - // repeated int64 ids = 3; - int ids_size() const; - void clear_ids(); - ::PROTOBUF_NAMESPACE_ID::int64 ids(int index) const; - void set_ids(int index, ::PROTOBUF_NAMESPACE_ID::int64 value); - void add_ids(::PROTOBUF_NAMESPACE_ID::int64 value); - const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >& - ids() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >* - mutable_ids(); - - // repeated float distances = 4; - int distances_size() const; - void clear_distances(); - float distances(int index) const; - void set_distances(int index, float value); - void add_distances(float value); - const ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >& - distances() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >* - mutable_distances(); - - // .milvus.grpc.Status status = 1; - bool has_status() const; - void clear_status(); - const ::milvus::grpc::Status& status() const; - ::milvus::grpc::Status* release_status(); - ::milvus::grpc::Status* mutable_status(); - void set_allocated_status(::milvus::grpc::Status* status); - - // int64 row_num = 2; - void clear_row_num(); - ::PROTOBUF_NAMESPACE_ID::int64 row_num() const; - void set_row_num(::PROTOBUF_NAMESPACE_ID::int64 value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.TopKQueryResult) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 > ids_; - mutable std::atomic _ids_cached_byte_size_; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< float > distances_; - mutable std::atomic _distances_cached_byte_size_; - ::milvus::grpc::Status* status_; - ::PROTOBUF_NAMESPACE_ID::int64 row_num_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class StringReply : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.StringReply) */ { - public: - StringReply(); - virtual ~StringReply(); - - StringReply(const StringReply& from); - StringReply(StringReply&& from) noexcept - : StringReply() { - *this = ::std::move(from); - } - - inline StringReply& operator=(const StringReply& from) { - CopyFrom(from); - return *this; - } - inline StringReply& operator=(StringReply&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const StringReply& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const StringReply* internal_default_instance() { - return reinterpret_cast( - &_StringReply_default_instance_); - } - static constexpr int kIndexInFileMessages = - 14; - - friend void swap(StringReply& a, StringReply& b) { - a.Swap(&b); - } - inline void Swap(StringReply* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline StringReply* New() const final { - return CreateMaybeMessage(nullptr); - } - - StringReply* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const StringReply& from); - void MergeFrom(const StringReply& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(StringReply* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.StringReply"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kStringReplyFieldNumber = 2, - kStatusFieldNumber = 1, - }; - // string string_reply = 2; - void clear_string_reply(); - const std::string& string_reply() const; - void set_string_reply(const std::string& value); - void set_string_reply(std::string&& value); - void set_string_reply(const char* value); - void set_string_reply(const char* value, size_t size); - std::string* mutable_string_reply(); - std::string* release_string_reply(); - void set_allocated_string_reply(std::string* string_reply); - - // .milvus.grpc.Status status = 1; - bool has_status() const; - void clear_status(); - const ::milvus::grpc::Status& status() const; - ::milvus::grpc::Status* release_status(); - ::milvus::grpc::Status* mutable_status(); - void set_allocated_status(::milvus::grpc::Status* status); - - // @@protoc_insertion_point(class_scope:milvus.grpc.StringReply) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr string_reply_; - ::milvus::grpc::Status* status_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class BoolReply : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.BoolReply) */ { - public: - BoolReply(); - virtual ~BoolReply(); - - BoolReply(const BoolReply& from); - BoolReply(BoolReply&& from) noexcept - : BoolReply() { - *this = ::std::move(from); - } - - inline BoolReply& operator=(const BoolReply& from) { - CopyFrom(from); - return *this; - } - inline BoolReply& operator=(BoolReply&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const BoolReply& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const BoolReply* internal_default_instance() { - return reinterpret_cast( - &_BoolReply_default_instance_); - } - static constexpr int kIndexInFileMessages = - 15; - - friend void swap(BoolReply& a, BoolReply& b) { - a.Swap(&b); - } - inline void Swap(BoolReply* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline BoolReply* New() const final { - return CreateMaybeMessage(nullptr); - } - - BoolReply* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const BoolReply& from); - void MergeFrom(const BoolReply& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(BoolReply* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.BoolReply"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kStatusFieldNumber = 1, - kBoolReplyFieldNumber = 2, - }; - // .milvus.grpc.Status status = 1; - bool has_status() const; - void clear_status(); - const ::milvus::grpc::Status& status() const; - ::milvus::grpc::Status* release_status(); - ::milvus::grpc::Status* mutable_status(); - void set_allocated_status(::milvus::grpc::Status* status); - - // bool bool_reply = 2; - void clear_bool_reply(); - bool bool_reply() const; - void set_bool_reply(bool value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.BoolReply) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::milvus::grpc::Status* status_; - bool bool_reply_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class CollectionRowCount : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.CollectionRowCount) */ { - public: - CollectionRowCount(); - virtual ~CollectionRowCount(); - - CollectionRowCount(const CollectionRowCount& from); - CollectionRowCount(CollectionRowCount&& from) noexcept - : CollectionRowCount() { - *this = ::std::move(from); - } - - inline CollectionRowCount& operator=(const CollectionRowCount& from) { - CopyFrom(from); - return *this; - } - inline CollectionRowCount& operator=(CollectionRowCount&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const CollectionRowCount& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const CollectionRowCount* internal_default_instance() { - return reinterpret_cast( - &_CollectionRowCount_default_instance_); - } - static constexpr int kIndexInFileMessages = - 16; - - friend void swap(CollectionRowCount& a, CollectionRowCount& b) { - a.Swap(&b); - } - inline void Swap(CollectionRowCount* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline CollectionRowCount* New() const final { - return CreateMaybeMessage(nullptr); - } - - CollectionRowCount* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const CollectionRowCount& from); - void MergeFrom(const CollectionRowCount& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(CollectionRowCount* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.CollectionRowCount"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kStatusFieldNumber = 1, - kCollectionRowCountFieldNumber = 2, - }; - // .milvus.grpc.Status status = 1; - bool has_status() const; - void clear_status(); - const ::milvus::grpc::Status& status() const; - ::milvus::grpc::Status* release_status(); - ::milvus::grpc::Status* mutable_status(); - void set_allocated_status(::milvus::grpc::Status* status); - - // int64 collection_row_count = 2; - void clear_collection_row_count(); - ::PROTOBUF_NAMESPACE_ID::int64 collection_row_count() const; - void set_collection_row_count(::PROTOBUF_NAMESPACE_ID::int64 value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.CollectionRowCount) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::milvus::grpc::Status* status_; - ::PROTOBUF_NAMESPACE_ID::int64 collection_row_count_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class Command : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.Command) */ { - public: - Command(); - virtual ~Command(); - - Command(const Command& from); - Command(Command&& from) noexcept - : Command() { - *this = ::std::move(from); - } - - inline Command& operator=(const Command& from) { - CopyFrom(from); - return *this; - } - inline Command& operator=(Command&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const Command& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const Command* internal_default_instance() { - return reinterpret_cast( - &_Command_default_instance_); - } - static constexpr int kIndexInFileMessages = - 17; - - friend void swap(Command& a, Command& b) { - a.Swap(&b); - } - inline void Swap(Command* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline Command* New() const final { - return CreateMaybeMessage(nullptr); - } - - Command* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const Command& from); - void MergeFrom(const Command& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(Command* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.Command"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kCmdFieldNumber = 1, - }; - // string cmd = 1; - void clear_cmd(); - const std::string& cmd() const; - void set_cmd(const std::string& value); - void set_cmd(std::string&& value); - void set_cmd(const char* value); - void set_cmd(const char* value, size_t size); - std::string* mutable_cmd(); - std::string* release_cmd(); - void set_allocated_cmd(std::string* cmd); - - // @@protoc_insertion_point(class_scope:milvus.grpc.Command) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr cmd_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class IndexParam : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.IndexParam) */ { - public: - IndexParam(); - virtual ~IndexParam(); - - IndexParam(const IndexParam& from); - IndexParam(IndexParam&& from) noexcept - : IndexParam() { - *this = ::std::move(from); - } - - inline IndexParam& operator=(const IndexParam& from) { - CopyFrom(from); - return *this; - } - inline IndexParam& operator=(IndexParam&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const IndexParam& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const IndexParam* internal_default_instance() { - return reinterpret_cast( - &_IndexParam_default_instance_); - } - static constexpr int kIndexInFileMessages = - 18; - - friend void swap(IndexParam& a, IndexParam& b) { - a.Swap(&b); - } - inline void Swap(IndexParam* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline IndexParam* New() const final { - return CreateMaybeMessage(nullptr); - } - - IndexParam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const IndexParam& from); - void MergeFrom(const IndexParam& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(IndexParam* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.IndexParam"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kExtraParamsFieldNumber = 4, - kCollectionNameFieldNumber = 2, - kStatusFieldNumber = 1, - kIndexTypeFieldNumber = 3, - }; - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - int extra_params_size() const; - void clear_extra_params(); - ::milvus::grpc::KeyValuePair* mutable_extra_params(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* - mutable_extra_params(); - const ::milvus::grpc::KeyValuePair& extra_params(int index) const; - ::milvus::grpc::KeyValuePair* add_extra_params(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& - extra_params() const; - - // string collection_name = 2; - void clear_collection_name(); - const std::string& collection_name() const; - void set_collection_name(const std::string& value); - void set_collection_name(std::string&& value); - void set_collection_name(const char* value); - void set_collection_name(const char* value, size_t size); - std::string* mutable_collection_name(); - std::string* release_collection_name(); - void set_allocated_collection_name(std::string* collection_name); - - // .milvus.grpc.Status status = 1; - bool has_status() const; - void clear_status(); - const ::milvus::grpc::Status& status() const; - ::milvus::grpc::Status* release_status(); - ::milvus::grpc::Status* mutable_status(); - void set_allocated_status(::milvus::grpc::Status* status); - - // int32 index_type = 3; - void clear_index_type(); - ::PROTOBUF_NAMESPACE_ID::int32 index_type() const; - void set_index_type(::PROTOBUF_NAMESPACE_ID::int32 value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.IndexParam) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair > extra_params_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr collection_name_; - ::milvus::grpc::Status* status_; - ::PROTOBUF_NAMESPACE_ID::int32 index_type_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class FlushParam : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.FlushParam) */ { - public: - FlushParam(); - virtual ~FlushParam(); - - FlushParam(const FlushParam& from); - FlushParam(FlushParam&& from) noexcept - : FlushParam() { - *this = ::std::move(from); - } - - inline FlushParam& operator=(const FlushParam& from) { - CopyFrom(from); - return *this; - } - inline FlushParam& operator=(FlushParam&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const FlushParam& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const FlushParam* internal_default_instance() { - return reinterpret_cast( - &_FlushParam_default_instance_); - } - static constexpr int kIndexInFileMessages = - 19; - - friend void swap(FlushParam& a, FlushParam& b) { - a.Swap(&b); - } - inline void Swap(FlushParam* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline FlushParam* New() const final { - return CreateMaybeMessage(nullptr); - } - - FlushParam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const FlushParam& from); - void MergeFrom(const FlushParam& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(FlushParam* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.FlushParam"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kCollectionNameArrayFieldNumber = 1, - }; - // repeated string collection_name_array = 1; - int collection_name_array_size() const; - void clear_collection_name_array(); - const std::string& collection_name_array(int index) const; - std::string* mutable_collection_name_array(int index); - void set_collection_name_array(int index, const std::string& value); - void set_collection_name_array(int index, std::string&& value); - void set_collection_name_array(int index, const char* value); - void set_collection_name_array(int index, const char* value, size_t size); - std::string* add_collection_name_array(); - void add_collection_name_array(const std::string& value); - void add_collection_name_array(std::string&& value); - void add_collection_name_array(const char* value); - void add_collection_name_array(const char* value, size_t size); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& collection_name_array() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* mutable_collection_name_array(); - - // @@protoc_insertion_point(class_scope:milvus.grpc.FlushParam) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField collection_name_array_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class DeleteByIDParam : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.DeleteByIDParam) */ { - public: - DeleteByIDParam(); - virtual ~DeleteByIDParam(); - - DeleteByIDParam(const DeleteByIDParam& from); - DeleteByIDParam(DeleteByIDParam&& from) noexcept - : DeleteByIDParam() { - *this = ::std::move(from); - } - - inline DeleteByIDParam& operator=(const DeleteByIDParam& from) { - CopyFrom(from); - return *this; - } - inline DeleteByIDParam& operator=(DeleteByIDParam&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const DeleteByIDParam& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const DeleteByIDParam* internal_default_instance() { - return reinterpret_cast( - &_DeleteByIDParam_default_instance_); - } - static constexpr int kIndexInFileMessages = - 20; - - friend void swap(DeleteByIDParam& a, DeleteByIDParam& b) { - a.Swap(&b); - } - inline void Swap(DeleteByIDParam* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline DeleteByIDParam* New() const final { - return CreateMaybeMessage(nullptr); - } - - DeleteByIDParam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const DeleteByIDParam& from); - void MergeFrom(const DeleteByIDParam& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(DeleteByIDParam* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.DeleteByIDParam"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kIdArrayFieldNumber = 2, - kCollectionNameFieldNumber = 1, - }; - // repeated int64 id_array = 2; - int id_array_size() const; - void clear_id_array(); - ::PROTOBUF_NAMESPACE_ID::int64 id_array(int index) const; - void set_id_array(int index, ::PROTOBUF_NAMESPACE_ID::int64 value); - void add_id_array(::PROTOBUF_NAMESPACE_ID::int64 value); - const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >& - id_array() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >* - mutable_id_array(); - - // string collection_name = 1; - void clear_collection_name(); - const std::string& collection_name() const; - void set_collection_name(const std::string& value); - void set_collection_name(std::string&& value); - void set_collection_name(const char* value); - void set_collection_name(const char* value, size_t size); - std::string* mutable_collection_name(); - std::string* release_collection_name(); - void set_allocated_collection_name(std::string* collection_name); - - // @@protoc_insertion_point(class_scope:milvus.grpc.DeleteByIDParam) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 > id_array_; - mutable std::atomic _id_array_cached_byte_size_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr collection_name_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class CollectionInfo : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.CollectionInfo) */ { - public: - CollectionInfo(); - virtual ~CollectionInfo(); - - CollectionInfo(const CollectionInfo& from); - CollectionInfo(CollectionInfo&& from) noexcept - : CollectionInfo() { - *this = ::std::move(from); - } - - inline CollectionInfo& operator=(const CollectionInfo& from) { - CopyFrom(from); - return *this; - } - inline CollectionInfo& operator=(CollectionInfo&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const CollectionInfo& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const CollectionInfo* internal_default_instance() { - return reinterpret_cast( - &_CollectionInfo_default_instance_); - } - static constexpr int kIndexInFileMessages = - 21; - - friend void swap(CollectionInfo& a, CollectionInfo& b) { - a.Swap(&b); - } - inline void Swap(CollectionInfo* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline CollectionInfo* New() const final { - return CreateMaybeMessage(nullptr); - } - - CollectionInfo* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const CollectionInfo& from); - void MergeFrom(const CollectionInfo& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(CollectionInfo* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.CollectionInfo"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kJsonInfoFieldNumber = 2, - kStatusFieldNumber = 1, - }; - // string json_info = 2; - void clear_json_info(); - const std::string& json_info() const; - void set_json_info(const std::string& value); - void set_json_info(std::string&& value); - void set_json_info(const char* value); - void set_json_info(const char* value, size_t size); - std::string* mutable_json_info(); - std::string* release_json_info(); - void set_allocated_json_info(std::string* json_info); - - // .milvus.grpc.Status status = 1; - bool has_status() const; - void clear_status(); - const ::milvus::grpc::Status& status() const; - ::milvus::grpc::Status* release_status(); - ::milvus::grpc::Status* mutable_status(); - void set_allocated_status(::milvus::grpc::Status* status); - - // @@protoc_insertion_point(class_scope:milvus.grpc.CollectionInfo) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr json_info_; - ::milvus::grpc::Status* status_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class VectorsIdentity : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.VectorsIdentity) */ { - public: - VectorsIdentity(); - virtual ~VectorsIdentity(); - - VectorsIdentity(const VectorsIdentity& from); - VectorsIdentity(VectorsIdentity&& from) noexcept - : VectorsIdentity() { - *this = ::std::move(from); - } - - inline VectorsIdentity& operator=(const VectorsIdentity& from) { - CopyFrom(from); - return *this; - } - inline VectorsIdentity& operator=(VectorsIdentity&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const VectorsIdentity& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const VectorsIdentity* internal_default_instance() { - return reinterpret_cast( - &_VectorsIdentity_default_instance_); - } - static constexpr int kIndexInFileMessages = - 22; - - friend void swap(VectorsIdentity& a, VectorsIdentity& b) { - a.Swap(&b); - } - inline void Swap(VectorsIdentity* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline VectorsIdentity* New() const final { - return CreateMaybeMessage(nullptr); - } - - VectorsIdentity* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const VectorsIdentity& from); - void MergeFrom(const VectorsIdentity& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(VectorsIdentity* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.VectorsIdentity"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kIdArrayFieldNumber = 2, - kCollectionNameFieldNumber = 1, - }; - // repeated int64 id_array = 2; - int id_array_size() const; - void clear_id_array(); - ::PROTOBUF_NAMESPACE_ID::int64 id_array(int index) const; - void set_id_array(int index, ::PROTOBUF_NAMESPACE_ID::int64 value); - void add_id_array(::PROTOBUF_NAMESPACE_ID::int64 value); - const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >& - id_array() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >* - mutable_id_array(); - - // string collection_name = 1; - void clear_collection_name(); - const std::string& collection_name() const; - void set_collection_name(const std::string& value); - void set_collection_name(std::string&& value); - void set_collection_name(const char* value); - void set_collection_name(const char* value, size_t size); - std::string* mutable_collection_name(); - std::string* release_collection_name(); - void set_allocated_collection_name(std::string* collection_name); - - // @@protoc_insertion_point(class_scope:milvus.grpc.VectorsIdentity) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 > id_array_; - mutable std::atomic _id_array_cached_byte_size_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr collection_name_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class VectorsData : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.VectorsData) */ { - public: - VectorsData(); - virtual ~VectorsData(); - - VectorsData(const VectorsData& from); - VectorsData(VectorsData&& from) noexcept - : VectorsData() { - *this = ::std::move(from); - } - - inline VectorsData& operator=(const VectorsData& from) { - CopyFrom(from); - return *this; - } - inline VectorsData& operator=(VectorsData&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const VectorsData& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const VectorsData* internal_default_instance() { - return reinterpret_cast( - &_VectorsData_default_instance_); - } - static constexpr int kIndexInFileMessages = - 23; - - friend void swap(VectorsData& a, VectorsData& b) { - a.Swap(&b); - } - inline void Swap(VectorsData* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline VectorsData* New() const final { - return CreateMaybeMessage(nullptr); - } - - VectorsData* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const VectorsData& from); - void MergeFrom(const VectorsData& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(VectorsData* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.VectorsData"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kVectorsDataFieldNumber = 2, - kStatusFieldNumber = 1, - }; - // repeated .milvus.grpc.RowRecord vectors_data = 2; - int vectors_data_size() const; - void clear_vectors_data(); - ::milvus::grpc::RowRecord* mutable_vectors_data(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >* - mutable_vectors_data(); - const ::milvus::grpc::RowRecord& vectors_data(int index) const; - ::milvus::grpc::RowRecord* add_vectors_data(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >& - vectors_data() const; - - // .milvus.grpc.Status status = 1; - bool has_status() const; - void clear_status(); - const ::milvus::grpc::Status& status() const; - ::milvus::grpc::Status* release_status(); - ::milvus::grpc::Status* mutable_status(); - void set_allocated_status(::milvus::grpc::Status* status); - - // @@protoc_insertion_point(class_scope:milvus.grpc.VectorsData) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord > vectors_data_; - ::milvus::grpc::Status* status_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class GetVectorIDsParam : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.GetVectorIDsParam) */ { - public: - GetVectorIDsParam(); - virtual ~GetVectorIDsParam(); - - GetVectorIDsParam(const GetVectorIDsParam& from); - GetVectorIDsParam(GetVectorIDsParam&& from) noexcept - : GetVectorIDsParam() { - *this = ::std::move(from); - } - - inline GetVectorIDsParam& operator=(const GetVectorIDsParam& from) { - CopyFrom(from); - return *this; - } - inline GetVectorIDsParam& operator=(GetVectorIDsParam&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const GetVectorIDsParam& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const GetVectorIDsParam* internal_default_instance() { - return reinterpret_cast( - &_GetVectorIDsParam_default_instance_); - } - static constexpr int kIndexInFileMessages = - 24; - - friend void swap(GetVectorIDsParam& a, GetVectorIDsParam& b) { - a.Swap(&b); - } - inline void Swap(GetVectorIDsParam* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline GetVectorIDsParam* New() const final { - return CreateMaybeMessage(nullptr); - } - - GetVectorIDsParam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const GetVectorIDsParam& from); - void MergeFrom(const GetVectorIDsParam& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(GetVectorIDsParam* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.GetVectorIDsParam"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kCollectionNameFieldNumber = 1, - kSegmentNameFieldNumber = 2, - }; - // string collection_name = 1; - void clear_collection_name(); - const std::string& collection_name() const; - void set_collection_name(const std::string& value); - void set_collection_name(std::string&& value); - void set_collection_name(const char* value); - void set_collection_name(const char* value, size_t size); - std::string* mutable_collection_name(); - std::string* release_collection_name(); - void set_allocated_collection_name(std::string* collection_name); - - // string segment_name = 2; - void clear_segment_name(); - const std::string& segment_name() const; - void set_segment_name(const std::string& value); - void set_segment_name(std::string&& value); - void set_segment_name(const char* value); - void set_segment_name(const char* value, size_t size); - std::string* mutable_segment_name(); - std::string* release_segment_name(); - void set_allocated_segment_name(std::string* segment_name); - - // @@protoc_insertion_point(class_scope:milvus.grpc.GetVectorIDsParam) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr collection_name_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr segment_name_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class VectorFieldParam : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.VectorFieldParam) */ { - public: - VectorFieldParam(); - virtual ~VectorFieldParam(); - - VectorFieldParam(const VectorFieldParam& from); - VectorFieldParam(VectorFieldParam&& from) noexcept - : VectorFieldParam() { - *this = ::std::move(from); - } - - inline VectorFieldParam& operator=(const VectorFieldParam& from) { - CopyFrom(from); - return *this; - } - inline VectorFieldParam& operator=(VectorFieldParam&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const VectorFieldParam& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const VectorFieldParam* internal_default_instance() { - return reinterpret_cast( - &_VectorFieldParam_default_instance_); - } - static constexpr int kIndexInFileMessages = - 25; - - friend void swap(VectorFieldParam& a, VectorFieldParam& b) { - a.Swap(&b); - } - inline void Swap(VectorFieldParam* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline VectorFieldParam* New() const final { - return CreateMaybeMessage(nullptr); - } - - VectorFieldParam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const VectorFieldParam& from); - void MergeFrom(const VectorFieldParam& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(VectorFieldParam* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.VectorFieldParam"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kDimensionFieldNumber = 1, - }; - // int64 dimension = 1; - void clear_dimension(); - ::PROTOBUF_NAMESPACE_ID::int64 dimension() const; - void set_dimension(::PROTOBUF_NAMESPACE_ID::int64 value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.VectorFieldParam) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::int64 dimension_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class FieldType : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.FieldType) */ { - public: - FieldType(); - virtual ~FieldType(); - - FieldType(const FieldType& from); - FieldType(FieldType&& from) noexcept - : FieldType() { - *this = ::std::move(from); - } - - inline FieldType& operator=(const FieldType& from) { - CopyFrom(from); - return *this; - } - inline FieldType& operator=(FieldType&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const FieldType& default_instance(); - - enum ValueCase { - kDataType = 1, - kVectorParam = 2, - VALUE_NOT_SET = 0, - }; - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const FieldType* internal_default_instance() { - return reinterpret_cast( - &_FieldType_default_instance_); - } - static constexpr int kIndexInFileMessages = - 26; - - friend void swap(FieldType& a, FieldType& b) { - a.Swap(&b); - } - inline void Swap(FieldType* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline FieldType* New() const final { - return CreateMaybeMessage(nullptr); - } - - FieldType* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const FieldType& from); - void MergeFrom(const FieldType& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(FieldType* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.FieldType"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kDataTypeFieldNumber = 1, - kVectorParamFieldNumber = 2, - }; - // .milvus.grpc.DataType data_type = 1; - private: - bool has_data_type() const; - public: - void clear_data_type(); - ::milvus::grpc::DataType data_type() const; - void set_data_type(::milvus::grpc::DataType value); - - // .milvus.grpc.VectorFieldParam vector_param = 2; - bool has_vector_param() const; - void clear_vector_param(); - const ::milvus::grpc::VectorFieldParam& vector_param() const; - ::milvus::grpc::VectorFieldParam* release_vector_param(); - ::milvus::grpc::VectorFieldParam* mutable_vector_param(); - void set_allocated_vector_param(::milvus::grpc::VectorFieldParam* vector_param); - - void clear_value(); - ValueCase value_case() const; - // @@protoc_insertion_point(class_scope:milvus.grpc.FieldType) - private: - class _Internal; - void set_has_data_type(); - void set_has_vector_param(); - - inline bool has_value() const; - inline void clear_has_value(); - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - union ValueUnion { - ValueUnion() {} - int data_type_; - ::milvus::grpc::VectorFieldParam* vector_param_; - } value_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - ::PROTOBUF_NAMESPACE_ID::uint32 _oneof_case_[1]; - - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class FieldParam : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.FieldParam) */ { - public: - FieldParam(); - virtual ~FieldParam(); - - FieldParam(const FieldParam& from); - FieldParam(FieldParam&& from) noexcept - : FieldParam() { - *this = ::std::move(from); - } - - inline FieldParam& operator=(const FieldParam& from) { - CopyFrom(from); - return *this; - } - inline FieldParam& operator=(FieldParam&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const FieldParam& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const FieldParam* internal_default_instance() { - return reinterpret_cast( - &_FieldParam_default_instance_); - } - static constexpr int kIndexInFileMessages = - 27; - - friend void swap(FieldParam& a, FieldParam& b) { - a.Swap(&b); - } - inline void Swap(FieldParam* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline FieldParam* New() const final { - return CreateMaybeMessage(nullptr); - } - - FieldParam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const FieldParam& from); - void MergeFrom(const FieldParam& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(FieldParam* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.FieldParam"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kExtraParamsFieldNumber = 4, - kNameFieldNumber = 2, - kTypeFieldNumber = 3, - kIdFieldNumber = 1, - }; - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - int extra_params_size() const; - void clear_extra_params(); - ::milvus::grpc::KeyValuePair* mutable_extra_params(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* - mutable_extra_params(); - const ::milvus::grpc::KeyValuePair& extra_params(int index) const; - ::milvus::grpc::KeyValuePair* add_extra_params(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& - extra_params() const; - - // string name = 2; - void clear_name(); - const std::string& name() const; - void set_name(const std::string& value); - void set_name(std::string&& value); - void set_name(const char* value); - void set_name(const char* value, size_t size); - std::string* mutable_name(); - std::string* release_name(); - void set_allocated_name(std::string* name); - - // .milvus.grpc.FieldType type = 3; - bool has_type() const; - void clear_type(); - const ::milvus::grpc::FieldType& type() const; - ::milvus::grpc::FieldType* release_type(); - ::milvus::grpc::FieldType* mutable_type(); - void set_allocated_type(::milvus::grpc::FieldType* type); - - // uint64 id = 1; - void clear_id(); - ::PROTOBUF_NAMESPACE_ID::uint64 id() const; - void set_id(::PROTOBUF_NAMESPACE_ID::uint64 value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.FieldParam) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair > extra_params_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr name_; - ::milvus::grpc::FieldType* type_; - ::PROTOBUF_NAMESPACE_ID::uint64 id_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class VectorFieldValue : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.VectorFieldValue) */ { - public: - VectorFieldValue(); - virtual ~VectorFieldValue(); - - VectorFieldValue(const VectorFieldValue& from); - VectorFieldValue(VectorFieldValue&& from) noexcept - : VectorFieldValue() { - *this = ::std::move(from); - } - - inline VectorFieldValue& operator=(const VectorFieldValue& from) { - CopyFrom(from); - return *this; - } - inline VectorFieldValue& operator=(VectorFieldValue&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const VectorFieldValue& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const VectorFieldValue* internal_default_instance() { - return reinterpret_cast( - &_VectorFieldValue_default_instance_); - } - static constexpr int kIndexInFileMessages = - 28; - - friend void swap(VectorFieldValue& a, VectorFieldValue& b) { - a.Swap(&b); - } - inline void Swap(VectorFieldValue* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline VectorFieldValue* New() const final { - return CreateMaybeMessage(nullptr); - } - - VectorFieldValue* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const VectorFieldValue& from); - void MergeFrom(const VectorFieldValue& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(VectorFieldValue* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.VectorFieldValue"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kValueFieldNumber = 1, - }; - // repeated .milvus.grpc.RowRecord value = 1; - int value_size() const; - void clear_value(); - ::milvus::grpc::RowRecord* mutable_value(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >* - mutable_value(); - const ::milvus::grpc::RowRecord& value(int index) const; - ::milvus::grpc::RowRecord* add_value(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >& - value() const; - - // @@protoc_insertion_point(class_scope:milvus.grpc.VectorFieldValue) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord > value_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class FieldValue : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.FieldValue) */ { - public: - FieldValue(); - virtual ~FieldValue(); - - FieldValue(const FieldValue& from); - FieldValue(FieldValue&& from) noexcept - : FieldValue() { - *this = ::std::move(from); - } - - inline FieldValue& operator=(const FieldValue& from) { - CopyFrom(from); - return *this; - } - inline FieldValue& operator=(FieldValue&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const FieldValue& default_instance(); - - enum ValueCase { - kInt32Value = 1, - kInt64Value = 2, - kFloatValue = 3, - kDoubleValue = 4, - kStringValue = 5, - kBoolValue = 6, - kVectorValue = 7, - VALUE_NOT_SET = 0, - }; - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const FieldValue* internal_default_instance() { - return reinterpret_cast( - &_FieldValue_default_instance_); - } - static constexpr int kIndexInFileMessages = - 29; - - friend void swap(FieldValue& a, FieldValue& b) { - a.Swap(&b); - } - inline void Swap(FieldValue* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline FieldValue* New() const final { - return CreateMaybeMessage(nullptr); - } - - FieldValue* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const FieldValue& from); - void MergeFrom(const FieldValue& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(FieldValue* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.FieldValue"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kInt32ValueFieldNumber = 1, - kInt64ValueFieldNumber = 2, - kFloatValueFieldNumber = 3, - kDoubleValueFieldNumber = 4, - kStringValueFieldNumber = 5, - kBoolValueFieldNumber = 6, - kVectorValueFieldNumber = 7, - }; - // int32 int32_value = 1; - private: - bool has_int32_value() const; - public: - void clear_int32_value(); - ::PROTOBUF_NAMESPACE_ID::int32 int32_value() const; - void set_int32_value(::PROTOBUF_NAMESPACE_ID::int32 value); - - // int64 int64_value = 2; - private: - bool has_int64_value() const; - public: - void clear_int64_value(); - ::PROTOBUF_NAMESPACE_ID::int64 int64_value() const; - void set_int64_value(::PROTOBUF_NAMESPACE_ID::int64 value); - - // float float_value = 3; - private: - bool has_float_value() const; - public: - void clear_float_value(); - float float_value() const; - void set_float_value(float value); - - // double double_value = 4; - private: - bool has_double_value() const; - public: - void clear_double_value(); - double double_value() const; - void set_double_value(double value); - - // string string_value = 5; - private: - bool has_string_value() const; - public: - void clear_string_value(); - const std::string& string_value() const; - void set_string_value(const std::string& value); - void set_string_value(std::string&& value); - void set_string_value(const char* value); - void set_string_value(const char* value, size_t size); - std::string* mutable_string_value(); - std::string* release_string_value(); - void set_allocated_string_value(std::string* string_value); - - // bool bool_value = 6; - private: - bool has_bool_value() const; - public: - void clear_bool_value(); - bool bool_value() const; - void set_bool_value(bool value); - - // .milvus.grpc.VectorFieldValue vector_value = 7; - bool has_vector_value() const; - void clear_vector_value(); - const ::milvus::grpc::VectorFieldValue& vector_value() const; - ::milvus::grpc::VectorFieldValue* release_vector_value(); - ::milvus::grpc::VectorFieldValue* mutable_vector_value(); - void set_allocated_vector_value(::milvus::grpc::VectorFieldValue* vector_value); - - void clear_value(); - ValueCase value_case() const; - // @@protoc_insertion_point(class_scope:milvus.grpc.FieldValue) - private: - class _Internal; - void set_has_int32_value(); - void set_has_int64_value(); - void set_has_float_value(); - void set_has_double_value(); - void set_has_string_value(); - void set_has_bool_value(); - void set_has_vector_value(); - - inline bool has_value() const; - inline void clear_has_value(); - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - union ValueUnion { - ValueUnion() {} - ::PROTOBUF_NAMESPACE_ID::int32 int32_value_; - ::PROTOBUF_NAMESPACE_ID::int64 int64_value_; - float float_value_; - double double_value_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr string_value_; - bool bool_value_; - ::milvus::grpc::VectorFieldValue* vector_value_; - } value_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - ::PROTOBUF_NAMESPACE_ID::uint32 _oneof_case_[1]; - - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class Mapping : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.Mapping) */ { - public: - Mapping(); - virtual ~Mapping(); - - Mapping(const Mapping& from); - Mapping(Mapping&& from) noexcept - : Mapping() { - *this = ::std::move(from); - } - - inline Mapping& operator=(const Mapping& from) { - CopyFrom(from); - return *this; - } - inline Mapping& operator=(Mapping&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const Mapping& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const Mapping* internal_default_instance() { - return reinterpret_cast( - &_Mapping_default_instance_); - } - static constexpr int kIndexInFileMessages = - 30; - - friend void swap(Mapping& a, Mapping& b) { - a.Swap(&b); - } - inline void Swap(Mapping* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline Mapping* New() const final { - return CreateMaybeMessage(nullptr); - } - - Mapping* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const Mapping& from); - void MergeFrom(const Mapping& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(Mapping* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.Mapping"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kFieldsFieldNumber = 4, - kCollectionNameFieldNumber = 3, - kStatusFieldNumber = 1, - kCollectionIdFieldNumber = 2, - }; - // repeated .milvus.grpc.FieldParam fields = 4; - int fields_size() const; - void clear_fields(); - ::milvus::grpc::FieldParam* mutable_fields(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::FieldParam >* - mutable_fields(); - const ::milvus::grpc::FieldParam& fields(int index) const; - ::milvus::grpc::FieldParam* add_fields(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::FieldParam >& - fields() const; - - // string collection_name = 3; - void clear_collection_name(); - const std::string& collection_name() const; - void set_collection_name(const std::string& value); - void set_collection_name(std::string&& value); - void set_collection_name(const char* value); - void set_collection_name(const char* value, size_t size); - std::string* mutable_collection_name(); - std::string* release_collection_name(); - void set_allocated_collection_name(std::string* collection_name); - - // .milvus.grpc.Status status = 1; - bool has_status() const; - void clear_status(); - const ::milvus::grpc::Status& status() const; - ::milvus::grpc::Status* release_status(); - ::milvus::grpc::Status* mutable_status(); - void set_allocated_status(::milvus::grpc::Status* status); - - // uint64 collection_id = 2; - void clear_collection_id(); - ::PROTOBUF_NAMESPACE_ID::uint64 collection_id() const; - void set_collection_id(::PROTOBUF_NAMESPACE_ID::uint64 value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.Mapping) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::FieldParam > fields_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr collection_name_; - ::milvus::grpc::Status* status_; - ::PROTOBUF_NAMESPACE_ID::uint64 collection_id_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class MappingList : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.MappingList) */ { - public: - MappingList(); - virtual ~MappingList(); - - MappingList(const MappingList& from); - MappingList(MappingList&& from) noexcept - : MappingList() { - *this = ::std::move(from); - } - - inline MappingList& operator=(const MappingList& from) { - CopyFrom(from); - return *this; - } - inline MappingList& operator=(MappingList&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const MappingList& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const MappingList* internal_default_instance() { - return reinterpret_cast( - &_MappingList_default_instance_); - } - static constexpr int kIndexInFileMessages = - 31; - - friend void swap(MappingList& a, MappingList& b) { - a.Swap(&b); - } - inline void Swap(MappingList* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline MappingList* New() const final { - return CreateMaybeMessage(nullptr); - } - - MappingList* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const MappingList& from); - void MergeFrom(const MappingList& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(MappingList* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.MappingList"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kMappingListFieldNumber = 2, - kStatusFieldNumber = 1, - }; - // repeated .milvus.grpc.Mapping mapping_list = 2; - int mapping_list_size() const; - void clear_mapping_list(); - ::milvus::grpc::Mapping* mutable_mapping_list(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::Mapping >* - mutable_mapping_list(); - const ::milvus::grpc::Mapping& mapping_list(int index) const; - ::milvus::grpc::Mapping* add_mapping_list(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::Mapping >& - mapping_list() const; - - // .milvus.grpc.Status status = 1; - bool has_status() const; - void clear_status(); - const ::milvus::grpc::Status& status() const; - ::milvus::grpc::Status* release_status(); - ::milvus::grpc::Status* mutable_status(); - void set_allocated_status(::milvus::grpc::Status* status); - - // @@protoc_insertion_point(class_scope:milvus.grpc.MappingList) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::Mapping > mapping_list_; - ::milvus::grpc::Status* status_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class TermQuery : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.TermQuery) */ { - public: - TermQuery(); - virtual ~TermQuery(); - - TermQuery(const TermQuery& from); - TermQuery(TermQuery&& from) noexcept - : TermQuery() { - *this = ::std::move(from); - } - - inline TermQuery& operator=(const TermQuery& from) { - CopyFrom(from); - return *this; - } - inline TermQuery& operator=(TermQuery&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const TermQuery& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const TermQuery* internal_default_instance() { - return reinterpret_cast( - &_TermQuery_default_instance_); - } - static constexpr int kIndexInFileMessages = - 32; - - friend void swap(TermQuery& a, TermQuery& b) { - a.Swap(&b); - } - inline void Swap(TermQuery* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline TermQuery* New() const final { - return CreateMaybeMessage(nullptr); - } - - TermQuery* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const TermQuery& from); - void MergeFrom(const TermQuery& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(TermQuery* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.TermQuery"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kExtraParamsFieldNumber = 5, - kFieldNameFieldNumber = 1, - kValuesFieldNumber = 2, - kValueNumFieldNumber = 3, - kBoostFieldNumber = 4, - }; - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - int extra_params_size() const; - void clear_extra_params(); - ::milvus::grpc::KeyValuePair* mutable_extra_params(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* - mutable_extra_params(); - const ::milvus::grpc::KeyValuePair& extra_params(int index) const; - ::milvus::grpc::KeyValuePair* add_extra_params(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& - extra_params() const; - - // string field_name = 1; - void clear_field_name(); - const std::string& field_name() const; - void set_field_name(const std::string& value); - void set_field_name(std::string&& value); - void set_field_name(const char* value); - void set_field_name(const char* value, size_t size); - std::string* mutable_field_name(); - std::string* release_field_name(); - void set_allocated_field_name(std::string* field_name); - - // bytes values = 2; - void clear_values(); - const std::string& values() const; - void set_values(const std::string& value); - void set_values(std::string&& value); - void set_values(const char* value); - void set_values(const void* value, size_t size); - std::string* mutable_values(); - std::string* release_values(); - void set_allocated_values(std::string* values); - - // int64 value_num = 3; - void clear_value_num(); - ::PROTOBUF_NAMESPACE_ID::int64 value_num() const; - void set_value_num(::PROTOBUF_NAMESPACE_ID::int64 value); - - // float boost = 4; - void clear_boost(); - float boost() const; - void set_boost(float value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.TermQuery) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair > extra_params_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr field_name_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr values_; - ::PROTOBUF_NAMESPACE_ID::int64 value_num_; - float boost_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class CompareExpr : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.CompareExpr) */ { - public: - CompareExpr(); - virtual ~CompareExpr(); - - CompareExpr(const CompareExpr& from); - CompareExpr(CompareExpr&& from) noexcept - : CompareExpr() { - *this = ::std::move(from); - } - - inline CompareExpr& operator=(const CompareExpr& from) { - CopyFrom(from); - return *this; - } - inline CompareExpr& operator=(CompareExpr&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const CompareExpr& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const CompareExpr* internal_default_instance() { - return reinterpret_cast( - &_CompareExpr_default_instance_); - } - static constexpr int kIndexInFileMessages = - 33; - - friend void swap(CompareExpr& a, CompareExpr& b) { - a.Swap(&b); - } - inline void Swap(CompareExpr* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline CompareExpr* New() const final { - return CreateMaybeMessage(nullptr); - } - - CompareExpr* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const CompareExpr& from); - void MergeFrom(const CompareExpr& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(CompareExpr* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.CompareExpr"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kOperandFieldNumber = 2, - kOperatorFieldNumber = 1, - }; - // string operand = 2; - void clear_operand(); - const std::string& operand() const; - void set_operand(const std::string& value); - void set_operand(std::string&& value); - void set_operand(const char* value); - void set_operand(const char* value, size_t size); - std::string* mutable_operand(); - std::string* release_operand(); - void set_allocated_operand(std::string* operand); - - // .milvus.grpc.CompareOperator operator = 1; - void clear_operator_(); - ::milvus::grpc::CompareOperator operator_() const; - void set_operator_(::milvus::grpc::CompareOperator value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.CompareExpr) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr operand_; - int operator__; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class RangeQuery : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.RangeQuery) */ { - public: - RangeQuery(); - virtual ~RangeQuery(); - - RangeQuery(const RangeQuery& from); - RangeQuery(RangeQuery&& from) noexcept - : RangeQuery() { - *this = ::std::move(from); - } - - inline RangeQuery& operator=(const RangeQuery& from) { - CopyFrom(from); - return *this; - } - inline RangeQuery& operator=(RangeQuery&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const RangeQuery& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const RangeQuery* internal_default_instance() { - return reinterpret_cast( - &_RangeQuery_default_instance_); - } - static constexpr int kIndexInFileMessages = - 34; - - friend void swap(RangeQuery& a, RangeQuery& b) { - a.Swap(&b); - } - inline void Swap(RangeQuery* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline RangeQuery* New() const final { - return CreateMaybeMessage(nullptr); - } - - RangeQuery* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const RangeQuery& from); - void MergeFrom(const RangeQuery& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(RangeQuery* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.RangeQuery"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kOperandFieldNumber = 2, - kExtraParamsFieldNumber = 4, - kFieldNameFieldNumber = 1, - kBoostFieldNumber = 3, - }; - // repeated .milvus.grpc.CompareExpr operand = 2; - int operand_size() const; - void clear_operand(); - ::milvus::grpc::CompareExpr* mutable_operand(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::CompareExpr >* - mutable_operand(); - const ::milvus::grpc::CompareExpr& operand(int index) const; - ::milvus::grpc::CompareExpr* add_operand(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::CompareExpr >& - operand() const; - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - int extra_params_size() const; - void clear_extra_params(); - ::milvus::grpc::KeyValuePair* mutable_extra_params(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* - mutable_extra_params(); - const ::milvus::grpc::KeyValuePair& extra_params(int index) const; - ::milvus::grpc::KeyValuePair* add_extra_params(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& - extra_params() const; - - // string field_name = 1; - void clear_field_name(); - const std::string& field_name() const; - void set_field_name(const std::string& value); - void set_field_name(std::string&& value); - void set_field_name(const char* value); - void set_field_name(const char* value, size_t size); - std::string* mutable_field_name(); - std::string* release_field_name(); - void set_allocated_field_name(std::string* field_name); - - // float boost = 3; - void clear_boost(); - float boost() const; - void set_boost(float value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.RangeQuery) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::CompareExpr > operand_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair > extra_params_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr field_name_; - float boost_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class VectorQuery : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.VectorQuery) */ { - public: - VectorQuery(); - virtual ~VectorQuery(); - - VectorQuery(const VectorQuery& from); - VectorQuery(VectorQuery&& from) noexcept - : VectorQuery() { - *this = ::std::move(from); - } - - inline VectorQuery& operator=(const VectorQuery& from) { - CopyFrom(from); - return *this; - } - inline VectorQuery& operator=(VectorQuery&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const VectorQuery& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const VectorQuery* internal_default_instance() { - return reinterpret_cast( - &_VectorQuery_default_instance_); - } - static constexpr int kIndexInFileMessages = - 35; - - friend void swap(VectorQuery& a, VectorQuery& b) { - a.Swap(&b); - } - inline void Swap(VectorQuery* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline VectorQuery* New() const final { - return CreateMaybeMessage(nullptr); - } - - VectorQuery* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const VectorQuery& from); - void MergeFrom(const VectorQuery& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(VectorQuery* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.VectorQuery"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kRecordsFieldNumber = 3, - kExtraParamsFieldNumber = 5, - kFieldNameFieldNumber = 1, - kTopkFieldNumber = 4, - kQueryBoostFieldNumber = 2, - }; - // repeated .milvus.grpc.RowRecord records = 3; - int records_size() const; - void clear_records(); - ::milvus::grpc::RowRecord* mutable_records(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >* - mutable_records(); - const ::milvus::grpc::RowRecord& records(int index) const; - ::milvus::grpc::RowRecord* add_records(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >& - records() const; - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - int extra_params_size() const; - void clear_extra_params(); - ::milvus::grpc::KeyValuePair* mutable_extra_params(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* - mutable_extra_params(); - const ::milvus::grpc::KeyValuePair& extra_params(int index) const; - ::milvus::grpc::KeyValuePair* add_extra_params(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& - extra_params() const; - - // string field_name = 1; - void clear_field_name(); - const std::string& field_name() const; - void set_field_name(const std::string& value); - void set_field_name(std::string&& value); - void set_field_name(const char* value); - void set_field_name(const char* value, size_t size); - std::string* mutable_field_name(); - std::string* release_field_name(); - void set_allocated_field_name(std::string* field_name); - - // int64 topk = 4; - void clear_topk(); - ::PROTOBUF_NAMESPACE_ID::int64 topk() const; - void set_topk(::PROTOBUF_NAMESPACE_ID::int64 value); - - // float query_boost = 2; - void clear_query_boost(); - float query_boost() const; - void set_query_boost(float value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.VectorQuery) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord > records_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair > extra_params_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr field_name_; - ::PROTOBUF_NAMESPACE_ID::int64 topk_; - float query_boost_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class BooleanQuery : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.BooleanQuery) */ { - public: - BooleanQuery(); - virtual ~BooleanQuery(); - - BooleanQuery(const BooleanQuery& from); - BooleanQuery(BooleanQuery&& from) noexcept - : BooleanQuery() { - *this = ::std::move(from); - } - - inline BooleanQuery& operator=(const BooleanQuery& from) { - CopyFrom(from); - return *this; - } - inline BooleanQuery& operator=(BooleanQuery&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const BooleanQuery& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const BooleanQuery* internal_default_instance() { - return reinterpret_cast( - &_BooleanQuery_default_instance_); - } - static constexpr int kIndexInFileMessages = - 36; - - friend void swap(BooleanQuery& a, BooleanQuery& b) { - a.Swap(&b); - } - inline void Swap(BooleanQuery* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline BooleanQuery* New() const final { - return CreateMaybeMessage(nullptr); - } - - BooleanQuery* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const BooleanQuery& from); - void MergeFrom(const BooleanQuery& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(BooleanQuery* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.BooleanQuery"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kGeneralQueryFieldNumber = 2, - kOccurFieldNumber = 1, - }; - // repeated .milvus.grpc.GeneralQuery general_query = 2; - int general_query_size() const; - void clear_general_query(); - ::milvus::grpc::GeneralQuery* mutable_general_query(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::GeneralQuery >* - mutable_general_query(); - const ::milvus::grpc::GeneralQuery& general_query(int index) const; - ::milvus::grpc::GeneralQuery* add_general_query(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::GeneralQuery >& - general_query() const; - - // .milvus.grpc.Occur occur = 1; - void clear_occur(); - ::milvus::grpc::Occur occur() const; - void set_occur(::milvus::grpc::Occur value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.BooleanQuery) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::GeneralQuery > general_query_; - int occur_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class GeneralQuery : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.GeneralQuery) */ { - public: - GeneralQuery(); - virtual ~GeneralQuery(); - - GeneralQuery(const GeneralQuery& from); - GeneralQuery(GeneralQuery&& from) noexcept - : GeneralQuery() { - *this = ::std::move(from); - } - - inline GeneralQuery& operator=(const GeneralQuery& from) { - CopyFrom(from); - return *this; - } - inline GeneralQuery& operator=(GeneralQuery&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const GeneralQuery& default_instance(); - - enum QueryCase { - kBooleanQuery = 1, - kTermQuery = 2, - kRangeQuery = 3, - kVectorQuery = 4, - QUERY_NOT_SET = 0, - }; - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const GeneralQuery* internal_default_instance() { - return reinterpret_cast( - &_GeneralQuery_default_instance_); - } - static constexpr int kIndexInFileMessages = - 37; - - friend void swap(GeneralQuery& a, GeneralQuery& b) { - a.Swap(&b); - } - inline void Swap(GeneralQuery* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline GeneralQuery* New() const final { - return CreateMaybeMessage(nullptr); - } - - GeneralQuery* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const GeneralQuery& from); - void MergeFrom(const GeneralQuery& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(GeneralQuery* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.GeneralQuery"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kBooleanQueryFieldNumber = 1, - kTermQueryFieldNumber = 2, - kRangeQueryFieldNumber = 3, - kVectorQueryFieldNumber = 4, - }; - // .milvus.grpc.BooleanQuery boolean_query = 1; - bool has_boolean_query() const; - void clear_boolean_query(); - const ::milvus::grpc::BooleanQuery& boolean_query() const; - ::milvus::grpc::BooleanQuery* release_boolean_query(); - ::milvus::grpc::BooleanQuery* mutable_boolean_query(); - void set_allocated_boolean_query(::milvus::grpc::BooleanQuery* boolean_query); - - // .milvus.grpc.TermQuery term_query = 2; - bool has_term_query() const; - void clear_term_query(); - const ::milvus::grpc::TermQuery& term_query() const; - ::milvus::grpc::TermQuery* release_term_query(); - ::milvus::grpc::TermQuery* mutable_term_query(); - void set_allocated_term_query(::milvus::grpc::TermQuery* term_query); - - // .milvus.grpc.RangeQuery range_query = 3; - bool has_range_query() const; - void clear_range_query(); - const ::milvus::grpc::RangeQuery& range_query() const; - ::milvus::grpc::RangeQuery* release_range_query(); - ::milvus::grpc::RangeQuery* mutable_range_query(); - void set_allocated_range_query(::milvus::grpc::RangeQuery* range_query); - - // .milvus.grpc.VectorQuery vector_query = 4; - bool has_vector_query() const; - void clear_vector_query(); - const ::milvus::grpc::VectorQuery& vector_query() const; - ::milvus::grpc::VectorQuery* release_vector_query(); - ::milvus::grpc::VectorQuery* mutable_vector_query(); - void set_allocated_vector_query(::milvus::grpc::VectorQuery* vector_query); - - void clear_query(); - QueryCase query_case() const; - // @@protoc_insertion_point(class_scope:milvus.grpc.GeneralQuery) - private: - class _Internal; - void set_has_boolean_query(); - void set_has_term_query(); - void set_has_range_query(); - void set_has_vector_query(); - - inline bool has_query() const; - inline void clear_has_query(); - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - union QueryUnion { - QueryUnion() {} - ::milvus::grpc::BooleanQuery* boolean_query_; - ::milvus::grpc::TermQuery* term_query_; - ::milvus::grpc::RangeQuery* range_query_; - ::milvus::grpc::VectorQuery* vector_query_; - } query_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - ::PROTOBUF_NAMESPACE_ID::uint32 _oneof_case_[1]; - - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class HSearchParam : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.HSearchParam) */ { - public: - HSearchParam(); - virtual ~HSearchParam(); - - HSearchParam(const HSearchParam& from); - HSearchParam(HSearchParam&& from) noexcept - : HSearchParam() { - *this = ::std::move(from); - } - - inline HSearchParam& operator=(const HSearchParam& from) { - CopyFrom(from); - return *this; - } - inline HSearchParam& operator=(HSearchParam&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const HSearchParam& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const HSearchParam* internal_default_instance() { - return reinterpret_cast( - &_HSearchParam_default_instance_); - } - static constexpr int kIndexInFileMessages = - 38; - - friend void swap(HSearchParam& a, HSearchParam& b) { - a.Swap(&b); - } - inline void Swap(HSearchParam* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline HSearchParam* New() const final { - return CreateMaybeMessage(nullptr); - } - - HSearchParam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const HSearchParam& from); - void MergeFrom(const HSearchParam& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(HSearchParam* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.HSearchParam"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kPartitionTagArrayFieldNumber = 2, - kExtraParamsFieldNumber = 4, - kCollectionNameFieldNumber = 1, - kGeneralQueryFieldNumber = 3, - }; - // repeated string partition_tag_array = 2; - int partition_tag_array_size() const; - void clear_partition_tag_array(); - const std::string& partition_tag_array(int index) const; - std::string* mutable_partition_tag_array(int index); - void set_partition_tag_array(int index, const std::string& value); - void set_partition_tag_array(int index, std::string&& value); - void set_partition_tag_array(int index, const char* value); - void set_partition_tag_array(int index, const char* value, size_t size); - std::string* add_partition_tag_array(); - void add_partition_tag_array(const std::string& value); - void add_partition_tag_array(std::string&& value); - void add_partition_tag_array(const char* value); - void add_partition_tag_array(const char* value, size_t size); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& partition_tag_array() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* mutable_partition_tag_array(); - - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - int extra_params_size() const; - void clear_extra_params(); - ::milvus::grpc::KeyValuePair* mutable_extra_params(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* - mutable_extra_params(); - const ::milvus::grpc::KeyValuePair& extra_params(int index) const; - ::milvus::grpc::KeyValuePair* add_extra_params(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& - extra_params() const; - - // string collection_name = 1; - void clear_collection_name(); - const std::string& collection_name() const; - void set_collection_name(const std::string& value); - void set_collection_name(std::string&& value); - void set_collection_name(const char* value); - void set_collection_name(const char* value, size_t size); - std::string* mutable_collection_name(); - std::string* release_collection_name(); - void set_allocated_collection_name(std::string* collection_name); - - // .milvus.grpc.GeneralQuery general_query = 3; - bool has_general_query() const; - void clear_general_query(); - const ::milvus::grpc::GeneralQuery& general_query() const; - ::milvus::grpc::GeneralQuery* release_general_query(); - ::milvus::grpc::GeneralQuery* mutable_general_query(); - void set_allocated_general_query(::milvus::grpc::GeneralQuery* general_query); - - // @@protoc_insertion_point(class_scope:milvus.grpc.HSearchParam) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField partition_tag_array_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair > extra_params_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr collection_name_; - ::milvus::grpc::GeneralQuery* general_query_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class HSearchInSegmentsParam : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.HSearchInSegmentsParam) */ { - public: - HSearchInSegmentsParam(); - virtual ~HSearchInSegmentsParam(); - - HSearchInSegmentsParam(const HSearchInSegmentsParam& from); - HSearchInSegmentsParam(HSearchInSegmentsParam&& from) noexcept - : HSearchInSegmentsParam() { - *this = ::std::move(from); - } - - inline HSearchInSegmentsParam& operator=(const HSearchInSegmentsParam& from) { - CopyFrom(from); - return *this; - } - inline HSearchInSegmentsParam& operator=(HSearchInSegmentsParam&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const HSearchInSegmentsParam& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const HSearchInSegmentsParam* internal_default_instance() { - return reinterpret_cast( - &_HSearchInSegmentsParam_default_instance_); - } - static constexpr int kIndexInFileMessages = - 39; - - friend void swap(HSearchInSegmentsParam& a, HSearchInSegmentsParam& b) { - a.Swap(&b); - } - inline void Swap(HSearchInSegmentsParam* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline HSearchInSegmentsParam* New() const final { - return CreateMaybeMessage(nullptr); - } - - HSearchInSegmentsParam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const HSearchInSegmentsParam& from); - void MergeFrom(const HSearchInSegmentsParam& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(HSearchInSegmentsParam* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.HSearchInSegmentsParam"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kSegmentIdArrayFieldNumber = 1, - kSearchParamFieldNumber = 2, - }; - // repeated string segment_id_array = 1; - int segment_id_array_size() const; - void clear_segment_id_array(); - const std::string& segment_id_array(int index) const; - std::string* mutable_segment_id_array(int index); - void set_segment_id_array(int index, const std::string& value); - void set_segment_id_array(int index, std::string&& value); - void set_segment_id_array(int index, const char* value); - void set_segment_id_array(int index, const char* value, size_t size); - std::string* add_segment_id_array(); - void add_segment_id_array(const std::string& value); - void add_segment_id_array(std::string&& value); - void add_segment_id_array(const char* value); - void add_segment_id_array(const char* value, size_t size); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& segment_id_array() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* mutable_segment_id_array(); - - // .milvus.grpc.HSearchParam search_param = 2; - bool has_search_param() const; - void clear_search_param(); - const ::milvus::grpc::HSearchParam& search_param() const; - ::milvus::grpc::HSearchParam* release_search_param(); - ::milvus::grpc::HSearchParam* mutable_search_param(); - void set_allocated_search_param(::milvus::grpc::HSearchParam* search_param); - - // @@protoc_insertion_point(class_scope:milvus.grpc.HSearchInSegmentsParam) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField segment_id_array_; - ::milvus::grpc::HSearchParam* search_param_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class AttrRecord : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.AttrRecord) */ { - public: - AttrRecord(); - virtual ~AttrRecord(); - - AttrRecord(const AttrRecord& from); - AttrRecord(AttrRecord&& from) noexcept - : AttrRecord() { - *this = ::std::move(from); - } - - inline AttrRecord& operator=(const AttrRecord& from) { - CopyFrom(from); - return *this; - } - inline AttrRecord& operator=(AttrRecord&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const AttrRecord& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const AttrRecord* internal_default_instance() { - return reinterpret_cast( - &_AttrRecord_default_instance_); - } - static constexpr int kIndexInFileMessages = - 40; - - friend void swap(AttrRecord& a, AttrRecord& b) { - a.Swap(&b); - } - inline void Swap(AttrRecord* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline AttrRecord* New() const final { - return CreateMaybeMessage(nullptr); - } - - AttrRecord* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const AttrRecord& from); - void MergeFrom(const AttrRecord& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(AttrRecord* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.AttrRecord"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kValueFieldNumber = 1, - }; - // repeated string value = 1; - int value_size() const; - void clear_value(); - const std::string& value(int index) const; - std::string* mutable_value(int index); - void set_value(int index, const std::string& value); - void set_value(int index, std::string&& value); - void set_value(int index, const char* value); - void set_value(int index, const char* value, size_t size); - std::string* add_value(); - void add_value(const std::string& value); - void add_value(std::string&& value); - void add_value(const char* value); - void add_value(const char* value, size_t size); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& value() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* mutable_value(); - - // @@protoc_insertion_point(class_scope:milvus.grpc.AttrRecord) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField value_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class HEntity : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.HEntity) */ { - public: - HEntity(); - virtual ~HEntity(); - - HEntity(const HEntity& from); - HEntity(HEntity&& from) noexcept - : HEntity() { - *this = ::std::move(from); - } - - inline HEntity& operator=(const HEntity& from) { - CopyFrom(from); - return *this; - } - inline HEntity& operator=(HEntity&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const HEntity& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const HEntity* internal_default_instance() { - return reinterpret_cast( - &_HEntity_default_instance_); - } - static constexpr int kIndexInFileMessages = - 41; - - friend void swap(HEntity& a, HEntity& b) { - a.Swap(&b); - } - inline void Swap(HEntity* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline HEntity* New() const final { - return CreateMaybeMessage(nullptr); - } - - HEntity* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const HEntity& from); - void MergeFrom(const HEntity& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(HEntity* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.HEntity"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kFieldNamesFieldNumber = 3, - kResultValuesFieldNumber = 6, - kAttrRecordsFieldNumber = 4, - kStatusFieldNumber = 1, - kEntityIdFieldNumber = 2, - kRowNumFieldNumber = 5, - }; - // repeated string field_names = 3; - int field_names_size() const; - void clear_field_names(); - const std::string& field_names(int index) const; - std::string* mutable_field_names(int index); - void set_field_names(int index, const std::string& value); - void set_field_names(int index, std::string&& value); - void set_field_names(int index, const char* value); - void set_field_names(int index, const char* value, size_t size); - std::string* add_field_names(); - void add_field_names(const std::string& value); - void add_field_names(std::string&& value); - void add_field_names(const char* value); - void add_field_names(const char* value, size_t size); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& field_names() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* mutable_field_names(); - - // repeated .milvus.grpc.FieldValue result_values = 6; - int result_values_size() const; - void clear_result_values(); - ::milvus::grpc::FieldValue* mutable_result_values(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::FieldValue >* - mutable_result_values(); - const ::milvus::grpc::FieldValue& result_values(int index) const; - ::milvus::grpc::FieldValue* add_result_values(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::FieldValue >& - result_values() const; - - // bytes attr_records = 4; - void clear_attr_records(); - const std::string& attr_records() const; - void set_attr_records(const std::string& value); - void set_attr_records(std::string&& value); - void set_attr_records(const char* value); - void set_attr_records(const void* value, size_t size); - std::string* mutable_attr_records(); - std::string* release_attr_records(); - void set_allocated_attr_records(std::string* attr_records); - - // .milvus.grpc.Status status = 1; - bool has_status() const; - void clear_status(); - const ::milvus::grpc::Status& status() const; - ::milvus::grpc::Status* release_status(); - ::milvus::grpc::Status* mutable_status(); - void set_allocated_status(::milvus::grpc::Status* status); - - // int64 entity_id = 2; - void clear_entity_id(); - ::PROTOBUF_NAMESPACE_ID::int64 entity_id() const; - void set_entity_id(::PROTOBUF_NAMESPACE_ID::int64 value); - - // int64 row_num = 5; - void clear_row_num(); - ::PROTOBUF_NAMESPACE_ID::int64 row_num() const; - void set_row_num(::PROTOBUF_NAMESPACE_ID::int64 value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.HEntity) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField field_names_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::FieldValue > result_values_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr attr_records_; - ::milvus::grpc::Status* status_; - ::PROTOBUF_NAMESPACE_ID::int64 entity_id_; - ::PROTOBUF_NAMESPACE_ID::int64 row_num_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class HQueryResult : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.HQueryResult) */ { - public: - HQueryResult(); - virtual ~HQueryResult(); - - HQueryResult(const HQueryResult& from); - HQueryResult(HQueryResult&& from) noexcept - : HQueryResult() { - *this = ::std::move(from); - } - - inline HQueryResult& operator=(const HQueryResult& from) { - CopyFrom(from); - return *this; - } - inline HQueryResult& operator=(HQueryResult&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const HQueryResult& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const HQueryResult* internal_default_instance() { - return reinterpret_cast( - &_HQueryResult_default_instance_); - } - static constexpr int kIndexInFileMessages = - 42; - - friend void swap(HQueryResult& a, HQueryResult& b) { - a.Swap(&b); - } - inline void Swap(HQueryResult* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline HQueryResult* New() const final { - return CreateMaybeMessage(nullptr); - } - - HQueryResult* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const HQueryResult& from); - void MergeFrom(const HQueryResult& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(HQueryResult* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.HQueryResult"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kEntitiesFieldNumber = 2, - kScoreFieldNumber = 4, - kDistanceFieldNumber = 5, - kStatusFieldNumber = 1, - kRowNumFieldNumber = 3, - }; - // repeated .milvus.grpc.HEntity entities = 2; - int entities_size() const; - void clear_entities(); - ::milvus::grpc::HEntity* mutable_entities(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::HEntity >* - mutable_entities(); - const ::milvus::grpc::HEntity& entities(int index) const; - ::milvus::grpc::HEntity* add_entities(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::HEntity >& - entities() const; - - // repeated float score = 4; - int score_size() const; - void clear_score(); - float score(int index) const; - void set_score(int index, float value); - void add_score(float value); - const ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >& - score() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >* - mutable_score(); - - // repeated float distance = 5; - int distance_size() const; - void clear_distance(); - float distance(int index) const; - void set_distance(int index, float value); - void add_distance(float value); - const ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >& - distance() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >* - mutable_distance(); - - // .milvus.grpc.Status status = 1; - bool has_status() const; - void clear_status(); - const ::milvus::grpc::Status& status() const; - ::milvus::grpc::Status* release_status(); - ::milvus::grpc::Status* mutable_status(); - void set_allocated_status(::milvus::grpc::Status* status); - - // int64 row_num = 3; - void clear_row_num(); - ::PROTOBUF_NAMESPACE_ID::int64 row_num() const; - void set_row_num(::PROTOBUF_NAMESPACE_ID::int64 value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.HQueryResult) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::HEntity > entities_; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< float > score_; - mutable std::atomic _score_cached_byte_size_; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< float > distance_; - mutable std::atomic _distance_cached_byte_size_; - ::milvus::grpc::Status* status_; - ::PROTOBUF_NAMESPACE_ID::int64 row_num_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class HInsertParam : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.HInsertParam) */ { - public: - HInsertParam(); - virtual ~HInsertParam(); - - HInsertParam(const HInsertParam& from); - HInsertParam(HInsertParam&& from) noexcept - : HInsertParam() { - *this = ::std::move(from); - } - - inline HInsertParam& operator=(const HInsertParam& from) { - CopyFrom(from); - return *this; - } - inline HInsertParam& operator=(HInsertParam&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const HInsertParam& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const HInsertParam* internal_default_instance() { - return reinterpret_cast( - &_HInsertParam_default_instance_); - } - static constexpr int kIndexInFileMessages = - 43; - - friend void swap(HInsertParam& a, HInsertParam& b) { - a.Swap(&b); - } - inline void Swap(HInsertParam* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline HInsertParam* New() const final { - return CreateMaybeMessage(nullptr); - } - - HInsertParam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const HInsertParam& from); - void MergeFrom(const HInsertParam& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(HInsertParam* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.HInsertParam"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kEntityIdArrayFieldNumber = 4, - kExtraParamsFieldNumber = 5, - kCollectionNameFieldNumber = 1, - kPartitionTagFieldNumber = 2, - kEntitiesFieldNumber = 3, - }; - // repeated int64 entity_id_array = 4; - int entity_id_array_size() const; - void clear_entity_id_array(); - ::PROTOBUF_NAMESPACE_ID::int64 entity_id_array(int index) const; - void set_entity_id_array(int index, ::PROTOBUF_NAMESPACE_ID::int64 value); - void add_entity_id_array(::PROTOBUF_NAMESPACE_ID::int64 value); - const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >& - entity_id_array() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >* - mutable_entity_id_array(); - - // repeated .milvus.grpc.KeyValuePair extra_params = 5; - int extra_params_size() const; - void clear_extra_params(); - ::milvus::grpc::KeyValuePair* mutable_extra_params(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* - mutable_extra_params(); - const ::milvus::grpc::KeyValuePair& extra_params(int index) const; - ::milvus::grpc::KeyValuePair* add_extra_params(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& - extra_params() const; - - // string collection_name = 1; - void clear_collection_name(); - const std::string& collection_name() const; - void set_collection_name(const std::string& value); - void set_collection_name(std::string&& value); - void set_collection_name(const char* value); - void set_collection_name(const char* value, size_t size); - std::string* mutable_collection_name(); - std::string* release_collection_name(); - void set_allocated_collection_name(std::string* collection_name); - - // string partition_tag = 2; - void clear_partition_tag(); - const std::string& partition_tag() const; - void set_partition_tag(const std::string& value); - void set_partition_tag(std::string&& value); - void set_partition_tag(const char* value); - void set_partition_tag(const char* value, size_t size); - std::string* mutable_partition_tag(); - std::string* release_partition_tag(); - void set_allocated_partition_tag(std::string* partition_tag); - - // .milvus.grpc.HEntity entities = 3; - bool has_entities() const; - void clear_entities(); - const ::milvus::grpc::HEntity& entities() const; - ::milvus::grpc::HEntity* release_entities(); - ::milvus::grpc::HEntity* mutable_entities(); - void set_allocated_entities(::milvus::grpc::HEntity* entities); - - // @@protoc_insertion_point(class_scope:milvus.grpc.HInsertParam) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 > entity_id_array_; - mutable std::atomic _entity_id_array_cached_byte_size_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair > extra_params_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr collection_name_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr partition_tag_; - ::milvus::grpc::HEntity* entities_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class HEntityIdentity : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.HEntityIdentity) */ { - public: - HEntityIdentity(); - virtual ~HEntityIdentity(); - - HEntityIdentity(const HEntityIdentity& from); - HEntityIdentity(HEntityIdentity&& from) noexcept - : HEntityIdentity() { - *this = ::std::move(from); - } - - inline HEntityIdentity& operator=(const HEntityIdentity& from) { - CopyFrom(from); - return *this; - } - inline HEntityIdentity& operator=(HEntityIdentity&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const HEntityIdentity& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const HEntityIdentity* internal_default_instance() { - return reinterpret_cast( - &_HEntityIdentity_default_instance_); - } - static constexpr int kIndexInFileMessages = - 44; - - friend void swap(HEntityIdentity& a, HEntityIdentity& b) { - a.Swap(&b); - } - inline void Swap(HEntityIdentity* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline HEntityIdentity* New() const final { - return CreateMaybeMessage(nullptr); - } - - HEntityIdentity* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const HEntityIdentity& from); - void MergeFrom(const HEntityIdentity& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(HEntityIdentity* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.HEntityIdentity"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kCollectionNameFieldNumber = 1, - kIdFieldNumber = 2, - }; - // string collection_name = 1; - void clear_collection_name(); - const std::string& collection_name() const; - void set_collection_name(const std::string& value); - void set_collection_name(std::string&& value); - void set_collection_name(const char* value); - void set_collection_name(const char* value, size_t size); - std::string* mutable_collection_name(); - std::string* release_collection_name(); - void set_allocated_collection_name(std::string* collection_name); - - // int64 id = 2; - void clear_id(); - ::PROTOBUF_NAMESPACE_ID::int64 id() const; - void set_id(::PROTOBUF_NAMESPACE_ID::int64 value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.HEntityIdentity) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr collection_name_; - ::PROTOBUF_NAMESPACE_ID::int64 id_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class HEntityIDs : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.HEntityIDs) */ { - public: - HEntityIDs(); - virtual ~HEntityIDs(); - - HEntityIDs(const HEntityIDs& from); - HEntityIDs(HEntityIDs&& from) noexcept - : HEntityIDs() { - *this = ::std::move(from); - } - - inline HEntityIDs& operator=(const HEntityIDs& from) { - CopyFrom(from); - return *this; - } - inline HEntityIDs& operator=(HEntityIDs&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const HEntityIDs& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const HEntityIDs* internal_default_instance() { - return reinterpret_cast( - &_HEntityIDs_default_instance_); - } - static constexpr int kIndexInFileMessages = - 45; - - friend void swap(HEntityIDs& a, HEntityIDs& b) { - a.Swap(&b); - } - inline void Swap(HEntityIDs* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline HEntityIDs* New() const final { - return CreateMaybeMessage(nullptr); - } - - HEntityIDs* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const HEntityIDs& from); - void MergeFrom(const HEntityIDs& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(HEntityIDs* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.HEntityIDs"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kEntityIdArrayFieldNumber = 2, - kStatusFieldNumber = 1, - }; - // repeated int64 entity_id_array = 2; - int entity_id_array_size() const; - void clear_entity_id_array(); - ::PROTOBUF_NAMESPACE_ID::int64 entity_id_array(int index) const; - void set_entity_id_array(int index, ::PROTOBUF_NAMESPACE_ID::int64 value); - void add_entity_id_array(::PROTOBUF_NAMESPACE_ID::int64 value); - const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >& - entity_id_array() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >* - mutable_entity_id_array(); - - // .milvus.grpc.Status status = 1; - bool has_status() const; - void clear_status(); - const ::milvus::grpc::Status& status() const; - ::milvus::grpc::Status* release_status(); - ::milvus::grpc::Status* mutable_status(); - void set_allocated_status(::milvus::grpc::Status* status); - - // @@protoc_insertion_point(class_scope:milvus.grpc.HEntityIDs) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 > entity_id_array_; - mutable std::atomic _entity_id_array_cached_byte_size_; - ::milvus::grpc::Status* status_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class HGetEntityIDsParam : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.HGetEntityIDsParam) */ { - public: - HGetEntityIDsParam(); - virtual ~HGetEntityIDsParam(); - - HGetEntityIDsParam(const HGetEntityIDsParam& from); - HGetEntityIDsParam(HGetEntityIDsParam&& from) noexcept - : HGetEntityIDsParam() { - *this = ::std::move(from); - } - - inline HGetEntityIDsParam& operator=(const HGetEntityIDsParam& from) { - CopyFrom(from); - return *this; - } - inline HGetEntityIDsParam& operator=(HGetEntityIDsParam&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const HGetEntityIDsParam& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const HGetEntityIDsParam* internal_default_instance() { - return reinterpret_cast( - &_HGetEntityIDsParam_default_instance_); - } - static constexpr int kIndexInFileMessages = - 46; - - friend void swap(HGetEntityIDsParam& a, HGetEntityIDsParam& b) { - a.Swap(&b); - } - inline void Swap(HGetEntityIDsParam* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline HGetEntityIDsParam* New() const final { - return CreateMaybeMessage(nullptr); - } - - HGetEntityIDsParam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const HGetEntityIDsParam& from); - void MergeFrom(const HGetEntityIDsParam& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(HGetEntityIDsParam* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.HGetEntityIDsParam"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kCollectionNameFieldNumber = 1, - kSegmentNameFieldNumber = 2, - }; - // string collection_name = 1; - void clear_collection_name(); - const std::string& collection_name() const; - void set_collection_name(const std::string& value); - void set_collection_name(std::string&& value); - void set_collection_name(const char* value); - void set_collection_name(const char* value, size_t size); - std::string* mutable_collection_name(); - std::string* release_collection_name(); - void set_allocated_collection_name(std::string* collection_name); - - // string segment_name = 2; - void clear_segment_name(); - const std::string& segment_name() const; - void set_segment_name(const std::string& value); - void set_segment_name(std::string&& value); - void set_segment_name(const char* value); - void set_segment_name(const char* value, size_t size); - std::string* mutable_segment_name(); - std::string* release_segment_name(); - void set_allocated_segment_name(std::string* segment_name); - - // @@protoc_insertion_point(class_scope:milvus.grpc.HGetEntityIDsParam) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr collection_name_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr segment_name_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class HDeleteByIDParam : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.HDeleteByIDParam) */ { - public: - HDeleteByIDParam(); - virtual ~HDeleteByIDParam(); - - HDeleteByIDParam(const HDeleteByIDParam& from); - HDeleteByIDParam(HDeleteByIDParam&& from) noexcept - : HDeleteByIDParam() { - *this = ::std::move(from); - } - - inline HDeleteByIDParam& operator=(const HDeleteByIDParam& from) { - CopyFrom(from); - return *this; - } - inline HDeleteByIDParam& operator=(HDeleteByIDParam&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const HDeleteByIDParam& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const HDeleteByIDParam* internal_default_instance() { - return reinterpret_cast( - &_HDeleteByIDParam_default_instance_); - } - static constexpr int kIndexInFileMessages = - 47; - - friend void swap(HDeleteByIDParam& a, HDeleteByIDParam& b) { - a.Swap(&b); - } - inline void Swap(HDeleteByIDParam* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline HDeleteByIDParam* New() const final { - return CreateMaybeMessage(nullptr); - } - - HDeleteByIDParam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const HDeleteByIDParam& from); - void MergeFrom(const HDeleteByIDParam& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(HDeleteByIDParam* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.HDeleteByIDParam"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kIdArrayFieldNumber = 2, - kCollectionNameFieldNumber = 1, - }; - // repeated int64 id_array = 2; - int id_array_size() const; - void clear_id_array(); - ::PROTOBUF_NAMESPACE_ID::int64 id_array(int index) const; - void set_id_array(int index, ::PROTOBUF_NAMESPACE_ID::int64 value); - void add_id_array(::PROTOBUF_NAMESPACE_ID::int64 value); - const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >& - id_array() const; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >* - mutable_id_array(); - - // string collection_name = 1; - void clear_collection_name(); - const std::string& collection_name() const; - void set_collection_name(const std::string& value); - void set_collection_name(std::string&& value); - void set_collection_name(const char* value); - void set_collection_name(const char* value, size_t size); - std::string* mutable_collection_name(); - std::string* release_collection_name(); - void set_allocated_collection_name(std::string* collection_name); - - // @@protoc_insertion_point(class_scope:milvus.grpc.HDeleteByIDParam) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 > id_array_; - mutable std::atomic _id_array_cached_byte_size_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr collection_name_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// ------------------------------------------------------------------- - -class HIndexParam : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.HIndexParam) */ { - public: - HIndexParam(); - virtual ~HIndexParam(); - - HIndexParam(const HIndexParam& from); - HIndexParam(HIndexParam&& from) noexcept - : HIndexParam() { - *this = ::std::move(from); - } - - inline HIndexParam& operator=(const HIndexParam& from) { - CopyFrom(from); - return *this; - } - inline HIndexParam& operator=(HIndexParam&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const HIndexParam& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const HIndexParam* internal_default_instance() { - return reinterpret_cast( - &_HIndexParam_default_instance_); - } - static constexpr int kIndexInFileMessages = - 48; - - friend void swap(HIndexParam& a, HIndexParam& b) { - a.Swap(&b); - } - inline void Swap(HIndexParam* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline HIndexParam* New() const final { - return CreateMaybeMessage(nullptr); - } - - HIndexParam* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const HIndexParam& from); - void MergeFrom(const HIndexParam& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(HIndexParam* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.HIndexParam"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_milvus_2eproto); - return ::descriptor_table_milvus_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kExtraParamsFieldNumber = 4, - kCollectionNameFieldNumber = 2, - kStatusFieldNumber = 1, - kIndexTypeFieldNumber = 3, - }; - // repeated .milvus.grpc.KeyValuePair extra_params = 4; - int extra_params_size() const; - void clear_extra_params(); - ::milvus::grpc::KeyValuePair* mutable_extra_params(int index); - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* - mutable_extra_params(); - const ::milvus::grpc::KeyValuePair& extra_params(int index) const; - ::milvus::grpc::KeyValuePair* add_extra_params(); - const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& - extra_params() const; - - // string collection_name = 2; - void clear_collection_name(); - const std::string& collection_name() const; - void set_collection_name(const std::string& value); - void set_collection_name(std::string&& value); - void set_collection_name(const char* value); - void set_collection_name(const char* value, size_t size); - std::string* mutable_collection_name(); - std::string* release_collection_name(); - void set_allocated_collection_name(std::string* collection_name); - - // .milvus.grpc.Status status = 1; - bool has_status() const; - void clear_status(); - const ::milvus::grpc::Status& status() const; - ::milvus::grpc::Status* release_status(); - ::milvus::grpc::Status* mutable_status(); - void set_allocated_status(::milvus::grpc::Status* status); - - // int32 index_type = 3; - void clear_index_type(); - ::PROTOBUF_NAMESPACE_ID::int32 index_type() const; - void set_index_type(::PROTOBUF_NAMESPACE_ID::int32 value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.HIndexParam) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair > extra_params_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr collection_name_; - ::milvus::grpc::Status* status_; - ::PROTOBUF_NAMESPACE_ID::int32 index_type_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_milvus_2eproto; -}; -// =================================================================== - - -// =================================================================== - -#ifdef __GNUC__ - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wstrict-aliasing" -#endif // __GNUC__ -// KeyValuePair - -// string key = 1; -inline void KeyValuePair::clear_key() { - key_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& KeyValuePair::key() const { - // @@protoc_insertion_point(field_get:milvus.grpc.KeyValuePair.key) - return key_.GetNoArena(); -} -inline void KeyValuePair::set_key(const std::string& value) { - - key_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.KeyValuePair.key) -} -inline void KeyValuePair::set_key(std::string&& value) { - - key_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.KeyValuePair.key) -} -inline void KeyValuePair::set_key(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - key_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.KeyValuePair.key) -} -inline void KeyValuePair::set_key(const char* value, size_t size) { - - key_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.KeyValuePair.key) -} -inline std::string* KeyValuePair::mutable_key() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.KeyValuePair.key) - return key_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* KeyValuePair::release_key() { - // @@protoc_insertion_point(field_release:milvus.grpc.KeyValuePair.key) - - return key_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void KeyValuePair::set_allocated_key(std::string* key) { - if (key != nullptr) { - - } else { - - } - key_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), key); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.KeyValuePair.key) -} - -// string value = 2; -inline void KeyValuePair::clear_value() { - value_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& KeyValuePair::value() const { - // @@protoc_insertion_point(field_get:milvus.grpc.KeyValuePair.value) - return value_.GetNoArena(); -} -inline void KeyValuePair::set_value(const std::string& value) { - - value_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.KeyValuePair.value) -} -inline void KeyValuePair::set_value(std::string&& value) { - - value_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.KeyValuePair.value) -} -inline void KeyValuePair::set_value(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - value_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.KeyValuePair.value) -} -inline void KeyValuePair::set_value(const char* value, size_t size) { - - value_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.KeyValuePair.value) -} -inline std::string* KeyValuePair::mutable_value() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.KeyValuePair.value) - return value_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* KeyValuePair::release_value() { - // @@protoc_insertion_point(field_release:milvus.grpc.KeyValuePair.value) - - return value_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void KeyValuePair::set_allocated_value(std::string* value) { - if (value != nullptr) { - - } else { - - } - value_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.KeyValuePair.value) -} - -// ------------------------------------------------------------------- - -// CollectionName - -// string collection_name = 1; -inline void CollectionName::clear_collection_name() { - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& CollectionName::collection_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.CollectionName.collection_name) - return collection_name_.GetNoArena(); -} -inline void CollectionName::set_collection_name(const std::string& value) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.CollectionName.collection_name) -} -inline void CollectionName::set_collection_name(std::string&& value) { - - collection_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.CollectionName.collection_name) -} -inline void CollectionName::set_collection_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.CollectionName.collection_name) -} -inline void CollectionName::set_collection_name(const char* value, size_t size) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.CollectionName.collection_name) -} -inline std::string* CollectionName::mutable_collection_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.CollectionName.collection_name) - return collection_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* CollectionName::release_collection_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.CollectionName.collection_name) - - return collection_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void CollectionName::set_allocated_collection_name(std::string* collection_name) { - if (collection_name != nullptr) { - - } else { - - } - collection_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), collection_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.CollectionName.collection_name) -} - -// ------------------------------------------------------------------- - -// CollectionNameList - -// .milvus.grpc.Status status = 1; -inline bool CollectionNameList::has_status() const { - return this != internal_default_instance() && status_ != nullptr; -} -inline const ::milvus::grpc::Status& CollectionNameList::status() const { - const ::milvus::grpc::Status* p = status_; - // @@protoc_insertion_point(field_get:milvus.grpc.CollectionNameList.status) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_Status_default_instance_); -} -inline ::milvus::grpc::Status* CollectionNameList::release_status() { - // @@protoc_insertion_point(field_release:milvus.grpc.CollectionNameList.status) - - ::milvus::grpc::Status* temp = status_; - status_ = nullptr; - return temp; -} -inline ::milvus::grpc::Status* CollectionNameList::mutable_status() { - - if (status_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::Status>(GetArenaNoVirtual()); - status_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.CollectionNameList.status) - return status_; -} -inline void CollectionNameList::set_allocated_status(::milvus::grpc::Status* status) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(status_); - } - if (status) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - status = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, status, submessage_arena); - } - - } else { - - } - status_ = status; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.CollectionNameList.status) -} - -// repeated string collection_names = 2; -inline int CollectionNameList::collection_names_size() const { - return collection_names_.size(); -} -inline void CollectionNameList::clear_collection_names() { - collection_names_.Clear(); -} -inline const std::string& CollectionNameList::collection_names(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.CollectionNameList.collection_names) - return collection_names_.Get(index); -} -inline std::string* CollectionNameList::mutable_collection_names(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.CollectionNameList.collection_names) - return collection_names_.Mutable(index); -} -inline void CollectionNameList::set_collection_names(int index, const std::string& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.CollectionNameList.collection_names) - collection_names_.Mutable(index)->assign(value); -} -inline void CollectionNameList::set_collection_names(int index, std::string&& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.CollectionNameList.collection_names) - collection_names_.Mutable(index)->assign(std::move(value)); -} -inline void CollectionNameList::set_collection_names(int index, const char* value) { - GOOGLE_DCHECK(value != nullptr); - collection_names_.Mutable(index)->assign(value); - // @@protoc_insertion_point(field_set_char:milvus.grpc.CollectionNameList.collection_names) -} -inline void CollectionNameList::set_collection_names(int index, const char* value, size_t size) { - collection_names_.Mutable(index)->assign( - reinterpret_cast(value), size); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.CollectionNameList.collection_names) -} -inline std::string* CollectionNameList::add_collection_names() { - // @@protoc_insertion_point(field_add_mutable:milvus.grpc.CollectionNameList.collection_names) - return collection_names_.Add(); -} -inline void CollectionNameList::add_collection_names(const std::string& value) { - collection_names_.Add()->assign(value); - // @@protoc_insertion_point(field_add:milvus.grpc.CollectionNameList.collection_names) -} -inline void CollectionNameList::add_collection_names(std::string&& value) { - collection_names_.Add(std::move(value)); - // @@protoc_insertion_point(field_add:milvus.grpc.CollectionNameList.collection_names) -} -inline void CollectionNameList::add_collection_names(const char* value) { - GOOGLE_DCHECK(value != nullptr); - collection_names_.Add()->assign(value); - // @@protoc_insertion_point(field_add_char:milvus.grpc.CollectionNameList.collection_names) -} -inline void CollectionNameList::add_collection_names(const char* value, size_t size) { - collection_names_.Add()->assign(reinterpret_cast(value), size); - // @@protoc_insertion_point(field_add_pointer:milvus.grpc.CollectionNameList.collection_names) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& -CollectionNameList::collection_names() const { - // @@protoc_insertion_point(field_list:milvus.grpc.CollectionNameList.collection_names) - return collection_names_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* -CollectionNameList::mutable_collection_names() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.CollectionNameList.collection_names) - return &collection_names_; -} - -// ------------------------------------------------------------------- - -// CollectionSchema - -// .milvus.grpc.Status status = 1; -inline bool CollectionSchema::has_status() const { - return this != internal_default_instance() && status_ != nullptr; -} -inline const ::milvus::grpc::Status& CollectionSchema::status() const { - const ::milvus::grpc::Status* p = status_; - // @@protoc_insertion_point(field_get:milvus.grpc.CollectionSchema.status) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_Status_default_instance_); -} -inline ::milvus::grpc::Status* CollectionSchema::release_status() { - // @@protoc_insertion_point(field_release:milvus.grpc.CollectionSchema.status) - - ::milvus::grpc::Status* temp = status_; - status_ = nullptr; - return temp; -} -inline ::milvus::grpc::Status* CollectionSchema::mutable_status() { - - if (status_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::Status>(GetArenaNoVirtual()); - status_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.CollectionSchema.status) - return status_; -} -inline void CollectionSchema::set_allocated_status(::milvus::grpc::Status* status) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(status_); - } - if (status) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - status = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, status, submessage_arena); - } - - } else { - - } - status_ = status; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.CollectionSchema.status) -} - -// string collection_name = 2; -inline void CollectionSchema::clear_collection_name() { - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& CollectionSchema::collection_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.CollectionSchema.collection_name) - return collection_name_.GetNoArena(); -} -inline void CollectionSchema::set_collection_name(const std::string& value) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.CollectionSchema.collection_name) -} -inline void CollectionSchema::set_collection_name(std::string&& value) { - - collection_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.CollectionSchema.collection_name) -} -inline void CollectionSchema::set_collection_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.CollectionSchema.collection_name) -} -inline void CollectionSchema::set_collection_name(const char* value, size_t size) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.CollectionSchema.collection_name) -} -inline std::string* CollectionSchema::mutable_collection_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.CollectionSchema.collection_name) - return collection_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* CollectionSchema::release_collection_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.CollectionSchema.collection_name) - - return collection_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void CollectionSchema::set_allocated_collection_name(std::string* collection_name) { - if (collection_name != nullptr) { - - } else { - - } - collection_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), collection_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.CollectionSchema.collection_name) -} - -// int64 dimension = 3; -inline void CollectionSchema::clear_dimension() { - dimension_ = PROTOBUF_LONGLONG(0); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 CollectionSchema::dimension() const { - // @@protoc_insertion_point(field_get:milvus.grpc.CollectionSchema.dimension) - return dimension_; -} -inline void CollectionSchema::set_dimension(::PROTOBUF_NAMESPACE_ID::int64 value) { - - dimension_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.CollectionSchema.dimension) -} - -// int64 index_file_size = 4; -inline void CollectionSchema::clear_index_file_size() { - index_file_size_ = PROTOBUF_LONGLONG(0); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 CollectionSchema::index_file_size() const { - // @@protoc_insertion_point(field_get:milvus.grpc.CollectionSchema.index_file_size) - return index_file_size_; -} -inline void CollectionSchema::set_index_file_size(::PROTOBUF_NAMESPACE_ID::int64 value) { - - index_file_size_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.CollectionSchema.index_file_size) -} - -// int32 metric_type = 5; -inline void CollectionSchema::clear_metric_type() { - metric_type_ = 0; -} -inline ::PROTOBUF_NAMESPACE_ID::int32 CollectionSchema::metric_type() const { - // @@protoc_insertion_point(field_get:milvus.grpc.CollectionSchema.metric_type) - return metric_type_; -} -inline void CollectionSchema::set_metric_type(::PROTOBUF_NAMESPACE_ID::int32 value) { - - metric_type_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.CollectionSchema.metric_type) -} - -// repeated .milvus.grpc.KeyValuePair extra_params = 6; -inline int CollectionSchema::extra_params_size() const { - return extra_params_.size(); -} -inline void CollectionSchema::clear_extra_params() { - extra_params_.Clear(); -} -inline ::milvus::grpc::KeyValuePair* CollectionSchema::mutable_extra_params(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.CollectionSchema.extra_params) - return extra_params_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* -CollectionSchema::mutable_extra_params() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.CollectionSchema.extra_params) - return &extra_params_; -} -inline const ::milvus::grpc::KeyValuePair& CollectionSchema::extra_params(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.CollectionSchema.extra_params) - return extra_params_.Get(index); -} -inline ::milvus::grpc::KeyValuePair* CollectionSchema::add_extra_params() { - // @@protoc_insertion_point(field_add:milvus.grpc.CollectionSchema.extra_params) - return extra_params_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& -CollectionSchema::extra_params() const { - // @@protoc_insertion_point(field_list:milvus.grpc.CollectionSchema.extra_params) - return extra_params_; -} - -// ------------------------------------------------------------------- - -// PartitionParam - -// string collection_name = 1; -inline void PartitionParam::clear_collection_name() { - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& PartitionParam::collection_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.PartitionParam.collection_name) - return collection_name_.GetNoArena(); -} -inline void PartitionParam::set_collection_name(const std::string& value) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.PartitionParam.collection_name) -} -inline void PartitionParam::set_collection_name(std::string&& value) { - - collection_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.PartitionParam.collection_name) -} -inline void PartitionParam::set_collection_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.PartitionParam.collection_name) -} -inline void PartitionParam::set_collection_name(const char* value, size_t size) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.PartitionParam.collection_name) -} -inline std::string* PartitionParam::mutable_collection_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.PartitionParam.collection_name) - return collection_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* PartitionParam::release_collection_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.PartitionParam.collection_name) - - return collection_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void PartitionParam::set_allocated_collection_name(std::string* collection_name) { - if (collection_name != nullptr) { - - } else { - - } - collection_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), collection_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.PartitionParam.collection_name) -} - -// string tag = 2; -inline void PartitionParam::clear_tag() { - tag_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& PartitionParam::tag() const { - // @@protoc_insertion_point(field_get:milvus.grpc.PartitionParam.tag) - return tag_.GetNoArena(); -} -inline void PartitionParam::set_tag(const std::string& value) { - - tag_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.PartitionParam.tag) -} -inline void PartitionParam::set_tag(std::string&& value) { - - tag_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.PartitionParam.tag) -} -inline void PartitionParam::set_tag(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - tag_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.PartitionParam.tag) -} -inline void PartitionParam::set_tag(const char* value, size_t size) { - - tag_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.PartitionParam.tag) -} -inline std::string* PartitionParam::mutable_tag() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.PartitionParam.tag) - return tag_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* PartitionParam::release_tag() { - // @@protoc_insertion_point(field_release:milvus.grpc.PartitionParam.tag) - - return tag_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void PartitionParam::set_allocated_tag(std::string* tag) { - if (tag != nullptr) { - - } else { - - } - tag_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), tag); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.PartitionParam.tag) -} - -// ------------------------------------------------------------------- - -// PartitionList - -// .milvus.grpc.Status status = 1; -inline bool PartitionList::has_status() const { - return this != internal_default_instance() && status_ != nullptr; -} -inline const ::milvus::grpc::Status& PartitionList::status() const { - const ::milvus::grpc::Status* p = status_; - // @@protoc_insertion_point(field_get:milvus.grpc.PartitionList.status) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_Status_default_instance_); -} -inline ::milvus::grpc::Status* PartitionList::release_status() { - // @@protoc_insertion_point(field_release:milvus.grpc.PartitionList.status) - - ::milvus::grpc::Status* temp = status_; - status_ = nullptr; - return temp; -} -inline ::milvus::grpc::Status* PartitionList::mutable_status() { - - if (status_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::Status>(GetArenaNoVirtual()); - status_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.PartitionList.status) - return status_; -} -inline void PartitionList::set_allocated_status(::milvus::grpc::Status* status) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(status_); - } - if (status) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - status = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, status, submessage_arena); - } - - } else { - - } - status_ = status; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.PartitionList.status) -} - -// repeated string partition_tag_array = 2; -inline int PartitionList::partition_tag_array_size() const { - return partition_tag_array_.size(); -} -inline void PartitionList::clear_partition_tag_array() { - partition_tag_array_.Clear(); -} -inline const std::string& PartitionList::partition_tag_array(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.PartitionList.partition_tag_array) - return partition_tag_array_.Get(index); -} -inline std::string* PartitionList::mutable_partition_tag_array(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.PartitionList.partition_tag_array) - return partition_tag_array_.Mutable(index); -} -inline void PartitionList::set_partition_tag_array(int index, const std::string& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.PartitionList.partition_tag_array) - partition_tag_array_.Mutable(index)->assign(value); -} -inline void PartitionList::set_partition_tag_array(int index, std::string&& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.PartitionList.partition_tag_array) - partition_tag_array_.Mutable(index)->assign(std::move(value)); -} -inline void PartitionList::set_partition_tag_array(int index, const char* value) { - GOOGLE_DCHECK(value != nullptr); - partition_tag_array_.Mutable(index)->assign(value); - // @@protoc_insertion_point(field_set_char:milvus.grpc.PartitionList.partition_tag_array) -} -inline void PartitionList::set_partition_tag_array(int index, const char* value, size_t size) { - partition_tag_array_.Mutable(index)->assign( - reinterpret_cast(value), size); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.PartitionList.partition_tag_array) -} -inline std::string* PartitionList::add_partition_tag_array() { - // @@protoc_insertion_point(field_add_mutable:milvus.grpc.PartitionList.partition_tag_array) - return partition_tag_array_.Add(); -} -inline void PartitionList::add_partition_tag_array(const std::string& value) { - partition_tag_array_.Add()->assign(value); - // @@protoc_insertion_point(field_add:milvus.grpc.PartitionList.partition_tag_array) -} -inline void PartitionList::add_partition_tag_array(std::string&& value) { - partition_tag_array_.Add(std::move(value)); - // @@protoc_insertion_point(field_add:milvus.grpc.PartitionList.partition_tag_array) -} -inline void PartitionList::add_partition_tag_array(const char* value) { - GOOGLE_DCHECK(value != nullptr); - partition_tag_array_.Add()->assign(value); - // @@protoc_insertion_point(field_add_char:milvus.grpc.PartitionList.partition_tag_array) -} -inline void PartitionList::add_partition_tag_array(const char* value, size_t size) { - partition_tag_array_.Add()->assign(reinterpret_cast(value), size); - // @@protoc_insertion_point(field_add_pointer:milvus.grpc.PartitionList.partition_tag_array) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& -PartitionList::partition_tag_array() const { - // @@protoc_insertion_point(field_list:milvus.grpc.PartitionList.partition_tag_array) - return partition_tag_array_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* -PartitionList::mutable_partition_tag_array() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.PartitionList.partition_tag_array) - return &partition_tag_array_; -} - -// ------------------------------------------------------------------- - -// RowRecord - -// repeated float float_data = 1; -inline int RowRecord::float_data_size() const { - return float_data_.size(); -} -inline void RowRecord::clear_float_data() { - float_data_.Clear(); -} -inline float RowRecord::float_data(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.RowRecord.float_data) - return float_data_.Get(index); -} -inline void RowRecord::set_float_data(int index, float value) { - float_data_.Set(index, value); - // @@protoc_insertion_point(field_set:milvus.grpc.RowRecord.float_data) -} -inline void RowRecord::add_float_data(float value) { - float_data_.Add(value); - // @@protoc_insertion_point(field_add:milvus.grpc.RowRecord.float_data) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >& -RowRecord::float_data() const { - // @@protoc_insertion_point(field_list:milvus.grpc.RowRecord.float_data) - return float_data_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >* -RowRecord::mutable_float_data() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.RowRecord.float_data) - return &float_data_; -} - -// bytes binary_data = 2; -inline void RowRecord::clear_binary_data() { - binary_data_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& RowRecord::binary_data() const { - // @@protoc_insertion_point(field_get:milvus.grpc.RowRecord.binary_data) - return binary_data_.GetNoArena(); -} -inline void RowRecord::set_binary_data(const std::string& value) { - - binary_data_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.RowRecord.binary_data) -} -inline void RowRecord::set_binary_data(std::string&& value) { - - binary_data_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.RowRecord.binary_data) -} -inline void RowRecord::set_binary_data(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - binary_data_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.RowRecord.binary_data) -} -inline void RowRecord::set_binary_data(const void* value, size_t size) { - - binary_data_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.RowRecord.binary_data) -} -inline std::string* RowRecord::mutable_binary_data() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.RowRecord.binary_data) - return binary_data_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* RowRecord::release_binary_data() { - // @@protoc_insertion_point(field_release:milvus.grpc.RowRecord.binary_data) - - return binary_data_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void RowRecord::set_allocated_binary_data(std::string* binary_data) { - if (binary_data != nullptr) { - - } else { - - } - binary_data_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), binary_data); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.RowRecord.binary_data) -} - -// ------------------------------------------------------------------- - -// InsertParam - -// string collection_name = 1; -inline void InsertParam::clear_collection_name() { - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& InsertParam::collection_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.InsertParam.collection_name) - return collection_name_.GetNoArena(); -} -inline void InsertParam::set_collection_name(const std::string& value) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.InsertParam.collection_name) -} -inline void InsertParam::set_collection_name(std::string&& value) { - - collection_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.InsertParam.collection_name) -} -inline void InsertParam::set_collection_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.InsertParam.collection_name) -} -inline void InsertParam::set_collection_name(const char* value, size_t size) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.InsertParam.collection_name) -} -inline std::string* InsertParam::mutable_collection_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.InsertParam.collection_name) - return collection_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* InsertParam::release_collection_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.InsertParam.collection_name) - - return collection_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void InsertParam::set_allocated_collection_name(std::string* collection_name) { - if (collection_name != nullptr) { - - } else { - - } - collection_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), collection_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.InsertParam.collection_name) -} - -// repeated .milvus.grpc.RowRecord row_record_array = 2; -inline int InsertParam::row_record_array_size() const { - return row_record_array_.size(); -} -inline void InsertParam::clear_row_record_array() { - row_record_array_.Clear(); -} -inline ::milvus::grpc::RowRecord* InsertParam::mutable_row_record_array(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.InsertParam.row_record_array) - return row_record_array_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >* -InsertParam::mutable_row_record_array() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.InsertParam.row_record_array) - return &row_record_array_; -} -inline const ::milvus::grpc::RowRecord& InsertParam::row_record_array(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.InsertParam.row_record_array) - return row_record_array_.Get(index); -} -inline ::milvus::grpc::RowRecord* InsertParam::add_row_record_array() { - // @@protoc_insertion_point(field_add:milvus.grpc.InsertParam.row_record_array) - return row_record_array_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >& -InsertParam::row_record_array() const { - // @@protoc_insertion_point(field_list:milvus.grpc.InsertParam.row_record_array) - return row_record_array_; -} - -// repeated int64 row_id_array = 3; -inline int InsertParam::row_id_array_size() const { - return row_id_array_.size(); -} -inline void InsertParam::clear_row_id_array() { - row_id_array_.Clear(); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 InsertParam::row_id_array(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.InsertParam.row_id_array) - return row_id_array_.Get(index); -} -inline void InsertParam::set_row_id_array(int index, ::PROTOBUF_NAMESPACE_ID::int64 value) { - row_id_array_.Set(index, value); - // @@protoc_insertion_point(field_set:milvus.grpc.InsertParam.row_id_array) -} -inline void InsertParam::add_row_id_array(::PROTOBUF_NAMESPACE_ID::int64 value) { - row_id_array_.Add(value); - // @@protoc_insertion_point(field_add:milvus.grpc.InsertParam.row_id_array) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >& -InsertParam::row_id_array() const { - // @@protoc_insertion_point(field_list:milvus.grpc.InsertParam.row_id_array) - return row_id_array_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >* -InsertParam::mutable_row_id_array() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.InsertParam.row_id_array) - return &row_id_array_; -} - -// string partition_tag = 4; -inline void InsertParam::clear_partition_tag() { - partition_tag_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& InsertParam::partition_tag() const { - // @@protoc_insertion_point(field_get:milvus.grpc.InsertParam.partition_tag) - return partition_tag_.GetNoArena(); -} -inline void InsertParam::set_partition_tag(const std::string& value) { - - partition_tag_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.InsertParam.partition_tag) -} -inline void InsertParam::set_partition_tag(std::string&& value) { - - partition_tag_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.InsertParam.partition_tag) -} -inline void InsertParam::set_partition_tag(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - partition_tag_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.InsertParam.partition_tag) -} -inline void InsertParam::set_partition_tag(const char* value, size_t size) { - - partition_tag_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.InsertParam.partition_tag) -} -inline std::string* InsertParam::mutable_partition_tag() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.InsertParam.partition_tag) - return partition_tag_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* InsertParam::release_partition_tag() { - // @@protoc_insertion_point(field_release:milvus.grpc.InsertParam.partition_tag) - - return partition_tag_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void InsertParam::set_allocated_partition_tag(std::string* partition_tag) { - if (partition_tag != nullptr) { - - } else { - - } - partition_tag_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), partition_tag); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.InsertParam.partition_tag) -} - -// repeated .milvus.grpc.KeyValuePair extra_params = 5; -inline int InsertParam::extra_params_size() const { - return extra_params_.size(); -} -inline void InsertParam::clear_extra_params() { - extra_params_.Clear(); -} -inline ::milvus::grpc::KeyValuePair* InsertParam::mutable_extra_params(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.InsertParam.extra_params) - return extra_params_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* -InsertParam::mutable_extra_params() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.InsertParam.extra_params) - return &extra_params_; -} -inline const ::milvus::grpc::KeyValuePair& InsertParam::extra_params(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.InsertParam.extra_params) - return extra_params_.Get(index); -} -inline ::milvus::grpc::KeyValuePair* InsertParam::add_extra_params() { - // @@protoc_insertion_point(field_add:milvus.grpc.InsertParam.extra_params) - return extra_params_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& -InsertParam::extra_params() const { - // @@protoc_insertion_point(field_list:milvus.grpc.InsertParam.extra_params) - return extra_params_; -} - -// ------------------------------------------------------------------- - -// VectorIds - -// .milvus.grpc.Status status = 1; -inline bool VectorIds::has_status() const { - return this != internal_default_instance() && status_ != nullptr; -} -inline const ::milvus::grpc::Status& VectorIds::status() const { - const ::milvus::grpc::Status* p = status_; - // @@protoc_insertion_point(field_get:milvus.grpc.VectorIds.status) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_Status_default_instance_); -} -inline ::milvus::grpc::Status* VectorIds::release_status() { - // @@protoc_insertion_point(field_release:milvus.grpc.VectorIds.status) - - ::milvus::grpc::Status* temp = status_; - status_ = nullptr; - return temp; -} -inline ::milvus::grpc::Status* VectorIds::mutable_status() { - - if (status_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::Status>(GetArenaNoVirtual()); - status_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.VectorIds.status) - return status_; -} -inline void VectorIds::set_allocated_status(::milvus::grpc::Status* status) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(status_); - } - if (status) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - status = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, status, submessage_arena); - } - - } else { - - } - status_ = status; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.VectorIds.status) -} - -// repeated int64 vector_id_array = 2; -inline int VectorIds::vector_id_array_size() const { - return vector_id_array_.size(); -} -inline void VectorIds::clear_vector_id_array() { - vector_id_array_.Clear(); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 VectorIds::vector_id_array(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.VectorIds.vector_id_array) - return vector_id_array_.Get(index); -} -inline void VectorIds::set_vector_id_array(int index, ::PROTOBUF_NAMESPACE_ID::int64 value) { - vector_id_array_.Set(index, value); - // @@protoc_insertion_point(field_set:milvus.grpc.VectorIds.vector_id_array) -} -inline void VectorIds::add_vector_id_array(::PROTOBUF_NAMESPACE_ID::int64 value) { - vector_id_array_.Add(value); - // @@protoc_insertion_point(field_add:milvus.grpc.VectorIds.vector_id_array) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >& -VectorIds::vector_id_array() const { - // @@protoc_insertion_point(field_list:milvus.grpc.VectorIds.vector_id_array) - return vector_id_array_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >* -VectorIds::mutable_vector_id_array() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.VectorIds.vector_id_array) - return &vector_id_array_; -} - -// ------------------------------------------------------------------- - -// SearchParam - -// string collection_name = 1; -inline void SearchParam::clear_collection_name() { - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& SearchParam::collection_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.SearchParam.collection_name) - return collection_name_.GetNoArena(); -} -inline void SearchParam::set_collection_name(const std::string& value) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.SearchParam.collection_name) -} -inline void SearchParam::set_collection_name(std::string&& value) { - - collection_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.SearchParam.collection_name) -} -inline void SearchParam::set_collection_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.SearchParam.collection_name) -} -inline void SearchParam::set_collection_name(const char* value, size_t size) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.SearchParam.collection_name) -} -inline std::string* SearchParam::mutable_collection_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.SearchParam.collection_name) - return collection_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* SearchParam::release_collection_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.SearchParam.collection_name) - - return collection_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void SearchParam::set_allocated_collection_name(std::string* collection_name) { - if (collection_name != nullptr) { - - } else { - - } - collection_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), collection_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.SearchParam.collection_name) -} - -// repeated string partition_tag_array = 2; -inline int SearchParam::partition_tag_array_size() const { - return partition_tag_array_.size(); -} -inline void SearchParam::clear_partition_tag_array() { - partition_tag_array_.Clear(); -} -inline const std::string& SearchParam::partition_tag_array(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.SearchParam.partition_tag_array) - return partition_tag_array_.Get(index); -} -inline std::string* SearchParam::mutable_partition_tag_array(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.SearchParam.partition_tag_array) - return partition_tag_array_.Mutable(index); -} -inline void SearchParam::set_partition_tag_array(int index, const std::string& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.SearchParam.partition_tag_array) - partition_tag_array_.Mutable(index)->assign(value); -} -inline void SearchParam::set_partition_tag_array(int index, std::string&& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.SearchParam.partition_tag_array) - partition_tag_array_.Mutable(index)->assign(std::move(value)); -} -inline void SearchParam::set_partition_tag_array(int index, const char* value) { - GOOGLE_DCHECK(value != nullptr); - partition_tag_array_.Mutable(index)->assign(value); - // @@protoc_insertion_point(field_set_char:milvus.grpc.SearchParam.partition_tag_array) -} -inline void SearchParam::set_partition_tag_array(int index, const char* value, size_t size) { - partition_tag_array_.Mutable(index)->assign( - reinterpret_cast(value), size); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.SearchParam.partition_tag_array) -} -inline std::string* SearchParam::add_partition_tag_array() { - // @@protoc_insertion_point(field_add_mutable:milvus.grpc.SearchParam.partition_tag_array) - return partition_tag_array_.Add(); -} -inline void SearchParam::add_partition_tag_array(const std::string& value) { - partition_tag_array_.Add()->assign(value); - // @@protoc_insertion_point(field_add:milvus.grpc.SearchParam.partition_tag_array) -} -inline void SearchParam::add_partition_tag_array(std::string&& value) { - partition_tag_array_.Add(std::move(value)); - // @@protoc_insertion_point(field_add:milvus.grpc.SearchParam.partition_tag_array) -} -inline void SearchParam::add_partition_tag_array(const char* value) { - GOOGLE_DCHECK(value != nullptr); - partition_tag_array_.Add()->assign(value); - // @@protoc_insertion_point(field_add_char:milvus.grpc.SearchParam.partition_tag_array) -} -inline void SearchParam::add_partition_tag_array(const char* value, size_t size) { - partition_tag_array_.Add()->assign(reinterpret_cast(value), size); - // @@protoc_insertion_point(field_add_pointer:milvus.grpc.SearchParam.partition_tag_array) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& -SearchParam::partition_tag_array() const { - // @@protoc_insertion_point(field_list:milvus.grpc.SearchParam.partition_tag_array) - return partition_tag_array_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* -SearchParam::mutable_partition_tag_array() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.SearchParam.partition_tag_array) - return &partition_tag_array_; -} - -// repeated .milvus.grpc.RowRecord query_record_array = 3; -inline int SearchParam::query_record_array_size() const { - return query_record_array_.size(); -} -inline void SearchParam::clear_query_record_array() { - query_record_array_.Clear(); -} -inline ::milvus::grpc::RowRecord* SearchParam::mutable_query_record_array(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.SearchParam.query_record_array) - return query_record_array_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >* -SearchParam::mutable_query_record_array() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.SearchParam.query_record_array) - return &query_record_array_; -} -inline const ::milvus::grpc::RowRecord& SearchParam::query_record_array(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.SearchParam.query_record_array) - return query_record_array_.Get(index); -} -inline ::milvus::grpc::RowRecord* SearchParam::add_query_record_array() { - // @@protoc_insertion_point(field_add:milvus.grpc.SearchParam.query_record_array) - return query_record_array_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >& -SearchParam::query_record_array() const { - // @@protoc_insertion_point(field_list:milvus.grpc.SearchParam.query_record_array) - return query_record_array_; -} - -// int64 topk = 4; -inline void SearchParam::clear_topk() { - topk_ = PROTOBUF_LONGLONG(0); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 SearchParam::topk() const { - // @@protoc_insertion_point(field_get:milvus.grpc.SearchParam.topk) - return topk_; -} -inline void SearchParam::set_topk(::PROTOBUF_NAMESPACE_ID::int64 value) { - - topk_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.SearchParam.topk) -} - -// repeated .milvus.grpc.KeyValuePair extra_params = 5; -inline int SearchParam::extra_params_size() const { - return extra_params_.size(); -} -inline void SearchParam::clear_extra_params() { - extra_params_.Clear(); -} -inline ::milvus::grpc::KeyValuePair* SearchParam::mutable_extra_params(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.SearchParam.extra_params) - return extra_params_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* -SearchParam::mutable_extra_params() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.SearchParam.extra_params) - return &extra_params_; -} -inline const ::milvus::grpc::KeyValuePair& SearchParam::extra_params(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.SearchParam.extra_params) - return extra_params_.Get(index); -} -inline ::milvus::grpc::KeyValuePair* SearchParam::add_extra_params() { - // @@protoc_insertion_point(field_add:milvus.grpc.SearchParam.extra_params) - return extra_params_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& -SearchParam::extra_params() const { - // @@protoc_insertion_point(field_list:milvus.grpc.SearchParam.extra_params) - return extra_params_; -} - -// ------------------------------------------------------------------- - -// SearchInFilesParam - -// repeated string file_id_array = 1; -inline int SearchInFilesParam::file_id_array_size() const { - return file_id_array_.size(); -} -inline void SearchInFilesParam::clear_file_id_array() { - file_id_array_.Clear(); -} -inline const std::string& SearchInFilesParam::file_id_array(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.SearchInFilesParam.file_id_array) - return file_id_array_.Get(index); -} -inline std::string* SearchInFilesParam::mutable_file_id_array(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.SearchInFilesParam.file_id_array) - return file_id_array_.Mutable(index); -} -inline void SearchInFilesParam::set_file_id_array(int index, const std::string& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.SearchInFilesParam.file_id_array) - file_id_array_.Mutable(index)->assign(value); -} -inline void SearchInFilesParam::set_file_id_array(int index, std::string&& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.SearchInFilesParam.file_id_array) - file_id_array_.Mutable(index)->assign(std::move(value)); -} -inline void SearchInFilesParam::set_file_id_array(int index, const char* value) { - GOOGLE_DCHECK(value != nullptr); - file_id_array_.Mutable(index)->assign(value); - // @@protoc_insertion_point(field_set_char:milvus.grpc.SearchInFilesParam.file_id_array) -} -inline void SearchInFilesParam::set_file_id_array(int index, const char* value, size_t size) { - file_id_array_.Mutable(index)->assign( - reinterpret_cast(value), size); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.SearchInFilesParam.file_id_array) -} -inline std::string* SearchInFilesParam::add_file_id_array() { - // @@protoc_insertion_point(field_add_mutable:milvus.grpc.SearchInFilesParam.file_id_array) - return file_id_array_.Add(); -} -inline void SearchInFilesParam::add_file_id_array(const std::string& value) { - file_id_array_.Add()->assign(value); - // @@protoc_insertion_point(field_add:milvus.grpc.SearchInFilesParam.file_id_array) -} -inline void SearchInFilesParam::add_file_id_array(std::string&& value) { - file_id_array_.Add(std::move(value)); - // @@protoc_insertion_point(field_add:milvus.grpc.SearchInFilesParam.file_id_array) -} -inline void SearchInFilesParam::add_file_id_array(const char* value) { - GOOGLE_DCHECK(value != nullptr); - file_id_array_.Add()->assign(value); - // @@protoc_insertion_point(field_add_char:milvus.grpc.SearchInFilesParam.file_id_array) -} -inline void SearchInFilesParam::add_file_id_array(const char* value, size_t size) { - file_id_array_.Add()->assign(reinterpret_cast(value), size); - // @@protoc_insertion_point(field_add_pointer:milvus.grpc.SearchInFilesParam.file_id_array) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& -SearchInFilesParam::file_id_array() const { - // @@protoc_insertion_point(field_list:milvus.grpc.SearchInFilesParam.file_id_array) - return file_id_array_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* -SearchInFilesParam::mutable_file_id_array() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.SearchInFilesParam.file_id_array) - return &file_id_array_; -} - -// .milvus.grpc.SearchParam search_param = 2; -inline bool SearchInFilesParam::has_search_param() const { - return this != internal_default_instance() && search_param_ != nullptr; -} -inline void SearchInFilesParam::clear_search_param() { - if (GetArenaNoVirtual() == nullptr && search_param_ != nullptr) { - delete search_param_; - } - search_param_ = nullptr; -} -inline const ::milvus::grpc::SearchParam& SearchInFilesParam::search_param() const { - const ::milvus::grpc::SearchParam* p = search_param_; - // @@protoc_insertion_point(field_get:milvus.grpc.SearchInFilesParam.search_param) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_SearchParam_default_instance_); -} -inline ::milvus::grpc::SearchParam* SearchInFilesParam::release_search_param() { - // @@protoc_insertion_point(field_release:milvus.grpc.SearchInFilesParam.search_param) - - ::milvus::grpc::SearchParam* temp = search_param_; - search_param_ = nullptr; - return temp; -} -inline ::milvus::grpc::SearchParam* SearchInFilesParam::mutable_search_param() { - - if (search_param_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::SearchParam>(GetArenaNoVirtual()); - search_param_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.SearchInFilesParam.search_param) - return search_param_; -} -inline void SearchInFilesParam::set_allocated_search_param(::milvus::grpc::SearchParam* search_param) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete search_param_; - } - if (search_param) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - search_param = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, search_param, submessage_arena); - } - - } else { - - } - search_param_ = search_param; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.SearchInFilesParam.search_param) -} - -// ------------------------------------------------------------------- - -// SearchByIDParam - -// string collection_name = 1; -inline void SearchByIDParam::clear_collection_name() { - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& SearchByIDParam::collection_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.SearchByIDParam.collection_name) - return collection_name_.GetNoArena(); -} -inline void SearchByIDParam::set_collection_name(const std::string& value) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.SearchByIDParam.collection_name) -} -inline void SearchByIDParam::set_collection_name(std::string&& value) { - - collection_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.SearchByIDParam.collection_name) -} -inline void SearchByIDParam::set_collection_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.SearchByIDParam.collection_name) -} -inline void SearchByIDParam::set_collection_name(const char* value, size_t size) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.SearchByIDParam.collection_name) -} -inline std::string* SearchByIDParam::mutable_collection_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.SearchByIDParam.collection_name) - return collection_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* SearchByIDParam::release_collection_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.SearchByIDParam.collection_name) - - return collection_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void SearchByIDParam::set_allocated_collection_name(std::string* collection_name) { - if (collection_name != nullptr) { - - } else { - - } - collection_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), collection_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.SearchByIDParam.collection_name) -} - -// repeated string partition_tag_array = 2; -inline int SearchByIDParam::partition_tag_array_size() const { - return partition_tag_array_.size(); -} -inline void SearchByIDParam::clear_partition_tag_array() { - partition_tag_array_.Clear(); -} -inline const std::string& SearchByIDParam::partition_tag_array(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.SearchByIDParam.partition_tag_array) - return partition_tag_array_.Get(index); -} -inline std::string* SearchByIDParam::mutable_partition_tag_array(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.SearchByIDParam.partition_tag_array) - return partition_tag_array_.Mutable(index); -} -inline void SearchByIDParam::set_partition_tag_array(int index, const std::string& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.SearchByIDParam.partition_tag_array) - partition_tag_array_.Mutable(index)->assign(value); -} -inline void SearchByIDParam::set_partition_tag_array(int index, std::string&& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.SearchByIDParam.partition_tag_array) - partition_tag_array_.Mutable(index)->assign(std::move(value)); -} -inline void SearchByIDParam::set_partition_tag_array(int index, const char* value) { - GOOGLE_DCHECK(value != nullptr); - partition_tag_array_.Mutable(index)->assign(value); - // @@protoc_insertion_point(field_set_char:milvus.grpc.SearchByIDParam.partition_tag_array) -} -inline void SearchByIDParam::set_partition_tag_array(int index, const char* value, size_t size) { - partition_tag_array_.Mutable(index)->assign( - reinterpret_cast(value), size); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.SearchByIDParam.partition_tag_array) -} -inline std::string* SearchByIDParam::add_partition_tag_array() { - // @@protoc_insertion_point(field_add_mutable:milvus.grpc.SearchByIDParam.partition_tag_array) - return partition_tag_array_.Add(); -} -inline void SearchByIDParam::add_partition_tag_array(const std::string& value) { - partition_tag_array_.Add()->assign(value); - // @@protoc_insertion_point(field_add:milvus.grpc.SearchByIDParam.partition_tag_array) -} -inline void SearchByIDParam::add_partition_tag_array(std::string&& value) { - partition_tag_array_.Add(std::move(value)); - // @@protoc_insertion_point(field_add:milvus.grpc.SearchByIDParam.partition_tag_array) -} -inline void SearchByIDParam::add_partition_tag_array(const char* value) { - GOOGLE_DCHECK(value != nullptr); - partition_tag_array_.Add()->assign(value); - // @@protoc_insertion_point(field_add_char:milvus.grpc.SearchByIDParam.partition_tag_array) -} -inline void SearchByIDParam::add_partition_tag_array(const char* value, size_t size) { - partition_tag_array_.Add()->assign(reinterpret_cast(value), size); - // @@protoc_insertion_point(field_add_pointer:milvus.grpc.SearchByIDParam.partition_tag_array) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& -SearchByIDParam::partition_tag_array() const { - // @@protoc_insertion_point(field_list:milvus.grpc.SearchByIDParam.partition_tag_array) - return partition_tag_array_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* -SearchByIDParam::mutable_partition_tag_array() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.SearchByIDParam.partition_tag_array) - return &partition_tag_array_; -} - -// repeated int64 id_array = 3; -inline int SearchByIDParam::id_array_size() const { - return id_array_.size(); -} -inline void SearchByIDParam::clear_id_array() { - id_array_.Clear(); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 SearchByIDParam::id_array(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.SearchByIDParam.id_array) - return id_array_.Get(index); -} -inline void SearchByIDParam::set_id_array(int index, ::PROTOBUF_NAMESPACE_ID::int64 value) { - id_array_.Set(index, value); - // @@protoc_insertion_point(field_set:milvus.grpc.SearchByIDParam.id_array) -} -inline void SearchByIDParam::add_id_array(::PROTOBUF_NAMESPACE_ID::int64 value) { - id_array_.Add(value); - // @@protoc_insertion_point(field_add:milvus.grpc.SearchByIDParam.id_array) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >& -SearchByIDParam::id_array() const { - // @@protoc_insertion_point(field_list:milvus.grpc.SearchByIDParam.id_array) - return id_array_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >* -SearchByIDParam::mutable_id_array() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.SearchByIDParam.id_array) - return &id_array_; -} - -// int64 topk = 4; -inline void SearchByIDParam::clear_topk() { - topk_ = PROTOBUF_LONGLONG(0); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 SearchByIDParam::topk() const { - // @@protoc_insertion_point(field_get:milvus.grpc.SearchByIDParam.topk) - return topk_; -} -inline void SearchByIDParam::set_topk(::PROTOBUF_NAMESPACE_ID::int64 value) { - - topk_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.SearchByIDParam.topk) -} - -// repeated .milvus.grpc.KeyValuePair extra_params = 5; -inline int SearchByIDParam::extra_params_size() const { - return extra_params_.size(); -} -inline void SearchByIDParam::clear_extra_params() { - extra_params_.Clear(); -} -inline ::milvus::grpc::KeyValuePair* SearchByIDParam::mutable_extra_params(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.SearchByIDParam.extra_params) - return extra_params_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* -SearchByIDParam::mutable_extra_params() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.SearchByIDParam.extra_params) - return &extra_params_; -} -inline const ::milvus::grpc::KeyValuePair& SearchByIDParam::extra_params(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.SearchByIDParam.extra_params) - return extra_params_.Get(index); -} -inline ::milvus::grpc::KeyValuePair* SearchByIDParam::add_extra_params() { - // @@protoc_insertion_point(field_add:milvus.grpc.SearchByIDParam.extra_params) - return extra_params_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& -SearchByIDParam::extra_params() const { - // @@protoc_insertion_point(field_list:milvus.grpc.SearchByIDParam.extra_params) - return extra_params_; -} - -// ------------------------------------------------------------------- - -// ReLoadSegmentsParam - -// string collection_name = 1; -inline void ReLoadSegmentsParam::clear_collection_name() { - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& ReLoadSegmentsParam::collection_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.ReLoadSegmentsParam.collection_name) - return collection_name_.GetNoArena(); -} -inline void ReLoadSegmentsParam::set_collection_name(const std::string& value) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.ReLoadSegmentsParam.collection_name) -} -inline void ReLoadSegmentsParam::set_collection_name(std::string&& value) { - - collection_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.ReLoadSegmentsParam.collection_name) -} -inline void ReLoadSegmentsParam::set_collection_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.ReLoadSegmentsParam.collection_name) -} -inline void ReLoadSegmentsParam::set_collection_name(const char* value, size_t size) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.ReLoadSegmentsParam.collection_name) -} -inline std::string* ReLoadSegmentsParam::mutable_collection_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.ReLoadSegmentsParam.collection_name) - return collection_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* ReLoadSegmentsParam::release_collection_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.ReLoadSegmentsParam.collection_name) - - return collection_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void ReLoadSegmentsParam::set_allocated_collection_name(std::string* collection_name) { - if (collection_name != nullptr) { - - } else { - - } - collection_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), collection_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.ReLoadSegmentsParam.collection_name) -} - -// repeated string segment_id_array = 2; -inline int ReLoadSegmentsParam::segment_id_array_size() const { - return segment_id_array_.size(); -} -inline void ReLoadSegmentsParam::clear_segment_id_array() { - segment_id_array_.Clear(); -} -inline const std::string& ReLoadSegmentsParam::segment_id_array(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.ReLoadSegmentsParam.segment_id_array) - return segment_id_array_.Get(index); -} -inline std::string* ReLoadSegmentsParam::mutable_segment_id_array(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.ReLoadSegmentsParam.segment_id_array) - return segment_id_array_.Mutable(index); -} -inline void ReLoadSegmentsParam::set_segment_id_array(int index, const std::string& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.ReLoadSegmentsParam.segment_id_array) - segment_id_array_.Mutable(index)->assign(value); -} -inline void ReLoadSegmentsParam::set_segment_id_array(int index, std::string&& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.ReLoadSegmentsParam.segment_id_array) - segment_id_array_.Mutable(index)->assign(std::move(value)); -} -inline void ReLoadSegmentsParam::set_segment_id_array(int index, const char* value) { - GOOGLE_DCHECK(value != nullptr); - segment_id_array_.Mutable(index)->assign(value); - // @@protoc_insertion_point(field_set_char:milvus.grpc.ReLoadSegmentsParam.segment_id_array) -} -inline void ReLoadSegmentsParam::set_segment_id_array(int index, const char* value, size_t size) { - segment_id_array_.Mutable(index)->assign( - reinterpret_cast(value), size); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.ReLoadSegmentsParam.segment_id_array) -} -inline std::string* ReLoadSegmentsParam::add_segment_id_array() { - // @@protoc_insertion_point(field_add_mutable:milvus.grpc.ReLoadSegmentsParam.segment_id_array) - return segment_id_array_.Add(); -} -inline void ReLoadSegmentsParam::add_segment_id_array(const std::string& value) { - segment_id_array_.Add()->assign(value); - // @@protoc_insertion_point(field_add:milvus.grpc.ReLoadSegmentsParam.segment_id_array) -} -inline void ReLoadSegmentsParam::add_segment_id_array(std::string&& value) { - segment_id_array_.Add(std::move(value)); - // @@protoc_insertion_point(field_add:milvus.grpc.ReLoadSegmentsParam.segment_id_array) -} -inline void ReLoadSegmentsParam::add_segment_id_array(const char* value) { - GOOGLE_DCHECK(value != nullptr); - segment_id_array_.Add()->assign(value); - // @@protoc_insertion_point(field_add_char:milvus.grpc.ReLoadSegmentsParam.segment_id_array) -} -inline void ReLoadSegmentsParam::add_segment_id_array(const char* value, size_t size) { - segment_id_array_.Add()->assign(reinterpret_cast(value), size); - // @@protoc_insertion_point(field_add_pointer:milvus.grpc.ReLoadSegmentsParam.segment_id_array) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& -ReLoadSegmentsParam::segment_id_array() const { - // @@protoc_insertion_point(field_list:milvus.grpc.ReLoadSegmentsParam.segment_id_array) - return segment_id_array_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* -ReLoadSegmentsParam::mutable_segment_id_array() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.ReLoadSegmentsParam.segment_id_array) - return &segment_id_array_; -} - -// ------------------------------------------------------------------- - -// TopKQueryResult - -// .milvus.grpc.Status status = 1; -inline bool TopKQueryResult::has_status() const { - return this != internal_default_instance() && status_ != nullptr; -} -inline const ::milvus::grpc::Status& TopKQueryResult::status() const { - const ::milvus::grpc::Status* p = status_; - // @@protoc_insertion_point(field_get:milvus.grpc.TopKQueryResult.status) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_Status_default_instance_); -} -inline ::milvus::grpc::Status* TopKQueryResult::release_status() { - // @@protoc_insertion_point(field_release:milvus.grpc.TopKQueryResult.status) - - ::milvus::grpc::Status* temp = status_; - status_ = nullptr; - return temp; -} -inline ::milvus::grpc::Status* TopKQueryResult::mutable_status() { - - if (status_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::Status>(GetArenaNoVirtual()); - status_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.TopKQueryResult.status) - return status_; -} -inline void TopKQueryResult::set_allocated_status(::milvus::grpc::Status* status) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(status_); - } - if (status) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - status = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, status, submessage_arena); - } - - } else { - - } - status_ = status; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.TopKQueryResult.status) -} - -// int64 row_num = 2; -inline void TopKQueryResult::clear_row_num() { - row_num_ = PROTOBUF_LONGLONG(0); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 TopKQueryResult::row_num() const { - // @@protoc_insertion_point(field_get:milvus.grpc.TopKQueryResult.row_num) - return row_num_; -} -inline void TopKQueryResult::set_row_num(::PROTOBUF_NAMESPACE_ID::int64 value) { - - row_num_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.TopKQueryResult.row_num) -} - -// repeated int64 ids = 3; -inline int TopKQueryResult::ids_size() const { - return ids_.size(); -} -inline void TopKQueryResult::clear_ids() { - ids_.Clear(); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 TopKQueryResult::ids(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.TopKQueryResult.ids) - return ids_.Get(index); -} -inline void TopKQueryResult::set_ids(int index, ::PROTOBUF_NAMESPACE_ID::int64 value) { - ids_.Set(index, value); - // @@protoc_insertion_point(field_set:milvus.grpc.TopKQueryResult.ids) -} -inline void TopKQueryResult::add_ids(::PROTOBUF_NAMESPACE_ID::int64 value) { - ids_.Add(value); - // @@protoc_insertion_point(field_add:milvus.grpc.TopKQueryResult.ids) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >& -TopKQueryResult::ids() const { - // @@protoc_insertion_point(field_list:milvus.grpc.TopKQueryResult.ids) - return ids_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >* -TopKQueryResult::mutable_ids() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.TopKQueryResult.ids) - return &ids_; -} - -// repeated float distances = 4; -inline int TopKQueryResult::distances_size() const { - return distances_.size(); -} -inline void TopKQueryResult::clear_distances() { - distances_.Clear(); -} -inline float TopKQueryResult::distances(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.TopKQueryResult.distances) - return distances_.Get(index); -} -inline void TopKQueryResult::set_distances(int index, float value) { - distances_.Set(index, value); - // @@protoc_insertion_point(field_set:milvus.grpc.TopKQueryResult.distances) -} -inline void TopKQueryResult::add_distances(float value) { - distances_.Add(value); - // @@protoc_insertion_point(field_add:milvus.grpc.TopKQueryResult.distances) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >& -TopKQueryResult::distances() const { - // @@protoc_insertion_point(field_list:milvus.grpc.TopKQueryResult.distances) - return distances_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >* -TopKQueryResult::mutable_distances() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.TopKQueryResult.distances) - return &distances_; -} - -// ------------------------------------------------------------------- - -// StringReply - -// .milvus.grpc.Status status = 1; -inline bool StringReply::has_status() const { - return this != internal_default_instance() && status_ != nullptr; -} -inline const ::milvus::grpc::Status& StringReply::status() const { - const ::milvus::grpc::Status* p = status_; - // @@protoc_insertion_point(field_get:milvus.grpc.StringReply.status) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_Status_default_instance_); -} -inline ::milvus::grpc::Status* StringReply::release_status() { - // @@protoc_insertion_point(field_release:milvus.grpc.StringReply.status) - - ::milvus::grpc::Status* temp = status_; - status_ = nullptr; - return temp; -} -inline ::milvus::grpc::Status* StringReply::mutable_status() { - - if (status_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::Status>(GetArenaNoVirtual()); - status_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.StringReply.status) - return status_; -} -inline void StringReply::set_allocated_status(::milvus::grpc::Status* status) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(status_); - } - if (status) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - status = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, status, submessage_arena); - } - - } else { - - } - status_ = status; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.StringReply.status) -} - -// string string_reply = 2; -inline void StringReply::clear_string_reply() { - string_reply_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& StringReply::string_reply() const { - // @@protoc_insertion_point(field_get:milvus.grpc.StringReply.string_reply) - return string_reply_.GetNoArena(); -} -inline void StringReply::set_string_reply(const std::string& value) { - - string_reply_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.StringReply.string_reply) -} -inline void StringReply::set_string_reply(std::string&& value) { - - string_reply_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.StringReply.string_reply) -} -inline void StringReply::set_string_reply(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - string_reply_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.StringReply.string_reply) -} -inline void StringReply::set_string_reply(const char* value, size_t size) { - - string_reply_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.StringReply.string_reply) -} -inline std::string* StringReply::mutable_string_reply() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.StringReply.string_reply) - return string_reply_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* StringReply::release_string_reply() { - // @@protoc_insertion_point(field_release:milvus.grpc.StringReply.string_reply) - - return string_reply_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void StringReply::set_allocated_string_reply(std::string* string_reply) { - if (string_reply != nullptr) { - - } else { - - } - string_reply_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), string_reply); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.StringReply.string_reply) -} - -// ------------------------------------------------------------------- - -// BoolReply - -// .milvus.grpc.Status status = 1; -inline bool BoolReply::has_status() const { - return this != internal_default_instance() && status_ != nullptr; -} -inline const ::milvus::grpc::Status& BoolReply::status() const { - const ::milvus::grpc::Status* p = status_; - // @@protoc_insertion_point(field_get:milvus.grpc.BoolReply.status) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_Status_default_instance_); -} -inline ::milvus::grpc::Status* BoolReply::release_status() { - // @@protoc_insertion_point(field_release:milvus.grpc.BoolReply.status) - - ::milvus::grpc::Status* temp = status_; - status_ = nullptr; - return temp; -} -inline ::milvus::grpc::Status* BoolReply::mutable_status() { - - if (status_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::Status>(GetArenaNoVirtual()); - status_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.BoolReply.status) - return status_; -} -inline void BoolReply::set_allocated_status(::milvus::grpc::Status* status) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(status_); - } - if (status) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - status = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, status, submessage_arena); - } - - } else { - - } - status_ = status; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.BoolReply.status) -} - -// bool bool_reply = 2; -inline void BoolReply::clear_bool_reply() { - bool_reply_ = false; -} -inline bool BoolReply::bool_reply() const { - // @@protoc_insertion_point(field_get:milvus.grpc.BoolReply.bool_reply) - return bool_reply_; -} -inline void BoolReply::set_bool_reply(bool value) { - - bool_reply_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.BoolReply.bool_reply) -} - -// ------------------------------------------------------------------- - -// CollectionRowCount - -// .milvus.grpc.Status status = 1; -inline bool CollectionRowCount::has_status() const { - return this != internal_default_instance() && status_ != nullptr; -} -inline const ::milvus::grpc::Status& CollectionRowCount::status() const { - const ::milvus::grpc::Status* p = status_; - // @@protoc_insertion_point(field_get:milvus.grpc.CollectionRowCount.status) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_Status_default_instance_); -} -inline ::milvus::grpc::Status* CollectionRowCount::release_status() { - // @@protoc_insertion_point(field_release:milvus.grpc.CollectionRowCount.status) - - ::milvus::grpc::Status* temp = status_; - status_ = nullptr; - return temp; -} -inline ::milvus::grpc::Status* CollectionRowCount::mutable_status() { - - if (status_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::Status>(GetArenaNoVirtual()); - status_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.CollectionRowCount.status) - return status_; -} -inline void CollectionRowCount::set_allocated_status(::milvus::grpc::Status* status) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(status_); - } - if (status) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - status = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, status, submessage_arena); - } - - } else { - - } - status_ = status; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.CollectionRowCount.status) -} - -// int64 collection_row_count = 2; -inline void CollectionRowCount::clear_collection_row_count() { - collection_row_count_ = PROTOBUF_LONGLONG(0); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 CollectionRowCount::collection_row_count() const { - // @@protoc_insertion_point(field_get:milvus.grpc.CollectionRowCount.collection_row_count) - return collection_row_count_; -} -inline void CollectionRowCount::set_collection_row_count(::PROTOBUF_NAMESPACE_ID::int64 value) { - - collection_row_count_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.CollectionRowCount.collection_row_count) -} - -// ------------------------------------------------------------------- - -// Command - -// string cmd = 1; -inline void Command::clear_cmd() { - cmd_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& Command::cmd() const { - // @@protoc_insertion_point(field_get:milvus.grpc.Command.cmd) - return cmd_.GetNoArena(); -} -inline void Command::set_cmd(const std::string& value) { - - cmd_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.Command.cmd) -} -inline void Command::set_cmd(std::string&& value) { - - cmd_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.Command.cmd) -} -inline void Command::set_cmd(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - cmd_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.Command.cmd) -} -inline void Command::set_cmd(const char* value, size_t size) { - - cmd_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.Command.cmd) -} -inline std::string* Command::mutable_cmd() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.Command.cmd) - return cmd_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* Command::release_cmd() { - // @@protoc_insertion_point(field_release:milvus.grpc.Command.cmd) - - return cmd_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void Command::set_allocated_cmd(std::string* cmd) { - if (cmd != nullptr) { - - } else { - - } - cmd_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), cmd); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.Command.cmd) -} - -// ------------------------------------------------------------------- - -// IndexParam - -// .milvus.grpc.Status status = 1; -inline bool IndexParam::has_status() const { - return this != internal_default_instance() && status_ != nullptr; -} -inline const ::milvus::grpc::Status& IndexParam::status() const { - const ::milvus::grpc::Status* p = status_; - // @@protoc_insertion_point(field_get:milvus.grpc.IndexParam.status) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_Status_default_instance_); -} -inline ::milvus::grpc::Status* IndexParam::release_status() { - // @@protoc_insertion_point(field_release:milvus.grpc.IndexParam.status) - - ::milvus::grpc::Status* temp = status_; - status_ = nullptr; - return temp; -} -inline ::milvus::grpc::Status* IndexParam::mutable_status() { - - if (status_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::Status>(GetArenaNoVirtual()); - status_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.IndexParam.status) - return status_; -} -inline void IndexParam::set_allocated_status(::milvus::grpc::Status* status) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(status_); - } - if (status) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - status = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, status, submessage_arena); - } - - } else { - - } - status_ = status; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.IndexParam.status) -} - -// string collection_name = 2; -inline void IndexParam::clear_collection_name() { - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& IndexParam::collection_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.IndexParam.collection_name) - return collection_name_.GetNoArena(); -} -inline void IndexParam::set_collection_name(const std::string& value) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.IndexParam.collection_name) -} -inline void IndexParam::set_collection_name(std::string&& value) { - - collection_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.IndexParam.collection_name) -} -inline void IndexParam::set_collection_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.IndexParam.collection_name) -} -inline void IndexParam::set_collection_name(const char* value, size_t size) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.IndexParam.collection_name) -} -inline std::string* IndexParam::mutable_collection_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.IndexParam.collection_name) - return collection_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* IndexParam::release_collection_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.IndexParam.collection_name) - - return collection_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void IndexParam::set_allocated_collection_name(std::string* collection_name) { - if (collection_name != nullptr) { - - } else { - - } - collection_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), collection_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.IndexParam.collection_name) -} - -// int32 index_type = 3; -inline void IndexParam::clear_index_type() { - index_type_ = 0; -} -inline ::PROTOBUF_NAMESPACE_ID::int32 IndexParam::index_type() const { - // @@protoc_insertion_point(field_get:milvus.grpc.IndexParam.index_type) - return index_type_; -} -inline void IndexParam::set_index_type(::PROTOBUF_NAMESPACE_ID::int32 value) { - - index_type_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.IndexParam.index_type) -} - -// repeated .milvus.grpc.KeyValuePair extra_params = 4; -inline int IndexParam::extra_params_size() const { - return extra_params_.size(); -} -inline void IndexParam::clear_extra_params() { - extra_params_.Clear(); -} -inline ::milvus::grpc::KeyValuePair* IndexParam::mutable_extra_params(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.IndexParam.extra_params) - return extra_params_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* -IndexParam::mutable_extra_params() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.IndexParam.extra_params) - return &extra_params_; -} -inline const ::milvus::grpc::KeyValuePair& IndexParam::extra_params(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.IndexParam.extra_params) - return extra_params_.Get(index); -} -inline ::milvus::grpc::KeyValuePair* IndexParam::add_extra_params() { - // @@protoc_insertion_point(field_add:milvus.grpc.IndexParam.extra_params) - return extra_params_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& -IndexParam::extra_params() const { - // @@protoc_insertion_point(field_list:milvus.grpc.IndexParam.extra_params) - return extra_params_; -} - -// ------------------------------------------------------------------- - -// FlushParam - -// repeated string collection_name_array = 1; -inline int FlushParam::collection_name_array_size() const { - return collection_name_array_.size(); -} -inline void FlushParam::clear_collection_name_array() { - collection_name_array_.Clear(); -} -inline const std::string& FlushParam::collection_name_array(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.FlushParam.collection_name_array) - return collection_name_array_.Get(index); -} -inline std::string* FlushParam::mutable_collection_name_array(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.FlushParam.collection_name_array) - return collection_name_array_.Mutable(index); -} -inline void FlushParam::set_collection_name_array(int index, const std::string& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.FlushParam.collection_name_array) - collection_name_array_.Mutable(index)->assign(value); -} -inline void FlushParam::set_collection_name_array(int index, std::string&& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.FlushParam.collection_name_array) - collection_name_array_.Mutable(index)->assign(std::move(value)); -} -inline void FlushParam::set_collection_name_array(int index, const char* value) { - GOOGLE_DCHECK(value != nullptr); - collection_name_array_.Mutable(index)->assign(value); - // @@protoc_insertion_point(field_set_char:milvus.grpc.FlushParam.collection_name_array) -} -inline void FlushParam::set_collection_name_array(int index, const char* value, size_t size) { - collection_name_array_.Mutable(index)->assign( - reinterpret_cast(value), size); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.FlushParam.collection_name_array) -} -inline std::string* FlushParam::add_collection_name_array() { - // @@protoc_insertion_point(field_add_mutable:milvus.grpc.FlushParam.collection_name_array) - return collection_name_array_.Add(); -} -inline void FlushParam::add_collection_name_array(const std::string& value) { - collection_name_array_.Add()->assign(value); - // @@protoc_insertion_point(field_add:milvus.grpc.FlushParam.collection_name_array) -} -inline void FlushParam::add_collection_name_array(std::string&& value) { - collection_name_array_.Add(std::move(value)); - // @@protoc_insertion_point(field_add:milvus.grpc.FlushParam.collection_name_array) -} -inline void FlushParam::add_collection_name_array(const char* value) { - GOOGLE_DCHECK(value != nullptr); - collection_name_array_.Add()->assign(value); - // @@protoc_insertion_point(field_add_char:milvus.grpc.FlushParam.collection_name_array) -} -inline void FlushParam::add_collection_name_array(const char* value, size_t size) { - collection_name_array_.Add()->assign(reinterpret_cast(value), size); - // @@protoc_insertion_point(field_add_pointer:milvus.grpc.FlushParam.collection_name_array) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& -FlushParam::collection_name_array() const { - // @@protoc_insertion_point(field_list:milvus.grpc.FlushParam.collection_name_array) - return collection_name_array_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* -FlushParam::mutable_collection_name_array() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.FlushParam.collection_name_array) - return &collection_name_array_; -} - -// ------------------------------------------------------------------- - -// DeleteByIDParam - -// string collection_name = 1; -inline void DeleteByIDParam::clear_collection_name() { - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& DeleteByIDParam::collection_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.DeleteByIDParam.collection_name) - return collection_name_.GetNoArena(); -} -inline void DeleteByIDParam::set_collection_name(const std::string& value) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.DeleteByIDParam.collection_name) -} -inline void DeleteByIDParam::set_collection_name(std::string&& value) { - - collection_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.DeleteByIDParam.collection_name) -} -inline void DeleteByIDParam::set_collection_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.DeleteByIDParam.collection_name) -} -inline void DeleteByIDParam::set_collection_name(const char* value, size_t size) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.DeleteByIDParam.collection_name) -} -inline std::string* DeleteByIDParam::mutable_collection_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.DeleteByIDParam.collection_name) - return collection_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* DeleteByIDParam::release_collection_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.DeleteByIDParam.collection_name) - - return collection_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void DeleteByIDParam::set_allocated_collection_name(std::string* collection_name) { - if (collection_name != nullptr) { - - } else { - - } - collection_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), collection_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.DeleteByIDParam.collection_name) -} - -// repeated int64 id_array = 2; -inline int DeleteByIDParam::id_array_size() const { - return id_array_.size(); -} -inline void DeleteByIDParam::clear_id_array() { - id_array_.Clear(); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 DeleteByIDParam::id_array(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.DeleteByIDParam.id_array) - return id_array_.Get(index); -} -inline void DeleteByIDParam::set_id_array(int index, ::PROTOBUF_NAMESPACE_ID::int64 value) { - id_array_.Set(index, value); - // @@protoc_insertion_point(field_set:milvus.grpc.DeleteByIDParam.id_array) -} -inline void DeleteByIDParam::add_id_array(::PROTOBUF_NAMESPACE_ID::int64 value) { - id_array_.Add(value); - // @@protoc_insertion_point(field_add:milvus.grpc.DeleteByIDParam.id_array) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >& -DeleteByIDParam::id_array() const { - // @@protoc_insertion_point(field_list:milvus.grpc.DeleteByIDParam.id_array) - return id_array_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >* -DeleteByIDParam::mutable_id_array() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.DeleteByIDParam.id_array) - return &id_array_; -} - -// ------------------------------------------------------------------- - -// CollectionInfo - -// .milvus.grpc.Status status = 1; -inline bool CollectionInfo::has_status() const { - return this != internal_default_instance() && status_ != nullptr; -} -inline const ::milvus::grpc::Status& CollectionInfo::status() const { - const ::milvus::grpc::Status* p = status_; - // @@protoc_insertion_point(field_get:milvus.grpc.CollectionInfo.status) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_Status_default_instance_); -} -inline ::milvus::grpc::Status* CollectionInfo::release_status() { - // @@protoc_insertion_point(field_release:milvus.grpc.CollectionInfo.status) - - ::milvus::grpc::Status* temp = status_; - status_ = nullptr; - return temp; -} -inline ::milvus::grpc::Status* CollectionInfo::mutable_status() { - - if (status_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::Status>(GetArenaNoVirtual()); - status_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.CollectionInfo.status) - return status_; -} -inline void CollectionInfo::set_allocated_status(::milvus::grpc::Status* status) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(status_); - } - if (status) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - status = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, status, submessage_arena); - } - - } else { - - } - status_ = status; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.CollectionInfo.status) -} - -// string json_info = 2; -inline void CollectionInfo::clear_json_info() { - json_info_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& CollectionInfo::json_info() const { - // @@protoc_insertion_point(field_get:milvus.grpc.CollectionInfo.json_info) - return json_info_.GetNoArena(); -} -inline void CollectionInfo::set_json_info(const std::string& value) { - - json_info_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.CollectionInfo.json_info) -} -inline void CollectionInfo::set_json_info(std::string&& value) { - - json_info_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.CollectionInfo.json_info) -} -inline void CollectionInfo::set_json_info(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - json_info_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.CollectionInfo.json_info) -} -inline void CollectionInfo::set_json_info(const char* value, size_t size) { - - json_info_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.CollectionInfo.json_info) -} -inline std::string* CollectionInfo::mutable_json_info() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.CollectionInfo.json_info) - return json_info_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* CollectionInfo::release_json_info() { - // @@protoc_insertion_point(field_release:milvus.grpc.CollectionInfo.json_info) - - return json_info_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void CollectionInfo::set_allocated_json_info(std::string* json_info) { - if (json_info != nullptr) { - - } else { - - } - json_info_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), json_info); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.CollectionInfo.json_info) -} - -// ------------------------------------------------------------------- - -// VectorsIdentity - -// string collection_name = 1; -inline void VectorsIdentity::clear_collection_name() { - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& VectorsIdentity::collection_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.VectorsIdentity.collection_name) - return collection_name_.GetNoArena(); -} -inline void VectorsIdentity::set_collection_name(const std::string& value) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.VectorsIdentity.collection_name) -} -inline void VectorsIdentity::set_collection_name(std::string&& value) { - - collection_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.VectorsIdentity.collection_name) -} -inline void VectorsIdentity::set_collection_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.VectorsIdentity.collection_name) -} -inline void VectorsIdentity::set_collection_name(const char* value, size_t size) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.VectorsIdentity.collection_name) -} -inline std::string* VectorsIdentity::mutable_collection_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.VectorsIdentity.collection_name) - return collection_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* VectorsIdentity::release_collection_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.VectorsIdentity.collection_name) - - return collection_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void VectorsIdentity::set_allocated_collection_name(std::string* collection_name) { - if (collection_name != nullptr) { - - } else { - - } - collection_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), collection_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.VectorsIdentity.collection_name) -} - -// repeated int64 id_array = 2; -inline int VectorsIdentity::id_array_size() const { - return id_array_.size(); -} -inline void VectorsIdentity::clear_id_array() { - id_array_.Clear(); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 VectorsIdentity::id_array(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.VectorsIdentity.id_array) - return id_array_.Get(index); -} -inline void VectorsIdentity::set_id_array(int index, ::PROTOBUF_NAMESPACE_ID::int64 value) { - id_array_.Set(index, value); - // @@protoc_insertion_point(field_set:milvus.grpc.VectorsIdentity.id_array) -} -inline void VectorsIdentity::add_id_array(::PROTOBUF_NAMESPACE_ID::int64 value) { - id_array_.Add(value); - // @@protoc_insertion_point(field_add:milvus.grpc.VectorsIdentity.id_array) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >& -VectorsIdentity::id_array() const { - // @@protoc_insertion_point(field_list:milvus.grpc.VectorsIdentity.id_array) - return id_array_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >* -VectorsIdentity::mutable_id_array() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.VectorsIdentity.id_array) - return &id_array_; -} - -// ------------------------------------------------------------------- - -// VectorsData - -// .milvus.grpc.Status status = 1; -inline bool VectorsData::has_status() const { - return this != internal_default_instance() && status_ != nullptr; -} -inline const ::milvus::grpc::Status& VectorsData::status() const { - const ::milvus::grpc::Status* p = status_; - // @@protoc_insertion_point(field_get:milvus.grpc.VectorsData.status) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_Status_default_instance_); -} -inline ::milvus::grpc::Status* VectorsData::release_status() { - // @@protoc_insertion_point(field_release:milvus.grpc.VectorsData.status) - - ::milvus::grpc::Status* temp = status_; - status_ = nullptr; - return temp; -} -inline ::milvus::grpc::Status* VectorsData::mutable_status() { - - if (status_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::Status>(GetArenaNoVirtual()); - status_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.VectorsData.status) - return status_; -} -inline void VectorsData::set_allocated_status(::milvus::grpc::Status* status) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(status_); - } - if (status) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - status = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, status, submessage_arena); - } - - } else { - - } - status_ = status; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.VectorsData.status) -} - -// repeated .milvus.grpc.RowRecord vectors_data = 2; -inline int VectorsData::vectors_data_size() const { - return vectors_data_.size(); -} -inline void VectorsData::clear_vectors_data() { - vectors_data_.Clear(); -} -inline ::milvus::grpc::RowRecord* VectorsData::mutable_vectors_data(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.VectorsData.vectors_data) - return vectors_data_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >* -VectorsData::mutable_vectors_data() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.VectorsData.vectors_data) - return &vectors_data_; -} -inline const ::milvus::grpc::RowRecord& VectorsData::vectors_data(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.VectorsData.vectors_data) - return vectors_data_.Get(index); -} -inline ::milvus::grpc::RowRecord* VectorsData::add_vectors_data() { - // @@protoc_insertion_point(field_add:milvus.grpc.VectorsData.vectors_data) - return vectors_data_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >& -VectorsData::vectors_data() const { - // @@protoc_insertion_point(field_list:milvus.grpc.VectorsData.vectors_data) - return vectors_data_; -} - -// ------------------------------------------------------------------- - -// GetVectorIDsParam - -// string collection_name = 1; -inline void GetVectorIDsParam::clear_collection_name() { - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& GetVectorIDsParam::collection_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.GetVectorIDsParam.collection_name) - return collection_name_.GetNoArena(); -} -inline void GetVectorIDsParam::set_collection_name(const std::string& value) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.GetVectorIDsParam.collection_name) -} -inline void GetVectorIDsParam::set_collection_name(std::string&& value) { - - collection_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.GetVectorIDsParam.collection_name) -} -inline void GetVectorIDsParam::set_collection_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.GetVectorIDsParam.collection_name) -} -inline void GetVectorIDsParam::set_collection_name(const char* value, size_t size) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.GetVectorIDsParam.collection_name) -} -inline std::string* GetVectorIDsParam::mutable_collection_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.GetVectorIDsParam.collection_name) - return collection_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* GetVectorIDsParam::release_collection_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.GetVectorIDsParam.collection_name) - - return collection_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void GetVectorIDsParam::set_allocated_collection_name(std::string* collection_name) { - if (collection_name != nullptr) { - - } else { - - } - collection_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), collection_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.GetVectorIDsParam.collection_name) -} - -// string segment_name = 2; -inline void GetVectorIDsParam::clear_segment_name() { - segment_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& GetVectorIDsParam::segment_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.GetVectorIDsParam.segment_name) - return segment_name_.GetNoArena(); -} -inline void GetVectorIDsParam::set_segment_name(const std::string& value) { - - segment_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.GetVectorIDsParam.segment_name) -} -inline void GetVectorIDsParam::set_segment_name(std::string&& value) { - - segment_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.GetVectorIDsParam.segment_name) -} -inline void GetVectorIDsParam::set_segment_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - segment_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.GetVectorIDsParam.segment_name) -} -inline void GetVectorIDsParam::set_segment_name(const char* value, size_t size) { - - segment_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.GetVectorIDsParam.segment_name) -} -inline std::string* GetVectorIDsParam::mutable_segment_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.GetVectorIDsParam.segment_name) - return segment_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* GetVectorIDsParam::release_segment_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.GetVectorIDsParam.segment_name) - - return segment_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void GetVectorIDsParam::set_allocated_segment_name(std::string* segment_name) { - if (segment_name != nullptr) { - - } else { - - } - segment_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), segment_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.GetVectorIDsParam.segment_name) -} - -// ------------------------------------------------------------------- - -// VectorFieldParam - -// int64 dimension = 1; -inline void VectorFieldParam::clear_dimension() { - dimension_ = PROTOBUF_LONGLONG(0); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 VectorFieldParam::dimension() const { - // @@protoc_insertion_point(field_get:milvus.grpc.VectorFieldParam.dimension) - return dimension_; -} -inline void VectorFieldParam::set_dimension(::PROTOBUF_NAMESPACE_ID::int64 value) { - - dimension_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.VectorFieldParam.dimension) -} - -// ------------------------------------------------------------------- - -// FieldType - -// .milvus.grpc.DataType data_type = 1; -inline bool FieldType::has_data_type() const { - return value_case() == kDataType; -} -inline void FieldType::set_has_data_type() { - _oneof_case_[0] = kDataType; -} -inline void FieldType::clear_data_type() { - if (has_data_type()) { - value_.data_type_ = 0; - clear_has_value(); - } -} -inline ::milvus::grpc::DataType FieldType::data_type() const { - // @@protoc_insertion_point(field_get:milvus.grpc.FieldType.data_type) - if (has_data_type()) { - return static_cast< ::milvus::grpc::DataType >(value_.data_type_); - } - return static_cast< ::milvus::grpc::DataType >(0); -} -inline void FieldType::set_data_type(::milvus::grpc::DataType value) { - if (!has_data_type()) { - clear_value(); - set_has_data_type(); - } - value_.data_type_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.FieldType.data_type) -} - -// .milvus.grpc.VectorFieldParam vector_param = 2; -inline bool FieldType::has_vector_param() const { - return value_case() == kVectorParam; -} -inline void FieldType::set_has_vector_param() { - _oneof_case_[0] = kVectorParam; -} -inline void FieldType::clear_vector_param() { - if (has_vector_param()) { - delete value_.vector_param_; - clear_has_value(); - } -} -inline ::milvus::grpc::VectorFieldParam* FieldType::release_vector_param() { - // @@protoc_insertion_point(field_release:milvus.grpc.FieldType.vector_param) - if (has_vector_param()) { - clear_has_value(); - ::milvus::grpc::VectorFieldParam* temp = value_.vector_param_; - value_.vector_param_ = nullptr; - return temp; - } else { - return nullptr; - } -} -inline const ::milvus::grpc::VectorFieldParam& FieldType::vector_param() const { - // @@protoc_insertion_point(field_get:milvus.grpc.FieldType.vector_param) - return has_vector_param() - ? *value_.vector_param_ - : *reinterpret_cast< ::milvus::grpc::VectorFieldParam*>(&::milvus::grpc::_VectorFieldParam_default_instance_); -} -inline ::milvus::grpc::VectorFieldParam* FieldType::mutable_vector_param() { - if (!has_vector_param()) { - clear_value(); - set_has_vector_param(); - value_.vector_param_ = CreateMaybeMessage< ::milvus::grpc::VectorFieldParam >( - GetArenaNoVirtual()); - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.FieldType.vector_param) - return value_.vector_param_; -} - -inline bool FieldType::has_value() const { - return value_case() != VALUE_NOT_SET; -} -inline void FieldType::clear_has_value() { - _oneof_case_[0] = VALUE_NOT_SET; -} -inline FieldType::ValueCase FieldType::value_case() const { - return FieldType::ValueCase(_oneof_case_[0]); -} -// ------------------------------------------------------------------- - -// FieldParam - -// uint64 id = 1; -inline void FieldParam::clear_id() { - id_ = PROTOBUF_ULONGLONG(0); -} -inline ::PROTOBUF_NAMESPACE_ID::uint64 FieldParam::id() const { - // @@protoc_insertion_point(field_get:milvus.grpc.FieldParam.id) - return id_; -} -inline void FieldParam::set_id(::PROTOBUF_NAMESPACE_ID::uint64 value) { - - id_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.FieldParam.id) -} - -// string name = 2; -inline void FieldParam::clear_name() { - name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& FieldParam::name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.FieldParam.name) - return name_.GetNoArena(); -} -inline void FieldParam::set_name(const std::string& value) { - - name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.FieldParam.name) -} -inline void FieldParam::set_name(std::string&& value) { - - name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.FieldParam.name) -} -inline void FieldParam::set_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.FieldParam.name) -} -inline void FieldParam::set_name(const char* value, size_t size) { - - name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.FieldParam.name) -} -inline std::string* FieldParam::mutable_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.FieldParam.name) - return name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* FieldParam::release_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.FieldParam.name) - - return name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void FieldParam::set_allocated_name(std::string* name) { - if (name != nullptr) { - - } else { - - } - name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.FieldParam.name) -} - -// .milvus.grpc.FieldType type = 3; -inline bool FieldParam::has_type() const { - return this != internal_default_instance() && type_ != nullptr; -} -inline void FieldParam::clear_type() { - if (GetArenaNoVirtual() == nullptr && type_ != nullptr) { - delete type_; - } - type_ = nullptr; -} -inline const ::milvus::grpc::FieldType& FieldParam::type() const { - const ::milvus::grpc::FieldType* p = type_; - // @@protoc_insertion_point(field_get:milvus.grpc.FieldParam.type) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_FieldType_default_instance_); -} -inline ::milvus::grpc::FieldType* FieldParam::release_type() { - // @@protoc_insertion_point(field_release:milvus.grpc.FieldParam.type) - - ::milvus::grpc::FieldType* temp = type_; - type_ = nullptr; - return temp; -} -inline ::milvus::grpc::FieldType* FieldParam::mutable_type() { - - if (type_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::FieldType>(GetArenaNoVirtual()); - type_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.FieldParam.type) - return type_; -} -inline void FieldParam::set_allocated_type(::milvus::grpc::FieldType* type) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete type_; - } - if (type) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - type = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, type, submessage_arena); - } - - } else { - - } - type_ = type; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.FieldParam.type) -} - -// repeated .milvus.grpc.KeyValuePair extra_params = 4; -inline int FieldParam::extra_params_size() const { - return extra_params_.size(); -} -inline void FieldParam::clear_extra_params() { - extra_params_.Clear(); -} -inline ::milvus::grpc::KeyValuePair* FieldParam::mutable_extra_params(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.FieldParam.extra_params) - return extra_params_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* -FieldParam::mutable_extra_params() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.FieldParam.extra_params) - return &extra_params_; -} -inline const ::milvus::grpc::KeyValuePair& FieldParam::extra_params(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.FieldParam.extra_params) - return extra_params_.Get(index); -} -inline ::milvus::grpc::KeyValuePair* FieldParam::add_extra_params() { - // @@protoc_insertion_point(field_add:milvus.grpc.FieldParam.extra_params) - return extra_params_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& -FieldParam::extra_params() const { - // @@protoc_insertion_point(field_list:milvus.grpc.FieldParam.extra_params) - return extra_params_; -} - -// ------------------------------------------------------------------- - -// VectorFieldValue - -// repeated .milvus.grpc.RowRecord value = 1; -inline int VectorFieldValue::value_size() const { - return value_.size(); -} -inline void VectorFieldValue::clear_value() { - value_.Clear(); -} -inline ::milvus::grpc::RowRecord* VectorFieldValue::mutable_value(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.VectorFieldValue.value) - return value_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >* -VectorFieldValue::mutable_value() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.VectorFieldValue.value) - return &value_; -} -inline const ::milvus::grpc::RowRecord& VectorFieldValue::value(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.VectorFieldValue.value) - return value_.Get(index); -} -inline ::milvus::grpc::RowRecord* VectorFieldValue::add_value() { - // @@protoc_insertion_point(field_add:milvus.grpc.VectorFieldValue.value) - return value_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >& -VectorFieldValue::value() const { - // @@protoc_insertion_point(field_list:milvus.grpc.VectorFieldValue.value) - return value_; -} - -// ------------------------------------------------------------------- - -// FieldValue - -// int32 int32_value = 1; -inline bool FieldValue::has_int32_value() const { - return value_case() == kInt32Value; -} -inline void FieldValue::set_has_int32_value() { - _oneof_case_[0] = kInt32Value; -} -inline void FieldValue::clear_int32_value() { - if (has_int32_value()) { - value_.int32_value_ = 0; - clear_has_value(); - } -} -inline ::PROTOBUF_NAMESPACE_ID::int32 FieldValue::int32_value() const { - // @@protoc_insertion_point(field_get:milvus.grpc.FieldValue.int32_value) - if (has_int32_value()) { - return value_.int32_value_; - } - return 0; -} -inline void FieldValue::set_int32_value(::PROTOBUF_NAMESPACE_ID::int32 value) { - if (!has_int32_value()) { - clear_value(); - set_has_int32_value(); - } - value_.int32_value_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.FieldValue.int32_value) -} - -// int64 int64_value = 2; -inline bool FieldValue::has_int64_value() const { - return value_case() == kInt64Value; -} -inline void FieldValue::set_has_int64_value() { - _oneof_case_[0] = kInt64Value; -} -inline void FieldValue::clear_int64_value() { - if (has_int64_value()) { - value_.int64_value_ = PROTOBUF_LONGLONG(0); - clear_has_value(); - } -} -inline ::PROTOBUF_NAMESPACE_ID::int64 FieldValue::int64_value() const { - // @@protoc_insertion_point(field_get:milvus.grpc.FieldValue.int64_value) - if (has_int64_value()) { - return value_.int64_value_; - } - return PROTOBUF_LONGLONG(0); -} -inline void FieldValue::set_int64_value(::PROTOBUF_NAMESPACE_ID::int64 value) { - if (!has_int64_value()) { - clear_value(); - set_has_int64_value(); - } - value_.int64_value_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.FieldValue.int64_value) -} - -// float float_value = 3; -inline bool FieldValue::has_float_value() const { - return value_case() == kFloatValue; -} -inline void FieldValue::set_has_float_value() { - _oneof_case_[0] = kFloatValue; -} -inline void FieldValue::clear_float_value() { - if (has_float_value()) { - value_.float_value_ = 0; - clear_has_value(); - } -} -inline float FieldValue::float_value() const { - // @@protoc_insertion_point(field_get:milvus.grpc.FieldValue.float_value) - if (has_float_value()) { - return value_.float_value_; - } - return 0; -} -inline void FieldValue::set_float_value(float value) { - if (!has_float_value()) { - clear_value(); - set_has_float_value(); - } - value_.float_value_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.FieldValue.float_value) -} - -// double double_value = 4; -inline bool FieldValue::has_double_value() const { - return value_case() == kDoubleValue; -} -inline void FieldValue::set_has_double_value() { - _oneof_case_[0] = kDoubleValue; -} -inline void FieldValue::clear_double_value() { - if (has_double_value()) { - value_.double_value_ = 0; - clear_has_value(); - } -} -inline double FieldValue::double_value() const { - // @@protoc_insertion_point(field_get:milvus.grpc.FieldValue.double_value) - if (has_double_value()) { - return value_.double_value_; - } - return 0; -} -inline void FieldValue::set_double_value(double value) { - if (!has_double_value()) { - clear_value(); - set_has_double_value(); - } - value_.double_value_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.FieldValue.double_value) -} - -// string string_value = 5; -inline bool FieldValue::has_string_value() const { - return value_case() == kStringValue; -} -inline void FieldValue::set_has_string_value() { - _oneof_case_[0] = kStringValue; -} -inline void FieldValue::clear_string_value() { - if (has_string_value()) { - value_.string_value_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - clear_has_value(); - } -} -inline const std::string& FieldValue::string_value() const { - // @@protoc_insertion_point(field_get:milvus.grpc.FieldValue.string_value) - if (has_string_value()) { - return value_.string_value_.GetNoArena(); - } - return *&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(); -} -inline void FieldValue::set_string_value(const std::string& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.FieldValue.string_value) - if (!has_string_value()) { - clear_value(); - set_has_string_value(); - value_.string_value_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - } - value_.string_value_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.FieldValue.string_value) -} -inline void FieldValue::set_string_value(std::string&& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.FieldValue.string_value) - if (!has_string_value()) { - clear_value(); - set_has_string_value(); - value_.string_value_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - } - value_.string_value_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.FieldValue.string_value) -} -inline void FieldValue::set_string_value(const char* value) { - GOOGLE_DCHECK(value != nullptr); - if (!has_string_value()) { - clear_value(); - set_has_string_value(); - value_.string_value_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - } - value_.string_value_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.FieldValue.string_value) -} -inline void FieldValue::set_string_value(const char* value, size_t size) { - if (!has_string_value()) { - clear_value(); - set_has_string_value(); - value_.string_value_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - } - value_.string_value_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string( - reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.FieldValue.string_value) -} -inline std::string* FieldValue::mutable_string_value() { - if (!has_string_value()) { - clear_value(); - set_has_string_value(); - value_.string_value_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.FieldValue.string_value) - return value_.string_value_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* FieldValue::release_string_value() { - // @@protoc_insertion_point(field_release:milvus.grpc.FieldValue.string_value) - if (has_string_value()) { - clear_has_value(); - return value_.string_value_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - } else { - return nullptr; - } -} -inline void FieldValue::set_allocated_string_value(std::string* string_value) { - if (has_value()) { - clear_value(); - } - if (string_value != nullptr) { - set_has_string_value(); - value_.string_value_.UnsafeSetDefault(string_value); - } - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.FieldValue.string_value) -} - -// bool bool_value = 6; -inline bool FieldValue::has_bool_value() const { - return value_case() == kBoolValue; -} -inline void FieldValue::set_has_bool_value() { - _oneof_case_[0] = kBoolValue; -} -inline void FieldValue::clear_bool_value() { - if (has_bool_value()) { - value_.bool_value_ = false; - clear_has_value(); - } -} -inline bool FieldValue::bool_value() const { - // @@protoc_insertion_point(field_get:milvus.grpc.FieldValue.bool_value) - if (has_bool_value()) { - return value_.bool_value_; - } - return false; -} -inline void FieldValue::set_bool_value(bool value) { - if (!has_bool_value()) { - clear_value(); - set_has_bool_value(); - } - value_.bool_value_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.FieldValue.bool_value) -} - -// .milvus.grpc.VectorFieldValue vector_value = 7; -inline bool FieldValue::has_vector_value() const { - return value_case() == kVectorValue; -} -inline void FieldValue::set_has_vector_value() { - _oneof_case_[0] = kVectorValue; -} -inline void FieldValue::clear_vector_value() { - if (has_vector_value()) { - delete value_.vector_value_; - clear_has_value(); - } -} -inline ::milvus::grpc::VectorFieldValue* FieldValue::release_vector_value() { - // @@protoc_insertion_point(field_release:milvus.grpc.FieldValue.vector_value) - if (has_vector_value()) { - clear_has_value(); - ::milvus::grpc::VectorFieldValue* temp = value_.vector_value_; - value_.vector_value_ = nullptr; - return temp; - } else { - return nullptr; - } -} -inline const ::milvus::grpc::VectorFieldValue& FieldValue::vector_value() const { - // @@protoc_insertion_point(field_get:milvus.grpc.FieldValue.vector_value) - return has_vector_value() - ? *value_.vector_value_ - : *reinterpret_cast< ::milvus::grpc::VectorFieldValue*>(&::milvus::grpc::_VectorFieldValue_default_instance_); -} -inline ::milvus::grpc::VectorFieldValue* FieldValue::mutable_vector_value() { - if (!has_vector_value()) { - clear_value(); - set_has_vector_value(); - value_.vector_value_ = CreateMaybeMessage< ::milvus::grpc::VectorFieldValue >( - GetArenaNoVirtual()); - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.FieldValue.vector_value) - return value_.vector_value_; -} - -inline bool FieldValue::has_value() const { - return value_case() != VALUE_NOT_SET; -} -inline void FieldValue::clear_has_value() { - _oneof_case_[0] = VALUE_NOT_SET; -} -inline FieldValue::ValueCase FieldValue::value_case() const { - return FieldValue::ValueCase(_oneof_case_[0]); -} -// ------------------------------------------------------------------- - -// Mapping - -// .milvus.grpc.Status status = 1; -inline bool Mapping::has_status() const { - return this != internal_default_instance() && status_ != nullptr; -} -inline const ::milvus::grpc::Status& Mapping::status() const { - const ::milvus::grpc::Status* p = status_; - // @@protoc_insertion_point(field_get:milvus.grpc.Mapping.status) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_Status_default_instance_); -} -inline ::milvus::grpc::Status* Mapping::release_status() { - // @@protoc_insertion_point(field_release:milvus.grpc.Mapping.status) - - ::milvus::grpc::Status* temp = status_; - status_ = nullptr; - return temp; -} -inline ::milvus::grpc::Status* Mapping::mutable_status() { - - if (status_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::Status>(GetArenaNoVirtual()); - status_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.Mapping.status) - return status_; -} -inline void Mapping::set_allocated_status(::milvus::grpc::Status* status) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(status_); - } - if (status) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - status = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, status, submessage_arena); - } - - } else { - - } - status_ = status; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.Mapping.status) -} - -// uint64 collection_id = 2; -inline void Mapping::clear_collection_id() { - collection_id_ = PROTOBUF_ULONGLONG(0); -} -inline ::PROTOBUF_NAMESPACE_ID::uint64 Mapping::collection_id() const { - // @@protoc_insertion_point(field_get:milvus.grpc.Mapping.collection_id) - return collection_id_; -} -inline void Mapping::set_collection_id(::PROTOBUF_NAMESPACE_ID::uint64 value) { - - collection_id_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.Mapping.collection_id) -} - -// string collection_name = 3; -inline void Mapping::clear_collection_name() { - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& Mapping::collection_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.Mapping.collection_name) - return collection_name_.GetNoArena(); -} -inline void Mapping::set_collection_name(const std::string& value) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.Mapping.collection_name) -} -inline void Mapping::set_collection_name(std::string&& value) { - - collection_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.Mapping.collection_name) -} -inline void Mapping::set_collection_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.Mapping.collection_name) -} -inline void Mapping::set_collection_name(const char* value, size_t size) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.Mapping.collection_name) -} -inline std::string* Mapping::mutable_collection_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.Mapping.collection_name) - return collection_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* Mapping::release_collection_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.Mapping.collection_name) - - return collection_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void Mapping::set_allocated_collection_name(std::string* collection_name) { - if (collection_name != nullptr) { - - } else { - - } - collection_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), collection_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.Mapping.collection_name) -} - -// repeated .milvus.grpc.FieldParam fields = 4; -inline int Mapping::fields_size() const { - return fields_.size(); -} -inline void Mapping::clear_fields() { - fields_.Clear(); -} -inline ::milvus::grpc::FieldParam* Mapping::mutable_fields(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.Mapping.fields) - return fields_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::FieldParam >* -Mapping::mutable_fields() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.Mapping.fields) - return &fields_; -} -inline const ::milvus::grpc::FieldParam& Mapping::fields(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.Mapping.fields) - return fields_.Get(index); -} -inline ::milvus::grpc::FieldParam* Mapping::add_fields() { - // @@protoc_insertion_point(field_add:milvus.grpc.Mapping.fields) - return fields_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::FieldParam >& -Mapping::fields() const { - // @@protoc_insertion_point(field_list:milvus.grpc.Mapping.fields) - return fields_; -} - -// ------------------------------------------------------------------- - -// MappingList - -// .milvus.grpc.Status status = 1; -inline bool MappingList::has_status() const { - return this != internal_default_instance() && status_ != nullptr; -} -inline const ::milvus::grpc::Status& MappingList::status() const { - const ::milvus::grpc::Status* p = status_; - // @@protoc_insertion_point(field_get:milvus.grpc.MappingList.status) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_Status_default_instance_); -} -inline ::milvus::grpc::Status* MappingList::release_status() { - // @@protoc_insertion_point(field_release:milvus.grpc.MappingList.status) - - ::milvus::grpc::Status* temp = status_; - status_ = nullptr; - return temp; -} -inline ::milvus::grpc::Status* MappingList::mutable_status() { - - if (status_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::Status>(GetArenaNoVirtual()); - status_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.MappingList.status) - return status_; -} -inline void MappingList::set_allocated_status(::milvus::grpc::Status* status) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(status_); - } - if (status) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - status = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, status, submessage_arena); - } - - } else { - - } - status_ = status; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.MappingList.status) -} - -// repeated .milvus.grpc.Mapping mapping_list = 2; -inline int MappingList::mapping_list_size() const { - return mapping_list_.size(); -} -inline void MappingList::clear_mapping_list() { - mapping_list_.Clear(); -} -inline ::milvus::grpc::Mapping* MappingList::mutable_mapping_list(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.MappingList.mapping_list) - return mapping_list_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::Mapping >* -MappingList::mutable_mapping_list() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.MappingList.mapping_list) - return &mapping_list_; -} -inline const ::milvus::grpc::Mapping& MappingList::mapping_list(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.MappingList.mapping_list) - return mapping_list_.Get(index); -} -inline ::milvus::grpc::Mapping* MappingList::add_mapping_list() { - // @@protoc_insertion_point(field_add:milvus.grpc.MappingList.mapping_list) - return mapping_list_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::Mapping >& -MappingList::mapping_list() const { - // @@protoc_insertion_point(field_list:milvus.grpc.MappingList.mapping_list) - return mapping_list_; -} - -// ------------------------------------------------------------------- - -// TermQuery - -// string field_name = 1; -inline void TermQuery::clear_field_name() { - field_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& TermQuery::field_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.TermQuery.field_name) - return field_name_.GetNoArena(); -} -inline void TermQuery::set_field_name(const std::string& value) { - - field_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.TermQuery.field_name) -} -inline void TermQuery::set_field_name(std::string&& value) { - - field_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.TermQuery.field_name) -} -inline void TermQuery::set_field_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - field_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.TermQuery.field_name) -} -inline void TermQuery::set_field_name(const char* value, size_t size) { - - field_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.TermQuery.field_name) -} -inline std::string* TermQuery::mutable_field_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.TermQuery.field_name) - return field_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* TermQuery::release_field_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.TermQuery.field_name) - - return field_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void TermQuery::set_allocated_field_name(std::string* field_name) { - if (field_name != nullptr) { - - } else { - - } - field_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), field_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.TermQuery.field_name) -} - -// bytes values = 2; -inline void TermQuery::clear_values() { - values_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& TermQuery::values() const { - // @@protoc_insertion_point(field_get:milvus.grpc.TermQuery.values) - return values_.GetNoArena(); -} -inline void TermQuery::set_values(const std::string& value) { - - values_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.TermQuery.values) -} -inline void TermQuery::set_values(std::string&& value) { - - values_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.TermQuery.values) -} -inline void TermQuery::set_values(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - values_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.TermQuery.values) -} -inline void TermQuery::set_values(const void* value, size_t size) { - - values_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.TermQuery.values) -} -inline std::string* TermQuery::mutable_values() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.TermQuery.values) - return values_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* TermQuery::release_values() { - // @@protoc_insertion_point(field_release:milvus.grpc.TermQuery.values) - - return values_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void TermQuery::set_allocated_values(std::string* values) { - if (values != nullptr) { - - } else { - - } - values_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), values); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.TermQuery.values) -} - -// int64 value_num = 3; -inline void TermQuery::clear_value_num() { - value_num_ = PROTOBUF_LONGLONG(0); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 TermQuery::value_num() const { - // @@protoc_insertion_point(field_get:milvus.grpc.TermQuery.value_num) - return value_num_; -} -inline void TermQuery::set_value_num(::PROTOBUF_NAMESPACE_ID::int64 value) { - - value_num_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.TermQuery.value_num) -} - -// float boost = 4; -inline void TermQuery::clear_boost() { - boost_ = 0; -} -inline float TermQuery::boost() const { - // @@protoc_insertion_point(field_get:milvus.grpc.TermQuery.boost) - return boost_; -} -inline void TermQuery::set_boost(float value) { - - boost_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.TermQuery.boost) -} - -// repeated .milvus.grpc.KeyValuePair extra_params = 5; -inline int TermQuery::extra_params_size() const { - return extra_params_.size(); -} -inline void TermQuery::clear_extra_params() { - extra_params_.Clear(); -} -inline ::milvus::grpc::KeyValuePair* TermQuery::mutable_extra_params(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.TermQuery.extra_params) - return extra_params_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* -TermQuery::mutable_extra_params() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.TermQuery.extra_params) - return &extra_params_; -} -inline const ::milvus::grpc::KeyValuePair& TermQuery::extra_params(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.TermQuery.extra_params) - return extra_params_.Get(index); -} -inline ::milvus::grpc::KeyValuePair* TermQuery::add_extra_params() { - // @@protoc_insertion_point(field_add:milvus.grpc.TermQuery.extra_params) - return extra_params_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& -TermQuery::extra_params() const { - // @@protoc_insertion_point(field_list:milvus.grpc.TermQuery.extra_params) - return extra_params_; -} - -// ------------------------------------------------------------------- - -// CompareExpr - -// .milvus.grpc.CompareOperator operator = 1; -inline void CompareExpr::clear_operator_() { - operator__ = 0; -} -inline ::milvus::grpc::CompareOperator CompareExpr::operator_() const { - // @@protoc_insertion_point(field_get:milvus.grpc.CompareExpr.operator) - return static_cast< ::milvus::grpc::CompareOperator >(operator__); -} -inline void CompareExpr::set_operator_(::milvus::grpc::CompareOperator value) { - - operator__ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.CompareExpr.operator) -} - -// string operand = 2; -inline void CompareExpr::clear_operand() { - operand_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& CompareExpr::operand() const { - // @@protoc_insertion_point(field_get:milvus.grpc.CompareExpr.operand) - return operand_.GetNoArena(); -} -inline void CompareExpr::set_operand(const std::string& value) { - - operand_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.CompareExpr.operand) -} -inline void CompareExpr::set_operand(std::string&& value) { - - operand_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.CompareExpr.operand) -} -inline void CompareExpr::set_operand(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - operand_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.CompareExpr.operand) -} -inline void CompareExpr::set_operand(const char* value, size_t size) { - - operand_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.CompareExpr.operand) -} -inline std::string* CompareExpr::mutable_operand() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.CompareExpr.operand) - return operand_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* CompareExpr::release_operand() { - // @@protoc_insertion_point(field_release:milvus.grpc.CompareExpr.operand) - - return operand_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void CompareExpr::set_allocated_operand(std::string* operand) { - if (operand != nullptr) { - - } else { - - } - operand_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), operand); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.CompareExpr.operand) -} - -// ------------------------------------------------------------------- - -// RangeQuery - -// string field_name = 1; -inline void RangeQuery::clear_field_name() { - field_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& RangeQuery::field_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.RangeQuery.field_name) - return field_name_.GetNoArena(); -} -inline void RangeQuery::set_field_name(const std::string& value) { - - field_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.RangeQuery.field_name) -} -inline void RangeQuery::set_field_name(std::string&& value) { - - field_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.RangeQuery.field_name) -} -inline void RangeQuery::set_field_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - field_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.RangeQuery.field_name) -} -inline void RangeQuery::set_field_name(const char* value, size_t size) { - - field_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.RangeQuery.field_name) -} -inline std::string* RangeQuery::mutable_field_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.RangeQuery.field_name) - return field_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* RangeQuery::release_field_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.RangeQuery.field_name) - - return field_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void RangeQuery::set_allocated_field_name(std::string* field_name) { - if (field_name != nullptr) { - - } else { - - } - field_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), field_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.RangeQuery.field_name) -} - -// repeated .milvus.grpc.CompareExpr operand = 2; -inline int RangeQuery::operand_size() const { - return operand_.size(); -} -inline void RangeQuery::clear_operand() { - operand_.Clear(); -} -inline ::milvus::grpc::CompareExpr* RangeQuery::mutable_operand(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.RangeQuery.operand) - return operand_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::CompareExpr >* -RangeQuery::mutable_operand() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.RangeQuery.operand) - return &operand_; -} -inline const ::milvus::grpc::CompareExpr& RangeQuery::operand(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.RangeQuery.operand) - return operand_.Get(index); -} -inline ::milvus::grpc::CompareExpr* RangeQuery::add_operand() { - // @@protoc_insertion_point(field_add:milvus.grpc.RangeQuery.operand) - return operand_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::CompareExpr >& -RangeQuery::operand() const { - // @@protoc_insertion_point(field_list:milvus.grpc.RangeQuery.operand) - return operand_; -} - -// float boost = 3; -inline void RangeQuery::clear_boost() { - boost_ = 0; -} -inline float RangeQuery::boost() const { - // @@protoc_insertion_point(field_get:milvus.grpc.RangeQuery.boost) - return boost_; -} -inline void RangeQuery::set_boost(float value) { - - boost_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.RangeQuery.boost) -} - -// repeated .milvus.grpc.KeyValuePair extra_params = 4; -inline int RangeQuery::extra_params_size() const { - return extra_params_.size(); -} -inline void RangeQuery::clear_extra_params() { - extra_params_.Clear(); -} -inline ::milvus::grpc::KeyValuePair* RangeQuery::mutable_extra_params(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.RangeQuery.extra_params) - return extra_params_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* -RangeQuery::mutable_extra_params() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.RangeQuery.extra_params) - return &extra_params_; -} -inline const ::milvus::grpc::KeyValuePair& RangeQuery::extra_params(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.RangeQuery.extra_params) - return extra_params_.Get(index); -} -inline ::milvus::grpc::KeyValuePair* RangeQuery::add_extra_params() { - // @@protoc_insertion_point(field_add:milvus.grpc.RangeQuery.extra_params) - return extra_params_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& -RangeQuery::extra_params() const { - // @@protoc_insertion_point(field_list:milvus.grpc.RangeQuery.extra_params) - return extra_params_; -} - -// ------------------------------------------------------------------- - -// VectorQuery - -// string field_name = 1; -inline void VectorQuery::clear_field_name() { - field_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& VectorQuery::field_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.VectorQuery.field_name) - return field_name_.GetNoArena(); -} -inline void VectorQuery::set_field_name(const std::string& value) { - - field_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.VectorQuery.field_name) -} -inline void VectorQuery::set_field_name(std::string&& value) { - - field_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.VectorQuery.field_name) -} -inline void VectorQuery::set_field_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - field_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.VectorQuery.field_name) -} -inline void VectorQuery::set_field_name(const char* value, size_t size) { - - field_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.VectorQuery.field_name) -} -inline std::string* VectorQuery::mutable_field_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.VectorQuery.field_name) - return field_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* VectorQuery::release_field_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.VectorQuery.field_name) - - return field_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void VectorQuery::set_allocated_field_name(std::string* field_name) { - if (field_name != nullptr) { - - } else { - - } - field_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), field_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.VectorQuery.field_name) -} - -// float query_boost = 2; -inline void VectorQuery::clear_query_boost() { - query_boost_ = 0; -} -inline float VectorQuery::query_boost() const { - // @@protoc_insertion_point(field_get:milvus.grpc.VectorQuery.query_boost) - return query_boost_; -} -inline void VectorQuery::set_query_boost(float value) { - - query_boost_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.VectorQuery.query_boost) -} - -// repeated .milvus.grpc.RowRecord records = 3; -inline int VectorQuery::records_size() const { - return records_.size(); -} -inline void VectorQuery::clear_records() { - records_.Clear(); -} -inline ::milvus::grpc::RowRecord* VectorQuery::mutable_records(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.VectorQuery.records) - return records_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >* -VectorQuery::mutable_records() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.VectorQuery.records) - return &records_; -} -inline const ::milvus::grpc::RowRecord& VectorQuery::records(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.VectorQuery.records) - return records_.Get(index); -} -inline ::milvus::grpc::RowRecord* VectorQuery::add_records() { - // @@protoc_insertion_point(field_add:milvus.grpc.VectorQuery.records) - return records_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::RowRecord >& -VectorQuery::records() const { - // @@protoc_insertion_point(field_list:milvus.grpc.VectorQuery.records) - return records_; -} - -// int64 topk = 4; -inline void VectorQuery::clear_topk() { - topk_ = PROTOBUF_LONGLONG(0); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 VectorQuery::topk() const { - // @@protoc_insertion_point(field_get:milvus.grpc.VectorQuery.topk) - return topk_; -} -inline void VectorQuery::set_topk(::PROTOBUF_NAMESPACE_ID::int64 value) { - - topk_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.VectorQuery.topk) -} - -// repeated .milvus.grpc.KeyValuePair extra_params = 5; -inline int VectorQuery::extra_params_size() const { - return extra_params_.size(); -} -inline void VectorQuery::clear_extra_params() { - extra_params_.Clear(); -} -inline ::milvus::grpc::KeyValuePair* VectorQuery::mutable_extra_params(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.VectorQuery.extra_params) - return extra_params_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* -VectorQuery::mutable_extra_params() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.VectorQuery.extra_params) - return &extra_params_; -} -inline const ::milvus::grpc::KeyValuePair& VectorQuery::extra_params(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.VectorQuery.extra_params) - return extra_params_.Get(index); -} -inline ::milvus::grpc::KeyValuePair* VectorQuery::add_extra_params() { - // @@protoc_insertion_point(field_add:milvus.grpc.VectorQuery.extra_params) - return extra_params_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& -VectorQuery::extra_params() const { - // @@protoc_insertion_point(field_list:milvus.grpc.VectorQuery.extra_params) - return extra_params_; -} - -// ------------------------------------------------------------------- - -// BooleanQuery - -// .milvus.grpc.Occur occur = 1; -inline void BooleanQuery::clear_occur() { - occur_ = 0; -} -inline ::milvus::grpc::Occur BooleanQuery::occur() const { - // @@protoc_insertion_point(field_get:milvus.grpc.BooleanQuery.occur) - return static_cast< ::milvus::grpc::Occur >(occur_); -} -inline void BooleanQuery::set_occur(::milvus::grpc::Occur value) { - - occur_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.BooleanQuery.occur) -} - -// repeated .milvus.grpc.GeneralQuery general_query = 2; -inline int BooleanQuery::general_query_size() const { - return general_query_.size(); -} -inline void BooleanQuery::clear_general_query() { - general_query_.Clear(); -} -inline ::milvus::grpc::GeneralQuery* BooleanQuery::mutable_general_query(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.BooleanQuery.general_query) - return general_query_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::GeneralQuery >* -BooleanQuery::mutable_general_query() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.BooleanQuery.general_query) - return &general_query_; -} -inline const ::milvus::grpc::GeneralQuery& BooleanQuery::general_query(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.BooleanQuery.general_query) - return general_query_.Get(index); -} -inline ::milvus::grpc::GeneralQuery* BooleanQuery::add_general_query() { - // @@protoc_insertion_point(field_add:milvus.grpc.BooleanQuery.general_query) - return general_query_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::GeneralQuery >& -BooleanQuery::general_query() const { - // @@protoc_insertion_point(field_list:milvus.grpc.BooleanQuery.general_query) - return general_query_; -} - -// ------------------------------------------------------------------- - -// GeneralQuery - -// .milvus.grpc.BooleanQuery boolean_query = 1; -inline bool GeneralQuery::has_boolean_query() const { - return query_case() == kBooleanQuery; -} -inline void GeneralQuery::set_has_boolean_query() { - _oneof_case_[0] = kBooleanQuery; -} -inline void GeneralQuery::clear_boolean_query() { - if (has_boolean_query()) { - delete query_.boolean_query_; - clear_has_query(); - } -} -inline ::milvus::grpc::BooleanQuery* GeneralQuery::release_boolean_query() { - // @@protoc_insertion_point(field_release:milvus.grpc.GeneralQuery.boolean_query) - if (has_boolean_query()) { - clear_has_query(); - ::milvus::grpc::BooleanQuery* temp = query_.boolean_query_; - query_.boolean_query_ = nullptr; - return temp; - } else { - return nullptr; - } -} -inline const ::milvus::grpc::BooleanQuery& GeneralQuery::boolean_query() const { - // @@protoc_insertion_point(field_get:milvus.grpc.GeneralQuery.boolean_query) - return has_boolean_query() - ? *query_.boolean_query_ - : *reinterpret_cast< ::milvus::grpc::BooleanQuery*>(&::milvus::grpc::_BooleanQuery_default_instance_); -} -inline ::milvus::grpc::BooleanQuery* GeneralQuery::mutable_boolean_query() { - if (!has_boolean_query()) { - clear_query(); - set_has_boolean_query(); - query_.boolean_query_ = CreateMaybeMessage< ::milvus::grpc::BooleanQuery >( - GetArenaNoVirtual()); - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.GeneralQuery.boolean_query) - return query_.boolean_query_; -} - -// .milvus.grpc.TermQuery term_query = 2; -inline bool GeneralQuery::has_term_query() const { - return query_case() == kTermQuery; -} -inline void GeneralQuery::set_has_term_query() { - _oneof_case_[0] = kTermQuery; -} -inline void GeneralQuery::clear_term_query() { - if (has_term_query()) { - delete query_.term_query_; - clear_has_query(); - } -} -inline ::milvus::grpc::TermQuery* GeneralQuery::release_term_query() { - // @@protoc_insertion_point(field_release:milvus.grpc.GeneralQuery.term_query) - if (has_term_query()) { - clear_has_query(); - ::milvus::grpc::TermQuery* temp = query_.term_query_; - query_.term_query_ = nullptr; - return temp; - } else { - return nullptr; - } -} -inline const ::milvus::grpc::TermQuery& GeneralQuery::term_query() const { - // @@protoc_insertion_point(field_get:milvus.grpc.GeneralQuery.term_query) - return has_term_query() - ? *query_.term_query_ - : *reinterpret_cast< ::milvus::grpc::TermQuery*>(&::milvus::grpc::_TermQuery_default_instance_); -} -inline ::milvus::grpc::TermQuery* GeneralQuery::mutable_term_query() { - if (!has_term_query()) { - clear_query(); - set_has_term_query(); - query_.term_query_ = CreateMaybeMessage< ::milvus::grpc::TermQuery >( - GetArenaNoVirtual()); - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.GeneralQuery.term_query) - return query_.term_query_; -} - -// .milvus.grpc.RangeQuery range_query = 3; -inline bool GeneralQuery::has_range_query() const { - return query_case() == kRangeQuery; -} -inline void GeneralQuery::set_has_range_query() { - _oneof_case_[0] = kRangeQuery; -} -inline void GeneralQuery::clear_range_query() { - if (has_range_query()) { - delete query_.range_query_; - clear_has_query(); - } -} -inline ::milvus::grpc::RangeQuery* GeneralQuery::release_range_query() { - // @@protoc_insertion_point(field_release:milvus.grpc.GeneralQuery.range_query) - if (has_range_query()) { - clear_has_query(); - ::milvus::grpc::RangeQuery* temp = query_.range_query_; - query_.range_query_ = nullptr; - return temp; - } else { - return nullptr; - } -} -inline const ::milvus::grpc::RangeQuery& GeneralQuery::range_query() const { - // @@protoc_insertion_point(field_get:milvus.grpc.GeneralQuery.range_query) - return has_range_query() - ? *query_.range_query_ - : *reinterpret_cast< ::milvus::grpc::RangeQuery*>(&::milvus::grpc::_RangeQuery_default_instance_); -} -inline ::milvus::grpc::RangeQuery* GeneralQuery::mutable_range_query() { - if (!has_range_query()) { - clear_query(); - set_has_range_query(); - query_.range_query_ = CreateMaybeMessage< ::milvus::grpc::RangeQuery >( - GetArenaNoVirtual()); - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.GeneralQuery.range_query) - return query_.range_query_; -} - -// .milvus.grpc.VectorQuery vector_query = 4; -inline bool GeneralQuery::has_vector_query() const { - return query_case() == kVectorQuery; -} -inline void GeneralQuery::set_has_vector_query() { - _oneof_case_[0] = kVectorQuery; -} -inline void GeneralQuery::clear_vector_query() { - if (has_vector_query()) { - delete query_.vector_query_; - clear_has_query(); - } -} -inline ::milvus::grpc::VectorQuery* GeneralQuery::release_vector_query() { - // @@protoc_insertion_point(field_release:milvus.grpc.GeneralQuery.vector_query) - if (has_vector_query()) { - clear_has_query(); - ::milvus::grpc::VectorQuery* temp = query_.vector_query_; - query_.vector_query_ = nullptr; - return temp; - } else { - return nullptr; - } -} -inline const ::milvus::grpc::VectorQuery& GeneralQuery::vector_query() const { - // @@protoc_insertion_point(field_get:milvus.grpc.GeneralQuery.vector_query) - return has_vector_query() - ? *query_.vector_query_ - : *reinterpret_cast< ::milvus::grpc::VectorQuery*>(&::milvus::grpc::_VectorQuery_default_instance_); -} -inline ::milvus::grpc::VectorQuery* GeneralQuery::mutable_vector_query() { - if (!has_vector_query()) { - clear_query(); - set_has_vector_query(); - query_.vector_query_ = CreateMaybeMessage< ::milvus::grpc::VectorQuery >( - GetArenaNoVirtual()); - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.GeneralQuery.vector_query) - return query_.vector_query_; -} - -inline bool GeneralQuery::has_query() const { - return query_case() != QUERY_NOT_SET; -} -inline void GeneralQuery::clear_has_query() { - _oneof_case_[0] = QUERY_NOT_SET; -} -inline GeneralQuery::QueryCase GeneralQuery::query_case() const { - return GeneralQuery::QueryCase(_oneof_case_[0]); -} -// ------------------------------------------------------------------- - -// HSearchParam - -// string collection_name = 1; -inline void HSearchParam::clear_collection_name() { - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& HSearchParam::collection_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.HSearchParam.collection_name) - return collection_name_.GetNoArena(); -} -inline void HSearchParam::set_collection_name(const std::string& value) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.HSearchParam.collection_name) -} -inline void HSearchParam::set_collection_name(std::string&& value) { - - collection_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.HSearchParam.collection_name) -} -inline void HSearchParam::set_collection_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.HSearchParam.collection_name) -} -inline void HSearchParam::set_collection_name(const char* value, size_t size) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.HSearchParam.collection_name) -} -inline std::string* HSearchParam::mutable_collection_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.HSearchParam.collection_name) - return collection_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* HSearchParam::release_collection_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.HSearchParam.collection_name) - - return collection_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void HSearchParam::set_allocated_collection_name(std::string* collection_name) { - if (collection_name != nullptr) { - - } else { - - } - collection_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), collection_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.HSearchParam.collection_name) -} - -// repeated string partition_tag_array = 2; -inline int HSearchParam::partition_tag_array_size() const { - return partition_tag_array_.size(); -} -inline void HSearchParam::clear_partition_tag_array() { - partition_tag_array_.Clear(); -} -inline const std::string& HSearchParam::partition_tag_array(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.HSearchParam.partition_tag_array) - return partition_tag_array_.Get(index); -} -inline std::string* HSearchParam::mutable_partition_tag_array(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.HSearchParam.partition_tag_array) - return partition_tag_array_.Mutable(index); -} -inline void HSearchParam::set_partition_tag_array(int index, const std::string& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.HSearchParam.partition_tag_array) - partition_tag_array_.Mutable(index)->assign(value); -} -inline void HSearchParam::set_partition_tag_array(int index, std::string&& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.HSearchParam.partition_tag_array) - partition_tag_array_.Mutable(index)->assign(std::move(value)); -} -inline void HSearchParam::set_partition_tag_array(int index, const char* value) { - GOOGLE_DCHECK(value != nullptr); - partition_tag_array_.Mutable(index)->assign(value); - // @@protoc_insertion_point(field_set_char:milvus.grpc.HSearchParam.partition_tag_array) -} -inline void HSearchParam::set_partition_tag_array(int index, const char* value, size_t size) { - partition_tag_array_.Mutable(index)->assign( - reinterpret_cast(value), size); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.HSearchParam.partition_tag_array) -} -inline std::string* HSearchParam::add_partition_tag_array() { - // @@protoc_insertion_point(field_add_mutable:milvus.grpc.HSearchParam.partition_tag_array) - return partition_tag_array_.Add(); -} -inline void HSearchParam::add_partition_tag_array(const std::string& value) { - partition_tag_array_.Add()->assign(value); - // @@protoc_insertion_point(field_add:milvus.grpc.HSearchParam.partition_tag_array) -} -inline void HSearchParam::add_partition_tag_array(std::string&& value) { - partition_tag_array_.Add(std::move(value)); - // @@protoc_insertion_point(field_add:milvus.grpc.HSearchParam.partition_tag_array) -} -inline void HSearchParam::add_partition_tag_array(const char* value) { - GOOGLE_DCHECK(value != nullptr); - partition_tag_array_.Add()->assign(value); - // @@protoc_insertion_point(field_add_char:milvus.grpc.HSearchParam.partition_tag_array) -} -inline void HSearchParam::add_partition_tag_array(const char* value, size_t size) { - partition_tag_array_.Add()->assign(reinterpret_cast(value), size); - // @@protoc_insertion_point(field_add_pointer:milvus.grpc.HSearchParam.partition_tag_array) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& -HSearchParam::partition_tag_array() const { - // @@protoc_insertion_point(field_list:milvus.grpc.HSearchParam.partition_tag_array) - return partition_tag_array_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* -HSearchParam::mutable_partition_tag_array() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.HSearchParam.partition_tag_array) - return &partition_tag_array_; -} - -// .milvus.grpc.GeneralQuery general_query = 3; -inline bool HSearchParam::has_general_query() const { - return this != internal_default_instance() && general_query_ != nullptr; -} -inline void HSearchParam::clear_general_query() { - if (GetArenaNoVirtual() == nullptr && general_query_ != nullptr) { - delete general_query_; - } - general_query_ = nullptr; -} -inline const ::milvus::grpc::GeneralQuery& HSearchParam::general_query() const { - const ::milvus::grpc::GeneralQuery* p = general_query_; - // @@protoc_insertion_point(field_get:milvus.grpc.HSearchParam.general_query) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_GeneralQuery_default_instance_); -} -inline ::milvus::grpc::GeneralQuery* HSearchParam::release_general_query() { - // @@protoc_insertion_point(field_release:milvus.grpc.HSearchParam.general_query) - - ::milvus::grpc::GeneralQuery* temp = general_query_; - general_query_ = nullptr; - return temp; -} -inline ::milvus::grpc::GeneralQuery* HSearchParam::mutable_general_query() { - - if (general_query_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::GeneralQuery>(GetArenaNoVirtual()); - general_query_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.HSearchParam.general_query) - return general_query_; -} -inline void HSearchParam::set_allocated_general_query(::milvus::grpc::GeneralQuery* general_query) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete general_query_; - } - if (general_query) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - general_query = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, general_query, submessage_arena); - } - - } else { - - } - general_query_ = general_query; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.HSearchParam.general_query) -} - -// repeated .milvus.grpc.KeyValuePair extra_params = 4; -inline int HSearchParam::extra_params_size() const { - return extra_params_.size(); -} -inline void HSearchParam::clear_extra_params() { - extra_params_.Clear(); -} -inline ::milvus::grpc::KeyValuePair* HSearchParam::mutable_extra_params(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.HSearchParam.extra_params) - return extra_params_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* -HSearchParam::mutable_extra_params() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.HSearchParam.extra_params) - return &extra_params_; -} -inline const ::milvus::grpc::KeyValuePair& HSearchParam::extra_params(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.HSearchParam.extra_params) - return extra_params_.Get(index); -} -inline ::milvus::grpc::KeyValuePair* HSearchParam::add_extra_params() { - // @@protoc_insertion_point(field_add:milvus.grpc.HSearchParam.extra_params) - return extra_params_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& -HSearchParam::extra_params() const { - // @@protoc_insertion_point(field_list:milvus.grpc.HSearchParam.extra_params) - return extra_params_; -} - -// ------------------------------------------------------------------- - -// HSearchInSegmentsParam - -// repeated string segment_id_array = 1; -inline int HSearchInSegmentsParam::segment_id_array_size() const { - return segment_id_array_.size(); -} -inline void HSearchInSegmentsParam::clear_segment_id_array() { - segment_id_array_.Clear(); -} -inline const std::string& HSearchInSegmentsParam::segment_id_array(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.HSearchInSegmentsParam.segment_id_array) - return segment_id_array_.Get(index); -} -inline std::string* HSearchInSegmentsParam::mutable_segment_id_array(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.HSearchInSegmentsParam.segment_id_array) - return segment_id_array_.Mutable(index); -} -inline void HSearchInSegmentsParam::set_segment_id_array(int index, const std::string& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.HSearchInSegmentsParam.segment_id_array) - segment_id_array_.Mutable(index)->assign(value); -} -inline void HSearchInSegmentsParam::set_segment_id_array(int index, std::string&& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.HSearchInSegmentsParam.segment_id_array) - segment_id_array_.Mutable(index)->assign(std::move(value)); -} -inline void HSearchInSegmentsParam::set_segment_id_array(int index, const char* value) { - GOOGLE_DCHECK(value != nullptr); - segment_id_array_.Mutable(index)->assign(value); - // @@protoc_insertion_point(field_set_char:milvus.grpc.HSearchInSegmentsParam.segment_id_array) -} -inline void HSearchInSegmentsParam::set_segment_id_array(int index, const char* value, size_t size) { - segment_id_array_.Mutable(index)->assign( - reinterpret_cast(value), size); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.HSearchInSegmentsParam.segment_id_array) -} -inline std::string* HSearchInSegmentsParam::add_segment_id_array() { - // @@protoc_insertion_point(field_add_mutable:milvus.grpc.HSearchInSegmentsParam.segment_id_array) - return segment_id_array_.Add(); -} -inline void HSearchInSegmentsParam::add_segment_id_array(const std::string& value) { - segment_id_array_.Add()->assign(value); - // @@protoc_insertion_point(field_add:milvus.grpc.HSearchInSegmentsParam.segment_id_array) -} -inline void HSearchInSegmentsParam::add_segment_id_array(std::string&& value) { - segment_id_array_.Add(std::move(value)); - // @@protoc_insertion_point(field_add:milvus.grpc.HSearchInSegmentsParam.segment_id_array) -} -inline void HSearchInSegmentsParam::add_segment_id_array(const char* value) { - GOOGLE_DCHECK(value != nullptr); - segment_id_array_.Add()->assign(value); - // @@protoc_insertion_point(field_add_char:milvus.grpc.HSearchInSegmentsParam.segment_id_array) -} -inline void HSearchInSegmentsParam::add_segment_id_array(const char* value, size_t size) { - segment_id_array_.Add()->assign(reinterpret_cast(value), size); - // @@protoc_insertion_point(field_add_pointer:milvus.grpc.HSearchInSegmentsParam.segment_id_array) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& -HSearchInSegmentsParam::segment_id_array() const { - // @@protoc_insertion_point(field_list:milvus.grpc.HSearchInSegmentsParam.segment_id_array) - return segment_id_array_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* -HSearchInSegmentsParam::mutable_segment_id_array() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.HSearchInSegmentsParam.segment_id_array) - return &segment_id_array_; -} - -// .milvus.grpc.HSearchParam search_param = 2; -inline bool HSearchInSegmentsParam::has_search_param() const { - return this != internal_default_instance() && search_param_ != nullptr; -} -inline void HSearchInSegmentsParam::clear_search_param() { - if (GetArenaNoVirtual() == nullptr && search_param_ != nullptr) { - delete search_param_; - } - search_param_ = nullptr; -} -inline const ::milvus::grpc::HSearchParam& HSearchInSegmentsParam::search_param() const { - const ::milvus::grpc::HSearchParam* p = search_param_; - // @@protoc_insertion_point(field_get:milvus.grpc.HSearchInSegmentsParam.search_param) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_HSearchParam_default_instance_); -} -inline ::milvus::grpc::HSearchParam* HSearchInSegmentsParam::release_search_param() { - // @@protoc_insertion_point(field_release:milvus.grpc.HSearchInSegmentsParam.search_param) - - ::milvus::grpc::HSearchParam* temp = search_param_; - search_param_ = nullptr; - return temp; -} -inline ::milvus::grpc::HSearchParam* HSearchInSegmentsParam::mutable_search_param() { - - if (search_param_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::HSearchParam>(GetArenaNoVirtual()); - search_param_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.HSearchInSegmentsParam.search_param) - return search_param_; -} -inline void HSearchInSegmentsParam::set_allocated_search_param(::milvus::grpc::HSearchParam* search_param) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete search_param_; - } - if (search_param) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - search_param = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, search_param, submessage_arena); - } - - } else { - - } - search_param_ = search_param; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.HSearchInSegmentsParam.search_param) -} - -// ------------------------------------------------------------------- - -// AttrRecord - -// repeated string value = 1; -inline int AttrRecord::value_size() const { - return value_.size(); -} -inline void AttrRecord::clear_value() { - value_.Clear(); -} -inline const std::string& AttrRecord::value(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.AttrRecord.value) - return value_.Get(index); -} -inline std::string* AttrRecord::mutable_value(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.AttrRecord.value) - return value_.Mutable(index); -} -inline void AttrRecord::set_value(int index, const std::string& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.AttrRecord.value) - value_.Mutable(index)->assign(value); -} -inline void AttrRecord::set_value(int index, std::string&& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.AttrRecord.value) - value_.Mutable(index)->assign(std::move(value)); -} -inline void AttrRecord::set_value(int index, const char* value) { - GOOGLE_DCHECK(value != nullptr); - value_.Mutable(index)->assign(value); - // @@protoc_insertion_point(field_set_char:milvus.grpc.AttrRecord.value) -} -inline void AttrRecord::set_value(int index, const char* value, size_t size) { - value_.Mutable(index)->assign( - reinterpret_cast(value), size); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.AttrRecord.value) -} -inline std::string* AttrRecord::add_value() { - // @@protoc_insertion_point(field_add_mutable:milvus.grpc.AttrRecord.value) - return value_.Add(); -} -inline void AttrRecord::add_value(const std::string& value) { - value_.Add()->assign(value); - // @@protoc_insertion_point(field_add:milvus.grpc.AttrRecord.value) -} -inline void AttrRecord::add_value(std::string&& value) { - value_.Add(std::move(value)); - // @@protoc_insertion_point(field_add:milvus.grpc.AttrRecord.value) -} -inline void AttrRecord::add_value(const char* value) { - GOOGLE_DCHECK(value != nullptr); - value_.Add()->assign(value); - // @@protoc_insertion_point(field_add_char:milvus.grpc.AttrRecord.value) -} -inline void AttrRecord::add_value(const char* value, size_t size) { - value_.Add()->assign(reinterpret_cast(value), size); - // @@protoc_insertion_point(field_add_pointer:milvus.grpc.AttrRecord.value) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& -AttrRecord::value() const { - // @@protoc_insertion_point(field_list:milvus.grpc.AttrRecord.value) - return value_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* -AttrRecord::mutable_value() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.AttrRecord.value) - return &value_; -} - -// ------------------------------------------------------------------- - -// HEntity - -// .milvus.grpc.Status status = 1; -inline bool HEntity::has_status() const { - return this != internal_default_instance() && status_ != nullptr; -} -inline const ::milvus::grpc::Status& HEntity::status() const { - const ::milvus::grpc::Status* p = status_; - // @@protoc_insertion_point(field_get:milvus.grpc.HEntity.status) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_Status_default_instance_); -} -inline ::milvus::grpc::Status* HEntity::release_status() { - // @@protoc_insertion_point(field_release:milvus.grpc.HEntity.status) - - ::milvus::grpc::Status* temp = status_; - status_ = nullptr; - return temp; -} -inline ::milvus::grpc::Status* HEntity::mutable_status() { - - if (status_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::Status>(GetArenaNoVirtual()); - status_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.HEntity.status) - return status_; -} -inline void HEntity::set_allocated_status(::milvus::grpc::Status* status) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(status_); - } - if (status) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - status = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, status, submessage_arena); - } - - } else { - - } - status_ = status; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.HEntity.status) -} - -// int64 entity_id = 2; -inline void HEntity::clear_entity_id() { - entity_id_ = PROTOBUF_LONGLONG(0); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 HEntity::entity_id() const { - // @@protoc_insertion_point(field_get:milvus.grpc.HEntity.entity_id) - return entity_id_; -} -inline void HEntity::set_entity_id(::PROTOBUF_NAMESPACE_ID::int64 value) { - - entity_id_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.HEntity.entity_id) -} - -// repeated string field_names = 3; -inline int HEntity::field_names_size() const { - return field_names_.size(); -} -inline void HEntity::clear_field_names() { - field_names_.Clear(); -} -inline const std::string& HEntity::field_names(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.HEntity.field_names) - return field_names_.Get(index); -} -inline std::string* HEntity::mutable_field_names(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.HEntity.field_names) - return field_names_.Mutable(index); -} -inline void HEntity::set_field_names(int index, const std::string& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.HEntity.field_names) - field_names_.Mutable(index)->assign(value); -} -inline void HEntity::set_field_names(int index, std::string&& value) { - // @@protoc_insertion_point(field_set:milvus.grpc.HEntity.field_names) - field_names_.Mutable(index)->assign(std::move(value)); -} -inline void HEntity::set_field_names(int index, const char* value) { - GOOGLE_DCHECK(value != nullptr); - field_names_.Mutable(index)->assign(value); - // @@protoc_insertion_point(field_set_char:milvus.grpc.HEntity.field_names) -} -inline void HEntity::set_field_names(int index, const char* value, size_t size) { - field_names_.Mutable(index)->assign( - reinterpret_cast(value), size); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.HEntity.field_names) -} -inline std::string* HEntity::add_field_names() { - // @@protoc_insertion_point(field_add_mutable:milvus.grpc.HEntity.field_names) - return field_names_.Add(); -} -inline void HEntity::add_field_names(const std::string& value) { - field_names_.Add()->assign(value); - // @@protoc_insertion_point(field_add:milvus.grpc.HEntity.field_names) -} -inline void HEntity::add_field_names(std::string&& value) { - field_names_.Add(std::move(value)); - // @@protoc_insertion_point(field_add:milvus.grpc.HEntity.field_names) -} -inline void HEntity::add_field_names(const char* value) { - GOOGLE_DCHECK(value != nullptr); - field_names_.Add()->assign(value); - // @@protoc_insertion_point(field_add_char:milvus.grpc.HEntity.field_names) -} -inline void HEntity::add_field_names(const char* value, size_t size) { - field_names_.Add()->assign(reinterpret_cast(value), size); - // @@protoc_insertion_point(field_add_pointer:milvus.grpc.HEntity.field_names) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField& -HEntity::field_names() const { - // @@protoc_insertion_point(field_list:milvus.grpc.HEntity.field_names) - return field_names_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField* -HEntity::mutable_field_names() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.HEntity.field_names) - return &field_names_; -} - -// bytes attr_records = 4; -inline void HEntity::clear_attr_records() { - attr_records_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& HEntity::attr_records() const { - // @@protoc_insertion_point(field_get:milvus.grpc.HEntity.attr_records) - return attr_records_.GetNoArena(); -} -inline void HEntity::set_attr_records(const std::string& value) { - - attr_records_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.HEntity.attr_records) -} -inline void HEntity::set_attr_records(std::string&& value) { - - attr_records_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.HEntity.attr_records) -} -inline void HEntity::set_attr_records(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - attr_records_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.HEntity.attr_records) -} -inline void HEntity::set_attr_records(const void* value, size_t size) { - - attr_records_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.HEntity.attr_records) -} -inline std::string* HEntity::mutable_attr_records() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.HEntity.attr_records) - return attr_records_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* HEntity::release_attr_records() { - // @@protoc_insertion_point(field_release:milvus.grpc.HEntity.attr_records) - - return attr_records_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void HEntity::set_allocated_attr_records(std::string* attr_records) { - if (attr_records != nullptr) { - - } else { - - } - attr_records_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), attr_records); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.HEntity.attr_records) -} - -// int64 row_num = 5; -inline void HEntity::clear_row_num() { - row_num_ = PROTOBUF_LONGLONG(0); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 HEntity::row_num() const { - // @@protoc_insertion_point(field_get:milvus.grpc.HEntity.row_num) - return row_num_; -} -inline void HEntity::set_row_num(::PROTOBUF_NAMESPACE_ID::int64 value) { - - row_num_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.HEntity.row_num) -} - -// repeated .milvus.grpc.FieldValue result_values = 6; -inline int HEntity::result_values_size() const { - return result_values_.size(); -} -inline void HEntity::clear_result_values() { - result_values_.Clear(); -} -inline ::milvus::grpc::FieldValue* HEntity::mutable_result_values(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.HEntity.result_values) - return result_values_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::FieldValue >* -HEntity::mutable_result_values() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.HEntity.result_values) - return &result_values_; -} -inline const ::milvus::grpc::FieldValue& HEntity::result_values(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.HEntity.result_values) - return result_values_.Get(index); -} -inline ::milvus::grpc::FieldValue* HEntity::add_result_values() { - // @@protoc_insertion_point(field_add:milvus.grpc.HEntity.result_values) - return result_values_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::FieldValue >& -HEntity::result_values() const { - // @@protoc_insertion_point(field_list:milvus.grpc.HEntity.result_values) - return result_values_; -} - -// ------------------------------------------------------------------- - -// HQueryResult - -// .milvus.grpc.Status status = 1; -inline bool HQueryResult::has_status() const { - return this != internal_default_instance() && status_ != nullptr; -} -inline const ::milvus::grpc::Status& HQueryResult::status() const { - const ::milvus::grpc::Status* p = status_; - // @@protoc_insertion_point(field_get:milvus.grpc.HQueryResult.status) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_Status_default_instance_); -} -inline ::milvus::grpc::Status* HQueryResult::release_status() { - // @@protoc_insertion_point(field_release:milvus.grpc.HQueryResult.status) - - ::milvus::grpc::Status* temp = status_; - status_ = nullptr; - return temp; -} -inline ::milvus::grpc::Status* HQueryResult::mutable_status() { - - if (status_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::Status>(GetArenaNoVirtual()); - status_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.HQueryResult.status) - return status_; -} -inline void HQueryResult::set_allocated_status(::milvus::grpc::Status* status) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(status_); - } - if (status) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - status = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, status, submessage_arena); - } - - } else { - - } - status_ = status; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.HQueryResult.status) -} - -// repeated .milvus.grpc.HEntity entities = 2; -inline int HQueryResult::entities_size() const { - return entities_.size(); -} -inline void HQueryResult::clear_entities() { - entities_.Clear(); -} -inline ::milvus::grpc::HEntity* HQueryResult::mutable_entities(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.HQueryResult.entities) - return entities_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::HEntity >* -HQueryResult::mutable_entities() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.HQueryResult.entities) - return &entities_; -} -inline const ::milvus::grpc::HEntity& HQueryResult::entities(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.HQueryResult.entities) - return entities_.Get(index); -} -inline ::milvus::grpc::HEntity* HQueryResult::add_entities() { - // @@protoc_insertion_point(field_add:milvus.grpc.HQueryResult.entities) - return entities_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::HEntity >& -HQueryResult::entities() const { - // @@protoc_insertion_point(field_list:milvus.grpc.HQueryResult.entities) - return entities_; -} - -// int64 row_num = 3; -inline void HQueryResult::clear_row_num() { - row_num_ = PROTOBUF_LONGLONG(0); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 HQueryResult::row_num() const { - // @@protoc_insertion_point(field_get:milvus.grpc.HQueryResult.row_num) - return row_num_; -} -inline void HQueryResult::set_row_num(::PROTOBUF_NAMESPACE_ID::int64 value) { - - row_num_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.HQueryResult.row_num) -} - -// repeated float score = 4; -inline int HQueryResult::score_size() const { - return score_.size(); -} -inline void HQueryResult::clear_score() { - score_.Clear(); -} -inline float HQueryResult::score(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.HQueryResult.score) - return score_.Get(index); -} -inline void HQueryResult::set_score(int index, float value) { - score_.Set(index, value); - // @@protoc_insertion_point(field_set:milvus.grpc.HQueryResult.score) -} -inline void HQueryResult::add_score(float value) { - score_.Add(value); - // @@protoc_insertion_point(field_add:milvus.grpc.HQueryResult.score) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >& -HQueryResult::score() const { - // @@protoc_insertion_point(field_list:milvus.grpc.HQueryResult.score) - return score_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >* -HQueryResult::mutable_score() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.HQueryResult.score) - return &score_; -} - -// repeated float distance = 5; -inline int HQueryResult::distance_size() const { - return distance_.size(); -} -inline void HQueryResult::clear_distance() { - distance_.Clear(); -} -inline float HQueryResult::distance(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.HQueryResult.distance) - return distance_.Get(index); -} -inline void HQueryResult::set_distance(int index, float value) { - distance_.Set(index, value); - // @@protoc_insertion_point(field_set:milvus.grpc.HQueryResult.distance) -} -inline void HQueryResult::add_distance(float value) { - distance_.Add(value); - // @@protoc_insertion_point(field_add:milvus.grpc.HQueryResult.distance) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >& -HQueryResult::distance() const { - // @@protoc_insertion_point(field_list:milvus.grpc.HQueryResult.distance) - return distance_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >* -HQueryResult::mutable_distance() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.HQueryResult.distance) - return &distance_; -} - -// ------------------------------------------------------------------- - -// HInsertParam - -// string collection_name = 1; -inline void HInsertParam::clear_collection_name() { - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& HInsertParam::collection_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.HInsertParam.collection_name) - return collection_name_.GetNoArena(); -} -inline void HInsertParam::set_collection_name(const std::string& value) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.HInsertParam.collection_name) -} -inline void HInsertParam::set_collection_name(std::string&& value) { - - collection_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.HInsertParam.collection_name) -} -inline void HInsertParam::set_collection_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.HInsertParam.collection_name) -} -inline void HInsertParam::set_collection_name(const char* value, size_t size) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.HInsertParam.collection_name) -} -inline std::string* HInsertParam::mutable_collection_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.HInsertParam.collection_name) - return collection_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* HInsertParam::release_collection_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.HInsertParam.collection_name) - - return collection_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void HInsertParam::set_allocated_collection_name(std::string* collection_name) { - if (collection_name != nullptr) { - - } else { - - } - collection_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), collection_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.HInsertParam.collection_name) -} - -// string partition_tag = 2; -inline void HInsertParam::clear_partition_tag() { - partition_tag_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& HInsertParam::partition_tag() const { - // @@protoc_insertion_point(field_get:milvus.grpc.HInsertParam.partition_tag) - return partition_tag_.GetNoArena(); -} -inline void HInsertParam::set_partition_tag(const std::string& value) { - - partition_tag_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.HInsertParam.partition_tag) -} -inline void HInsertParam::set_partition_tag(std::string&& value) { - - partition_tag_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.HInsertParam.partition_tag) -} -inline void HInsertParam::set_partition_tag(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - partition_tag_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.HInsertParam.partition_tag) -} -inline void HInsertParam::set_partition_tag(const char* value, size_t size) { - - partition_tag_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.HInsertParam.partition_tag) -} -inline std::string* HInsertParam::mutable_partition_tag() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.HInsertParam.partition_tag) - return partition_tag_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* HInsertParam::release_partition_tag() { - // @@protoc_insertion_point(field_release:milvus.grpc.HInsertParam.partition_tag) - - return partition_tag_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void HInsertParam::set_allocated_partition_tag(std::string* partition_tag) { - if (partition_tag != nullptr) { - - } else { - - } - partition_tag_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), partition_tag); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.HInsertParam.partition_tag) -} - -// .milvus.grpc.HEntity entities = 3; -inline bool HInsertParam::has_entities() const { - return this != internal_default_instance() && entities_ != nullptr; -} -inline void HInsertParam::clear_entities() { - if (GetArenaNoVirtual() == nullptr && entities_ != nullptr) { - delete entities_; - } - entities_ = nullptr; -} -inline const ::milvus::grpc::HEntity& HInsertParam::entities() const { - const ::milvus::grpc::HEntity* p = entities_; - // @@protoc_insertion_point(field_get:milvus.grpc.HInsertParam.entities) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_HEntity_default_instance_); -} -inline ::milvus::grpc::HEntity* HInsertParam::release_entities() { - // @@protoc_insertion_point(field_release:milvus.grpc.HInsertParam.entities) - - ::milvus::grpc::HEntity* temp = entities_; - entities_ = nullptr; - return temp; -} -inline ::milvus::grpc::HEntity* HInsertParam::mutable_entities() { - - if (entities_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::HEntity>(GetArenaNoVirtual()); - entities_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.HInsertParam.entities) - return entities_; -} -inline void HInsertParam::set_allocated_entities(::milvus::grpc::HEntity* entities) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete entities_; - } - if (entities) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - entities = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, entities, submessage_arena); - } - - } else { - - } - entities_ = entities; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.HInsertParam.entities) -} - -// repeated int64 entity_id_array = 4; -inline int HInsertParam::entity_id_array_size() const { - return entity_id_array_.size(); -} -inline void HInsertParam::clear_entity_id_array() { - entity_id_array_.Clear(); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 HInsertParam::entity_id_array(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.HInsertParam.entity_id_array) - return entity_id_array_.Get(index); -} -inline void HInsertParam::set_entity_id_array(int index, ::PROTOBUF_NAMESPACE_ID::int64 value) { - entity_id_array_.Set(index, value); - // @@protoc_insertion_point(field_set:milvus.grpc.HInsertParam.entity_id_array) -} -inline void HInsertParam::add_entity_id_array(::PROTOBUF_NAMESPACE_ID::int64 value) { - entity_id_array_.Add(value); - // @@protoc_insertion_point(field_add:milvus.grpc.HInsertParam.entity_id_array) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >& -HInsertParam::entity_id_array() const { - // @@protoc_insertion_point(field_list:milvus.grpc.HInsertParam.entity_id_array) - return entity_id_array_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >* -HInsertParam::mutable_entity_id_array() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.HInsertParam.entity_id_array) - return &entity_id_array_; -} - -// repeated .milvus.grpc.KeyValuePair extra_params = 5; -inline int HInsertParam::extra_params_size() const { - return extra_params_.size(); -} -inline void HInsertParam::clear_extra_params() { - extra_params_.Clear(); -} -inline ::milvus::grpc::KeyValuePair* HInsertParam::mutable_extra_params(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.HInsertParam.extra_params) - return extra_params_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* -HInsertParam::mutable_extra_params() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.HInsertParam.extra_params) - return &extra_params_; -} -inline const ::milvus::grpc::KeyValuePair& HInsertParam::extra_params(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.HInsertParam.extra_params) - return extra_params_.Get(index); -} -inline ::milvus::grpc::KeyValuePair* HInsertParam::add_extra_params() { - // @@protoc_insertion_point(field_add:milvus.grpc.HInsertParam.extra_params) - return extra_params_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& -HInsertParam::extra_params() const { - // @@protoc_insertion_point(field_list:milvus.grpc.HInsertParam.extra_params) - return extra_params_; -} - -// ------------------------------------------------------------------- - -// HEntityIdentity - -// string collection_name = 1; -inline void HEntityIdentity::clear_collection_name() { - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& HEntityIdentity::collection_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.HEntityIdentity.collection_name) - return collection_name_.GetNoArena(); -} -inline void HEntityIdentity::set_collection_name(const std::string& value) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.HEntityIdentity.collection_name) -} -inline void HEntityIdentity::set_collection_name(std::string&& value) { - - collection_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.HEntityIdentity.collection_name) -} -inline void HEntityIdentity::set_collection_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.HEntityIdentity.collection_name) -} -inline void HEntityIdentity::set_collection_name(const char* value, size_t size) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.HEntityIdentity.collection_name) -} -inline std::string* HEntityIdentity::mutable_collection_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.HEntityIdentity.collection_name) - return collection_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* HEntityIdentity::release_collection_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.HEntityIdentity.collection_name) - - return collection_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void HEntityIdentity::set_allocated_collection_name(std::string* collection_name) { - if (collection_name != nullptr) { - - } else { - - } - collection_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), collection_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.HEntityIdentity.collection_name) -} - -// int64 id = 2; -inline void HEntityIdentity::clear_id() { - id_ = PROTOBUF_LONGLONG(0); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 HEntityIdentity::id() const { - // @@protoc_insertion_point(field_get:milvus.grpc.HEntityIdentity.id) - return id_; -} -inline void HEntityIdentity::set_id(::PROTOBUF_NAMESPACE_ID::int64 value) { - - id_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.HEntityIdentity.id) -} - -// ------------------------------------------------------------------- - -// HEntityIDs - -// .milvus.grpc.Status status = 1; -inline bool HEntityIDs::has_status() const { - return this != internal_default_instance() && status_ != nullptr; -} -inline const ::milvus::grpc::Status& HEntityIDs::status() const { - const ::milvus::grpc::Status* p = status_; - // @@protoc_insertion_point(field_get:milvus.grpc.HEntityIDs.status) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_Status_default_instance_); -} -inline ::milvus::grpc::Status* HEntityIDs::release_status() { - // @@protoc_insertion_point(field_release:milvus.grpc.HEntityIDs.status) - - ::milvus::grpc::Status* temp = status_; - status_ = nullptr; - return temp; -} -inline ::milvus::grpc::Status* HEntityIDs::mutable_status() { - - if (status_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::Status>(GetArenaNoVirtual()); - status_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.HEntityIDs.status) - return status_; -} -inline void HEntityIDs::set_allocated_status(::milvus::grpc::Status* status) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(status_); - } - if (status) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - status = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, status, submessage_arena); - } - - } else { - - } - status_ = status; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.HEntityIDs.status) -} - -// repeated int64 entity_id_array = 2; -inline int HEntityIDs::entity_id_array_size() const { - return entity_id_array_.size(); -} -inline void HEntityIDs::clear_entity_id_array() { - entity_id_array_.Clear(); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 HEntityIDs::entity_id_array(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.HEntityIDs.entity_id_array) - return entity_id_array_.Get(index); -} -inline void HEntityIDs::set_entity_id_array(int index, ::PROTOBUF_NAMESPACE_ID::int64 value) { - entity_id_array_.Set(index, value); - // @@protoc_insertion_point(field_set:milvus.grpc.HEntityIDs.entity_id_array) -} -inline void HEntityIDs::add_entity_id_array(::PROTOBUF_NAMESPACE_ID::int64 value) { - entity_id_array_.Add(value); - // @@protoc_insertion_point(field_add:milvus.grpc.HEntityIDs.entity_id_array) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >& -HEntityIDs::entity_id_array() const { - // @@protoc_insertion_point(field_list:milvus.grpc.HEntityIDs.entity_id_array) - return entity_id_array_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >* -HEntityIDs::mutable_entity_id_array() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.HEntityIDs.entity_id_array) - return &entity_id_array_; -} - -// ------------------------------------------------------------------- - -// HGetEntityIDsParam - -// string collection_name = 1; -inline void HGetEntityIDsParam::clear_collection_name() { - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& HGetEntityIDsParam::collection_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.HGetEntityIDsParam.collection_name) - return collection_name_.GetNoArena(); -} -inline void HGetEntityIDsParam::set_collection_name(const std::string& value) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.HGetEntityIDsParam.collection_name) -} -inline void HGetEntityIDsParam::set_collection_name(std::string&& value) { - - collection_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.HGetEntityIDsParam.collection_name) -} -inline void HGetEntityIDsParam::set_collection_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.HGetEntityIDsParam.collection_name) -} -inline void HGetEntityIDsParam::set_collection_name(const char* value, size_t size) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.HGetEntityIDsParam.collection_name) -} -inline std::string* HGetEntityIDsParam::mutable_collection_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.HGetEntityIDsParam.collection_name) - return collection_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* HGetEntityIDsParam::release_collection_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.HGetEntityIDsParam.collection_name) - - return collection_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void HGetEntityIDsParam::set_allocated_collection_name(std::string* collection_name) { - if (collection_name != nullptr) { - - } else { - - } - collection_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), collection_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.HGetEntityIDsParam.collection_name) -} - -// string segment_name = 2; -inline void HGetEntityIDsParam::clear_segment_name() { - segment_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& HGetEntityIDsParam::segment_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.HGetEntityIDsParam.segment_name) - return segment_name_.GetNoArena(); -} -inline void HGetEntityIDsParam::set_segment_name(const std::string& value) { - - segment_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.HGetEntityIDsParam.segment_name) -} -inline void HGetEntityIDsParam::set_segment_name(std::string&& value) { - - segment_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.HGetEntityIDsParam.segment_name) -} -inline void HGetEntityIDsParam::set_segment_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - segment_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.HGetEntityIDsParam.segment_name) -} -inline void HGetEntityIDsParam::set_segment_name(const char* value, size_t size) { - - segment_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.HGetEntityIDsParam.segment_name) -} -inline std::string* HGetEntityIDsParam::mutable_segment_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.HGetEntityIDsParam.segment_name) - return segment_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* HGetEntityIDsParam::release_segment_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.HGetEntityIDsParam.segment_name) - - return segment_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void HGetEntityIDsParam::set_allocated_segment_name(std::string* segment_name) { - if (segment_name != nullptr) { - - } else { - - } - segment_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), segment_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.HGetEntityIDsParam.segment_name) -} - -// ------------------------------------------------------------------- - -// HDeleteByIDParam - -// string collection_name = 1; -inline void HDeleteByIDParam::clear_collection_name() { - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& HDeleteByIDParam::collection_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.HDeleteByIDParam.collection_name) - return collection_name_.GetNoArena(); -} -inline void HDeleteByIDParam::set_collection_name(const std::string& value) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.HDeleteByIDParam.collection_name) -} -inline void HDeleteByIDParam::set_collection_name(std::string&& value) { - - collection_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.HDeleteByIDParam.collection_name) -} -inline void HDeleteByIDParam::set_collection_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.HDeleteByIDParam.collection_name) -} -inline void HDeleteByIDParam::set_collection_name(const char* value, size_t size) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.HDeleteByIDParam.collection_name) -} -inline std::string* HDeleteByIDParam::mutable_collection_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.HDeleteByIDParam.collection_name) - return collection_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* HDeleteByIDParam::release_collection_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.HDeleteByIDParam.collection_name) - - return collection_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void HDeleteByIDParam::set_allocated_collection_name(std::string* collection_name) { - if (collection_name != nullptr) { - - } else { - - } - collection_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), collection_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.HDeleteByIDParam.collection_name) -} - -// repeated int64 id_array = 2; -inline int HDeleteByIDParam::id_array_size() const { - return id_array_.size(); -} -inline void HDeleteByIDParam::clear_id_array() { - id_array_.Clear(); -} -inline ::PROTOBUF_NAMESPACE_ID::int64 HDeleteByIDParam::id_array(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.HDeleteByIDParam.id_array) - return id_array_.Get(index); -} -inline void HDeleteByIDParam::set_id_array(int index, ::PROTOBUF_NAMESPACE_ID::int64 value) { - id_array_.Set(index, value); - // @@protoc_insertion_point(field_set:milvus.grpc.HDeleteByIDParam.id_array) -} -inline void HDeleteByIDParam::add_id_array(::PROTOBUF_NAMESPACE_ID::int64 value) { - id_array_.Add(value); - // @@protoc_insertion_point(field_add:milvus.grpc.HDeleteByIDParam.id_array) -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >& -HDeleteByIDParam::id_array() const { - // @@protoc_insertion_point(field_list:milvus.grpc.HDeleteByIDParam.id_array) - return id_array_; -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< ::PROTOBUF_NAMESPACE_ID::int64 >* -HDeleteByIDParam::mutable_id_array() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.HDeleteByIDParam.id_array) - return &id_array_; -} - -// ------------------------------------------------------------------- - -// HIndexParam - -// .milvus.grpc.Status status = 1; -inline bool HIndexParam::has_status() const { - return this != internal_default_instance() && status_ != nullptr; -} -inline const ::milvus::grpc::Status& HIndexParam::status() const { - const ::milvus::grpc::Status* p = status_; - // @@protoc_insertion_point(field_get:milvus.grpc.HIndexParam.status) - return p != nullptr ? *p : *reinterpret_cast( - &::milvus::grpc::_Status_default_instance_); -} -inline ::milvus::grpc::Status* HIndexParam::release_status() { - // @@protoc_insertion_point(field_release:milvus.grpc.HIndexParam.status) - - ::milvus::grpc::Status* temp = status_; - status_ = nullptr; - return temp; -} -inline ::milvus::grpc::Status* HIndexParam::mutable_status() { - - if (status_ == nullptr) { - auto* p = CreateMaybeMessage<::milvus::grpc::Status>(GetArenaNoVirtual()); - status_ = p; - } - // @@protoc_insertion_point(field_mutable:milvus.grpc.HIndexParam.status) - return status_; -} -inline void HIndexParam::set_allocated_status(::milvus::grpc::Status* status) { - ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaNoVirtual(); - if (message_arena == nullptr) { - delete reinterpret_cast< ::PROTOBUF_NAMESPACE_ID::MessageLite*>(status_); - } - if (status) { - ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = nullptr; - if (message_arena != submessage_arena) { - status = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( - message_arena, status, submessage_arena); - } - - } else { - - } - status_ = status; - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.HIndexParam.status) -} - -// string collection_name = 2; -inline void HIndexParam::clear_collection_name() { - collection_name_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& HIndexParam::collection_name() const { - // @@protoc_insertion_point(field_get:milvus.grpc.HIndexParam.collection_name) - return collection_name_.GetNoArena(); -} -inline void HIndexParam::set_collection_name(const std::string& value) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.HIndexParam.collection_name) -} -inline void HIndexParam::set_collection_name(std::string&& value) { - - collection_name_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.HIndexParam.collection_name) -} -inline void HIndexParam::set_collection_name(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.HIndexParam.collection_name) -} -inline void HIndexParam::set_collection_name(const char* value, size_t size) { - - collection_name_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.HIndexParam.collection_name) -} -inline std::string* HIndexParam::mutable_collection_name() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.HIndexParam.collection_name) - return collection_name_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* HIndexParam::release_collection_name() { - // @@protoc_insertion_point(field_release:milvus.grpc.HIndexParam.collection_name) - - return collection_name_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void HIndexParam::set_allocated_collection_name(std::string* collection_name) { - if (collection_name != nullptr) { - - } else { - - } - collection_name_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), collection_name); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.HIndexParam.collection_name) -} - -// int32 index_type = 3; -inline void HIndexParam::clear_index_type() { - index_type_ = 0; -} -inline ::PROTOBUF_NAMESPACE_ID::int32 HIndexParam::index_type() const { - // @@protoc_insertion_point(field_get:milvus.grpc.HIndexParam.index_type) - return index_type_; -} -inline void HIndexParam::set_index_type(::PROTOBUF_NAMESPACE_ID::int32 value) { - - index_type_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.HIndexParam.index_type) -} - -// repeated .milvus.grpc.KeyValuePair extra_params = 4; -inline int HIndexParam::extra_params_size() const { - return extra_params_.size(); -} -inline void HIndexParam::clear_extra_params() { - extra_params_.Clear(); -} -inline ::milvus::grpc::KeyValuePair* HIndexParam::mutable_extra_params(int index) { - // @@protoc_insertion_point(field_mutable:milvus.grpc.HIndexParam.extra_params) - return extra_params_.Mutable(index); -} -inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >* -HIndexParam::mutable_extra_params() { - // @@protoc_insertion_point(field_mutable_list:milvus.grpc.HIndexParam.extra_params) - return &extra_params_; -} -inline const ::milvus::grpc::KeyValuePair& HIndexParam::extra_params(int index) const { - // @@protoc_insertion_point(field_get:milvus.grpc.HIndexParam.extra_params) - return extra_params_.Get(index); -} -inline ::milvus::grpc::KeyValuePair* HIndexParam::add_extra_params() { - // @@protoc_insertion_point(field_add:milvus.grpc.HIndexParam.extra_params) - return extra_params_.Add(); -} -inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::milvus::grpc::KeyValuePair >& -HIndexParam::extra_params() const { - // @@protoc_insertion_point(field_list:milvus.grpc.HIndexParam.extra_params) - return extra_params_; -} - -#ifdef __GNUC__ - #pragma GCC diagnostic pop -#endif // __GNUC__ -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - -// ------------------------------------------------------------------- - - -// @@protoc_insertion_point(namespace_scope) - -} // namespace grpc -} // namespace milvus - -PROTOBUF_NAMESPACE_OPEN - -template <> struct is_proto_enum< ::milvus::grpc::DataType> : ::std::true_type {}; -template <> -inline const EnumDescriptor* GetEnumDescriptor< ::milvus::grpc::DataType>() { - return ::milvus::grpc::DataType_descriptor(); -} -template <> struct is_proto_enum< ::milvus::grpc::CompareOperator> : ::std::true_type {}; -template <> -inline const EnumDescriptor* GetEnumDescriptor< ::milvus::grpc::CompareOperator>() { - return ::milvus::grpc::CompareOperator_descriptor(); -} -template <> struct is_proto_enum< ::milvus::grpc::Occur> : ::std::true_type {}; -template <> -inline const EnumDescriptor* GetEnumDescriptor< ::milvus::grpc::Occur>() { - return ::milvus::grpc::Occur_descriptor(); -} - -PROTOBUF_NAMESPACE_CLOSE - -// @@protoc_insertion_point(global_scope) - -#include -#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_milvus_2eproto diff --git a/core/src/grpc/gen-status/status.grpc.pb.cc b/core/src/grpc/gen-status/status.grpc.pb.cc deleted file mode 100644 index de871f5413..0000000000 --- a/core/src/grpc/gen-status/status.grpc.pb.cc +++ /dev/null @@ -1,24 +0,0 @@ -// Generated by the gRPC C++ plugin. -// If you make any local change, they will be lost. -// source: status.proto - -#include "status.pb.h" -#include "status.grpc.pb.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -namespace milvus { -namespace grpc { - -} // namespace milvus -} // namespace grpc - diff --git a/core/src/grpc/gen-status/status.grpc.pb.h b/core/src/grpc/gen-status/status.grpc.pb.h deleted file mode 100644 index 2dcedb74de..0000000000 --- a/core/src/grpc/gen-status/status.grpc.pb.h +++ /dev/null @@ -1,46 +0,0 @@ -// Generated by the gRPC C++ plugin. -// If you make any local change, they will be lost. -// source: status.proto -#ifndef GRPC_status_2eproto__INCLUDED -#define GRPC_status_2eproto__INCLUDED - -#include "status.pb.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace grpc_impl { -class CompletionQueue; -class ServerCompletionQueue; -class ServerContext; -} // namespace grpc_impl - -namespace grpc { -namespace experimental { -template -class MessageAllocator; -} // namespace experimental -} // namespace grpc - -namespace milvus { -namespace grpc { - -} // namespace grpc -} // namespace milvus - - -#endif // GRPC_status_2eproto__INCLUDED diff --git a/core/src/grpc/gen-status/status.pb.cc b/core/src/grpc/gen-status/status.pb.cc deleted file mode 100644 index 32aedec9d2..0000000000 --- a/core/src/grpc/gen-status/status.pb.cc +++ /dev/null @@ -1,461 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: status.proto - -#include "status.pb.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -// @@protoc_insertion_point(includes) -#include -namespace milvus { -namespace grpc { -class StatusDefaultTypeInternal { - public: - ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; -} _Status_default_instance_; -} // namespace grpc -} // namespace milvus -static void InitDefaultsscc_info_Status_status_2eproto() { - GOOGLE_PROTOBUF_VERIFY_VERSION; - - { - void* ptr = &::milvus::grpc::_Status_default_instance_; - new (ptr) ::milvus::grpc::Status(); - ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); - } - ::milvus::grpc::Status::InitAsDefaultInstance(); -} - -::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_Status_status_2eproto = - {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, InitDefaultsscc_info_Status_status_2eproto}, {}}; - -static ::PROTOBUF_NAMESPACE_ID::Metadata file_level_metadata_status_2eproto[1]; -static const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* file_level_enum_descriptors_status_2eproto[1]; -static constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_status_2eproto = nullptr; - -const ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_status_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { - ~0u, // no _has_bits_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::Status, _internal_metadata_), - ~0u, // no _extensions_ - ~0u, // no _oneof_case_ - ~0u, // no _weak_field_map_ - PROTOBUF_FIELD_OFFSET(::milvus::grpc::Status, error_code_), - PROTOBUF_FIELD_OFFSET(::milvus::grpc::Status, reason_), -}; -static const ::PROTOBUF_NAMESPACE_ID::internal::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { - { 0, -1, sizeof(::milvus::grpc::Status)}, -}; - -static ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = { - reinterpret_cast(&::milvus::grpc::_Status_default_instance_), -}; - -const char descriptor_table_protodef_status_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = - "\n\014status.proto\022\013milvus.grpc\"D\n\006Status\022*\n" - "\nerror_code\030\001 \001(\0162\026.milvus.grpc.ErrorCod" - "e\022\016\n\006reason\030\002 \001(\t*\242\004\n\tErrorCode\022\013\n\007SUCCE" - "SS\020\000\022\024\n\020UNEXPECTED_ERROR\020\001\022\022\n\016CONNECT_FA" - "ILED\020\002\022\025\n\021PERMISSION_DENIED\020\003\022\031\n\025COLLECT" - "ION_NOT_EXISTS\020\004\022\024\n\020ILLEGAL_ARGUMENT\020\005\022\025" - "\n\021ILLEGAL_DIMENSION\020\007\022\026\n\022ILLEGAL_INDEX_T" - "YPE\020\010\022\033\n\027ILLEGAL_COLLECTION_NAME\020\t\022\020\n\014IL" - "LEGAL_TOPK\020\n\022\025\n\021ILLEGAL_ROWRECORD\020\013\022\025\n\021I" - "LLEGAL_VECTOR_ID\020\014\022\031\n\025ILLEGAL_SEARCH_RES" - "ULT\020\r\022\022\n\016FILE_NOT_FOUND\020\016\022\017\n\013META_FAILED" - "\020\017\022\020\n\014CACHE_FAILED\020\020\022\030\n\024CANNOT_CREATE_FO" - "LDER\020\021\022\026\n\022CANNOT_CREATE_FILE\020\022\022\030\n\024CANNOT" - "_DELETE_FOLDER\020\023\022\026\n\022CANNOT_DELETE_FILE\020\024" - "\022\025\n\021BUILD_INDEX_ERROR\020\025\022\021\n\rILLEGAL_NLIST" - "\020\026\022\027\n\023ILLEGAL_METRIC_TYPE\020\027\022\021\n\rOUT_OF_ME" - "MORY\020\030b\006proto3" - ; -static const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_status_2eproto_deps[1] = { -}; -static ::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase*const descriptor_table_status_2eproto_sccs[1] = { - &scc_info_Status_status_2eproto.base, -}; -static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_status_2eproto_once; -static bool descriptor_table_status_2eproto_initialized = false; -const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_status_2eproto = { - &descriptor_table_status_2eproto_initialized, descriptor_table_protodef_status_2eproto, "status.proto", 654, - &descriptor_table_status_2eproto_once, descriptor_table_status_2eproto_sccs, descriptor_table_status_2eproto_deps, 1, 0, - schemas, file_default_instances, TableStruct_status_2eproto::offsets, - file_level_metadata_status_2eproto, 1, file_level_enum_descriptors_status_2eproto, file_level_service_descriptors_status_2eproto, -}; - -// Force running AddDescriptors() at dynamic initialization time. -static bool dynamic_init_dummy_status_2eproto = ( ::PROTOBUF_NAMESPACE_ID::internal::AddDescriptors(&descriptor_table_status_2eproto), true); -namespace milvus { -namespace grpc { -const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* ErrorCode_descriptor() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_status_2eproto); - return file_level_enum_descriptors_status_2eproto[0]; -} -bool ErrorCode_IsValid(int value) { - switch (value) { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 7: - case 8: - case 9: - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - case 16: - case 17: - case 18: - case 19: - case 20: - case 21: - case 22: - case 23: - case 24: - return true; - default: - return false; - } -} - - -// =================================================================== - -void Status::InitAsDefaultInstance() { -} -class Status::_Internal { - public: -}; - -Status::Status() - : ::PROTOBUF_NAMESPACE_ID::Message(), _internal_metadata_(nullptr) { - SharedCtor(); - // @@protoc_insertion_point(constructor:milvus.grpc.Status) -} -Status::Status(const Status& from) - : ::PROTOBUF_NAMESPACE_ID::Message(), - _internal_metadata_(nullptr) { - _internal_metadata_.MergeFrom(from._internal_metadata_); - reason_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - if (!from.reason().empty()) { - reason_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.reason_); - } - error_code_ = from.error_code_; - // @@protoc_insertion_point(copy_constructor:milvus.grpc.Status) -} - -void Status::SharedCtor() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_Status_status_2eproto.base); - reason_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - error_code_ = 0; -} - -Status::~Status() { - // @@protoc_insertion_point(destructor:milvus.grpc.Status) - SharedDtor(); -} - -void Status::SharedDtor() { - reason_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} - -void Status::SetCachedSize(int size) const { - _cached_size_.Set(size); -} -const Status& Status::default_instance() { - ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_Status_status_2eproto.base); - return *internal_default_instance(); -} - - -void Status::Clear() { -// @@protoc_insertion_point(message_clear_start:milvus.grpc.Status) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - reason_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); - error_code_ = 0; - _internal_metadata_.Clear(); -} - -#if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -const char* Status::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { -#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure - while (!ctx->Done(&ptr)) { - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); - CHK_(ptr); - switch (tag >> 3) { - // .milvus.grpc.ErrorCode error_code = 1; - case 1: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 8)) { - ::PROTOBUF_NAMESPACE_ID::uint64 val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint(&ptr); - CHK_(ptr); - set_error_code(static_cast<::milvus::grpc::ErrorCode>(val)); - } else goto handle_unusual; - continue; - // string reason = 2; - case 2: - if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { - ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParserUTF8(mutable_reason(), ptr, ctx, "milvus.grpc.Status.reason"); - CHK_(ptr); - } else goto handle_unusual; - continue; - default: { - handle_unusual: - if ((tag & 7) == 4 || tag == 0) { - ctx->SetLastTag(tag); - goto success; - } - ptr = UnknownFieldParse(tag, &_internal_metadata_, ptr, ctx); - CHK_(ptr != nullptr); - continue; - } - } // switch - } // while -success: - return ptr; -failure: - ptr = nullptr; - goto success; -#undef CHK_ -} -#else // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER -bool Status::MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) { -#define DO_(EXPRESSION) if (!PROTOBUF_PREDICT_TRUE(EXPRESSION)) goto failure - ::PROTOBUF_NAMESPACE_ID::uint32 tag; - // @@protoc_insertion_point(parse_start:milvus.grpc.Status) - for (;;) { - ::std::pair<::PROTOBUF_NAMESPACE_ID::uint32, bool> p = input->ReadTagWithCutoffNoLastTag(127u); - tag = p.first; - if (!p.second) goto handle_unusual; - switch (::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::GetTagFieldNumber(tag)) { - // .milvus.grpc.ErrorCode error_code = 1; - case 1: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (8 & 0xFF)) { - int value = 0; - DO_((::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadPrimitive< - int, ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::TYPE_ENUM>( - input, &value))); - set_error_code(static_cast< ::milvus::grpc::ErrorCode >(value)); - } else { - goto handle_unusual; - } - break; - } - - // string reason = 2; - case 2: { - if (static_cast< ::PROTOBUF_NAMESPACE_ID::uint8>(tag) == (18 & 0xFF)) { - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::ReadString( - input, this->mutable_reason())); - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->reason().data(), static_cast(this->reason().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::PARSE, - "milvus.grpc.Status.reason")); - } else { - goto handle_unusual; - } - break; - } - - default: { - handle_unusual: - if (tag == 0) { - goto success; - } - DO_(::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SkipField( - input, tag, _internal_metadata_.mutable_unknown_fields())); - break; - } - } - } -success: - // @@protoc_insertion_point(parse_success:milvus.grpc.Status) - return true; -failure: - // @@protoc_insertion_point(parse_failure:milvus.grpc.Status) - return false; -#undef DO_ -} -#endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - -void Status::SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const { - // @@protoc_insertion_point(serialize_start:milvus.grpc.Status) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.ErrorCode error_code = 1; - if (this->error_code() != 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnum( - 1, this->error_code(), output); - } - - // string reason = 2; - if (this->reason().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->reason().data(), static_cast(this->reason().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.Status.reason"); - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringMaybeAliased( - 2, this->reason(), output); - } - - if (_internal_metadata_.have_unknown_fields()) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFields( - _internal_metadata_.unknown_fields(), output); - } - // @@protoc_insertion_point(serialize_end:milvus.grpc.Status) -} - -::PROTOBUF_NAMESPACE_ID::uint8* Status::InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const { - // @@protoc_insertion_point(serialize_to_array_start:milvus.grpc.Status) - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - // .milvus.grpc.ErrorCode error_code = 1; - if (this->error_code() != 0) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray( - 1, this->error_code(), target); - } - - // string reason = 2; - if (this->reason().size() > 0) { - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( - this->reason().data(), static_cast(this->reason().length()), - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, - "milvus.grpc.Status.reason"); - target = - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteStringToArray( - 2, this->reason(), target); - } - - if (_internal_metadata_.have_unknown_fields()) { - target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SerializeUnknownFieldsToArray( - _internal_metadata_.unknown_fields(), target); - } - // @@protoc_insertion_point(serialize_to_array_end:milvus.grpc.Status) - return target; -} - -size_t Status::ByteSizeLong() const { -// @@protoc_insertion_point(message_byte_size_start:milvus.grpc.Status) - size_t total_size = 0; - - if (_internal_metadata_.have_unknown_fields()) { - total_size += - ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::ComputeUnknownFieldsSize( - _internal_metadata_.unknown_fields()); - } - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - // Prevent compiler warnings about cached_has_bits being unused - (void) cached_has_bits; - - // string reason = 2; - if (this->reason().size() > 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( - this->reason()); - } - - // .milvus.grpc.ErrorCode error_code = 1; - if (this->error_code() != 0) { - total_size += 1 + - ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->error_code()); - } - - int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); - SetCachedSize(cached_size); - return total_size; -} - -void Status::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_merge_from_start:milvus.grpc.Status) - GOOGLE_DCHECK_NE(&from, this); - const Status* source = - ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( - &from); - if (source == nullptr) { - // @@protoc_insertion_point(generalized_merge_from_cast_fail:milvus.grpc.Status) - ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); - } else { - // @@protoc_insertion_point(generalized_merge_from_cast_success:milvus.grpc.Status) - MergeFrom(*source); - } -} - -void Status::MergeFrom(const Status& from) { -// @@protoc_insertion_point(class_specific_merge_from_start:milvus.grpc.Status) - GOOGLE_DCHECK_NE(&from, this); - _internal_metadata_.MergeFrom(from._internal_metadata_); - ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; - (void) cached_has_bits; - - if (from.reason().size() > 0) { - - reason_.AssignWithDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from.reason_); - } - if (from.error_code() != 0) { - set_error_code(from.error_code()); - } -} - -void Status::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { -// @@protoc_insertion_point(generalized_copy_from_start:milvus.grpc.Status) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -void Status::CopyFrom(const Status& from) { -// @@protoc_insertion_point(class_specific_copy_from_start:milvus.grpc.Status) - if (&from == this) return; - Clear(); - MergeFrom(from); -} - -bool Status::IsInitialized() const { - return true; -} - -void Status::InternalSwap(Status* other) { - using std::swap; - _internal_metadata_.Swap(&other->_internal_metadata_); - reason_.Swap(&other->reason_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - GetArenaNoVirtual()); - swap(error_code_, other->error_code_); -} - -::PROTOBUF_NAMESPACE_ID::Metadata Status::GetMetadata() const { - return GetMetadataStatic(); -} - - -// @@protoc_insertion_point(namespace_scope) -} // namespace grpc -} // namespace milvus -PROTOBUF_NAMESPACE_OPEN -template<> PROTOBUF_NOINLINE ::milvus::grpc::Status* Arena::CreateMaybeMessage< ::milvus::grpc::Status >(Arena* arena) { - return Arena::CreateInternal< ::milvus::grpc::Status >(arena); -} -PROTOBUF_NAMESPACE_CLOSE - -// @@protoc_insertion_point(global_scope) -#include diff --git a/core/src/grpc/gen-status/status.pb.h b/core/src/grpc/gen-status/status.pb.h deleted file mode 100644 index 94be8740cc..0000000000 --- a/core/src/grpc/gen-status/status.pb.h +++ /dev/null @@ -1,360 +0,0 @@ -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: status.proto - -#ifndef GOOGLE_PROTOBUF_INCLUDED_status_2eproto -#define GOOGLE_PROTOBUF_INCLUDED_status_2eproto - -#include -#include - -#include -#if PROTOBUF_VERSION < 3009000 -#error This file was generated by a newer version of protoc which is -#error incompatible with your Protocol Buffer headers. Please update -#error your headers. -#endif -#if 3009000 < PROTOBUF_MIN_PROTOC_VERSION -#error This file was generated by an older version of protoc which is -#error incompatible with your Protocol Buffer headers. Please -#error regenerate this file with a newer version of protoc. -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // IWYU pragma: export -#include // IWYU pragma: export -#include -#include -// @@protoc_insertion_point(includes) -#include -#define PROTOBUF_INTERNAL_EXPORT_status_2eproto -PROTOBUF_NAMESPACE_OPEN -namespace internal { -class AnyMetadata; -} // namespace internal -PROTOBUF_NAMESPACE_CLOSE - -// Internal implementation detail -- do not use these members. -struct TableStruct_status_2eproto { - static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[] - PROTOBUF_SECTION_VARIABLE(protodesc_cold); - static const ::PROTOBUF_NAMESPACE_ID::internal::AuxillaryParseTableField aux[] - PROTOBUF_SECTION_VARIABLE(protodesc_cold); - static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[1] - PROTOBUF_SECTION_VARIABLE(protodesc_cold); - static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[]; - static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[]; - static const ::PROTOBUF_NAMESPACE_ID::uint32 offsets[]; -}; -extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_status_2eproto; -namespace milvus { -namespace grpc { -class Status; -class StatusDefaultTypeInternal; -extern StatusDefaultTypeInternal _Status_default_instance_; -} // namespace grpc -} // namespace milvus -PROTOBUF_NAMESPACE_OPEN -template<> ::milvus::grpc::Status* Arena::CreateMaybeMessage<::milvus::grpc::Status>(Arena*); -PROTOBUF_NAMESPACE_CLOSE -namespace milvus { -namespace grpc { - -enum ErrorCode : int { - SUCCESS = 0, - UNEXPECTED_ERROR = 1, - CONNECT_FAILED = 2, - PERMISSION_DENIED = 3, - COLLECTION_NOT_EXISTS = 4, - ILLEGAL_ARGUMENT = 5, - ILLEGAL_DIMENSION = 7, - ILLEGAL_INDEX_TYPE = 8, - ILLEGAL_COLLECTION_NAME = 9, - ILLEGAL_TOPK = 10, - ILLEGAL_ROWRECORD = 11, - ILLEGAL_VECTOR_ID = 12, - ILLEGAL_SEARCH_RESULT = 13, - FILE_NOT_FOUND = 14, - META_FAILED = 15, - CACHE_FAILED = 16, - CANNOT_CREATE_FOLDER = 17, - CANNOT_CREATE_FILE = 18, - CANNOT_DELETE_FOLDER = 19, - CANNOT_DELETE_FILE = 20, - BUILD_INDEX_ERROR = 21, - ILLEGAL_NLIST = 22, - ILLEGAL_METRIC_TYPE = 23, - OUT_OF_MEMORY = 24, - ErrorCode_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::min(), - ErrorCode_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::max() -}; -bool ErrorCode_IsValid(int value); -constexpr ErrorCode ErrorCode_MIN = SUCCESS; -constexpr ErrorCode ErrorCode_MAX = OUT_OF_MEMORY; -constexpr int ErrorCode_ARRAYSIZE = ErrorCode_MAX + 1; - -const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* ErrorCode_descriptor(); -template -inline const std::string& ErrorCode_Name(T enum_t_value) { - static_assert(::std::is_same::value || - ::std::is_integral::value, - "Incorrect type passed to function ErrorCode_Name."); - return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum( - ErrorCode_descriptor(), enum_t_value); -} -inline bool ErrorCode_Parse( - const std::string& name, ErrorCode* value) { - return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum( - ErrorCode_descriptor(), name, value); -} -// =================================================================== - -class Status : - public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:milvus.grpc.Status) */ { - public: - Status(); - virtual ~Status(); - - Status(const Status& from); - Status(Status&& from) noexcept - : Status() { - *this = ::std::move(from); - } - - inline Status& operator=(const Status& from) { - CopyFrom(from); - return *this; - } - inline Status& operator=(Status&& from) noexcept { - if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) { - if (this != &from) InternalSwap(&from); - } else { - CopyFrom(from); - } - return *this; - } - - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { - return GetDescriptor(); - } - static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { - return GetMetadataStatic().descriptor; - } - static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { - return GetMetadataStatic().reflection; - } - static const Status& default_instance(); - - static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY - static inline const Status* internal_default_instance() { - return reinterpret_cast( - &_Status_default_instance_); - } - static constexpr int kIndexInFileMessages = - 0; - - friend void swap(Status& a, Status& b) { - a.Swap(&b); - } - inline void Swap(Status* other) { - if (other == this) return; - InternalSwap(other); - } - - // implements Message ---------------------------------------------- - - inline Status* New() const final { - return CreateMaybeMessage(nullptr); - } - - Status* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { - return CreateMaybeMessage(arena); - } - void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; - void CopyFrom(const Status& from); - void MergeFrom(const Status& from); - PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; - bool IsInitialized() const final; - - size_t ByteSizeLong() const final; - #if GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; - #else - bool MergePartialFromCodedStream( - ::PROTOBUF_NAMESPACE_ID::io::CodedInputStream* input) final; - #endif // GOOGLE_PROTOBUF_ENABLE_EXPERIMENTAL_PARSER - void SerializeWithCachedSizes( - ::PROTOBUF_NAMESPACE_ID::io::CodedOutputStream* output) const final; - ::PROTOBUF_NAMESPACE_ID::uint8* InternalSerializeWithCachedSizesToArray( - ::PROTOBUF_NAMESPACE_ID::uint8* target) const final; - int GetCachedSize() const final { return _cached_size_.Get(); } - - private: - inline void SharedCtor(); - inline void SharedDtor(); - void SetCachedSize(int size) const final; - void InternalSwap(Status* other); - friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; - static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { - return "milvus.grpc.Status"; - } - private: - inline ::PROTOBUF_NAMESPACE_ID::Arena* GetArenaNoVirtual() const { - return nullptr; - } - inline void* MaybeArenaPtr() const { - return nullptr; - } - public: - - ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; - private: - static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { - ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_status_2eproto); - return ::descriptor_table_status_2eproto.file_level_metadata[kIndexInFileMessages]; - } - - public: - - // nested types ---------------------------------------------------- - - // accessors ------------------------------------------------------- - - enum : int { - kReasonFieldNumber = 2, - kErrorCodeFieldNumber = 1, - }; - // string reason = 2; - void clear_reason(); - const std::string& reason() const; - void set_reason(const std::string& value); - void set_reason(std::string&& value); - void set_reason(const char* value); - void set_reason(const char* value, size_t size); - std::string* mutable_reason(); - std::string* release_reason(); - void set_allocated_reason(std::string* reason); - - // .milvus.grpc.ErrorCode error_code = 1; - void clear_error_code(); - ::milvus::grpc::ErrorCode error_code() const; - void set_error_code(::milvus::grpc::ErrorCode value); - - // @@protoc_insertion_point(class_scope:milvus.grpc.Status) - private: - class _Internal; - - ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; - ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr reason_; - int error_code_; - mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; - friend struct ::TableStruct_status_2eproto; -}; -// =================================================================== - - -// =================================================================== - -#ifdef __GNUC__ - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wstrict-aliasing" -#endif // __GNUC__ -// Status - -// .milvus.grpc.ErrorCode error_code = 1; -inline void Status::clear_error_code() { - error_code_ = 0; -} -inline ::milvus::grpc::ErrorCode Status::error_code() const { - // @@protoc_insertion_point(field_get:milvus.grpc.Status.error_code) - return static_cast< ::milvus::grpc::ErrorCode >(error_code_); -} -inline void Status::set_error_code(::milvus::grpc::ErrorCode value) { - - error_code_ = value; - // @@protoc_insertion_point(field_set:milvus.grpc.Status.error_code) -} - -// string reason = 2; -inline void Status::clear_reason() { - reason_.ClearToEmptyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline const std::string& Status::reason() const { - // @@protoc_insertion_point(field_get:milvus.grpc.Status.reason) - return reason_.GetNoArena(); -} -inline void Status::set_reason(const std::string& value) { - - reason_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value); - // @@protoc_insertion_point(field_set:milvus.grpc.Status.reason) -} -inline void Status::set_reason(std::string&& value) { - - reason_.SetNoArena( - &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value)); - // @@protoc_insertion_point(field_set_rvalue:milvus.grpc.Status.reason) -} -inline void Status::set_reason(const char* value) { - GOOGLE_DCHECK(value != nullptr); - - reason_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); - // @@protoc_insertion_point(field_set_char:milvus.grpc.Status.reason) -} -inline void Status::set_reason(const char* value, size_t size) { - - reason_.SetNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), - ::std::string(reinterpret_cast(value), size)); - // @@protoc_insertion_point(field_set_pointer:milvus.grpc.Status.reason) -} -inline std::string* Status::mutable_reason() { - - // @@protoc_insertion_point(field_mutable:milvus.grpc.Status.reason) - return reason_.MutableNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline std::string* Status::release_reason() { - // @@protoc_insertion_point(field_release:milvus.grpc.Status.reason) - - return reason_.ReleaseNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); -} -inline void Status::set_allocated_reason(std::string* reason) { - if (reason != nullptr) { - - } else { - - } - reason_.SetAllocatedNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), reason); - // @@protoc_insertion_point(field_set_allocated:milvus.grpc.Status.reason) -} - -#ifdef __GNUC__ - #pragma GCC diagnostic pop -#endif // __GNUC__ - -// @@protoc_insertion_point(namespace_scope) - -} // namespace grpc -} // namespace milvus - -PROTOBUF_NAMESPACE_OPEN - -template <> struct is_proto_enum< ::milvus::grpc::ErrorCode> : ::std::true_type {}; -template <> -inline const EnumDescriptor* GetEnumDescriptor< ::milvus::grpc::ErrorCode>() { - return ::milvus::grpc::ErrorCode_descriptor(); -} - -PROTOBUF_NAMESPACE_CLOSE - -// @@protoc_insertion_point(global_scope) - -#include -#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_status_2eproto diff --git a/core/src/grpc/milvus.proto b/core/src/grpc/milvus.proto deleted file mode 100644 index b818171096..0000000000 --- a/core/src/grpc/milvus.proto +++ /dev/null @@ -1,704 +0,0 @@ -syntax = "proto3"; - -import "status.proto"; - -package milvus.grpc; - -/** - * @brief general usage - */ -message KeyValuePair { - string key = 1; - string value = 2; -} - -/** - * @brief Collection name - */ -message CollectionName { - string collection_name = 1; -} - -/** - * @brief Collection name list - */ -message CollectionNameList { - Status status = 1; - repeated string collection_names = 2; -} - -/** - * @brief Collection schema - * metric_type: 1-L2, 2-IP - */ -message CollectionSchema { - Status status = 1; - string collection_name = 2; - int64 dimension = 3; - int64 index_file_size = 4; - int32 metric_type = 5; - repeated KeyValuePair extra_params = 6; -} - -/** - * @brief Params of partition - */ -message PartitionParam { - string collection_name = 1; - string tag = 2; -} - -/** - * @brief Partition list - */ -message PartitionList { - Status status = 1; - repeated string partition_tag_array = 2; -} - -/** - * @brief Record inserted - */ -message RowRecord { - repeated float float_data = 1; //float vector data - bytes binary_data = 2; //binary vector data -} - -/** - * @brief Params to be inserted - */ -message InsertParam { - string collection_name = 1; - repeated RowRecord row_record_array = 2; - repeated int64 row_id_array = 3; //optional - string partition_tag = 4; - repeated KeyValuePair extra_params = 5; -} - -/** - * @brief Vector ids - */ -message VectorIds { - Status status = 1; - repeated int64 vector_id_array = 2; -} - -/** - * @brief Params for searching vector - */ -message SearchParam { - string collection_name = 1; - repeated string partition_tag_array = 2; - repeated RowRecord query_record_array = 3; - int64 topk = 4; - repeated KeyValuePair extra_params = 5; -} - -/** - * @brief Params for searching vector in files - */ -message SearchInFilesParam { - repeated string file_id_array = 1; - SearchParam search_param = 2; -} - -/** - * @brief Params for searching vector by ID - */ -message SearchByIDParam { - string collection_name = 1; - repeated string partition_tag_array = 2; - repeated int64 id_array = 3; - int64 topk = 4; - repeated KeyValuePair extra_params = 5; -} - -/** - * @brief Params for reloading segments - */ -message ReLoadSegmentsParam { - string collection_name = 1; - repeated string segment_id_array = 2; -} - -/** - * @brief Query result params - */ -message TopKQueryResult { - Status status = 1; - int64 row_num = 2; - repeated int64 ids = 3; - repeated float distances = 4; -} - -/** - * @brief Server string Reply - */ -message StringReply { - Status status = 1; - string string_reply = 2; -} - -/** - * @brief Server bool Reply - */ -message BoolReply { - Status status = 1; - bool bool_reply = 2; -} - -/** - * @brief Return collection row count - */ -message CollectionRowCount { - Status status = 1; - int64 collection_row_count = 2; -} - -/** - * @brief Give server Command - */ -message Command { - string cmd = 1; -} - -/** - * @brief Index params - * @index_type: 0-invalid, 1-idmap, 2-ivflat, 3-ivfsq8, 4-nsgmix - */ -message IndexParam { - Status status = 1; - string collection_name = 2; - int32 index_type = 3; - repeated KeyValuePair extra_params = 4; -} - -/** - * @brief Flush params - */ -message FlushParam { - repeated string collection_name_array = 1; -} - -/** - * @brief Flush params - */ -message DeleteByIDParam { - string collection_name = 1; - repeated int64 id_array = 2; -} - -/** - * @brief collection information - */ -message CollectionInfo { - Status status = 1; - string json_info = 2; -} - -/** - * @brief vectors identity - */ -message VectorsIdentity { - string collection_name = 1; - repeated int64 id_array = 2; -} - -/** - * @brief vector data - */ -message VectorsData { - Status status = 1; - repeated RowRecord vectors_data = 2; -} - -/** - * @brief get vector ids from a segment parameters - */ -message GetVectorIDsParam { - string collection_name = 1; - string segment_name = 2; -} - - -/********************************************************************************************************************/ - -enum DataType { - NULL = 0; - INT8 = 1; - INT16 = 2; - INT32 = 3; - INT64 = 4; - - STRING = 20; - - BOOL = 30; - - FLOAT = 40; - DOUBLE = 41; - - VECTOR = 100; - UNKNOWN = 9999; -} - -/////////////////////////////////////////////////////////////////// - -message VectorFieldParam { - int64 dimension = 1; -} - -message FieldType { - oneof value { - DataType data_type = 1; - VectorFieldParam vector_param = 2; - } -} - -message FieldParam { - uint64 id = 1; - string name = 2; - FieldType type = 3; - repeated KeyValuePair extra_params = 4; -} - -message VectorFieldValue { - repeated RowRecord value = 1; -} - -message FieldValue { - oneof value { - int32 int32_value = 1; - int64 int64_value = 2; - float float_value = 3; - double double_value = 4; - string string_value = 5; - bool bool_value = 6; - VectorFieldValue vector_value = 7; - } -} - -/////////////////////////////////////////////////////////////////// - -message Mapping { - Status status = 1; - uint64 collection_id = 2; - string collection_name = 3; - repeated FieldParam fields = 4; -} - -message MappingList { - Status status = 1; - repeated Mapping mapping_list = 2; -} - -/////////////////////////////////////////////////////////////////// - -message TermQuery { - string field_name = 1; - bytes values = 2; - int64 value_num = 3; - float boost = 4; - repeated KeyValuePair extra_params = 5; -} - -enum CompareOperator { - LT = 0; - LTE = 1; - EQ = 2; - GT = 3; - GTE = 4; - NE = 5; -} - -message CompareExpr { - CompareOperator operator = 1; - string operand = 2; -} - -message RangeQuery { - string field_name = 1; - repeated CompareExpr operand = 2; - float boost = 3; - repeated KeyValuePair extra_params = 4; -} - -message VectorQuery { - string field_name = 1; - float query_boost = 2; - repeated RowRecord records = 3; - int64 topk = 4; - repeated KeyValuePair extra_params = 5; -} - -enum Occur { - INVALID = 0; - MUST = 1; - SHOULD = 2; - MUST_NOT = 3; -} - -message BooleanQuery { - Occur occur = 1; - repeated GeneralQuery general_query = 2; -} - -message GeneralQuery { - oneof query { - BooleanQuery boolean_query = 1; - TermQuery term_query = 2; - RangeQuery range_query = 3; - VectorQuery vector_query = 4; - } -} - -message HSearchParam { - string collection_name = 1; - repeated string partition_tag_array = 2; - GeneralQuery general_query = 3; - repeated KeyValuePair extra_params = 4; -} - -message HSearchInSegmentsParam { - repeated string segment_id_array = 1; - HSearchParam search_param = 2; -} - -/////////////////////////////////////////////////////////////////// - -message AttrRecord { - repeated string value = 1; -} - -message HEntity { - Status status = 1; - int64 entity_id = 2; - repeated string field_names = 3; - bytes attr_records = 4; - int64 row_num = 5; - repeated FieldValue result_values = 6; -} - -message HQueryResult { - Status status = 1; - repeated HEntity entities = 2; - int64 row_num = 3; - repeated float score = 4; - repeated float distance = 5; -} - -message HInsertParam { - string collection_name = 1; - string partition_tag = 2; - HEntity entities = 3; - repeated int64 entity_id_array = 4; - repeated KeyValuePair extra_params = 5; -} - -message HEntityIdentity { - string collection_name = 1; - int64 id = 2; -} - -message HEntityIDs { - Status status = 1; - repeated int64 entity_id_array = 2; -} - -message HGetEntityIDsParam { - string collection_name = 1; - string segment_name = 2; -} - -message HDeleteByIDParam { - string collection_name = 1; - repeated int64 id_array = 2; -} - -/////////////////////////////////////////////////////////////////// - -message HIndexParam { - Status status = 1; - string collection_name = 2; - int32 index_type = 3; - repeated KeyValuePair extra_params = 4; -} - - -service MilvusService { - /** - * @brief This method is used to create collection - * - * @param CollectionSchema, use to provide collection information to be created. - * - * @return Status - */ - rpc CreateCollection(CollectionSchema) returns (Status){} - - /** - * @brief This method is used to test collection existence. - * - * @param CollectionName, collection name is going to be tested. - * - * @return BoolReply - */ - rpc HasCollection(CollectionName) returns (BoolReply) {} - - /** - * @brief This method is used to get collection schema. - * - * @param CollectionName, target collection name. - * - * @return CollectionSchema - */ - rpc DescribeCollection(CollectionName) returns (CollectionSchema) {} - - /** - * @brief This method is used to get collection schema. - * - * @param CollectionName, target collection name. - * - * @return CollectionRowCount - */ - rpc CountCollection(CollectionName) returns (CollectionRowCount) {} - - /** - * @brief This method is used to list all collections. - * - * @param Command, dummy parameter. - * - * @return CollectionNameList - */ - rpc ShowCollections(Command) returns (CollectionNameList) {} - - /** - * @brief This method is used to get collection detail information. - * - * @param CollectionName, target collection name. - * - * @return CollectionInfo - */ - rpc ShowCollectionInfo(CollectionName) returns (CollectionInfo) {} - - /** - * @brief This method is used to delete collection. - * - * @param CollectionName, collection name is going to be deleted. - * - * @return CollectionNameList - */ - rpc DropCollection(CollectionName) returns (Status) {} - - /** - * @brief This method is used to build index by collection in sync mode. - * - * @param IndexParam, index paramters. - * - * @return Status - */ - rpc CreateIndex(IndexParam) returns (Status) {} - - /** - * @brief This method is used to describe index - * - * @param CollectionName, target collection name. - * - * @return IndexParam - */ - rpc DescribeIndex(CollectionName) returns (IndexParam) {} - - /** - * @brief This method is used to drop index - * - * @param CollectionName, target collection name. - * - * @return Status - */ - rpc DropIndex(CollectionName) returns (Status) {} - - /** - * @brief This method is used to create partition - * - * @param PartitionParam, partition parameters. - * - * @return Status - */ - rpc CreatePartition(PartitionParam) returns (Status) {} - - /** - * @brief This method is used to test partition existence. - * - * @param PartitionParam, target partition. - * - * @return BoolReply - */ - rpc HasPartition(PartitionParam) returns (BoolReply) {} - - /** - * @brief This method is used to show partition information - * - * @param CollectionName, target collection name. - * - * @return PartitionList - */ - rpc ShowPartitions(CollectionName) returns (PartitionList) {} - - /** - * @brief This method is used to drop partition - * - * @param PartitionParam, target partition. - * - * @return Status - */ - rpc DropPartition(PartitionParam) returns (Status) {} - - /** - * @brief This method is used to add vector array to collection. - * - * @param InsertParam, insert parameters. - * - * @return VectorIds - */ - rpc Insert(InsertParam) returns (VectorIds) {} - - /** - * @brief This method is used to get vectors data by id array. - * - * @param VectorsIdentity, target vector id array. - * - * @return VectorsData - */ - rpc GetVectorsByID(VectorsIdentity) returns (VectorsData) {} - - /** - * @brief This method is used to get vector ids from a segment - * - * @param GetVectorIDsParam, target collection and segment - * - * @return VectorIds - */ - rpc GetVectorIDs(GetVectorIDsParam) returns (VectorIds) {} - - /** - * @brief This method is used to query vector in collection. - * - * @param SearchParam, search parameters. - * - * @return TopKQueryResult - */ - rpc Search(SearchParam) returns (TopKQueryResult) {} - - /** - * @brief This method is used to query vector by id. - * - * @param SearchByIDParam, search parameters. - * - * @return TopKQueryResult - */ - rpc SearchByID(SearchByIDParam) returns (TopKQueryResult) {} - - /** - * @brief This method is used to query vector in specified files. - * - * @param SearchInFilesParam, search in files paremeters. - * - * @return TopKQueryResult - */ - rpc SearchInFiles(SearchInFilesParam) returns (TopKQueryResult) {} - - /** - * @brief This method is used to give the server status. - * - * @param Command, command string - * - * @return StringReply - */ - rpc Cmd(Command) returns (StringReply) {} - - /** - * @brief This method is used to delete vector by id - * - * @param DeleteByIDParam, delete parameters. - * - * @return status - */ - rpc DeleteByID(DeleteByIDParam) returns (Status) {} - - /** - * @brief This method is used to preload collection - * - * @param CollectionName, target collection name. - * - * @return Status - */ - rpc PreloadCollection(CollectionName) returns (Status) {} - - /** - * @brief This method is used to reload collection segments - * - * @param ReLoadSegmentsParam, target segments information. - * - * @return Status - */ - rpc ReloadSegments(ReLoadSegmentsParam) returns (Status) {} - - /** - * @brief This method is used to flush buffer into storage. - * - * @param FlushParam, flush parameters - * - * @return Status - */ - rpc Flush(FlushParam) returns (Status) {} - - /** - * @brief This method is used to compact collection - * - * @param CollectionName, target collection name. - * - * @return Status - */ - rpc Compact(CollectionName) returns (Status) {} - - /********************************New Interface********************************************/ - - rpc CreateHybridCollection(Mapping) returns (Status) {} - - rpc HasHybridCollection(CollectionName) returns (BoolReply) {} - - rpc DropHybridCollection(CollectionName) returns (Status) {} - - rpc DescribeHybridCollection(CollectionName) returns (Mapping) {} - - rpc CountHybridCollection(CollectionName) returns (CollectionRowCount) {} - - rpc ShowHybridCollections(Command) returns (MappingList) {} - - rpc ShowHybridCollectionInfo (CollectionName) returns (CollectionInfo) {} - - rpc PreloadHybridCollection(CollectionName) returns (Status) {} - - /////////////////////////////////////////////////////////////////// - -// rpc CreateIndex(IndexParam) returns (Status) {} -// -// rpc DescribeIndex(CollectionName) returns (IndexParam) {} -// -// rpc DropIndex(CollectionName) returns (Status) {} - - /////////////////////////////////////////////////////////////////// - - rpc InsertEntity(HInsertParam) returns (HEntityIDs) {} - - // TODO(yukun): will change to HQueryResult - rpc HybridSearch(HSearchParam) returns (TopKQueryResult) {} - - rpc HybridSearchInSegments(HSearchInSegmentsParam) returns (TopKQueryResult) {} - - rpc GetEntityByID(HEntityIdentity) returns (HEntity) {} - - rpc GetEntityIDs(HGetEntityIDsParam) returns (HEntityIDs) {} - - rpc DeleteEntitiesByID(HDeleteByIDParam) returns (Status) {} - - /////////////////////////////////////////////////////////////////// -} \ No newline at end of file diff --git a/core/src/grpc/status.proto b/core/src/grpc/status.proto deleted file mode 100644 index a4f018a5fc..0000000000 --- a/core/src/grpc/status.proto +++ /dev/null @@ -1,35 +0,0 @@ -syntax = "proto3"; - -package milvus.grpc; - -enum ErrorCode { - SUCCESS = 0; - UNEXPECTED_ERROR = 1; - CONNECT_FAILED = 2; - PERMISSION_DENIED = 3; - COLLECTION_NOT_EXISTS = 4; - ILLEGAL_ARGUMENT = 5; - ILLEGAL_DIMENSION = 7; - ILLEGAL_INDEX_TYPE = 8; - ILLEGAL_COLLECTION_NAME = 9; - ILLEGAL_TOPK = 10; - ILLEGAL_ROWRECORD = 11; - ILLEGAL_VECTOR_ID = 12; - ILLEGAL_SEARCH_RESULT = 13; - FILE_NOT_FOUND = 14; - META_FAILED = 15; - CACHE_FAILED = 16; - CANNOT_CREATE_FOLDER = 17; - CANNOT_CREATE_FILE = 18; - CANNOT_DELETE_FOLDER = 19; - CANNOT_DELETE_FILE = 20; - BUILD_INDEX_ERROR = 21; - ILLEGAL_NLIST = 22; - ILLEGAL_METRIC_TYPE = 23; - OUT_OF_MEMORY = 24; -} - -message Status { - ErrorCode error_code = 1; - string reason = 2; -} \ No newline at end of file diff --git a/core/src/index/.gitignore b/core/src/index/.gitignore deleted file mode 100644 index c263e61d36..0000000000 --- a/core/src/index/.gitignore +++ /dev/null @@ -1 +0,0 @@ -cmake_build \ No newline at end of file diff --git a/core/src/index/CMakeLists.txt b/core/src/index/CMakeLists.txt deleted file mode 100644 index 9dc3b3bb41..0000000000 --- a/core/src/index/CMakeLists.txt +++ /dev/null @@ -1,106 +0,0 @@ -#------------------------------------------------------------------------------- -# Copyright (C) 2019-2020 Zilliz. All rights reserved. -# -# Licensed 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 limitations under the License. -#------------------------------------------------------------------------------- - - -cmake_minimum_required(VERSION 3.12) -message(STATUS "------------------------------KNOWHERE-----------------------------------") -message(STATUS "Building using CMake version: ${CMAKE_VERSION}") - -set(KNOWHERE_VERSION "0.6.0") -string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" KNOWHERE_BASE_VERSION "${KNOWHERE_VERSION}") -project(knowhere VERSION "${KNOWHERE_BASE_VERSION}" LANGUAGES C CXX) -set(CMAKE_CXX_STANDARD 17) - -set(KNOWHERE_VERSION_MAJOR "${knowhere_VERSION_MAJOR}") -set(KNOWHERE_VERSION_MINOR "${knowhere_VERSION_MINOR}") -set(KNOWHERE_VERSION_PATCH "${knowhere_VERSION_PATCH}") -if (KNOWHERE_VERSION_MAJOR STREQUAL "" - OR KNOWHERE_VERSION_MINOR STREQUAL "" - OR KNOWHERE_VERSION_PATCH STREQUAL "") - message(FATAL_ERROR "Failed to determine Knowhere version from '${KNOWHERE_VERSION}'") -endif () - -message(STATUS "Knowhere version: " - "${KNOWHERE_VERSION_MAJOR}.${KNOWHERE_VERSION_MINOR}.${KNOWHERE_VERSION_PATCH} " - "(full: '${KNOWHERE_VERSION}')") - -# if no build build type is specified, default to release builds -if (NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release) -endif (NOT CMAKE_BUILD_TYPE) - -if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)") - message(STATUS "building milvus_engine on x86 architecture") - set(KNOWHERE_BUILD_ARCH x86_64) -elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "(ppc)") - message(STATUS "building milvus_engine on ppc architecture") - set(KNOWHERE_BUILD_ARCH ppc64le) -else () - message(WARNING "unknown processor type") - message(WARNING "CMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}") - set(KNOWHERE_BUILD_ARCH unknown) -endif () - -if (CMAKE_BUILD_TYPE STREQUAL "Release") - set(BUILD_TYPE "release") -else () - set(BUILD_TYPE "debug") -endif () -message(STATUS "Build type = ${BUILD_TYPE}") - -set(INDEX_SOURCE_DIR ${PROJECT_SOURCE_DIR}) -set(INDEX_BINARY_DIR ${PROJECT_BINARY_DIR}) -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${INDEX_SOURCE_DIR}/cmake") - -include(ExternalProject) -include(DefineOptionsCore) -include(BuildUtilsCore) - -if (MILVUS_GPU_VERSION OR KNOWHERE_GPU_VERSION) - message(STATUS "Building Knowhere GPU version") - add_compile_definitions("MILVUS_GPU_VERSION") - enable_language(CUDA) - find_package(CUDA 10 REQUIRED) - set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS} -Xcompiler -fPIC -std=c++11 -D_FORCE_INLINES --expt-extended-lambda") -else () - message(STATUS "Building Knowhere CPU version") -endif () - -include(ThirdPartyPackagesCore) - -if (CMAKE_BUILD_TYPE STREQUAL "Release") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -fPIC -DELPP_THREAD_SAFE -fopenmp") - if (KNOWHERE_GPU_VERSION) - set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS} -O3") - endif () -else () - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -fPIC -DELPP_THREAD_SAFE -fopenmp") - if (KNOWHERE_GPU_VERSION) - set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS} -O0 -g") - endif () -endif () - -add_subdirectory(knowhere) - -if (BUILD_COVERAGE STREQUAL "ON") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") -endif () - -set(INDEX_INCLUDE_DIRS ${INDEX_INCLUDE_DIRS} PARENT_SCOPE) - -if (KNOWHERE_BUILD_TESTS) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DELPP_DISABLE_LOGS") - add_subdirectory(unittest) -endif () - -config_summary() diff --git a/core/src/index/archive/KnowhereResource.cpp b/core/src/index/archive/KnowhereResource.cpp deleted file mode 100644 index c822b8ff4f..0000000000 --- a/core/src/index/archive/KnowhereResource.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "index/archive/KnowhereResource.h" -#ifdef MILVUS_GPU_VERSION -#include "knowhere/index/vector_index/helpers/FaissGpuResourceMgr.h" -#endif - -#include "config/Config.h" -#include "faiss/FaissHook.h" -#include "scheduler/Utils.h" -#include "utils/Error.h" -#include "utils/Log.h" - -#include -#include -#include -#include -#include -#include - -namespace milvus { -namespace engine { - -constexpr int64_t M_BYTE = 1024 * 1024; - -Status -KnowhereResource::Initialize() { - server::Config& config = server::Config::GetInstance(); - std::string simd_type; - STATUS_CHECK(config.GetEngineConfigSimdType(simd_type)); - if (simd_type == "avx512") { - faiss::faiss_use_avx512 = true; - faiss::faiss_use_avx2 = false; - faiss::faiss_use_sse = false; - } else if (simd_type == "avx2") { - faiss::faiss_use_avx512 = false; - faiss::faiss_use_avx2 = true; - faiss::faiss_use_sse = false; - } else if (simd_type == "sse") { - faiss::faiss_use_avx512 = false; - faiss::faiss_use_avx2 = false; - faiss::faiss_use_sse = true; - } else { - faiss::faiss_use_avx512 = true; - faiss::faiss_use_avx2 = true; - faiss::faiss_use_sse = true; - } - std::string cpu_flag; - if (faiss::hook_init(cpu_flag)) { - std::cout << "FAISS hook " << cpu_flag << std::endl; - LOG_ENGINE_DEBUG_ << "FAISS hook " << cpu_flag; - } else { - return Status(KNOWHERE_UNEXPECTED_ERROR, "FAISS hook fail, CPU not supported!"); - } - -#ifdef MILVUS_GPU_VERSION - bool enable_gpu = false; - STATUS_CHECK(config.GetGpuResourceConfigEnable(enable_gpu)); - fiu_do_on("KnowhereResource.Initialize.disable_gpu", enable_gpu = false); - if (not enable_gpu) - return Status::OK(); - - struct GpuResourceSetting { - int64_t pinned_memory = 256 * M_BYTE; - int64_t temp_memory = 256 * M_BYTE; - int64_t resource_num = 2; - }; - using GpuResourcesArray = std::map; - GpuResourcesArray gpu_resources; - - // get build index gpu resource - std::vector build_index_gpus; - STATUS_CHECK(config.GetGpuResourceConfigBuildIndexResources(build_index_gpus)); - - for (auto gpu_id : build_index_gpus) { - gpu_resources.insert(std::make_pair(gpu_id, GpuResourceSetting())); - } - - // get search gpu resource - std::vector search_gpus; - STATUS_CHECK(config.GetGpuResourceConfigSearchResources(search_gpus)); - - for (auto& gpu_id : search_gpus) { - gpu_resources.insert(std::make_pair(gpu_id, GpuResourceSetting())); - } - - // init gpu resources - for (auto iter = gpu_resources.begin(); iter != gpu_resources.end(); ++iter) { - knowhere::FaissGpuResourceMgr::GetInstance().InitDevice(iter->first, iter->second.pinned_memory, - iter->second.temp_memory, iter->second.resource_num); - } - -#endif - - return Status::OK(); -} - -Status -KnowhereResource::Finalize() { -#ifdef MILVUS_GPU_VERSION - knowhere::FaissGpuResourceMgr::GetInstance().Free(); // free gpu resource. -#endif - return Status::OK(); -} - -} // namespace engine -} // namespace milvus diff --git a/core/src/index/archive/KnowhereResource.h b/core/src/index/archive/KnowhereResource.h deleted file mode 100644 index 1e863cf27e..0000000000 --- a/core/src/index/archive/KnowhereResource.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "utils/Status.h" - -namespace milvus { -namespace engine { - -class KnowhereResource { - public: - static Status - Initialize(); - - static Status - Finalize(); -}; - -} // namespace engine -} // namespace milvus diff --git a/core/src/index/build.sh b/core/src/index/build.sh deleted file mode 100755 index 8557e0e946..0000000000 --- a/core/src/index/build.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/bin/bash - -BUILD_TYPE="Debug" -BUILD_UNITTEST="OFF" -INSTALL_PREFIX=$(pwd)/cmake_build -MAKE_CLEAN="OFF" -PROFILING="OFF" -FAISS_WITH_MKL="OFF" - -while getopts "p:d:t:uhrcgm" arg -do - case $arg in - t) - BUILD_TYPE=$OPTARG # BUILD_TYPE - ;; - u) - echo "Build and run unittest cases" ; - BUILD_UNITTEST="ON"; - ;; - p) - INSTALL_PREFIX=$OPTARG - ;; - r) - if [[ -d cmake_build ]]; then - rm ./cmake_build -r - MAKE_CLEAN="ON" - fi - ;; - g) - PROFILING="ON" - ;; - m) - FAISS_WITH_MKL="ON" - ;; - h) # help - echo " - -parameter: --t: build type(default: Debug) --u: building unit test options(default: OFF) --p: install prefix(default: $(pwd)/knowhere) --r: remove previous build directory(default: OFF) --g: profiling(default: OFF) --m: build faiss with MKL(default: OFF) - -usage: -./build.sh -t \${BUILD_TYPE} [-u] [-h] [-g] [-r] [-c] [-m] - " - exit 0 - ;; - ?) - echo "unknown argument" - exit 1 - ;; - esac -done - -if [[ ! -d cmake_build ]]; then - mkdir cmake_build - MAKE_CLEAN="ON" -fi - -cd cmake_build - -CUDA_COMPILER=/usr/local/cuda/bin/nvcc - -if [[ ${MAKE_CLEAN} == "ON" ]]; then - CMAKE_CMD="cmake -DBUILD_UNIT_TEST=${BUILD_UNITTEST} \ - -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} - -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ - -DCMAKE_CUDA_COMPILER=${CUDA_COMPILER} \ - -DMILVUS_ENABLE_PROFILING=${PROFILING} \ - -DFAISS_WITH_MKL=${FAISS_WITH_MKL} \ - ../" - echo ${CMAKE_CMD} - - ${CMAKE_CMD} - make clean -fi - -make -j 8 || exit 1 - -make install || exit 1 diff --git a/core/src/index/cmake/BuildUtilsCore.cmake b/core/src/index/cmake/BuildUtilsCore.cmake deleted file mode 100644 index 2261ce3e29..0000000000 --- a/core/src/index/cmake/BuildUtilsCore.cmake +++ /dev/null @@ -1,204 +0,0 @@ -# Define a function that check last file modification -function(Check_Last_Modify cache_check_lists_file_path working_dir last_modified_commit_id) - if (EXISTS "${working_dir}") - if (EXISTS "${cache_check_lists_file_path}") - set(GIT_LOG_SKIP_NUM 0) - set(_MATCH_ALL ON CACHE BOOL "Match all") - set(_LOOP_STATUS ON CACHE BOOL "Whether out of loop") - file(STRINGS ${cache_check_lists_file_path} CACHE_IGNORE_TXT) - while (_LOOP_STATUS) - foreach (_IGNORE_ENTRY ${CACHE_IGNORE_TXT}) - if (NOT _IGNORE_ENTRY MATCHES "^[^#]+") - continue() - endif () - - set(_MATCH_ALL OFF) - execute_process(COMMAND git log --no-merges -1 --skip=${GIT_LOG_SKIP_NUM} --name-status --pretty= WORKING_DIRECTORY ${working_dir} OUTPUT_VARIABLE CHANGE_FILES) - if (NOT CHANGE_FILES STREQUAL "") - string(REPLACE "\n" ";" _CHANGE_FILES ${CHANGE_FILES}) - foreach (_FILE_ENTRY ${_CHANGE_FILES}) - string(REGEX MATCH "[^ \t]+$" _FILE_NAME ${_FILE_ENTRY}) - execute_process(COMMAND sh -c "echo ${_FILE_NAME} | grep ${_IGNORE_ENTRY}" RESULT_VARIABLE return_code) - if (return_code EQUAL 0) - execute_process(COMMAND git log --no-merges -1 --skip=${GIT_LOG_SKIP_NUM} --pretty=%H WORKING_DIRECTORY ${working_dir} OUTPUT_VARIABLE LAST_MODIFIED_COMMIT_ID) - set(${last_modified_commit_id} ${LAST_MODIFIED_COMMIT_ID} PARENT_SCOPE) - set(_LOOP_STATUS OFF) - endif () - endforeach () - else () - set(_LOOP_STATUS OFF) - endif () - endforeach () - - if (_MATCH_ALL) - execute_process(COMMAND git log --no-merges -1 --skip=${GIT_LOG_SKIP_NUM} --pretty=%H WORKING_DIRECTORY ${working_dir} OUTPUT_VARIABLE LAST_MODIFIED_COMMIT_ID) - set(${last_modified_commit_id} ${LAST_MODIFIED_COMMIT_ID} PARENT_SCOPE) - set(_LOOP_STATUS OFF) - endif () - - math(EXPR GIT_LOG_SKIP_NUM "${GIT_LOG_SKIP_NUM} + 1") - endwhile (_LOOP_STATUS) - else () - execute_process(COMMAND git log --no-merges -1 --skip=${GIT_LOG_SKIP_NUM} --pretty=%H WORKING_DIRECTORY ${working_dir} OUTPUT_VARIABLE LAST_MODIFIED_COMMIT_ID) - set(${last_modified_commit_id} ${LAST_MODIFIED_COMMIT_ID} PARENT_SCOPE) - endif () - else () - message(FATAL_ERROR "The directory ${working_dir} does not exist") - endif () -endfunction() - -# Define a function that extracts a cached package -function(ExternalProject_Use_Cache project_name package_file install_path) - message(STATUS "Will use cached package file: ${package_file}") - - ExternalProject_Add(${project_name} - DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo - "No download step needed (using cached package)" - CONFIGURE_COMMAND ${CMAKE_COMMAND} -E echo - "No configure step needed (using cached package)" - BUILD_COMMAND ${CMAKE_COMMAND} -E echo - "No build step needed (using cached package)" - INSTALL_COMMAND ${CMAKE_COMMAND} -E echo - "No install step needed (using cached package)" - ) - - # We want our tar files to contain the Install/ prefix (not for any - # very special reason, only for consistency and so that we can identify them - # in the extraction logs) which means that we must extract them in the - # binary (top-level build) directory to have them installed in the right - # place for subsequent ExternalProjects to pick them up. It seems that the - # only way to control the working directory is with Add_Step! - ExternalProject_Add_Step(${project_name} extract - ALWAYS 1 - COMMAND - ${CMAKE_COMMAND} -E echo - "Extracting ${package_file} to ${install_path}" - COMMAND - ${CMAKE_COMMAND} -E tar xzf ${package_file} ${install_path} - WORKING_DIRECTORY ${INDEX_BINARY_DIR} - ) - - ExternalProject_Add_StepTargets(${project_name} extract) -endfunction() - -# Define a function that to create a new cached package -function(ExternalProject_Create_Cache project_name package_file install_path cache_username cache_password cache_path) - if (EXISTS ${package_file}) - message(STATUS "Removing existing package file: ${package_file}") - file(REMOVE ${package_file}) - endif () - - string(REGEX REPLACE "(.+)/.+$" "\\1" package_dir ${package_file}) - if (NOT EXISTS ${package_dir}) - file(MAKE_DIRECTORY ${package_dir}) - endif () - - message(STATUS "Will create cached package file: ${package_file}") - - ExternalProject_Add_Step(${project_name} package - DEPENDEES install - BYPRODUCTS ${package_file} - COMMAND ${CMAKE_COMMAND} -E echo "Updating cached package file: ${package_file}" - COMMAND ${CMAKE_COMMAND} -E tar czvf ${package_file} ${install_path} - COMMAND ${CMAKE_COMMAND} -E echo "Uploading package file ${package_file} to ${cache_path}" - COMMAND curl -u${cache_username}:${cache_password} -T ${package_file} ${cache_path} - ) - - ExternalProject_Add_StepTargets(${project_name} package) -endfunction() - -function(ADD_THIRDPARTY_LIB LIB_NAME) - set(options) - set(one_value_args SHARED_LIB STATIC_LIB) - set(multi_value_args DEPS INCLUDE_DIRECTORIES) - cmake_parse_arguments(ARG - "${options}" - "${one_value_args}" - "${multi_value_args}" - ${ARGN}) - if (ARG_UNPARSED_ARGUMENTS) - message(SEND_ERROR "Error: unrecognized arguments: ${ARG_UNPARSED_ARGUMENTS}") - endif () - - if (ARG_STATIC_LIB AND ARG_SHARED_LIB) - if (NOT ARG_STATIC_LIB) - message(FATAL_ERROR "No static or shared library provided for ${LIB_NAME}") - endif () - - set(AUG_LIB_NAME "${LIB_NAME}_static") - add_library(${AUG_LIB_NAME} STATIC IMPORTED) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES IMPORTED_LOCATION "${ARG_STATIC_LIB}") - if (ARG_DEPS) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES INTERFACE_LINK_LIBRARIES "${ARG_DEPS}") - endif () - message(STATUS "Added static library dependency ${AUG_LIB_NAME}: ${ARG_STATIC_LIB}") - if (ARG_INCLUDE_DIRECTORIES) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES INTERFACE_INCLUDE_DIRECTORIES - "${ARG_INCLUDE_DIRECTORIES}") - endif () - - set(AUG_LIB_NAME "${LIB_NAME}_shared") - add_library(${AUG_LIB_NAME} SHARED IMPORTED) - - if (WIN32) - # Mark the ".lib" location as part of a Windows DLL - set_target_properties(${AUG_LIB_NAME} - PROPERTIES IMPORTED_IMPLIB "${ARG_SHARED_LIB}") - else () - set_target_properties(${AUG_LIB_NAME} - PROPERTIES IMPORTED_LOCATION "${ARG_SHARED_LIB}") - endif () - if (ARG_DEPS) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES INTERFACE_LINK_LIBRARIES "${ARG_DEPS}") - endif () - message(STATUS "Added shared library dependency ${AUG_LIB_NAME}: ${ARG_SHARED_LIB}") - if (ARG_INCLUDE_DIRECTORIES) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES INTERFACE_INCLUDE_DIRECTORIES - "${ARG_INCLUDE_DIRECTORIES}") - endif () - elseif (ARG_STATIC_LIB) - set(AUG_LIB_NAME "${LIB_NAME}_static") - add_library(${AUG_LIB_NAME} STATIC IMPORTED) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES IMPORTED_LOCATION "${ARG_STATIC_LIB}") - if (ARG_DEPS) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES INTERFACE_LINK_LIBRARIES "${ARG_DEPS}") - endif () - message(STATUS "Added static library dependency ${AUG_LIB_NAME}: ${ARG_STATIC_LIB}") - if (ARG_INCLUDE_DIRECTORIES) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES INTERFACE_INCLUDE_DIRECTORIES - "${ARG_INCLUDE_DIRECTORIES}") - endif () - elseif (ARG_SHARED_LIB) - set(AUG_LIB_NAME "${LIB_NAME}_shared") - add_library(${AUG_LIB_NAME} SHARED IMPORTED) - - if (WIN32) - # Mark the ".lib" location as part of a Windows DLL - set_target_properties(${AUG_LIB_NAME} - PROPERTIES IMPORTED_IMPLIB "${ARG_SHARED_LIB}") - else () - set_target_properties(${AUG_LIB_NAME} - PROPERTIES IMPORTED_LOCATION "${ARG_SHARED_LIB}") - endif () - message(STATUS "Added shared library dependency ${AUG_LIB_NAME}: ${ARG_SHARED_LIB}") - if (ARG_DEPS) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES INTERFACE_LINK_LIBRARIES "${ARG_DEPS}") - endif () - if (ARG_INCLUDE_DIRECTORIES) - set_target_properties(${AUG_LIB_NAME} - PROPERTIES INTERFACE_INCLUDE_DIRECTORIES - "${ARG_INCLUDE_DIRECTORIES}") - endif () - else () - message(FATAL_ERROR "No static or shared library provided for ${LIB_NAME}") - endif () -endfunction() diff --git a/core/src/index/cmake/DefineOptionsCore.cmake b/core/src/index/cmake/DefineOptionsCore.cmake deleted file mode 100644 index f6825b4350..0000000000 --- a/core/src/index/cmake/DefineOptionsCore.cmake +++ /dev/null @@ -1,162 +0,0 @@ - -macro(set_option_category name) - set(KNOWHERE_OPTION_CATEGORY ${name}) - list(APPEND "KNOWHERE_OPTION_CATEGORIES" ${name}) -endmacro() - -macro(define_option name description default) - option(${name} ${description} ${default}) - list(APPEND "KNOWHERE_${KNOWHERE_OPTION_CATEGORY}_OPTION_NAMES" ${name}) - set("${name}_OPTION_DESCRIPTION" ${description}) - set("${name}_OPTION_DEFAULT" ${default}) - set("${name}_OPTION_TYPE" "bool") -endmacro() - -function(list_join lst glue out) - if ("${${lst}}" STREQUAL "") - set(${out} "" PARENT_SCOPE) - return() - endif () - - list(GET ${lst} 0 joined) - list(REMOVE_AT ${lst} 0) - foreach (item ${${lst}}) - set(joined "${joined}${glue}${item}") - endforeach () - set(${out} ${joined} PARENT_SCOPE) -endfunction() - -macro(define_option_string name description default) - set(${name} ${default} CACHE STRING ${description}) - list(APPEND "KNOWHERE_${KNOWHERE_OPTION_CATEGORY}_OPTION_NAMES" ${name}) - set("${name}_OPTION_DESCRIPTION" ${description}) - set("${name}_OPTION_DEFAULT" "\"${default}\"") - set("${name}_OPTION_TYPE" "string") - - set("${name}_OPTION_ENUM" ${ARGN}) - list_join("${name}_OPTION_ENUM" "|" "${name}_OPTION_ENUM") - if (NOT ("${${name}_OPTION_ENUM}" STREQUAL "")) - set_property(CACHE ${name} PROPERTY STRINGS ${ARGN}) - endif () -endmacro() - -#---------------------------------------------------------------------- -set_option_category("GPU version") - -if (MILVUS_GPU_VERSION) - define_option(KNOWHERE_GPU_VERSION "Build GPU version" ON) -else () - define_option(KNOWHERE_GPU_VERSION "Build GPU version" OFF) -endif () - -#---------------------------------------------------------------------- -set_option_category("Thirdparty") - -set(KNOWHERE_DEPENDENCY_SOURCE_DEFAULT "BUNDLED") - -define_option_string(KNOWHERE_DEPENDENCY_SOURCE - "Method to use for acquiring KNOWHERE's build dependencies" - "${KNOWHERE_DEPENDENCY_SOURCE_DEFAULT}" - "AUTO" - "BUNDLED" - "SYSTEM") - -define_option(KNOWHERE_VERBOSE_THIRDPARTY_BUILD - "Show output from ExternalProjects rather than just logging to files" ON) - -define_option(KNOWHERE_BOOST_USE_SHARED "Rely on boost shared libraries where relevant" OFF) - -define_option(KNOWHERE_BOOST_VENDORED "Use vendored Boost instead of existing Boost. \ -Note that this requires linking Boost statically" OFF) - -define_option(KNOWHERE_BOOST_HEADER_ONLY "Use only BOOST headers" OFF) - -define_option(KNOWHERE_WITH_ARROW "Build with ARROW" OFF) - -define_option(KNOWHERE_WITH_OPENBLAS "Build with OpenBLAS library" ON) - -define_option(KNOWHERE_WITH_FAISS "Build with FAISS library" ON) - -define_option(KNOWHERE_WITH_FAISS_GPU_VERSION "Build with FAISS GPU version" ON) - -define_option(FAISS_WITH_MKL "Build FAISS with MKL" OFF) - -#---------------------------------------------------------------------- -set_option_category("Test and benchmark") - -if (BUILD_UNIT_TEST) - define_option(KNOWHERE_BUILD_TESTS "Build the KNOWHERE googletest unit tests" ON) -else () - define_option(KNOWHERE_BUILD_TESTS "Build the KNOWHERE googletest unit tests" OFF) -endif (BUILD_UNIT_TEST) - -#---------------------------------------------------------------------- -macro(config_summary) - message(STATUS "---------------------------------------------------------------------") - message(STATUS "KNOWHERE version: ${KNOWHERE_VERSION}") - message(STATUS) - message(STATUS "Build configuration summary:") - - message(STATUS " Generator: ${CMAKE_GENERATOR}") - message(STATUS " Build type: ${CMAKE_BUILD_TYPE}") - message(STATUS " Source directory: ${CMAKE_CURRENT_SOURCE_DIR}") - if (${CMAKE_EXPORT_COMPILE_COMMANDS}) - message( - STATUS " Compile commands: ${INDEX_BINARY_DIR}/compile_commands.json") - endif () - - foreach (category ${KNOWHERE_OPTION_CATEGORIES}) - - message(STATUS) - message(STATUS "${category} options:") - - set(option_names ${KNOWHERE_${category}_OPTION_NAMES}) - - set(max_value_length 0) - foreach (name ${option_names}) - string(LENGTH "\"${${name}}\"" value_length) - if (${max_value_length} LESS ${value_length}) - set(max_value_length ${value_length}) - endif () - endforeach () - - foreach (name ${option_names}) - if ("${${name}_OPTION_TYPE}" STREQUAL "string") - set(value "\"${${name}}\"") - else () - set(value "${${name}}") - endif () - - set(default ${${name}_OPTION_DEFAULT}) - set(description ${${name}_OPTION_DESCRIPTION}) - string(LENGTH ${description} description_length) - if (${description_length} LESS 70) - string( - SUBSTRING - " " - ${description_length} -1 description_padding) - else () - set(description_padding " - ") - endif () - - set(comment "[${name}]") - - if ("${value}" STREQUAL "${default}") - set(comment "[default] ${comment}") - endif () - - if (NOT ("${${name}_OPTION_ENUM}" STREQUAL "")) - set(comment "${comment} [${${name}_OPTION_ENUM}]") - endif () - - string( - SUBSTRING "${value} " - 0 ${max_value_length} value) - - message(STATUS " ${description} ${description_padding} ${value} ${comment}") - endforeach () - - endforeach () - -endmacro() diff --git a/core/src/index/cmake/FindArrow.cmake b/core/src/index/cmake/FindArrow.cmake deleted file mode 100644 index fdf7c1437f..0000000000 --- a/core/src/index/cmake/FindArrow.cmake +++ /dev/null @@ -1,431 +0,0 @@ -# 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 limitations -# under the License. - -# - Find Arrow (arrow/api.h, libarrow.a, libarrow.so) -# This module defines -# ARROW_FOUND, whether Arrow has been found -# ARROW_FULL_SO_VERSION, full shared object version of found Arrow "100.0.0" -# ARROW_IMPORT_LIB, path to libarrow's import library (Windows only) -# ARROW_INCLUDE_DIR, directory containing headers -# ARROW_LIBS, deprecated. Use ARROW_LIB_DIR instead -# ARROW_LIB_DIR, directory containing Arrow libraries -# ARROW_SHARED_IMP_LIB, deprecated. Use ARROW_IMPORT_LIB instead -# ARROW_SHARED_LIB, path to libarrow's shared library -# ARROW_SO_VERSION, shared object version of found Arrow such as "100" -# ARROW_STATIC_LIB, path to libarrow.a -# ARROW_VERSION, version of found Arrow -# ARROW_VERSION_MAJOR, major version of found Arrow -# ARROW_VERSION_MINOR, minor version of found Arrow -# ARROW_VERSION_PATCH, patch version of found Arrow - -include(FindPkgConfig) -include(FindPackageHandleStandardArgs) - -set(ARROW_SEARCH_LIB_PATH_SUFFIXES) -if(CMAKE_LIBRARY_ARCHITECTURE) - list(APPEND ARROW_SEARCH_LIB_PATH_SUFFIXES "lib/${CMAKE_LIBRARY_ARCHITECTURE}") -endif() -list(APPEND ARROW_SEARCH_LIB_PATH_SUFFIXES - "lib64" - "lib32" - "lib" - "bin") -set(ARROW_CONFIG_SUFFIXES - "_RELEASE" - "_RELWITHDEBINFO" - "_MINSIZEREL" - "_DEBUG" - "") -if(CMAKE_BUILD_TYPE) - string(TOUPPER ${CMAKE_BUILD_TYPE} ARROW_CONFIG_SUFFIX_PREFERRED) - set(ARROW_CONFIG_SUFFIX_PREFERRED "_${ARROW_CONFIG_SUFFIX_PREFERRED}") - list(INSERT ARROW_CONFIG_SUFFIXES 0 "${ARROW_CONFIG_SUFFIX_PREFERRED}") -endif() - -if(NOT DEFINED ARROW_MSVC_STATIC_LIB_SUFFIX) - if(MSVC) - set(ARROW_MSVC_STATIC_LIB_SUFFIX "_static") - else() - set(ARROW_MSVC_STATIC_LIB_SUFFIX "") - endif() -endif() - -# Internal function. -# -# Set shared library name for ${base_name} to ${output_variable}. -# -# Example: -# arrow_build_shared_library_name(ARROW_SHARED_LIBRARY_NAME arrow) -# # -> ARROW_SHARED_LIBRARY_NAME=libarrow.so on Linux -# # -> ARROW_SHARED_LIBRARY_NAME=libarrow.dylib on macOS -# # -> ARROW_SHARED_LIBRARY_NAME=arrow.dll with MSVC on Windows -# # -> ARROW_SHARED_LIBRARY_NAME=libarrow.dll with MinGW on Windows -function(arrow_build_shared_library_name output_variable base_name) - set(${output_variable} - "${CMAKE_SHARED_LIBRARY_PREFIX}${base_name}${CMAKE_SHARED_LIBRARY_SUFFIX}" - PARENT_SCOPE) -endfunction() - -# Internal function. -# -# Set import library name for ${base_name} to ${output_variable}. -# This is useful only for MSVC build. Import library is used only -# with MSVC build. -# -# Example: -# arrow_build_import_library_name(ARROW_IMPORT_LIBRARY_NAME arrow) -# # -> ARROW_IMPORT_LIBRARY_NAME=arrow on Linux (meaningless) -# # -> ARROW_IMPORT_LIBRARY_NAME=arrow on macOS (meaningless) -# # -> ARROW_IMPORT_LIBRARY_NAME=arrow.lib with MSVC on Windows -# # -> ARROW_IMPORT_LIBRARY_NAME=libarrow.dll.a with MinGW on Windows -function(arrow_build_import_library_name output_variable base_name) - set(${output_variable} - "${CMAKE_IMPORT_LIBRARY_PREFIX}${base_name}${CMAKE_IMPORT_LIBRARY_SUFFIX}" - PARENT_SCOPE) -endfunction() - -# Internal function. -# -# Set static library name for ${base_name} to ${output_variable}. -# -# Example: -# arrow_build_static_library_name(ARROW_STATIC_LIBRARY_NAME arrow) -# # -> ARROW_STATIC_LIBRARY_NAME=libarrow.a on Linux -# # -> ARROW_STATIC_LIBRARY_NAME=libarrow.a on macOS -# # -> ARROW_STATIC_LIBRARY_NAME=arrow.lib with MSVC on Windows -# # -> ARROW_STATIC_LIBRARY_NAME=libarrow.dll.a with MinGW on Windows -function(arrow_build_static_library_name output_variable base_name) - set( - ${output_variable} - "${CMAKE_STATIC_LIBRARY_PREFIX}${base_name}${ARROW_MSVC_STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}" - PARENT_SCOPE) -endfunction() - -# Internal function. -# -# Set macro value for ${macro_name} in ${header_content} to ${output_variable}. -# -# Example: -# arrow_extract_macro_value(version_major -# "ARROW_VERSION_MAJOR" -# "#define ARROW_VERSION_MAJOR 1.0.0") -# # -> version_major=1.0.0 -function(arrow_extract_macro_value output_variable macro_name header_content) - string(REGEX MATCH "#define +${macro_name} +[^\r\n]+" macro_definition - "${header_content}") - string(REGEX - REPLACE "^#define +${macro_name} +(.+)$" "\\1" macro_value "${macro_definition}") - set(${output_variable} "${macro_value}" PARENT_SCOPE) -endfunction() - -# Internal macro only for arrow_find_package. -# -# Find package in HOME. -macro(arrow_find_package_home) - find_path(${prefix}_include_dir "${header_path}" - PATHS "${home}" - PATH_SUFFIXES "include" - NO_DEFAULT_PATH) - set(include_dir "${${prefix}_include_dir}") - set(${prefix}_INCLUDE_DIR "${include_dir}" PARENT_SCOPE) - - if(MSVC) - set(CMAKE_SHARED_LIBRARY_SUFFIXES_ORIGINAL ${CMAKE_FIND_LIBRARY_SUFFIXES}) - # .dll isn't found by find_library with MSVC because .dll isn't included in - # CMAKE_FIND_LIBRARY_SUFFIXES. - list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_SHARED_LIBRARY_SUFFIX}") - endif() - find_library(${prefix}_shared_lib - NAMES "${shared_lib_name}" - PATHS "${home}" - PATH_SUFFIXES ${ARROW_SEARCH_LIB_PATH_SUFFIXES} - NO_DEFAULT_PATH) - if(MSVC) - set(CMAKE_SHARED_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_ORIGINAL}) - endif() - set(shared_lib "${${prefix}_shared_lib}") - set(${prefix}_SHARED_LIB "${shared_lib}" PARENT_SCOPE) - if(shared_lib) - add_library(${target_shared} SHARED IMPORTED) - set_target_properties(${target_shared} PROPERTIES IMPORTED_LOCATION "${shared_lib}") - if(include_dir) - set_target_properties(${target_shared} - PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${include_dir}") - endif() - find_library(${prefix}_import_lib - NAMES "${import_lib_name}" - PATHS "${home}" - PATH_SUFFIXES ${ARROW_SEARCH_LIB_PATH_SUFFIXES} - NO_DEFAULT_PATH) - set(import_lib "${${prefix}_import_lib}") - set(${prefix}_IMPORT_LIB "${import_lib}" PARENT_SCOPE) - if(import_lib) - set_target_properties(${target_shared} PROPERTIES IMPORTED_IMPLIB "${import_lib}") - endif() - endif() - - find_library(${prefix}_static_lib - NAMES "${static_lib_name}" - PATHS "${home}" - PATH_SUFFIXES ${ARROW_SEARCH_LIB_PATH_SUFFIXES} - NO_DEFAULT_PATH) - set(static_lib "${${prefix}_static_lib}") - set(${prefix}_STATIC_LIB "${static_lib}" PARENT_SCOPE) - if(static_lib) - add_library(${target_static} STATIC IMPORTED) - set_target_properties(${target_static} PROPERTIES IMPORTED_LOCATION "${static_lib}") - if(include_dir) - set_target_properties(${target_static} - PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${include_dir}") - endif() - endif() -endmacro() - -# Internal macro only for arrow_find_package. -# -# Find package by CMake package configuration. -macro(arrow_find_package_cmake_package_configuration) - # ARROW-5575: We need to split target files for each component - if(TARGET ${target_shared} OR TARGET ${target_static}) - set(${cmake_package_name}_FOUND TRUE) - else() - find_package(${cmake_package_name} CONFIG) - endif() - if(${cmake_package_name}_FOUND) - set(${prefix}_USE_CMAKE_PACKAGE_CONFIG TRUE PARENT_SCOPE) - if(TARGET ${target_shared}) - foreach(suffix ${ARROW_CONFIG_SUFFIXES}) - get_target_property(shared_lib ${target_shared} IMPORTED_LOCATION${suffix}) - if(shared_lib) - # Remove shared library version: - # libarrow.so.100.0.0 -> libarrow.so - # Because ARROW_HOME and pkg-config approaches don't add - # shared library version. - string(REGEX - REPLACE "(${CMAKE_SHARED_LIBRARY_SUFFIX})[.0-9]+$" "\\1" shared_lib - "${shared_lib}") - set(${prefix}_SHARED_LIB "${shared_lib}" PARENT_SCOPE) - break() - endif() - endforeach() - endif() - if(TARGET ${target_static}) - foreach(suffix ${ARROW_CONFIG_SUFFIXES}) - get_target_property(static_lib ${target_static} IMPORTED_LOCATION${suffix}) - if(static_lib) - set(${prefix}_STATIC_LIB "${static_lib}" PARENT_SCOPE) - break() - endif() - endforeach() - endif() - endif() -endmacro() - -# Internal macro only for arrow_find_package. -# -# Find package by pkg-config. -macro(arrow_find_package_pkg_config) - pkg_check_modules(${prefix}_PC ${pkg_config_name}) - if(${prefix}_PC_FOUND) - set(${prefix}_USE_PKG_CONFIG TRUE PARENT_SCOPE) - - set(include_dir "${${prefix}_PC_INCLUDEDIR}") - set(lib_dir "${${prefix}_PC_LIBDIR}") - set(shared_lib_paths "${${prefix}_PC_LINK_LIBRARIES}") - # Use the first shared library path as the IMPORTED_LOCATION - # for ${target_shared}. This assumes that the first shared library - # path is the shared library path for this module. - list(GET shared_lib_paths 0 first_shared_lib_path) - # Use the rest shared library paths as the INTERFACE_LINK_LIBRARIES - # for ${target_shared}. This assumes that the rest shared library - # paths are dependency library paths for this module. - list(LENGTH shared_lib_paths n_shared_lib_paths) - if(n_shared_lib_paths LESS_EQUAL 1) - set(rest_shared_lib_paths) - else() - list(SUBLIST - shared_lib_paths - 1 - -1 - rest_shared_lib_paths) - endif() - - set(${prefix}_VERSION "${${prefix}_PC_VERSION}" PARENT_SCOPE) - set(${prefix}_INCLUDE_DIR "${include_dir}" PARENT_SCOPE) - set(${prefix}_SHARED_LIB "${first_shared_lib_path}" PARENT_SCOPE) - - add_library(${target_shared} SHARED IMPORTED) - set_target_properties(${target_shared} - PROPERTIES INTERFACE_INCLUDE_DIRECTORIES - "${include_dir}" - INTERFACE_LINK_LIBRARIES - "${rest_shared_lib_paths}" - IMPORTED_LOCATION - "${first_shared_lib_path}") - - find_library(${prefix}_static_lib - NAMES "${static_lib_name}" - PATHS "${lib_dir}" - NO_DEFAULT_PATH) - set(static_lib "${${prefix}_static_lib}") - set(${prefix}_STATIC_LIB "${static_lib}" PARENT_SCOPE) - if(static_lib) - add_library(${target_static} STATIC IMPORTED) - set_target_properties(${target_static} - PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${include_dir}" - IMPORTED_LOCATION "${static_lib}") - endif() - endif() -endmacro() - -function(arrow_find_package - prefix - home - base_name - header_path - cmake_package_name - pkg_config_name) - arrow_build_shared_library_name(shared_lib_name ${base_name}) - arrow_build_import_library_name(import_lib_name ${base_name}) - arrow_build_static_library_name(static_lib_name ${base_name}) - - set(target_shared ${base_name}_shared) - set(target_static ${base_name}_static) - - if(home) - arrow_find_package_home() - set(${prefix}_FIND_APPROACH "HOME: ${home}" PARENT_SCOPE) - else() - arrow_find_package_cmake_package_configuration() - if(${cmake_package_name}_FOUND) - set(${prefix}_FIND_APPROACH - "CMake package configuration: ${cmake_package_name}" - PARENT_SCOPE) - else() - arrow_find_package_pkg_config() - set(${prefix}_FIND_APPROACH "pkg-config: ${pkg_config_name}" PARENT_SCOPE) - endif() - endif() - - if(NOT include_dir) - if(TARGET ${target_shared}) - get_target_property(include_dir ${target_shared} INTERFACE_INCLUDE_DIRECTORIES) - elseif(TARGET ${target_static}) - get_target_property(include_dir ${target_static} INTERFACE_INCLUDE_DIRECTORIES) - endif() - endif() - if(include_dir) - set(${prefix}_INCLUDE_DIR "${include_dir}" PARENT_SCOPE) - endif() - - if(shared_lib) - get_filename_component(lib_dir "${shared_lib}" DIRECTORY) - elseif(static_lib) - get_filename_component(lib_dir "${static_lib}" DIRECTORY) - else() - set(lib_dir NOTFOUND) - endif() - set(${prefix}_LIB_DIR "${lib_dir}" PARENT_SCOPE) - # For backward compatibility - set(${prefix}_LIBS "${lib_dir}" PARENT_SCOPE) -endfunction() - -if(NOT "$ENV{ARROW_HOME}" STREQUAL "") - file(TO_CMAKE_PATH "$ENV{ARROW_HOME}" ARROW_HOME) -endif() -arrow_find_package(ARROW - "${ARROW_HOME}" - arrow - arrow/api.h - Arrow - arrow) - -if(ARROW_HOME) - if(ARROW_INCLUDE_DIR) - file(READ "${ARROW_INCLUDE_DIR}/arrow/util/config.h" ARROW_CONFIG_H_CONTENT) - arrow_extract_macro_value(ARROW_VERSION_MAJOR "ARROW_VERSION_MAJOR" - "${ARROW_CONFIG_H_CONTENT}") - arrow_extract_macro_value(ARROW_VERSION_MINOR "ARROW_VERSION_MINOR" - "${ARROW_CONFIG_H_CONTENT}") - arrow_extract_macro_value(ARROW_VERSION_PATCH "ARROW_VERSION_PATCH" - "${ARROW_CONFIG_H_CONTENT}") - if("${ARROW_VERSION_MAJOR}" STREQUAL "" - OR "${ARROW_VERSION_MINOR}" STREQUAL "" - OR "${ARROW_VERSION_PATCH}" STREQUAL "") - set(ARROW_VERSION "0.0.0") - else() - set(ARROW_VERSION - "${ARROW_VERSION_MAJOR}.${ARROW_VERSION_MINOR}.${ARROW_VERSION_PATCH}") - endif() - - arrow_extract_macro_value(ARROW_SO_VERSION_QUOTED "ARROW_SO_VERSION" - "${ARROW_CONFIG_H_CONTENT}") - string(REGEX REPLACE "^\"(.+)\"$" "\\1" ARROW_SO_VERSION "${ARROW_SO_VERSION_QUOTED}") - arrow_extract_macro_value(ARROW_FULL_SO_VERSION_QUOTED "ARROW_FULL_SO_VERSION" - "${ARROW_CONFIG_H_CONTENT}") - string(REGEX - REPLACE "^\"(.+)\"$" "\\1" ARROW_FULL_SO_VERSION - "${ARROW_FULL_SO_VERSION_QUOTED}") - endif() -else() - if(ARROW_USE_CMAKE_PACKAGE_CONFIG) - find_package(Arrow CONFIG) - elseif(ARROW_USE_PKG_CONFIG) - pkg_get_variable(ARROW_SO_VERSION arrow so_version) - pkg_get_variable(ARROW_FULL_SO_VERSION arrow full_so_version) - endif() -endif() - -set(ARROW_ABI_VERSION ${ARROW_SO_VERSION}) - -mark_as_advanced(ARROW_ABI_VERSION - ARROW_CONFIG_SUFFIXES - ARROW_FULL_SO_VERSION - ARROW_IMPORT_LIB - ARROW_INCLUDE_DIR - ARROW_LIBS - ARROW_LIB_DIR - ARROW_SEARCH_LIB_PATH_SUFFIXES - ARROW_SHARED_IMP_LIB - ARROW_SHARED_LIB - ARROW_SO_VERSION - ARROW_STATIC_LIB - ARROW_VERSION - ARROW_VERSION_MAJOR - ARROW_VERSION_MINOR - ARROW_VERSION_PATCH) - -find_package_handle_standard_args(Arrow REQUIRED_VARS - # The first required variable is shown - # in the found message. So this list is - # not sorted alphabetically. - ARROW_INCLUDE_DIR - ARROW_LIB_DIR - ARROW_FULL_SO_VERSION - ARROW_SO_VERSION - VERSION_VAR - ARROW_VERSION) -set(ARROW_FOUND ${Arrow_FOUND}) - -if(Arrow_FOUND AND NOT Arrow_FIND_QUIETLY) - message(STATUS "Arrow version: ${ARROW_VERSION} (${ARROW_FIND_APPROACH})") - message(STATUS "Arrow SO and ABI version: ${ARROW_SO_VERSION}") - message(STATUS "Arrow full SO version: ${ARROW_FULL_SO_VERSION}") - message(STATUS "Found the Arrow core shared library: ${ARROW_SHARED_LIB}") - message(STATUS "Found the Arrow core import library: ${ARROW_IMPORT_LIB}") - message(STATUS "Found the Arrow core static library: ${ARROW_STATIC_LIB}") -endif() diff --git a/core/src/index/cmake/FindOpenBLAS.cmake b/core/src/index/cmake/FindOpenBLAS.cmake deleted file mode 100644 index f8936889da..0000000000 --- a/core/src/index/cmake/FindOpenBLAS.cmake +++ /dev/null @@ -1,93 +0,0 @@ - -if (OpenBLAS_FOUND) # the git version propose a OpenBLASConfig.cmake - message(STATUS "OpenBLASConfig found") - set(OpenBLAS_INCLUDE_DIR ${OpenBLAS_INCLUDE_DIRS}) -else() - message("OpenBLASConfig not found") - unset(OpenBLAS_DIR CACHE) - set(OpenBLAS_INCLUDE_SEARCH_PATHS - /usr/local/openblas/include - /usr/include - /usr/include/openblas - /usr/include/openblas-base - /usr/local/include - /usr/local/include/openblas - /usr/local/include/openblas-base - /opt/OpenBLAS/include - /usr/local/opt/openblas/include - $ENV{OpenBLAS_HOME} - $ENV{OpenBLAS_HOME}/include - ) - - set(OpenBLAS_LIB_SEARCH_PATHS - /usr/local/openblas/lib - /lib/ - /lib/openblas-base - /lib64/ - /usr/lib - /usr/lib/openblas-base - /usr/lib64 - /usr/local/lib - /usr/local/lib64 - /usr/local/opt/openblas/lib - /opt/OpenBLAS/lib - $ENV{OpenBLAS} - $ENV{OpenBLAS}/lib - $ENV{OpenBLAS_HOME} - $ENV{OpenBLAS_HOME}/lib - ) - set(DEFAULT_OpenBLAS_LIB_PATH - /usr/local/openblas/lib - ${OPENBLAS_PREFIX}/lib) - - message("DEFAULT_OpenBLAS_LIB_PATH: ${DEFAULT_OpenBLAS_LIB_PATH}") - find_path(OpenBLAS_INCLUDE_DIR NAMES openblas_config.h lapacke.h PATHS ${OpenBLAS_INCLUDE_SEARCH_PATHS}) - find_library(OpenBLAS_LIB NAMES openblas PATHS ${DEFAULT_OpenBLAS_LIB_PATH} NO_DEFAULT_PATH) - find_library(OpenBLAS_LIB NAMES openblas PATHS ${OpenBLAS_LIB_SEARCH_PATHS}) - # mostly for debian - find_library(Lapacke_LIB NAMES lapacke PATHS ${DEFAULT_OpenBLAS_LIB_PATH} NO_DEFAULT_PATH) - find_library(Lapacke_LIB NAMES lapacke PATHS ${OpenBLAS_LIB_SEARCH_PATHS}) - - set(OpenBLAS_FOUND ON) - - # Check include files - if(NOT OpenBLAS_INCLUDE_DIR) - set(OpenBLAS_FOUND OFF) - message(STATUS "Could not find OpenBLAS include. Turning OpenBLAS_FOUND off") - else() - message(STATUS "find OpenBLAS include:${OpenBLAS_INCLUDE_DIR} ") - endif() - - # Check libraries - if(NOT OpenBLAS_LIB) - set(OpenBLAS_FOUND OFF) - message(STATUS "Could not find OpenBLAS lib. Turning OpenBLAS_FOUND off") - else() - message(STATUS "find OpenBLAS lib:${OpenBLAS_LIB} ") - endif() - - if (OpenBLAS_FOUND) - set(FOUND_OPENBLAS "true" PARENT_SCOPE) - set(OpenBLAS_LIBRARIES ${OpenBLAS_LIB}) - STRING(REGEX REPLACE "/libopenblas.so" "" OpenBLAS_LIB_DIR ${OpenBLAS_LIBRARIES}) - message(STATUS "find OpenBLAS libraries:${OpenBLAS_LIBRARIES} ") - if (Lapacke_LIB) - set(OpenBLAS_LIBRARIES ${OpenBLAS_LIBRARIES} ${Lapacke_LIB}) - endif() - if (NOT OpenBLAS_FIND_QUIETLY) - message(STATUS "Found OpenBLAS libraries: ${OpenBLAS_LIBRARIES}") - message(STATUS "Found OpenBLAS include: ${OpenBLAS_INCLUDE_DIR}") - endif() - else() - set(FOUND_OPENBLAS "false" PARENT_SCOPE) - if (OpenBLAS_FIND_REQUIRED) - message(FATAL_ERROR "Could not find OpenBLAS") - endif() - endif() -endif() - -mark_as_advanced( - OpenBLAS_INCLUDE_DIR - OpenBLAS_LIBRARIES - OpenBLAS_LIB_DIR -) diff --git a/core/src/index/cmake/ThirdPartyPackagesCore.cmake b/core/src/index/cmake/ThirdPartyPackagesCore.cmake deleted file mode 100644 index 40caeaffd1..0000000000 --- a/core/src/index/cmake/ThirdPartyPackagesCore.cmake +++ /dev/null @@ -1,627 +0,0 @@ -# Copyright (C) 2019-2020 Zilliz. All rights reserved. -# -# Licensed 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 limitations under the License. - -set(KNOWHERE_THIRDPARTY_DEPENDENCIES - Arrow - FAISS - GTest - OpenBLAS - MKL - ) - -message(STATUS "Using ${KNOWHERE_DEPENDENCY_SOURCE} approach to find dependencies") - -# For each dependency, set dependency source to global default, if unset -foreach (DEPENDENCY ${KNOWHERE_THIRDPARTY_DEPENDENCIES}) - if ("${${DEPENDENCY}_SOURCE}" STREQUAL "") - set(${DEPENDENCY}_SOURCE ${KNOWHERE_DEPENDENCY_SOURCE}) - endif () -endforeach () - -macro(build_dependency DEPENDENCY_NAME) - if ("${DEPENDENCY_NAME}" STREQUAL "Arrow") - build_arrow() - elseif ("${DEPENDENCY_NAME}" STREQUAL "GTest") - build_gtest() - elseif ("${DEPENDENCY_NAME}" STREQUAL "OpenBLAS") - build_openblas() - elseif ("${DEPENDENCY_NAME}" STREQUAL "FAISS") - build_faiss() - elseif ("${DEPENDENCY_NAME}" STREQUAL "MKL") - build_mkl() - else () - message(FATAL_ERROR "Unknown thirdparty dependency to build: ${DEPENDENCY_NAME}") - endif () -endmacro() - -macro(resolve_dependency DEPENDENCY_NAME) - if (${DEPENDENCY_NAME}_SOURCE STREQUAL "AUTO") - find_package(${DEPENDENCY_NAME} MODULE) - if (NOT ${${DEPENDENCY_NAME}_FOUND}) - build_dependency(${DEPENDENCY_NAME}) - endif () - elseif (${DEPENDENCY_NAME}_SOURCE STREQUAL "BUNDLED") - build_dependency(${DEPENDENCY_NAME}) - elseif (${DEPENDENCY_NAME}_SOURCE STREQUAL "SYSTEM") - find_package(${DEPENDENCY_NAME} REQUIRED) - endif () -endmacro() - -# ---------------------------------------------------------------------- -# Identify OS -if (UNIX) - if (APPLE) - set(CMAKE_OS_NAME "osx" CACHE STRING "Operating system name" FORCE) - else (APPLE) - ## Check for Debian GNU/Linux ________________ - find_file(DEBIAN_FOUND debian_version debconf.conf - PATHS /etc - ) - if (DEBIAN_FOUND) - set(CMAKE_OS_NAME "debian" CACHE STRING "Operating system name" FORCE) - endif (DEBIAN_FOUND) - ## Check for Fedora _________________________ - find_file(FEDORA_FOUND fedora-release - PATHS /etc - ) - if (FEDORA_FOUND) - set(CMAKE_OS_NAME "fedora" CACHE STRING "Operating system name" FORCE) - endif (FEDORA_FOUND) - ## Check for RedHat _________________________ - find_file(REDHAT_FOUND redhat-release inittab.RH - PATHS /etc - ) - if (REDHAT_FOUND) - set(CMAKE_OS_NAME "redhat" CACHE STRING "Operating system name" FORCE) - endif (REDHAT_FOUND) - ## Extra check for Ubuntu ____________________ - if (DEBIAN_FOUND) - ## At its core Ubuntu is a Debian system, with - ## a slightly altered configuration; hence from - ## a first superficial inspection a system will - ## be considered as Debian, which signifies an - ## extra check is required. - find_file(UBUNTU_EXTRA legal issue - PATHS /etc - ) - if (UBUNTU_EXTRA) - ## Scan contents of file - file(STRINGS ${UBUNTU_EXTRA} UBUNTU_FOUND - REGEX Ubuntu - ) - ## Check result of string search - if (UBUNTU_FOUND) - set(CMAKE_OS_NAME "ubuntu" CACHE STRING "Operating system name" FORCE) - set(DEBIAN_FOUND FALSE) - endif (UBUNTU_FOUND) - endif (UBUNTU_EXTRA) - endif (DEBIAN_FOUND) - endif (APPLE) -endif (UNIX) - - -# ---------------------------------------------------------------------- -# thirdparty directory -set(THIRDPARTY_DIR "${INDEX_SOURCE_DIR}/thirdparty") - -# ---------------------------------------------------------------------- -# ExternalProject options - -string(TOUPPER ${CMAKE_BUILD_TYPE} UPPERCASE_BUILD_TYPE) - -set(EP_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${UPPERCASE_BUILD_TYPE}}") -set(EP_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${UPPERCASE_BUILD_TYPE}}") - -if (NOT MSVC) - # Set -fPIC on all external projects - set(EP_CXX_FLAGS "${EP_CXX_FLAGS} -fPIC") - set(EP_C_FLAGS "${EP_C_FLAGS} -fPIC") -endif () - -# CC/CXX environment variables are captured on the first invocation of the -# builder (e.g make or ninja) instead of when CMake is invoked into to build -# directory. This leads to issues if the variables are exported in a subshell -# and the invocation of make/ninja is in distinct subshell without the same -# environment (CC/CXX). -set(EP_COMMON_TOOLCHAIN -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} - -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}) - -if (CMAKE_AR) - set(EP_COMMON_TOOLCHAIN ${EP_COMMON_TOOLCHAIN} -DCMAKE_AR=${CMAKE_AR}) -endif () - -if (CMAKE_RANLIB) - set(EP_COMMON_TOOLCHAIN ${EP_COMMON_TOOLCHAIN} -DCMAKE_RANLIB=${CMAKE_RANLIB}) -endif () - -# External projects are still able to override the following declarations. -# cmake command line will favor the last defined variable when a duplicate is -# encountered. This requires that `EP_COMMON_CMAKE_ARGS` is always the first -# argument. -set(EP_COMMON_CMAKE_ARGS - ${EP_COMMON_TOOLCHAIN} - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} - -DCMAKE_C_FLAGS=${EP_C_FLAGS} - -DCMAKE_C_FLAGS_${UPPERCASE_BUILD_TYPE}=${EP_C_FLAGS} - -DCMAKE_CXX_FLAGS=${EP_CXX_FLAGS} - -DCMAKE_CXX_FLAGS_${UPPERCASE_BUILD_TYPE}=${EP_CXX_FLAGS}) - -if (NOT KNOWHERE_VERBOSE_THIRDPARTY_BUILD) - set(EP_LOG_OPTIONS LOG_CONFIGURE 1 LOG_BUILD 1 LOG_INSTALL 1 LOG_DOWNLOAD 1) -else () - set(EP_LOG_OPTIONS) -endif () - -# Ensure that a default make is set -if ("${MAKE}" STREQUAL "") - if (NOT MSVC) - find_program(MAKE make) - endif () -endif () - -set(MAKE_BUILD_ARGS "-j8") - - -# ---------------------------------------------------------------------- -# Find pthreads - -set(THREADS_PREFER_PTHREAD_FLAG ON) -find_package(Threads REQUIRED) - -# ---------------------------------------------------------------------- -# Versions and URLs for toolchain builds, which also can be used to configure -# offline builds - -# Read toolchain versions from cpp/thirdparty/versions.txt -file(STRINGS "${THIRDPARTY_DIR}/versions.txt" TOOLCHAIN_VERSIONS_TXT) -foreach (_VERSION_ENTRY ${TOOLCHAIN_VERSIONS_TXT}) - # Exclude comments - if (NOT _VERSION_ENTRY MATCHES "^[^#][A-Za-z0-9-_]+_VERSION=") - continue() - endif () - - string(REGEX MATCH "^[^=]*" _LIB_NAME ${_VERSION_ENTRY}) - string(REPLACE "${_LIB_NAME}=" "" _LIB_VERSION ${_VERSION_ENTRY}) - - # Skip blank or malformed lines - if (${_LIB_VERSION} STREQUAL "") - continue() - endif () - - # For debugging - #message(STATUS "${_LIB_NAME}: ${_LIB_VERSION}") - - set(${_LIB_NAME} "${_LIB_VERSION}") -endforeach () - -set(FAISS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/faiss) - -if (DEFINED ENV{KNOWHERE_ARROW_URL}) - set(ARROW_SOURCE_URL "$ENV{KNOWHERE_ARROW_URL}") -else () - set(ARROW_SOURCE_URL - "https://github.com/apache/arrow.git" - ) -endif () - -if (DEFINED ENV{KNOWHERE_GTEST_URL}) - set(GTEST_SOURCE_URL "$ENV{KNOWHERE_GTEST_URL}") -else () - set(GTEST_SOURCE_URL - "https://github.com/google/googletest/archive/release-${GTEST_VERSION}.tar.gz") -endif () - -if (DEFINED ENV{KNOWHERE_OPENBLAS_URL}) - set(OPENBLAS_SOURCE_URL "$ENV{KNOWHERE_OPENBLAS_URL}") -else () - set(OPENBLAS_SOURCE_URL - "https://github.com/xianyi/OpenBLAS/archive/v${OPENBLAS_VERSION}.tar.gz") -endif () - -# ---------------------------------------------------------------------- -# ARROW -set(ARROW_PREFIX "${INDEX_BINARY_DIR}/arrow_ep-prefix/src/arrow_ep/cpp") - -macro(build_arrow) - message(STATUS "Building Apache ARROW-${ARROW_VERSION} from source") - set(ARROW_STATIC_LIB_NAME arrow) - set(ARROW_LIB_DIR "${ARROW_PREFIX}/lib") - set(ARROW_STATIC_LIB - "${ARROW_LIB_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${ARROW_STATIC_LIB_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX}" - ) - set(ARROW_INCLUDE_DIR "${ARROW_PREFIX}/include") - - set(ARROW_CMAKE_ARGS - ${EP_COMMON_CMAKE_ARGS} - -DARROW_BUILD_STATIC=ON - -DARROW_BUILD_SHARED=OFF - -DARROW_USE_GLOG=OFF - -DCMAKE_INSTALL_PREFIX=${ARROW_PREFIX} - -DCMAKE_INSTALL_LIBDIR=${ARROW_LIB_DIR} - -DARROW_CUDA=OFF - -DARROW_FLIGHT=OFF - -DARROW_GANDIVA=OFF - -DARROW_GANDIVA_JAVA=OFF - -DARROW_HDFS=OFF - -DARROW_HIVESERVER2=OFF - -DARROW_ORC=OFF - -DARROW_PARQUET=OFF - -DARROW_PLASMA=OFF - -DARROW_PLASMA_JAVA_CLIENT=OFF - -DARROW_PYTHON=OFF - -DARROW_WITH_BZ2=OFF - -DARROW_WITH_ZLIB=OFF - -DARROW_WITH_LZ4=OFF - -DARROW_WITH_SNAPPY=OFF - -DARROW_WITH_ZSTD=OFF - -DARROW_WITH_BROTLI=OFF - -DCMAKE_BUILD_TYPE=Release - -DARROW_DEPENDENCY_SOURCE=BUNDLED #Build all arrow dependencies from source instead of calling find_package first - -DBOOST_SOURCE=AUTO #try to find BOOST in the system default locations and build from source if not found - ) - - externalproject_add(arrow_ep - GIT_REPOSITORY - ${ARROW_SOURCE_URL} - GIT_TAG - ${ARROW_VERSION} - GIT_SHALLOW - TRUE - SOURCE_SUBDIR - cpp - ${EP_LOG_OPTIONS} - CMAKE_ARGS - ${ARROW_CMAKE_ARGS} - BUILD_COMMAND - "" - INSTALL_COMMAND - ${MAKE} ${MAKE_BUILD_ARGS} install - BUILD_BYPRODUCTS - "${ARROW_STATIC_LIB}" - ) - - file(MAKE_DIRECTORY "${ARROW_INCLUDE_DIR}") - add_library(arrow STATIC IMPORTED) - set_target_properties(arrow - PROPERTIES IMPORTED_LOCATION "${ARROW_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${ARROW_INCLUDE_DIR}") - add_dependencies(arrow arrow_ep) - - set(JEMALLOC_PREFIX "${INDEX_BINARY_DIR}/arrow_ep-prefix/src/arrow_ep-build/jemalloc_ep-prefix/src/jemalloc_ep") - - add_custom_command(TARGET arrow_ep POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory ${ARROW_LIB_DIR} - COMMAND ${CMAKE_COMMAND} -E copy ${JEMALLOC_PREFIX}/lib/libjemalloc_pic.a ${ARROW_LIB_DIR} - DEPENDS ${JEMALLOC_PREFIX}/lib/libjemalloc_pic.a) - -endmacro() - -if (KNOWHERE_WITH_ARROW AND NOT TARGET arrow_ep) - - resolve_dependency(Arrow) - - link_directories(SYSTEM ${ARROW_LIB_DIR}) - include_directories(SYSTEM ${ARROW_INCLUDE_DIR}) -endif () - -# ---------------------------------------------------------------------- -# OpenBLAS -set(OPENBLAS_PREFIX "${INDEX_BINARY_DIR}/openblas_ep-prefix/src/openblas_ep") -macro(build_openblas) - message(STATUS "Building OpenBLAS-${OPENBLAS_VERSION} from source") - set(OpenBLAS_INCLUDE_DIR "${OPENBLAS_PREFIX}/include") - set(OpenBLAS_LIB_DIR "${OPENBLAS_PREFIX}/lib") - set(OPENBLAS_SHARED_LIB - "${OPENBLAS_PREFIX}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}openblas${CMAKE_SHARED_LIBRARY_SUFFIX}") - set(OPENBLAS_STATIC_LIB - "${OPENBLAS_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}openblas${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(OPENBLAS_CMAKE_ARGS - ${EP_COMMON_CMAKE_ARGS} - -DCMAKE_BUILD_TYPE=Release - -DBUILD_SHARED_LIBS=ON - -DBUILD_STATIC_LIBS=ON - -DTARGET=CORE2 - -DDYNAMIC_ARCH=1 - -DDYNAMIC_OLDER=1 - -DUSE_THREAD=0 - -DUSE_OPENMP=0 - -DFC=gfortran - -DCC=gcc - -DINTERFACE64=0 - -DNUM_THREADS=128 - -DNO_LAPACKE=1 - "-DVERSION=${OPENBLAS_VERSION}" - "-DCMAKE_INSTALL_PREFIX=${OPENBLAS_PREFIX}" - -DCMAKE_INSTALL_LIBDIR=lib) - - externalproject_add(openblas_ep - URL - ${OPENBLAS_SOURCE_URL} - ${EP_LOG_OPTIONS} - CMAKE_ARGS - ${OPENBLAS_CMAKE_ARGS} - BUILD_COMMAND - ${MAKE} - ${MAKE_BUILD_ARGS} - BUILD_IN_SOURCE - 1 - INSTALL_COMMAND - ${MAKE} - PREFIX=${OPENBLAS_PREFIX} - install - BUILD_BYPRODUCTS - ${OPENBLAS_SHARED_LIB} - ${OPENBLAS_STATIC_LIB}) - - file(MAKE_DIRECTORY "${OpenBLAS_INCLUDE_DIR}") - add_library(openblas SHARED IMPORTED) - set_target_properties( - openblas - PROPERTIES - IMPORTED_LOCATION "${OPENBLAS_SHARED_LIB}" - LIBRARY_OUTPUT_NAME "openblas" - INTERFACE_INCLUDE_DIRECTORIES "${OpenBLAS_INCLUDE_DIR}") - add_dependencies(openblas openblas_ep) - get_target_property(OpenBLAS_INCLUDE_DIR openblas INTERFACE_INCLUDE_DIRECTORIES) - set(OpenBLAS_LIBRARIES "${OPENBLAS_SHARED_LIB}") -endmacro() - -if (KNOWHERE_WITH_OPENBLAS) - resolve_dependency(OpenBLAS) - include_directories(SYSTEM "${OpenBLAS_INCLUDE_DIR}") - link_directories(SYSTEM "${OpenBLAS_LIB_DIR}") -endif() - -# ---------------------------------------------------------------------- -# Google gtest - -macro(build_gtest) - message(STATUS "Building gtest-${GTEST_VERSION} from source") - set(GTEST_VENDORED TRUE) - set(GTEST_CMAKE_CXX_FLAGS "${EP_CXX_FLAGS}") - - if (APPLE) - set(GTEST_CMAKE_CXX_FLAGS - ${GTEST_CMAKE_CXX_FLAGS} - -DGTEST_USE_OWN_TR1_TUPLE=1 - -Wno-unused-value - -Wno-ignored-attributes) - endif () - - set(GTEST_PREFIX "${INDEX_BINARY_DIR}/googletest_ep-prefix/src/googletest_ep") - set(GTEST_INCLUDE_DIR "${GTEST_PREFIX}/include") - set(GTEST_STATIC_LIB - "${GTEST_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}") - set(GTEST_MAIN_STATIC_LIB - "${GTEST_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}gtest_main${CMAKE_STATIC_LIBRARY_SUFFIX}") - - set(GTEST_CMAKE_ARGS - ${EP_COMMON_CMAKE_ARGS} - "-DCMAKE_INSTALL_PREFIX=${GTEST_PREFIX}" - "-DCMAKE_INSTALL_LIBDIR=lib" - -DCMAKE_CXX_FLAGS=${GTEST_CMAKE_CXX_FLAGS} - -DCMAKE_BUILD_TYPE=Release) - - set(GMOCK_INCLUDE_DIR "${GTEST_PREFIX}/include") - set(GMOCK_STATIC_LIB - "${GTEST_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}gmock${CMAKE_STATIC_LIBRARY_SUFFIX}" - ) - - ExternalProject_Add(googletest_ep - URL - ${GTEST_SOURCE_URL} - BUILD_COMMAND - ${MAKE} - ${MAKE_BUILD_ARGS} - BUILD_BYPRODUCTS - ${GTEST_STATIC_LIB} - ${GTEST_MAIN_STATIC_LIB} - ${GMOCK_STATIC_LIB} - CMAKE_ARGS - ${GTEST_CMAKE_ARGS} - ${EP_LOG_OPTIONS}) - - # The include directory must exist before it is referenced by a target. - file(MAKE_DIRECTORY "${GTEST_INCLUDE_DIR}") - - add_library(gtest STATIC IMPORTED) - set_target_properties(gtest - PROPERTIES IMPORTED_LOCATION "${GTEST_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}") - - add_library(gtest_main STATIC IMPORTED) - set_target_properties(gtest_main - PROPERTIES IMPORTED_LOCATION "${GTEST_MAIN_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}") - - add_library(gmock STATIC IMPORTED) - set_target_properties(gmock - PROPERTIES IMPORTED_LOCATION "${GMOCK_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${GTEST_INCLUDE_DIR}") - - add_dependencies(gtest googletest_ep) - add_dependencies(gtest_main googletest_ep) - add_dependencies(gmock googletest_ep) - -endmacro() - -if (KNOWHERE_BUILD_TESTS AND NOT TARGET googletest_ep) - resolve_dependency(GTest) - - if (NOT GTEST_VENDORED) - endif () - - # TODO: Don't use global includes but rather target_include_directories - get_target_property(GTEST_INCLUDE_DIR gtest INTERFACE_INCLUDE_DIRECTORIES) - link_directories(SYSTEM "${GTEST_PREFIX}/lib") - include_directories(SYSTEM ${GTEST_INCLUDE_DIR}) -endif () - -# ---------------------------------------------------------------------- -# MKL - -macro(build_mkl) - - if (FAISS_WITH_MKL) - if (EXISTS "/proc/cpuinfo") - FILE(READ /proc/cpuinfo PROC_CPUINFO) - - SET(VENDOR_ID_RX "vendor_id[ \t]*:[ \t]*([a-zA-Z]+)\n") - STRING(REGEX MATCH "${VENDOR_ID_RX}" VENDOR_ID "${PROC_CPUINFO}") - STRING(REGEX REPLACE "${VENDOR_ID_RX}" "\\1" VENDOR_ID "${VENDOR_ID}") - - if (NOT ${VENDOR_ID} STREQUAL "GenuineIntel") - set(FAISS_WITH_MKL OFF) - endif () - endif () - - find_path(MKL_LIB_PATH - NAMES "libmkl_intel_ilp64.a" "libmkl_gnu_thread.a" "libmkl_core.a" - PATH_SUFFIXES "intel/compilers_and_libraries_${MKL_VERSION}/linux/mkl/lib/intel64/") - if (${MKL_LIB_PATH} STREQUAL "MKL_LIB_PATH-NOTFOUND") - message(FATAL_ERROR "Could not find MKL libraries") - endif () - message(STATUS "MKL lib path = ${MKL_LIB_PATH}") - - set(MKL_LIBS - ${MKL_LIB_PATH}/libmkl_intel_ilp64.a - ${MKL_LIB_PATH}/libmkl_gnu_thread.a - ${MKL_LIB_PATH}/libmkl_core.a - ) - endif () -endmacro() - -# ---------------------------------------------------------------------- -# FAISS - -macro(build_faiss) - message(STATUS "Building FAISS-${FAISS_VERSION} from source") - - set(FAISS_PREFIX "${INDEX_BINARY_DIR}/faiss_ep-prefix/src/faiss_ep") - set(FAISS_INCLUDE_DIR "${FAISS_PREFIX}/include") - set(FAISS_STATIC_LIB - "${FAISS_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}faiss${CMAKE_STATIC_LIBRARY_SUFFIX}") - - set(FAISS_CONFIGURE_ARGS - "--prefix=${FAISS_PREFIX}" - "CFLAGS=${EP_C_FLAGS}" - "CXXFLAGS=${EP_CXX_FLAGS} -mf16c -O3" - --without-python) - - if (FAISS_WITH_MKL) - set(FAISS_CONFIGURE_ARGS ${FAISS_CONFIGURE_ARGS} - "CPPFLAGS=-DFINTEGER=long -DMKL_ILP64 -m64 -I${MKL_LIB_PATH}/../../include" - "LDFLAGS=-L${MKL_LIB_PATH}" - ) - else () - message(STATUS "Build Faiss with OpenBlas/LAPACK") - if(OpenBLAS_FOUND) - set(FAISS_CONFIGURE_ARGS ${FAISS_CONFIGURE_ARGS} - "LDFLAGS=-L${OpenBLAS_LIB_DIR}") - else() - set(FAISS_CONFIGURE_ARGS ${FAISS_CONFIGURE_ARGS} - "LDFLAGS=-L${OPENBLAS_PREFIX}/lib") - endif() - endif () - - if (KNOWHERE_GPU_VERSION) - set(FAISS_CONFIGURE_ARGS ${FAISS_CONFIGURE_ARGS} - "--with-cuda=${CUDA_TOOLKIT_ROOT_DIR}" - "--with-cuda-arch=-gencode=arch=compute_60,code=sm_60 -gencode=arch=compute_61,code=sm_61 -gencode=arch=compute_70,code=sm_70 -gencode=arch=compute_75,code=sm_75" - ) - else () - set(FAISS_CONFIGURE_ARGS ${FAISS_CONFIGURE_ARGS} - "CPPFLAGS=-DUSE_CPU" - --without-cuda) - endif () - - message(STATUS "Building FAISS with configure args -${FAISS_CONFIGURE_ARGS}") - - if (DEFINED ENV{FAISS_SOURCE_URL}) - set(FAISS_SOURCE_URL "$ENV{FAISS_SOURCE_URL}") - externalproject_add(faiss_ep - URL - ${FAISS_SOURCE_URL} - ${EP_LOG_OPTIONS} - CONFIGURE_COMMAND - "./configure" - ${FAISS_CONFIGURE_ARGS} - BUILD_COMMAND - ${MAKE} ${MAKE_BUILD_ARGS} all - BUILD_IN_SOURCE - 1 - INSTALL_COMMAND - ${MAKE} install - BUILD_BYPRODUCTS - ${FAISS_STATIC_LIB}) - else () - externalproject_add(faiss_ep - DOWNLOAD_COMMAND - "" - SOURCE_DIR - ${FAISS_SOURCE_DIR} - ${EP_LOG_OPTIONS} - CONFIGURE_COMMAND - "./configure" - ${FAISS_CONFIGURE_ARGS} - BUILD_COMMAND - ${MAKE} ${MAKE_BUILD_ARGS} all - BUILD_IN_SOURCE - 1 - INSTALL_COMMAND - ${MAKE} install - BUILD_BYPRODUCTS - ${FAISS_STATIC_LIB}) - endif () - - if(NOT OpenBLAS_FOUND) - message("add faiss dependencies: openblas_ep") - ExternalProject_Add_StepDependencies(faiss_ep configure openblas_ep) - endif() - - file(MAKE_DIRECTORY "${FAISS_INCLUDE_DIR}") - add_library(faiss STATIC IMPORTED) - - set_target_properties( - faiss - PROPERTIES - IMPORTED_LOCATION "${FAISS_STATIC_LIB}" - INTERFACE_INCLUDE_DIRECTORIES "${FAISS_INCLUDE_DIR}" - ) - if (FAISS_WITH_MKL) - set_target_properties( - faiss - PROPERTIES - INTERFACE_LINK_LIBRARIES "${MKL_LIBS}") - else () - set_target_properties( - faiss - PROPERTIES - INTERFACE_LINK_LIBRARIES "${OpenBLAS_LIBRARIES}") - endif () - - add_dependencies(faiss faiss_ep) - -endmacro() - -if (KNOWHERE_WITH_FAISS AND NOT TARGET faiss_ep) - - if (FAISS_WITH_MKL) - resolve_dependency(MKL) - else () - message("faiss with no mkl") - endif () - - resolve_dependency(FAISS) - get_target_property(FAISS_INCLUDE_DIR faiss INTERFACE_INCLUDE_DIRECTORIES) - include_directories(SYSTEM "${FAISS_INCLUDE_DIR}") - link_directories(SYSTEM ${FAISS_PREFIX}/lib/) -endif () diff --git a/core/src/index/knowhere/CMakeLists.txt b/core/src/index/knowhere/CMakeLists.txt deleted file mode 100644 index 28ba1b35df..0000000000 --- a/core/src/index/knowhere/CMakeLists.txt +++ /dev/null @@ -1,125 +0,0 @@ -include_directories(${INDEX_SOURCE_DIR}/knowhere) -include_directories(${INDEX_SOURCE_DIR}/thirdparty) -include_directories(${INDEX_SOURCE_DIR}/thirdparty/SPTAG/AnnService) - -set(SPTAG_SOURCE_DIR ${INDEX_SOURCE_DIR}/thirdparty/SPTAG) -file(GLOB HDR_FILES - ${SPTAG_SOURCE_DIR}/AnnService/inc/Core/*.h - ${SPTAG_SOURCE_DIR}/AnnService/inc/Core/Common/*.h - ${SPTAG_SOURCE_DIR}/AnnService/inc/Core/BKT/*.h - ${SPTAG_SOURCE_DIR}/AnnService/inc/Core/KDT/*.h - ${SPTAG_SOURCE_DIR}/AnnService/inc/Helper/*.h) -file(GLOB SRC_FILES - ${SPTAG_SOURCE_DIR}/AnnService/src/Core/*.cpp - ${SPTAG_SOURCE_DIR}/AnnService/src/Core/Common/*.cpp - ${SPTAG_SOURCE_DIR}/AnnService/src/Core/BKT/*.cpp - ${SPTAG_SOURCE_DIR}/AnnService/src/Core/KDT/*.cpp - ${SPTAG_SOURCE_DIR}/AnnService/src/Helper/*.cpp) - -if (NOT TARGET SPTAGLibStatic) - add_library(SPTAGLibStatic STATIC ${SRC_FILES} ${HDR_FILES}) -endif () - -set(external_srcs - knowhere/common/Exception.cpp - knowhere/common/Log.cpp - knowhere/common/Timer.cpp - ) - -set(index_srcs - knowhere/index/vector_index/adapter/SptagAdapter.cpp - knowhere/index/vector_index/adapter/VectorAdapter.cpp - knowhere/index/vector_index/helpers/FaissIO.cpp - knowhere/index/vector_index/helpers/IndexParameter.cpp - knowhere/index/vector_index/helpers/SPTAGParameterMgr.cpp - knowhere/index/vector_index/impl/nsg/Distance.cpp - knowhere/index/vector_index/impl/nsg/NSG.cpp - knowhere/index/vector_index/impl/nsg/NSGHelper.cpp - knowhere/index/vector_index/impl/nsg/NSGIO.cpp - knowhere/index/vector_index/ConfAdapter.cpp - knowhere/index/vector_index/ConfAdapterMgr.cpp - knowhere/index/vector_index/FaissBaseBinaryIndex.cpp - knowhere/index/vector_index/FaissBaseIndex.cpp - knowhere/index/vector_index/IndexBinaryIDMAP.cpp - knowhere/index/vector_index/IndexBinaryIVF.cpp - knowhere/index/vector_index/IndexHNSW.cpp - knowhere/index/vector_index/IndexIDMAP.cpp - knowhere/index/vector_index/IndexIVF.cpp - knowhere/index/vector_index/IndexIVFPQ.cpp - knowhere/index/vector_index/IndexIVFSQ.cpp - knowhere/index/vector_index/IndexNSG.cpp - knowhere/index/vector_index/IndexSPTAG.cpp - knowhere/index/vector_index/IndexType.cpp - knowhere/index/vector_index/VecIndexFactory.cpp - knowhere/index/vector_index/IndexAnnoy.cpp - ) - -set(depend_libs - SPTAGLibStatic - faiss - gomp - gfortran - pthread - ) -if (FAISS_WITH_MKL) - set(depend_libs ${depend_libs} - "-Wl,--start-group \ - ${MKL_LIB_PATH}/libmkl_intel_ilp64.a \ - ${MKL_LIB_PATH}/libmkl_gnu_thread.a \ - ${MKL_LIB_PATH}/libmkl_core.a \ - -Wl,--end-group -lgomp -lpthread -lm -ldl" - ) -else () - set(depend_libs ${depend_libs} - ${OpenBLAS_LIBRARIES} - ${LAPACK_LIBRARIES} - ) -endif () - -if (KNOWHERE_GPU_VERSION) - include_directories(${CUDA_INCLUDE_DIRS}) - link_directories("${CUDA_TOOLKIT_ROOT_DIR}/lib64") - set(cuda_lib - cudart - cublas - ) - set(depend_libs ${depend_libs} - ${cuda_lib} - ) - - set(index_srcs ${index_srcs} - knowhere/index/vector_index/gpu/IndexGPUIDMAP.cpp - knowhere/index/vector_index/gpu/IndexGPUIVF.cpp - knowhere/index/vector_index/gpu/IndexGPUIVFPQ.cpp - knowhere/index/vector_index/gpu/IndexGPUIVFSQ.cpp - knowhere/index/vector_index/gpu/IndexIVFSQHybrid.cpp - knowhere/index/vector_index/helpers/Cloner.cpp - knowhere/index/vector_index/helpers/FaissGpuResourceMgr.cpp - ) - -endif () - -if (NOT TARGET knowhere) - add_library( - knowhere STATIC - ${external_srcs} - ${index_srcs} - ) -endif () - -target_link_libraries( - knowhere - ${depend_libs} -) - -set(INDEX_INCLUDE_DIRS - ${INDEX_SOURCE_DIR}/knowhere - ${INDEX_SOURCE_DIR}/thirdparty - ${INDEX_SOURCE_DIR}/thirdparty/SPTAG/AnnService -# ${ARROW_INCLUDE_DIR} - ${FAISS_INCLUDE_DIR} - ${OpenBLAS_INCLUDE_DIR} - ${LAPACK_INCLUDE_DIR} - ) - -set(INDEX_INCLUDE_DIRS ${INDEX_INCLUDE_DIRS} PARENT_SCOPE) diff --git a/core/src/index/knowhere/knowhere/common/BinarySet.h b/core/src/index/knowhere/knowhere/common/BinarySet.h deleted file mode 100644 index ca9df76ab6..0000000000 --- a/core/src/index/knowhere/knowhere/common/BinarySet.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace milvus { -namespace knowhere { - -struct Binary { - std::shared_ptr data; - int64_t size = 0; -}; -using BinaryPtr = std::shared_ptr; - -inline uint8_t* -CopyBinary(const BinaryPtr& bin) { - uint8_t* newdata = new uint8_t[bin->size]; - memcpy(newdata, bin->data.get(), bin->size); - return newdata; -} - -class BinarySet { - public: - BinaryPtr - GetByName(const std::string& name) const { - return binary_map_.at(name); - } - - void - Append(const std::string& name, BinaryPtr binary) { - binary_map_[name] = std::move(binary); - } - - void - Append(const std::string& name, std::shared_ptr data, int64_t size) { - auto binary = std::make_shared(); - binary->data = data; - binary->size = size; - binary_map_[name] = std::move(binary); - } - - // void - // Append(const std::string &name, void *data, int64_t size, ID id) { - // Binary binary; - // binary.data = data; - // binary.size = size; - // binary.id = id; - // binary_map_[name] = binary; - //} - - void - clear() { - binary_map_.clear(); - } - - public: - std::map binary_map_; -}; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/common/Config.h b/core/src/index/knowhere/knowhere/common/Config.h deleted file mode 100644 index c0dda5f3ce..0000000000 --- a/core/src/index/knowhere/knowhere/common/Config.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "src/utils/Json.h" - -namespace milvus { -namespace knowhere { - -using Config = milvus::json; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/common/Dataset.h b/core/src/index/knowhere/knowhere/common/Dataset.h deleted file mode 100644 index 2d742799f9..0000000000 --- a/core/src/index/knowhere/knowhere/common/Dataset.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace milvus { -namespace knowhere { - -using Value = std::any; -using ValuePtr = std::shared_ptr; - -class Dataset { - public: - Dataset() = default; - - template - void - Set(const std::string& k, T&& v) { - std::lock_guard lk(mutex_); - data_[k] = std::make_shared(std::forward(v)); - } - - template - T - Get(const std::string& k) { - std::lock_guard lk(mutex_); - try { - return std::any_cast(*(data_.at(k))); - } catch (...) { - throw std::logic_error("Can't find this key"); - } - } - - const std::map& - data() const { - return data_; - } - - private: - std::mutex mutex_; - std::map data_; -}; -using DatasetPtr = std::shared_ptr; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/common/Exception.cpp b/core/src/index/knowhere/knowhere/common/Exception.cpp deleted file mode 100644 index 7e849f7d90..0000000000 --- a/core/src/index/knowhere/knowhere/common/Exception.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include - -#include "Log.h" -#include "knowhere/common/Exception.h" - -namespace milvus { -namespace knowhere { - -KnowhereException::KnowhereException(const std::string& msg) : msg(msg) { -} - -KnowhereException::KnowhereException(const std::string& m, const char* funcName, const char* file, int line) { -#ifdef DEBUG - int size = snprintf(nullptr, 0, "Error in %s at %s:%d: %s", funcName, file, line, m.c_str()); - msg.resize(size + 1); - snprintf(&msg[0], msg.size(), "Error in %s at %s:%d: %s", funcName, file, line, m.c_str()); -#else - std::string file_path(file); - auto const pos = file_path.find_last_of('/'); - auto filename = file_path.substr(pos + 1).c_str(); - - int size = snprintf(nullptr, 0, "Error in %s at %s:%d: %s", funcName, filename, line, m.c_str()); - msg.resize(size + 1); - snprintf(&msg[0], msg.size(), "Error in %s at %s:%d: %s", funcName, filename, line, m.c_str()); -#endif -} - -const char* -KnowhereException::what() const noexcept { - return msg.c_str(); -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/common/Exception.h b/core/src/index/knowhere/knowhere/common/Exception.h deleted file mode 100644 index 76a08d1a90..0000000000 --- a/core/src/index/knowhere/knowhere/common/Exception.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include - -namespace milvus { -namespace knowhere { - -class KnowhereException : public std::exception { - public: - explicit KnowhereException(const std::string& msg); - - KnowhereException(const std::string& msg, const char* funName, const char* file, int line); - - const char* - what() const noexcept override; - - std::string msg; -}; - -#define KNOHWERE_ERROR_MSG(MSG) printf("%s", KnowhereException(MSG, __PRETTY_FUNCTION__, __FILE__, __LINE__).what()) - -#define KNOWHERE_THROW_MSG(MSG) \ - do { \ - throw KnowhereException(MSG, __PRETTY_FUNCTION__, __FILE__, __LINE__); \ - } while (false) - -#define KNOHERE_THROW_FORMAT(FMT, ...) \ - do { \ - std::string __s; \ - int __size = snprintf(nullptr, 0, FMT, __VA_ARGS__); \ - __s.resize(__size + 1); \ - snprintf(&__s[0], __s.size(), FMT, __VA_ARGS__); \ - throw faiss::FaissException(__s, __PRETTY_FUNCTION__, __FILE__, __LINE__); \ - } while (false) - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/common/Log.cpp b/core/src/index/knowhere/knowhere/common/Log.cpp deleted file mode 100644 index 9f112c55cd..0000000000 --- a/core/src/index/knowhere/knowhere/common/Log.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "knowhere/common/Log.h" - -#include -#include -#include -#include - -namespace milvus { -namespace knowhere { - -std::string -LogOut(const char* pattern, ...) { - size_t len = strnlen(pattern, 1024) + 256; - auto str_p = std::make_unique(len); - memset(str_p.get(), 0, len); - - va_list vl; - va_start(vl, pattern); - vsnprintf(str_p.get(), len, pattern, vl); - va_end(vl); - - return std::string(str_p.get()); -} - -void -SetThreadName(const std::string& name) { - pthread_setname_np(pthread_self(), name.c_str()); -} - -std::string -GetThreadName() { - std::string thread_name = "unamed"; - char name[16]; - size_t len = 16; - auto err = pthread_getname_np(pthread_self(), name, len); - if (not err) { - thread_name = name; - } - - return thread_name; -} - -void -log_trace_(const std::string& s) { - LOG_KNOWHERE_TRACE_ << s; -} - -void -log_debug_(const std::string& s) { - LOG_KNOWHERE_DEBUG_ << s; -} - -void -log_info_(const std::string& s) { - LOG_KNOWHERE_INFO_ << s; -} - -void -log_warning_(const std::string& s) { - LOG_KNOWHERE_WARNING_ << s; -} - -void -log_error_(const std::string& s) { - LOG_KNOWHERE_ERROR_ << s; -} - -void -log_fatal_(const std::string& s) { - LOG_KNOWHERE_FATAL_ << s; -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/common/Log.h b/core/src/index/knowhere/knowhere/common/Log.h deleted file mode 100644 index db1677e320..0000000000 --- a/core/src/index/knowhere/knowhere/common/Log.h +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -#include "easyloggingpp/easylogging++.h" - -namespace milvus { -namespace knowhere { - -std::string -LogOut(const char* pattern, ...); - -void -SetThreadName(const std::string& name); - -std::string -GetThreadName(); - -void -log_trace_(const std::string&); - -void -log_debug_(const std::string&); - -void -log_info_(const std::string&); - -void -log_warning_(const std::string&); - -void -log_error_(const std::string&); - -void -log_fatal_(const std::string&); - -/* - * Please use LOG_MODULE_LEVEL_C macro in member function of class - * and LOG_MODULE_LEVEL_ macro in other functions. - */ - -///////////////////////////////////////////////////////////////////////////////////////////////// -#define KNOWHERE_MODULE_NAME "KNOWHERE" -#define KNOWHERE_MODULE_CLASS_FUNCTION \ - LogOut("[%s][%s::%s][%s] ", KNOWHERE_MODULE_NAME, (typeid(*this).name()), __FUNCTION__, GetThreadName().c_str()) -#define KNOWHERE_MODULE_FUNCTION LogOut("[%s][%s][%s] ", KNOWHERE_MODULE_NAME, __FUNCTION__, GetThreadName().c_str()) - -#define LOG_KNOWHERE_TRACE_C LOG(TRACE) << KNOWHERE_MODULE_CLASS_FUNCTION -#define LOG_KNOWHERE_DEBUG_C LOG(DEBUG) << KNOWHERE_MODULE_CLASS_FUNCTION -#define LOG_KNOWHERE_INFO_C LOG(INFO) << KNOWHERE_MODULE_CLASS_FUNCTION -#define LOG_KNOWHERE_WARNING_C LOG(WARNING) << KNOWHERE_MODULE_CLASS_FUNCTION -#define LOG_KNOWHERE_ERROR_C LOG(ERROR) << KNOWHERE_MODULE_CLASS_FUNCTION -#define LOG_KNOWHERE_FATAL_C LOG(FATAL) << KNOWHERE_MODULE_CLASS_FUNCTION - -#define LOG_KNOWHERE_TRACE_ LOG(TRACE) << KNOWHERE_MODULE_FUNCTION -#define LOG_KNOWHERE_DEBUG_ LOG(DEBUG) << KNOWHERE_MODULE_FUNCTION -#define LOG_KNOWHERE_INFO_ LOG(INFO) << KNOWHERE_MODULE_FUNCTION -#define LOG_KNOWHERE_WARNING_ LOG(WARNING) << KNOWHERE_MODULE_FUNCTION -#define LOG_KNOWHERE_ERROR_ LOG(ERROR) << KNOWHERE_MODULE_FUNCTION -#define LOG_KNOWHERE_FATAL_ LOG(FATAL) << KNOWHERE_MODULE_FUNCTION - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/common/Timer.cpp b/core/src/index/knowhere/knowhere/common/Timer.cpp deleted file mode 100644 index ac4c4607ad..0000000000 --- a/core/src/index/knowhere/knowhere/common/Timer.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include // TODO(linxj): using Log instead - -#include "knowhere/common/Log.h" -#include "knowhere/common/Timer.h" - -namespace milvus { -namespace knowhere { - -TimeRecorder::TimeRecorder(const std::string& header, int64_t log_level) : header_(header), log_level_(log_level) { - start_ = last_ = stdclock::now(); -} - -TimeRecorder::~TimeRecorder() { -} - -std::string -TimeRecorder::GetTimeSpanStr(double span) { - std::string str_sec = std::to_string(span * 0.000001) + ((span > 1000000) ? " seconds" : " second"); - std::string str_ms = std::to_string(span * 0.001) + " ms"; - - return str_sec + " [" + str_ms + "]"; -} - -void -TimeRecorder::PrintTimeRecord(const std::string& msg, double span) { - std::string str_log; - if (!header_.empty()) - str_log += header_ + ": "; - str_log += msg; - str_log += " ("; - str_log += TimeRecorder::GetTimeSpanStr(span); - str_log += ")"; - - switch (log_level_) { - case 0: { - std::cout << str_log << std::endl; - break; - } - case 1: { - LOG_KNOWHERE_DEBUG_ << str_log; - break; - } - // case 2: { - // LOG_KNOWHERE_TRACE_ << str_log; - // break; - // } - // case 3: { - // LOG_KNOWHERE_WARNING_ << str_log; - // break; - // } - } -} - -double -TimeRecorder::RecordSection(const std::string& msg) { - stdclock::time_point curr = stdclock::now(); - double span = (std::chrono::duration(curr - last_)).count(); - last_ = curr; - - PrintTimeRecord(msg, span); - return span; -} - -double -TimeRecorder::ElapseFromBegin(const std::string& msg) { - stdclock::time_point curr = stdclock::now(); - double span = (std::chrono::duration(curr - start_)).count(); - - PrintTimeRecord(msg, span); - return span; -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/common/Timer.h b/core/src/index/knowhere/knowhere/common/Timer.h deleted file mode 100644 index 1e98c4ae99..0000000000 --- a/core/src/index/knowhere/knowhere/common/Timer.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include - -namespace milvus { -namespace knowhere { - -class TimeRecorder { - using stdclock = std::chrono::high_resolution_clock; - - public: - explicit TimeRecorder(const std::string& header, int64_t log_level = 0); - - ~TimeRecorder(); // trace = 0, debug = 1, info = 2, warn = 3, error = 4, critical = 5 - - double - RecordSection(const std::string& msg); - - double - ElapseFromBegin(const std::string& msg); - - static std::string - GetTimeSpanStr(double span); - - private: - void - PrintTimeRecord(const std::string& msg, double span); - - private: - std::string header_; - stdclock::time_point start_; - stdclock::time_point last_; - int64_t log_level_; -}; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/common/Typedef.h b/core/src/index/knowhere/knowhere/common/Typedef.h deleted file mode 100644 index 8b43a8159d..0000000000 --- a/core/src/index/knowhere/knowhere/common/Typedef.h +++ /dev/null @@ -1,29 +0,0 @@ - -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include - -namespace milvus { -namespace knowhere { - -using MetricType = std::string; -// using IndexType = std::string; -using IDType = int64_t; -using FloatType = float; -using BinaryType = uint8_t; -using GraphType = std::vector>; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/Index.h b/core/src/index/knowhere/knowhere/index/Index.h deleted file mode 100644 index c5a5047ba7..0000000000 --- a/core/src/index/knowhere/knowhere/index/Index.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include - -#include "cache/DataObj.h" -#include "knowhere/common/BinarySet.h" -#include "knowhere/common/Config.h" - -namespace milvus { -namespace knowhere { - -class Index : public milvus::cache::DataObj { - public: - virtual BinarySet - Serialize(const Config& config = Config()) = 0; - - virtual void - Load(const BinarySet&) = 0; -}; - -using IndexPtr = std::shared_ptr; - -// todo: remove from knowhere -class ToIndexData : public milvus::cache::DataObj { - public: - explicit ToIndexData(int64_t size) : size_(size) { - } - - private: - int64_t size_ = 0; -}; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/preprocessor/Preprocessor.h b/core/src/index/knowhere/knowhere/index/preprocessor/Preprocessor.h deleted file mode 100644 index 6cda87122f..0000000000 --- a/core/src/index/knowhere/knowhere/index/preprocessor/Preprocessor.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include - -#include "knowhere/common/Dataset.h" - -namespace milvus { -namespace knowhere { - -class Preprocessor { - public: - virtual DatasetPtr - Preprocess(const DatasetPtr& input) = 0; -}; - -using PreprocessorPtr = std::shared_ptr; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapter.cpp b/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapter.cpp deleted file mode 100644 index 682ff50795..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapter.cpp +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "knowhere/index/vector_index/ConfAdapter.h" -#include -#include -#include -#include -#include "knowhere/index/vector_index/helpers/IndexParameter.h" - -#ifdef MILVUS_GPU_VERSION -#include "faiss/gpu/utils/DeviceUtils.h" -#endif - -namespace milvus { -namespace knowhere { - -#define DEFAULT_MAX_DIM 32768 -#define DEFAULT_MIN_DIM 1 -#define DEFAULT_MAX_K 16384 -#define DEFAULT_MIN_K 1 -#define DEFAULT_MIN_ROWS 1 // minimum size for build index -#define DEFAULT_MAX_ROWS 50000000 - -#define CheckIntByRange(key, min, max) \ - if (!oricfg.contains(key) || !oricfg[key].is_number_integer() || oricfg[key].get() > max || \ - oricfg[key].get() < min) { \ - return false; \ - } - -#define CheckIntByValues(key, container) \ - if (!oricfg.contains(key) || !oricfg[key].is_number_integer()) { \ - return false; \ - } else { \ - auto finder = std::find(std::begin(container), std::end(container), oricfg[key].get()); \ - if (finder == std::end(container)) { \ - return false; \ - } \ - } - -#define CheckStrByValues(key, container) \ - if (!oricfg.contains(key) || !oricfg[key].is_string()) { \ - return false; \ - } else { \ - auto finder = std::find(std::begin(container), std::end(container), oricfg[key].get()); \ - if (finder == std::end(container)) { \ - return false; \ - } \ - } - -bool -ConfAdapter::CheckTrain(Config& oricfg, const IndexMode mode) { - static std::vector METRICS{knowhere::Metric::L2, knowhere::Metric::IP}; - CheckIntByRange(knowhere::meta::DIM, DEFAULT_MIN_DIM, DEFAULT_MAX_DIM); - CheckStrByValues(knowhere::Metric::TYPE, METRICS); - return true; -} - -bool -ConfAdapter::CheckSearch(Config& oricfg, const IndexType type, const IndexMode mode) { - CheckIntByRange(knowhere::meta::TOPK, DEFAULT_MIN_K - 1, DEFAULT_MAX_K); - return true; -} - -int64_t -MatchNlist(int64_t size, int64_t nlist) { - const int64_t TYPICAL_COUNT = 1000000; - const int64_t PER_NLIST = 16384; - - if (nlist * TYPICAL_COUNT > size * PER_NLIST) { - // nlist is too large, adjust to a proper value - nlist = std::max(1L, size * PER_NLIST / TYPICAL_COUNT); - } - return nlist; -} - -bool -IVFConfAdapter::CheckTrain(Config& oricfg, const IndexMode mode) { - static int64_t MAX_NLIST = 999999; - static int64_t MIN_NLIST = 1; - - CheckIntByRange(knowhere::IndexParams::nlist, MIN_NLIST, MAX_NLIST); - CheckIntByRange(knowhere::meta::ROWS, DEFAULT_MIN_ROWS, DEFAULT_MAX_ROWS); - - // int64_t nlist = oricfg[knowhere::IndexParams::nlist]; - // CheckIntByRange(knowhere::meta::ROWS, nlist, DEFAULT_MAX_ROWS); - - // auto tune params - int64_t nq = oricfg[knowhere::meta::ROWS].get(); - int64_t nlist = oricfg[knowhere::IndexParams::nlist].get(); - oricfg[knowhere::IndexParams::nlist] = MatchNlist(nq, nlist); - - // Best Practice - // static int64_t MIN_POINTS_PER_CENTROID = 40; - // static int64_t MAX_POINTS_PER_CENTROID = 256; - // CheckIntByRange(knowhere::meta::ROWS, MIN_POINTS_PER_CENTROID * nlist, MAX_POINTS_PER_CENTROID * nlist); - - return ConfAdapter::CheckTrain(oricfg, mode); -} - -bool -IVFConfAdapter::CheckSearch(Config& oricfg, const IndexType type, const IndexMode mode) { - static int64_t MIN_NPROBE = 1; - static int64_t MAX_NPROBE = 999999; // todo(linxj): [1, nlist] - - if (mode == IndexMode::MODE_GPU) { -#ifdef MILVUS_GPU_VERSION - CheckIntByRange(knowhere::IndexParams::nprobe, MIN_NPROBE, faiss::gpu::getMaxKSelection()); -#endif - } else { - CheckIntByRange(knowhere::IndexParams::nprobe, MIN_NPROBE, MAX_NPROBE); - } - - return ConfAdapter::CheckSearch(oricfg, type, mode); -} - -bool -IVFSQConfAdapter::CheckTrain(Config& oricfg, const IndexMode mode) { - static int64_t DEFAULT_NBITS = 8; - oricfg[knowhere::IndexParams::nbits] = DEFAULT_NBITS; - - return IVFConfAdapter::CheckTrain(oricfg, mode); -} - -bool -IVFPQConfAdapter::CheckTrain(Config& oricfg, const IndexMode mode) { - static int64_t DEFAULT_NBITS = 8; - static int64_t MAX_NLIST = 999999; - static int64_t MIN_NLIST = 1; - static std::vector METRICS{knowhere::Metric::L2, knowhere::Metric::IP}; - - oricfg[knowhere::IndexParams::nbits] = DEFAULT_NBITS; - - CheckStrByValues(knowhere::Metric::TYPE, METRICS); - CheckIntByRange(knowhere::meta::DIM, DEFAULT_MIN_DIM, DEFAULT_MAX_DIM); - CheckIntByRange(knowhere::meta::ROWS, DEFAULT_MIN_ROWS, DEFAULT_MAX_ROWS); - CheckIntByRange(knowhere::IndexParams::nlist, MIN_NLIST, MAX_NLIST); - - // int64_t nlist = oricfg[knowhere::IndexParams::nlist]; - // CheckIntByRange(knowhere::meta::ROWS, nlist, DEFAULT_MAX_ROWS); - - // auto tune params - oricfg[knowhere::IndexParams::nlist] = - MatchNlist(oricfg[knowhere::meta::ROWS].get(), oricfg[knowhere::IndexParams::nlist].get()); - - // Best Practice - // static int64_t MIN_POINTS_PER_CENTROID = 40; - // static int64_t MAX_POINTS_PER_CENTROID = 256; - // CheckIntByRange(knowhere::meta::ROWS, MIN_POINTS_PER_CENTROID * nlist, MAX_POINTS_PER_CENTROID * nlist); - - std::vector resset; - int64_t dimension = oricfg[knowhere::meta::DIM].get(); - IVFPQConfAdapter::GetValidMList(dimension, resset); - - CheckIntByValues(knowhere::IndexParams::m, resset); - - return true; -} - -void -IVFPQConfAdapter::GetValidMList(int64_t dimension, std::vector& resset) { - resset.clear(); - /* - * Faiss 1.6 - * Only 1, 2, 3, 4, 6, 8, 10, 12, 16, 20, 24, 28, 32 dims per sub-quantizer are currently supported with - * no precomputed codes. Precomputed codes supports any number of dimensions, but will involve memory overheads. - */ - static std::vector support_dim_per_subquantizer{32, 28, 24, 20, 16, 12, 10, 8, 6, 4, 3, 2, 1}; - static std::vector support_subquantizer{96, 64, 56, 48, 40, 32, 28, 24, 20, 16, 12, 8, 4, 3, 2, 1}; - - for (const auto& dimperquantizer : support_dim_per_subquantizer) { - if (!(dimension % dimperquantizer)) { - auto subquantzier_num = dimension / dimperquantizer; - auto finder = std::find(support_subquantizer.begin(), support_subquantizer.end(), subquantzier_num); - if (finder != support_subquantizer.end()) { - resset.push_back(subquantzier_num); - } - } - } -} - -bool -NSGConfAdapter::CheckTrain(Config& oricfg, const IndexMode mode) { - static int64_t MIN_KNNG = 5; - static int64_t MAX_KNNG = 300; - static int64_t MIN_SEARCH_LENGTH = 10; - static int64_t MAX_SEARCH_LENGTH = 300; - static int64_t MIN_OUT_DEGREE = 5; - static int64_t MAX_OUT_DEGREE = 300; - static int64_t MIN_CANDIDATE_POOL_SIZE = 50; - static int64_t MAX_CANDIDATE_POOL_SIZE = 1000; - static std::vector METRICS{knowhere::Metric::L2, knowhere::Metric::IP}; - - CheckStrByValues(knowhere::Metric::TYPE, METRICS); - CheckIntByRange(knowhere::meta::ROWS, DEFAULT_MIN_ROWS, DEFAULT_MAX_ROWS); - CheckIntByRange(knowhere::IndexParams::knng, MIN_KNNG, MAX_KNNG); - CheckIntByRange(knowhere::IndexParams::search_length, MIN_SEARCH_LENGTH, MAX_SEARCH_LENGTH); - CheckIntByRange(knowhere::IndexParams::out_degree, MIN_OUT_DEGREE, MAX_OUT_DEGREE); - CheckIntByRange(knowhere::IndexParams::candidate, MIN_CANDIDATE_POOL_SIZE, MAX_CANDIDATE_POOL_SIZE); - - // auto tune params - oricfg[knowhere::IndexParams::nlist] = MatchNlist(oricfg[knowhere::meta::ROWS].get(), 8192); - - int64_t nprobe = int(oricfg[knowhere::IndexParams::nlist].get() * 0.1); - oricfg[knowhere::IndexParams::nprobe] = nprobe < 1 ? 1 : nprobe; - - return true; -} - -bool -NSGConfAdapter::CheckSearch(Config& oricfg, const IndexType type, const IndexMode mode) { - static int64_t MIN_SEARCH_LENGTH = 1; - static int64_t MAX_SEARCH_LENGTH = 300; - - CheckIntByRange(knowhere::IndexParams::search_length, MIN_SEARCH_LENGTH, MAX_SEARCH_LENGTH); - - return ConfAdapter::CheckSearch(oricfg, type, mode); -} - -bool -HNSWConfAdapter::CheckTrain(Config& oricfg, const IndexMode mode) { - static int64_t MIN_EFCONSTRUCTION = 8; - static int64_t MAX_EFCONSTRUCTION = 512; - static int64_t MIN_M = 4; - static int64_t MAX_M = 64; - - CheckIntByRange(knowhere::meta::ROWS, DEFAULT_MIN_ROWS, DEFAULT_MAX_ROWS); - CheckIntByRange(knowhere::IndexParams::efConstruction, MIN_EFCONSTRUCTION, MAX_EFCONSTRUCTION); - CheckIntByRange(knowhere::IndexParams::M, MIN_M, MAX_M); - - return ConfAdapter::CheckTrain(oricfg, mode); -} - -bool -HNSWConfAdapter::CheckSearch(Config& oricfg, const IndexType type, const IndexMode mode) { - static int64_t MAX_EF = 4096; - - CheckIntByRange(knowhere::IndexParams::ef, oricfg[knowhere::meta::TOPK], MAX_EF); - - return ConfAdapter::CheckSearch(oricfg, type, mode); -} - -bool -BinIDMAPConfAdapter::CheckTrain(Config& oricfg, const IndexMode mode) { - static std::vector METRICS{knowhere::Metric::HAMMING, knowhere::Metric::JACCARD, - knowhere::Metric::TANIMOTO, knowhere::Metric::SUBSTRUCTURE, - knowhere::Metric::SUPERSTRUCTURE}; - - CheckIntByRange(knowhere::meta::DIM, DEFAULT_MIN_DIM, DEFAULT_MAX_DIM); - CheckStrByValues(knowhere::Metric::TYPE, METRICS); - - return true; -} - -bool -BinIVFConfAdapter::CheckTrain(Config& oricfg, const IndexMode mode) { - static std::vector METRICS{knowhere::Metric::HAMMING, knowhere::Metric::JACCARD, - knowhere::Metric::TANIMOTO}; - static int64_t MAX_NLIST = 999999; - static int64_t MIN_NLIST = 1; - - CheckIntByRange(knowhere::meta::ROWS, DEFAULT_MIN_ROWS, DEFAULT_MAX_ROWS); - CheckIntByRange(knowhere::meta::DIM, DEFAULT_MIN_DIM, DEFAULT_MAX_DIM); - CheckIntByRange(knowhere::IndexParams::nlist, MIN_NLIST, MAX_NLIST); - CheckStrByValues(knowhere::Metric::TYPE, METRICS); - - int64_t nlist = oricfg[knowhere::IndexParams::nlist]; - CheckIntByRange(knowhere::meta::ROWS, nlist, DEFAULT_MAX_ROWS); - - // Best Practice - // static int64_t MIN_POINTS_PER_CENTROID = 40; - // static int64_t MAX_POINTS_PER_CENTROID = 256; - // CheckIntByRange(knowhere::meta::ROWS, MIN_POINTS_PER_CENTROID * nlist, MAX_POINTS_PER_CENTROID * nlist); - - return true; -} - -bool -ANNOYConfAdapter::CheckTrain(Config& oricfg, const IndexMode mode) { - static int64_t MIN_NTREES = 1; - // too large of n_trees takes much time, if there is real requirement, change this threshold. - static int64_t MAX_NTREES = 1024; - - CheckIntByRange(knowhere::IndexParams::n_trees, MIN_NTREES, MAX_NTREES); - - return ConfAdapter::CheckTrain(oricfg, mode); -} - -bool -ANNOYConfAdapter::CheckSearch(Config& oricfg, const IndexType type, const IndexMode mode) { - return ConfAdapter::CheckSearch(oricfg, type, mode); -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapter.h b/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapter.h deleted file mode 100644 index d4ecd60537..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapter.h +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include - -#include "knowhere/common/Config.h" -#include "knowhere/index/vector_index/IndexType.h" - -namespace milvus { -namespace knowhere { - -class ConfAdapter { - public: - virtual bool - CheckTrain(Config& oricfg, const IndexMode mode); - - virtual bool - CheckSearch(Config& oricfg, const IndexType type, const IndexMode mode); -}; -using ConfAdapterPtr = std::shared_ptr; - -class IVFConfAdapter : public ConfAdapter { - public: - bool - CheckTrain(Config& oricfg, const IndexMode mode) override; - - bool - CheckSearch(Config& oricfg, const IndexType type, const IndexMode mode) override; -}; - -class IVFSQConfAdapter : public IVFConfAdapter { - public: - bool - CheckTrain(Config& oricfg, const IndexMode mode) override; -}; - -class IVFPQConfAdapter : public IVFConfAdapter { - public: - bool - CheckTrain(Config& oricfg, const IndexMode mode) override; - - static void - GetValidMList(int64_t dimension, std::vector& resset); -}; - -class NSGConfAdapter : public IVFConfAdapter { - public: - bool - CheckTrain(Config& oricfg, const IndexMode mode) override; - - bool - CheckSearch(Config& oricfg, const IndexType type, const IndexMode mode) override; -}; - -class BinIDMAPConfAdapter : public ConfAdapter { - public: - bool - CheckTrain(Config& oricfg, const IndexMode mode) override; -}; - -class BinIVFConfAdapter : public IVFConfAdapter { - public: - bool - CheckTrain(Config& oricfg, const IndexMode mode) override; -}; - -class HNSWConfAdapter : public ConfAdapter { - public: - bool - CheckTrain(Config& oricfg, const IndexMode mode) override; - - bool - CheckSearch(Config& oricfg, const IndexType type, const IndexMode mode) override; -}; - -class ANNOYConfAdapter : public ConfAdapter { - public: - bool - CheckTrain(Config& oricfg, const IndexMode mode) override; - - bool - CheckSearch(Config& oricfg, const IndexType type, const IndexMode mode) override; -}; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapterMgr.cpp b/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapterMgr.cpp deleted file mode 100644 index e9ef960369..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapterMgr.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "knowhere/index/vector_index/ConfAdapterMgr.h" - -#include "knowhere/common/Exception.h" -#include "knowhere/common/Log.h" - -namespace milvus { -namespace knowhere { - -ConfAdapterPtr -AdapterMgr::GetAdapter(const IndexType type) { - if (!init_) - RegisterAdapter(); - - try { - return collection_.at(type)(); - } catch (...) { - KNOWHERE_THROW_MSG("Can not find confadapter: " + type); - } -} - -#define REGISTER_CONF_ADAPTER(T, TYPE, NAME) static AdapterMgr::register_t reg_##NAME##_(TYPE) - -void -AdapterMgr::RegisterAdapter() { - init_ = true; - - REGISTER_CONF_ADAPTER(ConfAdapter, IndexEnum::INDEX_FAISS_IDMAP, idmap_adapter); - REGISTER_CONF_ADAPTER(IVFConfAdapter, IndexEnum::INDEX_FAISS_IVFFLAT, ivf_adapter); - REGISTER_CONF_ADAPTER(IVFPQConfAdapter, IndexEnum::INDEX_FAISS_IVFPQ, ivfpq_adapter); - REGISTER_CONF_ADAPTER(IVFSQConfAdapter, IndexEnum::INDEX_FAISS_IVFSQ8, ivfsq8_adapter); - REGISTER_CONF_ADAPTER(IVFSQConfAdapter, IndexEnum::INDEX_FAISS_IVFSQ8H, ivfsq8h_adapter); - REGISTER_CONF_ADAPTER(BinIDMAPConfAdapter, IndexEnum::INDEX_FAISS_BIN_IDMAP, idmap_bin_adapter); - REGISTER_CONF_ADAPTER(BinIDMAPConfAdapter, IndexEnum::INDEX_FAISS_BIN_IVFFLAT, ivf_bin_adapter); - REGISTER_CONF_ADAPTER(NSGConfAdapter, IndexEnum::INDEX_NSG, nsg_adapter); - REGISTER_CONF_ADAPTER(ConfAdapter, IndexEnum::INDEX_SPTAG_KDT_RNT, sptag_kdt_adapter); - REGISTER_CONF_ADAPTER(ConfAdapter, IndexEnum::INDEX_SPTAG_BKT_RNT, sptag_bkt_adapter); - REGISTER_CONF_ADAPTER(HNSWConfAdapter, IndexEnum::INDEX_HNSW, hnsw_adapter); - REGISTER_CONF_ADAPTER(ANNOYConfAdapter, IndexEnum::INDEX_ANNOY, annoy_adapter); -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapterMgr.h b/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapterMgr.h deleted file mode 100644 index 5d8c24f322..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/ConfAdapterMgr.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include - -#include "knowhere/index/vector_index/ConfAdapter.h" -#include "knowhere/index/vector_index/IndexType.h" - -namespace milvus { -namespace knowhere { - -class AdapterMgr { - public: - template - struct register_t { - explicit register_t(const IndexType type) { - AdapterMgr::GetInstance().collection_[type] = ([] { return std::make_shared(); }); - } - }; - - static AdapterMgr& - GetInstance() { - static AdapterMgr instance; - return instance; - } - - ConfAdapterPtr - GetAdapter(const IndexType indexType); - - void - RegisterAdapter(); - - protected: - bool init_ = false; - std::unordered_map> collection_; -}; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/FaissBaseBinaryIndex.cpp b/core/src/index/knowhere/knowhere/index/vector_index/FaissBaseBinaryIndex.cpp deleted file mode 100644 index 568b70457f..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/FaissBaseBinaryIndex.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/FaissBaseBinaryIndex.h" -#include "knowhere/index/vector_index/helpers/FaissIO.h" - -namespace milvus { -namespace knowhere { - -BinarySet -FaissBaseBinaryIndex::SerializeImpl(const IndexType& type) { - try { - faiss::IndexBinary* index = index_.get(); - - MemoryIOWriter writer; - faiss::write_index_binary(index, &writer); - std::shared_ptr data(writer.data_); - - BinarySet res_set; - res_set.Append("BinaryIVF", data, writer.rp); - return res_set; - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} - -void -FaissBaseBinaryIndex::LoadImpl(const BinarySet& index_binary, const IndexType& type) { - auto binary = index_binary.GetByName("BinaryIVF"); - - MemoryIOReader reader; - reader.total = binary->size; - reader.data_ = binary->data.get(); - - faiss::IndexBinary* index = faiss::read_index_binary(&reader); - index_.reset(index); -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/FaissBaseBinaryIndex.h b/core/src/index/knowhere/knowhere/index/vector_index/FaissBaseBinaryIndex.h deleted file mode 100644 index f4d8784749..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/FaissBaseBinaryIndex.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -#include - -#include "knowhere/common/BinarySet.h" -#include "knowhere/common/Dataset.h" -#include "knowhere/index/vector_index/IndexType.h" - -namespace milvus { -namespace knowhere { - -class FaissBaseBinaryIndex { - protected: - explicit FaissBaseBinaryIndex(std::shared_ptr index) : index_(std::move(index)) { - } - - virtual BinarySet - SerializeImpl(const IndexType& type); - - virtual void - LoadImpl(const BinarySet& index_binary, const IndexType& type); - - public: - std::shared_ptr index_ = nullptr; -}; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/FaissBaseIndex.cpp b/core/src/index/knowhere/knowhere/index/vector_index/FaissBaseIndex.cpp deleted file mode 100644 index f0a6facefc..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/FaissBaseIndex.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/FaissBaseIndex.h" -#include "knowhere/index/vector_index/IndexType.h" -#include "knowhere/index/vector_index/helpers/FaissIO.h" - -namespace milvus { -namespace knowhere { - -BinarySet -FaissBaseIndex::SerializeImpl(const IndexType& type) { - try { - fiu_do_on("FaissBaseIndex.SerializeImpl.throw_exception", throw std::exception()); - faiss::Index* index = index_.get(); - - MemoryIOWriter writer; - faiss::write_index(index, &writer); - std::shared_ptr data(writer.data_); - - BinarySet res_set; - // TODO(linxj): use virtual func Name() instead of raw string. - res_set.Append("IVF", data, writer.rp); - return res_set; - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} - -void -FaissBaseIndex::LoadImpl(const BinarySet& binary_set, const IndexType& type) { - auto binary = binary_set.GetByName("IVF"); - - MemoryIOReader reader; - reader.total = binary->size; - reader.data_ = binary->data.get(); - - faiss::Index* index = faiss::read_index(&reader); - index_.reset(index); - - SealImpl(); -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/FaissBaseIndex.h b/core/src/index/knowhere/knowhere/index/vector_index/FaissBaseIndex.h deleted file mode 100644 index 0da0123877..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/FaissBaseIndex.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include - -#include - -#include "knowhere/common/BinarySet.h" -#include "knowhere/index/vector_index/IndexType.h" - -namespace milvus { -namespace knowhere { - -class FaissBaseIndex { - protected: - explicit FaissBaseIndex(std::shared_ptr index) : index_(std::move(index)) { - } - - virtual BinarySet - SerializeImpl(const IndexType& type); - - virtual void - LoadImpl(const BinarySet&, const IndexType& type); - - virtual void - SealImpl() { /* do nothing */ - } - - public: - std::shared_ptr index_ = nullptr; -}; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexAnnoy.cpp b/core/src/index/knowhere/knowhere/index/vector_index/IndexAnnoy.cpp deleted file mode 100644 index d344106f0c..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexAnnoy.cpp +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "knowhere/index/vector_index/IndexAnnoy.h" - -#include -#include -#include -#include -#include -#include - -#include "hnswlib/hnswalg.h" -#include "hnswlib/space_ip.h" -#include "hnswlib/space_l2.h" -#include "knowhere/common/Exception.h" -#include "knowhere/common/Log.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" -#include "knowhere/index/vector_index/helpers/FaissIO.h" - -namespace milvus { -namespace knowhere { - -BinarySet -IndexAnnoy::Serialize(const Config& config) { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize or trained"); - } - - auto metric_type_length = metric_type_.length(); - std::shared_ptr metric_type(new uint8_t[metric_type_length]); - memcpy(metric_type.get(), metric_type_.data(), metric_type_.length()); - - auto dim = Dim(); - std::shared_ptr dim_data(new uint8_t[sizeof(uint64_t)]); - memcpy(dim_data.get(), &dim, sizeof(uint64_t)); - - auto index_length = index_->get_index_length(); - std::shared_ptr index_data(new uint8_t[index_length]); - memcpy(index_data.get(), index_->get_index(), (size_t)index_length); - - BinarySet res_set; - res_set.Append("annoy_metric_type", metric_type, metric_type_length); - res_set.Append("annoy_dim", dim_data, sizeof(uint64_t)); - res_set.Append("annoy_index_data", index_data, index_length); - return res_set; -} - -void -IndexAnnoy::Load(const BinarySet& index_binary) { - auto metric_type = index_binary.GetByName("annoy_metric_type"); - metric_type_.resize((size_t)metric_type->size); - memcpy(metric_type_.data(), metric_type->data.get(), (size_t)metric_type->size); - - auto dim_data = index_binary.GetByName("annoy_dim"); - uint64_t dim; - memcpy(&dim, dim_data->data.get(), (size_t)dim_data->size); - - if (metric_type_ == Metric::L2) { - index_ = std::make_shared>(dim); - } else if (metric_type_ == Metric::IP) { - index_ = std::make_shared>(dim); - } else { - KNOWHERE_THROW_MSG("metric not supported " + metric_type_); - } - - auto index_data = index_binary.GetByName("annoy_index_data"); - char* p = nullptr; - if (!index_->load_index(reinterpret_cast(index_data->data.get()), index_data->size, &p)) { - std::string error_msg(p); - free(p); - KNOWHERE_THROW_MSG(error_msg); - } -} - -void -IndexAnnoy::BuildAll(const DatasetPtr& dataset_ptr, const Config& config) { - if (index_) { - // it is builded all - LOG_KNOWHERE_DEBUG_ << "IndexAnnoy::BuildAll: index_ has been built!"; - return; - } - - GETTENSORWITHIDS(dataset_ptr) - - metric_type_ = config[Metric::TYPE]; - if (metric_type_ == Metric::L2) { - index_ = std::make_shared>(dim); - } else if (metric_type_ == Metric::IP) { - index_ = std::make_shared>(dim); - } else { - KNOWHERE_THROW_MSG("metric not supported " + metric_type_); - } - - for (int i = 0; i < rows; ++i) { - index_->add_item(p_ids[i], (const float*)p_data + dim * i); - } - - index_->build(config[IndexParams::n_trees].get()); -} - -DatasetPtr -IndexAnnoy::Query(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize or trained"); - } - - GETTENSOR(dataset_ptr) - auto k = config[meta::TOPK].get(); - auto search_k = config[IndexParams::search_k].get(); - auto all_num = rows * k; - auto p_id = (int64_t*)malloc(all_num * sizeof(int64_t)); - auto p_dist = (float*)malloc(all_num * sizeof(float)); - faiss::ConcurrentBitsetPtr blacklist = GetBlacklist(); - -#pragma omp parallel for - for (unsigned int i = 0; i < rows; ++i) { - std::vector result; - result.reserve(k); - std::vector distances; - distances.reserve(k); - index_->get_nns_by_vector((const float*)p_data + i * dim, k, search_k, &result, &distances, blacklist); - - int64_t result_num = result.size(); - auto local_p_id = p_id + k * i; - auto local_p_dist = p_dist + k * i; - memcpy(local_p_id, result.data(), result_num * sizeof(int64_t)); - memcpy(local_p_dist, distances.data(), result_num * sizeof(float)); - for (; result_num < k; result_num++) { - local_p_id[result_num] = -1; - local_p_dist[result_num] = 1.0 / 0.0; - } - } - - auto ret_ds = std::make_shared(); - ret_ds->Set(meta::IDS, p_id); - ret_ds->Set(meta::DISTANCE, p_dist); - return ret_ds; -} - -int64_t -IndexAnnoy::Count() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - return index_->get_n_items(); -} - -int64_t -IndexAnnoy::Dim() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - return index_->get_dim(); -} - -void -IndexAnnoy::UpdateIndexSize() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - index_size_ = index_->cal_size(); -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexAnnoy.h b/core/src/index/knowhere/knowhere/index/vector_index/IndexAnnoy.h deleted file mode 100644 index 7b86dc531d..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexAnnoy.h +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -#include "annoy/src/annoylib.h" -#include "annoy/src/kissrandom.h" - -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/VecIndex.h" - -namespace milvus { -namespace knowhere { - -class IndexAnnoy : public VecIndex { - public: - IndexAnnoy() { - index_type_ = IndexEnum::INDEX_ANNOY; - } - - BinarySet - Serialize(const Config& config = Config()) override; - - void - Load(const BinarySet& index_binary) override; - - void - BuildAll(const DatasetPtr& dataset_ptr, const Config& config) override; - - void - Train(const DatasetPtr& dataset_ptr, const Config& config) override { - KNOWHERE_THROW_MSG("Annoy not support build item dynamically, please invoke BuildAll interface."); - } - - void - Add(const DatasetPtr& dataset_ptr, const Config& config) override { - KNOWHERE_THROW_MSG("Annoy not support add item dynamically, please invoke BuildAll interface."); - } - - void - AddWithoutIds(const DatasetPtr&, const Config&) override { - KNOWHERE_THROW_MSG("Incremental index is not supported"); - } - - DatasetPtr - Query(const DatasetPtr& dataset_ptr, const Config& config) override; - - int64_t - Count() override; - - int64_t - Dim() override; - - void - UpdateIndexSize() override; - - private: - MetricType metric_type_; - std::shared_ptr> index_ = nullptr; -}; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexBinaryIDMAP.cpp b/core/src/index/knowhere/knowhere/index/vector_index/IndexBinaryIDMAP.cpp deleted file mode 100644 index deeff712eb..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexBinaryIDMAP.cpp +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "knowhere/index/vector_index/IndexBinaryIDMAP.h" - -#include -#include -#include - -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" - -namespace milvus { -namespace knowhere { - -BinarySet -BinaryIDMAP::Serialize(const Config& config) { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - - std::lock_guard lk(mutex_); - return SerializeImpl(index_type_); -} - -void -BinaryIDMAP::Load(const BinarySet& index_binary) { - std::lock_guard lk(mutex_); - LoadImpl(index_binary, index_type_); -} - -DatasetPtr -BinaryIDMAP::Query(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - GETTENSOR(dataset_ptr) - - int64_t k = config[meta::TOPK].get(); - auto elems = rows * k; - size_t p_id_size = sizeof(int64_t) * elems; - size_t p_dist_size = sizeof(float) * elems; - auto p_id = (int64_t*)malloc(p_id_size); - auto p_dist = (float*)malloc(p_dist_size); - - QueryImpl(rows, (uint8_t*)p_data, k, p_dist, p_id, config); - auto ret_ds = std::make_shared(); - ret_ds->Set(meta::IDS, p_id); - ret_ds->Set(meta::DISTANCE, p_dist); - - return ret_ds; -} - -int64_t -BinaryIDMAP::Count() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - return index_->ntotal; -} - -int64_t -BinaryIDMAP::Dim() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - return index_->d; -} - -void -BinaryIDMAP::Add(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - - std::lock_guard lk(mutex_); - GETTENSORWITHIDS(dataset_ptr) - - index_->add_with_ids(rows, (uint8_t*)p_data, p_ids); -} - -void -BinaryIDMAP::Train(const DatasetPtr& dataset_ptr, const Config& config) { - const char* desc = "BFlat"; - int64_t dim = config[meta::DIM].get(); - faiss::MetricType metric_type = GetMetricType(config[Metric::TYPE].get()); - auto index = faiss::index_binary_factory(dim, desc, metric_type); - index_.reset(index); -} - -const uint8_t* -BinaryIDMAP::GetRawVectors() { - try { - auto file_index = dynamic_cast(index_.get()); - auto flat_index = dynamic_cast(file_index->index); - return flat_index->xb.data(); - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} - -const int64_t* -BinaryIDMAP::GetRawIds() { - try { - auto file_index = dynamic_cast(index_.get()); - return file_index->id_map.data(); - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} - -void -BinaryIDMAP::AddWithoutIds(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - - std::lock_guard lk(mutex_); - GETTENSOR(dataset_ptr) - - std::vector new_ids(rows); - for (int i = 0; i < rows; ++i) { - new_ids[i] = i; - } - - index_->add_with_ids(rows, (uint8_t*)p_data, new_ids.data()); -} - -void -BinaryIDMAP::QueryImpl(int64_t n, const uint8_t* data, int64_t k, float* distances, int64_t* labels, - const Config& config) { - auto flat_index = dynamic_cast(index_.get())->index; - auto default_type = flat_index->metric_type; - if (config.contains(Metric::TYPE)) - flat_index->metric_type = GetMetricType(config[Metric::TYPE].get()); - - int32_t* i_distances = reinterpret_cast(distances); - index_->search(n, (uint8_t*)data, k, i_distances, labels, bitset_); - - // if hamming, it need transform int32 to float - if (flat_index->metric_type == faiss::METRIC_Hamming) { - int64_t num = n * k; - for (int64_t i = 0; i < num; i++) { - distances[i] = static_cast(i_distances[i]); - } - } - - flat_index->metric_type = default_type; -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexBinaryIDMAP.h b/core/src/index/knowhere/knowhere/index/vector_index/IndexBinaryIDMAP.h deleted file mode 100644 index 41545be6fc..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexBinaryIDMAP.h +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include - -#include "knowhere/index/vector_index/FaissBaseBinaryIndex.h" -#include "knowhere/index/vector_index/VecIndex.h" - -namespace milvus { -namespace knowhere { - -class BinaryIDMAP : public VecIndex, public FaissBaseBinaryIndex { - public: - BinaryIDMAP() : FaissBaseBinaryIndex(nullptr) { - index_type_ = IndexEnum::INDEX_FAISS_BIN_IDMAP; - } - - explicit BinaryIDMAP(std::shared_ptr index) : FaissBaseBinaryIndex(std::move(index)) { - index_type_ = IndexEnum::INDEX_FAISS_BIN_IDMAP; - } - - BinarySet - Serialize(const Config& config = Config()) override; - - void - Load(const BinarySet& index_binary) override; - - void - Train(const DatasetPtr&, const Config&) override; - - void - Add(const DatasetPtr&, const Config&) override; - - void - AddWithoutIds(const DatasetPtr&, const Config&) override; - - DatasetPtr - Query(const DatasetPtr&, const Config&) override; - - int64_t - Count() override; - - int64_t - Dim() override; - - int64_t - IndexSize() override { - return Count() * Dim() / 8; - } - - virtual const uint8_t* - GetRawVectors(); - - virtual const int64_t* - GetRawIds(); - - protected: - virtual void - QueryImpl(int64_t n, const uint8_t* data, int64_t k, float* distances, int64_t* labels, const Config& config); - - protected: - std::mutex mutex_; -}; - -using BinaryIDMAPPtr = std::shared_ptr; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexBinaryIVF.cpp b/core/src/index/knowhere/knowhere/index/vector_index/IndexBinaryIVF.cpp deleted file mode 100644 index 3153afd14a..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexBinaryIVF.cpp +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "knowhere/index/vector_index/IndexBinaryIVF.h" - -#include -#include - -#include -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/common/Log.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" - -namespace milvus { -namespace knowhere { - -using stdclock = std::chrono::high_resolution_clock; - -BinarySet -BinaryIVF::Serialize(const Config& config) { - if (!index_ || !index_->is_trained) { - KNOWHERE_THROW_MSG("index not initialize or trained"); - } - - std::lock_guard lk(mutex_); - return SerializeImpl(index_type_); -} - -void -BinaryIVF::Load(const BinarySet& index_binary) { - std::lock_guard lk(mutex_); - LoadImpl(index_binary, index_type_); -} - -DatasetPtr -BinaryIVF::Query(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_ || !index_->is_trained) { - KNOWHERE_THROW_MSG("index not initialize or trained"); - } - - GETTENSOR(dataset_ptr) - - try { - int64_t k = config[meta::TOPK].get(); - auto elems = rows * k; - - size_t p_id_size = sizeof(int64_t) * elems; - size_t p_dist_size = sizeof(float) * elems; - auto p_id = (int64_t*)malloc(p_id_size); - auto p_dist = (float*)malloc(p_dist_size); - - QueryImpl(rows, (uint8_t*)p_data, k, p_dist, p_id, config); - - auto ret_ds = std::make_shared(); - ret_ds->Set(meta::IDS, p_id); - ret_ds->Set(meta::DISTANCE, p_dist); - - return ret_ds; - } catch (faiss::FaissException& e) { - KNOWHERE_THROW_MSG(e.what()); - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} - -#if 0 -DatasetPtr -BinaryIVF::QueryById(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_ || !index_->is_trained) { - KNOWHERE_THROW_MSG("index not initialize or trained"); - } - - auto rows = dataset_ptr->Get(meta::ROWS); - auto p_data = dataset_ptr->Get(meta::IDS); - - try { - int64_t k = config[meta::TOPK].get(); - auto elems = rows * k; - - size_t p_id_size = sizeof(int64_t) * elems; - size_t p_dist_size = sizeof(float) * elems; - auto p_id = (int64_t*)malloc(p_id_size); - auto p_dist = (float*)malloc(p_dist_size); - - int32_t* pdistances = (int32_t*)p_dist; - index_->search_by_id(rows, p_data, k, pdistances, p_id, bitset_); - - auto ret_ds = std::make_shared(); - if (index_->metric_type == faiss::METRIC_Hamming) { - auto pf_dist = (float*)malloc(p_dist_size); - int32_t* pi_dist = (int32_t*)p_dist; - for (int i = 0; i < elems; i++) { - *(pf_dist + i) = (float)(*(pi_dist + i)); - } - ret_ds->Set(meta::IDS, p_id); - ret_ds->Set(meta::DISTANCE, pf_dist); - free(p_dist); - } else { - ret_ds->Set(meta::IDS, p_id); - ret_ds->Set(meta::DISTANCE, p_dist); - } - - return ret_ds; - } catch (faiss::FaissException& e) { - KNOWHERE_THROW_MSG(e.what()); - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} -#endif - -int64_t -BinaryIVF::Count() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - return index_->ntotal; -} - -int64_t -BinaryIVF::Dim() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - return index_->d; -} - -void -BinaryIVF::UpdateIndexSize() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - auto bin_ivf_index = dynamic_cast(index_.get()); - auto nb = bin_ivf_index->invlists->compute_ntotal(); - auto nlist = bin_ivf_index->nlist; - auto code_size = bin_ivf_index->code_size; - - // binary ivf codes, ids and quantizer - index_size_ = nb * code_size + nb * sizeof(int64_t) + nlist * code_size; -} - -void -BinaryIVF::Train(const DatasetPtr& dataset_ptr, const Config& config) { - GETTENSORWITHIDS(dataset_ptr) - - int64_t nlist = config[IndexParams::nlist]; - faiss::MetricType metric_type = GetMetricType(config[Metric::TYPE].get()); - faiss::IndexBinary* coarse_quantizer = new faiss::IndexBinaryFlat(dim, metric_type); - auto index = std::make_shared(coarse_quantizer, dim, nlist, metric_type); - index->train(rows, (uint8_t*)p_data); - index->add_with_ids(rows, (uint8_t*)p_data, p_ids); - index_ = index; -} - -#if 0 -DatasetPtr -BinaryIVF::GetVectorById(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_ || !index_->is_trained) { - KNOWHERE_THROW_MSG("index not initialize or trained"); - } - - // GETBINARYTENSOR(dataset_ptr) - // auto rows = dataset_ptr->Get(meta::ROWS); - auto p_data = dataset_ptr->Get(meta::IDS); - auto elems = dataset_ptr->Get(meta::DIM); - - try { - size_t p_x_size = sizeof(uint8_t) * elems; - auto p_x = (uint8_t*)malloc(p_x_size); - - index_->get_vector_by_id(1, p_data, p_x, bitset_); - - auto ret_ds = std::make_shared(); - ret_ds->Set(meta::TENSOR, p_x); - return ret_ds; - } catch (faiss::FaissException& e) { - KNOWHERE_THROW_MSG(e.what()); - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} -#endif - -std::shared_ptr -BinaryIVF::GenParams(const Config& config) { - auto params = std::make_shared(); - params->nprobe = config[IndexParams::nprobe]; - // params->max_codes = config["max_code"]; - return params; -} - -void -BinaryIVF::QueryImpl(int64_t n, const uint8_t* data, int64_t k, float* distances, int64_t* labels, - const Config& config) { - auto params = GenParams(config); - auto ivf_index = dynamic_cast(index_.get()); - ivf_index->nprobe = params->nprobe; - - stdclock::time_point before = stdclock::now(); - int32_t* i_distances = reinterpret_cast(distances); - index_->search(n, (uint8_t*)data, k, i_distances, labels, bitset_); - - stdclock::time_point after = stdclock::now(); - double search_cost = (std::chrono::duration(after - before)).count(); - LOG_KNOWHERE_DEBUG_ << "IVF search cost: " << search_cost - << ", quantization cost: " << faiss::indexIVF_stats.quantization_time - << ", data search cost: " << faiss::indexIVF_stats.search_time; - faiss::indexIVF_stats.quantization_time = 0; - faiss::indexIVF_stats.search_time = 0; - - // if hamming, it need transform int32 to float - if (ivf_index->metric_type == faiss::METRIC_Hamming) { - int64_t num = n * k; - for (int64_t i = 0; i < num; i++) { - distances[i] = static_cast(i_distances[i]); - } - } -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexBinaryIVF.h b/core/src/index/knowhere/knowhere/index/vector_index/IndexBinaryIVF.h deleted file mode 100644 index 8069704436..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexBinaryIVF.h +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include - -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/FaissBaseBinaryIndex.h" -#include "knowhere/index/vector_index/VecIndex.h" - -namespace milvus { -namespace knowhere { - -class BinaryIVF : public VecIndex, public FaissBaseBinaryIndex { - public: - BinaryIVF() : FaissBaseBinaryIndex(nullptr) { - index_type_ = IndexEnum::INDEX_FAISS_BIN_IVFFLAT; - } - - explicit BinaryIVF(std::shared_ptr index) : FaissBaseBinaryIndex(std::move(index)) { - index_type_ = IndexEnum::INDEX_FAISS_BIN_IVFFLAT; - } - - BinarySet - Serialize(const Config& config = Config()) override; - - void - BuildAll(const DatasetPtr& dataset_ptr, const Config& config) override { - Train(dataset_ptr, config); - } - - void - Load(const BinarySet& index_binary) override; - - void - Train(const DatasetPtr& dataset_ptr, const Config& config) override; - - void - Add(const DatasetPtr& dataset_ptr, const Config& config) override { - KNOWHERE_THROW_MSG("not support yet"); - } - - void - AddWithoutIds(const DatasetPtr&, const Config&) override { - KNOWHERE_THROW_MSG("AddWithoutIds is not supported"); - } - - DatasetPtr - Query(const DatasetPtr& dataset_ptr, const Config& config) override; - -#if 0 - DatasetPtr - QueryById(const DatasetPtr& dataset_ptr, const Config& config) override; -#endif - - int64_t - Count() override; - - int64_t - Dim() override; - - void - UpdateIndexSize() override; - -#if 0 - DatasetPtr - GetVectorById(const DatasetPtr& dataset_ptr, const Config& config); -#endif - - protected: - virtual std::shared_ptr - GenParams(const Config& config); - - virtual void - QueryImpl(int64_t n, const uint8_t* data, int64_t k, float* distances, int64_t* labels, const Config& config); - - protected: - std::mutex mutex_; -}; - -using BinaryIVFIndexPtr = std::shared_ptr; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexHNSW.cpp b/core/src/index/knowhere/knowhere/index/vector_index/IndexHNSW.cpp deleted file mode 100644 index 0b9989ba1e..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexHNSW.cpp +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "knowhere/index/vector_index/IndexHNSW.h" - -#include -#include -#include -#include -#include - -#include "faiss/BuilderSuspend.h" -#include "hnswlib/hnswalg.h" -#include "hnswlib/space_ip.h" -#include "hnswlib/space_l2.h" -#include "knowhere/common/Exception.h" -#include "knowhere/common/Log.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" -#include "knowhere/index/vector_index/helpers/FaissIO.h" - -namespace milvus { -namespace knowhere { - -// void -// normalize_vector(float* data, float* norm_array, size_t dim) { -// float norm = 0.0f; -// for (int i = 0; i < dim; i++) norm += data[i] * data[i]; -// norm = 1.0f / (sqrtf(norm) + 1e-30f); -// for (int i = 0; i < dim; i++) norm_array[i] = data[i] * norm; -// } - -BinarySet -IndexHNSW::Serialize(const Config& config) { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize or trained"); - } - - try { - MemoryIOWriter writer; - index_->saveIndex(writer); - std::shared_ptr data(writer.data_); - - BinarySet res_set; - res_set.Append("HNSW", data, writer.rp); - return res_set; - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} - -void -IndexHNSW::Load(const BinarySet& index_binary) { - try { - auto binary = index_binary.GetByName("HNSW"); - - MemoryIOReader reader; - reader.total = binary->size; - reader.data_ = binary->data.get(); - - hnswlib::SpaceInterface* space; - index_ = std::make_shared>(space); - index_->loadIndex(reader); - - normalize = index_->metric_type_ == 1; // 1 == InnerProduct - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} - -void -IndexHNSW::Train(const DatasetPtr& dataset_ptr, const Config& config) { - try { - GETTENSOR(dataset_ptr) - - hnswlib::SpaceInterface* space; - if (config[Metric::TYPE] == Metric::L2) { - space = new hnswlib::L2Space(dim); - } else if (config[Metric::TYPE] == Metric::IP) { - space = new hnswlib::InnerProductSpace(dim); - normalize = true; - } - index_ = std::make_shared>(space, rows, config[IndexParams::M].get(), - config[IndexParams::efConstruction].get()); - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} - -void -IndexHNSW::Add(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - - std::lock_guard lk(mutex_); - - GETTENSORWITHIDS(dataset_ptr) - - // if (normalize) { - // std::vector ep_norm_vector(Dim()); - // normalize_vector((float*)(p_data), ep_norm_vector.data(), Dim()); - // index_->addPoint((void*)(ep_norm_vector.data()), p_ids[0]); - // #pragma omp parallel for - // for (int i = 1; i < rows; ++i) { - // std::vector norm_vector(Dim()); - // normalize_vector((float*)(p_data + Dim() * i), norm_vector.data(), Dim()); - // index_->addPoint((void*)(norm_vector.data()), p_ids[i]); - // } - // } else { - // index_->addPoint((void*)(p_data), p_ids[0]); - // #pragma omp parallel for - // for (int i = 1; i < rows; ++i) { - // index_->addPoint((void*)(p_data + Dim() * i), p_ids[i]); - // } - // } - - index_->addPoint(p_data, p_ids[0]); -#pragma omp parallel for - for (int i = 1; i < rows; ++i) { - faiss::BuilderSuspend::check_wait(); - index_->addPoint(((float*)p_data + Dim() * i), p_ids[i]); - } -} - -DatasetPtr -IndexHNSW::Query(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize or trained"); - } - GETTENSOR(dataset_ptr) - - size_t k = config[meta::TOPK].get(); - size_t id_size = sizeof(int64_t) * k; - size_t dist_size = sizeof(float) * k; - auto p_id = (int64_t*)malloc(id_size * rows); - auto p_dist = (float*)malloc(dist_size * rows); - - index_->setEf(config[IndexParams::ef]); - - using P = std::pair; - auto compare = [](const P& v1, const P& v2) { return v1.first < v2.first; }; - - faiss::ConcurrentBitsetPtr blacklist = GetBlacklist(); -#pragma omp parallel for - for (unsigned int i = 0; i < rows; ++i) { - std::vector

ret; - const float* single_query = (float*)p_data + i * Dim(); - - // if (normalize) { - // std::vector norm_vector(Dim()); - // normalize_vector((float*)(single_query), norm_vector.data(), Dim()); - // ret = index_->searchKnn((float*)(norm_vector.data()), config[meta::TOPK].get(), compare); - // } else { - // ret = index_->searchKnn((float*)single_query, config[meta::TOPK].get(), compare); - // } - ret = index_->searchKnn((float*)single_query, k, compare, blacklist); - - while (ret.size() < k) { - ret.emplace_back(std::make_pair(-1, -1)); - } - std::vector dist; - std::vector ids; - - if (normalize) { - std::transform(ret.begin(), ret.end(), std::back_inserter(dist), - [](const std::pair& e) { return float(1 - e.first); }); - } else { - std::transform(ret.begin(), ret.end(), std::back_inserter(dist), - [](const std::pair& e) { return e.first; }); - } - std::transform(ret.begin(), ret.end(), std::back_inserter(ids), - [](const std::pair& e) { return e.second; }); - - memcpy(p_dist + i * k, dist.data(), dist_size); - memcpy(p_id + i * k, ids.data(), id_size); - } - - auto ret_ds = std::make_shared(); - ret_ds->Set(meta::IDS, p_id); - ret_ds->Set(meta::DISTANCE, p_dist); - return ret_ds; -} - -int64_t -IndexHNSW::Count() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - return index_->cur_element_count; -} - -int64_t -IndexHNSW::Dim() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - return (*(size_t*)index_->dist_func_param_); -} - -void -IndexHNSW::UpdateIndexSize() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - index_size_ = index_->cal_size(); -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexHNSW.h b/core/src/index/knowhere/knowhere/index/vector_index/IndexHNSW.h deleted file mode 100644 index 576f2c484a..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexHNSW.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -#include "hnswlib/hnswlib.h" - -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/VecIndex.h" - -namespace milvus { -namespace knowhere { - -class IndexHNSW : public VecIndex { - public: - IndexHNSW() { - index_type_ = IndexEnum::INDEX_HNSW; - } - - BinarySet - Serialize(const Config& config = Config()) override; - - void - Load(const BinarySet& index_binary) override; - - void - Train(const DatasetPtr& dataset_ptr, const Config& config) override; - - void - Add(const DatasetPtr& dataset_ptr, const Config& config) override; - - void - AddWithoutIds(const DatasetPtr&, const Config&) override { - KNOWHERE_THROW_MSG("Incremental index is not supported"); - } - - DatasetPtr - Query(const DatasetPtr& dataset_ptr, const Config& config) override; - - int64_t - Count() override; - - int64_t - Dim() override; - - void - UpdateIndexSize() override; - - private: - bool normalize = false; - std::mutex mutex_; - std::shared_ptr> index_; -}; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexIDMAP.cpp b/core/src/index/knowhere/knowhere/index/vector_index/IndexIDMAP.cpp deleted file mode 100644 index cf09eeaccb..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexIDMAP.cpp +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include "knowhere/index/vector_index/IndexIDMAP.h" - -#include -#include -#include -#include -#include -#include -#ifdef MILVUS_GPU_VERSION -#include -#endif - -#include -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" -#include "knowhere/index/vector_index/helpers/FaissIO.h" -#include "knowhere/index/vector_index/helpers/IndexParameter.h" -#ifdef MILVUS_GPU_VERSION -#include "knowhere/index/vector_index/gpu/IndexGPUIDMAP.h" -#include "knowhere/index/vector_index/helpers/FaissGpuResourceMgr.h" -#endif - -namespace milvus { -namespace knowhere { - -BinarySet -IDMAP::Serialize(const Config& config) { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - - std::lock_guard lk(mutex_); - return SerializeImpl(index_type_); -} - -void -IDMAP::Load(const BinarySet& binary_set) { - std::lock_guard lk(mutex_); - LoadImpl(binary_set, index_type_); -} - -void -IDMAP::Train(const DatasetPtr& dataset_ptr, const Config& config) { - const char* desc = "IDMap,Flat"; - int64_t dim = config[meta::DIM].get(); - faiss::MetricType metric_type = GetMetricType(config[Metric::TYPE].get()); - auto index = faiss::index_factory(dim, desc, metric_type); - index_.reset(index); -} - -void -IDMAP::Add(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - - std::lock_guard lk(mutex_); - GETTENSORWITHIDS(dataset_ptr) - index_->add_with_ids(rows, (float*)p_data, p_ids); -} - -void -IDMAP::AddWithoutIds(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - - std::lock_guard lk(mutex_); - auto rows = dataset_ptr->Get(meta::ROWS); - auto p_data = dataset_ptr->Get(meta::TENSOR); - - // TODO: caiyd need check - std::vector new_ids(rows); - for (int i = 0; i < rows; ++i) { - new_ids[i] = i; - } - - index_->add_with_ids(rows, (float*)p_data, new_ids.data()); -} - -DatasetPtr -IDMAP::Query(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - GETTENSOR(dataset_ptr) - - int64_t k = config[meta::TOPK].get(); - auto elems = rows * k; - size_t p_id_size = sizeof(int64_t) * elems; - size_t p_dist_size = sizeof(float) * elems; - auto p_id = (int64_t*)malloc(p_id_size); - auto p_dist = (float*)malloc(p_dist_size); - - // QueryImpl(rows, (float*)p_data, k, p_dist, p_id, Config()); - QueryImpl(rows, (float*)p_data, k, p_dist, p_id, config); - auto ret_ds = std::make_shared(); - ret_ds->Set(meta::IDS, p_id); - ret_ds->Set(meta::DISTANCE, p_dist); - return ret_ds; -} - -#if 0 -DatasetPtr -IDMAP::QueryById(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - // GETTENSOR(dataset) - auto rows = dataset_ptr->Get(meta::ROWS); - auto p_data = dataset_ptr->Get(meta::IDS); - - int64_t k = config[meta::TOPK].get(); - auto elems = rows * k; - size_t p_id_size = sizeof(int64_t) * elems; - size_t p_dist_size = sizeof(float) * elems; - auto p_id = (int64_t*)malloc(p_id_size); - auto p_dist = (float*)malloc(p_dist_size); - - // todo: enable search by id (zhiru) - // auto blacklist = dataset_ptr->Get("bitset"); - // index_->searchById(rows, (float*)p_data, config[meta::TOPK].get(), p_dist, p_id, blacklist); - index_->search_by_id(rows, p_data, k, p_dist, p_id, bitset_); - - auto ret_ds = std::make_shared(); - ret_ds->Set(meta::IDS, p_id); - ret_ds->Set(meta::DISTANCE, p_dist); - return ret_ds; -} -#endif - -int64_t -IDMAP::Count() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - return index_->ntotal; -} - -int64_t -IDMAP::Dim() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - return index_->d; -} - -VecIndexPtr -IDMAP::CopyCpuToGpu(const int64_t device_id, const Config& config) { -#ifdef MILVUS_GPU_VERSION - if (auto res = FaissGpuResourceMgr::GetInstance().GetRes(device_id)) { - ResScope rs(res, device_id, false); - auto gpu_index = faiss::gpu::index_cpu_to_gpu(res->faiss_res.get(), device_id, index_.get()); - - std::shared_ptr device_index; - device_index.reset(gpu_index); - return std::make_shared(device_index, device_id, res); - } else { - KNOWHERE_THROW_MSG("CopyCpuToGpu Error, can't get gpu_resource"); - } -#else - KNOWHERE_THROW_MSG("Calling IDMAP::CopyCpuToGpu when we are using CPU version"); -#endif -} - -const float* -IDMAP::GetRawVectors() { - try { - auto file_index = dynamic_cast(index_.get()); - auto flat_index = dynamic_cast(file_index->index); - return flat_index->xb.data(); - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} - -const int64_t* -IDMAP::GetRawIds() { - try { - auto file_index = dynamic_cast(index_.get()); - return file_index->id_map.data(); - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} - -#if 0 -DatasetPtr -IDMAP::GetVectorById(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - // GETTENSOR(dataset) - // auto rows = dataset_ptr->Get(meta::ROWS); - auto p_data = dataset_ptr->Get(meta::IDS); - auto elems = dataset_ptr->Get(meta::DIM); - - size_t p_x_size = sizeof(float) * elems; - auto p_x = (float*)malloc(p_x_size); - - index_->get_vector_by_id(1, p_data, p_x, bitset_); - - auto ret_ds = std::make_shared(); - ret_ds->Set(meta::TENSOR, p_x); - return ret_ds; -} -#endif - -void -IDMAP::QueryImpl(int64_t n, const float* data, int64_t k, float* distances, int64_t* labels, const Config& config) { - auto flat_index = dynamic_cast(index_.get())->index; - auto default_type = flat_index->metric_type; - if (config.contains(Metric::TYPE)) - flat_index->metric_type = GetMetricType(config[Metric::TYPE].get()); - index_->search(n, (float*)data, k, distances, labels, bitset_); - flat_index->metric_type = default_type; -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexIDMAP.h b/core/src/index/knowhere/knowhere/index/vector_index/IndexIDMAP.h deleted file mode 100644 index 128d9f99b4..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexIDMAP.h +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include - -#include "knowhere/index/vector_index/FaissBaseIndex.h" -#include "knowhere/index/vector_index/VecIndex.h" - -namespace milvus { -namespace knowhere { - -class IDMAP : public VecIndex, public FaissBaseIndex { - public: - IDMAP() : FaissBaseIndex(nullptr) { - index_type_ = IndexEnum::INDEX_FAISS_IDMAP; - } - - explicit IDMAP(std::shared_ptr index) : FaissBaseIndex(std::move(index)) { - index_type_ = IndexEnum::INDEX_FAISS_IDMAP; - } - - BinarySet - Serialize(const Config& config = Config()) override; - - void - Load(const BinarySet&) override; - - void - Train(const DatasetPtr&, const Config&) override; - - void - Add(const DatasetPtr&, const Config&) override; - - void - AddWithoutIds(const DatasetPtr&, const Config&) override; - - DatasetPtr - Query(const DatasetPtr&, const Config&) override; - -#if 0 - DatasetPtr - QueryById(const DatasetPtr& dataset, const Config& config) override; -#endif - - int64_t - Count() override; - - int64_t - Dim() override; - - int64_t - IndexSize() override { - return Count() * Dim() * sizeof(FloatType); - } - -#if 0 - DatasetPtr - GetVectorById(const DatasetPtr& dataset, const Config& config) override; -#endif - - VecIndexPtr - CopyCpuToGpu(const int64_t, const Config&); - - virtual const float* - GetRawVectors(); - - virtual const int64_t* - GetRawIds(); - - protected: - virtual void - QueryImpl(int64_t, const float*, int64_t, float*, int64_t*, const Config&); - - protected: - std::mutex mutex_; -}; - -using IDMAPPtr = std::shared_ptr; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexIVF.cpp b/core/src/index/knowhere/knowhere/index/vector_index/IndexIVF.cpp deleted file mode 100644 index b226685010..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexIVF.cpp +++ /dev/null @@ -1,353 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef MILVUS_GPU_VERSION -#include -#include -#endif - -#include -#include -#include -#include -#include -#include - -#include "faiss/BuilderSuspend.h" -#include "knowhere/common/Exception.h" -#include "knowhere/common/Log.h" -#include "knowhere/index/vector_index/IndexIVF.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" -#include "knowhere/index/vector_index/helpers/FaissIO.h" -#include "knowhere/index/vector_index/helpers/IndexParameter.h" -#ifdef MILVUS_GPU_VERSION -#include "knowhere/index/vector_index/gpu/IndexGPUIVF.h" -#include "knowhere/index/vector_index/helpers/FaissGpuResourceMgr.h" -#endif - -namespace milvus { -namespace knowhere { - -using stdclock = std::chrono::high_resolution_clock; - -BinarySet -IVF::Serialize(const Config& config) { - if (!index_ || !index_->is_trained) { - KNOWHERE_THROW_MSG("index not initialize or trained"); - } - - std::lock_guard lk(mutex_); - return SerializeImpl(index_type_); -} - -void -IVF::Load(const BinarySet& binary_set) { - std::lock_guard lk(mutex_); - LoadImpl(binary_set, index_type_); -} - -void -IVF::Train(const DatasetPtr& dataset_ptr, const Config& config) { - GETTENSOR(dataset_ptr) - - faiss::MetricType metric_type = GetMetricType(config[Metric::TYPE].get()); - faiss::Index* coarse_quantizer = new faiss::IndexFlat(dim, metric_type); - int64_t nlist = config[IndexParams::nlist].get(); - index_ = std::shared_ptr(new faiss::IndexIVFFlat(coarse_quantizer, dim, nlist, metric_type)); - index_->train(rows, (float*)p_data); -} - -void -IVF::Add(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_ || !index_->is_trained) { - KNOWHERE_THROW_MSG("index not initialize or trained"); - } - - std::lock_guard lk(mutex_); - GETTENSORWITHIDS(dataset_ptr) - index_->add_with_ids(rows, (float*)p_data, p_ids); -} - -void -IVF::AddWithoutIds(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_ || !index_->is_trained) { - KNOWHERE_THROW_MSG("index not initialize or trained"); - } - - std::lock_guard lk(mutex_); - GETTENSOR(dataset_ptr) - index_->add(rows, (float*)p_data); -} - -DatasetPtr -IVF::Query(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_ || !index_->is_trained) { - KNOWHERE_THROW_MSG("index not initialize or trained"); - } - - GETTENSOR(dataset_ptr) - - try { - fiu_do_on("IVF.Search.throw_std_exception", throw std::exception()); - fiu_do_on("IVF.Search.throw_faiss_exception", throw faiss::FaissException("")); - int64_t k = config[meta::TOPK].get(); - auto elems = rows * k; - - size_t p_id_size = sizeof(int64_t) * elems; - size_t p_dist_size = sizeof(float) * elems; - auto p_id = (int64_t*)malloc(p_id_size); - auto p_dist = (float*)malloc(p_dist_size); - - QueryImpl(rows, (float*)p_data, k, p_dist, p_id, config); - - // std::stringstream ss_res_id, ss_res_dist; - // for (int i = 0; i < 10; ++i) { - // printf("%llu", p_id[i]); - // printf("\n"); - // printf("%.6f", p_dist[i]); - // printf("\n"); - // ss_res_id << p_id[i] << " "; - // ss_res_dist << p_dist[i] << " "; - // } - // std::cout << std::endl << "after search: " << std::endl; - // std::cout << ss_res_id.str() << std::endl; - // std::cout << ss_res_dist.str() << std::endl << std::endl; - - auto ret_ds = std::make_shared(); - ret_ds->Set(meta::IDS, p_id); - ret_ds->Set(meta::DISTANCE, p_dist); - return ret_ds; - } catch (faiss::FaissException& e) { - KNOWHERE_THROW_MSG(e.what()); - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} - -#if 0 -DatasetPtr -IVF::QueryById(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_ || !index_->is_trained) { - KNOWHERE_THROW_MSG("index not initialize or trained"); - } - - auto rows = dataset_ptr->Get(meta::ROWS); - auto p_data = dataset_ptr->Get(meta::IDS); - - try { - int64_t k = config[meta::TOPK].get(); - auto elems = rows * k; - - size_t p_id_size = sizeof(int64_t) * elems; - size_t p_dist_size = sizeof(float) * elems; - auto p_id = (int64_t*)malloc(p_id_size); - auto p_dist = (float*)malloc(p_dist_size); - - // todo: enable search by id (zhiru) - // auto blacklist = dataset_ptr->Get("bitset"); - auto index_ivf = std::static_pointer_cast(index_); - index_ivf->search_by_id(rows, p_data, k, p_dist, p_id, bitset_); - - // std::stringstream ss_res_id, ss_res_dist; - // for (int i = 0; i < 10; ++i) { - // printf("%llu", res_ids[i]); - // printf("\n"); - // printf("%.6f", res_dis[i]); - // printf("\n"); - // ss_res_id << res_ids[i] << " "; - // ss_res_dist << res_dis[i] << " "; - // } - // std::cout << std::endl << "after search: " << std::endl; - // std::cout << ss_res_id.str() << std::endl; - // std::cout << ss_res_dist.str() << std::endl << std::endl; - - auto ret_ds = std::make_shared(); - ret_ds->Set(meta::IDS, p_id); - ret_ds->Set(meta::DISTANCE, p_dist); - return ret_ds; - } catch (faiss::FaissException& e) { - KNOWHERE_THROW_MSG(e.what()); - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} - -DatasetPtr -IVF::GetVectorById(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_ || !index_->is_trained) { - KNOWHERE_THROW_MSG("index not initialize or trained"); - } - - auto p_data = dataset_ptr->Get(meta::IDS); - auto elems = dataset_ptr->Get(meta::DIM); - - try { - size_t p_x_size = sizeof(float) * elems; - auto p_x = (float*)malloc(p_x_size); - - auto index_ivf = std::static_pointer_cast(index_); - index_ivf->get_vector_by_id(1, p_data, p_x, bitset_); - - auto ret_ds = std::make_shared(); - ret_ds->Set(meta::TENSOR, p_x); - return ret_ds; - } catch (faiss::FaissException& e) { - KNOWHERE_THROW_MSG(e.what()); - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} -#endif - -int64_t -IVF::Count() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - return index_->ntotal; -} - -int64_t -IVF::Dim() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - return index_->d; -} - -void -IVF::Seal() { - if (!index_ || !index_->is_trained) { - KNOWHERE_THROW_MSG("index not initialize or trained"); - } - SealImpl(); -} - -void -IVF::UpdateIndexSize() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - auto ivf_index = dynamic_cast(index_.get()); - auto nb = ivf_index->invlists->compute_ntotal(); - auto nlist = ivf_index->nlist; - auto code_size = ivf_index->code_size; - // ivf codes, ivf ids and quantizer - index_size_ = nb * code_size + nb * sizeof(int64_t) + nlist * code_size; -} - -VecIndexPtr -IVF::CopyCpuToGpu(const int64_t device_id, const Config& config) { -#ifdef MILVUS_GPU_VERSION - if (auto res = FaissGpuResourceMgr::GetInstance().GetRes(device_id)) { - ResScope rs(res, device_id, false); - auto gpu_index = faiss::gpu::index_cpu_to_gpu(res->faiss_res.get(), device_id, index_.get()); - - std::shared_ptr device_index; - device_index.reset(gpu_index); - return std::make_shared(device_index, device_id, res); - } else { - KNOWHERE_THROW_MSG("CopyCpuToGpu Error, can't get gpu_resource"); - } - -#else - KNOWHERE_THROW_MSG("Calling IVF::CopyCpuToGpu when we are using CPU version"); -#endif -} - -void -IVF::GenGraph(const float* data, const int64_t k, GraphType& graph, const Config& config) { - int64_t K = k + 1; - auto ntotal = Count(); - - size_t dim = config[meta::DIM]; - auto batch_size = 1000; - auto tail_batch_size = ntotal % batch_size; - auto batch_search_count = ntotal / batch_size; - auto total_search_count = tail_batch_size == 0 ? batch_search_count : batch_search_count + 1; - - std::vector res_dis(K * batch_size); - graph.resize(ntotal); - GraphType res_vec(total_search_count); - for (int i = 0; i < total_search_count; ++i) { - // it is usually used in NSG::train, to check BuilderSuspend - faiss::BuilderSuspend::check_wait(); - - auto b_size = (i == (total_search_count - 1)) && tail_batch_size != 0 ? tail_batch_size : batch_size; - - auto& res = res_vec[i]; - res.resize(K * b_size); - - auto xq = data + batch_size * dim * i; - QueryImpl(b_size, (float*)xq, K, res_dis.data(), res.data(), config); - - for (int j = 0; j < b_size; ++j) { - auto& node = graph[batch_size * i + j]; - node.resize(k); - auto start_pos = j * K + 1; - for (int m = 0, cursor = start_pos; m < k && cursor < start_pos + k; ++m, ++cursor) { - node[m] = res[cursor]; - } - } - } -} - -std::shared_ptr -IVF::GenParams(const Config& config) { - auto params = std::make_shared(); - params->nprobe = config[IndexParams::nprobe]; - // params->max_codes = config["max_codes"]; - return params; -} - -void -IVF::QueryImpl(int64_t n, const float* data, int64_t k, float* distances, int64_t* labels, const Config& config) { - auto params = GenParams(config); - auto ivf_index = dynamic_cast(index_.get()); - ivf_index->nprobe = params->nprobe; - stdclock::time_point before = stdclock::now(); - if (params->nprobe > 1 && n <= 4) { - ivf_index->parallel_mode = 1; - } else { - ivf_index->parallel_mode = 0; - } - ivf_index->search(n, (float*)data, k, distances, labels, bitset_); - stdclock::time_point after = stdclock::now(); - double search_cost = (std::chrono::duration(after - before)).count(); - LOG_KNOWHERE_DEBUG_ << "IVF search cost: " << search_cost - << ", quantization cost: " << faiss::indexIVF_stats.quantization_time - << ", data search cost: " << faiss::indexIVF_stats.search_time; - faiss::indexIVF_stats.quantization_time = 0; - faiss::indexIVF_stats.search_time = 0; -} - -void -IVF::SealImpl() { -#ifdef MILVUS_GPU_VERSION - faiss::Index* index = index_.get(); - auto idx = dynamic_cast(index); - if (idx != nullptr) { - idx->to_readonly(); - } -#endif -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexIVF.h b/core/src/index/knowhere/knowhere/index/vector_index/IndexIVF.h deleted file mode 100644 index d33481ed4b..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexIVF.h +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include -#include -#include - -#include - -#include "knowhere/common/Typedef.h" -#include "knowhere/index/vector_index/FaissBaseIndex.h" -#include "knowhere/index/vector_index/VecIndex.h" - -namespace milvus { -namespace knowhere { - -class IVF : public VecIndex, public FaissBaseIndex { - public: - IVF() : FaissBaseIndex(nullptr) { - index_type_ = IndexEnum::INDEX_FAISS_IVFFLAT; - } - - explicit IVF(std::shared_ptr index) : FaissBaseIndex(std::move(index)) { - index_type_ = IndexEnum::INDEX_FAISS_IVFFLAT; - } - - BinarySet - Serialize(const Config& config = Config()) override; - - void - Load(const BinarySet&) override; - - void - Train(const DatasetPtr&, const Config&) override; - - void - Add(const DatasetPtr&, const Config&) override; - - void - AddWithoutIds(const DatasetPtr&, const Config&) override; - - DatasetPtr - Query(const DatasetPtr&, const Config&) override; - -#if 0 - DatasetPtr - QueryById(const DatasetPtr& dataset, const Config& config) override; -#endif - - int64_t - Count() override; - - int64_t - Dim() override; - - void - UpdateIndexSize() override; - -#if 0 - DatasetPtr - GetVectorById(const DatasetPtr& dataset, const Config& config) override; -#endif - - virtual void - Seal(); - - virtual VecIndexPtr - CopyCpuToGpu(const int64_t, const Config&); - - virtual void - GenGraph(const float* data, const int64_t k, GraphType& graph, const Config& config); - - protected: - virtual std::shared_ptr - GenParams(const Config&); - - virtual void - QueryImpl(int64_t, const float*, int64_t, float*, int64_t*, const Config&); - - void - SealImpl() override; - - protected: - std::mutex mutex_; -}; - -using IVFPtr = std::shared_ptr; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFPQ.cpp b/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFPQ.cpp deleted file mode 100644 index ab12bc838c..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFPQ.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include - -#include -#include -#include -#ifdef MILVUS_GPU_VERSION -#include -#endif - -#include "knowhere/common/Exception.h" -#include "knowhere/common/Log.h" -#include "knowhere/index/vector_index/IndexIVFPQ.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" -#include "knowhere/index/vector_index/helpers/IndexParameter.h" -#ifdef MILVUS_GPU_VERSION -#include "knowhere/index/vector_index/gpu/IndexGPUIVF.h" -#include "knowhere/index/vector_index/gpu/IndexGPUIVFPQ.h" -#endif - -namespace milvus { -namespace knowhere { - -void -IVFPQ::Train(const DatasetPtr& dataset_ptr, const Config& config) { - GETTENSOR(dataset_ptr) - - faiss::MetricType metric_type = GetMetricType(config[Metric::TYPE].get()); - faiss::Index* coarse_quantizer = new faiss::IndexFlat(dim, metric_type); - index_ = std::shared_ptr(new faiss::IndexIVFPQ( - coarse_quantizer, dim, config[IndexParams::nlist].get(), config[IndexParams::m].get(), - config[IndexParams::nbits].get(), metric_type)); - - index_->train(rows, (float*)p_data); -} - -VecIndexPtr -IVFPQ::CopyCpuToGpu(const int64_t device_id, const Config& config) { -#ifdef MILVUS_GPU_VERSION - if (auto res = FaissGpuResourceMgr::GetInstance().GetRes(device_id)) { - ResScope rs(res, device_id, false); - auto gpu_index = faiss::gpu::index_cpu_to_gpu(res->faiss_res.get(), device_id, index_.get()); - - std::shared_ptr device_index; - device_index.reset(gpu_index); - return std::make_shared(device_index, device_id, res); - } else { - KNOWHERE_THROW_MSG("CopyCpuToGpu Error, can't get gpu_resource"); - } -#else - KNOWHERE_THROW_MSG("Calling IVFPQ::CopyCpuToGpu when we are using CPU version"); -#endif -} - -std::shared_ptr -IVFPQ::GenParams(const Config& config) { - auto params = std::make_shared(); - params->nprobe = config[IndexParams::nprobe]; - // params->scan_table_threshold = config["scan_table_threhold"] - // params->polysemous_ht = config["polysemous_ht"] - // params->max_codes = config["max_codes"] - - return params; -} - -void -IVFPQ::UpdateIndexSize() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - auto ivfpq_index = dynamic_cast(index_.get()); - auto nb = ivfpq_index->invlists->compute_ntotal(); - auto code_size = ivfpq_index->code_size; - auto pq = ivfpq_index->pq; - auto nlist = ivfpq_index->nlist; - auto d = ivfpq_index->d; - - // ivf codes, ivf ids and quantizer - auto capacity = nb * code_size + nb * sizeof(int64_t) + nlist * d * sizeof(float); - auto centroid_table = pq.M * pq.ksub * pq.dsub * sizeof(float); - auto precomputed_table = nlist * pq.M * pq.ksub * sizeof(float); - if (precomputed_table > ivfpq_index->precomputed_table_max_bytes) { - // will not precompute table - precomputed_table = 0; - } - index_size_ = capacity + centroid_table + precomputed_table; -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFPQ.h b/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFPQ.h deleted file mode 100644 index aed4072099..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFPQ.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include - -#include "knowhere/index/vector_index/IndexIVF.h" - -namespace milvus { -namespace knowhere { - -class IVFPQ : public IVF { - public: - IVFPQ() : IVF() { - index_type_ = IndexEnum::INDEX_FAISS_IVFPQ; - } - - explicit IVFPQ(std::shared_ptr index) : IVF(std::move(index)) { - index_type_ = IndexEnum::INDEX_FAISS_IVFPQ; - } - - void - Train(const DatasetPtr&, const Config&) override; - - VecIndexPtr - CopyCpuToGpu(const int64_t, const Config&) override; - - void - UpdateIndexSize() override; - - protected: - std::shared_ptr - GenParams(const Config& config) override; -}; - -using IVFPQPtr = std::shared_ptr; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFSQ.cpp b/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFSQ.cpp deleted file mode 100644 index 5ceb732c83..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFSQ.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include -#include - -#ifdef MILVUS_GPU_VERSION -#include -#include -#endif -#include -#include -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/IndexIVFSQ.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" -#include "knowhere/index/vector_index/helpers/IndexParameter.h" -#ifdef MILVUS_GPU_VERSION -#include "knowhere/index/vector_index/gpu/IndexGPUIVFSQ.h" -#include "knowhere/index/vector_index/helpers/FaissGpuResourceMgr.h" -#endif - -namespace milvus { -namespace knowhere { - -void -IVFSQ::Train(const DatasetPtr& dataset_ptr, const Config& config) { - GETTENSOR(dataset_ptr) - - std::stringstream index_type; - index_type << "IVF" << config[IndexParams::nlist] << "," - << "SQ" << config[IndexParams::nbits]; - index_ = std::shared_ptr( - faiss::index_factory(dim, index_type.str().c_str(), GetMetricType(config[Metric::TYPE].get()))); - index_->train(rows, (float*)p_data); -} - -VecIndexPtr -IVFSQ::CopyCpuToGpu(const int64_t device_id, const Config& config) { -#ifdef MILVUS_GPU_VERSION - if (auto res = FaissGpuResourceMgr::GetInstance().GetRes(device_id)) { - ResScope rs(res, device_id, false); - - auto gpu_index = faiss::gpu::index_cpu_to_gpu(res->faiss_res.get(), device_id, index_.get()); - - std::shared_ptr device_index; - device_index.reset(gpu_index); - return std::make_shared(device_index, device_id, res); - } else { - KNOWHERE_THROW_MSG("CopyCpuToGpu Error, can't get gpu_resource"); - } -#else - KNOWHERE_THROW_MSG("Calling IVFSQ::CopyCpuToGpu when we are using CPU version"); -#endif -} - -void -IVFSQ::UpdateIndexSize() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - auto ivfsq_index = dynamic_cast(index_.get()); - auto nb = ivfsq_index->invlists->compute_ntotal(); - auto code_size = ivfsq_index->code_size; - auto nlist = ivfsq_index->nlist; - auto d = ivfsq_index->d; - // ivf codes, ivf ids, sq trained vectors and quantizer - index_size_ = nb * code_size + nb * sizeof(int64_t) + 2 * d * sizeof(float) + nlist * d * sizeof(float); -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFSQ.h b/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFSQ.h deleted file mode 100644 index 0c33eda569..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexIVFSQ.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include - -#include "knowhere/index/vector_index/IndexIVF.h" - -namespace milvus { -namespace knowhere { - -class IVFSQ : public IVF { - public: - IVFSQ() : IVF() { - index_type_ = IndexEnum::INDEX_FAISS_IVFSQ8; - } - - explicit IVFSQ(std::shared_ptr index) : IVF(std::move(index)) { - index_type_ = IndexEnum::INDEX_FAISS_IVFSQ8; - } - - void - Train(const DatasetPtr&, const Config&) override; - - VecIndexPtr - CopyCpuToGpu(const int64_t, const Config&) override; - - void - UpdateIndexSize() override; -}; - -using IVFSQPtr = std::shared_ptr; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexNSG.cpp b/core/src/index/knowhere/knowhere/index/vector_index/IndexNSG.cpp deleted file mode 100644 index 32e87ca7da..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexNSG.cpp +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/common/Timer.h" -#include "knowhere/index/vector_index/IndexIDMAP.h" -#include "knowhere/index/vector_index/IndexIVF.h" -#include "knowhere/index/vector_index/IndexNSG.h" -#include "knowhere/index/vector_index/IndexType.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" -#include "knowhere/index/vector_index/impl/nsg/NSG.h" -#include "knowhere/index/vector_index/impl/nsg/NSGIO.h" - -#ifdef MILVUS_GPU_VERSION -#include "knowhere/index/vector_index/gpu/IndexGPUIDMAP.h" -#include "knowhere/index/vector_index/gpu/IndexGPUIVF.h" -#include "knowhere/index/vector_index/helpers/Cloner.h" -#endif - -namespace milvus { -namespace knowhere { - -BinarySet -NSG::Serialize(const Config& config) { - if (!index_ || !index_->is_trained) { - KNOWHERE_THROW_MSG("index not initialize or trained"); - } - - try { - fiu_do_on("NSG.Serialize.throw_exception", throw std::exception()); - std::lock_guard lk(mutex_); - impl::NsgIndex* index = index_.get(); - - MemoryIOWriter writer; - impl::write_index(index, writer); - std::shared_ptr data(writer.data_); - - BinarySet res_set; - res_set.Append("NSG", data, writer.rp); - return res_set; - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} - -void -NSG::Load(const BinarySet& index_binary) { - try { - fiu_do_on("NSG.Load.throw_exception", throw std::exception()); - std::lock_guard lk(mutex_); - auto binary = index_binary.GetByName("NSG"); - - MemoryIOReader reader; - reader.total = binary->size; - reader.data_ = binary->data.get(); - - auto index = impl::read_index(reader); - index_.reset(index); - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} - -DatasetPtr -NSG::Query(const DatasetPtr& dataset_ptr, const Config& config) { - if (!index_ || !index_->is_trained) { - KNOWHERE_THROW_MSG("index not initialize or trained"); - } - - GETTENSOR(dataset_ptr) - - try { - auto elems = rows * config[meta::TOPK].get(); - size_t p_id_size = sizeof(int64_t) * elems; - size_t p_dist_size = sizeof(float) * elems; - auto p_id = (int64_t*)malloc(p_id_size); - auto p_dist = (float*)malloc(p_dist_size); - - faiss::ConcurrentBitsetPtr blacklist = GetBlacklist(); - - impl::SearchParams s_params; - s_params.search_length = config[IndexParams::search_length]; - s_params.k = config[meta::TOPK]; - { - std::lock_guard lk(mutex_); - index_->Search((float*)p_data, rows, dim, config[meta::TOPK].get(), p_dist, p_id, s_params, - blacklist); - } - - auto ret_ds = std::make_shared(); - ret_ds->Set(meta::IDS, p_id); - ret_ds->Set(meta::DISTANCE, p_dist); - return ret_ds; - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} - -void -NSG::Train(const DatasetPtr& dataset_ptr, const Config& config) { - auto idmap = std::make_shared(); - idmap->Train(dataset_ptr, config); - idmap->AddWithoutIds(dataset_ptr, config); - impl::Graph knng; - const float* raw_data = idmap->GetRawVectors(); - const int64_t device_id = config[knowhere::meta::DEVICEID].get(); - const int64_t k = config[IndexParams::knng].get(); -#ifdef MILVUS_GPU_VERSION - if (device_id == -1) { - auto preprocess_index = std::make_shared(); - preprocess_index->Train(dataset_ptr, config); - preprocess_index->AddWithoutIds(dataset_ptr, config); - preprocess_index->GenGraph(raw_data, k, knng, config); - } else { - auto gpu_idx = cloner::CopyCpuToGpu(idmap, device_id, config); - auto gpu_idmap = std::dynamic_pointer_cast(gpu_idx); - gpu_idmap->GenGraph(raw_data, k, knng, config); - } -#else - auto preprocess_index = std::make_shared(); - preprocess_index->Train(dataset_ptr, config); - preprocess_index->AddWithoutIds(dataset_ptr, config); - preprocess_index->GenGraph(raw_data, k, knng, config); -#endif - - impl::BuildParams b_params; - b_params.candidate_pool_size = config[IndexParams::candidate]; - b_params.out_degree = config[IndexParams::out_degree]; - b_params.search_length = config[IndexParams::search_length]; - - GETTENSORWITHIDS(dataset_ptr) - - impl::NsgIndex::Metric_Type metric; - auto metric_str = config[Metric::TYPE].get(); - if (metric_str == knowhere::Metric::IP) { - metric = impl::NsgIndex::Metric_Type::Metric_Type_IP; - } else if (metric_str == knowhere::Metric::L2) { - metric = impl::NsgIndex::Metric_Type::Metric_Type_L2; - } else { - KNOWHERE_THROW_MSG("Metric is not supported"); - } - - index_ = std::make_shared(dim, rows, metric); - index_->SetKnnGraph(knng); - index_->Build_with_ids(rows, (float*)p_data, (int64_t*)p_ids, b_params); -} - -int64_t -NSG::Count() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - return index_->ntotal; -} - -int64_t -NSG::Dim() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - return index_->dimension; -} - -void -NSG::UpdateIndexSize() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - index_size_ = index_->GetSize(); -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexNSG.h b/core/src/index/knowhere/knowhere/index/vector_index/IndexNSG.h deleted file mode 100644 index 2cbc8369e5..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexNSG.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/common/Log.h" -#include "knowhere/index/vector_index/VecIndex.h" - -namespace milvus { -namespace knowhere { - -namespace impl { -class NsgIndex; -} - -class NSG : public VecIndex { - public: - explicit NSG(const int64_t gpu_num = -1) : gpu_(gpu_num) { - if (gpu_ >= 0) { - index_mode_ = IndexMode::MODE_GPU; - } - index_type_ = IndexEnum::INDEX_NSG; - } - - BinarySet - Serialize(const Config& config = Config()) override; - - void - Load(const BinarySet&) override; - - void - BuildAll(const DatasetPtr& dataset_ptr, const Config& config) override { - Train(dataset_ptr, config); - } - - void - Train(const DatasetPtr&, const Config&) override; - - void - Add(const DatasetPtr&, const Config&) override { - KNOWHERE_THROW_MSG("Incremental index is not supported"); - } - - void - AddWithoutIds(const DatasetPtr&, const Config&) override { - KNOWHERE_THROW_MSG("Addwithoutids is not supported"); - } - - DatasetPtr - Query(const DatasetPtr&, const Config&) override; - - int64_t - Count() override; - - int64_t - Dim() override; - - void - UpdateIndexSize() override; - - private: - std::mutex mutex_; - int64_t gpu_; - std::shared_ptr index_; -}; - -using NSGIndexPtr = std::shared_ptr(); - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexSPTAG.cpp b/core/src/index/knowhere/knowhere/index/vector_index/IndexSPTAG.cpp deleted file mode 100644 index 2dc86678f5..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexSPTAG.cpp +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include -#include - -#include -#include -#include - -#undef mkdir - -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/IndexSPTAG.h" -#include "knowhere/index/vector_index/adapter/SptagAdapter.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" -#include "knowhere/index/vector_index/helpers/SPTAGParameterMgr.h" - -namespace milvus { -namespace knowhere { - -CPUSPTAGRNG::CPUSPTAGRNG(const std::string& IndexType) { - if (IndexType == "KDT") { - index_ptr_ = SPTAG::VectorIndex::CreateInstance(SPTAG::IndexAlgoType::KDT, SPTAG::VectorValueType::Float); - index_ptr_->SetParameter("DistCalcMethod", "L2"); - index_type_ = IndexEnum::INDEX_SPTAG_KDT_RNT; - } else { - index_ptr_ = SPTAG::VectorIndex::CreateInstance(SPTAG::IndexAlgoType::BKT, SPTAG::VectorValueType::Float); - index_ptr_->SetParameter("DistCalcMethod", "L2"); - index_type_ = IndexEnum::INDEX_SPTAG_BKT_RNT; - } -} - -BinarySet -CPUSPTAGRNG::Serialize(const Config& config) { - std::string index_config; - std::vector index_blobs; - - std::shared_ptr> buffersize = index_ptr_->CalculateBufferSize(); - std::vector res(buffersize->size() + 1); - for (uint64_t i = 1; i < res.size(); i++) { - res[i] = new char[buffersize->at(i - 1)]; - auto ptr = &res[i][0]; - index_blobs.emplace_back(SPTAG::ByteArray((std::uint8_t*)ptr, buffersize->at(i - 1), false)); - } - - index_ptr_->SaveIndex(index_config, index_blobs); - - size_t length = index_config.length(); - char* cstr = new char[length]; - snprintf(cstr, length, "%s", index_config.c_str()); - - BinarySet binary_set; - std::shared_ptr sample; - sample.reset(static_cast(index_blobs[0].Data())); - std::shared_ptr tree; - tree.reset(static_cast(index_blobs[1].Data())); - std::shared_ptr graph; - graph.reset(static_cast(index_blobs[2].Data())); - std::shared_ptr deleteid; - deleteid.reset(static_cast(index_blobs[3].Data())); - std::shared_ptr metadata1; - metadata1.reset(static_cast(index_blobs[4].Data())); - std::shared_ptr metadata2; - metadata2.reset(static_cast(index_blobs[5].Data())); - std::shared_ptr x_cfg; - x_cfg.reset(static_cast((void*)cstr)); - - binary_set.Append("samples", sample, index_blobs[0].Length()); - binary_set.Append("tree", tree, index_blobs[1].Length()); - binary_set.Append("deleteid", deleteid, index_blobs[3].Length()); - binary_set.Append("metadata1", metadata1, index_blobs[4].Length()); - binary_set.Append("metadata2", metadata2, index_blobs[5].Length()); - binary_set.Append("config", x_cfg, length); - binary_set.Append("graph", graph, index_blobs[2].Length()); - - return binary_set; -} - -void -CPUSPTAGRNG::Load(const BinarySet& binary_set) { - std::string index_config; - std::vector index_blobs; - - auto samples = binary_set.GetByName("samples"); - index_blobs.push_back(SPTAG::ByteArray(samples->data.get(), samples->size, false)); - - auto tree = binary_set.GetByName("tree"); - index_blobs.push_back(SPTAG::ByteArray(tree->data.get(), tree->size, false)); - - auto graph = binary_set.GetByName("graph"); - index_blobs.push_back(SPTAG::ByteArray(graph->data.get(), graph->size, false)); - - auto deleteid = binary_set.GetByName("deleteid"); - index_blobs.push_back(SPTAG::ByteArray(deleteid->data.get(), deleteid->size, false)); - - auto metadata1 = binary_set.GetByName("metadata1"); - index_blobs.push_back(SPTAG::ByteArray(CopyBinary(metadata1), metadata1->size, true)); - - auto metadata2 = binary_set.GetByName("metadata2"); - index_blobs.push_back(SPTAG::ByteArray(metadata2->data.get(), metadata2->size, false)); - - auto config = binary_set.GetByName("config"); - index_config = reinterpret_cast(config->data.get()); - - index_ptr_->LoadIndex(index_config, index_blobs); -} - -void -CPUSPTAGRNG::Train(const DatasetPtr& origin, const Config& train_config) { - SetParameters(train_config); - - DatasetPtr dataset = origin; - - auto vectorset = ConvertToVectorSet(dataset); - auto metaset = ConvertToMetadataSet(dataset); - index_ptr_->BuildIndex(vectorset, metaset); -} - -void -CPUSPTAGRNG::SetParameters(const Config& config) { -#define Assign(param_name, str_name) \ - index_ptr_->SetParameter(str_name, std::to_string(build_cfg[param_name].get())) - - if (index_type_ == IndexEnum::INDEX_SPTAG_KDT_RNT) { - auto build_cfg = SPTAGParameterMgr::GetInstance().GetKDTParameters(); - - Assign("kdtnumber", "KDTNumber"); - Assign("numtopdimensionkdtsplit", "NumTopDimensionKDTSplit"); - Assign("samples", "Samples"); - Assign("tptnumber", "TPTNumber"); - Assign("tptleafsize", "TPTLeafSize"); - Assign("numtopdimensiontptsplit", "NumTopDimensionTPTSplit"); - Assign("neighborhoodsize", "NeighborhoodSize"); - Assign("graphneighborhoodscale", "GraphNeighborhoodScale"); - Assign("graphcefscale", "GraphCEFScale"); - Assign("refineiterations", "RefineIterations"); - Assign("cef", "CEF"); - Assign("maxcheckforrefinegraph", "MaxCheckForRefineGraph"); - Assign("numofthreads", "NumberOfThreads"); - Assign("maxcheck", "MaxCheck"); - Assign("thresholdofnumberofcontinuousnobetterpropagation", "ThresholdOfNumberOfContinuousNoBetterPropagation"); - Assign("numberofinitialdynamicpivots", "NumberOfInitialDynamicPivots"); - Assign("numberofotherdynamicpivots", "NumberOfOtherDynamicPivots"); - } else { - auto build_cfg = SPTAGParameterMgr::GetInstance().GetBKTParameters(); - - Assign("bktnumber", "BKTNumber"); - Assign("bktkmeansk", "BKTKMeansK"); - Assign("bktleafsize", "BKTLeafSize"); - Assign("samples", "Samples"); - Assign("tptnumber", "TPTNumber"); - Assign("tptleafsize", "TPTLeafSize"); - Assign("numtopdimensiontptsplit", "NumTopDimensionTPTSplit"); - Assign("neighborhoodsize", "NeighborhoodSize"); - Assign("graphneighborhoodscale", "GraphNeighborhoodScale"); - Assign("graphcefscale", "GraphCEFScale"); - Assign("refineiterations", "RefineIterations"); - Assign("cef", "CEF"); - Assign("maxcheckforrefinegraph", "MaxCheckForRefineGraph"); - Assign("numofthreads", "NumberOfThreads"); - Assign("maxcheck", "MaxCheck"); - Assign("thresholdofnumberofcontinuousnobetterpropagation", "ThresholdOfNumberOfContinuousNoBetterPropagation"); - Assign("numberofinitialdynamicpivots", "NumberOfInitialDynamicPivots"); - Assign("numberofotherdynamicpivots", "NumberOfOtherDynamicPivots"); - } -} - -DatasetPtr -CPUSPTAGRNG::Query(const DatasetPtr& dataset_ptr, const Config& config) { - SetParameters(config); - - float* p_data = (float*)dataset_ptr->Get(meta::TENSOR); - for (auto i = 0; i < 10; ++i) { - for (auto j = 0; j < 10; ++j) { - std::cout << p_data[i * 10 + j] << " "; - } - std::cout << std::endl; - } - std::vector query_results = ConvertToQueryResult(dataset_ptr, config); - -#pragma omp parallel for - for (auto i = 0; i < query_results.size(); ++i) { - auto target = (float*)query_results[i].GetTarget(); - std::cout << target[0] << ", " << target[1] << ", " << target[2] << std::endl; - index_ptr_->SearchIndex(query_results[i]); - } - - return ConvertToDataset(query_results); -} - -int64_t -CPUSPTAGRNG::Count() { - if (!index_ptr_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - return index_ptr_->GetNumSamples(); -} - -int64_t -CPUSPTAGRNG::Dim() { - if (!index_ptr_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - return index_ptr_->GetFeatureDim(); -} - -void -CPUSPTAGRNG::UpdateIndexSize() { - if (!index_ptr_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - index_size_ = index_ptr_->GetIndexSize(); -} - -// void -// CPUSPTAGRNG::Add(const DatasetPtr& origin, const Config& add_config) { -// SetParameters(add_config); -// DatasetPtr dataset = origin->Clone(); - -// // if (index_ptr_->GetDistCalcMethod() == SPTAG::DistCalcMethod::Cosine -// // && preprocessor_) { -// // preprocessor_->Preprocess(dataset); -// //} - -// auto vectorset = ConvertToVectorSet(dataset); -// auto metaset = ConvertToMetadataSet(dataset); -// index_ptr_->AddIndex(vectorset, metaset); -// } - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexSPTAG.h b/core/src/index/knowhere/knowhere/index/vector_index/IndexSPTAG.h deleted file mode 100644 index 945f3369b8..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexSPTAG.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -#include -#include -#include - -#include "knowhere/index/vector_index/VecIndex.h" - -namespace milvus { -namespace knowhere { - -class CPUSPTAGRNG : public VecIndex { - public: - explicit CPUSPTAGRNG(const std::string& IndexType); - - public: - BinarySet - Serialize(const Config& config = Config()) override; - - void - Load(const BinarySet& index_array) override; - - void - BuildAll(const DatasetPtr& dataset_ptr, const Config& config) override { - Train(dataset_ptr, config); - } - - void - Train(const DatasetPtr& dataset_ptr, const Config& config) override; - - void - Add(const DatasetPtr&, const Config&) override { - KNOWHERE_THROW_MSG("Incremental index is not supported"); - } - - void - AddWithoutIds(const DatasetPtr&, const Config&) override { - KNOWHERE_THROW_MSG("Incremental index is not supported"); - } - - DatasetPtr - Query(const DatasetPtr& dataset_ptr, const Config& config) override; - - int64_t - Count() override; - - int64_t - Dim() override; - - void - UpdateIndexSize() override; - - private: - void - SetParameters(const Config& config); - - private: - std::shared_ptr index_ptr_; -}; - -using CPUSPTAGRNGPtr = std::shared_ptr; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexType.cpp b/core/src/index/knowhere/knowhere/index/vector_index/IndexType.cpp deleted file mode 100644 index 7192e93f4c..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexType.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include "knowhere/index/vector_index/IndexType.h" -#include -#include "knowhere/common/Exception.h" - -namespace milvus { -namespace knowhere { - -/* for compatible with 0.7.0 */ -static std::unordered_map old_index_type_str_map = { - {(int32_t)OldIndexType::INVALID, "INVALID"}, - {(int32_t)OldIndexType::FAISS_IDMAP, IndexEnum::INDEX_FAISS_IDMAP}, - {(int32_t)OldIndexType::FAISS_IVFFLAT_CPU, IndexEnum::INDEX_FAISS_IVFFLAT}, - {(int32_t)OldIndexType::FAISS_IVFFLAT_GPU, IndexEnum::INDEX_FAISS_IVFFLAT}, - {(int32_t)OldIndexType::FAISS_IVFFLAT_MIX, IndexEnum::INDEX_FAISS_IVFFLAT}, - {(int32_t)OldIndexType::FAISS_IVFPQ_CPU, IndexEnum::INDEX_FAISS_IVFPQ}, - {(int32_t)OldIndexType::FAISS_IVFPQ_GPU, IndexEnum::INDEX_FAISS_IVFPQ}, - {(int32_t)OldIndexType::FAISS_IVFPQ_MIX, IndexEnum::INDEX_FAISS_IVFPQ}, - {(int32_t)OldIndexType::FAISS_IVFSQ8_MIX, IndexEnum::INDEX_FAISS_IVFSQ8}, - {(int32_t)OldIndexType::FAISS_IVFSQ8_CPU, IndexEnum::INDEX_FAISS_IVFSQ8}, - {(int32_t)OldIndexType::FAISS_IVFSQ8_GPU, IndexEnum::INDEX_FAISS_IVFSQ8}, - {(int32_t)OldIndexType::FAISS_IVFSQ8_HYBRID, IndexEnum::INDEX_FAISS_IVFSQ8H}, - {(int32_t)OldIndexType::NSG_MIX, IndexEnum::INDEX_NSG}, - {(int32_t)OldIndexType::SPTAG_KDT_RNT_CPU, IndexEnum::INDEX_SPTAG_KDT_RNT}, - {(int32_t)OldIndexType::SPTAG_BKT_RNT_CPU, IndexEnum::INDEX_SPTAG_BKT_RNT}, - {(int32_t)OldIndexType::HNSW, IndexEnum::INDEX_HNSW}, - {(int32_t)OldIndexType::ANNOY, IndexEnum::INDEX_ANNOY}, - {(int32_t)OldIndexType::FAISS_BIN_IDMAP, IndexEnum::INDEX_FAISS_BIN_IDMAP}, - {(int32_t)OldIndexType::FAISS_BIN_IVFLAT_CPU, IndexEnum::INDEX_FAISS_BIN_IVFFLAT}, -}; - -static std::unordered_map str_old_index_type_map = { - {"", (int32_t)OldIndexType::INVALID}, - {IndexEnum::INDEX_FAISS_IDMAP, (int32_t)OldIndexType::FAISS_IDMAP}, - {IndexEnum::INDEX_FAISS_IVFFLAT, (int32_t)OldIndexType::FAISS_IVFFLAT_CPU}, - {IndexEnum::INDEX_FAISS_IVFPQ, (int32_t)OldIndexType::FAISS_IVFPQ_CPU}, - {IndexEnum::INDEX_FAISS_IVFSQ8, (int32_t)OldIndexType::FAISS_IVFSQ8_CPU}, - {IndexEnum::INDEX_FAISS_IVFSQ8H, (int32_t)OldIndexType::FAISS_IVFSQ8_HYBRID}, - {IndexEnum::INDEX_NSG, (int32_t)OldIndexType::NSG_MIX}, - {IndexEnum::INDEX_SPTAG_KDT_RNT, (int32_t)OldIndexType::SPTAG_KDT_RNT_CPU}, - {IndexEnum::INDEX_SPTAG_BKT_RNT, (int32_t)OldIndexType::SPTAG_BKT_RNT_CPU}, - {IndexEnum::INDEX_HNSW, (int32_t)OldIndexType::HNSW}, - {IndexEnum::INDEX_ANNOY, (int32_t)OldIndexType::ANNOY}, - {IndexEnum::INDEX_FAISS_BIN_IDMAP, (int32_t)OldIndexType::FAISS_BIN_IDMAP}, - {IndexEnum::INDEX_FAISS_BIN_IVFFLAT, (int32_t)OldIndexType::FAISS_BIN_IVFLAT_CPU}, -}; - -/* used in 0.8.0 */ -namespace IndexEnum { -const char* INVALID = ""; -const char* INDEX_FAISS_IDMAP = "IDMAP"; -const char* INDEX_FAISS_IVFFLAT = "IVF_FLAT"; -const char* INDEX_FAISS_IVFPQ = "IVF_PQ"; -const char* INDEX_FAISS_IVFSQ8 = "IVF_SQ8"; -const char* INDEX_FAISS_IVFSQ8H = "IVF_SQ8_HYBRID"; -const char* INDEX_FAISS_BIN_IDMAP = "BIN_IDMAP"; -const char* INDEX_FAISS_BIN_IVFFLAT = "BIN_IVF_FLAT"; -const char* INDEX_NSG = "NSG"; -const char* INDEX_SPTAG_KDT_RNT = "SPTAG_KDT_RNT"; -const char* INDEX_SPTAG_BKT_RNT = "SPTAG_BKT_RNT"; -const char* INDEX_HNSW = "HNSW"; -const char* INDEX_ANNOY = "ANNOY"; -} // namespace IndexEnum - -std::string -OldIndexTypeToStr(const int32_t type) { - try { - return old_index_type_str_map.at(type); - } catch (...) { - KNOWHERE_THROW_MSG("Invalid index type " + std::to_string(type)); - } -} - -int32_t -StrToOldIndexType(const std::string& str) { - try { - return str_old_index_type_map.at(str); - } catch (...) { - KNOWHERE_THROW_MSG("Invalid index str " + str); - } -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/IndexType.h b/core/src/index/knowhere/knowhere/index/vector_index/IndexType.h deleted file mode 100644 index 941b56cbd0..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/IndexType.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include - -namespace milvus { -namespace knowhere { - -/* used in 0.7.0 */ -enum class OldIndexType { - INVALID = 0, - FAISS_IDMAP = 1, - FAISS_IVFFLAT_CPU, - FAISS_IVFFLAT_GPU, - FAISS_IVFFLAT_MIX, // build on gpu and search on cpu - FAISS_IVFPQ_CPU, - FAISS_IVFPQ_GPU, - SPTAG_KDT_RNT_CPU, - FAISS_IVFSQ8_MIX, - FAISS_IVFSQ8_CPU, - FAISS_IVFSQ8_GPU, - FAISS_IVFSQ8_HYBRID, // only support build on gpu. - NSG_MIX, - FAISS_IVFPQ_MIX, - SPTAG_BKT_RNT_CPU, - HNSW, - ANNOY, - FAISS_BIN_IDMAP = 100, - FAISS_BIN_IVFLAT_CPU = 101, -}; - -using IndexType = std::string; - -/* used in 0.8.0 */ -namespace IndexEnum { -extern const char* INVALID; -extern const char* INDEX_FAISS_IDMAP; -extern const char* INDEX_FAISS_IVFFLAT; -extern const char* INDEX_FAISS_IVFPQ; -extern const char* INDEX_FAISS_IVFSQ8; -extern const char* INDEX_FAISS_IVFSQ8H; -extern const char* INDEX_FAISS_BIN_IDMAP; -extern const char* INDEX_FAISS_BIN_IVFFLAT; -extern const char* INDEX_NSG; -extern const char* INDEX_SPTAG_KDT_RNT; -extern const char* INDEX_SPTAG_BKT_RNT; -extern const char* INDEX_HNSW; -extern const char* INDEX_ANNOY; -} // namespace IndexEnum - -enum class IndexMode { MODE_CPU = 0, MODE_GPU = 1 }; - -extern std::string -OldIndexTypeToStr(const int32_t type); - -extern int32_t -StrToOldIndexType(const std::string& str); -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/VecIndex.h b/core/src/index/knowhere/knowhere/index/vector_index/VecIndex.h deleted file mode 100644 index b567e87ed9..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/VecIndex.h +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include -#include -#include - -#include "knowhere/common/Dataset.h" -#include "knowhere/common/Exception.h" -#include "knowhere/common/Typedef.h" -#include "knowhere/index/Index.h" -#include "knowhere/index/vector_index/IndexType.h" - -namespace milvus { -namespace knowhere { - -class VecIndex : public Index { - public: - virtual void - BuildAll(const DatasetPtr& dataset_ptr, const Config& config) { - Train(dataset_ptr, config); - Add(dataset_ptr, config); - } - - virtual void - Train(const DatasetPtr& dataset, const Config& config) = 0; - - virtual void - Add(const DatasetPtr& dataset, const Config& config) = 0; - - virtual void - AddWithoutIds(const DatasetPtr& dataset, const Config& config) = 0; - - virtual DatasetPtr - Query(const DatasetPtr& dataset, const Config& config) = 0; - -#if 0 - virtual DatasetPtr - QueryById(const DatasetPtr& dataset, const Config& config) { - return nullptr; - } -#endif - - // virtual DatasetPtr - // QueryByRange(const DatasetPtr&, const Config&) = 0; - // - // virtual MetricType - // metric_type() = 0; - - virtual int64_t - Dim() = 0; - - virtual int64_t - Count() = 0; - - virtual IndexType - index_type() const { - return index_type_; - } - - virtual IndexMode - index_mode() const { - return index_mode_; - } - -#if 0 - virtual DatasetPtr - GetVectorById(const DatasetPtr& dataset, const Config& config) { - return nullptr; - } -#endif - - faiss::ConcurrentBitsetPtr - GetBlacklist() { - return bitset_; - } - - void - SetBlacklist(faiss::ConcurrentBitsetPtr bitset_ptr) { - bitset_ = std::move(bitset_ptr); - } - - const std::vector& - GetUids() const { - return uids_; - } - - void - SetUids(std::vector& uids) { - uids_.clear(); - uids_.swap(uids); - } - - size_t - BlacklistSize() { - if (bitset_) { - return bitset_->size() * sizeof(uint8_t); - } else { - return 0; - } - } - - size_t - UidsSize() { - return uids_.size() * sizeof(IDType); - } - - virtual int64_t - IndexSize() { - if (index_size_ == -1) { - KNOWHERE_THROW_MSG("Index size not set"); - } - return index_size_; - } - - void - SetIndexSize(int64_t size) { - index_size_ = size; - } - - virtual void - UpdateIndexSize() { - } - - int64_t - Size() override { - return BlacklistSize() + UidsSize() + IndexSize(); - } - - protected: - IndexType index_type_ = ""; - IndexMode index_mode_ = IndexMode::MODE_CPU; - faiss::ConcurrentBitsetPtr bitset_ = nullptr; - std::vector uids_; - int64_t index_size_ = -1; -}; - -using VecIndexPtr = std::shared_ptr; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/VecIndexFactory.cpp b/core/src/index/knowhere/knowhere/index/vector_index/VecIndexFactory.cpp deleted file mode 100644 index 8e3119ecac..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/VecIndexFactory.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include "knowhere/index/vector_index/VecIndexFactory.h" - -#include "knowhere/common/Exception.h" -#include "knowhere/common/Log.h" -#include "knowhere/index/vector_index/IndexAnnoy.h" -#include "knowhere/index/vector_index/IndexBinaryIDMAP.h" -#include "knowhere/index/vector_index/IndexBinaryIVF.h" -#include "knowhere/index/vector_index/IndexHNSW.h" -#include "knowhere/index/vector_index/IndexIDMAP.h" -#include "knowhere/index/vector_index/IndexIVF.h" -#include "knowhere/index/vector_index/IndexIVFPQ.h" -#include "knowhere/index/vector_index/IndexIVFSQ.h" -#include "knowhere/index/vector_index/IndexNSG.h" -#include "knowhere/index/vector_index/IndexSPTAG.h" - -#ifdef MILVUS_GPU_VERSION -#include -#include "knowhere/index/vector_index/gpu/IndexGPUIDMAP.h" -#include "knowhere/index/vector_index/gpu/IndexGPUIVF.h" -#include "knowhere/index/vector_index/gpu/IndexGPUIVFPQ.h" -#include "knowhere/index/vector_index/gpu/IndexGPUIVFSQ.h" -#include "knowhere/index/vector_index/gpu/IndexIVFSQHybrid.h" -#include "knowhere/index/vector_index/helpers/Cloner.h" -#endif - -namespace milvus { -namespace knowhere { - -VecIndexPtr -VecIndexFactory::CreateVecIndex(const IndexType& type, const IndexMode mode) { - auto gpu_device = -1; // TODO: remove hardcode here, get from invoker - if (type == IndexEnum::INDEX_FAISS_IDMAP) { - return std::make_shared(); - } else if (type == IndexEnum::INDEX_FAISS_IVFFLAT) { -#ifdef MILVUS_GPU_VERSION - if (mode == IndexMode::MODE_GPU) { - return std::make_shared(gpu_device); - } -#endif - return std::make_shared(); - } else if (type == IndexEnum::INDEX_FAISS_IVFPQ) { -#ifdef MILVUS_GPU_VERSION - if (mode == IndexMode::MODE_GPU) { - return std::make_shared(gpu_device); - } -#endif - return std::make_shared(); - } else if (type == IndexEnum::INDEX_FAISS_IVFSQ8) { -#ifdef MILVUS_GPU_VERSION - if (mode == IndexMode::MODE_GPU) { - return std::make_shared(gpu_device); - } -#endif - return std::make_shared(); -#ifdef MILVUS_GPU_VERSION - } else if (type == IndexEnum::INDEX_FAISS_IVFSQ8H) { - return std::make_shared(gpu_device); -#endif - } else if (type == IndexEnum::INDEX_FAISS_BIN_IDMAP) { - return std::make_shared(); - } else if (type == IndexEnum::INDEX_FAISS_BIN_IVFFLAT) { - return std::make_shared(); - } else if (type == IndexEnum::INDEX_NSG) { - return std::make_shared(-1); - } else if (type == IndexEnum::INDEX_SPTAG_KDT_RNT) { - return std::make_shared("KDT"); - } else if (type == IndexEnum::INDEX_SPTAG_BKT_RNT) { - return std::make_shared("BKT"); - } else if (type == IndexEnum::INDEX_HNSW) { - return std::make_shared(); - } else if (type == IndexEnum::INDEX_ANNOY) { - return std::make_shared(); - } else { - return nullptr; - } -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/VecIndexFactory.h b/core/src/index/knowhere/knowhere/index/vector_index/VecIndexFactory.h deleted file mode 100644 index 7a09b54bff..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/VecIndexFactory.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include - -#include "knowhere/index/vector_index/IndexType.h" -#include "knowhere/index/vector_index/VecIndex.h" - -namespace milvus { -namespace knowhere { - -class VecIndexFactory { - private: - VecIndexFactory() = default; - VecIndexFactory(const VecIndexFactory&) = delete; - VecIndexFactory - operator=(const VecIndexFactory&) = delete; - - public: - static VecIndexFactory& - GetInstance() { - static VecIndexFactory inst; - return inst; - } - - knowhere::VecIndexPtr - CreateVecIndex(const IndexType& type, const IndexMode mode = IndexMode::MODE_CPU); -}; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/adapter/SptagAdapter.cpp b/core/src/index/knowhere/knowhere/index/vector_index/adapter/SptagAdapter.cpp deleted file mode 100644 index 030aa8665f..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/adapter/SptagAdapter.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "knowhere/index/vector_index/adapter/SptagAdapter.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" - -namespace milvus { -namespace knowhere { - -std::shared_ptr -ConvertToMetadataSet(const DatasetPtr& dataset_ptr) { - auto elems = dataset_ptr->Get(meta::ROWS); - auto p_data = dataset_ptr->Get(meta::IDS); - - auto p_offset = (int64_t*)malloc(sizeof(int64_t) * (elems + 1)); - for (auto i = 0; i <= elems; ++i) p_offset[i] = i * 8; - - std::shared_ptr metaset( - new SPTAG::MemMetadataSet(SPTAG::ByteArray((std::uint8_t*)p_data, elems * sizeof(int64_t), false), - SPTAG::ByteArray((std::uint8_t*)p_offset, elems * sizeof(int64_t), true), elems)); - - return metaset; -} - -std::shared_ptr -ConvertToVectorSet(const DatasetPtr& dataset_ptr) { - GETTENSOR(dataset_ptr); - size_t num_bytes = rows * dim * sizeof(float); - SPTAG::ByteArray byte_array((uint8_t*)p_data, num_bytes, false); - - auto vectorset = std::make_shared(byte_array, SPTAG::VectorValueType::Float, dim, rows); - return vectorset; -} - -std::vector -ConvertToQueryResult(const DatasetPtr& dataset_ptr, const Config& config) { - GETTENSOR(dataset_ptr); - - int64_t k = config[meta::TOPK].get(); - std::vector query_results(rows, SPTAG::QueryResult(nullptr, k, true)); - for (auto i = 0; i < rows; ++i) { - query_results[i].SetTarget((float*)p_data + i * dim); - } - - return query_results; -} - -DatasetPtr -ConvertToDataset(std::vector query_results) { - auto k = query_results[0].GetResultNum(); - auto elems = query_results.size() * k; - - size_t p_id_size = sizeof(int64_t) * elems; - size_t p_dist_size = sizeof(float) * elems; - auto p_id = (int64_t*)malloc(p_id_size); - auto p_dist = (float*)malloc(p_dist_size); - -#pragma omp parallel for - for (auto i = 0; i < query_results.size(); ++i) { - auto results = query_results[i].GetResults(); - auto num_result = query_results[i].GetResultNum(); - for (auto j = 0; j < num_result; ++j) { - // p_id[i * k + j] = results[j].VID; - p_id[i * k + j] = *(int64_t*)query_results[i].GetMetadata(j).Data(); - p_dist[i * k + j] = results[j].Dist; - } - } - - auto ret_ds = std::make_shared(); - ret_ds->Set(meta::IDS, p_id); - ret_ds->Set(meta::DISTANCE, p_dist); - return ret_ds; -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/adapter/SptagAdapter.h b/core/src/index/knowhere/knowhere/index/vector_index/adapter/SptagAdapter.h deleted file mode 100644 index a8ff4eaf58..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/adapter/SptagAdapter.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include - -#include "knowhere/common/Config.h" -#include "knowhere/common/Dataset.h" - -namespace milvus { -namespace knowhere { - -std::shared_ptr -ConvertToVectorSet(const DatasetPtr& dataset_ptr); - -std::shared_ptr -ConvertToMetadataSet(const DatasetPtr& dataset_ptr); - -std::vector -ConvertToQueryResult(const DatasetPtr& dataset_ptr, const Config& config); - -DatasetPtr -ConvertToDataset(std::vector query_results); - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/adapter/VectorAdapter.cpp b/core/src/index/knowhere/knowhere/index/vector_index/adapter/VectorAdapter.cpp deleted file mode 100644 index a9ba05298a..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/adapter/VectorAdapter.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include - -#include "knowhere/common/Dataset.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" -#include "knowhere/index/vector_index/helpers/IndexParameter.h" - -namespace milvus { -namespace knowhere { - -DatasetPtr -GenDatasetWithIds(const int64_t nb, const int64_t dim, const void* xb, const int64_t* ids) { - auto ret_ds = std::make_shared(); - ret_ds->Set(meta::ROWS, nb); - ret_ds->Set(meta::DIM, dim); - ret_ds->Set(meta::TENSOR, xb); - ret_ds->Set(meta::IDS, ids); - return ret_ds; -} - -DatasetPtr -GenDataset(const int64_t nb, const int64_t dim, const void* xb) { - auto ret_ds = std::make_shared(); - ret_ds->Set(meta::ROWS, nb); - ret_ds->Set(meta::DIM, dim); - ret_ds->Set(meta::TENSOR, xb); - return ret_ds; -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/adapter/VectorAdapter.h b/core/src/index/knowhere/knowhere/index/vector_index/adapter/VectorAdapter.h deleted file mode 100644 index 44086796b5..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/adapter/VectorAdapter.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include "knowhere/common/Dataset.h" -#include "knowhere/index/vector_index/helpers/IndexParameter.h" - -namespace milvus { -namespace knowhere { - -#define GETTENSOR(dataset_ptr) \ - int64_t dim = dataset_ptr->Get(meta::DIM); \ - int64_t rows = dataset_ptr->Get(meta::ROWS); \ - const void* p_data = dataset_ptr->Get(meta::TENSOR); - -#define GETTENSORWITHIDS(dataset_ptr) \ - int64_t dim = dataset_ptr->Get(meta::DIM); \ - int64_t rows = dataset_ptr->Get(meta::ROWS); \ - const void* p_data = dataset_ptr->Get(meta::TENSOR); \ - const int64_t* p_ids = dataset_ptr->Get(meta::IDS); - -extern DatasetPtr -GenDatasetWithIds(const int64_t nb, const int64_t dim, const void* xb, const int64_t* ids); - -extern DatasetPtr -GenDataset(const int64_t nb, const int64_t dim, const void* xb); - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/gpu/GPUIndex.h b/core/src/index/knowhere/knowhere/index/vector_index/gpu/GPUIndex.h deleted file mode 100644 index 1e079efd7c..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/gpu/GPUIndex.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include "knowhere/index/vector_index/VecIndex.h" -#include "knowhere/index/vector_index/helpers/FaissGpuResourceMgr.h" - -namespace milvus { -namespace knowhere { - -class GPUIndex { - public: - explicit GPUIndex(const int& device_id) : gpu_id_(device_id) { - } - - GPUIndex(const int& device_id, const ResPtr& resource) : gpu_id_(device_id), res_(resource) { - } - - virtual VecIndexPtr - CopyGpuToCpu(const Config&) = 0; - - virtual VecIndexPtr - CopyGpuToGpu(const int64_t, const Config&) = 0; - - void - SetGpuDevice(const int& gpu_id) { - gpu_id_ = gpu_id; - } - - const int64_t - GetGpuDevice() { - return gpu_id_; - } - - protected: - int64_t gpu_id_; - ResWPtr res_; -}; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIDMAP.cpp b/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIDMAP.cpp deleted file mode 100644 index f91afa19a6..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIDMAP.cpp +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include -#include -#include -#include -#ifdef MILVUS_GPU_VERSION -#include -#endif -#include -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/IndexIDMAP.h" -#include "knowhere/index/vector_index/IndexType.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" -#include "knowhere/index/vector_index/gpu/IndexGPUIDMAP.h" -#include "knowhere/index/vector_index/helpers/FaissIO.h" - -namespace milvus { -namespace knowhere { - -VecIndexPtr -GPUIDMAP::CopyGpuToCpu(const Config& config) { - std::lock_guard lk(mutex_); - - faiss::Index* device_index = index_.get(); - faiss::Index* host_index = faiss::gpu::index_gpu_to_cpu(device_index); - - std::shared_ptr new_index; - new_index.reset(host_index); - return std::make_shared(new_index); -} - -BinarySet -GPUIDMAP::SerializeImpl(const IndexType& type) { - try { - fiu_do_on("GPUIDMP.SerializeImpl.throw_exception", throw std::exception()); - MemoryIOWriter writer; - { - faiss::Index* index = index_.get(); - faiss::Index* host_index = faiss::gpu::index_gpu_to_cpu(index); - - faiss::write_index(host_index, &writer); - delete host_index; - } - std::shared_ptr data(writer.data_); - - BinarySet res_set; - res_set.Append("IVF", data, writer.rp); - - return res_set; - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} - -void -GPUIDMAP::LoadImpl(const BinarySet& index_binary, const IndexType& type) { - auto binary = index_binary.GetByName("IVF"); - MemoryIOReader reader; - { - reader.total = binary->size; - reader.data_ = binary->data.get(); - - faiss::Index* index = faiss::read_index(&reader); - - if (auto res = FaissGpuResourceMgr::GetInstance().GetRes(gpu_id_)) { - ResScope rs(res, gpu_id_, false); - auto device_index = faiss::gpu::index_cpu_to_gpu(res->faiss_res.get(), gpu_id_, index); - index_.reset(device_index); - res_ = res; - } else { - KNOWHERE_THROW_MSG("Load error, can't get gpu resource"); - } - - delete index; - } -} - -VecIndexPtr -GPUIDMAP::CopyGpuToGpu(const int64_t device_id, const Config& config) { - auto cpu_index = CopyGpuToCpu(config); - return std::static_pointer_cast(cpu_index)->CopyCpuToGpu(device_id, config); -} - -const float* -GPUIDMAP::GetRawVectors() { - KNOWHERE_THROW_MSG("Not support"); -} - -const int64_t* -GPUIDMAP::GetRawIds() { - KNOWHERE_THROW_MSG("Not support"); -} - -void -GPUIDMAP::QueryImpl(int64_t n, const float* data, int64_t k, float* distances, int64_t* labels, const Config& config) { - ResScope rs(res_, gpu_id_); - - auto flat_index = dynamic_cast(index_.get())->index; - auto default_type = flat_index->metric_type; - if (config.contains(Metric::TYPE)) - flat_index->metric_type = GetMetricType(config[Metric::TYPE].get()); - index_->search(n, (float*)data, k, distances, labels, bitset_); - flat_index->metric_type = default_type; -} - -void -GPUIDMAP::GenGraph(const float* data, const int64_t k, GraphType& graph, const Config& config) { - int64_t K = k + 1; - auto ntotal = Count(); - - size_t dim = config[meta::DIM]; - auto batch_size = 1000; - auto tail_batch_size = ntotal % batch_size; - auto batch_search_count = ntotal / batch_size; - auto total_search_count = tail_batch_size == 0 ? batch_search_count : batch_search_count + 1; - - std::vector res_dis(K * batch_size); - graph.resize(ntotal); - Graph res_vec(total_search_count); - for (int i = 0; i < total_search_count; ++i) { - auto b_size = (i == (total_search_count - 1)) && tail_batch_size != 0 ? tail_batch_size : batch_size; - - auto& res = res_vec[i]; - res.resize(K * b_size); - - auto xq = data + batch_size * dim * i; - QueryImpl(b_size, (float*)xq, K, res_dis.data(), res.data(), config); - - for (int j = 0; j < b_size; ++j) { - auto& node = graph[batch_size * i + j]; - node.resize(k); - auto start_pos = j * K + 1; - for (int m = 0, cursor = start_pos; m < k && cursor < start_pos + k; ++m, ++cursor) { - node[m] = res[cursor]; - } - } - } -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIDMAP.h b/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIDMAP.h deleted file mode 100644 index b035cb1407..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIDMAP.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include -#include - -#include "knowhere/index/vector_index/IndexIDMAP.h" -#include "knowhere/index/vector_index/gpu/GPUIndex.h" - -namespace milvus { -namespace knowhere { - -using Graph = std::vector>; - -class GPUIDMAP : public IDMAP, public GPUIndex { - public: - explicit GPUIDMAP(std::shared_ptr index, const int64_t device_id, ResPtr& res) - : IDMAP(std::move(index)), GPUIndex(device_id, res) { - index_mode_ = IndexMode::MODE_GPU; - } - - VecIndexPtr - CopyGpuToCpu(const Config&) override; - - VecIndexPtr - CopyGpuToGpu(const int64_t, const Config&) override; - - const float* - GetRawVectors() override; - - const int64_t* - GetRawIds() override; - - void - GenGraph(const float*, const int64_t, GraphType&, const Config&); - - protected: - BinarySet - SerializeImpl(const IndexType&) override; - - void - LoadImpl(const BinarySet&, const IndexType&) override; - - void - QueryImpl(int64_t, const float*, int64_t, float*, int64_t*, const Config&) override; -}; - -using GPUIDMAPPtr = std::shared_ptr; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVF.cpp b/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVF.cpp deleted file mode 100644 index 3552a19045..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVF.cpp +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include - -#include -#include -#include -#include -#include -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" -#include "knowhere/index/vector_index/gpu/IndexGPUIVF.h" -#include "knowhere/index/vector_index/helpers/Cloner.h" -#include "knowhere/index/vector_index/helpers/FaissIO.h" -#include "knowhere/index/vector_index/helpers/IndexParameter.h" - -namespace milvus { -namespace knowhere { - -void -GPUIVF::Train(const DatasetPtr& dataset_ptr, const Config& config) { - GETTENSOR(dataset_ptr) - gpu_id_ = config[knowhere::meta::DEVICEID]; - - auto gpu_res = FaissGpuResourceMgr::GetInstance().GetRes(gpu_id_); - if (gpu_res != nullptr) { - ResScope rs(gpu_res, gpu_id_, true); - faiss::gpu::GpuIndexIVFFlatConfig idx_config; - idx_config.device = gpu_id_; - int32_t nlist = config[IndexParams::nlist]; - faiss::MetricType metric_type = GetMetricType(config[Metric::TYPE].get()); - auto device_index = - new faiss::gpu::GpuIndexIVFFlat(gpu_res->faiss_res.get(), dim, nlist, metric_type, idx_config); - device_index->train(rows, (float*)p_data); - - index_.reset(device_index); - res_ = gpu_res; - } else { - KNOWHERE_THROW_MSG("Build IVF can't get gpu resource"); - } -} - -void -GPUIVF::Add(const DatasetPtr& dataset_ptr, const Config& config) { - if (auto spt = res_.lock()) { - ResScope rs(res_, gpu_id_); - IVF::Add(dataset_ptr, config); - } else { - KNOWHERE_THROW_MSG("Add IVF can't get gpu resource"); - } -} - -VecIndexPtr -GPUIVF::CopyGpuToCpu(const Config& config) { - std::lock_guard lk(mutex_); - - if (auto device_idx = std::dynamic_pointer_cast(index_)) { - faiss::Index* device_index = index_.get(); - faiss::Index* host_index = faiss::gpu::index_gpu_to_cpu(device_index); - - std::shared_ptr new_index; - new_index.reset(host_index); - return std::make_shared(new_index); - } else { - return std::make_shared(index_); - } -} - -VecIndexPtr -GPUIVF::CopyGpuToGpu(const int64_t device_id, const Config& config) { - auto host_index = CopyGpuToCpu(config); - return std::static_pointer_cast(host_index)->CopyCpuToGpu(device_id, config); -} - -BinarySet -GPUIVF::SerializeImpl(const IndexType& type) { - if (!index_ || !index_->is_trained) { - KNOWHERE_THROW_MSG("index not initialize or trained"); - } - - try { - fiu_do_on("GPUIVF.SerializeImpl.throw_exception", throw std::exception()); - MemoryIOWriter writer; - { - faiss::Index* index = index_.get(); - faiss::Index* host_index = faiss::gpu::index_gpu_to_cpu(index); - - faiss::write_index(host_index, &writer); - delete host_index; - } - std::shared_ptr data(writer.data_); - - BinarySet res_set; - res_set.Append("IVF", data, writer.rp); - - return res_set; - } catch (std::exception& e) { - KNOWHERE_THROW_MSG(e.what()); - } -} - -void -GPUIVF::LoadImpl(const BinarySet& binary_set, const IndexType& type) { - auto binary = binary_set.GetByName("IVF"); - MemoryIOReader reader; - { - reader.total = binary->size; - reader.data_ = binary->data.get(); - - faiss::Index* index = faiss::read_index(&reader); - - if (auto temp_res = FaissGpuResourceMgr::GetInstance().GetRes(gpu_id_)) { - ResScope rs(temp_res, gpu_id_, false); - auto device_index = faiss::gpu::index_cpu_to_gpu(temp_res->faiss_res.get(), gpu_id_, index); - index_.reset(device_index); - res_ = temp_res; - } else { - KNOWHERE_THROW_MSG("Load error, can't get gpu resource"); - } - - delete index; - } -} - -void -GPUIVF::QueryImpl(int64_t n, const float* data, int64_t k, float* distances, int64_t* labels, const Config& config) { - std::lock_guard lk(mutex_); - - auto device_index = std::dynamic_pointer_cast(index_); - fiu_do_on("GPUIVF.search_impl.invald_index", device_index = nullptr); - if (device_index) { - device_index->nprobe = config[IndexParams::nprobe]; - ResScope rs(res_, gpu_id_); - - // if query size > 2048 we search by blocks to avoid malloc issue - const int64_t block_size = 2048; - int64_t dim = device_index->d; - for (int64_t i = 0; i < n; i += block_size) { - int64_t search_size = (n - i > block_size) ? block_size : (n - i); - device_index->search(search_size, (float*)data + i * dim, k, distances + i * k, labels + i * k, bitset_); - } - } else { - KNOWHERE_THROW_MSG("Not a GpuIndexIVF type."); - } -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVF.h b/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVF.h deleted file mode 100644 index 49d1b3eef0..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVF.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include - -#include "knowhere/index/vector_index/IndexIVF.h" -#include "knowhere/index/vector_index/gpu/GPUIndex.h" - -namespace milvus { -namespace knowhere { - -class GPUIVF : public IVF, public GPUIndex { - public: - explicit GPUIVF(const int& device_id) : IVF(), GPUIndex(device_id) { - index_mode_ = IndexMode::MODE_GPU; - } - - explicit GPUIVF(std::shared_ptr index, const int64_t device_id, ResPtr& res) - : IVF(std::move(index)), GPUIndex(device_id, res) { - index_mode_ = IndexMode::MODE_GPU; - } - - void - Train(const DatasetPtr&, const Config&) override; - - void - Add(const DatasetPtr&, const Config&) override; - - VecIndexPtr - CopyGpuToCpu(const Config&) override; - - VecIndexPtr - CopyGpuToGpu(const int64_t, const Config&) override; - - protected: - BinarySet - SerializeImpl(const IndexType&) override; - - void - LoadImpl(const BinarySet&, const IndexType&) override; - - void - QueryImpl(int64_t, const float*, int64_t, float*, int64_t*, const Config&) override; -}; - -using GPUIVFPtr = std::shared_ptr; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFPQ.cpp b/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFPQ.cpp deleted file mode 100644 index d9465761d9..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFPQ.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include - -#include -#include -#include -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/IndexIVFPQ.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" -#include "knowhere/index/vector_index/gpu/IndexGPUIVFPQ.h" -#include "knowhere/index/vector_index/helpers/IndexParameter.h" - -namespace milvus { -namespace knowhere { - -void -GPUIVFPQ::Train(const DatasetPtr& dataset_ptr, const Config& config) { - GETTENSOR(dataset_ptr) - gpu_id_ = config[knowhere::meta::DEVICEID]; - - auto gpu_res = FaissGpuResourceMgr::GetInstance().GetRes(gpu_id_); - if (gpu_res != nullptr) { - ResScope rs(gpu_res, gpu_id_, true); - auto device_index = - new faiss::gpu::GpuIndexIVFPQ(gpu_res->faiss_res.get(), dim, config[IndexParams::nlist].get(), - config[IndexParams::m], config[IndexParams::nbits], - GetMetricType(config[Metric::TYPE].get())); // IP not support - device_index->train(rows, (float*)p_data); - - index_.reset(device_index); - res_ = gpu_res; - } else { - KNOWHERE_THROW_MSG("Build IVFPQ can't get gpu resource"); - } -} - -VecIndexPtr -GPUIVFPQ::CopyGpuToCpu(const Config& config) { - std::lock_guard lk(mutex_); - - faiss::Index* device_index = index_.get(); - faiss::Index* host_index = faiss::gpu::index_gpu_to_cpu(device_index); - - std::shared_ptr new_index; - new_index.reset(host_index); - return std::make_shared(new_index); -} - -std::shared_ptr -GPUIVFPQ::GenParams(const Config& config) { - auto params = std::make_shared(); - params->nprobe = config[IndexParams::nprobe]; - // params->scan_table_threshold = config["scan_table_threhold"] - // params->polysemous_ht = config["polysemous_ht"] - // params->max_codes = config["max_codes"] - - return params; -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFPQ.h b/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFPQ.h deleted file mode 100644 index df83efbe79..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFPQ.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include - -#include "knowhere/index/vector_index/gpu/IndexGPUIVF.h" - -namespace milvus { -namespace knowhere { - -class GPUIVFPQ : public GPUIVF { - public: - explicit GPUIVFPQ(const int& device_id) : GPUIVF(device_id) { - index_type_ = IndexEnum::INDEX_FAISS_IVFPQ; - } - - GPUIVFPQ(std::shared_ptr index, const int64_t device_id, ResPtr& res) - : GPUIVF(std::move(index), device_id, res) { - index_type_ = IndexEnum::INDEX_FAISS_IVFPQ; - } - - void - Train(const DatasetPtr&, const Config&) override; - - VecIndexPtr - CopyGpuToCpu(const Config&) override; - - protected: - std::shared_ptr - GenParams(const Config& config) override; -}; - -using GPUIVFPQPtr = std::shared_ptr; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFSQ.cpp b/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFSQ.cpp deleted file mode 100644 index 36e5dc13bd..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFSQ.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include -#include - -#include -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/IndexIVFSQ.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" -#include "knowhere/index/vector_index/gpu/IndexGPUIVFSQ.h" -#include "knowhere/index/vector_index/helpers/IndexParameter.h" - -namespace milvus { -namespace knowhere { - -void -GPUIVFSQ::Train(const DatasetPtr& dataset_ptr, const Config& config) { - GETTENSOR(dataset_ptr) - gpu_id_ = config[knowhere::meta::DEVICEID]; - - std::stringstream index_type; - index_type << "IVF" << config[IndexParams::nlist] << "," - << "SQ" << config[IndexParams::nbits]; - faiss::MetricType metric_type = GetMetricType(config[Metric::TYPE].get()); - auto build_index = faiss::index_factory(dim, index_type.str().c_str(), metric_type); - - auto gpu_res = FaissGpuResourceMgr::GetInstance().GetRes(gpu_id_); - if (gpu_res != nullptr) { - ResScope rs(gpu_res, gpu_id_, true); - auto device_index = faiss::gpu::index_cpu_to_gpu(gpu_res->faiss_res.get(), gpu_id_, build_index); - device_index->train(rows, (float*)p_data); - - index_.reset(device_index); - res_ = gpu_res; - } else { - KNOWHERE_THROW_MSG("Build IVFSQ can't get gpu resource"); - } -} - -VecIndexPtr -GPUIVFSQ::CopyGpuToCpu(const Config& config) { - std::lock_guard lk(mutex_); - - faiss::Index* device_index = index_.get(); - faiss::Index* host_index = faiss::gpu::index_gpu_to_cpu(device_index); - - std::shared_ptr new_index; - new_index.reset(host_index); - return std::make_shared(new_index); -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFSQ.h b/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFSQ.h deleted file mode 100644 index df886046a6..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFSQ.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include - -#include "knowhere/index/vector_index/gpu/IndexGPUIVF.h" - -namespace milvus { -namespace knowhere { - -class GPUIVFSQ : public GPUIVF { - public: - explicit GPUIVFSQ(const int& device_id) : GPUIVF(device_id) { - index_type_ = IndexEnum::INDEX_FAISS_IVFSQ8; - } - - explicit GPUIVFSQ(std::shared_ptr index, const int64_t device_id, ResPtr& res) - : GPUIVF(std::move(index), device_id, res) { - index_type_ = IndexEnum::INDEX_FAISS_IVFSQ8; - } - - void - Train(const DatasetPtr&, const Config&) override; - - VecIndexPtr - CopyGpuToCpu(const Config&) override; -}; - -using GPUIVFSQPtr = std::shared_ptr; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexIVFSQHybrid.cpp b/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexIVFSQHybrid.cpp deleted file mode 100644 index e95ae9f8d7..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexIVFSQHybrid.cpp +++ /dev/null @@ -1,290 +0,0 @@ -// -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include -#include -#include -#include -#include -#include -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" -#include "knowhere/index/vector_index/gpu/IndexIVFSQHybrid.h" -#include "knowhere/index/vector_index/helpers/FaissIO.h" -#include "knowhere/index/vector_index/helpers/IndexParameter.h" - -namespace milvus { -namespace knowhere { - -#ifdef MILVUS_GPU_VERSION - -void -IVFSQHybrid::Train(const DatasetPtr& dataset_ptr, const Config& config) { - GETTENSOR(dataset_ptr) - gpu_id_ = config[knowhere::meta::DEVICEID]; - - std::stringstream index_type; - index_type << "IVF" << config[IndexParams::nlist] << "," - << "SQ8Hybrid"; - auto build_index = - faiss::index_factory(dim, index_type.str().c_str(), GetMetricType(config[Metric::TYPE].get())); - - auto gpu_res = FaissGpuResourceMgr::GetInstance().GetRes(gpu_id_); - if (gpu_res != nullptr) { - ResScope rs(gpu_res, gpu_id_, true); - auto device_index = faiss::gpu::index_cpu_to_gpu(gpu_res->faiss_res.get(), gpu_id_, build_index); - device_index->train(rows, (float*)p_data); - - index_.reset(device_index); - res_ = gpu_res; - gpu_mode_ = 2; - } else { - KNOWHERE_THROW_MSG("Build IVFSQHybrid can't get gpu resource"); - } - - delete build_index; -} - -VecIndexPtr -IVFSQHybrid::CopyGpuToCpu(const Config& config) { - if (gpu_mode_ == 0) { - return std::make_shared(index_); - } - std::lock_guard lk(mutex_); - - faiss::Index* device_index = index_.get(); - faiss::Index* host_index = faiss::gpu::index_gpu_to_cpu(device_index); - - if (auto* ivf_index = dynamic_cast(host_index)) { - if (ivf_index != nullptr) { - ivf_index->to_readonly(); - } - ivf_index->backup_quantizer(); - } - - std::shared_ptr new_index; - new_index.reset(host_index); - return std::make_shared(new_index); -} - -VecIndexPtr -IVFSQHybrid::CopyCpuToGpu(const int64_t device_id, const Config& config) { - if (auto res = FaissGpuResourceMgr::GetInstance().GetRes(device_id)) { - ResScope rs(res, device_id, false); - faiss::gpu::GpuClonerOptions option; - option.allInGpu = true; - - auto idx = dynamic_cast(index_.get()); - idx->restore_quantizer(); - auto gpu_index = faiss::gpu::index_cpu_to_gpu(res->faiss_res.get(), device_id, index_.get(), &option); - std::shared_ptr device_index = std::shared_ptr(gpu_index); - auto new_idx = std::make_shared(device_index, device_id, res); - return new_idx; - } else { - KNOWHERE_THROW_MSG("CopyCpuToGpu Error, can't get gpu: " + std::to_string(gpu_id_) + "resource"); - } -} - -std::pair -IVFSQHybrid::CopyCpuToGpuWithQuantizer(const int64_t device_id, const Config& config) { - if (auto res = FaissGpuResourceMgr::GetInstance().GetRes(device_id)) { - ResScope rs(res, device_id, false); - faiss::gpu::GpuClonerOptions option; - option.allInGpu = true; - - faiss::IndexComposition index_composition; - index_composition.index = index_.get(); - index_composition.quantizer = nullptr; - index_composition.mode = 0; // copy all - - auto gpu_index = faiss::gpu::index_cpu_to_gpu(res->faiss_res.get(), device_id, &index_composition, &option); - - std::shared_ptr device_index; - device_index.reset(gpu_index); - auto new_idx = std::make_shared(device_index, device_id, res); - - auto q = std::make_shared(); - q->quantizer = index_composition.quantizer; - q->size = index_composition.quantizer->d * index_composition.quantizer->getNumVecs() * sizeof(float); - q->gpu_id = device_id; - return std::make_pair(new_idx, q); - } else { - KNOWHERE_THROW_MSG("CopyCpuToGpu Error, can't get gpu: " + std::to_string(gpu_id_) + "resource"); - } -} - -VecIndexPtr -IVFSQHybrid::LoadData(const knowhere::QuantizerPtr& quantizer_ptr, const Config& config) { - int64_t gpu_id = config[knowhere::meta::DEVICEID]; - - if (auto res = FaissGpuResourceMgr::GetInstance().GetRes(gpu_id)) { - ResScope rs(res, gpu_id, false); - faiss::gpu::GpuClonerOptions option; - option.allInGpu = true; - - auto ivf_quantizer = std::dynamic_pointer_cast(quantizer_ptr); - if (ivf_quantizer == nullptr) - KNOWHERE_THROW_MSG("quantizer type not faissivfquantizer"); - - auto index_composition = new faiss::IndexComposition; - index_composition->index = index_.get(); - index_composition->quantizer = ivf_quantizer->quantizer; - index_composition->mode = 2; // only 2 - - auto gpu_index = faiss::gpu::index_cpu_to_gpu(res->faiss_res.get(), gpu_id, index_composition, &option); - std::shared_ptr new_idx; - new_idx.reset(gpu_index); - auto sq_idx = std::make_shared(new_idx, gpu_id, res); - return sq_idx; - } else { - KNOWHERE_THROW_MSG("CopyCpuToGpu Error, can't get gpu: " + std::to_string(gpu_id) + "resource"); - } -} - -QuantizerPtr -IVFSQHybrid::LoadQuantizer(const Config& config) { - auto gpu_id = config[knowhere::meta::DEVICEID].get(); - - if (auto res = FaissGpuResourceMgr::GetInstance().GetRes(gpu_id)) { - ResScope rs(res, gpu_id, false); - faiss::gpu::GpuClonerOptions option; - option.allInGpu = true; - - auto index_composition = new faiss::IndexComposition; - index_composition->index = index_.get(); - index_composition->quantizer = nullptr; - index_composition->mode = 1; // only 1 - - auto gpu_index = faiss::gpu::index_cpu_to_gpu(res->faiss_res.get(), gpu_id, index_composition, &option); - delete gpu_index; - - auto q = std::make_shared(); - - auto& q_ptr = index_composition->quantizer; - q->size = q_ptr->d * q_ptr->getNumVecs() * sizeof(float); - q->quantizer = q_ptr; - q->gpu_id = gpu_id; - res_ = res; - gpu_mode_ = 1; - return q; - } else { - KNOWHERE_THROW_MSG("CopyCpuToGpu Error, can't get gpu: " + std::to_string(gpu_id) + "resource"); - } -} - -void -IVFSQHybrid::SetQuantizer(const QuantizerPtr& quantizer_ptr) { - auto ivf_quantizer = std::dynamic_pointer_cast(quantizer_ptr); - if (ivf_quantizer == nullptr) { - KNOWHERE_THROW_MSG("Quantizer type error"); - } - - faiss::IndexIVF* ivf_index = dynamic_cast(index_.get()); - - faiss::gpu::GpuIndexFlat* is_gpu_flat_index = dynamic_cast(ivf_index->quantizer); - if (is_gpu_flat_index == nullptr) { - // delete ivf_index->quantizer; - ivf_index->quantizer = ivf_quantizer->quantizer; - } - quantizer_gpu_id_ = ivf_quantizer->gpu_id; - gpu_mode_ = 1; -} - -void -IVFSQHybrid::UnsetQuantizer() { - auto* ivf_index = dynamic_cast(index_.get()); - if (ivf_index == nullptr) { - KNOWHERE_THROW_MSG("Index type error"); - } - - ivf_index->quantizer = nullptr; - quantizer_gpu_id_ = -1; -} - -BinarySet -IVFSQHybrid::SerializeImpl(const IndexType& type) { - if (!index_ || !index_->is_trained) { - KNOWHERE_THROW_MSG("index not initialize or trained"); - } - - fiu_do_on("IVFSQHybrid.SerializeImpl.zero_gpu_mode", gpu_mode_ = 0); - if (gpu_mode_ == 0) { - MemoryIOWriter writer; - faiss::write_index(index_.get(), &writer); - - std::shared_ptr data(writer.data_); - - BinarySet res_set; - res_set.Append("IVF", data, writer.rp); - - return res_set; - } else if (gpu_mode_ == 2) { - return GPUIVF::SerializeImpl(type); - } else { - KNOWHERE_THROW_MSG("Can't serialize IVFSQ8Hybrid"); - } -} - -void -IVFSQHybrid::LoadImpl(const BinarySet& binary_set, const IndexType& type) { - FaissBaseIndex::LoadImpl(binary_set, index_type_); // load on cpu - auto* ivf_index = dynamic_cast(index_.get()); - ivf_index->backup_quantizer(); - gpu_mode_ = 0; -} - -void -IVFSQHybrid::QueryImpl(int64_t n, const float* data, int64_t k, float* distances, int64_t* labels, - const Config& config) { - if (gpu_mode_ == 2) { - GPUIVF::QueryImpl(n, data, k, distances, labels, config); - // index_->search(n, (float*)data, k, distances, labels); - } else if (gpu_mode_ == 1) { // hybrid - if (auto res = FaissGpuResourceMgr::GetInstance().GetRes(quantizer_gpu_id_)) { - ResScope rs(res, quantizer_gpu_id_, true); - IVF::QueryImpl(n, data, k, distances, labels, config); - } else { - KNOWHERE_THROW_MSG("Hybrid Search Error, can't get gpu: " + std::to_string(quantizer_gpu_id_) + "resource"); - } - } else if (gpu_mode_ == 0) { - IVF::QueryImpl(n, data, k, distances, labels, config); - } -} - -void -IVFSQHybrid::UpdateIndexSize() { - if (!index_) { - KNOWHERE_THROW_MSG("index not initialize"); - } - auto ivfsqh_index = dynamic_cast(index_.get()); - auto nb = ivfsqh_index->invlists->compute_ntotal(); - auto code_size = ivfsqh_index->code_size; - auto nlist = ivfsqh_index->nlist; - auto d = ivfsqh_index->d; - // ivf codes, ivf ids, sq trained vectors and quantizer - index_size_ = nb * code_size + nb * sizeof(int64_t) + 2 * d * sizeof(float) + nlist * d * sizeof(float); -} - -FaissIVFQuantizer::~FaissIVFQuantizer() { - if (quantizer != nullptr) { - delete quantizer; - quantizer = nullptr; - } - // else do nothing -} - -#endif - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexIVFSQHybrid.h b/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexIVFSQHybrid.h deleted file mode 100644 index 4aeb7f6867..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/gpu/IndexIVFSQHybrid.h +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include - -#include -#include - -#include "knowhere/index/vector_index/gpu/IndexGPUIVFSQ.h" -#include "knowhere/index/vector_index/gpu/Quantizer.h" - -namespace milvus { -namespace knowhere { - -#ifdef MILVUS_GPU_VERSION - -struct FaissIVFQuantizer : public Quantizer { - faiss::gpu::GpuIndexFlat* quantizer = nullptr; - int64_t gpu_id; - - ~FaissIVFQuantizer() override; -}; -using FaissIVFQuantizerPtr = std::shared_ptr; - -class IVFSQHybrid : public GPUIVFSQ { - public: - explicit IVFSQHybrid(const int& device_id) : GPUIVFSQ(device_id) { - index_type_ = IndexEnum::INDEX_FAISS_IVFSQ8H; - gpu_mode_ = 0; - } - - explicit IVFSQHybrid(std::shared_ptr index) : GPUIVFSQ(-1) { - index_type_ = IndexEnum::INDEX_FAISS_IVFSQ8H; - index_ = index; - gpu_mode_ = 0; - } - - explicit IVFSQHybrid(std::shared_ptr index, const int64_t device_id, ResPtr& resource) - : GPUIVFSQ(index, device_id, resource) { - index_type_ = IndexEnum::INDEX_FAISS_IVFSQ8H; - gpu_mode_ = 2; - } - - public: - void - Train(const DatasetPtr&, const Config&) override; - - VecIndexPtr - CopyGpuToCpu(const Config&) override; - - VecIndexPtr - CopyCpuToGpu(const int64_t, const Config&) override; - - std::pair - CopyCpuToGpuWithQuantizer(const int64_t, const Config&); - - VecIndexPtr - LoadData(const knowhere::QuantizerPtr&, const Config&); - - QuantizerPtr - LoadQuantizer(const Config& conf); - - void - SetQuantizer(const QuantizerPtr& q); - - void - UnsetQuantizer(); - - void - UpdateIndexSize() override; - - protected: - BinarySet - SerializeImpl(const IndexType&) override; - - void - LoadImpl(const BinarySet&, const IndexType&) override; - - void - QueryImpl(int64_t, const float*, int64_t, float*, int64_t*, const Config&) override; - - protected: - int64_t gpu_mode_ = 0; // 0,1,2 - int64_t quantizer_gpu_id_ = -1; -}; - -using IVFSQHybridPtr = std::shared_ptr; - -#endif - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/gpu/Quantizer.h b/core/src/index/knowhere/knowhere/index/vector_index/gpu/Quantizer.h deleted file mode 100644 index 89f1e03d79..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/gpu/Quantizer.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include "knowhere/common/Config.h" - -namespace milvus { -namespace knowhere { - -struct Quantizer { - virtual ~Quantizer() = default; - - int64_t size = -1; -}; -using QuantizerPtr = std::shared_ptr; - -// struct QuantizerCfg : Cfg { -// int64_t mode = -1; // 0: all data, 1: copy quantizer, 2: copy data -// }; -// using QuantizerConfig = std::shared_ptr; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/helpers/BuilderSuspend.h b/core/src/index/knowhere/knowhere/index/vector_index/helpers/BuilderSuspend.h deleted file mode 100644 index d77d9a9dea..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/helpers/BuilderSuspend.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include "faiss/BuilderSuspend.h" - -namespace milvus { -namespace knowhere { - -inline void -BuilderSuspend() { - faiss::BuilderSuspend::suspend(); -} - -inline void -BuildResume() { - faiss::BuilderSuspend::resume(); -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/helpers/Cloner.cpp b/core/src/index/knowhere/knowhere/index/vector_index/helpers/Cloner.cpp deleted file mode 100644 index 2ba189c513..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/helpers/Cloner.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#ifdef MILVUS_GPU_VERSION -#include "knowhere/index/vector_index/helpers/Cloner.h" -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/IndexIDMAP.h" -#include "knowhere/index/vector_index/IndexIVF.h" -#include "knowhere/index/vector_index/IndexIVFPQ.h" -#include "knowhere/index/vector_index/IndexIVFSQ.h" -#include "knowhere/index/vector_index/gpu/GPUIndex.h" -#include "knowhere/index/vector_index/gpu/IndexGPUIVF.h" -#include "knowhere/index/vector_index/gpu/IndexIVFSQHybrid.h" - -namespace milvus { -namespace knowhere { -namespace cloner { - -void -CopyIndexData(const VecIndexPtr& dst_index, const VecIndexPtr& src_index) { - /* do real copy */ - auto uids = src_index->GetUids(); - dst_index->SetUids(uids); - - dst_index->SetBlacklist(src_index->GetBlacklist()); - dst_index->SetIndexSize(src_index->IndexSize()); -} - -VecIndexPtr -CopyGpuToCpu(const VecIndexPtr& index, const Config& config) { - if (auto device_index = std::dynamic_pointer_cast(index)) { - VecIndexPtr result = device_index->CopyGpuToCpu(config); - CopyIndexData(result, index); - return result; - } else { - KNOWHERE_THROW_MSG("index type is not gpuindex"); - } -} - -VecIndexPtr -CopyCpuToGpu(const VecIndexPtr& index, const int64_t device_id, const Config& config) { - VecIndexPtr result; - if (auto device_index = std::dynamic_pointer_cast(index)) { - result = device_index->CopyCpuToGpu(device_id, config); - } else if (auto device_index = std::dynamic_pointer_cast(index)) { - result = device_index->CopyGpuToGpu(device_id, config); - } else if (auto cpu_index = std::dynamic_pointer_cast(index)) { - result = cpu_index->CopyCpuToGpu(device_id, config); - } else if (auto cpu_index = std::dynamic_pointer_cast(index)) { - result = cpu_index->CopyCpuToGpu(device_id, config); - } else if (auto cpu_index = std::dynamic_pointer_cast(index)) { - result = cpu_index->CopyCpuToGpu(device_id, config); - } else if (auto cpu_index = std::dynamic_pointer_cast(index)) { - result = cpu_index->CopyCpuToGpu(device_id, config); - } else { - KNOWHERE_THROW_MSG("this index type not support transfer to gpu"); - } - - CopyIndexData(result, index); - return result; -} - -} // namespace cloner -} // namespace knowhere -} // namespace milvus -#endif diff --git a/core/src/index/knowhere/knowhere/index/vector_index/helpers/Cloner.h b/core/src/index/knowhere/knowhere/index/vector_index/helpers/Cloner.h deleted file mode 100644 index 8252d27d4a..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/helpers/Cloner.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include "knowhere/index/vector_index/VecIndex.h" - -namespace milvus { -namespace knowhere { -namespace cloner { - -extern VecIndexPtr -CopyCpuToGpu(const VecIndexPtr& index, const int64_t device_id, const Config& config); - -extern VecIndexPtr -CopyGpuToCpu(const VecIndexPtr& index, const Config& config); - -} // namespace cloner -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/helpers/FaissGpuResourceMgr.cpp b/core/src/index/knowhere/knowhere/index/vector_index/helpers/FaissGpuResourceMgr.cpp deleted file mode 100644 index 5a37efe4b2..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/helpers/FaissGpuResourceMgr.cpp +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include "knowhere/index/vector_index/helpers/FaissGpuResourceMgr.h" -#include "knowhere/common/Log.h" - -#include -#include - -namespace milvus { -namespace knowhere { - -constexpr int64_t MB = 1LL << 20; - -FaissGpuResourceMgr& -FaissGpuResourceMgr::GetInstance() { - static FaissGpuResourceMgr instance; - return instance; -} - -void -FaissGpuResourceMgr::AllocateTempMem(ResPtr& resource, const int64_t device_id, const int64_t size) { - if (size) { - resource->faiss_res->setTempMemory(size); - } else { - auto search = devices_params_.find(device_id); - if (search != devices_params_.end()) { - resource->faiss_res->setTempMemory(search->second.temp_mem_size); - } - // else do nothing. allocate when use. - } -} - -void -FaissGpuResourceMgr::InitDevice(int64_t device_id, int64_t pin_mem_size, int64_t temp_mem_size, int64_t res_num) { - DeviceParams params; - params.pinned_mem_size = pin_mem_size; - params.temp_mem_size = temp_mem_size; - params.resource_num = res_num; - - devices_params_.emplace(device_id, params); - LOG_KNOWHERE_DEBUG_ << "DEVICEID " << device_id << ", pin_mem_size " << pin_mem_size / MB << "MB, temp_mem_size " - << temp_mem_size / MB << "MB, resource count " << res_num; -} - -void -FaissGpuResourceMgr::InitResource() { - if (!initialized_) { - std::lock_guard lock(init_mutex_); - - if (!initialized_) { - for (auto& device : devices_params_) { - auto& device_id = device.first; - - mutex_cache_.emplace(device_id, std::make_unique()); - - auto& device_param = device.second; - auto& bq = idle_map_[device_id]; - - for (int64_t i = 0; i < device_param.resource_num; ++i) { - auto raw_resource = std::make_shared(); - - // TODO(linxj): enable set pinned memory - auto res_wrapper = std::make_shared(raw_resource); - AllocateTempMem(res_wrapper, device_id, 0); - - bq.Put(res_wrapper); - } - LOG_KNOWHERE_DEBUG_ << "DEVICEID " << device_id << ", resource count " << bq.Size(); - } - initialized_ = true; - } - } -} - -ResPtr -FaissGpuResourceMgr::GetRes(const int64_t device_id, const int64_t alloc_size) { - fiu_return_on("FaissGpuResourceMgr.GetRes.ret_null", nullptr); - InitResource(); - - auto finder = idle_map_.find(device_id); - if (finder != idle_map_.end()) { - auto& bq = finder->second; - auto&& resource = bq.Take(); - AllocateTempMem(resource, device_id, alloc_size); - return resource; - } else { - LOG_KNOWHERE_ERROR_ << "GPU device " << device_id << " not initialized"; - for (auto& item : idle_map_) { - auto& bq = item.second; - LOG_KNOWHERE_ERROR_ << "DEVICEID " << item.first << ", resource count " << bq.Size(); - } - return nullptr; - } -} - -void -FaissGpuResourceMgr::MoveToIdle(const int64_t device_id, const ResPtr& res) { - auto finder = idle_map_.find(device_id); - if (finder != idle_map_.end()) { - auto& bq = finder->second; - bq.Put(res); - } -} - -void -FaissGpuResourceMgr::Free() { - for (auto& item : idle_map_) { - auto& bq = item.second; - while (!bq.Empty()) { - bq.Take(); - } - } - initialized_ = false; -} - -void -FaissGpuResourceMgr::Dump() { - for (auto& item : idle_map_) { - auto& bq = item.second; - LOG_KNOWHERE_DEBUG_ << "DEVICEID: " << item.first << ", resource count:" << bq.Size(); - } -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/helpers/FaissGpuResourceMgr.h b/core/src/index/knowhere/knowhere/index/vector_index/helpers/FaissGpuResourceMgr.h deleted file mode 100644 index 9b86ac57a5..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/helpers/FaissGpuResourceMgr.h +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include -#include -#include - -#include - -#include "src/utils/BlockingQueue.h" - -namespace milvus { -namespace knowhere { - -struct Resource { - explicit Resource(std::shared_ptr& r) : faiss_res(r) { - static int64_t global_id = 0; - id = global_id++; - } - - std::shared_ptr faiss_res; - int64_t id; - std::mutex mutex; -}; -using ResPtr = std::shared_ptr; -using ResWPtr = std::weak_ptr; - -class FaissGpuResourceMgr { - public: - friend class ResScope; - using ResBQ = milvus::server::BlockingQueue; - - public: - struct DeviceParams { - int64_t temp_mem_size = 0; - int64_t pinned_mem_size = 0; - int64_t resource_num = 2; - }; - - public: - static FaissGpuResourceMgr& - GetInstance(); - - // Free gpu resource, avoid cudaGetDevice error when deallocate. - // this func should be invoke before main return - void - Free(); - - void - AllocateTempMem(ResPtr& resource, const int64_t device_id, const int64_t size); - - void - InitDevice(int64_t device_id, int64_t pin_mem_size = 0, int64_t temp_mem_size = 0, int64_t res_num = 2); - - void - InitResource(); - - // allocate gpu memory invoke by build or copy_to_gpu - ResPtr - GetRes(const int64_t device_id, const int64_t alloc_size = 0); - - void - MoveToIdle(const int64_t device_id, const ResPtr& res); - - void - Dump(); - - protected: - bool initialized_ = false; - std::mutex init_mutex_; - - std::map> mutex_cache_; - std::map devices_params_; - std::map idle_map_; -}; - -class ResScope { - public: - ResScope(ResPtr& res, const int64_t device_id, const bool isown) - : resource(res), device_id(device_id), move(true), own(isown) { - Lock(); - } - - ResScope(ResWPtr& res, const int64_t device_id, const bool isown) - : resource(res), device_id(device_id), move(true), own(isown) { - Lock(); - } - - // specif for search - // get the ownership of gpuresource and gpu - ResScope(ResWPtr& res, const int64_t device_id) : device_id(device_id), move(false), own(true) { - resource = res.lock(); - Lock(); - } - - void - Lock() { - if (own) - FaissGpuResourceMgr::GetInstance().mutex_cache_[device_id]->lock(); - resource->mutex.lock(); - } - - ~ResScope() { - if (own) - FaissGpuResourceMgr::GetInstance().mutex_cache_[device_id]->unlock(); - if (move) - FaissGpuResourceMgr::GetInstance().MoveToIdle(device_id, resource); - resource->mutex.unlock(); - } - - private: - ResPtr resource; // hold resource until deconstruct - int64_t device_id; - bool move = true; - bool own = false; -}; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/helpers/FaissIO.cpp b/core/src/index/knowhere/knowhere/index/vector_index/helpers/FaissIO.cpp deleted file mode 100644 index b64db7cbf9..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/helpers/FaissIO.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include - -#include "knowhere/common/Log.h" -#include "knowhere/index/vector_index/helpers/FaissIO.h" - -namespace milvus { -namespace knowhere { - -// TODO(linxj): Get From Config File -static size_t magic_num = 2; - -size_t -MemoryIOWriter::operator()(const void* ptr, size_t size, size_t nitems) { - auto total_need = size * nitems + rp; - - if (!data_) { // data == nullptr - total = total_need * magic_num; - rp = size * nitems; - data_ = new uint8_t[total]; - memcpy((void*)(data_), ptr, rp); - return nitems; - } - - if (total_need > total) { - total = total_need * magic_num; - auto new_data = new uint8_t[total]; - memcpy((void*)new_data, (void*)data_, rp); - delete[] data_; - data_ = new_data; - - memcpy((void*)(data_ + rp), ptr, size * nitems); - rp = total_need; - } else { - memcpy((void*)(data_ + rp), ptr, size * nitems); - rp = total_need; - } - - return nitems; -} - -size_t -MemoryIOReader::operator()(void* ptr, size_t size, size_t nitems) { - if (rp >= total) - return 0; - size_t nremain = (total - rp) / size; - if (nremain < nitems) - nitems = nremain; - memcpy(ptr, (void*)(data_ + rp), size * nitems); - rp += size * nitems; - return nitems; -} - -void -enable_faiss_logging() { - faiss::LOG_DEBUG_ = &log_debug_; -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/helpers/FaissIO.h b/core/src/index/knowhere/knowhere/index/vector_index/helpers/FaissIO.h deleted file mode 100644 index e777c6746e..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/helpers/FaissIO.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include - -namespace milvus { -namespace knowhere { - -struct MemoryIOWriter : public faiss::IOWriter { - uint8_t* data_ = nullptr; - size_t total = 0; - size_t rp = 0; - - size_t - operator()(const void* ptr, size_t size, size_t nitems) override; - - template - size_t - write(T* ptr, size_t size, size_t nitems = 1) { - return operator()((const void*)ptr, size, nitems); - } -}; - -struct MemoryIOReader : public faiss::IOReader { - uint8_t* data_; - size_t rp = 0; - size_t total = 0; - - size_t - operator()(void* ptr, size_t size, size_t nitems) override; - - template - size_t - read(T* ptr, size_t size, size_t nitems = 1) { - return operator()((void*)ptr, size, nitems); - } -}; - -void -enable_faiss_logging(); - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/helpers/IndexParameter.cpp b/core/src/index/knowhere/knowhere/index/vector_index/helpers/IndexParameter.cpp deleted file mode 100644 index 2d63a3f4a0..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/helpers/IndexParameter.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "knowhere/index/vector_index/helpers/IndexParameter.h" -#include "knowhere/common/Exception.h" - -#include - -namespace milvus { -namespace knowhere { - -faiss::MetricType -GetMetricType(const std::string& type) { - if (type == Metric::L2) { - return faiss::METRIC_L2; - } - if (type == Metric::IP) { - return faiss::METRIC_INNER_PRODUCT; - } - if (type == Metric::JACCARD) { - return faiss::METRIC_Jaccard; - } - if (type == Metric::TANIMOTO) { - return faiss::METRIC_Tanimoto; - } - if (type == Metric::HAMMING) { - return faiss::METRIC_Hamming; - } - if (type == Metric::SUBSTRUCTURE) { - return faiss::METRIC_Substructure; - } - if (type == Metric::SUPERSTRUCTURE) { - return faiss::METRIC_Superstructure; - } - - KNOWHERE_THROW_MSG("Metric type is invalid"); -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/helpers/IndexParameter.h b/core/src/index/knowhere/knowhere/index/vector_index/helpers/IndexParameter.h deleted file mode 100644 index b37988d881..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/helpers/IndexParameter.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -namespace milvus { -namespace knowhere { - -namespace meta { -constexpr const char* DIM = "dim"; -constexpr const char* TENSOR = "tensor"; -constexpr const char* ROWS = "rows"; -constexpr const char* IDS = "ids"; -constexpr const char* DISTANCE = "distance"; -constexpr const char* TOPK = "k"; -constexpr const char* DEVICEID = "gpu_id"; -}; // namespace meta - -namespace IndexParams { -// IVF Params -constexpr const char* nprobe = "nprobe"; -constexpr const char* nlist = "nlist"; -constexpr const char* m = "m"; // PQ -constexpr const char* nbits = "nbits"; // PQ/SQ - -// NSG Params -constexpr const char* knng = "knng"; -constexpr const char* search_length = "search_length"; -constexpr const char* out_degree = "out_degree"; -constexpr const char* candidate = "candidate_pool_size"; - -// HNSW Params -constexpr const char* efConstruction = "efConstruction"; -constexpr const char* M = "M"; -constexpr const char* ef = "ef"; - -// Annoy Params -constexpr const char* n_trees = "n_trees"; -constexpr const char* search_k = "search_k"; -} // namespace IndexParams - -namespace Metric { -constexpr const char* TYPE = "metric_type"; -constexpr const char* IP = "IP"; -constexpr const char* L2 = "L2"; -constexpr const char* HAMMING = "HAMMING"; -constexpr const char* JACCARD = "JACCARD"; -constexpr const char* TANIMOTO = "TANIMOTO"; -constexpr const char* SUBSTRUCTURE = "SUBSTRUCTURE"; -constexpr const char* SUPERSTRUCTURE = "SUPERSTRUCTURE"; -} // namespace Metric - -extern faiss::MetricType -GetMetricType(const std::string& type); - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/helpers/SPTAGParameterMgr.cpp b/core/src/index/knowhere/knowhere/index/vector_index/helpers/SPTAGParameterMgr.cpp deleted file mode 100644 index 968002f389..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/helpers/SPTAGParameterMgr.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include - -#include "knowhere/index/vector_index/helpers/SPTAGParameterMgr.h" - -namespace milvus { -namespace knowhere { - -const Config& -SPTAGParameterMgr::GetKDTParameters() { - return kdt_config_; -} - -const Config& -SPTAGParameterMgr::GetBKTParameters() { - return bkt_config_; -} - -SPTAGParameterMgr::SPTAGParameterMgr() { - kdt_config_["kdtnumber"] = 1; - kdt_config_["numtopdimensionkdtsplit"] = 5; - kdt_config_["samples"] = 100; - kdt_config_["tptnumber"] = 1; - kdt_config_["tptleafsize"] = 2000; - kdt_config_["numtopdimensiontptsplit"] = 5; - kdt_config_["neighborhoodsize"] = 32; - kdt_config_["graphneighborhoodscale"] = 2; - kdt_config_["graphcefscale"] = 2; - kdt_config_["refineiterations"] = 0; - kdt_config_["cef"] = 1000; - kdt_config_["maxcheckforrefinegraph"] = 10000; - kdt_config_["numofthreads"] = 1; - kdt_config_["maxcheck"] = 8192; - kdt_config_["thresholdofnumberofcontinuousnobetterpropagation"] = 3; - kdt_config_["numberofinitialdynamicpivots"] = 50; - kdt_config_["numberofotherdynamicpivots"] = 4; - - bkt_config_["bktnumber"] = 1; - bkt_config_["bktkmeansk"] = 32; - bkt_config_["bktleafsize"] = 8; - bkt_config_["samples"] = 100; - bkt_config_["tptnumber"] = 1; - bkt_config_["tptleafsize"] = 2000; - bkt_config_["numtopdimensiontptsplit"] = 5; - bkt_config_["neighborhoodsize"] = 32; - bkt_config_["graphneighborhoodscale"] = 2; - bkt_config_["graphcefscale"] = 2; - bkt_config_["refineiterations"] = 0; - bkt_config_["cef"] = 1000; - bkt_config_["maxcheckforrefinegraph"] = 10000; - bkt_config_["numofthreads"] = 1; - bkt_config_["maxcheck"] = 8192; - bkt_config_["thresholdofnumberofcontinuousnobetterpropagation"] = 3; - bkt_config_["numberofinitialdynamicpivots"] = 50; - bkt_config_["numberofotherdynamicpivots"] = 4; -} - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/helpers/SPTAGParameterMgr.h b/core/src/index/knowhere/knowhere/index/vector_index/helpers/SPTAGParameterMgr.h deleted file mode 100644 index 300ddc7db6..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/helpers/SPTAGParameterMgr.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include - -#include -#include "IndexParameter.h" -#include "knowhere/common/Config.h" - -namespace milvus { -namespace knowhere { - -class SPTAGParameterMgr { - public: - const Config& - GetKDTParameters(); - - const Config& - GetBKTParameters(); - - public: - static SPTAGParameterMgr& - GetInstance() { - static SPTAGParameterMgr instance; - return instance; - } - - SPTAGParameterMgr(const SPTAGParameterMgr&) = delete; - - SPTAGParameterMgr& - operator=(const SPTAGParameterMgr&) = delete; - - private: - SPTAGParameterMgr(); - - private: - Config kdt_config_; - Config bkt_config_; -}; - -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/Distance.cpp b/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/Distance.cpp deleted file mode 100644 index 77897c7bef..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/Distance.cpp +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include -#include - -#include "knowhere/index/vector_index/impl/nsg/Distance.h" - -namespace milvus { -namespace knowhere { -namespace impl { - -#if 0 /* use FAISS distance calculation algorithm instead */ - -float -DistanceL2::Compare(const float* a, const float* b, unsigned size) const { - float result = 0; - -#ifdef __GNUC__ -#ifdef __AVX__ - -#define AVX_L2SQR(addr1, addr2, dest, tmp1, tmp2) \ - tmp1 = _mm256_loadu_ps(addr1); \ - tmp2 = _mm256_loadu_ps(addr2); \ - tmp1 = _mm256_sub_ps(tmp1, tmp2); \ - tmp1 = _mm256_mul_ps(tmp1, tmp1); \ - dest = _mm256_add_ps(dest, tmp1); - - __m256 sum; - __m256 l0, l1; - __m256 r0, r1; - unsigned D = (size + 7) & ~7U; - unsigned DR = D % 16; - unsigned DD = D - DR; - const float* l = a; - const float* r = b; - const float* e_l = l + DD; - const float* e_r = r + DD; - float unpack[8] __attribute__((aligned(32))) = {0, 0, 0, 0, 0, 0, 0, 0}; - - sum = _mm256_loadu_ps(unpack); - if (DR) { - AVX_L2SQR(e_l, e_r, sum, l0, r0); - } - - for (unsigned i = 0; i < DD; i += 16, l += 16, r += 16) { - AVX_L2SQR(l, r, sum, l0, r0); - AVX_L2SQR(l + 8, r + 8, sum, l1, r1); - } - _mm256_storeu_ps(unpack, sum); - result = unpack[0] + unpack[1] + unpack[2] + unpack[3] + unpack[4] + unpack[5] + unpack[6] + unpack[7]; - -#else -#ifdef __SSE2__ -#define SSE_L2SQR(addr1, addr2, dest, tmp1, tmp2) \ - tmp1 = _mm_load_ps(addr1); \ - tmp2 = _mm_load_ps(addr2); \ - tmp1 = _mm_sub_ps(tmp1, tmp2); \ - tmp1 = _mm_mul_ps(tmp1, tmp1); \ - dest = _mm_add_ps(dest, tmp1); - - __m128 sum; - __m128 l0, l1, l2, l3; - __m128 r0, r1, r2, r3; - unsigned D = (size + 3) & ~3U; - unsigned DR = D % 16; - unsigned DD = D - DR; - const float* l = a; - const float* r = b; - const float* e_l = l + DD; - const float* e_r = r + DD; - float unpack[4] __attribute__((aligned(16))) = {0, 0, 0, 0}; - - sum = _mm_load_ps(unpack); - switch (DR) { - case 12: - SSE_L2SQR(e_l + 8, e_r + 8, sum, l2, r2); - case 8: - SSE_L2SQR(e_l + 4, e_r + 4, sum, l1, r1); - case 4: - SSE_L2SQR(e_l, e_r, sum, l0, r0); - default: - break; - } - for (unsigned i = 0; i < DD; i += 16, l += 16, r += 16) { - SSE_L2SQR(l, r, sum, l0, r0); - SSE_L2SQR(l + 4, r + 4, sum, l1, r1); - SSE_L2SQR(l + 8, r + 8, sum, l2, r2); - SSE_L2SQR(l + 12, r + 12, sum, l3, r3); - } - _mm_storeu_ps(unpack, sum); - result += unpack[0] + unpack[1] + unpack[2] + unpack[3]; - -// nomal distance -#else - - float diff0, diff1, diff2, diff3; - const float* last = a + size; - const float* unroll_group = last - 3; - - /* Process 4 items with each loop for efficiency. */ - while (a < unroll_group) { - diff0 = a[0] - b[0]; - diff1 = a[1] - b[1]; - diff2 = a[2] - b[2]; - diff3 = a[3] - b[3]; - result += diff0 * diff0 + diff1 * diff1 + diff2 * diff2 + diff3 * diff3; - a += 4; - b += 4; - } - /* Process last 0-3 pixels. Not needed for standard vector lengths. */ - while (a < last) { - diff0 = *a++ - *b++; - result += diff0 * diff0; - } -#endif -#endif -#endif - - return result; -} - -float -DistanceIP::Compare(const float* a, const float* b, unsigned size) const { - float result = 0; - -#ifdef __GNUC__ -#ifdef __AVX__ -#define AVX_DOT(addr1, addr2, dest, tmp1, tmp2) \ - tmp1 = _mm256_loadu_ps(addr1); \ - tmp2 = _mm256_loadu_ps(addr2); \ - tmp1 = _mm256_mul_ps(tmp1, tmp2); \ - dest = _mm256_add_ps(dest, tmp1); - - __m256 sum; - __m256 l0, l1; - __m256 r0, r1; - unsigned D = (size + 7) & ~7U; - unsigned DR = D % 16; - unsigned DD = D - DR; - const float* l = a; - const float* r = b; - const float* e_l = l + DD; - const float* e_r = r + DD; - float unpack[8] __attribute__((aligned(32))) = {0, 0, 0, 0, 0, 0, 0, 0}; - - sum = _mm256_loadu_ps(unpack); - if (DR) { - AVX_DOT(e_l, e_r, sum, l0, r0); - } - - for (unsigned i = 0; i < DD; i += 16, l += 16, r += 16) { - AVX_DOT(l, r, sum, l0, r0); - AVX_DOT(l + 8, r + 8, sum, l1, r1); - } - _mm256_storeu_ps(unpack, sum); - result = unpack[0] + unpack[1] + unpack[2] + unpack[3] + unpack[4] + unpack[5] + unpack[6] + unpack[7]; - -#else -#ifdef __SSE2__ -#define SSE_DOT(addr1, addr2, dest, tmp1, tmp2) \ - tmp1 = _mm128_loadu_ps(addr1); \ - tmp2 = _mm128_loadu_ps(addr2); \ - tmp1 = _mm128_mul_ps(tmp1, tmp2); \ - dest = _mm128_add_ps(dest, tmp1); - __m128 sum; - __m128 l0, l1, l2, l3; - __m128 r0, r1, r2, r3; - unsigned D = (size + 3) & ~3U; - unsigned DR = D % 16; - unsigned DD = D - DR; - const float* l = a; - const float* r = b; - const float* e_l = l + DD; - const float* e_r = r + DD; - float unpack[4] __attribute__((aligned(16))) = {0, 0, 0, 0}; - - sum = _mm_load_ps(unpack); - switch (DR) { - case 12: - SSE_DOT(e_l + 8, e_r + 8, sum, l2, r2); - case 8: - SSE_DOT(e_l + 4, e_r + 4, sum, l1, r1); - case 4: - SSE_DOT(e_l, e_r, sum, l0, r0); - default: - break; - } - for (unsigned i = 0; i < DD; i += 16, l += 16, r += 16) { - SSE_DOT(l, r, sum, l0, r0); - SSE_DOT(l + 4, r + 4, sum, l1, r1); - SSE_DOT(l + 8, r + 8, sum, l2, r2); - SSE_DOT(l + 12, r + 12, sum, l3, r3); - } - _mm_storeu_ps(unpack, sum); - result += unpack[0] + unpack[1] + unpack[2] + unpack[3]; -#else - - float dot0, dot1, dot2, dot3; - const float* last = a + size; - const float* unroll_group = last - 3; - - /* Process 4 items with each loop for efficiency. */ - while (a < unroll_group) { - dot0 = a[0] * b[0]; - dot1 = a[1] * b[1]; - dot2 = a[2] * b[2]; - dot3 = a[3] * b[3]; - result += dot0 + dot1 + dot2 + dot3; - a += 4; - b += 4; - } - /* Process last 0-3 pixels. Not needed for standard vector lengths. */ - while (a < last) { - result += *a++ * *b++; - } -#endif -#endif -#endif - return result; -} - -#else - -float -DistanceL2::Compare(const float* a, const float* b, unsigned size) const { - return faiss::fvec_L2sqr(a, b, (size_t)size); -} - -float -DistanceIP::Compare(const float* a, const float* b, unsigned size) const { - return -(faiss::fvec_inner_product(a, b, (size_t)size)); -} - -#endif - -} // namespace impl -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/Distance.h b/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/Distance.h deleted file mode 100644 index 9561d95936..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/Distance.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -namespace milvus { -namespace knowhere { -namespace impl { - -struct Distance { - virtual float - Compare(const float* a, const float* b, unsigned size) const = 0; -}; - -struct DistanceL2 : public Distance { - float - Compare(const float* a, const float* b, unsigned size) const override; -}; - -struct DistanceIP : public Distance { - float - Compare(const float* a, const float* b, unsigned size) const override; -}; - -} // namespace impl -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSG.cpp b/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSG.cpp deleted file mode 100644 index bb8ce84f7e..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSG.cpp +++ /dev/null @@ -1,893 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include "knowhere/index/vector_index/impl/nsg/NSG.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "faiss/BuilderSuspend.h" -#include "knowhere/common/Exception.h" -#include "knowhere/common/Log.h" -#include "knowhere/common/Timer.h" -#include "knowhere/index/vector_index/impl/nsg/NSGHelper.h" - -namespace milvus { -namespace knowhere { -namespace impl { - -unsigned int seed = 100; - -NsgIndex::NsgIndex(const size_t& dimension, const size_t& n, Metric_Type metric) - : dimension(dimension), ntotal(n), metric_type(metric) { - if (metric == Metric_Type::Metric_Type_L2) { - distance_ = new DistanceL2; - } else if (metric == Metric_Type::Metric_Type_IP) { - distance_ = new DistanceIP; - } -} - -NsgIndex::~NsgIndex() { - delete[] ori_data_; - delete[] ids_; - delete distance_; -} - -void -NsgIndex::Build_with_ids(size_t nb, const float* data, const int64_t* ids, const BuildParams& parameters) { - ntotal = nb; - ori_data_ = new float[ntotal * dimension]; - ids_ = new int64_t[ntotal]; - memcpy((void*)ori_data_, (void*)data, sizeof(float) * ntotal * dimension); - memcpy((void*)ids_, (void*)ids, sizeof(int64_t) * ntotal); - - search_length = parameters.search_length; - out_degree = parameters.out_degree; - candidate_pool_size = parameters.candidate_pool_size; - - TimeRecorder rc("NSG", 1); - InitNavigationPoint(); - rc.RecordSection("init"); - - Link(); - rc.RecordSection("Link"); - - CheckConnectivity(); - rc.RecordSection("Connect"); - rc.ElapseFromBegin("finish"); - - is_trained = true; - - int total_degree = 0; - for (size_t i = 0; i < ntotal; ++i) { - total_degree += nsg[i].size(); - } - LOG_KNOWHERE_DEBUG_ << "Graph physical size: " << total_degree * sizeof(node_t) / 1024 / 1024 << "m"; - LOG_KNOWHERE_DEBUG_ << "Average degree: " << total_degree / ntotal; - - // Debug code - // for (size_t i = 0; i < ntotal; i++) { - // auto& x = nsg[i]; - // for (size_t j = 0; j < x.size(); j++) { - // std::cout << "id: " << x[j] << std::endl; - // } - // std::cout << std::endl; - // } -} - -void -NsgIndex::InitNavigationPoint() { - // calculate the center of vectors - auto center = new float[dimension]; - memset(center, 0, sizeof(float) * dimension); - - for (size_t i = 0; i < ntotal; i++) { - for (size_t j = 0; j < dimension; j++) { - center[j] += ori_data_[i * dimension + j]; - } - } - for (size_t j = 0; j < dimension; j++) { - center[j] /= ntotal; - } - - // select navigation point - std::vector resset; - navigation_point = rand_r(&seed) % ntotal; // random initialize navigating point - GetNeighbors(center, resset, knng); - navigation_point = resset[0].id; - - // Debug code - // std::cout << "ep: " << navigation_point << std::endl; - // for (int k = 0; k < resset.size(); ++k) { - // std::cout << "id: " << resset[k].id << ", dis: " << resset[k].distance << std::endl; - // } - // std::cout << std::endl; - // - // std::cout << "ep: " << navigation_point << std::endl; - // - // float r1 = distance_->Compare(center, ori_data_ + navigation_point * dimension, dimension); - // assert(r1 == resset[0].distance); -} - -// Specify Link -void -NsgIndex::GetNeighbors(const float* query, std::vector& resset, std::vector& fullset, - boost::dynamic_bitset<>& has_calculated_dist) { - auto& graph = knng; - size_t buffer_size = search_length; - - if (buffer_size > ntotal) { - KNOWHERE_THROW_MSG("Build Error, search_length > ntotal"); - } - - resset.resize(search_length); - std::vector init_ids(buffer_size); - // std::vector init_ids; - - { - /* - * copy navigation-point neighbor, pick random node if less than buffer size - */ - size_t count = 0; - - // Get all neighbors - for (size_t i = 0; i < init_ids.size() && i < graph[navigation_point].size(); ++i) { - // for (size_t i = 0; i < graph[navigation_point].size(); ++i) { - // init_ids.push_back(graph[navigation_point][i]); - init_ids[i] = graph[navigation_point][i]; - has_calculated_dist[init_ids[i]] = true; - ++count; - } - while (count < buffer_size) { - node_t id = rand_r(&seed) % ntotal; - if (has_calculated_dist[id]) - continue; // duplicate id - // init_ids.push_back(id); - init_ids[count] = id; - ++count; - has_calculated_dist[id] = true; - } - } - - { - // resset.resize(init_ids.size()); - - // init resset and sort by distance - for (size_t i = 0; i < init_ids.size(); ++i) { - node_t id = init_ids[i]; - - if (id >= static_cast(ntotal)) { - KNOWHERE_THROW_MSG("Build Index Error, id > ntotal"); - continue; - } - - float dist = distance_->Compare(ori_data_ + dimension * id, query, dimension); - resset[i] = Neighbor(id, dist, false); - - //// difference from other GetNeighbors - fullset.push_back(resset[i]); - /////////////////////////////////////// - } - std::sort(resset.begin(), resset.end()); // sort by distance - - // search nearest neighbor - size_t cursor = 0; - while (cursor < buffer_size) { - size_t nearest_updated_pos = buffer_size; - - if (!resset[cursor].has_explored) { - resset[cursor].has_explored = true; - - node_t start_pos = resset[cursor].id; - auto& wait_for_search_node_vec = graph[start_pos]; - for (size_t i = 0; i < wait_for_search_node_vec.size(); ++i) { - node_t id = wait_for_search_node_vec[i]; - if (has_calculated_dist[id]) - continue; - has_calculated_dist[id] = true; - - float dist = distance_->Compare(query, ori_data_ + dimension * id, dimension); - Neighbor nn(id, dist, false); - fullset.push_back(nn); - - if (dist >= resset[buffer_size - 1].distance) - continue; - - size_t pos = InsertIntoPool(resset.data(), buffer_size, nn); // replace with a closer node - if (pos < nearest_updated_pos) - nearest_updated_pos = pos; - - // assert(buffer_size + 1 >= resset.size()); - if (buffer_size + 1 < resset.size()) - ++buffer_size; - } - } - if (cursor >= nearest_updated_pos) { - cursor = nearest_updated_pos; // re-search from new pos - } else { - ++cursor; - } - } - } -} - -// FindUnconnectedNode -void -NsgIndex::GetNeighbors(const float* query, std::vector& resset, std::vector& fullset) { - auto& graph = nsg; - size_t buffer_size = search_length; - - if (buffer_size > ntotal) { - KNOWHERE_THROW_MSG("Build Error, search_length > ntotal"); - } - - // std::vector init_ids; - std::vector init_ids(buffer_size); - resset.resize(buffer_size); - boost::dynamic_bitset<> has_calculated_dist{ntotal, 0}; - - { - /* - * copy navigation-point neighbor, pick random node if less than buffer size - */ - size_t count = 0; - - // Get all neighbors - for (size_t i = 0; i < init_ids.size() && i < graph[navigation_point].size(); ++i) { - // for (size_t i = 0; i < graph[navigation_point].size(); ++i) { - // init_ids.push_back(graph[navigation_point][i]); - init_ids[i] = graph[navigation_point][i]; - has_calculated_dist[init_ids[i]] = true; - ++count; - } - while (count < buffer_size) { - node_t id = rand_r(&seed) % ntotal; - if (has_calculated_dist[id]) - continue; // duplicate id - // init_ids.push_back(id); - init_ids[count] = id; - ++count; - has_calculated_dist[id] = true; - } - } - - { - // resset.resize(init_ids.size()); - - // init resset and sort by distance - for (size_t i = 0; i < init_ids.size(); ++i) { - node_t id = init_ids[i]; - - if (id >= static_cast(ntotal)) { - KNOWHERE_THROW_MSG("Build Index Error, id > ntotal"); - continue; - } - - float dist = distance_->Compare(ori_data_ + id * dimension, query, dimension); - resset[i] = Neighbor(id, dist, false); - } - std::sort(resset.begin(), resset.end()); // sort by distance - - // search nearest neighbor - size_t cursor = 0; - while (cursor < buffer_size) { - size_t nearest_updated_pos = buffer_size; - - if (!resset[cursor].has_explored) { - resset[cursor].has_explored = true; - - node_t start_pos = resset[cursor].id; - auto& wait_for_search_node_vec = graph[start_pos]; - for (size_t i = 0; i < wait_for_search_node_vec.size(); ++i) { - node_t id = wait_for_search_node_vec[i]; - if (has_calculated_dist[id]) - continue; - has_calculated_dist[id] = true; - - float dist = distance_->Compare(ori_data_ + dimension * id, query, dimension); - Neighbor nn(id, dist, false); - fullset.push_back(nn); - - if (dist >= resset[buffer_size - 1].distance) - continue; - - size_t pos = InsertIntoPool(resset.data(), buffer_size, nn); // replace with a closer node - if (pos < nearest_updated_pos) - nearest_updated_pos = pos; - - // assert(buffer_size + 1 >= resset.size()); - if (buffer_size + 1 < resset.size()) - ++buffer_size; // trick - } - } - if (cursor >= nearest_updated_pos) { - cursor = nearest_updated_pos; // re-search from new pos - } else { - ++cursor; - } - } - } -} - -void -NsgIndex::GetNeighbors(const float* query, std::vector& resset, Graph& graph, SearchParams* params) { - size_t buffer_size = params ? params->search_length : search_length; - - if (buffer_size > ntotal) { - KNOWHERE_THROW_MSG("Build Error, search_length > ntotal"); - } - - std::vector init_ids(buffer_size); - resset.resize(buffer_size); - boost::dynamic_bitset<> has_calculated_dist{ntotal, 0}; - - { - /* - * copy navigation-point neighbor, pick random node if less than buffer size - */ - size_t count = 0; - - // Get all neighbors - for (size_t i = 0; i < init_ids.size() && i < graph[navigation_point].size(); ++i) { - init_ids[i] = graph[navigation_point][i]; - has_calculated_dist[init_ids[i]] = true; - ++count; - } - while (count < buffer_size) { - node_t id = rand_r(&seed) % ntotal; - if (has_calculated_dist[id]) - continue; // duplicate id - init_ids[count] = id; - ++count; - has_calculated_dist[id] = true; - } - } - - { - // resset.resize(init_ids.size()); - - // init resset and sort by distance - for (size_t i = 0; i < init_ids.size(); ++i) { - node_t id = init_ids[i]; - - if (id >= static_cast(ntotal)) { - KNOWHERE_THROW_MSG("Build Index Error, id > ntotal"); - } - - float dist = distance_->Compare(ori_data_ + id * dimension, query, dimension); - resset[i] = Neighbor(id, dist, false); - } - std::sort(resset.begin(), resset.end()); // sort by distance - - // search nearest neighbor - size_t cursor = 0; - while (cursor < buffer_size) { - size_t nearest_updated_pos = buffer_size; - - if (!resset[cursor].has_explored) { - resset[cursor].has_explored = true; - - node_t start_pos = resset[cursor].id; - auto& wait_for_search_node_vec = graph[start_pos]; - for (size_t i = 0; i < wait_for_search_node_vec.size(); ++i) { - node_t id = wait_for_search_node_vec[i]; - if (has_calculated_dist[id]) - continue; - has_calculated_dist[id] = true; - - float dist = distance_->Compare(query, ori_data_ + dimension * id, dimension); - - if (dist >= resset[buffer_size - 1].distance) - continue; - - //// difference from other GetNeighbors - Neighbor nn(id, dist, false); - /////////////////////////////////////// - - size_t pos = InsertIntoPool(resset.data(), buffer_size, nn); // replace with a closer node - if (pos < nearest_updated_pos) - nearest_updated_pos = pos; - - //>> Debug code - ///// - // std::cout << "pos: " << pos << ", nn: " << nn.id << ":" << nn.distance << ", nup: " << - // nearest_updated_pos << std::endl; - ///// - // trick: avoid search query search_length < init_ids.size() ... - if (buffer_size + 1 < resset.size()) - ++buffer_size; - } - } - if (cursor >= nearest_updated_pos) { - cursor = nearest_updated_pos; // re-search from new pos - } else { - ++cursor; - } - } - } -} - -void -NsgIndex::Link() { - float* cut_graph_dist = new float[ntotal * out_degree]; - nsg.resize(ntotal); - -#pragma omp parallel - { - std::vector fullset; - std::vector temp; - boost::dynamic_bitset<> flags{ntotal, 0}; -#pragma omp for schedule(dynamic, 100) - for (size_t n = 0; n < ntotal; ++n) { - faiss::BuilderSuspend::check_wait(); - fullset.clear(); - temp.clear(); - flags.reset(); - GetNeighbors(ori_data_ + dimension * n, temp, fullset, flags); - SyncPrune(n, fullset, flags, cut_graph_dist); - } - - // Debug code - // std::cout << "ep: " << 0 << std::endl; - // for (int k = 0; k < fullset.size(); ++k) { - // std::cout << "id: " << fullset[k].id << ", dis: " << fullset[k].distance << std::endl; - // } - } - knng.clear(); - - // Debug code - // for (size_t i = 0; i < ntotal; i++) - // { - // auto& x = nsg[i]; - // for (size_t j=0; j < x.size(); j++) - // { - // std::cout << "id: " << x[j] << std::endl; - // } - // std::cout << std::endl; - // } - - std::vector mutex_vec(ntotal); -#pragma omp for schedule(dynamic, 100) - for (unsigned n = 0; n < ntotal; ++n) { - faiss::BuilderSuspend::check_wait(); - InterInsert(n, mutex_vec, cut_graph_dist); - } - delete[] cut_graph_dist; -} - -void -NsgIndex::SyncPrune(size_t n, std::vector& pool, boost::dynamic_bitset<>& has_calculated, - float* cut_graph_dist) { - // avoid lose nearest neighbor in knng - for (size_t i = 0; i < knng[n].size(); ++i) { - auto id = knng[n][i]; - if (has_calculated[id]) - continue; - float dist = distance_->Compare(ori_data_ + dimension * n, ori_data_ + dimension * id, dimension); - pool.emplace_back(Neighbor(id, dist, true)); - } - - // sort and find closest node - unsigned cursor = 0; - std::sort(pool.begin(), pool.end()); - std::vector result; - if (pool[cursor].id == static_cast(n)) { - cursor++; - } - result.push_back(pool[cursor]); // init result with nearest neighbor - - SelectEdge(cursor, pool, result, true); - - // filling the cut_graph - auto& des_id_pool = nsg[n]; - float* des_dist_pool = cut_graph_dist + n * out_degree; - for (size_t i = 0; i < result.size(); ++i) { - des_id_pool.push_back(result[i].id); - des_dist_pool[i] = result[i].distance; - } - if (result.size() < out_degree) { - des_dist_pool[result.size()] = -1; - } - //>> Optimize: reserve id_pool capacity -} - -//>> Optimize: remove read-lock -void -NsgIndex::InterInsert(unsigned n, std::vector& mutex_vec, float* cut_graph_dist) { - auto& current = n; - - auto& neighbor_id_pool = nsg[current]; - float* neighbor_dist_pool = cut_graph_dist + current * out_degree; - for (size_t i = 0; i < out_degree; ++i) { - if (neighbor_dist_pool[i] == -1) - break; - - size_t current_neighbor = neighbor_id_pool[i]; // center's neighbor id - auto& nsn_id_pool = nsg[current_neighbor]; // nsn => neighbor's neighbor - float* nsn_dist_pool = cut_graph_dist + current_neighbor * out_degree; - - std::vector wait_for_link_pool; // maintain candidate neighbor of the current neighbor. - int duplicate = false; - { - LockGuard lk(mutex_vec[current_neighbor]); - for (size_t j = 0; j < out_degree; ++j) { - if (nsn_dist_pool[j] == -1) - break; - - // At least one edge can be connected back - if (n == nsn_id_pool[j]) { - duplicate = true; - break; - } - - Neighbor nsn(nsn_id_pool[j], nsn_dist_pool[j]); - wait_for_link_pool.push_back(nsn); - } - } - if (duplicate) - continue; - - // original: (neighbor) <------- (current) - // after: (neighbor) -------> (current) - // current node as a neighbor of its neighbor - Neighbor current_as_neighbor(n, neighbor_dist_pool[i]); - wait_for_link_pool.push_back(current_as_neighbor); - - // re-selectEdge if candidate neighbor num > out_degree - if (wait_for_link_pool.size() > out_degree) { - std::vector result; - - unsigned start = 0; - std::sort(wait_for_link_pool.begin(), wait_for_link_pool.end()); - result.push_back(wait_for_link_pool[start]); - - SelectEdge(start, wait_for_link_pool, result); - - { - LockGuard lk(mutex_vec[current_neighbor]); - for (size_t j = 0; j < result.size(); ++j) { - nsn_id_pool[j] = result[j].id; - nsn_dist_pool[j] = result[j].distance; - } - } - } else { - LockGuard lk(mutex_vec[current_neighbor]); - for (size_t j = 0; j < out_degree; ++j) { - if (nsn_dist_pool[j] == -1) { - nsn_id_pool.push_back(current_as_neighbor.id); - nsn_dist_pool[j] = current_as_neighbor.distance; - if (j + 1 < out_degree) - nsn_dist_pool[j + 1] = -1; - break; - } - } - } - } -} - -void -NsgIndex::SelectEdge(unsigned& cursor, std::vector& sort_pool, std::vector& result, bool limit) { - auto& pool = sort_pool; - - /* - * edge selection - * - * search in pool and search deepth is under candidate_pool_size - * max result size equal to out_degress - */ - size_t search_deepth = limit ? candidate_pool_size : pool.size(); - while (result.size() < out_degree && cursor < search_deepth && (++cursor) < pool.size()) { - auto& p = pool[cursor]; - bool should_link = true; - for (size_t t = 0; t < result.size(); ++t) { - float dist = - distance_->Compare(ori_data_ + dimension * result[t].id, ori_data_ + dimension * p.id, dimension); - - if (dist < p.distance) { - should_link = false; - break; - } - } - if (should_link) - result.push_back(p); - } -} - -void -NsgIndex::CheckConnectivity() { - auto root = navigation_point; - boost::dynamic_bitset<> has_linked{ntotal, 0}; - int64_t linked_count = 0; - - while (linked_count < static_cast(ntotal)) { - faiss::BuilderSuspend::check_wait(); - DFS(root, has_linked, linked_count); - if (linked_count >= static_cast(ntotal)) { - break; - } - FindUnconnectedNode(has_linked, root); - } -} - -void -NsgIndex::DFS(size_t root, boost::dynamic_bitset<>& has_linked, int64_t& linked_count) { - size_t start = root; - std::stack s; - s.push(root); - if (!has_linked[root]) { - linked_count++; // not link - } - has_linked[root] = true; // link start... - - while (!s.empty()) { - size_t next = ntotal + 1; - - for (unsigned i = 0; i < nsg[start].size(); i++) { - if (has_linked[nsg[start][i]] == false) { // if not link - next = nsg[start][i]; - break; - } - } - if (next == (ntotal + 1)) { - s.pop(); - if (s.empty()) - break; - start = s.top(); - continue; - } - start = next; - has_linked[start] = true; - s.push(start); - ++linked_count; - } -} - -void -NsgIndex::FindUnconnectedNode(boost::dynamic_bitset<>& has_linked, int64_t& root) { - // find any of unlinked-node - size_t id = ntotal; - for (size_t i = 0; i < ntotal; i++) { // find not link - if (has_linked[i] == false) { - id = i; - break; - } - } - - if (id == ntotal) - return; // No Unlinked Node - - // search unlinked-node's neighbor - std::vector tmp, pool; - GetNeighbors(ori_data_ + dimension * id, tmp, pool); - std::sort(pool.begin(), pool.end()); - - size_t found = 0; - for (size_t i = 0; i < pool.size(); i++) { // find nearest neighbor and add unlinked-node as its neighbor - if (has_linked[pool[i].id]) { - root = pool[i].id; - found = 1; - break; - } - } - if (found == 0) { - while (true) { // random a linked-node and add unlinked-node as its neighbor - size_t rid = rand_r(&seed) % ntotal; - if (has_linked[rid]) { - root = rid; - break; - } - } - } - nsg[root].push_back(id); -} - -// void -// NsgIndex::GetNeighbors(const float* query, node_t* I, float* D, SearchParams* params) { -// size_t buffer_size = params ? params->search_length : search_length; - -// if (buffer_size > ntotal) { -// KNOWHERE_THROW_MSG("Search Error, search_length > ntotal"); -// } - -// std::vector resset(buffer_size); -// std::vector init_ids(buffer_size); -// boost::dynamic_bitset<> has_calculated_dist{ntotal, 0}; - -// { -// /* -// * copy navigation-point neighbor, pick random node if less than buffer size -// */ -// size_t count = 0; - -// // Get all neighbors -// for (size_t i = 0; i < init_ids.size() && i < nsg[navigation_point].size(); ++i) { -// init_ids[i] = nsg[navigation_point][i]; -// has_calculated_dist[init_ids[i]] = true; -// ++count; -// } -// while (count < buffer_size) { -// node_t id = rand_r(&seed) % ntotal; -// if (has_calculated_dist[id]) -// continue; // duplicate id -// init_ids[count] = id; -// ++count; -// has_calculated_dist[id] = true; -// } -// } - -// { -// // init resset and sort by distance -// for (size_t i = 0; i < init_ids.size(); ++i) { -// node_t id = init_ids[i]; - -// if (id >= static_cast(ntotal)) { -// KNOWHERE_THROW_MSG("Search Error, id > ntotal"); -// } - -// float dist = distance_->Compare(ori_data_ + id * dimension, query, dimension); -// resset[i] = Neighbor(id, dist, false); -// } -// std::sort(resset.begin(), resset.end()); // sort by distance - -// // search nearest neighbor -// size_t cursor = 0; -// while (cursor < buffer_size) { -// size_t nearest_updated_pos = buffer_size; - -// if (!resset[cursor].has_explored) { -// resset[cursor].has_explored = true; - -// node_t start_pos = resset[cursor].id; -// auto& wait_for_search_node_vec = nsg[start_pos]; -// for (size_t i = 0; i < wait_for_search_node_vec.size(); ++i) { -// node_t id = wait_for_search_node_vec[i]; -// if (has_calculated_dist[id]) -// continue; -// has_calculated_dist[id] = true; - -// float dist = distance_->Compare(query, ori_data_ + dimension * id, dimension); - -// if (dist >= resset[buffer_size - 1].distance) -// continue; - -// //// difference from other GetNeighbors -// Neighbor nn(id, dist, false); -// /////////////////////////////////////// - -// size_t pos = InsertIntoPool(resset.data(), buffer_size, nn); // replace with a closer node -// if (pos < nearest_updated_pos) -// nearest_updated_pos = pos; - -// //>> Debug code -// ///// -// // std::cout << "pos: " << pos << ", nn: " << nn.id << ":" << nn.distance << ", nup: " << -// // nearest_updated_pos << std::endl; -// ///// - -// // trick: avoid search query search_length < init_ids.size() ... -// if (buffer_size + 1 < resset.size()) -// ++buffer_size; -// } -// } -// if (cursor >= nearest_updated_pos) { -// cursor = nearest_updated_pos; // re-search from new pos -// } else { -// ++cursor; -// } -// } -// } - -// if ((resset.size() - params->k) >= 0) { -// for (size_t i = 0; i < params->k; ++i) { -// I[i] = resset[i].id; -// D[i] = resset[i].distance; -// } -// } else { -// size_t i = 0; -// for (; i < resset.size(); ++i) { -// I[i] = resset[i].id; -// D[i] = resset[i].distance; -// } -// for (; i < params->k; ++i) { -// I[i] = -1; -// D[i] = -1; -// } -// } -// } - -// void -// NsgIndex::Search(const float* query, const unsigned& nq, const unsigned& dim, const unsigned& k, float* dist, -// int64_t* ids, SearchParams& params) { -// // if (k >= 45) { -// // params.search_length = k; -// // } - -// TimeRecorder rc("nsgsearch", 1); - -// if (nq == 1) { -// GetNeighbors(query, ids, dist, ¶ms); -// } else { -// #pragma omp parallel for -// for (unsigned int i = 0; i < nq; ++i) { -// const float* single_query = query + i * dim; -// GetNeighbors(single_query, ids + i * k, dist + i * k, ¶ms); -// } -// } -// rc.ElapseFromBegin("seach finish"); -// } - -void -NsgIndex::Search(const float* query, const unsigned& nq, const unsigned& dim, const unsigned& k, float* dist, - int64_t* ids, SearchParams& params, faiss::ConcurrentBitsetPtr bitset) { - std::vector> resset(nq); - - TimeRecorder rc("NsgIndex::search", 1); - if (nq == 1) { - GetNeighbors(query, resset[0], nsg, ¶ms); - } else { -#pragma omp parallel for - for (unsigned int i = 0; i < nq; ++i) { - const float* single_query = query + i * dim; - GetNeighbors(single_query, resset[i], nsg, ¶ms); - } - } - rc.RecordSection("search"); - - bool is_ip = (metric_type == Metric_Type::Metric_Type_IP); - for (unsigned int i = 0; i < nq; ++i) { - unsigned int pos = 0; - for (unsigned int j = 0; j < resset[i].size(); ++j) { - if (pos >= k) - break; // already top k - if (!bitset || !bitset->test((faiss::ConcurrentBitset::id_type_t)resset[i][j].id)) { - ids[i * k + pos] = ids_[resset[i][j].id]; - dist[i * k + pos] = is_ip ? -resset[i][j].distance : resset[i][j].distance; - ++pos; - } - } - // fill with -1 - for (unsigned int j = pos; j < k; ++j) { - ids[i * k + j] = -1; - dist[i * k + j] = -1; - } - } - rc.RecordSection("merge"); -} - -void -NsgIndex::SetKnnGraph(Graph& g) { - knng = std::move(g); -} - -int64_t -NsgIndex::GetSize() { - int64_t ret = 0; - ret += sizeof(*this); - ret += ntotal * dimension * sizeof(float); - ret += ntotal * sizeof(int64_t); - ret += sizeof(*distance_); - for (auto i = 0; i < nsg.size(); ++i) { - ret += nsg[i].size() * sizeof(node_t); - } - for (auto i = 0; i < knng.size(); ++i) { - ret += knng[i].size() * sizeof(node_t); - } - return ret; -} - -} // namespace impl -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSG.h b/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSG.h deleted file mode 100644 index 2de1c369c5..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSG.h +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include -#include -#include -#include -#include - -#include "Distance.h" -#include "Neighbor.h" -#include "knowhere/common/Config.h" -#include "knowhere/index/vector_index/helpers/IndexParameter.h" - -namespace milvus { -namespace knowhere { -namespace impl { - -using node_t = int64_t; - -struct BuildParams { - size_t search_length; - size_t out_degree; - size_t candidate_pool_size; -}; - -struct SearchParams { - size_t search_length; - size_t k; -}; - -using Graph = std::vector>; - -class NsgIndex { - public: - enum Metric_Type { - Metric_Type_L2, - Metric_Type_IP, - }; - - size_t dimension; - size_t ntotal; // totabl nb of indexed vectors - int32_t metric_type; // enum Metric_Type - Distance* distance_; - - float* ori_data_; - int64_t* ids_; - Graph nsg; // final graph - Graph knng; // reset after build - - node_t navigation_point; // offset of node in origin data - - bool is_trained = false; - - /* - * build and search parameter - */ - size_t search_length; - size_t candidate_pool_size; // search deepth in fullset - size_t out_degree; - - public: - explicit NsgIndex(const size_t& dimension, const size_t& n, Metric_Type metric); - - NsgIndex() = default; - - virtual ~NsgIndex(); - - void - SetKnnGraph(Graph& knng); - - virtual void - Build_with_ids(size_t nb, const float* data, const int64_t* ids, const BuildParams& parameters); - - void - Search(const float* query, const unsigned& nq, const unsigned& dim, const unsigned& k, float* dist, int64_t* ids, - SearchParams& params, faiss::ConcurrentBitsetPtr bitset = nullptr); - - int64_t - GetSize(); - - // Not support yet. - // virtual void Add() = 0; - // virtual void Add_with_ids() = 0; - // virtual void Delete() = 0; - // virtual void Delete_with_ids() = 0; - // virtual void Rebuild(size_t nb, - // const float *data, - // const int64_t *ids, - // const Parameters ¶meters) = 0; - // virtual void Build(size_t nb, - // const float *data, - // const BuildParam ¶meters); - - protected: - virtual void - InitNavigationPoint(); - - // link specify - void - GetNeighbors(const float* query, std::vector& resset, std::vector& fullset, - boost::dynamic_bitset<>& has_calculated_dist); - - // FindUnconnectedNode - void - GetNeighbors(const float* query, std::vector& resset, std::vector& fullset); - - // navigation-point - void - GetNeighbors(const float* query, std::vector& resset, Graph& graph, SearchParams* param = nullptr); - - // only for search - // void - // GetNeighbors(const float* query, node_t* I, float* D, SearchParams* params); - - void - Link(); - - void - SyncPrune(size_t q, std::vector& pool, boost::dynamic_bitset<>& has_calculated, float* cut_graph_dist); - - void - SelectEdge(unsigned& cursor, std::vector& sort_pool, std::vector& result, bool limit = false); - - void - InterInsert(unsigned n, std::vector& mutex_vec, float* dist); - - void - CheckConnectivity(); - - void - DFS(size_t root, boost::dynamic_bitset<>& flags, int64_t& count); - - void - FindUnconnectedNode(boost::dynamic_bitset<>& flags, int64_t& root); -}; - -} // namespace impl -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSGHelper.cpp b/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSGHelper.cpp deleted file mode 100644 index 334cc5d02c..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSGHelper.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include - -#include "knowhere/index/vector_index/impl/nsg/NSGHelper.h" - -namespace milvus { -namespace knowhere { -namespace impl { - -// TODO: impl search && insert && return insert pos. why not just find and swap? -int -InsertIntoPool(Neighbor* addr, unsigned K, Neighbor nn) { - //>> Fix: Add assert - // for (unsigned int i = 0; i < K; ++i) { - // assert(addr[i].id != nn.id); - // } - - // find the location to insert - int left = 0, right = K - 1; - if (addr[left].distance > nn.distance) { - //>> Fix: memmove overflow, dump when vector deconstruct - memmove((char*)&addr[left + 1], &addr[left], (K - 1) * sizeof(Neighbor)); - addr[left] = nn; - return left; - } - if (addr[right].distance < nn.distance) { - addr[K] = nn; - return K; - } - while (left < right - 1) { - int mid = (left + right) / 2; - if (addr[mid].distance > nn.distance) - right = mid; - else - left = mid; - } - // check equal ID - - while (left > 0) { - if (addr[left].distance < nn.distance) // pos is right - break; - if (addr[left].id == nn.id) - return K + 1; - left--; - } - if (addr[left].id == nn.id || addr[right].id == nn.id) - return K + 1; - - //>> Fix: memmove overflow, dump when vector deconstruct - memmove((char*)&addr[right + 1], &addr[right], (K - 1 - right) * sizeof(Neighbor)); - addr[right] = nn; - return right; -} - -}; // namespace impl -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSGHelper.h b/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSGHelper.h deleted file mode 100644 index f4be91db37..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSGHelper.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include "Neighbor.h" - -namespace milvus { -namespace knowhere { -namespace impl { - -extern int -InsertIntoPool(Neighbor* addr, unsigned K, Neighbor nn); - -} // namespace impl -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSGIO.cpp b/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSGIO.cpp deleted file mode 100644 index e52512d5b5..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSGIO.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#include - -#include "knowhere/index/vector_index/impl/nsg/NSGIO.h" - -namespace milvus { -namespace knowhere { -namespace impl { - -void -write_index(NsgIndex* index, MemoryIOWriter& writer) { - writer(&index->metric_type, sizeof(int32_t), 1); - writer(&index->ntotal, sizeof(index->ntotal), 1); - writer(&index->dimension, sizeof(index->dimension), 1); - writer(&index->navigation_point, sizeof(index->navigation_point), 1); - writer(index->ori_data_, sizeof(float) * index->ntotal * index->dimension, 1); - writer(index->ids_, sizeof(int64_t) * index->ntotal, 1); - - for (unsigned i = 0; i < index->ntotal; ++i) { - auto neighbor_num = (node_t)index->nsg[i].size(); - writer(&neighbor_num, sizeof(node_t), 1); - writer(index->nsg[i].data(), neighbor_num * sizeof(node_t), 1); - } -} - -NsgIndex* -read_index(MemoryIOReader& reader) { - size_t ntotal; - size_t dimension; - int32_t metric; - reader(&metric, sizeof(int32_t), 1); - reader(&ntotal, sizeof(size_t), 1); - reader(&dimension, sizeof(size_t), 1); - auto index = new NsgIndex(dimension, ntotal, (impl::NsgIndex::Metric_Type)metric); - reader(&index->navigation_point, sizeof(index->navigation_point), 1); - - index->ori_data_ = new float[index->ntotal * index->dimension]; - index->ids_ = new int64_t[index->ntotal]; - reader(index->ori_data_, sizeof(float) * index->ntotal * index->dimension, 1); - reader(index->ids_, sizeof(int64_t) * index->ntotal, 1); - - index->nsg.reserve(index->ntotal); - index->nsg.resize(index->ntotal); - node_t neighbor_num; - for (unsigned i = 0; i < index->ntotal; ++i) { - reader(&neighbor_num, sizeof(node_t), 1); - index->nsg[i].reserve(neighbor_num); - index->nsg[i].resize(neighbor_num); - reader(index->nsg[i].data(), neighbor_num * sizeof(node_t), 1); - } - - index->is_trained = true; - return index; -} - -} // namespace impl -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSGIO.h b/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSGIO.h deleted file mode 100644 index e40092d7c8..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/NSGIO.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include "NSG.h" -#include "knowhere/index/vector_index/helpers/FaissIO.h" - -namespace milvus { -namespace knowhere { -namespace impl { - -extern void -write_index(NsgIndex* index, MemoryIOWriter& writer); - -extern NsgIndex* -read_index(MemoryIOReader& reader); - -} // namespace impl -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/Neighbor.h b/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/Neighbor.h deleted file mode 100644 index 6644f8f262..0000000000 --- a/core/src/index/knowhere/knowhere/index/vector_index/impl/nsg/Neighbor.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License - -#pragma once - -#include - -namespace milvus { -namespace knowhere { -namespace impl { - -using node_t = int64_t; - -// TODO: search use simple neighbor -struct Neighbor { - node_t id; // offset of node in origin data - float distance; - bool has_explored; - - Neighbor() = default; - - explicit Neighbor(node_t id, float distance, bool f) : id{id}, distance{distance}, has_explored(f) { - } - - explicit Neighbor(node_t id, float distance) : id{id}, distance{distance}, has_explored(false) { - } - - inline bool - operator<(const Neighbor& other) const { - return distance < other.distance; - } -}; - -typedef std::lock_guard LockGuard; - -} // namespace impl -} // namespace knowhere -} // namespace milvus diff --git a/core/src/index/thirdparty/SPTAG/.github/ISSUE_TEMPLATE/bug_report.md b/core/src/index/thirdparty/SPTAG/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index b735373365..0000000000 --- a/core/src/index/thirdparty/SPTAG/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] - -**Additional context** -Add any other context about the problem here. diff --git a/core/src/index/thirdparty/SPTAG/.github/ISSUE_TEMPLATE/feature_request.md b/core/src/index/thirdparty/SPTAG/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 066b2d920a..0000000000 --- a/core/src/index/thirdparty/SPTAG/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/core/src/index/thirdparty/SPTAG/.gitignore b/core/src/index/thirdparty/SPTAG/.gitignore deleted file mode 100644 index 973785834c..0000000000 --- a/core/src/index/thirdparty/SPTAG/.gitignore +++ /dev/null @@ -1,91 +0,0 @@ -# Prerequisites -*.d - -# Object files -*.o -*.ko -*.obj -*.elf - -# Linker output -*.ilk -*.map -*.exp - -# Precompiled Headers -*.gch -*.pch - -# Libraries -*.lib -*.a -*.la -*.lo - -# Shared objects (inc. Windows DLLs) -*.dll -*.so -*.so.* -*.dylib - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 -*.hex - -# Debug files -*.dSYM/ -*.su -*.idb -*.pdb - -# Kernel Module Compile Results -*.mod* -*.cmd -.tmp_versions/ -modules.order -Module.symvers -Mkfile.old -dkms.conf -/.vs/SPTAG/v14 -/AnnService/x64 -/obj/x64_Release -/PythonWrapper/x64/Release -/x64/Release -/SPTAG.VC.db -/SPTAG.VC.VC.opendb -/AnnService/CoreLibrary.vcxproj.user -/AnnService/IndexBuilder.vcxproj.user -/AnnService/Server.vcxproj.user -/AnnService/SocketLib.vcxproj.user -/PythonWrapper/PythonCore.vcxproj.user -/build -/PythonWrapper/inc/ClientInterface_wrap.cxx -/PythonWrapper/inc/CoreInterface_wrap.cxx -/PythonWrapper/inc/SPTAG.py -/PythonWrapper/inc/SPTAGClient.py -/ipch/TEST-4fb66b42 -/obj/x64_Debug -/x64/Debug -/packages -/Search/Search.vcxproj.user -/AnnService/IndexSearcher.vcxproj.user -/Wrappers/inc/SWIGTYPE_p_RemoteSearchResult.java -/Wrappers/inc/SWIGTYPE_p_QueryResult.java -/Wrappers/inc/SPTAGJNI.java -/Wrappers/inc/SPTAGClientJNI.java -/Wrappers/inc/SPTAGClient.py -/Wrappers/inc/SPTAGClient.java -/Wrappers/inc/SPTAG.py -/Wrappers/inc/SPTAG.java -/Wrappers/inc/CoreInterface_pwrap.cpp -/Wrappers/inc/CoreInterface_jwrap.cpp -/Wrappers/inc/ClientInterface_pwrap.cpp -/Wrappers/inc/ClientInterface_jwrap.cpp -/Wrappers/inc/AnnIndex.java -/Wrappers/inc/AnnClient.java -/AnnService.users - Copy.props -/.vs diff --git a/core/src/index/thirdparty/SPTAG/AnnService.users.props b/core/src/index/thirdparty/SPTAG/AnnService.users.props deleted file mode 100644 index e65231357f..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService.users.props +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - $(SystemVersionDef) %(AdditionalOptions) - - - - - $(SolutionDir)\$(Platform)\$(Configuration)\ - $(SolutionDir)\$(Platform)\$(Configuration)\ - - - - - diff --git a/core/src/index/thirdparty/SPTAG/AnnService/Aggregator.vcxproj b/core/src/index/thirdparty/SPTAG/AnnService/Aggregator.vcxproj deleted file mode 100644 index 4e5514f057..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/Aggregator.vcxproj +++ /dev/null @@ -1,178 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {D7F09A63-BDCA-4F6C-A864-8551D1FE447A} - Aggregator - 8.1 - - - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - - - - - - - - - - - - - - - - - - - $(SolutionDir)obj\$(Platform)_$(Configuration)\$(ProjectName)\ - $(ProjectDir);$(IncludePath) - $(OutAppDir) - $(OutLibDir);$(LibraryPath) - - - false - - - - Level3 - Disabled - true - true - _MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - Guard - ProgramDatabase - - - CoreLibrary.lib;SocketLib.lib;%(AdditionalDependencies) - /guard:cf - %(AdditionalOptions) - - - - - Level3 - Disabled - true - true - - - CoreLibrary.lib;SocketLib.lib;%(AdditionalDependencies) - - - - - Level3 - MaxSpeed - true - true - true - true - - - true - true - CoreLibrary.lib;SocketLib.lib;%(AdditionalDependencies) - - - - - Level3 - MaxSpeed - true - true - true - true - _MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - - - true - true - CoreLibrary.lib;SocketLib.lib;%(AdditionalDependencies) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/Aggregator.vcxproj.filters b/core/src/index/thirdparty/SPTAG/AnnService/Aggregator.vcxproj.filters deleted file mode 100644 index de08faca3f..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/Aggregator.vcxproj.filters +++ /dev/null @@ -1,44 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/CMakeLists.txt b/core/src/index/thirdparty/SPTAG/AnnService/CMakeLists.txt deleted file mode 100644 index fffc5ce426..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -file(GLOB HDR_FILES ${PROJECT_SOURCE_DIR}/AnnService/inc/Core/*.h ${PROJECT_SOURCE_DIR}/AnnService/inc/Core/Common/*.h ${PROJECT_SOURCE_DIR}/AnnService/inc/Core/BKT/*.h ${PROJECT_SOURCE_DIR}/AnnService/inc/Core/KDT/*.h ${PROJECT_SOURCE_DIR}/AnnService/inc/Helper/*.h ${PROJECT_SOURCE_DIR}/AnnService/inc/Helper/VectorSetReaders/*.h) -file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/AnnService/src/Core/*.cpp ${PROJECT_SOURCE_DIR}/AnnService/src/Core/Common/*.cpp ${PROJECT_SOURCE_DIR}/AnnService/src/Core/BKT/*.cpp ${PROJECT_SOURCE_DIR}/AnnService/src/Core/KDT/*.cpp ${PROJECT_SOURCE_DIR}/AnnService/src/Helper/*.cpp ${PROJECT_SOURCE_DIR}/AnnService/src/Helper/VectorSetReaders/*.cpp) - -include_directories(${PROJECT_SOURCE_DIR}/AnnService) - -add_library (SPTAGLib SHARED ${SRC_FILES} ${HDR_FILES}) -target_link_libraries (SPTAGLib) -add_library (SPTAGLibStatic STATIC ${SRC_FILES} ${HDR_FILES}) -set_target_properties(SPTAGLibStatic PROPERTIES OUTPUT_NAME SPTAGLib) - -file(GLOB SERVER_HDR_FILES ${HDR_FILES} ${PROJECT_SOURCE_DIR}/AnnService/inc/Server/*.h ${PROJECT_SOURCE_DIR}/AnnService/inc/Socket/*.h) -file(GLOB SERVER_FILES ${SRC_FILES} ${PROJECT_SOURCE_DIR}/AnnService/src/Server/*.cpp ${PROJECT_SOURCE_DIR}/AnnService/src/Socket/*.cpp) -add_executable (server ${SERVER_FILES} ${SERVER_HDR_FILES}) -target_link_libraries(server ${Boost_LIBRARIES}) - -file(GLOB CLIENT_HDR_FILES ${HDR_FILES} ${PROJECT_SOURCE_DIR}/AnnService/inc/Client/*.h ${PROJECT_SOURCE_DIR}/AnnService/inc/Socket/*.h) -file(GLOB CLIENT_FILES ${SRC_FILES} ${PROJECT_SOURCE_DIR}/AnnService/src/Client/*.cpp ${PROJECT_SOURCE_DIR}/AnnService/src/Socket/*.cpp) -add_executable (client ${CLIENT_FILES} ${CLIENT_HDR_FILES}) -target_link_libraries(client ${Boost_LIBRARIES}) - -file(GLOB AGG_HDR_FILES ${HDR_FILES} ${PROJECT_SOURCE_DIR}/AnnService/inc/Aggregator/*.h ${PROJECT_SOURCE_DIR}/AnnService/inc/Socket/*.h) -file(GLOB AGG_FILES ${SRC_FILES} ${PROJECT_SOURCE_DIR}/AnnService/src/Aggregator/*.cpp ${PROJECT_SOURCE_DIR}/AnnService/src/Socket/*.cpp) -add_executable (aggregator ${AGG_FILES} ${AGG_HDR_FILES}) -target_link_libraries(aggregator ${Boost_LIBRARIES}) - -file(GLOB BUILDER_HDR_FILES ${HDR_FILES} ${PROJECT_SOURCE_DIR}/AnnService/inc/IndexBuilder/*.h) -file(GLOB BUILDER_FILES ${SRC_FILES} ${PROJECT_SOURCE_DIR}/AnnService/src/IndexBuilder/*.cpp) -add_executable (indexbuilder ${BUILDER_FILES} ${BUILDER_HDR_FILES}) -target_link_libraries(indexbuilder ${Boost_LIBRARIES}) - -file(GLOB SEARCHER_FILES ${SRC_FILES} ${PROJECT_SOURCE_DIR}/AnnService/src/IndexSearcher/*.cpp) -add_executable (indexsearcher ${SEARCHER_FILES} ${HDR_FILES}) -target_link_libraries(indexsearcher ${Boost_LIBRARIES}) - -install(TARGETS SPTAGLib SPTAGLibStatic server client aggregator indexbuilder indexsearcher - RUNTIME DESTINATION bin - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib) diff --git a/core/src/index/thirdparty/SPTAG/AnnService/Client.vcxproj b/core/src/index/thirdparty/SPTAG/AnnService/Client.vcxproj deleted file mode 100644 index 34cb9def7a..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/Client.vcxproj +++ /dev/null @@ -1,145 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {A89D70C3-C53B-42DE-A5CE-9A472540F5CB} - Client - 8.1 - - - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - - - - - - - - - - - - - - - - - - - $(SolutionDir)obj\$(Platform)_$(Configuration)\$(ProjectName)\ - $(ProjectDir);$(IncludePath) - $(OutAppDir) - $(OutLibDir);$(LibraryPath) - - - false - - - - CoreLibrary.lib;SocketLib.lib;%(AdditionalDependencies) - - - - - CoreLibrary.lib;SocketLib.lib;%(AdditionalDependencies) - - - - - CoreLibrary.lib;SocketLib.lib;%(AdditionalDependencies) - /guard:cf %(AdditionalOptions) - - - Guard - ProgramDatabase - _MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - - - - - CoreLibrary.lib;SocketLib.lib;%(AdditionalDependencies) - - - _MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - - - - - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/Client.vcxproj.filters b/core/src/index/thirdparty/SPTAG/AnnService/Client.vcxproj.filters deleted file mode 100644 index 31d84bd1d5..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/Client.vcxproj.filters +++ /dev/null @@ -1,32 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/CoreLibrary.vcxproj b/core/src/index/thirdparty/SPTAG/AnnService/CoreLibrary.vcxproj deleted file mode 100644 index 08921f2444..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/CoreLibrary.vcxproj +++ /dev/null @@ -1,192 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} - CoreLibrary - 8.1 - CoreLibrary - - - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - StaticLibrary - true - v140 - MultiByte - - - StaticLibrary - false - v140 - true - MultiByte - - - - - - - - - - - - - - - - - - - - - $(IncludePath);$(ProjectDir) - - - $(SolutionDir)obj\$(Platform)_$(Configuration)\$(ProjectName)\ - $(OutLibDir) - - - - Level3 - Disabled - true - true - /Zc:twoPhase- %(AdditionalOptions) - - - - - Level3 - Disabled - true - true - true - _MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - Guard - ProgramDatabase - - - - - Level3 - MaxSpeed - true - true - true - true - /Zc:twoPhase- %(AdditionalOptions) - - - true - true - - - - - Level3 - MaxSpeed - true - true - true - true - true - _MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - - - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/CoreLibrary.vcxproj.filters b/core/src/index/thirdparty/SPTAG/AnnService/CoreLibrary.vcxproj.filters deleted file mode 100644 index 94f27df3f1..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/CoreLibrary.vcxproj.filters +++ /dev/null @@ -1,202 +0,0 @@ - - - - - {c260e4c4-ec44-4d50-941f-078454da2a89} - - - {c7b9ab49-a99f-4eb4-b8ab-61f0730b9a89} - - - {a306a099-8e3f-433d-b065-9d99433f422e} - - - {33f272c5-907e-4848-bbd7-4340fe44f511} - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {47ec2958-e880-4c1a-b663-04fc48c799af} - - - {10d17e5e-9ad2-4000-96d4-83b616480b97} - - - {23fdeb31-9052-47d1-8edb-9b47c4b02707} - - - {6dff7b24-66ea-40b4-b408-d8fe264a6caa} - - - {b0f1e81d-ca05-426e-bffa-75513a52ca6b} - - - {774592a9-40aa-4342-a4af-b711a1cc4d52} - - - {8fb36afb-73ed-4c3d-8c9b-c3581d80c5d1} - - - {f7bc0bc7-1af5-4870-b8ee-fabdbabdb4c4} - - - {5c1449e0-38b7-4c82-976e-cbdc488d3fb5} - - - - - Header Files\Core - - - Header Files\Core - - - Header Files\Core - - - Header Files\Core - - - Header Files\Core - - - Header Files\Core - - - Header Files\Core\BKT - - - Header Files\Helper - - - Header Files\Helper - - - Header Files\Helper - - - Header Files\Core - - - Header Files\Core - - - Header Files\Helper - - - Header Files\Helper - - - Header Files\Helper - - - Header Files\Core\BKT - - - Header Files\Core\Common - - - Header Files\Core\Common - - - Header Files\Core\Common - - - Header Files\Core\Common - - - Header Files\Core\Common - - - Header Files\Core\Common - - - Header Files\Core\Common - - - Header Files\Core\Common - - - Header Files\Core\KDT - - - Header Files\Core\KDT - - - Header Files\Core\Common - - - Header Files\Core\Common - - - Header Files\Core\Common - - - Header Files\Core\Common - - - Header Files\Core\Common - - - Header Files\Helper - - - Header Files\Helper - - - Header Files\Helper\VectorSetReaders - - - Header Files\Helper - - - - - Source Files\Core - - - Source Files\Core\Common - - - Source Files\Helper - - - Source Files\Helper - - - Source Files\Core - - - Source Files\Core - - - Source Files\Helper - - - Source Files\Helper - - - Source Files\Helper - - - Source Files\Core\BKT - - - Source Files\Core\KDT - - - Source Files\Core\Common - - - Source Files\Helper\VectorSetReaders - - - Source Files\Helper - - - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/IndexBuilder.vcxproj b/core/src/index/thirdparty/SPTAG/AnnService/IndexBuilder.vcxproj deleted file mode 100644 index a5d05fb473..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/IndexBuilder.vcxproj +++ /dev/null @@ -1,173 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {F492F794-E78B-4B1F-A556-5E045B9163D5} - IndexBuilder - 8.1 - - - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - - - - - - - - - - - - - - - - - - $(SolutionDir)obj\$(Platform)_$(Configuration)\$(ProjectName)\ - $(ProjectDir);$(IncludePath) - $(OutAppDir) - $(OutLibDir);$(LibraryPath) - - - false - - - - CoreLibrary.lib;%(AdditionalDependencies) - - - - - Level3 - MaxSpeed - true - true - true - true - _MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - true - - - true - true - - - - - Level3 - Disabled - true - true - - - - - Level3 - Disabled - true - true - _MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - Guard - ProgramDatabase - true - - - /guard:cf %(AdditionalOptions) - - - - - Level3 - MaxSpeed - true - true - true - true - - - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/IndexBuilder.vcxproj.filters b/core/src/index/thirdparty/SPTAG/AnnService/IndexBuilder.vcxproj.filters deleted file mode 100644 index 0733fae1c1..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/IndexBuilder.vcxproj.filters +++ /dev/null @@ -1,32 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/IndexSearcher.vcxproj b/core/src/index/thirdparty/SPTAG/AnnService/IndexSearcher.vcxproj deleted file mode 100644 index 266ac576b3..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/IndexSearcher.vcxproj +++ /dev/null @@ -1,170 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {97615D3B-9FA0-469E-B229-95A91A5087E0} - IndexSearcher - 8.1 - IndexSearcher - - - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - - - - - - - - - - - - - - - - - - $(SolutionDir)obj\$(Platform)_$(Configuration)\$(ProjectName)\ - $(ProjectDir);$(SolutionDir)AnnService\;$(IncludePath) - $(OutLibDir);$(LibraryPath) - $(OutAppDir) - - - false - - - - CoreLibrary.lib;%(AdditionalDependencies) - - - - - Level3 - MaxSpeed - true - true - true - true - _MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - true - - - true - true - - - - - Level3 - Disabled - true - true - - - - - Level3 - Disabled - true - true - _MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - Guard - ProgramDatabase - true - - - /guard:cf %(AdditionalOptions) - - - - - Level3 - MaxSpeed - true - true - true - true - - - true - true - - - - - - - - Designer - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/IndexSearcher.vcxproj.filters b/core/src/index/thirdparty/SPTAG/AnnService/IndexSearcher.vcxproj.filters deleted file mode 100644 index 82f7700c53..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/IndexSearcher.vcxproj.filters +++ /dev/null @@ -1,25 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/Server.vcxproj b/core/src/index/thirdparty/SPTAG/AnnService/Server.vcxproj deleted file mode 100644 index d830f3bc0d..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/Server.vcxproj +++ /dev/null @@ -1,153 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {E28B1222-8BEA-4A92-8FE0-088EBDAA7FE0} - Server - 8.1 - - - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - - - - - - - - - - - - - - - - - - - $(SolutionDir)obj\$(Platform)_$(Configuration)\$(ProjectName)\ - $(ProjectDir);$(IncludePath) - $(OutAppDir) - $(OutLibDir);$(LibraryPath) - - - false - - - - CoreLibrary.lib;SocketLib.lib;%(AdditionalDependencies) - - - - - CoreLibrary.lib;SocketLib.lib;%(AdditionalDependencies) - - - - - CoreLibrary.lib;SocketLib.lib;%(AdditionalDependencies) - /guard:cf %(AdditionalOptions) - - - _MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - Guard - ProgramDatabase - - - - - CoreLibrary.lib;SocketLib.lib;%(AdditionalDependencies) - - - _MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/Server.vcxproj.filters b/core/src/index/thirdparty/SPTAG/AnnService/Server.vcxproj.filters deleted file mode 100644 index da95d13f40..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/Server.vcxproj.filters +++ /dev/null @@ -1,56 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/SocketLib.vcxproj b/core/src/index/thirdparty/SPTAG/AnnService/SocketLib.vcxproj deleted file mode 100644 index 84f968fa0a..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/SocketLib.vcxproj +++ /dev/null @@ -1,123 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {F9A72303-6381-4C80-86FF-606A2F6F7B96} - SocketLib - 8.1 - - - - StaticLibrary - true - v140 - MultiByte - - - StaticLibrary - false - v140 - true - MultiByte - - - StaticLibrary - true - v140 - MultiByte - - - StaticLibrary - false - v140 - true - MultiByte - - - - - - - - - - - - - - - - - - - - - - $(ProjectDir);$(IncludePath) - $(SolutionDir)obj\$(Platform)_$(Configuration)\$(ProjectName)\ - $(OutLibDir)\ - - - - Guard - ProgramDatabase - _MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - - - - - _MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/SocketLib.vcxproj.filters b/core/src/index/thirdparty/SPTAG/AnnService/SocketLib.vcxproj.filters deleted file mode 100644 index 5aa20f3bbf..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/SocketLib.vcxproj.filters +++ /dev/null @@ -1,65 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Aggregator/AggregatorContext.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Aggregator/AggregatorContext.h deleted file mode 100644 index 60c86137be..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Aggregator/AggregatorContext.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_AGGREGATOR_AGGREGATORCONTEXT_H_ -#define _SPTAG_AGGREGATOR_AGGREGATORCONTEXT_H_ - -#include "inc/Socket/Common.h" -#include "AggregatorSettings.h" - -#include -#include -#include - -namespace SPTAG -{ -namespace Aggregator -{ - -enum RemoteMachineStatus : uint8_t -{ - Disconnected = 0, - - Connecting, - - Connected -}; - - -struct RemoteMachine -{ - RemoteMachine(); - - std::string m_address; - - std::string m_port; - - Socket::ConnectionID m_connectionID; - - std::atomic m_status; -}; - -class AggregatorContext -{ -public: - AggregatorContext(const std::string& p_filePath); - - ~AggregatorContext(); - - bool IsInitialized() const; - - const std::vector>& GetRemoteServers() const; - - const std::shared_ptr& GetSettings() const; - -private: - std::vector> m_remoteServers; - - std::shared_ptr m_settings; - - bool m_initialized; -}; - -} // namespace Aggregator -} // namespace AnnService - - -#endif // _SPTAG_AGGREGATOR_AGGREGATORCONTEXT_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Aggregator/AggregatorExecutionContext.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Aggregator/AggregatorExecutionContext.h deleted file mode 100644 index 12948a218a..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Aggregator/AggregatorExecutionContext.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_AGGREGATOR_AGGREGATOREXECUTIONCONTEXT_H_ -#define _SPTAG_AGGREGATOR_AGGREGATOREXECUTIONCONTEXT_H_ - -#include "inc/Socket/RemoteSearchQuery.h" -#include "inc/Socket/Packet.h" - -#include -#include - -namespace SPTAG -{ -namespace Aggregator -{ - -typedef std::shared_ptr AggregatorResult; - -class AggregatorExecutionContext -{ -public: - AggregatorExecutionContext(std::size_t p_totalServerNumber, - Socket::PacketHeader p_requestHeader); - - ~AggregatorExecutionContext(); - - std::size_t GetServerNumber() const; - - AggregatorResult& GetResult(std::size_t p_num); - - const Socket::PacketHeader& GetRequestHeader() const; - - bool IsCompletedAfterFinsh(std::uint32_t p_finishedCount); - -private: - std::atomic m_unfinishedCount; - - std::vector m_results; - - Socket::PacketHeader m_requestHeader; - -}; - - - - -} // namespace Aggregator -} // namespace AnnService - - -#endif // _SPTAG_AGGREGATOR_AGGREGATOREXECUTIONCONTEXT_H_ - diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Aggregator/AggregatorService.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Aggregator/AggregatorService.h deleted file mode 100644 index 4d864aa5f0..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Aggregator/AggregatorService.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_AGGREGATOR_AGGREGATORSERVICE_H_ -#define _SPTAG_AGGREGATOR_AGGREGATORSERVICE_H_ - -#include "AggregatorContext.h" -#include "AggregatorExecutionContext.h" -#include "inc/Socket/Server.h" -#include "inc/Socket/Client.h" -#include "inc/Socket/ResourceManager.h" - -#include - -#include -#include -#include -#include - -namespace SPTAG -{ -namespace Aggregator -{ - -class AggregatorService -{ -public: - AggregatorService(); - - ~AggregatorService(); - - bool Initialize(); - - void Run(); - -private: - - void StartClient(); - - void StartListen(); - - void WaitForShutdown(); - - void ConnectToPendingServers(); - - void AddToPendingServers(std::shared_ptr p_remoteServer); - - void SearchRequestHanlder(Socket::ConnectionID p_localConnectionID, Socket::Packet p_packet); - - void SearchResponseHanlder(Socket::ConnectionID p_localConnectionID, Socket::Packet p_packet); - - void AggregateResults(std::shared_ptr p_exectionContext); - - std::shared_ptr GetContext(); - -private: - typedef std::function AggregatorCallback; - - std::shared_ptr m_aggregatorContext; - - std::shared_ptr m_socketServer; - - std::shared_ptr m_socketClient; - - bool m_initalized; - - std::unique_ptr m_threadPool; - - boost::asio::io_context m_ioContext; - - boost::asio::signal_set m_shutdownSignals; - - std::vector> m_pendingConnectServers; - - std::mutex m_pendingConnectServersMutex; - - boost::asio::deadline_timer m_pendingConnectServersTimer; - - Socket::ResourceManager m_aggregatorCallbackManager; -}; - - - -} // namespace Aggregator -} // namespace AnnService - - -#endif // _SPTAG_AGGREGATOR_AGGREGATORSERVICE_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Aggregator/AggregatorSettings.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Aggregator/AggregatorSettings.h deleted file mode 100644 index cb1e9fe7f7..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Aggregator/AggregatorSettings.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_AGGREGATOR_AGGREGATORSETTINGS_H_ -#define _SPTAG_AGGREGATOR_AGGREGATORSETTINGS_H_ - -#include "../Core/Common.h" - -#include - -namespace SPTAG -{ -namespace Aggregator -{ - -struct AggregatorSettings -{ - AggregatorSettings(); - - std::string m_listenAddr; - - std::string m_listenPort; - - std::uint32_t m_searchTimeout; - - SizeType m_threadNum; - - SizeType m_socketThreadNum; -}; - - - - -} // namespace Aggregator -} // namespace AnnService - - -#endif // _SPTAG_AGGREGATOR_AGGREGATORSETTINGS_H_ - diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Client/ClientWrapper.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Client/ClientWrapper.h deleted file mode 100644 index d96a67061e..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Client/ClientWrapper.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_CLIENT_CLIENTWRAPPER_H_ -#define _SPTAG_CLIENT_CLIENTWRAPPER_H_ - -#include "inc/Socket/Client.h" -#include "inc/Socket/RemoteSearchQuery.h" -#include "inc/Socket/ResourceManager.h" -#include "Options.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace SPTAG -{ -namespace Client -{ - -class ClientWrapper -{ -public: - typedef std::function Callback; - - ClientWrapper(const ClientOptions& p_options); - - ~ClientWrapper(); - - void SendQueryAsync(const Socket::RemoteQuery& p_query, - Callback p_callback, - const ClientOptions& p_options); - - void WaitAllFinished(); - - bool IsAvailable() const; - -private: - typedef std::pair ConnectionPair; - - Socket::PacketHandlerMapPtr GetHandlerMap(); - - void DecreaseUnfnishedJobCount(); - - const ConnectionPair& GetConnection(); - - void SearchResponseHanlder(Socket::ConnectionID p_localConnectionID, Socket::Packet p_packet); - - void HandleDeadConnection(Socket::ConnectionID p_cid); - -private: - ClientOptions m_options; - - std::unique_ptr m_client; - - std::atomic m_unfinishedJobCount; - - std::atomic_bool m_isWaitingFinish; - - std::condition_variable m_waitingQueue; - - std::mutex m_waitingMutex; - - std::vector m_connections; - - std::atomic m_spinCountOfConnection; - - Socket::ResourceManager m_callbackManager; -}; - - -} // namespace Socket -} // namespace SPTAG - -#endif // _SPTAG_CLIENT_OPTIONS_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Client/Options.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Client/Options.h deleted file mode 100644 index 062061f042..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Client/Options.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_CLIENT_OPTIONS_H_ -#define _SPTAG_CLIENT_OPTIONS_H_ - -#include "inc/Helper/ArgumentsParser.h" - -#include -#include -#include - -namespace SPTAG -{ -namespace Client -{ - -class ClientOptions : public Helper::ArgumentsParser -{ -public: - ClientOptions(); - - virtual ~ClientOptions(); - - std::string m_serverAddr; - - std::string m_serverPort; - - // in milliseconds. - std::uint32_t m_searchTimeout; - - std::uint32_t m_threadNum; - - std::uint32_t m_socketThreadNum; - -}; - - -} // namespace Socket -} // namespace SPTAG - -#endif // _SPTAG_CLIENT_OPTIONS_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/BKT/Index.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/BKT/Index.h deleted file mode 100644 index e4c52586c7..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/BKT/Index.h +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_BKT_INDEX_H_ -#define _SPTAG_BKT_INDEX_H_ - -#include "../Common.h" -#include "../VectorIndex.h" - -#include "../Common/CommonUtils.h" -#include "../Common/DistanceUtils.h" -#include "../Common/QueryResultSet.h" -#include "../Common/Dataset.h" -#include "../Common/WorkSpace.h" -#include "../Common/WorkSpacePool.h" -#include "../Common/RelativeNeighborhoodGraph.h" -#include "../Common/BKTree.h" -#include "inc/Helper/ConcurrentSet.h" -#include "inc/Helper/SimpleIniReader.h" -#include "inc/Helper/StringConvert.h" - -#include -#include - -namespace SPTAG -{ - - namespace Helper - { - class IniReader; - } - - namespace BKT - { - template - class Index : public VectorIndex - { - private: - // data points - COMMON::Dataset m_pSamples; - - // BKT structures. - COMMON::BKTree m_pTrees; - - // Graph structure - COMMON::RelativeNeighborhoodGraph m_pGraph; - - std::string m_sBKTFilename; - std::string m_sGraphFilename; - std::string m_sDataPointsFilename; - std::string m_sDeleteDataPointsFilename; - - std::mutex m_dataAddLock; // protect data and graph - Helper::Concurrent::ConcurrentSet m_deletedID; - float m_fDeletePercentageForRefine; - std::unique_ptr m_workSpacePool; - - int m_iNumberOfThreads; - DistCalcMethod m_iDistCalcMethod; - float(*m_fComputeDistance)(const T* pX, const T* pY, DimensionType length); - - int m_iMaxCheck; - int m_iThresholdOfNumberOfContinuousNoBetterPropagation; - int m_iNumberOfInitialDynamicPivots; - int m_iNumberOfOtherDynamicPivots; - public: - Index() - { -#define DefineBKTParameter(VarName, VarType, DefaultValue, RepresentStr) \ - VarName = DefaultValue; \ - -#include "inc/Core/BKT/ParameterDefinitionList.h" -#undef DefineBKTParameter - - m_pSamples.SetName("Vector"); - m_fComputeDistance = COMMON::DistanceCalcSelector(m_iDistCalcMethod); - } - - ~Index() {} - - inline SizeType GetNumSamples() const { return m_pSamples.R(); } - inline SizeType GetIndexSize() const { return sizeof(*this); } - inline DimensionType GetFeatureDim() const { return m_pSamples.C(); } - - inline int GetCurrMaxCheck() const { return m_iMaxCheck; } - inline int GetNumThreads() const { return m_iNumberOfThreads; } - inline DistCalcMethod GetDistCalcMethod() const { return m_iDistCalcMethod; } - inline IndexAlgoType GetIndexAlgoType() const { return IndexAlgoType::BKT; } - inline VectorValueType GetVectorValueType() const { return GetEnumValueType(); } - - inline float ComputeDistance(const void* pX, const void* pY) const { return m_fComputeDistance((const T*)pX, (const T*)pY, m_pSamples.C()); } - inline const void* GetSample(const SizeType idx) const { return (void*)m_pSamples[idx]; } - inline bool ContainSample(const SizeType idx) const { return !m_deletedID.contains(idx); } - inline bool NeedRefine() const { return m_deletedID.size() >= (size_t)(GetNumSamples() * m_fDeletePercentageForRefine); } - std::shared_ptr> BufferSize() const - { - std::shared_ptr> buffersize(new std::vector); - buffersize->push_back(m_pSamples.BufferSize()); - buffersize->push_back(m_pTrees.BufferSize()); - buffersize->push_back(m_pGraph.BufferSize()); - buffersize->push_back(m_deletedID.bufferSize()); - return std::move(buffersize); - } - - ErrorCode SaveConfig(std::ostream& p_configout) const; - ErrorCode SaveIndexData(const std::string& p_folderPath); - ErrorCode SaveIndexData(const std::vector& p_indexStreams); - - ErrorCode LoadConfig(Helper::IniReader& p_reader); - ErrorCode LoadIndexData(const std::string& p_folderPath); - ErrorCode LoadIndexDataFromMemory(const std::vector& p_indexBlobs); - - ErrorCode BuildIndex(const void* p_data, SizeType p_vectorNum, DimensionType p_dimension); - ErrorCode SearchIndex(QueryResult &p_query) const; - ErrorCode AddIndex(const void* p_vectors, SizeType p_vectorNum, DimensionType p_dimension, SizeType* p_start = nullptr); - ErrorCode DeleteIndex(const void* p_vectors, SizeType p_vectorNum); - ErrorCode DeleteIndex(const SizeType& p_id); - - ErrorCode SetParameter(const char* p_param, const char* p_value); - std::string GetParameter(const char* p_param) const; - - ErrorCode RefineIndex(const std::string& p_folderPath); - ErrorCode RefineIndex(const std::vector& p_indexStreams); - - private: - void SearchIndexWithDeleted(COMMON::QueryResultSet &p_query, COMMON::WorkSpace &p_space, const Helper::Concurrent::ConcurrentSet &p_deleted) const; - void SearchIndexWithoutDeleted(COMMON::QueryResultSet &p_query, COMMON::WorkSpace &p_space) const; - }; - } // namespace BKT -} // namespace SPTAG - -#endif // _SPTAG_BKT_INDEX_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/BKT/ParameterDefinitionList.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/BKT/ParameterDefinitionList.h deleted file mode 100644 index 3f6f9e0222..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/BKT/ParameterDefinitionList.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifdef DefineBKTParameter - -// DefineBKTParameter(VarName, VarType, DefaultValue, RepresentStr) -DefineBKTParameter(m_sBKTFilename, std::string, std::string("tree.bin"), "TreeFilePath") -DefineBKTParameter(m_sGraphFilename, std::string, std::string("graph.bin"), "GraphFilePath") -DefineBKTParameter(m_sDataPointsFilename, std::string, std::string("vectors.bin"), "VectorFilePath") -DefineBKTParameter(m_sDeleteDataPointsFilename, std::string, std::string("deletes.bin"), "DeleteVectorFilePath") - -DefineBKTParameter(m_pTrees.m_iTreeNumber, int, 1L, "BKTNumber") -DefineBKTParameter(m_pTrees.m_iBKTKmeansK, int, 32L, "BKTKmeansK") -DefineBKTParameter(m_pTrees.m_iBKTLeafSize, int, 8L, "BKTLeafSize") -DefineBKTParameter(m_pTrees.m_iSamples, int, 1000L, "Samples") - - -DefineBKTParameter(m_pGraph.m_iTPTNumber, int, 32L, "TPTNumber") -DefineBKTParameter(m_pGraph.m_iTPTLeafSize, int, 2000L, "TPTLeafSize") -DefineBKTParameter(m_pGraph.m_numTopDimensionTPTSplit, int, 5L, "NumTopDimensionTpTreeSplit") - -DefineBKTParameter(m_pGraph.m_iNeighborhoodSize, DimensionType, 32L, "NeighborhoodSize") -DefineBKTParameter(m_pGraph.m_iNeighborhoodScale, int, 2L, "GraphNeighborhoodScale") -DefineBKTParameter(m_pGraph.m_iCEFScale, int, 2L, "GraphCEFScale") -DefineBKTParameter(m_pGraph.m_iRefineIter, int, 0L, "RefineIterations") -DefineBKTParameter(m_pGraph.m_iCEF, int, 1000L, "CEF") -DefineBKTParameter(m_pGraph.m_iMaxCheckForRefineGraph, int, 10000L, "MaxCheckForRefineGraph") - -DefineBKTParameter(m_iNumberOfThreads, int, 1L, "NumberOfThreads") -DefineBKTParameter(m_iDistCalcMethod, SPTAG::DistCalcMethod, SPTAG::DistCalcMethod::Cosine, "DistCalcMethod") - -DefineBKTParameter(m_fDeletePercentageForRefine, float, 0.4F, "DeletePercentageForRefine") -DefineBKTParameter(m_iMaxCheck, int, 8192L, "MaxCheck") -DefineBKTParameter(m_iThresholdOfNumberOfContinuousNoBetterPropagation, int, 3L, "ThresholdOfNumberOfContinuousNoBetterPropagation") -DefineBKTParameter(m_iNumberOfInitialDynamicPivots, int, 50L, "NumberOfInitialDynamicPivots") -DefineBKTParameter(m_iNumberOfOtherDynamicPivots, int, 4L, "NumberOfOtherDynamicPivots") - -#endif diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common.h deleted file mode 100644 index 02182a4bf2..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common.h +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_CORE_COMMONDEFS_H_ -#define _SPTAG_CORE_COMMONDEFS_H_ - -#include -#include -#include -#include -#include -#include -#include - -#ifndef _MSC_VER -#include -#include -#define FolderSep '/' -#define mkdir(a) mkdir(a, ACCESSPERMS) -inline bool direxists(const char* path) { - struct stat info; - return stat(path, &info) == 0 && (info.st_mode & S_IFDIR); -} -inline bool fileexists(const char* path) { - struct stat info; - return stat(path, &info) == 0 && (info.st_mode & S_IFDIR) == 0; -} -template -inline T min(T a, T b) { - return a < b ? a : b; -} -template -inline T max(T a, T b) { - return a > b ? a : b; -} - -#ifndef _rotl -#define _rotl(x, n) (((x) << (n)) | ((x) >> (32-(n)))) -#endif - -#else -#define WIN32_LEAN_AND_MEAN -#include -#include -#define FolderSep '\\' -#define mkdir(a) CreateDirectory(a, NULL) -inline bool direxists(const char* path) { - auto dwAttr = GetFileAttributes((LPCSTR)path); - return (dwAttr != INVALID_FILE_ATTRIBUTES) && (dwAttr & FILE_ATTRIBUTE_DIRECTORY); -} -inline bool fileexists(const char* path) { - auto dwAttr = GetFileAttributes((LPCSTR)path); - return (dwAttr != INVALID_FILE_ATTRIBUTES) && (dwAttr & FILE_ATTRIBUTE_DIRECTORY) == 0; -} -#endif - -namespace SPTAG -{ -typedef std::int32_t SizeType; -typedef std::int32_t DimensionType; - -const SizeType MaxSize = (std::numeric_limits::max)(); -const float MinDist = (std::numeric_limits::min)(); -const float MaxDist = (std::numeric_limits::max)(); -const float Epsilon = 0.000000001f; - -class MyException : public std::exception -{ -private: - std::string Exp; -public: - MyException(std::string e) { Exp = e; } -#ifdef _MSC_VER - const char* what() const { return Exp.c_str(); } -#else - const char* what() const noexcept { return Exp.c_str(); } -#endif -}; - -enum class ErrorCode : std::uint16_t -{ -#define DefineErrorCode(Name, Value) Name = Value, -#include "DefinitionList.h" -#undef DefineErrorCode - - Undefined -}; -static_assert(static_cast(ErrorCode::Undefined) != 0, "Empty ErrorCode!"); - - -enum class DistCalcMethod : std::uint8_t -{ -#define DefineDistCalcMethod(Name) Name, -#include "DefinitionList.h" -#undef DefineDistCalcMethod - - Undefined -}; -static_assert(static_cast(DistCalcMethod::Undefined) != 0, "Empty DistCalcMethod!"); - - -enum class VectorValueType : std::uint8_t -{ -#define DefineVectorValueType(Name, Type) Name, -#include "DefinitionList.h" -#undef DefineVectorValueType - - Undefined -}; -static_assert(static_cast(VectorValueType::Undefined) != 0, "Empty VectorValueType!"); - - -enum class IndexAlgoType : std::uint8_t -{ -#define DefineIndexAlgo(Name) Name, -#include "DefinitionList.h" -#undef DefineIndexAlgo - - Undefined -}; -static_assert(static_cast(IndexAlgoType::Undefined) != 0, "Empty IndexAlgoType!"); - - -template -constexpr VectorValueType GetEnumValueType() -{ - return VectorValueType::Undefined; -} - - -#define DefineVectorValueType(Name, Type) \ -template<> \ -constexpr VectorValueType GetEnumValueType() \ -{ \ - return VectorValueType::Name; \ -} \ - -#include "DefinitionList.h" -#undef DefineVectorValueType - - -inline std::size_t GetValueTypeSize(VectorValueType p_valueType) -{ - switch (p_valueType) - { -#define DefineVectorValueType(Name, Type) \ - case VectorValueType::Name: \ - return sizeof(Type); \ - -#include "DefinitionList.h" -#undef DefineVectorValueType - - default: - break; - } - - return 0; -} - -} // namespace SPTAG - -#endif // _SPTAG_CORE_COMMONDEFS_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/BKTree.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/BKTree.h deleted file mode 100644 index 56583be164..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/BKTree.h +++ /dev/null @@ -1,490 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_COMMON_BKTREE_H_ -#define _SPTAG_COMMON_BKTREE_H_ - -#include -#include -#include -#include - -#include "../VectorIndex.h" - -#include "CommonUtils.h" -#include "QueryResultSet.h" -#include "WorkSpace.h" - -#pragma warning(disable:4996) // 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. - -namespace SPTAG -{ - namespace COMMON - { - // node type for storing BKT - struct BKTNode - { - SizeType centerid; - SizeType childStart; - SizeType childEnd; - - BKTNode(SizeType cid = -1) : centerid(cid), childStart(-1), childEnd(-1) {} - }; - - template - struct KmeansArgs { - int _K; - DimensionType _D; - int _T; - T* centers; - SizeType* counts; - float* newCenters; - SizeType* newCounts; - int* label; - SizeType* clusterIdx; - float* clusterDist; - T* newTCenters; - - KmeansArgs(int k, DimensionType dim, SizeType datasize, int threadnum) : _K(k), _D(dim), _T(threadnum) { - centers = new T[k * dim]; - counts = new SizeType[k]; - newCenters = new float[threadnum * k * dim]; - newCounts = new SizeType[threadnum * k]; - label = new int[datasize]; - clusterIdx = new SizeType[threadnum * k]; - clusterDist = new float[threadnum * k]; - newTCenters = new T[k * dim]; - } - - ~KmeansArgs() { - delete[] centers; - delete[] counts; - delete[] newCenters; - delete[] newCounts; - delete[] label; - delete[] clusterIdx; - delete[] clusterDist; - delete[] newTCenters; - } - - inline void ClearCounts() { - memset(newCounts, 0, sizeof(SizeType) * _T * _K); - } - - inline void ClearCenters() { - memset(newCenters, 0, sizeof(float) * _T * _K * _D); - } - - inline void ClearDists(float dist) { - for (int i = 0; i < _T * _K; i++) { - clusterIdx[i] = -1; - clusterDist[i] = dist; - } - } - - void Shuffle(std::vector& indices, SizeType first, SizeType last) { - SizeType* pos = new SizeType[_K]; - pos[0] = first; - for (int k = 1; k < _K; k++) pos[k] = pos[k - 1] + newCounts[k - 1]; - - for (int k = 0; k < _K; k++) { - if (newCounts[k] == 0) continue; - SizeType i = pos[k]; - while (newCounts[k] > 0) { - SizeType swapid = pos[label[i]] + newCounts[label[i]] - 1; - newCounts[label[i]]--; - std::swap(indices[i], indices[swapid]); - std::swap(label[i], label[swapid]); - } - while (indices[i] != clusterIdx[k]) i++; - std::swap(indices[i], indices[pos[k] + counts[k] - 1]); - } - delete[] pos; - } - }; - - class BKTree - { - public: - BKTree(): m_iTreeNumber(1), m_iBKTKmeansK(32), m_iBKTLeafSize(8), m_iSamples(1000) {} - - BKTree(BKTree& other): m_iTreeNumber(other.m_iTreeNumber), - m_iBKTKmeansK(other.m_iBKTKmeansK), - m_iBKTLeafSize(other.m_iBKTLeafSize), - m_iSamples(other.m_iSamples) {} - ~BKTree() {} - - inline const BKTNode& operator[](SizeType index) const { return m_pTreeRoots[index]; } - inline BKTNode& operator[](SizeType index) { return m_pTreeRoots[index]; } - - inline SizeType size() const { return (SizeType)m_pTreeRoots.size(); } - - inline const std::unordered_map& GetSampleMap() const { return m_pSampleCenterMap; } - - template - void BuildTrees(VectorIndex* index, std::vector* indices = nullptr) - { - struct BKTStackItem { - SizeType index, first, last; - BKTStackItem(SizeType index_, SizeType first_, SizeType last_) : index(index_), first(first_), last(last_) {} - }; - std::stack ss; - - std::vector localindices; - if (indices == nullptr) { - localindices.resize(index->GetNumSamples()); - for (SizeType i = 0; i < index->GetNumSamples(); i++) localindices[i] = i; - } - else { - localindices.assign(indices->begin(), indices->end()); - } - KmeansArgs args(m_iBKTKmeansK, index->GetFeatureDim(), (SizeType)localindices.size(), omp_get_num_threads()); - - m_pSampleCenterMap.clear(); - for (char i = 0; i < m_iTreeNumber; i++) - { - std::random_shuffle(localindices.begin(), localindices.end()); - - m_pTreeStart.push_back((SizeType)m_pTreeRoots.size()); - m_pTreeRoots.push_back(BKTNode((SizeType)localindices.size())); - std::cout << "Start to build BKTree " << i + 1 << std::endl; - - ss.push(BKTStackItem(m_pTreeStart[i], 0, (SizeType)localindices.size())); - while (!ss.empty()) { - BKTStackItem item = ss.top(); ss.pop(); - SizeType newBKTid = (SizeType)m_pTreeRoots.size(); - m_pTreeRoots[item.index].childStart = newBKTid; - if (item.last - item.first <= m_iBKTLeafSize) { - for (SizeType j = item.first; j < item.last; j++) { - m_pTreeRoots.push_back(BKTNode(localindices[j])); - } - } - else { // clustering the data into BKTKmeansK clusters - int numClusters = KmeansClustering(index, localindices, item.first, item.last, args); - if (numClusters <= 1) { - SizeType end = min(item.last + 1, (SizeType)localindices.size()); - std::sort(localindices.begin() + item.first, localindices.begin() + end); - m_pTreeRoots[item.index].centerid = localindices[item.first]; - m_pTreeRoots[item.index].childStart = -m_pTreeRoots[item.index].childStart; - for (SizeType j = item.first + 1; j < end; j++) { - m_pTreeRoots.push_back(BKTNode(localindices[j])); - m_pSampleCenterMap[localindices[j]] = m_pTreeRoots[item.index].centerid; - } - m_pSampleCenterMap[-1 - m_pTreeRoots[item.index].centerid] = item.index; - } - else { - for (int k = 0; k < m_iBKTKmeansK; k++) { - if (args.counts[k] == 0) continue; - m_pTreeRoots.push_back(BKTNode(localindices[item.first + args.counts[k] - 1])); - if (args.counts[k] > 1) ss.push(BKTStackItem(newBKTid++, item.first, item.first + args.counts[k] - 1)); - item.first += args.counts[k]; - } - } - } - m_pTreeRoots[item.index].childEnd = (SizeType)m_pTreeRoots.size(); - } - std::cout << i + 1 << " BKTree built, " << m_pTreeRoots.size() - m_pTreeStart[i] << " " << localindices.size() << std::endl; - } - } - - inline std::uint64_t BufferSize() const - { - return sizeof(int) + sizeof(SizeType) * m_iTreeNumber + - sizeof(SizeType) + sizeof(BKTNode) * m_pTreeRoots.size(); - } - - bool SaveTrees(std::ostream& p_outstream) const - { - p_outstream.write((char*)&m_iTreeNumber, sizeof(int)); - p_outstream.write((char*)m_pTreeStart.data(), sizeof(SizeType) * m_iTreeNumber); - SizeType treeNodeSize = (SizeType)m_pTreeRoots.size(); - p_outstream.write((char*)&treeNodeSize, sizeof(SizeType)); - p_outstream.write((char*)m_pTreeRoots.data(), sizeof(BKTNode) * treeNodeSize); - std::cout << "Save BKT (" << m_iTreeNumber << "," << treeNodeSize << ") Finish!" << std::endl; - return true; - } - - bool SaveTrees(std::string sTreeFileName) const - { - std::cout << "Save BKT to " << sTreeFileName << std::endl; - std::ofstream output(sTreeFileName, std::ios::binary); - if (!output.is_open()) return false; - SaveTrees(output); - output.close(); - return true; - } - - bool LoadTrees(char* pBKTMemFile) - { - m_iTreeNumber = *((int*)pBKTMemFile); - pBKTMemFile += sizeof(int); - m_pTreeStart.resize(m_iTreeNumber); - memcpy(m_pTreeStart.data(), pBKTMemFile, sizeof(SizeType) * m_iTreeNumber); - pBKTMemFile += sizeof(SizeType)*m_iTreeNumber; - - SizeType treeNodeSize = *((SizeType*)pBKTMemFile); - pBKTMemFile += sizeof(SizeType); - m_pTreeRoots.resize(treeNodeSize); - memcpy(m_pTreeRoots.data(), pBKTMemFile, sizeof(BKTNode) * treeNodeSize); - std::cout << "Load BKT (" << m_iTreeNumber << "," << treeNodeSize << ") Finish!" << std::endl; - return true; - } - - bool LoadTrees(std::string sTreeFileName) - { - std::cout << "Load BKT From " << sTreeFileName << std::endl; - std::ifstream input(sTreeFileName, std::ios::binary); - if (!input.is_open()) return false; - - input.read((char*)&m_iTreeNumber, sizeof(int)); - m_pTreeStart.resize(m_iTreeNumber); - input.read((char*)m_pTreeStart.data(), sizeof(SizeType) * m_iTreeNumber); - - SizeType treeNodeSize; - input.read((char*)&treeNodeSize, sizeof(SizeType)); - m_pTreeRoots.resize(treeNodeSize); - input.read((char*)m_pTreeRoots.data(), sizeof(BKTNode) * treeNodeSize); - input.close(); - std::cout << "Load BKT (" << m_iTreeNumber << "," << treeNodeSize << ") Finish!" << std::endl; - return true; - } - - template - void InitSearchTrees(const VectorIndex* p_index, const COMMON::QueryResultSet &p_query, COMMON::WorkSpace &p_space) const - { - for (char i = 0; i < m_iTreeNumber; i++) { - const BKTNode& node = m_pTreeRoots[m_pTreeStart[i]]; - if (node.childStart < 0) { - p_space.m_SPTQueue.insert(COMMON::HeapCell(m_pTreeStart[i], p_index->ComputeDistance((const void*)p_query.GetTarget(), p_index->GetSample(node.centerid)))); - } - else { - for (SizeType begin = node.childStart; begin < node.childEnd; begin++) { - SizeType index = m_pTreeRoots[begin].centerid; - p_space.m_SPTQueue.insert(COMMON::HeapCell(begin, p_index->ComputeDistance((const void*)p_query.GetTarget(), p_index->GetSample(index)))); - } - } - } - } - - template - void SearchTrees(const VectorIndex* p_index, const COMMON::QueryResultSet &p_query, - COMMON::WorkSpace &p_space, const int p_limits) const - { - do - { - COMMON::HeapCell bcell = p_space.m_SPTQueue.pop(); - const BKTNode& tnode = m_pTreeRoots[bcell.node]; - if (tnode.childStart < 0) { - if (!p_space.CheckAndSet(tnode.centerid)) { - p_space.m_iNumberOfCheckedLeaves++; - p_space.m_NGQueue.insert(COMMON::HeapCell(tnode.centerid, bcell.distance)); - } - if (p_space.m_iNumberOfCheckedLeaves >= p_limits) break; - } - else { - if (!p_space.CheckAndSet(tnode.centerid)) { - p_space.m_NGQueue.insert(COMMON::HeapCell(tnode.centerid, bcell.distance)); - } - for (SizeType begin = tnode.childStart; begin < tnode.childEnd; begin++) { - SizeType index = m_pTreeRoots[begin].centerid; - p_space.m_SPTQueue.insert(COMMON::HeapCell(begin, p_index->ComputeDistance((const void*)p_query.GetTarget(), p_index->GetSample(index)))); - } - } - } while (!p_space.m_SPTQueue.empty()); - } - - private: - - template - float KmeansAssign(VectorIndex* p_index, - std::vector& indices, - const SizeType first, const SizeType last, KmeansArgs& args, const bool updateCenters) const { - float currDist = 0; - int threads = omp_get_num_threads(); - float lambda = (updateCenters) ? COMMON::Utils::GetBase() * COMMON::Utils::GetBase() / (100.0f * (last - first)) : 0.0f; - SizeType subsize = (last - first - 1) / threads + 1; - -#pragma omp parallel for - for (int tid = 0; tid < threads; tid++) - { - SizeType istart = first + tid * subsize; - SizeType iend = min(first + (tid + 1) * subsize, last); - SizeType *inewCounts = args.newCounts + tid * m_iBKTKmeansK; - float *inewCenters = args.newCenters + tid * m_iBKTKmeansK * p_index->GetFeatureDim(); - SizeType * iclusterIdx = args.clusterIdx + tid * m_iBKTKmeansK; - float * iclusterDist = args.clusterDist + tid * m_iBKTKmeansK; - float idist = 0; - for (SizeType i = istart; i < iend; i++) { - int clusterid = 0; - float smallestDist = MaxDist; - for (int k = 0; k < m_iBKTKmeansK; k++) { - float dist = p_index->ComputeDistance(p_index->GetSample(indices[i]), (const void*)(args.centers + k*p_index->GetFeatureDim())) + lambda*args.counts[k]; - if (dist > -MaxDist && dist < smallestDist) { - clusterid = k; smallestDist = dist; - } - } - args.label[i] = clusterid; - inewCounts[clusterid]++; - idist += smallestDist; - if (updateCenters) { - const T* v = (const T*)p_index->GetSample(indices[i]); - float* center = inewCenters + clusterid*p_index->GetFeatureDim(); - for (DimensionType j = 0; j < p_index->GetFeatureDim(); j++) center[j] += v[j]; - if (smallestDist > iclusterDist[clusterid]) { - iclusterDist[clusterid] = smallestDist; - iclusterIdx[clusterid] = indices[i]; - } - } - else { - if (smallestDist <= iclusterDist[clusterid]) { - iclusterDist[clusterid] = smallestDist; - iclusterIdx[clusterid] = indices[i]; - } - } - } - COMMON::Utils::atomic_float_add(&currDist, idist); - } - - for (int i = 1; i < threads; i++) { - for (int k = 0; k < m_iBKTKmeansK; k++) - args.newCounts[k] += args.newCounts[i*m_iBKTKmeansK + k]; - } - - if (updateCenters) { - for (int i = 1; i < threads; i++) { - float* currCenter = args.newCenters + i*m_iBKTKmeansK*p_index->GetFeatureDim(); - for (size_t j = 0; j < ((size_t)m_iBKTKmeansK) * p_index->GetFeatureDim(); j++) args.newCenters[j] += currCenter[j]; - - for (int k = 0; k < m_iBKTKmeansK; k++) { - if (args.clusterIdx[i*m_iBKTKmeansK + k] != -1 && args.clusterDist[i*m_iBKTKmeansK + k] > args.clusterDist[k]) { - args.clusterDist[k] = args.clusterDist[i*m_iBKTKmeansK + k]; - args.clusterIdx[k] = args.clusterIdx[i*m_iBKTKmeansK + k]; - } - } - } - - int maxcluster = -1; - SizeType maxCount = 0; - for (int k = 0; k < m_iBKTKmeansK; k++) { - if (args.newCounts[k] > maxCount && DistanceUtils::ComputeL2Distance((T*)p_index->GetSample(args.clusterIdx[k]), args.centers + k * p_index->GetFeatureDim(), p_index->GetFeatureDim()) > 1e-6) - { - maxcluster = k; - maxCount = args.newCounts[k]; - } - } - - if (maxcluster != -1 && (args.clusterIdx[maxcluster] < 0 || args.clusterIdx[maxcluster] >= p_index->GetNumSamples())) - std::cout << "first:" << first << " last:" << last << " maxcluster:" << maxcluster << "(" << args.newCounts[maxcluster] << ") Error dist:" << args.clusterDist[maxcluster] << std::endl; - - for (int k = 0; k < m_iBKTKmeansK; k++) { - T* TCenter = args.newTCenters + k * p_index->GetFeatureDim(); - if (args.newCounts[k] == 0) { - if (maxcluster != -1) { - //int nextid = Utils::rand_int(last, first); - //while (args.label[nextid] != maxcluster) nextid = Utils::rand_int(last, first); - SizeType nextid = args.clusterIdx[maxcluster]; - std::memcpy(TCenter, p_index->GetSample(nextid), sizeof(T)*p_index->GetFeatureDim()); - } - else { - std::memcpy(TCenter, args.centers + k * p_index->GetFeatureDim(), sizeof(T)*p_index->GetFeatureDim()); - } - } - else { - float* currCenters = args.newCenters + k * p_index->GetFeatureDim(); - for (DimensionType j = 0; j < p_index->GetFeatureDim(); j++) currCenters[j] /= args.newCounts[k]; - - if (p_index->GetDistCalcMethod() == DistCalcMethod::Cosine) { - COMMON::Utils::Normalize(currCenters, p_index->GetFeatureDim(), COMMON::Utils::GetBase()); - } - for (DimensionType j = 0; j < p_index->GetFeatureDim(); j++) TCenter[j] = (T)(currCenters[j]); - } - } - } - else { - for (int i = 1; i < threads; i++) { - for (int k = 0; k < m_iBKTKmeansK; k++) { - if (args.clusterIdx[i*m_iBKTKmeansK + k] != -1 && args.clusterDist[i*m_iBKTKmeansK + k] <= args.clusterDist[k]) { - args.clusterDist[k] = args.clusterDist[i*m_iBKTKmeansK + k]; - args.clusterIdx[k] = args.clusterIdx[i*m_iBKTKmeansK + k]; - } - } - } - } - return currDist; - } - - template - int KmeansClustering(VectorIndex* p_index, - std::vector& indices, const SizeType first, const SizeType last, KmeansArgs& args) const { - int iterLimit = 100; - - SizeType batchEnd = min(first + m_iSamples, last); - float currDiff, currDist, minClusterDist = MaxDist; - for (int numKmeans = 0; numKmeans < 3; numKmeans++) { - for (int k = 0; k < m_iBKTKmeansK; k++) { - SizeType randid = COMMON::Utils::rand(last, first); - std::memcpy(args.centers + k*p_index->GetFeatureDim(), p_index->GetSample(indices[randid]), sizeof(T)*p_index->GetFeatureDim()); - } - args.ClearCounts(); - currDist = KmeansAssign(p_index, indices, first, batchEnd, args, false); - if (currDist < minClusterDist) { - minClusterDist = currDist; - memcpy(args.newTCenters, args.centers, sizeof(T)*m_iBKTKmeansK*p_index->GetFeatureDim()); - memcpy(args.counts, args.newCounts, sizeof(SizeType) * m_iBKTKmeansK); - } - } - - minClusterDist = MaxDist; - int noImprovement = 0; - for (int iter = 0; iter < iterLimit; iter++) { - std::memcpy(args.centers, args.newTCenters, sizeof(T)*m_iBKTKmeansK*p_index->GetFeatureDim()); - std::random_shuffle(indices.begin() + first, indices.begin() + last); - - args.ClearCenters(); - args.ClearCounts(); - args.ClearDists(-MaxDist); - currDist = KmeansAssign(p_index, indices, first, batchEnd, args, true); - memcpy(args.counts, args.newCounts, sizeof(SizeType) * m_iBKTKmeansK); - - currDiff = 0; - for (int k = 0; k < m_iBKTKmeansK; k++) { - currDiff += p_index->ComputeDistance((const void*)(args.centers + k*p_index->GetFeatureDim()), (const void*)(args.newTCenters + k*p_index->GetFeatureDim())); - } - - if (currDist < minClusterDist) { - noImprovement = 0; - minClusterDist = currDist; - } - else { - noImprovement++; - } - if (currDiff < 1e-3 || noImprovement >= 5) break; - } - - args.ClearCounts(); - args.ClearDists(MaxDist); - currDist = KmeansAssign(p_index, indices, first, last, args, false); - memcpy(args.counts, args.newCounts, sizeof(SizeType) * m_iBKTKmeansK); - - int numClusters = 0; - for (int i = 0; i < m_iBKTKmeansK; i++) if (args.counts[i] > 0) numClusters++; - - if (numClusters <= 1) { - //if (last - first > 1) std::cout << "large cluster:" << last - first << " dist:" << currDist << std::endl; - return numClusters; - } - args.Shuffle(indices, first, last); - return numClusters; - } - - private: - std::vector m_pTreeStart; - std::vector m_pTreeRoots; - std::unordered_map m_pSampleCenterMap; - - public: - int m_iTreeNumber, m_iBKTKmeansK, m_iBKTLeafSize, m_iSamples; - }; - } -} -#endif diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/CommonUtils.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/CommonUtils.h deleted file mode 100644 index 96a8d0b4fa..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/CommonUtils.h +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_COMMON_COMMONUTILS_H_ -#define _SPTAG_COMMON_COMMONUTILS_H_ - -#include "../Common.h" - -#include - -#include -#include -#include -#include - -#include -#include -#include - -#define PREFETCH - -#ifndef _MSC_VER -#include -#include -#include -#include - -#define InterlockedCompareExchange(a,b,c) __sync_val_compare_and_swap(a, c, b) -#define Sleep(a) usleep(a * 1000) -#define strtok_s(a, b, c) strtok_r(a, b, c) -#endif - -namespace SPTAG -{ - namespace COMMON - { - class Utils { - public: - static SizeType rand(SizeType high = MaxSize, SizeType low = 0) // Generates a random int value. - { - return low + (SizeType)(float(high - low)*(std::rand() / (RAND_MAX + 1.0))); - } - - static inline float atomic_float_add(volatile float* ptr, const float operand) - { - union { - volatile long iOld; - float fOld; - }; - union { - long iNew; - float fNew; - }; - - while (true) { - iOld = *(volatile long *)ptr; - fNew = fOld + operand; - if (InterlockedCompareExchange((long *)ptr, iNew, iOld) == iOld) { - return fNew; - } - } - } - - static double GetVector(char* cstr, const char* sep, std::vector& arr, DimensionType& NumDim) { - char* current; - char* context = nullptr; - - DimensionType i = 0; - double sum = 0; - arr.clear(); - current = strtok_s(cstr, sep, &context); - while (current != nullptr && (i < NumDim || NumDim < 0)) { - try { - float val = (float)atof(current); - arr.push_back(val); - } - catch (std::exception e) { - std::cout << "Exception:" << e.what() << std::endl; - return -2; - } - - sum += arr[i] * arr[i]; - current = strtok_s(nullptr, sep, &context); - i++; - } - - if (NumDim < 0) NumDim = i; - if (i < NumDim) return -2; - return std::sqrt(sum); - } - - template - static void Normalize(T* arr, DimensionType col, int base) { - double vecLen = 0; - for (DimensionType j = 0; j < col; j++) { - double val = arr[j]; - vecLen += val * val; - } - vecLen = std::sqrt(vecLen); - if (vecLen < 1e-6) { - T val = (T)(1.0 / std::sqrt((double)col) * base); - for (DimensionType j = 0; j < col; j++) arr[j] = val; - } - else { - for (DimensionType j = 0; j < col; j++) arr[j] = (T)(arr[j] / vecLen * base); - } - } - - static size_t ProcessLine(std::string& currentLine, std::vector& arr, DimensionType& D, int base, DistCalcMethod distCalcMethod) { - size_t index; - double vecLen; - if (currentLine.length() == 0 || (index = currentLine.find_last_of("\t")) == std::string::npos || (vecLen = GetVector(const_cast(currentLine.c_str() + index + 1), "|", arr, D)) < -1) { - std::cout << "Parse vector error: " + currentLine << std::endl; - //throw MyException("Error in parsing data " + currentLine); - return -1; - } - if (distCalcMethod == DistCalcMethod::Cosine) { - Normalize(arr.data(), D, base); - } - return index; - } - - template - static void PrepareQuerys(std::ifstream& inStream, std::vector& qString, std::vector>& Query, SizeType& NumQuery, DimensionType& NumDim, DistCalcMethod distCalcMethod, int base) { - std::string currentLine; - std::vector arr; - SizeType i = 0; - size_t index; - while ((NumQuery < 0 || i < NumQuery) && !inStream.eof()) { - std::getline(inStream, currentLine); - if (currentLine.length() <= 1 || (index = ProcessLine(currentLine, arr, NumDim, base, distCalcMethod)) < 0) { - continue; - } - qString.push_back(currentLine.substr(0, index)); - if ((SizeType)Query.size() < i + 1) Query.push_back(std::vector(NumDim, 0)); - - for (DimensionType j = 0; j < NumDim; j++) Query[i][j] = (T)arr[j]; - i++; - } - NumQuery = i; - std::cout << "Load data: (" << NumQuery << ", " << NumDim << ")" << std::endl; - } - - template - static inline int GetBase() { - if (GetEnumValueType() != VectorValueType::Float) { - return (int)(std::numeric_limits::max)(); - } - return 1; - } - - static inline void AddNeighbor(SizeType idx, float dist, SizeType *neighbors, float *dists, DimensionType size) - { - size--; - if (dist < dists[size] || (dist == dists[size] && idx < neighbors[size])) - { - DimensionType nb; - for (nb = 0; nb <= size && neighbors[nb] != idx; nb++); - - if (nb > size) - { - nb = size; - while (nb > 0 && (dist < dists[nb - 1] || (dist == dists[nb - 1] && idx < neighbors[nb - 1]))) - { - dists[nb] = dists[nb - 1]; - neighbors[nb] = neighbors[nb - 1]; - nb--; - } - dists[nb] = dist; - neighbors[nb] = idx; - } - } - } - }; - } -} - -#endif // _SPTAG_COMMON_COMMONUTILS_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/DataUtils.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/DataUtils.h deleted file mode 100644 index 5d751c4c98..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/DataUtils.h +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_COMMON_DATAUTILS_H_ -#define _SPTAG_COMMON_DATAUTILS_H_ - -#include -#include -#include "CommonUtils.h" -#include "../../Helper/CommonHelper.h" - -namespace SPTAG -{ - namespace COMMON - { - const int bufsize = 1 << 30; - - class DataUtils { - public: - static bool MergeIndex(const std::string& p_vectorfile1, const std::string& p_metafile1, const std::string& p_metaindexfile1, - const std::string& p_vectorfile2, const std::string& p_metafile2, const std::string& p_metaindexfile2) { - std::ifstream inputStream1, inputStream2; - std::ofstream outputStream; - std::unique_ptr bufferHolder(new char[bufsize]); - char * buf = bufferHolder.get(); - SizeType R1, R2; - DimensionType C1, C2; - -#define MergeVector(inputStream, vectorFile, R, C) \ - inputStream.open(vectorFile, std::ifstream::binary); \ - if (!inputStream.is_open()) { \ - std::cout << "Cannot open vector file: " << vectorFile <<"!" << std::endl; \ - return false; \ - } \ - inputStream.read((char *)&(R), sizeof(SizeType)); \ - inputStream.read((char *)&(C), sizeof(DimensionType)); \ - - MergeVector(inputStream1, p_vectorfile1, R1, C1) - MergeVector(inputStream2, p_vectorfile2, R2, C2) -#undef MergeVector - if (C1 != C2) { - inputStream1.close(); inputStream2.close(); - std::cout << "Vector dimensions are not the same!" << std::endl; - return false; - } - R1 += R2; - outputStream.open(p_vectorfile1 + "_tmp", std::ofstream::binary); - outputStream.write((char *)&R1, sizeof(SizeType)); - outputStream.write((char *)&C1, sizeof(DimensionType)); - while (!inputStream1.eof()) { - inputStream1.read(buf, bufsize); - outputStream.write(buf, inputStream1.gcount()); - } - while (!inputStream2.eof()) { - inputStream2.read(buf, bufsize); - outputStream.write(buf, inputStream2.gcount()); - } - inputStream1.close(); inputStream2.close(); - outputStream.close(); - - if (p_metafile1 != "" && p_metafile2 != "") { - outputStream.open(p_metafile1 + "_tmp", std::ofstream::binary); -#define MergeMeta(inputStream, metaFile) \ - inputStream.open(metaFile, std::ifstream::binary); \ - if (!inputStream.is_open()) { \ - std::cout << "Cannot open meta file: " << metaFile << "!" << std::endl; \ - return false; \ - } \ - while (!inputStream.eof()) { \ - inputStream.read(buf, bufsize); \ - outputStream.write(buf, inputStream.gcount()); \ - } \ - inputStream.close(); \ - - MergeMeta(inputStream1, p_metafile1) - MergeMeta(inputStream2, p_metafile2) -#undef MergeMeta - outputStream.close(); - delete[] buf; - - std::uint64_t * offsets = reinterpret_cast(buf); - std::uint64_t lastoff = 0; - outputStream.open(p_metaindexfile1 + "_tmp", std::ofstream::binary); - outputStream.write((char *)&R1, sizeof(SizeType)); -#define MergeMetaIndex(inputStream, metaIndexFile) \ - inputStream.open(metaIndexFile, std::ifstream::binary); \ - if (!inputStream.is_open()) { \ - std::cout << "Cannot open meta index file: " << metaIndexFile << "!" << std::endl; \ - return false; \ - } \ - inputStream.read((char *)&R2, sizeof(SizeType)); \ - inputStream.read((char *)offsets, sizeof(std::uint64_t)*(R2 + 1)); \ - inputStream.close(); \ - for (SizeType j = 0; j < R2 + 1; j++) offsets[j] += lastoff; \ - outputStream.write((char *)offsets, sizeof(std::uint64_t)*R2); \ - lastoff = offsets[R2]; \ - - MergeMetaIndex(inputStream1, p_metaindexfile1) - MergeMetaIndex(inputStream2, p_metaindexfile2) -#undef MergeMetaIndex - outputStream.write((char *)&lastoff, sizeof(std::uint64_t)); - outputStream.close(); - - rename((p_metafile1 + "_tmp").c_str(), p_metafile1.c_str()); - rename((p_metaindexfile1 + "_tmp").c_str(), p_metaindexfile1.c_str()); - } - rename((p_vectorfile1 + "_tmp").c_str(), p_vectorfile1.c_str()); - - std::cout << "Merged -> numSamples:" << R1 << " D:" << C1 << std::endl; - return true; - } - }; - } -} - -#endif // _SPTAG_COMMON_DATAUTILS_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/Dataset.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/Dataset.h deleted file mode 100644 index 0208f6d983..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/Dataset.h +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_COMMON_DATASET_H_ -#define _SPTAG_COMMON_DATASET_H_ - -#include - -#if defined(_MSC_VER) || defined(__INTEL_COMPILER) -#include -#else -#include -#endif // defined(__GNUC__) - -#define ALIGN 32 - -#define aligned_malloc(a, b) _mm_malloc(a, b) -#define aligned_free(a) _mm_free(a) - -#pragma warning(disable:4996) // 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. - -namespace SPTAG -{ - namespace COMMON - { - // structure to save Data and Graph - template - class Dataset - { - private: - std::string name = "Data"; - SizeType rows = 0; - DimensionType cols = 1; - bool ownData = false; - T* data = nullptr; - SizeType incRows = 0; - std::vector incBlocks; - static const SizeType rowsInBlock = 1024 * 1024; - public: - Dataset() - { - incBlocks.reserve(MaxSize / rowsInBlock + 1); - } - Dataset(SizeType rows_, DimensionType cols_, T* data_ = nullptr, bool transferOnwership_ = true) - { - Initialize(rows_, cols_, data_, transferOnwership_); - incBlocks.reserve(MaxSize / rowsInBlock + 1); - } - ~Dataset() - { - if (ownData) aligned_free(data); - for (T* ptr : incBlocks) aligned_free(ptr); - incBlocks.clear(); - } - void Initialize(SizeType rows_, DimensionType cols_, T* data_ = nullptr, bool transferOnwership_ = true) - { - rows = rows_; - cols = cols_; - data = data_; - if (data_ == nullptr || !transferOnwership_) - { - ownData = true; - data = (T*)aligned_malloc(((size_t)rows) * cols * sizeof(T), ALIGN); - if (data_ != nullptr) memcpy(data, data_, ((size_t)rows) * cols * sizeof(T)); - else std::memset(data, -1, ((size_t)rows) * cols * sizeof(T)); - } - } - void SetName(const std::string name_) { name = name_; } - void SetR(SizeType R_) - { - if (R_ >= rows) - incRows = R_ - rows; - else - { - rows = R_; - incRows = 0; - } - } - inline SizeType R() const { return rows + incRows; } - inline DimensionType C() const { return cols; } - inline std::uint64_t BufferSize() const { return sizeof(SizeType) + sizeof(DimensionType) + sizeof(T) * R() * C(); } - - inline const T* At(SizeType index) const - { - if (index >= rows) { - SizeType incIndex = index - rows; - return incBlocks[incIndex / rowsInBlock] + ((size_t)(incIndex % rowsInBlock)) * cols; - } - return data + ((size_t)index) * cols; - } - - T* operator[](SizeType index) - { - return (T*)At(index); - } - - const T* operator[](SizeType index) const - { - return At(index); - } - - ErrorCode AddBatch(const T* pData, SizeType num) - { - if (R() > MaxSize - num) return ErrorCode::MemoryOverFlow; - - SizeType written = 0; - while (written < num) { - SizeType curBlockIdx = (incRows + written) / rowsInBlock; - if (curBlockIdx >= (SizeType)incBlocks.size()) { - T* newBlock = (T*)aligned_malloc(((size_t)rowsInBlock) * cols * sizeof(T), ALIGN); - if (newBlock == nullptr) return ErrorCode::MemoryOverFlow; - incBlocks.push_back(newBlock); - } - SizeType curBlockPos = (incRows + written) % rowsInBlock; - SizeType toWrite = min(rowsInBlock - curBlockPos, num - written); - std::memcpy(incBlocks[curBlockIdx] + ((size_t)curBlockPos) * cols, pData + ((size_t)written) * cols, ((size_t)toWrite) * cols * sizeof(T)); - written += toWrite; - } - incRows += written; - return ErrorCode::Success; - } - - ErrorCode AddBatch(SizeType num) - { - if (R() > MaxSize - num) return ErrorCode::MemoryOverFlow; - - SizeType written = 0; - while (written < num) { - SizeType curBlockIdx = (incRows + written) / rowsInBlock; - if (curBlockIdx >= (SizeType)incBlocks.size()) { - T* newBlock = (T*)aligned_malloc(((size_t)rowsInBlock) * cols * sizeof(T), ALIGN); - if (newBlock == nullptr) return ErrorCode::MemoryOverFlow; - incBlocks.push_back(newBlock); - } - SizeType curBlockPos = (incRows + written) % rowsInBlock; - SizeType toWrite = min(rowsInBlock - curBlockPos, num - written); - std::memset(incBlocks[curBlockIdx] + ((size_t)curBlockPos) * cols, -1, ((size_t)toWrite) * cols * sizeof(T)); - written += toWrite; - } - incRows += written; - return ErrorCode::Success; - } - - bool Save(std::ostream& p_outstream) const - { - SizeType CR = R(); - p_outstream.write((char*)&CR, sizeof(SizeType)); - p_outstream.write((char*)&cols, sizeof(DimensionType)); - p_outstream.write((char*)data, sizeof(T) * cols * rows); - - SizeType blocks = incRows / rowsInBlock; - for (int i = 0; i < blocks; i++) - p_outstream.write((char*)incBlocks[i], sizeof(T) * cols * rowsInBlock); - - SizeType remain = incRows % rowsInBlock; - if (remain > 0) p_outstream.write((char*)incBlocks[blocks], sizeof(T) * cols * remain); - std::cout << "Save " << name << " (" << CR << ", " << cols << ") Finish!" << std::endl; - return true; - } - - bool Save(std::string sDataPointsFileName) const - { - std::cout << "Save " << name << " To " << sDataPointsFileName << std::endl; - std::ofstream output(sDataPointsFileName, std::ios::binary); - if (!output.is_open()) return false; - Save(output); - output.close(); - return true; - } - - bool Load(std::string sDataPointsFileName) - { - std::cout << "Load " << name << " From " << sDataPointsFileName << std::endl; - std::ifstream input(sDataPointsFileName, std::ios::binary); - if (!input.is_open()) return false; - - input.read((char*)&rows, sizeof(SizeType)); - input.read((char*)&cols, sizeof(DimensionType)); - - Initialize(rows, cols); - input.read((char*)data, sizeof(T) * cols * rows); - input.close(); - std::cout << "Load " << name << " (" << rows << ", " << cols << ") Finish!" << std::endl; - return true; - } - - // Functions for loading models from memory mapped files - bool Load(char* pDataPointsMemFile) - { - SizeType R; - DimensionType C; - R = *((SizeType*)pDataPointsMemFile); - pDataPointsMemFile += sizeof(SizeType); - - C = *((DimensionType*)pDataPointsMemFile); - pDataPointsMemFile += sizeof(DimensionType); - - Initialize(R, C, (T*)pDataPointsMemFile, false); - std::cout << "Load " << name << " (" << R << ", " << C << ") Finish!" << std::endl; - return true; - } - - bool Refine(const std::vector& indices, std::ostream& output) - { - SizeType R = (SizeType)(indices.size()); - output.write((char*)&R, sizeof(SizeType)); - output.write((char*)&cols, sizeof(DimensionType)); - - for (SizeType i = 0; i < R; i++) { - output.write((char*)At(indices[i]), sizeof(T) * cols); - } - std::cout << "Save Refine " << name << " (" << R << ", " << cols << ") Finish!" << std::endl; - return true; - } - - bool Refine(const std::vector& indices, std::string sDataPointsFileName) - { - std::cout << "Save Refine " << name << " To " << sDataPointsFileName << std::endl; - std::ofstream output(sDataPointsFileName, std::ios::binary); - if (!output.is_open()) return false; - Refine(indices, output); - output.close(); - return true; - } - }; - } -} - -#endif // _SPTAG_COMMON_DATASET_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/DistanceUtils.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/DistanceUtils.h deleted file mode 100644 index 8e1d349245..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/DistanceUtils.h +++ /dev/null @@ -1,610 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_COMMON_DISTANCEUTILS_H_ -#define _SPTAG_COMMON_DISTANCEUTILS_H_ - -#include -#include - -#include "CommonUtils.h" - -#define SSE - -#ifndef _MSC_VER -#define DIFF128 diff128 -#define DIFF256 diff256 -#else -#define DIFF128 diff128.m128_f32 -#define DIFF256 diff256.m256_f32 -#endif - -namespace SPTAG -{ - namespace COMMON - { - class DistanceUtils - { - public: - static inline __m128 _mm_mul_epi8(__m128i X, __m128i Y) - { - __m128i zero = _mm_setzero_si128(); - - __m128i sign_x = _mm_cmplt_epi8(X, zero); - __m128i sign_y = _mm_cmplt_epi8(Y, zero); - - __m128i xlo = _mm_unpacklo_epi8(X, sign_x); - __m128i xhi = _mm_unpackhi_epi8(X, sign_x); - __m128i ylo = _mm_unpacklo_epi8(Y, sign_y); - __m128i yhi = _mm_unpackhi_epi8(Y, sign_y); - - return _mm_cvtepi32_ps(_mm_add_epi32(_mm_madd_epi16(xlo, ylo), _mm_madd_epi16(xhi, yhi))); - } - - static inline __m128 _mm_sqdf_epi8(__m128i X, __m128i Y) - { - __m128i zero = _mm_setzero_si128(); - - __m128i sign_x = _mm_cmplt_epi8(X, zero); - __m128i sign_y = _mm_cmplt_epi8(Y, zero); - - __m128i xlo = _mm_unpacklo_epi8(X, sign_x); - __m128i xhi = _mm_unpackhi_epi8(X, sign_x); - __m128i ylo = _mm_unpacklo_epi8(Y, sign_y); - __m128i yhi = _mm_unpackhi_epi8(Y, sign_y); - - __m128i dlo = _mm_sub_epi16(xlo, ylo); - __m128i dhi = _mm_sub_epi16(xhi, yhi); - - return _mm_cvtepi32_ps(_mm_add_epi32(_mm_madd_epi16(dlo, dlo), _mm_madd_epi16(dhi, dhi))); - } - - static inline __m128 _mm_mul_epu8(__m128i X, __m128i Y) - { - __m128i zero = _mm_setzero_si128(); - - __m128i xlo = _mm_unpacklo_epi8(X, zero); - __m128i xhi = _mm_unpackhi_epi8(X, zero); - __m128i ylo = _mm_unpacklo_epi8(Y, zero); - __m128i yhi = _mm_unpackhi_epi8(Y, zero); - - return _mm_cvtepi32_ps(_mm_add_epi32(_mm_madd_epi16(xlo, ylo), _mm_madd_epi16(xhi, yhi))); - } - - static inline __m128 _mm_sqdf_epu8(__m128i X, __m128i Y) - { - __m128i zero = _mm_setzero_si128(); - - __m128i xlo = _mm_unpacklo_epi8(X, zero); - __m128i xhi = _mm_unpackhi_epi8(X, zero); - __m128i ylo = _mm_unpacklo_epi8(Y, zero); - __m128i yhi = _mm_unpackhi_epi8(Y, zero); - - __m128i dlo = _mm_sub_epi16(xlo, ylo); - __m128i dhi = _mm_sub_epi16(xhi, yhi); - - return _mm_cvtepi32_ps(_mm_add_epi32(_mm_madd_epi16(dlo, dlo), _mm_madd_epi16(dhi, dhi))); - } - - static inline __m128 _mm_mul_epi16(__m128i X, __m128i Y) - { - return _mm_cvtepi32_ps(_mm_madd_epi16(X, Y)); - } - - static inline __m128 _mm_sqdf_epi16(__m128i X, __m128i Y) - { - __m128i zero = _mm_setzero_si128(); - - __m128i sign_x = _mm_cmplt_epi16(X, zero); - __m128i sign_y = _mm_cmplt_epi16(Y, zero); - - __m128i xlo = _mm_unpacklo_epi16(X, sign_x); - __m128i xhi = _mm_unpackhi_epi16(X, sign_x); - __m128i ylo = _mm_unpacklo_epi16(Y, sign_y); - __m128i yhi = _mm_unpackhi_epi16(Y, sign_y); - - __m128 dlo = _mm_cvtepi32_ps(_mm_sub_epi32(xlo, ylo)); - __m128 dhi = _mm_cvtepi32_ps(_mm_sub_epi32(xhi, yhi)); - - return _mm_add_ps(_mm_mul_ps(dlo, dlo), _mm_mul_ps(dhi, dhi)); - } - static inline __m128 _mm_sqdf_ps(__m128 X, __m128 Y) - { - __m128 d = _mm_sub_ps(X, Y); - return _mm_mul_ps(d, d); - } -#if defined(AVX) - static inline __m256 _mm256_mul_epi8(__m256i X, __m256i Y) - { - __m256i zero = _mm256_setzero_si256(); - - __m256i sign_x = _mm256_cmpgt_epi8(zero, X); - __m256i sign_y = _mm256_cmpgt_epi8(zero, Y); - - __m256i xlo = _mm256_unpacklo_epi8(X, sign_x); - __m256i xhi = _mm256_unpackhi_epi8(X, sign_x); - __m256i ylo = _mm256_unpacklo_epi8(Y, sign_y); - __m256i yhi = _mm256_unpackhi_epi8(Y, sign_y); - - return _mm256_cvtepi32_ps(_mm256_add_epi32(_mm256_madd_epi16(xlo, ylo), _mm256_madd_epi16(xhi, yhi))); - } - static inline __m256 _mm256_sqdf_epi8(__m256i X, __m256i Y) - { - __m256i zero = _mm256_setzero_si256(); - - __m256i sign_x = _mm256_cmpgt_epi8(zero, X); - __m256i sign_y = _mm256_cmpgt_epi8(zero, Y); - - __m256i xlo = _mm256_unpacklo_epi8(X, sign_x); - __m256i xhi = _mm256_unpackhi_epi8(X, sign_x); - __m256i ylo = _mm256_unpacklo_epi8(Y, sign_y); - __m256i yhi = _mm256_unpackhi_epi8(Y, sign_y); - - __m256i dlo = _mm256_sub_epi16(xlo, ylo); - __m256i dhi = _mm256_sub_epi16(xhi, yhi); - - return _mm256_cvtepi32_ps(_mm256_add_epi32(_mm256_madd_epi16(dlo, dlo), _mm256_madd_epi16(dhi, dhi))); - } - static inline __m256 _mm256_mul_epu8(__m256i X, __m256i Y) - { - __m256i zero = _mm256_setzero_si256(); - - __m256i xlo = _mm256_unpacklo_epi8(X, zero); - __m256i xhi = _mm256_unpackhi_epi8(X, zero); - __m256i ylo = _mm256_unpacklo_epi8(Y, zero); - __m256i yhi = _mm256_unpackhi_epi8(Y, zero); - - return _mm256_cvtepi32_ps(_mm256_add_epi32(_mm256_madd_epi16(xlo, ylo), _mm256_madd_epi16(xhi, yhi))); - } - static inline __m256 _mm256_sqdf_epu8(__m256i X, __m256i Y) - { - __m256i zero = _mm256_setzero_si256(); - - __m256i xlo = _mm256_unpacklo_epi8(X, zero); - __m256i xhi = _mm256_unpackhi_epi8(X, zero); - __m256i ylo = _mm256_unpacklo_epi8(Y, zero); - __m256i yhi = _mm256_unpackhi_epi8(Y, zero); - - __m256i dlo = _mm256_sub_epi16(xlo, ylo); - __m256i dhi = _mm256_sub_epi16(xhi, yhi); - - return _mm256_cvtepi32_ps(_mm256_add_epi32(_mm256_madd_epi16(dlo, dlo), _mm256_madd_epi16(dhi, dhi))); - } - static inline __m256 _mm256_mul_epi16(__m256i X, __m256i Y) - { - return _mm256_cvtepi32_ps(_mm256_madd_epi16(X, Y)); - } - static inline __m256 _mm256_sqdf_epi16(__m256i X, __m256i Y) - { - __m256i zero = _mm256_setzero_si256(); - - __m256i sign_x = _mm256_cmpgt_epi16(zero, X); - __m256i sign_y = _mm256_cmpgt_epi16(zero, Y); - - __m256i xlo = _mm256_unpacklo_epi16(X, sign_x); - __m256i xhi = _mm256_unpackhi_epi16(X, sign_x); - __m256i ylo = _mm256_unpacklo_epi16(Y, sign_y); - __m256i yhi = _mm256_unpackhi_epi16(Y, sign_y); - - __m256 dlo = _mm256_cvtepi32_ps(_mm256_sub_epi32(xlo, ylo)); - __m256 dhi = _mm256_cvtepi32_ps(_mm256_sub_epi32(xhi, yhi)); - - return _mm256_add_ps(_mm256_mul_ps(dlo, dlo), _mm256_mul_ps(dhi, dhi)); - } - static inline __m256 _mm256_sqdf_ps(__m256 X, __m256 Y) - { - __m256 d = _mm256_sub_ps(X, Y); - return _mm256_mul_ps(d, d); - } -#endif -/* - template - static float ComputeL2Distance(const T *pX, const T *pY, DimensionType length) - { - float diff = 0; - const T* pEnd1 = pX + length; - while (pX < pEnd1) { - float c1 = ((float)(*pX++) - (float)(*pY++)); diff += c1 * c1; - } - return diff; - } -*/ -#define REPEAT(type, ctype, delta, load, exec, acc, result) \ - { \ - type c1 = load((ctype *)(pX)); \ - type c2 = load((ctype *)(pY)); \ - pX += delta; pY += delta; \ - result = acc(result, exec(c1, c2)); \ - } \ - - static float ComputeL2Distance(const std::int8_t *pX, const std::int8_t *pY, DimensionType length) - { - const std::int8_t* pEnd32 = pX + ((length >> 5) << 5); - const std::int8_t* pEnd16 = pX + ((length >> 4) << 4); - const std::int8_t* pEnd4 = pX + ((length >> 2) << 2); - const std::int8_t* pEnd1 = pX + length; -#if defined(SSE) - __m128 diff128 = _mm_setzero_ps(); - while (pX < pEnd32) { - REPEAT(__m128i, __m128i, 16, _mm_loadu_si128, _mm_sqdf_epi8, _mm_add_ps, diff128) - REPEAT(__m128i, __m128i, 16, _mm_loadu_si128, _mm_sqdf_epi8, _mm_add_ps, diff128) - } - while (pX < pEnd16) { - REPEAT(__m128i, __m128i, 16, _mm_loadu_si128, _mm_sqdf_epi8, _mm_add_ps, diff128) - } - float diff = DIFF128[0] + DIFF128[1] + DIFF128[2] + DIFF128[3]; -#elif defined(AVX) - __m256 diff256 = _mm256_setzero_ps(); - while (pX < pEnd32) { - REPEAT(__m256i, __m256i, 32, _mm256_loadu_si256, _mm256_sqdf_epi8, _mm256_add_ps, diff256) - } - __m128 diff128 = _mm_add_ps(_mm256_castps256_ps128(diff256), _mm256_extractf128_ps(diff256, 1)); - while (pX < pEnd16) { - REPEAT(__m128i, __m128i, 16, _mm_loadu_si128, _mm_sqdf_epi8, _mm_add_ps, diff128) - } - float diff = DIFF128[0] + DIFF128[1] + DIFF128[2] + DIFF128[3]; -#else - float diff = 0; -#endif - while (pX < pEnd4) { - float c1 = ((float)(*pX++) - (float)(*pY++)); diff += c1 * c1; - c1 = ((float)(*pX++) - (float)(*pY++)); diff += c1 * c1; - c1 = ((float)(*pX++) - (float)(*pY++)); diff += c1 * c1; - c1 = ((float)(*pX++) - (float)(*pY++)); diff += c1 * c1; - } - while (pX < pEnd1) { - float c1 = ((float)(*pX++) - (float)(*pY++)); diff += c1 * c1; - } - return diff; - } - - static float ComputeL2Distance(const std::uint8_t *pX, const std::uint8_t *pY, DimensionType length) - { - const std::uint8_t* pEnd32 = pX + ((length >> 5) << 5); - const std::uint8_t* pEnd16 = pX + ((length >> 4) << 4); - const std::uint8_t* pEnd4 = pX + ((length >> 2) << 2); - const std::uint8_t* pEnd1 = pX + length; -#if defined(SSE) - __m128 diff128 = _mm_setzero_ps(); - while (pX < pEnd32) { - REPEAT(__m128i, __m128i, 16, _mm_loadu_si128, _mm_sqdf_epu8, _mm_add_ps, diff128) - REPEAT(__m128i, __m128i, 16, _mm_loadu_si128, _mm_sqdf_epu8, _mm_add_ps, diff128) - } - while (pX < pEnd16) { - REPEAT(__m128i, __m128i, 16, _mm_loadu_si128, _mm_sqdf_epu8, _mm_add_ps, diff128) - } - float diff = DIFF128[0] + DIFF128[1] + DIFF128[2] + DIFF128[3]; -#elif defined(AVX) - __m256 diff256 = _mm256_setzero_ps(); - while (pX < pEnd32) { - REPEAT(__m256i, __m256i, 32, _mm256_loadu_si256, _mm256_sqdf_epu8, _mm256_add_ps, diff256) - } - __m128 diff128 = _mm_add_ps(_mm256_castps256_ps128(diff256), _mm256_extractf128_ps(diff256, 1)); - while (pX < pEnd16) { - REPEAT(__m128i, __m128i, 16, _mm_loadu_si128, _mm_sqdf_epu8, _mm_add_ps, diff128) - } - float diff = DIFF128[0] + DIFF128[1] + DIFF128[2] + DIFF128[3]; -#else - float diff = 0; -#endif - while (pX < pEnd4) { - float c1 = ((float)(*pX++) - (float)(*pY++)); diff += c1 * c1; - c1 = ((float)(*pX++) - (float)(*pY++)); diff += c1 * c1; - c1 = ((float)(*pX++) - (float)(*pY++)); diff += c1 * c1; - c1 = ((float)(*pX++) - (float)(*pY++)); diff += c1 * c1; - } - while (pX < pEnd1) { - float c1 = ((float)(*pX++) - (float)(*pY++)); diff += c1 * c1; - } - return diff; - } - - static float ComputeL2Distance(const std::int16_t *pX, const std::int16_t *pY, DimensionType length) - { - const std::int16_t* pEnd16 = pX + ((length >> 4) << 4); - const std::int16_t* pEnd8 = pX + ((length >> 3) << 3); - const std::int16_t* pEnd4 = pX + ((length >> 2) << 2); - const std::int16_t* pEnd1 = pX + length; -#if defined(SSE) - __m128 diff128 = _mm_setzero_ps(); - while (pX < pEnd16) { - REPEAT(__m128i, __m128i, 8, _mm_loadu_si128, _mm_sqdf_epi16, _mm_add_ps, diff128) - REPEAT(__m128i, __m128i, 8, _mm_loadu_si128, _mm_sqdf_epi16, _mm_add_ps, diff128) - } - while (pX < pEnd8) { - REPEAT(__m128i, __m128i, 8, _mm_loadu_si128, _mm_sqdf_epi16, _mm_add_ps, diff128) - } - float diff = DIFF128[0] + DIFF128[1] + DIFF128[2] + DIFF128[3]; -#elif defined(AVX) - __m256 diff256 = _mm256_setzero_ps(); - while (pX < pEnd16) { - REPEAT(__m256i, __m256i, 16, _mm256_loadu_si256, _mm256_sqdf_epi16, _mm256_add_ps, diff256) - } - __m128 diff128 = _mm_add_ps(_mm256_castps256_ps128(diff256), _mm256_extractf128_ps(diff256, 1)); - while (pX < pEnd8) { - REPEAT(__m128i, __m128i, 8, _mm_loadu_si128, _mm_sqdf_epi16, _mm_add_ps, diff128) - } - float diff = DIFF128[0] + DIFF128[1] + DIFF128[2] + DIFF128[3]; -#else - float diff = 0; -#endif - while (pX < pEnd4) { - float c1 = ((float)(*pX++) - (float)(*pY++)); diff += c1 * c1; - c1 = ((float)(*pX++) - (float)(*pY++)); diff += c1 * c1; - c1 = ((float)(*pX++) - (float)(*pY++)); diff += c1 * c1; - c1 = ((float)(*pX++) - (float)(*pY++)); diff += c1 * c1; - } - - while (pX < pEnd1) { - float c1 = ((float)(*pX++) - (float)(*pY++)); diff += c1 * c1; - } - return diff; - } - - static float ComputeL2Distance(const float *pX, const float *pY, DimensionType length) - { - const float* pEnd16 = pX + ((length >> 4) << 4); - const float* pEnd4 = pX + ((length >> 2) << 2); - const float* pEnd1 = pX + length; -#if defined(SSE) - __m128 diff128 = _mm_setzero_ps(); - while (pX < pEnd16) - { - REPEAT(__m128, const float, 4, _mm_loadu_ps, _mm_sqdf_ps, _mm_add_ps, diff128) - REPEAT(__m128, const float, 4, _mm_loadu_ps, _mm_sqdf_ps, _mm_add_ps, diff128) - REPEAT(__m128, const float, 4, _mm_loadu_ps, _mm_sqdf_ps, _mm_add_ps, diff128) - REPEAT(__m128, const float, 4, _mm_loadu_ps, _mm_sqdf_ps, _mm_add_ps, diff128) - } - while (pX < pEnd4) - { - REPEAT(__m128, const float, 4, _mm_loadu_ps, _mm_sqdf_ps, _mm_add_ps, diff128) - } - float diff = DIFF128[0] + DIFF128[1] + DIFF128[2] + DIFF128[3]; -#elif defined(AVX) - __m256 diff256 = _mm256_setzero_ps(); - while (pX < pEnd16) - { - REPEAT(__m256, const float, 8, _mm256_loadu_ps, _mm256_sqdf_ps, _mm256_add_ps, diff256) - REPEAT(__m256, const float, 8, _mm256_loadu_ps, _mm256_sqdf_ps, _mm256_add_ps, diff256) - } - __m128 diff128 = _mm_add_ps(_mm256_castps256_ps128(diff256), _mm256_extractf128_ps(diff256, 1)); - while (pX < pEnd4) - { - REPEAT(__m128, const float, 4, _mm_loadu_ps, _mm_sqdf_ps, _mm_add_ps, diff128) - } - float diff = DIFF128[0] + DIFF128[1] + DIFF128[2] + DIFF128[3]; -#else - float diff = 0; - while (pX < pEnd4) { - float c1 = (*pX++) - (*pY++); diff += c1 * c1; - c1 = (*pX++) - (*pY++); diff += c1 * c1; - c1 = (*pX++) - (*pY++); diff += c1 * c1; - c1 = (*pX++) - (*pY++); diff += c1 * c1; - } -#endif - while (pX < pEnd1) { - float c1 = (*pX++) - (*pY++); diff += c1 * c1; - } - return diff; - } -/* - template - static float ComputeCosineDistance(const T *pX, const T *pY, DimensionType length) { - float diff = 0; - const T* pEnd1 = pX + length; - while (pX < pEnd1) diff += (*pX++) * (*pY++); - return 1 - diff; - } -*/ - static float ComputeCosineDistance(const std::int8_t *pX, const std::int8_t *pY, DimensionType length) { - const std::int8_t* pEnd32 = pX + ((length >> 5) << 5); - const std::int8_t* pEnd16 = pX + ((length >> 4) << 4); - const std::int8_t* pEnd4 = pX + ((length >> 2) << 2); - const std::int8_t* pEnd1 = pX + length; -#if defined(SSE) - - __m128 diff128 = _mm_setzero_ps(); - while (pX < pEnd32) { - REPEAT(__m128i, __m128i, 16, _mm_loadu_si128, _mm_mul_epi8, _mm_add_ps, diff128) - REPEAT(__m128i, __m128i, 16, _mm_loadu_si128, _mm_mul_epi8, _mm_add_ps, diff128) - } - while (pX < pEnd16) { - REPEAT(__m128i, __m128i, 16, _mm_loadu_si128, _mm_mul_epi8, _mm_add_ps, diff128) - } - float diff = DIFF128[0] + DIFF128[1] + DIFF128[2] + DIFF128[3]; -#elif defined(AVX) - __m256 diff256 = _mm256_setzero_ps(); - while (pX < pEnd32) { - REPEAT(__m256i, __m256i, 32, _mm256_loadu_si256, _mm256_mul_epi8, _mm256_add_ps, diff256) - } - __m128 diff128 = _mm_add_ps(_mm256_castps256_ps128(diff256), _mm256_extractf128_ps(diff256, 1)); - while (pX < pEnd16) { - REPEAT(__m128i, __m128i, 16, _mm_loadu_si128, _mm_mul_epi8, _mm_add_ps, diff128) - } - float diff = DIFF128[0] + DIFF128[1] + DIFF128[2] + DIFF128[3]; -#else - float diff = 0; -#endif - while (pX < pEnd4) - { - float c1 = ((float)(*pX++) * (float)(*pY++)); diff += c1; - c1 = ((float)(*pX++) * (float)(*pY++)); diff += c1; - c1 = ((float)(*pX++) * (float)(*pY++)); diff += c1; - c1 = ((float)(*pX++) * (float)(*pY++)); diff += c1; - } - while (pX < pEnd1) diff += ((float)(*pX++) * (float)(*pY++)); - return 16129 - diff; - } - - static float ComputeCosineDistance(const std::uint8_t *pX, const std::uint8_t *pY, DimensionType length) { - const std::uint8_t* pEnd32 = pX + ((length >> 5) << 5); - const std::uint8_t* pEnd16 = pX + ((length >> 4) << 4); - const std::uint8_t* pEnd4 = pX + ((length >> 2) << 2); - const std::uint8_t* pEnd1 = pX + length; -#if defined(SSE) - - __m128 diff128 = _mm_setzero_ps(); - while (pX < pEnd32) { - REPEAT(__m128i, __m128i, 16, _mm_loadu_si128, _mm_mul_epu8, _mm_add_ps, diff128) - REPEAT(__m128i, __m128i, 16, _mm_loadu_si128, _mm_mul_epu8, _mm_add_ps, diff128) - } - while (pX < pEnd16) { - REPEAT(__m128i, __m128i, 16, _mm_loadu_si128, _mm_mul_epu8, _mm_add_ps, diff128) - } - float diff = DIFF128[0] + DIFF128[1] + DIFF128[2] + DIFF128[3]; -#elif defined(AVX) - __m256 diff256 = _mm256_setzero_ps(); - while (pX < pEnd32) { - REPEAT(__m256i, __m256i, 32, _mm256_loadu_si256, _mm256_mul_epu8, _mm256_add_ps, diff256) - } - __m128 diff128 = _mm_add_ps(_mm256_castps256_ps128(diff256), _mm256_extractf128_ps(diff256, 1)); - while (pX < pEnd16) { - REPEAT(__m128i, __m128i, 16, _mm_loadu_si128, _mm_mul_epu8, _mm_add_ps, diff128) - } - float diff = DIFF128[0] + DIFF128[1] + DIFF128[2] + DIFF128[3]; -#else - float diff = 0; -#endif - while (pX < pEnd4) - { - float c1 = ((float)(*pX++) * (float)(*pY++)); diff += c1; - c1 = ((float)(*pX++) * (float)(*pY++)); diff += c1; - c1 = ((float)(*pX++) * (float)(*pY++)); diff += c1; - c1 = ((float)(*pX++) * (float)(*pY++)); diff += c1; - } - while (pX < pEnd1) diff += ((float)(*pX++) * (float)(*pY++)); - return 65025 - diff; - } - - static float ComputeCosineDistance(const std::int16_t *pX, const std::int16_t *pY, DimensionType length) { - const std::int16_t* pEnd16 = pX + ((length >> 4) << 4); - const std::int16_t* pEnd8 = pX + ((length >> 3) << 3); - const std::int16_t* pEnd4 = pX + ((length >> 2) << 2); - const std::int16_t* pEnd1 = pX + length; -#if defined(SSE) - __m128 diff128 = _mm_setzero_ps(); - while (pX < pEnd16) { - REPEAT(__m128i, __m128i, 8, _mm_loadu_si128, _mm_mul_epi16, _mm_add_ps, diff128) - REPEAT(__m128i, __m128i, 8, _mm_loadu_si128, _mm_mul_epi16, _mm_add_ps, diff128) - } - while (pX < pEnd8) { - REPEAT(__m128i, __m128i, 8, _mm_loadu_si128, _mm_mul_epi16, _mm_add_ps, diff128) - } - float diff = DIFF128[0] + DIFF128[1] + DIFF128[2] + DIFF128[3]; - -#elif defined(AVX) - __m256 diff256 = _mm256_setzero_ps(); - while (pX < pEnd16) { - REPEAT(__m256i, __m256i, 16, _mm256_loadu_si256, _mm256_mul_epi16, _mm256_add_ps, diff256) - } - __m128 diff128 = _mm_add_ps(_mm256_castps256_ps128(diff256), _mm256_extractf128_ps(diff256, 1)); - while (pX < pEnd8) { - REPEAT(__m128i, __m128i, 8, _mm_loadu_si128, _mm_mul_epi16, _mm_add_ps, diff128) - } - float diff = DIFF128[0] + DIFF128[1] + DIFF128[2] + DIFF128[3]; -#else - float diff = 0; -#endif - while (pX < pEnd4) - { - float c1 = ((float)(*pX++) * (float)(*pY++)); diff += c1; - c1 = ((float)(*pX++) * (float)(*pY++)); diff += c1; - c1 = ((float)(*pX++) * (float)(*pY++)); diff += c1; - c1 = ((float)(*pX++) * (float)(*pY++)); diff += c1; - } - - while (pX < pEnd1) diff += ((float)(*pX++) * (float)(*pY++)); - return 1073676289 - diff; - } - - static float ComputeCosineDistance(const float *pX, const float *pY, DimensionType length) { - const float* pEnd16 = pX + ((length >> 4) << 4); - const float* pEnd4 = pX + ((length >> 2) << 2); - const float* pEnd1 = pX + length; -#if defined(SSE) - __m128 diff128 = _mm_setzero_ps(); - while (pX < pEnd16) - { - REPEAT(__m128, const float, 4, _mm_loadu_ps, _mm_mul_ps, _mm_add_ps, diff128) - REPEAT(__m128, const float, 4, _mm_loadu_ps, _mm_mul_ps, _mm_add_ps, diff128) - REPEAT(__m128, const float, 4, _mm_loadu_ps, _mm_mul_ps, _mm_add_ps, diff128) - REPEAT(__m128, const float, 4, _mm_loadu_ps, _mm_mul_ps, _mm_add_ps, diff128) - } - while (pX < pEnd4) - { - REPEAT(__m128, const float, 4, _mm_loadu_ps, _mm_mul_ps, _mm_add_ps, diff128) - } - float diff = DIFF128[0] + DIFF128[1] + DIFF128[2] + DIFF128[3]; - -#elif defined(AVX) - __m256 diff256 = _mm256_setzero_ps(); - while (pX < pEnd16) - { - REPEAT(__m256, const float, 8, _mm256_loadu_ps, _mm256_mul_ps, _mm256_add_ps, diff256) - REPEAT(__m256, const float, 8, _mm256_loadu_ps, _mm256_mul_ps, _mm256_add_ps, diff256) - } - __m128 diff128 = _mm_add_ps(_mm256_castps256_ps128(diff256), _mm256_extractf128_ps(diff256, 1)); - while (pX < pEnd4) - { - REPEAT(__m128, const float, 4, _mm_loadu_ps, _mm_mul_ps, _mm_add_ps, diff128) - } - float diff = DIFF128[0] + DIFF128[1] + DIFF128[2] + DIFF128[3]; -#else - float diff = 0; - while (pX < pEnd4) - { - float c1 = (*pX++) * (*pY++); diff += c1; - c1 = (*pX++) * (*pY++); diff += c1; - c1 = (*pX++) * (*pY++); diff += c1; - c1 = (*pX++) * (*pY++); diff += c1; - } -#endif - while (pX < pEnd1) diff += (*pX++) * (*pY++); - return 1 - diff; - } - - template - static inline float ComputeDistance(const T *p1, const T *p2, DimensionType length, SPTAG::DistCalcMethod distCalcMethod) - { - if (distCalcMethod == SPTAG::DistCalcMethod::L2) - return ComputeL2Distance(p1, p2, length); - - return ComputeCosineDistance(p1, p2, length); - } - - static inline float ConvertCosineSimilarityToDistance(float cs) - { - // Cosine similarity is in [-1, 1], the higher the value, the closer are the two vectors. - // However, the tree is built and searched based on "distance" between two vectors, that's >=0. The smaller the value, the closer are the two vectors. - // So we do a linear conversion from a cosine similarity to a distance value. - return 1 - cs; //[1, 3] - } - - static inline float ConvertDistanceBackToCosineSimilarity(float d) - { - return 1 - d; - } - }; - - - template - float (*DistanceCalcSelector(SPTAG::DistCalcMethod p_method)) (const T*, const T*, DimensionType) - { - switch (p_method) - { - case SPTAG::DistCalcMethod::Cosine: - return &(DistanceUtils::ComputeCosineDistance); - - case SPTAG::DistCalcMethod::L2: - return &(DistanceUtils::ComputeL2Distance); - - default: - break; - } - - return nullptr; - } - } -} - -#endif // _SPTAG_COMMON_DISTANCEUTILS_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/FineGrainedLock.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/FineGrainedLock.h deleted file mode 100644 index 0de7ed8b36..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/FineGrainedLock.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_COMMON_FINEGRAINEDLOCK_H_ -#define _SPTAG_COMMON_FINEGRAINEDLOCK_H_ - -#include -#include -#include - -namespace SPTAG -{ - namespace COMMON - { - class FineGrainedLock { - public: - FineGrainedLock() {} - ~FineGrainedLock() { - for (size_t i = 0; i < locks.size(); i++) - locks[i].reset(); - locks.clear(); - } - - void resize(SizeType n) { - SizeType current = (SizeType)locks.size(); - if (current <= n) { - locks.resize(n); - for (SizeType i = current; i < n; i++) - locks[i].reset(new std::mutex); - } - else { - for (SizeType i = n; i < current; i++) - locks[i].reset(); - locks.resize(n); - } - } - - std::mutex& operator[](SizeType idx) { - return *locks[idx]; - } - - const std::mutex& operator[](SizeType idx) const { - return *locks[idx]; - } - private: - std::vector> locks; - }; - } -} - -#endif // _SPTAG_COMMON_FINEGRAINEDLOCK_H_ \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/Heap.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/Heap.h deleted file mode 100644 index 261aa498a6..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/Heap.h +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_COMMON_HEAP_H_ -#define _SPTAG_COMMON_HEAP_H_ - -namespace SPTAG -{ - namespace COMMON - { - - // priority queue - template - class Heap { - public: - Heap() : heap(nullptr), length(0), count(0) {} - - Heap(int size) { Resize(size); } - - void Resize(int size) - { - length = size; - heap.reset(new T[length + 1]); // heap uses 1-based indexing - count = 0; - lastlevel = int(pow(2.0, floor(log2(size)))); - } - ~Heap() {} - inline int size() { return count; } - inline bool empty() { return count == 0; } - inline void clear() { count = 0; } - inline T& Top() { if (count == 0) return heap[0]; else return heap[1]; } - - // Insert a new element in the heap. - void insert(T value) - { - /* If heap is full, then return without adding this element. */ - int loc; - if (count == length) { - int maxi = lastlevel; - for (int i = lastlevel + 1; i <= length; i++) - if (heap[maxi] < heap[i]) maxi = i; - if (value > heap[maxi]) return; - loc = maxi; - } - else { - loc = ++(count); /* Remember 1-based indexing. */ - } - /* Keep moving parents down until a place is found for this node. */ - int par = (loc >> 1); /* Location of parent. */ - while (par > 0 && value < heap[par]) { - heap[loc] = heap[par]; /* Move parent down to loc. */ - loc = par; - par >>= 1; - } - /* Insert the element at the determined location. */ - heap[loc] = value; - } - // Returns the node of minimum value from the heap (top of the heap). - bool pop(T& value) - { - if (count == 0) return false; - /* Switch first node with last. */ - value = heap[1]; - std::swap(heap[1], heap[count]); - count--; - heapify(); /* Move new node 1 to right position. */ - return true; /* Return old last node. */ - } - T& pop() - { - if (count == 0) return heap[0]; - /* Switch first node with last. */ - std::swap(heap[1], heap[count]); - count--; - heapify(); /* Move new node 1 to right position. */ - return heap[count + 1]; /* Return old last node. */ - } - private: - // Storage array for the heap. - // Type T must be comparable. - std::unique_ptr heap; - int length; - int count; // Number of element in the heap - int lastlevel; - // Reorganizes the heap (a parent is smaller than its children) starting with a node. - - void heapify() - { - int parent = 1, next = 2; - while (next < count) { - if (heap[next] > heap[next + 1]) next++; - if (heap[next] < heap[parent]) { - std::swap(heap[parent], heap[next]); - parent = next; - next <<= 1; - } - else break; - } - if (next == count && heap[next] < heap[parent]) std::swap(heap[parent], heap[next]); - } - }; - } -} - -#endif // _SPTAG_COMMON_HEAP_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/KDTree.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/KDTree.h deleted file mode 100644 index e46c133940..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/KDTree.h +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_COMMON_KDTREE_H_ -#define _SPTAG_COMMON_KDTREE_H_ - -#include -#include -#include - -#include "../VectorIndex.h" - -#include "CommonUtils.h" -#include "QueryResultSet.h" -#include "WorkSpace.h" - -#pragma warning(disable:4996) // 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. - -namespace SPTAG -{ - namespace COMMON - { - // node type for storing KDT - struct KDTNode - { - SizeType left; - SizeType right; - DimensionType split_dim; - float split_value; - }; - - class KDTree - { - public: - KDTree() : m_iTreeNumber(2), m_numTopDimensionKDTSplit(5), m_iSamples(1000) {} - - KDTree(KDTree& other) : m_iTreeNumber(other.m_iTreeNumber), - m_numTopDimensionKDTSplit(other.m_numTopDimensionKDTSplit), - m_iSamples(other.m_iSamples) {} - ~KDTree() {} - - inline const KDTNode& operator[](SizeType index) const { return m_pTreeRoots[index]; } - inline KDTNode& operator[](SizeType index) { return m_pTreeRoots[index]; } - - inline SizeType size() const { return (SizeType)m_pTreeRoots.size(); } - - template - void BuildTrees(VectorIndex* p_index, std::vector* indices = nullptr) - { - std::vector localindices; - if (indices == nullptr) { - localindices.resize(p_index->GetNumSamples()); - for (SizeType i = 0; i < p_index->GetNumSamples(); i++) localindices[i] = i; - } - else { - localindices.assign(indices->begin(), indices->end()); - } - - m_pTreeRoots.resize(m_iTreeNumber * localindices.size()); - m_pTreeStart.resize(m_iTreeNumber, 0); -#pragma omp parallel for - for (int i = 0; i < m_iTreeNumber; i++) - { - Sleep(i * 100); std::srand(clock()); - - std::vector pindices(localindices.begin(), localindices.end()); - std::random_shuffle(pindices.begin(), pindices.end()); - - m_pTreeStart[i] = i * (SizeType)pindices.size(); - std::cout << "Start to build KDTree " << i + 1 << std::endl; - SizeType iTreeSize = m_pTreeStart[i]; - DivideTree(p_index, pindices, 0, (SizeType)pindices.size() - 1, m_pTreeStart[i], iTreeSize); - std::cout << i + 1 << " KDTree built, " << iTreeSize - m_pTreeStart[i] << " " << pindices.size() << std::endl; - } - } - - inline std::uint64_t BufferSize() const - { - return sizeof(int) + sizeof(SizeType) * m_iTreeNumber + - sizeof(SizeType) + sizeof(KDTNode) * m_pTreeRoots.size(); - } - - bool SaveTrees(std::ostream& p_outstream) const - { - p_outstream.write((char*)&m_iTreeNumber, sizeof(int)); - p_outstream.write((char*)m_pTreeStart.data(), sizeof(SizeType) * m_iTreeNumber); - SizeType treeNodeSize = (SizeType)m_pTreeRoots.size(); - p_outstream.write((char*)&treeNodeSize, sizeof(SizeType)); - p_outstream.write((char*)m_pTreeRoots.data(), sizeof(KDTNode) * treeNodeSize); - std::cout << "Save KDT (" << m_iTreeNumber << "," << treeNodeSize << ") Finish!" << std::endl; - return true; - } - - bool SaveTrees(std::string sTreeFileName) const - { - std::cout << "Save KDT to " << sTreeFileName << std::endl; - std::ofstream output(sTreeFileName, std::ios::binary); - if (!output.is_open()) return false; - SaveTrees(output); - output.close(); - return true; - } - - bool LoadTrees(char* pKDTMemFile) - { - m_iTreeNumber = *((int*)pKDTMemFile); - pKDTMemFile += sizeof(int); - m_pTreeStart.resize(m_iTreeNumber); - memcpy(m_pTreeStart.data(), pKDTMemFile, sizeof(SizeType) * m_iTreeNumber); - pKDTMemFile += sizeof(SizeType)*m_iTreeNumber; - - SizeType treeNodeSize = *((SizeType*)pKDTMemFile); - pKDTMemFile += sizeof(SizeType); - m_pTreeRoots.resize(treeNodeSize); - memcpy(m_pTreeRoots.data(), pKDTMemFile, sizeof(KDTNode) * treeNodeSize); - std::cout << "Load KDT (" << m_iTreeNumber << "," << treeNodeSize << ") Finish!" << std::endl; - return true; - } - - bool LoadTrees(std::string sTreeFileName) - { - std::cout << "Load KDT From " << sTreeFileName << std::endl; - std::ifstream input(sTreeFileName, std::ios::binary); - if (!input.is_open()) return false; - - input.read((char*)&m_iTreeNumber, sizeof(int)); - m_pTreeStart.resize(m_iTreeNumber); - input.read((char*)m_pTreeStart.data(), sizeof(SizeType) * m_iTreeNumber); - - SizeType treeNodeSize; - input.read((char*)&treeNodeSize, sizeof(SizeType)); - m_pTreeRoots.resize(treeNodeSize); - input.read((char*)m_pTreeRoots.data(), sizeof(KDTNode) * treeNodeSize); - input.close(); - std::cout << "Load KDT (" << m_iTreeNumber << "," << treeNodeSize << ") Finish!" << std::endl; - return true; - } - - template - void InitSearchTrees(const VectorIndex* p_index, const COMMON::QueryResultSet &p_query, COMMON::WorkSpace &p_space, const int p_limits) const - { - for (int i = 0; i < m_iTreeNumber; i++) { - KDTSearch(p_index, p_query, p_space, m_pTreeStart[i], true, 0); - } - - while (!p_space.m_SPTQueue.empty() && p_space.m_iNumberOfCheckedLeaves < p_limits) - { - auto& tcell = p_space.m_SPTQueue.pop(); - if (p_query.worstDist() < tcell.distance) break; - KDTSearch(p_index, p_query, p_space, tcell.node, true, tcell.distance); - } - } - - template - void SearchTrees(const VectorIndex* p_index, const COMMON::QueryResultSet &p_query, COMMON::WorkSpace &p_space, const int p_limits) const - { - while (!p_space.m_SPTQueue.empty() && p_space.m_iNumberOfCheckedLeaves < p_limits) - { - auto& tcell = p_space.m_SPTQueue.pop(); - KDTSearch(p_index, p_query, p_space, tcell.node, false, tcell.distance); - } - } - - private: - - template - void KDTSearch(const VectorIndex* p_index, const COMMON::QueryResultSet &p_query, - COMMON::WorkSpace& p_space, const SizeType node, const bool isInit, const float distBound) const { - if (node < 0) - { - SizeType index = -node - 1; - if (index >= p_index->GetNumSamples()) return; -#ifdef PREFETCH - const char* data = (const char *)(p_index->GetSample(index)); - _mm_prefetch(data, _MM_HINT_T0); - _mm_prefetch(data + 64, _MM_HINT_T0); -#endif - if (p_space.CheckAndSet(index)) return; - - ++p_space.m_iNumberOfTreeCheckedLeaves; - ++p_space.m_iNumberOfCheckedLeaves; - p_space.m_NGQueue.insert(COMMON::HeapCell(index, p_index->ComputeDistance((const void*)p_query.GetTarget(), (const void*)data))); - return; - } - - auto& tnode = m_pTreeRoots[node]; - - float diff = (p_query.GetTarget())[tnode.split_dim] - tnode.split_value; - float distanceBound = distBound + diff * diff; - SizeType otherChild, bestChild; - if (diff < 0) - { - bestChild = tnode.left; - otherChild = tnode.right; - } - else - { - otherChild = tnode.left; - bestChild = tnode.right; - } - - if (!isInit || distanceBound < p_query.worstDist()) - { - p_space.m_SPTQueue.insert(COMMON::HeapCell(otherChild, distanceBound)); - } - KDTSearch(p_index, p_query, p_space, bestChild, isInit, distBound); - } - - - template - void DivideTree(VectorIndex* p_index, std::vector& indices, SizeType first, SizeType last, - SizeType index, SizeType &iTreeSize) { - ChooseDivision(p_index, m_pTreeRoots[index], indices, first, last); - SizeType i = Subdivide(p_index, m_pTreeRoots[index], indices, first, last); - if (i - 1 <= first) - { - m_pTreeRoots[index].left = -indices[first] - 1; - } - else - { - iTreeSize++; - m_pTreeRoots[index].left = iTreeSize; - DivideTree(p_index, indices, first, i - 1, iTreeSize, iTreeSize); - } - if (last == i) - { - m_pTreeRoots[index].right = -indices[last] - 1; - } - else - { - iTreeSize++; - m_pTreeRoots[index].right = iTreeSize; - DivideTree(p_index, indices, i, last, iTreeSize, iTreeSize); - } - } - - template - void ChooseDivision(VectorIndex* p_index, KDTNode& node, const std::vector& indices, const SizeType first, const SizeType last) - { - std::vector meanValues(p_index->GetFeatureDim(), 0); - std::vector varianceValues(p_index->GetFeatureDim(), 0); - SizeType end = min(first + m_iSamples, last); - SizeType count = end - first + 1; - // calculate the mean of each dimension - for (SizeType j = first; j <= end; j++) - { - const T* v = (const T*)p_index->GetSample(indices[j]); - for (DimensionType k = 0; k < p_index->GetFeatureDim(); k++) - { - meanValues[k] += v[k]; - } - } - for (DimensionType k = 0; k < p_index->GetFeatureDim(); k++) - { - meanValues[k] /= count; - } - // calculate the variance of each dimension - for (SizeType j = first; j <= end; j++) - { - const T* v = (const T*)p_index->GetSample(indices[j]); - for (DimensionType k = 0; k < p_index->GetFeatureDim(); k++) - { - float dist = v[k] - meanValues[k]; - varianceValues[k] += dist*dist; - } - } - // choose the split dimension as one of the dimension inside TOP_DIM maximum variance - node.split_dim = SelectDivisionDimension(varianceValues); - // determine the threshold - node.split_value = meanValues[node.split_dim]; - } - - DimensionType SelectDivisionDimension(const std::vector& varianceValues) const - { - // Record the top maximum variances - std::vector topind(m_numTopDimensionKDTSplit); - int num = 0; - // order the variances - for (DimensionType i = 0; i < (DimensionType)varianceValues.size(); i++) - { - if (num < m_numTopDimensionKDTSplit || varianceValues[i] > varianceValues[topind[num - 1]]) - { - if (num < m_numTopDimensionKDTSplit) - { - topind[num++] = i; - } - else - { - topind[num - 1] = i; - } - int j = num - 1; - // order the TOP_DIM variances - while (j > 0 && varianceValues[topind[j]] > varianceValues[topind[j - 1]]) - { - std::swap(topind[j], topind[j - 1]); - j--; - } - } - } - // randomly choose a dimension from TOP_DIM - return topind[COMMON::Utils::rand(num)]; - } - - template - SizeType Subdivide(VectorIndex* p_index, const KDTNode& node, std::vector& indices, const SizeType first, const SizeType last) const - { - SizeType i = first; - SizeType j = last; - // decide which child one point belongs - while (i <= j) - { - SizeType ind = indices[i]; - const T* v = (const T*)p_index->GetSample(ind); - float val = v[node.split_dim]; - if (val < node.split_value) - { - i++; - } - else - { - std::swap(indices[i], indices[j]); - j--; - } - } - // if all the points in the node are equal,equally split the node into 2 - if ((i == first) || (i == last + 1)) - { - i = (first + last + 1) / 2; - } - return i; - } - - private: - std::vector m_pTreeStart; - std::vector m_pTreeRoots; - - public: - int m_iTreeNumber, m_numTopDimensionKDTSplit, m_iSamples; - }; - } -} -#endif diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/NeighborhoodGraph.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/NeighborhoodGraph.h deleted file mode 100644 index ea47125c36..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/NeighborhoodGraph.h +++ /dev/null @@ -1,403 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_COMMON_NG_H_ -#define _SPTAG_COMMON_NG_H_ - -#include "../VectorIndex.h" - -#include "CommonUtils.h" -#include "Dataset.h" -#include "FineGrainedLock.h" -#include "QueryResultSet.h" - -namespace SPTAG -{ - namespace COMMON - { - class NeighborhoodGraph - { - public: - NeighborhoodGraph(): m_iTPTNumber(32), - m_iTPTLeafSize(2000), - m_iSamples(1000), - m_numTopDimensionTPTSplit(5), - m_iNeighborhoodSize(32), - m_iNeighborhoodScale(2), - m_iCEFScale(2), - m_iRefineIter(0), - m_iCEF(1000), - m_iMaxCheckForRefineGraph(10000) - { - m_pNeighborhoodGraph.SetName("Graph"); - } - - ~NeighborhoodGraph() {} - - virtual void InsertNeighbors(VectorIndex* index, const SizeType node, SizeType insertNode, float insertDist) = 0; - - virtual void RebuildNeighbors(VectorIndex* index, const SizeType node, SizeType* nodes, const BasicResult* queryResults, const int numResults) = 0; - - virtual float GraphAccuracyEstimation(VectorIndex* index, const SizeType samples, const std::unordered_map* idmap = nullptr) = 0; - - template - void BuildGraph(VectorIndex* index, const std::unordered_map* idmap = nullptr) - { - std::cout << "build RNG graph!" << std::endl; - - m_iGraphSize = index->GetNumSamples(); - m_iNeighborhoodSize = m_iNeighborhoodSize * m_iNeighborhoodScale; - m_pNeighborhoodGraph.Initialize(m_iGraphSize, m_iNeighborhoodSize); - m_dataUpdateLock.resize(m_iGraphSize); - - if (m_iGraphSize < 1000) { - RefineGraph(index, idmap); - std::cout << "Build RNG Graph end!" << std::endl; - return; - } - - { - COMMON::Dataset NeighborhoodDists(m_iGraphSize, m_iNeighborhoodSize); - std::vector> TptreeDataIndices(m_iTPTNumber, std::vector(m_iGraphSize)); - std::vector>> TptreeLeafNodes(m_iTPTNumber, std::vector>()); - - for (SizeType i = 0; i < m_iGraphSize; i++) - for (DimensionType j = 0; j < m_iNeighborhoodSize; j++) - (NeighborhoodDists)[i][j] = MaxDist; - - std::cout << "Parallel TpTree Partition begin " << std::endl; -#pragma omp parallel for schedule(dynamic) - for (int i = 0; i < m_iTPTNumber; i++) - { - Sleep(i * 100); std::srand(clock()); - for (SizeType j = 0; j < m_iGraphSize; j++) TptreeDataIndices[i][j] = j; - std::random_shuffle(TptreeDataIndices[i].begin(), TptreeDataIndices[i].end()); - PartitionByTptree(index, TptreeDataIndices[i], 0, m_iGraphSize - 1, TptreeLeafNodes[i]); - std::cout << "Finish Getting Leaves for Tree " << i << std::endl; - } - std::cout << "Parallel TpTree Partition done" << std::endl; - - for (int i = 0; i < m_iTPTNumber; i++) - { -#pragma omp parallel for schedule(dynamic) - for (SizeType j = 0; j < (SizeType)TptreeLeafNodes[i].size(); j++) - { - SizeType start_index = TptreeLeafNodes[i][j].first; - SizeType end_index = TptreeLeafNodes[i][j].second; - if (omp_get_thread_num() == 0) std::cout << "\rProcessing Tree " << i << ' ' << j * 100 / TptreeLeafNodes[i].size() << '%'; - for (SizeType x = start_index; x < end_index; x++) - { - for (SizeType y = x + 1; y <= end_index; y++) - { - SizeType p1 = TptreeDataIndices[i][x]; - SizeType p2 = TptreeDataIndices[i][y]; - float dist = index->ComputeDistance(index->GetSample(p1), index->GetSample(p2)); - if (idmap != nullptr) { - p1 = (idmap->find(p1) == idmap->end()) ? p1 : idmap->at(p1); - p2 = (idmap->find(p2) == idmap->end()) ? p2 : idmap->at(p2); - } - COMMON::Utils::AddNeighbor(p2, dist, (m_pNeighborhoodGraph)[p1], (NeighborhoodDists)[p1], m_iNeighborhoodSize); - COMMON::Utils::AddNeighbor(p1, dist, (m_pNeighborhoodGraph)[p2], (NeighborhoodDists)[p2], m_iNeighborhoodSize); - } - } - } - TptreeDataIndices[i].clear(); - TptreeLeafNodes[i].clear(); - std::cout << std::endl; - } - TptreeDataIndices.clear(); - TptreeLeafNodes.clear(); - } - - if (m_iMaxCheckForRefineGraph > 0) { - RefineGraph(index, idmap); - } - } - - template - void RefineGraph(VectorIndex* index, const std::unordered_map* idmap = nullptr) - { - m_iCEF *= m_iCEFScale; - m_iMaxCheckForRefineGraph *= m_iCEFScale; - -#pragma omp parallel for schedule(dynamic) - for (SizeType i = 0; i < m_iGraphSize; i++) - { - RefineNode(index, i, false); - if (i % 1000 == 0) std::cout << "\rRefine 1 " << (i * 100 / m_iGraphSize) << "%"; - } - std::cout << "Refine RNG, graph acc:" << GraphAccuracyEstimation(index, 100, idmap) << std::endl; - - m_iCEF /= m_iCEFScale; - m_iMaxCheckForRefineGraph /= m_iCEFScale; - m_iNeighborhoodSize /= m_iNeighborhoodScale; - -#pragma omp parallel for schedule(dynamic) - for (SizeType i = 0; i < m_iGraphSize; i++) - { - RefineNode(index, i, false); - if (i % 1000 == 0) std::cout << "\rRefine 2 " << (i * 100 / m_iGraphSize) << "%"; - } - std::cout << "Refine RNG, graph acc:" << GraphAccuracyEstimation(index, 100, idmap) << std::endl; - - if (idmap != nullptr) { - for (auto iter = idmap->begin(); iter != idmap->end(); iter++) - if (iter->first < 0) - { - m_pNeighborhoodGraph[-1 - iter->first][m_iNeighborhoodSize - 1] = -2 - iter->second; - } - } - } - - template - ErrorCode RefineGraph(VectorIndex* index, std::vector& indices, std::vector& reverseIndices, - std::ostream& output, const std::unordered_map* idmap = nullptr) - { - SizeType R = (SizeType)indices.size(); - -#pragma omp parallel for schedule(dynamic) - for (SizeType i = 0; i < R; i++) - { - RefineNode(index, indices[i], false); - SizeType* nodes = m_pNeighborhoodGraph[indices[i]]; - for (DimensionType j = 0; j < m_iNeighborhoodSize; j++) - { - if (nodes[j] < 0) nodes[j] = -1; - else nodes[j] = reverseIndices[nodes[j]]; - } - if (idmap == nullptr || idmap->find(-1 - indices[i]) == idmap->end()) continue; - nodes[m_iNeighborhoodSize - 1] = -2 - idmap->at(-1 - indices[i]); - } - - m_pNeighborhoodGraph.Refine(indices, output); - return ErrorCode::Success; - } - - - template - void RefineNode(VectorIndex* index, const SizeType node, bool updateNeighbors) - { - COMMON::QueryResultSet query((const T*)index->GetSample(node), m_iCEF + 1); - index->SearchIndex(query); - RebuildNeighbors(index, node, m_pNeighborhoodGraph[node], query.GetResults(), m_iCEF + 1); - - if (updateNeighbors) { - // update neighbors - for (int j = 0; j <= m_iCEF; j++) - { - BasicResult* item = query.GetResult(j); - if (item->VID < 0) break; - if (item->VID == node) continue; - - std::lock_guard lock(m_dataUpdateLock[item->VID]); - InsertNeighbors(index, item->VID, node, item->Dist); - } - } - } - - template - void PartitionByTptree(VectorIndex* index, std::vector& indices, const SizeType first, const SizeType last, - std::vector> & leaves) - { - if (last - first <= m_iTPTLeafSize) - { - leaves.push_back(std::make_pair(first, last)); - } - else - { - std::vector Mean(index->GetFeatureDim(), 0); - - int iIteration = 100; - SizeType end = min(first + m_iSamples, last); - SizeType count = end - first + 1; - // calculate the mean of each dimension - for (SizeType j = first; j <= end; j++) - { - const T* v = (const T*)index->GetSample(indices[j]); - for (DimensionType k = 0; k < index->GetFeatureDim(); k++) - { - Mean[k] += v[k]; - } - } - for (DimensionType k = 0; k < index->GetFeatureDim(); k++) - { - Mean[k] /= count; - } - std::vector Variance; - Variance.reserve(index->GetFeatureDim()); - for (DimensionType j = 0; j < index->GetFeatureDim(); j++) - { - Variance.push_back(BasicResult(j, 0)); - } - // calculate the variance of each dimension - for (SizeType j = first; j <= end; j++) - { - const T* v = (const T*)index->GetSample(indices[j]); - for (DimensionType k = 0; k < index->GetFeatureDim(); k++) - { - float dist = v[k] - Mean[k]; - Variance[k].Dist += dist*dist; - } - } - std::sort(Variance.begin(), Variance.end(), COMMON::Compare); - std::vector indexs(m_numTopDimensionTPTSplit); - std::vector weight(m_numTopDimensionTPTSplit), bestweight(m_numTopDimensionTPTSplit); - float bestvariance = Variance[index->GetFeatureDim() - 1].Dist; - for (int i = 0; i < m_numTopDimensionTPTSplit; i++) - { - indexs[i] = Variance[index->GetFeatureDim() - 1 - i].VID; - bestweight[i] = 0; - } - bestweight[0] = 1; - float bestmean = Mean[indexs[0]]; - - std::vector Val(count); - for (int i = 0; i < iIteration; i++) - { - float sumweight = 0; - for (int j = 0; j < m_numTopDimensionTPTSplit; j++) - { - weight[j] = float(rand() % 10000) / 5000.0f - 1.0f; - sumweight += weight[j] * weight[j]; - } - sumweight = sqrt(sumweight); - for (int j = 0; j < m_numTopDimensionTPTSplit; j++) - { - weight[j] /= sumweight; - } - float mean = 0; - for (SizeType j = 0; j < count; j++) - { - Val[j] = 0; - const T* v = (const T*)index->GetSample(indices[first + j]); - for (int k = 0; k < m_numTopDimensionTPTSplit; k++) - { - Val[j] += weight[k] * v[indexs[k]]; - } - mean += Val[j]; - } - mean /= count; - float var = 0; - for (SizeType j = 0; j < count; j++) - { - float dist = Val[j] - mean; - var += dist * dist; - } - if (var > bestvariance) - { - bestvariance = var; - bestmean = mean; - for (int j = 0; j < m_numTopDimensionTPTSplit; j++) - { - bestweight[j] = weight[j]; - } - } - } - SizeType i = first; - SizeType j = last; - // decide which child one point belongs - while (i <= j) - { - float val = 0; - const T* v = (const T*)index->GetSample(indices[i]); - for (int k = 0; k < m_numTopDimensionTPTSplit; k++) - { - val += bestweight[k] * v[indexs[k]]; - } - if (val < bestmean) - { - i++; - } - else - { - std::swap(indices[i], indices[j]); - j--; - } - } - // if all the points in the node are equal,equally split the node into 2 - if ((i == first) || (i == last + 1)) - { - i = (first + last + 1) / 2; - } - - Mean.clear(); - Variance.clear(); - Val.clear(); - indexs.clear(); - weight.clear(); - bestweight.clear(); - - PartitionByTptree(index, indices, first, i - 1, leaves); - PartitionByTptree(index, indices, i, last, leaves); - } - } - - inline std::uint64_t BufferSize() const - { - return m_pNeighborhoodGraph.BufferSize(); - } - - bool LoadGraph(std::string sGraphFilename) - { - if (!m_pNeighborhoodGraph.Load(sGraphFilename)) return false; - - m_iGraphSize = m_pNeighborhoodGraph.R(); - m_iNeighborhoodSize = m_pNeighborhoodGraph.C(); - m_dataUpdateLock.resize(m_iGraphSize); - return true; - } - - bool LoadGraph(char* pGraphMemFile) - { - m_pNeighborhoodGraph.Load(pGraphMemFile); - - m_iGraphSize = m_pNeighborhoodGraph.R(); - m_iNeighborhoodSize = m_pNeighborhoodGraph.C(); - m_dataUpdateLock.resize(m_iGraphSize); - return true; - } - - bool SaveGraph(std::string sGraphFilename) const - { - return m_pNeighborhoodGraph.Save(sGraphFilename); - } - - bool SaveGraph(std::ostream& output) const - { - return m_pNeighborhoodGraph.Save(output); - } - - inline ErrorCode AddBatch(SizeType num) - { - ErrorCode ret = m_pNeighborhoodGraph.AddBatch(num); - if (ret != ErrorCode::Success) return ret; - - m_iGraphSize += num; - m_dataUpdateLock.resize(m_iGraphSize); - return ErrorCode::Success; - } - - inline SizeType* operator[](SizeType index) { return m_pNeighborhoodGraph[index]; } - - inline const SizeType* operator[](SizeType index) const { return m_pNeighborhoodGraph[index]; } - - inline void SetR(SizeType rows) { m_pNeighborhoodGraph.SetR(rows); m_iGraphSize = rows; m_dataUpdateLock.resize(m_iGraphSize); } - - inline SizeType R() const { return m_iGraphSize; } - - static std::shared_ptr CreateInstance(std::string type); - - protected: - // Graph structure - SizeType m_iGraphSize; - COMMON::Dataset m_pNeighborhoodGraph; - COMMON::FineGrainedLock m_dataUpdateLock; // protect one row of the graph - - public: - int m_iTPTNumber, m_iTPTLeafSize, m_iSamples, m_numTopDimensionTPTSplit; - DimensionType m_iNeighborhoodSize; - int m_iNeighborhoodScale, m_iCEFScale, m_iRefineIter, m_iCEF, m_iMaxCheckForRefineGraph; - }; - } -} -#endif diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/QueryResultSet.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/QueryResultSet.h deleted file mode 100644 index ff8fa14dfd..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/QueryResultSet.h +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_COMMON_QUERYRESULTSET_H_ -#define _SPTAG_COMMON_QUERYRESULTSET_H_ - -#include "../SearchQuery.h" - -namespace SPTAG -{ -namespace COMMON -{ - -inline bool operator < (const BasicResult& lhs, const BasicResult& rhs) -{ - return ((lhs.Dist < rhs.Dist) || ((lhs.Dist == rhs.Dist) && (lhs.VID < rhs.VID))); -} - - -inline bool Compare(const BasicResult& lhs, const BasicResult& rhs) -{ - return ((lhs.Dist < rhs.Dist) || ((lhs.Dist == rhs.Dist) && (lhs.VID < rhs.VID))); -} - - -// Space to save temporary answer, similar with TopKCache -template -class QueryResultSet : public QueryResult -{ -public: - QueryResultSet(const T* _target, int _K) : QueryResult(_target, _K, false) - { - } - - QueryResultSet(const QueryResultSet& other) : QueryResult(other) - { - } - - inline void SetTarget(const T *p_target) - { - m_target = p_target; - } - - inline const T* GetTarget() const - { - return reinterpret_cast(m_target); - } - - inline float worstDist() const - { - return m_results[0].Dist; - } - - bool AddPoint(const SizeType index, float dist) - { - if (dist < m_results[0].Dist || (dist == m_results[0].Dist && index < m_results[0].VID)) - { - m_results[0].VID = index; - m_results[0].Dist = dist; - Heapify(m_resultNum); - return true; - } - return false; - } - - inline void SortResult() - { - for (int i = m_resultNum - 1; i >= 0; i--) - { - std::swap(m_results[0], m_results[i]); - Heapify(i); - } - } - -private: - void Heapify(int count) - { - int parent = 0, next = 1, maxidx = count - 1; - while (next < maxidx) - { - if (m_results[next] < m_results[next + 1]) next++; - if (m_results[parent] < m_results[next]) - { - std::swap(m_results[next], m_results[parent]); - parent = next; - next = (parent << 1) + 1; - } - else break; - } - if (next == maxidx && m_results[parent] < m_results[next]) std::swap(m_results[parent], m_results[next]); - } -}; -} -} - -#endif // _SPTAG_COMMON_QUERYRESULTSET_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/RelativeNeighborhoodGraph.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/RelativeNeighborhoodGraph.h deleted file mode 100644 index 33ab01927b..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/RelativeNeighborhoodGraph.h +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_COMMON_RNG_H_ -#define _SPTAG_COMMON_RNG_H_ - -#include "NeighborhoodGraph.h" - -namespace SPTAG -{ - namespace COMMON - { - class RelativeNeighborhoodGraph: public NeighborhoodGraph - { - public: - void RebuildNeighbors(VectorIndex* index, const SizeType node, SizeType* nodes, const BasicResult* queryResults, const int numResults) { - DimensionType count = 0; - for (int j = 0; j < numResults && count < m_iNeighborhoodSize; j++) { - const BasicResult& item = queryResults[j]; - if (item.VID < 0) break; - if (item.VID == node) continue; - - bool good = true; - for (DimensionType k = 0; k < count; k++) { - if (index->ComputeDistance(index->GetSample(nodes[k]), index->GetSample(item.VID)) <= item.Dist) { - good = false; - break; - } - } - if (good) nodes[count++] = item.VID; - } - for (DimensionType j = count; j < m_iNeighborhoodSize; j++) nodes[j] = -1; - } - - void InsertNeighbors(VectorIndex* index, const SizeType node, SizeType insertNode, float insertDist) - { - SizeType* nodes = m_pNeighborhoodGraph[node]; - for (DimensionType k = 0; k < m_iNeighborhoodSize; k++) - { - SizeType tmpNode = nodes[k]; - if (tmpNode < -1) continue; - - if (tmpNode < 0) - { - bool good = true; - for (DimensionType t = 0; t < k; t++) { - if (index->ComputeDistance(index->GetSample(insertNode), index->GetSample(nodes[t])) < insertDist) { - good = false; - break; - } - } - if (good) { - nodes[k] = insertNode; - } - break; - } - float tmpDist = index->ComputeDistance(index->GetSample(node), index->GetSample(tmpNode)); - if (insertDist < tmpDist || (insertDist == tmpDist && insertNode < tmpNode)) - { - bool good = true; - for (DimensionType t = 0; t < k; t++) { - if (index->ComputeDistance(index->GetSample(insertNode), index->GetSample(nodes[t])) < insertDist) { - good = false; - break; - } - } - if (good) { - nodes[k] = insertNode; - insertNode = tmpNode; - insertDist = tmpDist; - } - else { - break; - } - } - } - } - - float GraphAccuracyEstimation(VectorIndex* index, const SizeType samples, const std::unordered_map* idmap = nullptr) - { - DimensionType* correct = new DimensionType[samples]; - -#pragma omp parallel for schedule(dynamic) - for (SizeType i = 0; i < samples; i++) - { - SizeType x = COMMON::Utils::rand(m_iGraphSize); - //int x = i; - COMMON::QueryResultSet query(nullptr, m_iCEF); - for (SizeType y = 0; y < m_iGraphSize; y++) - { - if ((idmap != nullptr && idmap->find(y) != idmap->end())) continue; - float dist = index->ComputeDistance(index->GetSample(x), index->GetSample(y)); - query.AddPoint(y, dist); - } - query.SortResult(); - SizeType * exact_rng = new SizeType[m_iNeighborhoodSize]; - RebuildNeighbors(index, x, exact_rng, query.GetResults(), m_iCEF); - - correct[i] = 0; - for (DimensionType j = 0; j < m_iNeighborhoodSize; j++) { - if (exact_rng[j] == -1) { - correct[i] += m_iNeighborhoodSize - j; - break; - } - for (DimensionType k = 0; k < m_iNeighborhoodSize; k++) - if ((m_pNeighborhoodGraph)[x][k] == exact_rng[j]) { - correct[i]++; - break; - } - } - delete[] exact_rng; - } - float acc = 0; - for (SizeType i = 0; i < samples; i++) acc += float(correct[i]); - acc = acc / samples / m_iNeighborhoodSize; - delete[] correct; - return acc; - } - - }; - } -} -#endif \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/WorkSpace.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/WorkSpace.h deleted file mode 100644 index c236d45a1c..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/WorkSpace.h +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_COMMON_WORKSPACE_H_ -#define _SPTAG_COMMON_WORKSPACE_H_ - -#include "CommonUtils.h" -#include "Heap.h" - -namespace SPTAG -{ - namespace COMMON - { - // node type in the priority queue - struct HeapCell - { - SizeType node; - float distance; - - HeapCell(SizeType _node = -1, float _distance = MaxDist) : node(_node), distance(_distance) {} - - inline bool operator < (const HeapCell& rhs) - { - return distance < rhs.distance; - } - - inline bool operator > (const HeapCell& rhs) - { - return distance > rhs.distance; - } - }; - - class OptHashPosVector - { - protected: - // Max loop number in one hash block. - static const int m_maxLoop = 8; - - // Max pool size. - static const int m_poolSize = 8191; - - // Could we use the second hash block. - bool m_secondHash; - - // Record 2 hash tables. - // [0~m_poolSize + 1) is the first block. - // [m_poolSize + 1, 2*(m_poolSize + 1)) is the second block; - SizeType m_hashTable[(m_poolSize + 1) * 2]; - - - inline unsigned hash_func2(unsigned idx, int loop) - { - return (idx + loop) & m_poolSize; - } - - - inline unsigned hash_func(unsigned idx) - { - return ((unsigned)(idx * 99991) + _rotl(idx, 2) + 101) & m_poolSize; - } - - public: - OptHashPosVector() {} - - ~OptHashPosVector() {} - - - void Init(SizeType size) - { - m_secondHash = true; - clear(); - } - - void clear() - { - if (!m_secondHash) - { - // Clear first block. - memset(&m_hashTable[0], 0, sizeof(SizeType)*(m_poolSize + 1)); - } - else - { - // Clear all blocks. - memset(&m_hashTable[0], 0, 2 * sizeof(SizeType) * (m_poolSize + 1)); - m_secondHash = false; - } - } - - - inline bool CheckAndSet(SizeType idx) - { - // Inner Index is begin from 1 - return _CheckAndSet(&m_hashTable[0], idx + 1) == 0; - } - - - inline int _CheckAndSet(SizeType* hashTable, SizeType idx) - { - unsigned index; - - // Get first hash position. - index = hash_func((unsigned)idx); - for (int loop = 0; loop < m_maxLoop; ++loop) - { - if (!hashTable[index]) - { - // index first match and record it. - hashTable[index] = idx; - return 1; - } - if (hashTable[index] == idx) - { - // Hit this item in hash table. - return 0; - } - // Get next hash position. - index = hash_func2(index, loop); - } - - if (hashTable == &m_hashTable[0]) - { - // Use second hash block. - m_secondHash = true; - return _CheckAndSet(&m_hashTable[m_poolSize + 1], idx); - } - - // Do not include this item. - return -1; - } - }; - - // Variables for each single NN search - struct WorkSpace - { - void Initialize(int maxCheck, SizeType dataSize) - { - nodeCheckStatus.Init(dataSize); - m_SPTQueue.Resize(maxCheck * 10); - m_NGQueue.Resize(maxCheck * 30); - - m_iNumberOfTreeCheckedLeaves = 0; - m_iNumberOfCheckedLeaves = 0; - m_iContinuousLimit = maxCheck / 64; - m_iMaxCheck = maxCheck; - m_iNumOfContinuousNoBetterPropagation = 0; - } - - void Reset(int maxCheck) - { - nodeCheckStatus.clear(); - m_SPTQueue.clear(); - m_NGQueue.clear(); - - m_iNumberOfTreeCheckedLeaves = 0; - m_iNumberOfCheckedLeaves = 0; - m_iContinuousLimit = maxCheck / 64; - m_iMaxCheck = maxCheck; - m_iNumOfContinuousNoBetterPropagation = 0; - } - - inline bool CheckAndSet(SizeType idx) - { - return nodeCheckStatus.CheckAndSet(idx); - } - - OptHashPosVector nodeCheckStatus; - //OptHashPosVector nodeCheckStatus; - - // counter for dynamic pivoting - int m_iNumOfContinuousNoBetterPropagation; - int m_iContinuousLimit; - int m_iNumberOfTreeCheckedLeaves; - int m_iNumberOfCheckedLeaves; - int m_iMaxCheck; - - // Prioriy queue used for neighborhood graph - Heap m_NGQueue; - - // Priority queue Used for BKT-Tree - Heap m_SPTQueue; - }; - } -} - -#endif // _SPTAG_COMMON_WORKSPACE_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/WorkSpacePool.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/WorkSpacePool.h deleted file mode 100644 index a322f42af4..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/Common/WorkSpacePool.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_COMMON_WORKSPACEPOOL_H_ -#define _SPTAG_COMMON_WORKSPACEPOOL_H_ - -#include "WorkSpace.h" - -#include -#include - -namespace SPTAG -{ -namespace COMMON -{ - -class WorkSpacePool -{ -public: - WorkSpacePool(int p_maxCheck, SizeType p_vectorCount); - - virtual ~WorkSpacePool(); - - std::shared_ptr Rent(); - - void Return(const std::shared_ptr& p_workSpace); - - void Init(int size); - -private: - std::list> m_workSpacePool; - - std::mutex m_workSpacePoolMutex; - - int m_maxCheck; - - SizeType m_vectorCount; -}; - -} -} - -#endif // _SPTAG_COMMON_WORKSPACEPOOL_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/CommonDataStructure.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/CommonDataStructure.h deleted file mode 100644 index c158fc8802..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/CommonDataStructure.h +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_COMMONDATASTRUCTURE_H_ -#define _SPTAG_COMMONDATASTRUCTURE_H_ - -#include "inc/Core/Common.h" - -namespace SPTAG -{ - -template -class Array -{ -public: - Array(); - - Array(T* p_array, std::size_t p_length, bool p_transferOwnership); - - Array(T* p_array, std::size_t p_length, std::shared_ptr p_dataHolder); - - Array(Array&& p_right); - - Array(const Array& p_right); - - Array& operator= (Array&& p_right); - - Array& operator= (const Array& p_right); - - T& operator[] (std::size_t p_index); - - const T& operator[] (std::size_t p_index) const; - - ~Array(); - - T* Data() const; - - std::size_t Length() const; - - std::shared_ptr DataHolder() const; - - void Set(T* p_array, std::size_t p_length, bool p_transferOwnership); - - void Clear(); - - static Array Alloc(std::size_t p_length); - - const static Array c_empty; - -private: - T* m_data; - - std::size_t m_length; - - // Notice this is holding an array. Set correct deleter for this. - std::shared_ptr m_dataHolder; -}; - -template -const Array Array::c_empty; - - -template -Array::Array() - : m_data(nullptr), - m_length(0) -{ -} - -template -Array::Array(T* p_array, std::size_t p_length, bool p_transferOnwership) - - : m_data(p_array), - m_length(p_length) -{ - if (p_transferOnwership) - { - m_dataHolder.reset(m_data, std::default_delete()); - } -} - - -template -Array::Array(T* p_array, std::size_t p_length, std::shared_ptr p_dataHolder) - : m_data(p_array), - m_length(p_length), - m_dataHolder(std::move(p_dataHolder)) -{ -} - - -template -Array::Array(Array&& p_right) - : m_data(p_right.m_data), - m_length(p_right.m_length), - m_dataHolder(std::move(p_right.m_dataHolder)) -{ -} - - -template -Array::Array(const Array& p_right) - : m_data(p_right.m_data), - m_length(p_right.m_length), - m_dataHolder(p_right.m_dataHolder) -{ -} - - -template -Array& -Array::operator= (Array&& p_right) -{ - m_data = p_right.m_data; - m_length = p_right.m_length; - m_dataHolder = std::move(p_right.m_dataHolder); - - return *this; -} - - -template -Array& -Array::operator= (const Array& p_right) -{ - m_data = p_right.m_data; - m_length = p_right.m_length; - m_dataHolder = p_right.m_dataHolder; - - return *this; -} - - -template -T& -Array::operator[] (std::size_t p_index) -{ - return m_data[p_index]; -} - - -template -const T& -Array::operator[] (std::size_t p_index) const -{ - return m_data[p_index]; -} - - -template -Array::~Array() -{ -} - - -template -T* -Array::Data() const -{ - return m_data; -} - - -template -std::size_t -Array::Length() const -{ - return m_length; -} - - -template -std::shared_ptr -Array::DataHolder() const -{ - return m_dataHolder; -} - - -template -void -Array::Set(T* p_array, std::size_t p_length, bool p_transferOwnership) -{ - m_data = p_array; - m_length = p_length; - - if (p_transferOwnership) - { - m_dataHolder.reset(m_data, std::default_delete()); - } -} - - -template -void -Array::Clear() -{ - m_data = nullptr; - m_length = 0; - m_dataHolder.reset(); -} - - -template -Array -Array::Alloc(std::size_t p_length) -{ - Array arr; - if (0 == p_length) - { - return arr; - } - - arr.m_dataHolder.reset(new T[p_length], std::default_delete()); - - arr.m_length = p_length; - arr.m_data = arr.m_dataHolder.get(); - return arr; -} - - -typedef Array ByteArray; - -} // namespace SPTAG - -#endif // _SPTAG_COMMONDATASTRUCTURE_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/DefinitionList.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/DefinitionList.h deleted file mode 100644 index 91014963c6..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/DefinitionList.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifdef DefineVectorValueType - -DefineVectorValueType(Int8, std::int8_t) -DefineVectorValueType(UInt8, std::uint8_t) -DefineVectorValueType(Int16, std::int16_t) -DefineVectorValueType(Float, float) - -#endif // DefineVectorValueType - - -#ifdef DefineDistCalcMethod - -DefineDistCalcMethod(L2) -DefineDistCalcMethod(Cosine) - -#endif // DefineDistCalcMethod - - -#ifdef DefineErrorCode - -// 0x0000 ~ 0x0FFF General Status -DefineErrorCode(Success, 0x0000) -DefineErrorCode(Fail, 0x0001) -DefineErrorCode(FailedOpenFile, 0x0002) -DefineErrorCode(FailedCreateFile, 0x0003) -DefineErrorCode(ParamNotFound, 0x0010) -DefineErrorCode(FailedParseValue, 0x0011) -DefineErrorCode(MemoryOverFlow, 0x0012) -DefineErrorCode(LackOfInputs, 0x0013) - -// 0x1000 ~ 0x1FFF Index Build Status - -// 0x2000 ~ 0x2FFF Index Serve Status - -// 0x3000 ~ 0x3FFF Helper Function Status -DefineErrorCode(ReadIni_FailedParseSection, 0x3000) -DefineErrorCode(ReadIni_FailedParseParam, 0x3001) -DefineErrorCode(ReadIni_DuplicatedSection, 0x3002) -DefineErrorCode(ReadIni_DuplicatedParam, 0x3003) - - -// 0x4000 ~ 0x4FFF Socket Library Status -DefineErrorCode(Socket_FailedResolveEndPoint, 0x4000) -DefineErrorCode(Socket_FailedConnectToEndPoint, 0x4001) - - -#endif // DefineErrorCode - - - -#ifdef DefineIndexAlgo - -DefineIndexAlgo(BKT) -DefineIndexAlgo(KDT) - -#endif // DefineIndexAlgo diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/KDT/Index.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/KDT/Index.h deleted file mode 100644 index f3240ebdb2..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/KDT/Index.h +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_KDT_INDEX_H_ -#define _SPTAG_KDT_INDEX_H_ - -#include "../Common.h" -#include "../VectorIndex.h" - -#include "../Common/CommonUtils.h" -#include "../Common/DistanceUtils.h" -#include "../Common/QueryResultSet.h" -#include "../Common/Dataset.h" -#include "../Common/WorkSpace.h" -#include "../Common/WorkSpacePool.h" -#include "../Common/RelativeNeighborhoodGraph.h" -#include "../Common/KDTree.h" -#include "inc/Helper/ConcurrentSet.h" -#include "inc/Helper/StringConvert.h" -#include "inc/Helper/SimpleIniReader.h" - -#include -#include - -namespace SPTAG -{ - - namespace Helper - { - class IniReader; - } - - namespace KDT - { - template - class Index : public VectorIndex - { - private: - // data points - COMMON::Dataset m_pSamples; - - // KDT structures. - COMMON::KDTree m_pTrees; - - // Graph structure - COMMON::RelativeNeighborhoodGraph m_pGraph; - - std::string m_sKDTFilename; - std::string m_sGraphFilename; - std::string m_sDataPointsFilename; - std::string m_sDeleteDataPointsFilename; - - std::mutex m_dataAddLock; // protect data and graph - Helper::Concurrent::ConcurrentSet m_deletedID; - float m_fDeletePercentageForRefine; - std::unique_ptr m_workSpacePool; - - int m_iNumberOfThreads; - DistCalcMethod m_iDistCalcMethod; - float(*m_fComputeDistance)(const T* pX, const T* pY, DimensionType length); - - int m_iMaxCheck; - int m_iThresholdOfNumberOfContinuousNoBetterPropagation; - int m_iNumberOfInitialDynamicPivots; - int m_iNumberOfOtherDynamicPivots; - public: - Index() - { -#define DefineKDTParameter(VarName, VarType, DefaultValue, RepresentStr) \ - VarName = DefaultValue; \ - -#include "inc/Core/KDT/ParameterDefinitionList.h" -#undef DefineKDTParameter - - m_pSamples.SetName("Vector"); - m_fComputeDistance = COMMON::DistanceCalcSelector(m_iDistCalcMethod); - } - - ~Index() {} - - inline SizeType GetNumSamples() const { return m_pSamples.R(); } - inline SizeType GetIndexSize() const { return sizeof(*this); } - inline DimensionType GetFeatureDim() const { return m_pSamples.C(); } - - inline int GetCurrMaxCheck() const { return m_iMaxCheck; } - inline int GetNumThreads() const { return m_iNumberOfThreads; } - inline DistCalcMethod GetDistCalcMethod() const { return m_iDistCalcMethod; } - inline IndexAlgoType GetIndexAlgoType() const { return IndexAlgoType::KDT; } - inline VectorValueType GetVectorValueType() const { return GetEnumValueType(); } - - inline float ComputeDistance(const void* pX, const void* pY) const { return m_fComputeDistance((const T*)pX, (const T*)pY, m_pSamples.C()); } - inline const void* GetSample(const SizeType idx) const { return (void*)m_pSamples[idx]; } - inline bool ContainSample(const SizeType idx) const { return !m_deletedID.contains(idx); } - inline bool NeedRefine() const { return m_deletedID.size() >= (size_t)(GetNumSamples() * m_fDeletePercentageForRefine); } - std::shared_ptr> BufferSize() const - { - std::shared_ptr> buffersize(new std::vector); - buffersize->push_back(m_pSamples.BufferSize()); - buffersize->push_back(m_pTrees.BufferSize()); - buffersize->push_back(m_pGraph.BufferSize()); - buffersize->push_back(m_deletedID.bufferSize()); - return std::move(buffersize); - } - - ErrorCode SaveConfig(std::ostream& p_configout) const; - ErrorCode SaveIndexData(const std::string& p_folderPath); - ErrorCode SaveIndexData(const std::vector& p_indexStreams); - - ErrorCode LoadConfig(Helper::IniReader& p_reader); - ErrorCode LoadIndexData(const std::string& p_folderPath); - ErrorCode LoadIndexDataFromMemory(const std::vector& p_indexBlobs); - - ErrorCode BuildIndex(const void* p_data, SizeType p_vectorNum, DimensionType p_dimension); - ErrorCode SearchIndex(QueryResult &p_query) const; - ErrorCode AddIndex(const void* p_vectors, SizeType p_vectorNum, DimensionType p_dimension, SizeType* p_start = nullptr); - ErrorCode DeleteIndex(const void* p_vectors, SizeType p_vectorNum); - ErrorCode DeleteIndex(const SizeType& p_id); - - ErrorCode SetParameter(const char* p_param, const char* p_value); - std::string GetParameter(const char* p_param) const; - - ErrorCode RefineIndex(const std::string& p_folderPath); - ErrorCode RefineIndex(const std::vector& p_indexStreams); - - private: - void SearchIndexWithDeleted(COMMON::QueryResultSet &p_query, COMMON::WorkSpace &p_space, const Helper::Concurrent::ConcurrentSet &p_deleted) const; - void SearchIndexWithoutDeleted(COMMON::QueryResultSet &p_query, COMMON::WorkSpace &p_space) const; - }; - } // namespace KDT -} // namespace SPTAG - -#endif // _SPTAG_KDT_INDEX_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/KDT/ParameterDefinitionList.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/KDT/ParameterDefinitionList.h deleted file mode 100644 index c36cb178c1..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/KDT/ParameterDefinitionList.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifdef DefineKDTParameter - -// DefineKDTParameter(VarName, VarType, DefaultValue, RepresentStr) -DefineKDTParameter(m_sKDTFilename, std::string, std::string("tree.bin"), "TreeFilePath") -DefineKDTParameter(m_sGraphFilename, std::string, std::string("graph.bin"), "GraphFilePath") -DefineKDTParameter(m_sDataPointsFilename, std::string, std::string("vectors.bin"), "VectorFilePath") -DefineKDTParameter(m_sDeleteDataPointsFilename, std::string, std::string("deletes.bin"), "DeleteVectorFilePath") - -DefineKDTParameter(m_pTrees.m_iTreeNumber, int, 1L, "KDTNumber") -DefineKDTParameter(m_pTrees.m_numTopDimensionKDTSplit, int, 5L, "NumTopDimensionKDTSplit") -DefineKDTParameter(m_pTrees.m_iSamples, int, 100L, "Samples") - -DefineKDTParameter(m_pGraph.m_iTPTNumber, int, 32L, "TPTNumber") -DefineKDTParameter(m_pGraph.m_iTPTLeafSize, int, 2000L, "TPTLeafSize") -DefineKDTParameter(m_pGraph.m_numTopDimensionTPTSplit, int, 5L, "NumTopDimensionTPTSplit") - -DefineKDTParameter(m_pGraph.m_iNeighborhoodSize, DimensionType, 32L, "NeighborhoodSize") -DefineKDTParameter(m_pGraph.m_iNeighborhoodScale, int, 2L, "GraphNeighborhoodScale") -DefineKDTParameter(m_pGraph.m_iCEFScale, int, 2L, "GraphCEFScale") -DefineKDTParameter(m_pGraph.m_iRefineIter, int, 0L, "RefineIterations") -DefineKDTParameter(m_pGraph.m_iCEF, int, 1000L, "CEF") -DefineKDTParameter(m_pGraph.m_iMaxCheckForRefineGraph, int, 10000L, "MaxCheckForRefineGraph") - -DefineKDTParameter(m_iNumberOfThreads, int, 1L, "NumberOfThreads") -DefineKDTParameter(m_iDistCalcMethod, SPTAG::DistCalcMethod, SPTAG::DistCalcMethod::Cosine, "DistCalcMethod") - -DefineKDTParameter(m_fDeletePercentageForRefine, float, 0.4F, "DeletePercentageForRefine") -DefineKDTParameter(m_iMaxCheck, int, 8192L, "MaxCheck") -DefineKDTParameter(m_iThresholdOfNumberOfContinuousNoBetterPropagation, int, 3L, "ThresholdOfNumberOfContinuousNoBetterPropagation") -DefineKDTParameter(m_iNumberOfInitialDynamicPivots, int, 50L, "NumberOfInitialDynamicPivots") -DefineKDTParameter(m_iNumberOfOtherDynamicPivots, int, 4L, "NumberOfOtherDynamicPivots") - -#endif diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/MetadataSet.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/MetadataSet.h deleted file mode 100644 index 37eba14491..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/MetadataSet.h +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_METADATASET_H_ -#define _SPTAG_METADATASET_H_ - -#include "CommonDataStructure.h" - -#include -#include - -namespace SPTAG -{ - -class MetadataSet -{ -public: - MetadataSet(); - - virtual ~MetadataSet(); - - virtual ByteArray GetMetadata(SizeType p_vectorID) const = 0; - - virtual SizeType Count() const = 0; - - virtual bool Available() const = 0; - - virtual std::pair BufferSize() const = 0; - - virtual void AddBatch(MetadataSet& data) = 0; - - virtual ErrorCode SaveMetadata(std::ostream& p_metaOut, std::ostream& p_metaIndexOut) = 0; - - virtual ErrorCode SaveMetadata(const std::string& p_metaFile, const std::string& p_metaindexFile) = 0; - - virtual ErrorCode RefineMetadata(std::vector& indices, std::ostream& p_metaOut, std::ostream& p_metaIndexOut); - - virtual ErrorCode RefineMetadata(std::vector& indices, const std::string& p_metaFile, const std::string& p_metaindexFile); -}; - - -class FileMetadataSet : public MetadataSet -{ -public: - FileMetadataSet(const std::string& p_metaFile, const std::string& p_metaindexFile); - - ~FileMetadataSet(); - - ByteArray GetMetadata(SizeType p_vectorID) const; - - SizeType Count() const; - - bool Available() const; - - std::pair BufferSize() const; - - void AddBatch(MetadataSet& data); - - ErrorCode SaveMetadata(std::ostream& p_metaOut, std::ostream& p_metaIndexOut); - - ErrorCode SaveMetadata(const std::string& p_metaFile, const std::string& p_metaindexFile); - -private: - std::ifstream* m_fp = nullptr; - - std::vector m_pOffsets; - - SizeType m_count; - - std::string m_metaFile; - - std::string m_metaindexFile; - - std::vector m_newdata; -}; - - -class MemMetadataSet : public MetadataSet -{ -public: - MemMetadataSet(ByteArray p_metadata, ByteArray p_offsets, SizeType p_count); - - ~MemMetadataSet(); - - ByteArray GetMetadata(SizeType p_vectorID) const; - - SizeType Count() const; - - bool Available() const; - - std::pair BufferSize() const; - - void AddBatch(MetadataSet& data); - - ErrorCode SaveMetadata(std::ostream& p_metaOut, std::ostream& p_metaIndexOut); - - ErrorCode SaveMetadata(const std::string& p_metaFile, const std::string& p_metaindexFile); - -private: - std::vector m_offsets; - - SizeType m_count; - - ByteArray m_metadataHolder; - - ByteArray m_offsetHolder; - - std::vector m_newdata; -}; - - -} // namespace SPTAG - -#endif // _SPTAG_METADATASET_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/SearchQuery.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/SearchQuery.h deleted file mode 100644 index 017b1e2e01..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/SearchQuery.h +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_SEARCHQUERY_H_ -#define _SPTAG_SEARCHQUERY_H_ - -#include "SearchResult.h" - -#include - -namespace SPTAG -{ - -// Space to save temporary answer, similar with TopKCache -class QueryResult -{ -public: - typedef BasicResult* iterator; - typedef const BasicResult* const_iterator; - - QueryResult() - : m_target(nullptr), - m_resultNum(0), - m_withMeta(false) - { - } - - - QueryResult(const void* p_target, int p_resultNum, bool p_withMeta) - { - Init(p_target, p_resultNum, p_withMeta); - } - - - QueryResult(const void* p_target, int p_resultNum, bool p_withMeta, BasicResult* p_results) - : m_target(p_target), - m_resultNum(p_resultNum), - m_withMeta(p_withMeta) - { - m_results.Set(p_results, p_resultNum, false); - } - - - QueryResult(const QueryResult& p_other) - { - Init(p_other.m_target, p_other.m_resultNum, p_other.m_withMeta); - if (m_resultNum > 0) - { - std::copy(p_other.m_results.Data(), p_other.m_results.Data() + m_resultNum, m_results.Data()); - } - } - - - QueryResult& operator=(const QueryResult& p_other) - { - Init(p_other.m_target, p_other.m_resultNum, p_other.m_withMeta); - if (m_resultNum > 0) - { - std::copy(p_other.m_results.Data(), p_other.m_results.Data() + m_resultNum, m_results.Data()); - } - - return *this; - } - - - ~QueryResult() - { - } - - - inline void Init(const void* p_target, int p_resultNum, bool p_withMeta) - { - m_target = p_target; - m_resultNum = p_resultNum; - m_withMeta = p_withMeta; - - m_results = Array::Alloc(p_resultNum); - } - - - inline int GetResultNum() const - { - return m_resultNum; - } - - - inline const void* GetTarget() - { - return m_target; - } - - - inline void SetTarget(const void* p_target) - { - m_target = p_target; - } - - - inline BasicResult* GetResult(int i) const - { - return i < m_resultNum ? m_results.Data() + i : nullptr; - } - - - inline void SetResult(int p_index, SizeType p_VID, float p_dist) - { - if (p_index < m_resultNum) - { - m_results[p_index].VID = p_VID; - m_results[p_index].Dist = p_dist; - } - } - - - inline BasicResult* GetResults() const - { - return m_results.Data(); - } - - - inline bool WithMeta() const - { - return m_withMeta; - } - - - inline const ByteArray& GetMetadata(int p_index) const - { - if (p_index < m_resultNum && m_withMeta) - { - return m_results[p_index].Meta; - } - - return ByteArray::c_empty; - } - - - inline void SetMetadata(int p_index, ByteArray p_metadata) - { - if (p_index < m_resultNum && m_withMeta) - { - m_results[p_index].Meta = std::move(p_metadata); - } - } - - - inline void Reset() - { - for (int i = 0; i < m_resultNum; i++) - { - m_results[i].VID = -1; - m_results[i].Dist = MaxDist; - m_results[i].Meta.Clear(); - } - } - - - iterator begin() - { - return m_results.Data(); - } - - - iterator end() - { - return m_results.Data() + m_resultNum; - } - - - const_iterator begin() const - { - return m_results.Data(); - } - - - const_iterator end() const - { - return m_results.Data() + m_resultNum; - } - - -protected: - const void* m_target; - - int m_resultNum; - - bool m_withMeta; - - Array m_results; -}; -} // namespace SPTAG - -#endif // _SPTAG_SEARCHQUERY_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/SearchResult.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/SearchResult.h deleted file mode 100644 index 64e173030b..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/SearchResult.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_SEARCHRESULT_H_ -#define _SPTAG_SEARCHRESULT_H_ - -#include "CommonDataStructure.h" - -namespace SPTAG -{ - struct BasicResult - { - SizeType VID; - float Dist; - ByteArray Meta; - - BasicResult() : VID(-1), Dist(MaxDist) {} - - BasicResult(SizeType p_vid, float p_dist) : VID(p_vid), Dist(p_dist) {} - - BasicResult(SizeType p_vid, float p_dist, ByteArray p_meta) : VID(p_vid), Dist(p_dist), Meta(p_meta) {} - }; - -} // namespace SPTAG - -#endif // _SPTAG_SEARCHRESULT_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/VectorIndex.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/VectorIndex.h deleted file mode 100644 index b93caf0a9e..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/VectorIndex.h +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_VECTORINDEX_H_ -#define _SPTAG_VECTORINDEX_H_ - -#include "Common.h" -#include "SearchQuery.h" -#include "VectorSet.h" -#include "MetadataSet.h" -#include "inc/Helper/SimpleIniReader.h" - -#include - -namespace SPTAG -{ - -class VectorIndex -{ -public: - VectorIndex(); - - virtual ~VectorIndex(); - - virtual ErrorCode BuildIndex(const void* p_data, SizeType p_vectorNum, DimensionType p_dimension) = 0; - - virtual ErrorCode AddIndex(const void* p_vectors, SizeType p_vectorNum, DimensionType p_dimension, SizeType* p_start = nullptr) = 0; - - virtual ErrorCode DeleteIndex(const void* p_vectors, SizeType p_vectorNum) = 0; - - virtual ErrorCode SearchIndex(QueryResult& p_results) const = 0; - - virtual float ComputeDistance(const void* pX, const void* pY) const = 0; - virtual const void* GetSample(const SizeType idx) const = 0; - virtual bool ContainSample(const SizeType idx) const = 0; - virtual bool NeedRefine() const = 0; - - virtual DimensionType GetFeatureDim() const = 0; - virtual SizeType GetNumSamples() const = 0; - virtual SizeType GetIndexSize() const = 0; - - virtual DistCalcMethod GetDistCalcMethod() const = 0; - virtual IndexAlgoType GetIndexAlgoType() const = 0; - virtual VectorValueType GetVectorValueType() const = 0; - - virtual std::string GetParameter(const char* p_param) const = 0; - virtual ErrorCode SetParameter(const char* p_param, const char* p_value) = 0; - - virtual std::shared_ptr> CalculateBufferSize() const; - - virtual ErrorCode LoadIndex(const std::string& p_config, const std::vector& p_indexBlobs); - - virtual ErrorCode LoadIndex(const std::string& p_folderPath); - - virtual ErrorCode SaveIndex(std::string& p_config, const std::vector& p_indexBlobs); - - virtual ErrorCode SaveIndex(const std::string& p_folderPath); - - virtual ErrorCode BuildIndex(std::shared_ptr p_vectorSet, std::shared_ptr p_metadataSet, bool p_withMetaIndex = false); - - virtual ErrorCode AddIndex(std::shared_ptr p_vectorSet, std::shared_ptr p_metadataSet); - - virtual ErrorCode DeleteIndex(ByteArray p_meta); - - virtual const void* GetSample(ByteArray p_meta); - - virtual ErrorCode SearchIndex(const void* p_vector, int p_neighborCount, bool p_withMeta, BasicResult* p_results) const; - - virtual std::string GetParameter(const std::string& p_param) const; - virtual ErrorCode SetParameter(const std::string& p_param, const std::string& p_value); - - virtual ByteArray GetMetadata(SizeType p_vectorID) const; - virtual void SetMetadata(const std::string& p_metadataFilePath, const std::string& p_metadataIndexPath); - - virtual std::string GetIndexName() const - { - if (m_sIndexName == "") return Helper::Convert::ConvertToString(GetIndexAlgoType()); - return m_sIndexName; - } - virtual void SetIndexName(std::string p_name) { m_sIndexName = p_name; } - - static std::shared_ptr CreateInstance(IndexAlgoType p_algo, VectorValueType p_valuetype); - - static ErrorCode MergeIndex(const char* p_indexFilePath1, const char* p_indexFilePath2); - - static ErrorCode LoadIndex(const std::string& p_loaderFilePath, std::shared_ptr& p_vectorIndex); - - static ErrorCode LoadIndex(const std::string& p_config, const std::vector& p_indexBlobs, std::shared_ptr& p_vectorIndex); - -protected: - virtual std::shared_ptr> BufferSize() const = 0; - - virtual ErrorCode SaveConfig(std::ostream& p_configout) const = 0; - - virtual ErrorCode SaveIndexData(const std::string& p_folderPath) = 0; - - virtual ErrorCode SaveIndexData(const std::vector& p_indexStreams) = 0; - - virtual ErrorCode LoadConfig(Helper::IniReader& p_reader) = 0; - - virtual ErrorCode LoadIndexData(const std::string& p_folderPath) = 0; - - virtual ErrorCode LoadIndexDataFromMemory(const std::vector& p_indexBlobs) = 0; - - virtual ErrorCode DeleteIndex(const SizeType& p_id) = 0; - - virtual ErrorCode RefineIndex(const std::string& p_folderPath) = 0; - - virtual ErrorCode RefineIndex(const std::vector& p_indexStreams) = 0; - -private: - void BuildMetaMapping(); - - ErrorCode LoadIndexConfig(Helper::IniReader& p_reader); - - ErrorCode SaveIndexConfig(std::ostream& p_configOut); - -protected: - std::string m_sIndexName; - std::string m_sMetadataFile = "metadata.bin"; - std::string m_sMetadataIndexFile = "metadataIndex.bin"; - std::shared_ptr m_pMetadata; - std::unique_ptr> m_pMetaToVec; -}; - - -} // namespace SPTAG - -#endif // _SPTAG_VECTORINDEX_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/VectorSet.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/VectorSet.h deleted file mode 100644 index c394c701ff..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Core/VectorSet.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_VECTORSET_H_ -#define _SPTAG_VECTORSET_H_ - -#include "CommonDataStructure.h" - -namespace SPTAG -{ - -class VectorSet -{ -public: - VectorSet(); - - virtual ~VectorSet(); - - virtual VectorValueType GetValueType() const = 0; - - virtual void* GetVector(SizeType p_vectorID) const = 0; - - virtual void* GetData() const = 0; - - virtual DimensionType Dimension() const = 0; - - virtual SizeType Count() const = 0; - - virtual bool Available() const = 0; - - virtual ErrorCode Save(const std::string& p_vectorFile) const = 0; -}; - - -class BasicVectorSet : public VectorSet -{ -public: - BasicVectorSet(const ByteArray& p_bytesArray, - VectorValueType p_valueType, - DimensionType p_dimension, - SizeType p_vectorCount); - - virtual ~BasicVectorSet(); - - virtual VectorValueType GetValueType() const; - - virtual void* GetVector(SizeType p_vectorID) const; - - virtual void* GetData() const; - - virtual DimensionType Dimension() const; - - virtual SizeType Count() const; - - virtual bool Available() const; - - virtual ErrorCode Save(const std::string& p_vectorFile) const; - -private: - ByteArray m_data; - - VectorValueType m_valueType; - - DimensionType m_dimension; - - SizeType m_vectorCount; - - SizeType m_perVectorDataSize; -}; - -} // namespace SPTAG - -#endif // _SPTAG_VECTORSET_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/ArgumentsParser.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/ArgumentsParser.h deleted file mode 100644 index 0ae19b8e8f..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/ArgumentsParser.h +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_HELPER_ARGUMENTSPARSER_H_ -#define _SPTAG_HELPER_ARGUMENTSPARSER_H_ - -#include "inc/Helper/StringConvert.h" - -#include -#include -#include -#include -#include - -namespace SPTAG -{ -namespace Helper -{ - -class ArgumentsParser -{ -public: - ArgumentsParser(); - - virtual ~ArgumentsParser(); - - virtual bool Parse(int p_argc, char** p_args); - - virtual void PrintHelp(); - -protected: - class IArgument - { - public: - IArgument(); - - virtual ~IArgument(); - - virtual bool ParseValue(int& p_restArgc, char** (&p_args)) = 0; - - virtual void PrintDescription(FILE* p_output) = 0; - - virtual bool IsRequiredButNotSet() const = 0; - }; - - - template - class ArgumentT : public IArgument - { - public: - ArgumentT(DataType& p_target, - const std::string& p_representStringShort, - const std::string& p_representString, - const std::string& p_description, - bool p_followedValue, - const DataType& p_switchAsValue, - bool p_isRequired) - : m_value(p_target), - m_representStringShort(p_representStringShort), - m_representString(p_representString), - m_description(p_description), - m_followedValue(p_followedValue), - c_switchAsValue(p_switchAsValue), - m_isRequired(p_isRequired), - m_isSet(false) - { - } - - virtual ~ArgumentT() - { - } - - - virtual bool ParseValue(int& p_restArgc, char** (&p_args)) - { - if (0 == p_restArgc) - { - return true; - } - - if (0 != strcmp(*p_args, m_representString.c_str()) - && 0 != strcmp(*p_args, m_representStringShort.c_str())) - { - return true; - } - - if (!m_followedValue) - { - m_value = c_switchAsValue; - --p_restArgc; - ++p_args; - m_isSet = true; - return true; - } - - if (p_restArgc < 2) - { - return false; - } - - DataType tmp; - if (!Helper::Convert::ConvertStringTo(p_args[1], tmp)) - { - return false; - } - - m_value = std::move(tmp); - - p_restArgc -= 2; - p_args += 2; - m_isSet = true; - return true; - } - - - virtual void PrintDescription(FILE* p_output) - { - std::size_t padding = 30; - if (!m_representStringShort.empty()) - { - fprintf(p_output, "%s", m_representStringShort.c_str()); - padding -= m_representStringShort.size(); - } - - if (!m_representString.empty()) - { - if (!m_representStringShort.empty()) - { - fprintf(p_output, ", "); - padding -= 2; - } - - fprintf(p_output, "%s", m_representString.c_str()); - padding -= m_representString.size(); - } - - if (m_followedValue) - { - fprintf(p_output, " "); - padding -= 8; - } - - while (padding-- > 0) - { - fputc(' ', p_output); - } - - fprintf(p_output, "%s", m_description.c_str()); - } - - - virtual bool IsRequiredButNotSet() const - { - return m_isRequired && !m_isSet; - } - - private: - DataType & m_value; - - std::string m_representStringShort; - - std::string m_representString; - - std::string m_description; - - bool m_followedValue; - - const DataType c_switchAsValue; - - bool m_isRequired; - - bool m_isSet; - }; - - - template - void AddRequiredOption(DataType& p_target, - const std::string& p_representStringShort, - const std::string& p_representString, - const std::string& p_description) - { - m_arguments.emplace_back(std::shared_ptr( - new ArgumentT(p_target, - p_representStringShort, - p_representString, - p_description, - true, - DataType(), - true))); - } - - - template - void AddOptionalOption(DataType& p_target, - const std::string& p_representStringShort, - const std::string& p_representString, - const std::string& p_description) - { - m_arguments.emplace_back(std::shared_ptr( - new ArgumentT(p_target, - p_representStringShort, - p_representString, - p_description, - true, - DataType(), - false))); - } - - - template - void AddRequiredSwitch(DataType& p_target, - const std::string& p_representStringShort, - const std::string& p_representString, - const std::string& p_description, - const DataType& p_switchAsValue) - { - m_arguments.emplace_back(std::shared_ptr( - new ArgumentT(p_target, - p_representStringShort, - p_representString, - p_description, - false, - p_switchAsValue, - true))); - } - - - template - void AddOptionalSwitch(DataType& p_target, - const std::string& p_representStringShort, - const std::string& p_representString, - const std::string& p_description, - const DataType& p_switchAsValue) - { - m_arguments.emplace_back(std::shared_ptr( - new ArgumentT(p_target, - p_representStringShort, - p_representString, - p_description, - false, - p_switchAsValue, - false))); - } - -private: - std::vector> m_arguments; -}; - - -} // namespace Helper -} // namespace SPTAG - -#endif // _SPTAG_HELPER_ARGUMENTSPARSER_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/Base64Encode.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/Base64Encode.h deleted file mode 100644 index 8e7919345d..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/Base64Encode.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_HELPER_BASE64ENCODE_H_ -#define _SPTAG_HELPER_BASE64ENCODE_H_ - -#include -#include -#include - -namespace SPTAG -{ -namespace Helper -{ -namespace Base64 -{ - -bool Encode(const std::uint8_t* p_in, std::size_t p_inLen, char* p_out, std::size_t& p_outLen); - -bool Encode(const std::uint8_t* p_in, std::size_t p_inLen, std::ostream& p_out, std::size_t& p_outLen); - -bool Decode(const char* p_in, std::size_t p_inLen, std::uint8_t* p_out, std::size_t& p_outLen); - -std::size_t CapacityForEncode(std::size_t p_inLen); - -std::size_t CapacityForDecode(std::size_t p_inLen); - - -} // namespace Base64 -} // namespace Helper -} // namespace SPTAG - -#endif // _SPTAG_HELPER_BASE64ENCODE_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/BufferStream.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/BufferStream.h deleted file mode 100644 index c97be04f12..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/BufferStream.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_HELPER_BUFFERSTREAM_H_ -#define _SPTAG_HELPER_BUFFERSTREAM_H_ - -#include -#include -#include - -namespace SPTAG -{ - namespace Helper - { - struct streambuf : public std::basic_streambuf - { - streambuf(char* buffer, size_t size) - { - setp(buffer, buffer + size); - } - }; - - class obufferstream : public std::ostream - { - public: - obufferstream(streambuf* buf, bool transferOwnership) : std::ostream(buf) - { - if (transferOwnership) - m_bufHolder.reset(buf, std::default_delete()); - } - - private: - std::shared_ptr m_bufHolder; - }; - } // namespace Helper -} // namespace SPTAG - -#endif // _SPTAG_HELPER_BUFFERSTREAM_H_ - diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/CommonHelper.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/CommonHelper.h deleted file mode 100644 index 7f14784707..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/CommonHelper.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_HELPER_COMMONHELPER_H_ -#define _SPTAG_HELPER_COMMONHELPER_H_ - -#include "../Core/Common.h" - -#include -#include -#include -#include -#include -#include - - -namespace SPTAG -{ -namespace Helper -{ -namespace StrUtils -{ - -void ToLowerInPlace(std::string& p_str); - -std::vector SplitString(const std::string& p_str, const std::string& p_separator); - -std::pair FindTrimmedSegment(const char* p_begin, - const char* p_end, - const std::function& p_isSkippedChar); - -bool StartsWith(const char* p_str, const char* p_prefix); - -bool StrEqualIgnoreCase(const char* p_left, const char* p_right); - -} // namespace StrUtils -} // namespace Helper -} // namespace SPTAG - -#endif // _SPTAG_HELPER_COMMONHELPER_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/Concurrent.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/Concurrent.h deleted file mode 100644 index 35c7cc93e4..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/Concurrent.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_HELPER_CONCURRENT_H_ -#define _SPTAG_HELPER_CONCURRENT_H_ - - -#include -#include -#include - - -namespace SPTAG -{ -namespace Helper -{ -namespace Concurrent -{ - -class SpinLock -{ -public: - SpinLock() = default; - - void Lock() noexcept - { - while (m_lock.test_and_set(std::memory_order_acquire)) - { - } - } - - void Unlock() noexcept - { - m_lock.clear(std::memory_order_release); - } - - SpinLock(const SpinLock&) = delete; - SpinLock& operator = (const SpinLock&) = delete; - -private: - std::atomic_flag m_lock = ATOMIC_FLAG_INIT; -}; - -template -class LockGuard { -public: - LockGuard(Lock& lock) noexcept - : m_lock(lock) { - lock.Lock(); - } - - LockGuard(Lock& lock, std::adopt_lock_t) noexcept - : m_lock(lock) {} - - ~LockGuard() { - m_lock.Unlock(); - } - - LockGuard(const LockGuard&) = delete; - LockGuard& operator=(const LockGuard&) = delete; - -private: - Lock& m_lock; -}; - - -class WaitSignal -{ -public: - WaitSignal(); - - WaitSignal(std::uint32_t p_unfinished); - - ~WaitSignal(); - - void Reset(std::uint32_t p_unfinished); - - void Wait(); - - void FinishOne(); - -private: - std::atomic m_unfinished; - - std::atomic_bool m_isWaiting; - - std::mutex m_mutex; - - std::condition_variable m_cv; -}; - - -} // namespace Base64 -} // namespace Helper -} // namespace SPTAG - -#endif // _SPTAG_HELPER_CONCURRENT_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/ConcurrentSet.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/ConcurrentSet.h deleted file mode 100644 index 61254dc2eb..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/ConcurrentSet.h +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_HELPER_CONCURRENTSET_H_ -#define _SPTAG_HELPER_CONCURRENTSET_H_ - -#include -#include - -namespace SPTAG -{ - namespace Helper - { - namespace Concurrent - { - template - class ConcurrentSet - { - public: - ConcurrentSet(); - - ~ConcurrentSet(); - - size_t size() const; - - bool contains(const T& key) const; - - void insert(const T& key); - - std::shared_timed_mutex& getLock(); - - bool save(std::ostream& output); - - bool save(std::string filename); - - bool load(std::string filename); - - bool load(char* pmemoryFile); - - std::uint64_t bufferSize() const; - - private: - std::unique_ptr m_lock; - std::unordered_set m_data; - }; - - template - ConcurrentSet::ConcurrentSet() - { - m_lock.reset(new std::shared_timed_mutex); - } - - template - ConcurrentSet::~ConcurrentSet() - { - } - - template - size_t ConcurrentSet::size() const - { - std::shared_lock lock(*m_lock); - return m_data.size(); - } - - template - bool ConcurrentSet::contains(const T& key) const - { - std::shared_lock lock(*m_lock); - return (m_data.find(key) != m_data.end()); - } - - template - void ConcurrentSet::insert(const T& key) - { - std::unique_lock lock(*m_lock); - m_data.insert(key); - } - - template - std::shared_timed_mutex& ConcurrentSet::getLock() - { - return *m_lock; - } - - template - std::uint64_t ConcurrentSet::bufferSize() const - { - return sizeof(SizeType) + sizeof(T) * m_data.size(); - } - - template - bool ConcurrentSet::save(std::ostream& output) - { - SizeType count = (SizeType)m_data.size(); - output.write((char*)&count, sizeof(SizeType)); - for (auto iter = m_data.begin(); iter != m_data.end(); iter++) - output.write((char*)&(*iter), sizeof(T)); - std::cout << "Save DeleteID (" << count << ") Finish!" << std::endl; - return true; - } - - template - bool ConcurrentSet::save(std::string filename) - { - std::cout << "Save DeleteID To " << filename << std::endl; - std::ofstream output(filename, std::ios::binary); - if (!output.is_open()) return false; - save(output); - output.close(); - return true; - } - - template - bool ConcurrentSet::load(std::string filename) - { - std::cout << "Load DeleteID From " << filename << std::endl; - std::ifstream input(filename, std::ios::binary); - if (!input.is_open()) return false; - - SizeType count; - T ID; - input.read((char*)&count, sizeof(SizeType)); - for (SizeType i = 0; i < count; i++) - { - input.read((char*)&ID, sizeof(T)); - m_data.insert(ID); - } - input.close(); - std::cout << "Load DeleteID (" << count << ") Finish!" << std::endl; - return true; - } - - template - bool ConcurrentSet::load(char* pmemoryFile) - { - SizeType count; - count = *((SizeType*)pmemoryFile); - pmemoryFile += sizeof(SizeType); - - m_data.insert((T*)pmemoryFile, ((T*)pmemoryFile) + count); - pmemoryFile += sizeof(T) * count; - std::cout << "Load DeleteID (" << count << ") Finish!" << std::endl; - return true; - } - } - } -} -#endif // _SPTAG_HELPER_CONCURRENTSET_H_ \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/SimpleIniReader.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/SimpleIniReader.h deleted file mode 100644 index ad8d58f6f7..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/SimpleIniReader.h +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_HELPER_INIREADER_H_ -#define _SPTAG_HELPER_INIREADER_H_ - -#include "../Core/Common.h" -#include "StringConvert.h" - -#include -#include -#include -#include -#include - - -namespace SPTAG -{ -namespace Helper -{ - -// Simple INI Reader with basic functions. Case insensitive. -class IniReader -{ -public: - typedef std::map ParameterValueMap; - - IniReader(); - - ~IniReader(); - - ErrorCode LoadIniFile(const std::string& p_iniFilePath); - - ErrorCode LoadIni(std::istream& p_input); - - bool DoesSectionExist(const std::string& p_section) const; - - bool DoesParameterExist(const std::string& p_section, const std::string& p_param) const; - - const ParameterValueMap& GetParameters(const std::string& p_section) const; - - template - DataType GetParameter(const std::string& p_section, const std::string& p_param, const DataType& p_defaultVal) const; - - void SetParameter(const std::string& p_section, const std::string& p_param, const std::string& p_val); - -private: - bool GetRawValue(const std::string& p_section, const std::string& p_param, std::string& p_value) const; - - template - static inline DataType ConvertStringTo(std::string&& p_str, const DataType& p_defaultVal); - -private: - const static ParameterValueMap c_emptyParameters; - - std::map> m_parameters; -}; - - -template -DataType -IniReader::GetParameter(const std::string& p_section, const std::string& p_param, const DataType& p_defaultVal) const -{ - std::string value; - if (!GetRawValue(p_section, p_param, value)) - { - return p_defaultVal; - } - - return ConvertStringTo(std::move(value), p_defaultVal); -} - - -template -inline DataType -IniReader::ConvertStringTo(std::string&& p_str, const DataType& p_defaultVal) -{ - DataType value; - if (Convert::ConvertStringTo(p_str.c_str(), value)) - { - return value; - } - - return p_defaultVal; -} - - -template <> -inline std::string -IniReader::ConvertStringTo(std::string&& p_str, const std::string& p_defaultVal) -{ - return std::move(p_str); -} - - -} // namespace Helper -} // namespace SPTAG - -#endif // _SPTAG_HELPER_INIREADER_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/StringConvert.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/StringConvert.h deleted file mode 100644 index b6e53df785..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/StringConvert.h +++ /dev/null @@ -1,374 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_HELPER_STRINGCONVERTHELPER_H_ -#define _SPTAG_HELPER_STRINGCONVERTHELPER_H_ - -#include "inc/Core/Common.h" -#include "CommonHelper.h" - -#include -#include -#include -#include -#include -#include - -namespace SPTAG -{ -namespace Helper -{ -namespace Convert -{ - -template -inline bool ConvertStringTo(const char* p_str, DataType& p_value) -{ - if (nullptr == p_str) - { - return false; - } - - std::istringstream sstream; - sstream.str(p_str); - if (p_str >> p_value) - { - return true; - } - - return false; -} - - -template -inline std::string ConvertToString(const DataType& p_value) -{ - return std::to_string(p_value); -} - - -// Specialization of ConvertStringTo<>(). - -template -inline bool ConvertStringToSignedInt(const char* p_str, DataType& p_value) -{ - static_assert(std::is_integral::value && std::is_signed::value, "type check"); - - if (nullptr == p_str) - { - return false; - } - - char* end = nullptr; - errno = 0; - auto val = std::strtoll(p_str, &end, 10); - if (errno == ERANGE || end == p_str || *end != '\0') - { - return false; - } - - if (val < (std::numeric_limits::min)() || val >(std::numeric_limits::max)()) - { - return false; - } - - p_value = static_cast(val); - return true; -} - - -template -inline bool ConvertStringToUnsignedInt(const char* p_str, DataType& p_value) -{ - static_assert(std::is_integral::value && std::is_unsigned::value, "type check"); - - if (nullptr == p_str) - { - return false; - } - - char* end = nullptr; - errno = 0; - auto val = std::strtoull(p_str, &end, 10); - if (errno == ERANGE || end == p_str || *end != '\0') - { - return false; - } - - if (val < (std::numeric_limits::min)() || val >(std::numeric_limits::max)()) - { - return false; - } - - p_value = static_cast(val); - return true; -} - - -template <> -inline bool ConvertStringTo(const char* p_str, std::string& p_value) -{ - if (nullptr == p_str) - { - return false; - } - - p_value = p_str; - return true; -} - - -template <> -inline bool ConvertStringTo(const char* p_str, float& p_value) -{ - if (nullptr == p_str) - { - return false; - } - - char* end = nullptr; - errno = 0; - p_value = std::strtof(p_str, &end); - return (errno != ERANGE && end != p_str && *end == '\0'); -} - - -template <> -inline bool ConvertStringTo(const char* p_str, double& p_value) -{ - if (nullptr == p_str) - { - return false; - } - - char* end = nullptr; - errno = 0; - p_value = std::strtod(p_str, &end); - return (errno != ERANGE && end != p_str && *end == '\0'); -} - - -template <> -inline bool ConvertStringTo(const char* p_str, std::int8_t& p_value) -{ - return ConvertStringToSignedInt(p_str, p_value); -} - - -template <> -inline bool ConvertStringTo(const char* p_str, std::int16_t& p_value) -{ - return ConvertStringToSignedInt(p_str, p_value); -} - - -template <> -inline bool ConvertStringTo(const char* p_str, std::int32_t& p_value) -{ - return ConvertStringToSignedInt(p_str, p_value); -} - - -template <> -inline bool ConvertStringTo(const char* p_str, std::int64_t& p_value) -{ - return ConvertStringToSignedInt(p_str, p_value); -} - - -template <> -inline bool ConvertStringTo(const char* p_str, std::uint8_t& p_value) -{ - return ConvertStringToUnsignedInt(p_str, p_value); -} - - -template <> -inline bool ConvertStringTo(const char* p_str, std::uint16_t& p_value) -{ - return ConvertStringToUnsignedInt(p_str, p_value); -} - - -template <> -inline bool ConvertStringTo(const char* p_str, std::uint32_t& p_value) -{ - return ConvertStringToUnsignedInt(p_str, p_value); -} - - -template <> -inline bool ConvertStringTo(const char* p_str, std::uint64_t& p_value) -{ - return ConvertStringToUnsignedInt(p_str, p_value); -} - - -template <> -inline bool ConvertStringTo(const char* p_str, bool& p_value) -{ - if (StrUtils::StrEqualIgnoreCase(p_str, "true")) - { - p_value = true; - - } - else if (StrUtils::StrEqualIgnoreCase(p_str, "false")) - { - p_value = false; - } - else - { - return false; - } - - return true; -} - - -template <> -inline bool ConvertStringTo(const char* p_str, IndexAlgoType& p_value) -{ - if (nullptr == p_str) - { - return false; - } - -#define DefineIndexAlgo(Name) \ - else if (StrUtils::StrEqualIgnoreCase(p_str, #Name)) \ - { \ - p_value = IndexAlgoType::Name; \ - return true; \ - } \ - -#include "inc/Core/DefinitionList.h" -#undef DefineIndexAlgo - - return false; -} - - -template <> -inline bool ConvertStringTo(const char* p_str, DistCalcMethod& p_value) -{ - if (nullptr == p_str) - { - return false; - } - -#define DefineDistCalcMethod(Name) \ - else if (StrUtils::StrEqualIgnoreCase(p_str, #Name)) \ - { \ - p_value = DistCalcMethod::Name; \ - return true; \ - } \ - -#include "inc/Core/DefinitionList.h" -#undef DefineDistCalcMethod - - return false; -} - - -template <> -inline bool ConvertStringTo(const char* p_str, VectorValueType& p_value) -{ - if (nullptr == p_str) - { - return false; - } - -#define DefineVectorValueType(Name, Type) \ - else if (StrUtils::StrEqualIgnoreCase(p_str, #Name)) \ - { \ - p_value = VectorValueType::Name; \ - return true; \ - } \ - -#include "inc/Core/DefinitionList.h" -#undef DefineVectorValueType - - return false; -} - - -// Specialization of ConvertToString<>(). - -template<> -inline std::string ConvertToString(const std::string& p_value) -{ - return p_value; -} - - -template<> -inline std::string ConvertToString(const bool& p_value) -{ - return p_value ? "true" : "false"; -} - - -template <> -inline std::string ConvertToString(const IndexAlgoType& p_value) -{ - switch (p_value) - { -#define DefineIndexAlgo(Name) \ - case IndexAlgoType::Name: \ - return #Name; \ - -#include "inc/Core/DefinitionList.h" -#undef DefineIndexAlgo - - default: - break; - } - - return "Undefined"; -} - - -template <> -inline std::string ConvertToString(const DistCalcMethod& p_value) -{ - switch (p_value) - { -#define DefineDistCalcMethod(Name) \ - case DistCalcMethod::Name: \ - return #Name; \ - -#include "inc/Core/DefinitionList.h" -#undef DefineDistCalcMethod - - default: - break; - } - - return "Undefined"; -} - - -template <> -inline std::string ConvertToString(const VectorValueType& p_value) -{ - switch (p_value) - { -#define DefineVectorValueType(Name, Type) \ - case VectorValueType::Name: \ - return #Name; \ - -#include "inc/Core/DefinitionList.h" -#undef DefineVectorValueType - - default: - break; - } - - return "Undefined"; -} - - -} // namespace Convert -} // namespace Helper -} // namespace SPTAG - -#endif // _SPTAG_HELPER_STRINGCONVERTHELPER_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/VectorSetReader.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/VectorSetReader.h deleted file mode 100644 index cd148c1d04..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/VectorSetReader.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_HELPER_VECTORSETREADER_H_ -#define _SPTAG_HELPER_VECTORSETREADER_H_ - -#include "inc/Core/Common.h" -#include "inc/Core/VectorSet.h" -#include "inc/Core/MetadataSet.h" -#include "inc/Helper/ArgumentsParser.h" - -#include - -namespace SPTAG -{ -namespace Helper -{ - -class ReaderOptions : public ArgumentsParser -{ -public: - ReaderOptions(VectorValueType p_valueType, DimensionType p_dimension, std::string p_vectorDelimiter = "|", std::uint32_t p_threadNum = 32); - - ~ReaderOptions(); - - std::uint32_t m_threadNum; - - DimensionType m_dimension; - - std::string m_vectorDelimiter; - - SPTAG::VectorValueType m_inputValueType; -}; - -class VectorSetReader -{ -public: - VectorSetReader(std::shared_ptr p_options); - - virtual ~VectorSetReader(); - - virtual ErrorCode LoadFile(const std::string& p_filePath) = 0; - - virtual std::shared_ptr GetVectorSet() const = 0; - - virtual std::shared_ptr GetMetadataSet() const = 0; - - static std::shared_ptr CreateInstance(std::shared_ptr p_options); - -protected: - std::shared_ptr m_options; -}; - - - -} // namespace Helper -} // namespace SPTAG - -#endif // _SPTAG_HELPER_VECTORSETREADER_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/VectorSetReaders/DefaultReader.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/VectorSetReaders/DefaultReader.h deleted file mode 100644 index 52c8404caf..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Helper/VectorSetReaders/DefaultReader.h +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_HELPER_VECTORSETREADERS_DEFAULTREADER_H_ -#define _SPTAG_HELPER_VECTORSETREADERS_DEFAULTREADER_H_ - -#include "../VectorSetReader.h" -#include "inc/Helper/Concurrent.h" - -#include -#include -#include - -namespace SPTAG -{ -namespace Helper -{ - -class DefaultReader : public VectorSetReader -{ -public: - DefaultReader(std::shared_ptr p_options); - - virtual ~DefaultReader(); - - virtual ErrorCode LoadFile(const std::string& p_filePaths); - - virtual std::shared_ptr GetVectorSet() const; - - virtual std::shared_ptr GetMetadataSet() const; - -private: - typedef std::pair FileInfoPair; - - static std::vector GetFileSizes(const std::string& p_filePaths); - - void LoadFileInternal(const std::string& p_filePath, - std::uint32_t p_subtaskID, - std::uint32_t p_fileBlockID, - std::size_t p_fileBlockSize); - - void MergeData(); - - template - bool TranslateVector(char* p_str, DataType* p_vector) - { - DimensionType eleCount = 0; - char* next = p_str; - while ((*next) != '\0') - { - while ((*next) != '\0' && m_options->m_vectorDelimiter.find(*next) == std::string::npos) - { - ++next; - } - - bool reachEnd = ('\0' == (*next)); - *next = '\0'; - if (p_str != next) - { - if (eleCount >= m_options->m_dimension) - { - return false; - } - - if (!Helper::Convert::ConvertStringTo(p_str, p_vector[eleCount++])) - { - return false; - } - } - - if (reachEnd) - { - break; - } - - ++next; - p_str = next; - } - - return eleCount == m_options->m_dimension; - } - -private: - std::uint32_t m_subTaskCount; - - std::size_t m_subTaskBlocksize; - - std::atomic m_totalRecordCount; - - std::atomic m_totalRecordVectorBytes; - - std::vector m_subTaskRecordCount; - - std::string m_vectorOutput; - - std::string m_metadataConentOutput; - - std::string m_metadataIndexOutput; - - Helper::Concurrent::WaitSignal m_waitSignal; -}; - - - -} // namespace Helper -} // namespace SPTAG - -#endif // _SPTAG_HELPER_VECTORSETREADERS_DEFAULT_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/IndexBuilder/Options.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/IndexBuilder/Options.h deleted file mode 100644 index b3b3e21e58..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/IndexBuilder/Options.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_INDEXBUILDER_OPTIONS_H_ -#define _SPTAG_INDEXBUILDER_OPTIONS_H_ - -#include "inc/Core/Common.h" -#include "inc/Helper/VectorSetReader.h" - -#include -#include -#include - -namespace SPTAG -{ -namespace IndexBuilder -{ - -class BuilderOptions : public Helper::ReaderOptions -{ -public: - BuilderOptions(); - - ~BuilderOptions(); - - std::string m_inputFiles; - - std::string m_outputFolder; - - SPTAG::IndexAlgoType m_indexAlgoType; - - std::string m_builderConfigFile; -}; - - -} // namespace IndexBuilder -} // namespace SPTAG - -#endif // _SPTAG_INDEXBUILDER_OPTIONS_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/IndexBuilder/ThreadPool.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/IndexBuilder/ThreadPool.h deleted file mode 100644 index 7256f71ae5..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/IndexBuilder/ThreadPool.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_INDEXBUILDER_THREADPOOL_H_ -#define _SPTAG_INDEXBUILDER_THREADPOOL_H_ - -#include -#include - -namespace SPTAG -{ -namespace IndexBuilder -{ -namespace ThreadPool -{ - -void Init(std::uint32_t p_threadNum); - -bool Queue(std::function p_workItem); - -std::uint32_t CurrentThreadNum(); - -} -} // namespace IndexBuilder -} // namespace SPTAG - -#endif // _SPTAG_INDEXBUILDER_THREADPOOL_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Server/QueryParser.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Server/QueryParser.h deleted file mode 100644 index 9444e40862..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Server/QueryParser.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_SERVER_QUERYPARSER_H_ -#define _SPTAG_SERVER_QUERYPARSER_H_ - -#include "../Core/Common.h" -#include "../Core/CommonDataStructure.h" - -#include - -namespace SPTAG -{ -namespace Service -{ - - -class QueryParser -{ -public: - typedef std::pair OptionPair; - - QueryParser(); - - ~QueryParser(); - - ErrorCode Parse(const std::string& p_query, const char* p_vectorSeparator); - - const std::vector& GetVectorElements() const; - - const std::vector& GetOptions() const; - - const char* GetVectorBase64() const; - - SizeType GetVectorBase64Length() const; - -private: - std::vector m_options; - - std::vector m_vectorElements; - - const char* m_vectorBase64; - - SizeType m_vectorBase64Length; - - ByteArray m_dataHolder; - - static const char* c_defaultVectorSeparator; -}; - - -} // namespace Server -} // namespace AnnService - - -#endif // _SPTAG_SERVER_QUERYPARSER_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Server/SearchExecutionContext.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Server/SearchExecutionContext.h deleted file mode 100644 index cba4df4651..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Server/SearchExecutionContext.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_SERVER_SEARCHEXECUTIONCONTEXT_H_ -#define _SPTAG_SERVER_SEARCHEXECUTIONCONTEXT_H_ - -#include "inc/Core/VectorIndex.h" -#include "inc/Core/SearchQuery.h" -#include "inc/Socket/RemoteSearchQuery.h" -#include "ServiceSettings.h" -#include "QueryParser.h" - -#include -#include -#include - - -namespace SPTAG -{ -namespace Service -{ - -typedef Socket::IndexSearchResult SearchResult; - -class SearchExecutionContext -{ -public: - SearchExecutionContext(const std::shared_ptr& p_serviceSettings); - - ~SearchExecutionContext(); - - ErrorCode ParseQuery(const std::string& p_query); - - ErrorCode ExtractOption(); - - ErrorCode ExtractVector(VectorValueType p_targetType); - - void AddResults(std::string p_indexName, QueryResult& p_results); - - std::vector& GetResults(); - - const std::vector& GetResults() const; - - const ByteArray& GetVector() const; - - const std::vector& GetSelectedIndexNames() const; - - const SizeType GetVectorDimension() const; - - const std::vector& GetOptions() const; - - const SizeType GetResultNum() const; - - const bool GetExtractMetadata() const; - -private: - const std::shared_ptr c_serviceSettings; - - QueryParser m_queryParser; - - std::vector m_indexNames; - - ByteArray m_vector; - - SizeType m_vectorDimension; - - std::vector m_results; - - VectorValueType m_inputValueType; - - bool m_extractMetadata; - - SizeType m_resultNum; -}; - -} // namespace Server -} // namespace AnnService - - -#endif // _SPTAG_SERVER_SEARCHEXECUTIONCONTEXT_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Server/SearchExecutor.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Server/SearchExecutor.h deleted file mode 100644 index 201832651b..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Server/SearchExecutor.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_SERVER_SEARCHEXECUTOR_H_ -#define _SPTAG_SERVER_SEARCHEXECUTOR_H_ - -#include "ServiceContext.h" -#include "ServiceSettings.h" -#include "SearchExecutionContext.h" -#include "QueryParser.h" - -#include -#include -#include - -namespace SPTAG -{ -namespace Service -{ - -class SearchExecutor -{ -public: - typedef std::function)> CallBack; - - SearchExecutor(std::string p_queryString, - std::shared_ptr p_serviceContext, - const CallBack& p_callback); - - ~SearchExecutor(); - - void Execute(); - -private: - void ExecuteInternal(); - - void SelectIndex(); - -private: - CallBack m_callback; - - const std::shared_ptr c_serviceContext; - - std::shared_ptr m_executionContext; - - std::string m_queryString; - - std::vector> m_selectedIndex; -}; - - -} // namespace Server -} // namespace AnnService - - -#endif // _SPTAG_SERVER_SEARCHEXECUTOR_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Server/SearchService.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Server/SearchService.h deleted file mode 100644 index 34d0c6064c..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Server/SearchService.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_SERVER_SERVICE_H_ -#define _SPTAG_SERVER_SERVICE_H_ - -#include "ServiceContext.h" -#include "../Socket/Server.h" - -#include - -#include -#include -#include -#include - -namespace SPTAG -{ -namespace Service -{ - -class SearchExecutionContext; - -class SearchService -{ -public: - SearchService(); - - ~SearchService(); - - bool Initialize(int p_argNum, char* p_args[]); - - void Run(); - -private: - void RunSocketMode(); - - void RunInteractiveMode(); - - void SearchHanlder(Socket::ConnectionID p_localConnectionID, Socket::Packet p_packet); - - void SearchHanlderCallback(std::shared_ptr p_exeContext, - Socket::Packet p_srcPacket); - -private: - enum class ServeMode : std::uint8_t - { - Interactive, - - Socket - }; - - std::shared_ptr m_serviceContext; - - std::shared_ptr m_socketServer; - - bool m_initialized; - - ServeMode m_serveMode; - - std::unique_ptr m_threadPool; - - boost::asio::io_context m_ioContext; - - boost::asio::signal_set m_shutdownSignals; -}; - - -} // namespace Server -} // namespace AnnService - - -#endif // _SPTAG_SERVER_SERVICE_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Server/ServiceContext.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Server/ServiceContext.h deleted file mode 100644 index b1a7b84045..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Server/ServiceContext.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_SERVER_SERVICECONTEX_H_ -#define _SPTAG_SERVER_SERVICECONTEX_H_ - -#include "inc/Core/VectorIndex.h" -#include "ServiceSettings.h" - -#include -#include - -namespace SPTAG -{ -namespace Service -{ - -class ServiceContext -{ -public: - ServiceContext(const std::string& p_configFilePath); - - ~ServiceContext(); - - const std::map>& GetIndexMap() const; - - const std::shared_ptr& GetServiceSettings() const; - - bool IsInitialized() const; - -private: - bool m_initialized; - - std::shared_ptr m_settings; - - std::map> m_fullIndexList; -}; - - -} // namespace Server -} // namespace AnnService - -#endif // _SPTAG_SERVER_SERVICECONTEX_H_ - diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Server/ServiceSettings.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Server/ServiceSettings.h deleted file mode 100644 index 9077487355..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Server/ServiceSettings.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_SERVER_SERVICESTTINGS_H_ -#define _SPTAG_SERVER_SERVICESTTINGS_H_ - -#include "../Core/Common.h" - -#include - -namespace SPTAG -{ -namespace Service -{ - -struct ServiceSettings -{ - ServiceSettings(); - - std::string m_vectorSeparator; - - std::string m_listenAddr; - - std::string m_listenPort; - - SizeType m_defaultMaxResultNumber; - - SizeType m_threadNum; - - SizeType m_socketThreadNum; -}; - - - - -} // namespace Server -} // namespace AnnService - - -#endif // _SPTAG_SERVER_SERVICESTTINGS_H_ - diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Client.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Client.h deleted file mode 100644 index a57465dfd7..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Client.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_SOCKET_CLIENT_H_ -#define _SPTAG_SOCKET_CLIENT_H_ - -#include "inc/Core/Common.h" -#include "Connection.h" -#include "ConnectionManager.h" -#include "Packet.h" - -#include -#include -#include -#include - -namespace SPTAG -{ -namespace Socket -{ - -class Client -{ -public: - typedef std::function ConnectCallback; - - Client(const PacketHandlerMapPtr& p_handlerMap, - std::size_t p_threadNum, - std::uint32_t p_heartbeatIntervalSeconds); - - ~Client(); - - ConnectionID ConnectToServer(const std::string& p_address, - const std::string& p_port, - SPTAG::ErrorCode& p_ec); - - void AsyncConnectToServer(const std::string& p_address, - const std::string& p_port, - ConnectCallback p_callback); - - void SendPacket(ConnectionID p_connection, Packet p_packet, std::function p_callback); - - void SetEventOnConnectionClose(std::function p_event); - -private: - void KeepIoContext(); - -private: - std::atomic_bool m_stopped; - - std::uint32_t m_heartbeatIntervalSeconds; - - boost::asio::io_context m_ioContext; - - boost::asio::deadline_timer m_deadlineTimer; - - std::shared_ptr m_connectionManager; - - std::vector m_threadPool; - - const PacketHandlerMapPtr c_requestHandlerMap; -}; - - -} // namespace Socket -} // namespace SPTAG - -#endif // _SPTAG_SOCKET_CLIENT_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Common.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Common.h deleted file mode 100644 index dc06af1bb4..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Common.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_SOCKET_COMMON_H_ -#define _SPTAG_SOCKET_COMMON_H_ - -#include - -namespace SPTAG -{ -namespace Socket -{ - -typedef std::uint32_t ConnectionID; - -typedef std::uint32_t ResourceID; - -extern const ConnectionID c_invalidConnectionID; - -extern const ResourceID c_invalidResourceID; - -} // namespace Socket -} // namespace SPTAG - -#endif // _SPTAG_SOCKET_COMMON_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Connection.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Connection.h deleted file mode 100644 index 1d75d093b3..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Connection.h +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_SOCKET_CONNECTION_H_ -#define _SPTAG_SOCKET_CONNECTION_H_ - -#include "Packet.h" - -#include -#include -#include - -#include -#include -#include - -namespace SPTAG -{ -namespace Socket -{ - -class ConnectionManager; - -class Connection : public std::enable_shared_from_this -{ -public: - typedef std::shared_ptr Ptr; - - Connection(ConnectionID p_connectionID, - boost::asio::ip::tcp::socket&& p_socket, - const PacketHandlerMapPtr& p_handlerMap, - std::weak_ptr p_connectionManager); - - void Start(); - - void Stop(); - - void StartHeartbeat(std::size_t p_intervalSeconds); - - void AsyncSend(Packet p_packet, std::function p_callback); - - ConnectionID GetConnectionID() const; - - ConnectionID GetRemoteConnectionID() const; - - Connection(const Connection&) = delete; - Connection& operator=(const Connection&) = delete; - -private: - void AsyncReadHeader(); - - void AsyncReadBody(); - - void HandleReadHeader(boost::system::error_code p_ec, std::size_t p_bytesTransferred); - - void HandleReadBody(boost::system::error_code p_ec, std::size_t p_bytesTransferred); - - void SendHeartbeat(std::size_t p_intervalSeconds); - - void SendRegister(); - - void HandleHeartbeatRequest(); - - void HandleRegisterRequest(); - - void HandleRegisterResponse(); - - void HandleNoHandlerResponse(); - - void OnConnectionFail(const boost::system::error_code& p_ec); - -private: - const ConnectionID c_connectionID; - - ConnectionID m_remoteConnectionID; - - const std::weak_ptr c_connectionManager; - - const PacketHandlerMapPtr c_handlerMap; - - boost::asio::ip::tcp::socket m_socket; - - boost::asio::io_context::strand m_strand; - - boost::asio::deadline_timer m_heartbeatTimer; - - std::uint8_t m_packetHeaderReadBuffer[PacketHeader::c_bufferSize]; - - Packet m_packetRead; - - std::atomic_bool m_stopped; - - std::atomic_bool m_heartbeatStarted; -}; - - -} // namespace Socket -} // namespace SPTAG - -#endif // _SPTAG_SOCKET_CONNECTION_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/ConnectionManager.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/ConnectionManager.h deleted file mode 100644 index e487c61053..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/ConnectionManager.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_SOCKET_CONNECTIONMANAGER_H_ -#define _SPTAG_SOCKET_CONNECTIONMANAGER_H_ - -#include "Connection.h" -#include "inc/Helper/Concurrent.h" - -#include -#include -#include -#include -#include - -#include - -namespace SPTAG -{ -namespace Socket -{ - -class ConnectionManager : public std::enable_shared_from_this -{ -public: - ConnectionManager(); - - ConnectionID AddConnection(boost::asio::ip::tcp::socket&& p_socket, - const PacketHandlerMapPtr& p_handlerMap, - std::uint32_t p_heartbeatIntervalSeconds); - - void RemoveConnection(ConnectionID p_connectionID); - - Connection::Ptr GetConnection(ConnectionID p_connectionID); - - void SetEventOnRemoving(std::function p_event); - - void StopAll(); - -private: - inline static std::uint32_t GetPosition(ConnectionID p_connectionID); - -private: - static constexpr std::uint32_t c_connectionPoolSize = 1 << 8; - - static constexpr std::uint32_t c_connectionPoolMask = c_connectionPoolSize - 1; - - struct ConnectionItem - { - ConnectionItem(); - - std::atomic_bool m_isEmpty; - - Connection::Ptr m_connection; - }; - - // Start from 1. 0 means not assigned. - std::atomic m_nextConnectionID; - - std::atomic m_connectionCount; - - std::array m_connections; - - Helper::Concurrent::SpinLock m_spinLock; - - std::function m_eventOnRemoving; -}; - - -} // namespace Socket -} // namespace SPTAG - -#endif // _SPTAG_SOCKET_CONNECTIONMANAGER_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Packet.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Packet.h deleted file mode 100644 index 8c99b09fed..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Packet.h +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_SOCKET_PACKET_H_ -#define _SPTAG_SOCKET_PACKET_H_ - -#include "Common.h" - -#include -#include -#include -#include -#include - -namespace SPTAG -{ -namespace Socket -{ - -enum class PacketType : std::uint8_t -{ - Undefined = 0x00, - - HeartbeatRequest = 0x01, - - RegisterRequest = 0x02, - - SearchRequest = 0x03, - - ResponseMask = 0x80, - - HeartbeatResponse = ResponseMask | HeartbeatRequest, - - RegisterResponse = ResponseMask | RegisterRequest, - - SearchResponse = ResponseMask | SearchRequest -}; - - -enum class PacketProcessStatus : std::uint8_t -{ - Ok = 0x00, - - Timeout = 0x01, - - Dropped = 0x02, - - Failed = 0x03 -}; - - -struct PacketHeader -{ - static constexpr std::size_t c_bufferSize = 16; - - PacketHeader(); - PacketHeader(PacketHeader&& p_right); - PacketHeader(const PacketHeader& p_right); - - std::size_t WriteBuffer(std::uint8_t* p_buffer); - - void ReadBuffer(const std::uint8_t* p_buffer); - - PacketType m_packetType; - - PacketProcessStatus m_processStatus; - - std::uint32_t m_bodyLength; - - // Meaning of this is different with different PacketType. - // In most request case, it means connection expeced for response. - // In most response case, it means connection which handled request. - ConnectionID m_connectionID; - - ResourceID m_resourceID; -}; - - -static_assert(sizeof(PacketHeader) <= PacketHeader::c_bufferSize, ""); - - -class Packet -{ -public: - Packet(); - Packet(Packet&& p_right); - Packet(const Packet& p_right); - - PacketHeader& Header(); - - std::uint8_t* HeaderBuffer() const; - - std::uint8_t* Body() const; - - std::uint8_t* Buffer() const; - - std::uint32_t BufferLength() const; - - std::uint32_t BufferCapacity() const; - - void AllocateBuffer(std::uint32_t p_bodyCapacity); - -private: - PacketHeader m_header; - - std::shared_ptr m_buffer; - - std::uint32_t m_bufferCapacity; -}; - - -struct PacketTypeHash -{ - std::size_t operator()(const PacketType& p_val) const - { - return static_cast(p_val); - } -}; - - -typedef std::function PacketHandler; - -typedef std::unordered_map PacketHandlerMap; -typedef std::shared_ptr PacketHandlerMapPtr; - - -namespace PacketTypeHelper -{ - -bool IsRequestPacket(PacketType p_type); - -bool IsResponsePacket(PacketType p_type); - -PacketType GetCrosspondingResponseType(PacketType p_type); - -} - - -} // namespace SPTAG -} // namespace Socket - -#endif // _SPTAG_SOCKET_SOCKETSERVER_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/RemoteSearchQuery.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/RemoteSearchQuery.h deleted file mode 100644 index 900aa6cb16..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/RemoteSearchQuery.h +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_SOCKET_REMOTESEARCHQUERY_H_ -#define _SPTAG_SOCKET_REMOTESEARCHQUERY_H_ - -#include "inc/Core/CommonDataStructure.h" -#include "inc/Core/SearchQuery.h" - -#include -#include -#include -#include -#include - -namespace SPTAG -{ -namespace Socket -{ - -// TODO: use Bond replace below structures. - -struct RemoteQuery -{ - static constexpr std::uint16_t MajorVersion() { return 1; } - static constexpr std::uint16_t MirrorVersion() { return 0; } - - enum class QueryType : std::uint8_t - { - String = 0 - }; - - RemoteQuery(); - - std::size_t EstimateBufferSize() const; - - std::uint8_t* Write(std::uint8_t* p_buffer) const; - - const std::uint8_t* Read(const std::uint8_t* p_buffer); - - - QueryType m_type; - - std::string m_queryString; -}; - - -struct IndexSearchResult -{ - std::string m_indexName; - - QueryResult m_results; -}; - - -struct RemoteSearchResult -{ - static constexpr std::uint16_t MajorVersion() { return 1; } - static constexpr std::uint16_t MirrorVersion() { return 0; } - - enum class ResultStatus : std::uint8_t - { - Success = 0, - - Timeout = 1, - - FailedNetwork = 2, - - FailedExecute = 3, - - Dropped = 4 - }; - - RemoteSearchResult(); - - RemoteSearchResult(const RemoteSearchResult& p_right); - - RemoteSearchResult(RemoteSearchResult&& p_right); - - RemoteSearchResult& operator=(RemoteSearchResult&& p_right); - - std::size_t EstimateBufferSize() const; - - std::uint8_t* Write(std::uint8_t* p_buffer) const; - - const std::uint8_t* Read(const std::uint8_t* p_buffer); - - - ResultStatus m_status; - - std::vector m_allIndexResults; -}; - - - -} // namespace SPTAG -} // namespace Socket - -#endif // _SPTAG_SOCKET_REMOTESEARCHQUERY_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/ResourceManager.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/ResourceManager.h deleted file mode 100644 index 404cac830f..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/ResourceManager.h +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_SOCKET_RESOURCEMANAGER_H_ -#define _SPTAG_SOCKET_RESOURCEMANAGER_H_ - -#include "Common.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace std -{ -typedef atomic atomic_uint32_t; -} - -namespace SPTAG -{ -namespace Socket -{ - -template -class ResourceManager : public std::enable_shared_from_this> -{ -public: - typedef std::function)> TimeoutCallback; - - ResourceManager() - : m_nextResourceID(1), - m_isStopped(false), - m_timeoutItemCount(0) - { - m_timeoutChecker = std::thread(&ResourceManager::StartCheckTimeout, this); - } - - - ~ResourceManager() - { - m_isStopped = true; - m_timeoutChecker.join(); - } - - - ResourceID Add(const std::shared_ptr& p_resource, - std::uint32_t p_timeoutMilliseconds, - TimeoutCallback p_timeoutCallback) - { - ResourceID rid = m_nextResourceID.fetch_add(1); - while (c_invalidResourceID == rid) - { - rid = m_nextResourceID.fetch_add(1); - } - - { - std::lock_guard guard(m_resourcesMutex); - m_resources.emplace(rid, p_resource); - } - - if (p_timeoutMilliseconds > 0) - { - std::unique_ptr item(new ResourceItem); - - item->m_resourceID = rid; - item->m_callback = std::move(p_timeoutCallback); - item->m_expireTime = m_clock.now() + std::chrono::milliseconds(p_timeoutMilliseconds); - - { - std::lock_guard guard(m_timeoutListMutex); - m_timeoutList.emplace_back(std::move(item)); - } - - ++m_timeoutItemCount; - } - - return rid; - } - - - std::shared_ptr GetAndRemove(ResourceID p_resourceID) - { - std::shared_ptr ret; - std::lock_guard guard(m_resourcesMutex); - auto iter = m_resources.find(p_resourceID); - if (iter != m_resources.end()) - { - ret = iter->second; - m_resources.erase(iter); - } - - return ret; - } - - - void Remove(ResourceID p_resourceID) - { - std::lock_guard guard(m_resourcesMutex); - auto iter = m_resources.find(p_resourceID); - if (iter != m_resources.end()) - { - m_resources.erase(iter); - } - } - -private: - void StartCheckTimeout() - { - std::vector> timeouted; - timeouted.reserve(1024); - while (!m_isStopped) - { - if (m_timeoutItemCount > 0) - { - std::lock_guard guard(m_timeoutListMutex); - while (!m_timeoutList.empty() - && m_timeoutList.front()->m_expireTime <= m_clock.now()) - { - timeouted.emplace_back(std::move(m_timeoutList.front())); - m_timeoutList.pop_front(); - --m_timeoutItemCount; - } - } - - if (timeouted.empty()) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - else - { - for (auto& item : timeouted) - { - auto resource = GetAndRemove(item->m_resourceID); - if (nullptr != resource) - { - item->m_callback(std::move(resource)); - } - } - - timeouted.clear(); - } - } - } - - -private: - struct ResourceItem - { - ResourceItem() - : m_resourceID(c_invalidResourceID) - { - } - - ResourceID m_resourceID; - - TimeoutCallback m_callback; - - std::chrono::time_point m_expireTime; - }; - - std::deque> m_timeoutList; - - std::atomic m_timeoutItemCount; - - std::mutex m_timeoutListMutex; - - std::unordered_map> m_resources; - - std::atomic m_nextResourceID; - - std::mutex m_resourcesMutex; - - std::chrono::high_resolution_clock m_clock; - - std::thread m_timeoutChecker; - - bool m_isStopped; -}; - - -} // namespace Socket -} // namespace SPTAG - -#endif // _SPTAG_SOCKET_RESOURCEMANAGER_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Server.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Server.h deleted file mode 100644 index aac97bf84b..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/Server.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_SOCKET_SERVER_H_ -#define _SPTAG_SOCKET_SERVER_H_ - -#include "Connection.h" -#include "ConnectionManager.h" -#include "Packet.h" - -#include -#include -#include - -namespace SPTAG -{ -namespace Socket -{ - -class Server -{ -public: - Server(const std::string& p_address, - const std::string& p_port, - const PacketHandlerMapPtr& p_handlerMap, - std::size_t p_threadNum); - - ~Server(); - - void StartListen(); - - void SendPacket(ConnectionID p_connection, Packet p_packet, std::function p_callback); - - void SetEventOnConnectionClose(std::function p_event); - -private: - void StartAccept(); - -private: - boost::asio::io_context m_ioContext; - - boost::asio::ip::tcp::acceptor m_acceptor; - - std::shared_ptr m_connectionManager; - - std::vector m_threadPool; - - const PacketHandlerMapPtr m_requestHandlerMap; -}; - - -} // namespace Socket -} // namespace SPTAG - -#endif // _SPTAG_SOCKET_SERVER_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/SimpleSerialization.h b/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/SimpleSerialization.h deleted file mode 100644 index 6da925625b..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/inc/Socket/SimpleSerialization.h +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_SOCKET_SIMPLESERIALIZATION_H_ -#define _SPTAG_SOCKET_SIMPLESERIALIZATION_H_ - -#include "inc/Core/CommonDataStructure.h" - -#include -#include -#include -#include - -namespace SPTAG -{ -namespace Socket -{ -namespace SimpleSerialization -{ - - template - inline std::uint8_t* - SimpleWriteBuffer(const T& p_val, std::uint8_t* p_buffer) - { - static_assert(std::is_fundamental::value || std::is_enum::value, - "Only applied for fundanmental type."); - - *(reinterpret_cast(p_buffer)) = p_val; - return p_buffer + sizeof(T); - } - - - template - inline const std::uint8_t* - SimpleReadBuffer(const std::uint8_t* p_buffer, T& p_val) - { - static_assert(std::is_fundamental::value || std::is_enum::value, - "Only applied for fundanmental type."); - - p_val = *(reinterpret_cast(p_buffer)); - return p_buffer + sizeof(T); - } - - - template - inline std::size_t - EstimateBufferSize(const T& p_val) - { - static_assert(std::is_fundamental::value || std::is_enum::value, - "Only applied for fundanmental type."); - - return sizeof(T); - } - - - template<> - inline std::uint8_t* - SimpleWriteBuffer(const std::string& p_val, std::uint8_t* p_buffer) - { - p_buffer = SimpleWriteBuffer(static_cast(p_val.size()), p_buffer); - - std::memcpy(p_buffer, p_val.c_str(), p_val.size()); - return p_buffer + p_val.size(); - } - - - template<> - inline const std::uint8_t* - SimpleReadBuffer(const std::uint8_t* p_buffer, std::string& p_val) - { - p_val.clear(); - std::uint32_t len = 0; - p_buffer = SimpleReadBuffer(p_buffer, len); - - if (len > 0) - { - p_val.reserve(len); - p_val.assign(reinterpret_cast(p_buffer), len); - } - - return p_buffer + len; - } - - - template<> - inline std::size_t - EstimateBufferSize(const std::string& p_val) - { - return sizeof(std::uint32_t) + p_val.size(); - } - - - template<> - inline std::uint8_t* - SimpleWriteBuffer(const ByteArray& p_val, std::uint8_t* p_buffer) - { - p_buffer = SimpleWriteBuffer(static_cast(p_val.Length()), p_buffer); - - std::memcpy(p_buffer, p_val.Data(), p_val.Length()); - return p_buffer + p_val.Length(); - } - - - template<> - inline const std::uint8_t* - SimpleReadBuffer(const std::uint8_t* p_buffer, ByteArray& p_val) - { - p_val.Clear(); - std::uint32_t len = 0; - p_buffer = SimpleReadBuffer(p_buffer, len); - - if (len > 0) - { - p_val = ByteArray::Alloc(len); - std::memcpy(p_val.Data(), p_buffer, len); - } - - return p_buffer + len; - } - - - template<> - inline std::size_t - EstimateBufferSize(const ByteArray& p_val) - { - return sizeof(std::uint32_t) + p_val.Length(); - } - - - template - inline std::uint8_t* - SimpleWriteSharedPtrBuffer(const std::shared_ptr& p_val, std::uint8_t* p_buffer) - { - if (nullptr == p_val) - { - return SimpleWriteBuffer(false, p_buffer); - } - - p_buffer = SimpleWriteBuffer(true, p_buffer); - p_buffer = SimpleWriteBuffer(*p_val, p_buffer); - return p_buffer; - } - - - template - inline const std::uint8_t* - SimpleReadSharedPtrBuffer(const std::uint8_t* p_buffer, std::shared_ptr& p_val) - { - p_val.reset(); - bool isNotNull = false; - p_buffer = SimpleReadBuffer(p_buffer, isNotNull); - - if (isNotNull) - { - p_val.reset(new T); - p_buffer = SimpleReadBuffer(p_buffer, *p_val); - } - - return p_buffer; - } - - - template - inline std::size_t - EstimateSharedPtrBufferSize(const std::shared_ptr& p_val) - { - return sizeof(bool) + (nullptr == p_val ? 0 : EstimateBufferSize(*p_val)); - } - -} // namespace SimpleSerialization -} // namespace SPTAG -} // namespace Socket - -#endif // _SPTAG_SOCKET_SIMPLESERIALIZATION_H_ diff --git a/core/src/index/thirdparty/SPTAG/AnnService/packages.config b/core/src/index/thirdparty/SPTAG/AnnService/packages.config deleted file mode 100644 index 2dbed9b530..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/packages.config +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/AggregatorContext.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/AggregatorContext.cpp deleted file mode 100644 index a36c2c61e9..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/AggregatorContext.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Aggregator/AggregatorContext.h" -#include "inc/Helper/SimpleIniReader.h" - -using namespace SPTAG; -using namespace SPTAG::Aggregator; - -RemoteMachine::RemoteMachine() - : m_connectionID(Socket::c_invalidConnectionID), - m_status(RemoteMachineStatus::Disconnected) -{ -} - - -AggregatorContext::AggregatorContext(const std::string& p_filePath) - : m_initialized(false) -{ - Helper::IniReader iniReader; - if (ErrorCode::Success != iniReader.LoadIniFile(p_filePath)) - { - return; - } - - m_settings.reset(new AggregatorSettings); - - m_settings->m_listenAddr = iniReader.GetParameter("Service", "ListenAddr", std::string("0.0.0.0")); - m_settings->m_listenPort = iniReader.GetParameter("Service", "ListenPort", std::string("8100")); - m_settings->m_threadNum = iniReader.GetParameter("Service", "ThreadNumber", static_cast(8)); - m_settings->m_socketThreadNum = iniReader.GetParameter("Service", "SocketThreadNumber", static_cast(8)); - - const std::string emptyStr; - - std::uint32_t serverNum = iniReader.GetParameter("Servers", "Number", static_cast(0)); - - for (std::uint32_t i = 0; i < serverNum; ++i) - { - std::string sectionName("Server_"); - sectionName += std::to_string(i); - if (!iniReader.DoesSectionExist(sectionName)) - { - continue; - } - - std::shared_ptr remoteMachine(new RemoteMachine); - - remoteMachine->m_address = iniReader.GetParameter(sectionName, "Address", emptyStr); - remoteMachine->m_port = iniReader.GetParameter(sectionName, "Port", emptyStr); - - if (remoteMachine->m_address.empty() || remoteMachine->m_port.empty()) - { - continue; - } - - m_remoteServers.push_back(std::move(remoteMachine)); - } - - m_initialized = true; -} - - -AggregatorContext::~AggregatorContext() -{ -} - - -bool -AggregatorContext::IsInitialized() const -{ - return m_initialized; -} - - -const std::vector>& -AggregatorContext::GetRemoteServers() const -{ - return m_remoteServers; -} - - -const std::shared_ptr& -AggregatorContext::GetSettings() const -{ - return m_settings; -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/AggregatorExecutionContext.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/AggregatorExecutionContext.cpp deleted file mode 100644 index 8f7a28375a..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/AggregatorExecutionContext.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Aggregator/AggregatorExecutionContext.h" - -using namespace SPTAG; -using namespace SPTAG::Aggregator; - -AggregatorExecutionContext::AggregatorExecutionContext(std::size_t p_totalServerNumber, - Socket::PacketHeader p_requestHeader) - : m_requestHeader(std::move(p_requestHeader)) -{ - m_results.clear(); - m_results.resize(p_totalServerNumber); - - m_unfinishedCount = static_cast(p_totalServerNumber); -} - - -AggregatorExecutionContext::~AggregatorExecutionContext() -{ -} - - -std::size_t -AggregatorExecutionContext::GetServerNumber() const -{ - return m_results.size(); -} - - -AggregatorResult& -AggregatorExecutionContext::GetResult(std::size_t p_num) -{ - return m_results[p_num]; -} - - -const Socket::PacketHeader& -AggregatorExecutionContext::GetRequestHeader() const -{ - return m_requestHeader; -} - - -bool -AggregatorExecutionContext::IsCompletedAfterFinsh(std::uint32_t p_finishedCount) -{ - auto lastCount = m_unfinishedCount.fetch_sub(p_finishedCount); - return lastCount <= p_finishedCount; -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/AggregatorService.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/AggregatorService.cpp deleted file mode 100644 index 24c1672cde..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/AggregatorService.cpp +++ /dev/null @@ -1,366 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Aggregator/AggregatorService.h" - -using namespace SPTAG; -using namespace SPTAG::Aggregator; - -AggregatorService::AggregatorService() - : m_shutdownSignals(m_ioContext), - m_pendingConnectServersTimer(m_ioContext) -{ -} - - -AggregatorService::~AggregatorService() -{ -} - - -bool -AggregatorService::Initialize() -{ - std::string configFilePath = "Aggregator.ini"; - m_aggregatorContext.reset(new AggregatorContext(configFilePath)); - - m_initalized = m_aggregatorContext->IsInitialized(); - - return m_initalized; -} - - -void -AggregatorService::Run() -{ - auto threadNum = max((SPTAG::SizeType)1, GetContext()->GetSettings()->m_threadNum); - m_threadPool.reset(new boost::asio::thread_pool(threadNum)); - - StartClient(); - StartListen(); - WaitForShutdown(); -} - - -void -AggregatorService::StartClient() -{ - auto context = GetContext(); - Socket::PacketHandlerMapPtr handlerMap(new Socket::PacketHandlerMap); - handlerMap->emplace(Socket::PacketType::SearchResponse, - [this](Socket::ConnectionID p_srcID, Socket::Packet p_packet) - { - boost::asio::post(*m_threadPool, - std::bind(&AggregatorService::SearchResponseHanlder, - this, - p_srcID, - std::move(p_packet))); - }); - - - m_socketClient.reset(new Socket::Client(handlerMap, - context->GetSettings()->m_socketThreadNum, - 30)); - - m_socketClient->SetEventOnConnectionClose([this](Socket::ConnectionID p_cid) - { - auto context = this->GetContext(); - for (const auto& server : context->GetRemoteServers()) - { - if (nullptr != server && p_cid == server->m_connectionID) - { - server->m_status = RemoteMachineStatus::Disconnected; - this->AddToPendingServers(server); - } - } - }); - - { - std::lock_guard guard(m_pendingConnectServersMutex); - m_pendingConnectServers = context->GetRemoteServers(); - } - - ConnectToPendingServers(); -} - - -void -AggregatorService::StartListen() -{ - auto context = GetContext(); - Socket::PacketHandlerMapPtr handlerMap(new Socket::PacketHandlerMap); - handlerMap->emplace(Socket::PacketType::SearchRequest, - [this](Socket::ConnectionID p_srcID, Socket::Packet p_packet) - { - boost::asio::post(*m_threadPool, - std::bind(&AggregatorService::SearchRequestHanlder, - this, - p_srcID, - std::move(p_packet))); - }); - - m_socketServer.reset(new Socket::Server(context->GetSettings()->m_listenAddr, - context->GetSettings()->m_listenPort, - handlerMap, - context->GetSettings()->m_socketThreadNum)); - - fprintf(stderr, - "Start to listen %s:%s ...\n", - context->GetSettings()->m_listenAddr.c_str(), - context->GetSettings()->m_listenPort.c_str()); -} - - -void -AggregatorService::WaitForShutdown() -{ - m_shutdownSignals.add(SIGINT); - m_shutdownSignals.add(SIGTERM); -#ifdef SIGQUIT - m_shutdownSignals.add(SIGQUIT); -#endif - - m_shutdownSignals.async_wait([this](boost::system::error_code p_ec, int p_signal) - { - fprintf(stderr, "Received shutdown signals.\n"); - m_pendingConnectServersTimer.cancel(); - }); - - m_ioContext.run(); - fprintf(stderr, "Start shutdown procedure.\n"); - - m_socketServer.reset(); - m_threadPool->stop(); - m_threadPool->join(); -} - - -void -AggregatorService::ConnectToPendingServers() -{ - auto context = GetContext(); - std::vector> pendingList; - pendingList.reserve(context->GetRemoteServers().size()); - - { - std::lock_guard guard(m_pendingConnectServersMutex); - pendingList.swap(m_pendingConnectServers); - } - - for (auto& pendingServer : pendingList) - { - if (pendingServer->m_status != RemoteMachineStatus::Disconnected) - { - continue; - } - - pendingServer->m_status = RemoteMachineStatus::Connecting; - std::shared_ptr server = pendingServer; - auto runner = [server, this]() - { - ErrorCode errCode; - auto cid = m_socketClient->ConnectToServer(server->m_address, server->m_port, errCode); - if (Socket::c_invalidConnectionID == cid) - { - if (ErrorCode::Socket_FailedResolveEndPoint == errCode) - { - fprintf(stderr, - "[Error] Failed to resolve %s %s.\n", - server->m_address.c_str(), - server->m_port.c_str()); - } - else - { - this->AddToPendingServers(std::move(server)); - } - } - else - { - server->m_connectionID = cid; - server->m_status = RemoteMachineStatus::Connected; - } - }; - boost::asio::post(*m_threadPool, std::move(runner)); - } - - m_pendingConnectServersTimer.expires_from_now(boost::posix_time::seconds(30)); - m_pendingConnectServersTimer.async_wait([this](const boost::system::error_code& p_ec) - { - if (boost::asio::error::operation_aborted != p_ec) - { - ConnectToPendingServers(); - } - }); -} - - -void -AggregatorService::AddToPendingServers(std::shared_ptr p_remoteServer) -{ - std::lock_guard guard(m_pendingConnectServersMutex); - m_pendingConnectServers.emplace_back(std::move(p_remoteServer)); -} - - -void -AggregatorService::SearchRequestHanlder(Socket::ConnectionID p_localConnectionID, Socket::Packet p_packet) -{ - auto context = GetContext(); - std::vector remoteServers; - remoteServers.reserve(context->GetRemoteServers().size()); - - for (const auto& server : context->GetRemoteServers()) - { - if (RemoteMachineStatus::Connected != server->m_status) - { - continue; - } - - remoteServers.push_back(server->m_connectionID); - } - - Socket::PacketHeader requestHeader = p_packet.Header(); - if (Socket::c_invalidConnectionID == requestHeader.m_connectionID) - { - requestHeader.m_connectionID = p_localConnectionID; - } - - std::shared_ptr executionContext( - new AggregatorExecutionContext(remoteServers.size(), requestHeader)); - - for (std::uint32_t i = 0; i < remoteServers.size(); ++i) - { - AggregatorCallback callback = [this, executionContext, i](Socket::RemoteSearchResult p_result) - { - executionContext->GetResult(i).reset(new Socket::RemoteSearchResult(std::move(p_result))); - if (executionContext->IsCompletedAfterFinsh(1)) - { - this->AggregateResults(std::move(executionContext)); - } - }; - - auto timeoutCallback = [](std::shared_ptr p_callback) - { - if (nullptr != p_callback) - { - Socket::RemoteSearchResult result; - result.m_status = Socket::RemoteSearchResult::ResultStatus::Timeout; - - (*p_callback)(std::move(result)); - } - }; - - auto connectCallback = [callback](bool p_connectSucc) - { - if (!p_connectSucc) - { - Socket::RemoteSearchResult result; - result.m_status = Socket::RemoteSearchResult::ResultStatus::FailedNetwork; - - callback(std::move(result)); - } - }; - - Socket::Packet packet; - packet.Header().m_packetType = Socket::PacketType::SearchRequest; - packet.Header().m_processStatus = Socket::PacketProcessStatus::Ok; - packet.Header().m_bodyLength = p_packet.Header().m_bodyLength; - packet.Header().m_connectionID = Socket::c_invalidConnectionID; - packet.Header().m_resourceID = m_aggregatorCallbackManager.Add(std::make_shared(std::move(callback)), - context->GetSettings()->m_searchTimeout, - std::move(timeoutCallback)); - - packet.AllocateBuffer(packet.Header().m_bodyLength); - packet.Header().WriteBuffer(packet.HeaderBuffer()); - memcpy(packet.Body(), p_packet.Body(), packet.Header().m_bodyLength); - - m_socketClient->SendPacket(remoteServers[i], std::move(packet), connectCallback); - } -} - - -void -AggregatorService::SearchResponseHanlder(Socket::ConnectionID p_localConnectionID, Socket::Packet p_packet) -{ - auto callback = m_aggregatorCallbackManager.GetAndRemove(p_packet.Header().m_resourceID); - if (nullptr == callback) - { - return; - } - - if (p_packet.Header().m_processStatus != Socket::PacketProcessStatus::Ok || 0 == p_packet.Header().m_bodyLength) - { - Socket::RemoteSearchResult result; - result.m_status = Socket::RemoteSearchResult::ResultStatus::FailedExecute; - - (*callback)(std::move(result)); - } - else - { - Socket::RemoteSearchResult result; - result.Read(p_packet.Body()); - (*callback)(std::move(result)); - } -} - - -std::shared_ptr -AggregatorService::GetContext() -{ - // Add mutex if necessary. - return m_aggregatorContext; -} - - -void -AggregatorService::AggregateResults(std::shared_ptr p_exectionContext) -{ - if (nullptr == p_exectionContext) - { - return; - } - - Socket::Packet packet; - packet.Header().m_packetType = Socket::PacketType::SearchResponse; - packet.Header().m_processStatus = Socket::PacketProcessStatus::Ok; - packet.Header().m_resourceID = p_exectionContext->GetRequestHeader().m_resourceID; - - Socket::RemoteSearchResult remoteResult; - remoteResult.m_status = Socket::RemoteSearchResult::ResultStatus::Success; - - std::size_t resultNum = 0; - for (std::size_t i = 0; i < p_exectionContext->GetServerNumber(); ++i) - { - const auto& result = p_exectionContext->GetResult(i); - if (nullptr == result) - { - continue; - } - - resultNum += result->m_allIndexResults.size(); - } - - remoteResult.m_allIndexResults.reserve(resultNum); - for (std::size_t i = 0; i < p_exectionContext->GetServerNumber(); ++i) - { - const auto& result = p_exectionContext->GetResult(i); - if (nullptr == result) - { - continue; - } - - for (auto& indexRes : result->m_allIndexResults) - { - remoteResult.m_allIndexResults.emplace_back(std::move(indexRes)); - } - } - - std::uint32_t cap = static_cast(remoteResult.EstimateBufferSize()); - packet.AllocateBuffer(cap); - packet.Header().m_bodyLength = static_cast(remoteResult.Write(packet.Body()) - packet.Body()); - packet.Header().WriteBuffer(packet.HeaderBuffer()); - - m_socketServer->SendPacket(p_exectionContext->GetRequestHeader().m_connectionID, - std::move(packet), - nullptr); -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/AggregatorSettings.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/AggregatorSettings.cpp deleted file mode 100644 index a3e2bc6806..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/AggregatorSettings.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Aggregator/AggregatorSettings.h" - -using namespace SPTAG; -using namespace SPTAG::Aggregator; - -AggregatorSettings::AggregatorSettings() - : m_searchTimeout(100), - m_threadNum(8), - m_socketThreadNum(8) -{ -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/main.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/main.cpp deleted file mode 100644 index 2a06025d5e..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Aggregator/main.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Aggregator/AggregatorService.h" - -SPTAG::Aggregator::AggregatorService g_service; - -int main(int argc, char* argv[]) -{ - if (!g_service.Initialize()) - { - return 1; - } - - g_service.Run(); - - return 0; -} - diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Client/ClientWrapper.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Client/ClientWrapper.cpp deleted file mode 100644 index 7e91c63195..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Client/ClientWrapper.cpp +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Client/ClientWrapper.h" - -using namespace SPTAG; -using namespace SPTAG::Socket; -using namespace SPTAG::Client; - -ClientWrapper::ClientWrapper(const ClientOptions& p_options) - : m_options(p_options), - m_unfinishedJobCount(0), - m_isWaitingFinish(false) -{ - m_client.reset(new SPTAG::Socket::Client(GetHandlerMap(), p_options.m_socketThreadNum, 30)); - m_client->SetEventOnConnectionClose(std::bind(&ClientWrapper::HandleDeadConnection, - this, - std::placeholders::_1)); - - m_connections.reserve(m_options.m_threadNum); - for (std::uint32_t i = 0; i < m_options.m_threadNum; ++i) - { - SPTAG::ErrorCode errCode; - ConnectionPair conn(c_invalidConnectionID, c_invalidConnectionID); - conn.first = m_client->ConnectToServer(p_options.m_serverAddr, p_options.m_serverPort, errCode); - if (SPTAG::ErrorCode::Socket_FailedResolveEndPoint == errCode) - { - fprintf(stderr, "Unable to resolve remote address.\n"); - return; - } - - if (c_invalidConnectionID != conn.first) - { - m_connections.emplace_back(std::move(conn)); - } - } -} - - -ClientWrapper::~ClientWrapper() -{ -} - - -void -ClientWrapper::SendQueryAsync(const Socket::RemoteQuery& p_query, - Callback p_callback, - const ClientOptions& p_options) -{ - if (!bool(p_callback)) - { - return; - } - - auto conn = GetConnection(); - - auto timeoutCallback = [this](std::shared_ptr p_callback) - { - DecreaseUnfnishedJobCount(); - if (nullptr != p_callback) - { - Socket::RemoteSearchResult result; - result.m_status = Socket::RemoteSearchResult::ResultStatus::Timeout; - - (*p_callback)(std::move(result)); - } - }; - - - auto connectCallback = [p_callback, this](bool p_connectSucc) - { - if (!p_connectSucc) - { - Socket::RemoteSearchResult result; - result.m_status = Socket::RemoteSearchResult::ResultStatus::FailedNetwork; - - p_callback(std::move(result)); - DecreaseUnfnishedJobCount(); - } - }; - - Socket::Packet packet; - packet.Header().m_connectionID = c_invalidConnectionID; - packet.Header().m_packetType = PacketType::SearchRequest; - packet.Header().m_processStatus = PacketProcessStatus::Ok; - packet.Header().m_resourceID = m_callbackManager.Add(std::make_shared(std::move(p_callback)), - p_options.m_searchTimeout, - std::move(timeoutCallback)); - - packet.Header().m_bodyLength = static_cast(p_query.EstimateBufferSize()); - packet.AllocateBuffer(packet.Header().m_bodyLength); - p_query.Write(packet.Body()); - packet.Header().WriteBuffer(packet.HeaderBuffer()); - - ++m_unfinishedJobCount; - m_client->SendPacket(conn.first, std::move(packet), connectCallback); -} - - -void -ClientWrapper::WaitAllFinished() -{ - if (m_unfinishedJobCount > 0) - { - std::unique_lock lock(m_waitingMutex); - if (m_unfinishedJobCount > 0) - { - m_isWaitingFinish = true; - m_waitingQueue.wait(lock); - } - } -} - - -PacketHandlerMapPtr -ClientWrapper::GetHandlerMap() -{ - PacketHandlerMapPtr handlerMap(new PacketHandlerMap); - handlerMap->emplace(PacketType::RegisterResponse, - [this](ConnectionID p_localConnectionID, Packet p_packet) -> void - { - for (auto& conn : m_connections) - { - if (conn.first == p_localConnectionID) - { - conn.second = p_packet.Header().m_connectionID; - return; - } - } - }); - - handlerMap->emplace(PacketType::SearchResponse, - std::bind(&ClientWrapper::SearchResponseHanlder, - this, - std::placeholders::_1, - std::placeholders::_2)); - - return handlerMap; -} - - -void -ClientWrapper::DecreaseUnfnishedJobCount() -{ - --m_unfinishedJobCount; - if (0 == m_unfinishedJobCount) - { - std::lock_guard guard(m_waitingMutex); - if (0 == m_unfinishedJobCount && m_isWaitingFinish) - { - m_waitingQueue.notify_all(); - m_isWaitingFinish = false; - } - } -} - - -const ClientWrapper::ConnectionPair& -ClientWrapper::GetConnection() -{ - if (m_connections.size() == 1) - { - return m_connections.front(); - } - - std::size_t triedCount = 0; - std::uint32_t pos = m_spinCountOfConnection.fetch_add(1) % m_connections.size(); - while (c_invalidConnectionID == m_connections[pos].first && triedCount < m_connections.size()) - { - pos = m_spinCountOfConnection.fetch_add(1) % m_connections.size(); - ++triedCount; - } - - return m_connections[pos]; -} - - -void -ClientWrapper::SearchResponseHanlder(Socket::ConnectionID p_localConnectionID, Socket::Packet p_packet) -{ - std::shared_ptr callback = m_callbackManager.GetAndRemove(p_packet.Header().m_resourceID); - if (nullptr == callback) - { - return; - } - - if (p_packet.Header().m_processStatus != PacketProcessStatus::Ok || 0 == p_packet.Header().m_bodyLength) - { - Socket::RemoteSearchResult result; - result.m_status = Socket::RemoteSearchResult::ResultStatus::FailedExecute; - - (*callback)(std::move(result)); - } - else - { - Socket::RemoteSearchResult result; - result.Read(p_packet.Body()); - (*callback)(std::move(result)); - } - - DecreaseUnfnishedJobCount(); -} - - -void -ClientWrapper::HandleDeadConnection(Socket::ConnectionID p_cid) -{ - for (auto& conn : m_connections) - { - if (conn.first == p_cid) - { - conn.first = c_invalidConnectionID; - conn.second = c_invalidConnectionID; - - SPTAG::ErrorCode errCode; - while (c_invalidConnectionID == conn.first) - { - conn.first = m_client->ConnectToServer(m_options.m_serverAddr, m_options.m_serverPort, errCode); - if (SPTAG::ErrorCode::Socket_FailedResolveEndPoint == errCode) - { - break; - } - } - - return; - } - } -} - - -bool -ClientWrapper::IsAvailable() const -{ - return !m_connections.empty(); -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Client/Options.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Client/Options.cpp deleted file mode 100644 index bb067d3d58..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Client/Options.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Client/Options.h" -#include "inc/Helper/StringConvert.h" - -#include - -using namespace SPTAG; -using namespace SPTAG::Client; - -ClientOptions::ClientOptions() - : m_searchTimeout(9000), - m_threadNum(1), - m_socketThreadNum(2) -{ - AddRequiredOption(m_serverAddr, "-s", "--server", "Server address."); - AddRequiredOption(m_serverPort, "-p", "--port", "Server port."); - AddOptionalOption(m_searchTimeout, "-t", "", "Search timeout."); - AddOptionalOption(m_threadNum, "-cth", "", "Client Thread Number."); - AddOptionalOption(m_socketThreadNum, "-sth", "", "Socket Thread Number."); -} - - -ClientOptions::~ClientOptions() -{ -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Client/main.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Client/main.cpp deleted file mode 100644 index 52888e3374..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Client/main.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Client/Options.h" -#include "inc/Client/ClientWrapper.h" - -#include -#include -#include - -std::unique_ptr g_client; - -int main(int argc, char** argv) -{ - SPTAG::Client::ClientOptions options; - if (!options.Parse(argc - 1, argv + 1)) - { - return 1; - } - - g_client.reset(new SPTAG::Client::ClientWrapper(options)); - if (!g_client->IsAvailable()) - { - return 1; - } - - g_client->WaitAllFinished(); - fprintf(stdout, "connection done\n"); - - std::string line; - std::cout << "Query: " << std::flush; - while (std::getline(std::cin, line)) - { - if (line.empty()) - { - break; - } - - SPTAG::Socket::RemoteQuery query; - query.m_type = SPTAG::Socket::RemoteQuery::QueryType::String; - query.m_queryString = std::move(line); - - SPTAG::Socket::RemoteSearchResult result; - auto callback = [&result](SPTAG::Socket::RemoteSearchResult p_result) - { - result = std::move(p_result); - }; - - g_client->SendQueryAsync(query, callback, options); - g_client->WaitAllFinished(); - - std::cout << "Status: " << static_cast(result.m_status) << std::endl; - - for (const auto& indexRes : result.m_allIndexResults) - { - std::cout << "Index: " << indexRes.m_indexName << std::endl; - - int idx = 0; - for (const auto& res : indexRes.m_results) - { - std::cout << "------------------" << std::endl; - std::cout << "DocIndex: " << res.VID << " Distance: " << res.Dist; - if (indexRes.m_results.WithMeta()) - { - const auto& metadata = indexRes.m_results.GetMetadata(idx); - std::cout << " MetaData: " << std::string((char*)metadata.Data(), metadata.Length()); - } - std::cout << std::endl; - ++idx; - } - } - - std::cout << "Query: " << std::flush; - } - - return 0; -} - diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Core/BKT/BKTIndex.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Core/BKT/BKTIndex.cpp deleted file mode 100644 index e8928726f4..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Core/BKT/BKTIndex.cpp +++ /dev/null @@ -1,403 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Core/BKT/Index.h" - -#pragma warning(disable:4996) // 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. -#pragma warning(disable:4242) // '=' : conversion from 'int' to 'short', possible loss of data -#pragma warning(disable:4244) // '=' : conversion from 'int' to 'short', possible loss of data -#pragma warning(disable:4127) // conditional expression is constant - -namespace SPTAG -{ - namespace BKT - { - template - ErrorCode Index::LoadConfig(Helper::IniReader& p_reader) - { -#define DefineBKTParameter(VarName, VarType, DefaultValue, RepresentStr) \ - SetParameter(RepresentStr, \ - p_reader.GetParameter("Index", \ - RepresentStr, \ - std::string(#DefaultValue)).c_str()); \ - -#include "inc/Core/BKT/ParameterDefinitionList.h" -#undef DefineBKTParameter - return ErrorCode::Success; - } - - template - ErrorCode Index::LoadIndexDataFromMemory(const std::vector& p_indexBlobs) - { - if (p_indexBlobs.size() < 3) return ErrorCode::LackOfInputs; - - if (!m_pSamples.Load((char*)p_indexBlobs[0].Data())) return ErrorCode::FailedParseValue; - if (!m_pTrees.LoadTrees((char*)p_indexBlobs[1].Data())) return ErrorCode::FailedParseValue; - if (!m_pGraph.LoadGraph((char*)p_indexBlobs[2].Data())) return ErrorCode::FailedParseValue; - if (p_indexBlobs.size() > 3 && !m_deletedID.load((char*)p_indexBlobs[3].Data())) return ErrorCode::FailedParseValue; - - m_workSpacePool.reset(new COMMON::WorkSpacePool(m_iMaxCheck, GetNumSamples())); - m_workSpacePool->Init(m_iNumberOfThreads); - return ErrorCode::Success; - } - - template - ErrorCode Index::LoadIndexData(const std::string& p_folderPath) - { - if (!m_pSamples.Load(p_folderPath + m_sDataPointsFilename)) return ErrorCode::Fail; - if (!m_pTrees.LoadTrees(p_folderPath + m_sBKTFilename)) return ErrorCode::Fail; - if (!m_pGraph.LoadGraph(p_folderPath + m_sGraphFilename)) return ErrorCode::Fail; - if (!m_deletedID.load(p_folderPath + m_sDeleteDataPointsFilename)) return ErrorCode::Fail; - - m_workSpacePool.reset(new COMMON::WorkSpacePool(m_iMaxCheck, GetNumSamples())); - m_workSpacePool->Init(m_iNumberOfThreads); - return ErrorCode::Success; - } - - template - ErrorCode Index::SaveConfig(std::ostream& p_configOut) const - { -#define DefineBKTParameter(VarName, VarType, DefaultValue, RepresentStr) \ - p_configOut << RepresentStr << "=" << GetParameter(RepresentStr) << std::endl; - -#include "inc/Core/BKT/ParameterDefinitionList.h" -#undef DefineBKTParameter - p_configOut << std::endl; - return ErrorCode::Success; - } - - template - ErrorCode - Index::SaveIndexData(const std::string& p_folderPath) - { - std::lock_guard lock(m_dataAddLock); - std::shared_lock sharedlock(m_deletedID.getLock()); - - if (!m_pSamples.Save(p_folderPath + m_sDataPointsFilename)) return ErrorCode::Fail; - if (!m_pTrees.SaveTrees(p_folderPath + m_sBKTFilename)) return ErrorCode::Fail; - if (!m_pGraph.SaveGraph(p_folderPath + m_sGraphFilename)) return ErrorCode::Fail; - if (!m_deletedID.save(p_folderPath + m_sDeleteDataPointsFilename)) return ErrorCode::Fail; - return ErrorCode::Success; - } - - template - ErrorCode Index::SaveIndexData(const std::vector& p_indexStreams) - { - if (p_indexStreams.size() < 4) return ErrorCode::LackOfInputs; - - std::lock_guard lock(m_dataAddLock); - std::shared_lock sharedlock(m_deletedID.getLock()); - - if (!m_pSamples.Save(*p_indexStreams[0])) return ErrorCode::Fail; - if (!m_pTrees.SaveTrees(*p_indexStreams[1])) return ErrorCode::Fail; - if (!m_pGraph.SaveGraph(*p_indexStreams[2])) return ErrorCode::Fail; - if (!m_deletedID.save(*p_indexStreams[3])) return ErrorCode::Fail; - return ErrorCode::Success; - } - -#pragma region K-NN search - -#define Search(CheckDeleted1) \ - m_pTrees.InitSearchTrees(this, p_query, p_space); \ - const DimensionType checkPos = m_pGraph.m_iNeighborhoodSize - 1; \ - while (!p_space.m_SPTQueue.empty()) { \ - m_pTrees.SearchTrees(this, p_query, p_space, m_iNumberOfOtherDynamicPivots + p_space.m_iNumberOfCheckedLeaves); \ - while (!p_space.m_NGQueue.empty()) { \ - COMMON::HeapCell gnode = p_space.m_NGQueue.pop(); \ - const SizeType *node = m_pGraph[gnode.node]; \ - _mm_prefetch((const char *)node, _MM_HINT_T0); \ - CheckDeleted1 { \ - if (p_query.AddPoint(gnode.node, gnode.distance)) { \ - p_space.m_iNumOfContinuousNoBetterPropagation = 0; \ - SizeType checkNode = node[checkPos]; \ - if (checkNode < -1) { \ - const COMMON::BKTNode& tnode = m_pTrees[-2 - checkNode]; \ - for (SizeType i = -tnode.childStart; i < tnode.childEnd; i++) { \ - if (!p_query.AddPoint(m_pTrees[i].centerid, gnode.distance)) break; \ - } \ - } \ - } \ - else { \ - p_space.m_iNumOfContinuousNoBetterPropagation++; \ - if (p_space.m_iNumOfContinuousNoBetterPropagation > p_space.m_iContinuousLimit || p_space.m_iNumberOfCheckedLeaves > p_space.m_iMaxCheck) { \ - p_query.SortResult(); return; \ - } \ - } \ - } \ - for (DimensionType i = 0; i <= checkPos; i++) { \ - _mm_prefetch((const char *)(m_pSamples)[node[i]], _MM_HINT_T0); \ - } \ - for (DimensionType i = 0; i <= checkPos; i++) { \ - SizeType nn_index = node[i]; \ - if (nn_index < 0) break; \ - if (p_space.CheckAndSet(nn_index)) continue; \ - float distance2leaf = m_fComputeDistance(p_query.GetTarget(), (m_pSamples)[nn_index], GetFeatureDim()); \ - p_space.m_iNumberOfCheckedLeaves++; \ - p_space.m_NGQueue.insert(COMMON::HeapCell(nn_index, distance2leaf)); \ - } \ - if (p_space.m_NGQueue.Top().distance > p_space.m_SPTQueue.Top().distance) { \ - break; \ - } \ - } \ - } \ - p_query.SortResult(); \ - - template - void Index::SearchIndexWithDeleted(COMMON::QueryResultSet &p_query, COMMON::WorkSpace &p_space, const Helper::Concurrent::ConcurrentSet &p_deleted) const - { - Search(if (!p_deleted.contains(gnode.node))) - } - - template - void Index::SearchIndexWithoutDeleted(COMMON::QueryResultSet &p_query, COMMON::WorkSpace &p_space) const - { - Search(;) - } - - template - ErrorCode - Index::SearchIndex(QueryResult &p_query) const - { - auto workSpace = m_workSpacePool->Rent(); - workSpace->Reset(m_iMaxCheck); - - if (m_deletedID.size() > 0) - SearchIndexWithDeleted(*((COMMON::QueryResultSet*)&p_query), *workSpace, m_deletedID); - else - SearchIndexWithoutDeleted(*((COMMON::QueryResultSet*)&p_query), *workSpace); - - m_workSpacePool->Return(workSpace); - - if (p_query.WithMeta() && nullptr != m_pMetadata) - { - for (int i = 0; i < p_query.GetResultNum(); ++i) - { - SizeType result = p_query.GetResult(i)->VID; - p_query.SetMetadata(i, (result < 0) ? ByteArray::c_empty : m_pMetadata->GetMetadata(result)); - } - } - return ErrorCode::Success; - } -#pragma endregion - - template - ErrorCode Index::BuildIndex(const void* p_data, SizeType p_vectorNum, DimensionType p_dimension) - { - omp_set_num_threads(m_iNumberOfThreads); - - m_pSamples.Initialize(p_vectorNum, p_dimension, (T*)p_data, false); - - if (DistCalcMethod::Cosine == m_iDistCalcMethod) - { - int base = COMMON::Utils::GetBase(); -#pragma omp parallel for - for (SizeType i = 0; i < GetNumSamples(); i++) { - COMMON::Utils::Normalize(m_pSamples[i], GetFeatureDim(), base); - } - } - - m_workSpacePool.reset(new COMMON::WorkSpacePool(m_iMaxCheck, GetNumSamples())); - m_workSpacePool->Init(m_iNumberOfThreads); - - m_pTrees.BuildTrees(this); - m_pGraph.BuildGraph(this, &(m_pTrees.GetSampleMap())); - - return ErrorCode::Success; - } - - template - ErrorCode Index::RefineIndex(const std::vector& p_indexStreams) - { - std::lock_guard lock(m_dataAddLock); - std::shared_lock sharedlock(m_deletedID.getLock()); - - SizeType newR = GetNumSamples(); - - std::vector indices; - std::vector reverseIndices(newR); - for (SizeType i = 0; i < newR; i++) { - if (!m_deletedID.contains(i)) { - indices.push_back(i); - reverseIndices[i] = i; - } - else { - while (m_deletedID.contains(newR - 1) && newR > i) newR--; - if (newR == i) break; - indices.push_back(newR - 1); - reverseIndices[newR - 1] = i; - newR--; - } - } - - std::cout << "Refine... from " << GetNumSamples() << "->" << newR << std::endl; - - if (false == m_pSamples.Refine(indices, *p_indexStreams[0])) return ErrorCode::Fail; - if (nullptr != m_pMetadata && (p_indexStreams.size() < 6 || ErrorCode::Success != m_pMetadata->RefineMetadata(indices, *p_indexStreams[4], *p_indexStreams[5]))) return ErrorCode::Fail; - - COMMON::BKTree newTrees(m_pTrees); - newTrees.BuildTrees(this, &indices); -#pragma omp parallel for - for (SizeType i = 0; i < newTrees.size(); i++) { - newTrees[i].centerid = reverseIndices[newTrees[i].centerid]; - } - newTrees.SaveTrees(*p_indexStreams[1]); - - m_pGraph.RefineGraph(this, indices, reverseIndices, *p_indexStreams[2], &(newTrees.GetSampleMap())); - - Helper::Concurrent::ConcurrentSet newDeletedID; - newDeletedID.save(*p_indexStreams[3]); - return ErrorCode::Success; - } - - template - ErrorCode Index::RefineIndex(const std::string& p_folderPath) - { - std::string folderPath(p_folderPath); - if (!folderPath.empty() && *(folderPath.rbegin()) != FolderSep) - { - folderPath += FolderSep; - } - - if (!direxists(folderPath.c_str())) - { - mkdir(folderPath.c_str()); - } - - std::vector streams; - streams.push_back(new std::ofstream(folderPath + m_sDataPointsFilename, std::ios::binary)); - streams.push_back(new std::ofstream(folderPath + m_sBKTFilename, std::ios::binary)); - streams.push_back(new std::ofstream(folderPath + m_sGraphFilename, std::ios::binary)); - streams.push_back(new std::ofstream(folderPath + m_sDeleteDataPointsFilename, std::ios::binary)); - if (nullptr != m_pMetadata) - { - streams.push_back(new std::ofstream(folderPath + m_sMetadataFile, std::ios::binary)); - streams.push_back(new std::ofstream(folderPath + m_sMetadataIndexFile, std::ios::binary)); - } - - for (size_t i = 0; i < streams.size(); i++) - if (!(((std::ofstream*)streams[i])->is_open())) return ErrorCode::FailedCreateFile; - - ErrorCode ret = RefineIndex(streams); - - for (size_t i = 0; i < streams.size(); i++) - { - ((std::ofstream*)streams[i])->close(); - delete streams[i]; - } - return ret; - } - - template - ErrorCode Index::DeleteIndex(const void* p_vectors, SizeType p_vectorNum) { - const T* ptr_v = (const T*)p_vectors; -#pragma omp parallel for schedule(dynamic) - for (SizeType i = 0; i < p_vectorNum; i++) { - COMMON::QueryResultSet query(ptr_v + i * GetFeatureDim(), m_pGraph.m_iCEF); - SearchIndex(query); - - for (int i = 0; i < m_pGraph.m_iCEF; i++) { - if (query.GetResult(i)->Dist < 1e-6) { - m_deletedID.insert(query.GetResult(i)->VID); - } - } - } - return ErrorCode::Success; - } - - template - ErrorCode Index::DeleteIndex(const SizeType& p_id) { - m_deletedID.insert(p_id); - return ErrorCode::Success; - } - - template - ErrorCode Index::AddIndex(const void* p_vectors, SizeType p_vectorNum, DimensionType p_dimension, SizeType* p_start) - { - SizeType begin, end; - { - std::lock_guard lock(m_dataAddLock); - - begin = GetNumSamples(); - end = GetNumSamples() + p_vectorNum; - - if (p_start != nullptr) *p_start = begin; - - if (begin == 0) return BuildIndex(p_vectors, p_vectorNum, p_dimension); - - if (p_dimension != GetFeatureDim()) return ErrorCode::FailedParseValue; - - if (m_pSamples.AddBatch((const T*)p_vectors, p_vectorNum) != ErrorCode::Success || m_pGraph.AddBatch(p_vectorNum) != ErrorCode::Success) { - std::cout << "Memory Error: Cannot alloc space for vectors" << std::endl; - m_pSamples.SetR(begin); - m_pGraph.SetR(begin); - return ErrorCode::MemoryOverFlow; - } - if (DistCalcMethod::Cosine == m_iDistCalcMethod) - { - int base = COMMON::Utils::GetBase(); - for (SizeType i = begin; i < end; i++) { - COMMON::Utils::Normalize((T*)m_pSamples[i], GetFeatureDim(), base); - } - } - } - - for (SizeType node = begin; node < end; node++) - { - m_pGraph.RefineNode(this, node, true); - } - std::cout << "Add " << p_vectorNum << " vectors" << std::endl; - return ErrorCode::Success; - } - - template - ErrorCode - Index::SetParameter(const char* p_param, const char* p_value) - { - if (nullptr == p_param || nullptr == p_value) return ErrorCode::Fail; - -#define DefineBKTParameter(VarName, VarType, DefaultValue, RepresentStr) \ - else if (SPTAG::Helper::StrUtils::StrEqualIgnoreCase(p_param, RepresentStr)) \ - { \ - fprintf(stderr, "Setting %s with value %s\n", RepresentStr, p_value); \ - VarType tmp; \ - if (SPTAG::Helper::Convert::ConvertStringTo(p_value, tmp)) \ - { \ - VarName = tmp; \ - } \ - } \ - -#include "inc/Core/BKT/ParameterDefinitionList.h" -#undef DefineBKTParameter - - m_fComputeDistance = COMMON::DistanceCalcSelector(m_iDistCalcMethod); - return ErrorCode::Success; - } - - - template - std::string - Index::GetParameter(const char* p_param) const - { - if (nullptr == p_param) return std::string(); - -#define DefineBKTParameter(VarName, VarType, DefaultValue, RepresentStr) \ - else if (SPTAG::Helper::StrUtils::StrEqualIgnoreCase(p_param, RepresentStr)) \ - { \ - return SPTAG::Helper::Convert::ConvertToString(VarName); \ - } \ - -#include "inc/Core/BKT/ParameterDefinitionList.h" -#undef DefineBKTParameter - - return std::string(); - } - } -} - -#define DefineVectorValueType(Name, Type) \ -template class SPTAG::BKT::Index; \ - -#include "inc/Core/DefinitionList.h" -#undef DefineVectorValueType - - diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Core/Common/NeighborhoodGraph.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Core/Common/NeighborhoodGraph.cpp deleted file mode 100644 index 94115dd0a3..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Core/Common/NeighborhoodGraph.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Core/Common/NeighborhoodGraph.h" -#include "inc/Core/Common/RelativeNeighborhoodGraph.h" - -using namespace SPTAG::COMMON; - -std::shared_ptr NeighborhoodGraph::CreateInstance(std::string type) -{ - std::shared_ptr res; - if (type == "RNG") - { - res.reset(new RelativeNeighborhoodGraph); - } - return res; -} \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Core/Common/WorkSpacePool.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Core/Common/WorkSpacePool.cpp deleted file mode 100644 index a88dbdb2d5..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Core/Common/WorkSpacePool.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Core/Common/WorkSpacePool.h" - -using namespace SPTAG; -using namespace SPTAG::COMMON; - - -WorkSpacePool::WorkSpacePool(int p_maxCheck, SizeType p_vectorCount) - : m_maxCheck(p_maxCheck), - m_vectorCount(p_vectorCount) -{ -} - - -WorkSpacePool::~WorkSpacePool() -{ - for (auto& workSpace : m_workSpacePool) - workSpace.reset(); - m_workSpacePool.clear(); -} - - -std::shared_ptr -WorkSpacePool::Rent() -{ - std::shared_ptr workSpace; - - { - std::lock_guard lock(m_workSpacePoolMutex); - if (!m_workSpacePool.empty()) - { - workSpace = m_workSpacePool.front(); - m_workSpacePool.pop_front(); - } - else - { - workSpace.reset(new WorkSpace); - workSpace->Initialize(m_maxCheck, m_vectorCount); - } - } - return workSpace; -} - - -void -WorkSpacePool::Return(const std::shared_ptr& p_workSpace) -{ - { - std::lock_guard lock(m_workSpacePoolMutex); - m_workSpacePool.push_back(p_workSpace); - } -} - - -void -WorkSpacePool::Init(int size) -{ - for (int i = 0; i < size; i++) - { - std::shared_ptr workSpace(new WorkSpace); - workSpace->Initialize(m_maxCheck, m_vectorCount); - m_workSpacePool.push_back(std::move(workSpace)); - } -} \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Core/KDT/KDTIndex.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Core/KDT/KDTIndex.cpp deleted file mode 100644 index da3c10e095..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Core/KDT/KDTIndex.cpp +++ /dev/null @@ -1,396 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Core/KDT/Index.h" - -#pragma warning(disable:4996) // 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. -#pragma warning(disable:4242) // '=' : conversion from 'int' to 'short', possible loss of data -#pragma warning(disable:4244) // '=' : conversion from 'int' to 'short', possible loss of data -#pragma warning(disable:4127) // conditional expression is constant - -namespace SPTAG -{ - namespace KDT - { - template - ErrorCode Index::LoadConfig(Helper::IniReader& p_reader) - { -#define DefineKDTParameter(VarName, VarType, DefaultValue, RepresentStr) \ - SetParameter(RepresentStr, \ - p_reader.GetParameter("Index", \ - RepresentStr, \ - std::string(#DefaultValue)).c_str()); \ - -#include "inc/Core/KDT/ParameterDefinitionList.h" -#undef DefineKDTParameter - return ErrorCode::Success; - } - - template - ErrorCode Index::LoadIndexDataFromMemory(const std::vector& p_indexBlobs) - { - if (p_indexBlobs.size() < 3) return ErrorCode::LackOfInputs; - - if (!m_pSamples.Load((char*)p_indexBlobs[0].Data())) return ErrorCode::FailedParseValue; - if (!m_pTrees.LoadTrees((char*)p_indexBlobs[1].Data())) return ErrorCode::FailedParseValue; - if (!m_pGraph.LoadGraph((char*)p_indexBlobs[2].Data())) return ErrorCode::FailedParseValue; - if (p_indexBlobs.size() > 3 && !m_deletedID.load((char*)p_indexBlobs[3].Data())) return ErrorCode::FailedParseValue; - - m_workSpacePool.reset(new COMMON::WorkSpacePool(m_iMaxCheck, GetNumSamples())); - m_workSpacePool->Init(m_iNumberOfThreads); - return ErrorCode::Success; - } - - template - ErrorCode Index::LoadIndexData(const std::string& p_folderPath) - { - if (!m_pSamples.Load(p_folderPath + m_sDataPointsFilename)) return ErrorCode::Fail; - if (!m_pTrees.LoadTrees(p_folderPath + m_sKDTFilename)) return ErrorCode::Fail; - if (!m_pGraph.LoadGraph(p_folderPath + m_sGraphFilename)) return ErrorCode::Fail; - if (!m_deletedID.load(p_folderPath + m_sDeleteDataPointsFilename)) return ErrorCode::Fail; - - m_workSpacePool.reset(new COMMON::WorkSpacePool(m_iMaxCheck, GetNumSamples())); - m_workSpacePool->Init(m_iNumberOfThreads); - return ErrorCode::Success; - } - - template - ErrorCode Index::SaveConfig(std::ostream& p_configOut) const - { -#define DefineKDTParameter(VarName, VarType, DefaultValue, RepresentStr) \ - p_configOut << RepresentStr << "=" << GetParameter(RepresentStr) << std::endl; - -#include "inc/Core/KDT/ParameterDefinitionList.h" -#undef DefineKDTParameter - p_configOut << std::endl; - return ErrorCode::Success; - } - - template - ErrorCode Index::SaveIndexData(const std::string& p_folderPath) - { - std::lock_guard lock(m_dataAddLock); - std::shared_lock sharedlock(m_deletedID.getLock()); - - if (!m_pSamples.Save(p_folderPath + m_sDataPointsFilename)) return ErrorCode::Fail; - if (!m_pTrees.SaveTrees(p_folderPath + m_sKDTFilename)) return ErrorCode::Fail; - if (!m_pGraph.SaveGraph(p_folderPath + m_sGraphFilename)) return ErrorCode::Fail; - if (!m_deletedID.save(p_folderPath + m_sDeleteDataPointsFilename)) return ErrorCode::Fail; - return ErrorCode::Success; - } - - template - ErrorCode Index::SaveIndexData(const std::vector& p_indexStreams) - { - if (p_indexStreams.size() < 4) return ErrorCode::LackOfInputs; - - std::lock_guard lock(m_dataAddLock); - std::shared_lock sharedlock(m_deletedID.getLock()); - - if (!m_pSamples.Save(*p_indexStreams[0])) return ErrorCode::Fail; - if (!m_pTrees.SaveTrees(*p_indexStreams[1])) return ErrorCode::Fail; - if (!m_pGraph.SaveGraph(*p_indexStreams[2])) return ErrorCode::Fail; - if (!m_deletedID.save(*p_indexStreams[3])) return ErrorCode::Fail; - return ErrorCode::Success; - } - -#pragma region K-NN search - -#define Search(CheckDeleted1) \ - m_pTrees.InitSearchTrees(this, p_query, p_space, m_iNumberOfInitialDynamicPivots); \ - while (!p_space.m_NGQueue.empty()) { \ - COMMON::HeapCell gnode = p_space.m_NGQueue.pop(); \ - const SizeType *node = m_pGraph[gnode.node]; \ - _mm_prefetch((const char *)node, _MM_HINT_T0); \ - CheckDeleted1 { \ - if (!p_query.AddPoint(gnode.node, gnode.distance) && p_space.m_iNumberOfCheckedLeaves > p_space.m_iMaxCheck) { \ - p_query.SortResult(); return; \ - } \ - } \ - for (DimensionType i = 0; i < m_pGraph.m_iNeighborhoodSize; i++) \ - _mm_prefetch((const char *)(m_pSamples)[node[i]], _MM_HINT_T0); \ - float upperBound = max(p_query.worstDist(), gnode.distance); \ - bool bLocalOpt = true; \ - for (DimensionType i = 0; i < m_pGraph.m_iNeighborhoodSize; i++) { \ - SizeType nn_index = node[i]; \ - if (nn_index < 0) break; \ - if (p_space.CheckAndSet(nn_index)) continue; \ - float distance2leaf = m_fComputeDistance(p_query.GetTarget(), (m_pSamples)[nn_index], GetFeatureDim()); \ - if (distance2leaf <= upperBound) bLocalOpt = false; \ - p_space.m_iNumberOfCheckedLeaves++; \ - p_space.m_NGQueue.insert(COMMON::HeapCell(nn_index, distance2leaf)); \ - } \ - if (bLocalOpt) p_space.m_iNumOfContinuousNoBetterPropagation++; \ - else p_space.m_iNumOfContinuousNoBetterPropagation = 0; \ - if (p_space.m_iNumOfContinuousNoBetterPropagation > m_iThresholdOfNumberOfContinuousNoBetterPropagation) { \ - if (p_space.m_iNumberOfTreeCheckedLeaves <= p_space.m_iNumberOfCheckedLeaves / 10) { \ - m_pTrees.SearchTrees(this, p_query, p_space, m_iNumberOfOtherDynamicPivots + p_space.m_iNumberOfCheckedLeaves); \ - } else if (gnode.distance > p_query.worstDist()) { \ - break; \ - } \ - } \ - } \ - p_query.SortResult(); \ - - template - void Index::SearchIndexWithDeleted(COMMON::QueryResultSet &p_query, COMMON::WorkSpace &p_space, const Helper::Concurrent::ConcurrentSet &p_deleted) const - { - Search(if (!p_deleted.contains(gnode.node))) - } - - template - void Index::SearchIndexWithoutDeleted(COMMON::QueryResultSet &p_query, COMMON::WorkSpace &p_space) const - { - Search(;) - } - - template - ErrorCode - Index::SearchIndex(QueryResult &p_query) const - { - auto workSpace = m_workSpacePool->Rent(); - workSpace->Reset(m_iMaxCheck); - - if (m_deletedID.size() > 0) - SearchIndexWithDeleted(*((COMMON::QueryResultSet*)&p_query), *workSpace, m_deletedID); - else - SearchIndexWithoutDeleted(*((COMMON::QueryResultSet*)&p_query), *workSpace); - - m_workSpacePool->Return(workSpace); - - if (p_query.WithMeta() && nullptr != m_pMetadata) - { - for (int i = 0; i < p_query.GetResultNum(); ++i) - { - SizeType result = p_query.GetResult(i)->VID; - p_query.SetMetadata(i, (result < 0) ? ByteArray::c_empty : m_pMetadata->GetMetadata(result)); - } - } - return ErrorCode::Success; - } -#pragma endregion - - template - ErrorCode Index::BuildIndex(const void* p_data, SizeType p_vectorNum, DimensionType p_dimension) - { - omp_set_num_threads(m_iNumberOfThreads); - - m_pSamples.Initialize(p_vectorNum, p_dimension, (T*)p_data, false); - - if (DistCalcMethod::Cosine == m_iDistCalcMethod) - { - int base = COMMON::Utils::GetBase(); -#pragma omp parallel for - for (SizeType i = 0; i < GetNumSamples(); i++) { - COMMON::Utils::Normalize(m_pSamples[i], GetFeatureDim(), base); - } - } - - m_workSpacePool.reset(new COMMON::WorkSpacePool(m_iMaxCheck, GetNumSamples())); - m_workSpacePool->Init(m_iNumberOfThreads); - - m_pTrees.BuildTrees(this); - m_pGraph.BuildGraph(this); - - return ErrorCode::Success; - } - - template - ErrorCode Index::RefineIndex(const std::vector& p_indexStreams) - { - std::lock_guard lock(m_dataAddLock); - std::shared_lock sharedlock(m_deletedID.getLock()); - - SizeType newR = GetNumSamples(); - - std::vector indices; - std::vector reverseIndices(newR); - for (SizeType i = 0; i < newR; i++) { - if (!m_deletedID.contains(i)) { - indices.push_back(i); - reverseIndices[i] = i; - } - else { - while (m_deletedID.contains(newR - 1) && newR > i) newR--; - if (newR == i) break; - indices.push_back(newR - 1); - reverseIndices[newR - 1] = i; - newR--; - } - } - - std::cout << "Refine... from " << GetNumSamples() << "->" << newR << std::endl; - - if (false == m_pSamples.Refine(indices, *p_indexStreams[0])) return ErrorCode::Fail; - if (nullptr != m_pMetadata && (p_indexStreams.size() < 6 || ErrorCode::Success != m_pMetadata->RefineMetadata(indices, *p_indexStreams[4], *p_indexStreams[5]))) return ErrorCode::Fail; - - m_pGraph.RefineGraph(this, indices, reverseIndices, *p_indexStreams[2]); - - COMMON::KDTree newTrees(m_pTrees); - newTrees.BuildTrees(this, &indices); -#pragma omp parallel for - for (SizeType i = 0; i < newTrees.size(); i++) { - if (newTrees[i].left < 0) - newTrees[i].left = -reverseIndices[-newTrees[i].left - 1] - 1; - if (newTrees[i].right < 0) - newTrees[i].right = -reverseIndices[-newTrees[i].right - 1] - 1; - } - newTrees.SaveTrees(*p_indexStreams[1]); - - Helper::Concurrent::ConcurrentSet newDeletedID; - newDeletedID.save(*p_indexStreams[3]); - return ErrorCode::Success; - } - - template - ErrorCode Index::RefineIndex(const std::string& p_folderPath) - { - std::string folderPath(p_folderPath); - if (!folderPath.empty() && *(folderPath.rbegin()) != FolderSep) - { - folderPath += FolderSep; - } - - if (!direxists(folderPath.c_str())) - { - mkdir(folderPath.c_str()); - } - - std::vector streams; - streams.push_back(new std::ofstream(folderPath + m_sDataPointsFilename, std::ios::binary)); - streams.push_back(new std::ofstream(folderPath + m_sKDTFilename, std::ios::binary)); - streams.push_back(new std::ofstream(folderPath + m_sGraphFilename, std::ios::binary)); - streams.push_back(new std::ofstream(folderPath + m_sDeleteDataPointsFilename, std::ios::binary)); - if (nullptr != m_pMetadata) - { - streams.push_back(new std::ofstream(folderPath + m_sMetadataFile, std::ios::binary)); - streams.push_back(new std::ofstream(folderPath + m_sMetadataIndexFile, std::ios::binary)); - } - - for (size_t i = 0; i < streams.size(); i++) - if (!(((std::ofstream*)streams[i])->is_open())) return ErrorCode::FailedCreateFile; - - ErrorCode ret = RefineIndex(streams); - - for (size_t i = 0; i < streams.size(); i++) - { - ((std::ofstream*)streams[i])->close(); - delete streams[i]; - } - return ret; - } - - template - ErrorCode Index::DeleteIndex(const void* p_vectors, SizeType p_vectorNum) { - const T* ptr_v = (const T*)p_vectors; -#pragma omp parallel for schedule(dynamic) - for (SizeType i = 0; i < p_vectorNum; i++) { - COMMON::QueryResultSet query(ptr_v + i * GetFeatureDim(), m_pGraph.m_iCEF); - SearchIndex(query); - - for (int i = 0; i < m_pGraph.m_iCEF; i++) { - if (query.GetResult(i)->Dist < 1e-6) { - m_deletedID.insert(query.GetResult(i)->VID); - } - } - } - return ErrorCode::Success; - } - - template - ErrorCode Index::DeleteIndex(const SizeType& p_id) { - m_deletedID.insert(p_id); - return ErrorCode::Success; - } - - template - ErrorCode Index::AddIndex(const void* p_vectors, SizeType p_vectorNum, DimensionType p_dimension, SizeType* p_start) - { - SizeType begin, end; - { - std::lock_guard lock(m_dataAddLock); - - begin = GetNumSamples(); - end = GetNumSamples() + p_vectorNum; - - if (p_start != nullptr) *p_start = begin; - - if (begin == 0) return BuildIndex(p_vectors, p_vectorNum, p_dimension); - - if (p_dimension != GetFeatureDim()) return ErrorCode::FailedParseValue; - - if (m_pSamples.AddBatch((const T*)p_vectors, p_vectorNum) != ErrorCode::Success || m_pGraph.AddBatch(p_vectorNum) != ErrorCode::Success) { - std::cout << "Memory Error: Cannot alloc space for vectors" << std::endl; - m_pSamples.SetR(begin); - m_pGraph.SetR(begin); - return ErrorCode::MemoryOverFlow; - } - if (DistCalcMethod::Cosine == m_iDistCalcMethod) - { - int base = COMMON::Utils::GetBase(); - for (SizeType i = begin; i < end; i++) { - COMMON::Utils::Normalize((T*)m_pSamples[i], GetFeatureDim(), base); - } - } - } - - for (SizeType node = begin; node < end; node++) - { - m_pGraph.RefineNode(this, node, true); - } - std::cout << "Add " << p_vectorNum << " vectors" << std::endl; - return ErrorCode::Success; - } - - template - ErrorCode - Index::SetParameter(const char* p_param, const char* p_value) - { - if (nullptr == p_param || nullptr == p_value) return ErrorCode::Fail; - -#define DefineKDTParameter(VarName, VarType, DefaultValue, RepresentStr) \ - else if (SPTAG::Helper::StrUtils::StrEqualIgnoreCase(p_param, RepresentStr)) \ - { \ - fprintf(stderr, "Setting %s with value %s\n", RepresentStr, p_value); \ - VarType tmp; \ - if (SPTAG::Helper::Convert::ConvertStringTo(p_value, tmp)) \ - { \ - VarName = tmp; \ - } \ - } \ - -#include "inc/Core/KDT/ParameterDefinitionList.h" -#undef DefineKDTParameter - - m_fComputeDistance = COMMON::DistanceCalcSelector(m_iDistCalcMethod); - return ErrorCode::Success; - } - - - template - std::string - Index::GetParameter(const char* p_param) const - { - if (nullptr == p_param) return std::string(); - -#define DefineKDTParameter(VarName, VarType, DefaultValue, RepresentStr) \ - else if (SPTAG::Helper::StrUtils::StrEqualIgnoreCase(p_param, RepresentStr)) \ - { \ - return SPTAG::Helper::Convert::ConvertToString(VarName); \ - } \ - -#include "inc/Core/KDT/ParameterDefinitionList.h" -#undef DefineKDTParameter - - return std::string(); - } - } -} - -#define DefineVectorValueType(Name, Type) \ -template class SPTAG::KDT::Index; \ - -#include "inc/Core/DefinitionList.h" -#undef DefineVectorValueType - - diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Core/MetadataSet.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Core/MetadataSet.cpp deleted file mode 100644 index 137eb5d13a..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Core/MetadataSet.cpp +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Core/MetadataSet.h" - -#include -#include - -using namespace SPTAG; - -ErrorCode -MetadataSet::RefineMetadata(std::vector& indices, std::ostream& p_metaOut, std::ostream& p_metaIndexOut) -{ - SizeType R = (SizeType)indices.size(); - p_metaIndexOut.write((char*)&R, sizeof(SizeType)); - std::uint64_t offset = 0; - for (SizeType i = 0; i < R; i++) { - p_metaIndexOut.write((char*)&offset, sizeof(std::uint64_t)); - ByteArray meta = GetMetadata(indices[i]); - p_metaOut.write((char*)meta.Data(), sizeof(uint8_t)*meta.Length()); - offset += meta.Length(); - } - p_metaIndexOut.write((char*)&offset, sizeof(std::uint64_t)); - return ErrorCode::Success; -} - - -ErrorCode -MetadataSet::RefineMetadata(std::vector& indices, const std::string& p_metaFile, const std::string& p_metaindexFile) -{ - std::ofstream metaOut(p_metaFile + "_tmp", std::ios::binary); - std::ofstream metaIndexOut(p_metaindexFile, std::ios::binary); - if (!metaOut.is_open() || !metaIndexOut.is_open()) return ErrorCode::FailedCreateFile; - - RefineMetadata(indices, metaOut, metaIndexOut); - metaOut.close(); - metaIndexOut.close(); - - if (fileexists(p_metaFile.c_str())) std::remove(p_metaFile.c_str()); - std::rename((p_metaFile + "_tmp").c_str(), p_metaFile.c_str()); - return ErrorCode::Success; -} - - -MetadataSet::MetadataSet() -{ -} - - -MetadataSet:: ~MetadataSet() -{ -} - - -FileMetadataSet::FileMetadataSet(const std::string& p_metafile, const std::string& p_metaindexfile) - : m_metaFile(p_metafile), - m_metaindexFile(p_metaindexfile) -{ - m_fp = new std::ifstream(p_metafile, std::ifstream::binary); - std::ifstream fpidx(p_metaindexfile, std::ifstream::binary); - if (!m_fp->is_open() || !fpidx.is_open()) - { - std::cerr << "ERROR: Cannot open meta files " << p_metafile << " and " << p_metaindexfile << "!" << std::endl; - return; - } - - fpidx.read((char *)&m_count, sizeof(m_count)); - m_pOffsets.resize(m_count + 1); - fpidx.read((char *)m_pOffsets.data(), sizeof(std::uint64_t) * (m_count + 1)); - fpidx.close(); -} - - -FileMetadataSet::~FileMetadataSet() -{ - if (m_fp) - { - m_fp->close(); - delete m_fp; - } -} - - -ByteArray -FileMetadataSet::GetMetadata(SizeType p_vectorID) const -{ - std::uint64_t startoff = m_pOffsets[p_vectorID]; - std::uint64_t bytes = m_pOffsets[p_vectorID + 1] - startoff; - if (p_vectorID < m_count) { - m_fp->seekg(startoff, std::ios_base::beg); - ByteArray b = ByteArray::Alloc(bytes); - m_fp->read((char*)b.Data(), bytes); - return b; - } - else { - startoff -= m_pOffsets[m_count]; - return ByteArray((std::uint8_t*)m_newdata.data() + startoff, bytes, false); - } -} - - -SizeType -FileMetadataSet::Count() const -{ - return static_cast(m_pOffsets.size() - 1); -} - - -bool -FileMetadataSet::Available() const -{ - return m_fp && m_fp->is_open() && m_pOffsets.size() > 1; -} - - -std::pair -FileMetadataSet::BufferSize() const -{ - return std::make_pair(m_pOffsets[m_pOffsets.size() - 1], - sizeof(SizeType) + sizeof(std::uint64_t) * m_pOffsets.size()); -} - - -void -FileMetadataSet::AddBatch(MetadataSet& data) -{ - for (SizeType i = 0; i < data.Count(); i++) - { - ByteArray newdata = data.GetMetadata(i); - m_newdata.insert(m_newdata.end(), newdata.Data(), newdata.Data() + newdata.Length()); - m_pOffsets.push_back(m_pOffsets[m_pOffsets.size() - 1] + newdata.Length()); - } -} - - - -ErrorCode -FileMetadataSet::SaveMetadata(std::ostream& p_metaOut, std::ostream& p_metaIndexOut) -{ - m_fp->seekg(0, std::ios_base::beg); - - int bufsize = 1000000; - char* buf = new char[bufsize]; - while (!m_fp->eof()) { - m_fp->read(buf, bufsize); - p_metaOut.write(buf, m_fp->gcount()); - } - delete[] buf; - - if (m_newdata.size() > 0) { - p_metaOut.write((char*)m_newdata.data(), m_newdata.size()); - } - - SizeType count = Count(); - p_metaIndexOut.write((char*)&count, sizeof(SizeType)); - p_metaIndexOut.write((char*)m_pOffsets.data(), sizeof(std::uint64_t) * m_pOffsets.size()); - return ErrorCode::Success; -} - - -ErrorCode -FileMetadataSet::SaveMetadata(const std::string& p_metaFile, const std::string& p_metaindexFile) -{ - std::ofstream metaOut(p_metaFile + "_tmp", std::ios::binary); - std::ofstream metaIndexOut(p_metaindexFile, std::ios::binary); - if (!metaOut.is_open() || !metaIndexOut.is_open()) return ErrorCode::FailedCreateFile; - - SaveMetadata(metaOut, metaIndexOut); - metaOut.close(); - metaIndexOut.close(); - - m_fp->close(); - if (fileexists(p_metaFile.c_str())) std::remove(p_metaFile.c_str()); - std::rename((p_metaFile + "_tmp").c_str(), p_metaFile.c_str()); - m_fp->open(p_metaFile, std::ifstream::binary); - m_count = Count(); - m_newdata.clear(); - return ErrorCode::Success; -} - - -MemMetadataSet::MemMetadataSet(ByteArray p_metadata, ByteArray p_offsets, SizeType p_count) - : m_metadataHolder(std::move(p_metadata)), - m_offsetHolder(std::move(p_offsets)), - m_count(p_count) -{ - const std::uint64_t* newdata = reinterpret_cast(m_offsetHolder.Data()); - m_offsets.insert(m_offsets.end(), newdata, newdata + p_count + 1); -} - - -MemMetadataSet::~MemMetadataSet() -{ -} - - -ByteArray -MemMetadataSet::GetMetadata(SizeType p_vectorID) const -{ - if (p_vectorID < m_count) - { - return ByteArray(m_metadataHolder.Data() + m_offsets[p_vectorID], - m_offsets[p_vectorID + 1] - m_offsets[p_vectorID], - false); - } - else if (p_vectorID < (SizeType)(m_offsets.size() - 1)) { - return ByteArray((std::uint8_t*)m_newdata.data() + m_offsets[p_vectorID] - m_offsets[m_count], - m_offsets[p_vectorID + 1] - m_offsets[p_vectorID], - false); - } - - return ByteArray::c_empty; -} - - -SizeType -MemMetadataSet::Count() const -{ - return static_cast(m_offsets.size() - 1); -} - - -bool -MemMetadataSet::Available() const -{ - return m_metadataHolder.Length() > 0 && m_offsetHolder.Length() > 0; -} - - -std::pair -MemMetadataSet::BufferSize() const -{ - return std::make_pair(m_offsets[m_offsets.size() - 1], - sizeof(SizeType) + sizeof(std::uint64_t) * m_offsets.size()); -} - -void -MemMetadataSet::AddBatch(MetadataSet& data) -{ - for (SizeType i = 0; i < data.Count(); i++) - { - ByteArray newdata = data.GetMetadata(i); - m_newdata.insert(m_newdata.end(), newdata.Data(), newdata.Data() + newdata.Length()); - m_offsets.push_back(m_offsets[m_offsets.size() - 1] + newdata.Length()); - } -} - - -ErrorCode -MemMetadataSet::SaveMetadata(std::ostream& p_metaOut, std::ostream& p_metaIndexOut) -{ - p_metaOut.write(reinterpret_cast(m_metadataHolder.Data()), m_metadataHolder.Length()); - if (m_newdata.size() > 0) { - p_metaOut.write((char*)m_newdata.data(), m_newdata.size()); - } - - SizeType count = Count(); - p_metaIndexOut.write((char*)&count, sizeof(SizeType)); - p_metaIndexOut.write((char*)m_offsets.data(), sizeof(std::uint64_t) * m_offsets.size()); - return ErrorCode::Success; -} - - - -ErrorCode -MemMetadataSet::SaveMetadata(const std::string& p_metaFile, const std::string& p_metaindexFile) -{ - std::ofstream metaOut(p_metaFile + "_tmp", std::ios::binary); - std::ofstream metaIndexOut(p_metaindexFile, std::ios::binary); - if (!metaOut.is_open() || !metaIndexOut.is_open()) return ErrorCode::FailedCreateFile; - - SaveMetadata(metaOut, metaIndexOut); - metaOut.close(); - metaIndexOut.close(); - - if (fileexists(p_metaFile.c_str())) std::remove(p_metaFile.c_str()); - std::rename((p_metaFile + "_tmp").c_str(), p_metaFile.c_str()); - return ErrorCode::Success; -} - diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Core/VectorIndex.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Core/VectorIndex.cpp deleted file mode 100644 index 9c7ccf5492..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Core/VectorIndex.cpp +++ /dev/null @@ -1,428 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Core/VectorIndex.h" -#include "inc/Core/Common/DataUtils.h" -#include "inc/Helper/CommonHelper.h" -#include "inc/Helper/StringConvert.h" -#include "inc/Helper/SimpleIniReader.h" -#include "inc/Helper/BufferStream.h" - -#include "inc/Core/BKT/Index.h" -#include "inc/Core/KDT/Index.h" -#include - - -using namespace SPTAG; - - -VectorIndex::VectorIndex() -{ -} - - -VectorIndex::~VectorIndex() -{ -} - - -std::string -VectorIndex::GetParameter(const std::string& p_param) const -{ - return GetParameter(p_param.c_str()); -} - - -ErrorCode -VectorIndex::SetParameter(const std::string& p_param, const std::string& p_value) -{ - return SetParameter(p_param.c_str(), p_value.c_str()); -} - - -void -VectorIndex::SetMetadata(const std::string& p_metadataFilePath, const std::string& p_metadataIndexPath) { - m_pMetadata.reset(new FileMetadataSet(p_metadataFilePath, p_metadataIndexPath)); -} - - -ByteArray -VectorIndex::GetMetadata(SizeType p_vectorID) const { - if (nullptr != m_pMetadata) - { - return m_pMetadata->GetMetadata(p_vectorID); - } - return ByteArray::c_empty; -} - - -std::shared_ptr> VectorIndex::CalculateBufferSize() const -{ - std::shared_ptr> ret = BufferSize(); - if (m_pMetadata != nullptr) - { - auto metasize = m_pMetadata->BufferSize(); - ret->push_back(metasize.first); - ret->push_back(metasize.second); - } - return std::move(ret); -} - - -ErrorCode -VectorIndex::LoadIndexConfig(Helper::IniReader& p_reader) -{ - std::string metadataSection("MetaData"); - if (p_reader.DoesSectionExist(metadataSection)) - { - m_sMetadataFile = p_reader.GetParameter(metadataSection, "MetaDataFilePath", std::string()); - m_sMetadataIndexFile = p_reader.GetParameter(metadataSection, "MetaDataIndexPath", std::string()); - } - - if (DistCalcMethod::Undefined == p_reader.GetParameter("Index", "DistCalcMethod", DistCalcMethod::Undefined)) - { - std::cerr << "Error: Failed to load parameter DistCalcMethod." << std::endl; - return ErrorCode::Fail; - } - return LoadConfig(p_reader); -} - - -ErrorCode -VectorIndex::SaveIndexConfig(std::ostream& p_configOut) -{ - if (nullptr != m_pMetadata) - { - p_configOut << "[MetaData]" << std::endl; - p_configOut << "MetaDataFilePath=" << m_sMetadataFile << std::endl; - p_configOut << "MetaDataIndexPath=" << m_sMetadataIndexFile << std::endl; - if (nullptr != m_pMetaToVec) p_configOut << "MetaDataToVectorIndex=true" << std::endl; - p_configOut << std::endl; - } - - p_configOut << "[Index]" << std::endl; - p_configOut << "IndexAlgoType=" << Helper::Convert::ConvertToString(GetIndexAlgoType()) << std::endl; - p_configOut << "ValueType=" << Helper::Convert::ConvertToString(GetVectorValueType()) << std::endl; - p_configOut << std::endl; - - return SaveConfig(p_configOut); -} - - -void -VectorIndex::BuildMetaMapping() -{ - m_pMetaToVec.reset(new std::unordered_map); - for (SizeType i = 0; i < m_pMetadata->Count(); i++) { - ByteArray meta = m_pMetadata->GetMetadata(i); - m_pMetaToVec->emplace(std::string((char*)meta.Data(), meta.Length()), i); - } -} - - -ErrorCode -VectorIndex::LoadIndex(const std::string& p_config, const std::vector& p_indexBlobs) -{ - SPTAG::Helper::IniReader p_reader; - std::istringstream p_configin(p_config); - if (SPTAG::ErrorCode::Success != p_reader.LoadIni(p_configin)) return ErrorCode::FailedParseValue; - LoadIndexConfig(p_reader); - - if (p_reader.DoesSectionExist("MetaData") && p_indexBlobs.size() > 4) - { - ByteArray pMetaIndex = p_indexBlobs[p_indexBlobs.size() - 1]; - m_pMetadata.reset(new MemMetadataSet(p_indexBlobs[p_indexBlobs.size() - 2], - ByteArray(pMetaIndex.Data() + sizeof(SizeType), pMetaIndex.Length() - sizeof(SizeType), false), - *((SizeType*)pMetaIndex.Data()))); - - if (!m_pMetadata->Available()) - { - std::cerr << "Error: Failed to load metadata." << std::endl; - return ErrorCode::Fail; - } - - if (p_reader.GetParameter("MetaData", "MetaDataToVectorIndex", std::string()) == "true") - { - BuildMetaMapping(); - } - } - return LoadIndexDataFromMemory(p_indexBlobs); -} - - -ErrorCode -VectorIndex::LoadIndex(const std::string& p_folderPath) -{ - std::string folderPath(p_folderPath); - if (!folderPath.empty() && *(folderPath.rbegin()) != FolderSep) - { - folderPath += FolderSep; - } - - Helper::IniReader p_configReader; - if (ErrorCode::Success != p_configReader.LoadIniFile(folderPath + "/indexloader.ini")) return ErrorCode::FailedOpenFile; - LoadIndexConfig(p_configReader); - - if (p_configReader.DoesSectionExist("MetaData")) - { - m_pMetadata.reset(new FileMetadataSet(folderPath + m_sMetadataFile, folderPath + m_sMetadataIndexFile)); - - if (!m_pMetadata->Available()) - { - std::cerr << "Error: Failed to load metadata." << std::endl; - return ErrorCode::Fail; - } - - if (p_configReader.GetParameter("MetaData", "MetaDataToVectorIndex", std::string()) == "true") - { - BuildMetaMapping(); - } - } - return LoadIndexData(folderPath); -} - - -ErrorCode -VectorIndex::SaveIndex(std::string& p_config, const std::vector& p_indexBlobs) -{ - std::ostringstream p_configStream; - SaveIndexConfig(p_configStream); - p_config = p_configStream.str(); - - std::vector p_indexStreams; - for (size_t i = 0; i < p_indexBlobs.size(); i++) - { - p_indexStreams.push_back(new Helper::obufferstream(new Helper::streambuf((char*)p_indexBlobs[i].Data(), p_indexBlobs[i].Length()), true)); - } - - ErrorCode ret = ErrorCode::Success; - if (NeedRefine()) - { - ret = RefineIndex(p_indexStreams); - } - else - { - if (m_pMetadata != nullptr && p_indexStreams.size() > 5) - { - ret = m_pMetadata->SaveMetadata(*p_indexStreams[p_indexStreams.size() - 2], *p_indexStreams[p_indexStreams.size() - 1]); - } - if (ErrorCode::Success == ret) ret = SaveIndexData(p_indexStreams); - } - for (size_t i = 0; i < p_indexStreams.size(); i++) - { - delete p_indexStreams[i]; - } - return ret; -} - - -ErrorCode -VectorIndex::SaveIndex(const std::string& p_folderPath) -{ - std::string folderPath(p_folderPath); - if (!folderPath.empty() && *(folderPath.rbegin()) != FolderSep) - { - folderPath += FolderSep; - } - - if (!direxists(folderPath.c_str())) - { - mkdir(folderPath.c_str()); - } - - std::ofstream configFile(folderPath + "indexloader.ini"); - if (!configFile.is_open()) return ErrorCode::FailedCreateFile; - SaveIndexConfig(configFile); - configFile.close(); - - if (NeedRefine()) return RefineIndex(p_folderPath); - - if (m_pMetadata != nullptr) - { - ErrorCode ret = m_pMetadata->SaveMetadata(folderPath + m_sMetadataFile, folderPath + m_sMetadataIndexFile); - if (ErrorCode::Success != ret) return ret; - } - return SaveIndexData(folderPath); -} - -ErrorCode -VectorIndex::BuildIndex(std::shared_ptr p_vectorSet, - std::shared_ptr p_metadataSet, bool p_withMetaIndex) -{ - if (nullptr == p_vectorSet || p_vectorSet->Count() == 0 || p_vectorSet->Dimension() == 0 || p_vectorSet->GetValueType() != GetVectorValueType()) - { - return ErrorCode::Fail; - } - - BuildIndex(p_vectorSet->GetData(), p_vectorSet->Count(), p_vectorSet->Dimension()); - m_pMetadata = std::move(p_metadataSet); - if (p_withMetaIndex && m_pMetadata != nullptr) - { - BuildMetaMapping(); - } - return ErrorCode::Success; -} - - -ErrorCode -VectorIndex::SearchIndex(const void* p_vector, int p_neighborCount, bool p_withMeta, BasicResult* p_results) const { - QueryResult res(p_vector, p_neighborCount, p_withMeta, p_results); - SearchIndex(res); - return ErrorCode::Success; -} - - -ErrorCode -VectorIndex::AddIndex(std::shared_ptr p_vectorSet, std::shared_ptr p_metadataSet) { - if (nullptr == p_vectorSet || p_vectorSet->Count() == 0 || p_vectorSet->Dimension() == 0 || p_vectorSet->GetValueType() != GetVectorValueType()) - { - return ErrorCode::Fail; - } - - SizeType currStart; - ErrorCode ret = AddIndex(p_vectorSet->GetData(), p_vectorSet->Count(), p_vectorSet->Dimension(), &currStart); - if (ret != ErrorCode::Success) return ret; - - if (m_pMetadata == nullptr) { - if (currStart == 0) - m_pMetadata = std::move(p_metadataSet); - else - return ErrorCode::Success; - } - else { - m_pMetadata->AddBatch(*p_metadataSet); - } - - if (m_pMetaToVec != nullptr) { - for (SizeType i = 0; i < p_vectorSet->Count(); i++) { - ByteArray meta = m_pMetadata->GetMetadata(currStart + i); - DeleteIndex(meta); - m_pMetaToVec->emplace(std::string((char*)meta.Data(), meta.Length()), currStart + i); - } - } - return ErrorCode::Success; -} - - -ErrorCode -VectorIndex::DeleteIndex(ByteArray p_meta) { - if (m_pMetaToVec == nullptr) return ErrorCode::Fail; - - std::string meta((char*)p_meta.Data(), p_meta.Length()); - auto iter = m_pMetaToVec->find(meta); - if (iter != m_pMetaToVec->end()) DeleteIndex(iter->second); - return ErrorCode::Success; -} - - -const void* VectorIndex::GetSample(ByteArray p_meta) -{ - if (m_pMetaToVec == nullptr) return nullptr; - - std::string meta((char*)p_meta.Data(), p_meta.Length()); - auto iter = m_pMetaToVec->find(meta); - if (iter != m_pMetaToVec->end()) return GetSample(iter->second); - return nullptr; -} - - -std::shared_ptr -VectorIndex::CreateInstance(IndexAlgoType p_algo, VectorValueType p_valuetype) -{ - if (IndexAlgoType::Undefined == p_algo || VectorValueType::Undefined == p_valuetype) - { - return nullptr; - } - - if (p_algo == IndexAlgoType::BKT) { - switch (p_valuetype) - { -#define DefineVectorValueType(Name, Type) \ - case VectorValueType::Name: \ - return std::shared_ptr(new BKT::Index); \ - -#include "inc/Core/DefinitionList.h" -#undef DefineVectorValueType - - default: break; - } - } - else if (p_algo == IndexAlgoType::KDT) { - switch (p_valuetype) - { -#define DefineVectorValueType(Name, Type) \ - case VectorValueType::Name: \ - return std::shared_ptr(new KDT::Index); \ - -#include "inc/Core/DefinitionList.h" -#undef DefineVectorValueType - - default: break; - } - } - return nullptr; -} - - -ErrorCode -VectorIndex::LoadIndex(const std::string& p_loaderFilePath, std::shared_ptr& p_vectorIndex) -{ - Helper::IniReader iniReader; - if (ErrorCode::Success != iniReader.LoadIniFile(p_loaderFilePath + "/indexloader.ini")) return ErrorCode::FailedOpenFile; - - IndexAlgoType algoType = iniReader.GetParameter("Index", "IndexAlgoType", IndexAlgoType::Undefined); - VectorValueType valueType = iniReader.GetParameter("Index", "ValueType", VectorValueType::Undefined); - - p_vectorIndex = CreateInstance(algoType, valueType); - if (p_vectorIndex == nullptr) return ErrorCode::FailedParseValue; - - return p_vectorIndex->LoadIndex(p_loaderFilePath); -} - - - -ErrorCode -VectorIndex::LoadIndex(const std::string& p_config, const std::vector& p_indexBlobs, std::shared_ptr& p_vectorIndex) -{ - SPTAG::Helper::IniReader iniReader; - std::istringstream p_configin(p_config); - if (SPTAG::ErrorCode::Success != iniReader.LoadIni(p_configin)) return ErrorCode::FailedParseValue; - - IndexAlgoType algoType = iniReader.GetParameter("Index", "IndexAlgoType", IndexAlgoType::Undefined); - VectorValueType valueType = iniReader.GetParameter("Index", "ValueType", VectorValueType::Undefined); - - p_vectorIndex = CreateInstance(algoType, valueType); - if (p_vectorIndex == nullptr) return ErrorCode::FailedParseValue; - - return p_vectorIndex->LoadIndex(p_config, p_indexBlobs); -} - - -ErrorCode -VectorIndex::MergeIndex(const char* p_indexFilePath1, const char* p_indexFilePath2) -{ - std::string folderPath1(p_indexFilePath1), folderPath2(p_indexFilePath2); - - std::shared_ptr index1, index2; - LoadIndex(folderPath1, index1); - LoadIndex(folderPath2, index2); - - std::shared_ptr p_vectorSet; - std::shared_ptr p_metaSet; - size_t vectorSize = GetValueTypeSize(index2->GetVectorValueType()) * index2->GetFeatureDim(); - std::uint64_t offsets[2] = { 0 }; - ByteArray metaoffset((std::uint8_t*)offsets, 2 * sizeof(std::uint64_t), false); - for (SizeType i = 0; i < index2->GetNumSamples(); i++) - if (index2->ContainSample(i)) - { - p_vectorSet.reset(new BasicVectorSet(ByteArray((std::uint8_t*)index2->GetSample(i), vectorSize, false), - index2->GetVectorValueType(), index2->GetFeatureDim(), 1)); - ByteArray meta = index2->GetMetadata(i); - offsets[1] = meta.Length(); - p_metaSet.reset(new MemMetadataSet(meta, metaoffset, 1)); - index1->AddIndex(p_vectorSet, p_metaSet); - } - - index1->SaveIndex(folderPath1); - return ErrorCode::Success; -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Core/VectorSet.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Core/VectorSet.cpp deleted file mode 100644 index 45dd74dd78..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Core/VectorSet.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Core/VectorSet.h" - -using namespace SPTAG; - -#pragma warning(disable:4996) // 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. - -VectorSet::VectorSet() -{ -} - - -VectorSet::~VectorSet() -{ -} - - -BasicVectorSet::BasicVectorSet(const ByteArray& p_bytesArray, - VectorValueType p_valueType, - DimensionType p_dimension, - SizeType p_vectorCount) - : m_data(p_bytesArray), - m_valueType(p_valueType), - m_dimension(p_dimension), - m_vectorCount(p_vectorCount), - m_perVectorDataSize(static_cast(p_dimension * GetValueTypeSize(p_valueType))) -{ -} - - -BasicVectorSet::~BasicVectorSet() -{ -} - - -VectorValueType -BasicVectorSet::GetValueType() const -{ - return m_valueType; -} - - -void* -BasicVectorSet::GetVector(SizeType p_vectorID) const -{ - if (p_vectorID < 0 || p_vectorID >= m_vectorCount) - { - return nullptr; - } - - return reinterpret_cast(m_data.Data() + ((size_t)p_vectorID) * m_perVectorDataSize); -} - - -void* -BasicVectorSet::GetData() const -{ - return reinterpret_cast(m_data.Data()); -} - -DimensionType -BasicVectorSet::Dimension() const -{ - return m_dimension; -} - - -SizeType -BasicVectorSet::Count() const -{ - return m_vectorCount; -} - - -bool -BasicVectorSet::Available() const -{ - return m_data.Data() != nullptr; -} - - -ErrorCode -BasicVectorSet::Save(const std::string& p_vectorFile) const -{ - FILE * fp = fopen(p_vectorFile.c_str(), "wb"); - if (fp == NULL) return ErrorCode::FailedOpenFile; - - fwrite(&m_vectorCount, sizeof(SizeType), 1, fp); - fwrite(&m_dimension, sizeof(DimensionType), 1, fp); - - fwrite((const void*)(m_data.Data()), m_data.Length(), 1, fp); - fclose(fp); - return ErrorCode::Success; -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/ArgumentsParser.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/ArgumentsParser.cpp deleted file mode 100644 index 4f630ec01c..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/ArgumentsParser.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Helper/ArgumentsParser.h" - -using namespace SPTAG::Helper; - - -ArgumentsParser::IArgument::IArgument() -{ -} - - -ArgumentsParser::IArgument::~IArgument() -{ -} - - -ArgumentsParser::ArgumentsParser() -{ -} - - -ArgumentsParser::~ArgumentsParser() -{ -} - - -bool -ArgumentsParser::Parse(int p_argc, char** p_args) -{ - while (p_argc > 0) - { - int last = p_argc; - for (auto& option : m_arguments) - { - if (!option->ParseValue(p_argc, p_args)) - { - fprintf(stderr, "Failed to parse args around \"%s\"\n", *p_args); - PrintHelp(); - return false; - } - } - - if (last == p_argc) - { - p_argc -= 1; - p_args += 1; - } - } - - bool isValid = true; - for (auto& option : m_arguments) - { - if (option->IsRequiredButNotSet()) - { - fprintf(stderr, "Required option not set:\n "); - option->PrintDescription(stderr); - fprintf(stderr, "\n"); - isValid = false; - } - } - - if (!isValid) - { - fprintf(stderr, "\n"); - PrintHelp(); - return false; - } - - return true; -} - - -void -ArgumentsParser::PrintHelp() -{ - fprintf(stderr, "Usage: "); - for (auto& option : m_arguments) - { - fprintf(stderr, "\n "); - option->PrintDescription(stderr); - } - - fprintf(stderr, "\n\n"); -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/Base64Encode.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/Base64Encode.cpp deleted file mode 100644 index 5992fa5a31..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/Base64Encode.cpp +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Helper/Base64Encode.h" - -using namespace SPTAG; -using namespace SPTAG::Helper; - -namespace -{ -namespace Local -{ -const char c_encTable[] = -{ - 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', - 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', - '0','1','2','3','4','5','6','7','8','9','+','/' -}; - - -const std::uint8_t c_decTable[] = -{ - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 0x00 - 0x0f - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 0x10 - 0x1f - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, // 0x20 - 0x2f - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, // 0x30 - 0x3f - 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 0x40 - 0x4f - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, // 0x50 - 0x5f - 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 0x60 - 0x6f - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, // 0x70 - 0x7f - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 0x80 - 0x8f - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 0x90 - 0x9f - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 0xa0 - 0xaf - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 0xb0 - 0xbf - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 0xc0 - 0xcf - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 0xd0 - 0xdf - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 0xe0 - 0xef - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 0xf0 - 0xff -}; - - -const char c_paddingChar = '='; -} -} - - -bool -Base64::Encode(const std::uint8_t* p_in, std::size_t p_inLen, char* p_out, std::size_t& p_outLen) -{ - using namespace Local; - - p_outLen = 0; - while (p_inLen >= 3) - { - p_out[0] = c_encTable[p_in[0] >> 2]; - p_out[1] = c_encTable[((p_in[0] & 0x03) << 4) | ((p_in[1] & 0xf0) >> 4)]; - p_out[2] = c_encTable[((p_in[1] & 0x0f) << 2) | ((p_in[2] & 0xc0) >> 6)]; - p_out[3] = c_encTable[p_in[2] & 0x3f]; - - p_in += 3; - p_inLen -= 3; - p_out += 4; - p_outLen += 4; - } - - switch (p_inLen) - { - case 1: - p_out[0] = c_encTable[p_in[0] >> 2]; - p_out[1] = c_encTable[(p_in[0] & 0x03) << 4]; - p_out[2] = c_paddingChar; - p_out[3] = c_paddingChar; - - p_outLen += 4; - break; - - case 2: - p_out[0] = c_encTable[p_in[0] >> 2]; - p_out[1] = c_encTable[((p_in[0] & 0x03) << 4) | ((p_in[1] & 0xf0) >> 4)]; - p_out[2] = c_encTable[(p_in[1] & 0x0f) << 2]; - p_out[3] = c_paddingChar; - - p_outLen += 4; - break; - } - - return true; -} - - -bool -Base64::Encode(const std::uint8_t* p_in, std::size_t p_inLen, std::ostream& p_out, std::size_t& p_outLen) -{ - using namespace Local; - - p_outLen = 0; - while (p_inLen >= 3) - { - p_out << c_encTable[p_in[0] >> 2]; - p_out << c_encTable[((p_in[0] & 0x03) << 4) | ((p_in[1] & 0xf0) >> 4)]; - p_out << c_encTable[((p_in[1] & 0x0f) << 2) | ((p_in[2] & 0xc0) >> 6)]; - p_out << c_encTable[p_in[2] & 0x3f]; - - p_in += 3; - p_inLen -= 3; - p_outLen += 4; - } - - switch (p_inLen) - { - case 1: - p_out << c_encTable[p_in[0] >> 2]; - p_out << c_encTable[(p_in[0] & 0x03) << 4]; - p_out << c_paddingChar; - p_out << c_paddingChar; - - p_outLen += 4; - break; - - case 2: - p_out << c_encTable[p_in[0] >> 2]; - p_out << c_encTable[((p_in[0] & 0x03) << 4) | ((p_in[1] & 0xf0) >> 4)]; - p_out << c_encTable[(p_in[1] & 0x0f) << 2]; - p_out << c_paddingChar; - - p_outLen += 4; - break; - - default: - break; - } - - return true; -} - - -bool -Base64::Decode(const char* p_in, std::size_t p_inLen, std::uint8_t* p_out, std::size_t& p_outLen) -{ - using namespace Local; - - // Should always be padding. - if ((p_inLen & 0x03) != 0) - { - return false; - } - - std::uint8_t u0 = 0; - std::uint8_t u1 = 0; - std::uint8_t u2 = 0; - std::uint8_t u3 = 0; - - p_outLen = 0; - while (p_inLen > 4) - { - u0 = c_decTable[static_cast(p_in[0])]; - u1 = c_decTable[static_cast(p_in[1])]; - u2 = c_decTable[static_cast(p_in[2])]; - u3 = c_decTable[static_cast(p_in[3])]; - - if (u0 > 63 || u1 > 63 || u2 > 63 || u3 > 63) - { - return false; - } - - p_out[0] = (u0 << 2) | (u1 >> 4); - p_out[1] = (u1 << 4) | (u2 >> 2); - p_out[2] = (u2 << 6) | u3; - - p_inLen -= 4; - p_in += 4; - p_out += 3; - p_outLen += 3; - } - - u0 = c_decTable[static_cast(p_in[0])]; - u1 = c_decTable[static_cast(p_in[1])]; - u2 = c_decTable[static_cast(p_in[2])]; - u3 = c_decTable[static_cast(p_in[3])]; - - if (u0 > 63 || u1 > 63 || (c_paddingChar == p_in[2] && c_paddingChar != p_in[3])) - { - return false; - } - - if (u2 > 63 && c_paddingChar != p_in[2]) - { - return false; - } - - if (u3 > 63 && c_paddingChar != p_in[3]) - { - return false; - } - - - p_out[0] = (u0 << 2) | (u1 >> 4); - ++p_outLen; - if (c_paddingChar == p_in[2]) - { - if ((u1 & 0x0F) != 0) - { - return false; - } - } - else - { - p_out[1] = (u1 << 4) | (u2 >> 2); - ++p_outLen; - if (c_paddingChar == p_in[3]) - { - if ((u3 & 0x03) != 0) - { - return false; - } - } - else - { - p_out[2] = (u2 << 6) | u3; - ++p_outLen; - } - } - - return true; -} - - -std::size_t -Base64::CapacityForEncode(std::size_t p_inLen) -{ - return ((p_inLen + 2) / 3) * 4; -} - - -std::size_t -Base64::CapacityForDecode(std::size_t p_inLen) -{ - return (p_inLen / 4) * 3 + ((p_inLen % 4) * 2) / 3; -} - diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/CommonHelper.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/CommonHelper.cpp deleted file mode 100644 index 2d4dc0de5c..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/CommonHelper.cpp +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Helper/CommonHelper.h" - -#include -#include -#include -#include - -using namespace SPTAG; -using namespace SPTAG::Helper; - -void -StrUtils::ToLowerInPlace(std::string& p_str) -{ - for (char& ch : p_str) - { - if (std::isupper(ch)) - { - ch = ch | 0x20; - } - } -} - - -std::vector -StrUtils::SplitString(const std::string& p_str, const std::string& p_separator) -{ - std::vector ret; - - std::size_t begin = p_str.find_first_not_of(p_separator); - while (std::string::npos != begin) - { - std::size_t end = p_str.find_first_of(p_separator, begin); - if (std::string::npos == end) - { - ret.emplace_back(p_str.substr(begin, p_str.size() - begin)); - break; - } - else - { - ret.emplace_back(p_str.substr(begin, end - begin)); - } - - begin = p_str.find_first_not_of(p_separator, end); - } - - return ret; -} - - -std::pair -StrUtils::FindTrimmedSegment(const char* p_begin, - const char* p_end, - const std::function& p_isSkippedChar) -{ - while (p_begin < p_end) - { - if (!p_isSkippedChar(*p_begin)) - { - break; - } - - ++p_begin; - } - - while (p_end > p_begin) - { - if (!p_isSkippedChar(*(p_end - 1))) - { - break; - } - - --p_end; - } - - return std::make_pair(p_begin, p_end); -} - - -bool -StrUtils::StartsWith(const char* p_str, const char* p_prefix) -{ - if (nullptr == p_prefix) - { - return true; - } - - if (nullptr == p_str) - { - return false; - } - - while ('\0' != (*p_prefix) && '\0' != (*p_str)) - { - if (*p_prefix != *p_str) - { - return false; - } - ++p_prefix; - ++p_str; - } - - return '\0' == *p_prefix; -} - - -bool -StrUtils::StrEqualIgnoreCase(const char* p_left, const char* p_right) -{ - if (p_left == p_right) - { - return true; - } - - if (p_left == nullptr || p_right == nullptr) - { - return false; - } - - auto tryConv = [](char p_ch) -> char - { - if ('a' <= p_ch && p_ch <= 'z') - { - return p_ch - 32; - } - - return p_ch; - }; - - while (*p_left != '\0' && *p_right != '\0') - { - if (tryConv(*p_left) != tryConv(*p_right)) - { - return false; - } - - ++p_left; - ++p_right; - } - - return *p_left == *p_right; -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/Concurrent.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/Concurrent.cpp deleted file mode 100644 index cbb1bdb643..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/Concurrent.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Helper/Concurrent.h" - -using namespace SPTAG; -using namespace SPTAG::Helper::Concurrent; - -WaitSignal::WaitSignal() - : m_isWaiting(false), - m_unfinished(0) -{ -} - - -WaitSignal::WaitSignal(std::uint32_t p_unfinished) - : m_isWaiting(false), - m_unfinished(p_unfinished) -{ -} - - -WaitSignal::~WaitSignal() -{ - std::lock_guard guard(m_mutex); - if (m_isWaiting) - { - m_cv.notify_all(); - } -} - - -void -WaitSignal::Reset(std::uint32_t p_unfinished) -{ - std::lock_guard guard(m_mutex); - if (m_isWaiting) - { - m_cv.notify_all(); - } - - m_isWaiting = false; - m_unfinished = p_unfinished; -} - - -void -WaitSignal::Wait() -{ - std::unique_lock lock(m_mutex); - if (m_unfinished > 0) - { - m_isWaiting = true; - m_cv.wait(lock); - } -} - - -void -WaitSignal::FinishOne() -{ - if (1 == m_unfinished.fetch_sub(1)) - { - std::lock_guard guard(m_mutex); - if (m_isWaiting) - { - m_isWaiting = false; - m_cv.notify_all(); - } - } -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/SimpleIniReader.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/SimpleIniReader.cpp deleted file mode 100644 index 28610dbe19..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/SimpleIniReader.cpp +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Helper/SimpleIniReader.h" -#include "inc/Helper/CommonHelper.h" - -#include -#include -#include -#include - -using namespace SPTAG; -using namespace SPTAG::Helper; - -const IniReader::ParameterValueMap IniReader::c_emptyParameters; - - -IniReader::IniReader() -{ -} - - -IniReader::~IniReader() -{ -} - - -ErrorCode IniReader::LoadIni(std::istream& p_input) -{ - const std::size_t c_bufferSize = 1 << 16; - - std::unique_ptr line(new char[c_bufferSize]); - - std::string currSection; - std::shared_ptr currParamMap(new ParameterValueMap); - - if (m_parameters.count(currSection) == 0) - { - m_parameters.emplace(currSection, currParamMap); - } - - auto isSpace = [](char p_ch) -> bool - { - return std::isspace(p_ch) != 0; - }; - - while (!p_input.eof()) - { - if (!p_input.getline(line.get(), c_bufferSize)) - { - break; - } - - std::size_t len = 0; - while (len < c_bufferSize && line[len] != '\0') - { - ++len; - } - - auto nonSpaceSeg = StrUtils::FindTrimmedSegment(line.get(), line.get() + len, isSpace); - - if (nonSpaceSeg.second <= nonSpaceSeg.first) - { - // Blank line. - continue; - } - - if (';' == *nonSpaceSeg.first) - { - // Comments. - continue; - } - else if ('[' == *nonSpaceSeg.first) - { - // Parse Section - if (']' != *(nonSpaceSeg.second - 1)) - { - return ErrorCode::ReadIni_FailedParseSection; - } - - auto sectionSeg = StrUtils::FindTrimmedSegment(nonSpaceSeg.first + 1, nonSpaceSeg.second - 1, isSpace); - - if (sectionSeg.second <= sectionSeg.first) - { - // Empty section name. - return ErrorCode::ReadIni_FailedParseSection; - } - - currSection.assign(sectionSeg.first, sectionSeg.second); - StrUtils::ToLowerInPlace(currSection); - - if (m_parameters.count(currSection) == 0) - { - currParamMap.reset(new ParameterValueMap); - m_parameters.emplace(currSection, currParamMap); - } - else - { - return ErrorCode::ReadIni_DuplicatedSection; - } - } - else - { - // Parameter Value Pair. - const char* equalSignLoc = nonSpaceSeg.first; - while (equalSignLoc < nonSpaceSeg.second && '=' != *equalSignLoc) - { - ++equalSignLoc; - } - - if (equalSignLoc >= nonSpaceSeg.second) - { - return ErrorCode::ReadIni_FailedParseParam; - } - - auto paramSeg = StrUtils::FindTrimmedSegment(nonSpaceSeg.first, equalSignLoc, isSpace); - - if (paramSeg.second <= paramSeg.first) - { - // Empty parameter name. - return ErrorCode::ReadIni_FailedParseParam; - } - - std::string paramName(paramSeg.first, paramSeg.second); - StrUtils::ToLowerInPlace(paramName); - - if (currParamMap->count(paramName) == 0) - { - currParamMap->emplace(std::move(paramName), std::string(equalSignLoc + 1, nonSpaceSeg.second)); - } - else - { - return ErrorCode::ReadIni_DuplicatedParam; - } - } - } - return ErrorCode::Success; -} - - -ErrorCode -IniReader::LoadIniFile(const std::string& p_iniFilePath) -{ - std::ifstream input(p_iniFilePath); - if (!input.is_open()) return ErrorCode::FailedOpenFile; - ErrorCode ret = LoadIni(input); - input.close(); - return ret; -} - - -bool -IniReader::DoesSectionExist(const std::string& p_section) const -{ - std::string section(p_section); - StrUtils::ToLowerInPlace(section); - return m_parameters.count(section) != 0; -} - - -bool -IniReader::DoesParameterExist(const std::string& p_section, const std::string& p_param) const -{ - std::string name(p_section); - StrUtils::ToLowerInPlace(name); - auto iter = m_parameters.find(name); - if (iter == m_parameters.cend()) - { - return false; - } - - const auto& paramMap = iter->second; - if (paramMap == nullptr) - { - return false; - } - - name = p_param; - StrUtils::ToLowerInPlace(name); - return paramMap->count(name) != 0; -} - - -bool -IniReader::GetRawValue(const std::string& p_section, const std::string& p_param, std::string& p_value) const -{ - std::string name(p_section); - StrUtils::ToLowerInPlace(name); - auto sectionIter = m_parameters.find(name); - if (sectionIter == m_parameters.cend()) - { - return false; - } - - const auto& paramMap = sectionIter->second; - if (paramMap == nullptr) - { - return false; - } - - name = p_param; - StrUtils::ToLowerInPlace(name); - auto paramIter = paramMap->find(name); - if (paramIter == paramMap->cend()) - { - return false; - } - - p_value = paramIter->second; - return true; -} - - -const IniReader::ParameterValueMap& -IniReader::GetParameters(const std::string& p_section) const -{ - std::string name(p_section); - StrUtils::ToLowerInPlace(name); - auto sectionIter = m_parameters.find(name); - if (sectionIter == m_parameters.cend() || nullptr == sectionIter->second) - { - return c_emptyParameters; - } - - return *(sectionIter->second); -} - -void -IniReader::SetParameter(const std::string& p_section, const std::string& p_param, const std::string& p_val) -{ - std::string name(p_section); - StrUtils::ToLowerInPlace(name); - auto sectionIter = m_parameters.find(name); - if (sectionIter == m_parameters.cend() || sectionIter->second == nullptr) - { - m_parameters[name] = std::shared_ptr(new ParameterValueMap); - } - - std::string param(p_param); - StrUtils::ToLowerInPlace(param); - (*m_parameters[name])[param] = p_val; -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/VectorSetReader.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/VectorSetReader.cpp deleted file mode 100644 index 44371ae242..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/VectorSetReader.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Helper/VectorSetReader.h" -#include "inc/Helper/VectorSetReaders/DefaultReader.h" - - -using namespace SPTAG; -using namespace SPTAG::Helper; - - -ReaderOptions::ReaderOptions(VectorValueType p_valueType, DimensionType p_dimension, std::string p_vectorDelimiter, std::uint32_t p_threadNum) - : m_threadNum(p_threadNum), m_dimension(p_dimension), m_vectorDelimiter(p_vectorDelimiter), m_inputValueType(p_valueType) -{ - AddOptionalOption(m_threadNum, "-t", "--thread", "Thread Number."); - AddOptionalOption(m_vectorDelimiter, "", "--delimiter", "Vector delimiter."); - AddRequiredOption(m_dimension, "-d", "--dimension", "Dimension of vector."); - AddRequiredOption(m_inputValueType, "-v", "--vectortype", "Input vector data type. Default is float."); -} - - -ReaderOptions::~ReaderOptions() -{ -} - - -VectorSetReader::VectorSetReader(std::shared_ptr p_options) - : m_options(p_options) -{ -} - - -VectorSetReader:: ~VectorSetReader() -{ -} - - -std::shared_ptr -VectorSetReader::CreateInstance(std::shared_ptr p_options) -{ - return std::shared_ptr(new DefaultReader(std::move(p_options))); -} - - diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/VectorSetReaders/DefaultReader.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/VectorSetReaders/DefaultReader.cpp deleted file mode 100644 index 4d775f4a50..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Helper/VectorSetReaders/DefaultReader.cpp +++ /dev/null @@ -1,514 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Helper/VectorSetReaders/DefaultReader.h" -#include "inc/Helper/StringConvert.h" -#include "inc/Helper/CommonHelper.h" - -#include -#include -#include -#include - -using namespace SPTAG; -using namespace SPTAG::Helper; - -namespace -{ -namespace Local -{ - -class BinaryLineReader -{ -public: - BinaryLineReader(std::istream& p_inStream) - : m_inStream(p_inStream) - { - m_buffer.reset(new char[c_bufferSize]); - } - - - bool Eof() - { - return m_inStream.eof() && (m_curOffset == m_curTotal); - } - - - std::size_t GetLine(std::unique_ptr& p_buffer, std::size_t& p_bufferSize, std::size_t& p_length) - { - std::size_t consumedCount = 0; - p_length = 0; - while (true) - { - while (m_curOffset < m_curTotal) - { - if (p_bufferSize > p_length) - { - ++consumedCount; - if (!IsDelimiter(m_buffer[m_curOffset])) - { - p_buffer[p_length++] = m_buffer[m_curOffset++]; - } - else - { - ++m_curOffset; - p_buffer[p_length] = '\0'; - return consumedCount + MoveToNextValid(); - } - } - else - { - p_bufferSize *= 2; - std::unique_ptr newBuffer(new char[p_bufferSize]); - memcpy(newBuffer.get(), p_buffer.get(), p_length); - p_buffer.swap(newBuffer); - } - } - - if (m_inStream.eof()) - { - break; - } - - m_inStream.read(m_buffer.get(), c_bufferSize); - m_curTotal = m_inStream.gcount(); - m_curOffset = 0; - } - - if (p_bufferSize <= p_length) - { - p_bufferSize *= 2; - std::unique_ptr newBuffer(new char[p_bufferSize]); - memcpy(newBuffer.get(), p_buffer.get(), p_length); - p_buffer.swap(newBuffer); - } - - p_buffer[p_length] = '\0'; - return consumedCount; - } - - -private: - std::size_t MoveToNextValid() - { - std::size_t skipped = 0; - while (true) - { - while (m_curOffset < m_curTotal) - { - if (IsDelimiter(m_buffer[m_curOffset])) - { - ++skipped; - ++m_curOffset; - } - else - { - return skipped; - } - } - - if (m_inStream.eof()) - { - break; - } - - m_inStream.read(m_buffer.get(), c_bufferSize); - m_curTotal = m_inStream.gcount(); - m_curOffset = 0; - } - - return skipped; - } - - bool IsDelimiter(char p_ch) - { - return p_ch == '\r' || p_ch == '\n'; - } - - static const std::size_t c_bufferSize = 1 << 10; - - std::unique_ptr m_buffer; - - std::istream& m_inStream; - - std::size_t m_curOffset; - - std::size_t m_curTotal; -}; - -} // namespace Local -} // namespace - - -DefaultReader::DefaultReader(std::shared_ptr p_options) - : VectorSetReader(std::move(p_options)), - m_subTaskBlocksize(0) -{ - omp_set_num_threads(m_options->m_threadNum); - - std::string tempFolder("tempfolder"); - if (!direxists(tempFolder.c_str())) - { - mkdir(tempFolder.c_str()); - } - - tempFolder += FolderSep; - m_vectorOutput = tempFolder + "vectorset.bin"; - m_metadataConentOutput = tempFolder + "metadata.bin"; - m_metadataIndexOutput = tempFolder + "metadataindex.bin"; -} - - -DefaultReader::~DefaultReader() -{ - if (fileexists(m_vectorOutput.c_str())) - { - remove(m_vectorOutput.c_str()); - } - - if (fileexists(m_metadataIndexOutput.c_str())) - { - remove(m_metadataIndexOutput.c_str()); - } - - if (fileexists(m_metadataConentOutput.c_str())) - { - remove(m_metadataConentOutput.c_str()); - } -} - - -ErrorCode -DefaultReader::LoadFile(const std::string& p_filePaths) -{ - const auto& files = GetFileSizes(p_filePaths); - std::vector> subWorks; - subWorks.reserve(files.size() * m_options->m_threadNum); - - m_subTaskCount = 0; - for (const auto& fileInfo : files) - { - if (fileInfo.second == (std::numeric_limits::max)()) - { - std::stringstream msg; - msg << "File " << fileInfo.first << " not exists or can't access."; - std::cerr << msg.str() << std::endl; - exit(1); - } - - std::uint32_t fileTaskCount = 0; - std::size_t blockSize = m_subTaskBlocksize; - if (0 == blockSize) - { - fileTaskCount = m_options->m_threadNum; - blockSize = (fileInfo.second + fileTaskCount - 1) / fileTaskCount; - } - else - { - fileTaskCount = static_cast((fileInfo.second + blockSize - 1) / blockSize); - } - - for (std::uint32_t i = 0; i < fileTaskCount; ++i) - { - subWorks.emplace_back(std::bind(&DefaultReader::LoadFileInternal, - this, - fileInfo.first, - m_subTaskCount++, - i, - blockSize)); - } - } - - m_totalRecordCount = 0; - m_totalRecordVectorBytes = 0; - m_subTaskRecordCount.clear(); - m_subTaskRecordCount.resize(m_subTaskCount, 0); - - m_waitSignal.Reset(m_subTaskCount); - -#pragma omp parallel for schedule(dynamic) - for (int64_t i = 0; i < (int64_t)subWorks.size(); i++) - { - subWorks[i](); - } - - m_waitSignal.Wait(); - - MergeData(); - - return ErrorCode::Success; -} - - -std::shared_ptr -DefaultReader::GetVectorSet() const -{ - ByteArray vectorSet = ByteArray::Alloc(m_totalRecordVectorBytes); - char* vecBuf = reinterpret_cast(vectorSet.Data()); - - std::ifstream inputStream; - inputStream.open(m_vectorOutput, std::ifstream::binary); - inputStream.seekg(sizeof(SizeType) + sizeof(DimensionType), std::ifstream::beg); - inputStream.read(vecBuf, m_totalRecordVectorBytes); - inputStream.close(); - - return std::shared_ptr(new BasicVectorSet(vectorSet, - m_options->m_inputValueType, - m_options->m_dimension, - m_totalRecordCount)); -} - - -std::shared_ptr -DefaultReader::GetMetadataSet() const -{ - return std::shared_ptr(new FileMetadataSet(m_metadataConentOutput, m_metadataIndexOutput)); -} - - -void -DefaultReader::LoadFileInternal(const std::string& p_filePath, - std::uint32_t p_subTaskID, - std::uint32_t p_fileBlockID, - std::size_t p_fileBlockSize) -{ - std::size_t lineBufferSize = 1 << 16; - std::unique_ptr currentLine(new char[lineBufferSize]); - - std::ifstream inputStream; - std::ofstream outputStream; - std::ofstream metaStreamContent; - std::ofstream metaStreamIndex; - - SizeType recordCount = 0; - std::uint64_t metaOffset = 0; - std::size_t totalRead = 0; - std::streamoff startpos = p_fileBlockID * p_fileBlockSize; - - inputStream.open(p_filePath, std::ios_base::in | std::ios_base::binary); - if (inputStream.is_open() == false) - { - std::stringstream msg; - msg << "Unable to open file: " << p_filePath << std::endl; - const auto& msgStr = msg.str(); - std::cerr << msgStr; - throw MyException(msgStr); - exit(1); - } - - { - std::stringstream msg; - msg << "Begin Subtask: " << p_subTaskID << ", start offset position:" << startpos << std::endl; - std::cout << msg.str(); - } - - std::string subFileSuffix("_"); - subFileSuffix += std::to_string(p_subTaskID); - subFileSuffix += ".tmp"; - - outputStream.open(m_vectorOutput + subFileSuffix, std::ofstream::binary); - metaStreamContent.open(m_metadataConentOutput + subFileSuffix, std::ofstream::binary); - metaStreamIndex.open(m_metadataIndexOutput + subFileSuffix, std::ofstream::binary); - - inputStream.seekg(startpos, std::ifstream::beg); - - Local::BinaryLineReader lineReader(inputStream); - - std::size_t lineLength; - if (p_fileBlockID != 0) - { - totalRead += lineReader.GetLine(currentLine, lineBufferSize, lineLength); - } - - std::size_t vectorByteSize = GetValueTypeSize(m_options->m_inputValueType) * m_options->m_dimension; - std::unique_ptr vector; - vector.reset(new std::uint8_t[vectorByteSize]); - - while (!lineReader.Eof() && totalRead <= p_fileBlockSize) - { - totalRead += lineReader.GetLine(currentLine, lineBufferSize, lineLength); - if (0 == lineLength) - { - continue; - } - - std::size_t tabIndex = lineLength - 1; - while (tabIndex > 0 && currentLine[tabIndex] != '\t') - { - --tabIndex; - } - - if (0 == tabIndex && currentLine[tabIndex] != '\t') - { - std::stringstream msg; - msg << "Subtask: " << p_subTaskID << " cannot parsing line:" << currentLine.get() << std::endl; - std::cout << msg.str(); - exit(1); - } - - bool parseSuccess = false; - switch (m_options->m_inputValueType) - { -#define DefineVectorValueType(Name, Type) \ - case VectorValueType::Name: \ - parseSuccess = TranslateVector(currentLine.get() + tabIndex + 1, reinterpret_cast(vector.get())); \ - break; \ - -#include "inc/Core/DefinitionList.h" -#undef DefineVectorValueType - - default: - parseSuccess = false; - break; - } - - if (!parseSuccess) - { - std::stringstream msg; - msg << "Subtask: " << p_subTaskID << " cannot parsing vector:" << (currentLine.get() + tabIndex + 1) << std::endl; - std::cout << msg.str(); - exit(1); - } - - ++recordCount; - outputStream.write(reinterpret_cast(vector.get()), vectorByteSize); - metaStreamContent.write(currentLine.get(), tabIndex); - metaStreamIndex.write(reinterpret_cast(&metaOffset), sizeof(metaOffset)); - - metaOffset += tabIndex; - } - - metaStreamIndex.write(reinterpret_cast(&metaOffset), sizeof(metaOffset)); - - inputStream.close(); - outputStream.close(); - metaStreamContent.close(); - metaStreamIndex.close(); - - m_totalRecordCount += recordCount; - m_subTaskRecordCount[p_subTaskID] = recordCount; - m_totalRecordVectorBytes += recordCount * vectorByteSize; - - m_waitSignal.FinishOne(); -} - - -void -DefaultReader::MergeData() -{ - const std::size_t bufferSize = 1 << 30; - const std::size_t bufferSizeTrim64 = (bufferSize / sizeof(std::uint64_t)) * sizeof(std::uint64_t); - std::ifstream inputStream; - std::ofstream outputStream; - - std::unique_ptr bufferHolder(new char[bufferSize]); - char* buf = bufferHolder.get(); - - SizeType totalRecordCount = m_totalRecordCount; - - outputStream.open(m_vectorOutput, std::ofstream::binary); - - outputStream.write(reinterpret_cast(&totalRecordCount), sizeof(totalRecordCount)); - outputStream.write(reinterpret_cast(&(m_options->m_dimension)), sizeof(m_options->m_dimension)); - - for (std::uint32_t i = 0; i < m_subTaskCount; ++i) - { - std::string file = m_vectorOutput; - file += "_"; - file += std::to_string(i); - file += ".tmp"; - - inputStream.open(file, std::ifstream::binary); - outputStream << inputStream.rdbuf(); - - inputStream.close(); - remove(file.c_str()); - } - - outputStream.close(); - - outputStream.open(m_metadataConentOutput, std::ofstream::binary); - for (std::uint32_t i = 0; i < m_subTaskCount; ++i) - { - std::string file = m_metadataConentOutput; - file += "_"; - file += std::to_string(i); - file += ".tmp"; - - inputStream.open(file, std::ifstream::binary); - outputStream << inputStream.rdbuf(); - - inputStream.close(); - remove(file.c_str()); - } - - outputStream.close(); - - outputStream.open(m_metadataIndexOutput, std::ofstream::binary); - - outputStream.write(reinterpret_cast(&totalRecordCount), sizeof(totalRecordCount)); - - std::uint64_t totalOffset = 0; - for (std::uint32_t i = 0; i < m_subTaskCount; ++i) - { - std::string file = m_metadataIndexOutput; - file += "_"; - file += std::to_string(i); - file += ".tmp"; - - inputStream.open(file, std::ifstream::binary); - for (SizeType remains = m_subTaskRecordCount[i]; remains > 0;) - { - std::size_t readBytesCount = min(remains * sizeof(std::uint64_t), bufferSizeTrim64); - inputStream.read(buf, readBytesCount); - std::uint64_t* offset = reinterpret_cast(buf); - for (std::uint64_t i = 0; i < readBytesCount / sizeof(std::uint64_t); ++i) - { - offset[i] += totalOffset; - } - - outputStream.write(buf, readBytesCount); - remains -= static_cast(readBytesCount / sizeof(std::uint64_t)); - } - - inputStream.read(buf, sizeof(std::uint64_t)); - totalOffset += *(reinterpret_cast(buf)); - - inputStream.close(); - remove(file.c_str()); - } - - outputStream.write(reinterpret_cast(&totalOffset), sizeof(totalOffset)); - outputStream.close(); -} - - -std::vector -DefaultReader::GetFileSizes(const std::string& p_filePaths) -{ - const auto& files = Helper::StrUtils::SplitString(p_filePaths, ","); - std::vector res; - res.reserve(files.size()); - - for (const auto& filePath : files) - { - if (!fileexists(filePath.c_str())) - { - res.emplace_back(filePath, (std::numeric_limits::max)()); - continue; - } -#ifndef _MSC_VER - struct stat stat_buf; - stat(filePath.c_str(), &stat_buf); -#else - struct _stat64 stat_buf; - _stat64(filePath.c_str(), &stat_buf); -#endif - std::size_t fileSize = stat_buf.st_size; - res.emplace_back(filePath, static_cast(fileSize)); - } - - return res; -} - - diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/IndexBuilder/Options.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/IndexBuilder/Options.cpp deleted file mode 100644 index 6360b73c2a..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/IndexBuilder/Options.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/IndexBuilder/Options.h" -#include "inc/Helper/StringConvert.h" - -#include - -using namespace SPTAG; -using namespace SPTAG::IndexBuilder; - - -BuilderOptions::BuilderOptions() - : Helper::ReaderOptions(VectorValueType::Float, 0, "|", 32) -{ - AddRequiredOption(m_inputFiles, "-i", "--input", "Input raw data."); - AddRequiredOption(m_outputFolder, "-o", "--outputfolder", "Output folder."); - AddRequiredOption(m_indexAlgoType, "-a", "--algo", "Index Algorithm type."); - AddOptionalOption(m_builderConfigFile, "-c", "--config", "Config file for builder."); -} - - -BuilderOptions::~BuilderOptions() -{ -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/IndexBuilder/ThreadPool.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/IndexBuilder/ThreadPool.cpp deleted file mode 100644 index 0ecddc1279..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/IndexBuilder/ThreadPool.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/IndexBuilder/ThreadPool.h" - -#include - -#include -#include - -using namespace SPTAG::IndexBuilder; - -namespace Local -{ -std::unique_ptr g_threadPool; - -std::atomic_bool g_initialized(false); - -std::uint32_t g_threadNum = 1; -} - - -void -ThreadPool::Init(std::uint32_t p_threadNum) -{ - if (Local::g_initialized.exchange(true)) - { - return; - } - - Local::g_threadNum = std::max((std::uint32_t)1, p_threadNum); - - Local::g_threadPool.reset(new boost::asio::thread_pool(Local::g_threadNum)); -} - - -bool -ThreadPool::Queue(std::function p_workItem) -{ - if (nullptr == Local::g_threadPool) - { - return false; - } - - boost::asio::post(*Local::g_threadPool, std::move(p_workItem)); - return true; -} - - -std::uint32_t -ThreadPool::CurrentThreadNum() -{ - return Local::g_threadNum; -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/IndexBuilder/main.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/IndexBuilder/main.cpp deleted file mode 100644 index 040703c3ca..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/IndexBuilder/main.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/IndexBuilder/Options.h" -#include "inc/Helper/VectorSetReader.h" -#include "inc/Core/VectorIndex.h" -#include "inc/Core/Common.h" -#include "inc/Helper/SimpleIniReader.h" - -#include -#include - -using namespace SPTAG; - -int main(int argc, char* argv[]) -{ - std::shared_ptr options(new IndexBuilder::BuilderOptions); - if (!options->Parse(argc - 1, argv + 1)) - { - exit(1); - } - - auto indexBuilder = VectorIndex::CreateInstance(options->m_indexAlgoType, options->m_inputValueType); - - Helper::IniReader iniReader; - if (!options->m_builderConfigFile.empty()) - { - iniReader.LoadIniFile(options->m_builderConfigFile); - } - - for (int i = 1; i < argc; i++) - { - std::string param(argv[i]); - size_t idx = param.find("="); - if (idx == std::string::npos) continue; - - std::string paramName = param.substr(0, idx); - std::string paramVal = param.substr(idx + 1); - std::string sectionName; - idx = paramName.find("."); - if (idx != std::string::npos) { - sectionName = paramName.substr(0, idx); - paramName = paramName.substr(idx + 1); - } - iniReader.SetParameter(sectionName, paramName, paramVal); - std::cout << "Set [" << sectionName << "]" << paramName << " = " << paramVal << std::endl; - } - - if (!iniReader.DoesParameterExist("Index", "NumberOfThreads")) { - iniReader.SetParameter("Index", "NumberOfThreads", std::to_string(options->m_threadNum)); - } - for (const auto& iter : iniReader.GetParameters("Index")) - { - indexBuilder->SetParameter(iter.first.c_str(), iter.second.c_str()); - } - - ErrorCode code; - if (options->m_inputFiles.find("BIN:") == 0) { - std::vector files = SPTAG::Helper::StrUtils::SplitString(options->m_inputFiles.substr(4), ","); - std::ifstream inputStream(files[0], std::ifstream::binary); - if (!inputStream.is_open()) { - fprintf(stderr, "Failed to read input file.\n"); - exit(1); - } - SizeType row; - DimensionType col; - inputStream.read((char*)&row, sizeof(SizeType)); - inputStream.read((char*)&col, sizeof(DimensionType)); - std::uint64_t totalRecordVectorBytes = ((std::uint64_t)GetValueTypeSize(options->m_inputValueType)) * row * col; - ByteArray vectorSet = ByteArray::Alloc(totalRecordVectorBytes); - char* vecBuf = reinterpret_cast(vectorSet.Data()); - inputStream.read(vecBuf, totalRecordVectorBytes); - inputStream.close(); - std::shared_ptr p_vectorSet(new BasicVectorSet(vectorSet, options->m_inputValueType, col, row)); - - std::shared_ptr p_metaSet = nullptr; - if (files.size() >= 3) { - p_metaSet.reset(new FileMetadataSet(files[1], files[2])); - } - code = indexBuilder->BuildIndex(p_vectorSet, p_metaSet); - indexBuilder->SaveIndex(options->m_outputFolder); - } - else { - auto vectorReader = Helper::VectorSetReader::CreateInstance(options); - if (ErrorCode::Success != vectorReader->LoadFile(options->m_inputFiles)) - { - fprintf(stderr, "Failed to read input file.\n"); - exit(1); - } - code = indexBuilder->BuildIndex(vectorReader->GetVectorSet(), vectorReader->GetMetadataSet()); - indexBuilder->SaveIndex(options->m_outputFolder); - } - - if (ErrorCode::Success != code) - { - fprintf(stderr, "Failed to build index.\n"); - exit(1); - } - return 0; -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/IndexSearcher/main.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/IndexSearcher/main.cpp deleted file mode 100644 index 0a8c84c2e3..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/IndexSearcher/main.cpp +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Helper/SimpleIniReader.h" -#include "inc/Helper/CommonHelper.h" -#include "inc/Core/Common.h" -#include "inc/Core/MetadataSet.h" -#include "inc/Core/VectorIndex.h" -#include "inc/Core/SearchQuery.h" -#include "inc/Core/Common/WorkSpace.h" -#include "inc/Core/Common/DataUtils.h" -#include -#include - -using namespace SPTAG; - -template -float CalcRecall(std::vector &results, const std::vector> &truth, SizeType NumQuerys, int K, std::ofstream& log) -{ - float meanrecall = 0, minrecall = MaxDist, maxrecall = 0, stdrecall = 0; - std::vector thisrecall(NumQuerys, 0); - for (SizeType i = 0; i < NumQuerys; i++) - { - for (SizeType id : truth[i]) - { - for (int j = 0; j < K; j++) - { - if (results[i].GetResult(j)->VID == id) - { - thisrecall[i] += 1; - break; - } - } - } - thisrecall[i] /= K; - meanrecall += thisrecall[i]; - if (thisrecall[i] < minrecall) minrecall = thisrecall[i]; - if (thisrecall[i] > maxrecall) maxrecall = thisrecall[i]; - } - meanrecall /= NumQuerys; - for (SizeType i = 0; i < NumQuerys; i++) - { - stdrecall += (thisrecall[i] - meanrecall) * (thisrecall[i] - meanrecall); - } - stdrecall = std::sqrt(stdrecall / NumQuerys); - log << meanrecall << " " << stdrecall << " " << minrecall << " " << maxrecall << std::endl; - return meanrecall; -} - -void LoadTruth(std::ifstream& fp, std::vector>& truth, SizeType NumQuerys, int K) -{ - SizeType get; - std::string line; - for (SizeType i = 0; i < NumQuerys; ++i) - { - truth[i].clear(); - for (int j = 0; j < K; ++j) - { - fp >> get; - truth[i].insert(get); - } - std::getline(fp, line); - } -} - -template -int Process(Helper::IniReader& reader, VectorIndex& index) -{ - std::string queryFile = reader.GetParameter("Index", "QueryFile", std::string("querys.bin")); - std::string truthFile = reader.GetParameter("Index", "TruthFile", std::string("truth.txt")); - std::string outputFile = reader.GetParameter("Index", "ResultFile", std::string("")); - - SizeType numBatchQuerys = reader.GetParameter("Index", "NumBatchQuerys", (SizeType)10000); - SizeType numDebugQuerys = reader.GetParameter("Index", "NumDebugQuerys", (SizeType)-1); - int K = reader.GetParameter("Index", "K", 32); - - std::vector maxCheck = Helper::StrUtils::SplitString(reader.GetParameter("Index", "MaxCheck", std::string("2048")), "#"); - - std::ifstream inStream(queryFile); - std::ifstream ftruth(truthFile); - std::ofstream fp; - if (!inStream.is_open()) - { - std::cout << "ERROR: Cannot Load Query file " << queryFile << "!" << std::endl; - return -1; - } - if (outputFile != "") - { - fp.open(outputFile); - if (!fp.is_open()) - { - std::cout << "ERROR: Cannot open " << outputFile << " for write!" << std::endl; - } - } - - std::ofstream log(index.GetIndexName() + "_" + std::to_string(K) + ".txt"); - if (!log.is_open()) - { - std::cout << "ERROR: Cannot open logging file!" << std::endl; - return -1; - } - - SizeType numQuerys = (numDebugQuerys >= 0) ? numDebugQuerys : numBatchQuerys; - - std::vector> Query(numQuerys, std::vector(index.GetFeatureDim(), 0)); - std::vector> truth(numQuerys); - std::vector results(numQuerys, QueryResult(NULL, K, 0)); - - clock_t * latencies = new clock_t[numQuerys + 1]; - - int base = 1; - if (index.GetDistCalcMethod() == DistCalcMethod::Cosine) { - base = COMMON::Utils::GetBase(); - } - int basesquare = base * base; - - DimensionType dims = index.GetFeatureDim(); - std::vector QStrings; - while (!inStream.eof()) - { - QStrings.clear(); - COMMON::Utils::PrepareQuerys(inStream, QStrings, Query, numQuerys, dims, index.GetDistCalcMethod(), base); - if (numQuerys == 0) break; - - for (SizeType i = 0; i < numQuerys; i++) results[i].SetTarget(Query[i].data()); - if (ftruth.is_open()) LoadTruth(ftruth, truth, numQuerys, K); - - std::cout << " \t[avg] \t[99%] \t[95%] \t[recall] \t[mem]" << std::endl; - - SizeType subSize = (numQuerys - 1) / omp_get_num_threads() + 1; - for (std::string& mc : maxCheck) - { - index.SetParameter("MaxCheck", mc.c_str()); - for (SizeType i = 0; i < numQuerys; i++) results[i].Reset(); - -#pragma omp parallel for - for (int tid = 0; tid < omp_get_num_threads(); tid++) - { - SizeType start = tid * subSize; - SizeType end = min((tid + 1) * subSize, numQuerys); - for (SizeType i = start; i < end; i++) - { - latencies[i] = clock(); - index.SearchIndex(results[i]); - } - } - - latencies[numQuerys] = clock(); - - float timeMean = 0, timeMin = MaxDist, timeMax = 0, timeStd = 0; - for (SizeType i = 0; i < numQuerys; i++) - { - if (latencies[i + 1] >= latencies[i]) - latencies[i] = latencies[i + 1] - latencies[i]; - else - latencies[i] = latencies[numQuerys] - latencies[i]; - timeMean += latencies[i]; - if (latencies[i] > timeMax) timeMax = (float)latencies[i]; - if (latencies[i] < timeMin) timeMin = (float)latencies[i]; - } - timeMean /= numQuerys; - for (SizeType i = 0; i < numQuerys; i++) timeStd += ((float)latencies[i] - timeMean) * ((float)latencies[i] - timeMean); - timeStd = std::sqrt(timeStd / numQuerys); - log << timeMean << " " << timeStd << " " << timeMin << " " << timeMax << " "; - - std::sort(latencies, latencies + numQuerys, [](clock_t x, clock_t y) - { - return x < y; - }); - float l99 = float(latencies[SizeType(numQuerys * 0.99)]) / CLOCKS_PER_SEC; - float l95 = float(latencies[SizeType(numQuerys * 0.95)]) / CLOCKS_PER_SEC; - - float recall = 0; - if (ftruth.is_open()) - { - recall = CalcRecall(results, truth, numQuerys, K, log); - } - -#ifndef _MSC_VER - struct rusage rusage; - getrusage(RUSAGE_SELF, &rusage); - unsigned long long peakWSS = rusage.ru_maxrss * 1024 / 1000000000; -#else - PROCESS_MEMORY_COUNTERS pmc; - GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)); - unsigned long long peakWSS = pmc.PeakWorkingSetSize / 1000000000; -#endif - std::cout << mc << "\t" << std::fixed << std::setprecision(6) << (timeMean / CLOCKS_PER_SEC) << "\t" << std::setprecision(4) << l99 << "\t" << l95 << "\t" << recall << "\t\t" << peakWSS << "GB" << std::endl; - - } - - if (fp.is_open()) - { - fp << std::setprecision(3) << std::fixed; - for (SizeType i = 0; i < numQuerys; i++) - { - fp << QStrings[i] << ":"; - for (int j = 0; j < K; j++) - { - if (results[i].GetResult(j)->VID < 0) { - fp << results[i].GetResult(j)->Dist << "@" << results[i].GetResult(j)->VID << std::endl; - } - else { - ByteArray vm = index.GetMetadata(results[i].GetResult(j)->VID); - fp << (results[i].GetResult(j)->Dist / basesquare) << "@"; - fp.write((const char*)vm.Data(), vm.Length()); - } - fp << "|"; - } - fp << std::endl; - } - } - - if (numQuerys < numBatchQuerys || numDebugQuerys >= 0) break; - } - std::cout << "Output results finish!" << std::endl; - - inStream.close(); - fp.close(); - log.close(); - ftruth.close(); - delete[] latencies; - - QStrings.clear(); - results.clear(); - - return 0; -} - -int main(int argc, char** argv) -{ - if (argc < 2) - { - std::cerr << "IndexSearcher.exe folder" << std::endl; - return -1; - } - - std::shared_ptr vecIndex; - auto ret = SPTAG::VectorIndex::LoadIndex(argv[1], vecIndex); - if (SPTAG::ErrorCode::Success != ret || nullptr == vecIndex) - { - std::cerr << "Cannot open configure file!" << std::endl; - return -1; - } - - Helper::IniReader iniReader; - for (int i = 1; i < argc; i++) - { - std::string param(argv[i]); - size_t idx = param.find("="); - if (idx == std::string::npos) continue; - - std::string paramName = param.substr(0, idx); - std::string paramVal = param.substr(idx + 1); - std::string sectionName; - idx = paramName.find("."); - if (idx != std::string::npos) { - sectionName = paramName.substr(0, idx); - paramName = paramName.substr(idx + 1); - } - iniReader.SetParameter(sectionName, paramName, paramVal); - std::cout << "Set [" << sectionName << "]" << paramName << " = " << paramVal << std::endl; - } - - switch (vecIndex->GetVectorValueType()) - { -#define DefineVectorValueType(Name, Type) \ - case VectorValueType::Name: \ - Process(iniReader, *(vecIndex.get())); \ - break; \ - -#include "inc/Core/DefinitionList.h" -#undef DefineVectorValueType - - default: break; - } - return 0; -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Server/QueryParser.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Server/QueryParser.cpp deleted file mode 100644 index 0fb47e9390..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Server/QueryParser.cpp +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Server/QueryParser.h" - -#include -#include - -using namespace SPTAG; -using namespace SPTAG::Service; - - -const char* QueryParser::c_defaultVectorSeparator = "|"; - - -QueryParser::QueryParser() - : m_vectorBase64(nullptr), - m_vectorBase64Length(0) -{ -} - - -QueryParser::~QueryParser() -{ -} - - -ErrorCode -QueryParser::Parse(const std::string& p_query, const char* p_vectorSeparator) -{ - if (p_vectorSeparator == nullptr) - { - p_vectorSeparator = c_defaultVectorSeparator; - } - - m_vectorElements.clear(); - m_options.clear(); - - m_dataHolder = ByteArray::Alloc(p_query.size() + 1); - memcpy(m_dataHolder.Data(), p_query.c_str(), p_query.size() + 1); - - enum class State : uint8_t - { - OptionNameBegin, - OptionName, - OptionValueBegin, - OptionValue, - Vector, - VectorBase64, - None - }; - - State currState = State::None; - - char* optionName = nullptr; - char* vectorStrBegin = nullptr; - char* vectorStrEnd = nullptr; - SizeType estDimension = 0; - - char* iter = nullptr; - - for (iter = reinterpret_cast(m_dataHolder.Data()); *iter != '\0'; ++iter) - { - if (std::isspace(*iter)) - { - *iter = '\0'; - if (State::Vector == currState) - { - ++estDimension; - vectorStrEnd = iter; - } - else if (State::VectorBase64 == currState) - { - m_vectorBase64Length = iter - m_vectorBase64; - } - - currState = State::None; - continue; - } - - switch (currState) - { - case State::None: - if ('$' == *iter) - { - currState = State::OptionNameBegin; - } - else if ('#' == *iter) - { - currState = State::VectorBase64; - m_vectorBase64 = iter + 1; - } - else - { - currState = State::Vector; - vectorStrBegin = iter; - } - - break; - - case State::OptionNameBegin: - optionName = iter; - currState = State::OptionName; - break; - - case State::OptionName: - if (':' == *iter || '=' == *iter) - { - *iter = '\0'; - currState = State::OptionValueBegin; - } - else if (std::isupper(*iter)) - { - // Convert OptionName to lowercase. - *iter = (*iter) | 0x20; - } - - break; - - case State::OptionValueBegin: - currState = State::OptionValue; - m_options.emplace_back(optionName, iter); - break; - - case State::Vector: - if (std::strchr(p_vectorSeparator, *iter) != nullptr) - { - ++estDimension; - *iter = '\0'; - } - - break; - - default: - break; - } - } - - if (State::Vector == currState) - { - ++estDimension; - vectorStrEnd = iter; - } - else if (State::VectorBase64 == currState) - { - m_vectorBase64Length = iter - m_vectorBase64; - } - - if (vectorStrBegin == nullptr || 0 == estDimension) - { - return ErrorCode::Fail; - } - - m_vectorElements.reserve(estDimension); - while (vectorStrBegin < vectorStrEnd) - { - while (vectorStrBegin < vectorStrEnd && '\0' == *vectorStrBegin) - { - ++vectorStrBegin; - } - - if (vectorStrBegin >= vectorStrEnd) - { - break; - } - - m_vectorElements.push_back(vectorStrBegin); - - while (vectorStrBegin < vectorStrEnd && '\0' != *vectorStrBegin) - { - ++vectorStrBegin; - } - } - - if (m_vectorElements.empty()) - { - return ErrorCode::Fail; - } - - return ErrorCode::Success; -} - - -const std::vector& -QueryParser::GetVectorElements() const -{ - return m_vectorElements; -} - - -const std::vector& -QueryParser::GetOptions() const -{ - return m_options; -} - - -const char* -QueryParser::GetVectorBase64() const -{ - return m_vectorBase64; -} - - -SizeType -QueryParser::GetVectorBase64Length() const -{ - return m_vectorBase64Length; -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Server/SearchExecutionContext.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Server/SearchExecutionContext.cpp deleted file mode 100644 index 36ff082404..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Server/SearchExecutionContext.cpp +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Server/SearchExecutionContext.h" -#include "inc/Helper/StringConvert.h" -#include "inc/Helper/CommonHelper.h" -#include "inc/Helper/Base64Encode.h" - -using namespace SPTAG; -using namespace SPTAG::Service; - -namespace -{ -namespace Local -{ - -template -ErrorCode -ConvertVectorFromString(const std::vector& p_source, ByteArray& p_dest, SizeType& p_dimension) -{ - p_dimension = 0; - p_dest = ByteArray::Alloc(p_source.size() * sizeof(ValueType)); - ValueType* arr = reinterpret_cast(p_dest.Data()); - for (std::size_t i = 0; i < p_source.size(); ++i) - { - if (!Helper::Convert::ConvertStringTo(p_source[i], arr[i])) - { - p_dest.Clear(); - p_dimension = 0; - return ErrorCode::Fail; - } - - ++p_dimension; - } - - return ErrorCode::Success; -} - -} -} - - -SearchExecutionContext::SearchExecutionContext(const std::shared_ptr& p_serviceSettings) - : c_serviceSettings(p_serviceSettings), - m_vectorDimension(0), - m_inputValueType(VectorValueType::Undefined), - m_extractMetadata(false), - m_resultNum(p_serviceSettings->m_defaultMaxResultNumber) -{ -} - - -SearchExecutionContext::~SearchExecutionContext() -{ - m_results.clear(); -} - - -ErrorCode -SearchExecutionContext::ParseQuery(const std::string& p_query) -{ - return m_queryParser.Parse(p_query, c_serviceSettings->m_vectorSeparator.c_str()); -} - - -ErrorCode -SearchExecutionContext::ExtractOption() -{ - for (const auto& optionPair : m_queryParser.GetOptions()) - { - if (Helper::StrUtils::StrEqualIgnoreCase(optionPair.first, "indexname")) - { - const char* begin = optionPair.second; - const char* end = optionPair.second; - while (*end != '\0') - { - while (*end != '\0' && *end != ',') - { - ++end; - } - - if (end != begin) - { - m_indexNames.emplace_back(begin, end - begin); - } - - if (*end != '\0') - { - ++end; - begin = end; - } - } - } - else if (Helper::StrUtils::StrEqualIgnoreCase(optionPair.first, "datatype")) - { - Helper::Convert::ConvertStringTo(optionPair.second, m_inputValueType); - } - else if (Helper::StrUtils::StrEqualIgnoreCase(optionPair.first, "extractmetadata")) - { - Helper::Convert::ConvertStringTo(optionPair.second, m_extractMetadata); - } - else if (Helper::StrUtils::StrEqualIgnoreCase(optionPair.first, "resultnum")) - { - Helper::Convert::ConvertStringTo(optionPair.second, m_resultNum); - } - } - - return ErrorCode::Success; -} - - -ErrorCode -SearchExecutionContext::ExtractVector(VectorValueType p_targetType) -{ - if (!m_queryParser.GetVectorElements().empty()) - { - switch (p_targetType) - { -#define DefineVectorValueType(Name, Type) \ - case VectorValueType::Name: \ - return Local::ConvertVectorFromString( \ - m_queryParser.GetVectorElements(), m_vector, m_vectorDimension); \ - break; \ - -#include "inc/Core/DefinitionList.h" -#undef DefineVectorValueType - - default: - break; - } - } - else if (m_queryParser.GetVectorBase64() != nullptr - && m_queryParser.GetVectorBase64Length() != 0) - { - SizeType estLen = m_queryParser.GetVectorBase64Length(); - auto temp = ByteArray::Alloc(Helper::Base64::CapacityForDecode(estLen)); - std::size_t outLen = 0; - if (!Helper::Base64::Decode(m_queryParser.GetVectorBase64(), estLen, temp.Data(), outLen)) - { - return ErrorCode::Fail; - } - - if (outLen % GetValueTypeSize(p_targetType) != 0) - { - return ErrorCode::Fail; - } - - m_vectorDimension = outLen / GetValueTypeSize(p_targetType); - m_vector = ByteArray(temp.Data(), outLen, temp.DataHolder()); - - return ErrorCode::Success; - } - - return ErrorCode::Fail; -} - - -const std::vector& -SearchExecutionContext::GetSelectedIndexNames() const -{ - return m_indexNames; -} - - -void -SearchExecutionContext::AddResults(std::string p_indexName, QueryResult& p_results) -{ - m_results.emplace_back(); - m_results.back().m_indexName.swap(p_indexName); - m_results.back().m_results = p_results; -} - - -std::vector& -SearchExecutionContext::GetResults() -{ - return m_results; -} - - -const std::vector& -SearchExecutionContext::GetResults() const -{ - return m_results; -} - - -const ByteArray& -SearchExecutionContext::GetVector() const -{ - return m_vector; -} - - -const SizeType -SearchExecutionContext::GetVectorDimension() const -{ - return m_vectorDimension; -} - - -const std::vector& -SearchExecutionContext::GetOptions() const -{ - return m_queryParser.GetOptions(); -} - - -const SizeType -SearchExecutionContext::GetResultNum() const -{ - return m_resultNum; -} - - -const bool -SearchExecutionContext::GetExtractMetadata() const -{ - return m_extractMetadata; -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Server/SearchExecutor.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Server/SearchExecutor.cpp deleted file mode 100644 index 2bc3832d88..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Server/SearchExecutor.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Server/SearchExecutor.h" - -using namespace SPTAG; -using namespace SPTAG::Service; - - -SearchExecutor::SearchExecutor(std::string p_queryString, - std::shared_ptr p_serviceContext, - const CallBack& p_callback) - : m_callback(p_callback), - c_serviceContext(std::move(p_serviceContext)), - m_queryString(std::move(p_queryString)) -{ -} - - -SearchExecutor::~SearchExecutor() -{ -} - - -void -SearchExecutor::Execute() -{ - ExecuteInternal(); - if (bool(m_callback)) - { - m_callback(std::move(m_executionContext)); - } -} - - -void -SearchExecutor::ExecuteInternal() -{ - m_executionContext.reset(new SearchExecutionContext(c_serviceContext->GetServiceSettings())); - - m_executionContext->ParseQuery(m_queryString); - m_executionContext->ExtractOption(); - - SelectIndex(); - - if (m_selectedIndex.empty()) - { - return; - } - - const auto& firstIndex = m_selectedIndex.front(); - - if (ErrorCode::Success != m_executionContext->ExtractVector(firstIndex->GetVectorValueType())) - { - return; - } - - if (m_executionContext->GetVectorDimension() != firstIndex->GetFeatureDim()) - { - return; - } - - QueryResult query(m_executionContext->GetVector().Data(), - m_executionContext->GetResultNum(), - m_executionContext->GetExtractMetadata()); - - for (const auto& vectorIndex : m_selectedIndex) - { - if (vectorIndex->GetVectorValueType() != firstIndex->GetVectorValueType() - || vectorIndex->GetFeatureDim() != firstIndex->GetFeatureDim()) - { - continue; - } - - query.Reset(); - if (ErrorCode::Success == vectorIndex->SearchIndex(query)) - { - m_executionContext->AddResults(vectorIndex->GetIndexName(), query); - } - } -} - - -void -SearchExecutor::SelectIndex() -{ - const auto& indexNames = m_executionContext->GetSelectedIndexNames(); - const auto& indexMap = c_serviceContext->GetIndexMap(); - if (indexMap.empty()) - { - return; - } - - if (indexNames.empty()) - { - if (indexMap.size() == 1) - { - m_selectedIndex.push_back(indexMap.begin()->second); - } - } - else - { - for (const auto& indexName : indexNames) - { - auto iter = indexMap.find(indexName); - if (iter != indexMap.cend()) - { - m_selectedIndex.push_back(iter->second); - } - } - } -} - diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Server/SearchService.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Server/SearchService.cpp deleted file mode 100644 index 83096fbcde..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Server/SearchService.cpp +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Server/SearchService.h" -#include "inc/Server/SearchExecutor.h" -#include "inc/Socket/RemoteSearchQuery.h" -#include "inc/Helper/CommonHelper.h" -#include "inc/Helper/ArgumentsParser.h" - -#include - -using namespace SPTAG; -using namespace SPTAG::Service; - - -namespace -{ -namespace Local -{ - -class SerivceCmdOptions : public Helper::ArgumentsParser -{ -public: - SerivceCmdOptions() - : m_serveMode("interactive"), - m_configFile("AnnService.ini") - { - AddOptionalOption(m_serveMode, "-m", "--mode", "Service mode, interactive or socket."); - AddOptionalOption(m_configFile, "-c", "--config", "Service config file path."); - } - - virtual ~SerivceCmdOptions() - { - } - - std::string m_serveMode; - - std::string m_configFile; -}; - -} - -} // namespace - - -SearchService::SearchService() - : m_initialized(false), - m_shutdownSignals(m_ioContext), - m_serveMode(ServeMode::Interactive) -{ -} - - -SearchService::~SearchService() -{ -} - - -bool -SearchService::Initialize(int p_argNum, char* p_args[]) -{ - Local::SerivceCmdOptions cmdOptions; - if (!cmdOptions.Parse(p_argNum - 1, p_args + 1)) - { - return false; - } - - if (Helper::StrUtils::StrEqualIgnoreCase(cmdOptions.m_serveMode.c_str(), "interactive")) - { - m_serveMode = ServeMode::Interactive; - } - else if (Helper::StrUtils::StrEqualIgnoreCase(cmdOptions.m_serveMode.c_str(), "socket")) - { - m_serveMode = ServeMode::Socket; - } - else - { - fprintf(stderr, "Failed parse Serve Mode!\n"); - return false; - } - - m_serviceContext.reset(new ServiceContext(cmdOptions.m_configFile)); - - m_initialized = m_serviceContext->IsInitialized(); - - return m_initialized; -} - - -void -SearchService::Run() -{ - if (!m_initialized) - { - return; - } - - switch (m_serveMode) - { - case ServeMode::Interactive: - RunInteractiveMode(); - break; - - case ServeMode::Socket: - RunSocketMode(); - break; - - default: - break; - } -} - - -void -SearchService::RunSocketMode() -{ - auto threadNum = max((SizeType)1, m_serviceContext->GetServiceSettings()->m_threadNum); - m_threadPool.reset(new boost::asio::thread_pool(threadNum)); - - Socket::PacketHandlerMapPtr handlerMap(new Socket::PacketHandlerMap); - handlerMap->emplace(Socket::PacketType::SearchRequest, - [this](Socket::ConnectionID p_srcID, Socket::Packet p_packet) - { - boost::asio::post(*m_threadPool, std::bind(&SearchService::SearchHanlder, this, p_srcID, std::move(p_packet))); - }); - - m_socketServer.reset(new Socket::Server(m_serviceContext->GetServiceSettings()->m_listenAddr, - m_serviceContext->GetServiceSettings()->m_listenPort, - handlerMap, - m_serviceContext->GetServiceSettings()->m_socketThreadNum)); - - fprintf(stderr, - "Start to listen %s:%s ...\n", - m_serviceContext->GetServiceSettings()->m_listenAddr.c_str(), - m_serviceContext->GetServiceSettings()->m_listenPort.c_str()); - - m_shutdownSignals.add(SIGINT); - m_shutdownSignals.add(SIGTERM); -#ifdef SIGQUIT - m_shutdownSignals.add(SIGQUIT); -#endif - - m_shutdownSignals.async_wait([this](boost::system::error_code p_ec, int p_signal) - { - fprintf(stderr, "Received shutdown signals.\n"); - }); - - m_ioContext.run(); - fprintf(stderr, "Start shutdown procedure.\n"); - - m_socketServer.reset(); - m_threadPool->stop(); - m_threadPool->join(); -} - - -void -SearchService::RunInteractiveMode() -{ - const std::size_t bufferSize = 1 << 16; - std::unique_ptr inputBuffer(new char[bufferSize]); - while (true) - { - std::cout << "Query: "; - if (!fgets(inputBuffer.get(), bufferSize, stdin)) - { - break; - } - - auto callback = [](std::shared_ptr p_exeContext) - { - std::cout << "Result:" << std::endl; - if (nullptr == p_exeContext) - { - std::cout << "Not Executed." << std::endl; - return; - } - - const auto& results = p_exeContext->GetResults(); - for (const auto& result : results) - { - std::cout << "Index: " << result.m_indexName << std::endl; - int idx = 0; - for (const auto& res : result.m_results) - { - std::cout << "------------------" << std::endl; - std::cout << "DocIndex: " << res.VID << " Distance: " << res.Dist; - if (result.m_results.WithMeta()) - { - const auto& metadata = result.m_results.GetMetadata(idx); - std::cout << " MetaData: " << std::string((char*)metadata.Data(), metadata.Length()); - } - std::cout << std::endl; - ++idx; - } - } - }; - - SearchExecutor executor(inputBuffer.get(), m_serviceContext, callback); - executor.Execute(); - } -} - - -void -SearchService::SearchHanlder(Socket::ConnectionID p_localConnectionID, Socket::Packet p_packet) -{ - if (p_packet.Header().m_bodyLength == 0) - { - return; - } - - if (Socket::c_invalidConnectionID == p_packet.Header().m_connectionID) - { - p_packet.Header().m_connectionID = p_localConnectionID; - } - - Socket::RemoteQuery remoteQuery; - remoteQuery.Read(p_packet.Body()); - - auto callback = std::bind(&SearchService::SearchHanlderCallback, - this, - std::placeholders::_1, - std::move(p_packet)); - - SearchExecutor executor(std::move(remoteQuery.m_queryString), - m_serviceContext, - callback); - executor.Execute(); -} - - -void -SearchService::SearchHanlderCallback(std::shared_ptr p_exeContext, - Socket::Packet p_srcPacket) -{ - Socket::Packet ret; - ret.Header().m_packetType = Socket::PacketType::SearchResponse; - ret.Header().m_processStatus = Socket::PacketProcessStatus::Ok; - ret.Header().m_connectionID = p_srcPacket.Header().m_connectionID; - ret.Header().m_resourceID = p_srcPacket.Header().m_resourceID; - - if (nullptr == p_exeContext) - { - ret.Header().m_processStatus = Socket::PacketProcessStatus::Failed; - ret.AllocateBuffer(0); - ret.Header().WriteBuffer(ret.HeaderBuffer()); - } - else - { - Socket::RemoteSearchResult remoteResult; - remoteResult.m_status = Socket::RemoteSearchResult::ResultStatus::Success; - remoteResult.m_allIndexResults.swap(p_exeContext->GetResults()); - ret.AllocateBuffer(static_cast(remoteResult.EstimateBufferSize())); - auto bodyEnd = remoteResult.Write(ret.Body()); - - ret.Header().m_bodyLength = static_cast(bodyEnd - ret.Body()); - ret.Header().WriteBuffer(ret.HeaderBuffer()); - } - - m_socketServer->SendPacket(p_srcPacket.Header().m_connectionID, std::move(ret), nullptr); -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Server/ServiceContext.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Server/ServiceContext.cpp deleted file mode 100644 index 8d62b2c7af..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Server/ServiceContext.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Server/ServiceContext.h" -#include "inc/Helper/SimpleIniReader.h" -#include "inc/Helper/CommonHelper.h" -#include "inc/Helper/StringConvert.h" - -using namespace SPTAG; -using namespace SPTAG::Service; - - -ServiceContext::ServiceContext(const std::string& p_configFilePath) - : m_initialized(false) -{ - Helper::IniReader iniReader; - if (ErrorCode::Success != iniReader.LoadIniFile(p_configFilePath)) - { - return; - } - - m_settings.reset(new ServiceSettings); - - m_settings->m_listenAddr = iniReader.GetParameter("Service", "ListenAddr", std::string("0.0.0.0")); - m_settings->m_listenPort = iniReader.GetParameter("Service", "ListenPort", std::string("8000")); - m_settings->m_threadNum = iniReader.GetParameter("Service", "ThreadNumber", static_cast(8)); - m_settings->m_socketThreadNum = iniReader.GetParameter("Service", "SocketThreadNumber", static_cast(8)); - - m_settings->m_defaultMaxResultNumber = iniReader.GetParameter("QueryConfig", "DefaultMaxResultNumber", static_cast(10)); - m_settings->m_vectorSeparator = iniReader.GetParameter("QueryConfig", "DefaultSeparator", std::string("|")); - - const std::string emptyStr; - - std::string indexListStr = iniReader.GetParameter("Index", "List", emptyStr); - const auto& indexList = Helper::StrUtils::SplitString(indexListStr, ","); - - for (const auto& indexName : indexList) - { - std::string sectionName("Index_"); - sectionName += indexName.c_str(); - if (!iniReader.DoesParameterExist(sectionName, "IndexFolder")) - { - continue; - } - - std::string indexFolder = iniReader.GetParameter(sectionName, "IndexFolder", emptyStr); - - std::shared_ptr vectorIndex; - if (ErrorCode::Success == VectorIndex::LoadIndex(indexFolder, vectorIndex)) - { - vectorIndex->SetIndexName(indexName); - m_fullIndexList.emplace(indexName, vectorIndex); - } - else - { - fprintf(stderr, "Failed loading index: %s\n", indexName.c_str()); - } - } - - m_initialized = true; -} - - -ServiceContext::~ServiceContext() -{ - -} - - -const std::map>& -ServiceContext::GetIndexMap() const -{ - return m_fullIndexList; -} - - -const std::shared_ptr& -ServiceContext::GetServiceSettings() const -{ - return m_settings; -} - - -bool -ServiceContext::IsInitialized() const -{ - return m_initialized; -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Server/ServiceSettings.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Server/ServiceSettings.cpp deleted file mode 100644 index d51153195b..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Server/ServiceSettings.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Server/ServiceSettings.h" - -using namespace SPTAG; -using namespace SPTAG::Service; - - -ServiceSettings::ServiceSettings() - : m_defaultMaxResultNumber(10), - m_threadNum(12) -{ -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Server/main.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Server/main.cpp deleted file mode 100644 index 5aa5dc1e59..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Server/main.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Server/SearchService.h" - -SPTAG::Service::SearchService g_service; - -int main(int argc, char* argv[]) -{ - if (!g_service.Initialize(argc, argv)) - { - return 1; - } - - g_service.Run(); - - return 0; -} - diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Client.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Client.cpp deleted file mode 100644 index 9c4101e4f4..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Client.cpp +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Socket/Client.h" - -#include - -using namespace SPTAG::Socket; - - -Client::Client(const PacketHandlerMapPtr& p_handlerMap, - std::size_t p_threadNum, - std::uint32_t p_heartbeatIntervalSeconds) - : c_requestHandlerMap(p_handlerMap), - m_connectionManager(new ConnectionManager), - m_deadlineTimer(m_ioContext), - m_heartbeatIntervalSeconds(p_heartbeatIntervalSeconds), - m_stopped(false) -{ - KeepIoContext(); - m_threadPool.reserve(p_threadNum); - for (std::size_t i = 0; i < p_threadNum; ++i) - { - m_threadPool.emplace_back(std::move(std::thread([this]() { m_ioContext.run(); }))); - } -} - - -Client::~Client() -{ - m_stopped = true; - - m_deadlineTimer.cancel(); - m_connectionManager->StopAll(); - while (!m_ioContext.stopped()) - { - m_ioContext.stop(); - } - - for (auto& t : m_threadPool) - { - t.join(); - } -} - - -ConnectionID -Client::ConnectToServer(const std::string& p_address, - const std::string& p_port, - SPTAG::ErrorCode& p_ec) -{ - boost::asio::ip::tcp::resolver resolver(m_ioContext); - - boost::system::error_code errCode; - auto endPoints = resolver.resolve(p_address, p_port, errCode); - if (errCode || endPoints.empty()) - { - p_ec = ErrorCode::Socket_FailedResolveEndPoint; - return c_invalidConnectionID; - } - - boost::asio::ip::tcp::socket socket(m_ioContext); - for (const auto ep : endPoints) - { - errCode.clear(); - socket.connect(ep, errCode); - if (!errCode) - { - break; - } - - socket.close(errCode); - } - - if (socket.is_open()) - { - p_ec = ErrorCode::Success; - return m_connectionManager->AddConnection(std::move(socket), - c_requestHandlerMap, - m_heartbeatIntervalSeconds); - } - - p_ec = ErrorCode::Socket_FailedConnectToEndPoint; - return c_invalidConnectionID; -} - - -void -Client::AsyncConnectToServer(const std::string& p_address, - const std::string& p_port, - ConnectCallback p_callback) -{ - boost::asio::post(m_ioContext, - [this, p_address, p_port, p_callback]() - { - SPTAG::ErrorCode errCode; - auto connID = ConnectToServer(p_address, p_port, errCode); - if (bool(p_callback)) - { - p_callback(connID, errCode); - } - }); -} - - -void -Client::SendPacket(ConnectionID p_connection, Packet p_packet, std::function p_callback) -{ - auto connection = m_connectionManager->GetConnection(p_connection); - if (nullptr != connection) - { - connection->AsyncSend(std::move(p_packet), std::move(p_callback)); - } - else if (bool(p_callback)) - { - p_callback(false); - } -} - - -void -Client::SetEventOnConnectionClose(std::function p_event) -{ - m_connectionManager->SetEventOnRemoving(std::move(p_event)); -} - - -void -Client::KeepIoContext() -{ - if (m_stopped) - { - return; - } - - m_deadlineTimer.expires_from_now(boost::posix_time::hours(24)); - m_deadlineTimer.async_wait([this](boost::system::error_code p_ec) - { - this->KeepIoContext(); - }); -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Common.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Common.cpp deleted file mode 100644 index 2cfc1178ed..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Common.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Socket/Common.h" - - -using namespace SPTAG::Socket; - -const ConnectionID SPTAG::Socket::c_invalidConnectionID = 0; - -const ResourceID SPTAG::Socket::c_invalidResourceID = 0; diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Connection.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Connection.cpp deleted file mode 100644 index 6e536cbfcf..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Connection.cpp +++ /dev/null @@ -1,403 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Socket/Connection.h" -#include "inc/Socket/ConnectionManager.h" - -#include -#include -#include -#include -#include - -#include - -using namespace SPTAG::Socket; - -Connection::Connection(ConnectionID p_connectionID, - boost::asio::ip::tcp::socket&& p_socket, - const PacketHandlerMapPtr& p_handlerMap, - std::weak_ptr p_connectionManager) - : c_connectionID(p_connectionID), - c_handlerMap(p_handlerMap), - c_connectionManager(std::move(p_connectionManager)), - m_socket(std::move(p_socket)), - m_strand(p_socket.get_executor().context()), - m_heartbeatTimer(p_socket.get_executor().context()), - m_remoteConnectionID(c_invalidConnectionID), - m_stopped(true), - m_heartbeatStarted(false) -{ -} - - -void -Connection::Start() -{ -#ifdef _DEBUG - fprintf(stderr, "Connection Start, local: %u, remote: %s:%u\n", - static_cast(m_socket.local_endpoint().port()), - m_socket.remote_endpoint().address().to_string().c_str(), - static_cast(m_socket.remote_endpoint().port())); -#endif - - if (!m_stopped.exchange(false)) - { - return; - } - - SendRegister(); - AsyncReadHeader(); -} - - -void -Connection::Stop() -{ -#ifdef _DEBUG - fprintf(stderr, "Connection Stop, local: %u, remote: %s:%u\n", - static_cast(m_socket.local_endpoint().port()), - m_socket.remote_endpoint().address().to_string().c_str(), - static_cast(m_socket.remote_endpoint().port())); -#endif - - if (m_stopped.exchange(true)) - { - return; - } - - boost::system::error_code errCode; - if (m_heartbeatStarted.exchange(false)) - { - m_heartbeatTimer.cancel(errCode); - } - - m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, errCode); - m_socket.close(errCode); -} - - -void -Connection::StartHeartbeat(std::size_t p_intervalSeconds) -{ - if (m_stopped || m_heartbeatStarted.exchange(true)) - { - return; - } - - SendHeartbeat(p_intervalSeconds); -} - - -ConnectionID -Connection::GetConnectionID() const -{ - return c_connectionID; -} - - -ConnectionID -Connection::GetRemoteConnectionID() const -{ - return m_remoteConnectionID; -} - - -void -Connection::AsyncSend(Packet p_packet, std::function p_callback) -{ - if (m_stopped) - { - if (bool(p_callback)) - { - p_callback(false); - } - - return; - } - - auto sharedThis = shared_from_this(); - boost::asio::post(m_strand, - [sharedThis, p_packet, p_callback]() - { - auto handler = [p_callback, p_packet, sharedThis](boost::system::error_code p_ec, - std::size_t p_bytesTransferred) - { - if (p_ec && boost::asio::error::operation_aborted != p_ec) - { - sharedThis->OnConnectionFail(p_ec); - } - - if (bool(p_callback)) - { - p_callback(!p_ec); - } - }; - - boost::asio::async_write(sharedThis->m_socket, - boost::asio::buffer(p_packet.Buffer(), - p_packet.BufferLength()), - std::move(handler)); - }); -} - - -void -Connection::AsyncReadHeader() -{ - if (m_stopped) - { - return; - } - - auto sharedThis = shared_from_this(); - boost::asio::post(m_strand, - [sharedThis]() - { - auto handler = boost::bind(&Connection::HandleReadHeader, - sharedThis, - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred); - - boost::asio::async_read(sharedThis->m_socket, - boost::asio::buffer(sharedThis->m_packetHeaderReadBuffer), - std::move(handler)); - }); -} - - -void -Connection::AsyncReadBody() -{ - if (m_stopped) - { - return; - } - - auto sharedThis = shared_from_this(); - boost::asio::post(m_strand, - [sharedThis]() - { - auto handler = boost::bind(&Connection::HandleReadBody, - sharedThis, - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred); - - boost::asio::async_read(sharedThis->m_socket, - boost::asio::buffer(sharedThis->m_packetRead.Body(), - sharedThis->m_packetRead.Header().m_bodyLength), - std::move(handler)); - }); -} - - -void -Connection::HandleReadHeader(boost::system::error_code p_ec, std::size_t p_bytesTransferred) -{ - if (!p_ec) - { - m_packetRead.Header().ReadBuffer(m_packetHeaderReadBuffer); - if (m_packetRead.Header().m_bodyLength > 0) - { - m_packetRead.AllocateBuffer(m_packetRead.Header().m_bodyLength); - AsyncReadBody(); - } - else - { - HandleReadBody(p_ec, p_bytesTransferred); - } - - return; - } - else if (boost::asio::error::operation_aborted != p_ec) - { - OnConnectionFail(p_ec); - return; - } - - AsyncReadHeader(); -} - - -void -Connection::HandleReadBody(boost::system::error_code p_ec, std::size_t p_bytesTransferred) -{ - if (!p_ec) - { - bool foundHanlder = true; - switch (m_packetRead.Header().m_packetType) - { - case PacketType::HeartbeatRequest: - HandleHeartbeatRequest(); - break; - - case PacketType::HeartbeatResponse: - break; - - case PacketType::RegisterRequest: - HandleRegisterRequest(); - break; - - case PacketType::RegisterResponse: - HandleRegisterResponse(); - break; - - default: - foundHanlder = false; - break; - } - - if (nullptr != c_handlerMap) - { - auto iter = c_handlerMap->find(m_packetRead.Header().m_packetType); - if (c_handlerMap->cend() != iter && bool(iter->second)) - { - (iter->second)(c_connectionID, std::move(m_packetRead)); - foundHanlder = true; - } - } - - if (!foundHanlder) - { - HandleNoHandlerResponse(); - } - } - else if (boost::asio::error::operation_aborted != p_ec) - { - OnConnectionFail(p_ec); - return; - } - - AsyncReadHeader(); -} - - -void -Connection::SendHeartbeat(std::size_t p_intervalSeconds) -{ - if (m_stopped) - { - return; - } - - Packet msg; - msg.Header().m_packetType = PacketType::HeartbeatRequest; - msg.Header().m_processStatus = PacketProcessStatus::Ok; - msg.Header().m_connectionID = 0; - - msg.AllocateBuffer(0); - msg.Header().WriteBuffer(msg.HeaderBuffer()); - - AsyncSend(std::move(msg), nullptr); - - m_heartbeatTimer.expires_from_now(boost::posix_time::seconds(p_intervalSeconds)); - m_heartbeatTimer.async_wait(boost::bind(&Connection::SendHeartbeat, - shared_from_this(), - p_intervalSeconds)); -} - - -void -Connection::SendRegister() -{ - Packet msg; - msg.Header().m_packetType = PacketType::RegisterRequest; - msg.Header().m_processStatus = PacketProcessStatus::Ok; - msg.Header().m_connectionID = 0; - - msg.AllocateBuffer(0); - msg.Header().WriteBuffer(msg.HeaderBuffer()); - - AsyncSend(std::move(msg), nullptr); -} - - -void -Connection::HandleHeartbeatRequest() -{ - Packet msg; - msg.Header().m_packetType = PacketType::HeartbeatResponse; - msg.Header().m_processStatus = PacketProcessStatus::Ok; - - msg.AllocateBuffer(0); - - if (0 == m_packetRead.Header().m_connectionID - || c_connectionID == m_packetRead.Header().m_connectionID) - { - m_packetRead.Header().m_connectionID; - msg.Header().WriteBuffer(msg.HeaderBuffer()); - - AsyncSend(std::move(msg), nullptr); - } - else - { - msg.Header().m_connectionID = m_packetRead.Header().m_connectionID; - msg.Header().WriteBuffer(msg.HeaderBuffer()); - - auto mgr = c_connectionManager.lock(); - if (nullptr != mgr) - { - auto con = mgr->GetConnection(m_packetRead.Header().m_connectionID); - if (nullptr != con) - { - con->AsyncSend(std::move(msg), nullptr); - } - } - } -} - - -void -Connection::HandleRegisterRequest() -{ - Packet msg; - msg.Header().m_packetType = PacketType::RegisterResponse; - msg.Header().m_processStatus = PacketProcessStatus::Ok; - msg.Header().m_connectionID = c_connectionID; - msg.Header().m_resourceID = m_packetRead.Header().m_resourceID; - - msg.AllocateBuffer(0); - msg.Header().WriteBuffer(msg.HeaderBuffer()); - - AsyncSend(std::move(msg), nullptr); -} - - -void -Connection::HandleRegisterResponse() -{ - m_remoteConnectionID = m_packetRead.Header().m_connectionID; -} - - -void -Connection::HandleNoHandlerResponse() -{ - auto packetType = m_packetRead.Header().m_packetType; - if (!PacketTypeHelper::IsRequestPacket(packetType)) - { - return; - } - - Packet msg; - msg.Header().m_packetType = PacketTypeHelper::GetCrosspondingResponseType(packetType); - msg.Header().m_processStatus = PacketProcessStatus::Dropped; - msg.Header().m_connectionID = c_connectionID; - msg.Header().m_resourceID = m_packetRead.Header().m_resourceID; - - msg.AllocateBuffer(0); - msg.Header().WriteBuffer(msg.HeaderBuffer()); - - AsyncSend(std::move(msg), nullptr); -} - - -void -Connection::OnConnectionFail(const boost::system::error_code& p_ec) -{ - auto mgr = c_connectionManager.lock(); - if (nullptr != mgr) - { - mgr->RemoveConnection(c_connectionID); - } -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/ConnectionManager.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/ConnectionManager.cpp deleted file mode 100644 index 9d52dbc8b1..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/ConnectionManager.cpp +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Socket/ConnectionManager.h" - -using namespace SPTAG::Socket; - - -ConnectionManager::ConnectionItem::ConnectionItem() - : m_isEmpty(true) -{ -} - - -ConnectionManager::ConnectionManager() - : m_nextConnectionID(1), - m_connectionCount(0) -{ -} - - -ConnectionID -ConnectionManager::AddConnection(boost::asio::ip::tcp::socket&& p_socket, - const PacketHandlerMapPtr& p_handler, - std::uint32_t p_heartbeatIntervalSeconds) -{ - ConnectionID currID = m_nextConnectionID.fetch_add(1); - while (c_invalidConnectionID == currID || !m_connections[GetPosition(currID)].m_isEmpty.exchange(false)) - { - if (m_connectionCount >= c_connectionPoolSize) - { - return c_invalidConnectionID; - } - - currID = m_nextConnectionID.fetch_add(1); - } - - ++m_connectionCount; - - auto connection = std::make_shared(currID, - std::move(p_socket), - p_handler, - std::weak_ptr(shared_from_this())); - - { - Helper::Concurrent::LockGuard guard(m_spinLock); - m_connections[GetPosition(currID)].m_connection = connection; - } - - connection->Start(); - if (p_heartbeatIntervalSeconds > 0) - { - connection->StartHeartbeat(p_heartbeatIntervalSeconds); - } - - return currID; -} - - -void -ConnectionManager::RemoveConnection(ConnectionID p_connectionID) -{ - auto position = GetPosition(p_connectionID); - if (m_connections[position].m_isEmpty.exchange(true)) - { - return; - } - - Connection::Ptr conn; - - { - Helper::Concurrent::LockGuard guard(m_spinLock); - conn = std::move(m_connections[position].m_connection); - } - - --m_connectionCount; - - conn->Stop(); - conn.reset(); - - if (bool(m_eventOnRemoving)) - { - m_eventOnRemoving(p_connectionID); - } -} - - -Connection::Ptr -ConnectionManager::GetConnection(ConnectionID p_connectionID) -{ - auto position = GetPosition(p_connectionID); - Connection::Ptr ret; - - { - Helper::Concurrent::LockGuard guard(m_spinLock); - ret = m_connections[position].m_connection; - } - - if (nullptr == ret || ret->GetConnectionID() != p_connectionID) - { - return nullptr; - } - - return ret; -} - - -void -ConnectionManager::SetEventOnRemoving(std::function p_event) -{ - m_eventOnRemoving = std::move(p_event); -} - - -void -ConnectionManager::StopAll() -{ - Helper::Concurrent::LockGuard guard(m_spinLock); - for (auto& connection : m_connections) - { - if (nullptr != connection.m_connection) - { - connection.m_connection->Stop(); - } - } -} - - -std::uint32_t -ConnectionManager::GetPosition(ConnectionID p_connectionID) -{ - return static_cast(p_connectionID) & c_connectionPoolMask; -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Packet.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Packet.cpp deleted file mode 100644 index 335400bbd8..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Packet.cpp +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Socket/Packet.h" -#include "inc/Socket/SimpleSerialization.h" - -#include - -using namespace SPTAG::Socket; - -PacketHeader::PacketHeader() - : m_packetType(PacketType::Undefined), - m_processStatus(PacketProcessStatus::Ok), - m_bodyLength(0), - m_connectionID(c_invalidConnectionID), - m_resourceID(c_invalidResourceID) -{ -} - - -PacketHeader::PacketHeader(PacketHeader&& p_right) - : m_packetType(std::move(p_right.m_packetType)), - m_processStatus(std::move(p_right.m_processStatus)), - m_bodyLength(std::move(p_right.m_bodyLength)), - m_connectionID(std::move(p_right.m_connectionID)), - m_resourceID(std::move(p_right.m_resourceID)) -{ -} - - -PacketHeader::PacketHeader(const PacketHeader& p_right) - : m_packetType(p_right.m_packetType), - m_processStatus(p_right.m_processStatus), - m_bodyLength(p_right.m_bodyLength), - m_connectionID(p_right.m_connectionID), - m_resourceID(p_right.m_resourceID) -{ -} - - -std::size_t -PacketHeader::WriteBuffer(std::uint8_t* p_buffer) -{ - std::uint8_t* buff = p_buffer; - buff = SimpleSerialization::SimpleWriteBuffer(m_packetType, buff); - buff = SimpleSerialization::SimpleWriteBuffer(m_processStatus, buff); - buff = SimpleSerialization::SimpleWriteBuffer(m_bodyLength, buff); - buff = SimpleSerialization::SimpleWriteBuffer(m_connectionID, buff); - buff = SimpleSerialization::SimpleWriteBuffer(m_resourceID, buff); - - return p_buffer - buff; -} - - -void -PacketHeader::ReadBuffer(const std::uint8_t* p_buffer) -{ - const std::uint8_t* buff = p_buffer; - buff = SimpleSerialization::SimpleReadBuffer(buff, m_packetType); - buff = SimpleSerialization::SimpleReadBuffer(buff, m_processStatus); - buff = SimpleSerialization::SimpleReadBuffer(buff, m_bodyLength); - buff = SimpleSerialization::SimpleReadBuffer(buff, m_connectionID); - buff = SimpleSerialization::SimpleReadBuffer(buff, m_resourceID); -} - - -Packet::Packet() -{ -} - - -Packet::Packet(Packet&& p_right) - : m_header(std::move(p_right.m_header)), - m_buffer(std::move(p_right.m_buffer)), - m_bufferCapacity(std::move(p_right.m_bufferCapacity)) -{ -} - - -Packet::Packet(const Packet& p_right) - : m_header(p_right.m_header), - m_buffer(p_right.m_buffer), - m_bufferCapacity(p_right.m_bufferCapacity) -{ -} - - -PacketHeader& -Packet::Header() -{ - return m_header; -} - - -std::uint8_t* -Packet::HeaderBuffer() const -{ - return m_buffer.get(); -} - - -std::uint8_t* -Packet::Body() const -{ - if (nullptr != m_buffer && PacketHeader::c_bufferSize < m_bufferCapacity) - { - return m_buffer.get() + PacketHeader::c_bufferSize; - } - - return nullptr; -} - - -std::uint8_t* -Packet::Buffer() const -{ - return m_buffer.get(); -} - - -std::uint32_t -Packet::BufferLength() const -{ - return PacketHeader::c_bufferSize + m_header.m_bodyLength; -} - - -std::uint32_t -Packet::BufferCapacity() const -{ - return m_bufferCapacity; -} - - -void -Packet::AllocateBuffer(std::uint32_t p_bodyCapacity) -{ - m_bufferCapacity = PacketHeader::c_bufferSize + p_bodyCapacity; - m_buffer.reset(new std::uint8_t[m_bufferCapacity], std::default_delete()); -} - - -bool -PacketTypeHelper::IsRequestPacket(PacketType p_type) -{ - if (PacketType::Undefined == p_type || PacketType::ResponseMask == p_type) - { - return false; - } - - return (static_cast(p_type) & static_cast(PacketType::ResponseMask)) == 0; -} - - -bool -PacketTypeHelper::IsResponsePacket(PacketType p_type) -{ - if (PacketType::Undefined == p_type || PacketType::ResponseMask == p_type) - { - return false; - } - - return (static_cast(p_type) & static_cast(PacketType::ResponseMask)) != 0; -} - - -PacketType -PacketTypeHelper::GetCrosspondingResponseType(PacketType p_type) -{ - if (PacketType::Undefined == p_type || PacketType::ResponseMask == p_type) - { - return PacketType::Undefined; - } - - auto ret = static_cast(p_type) | static_cast(PacketType::ResponseMask); - return static_cast(ret); -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/RemoteSearchQuery.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/RemoteSearchQuery.cpp deleted file mode 100644 index 2cb450328e..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/RemoteSearchQuery.cpp +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Socket/RemoteSearchQuery.h" -#include "inc/Socket/SimpleSerialization.h" - -using namespace SPTAG; -using namespace SPTAG::Socket; - - -RemoteQuery::RemoteQuery() - : m_type(QueryType::String) -{ -} - - -std::size_t -RemoteQuery::EstimateBufferSize() const -{ - std::size_t sum = 0; - sum += SimpleSerialization::EstimateBufferSize(MajorVersion()); - sum += SimpleSerialization::EstimateBufferSize(MirrorVersion()); - sum += SimpleSerialization::EstimateBufferSize(m_type); - sum += SimpleSerialization::EstimateBufferSize(m_queryString); - - return sum; -} - - -std::uint8_t* -RemoteQuery::Write(std::uint8_t* p_buffer) const -{ - p_buffer = SimpleSerialization::SimpleWriteBuffer(MajorVersion(), p_buffer); - p_buffer = SimpleSerialization::SimpleWriteBuffer(MirrorVersion(), p_buffer); - - p_buffer = SimpleSerialization::SimpleWriteBuffer(m_type, p_buffer); - p_buffer = SimpleSerialization::SimpleWriteBuffer(m_queryString, p_buffer); - - return p_buffer; -} - - -const std::uint8_t* -RemoteQuery::Read(const std::uint8_t* p_buffer) -{ - decltype(MajorVersion()) majorVer = 0; - decltype(MirrorVersion()) mirrorVer = 0; - - p_buffer = SimpleSerialization::SimpleReadBuffer(p_buffer, majorVer); - p_buffer = SimpleSerialization::SimpleReadBuffer(p_buffer, mirrorVer); - if (majorVer != MajorVersion()) - { - return nullptr; - } - - p_buffer = SimpleSerialization::SimpleReadBuffer(p_buffer, m_type); - p_buffer = SimpleSerialization::SimpleReadBuffer(p_buffer, m_queryString); - - return p_buffer; -} - - -RemoteSearchResult::RemoteSearchResult() - : m_status(ResultStatus::Timeout) -{ -} - - -RemoteSearchResult::RemoteSearchResult(const RemoteSearchResult& p_right) - : m_status(p_right.m_status), - m_allIndexResults(p_right.m_allIndexResults) -{ -} - - -RemoteSearchResult::RemoteSearchResult(RemoteSearchResult&& p_right) - : m_status(std::move(p_right.m_status)), - m_allIndexResults(std::move(p_right.m_allIndexResults)) -{ -} - - -RemoteSearchResult& -RemoteSearchResult::operator=(RemoteSearchResult&& p_right) -{ - m_status = p_right.m_status; - m_allIndexResults = std::move(p_right.m_allIndexResults); - - return *this; -} - - -std::size_t -RemoteSearchResult::EstimateBufferSize() const -{ - std::size_t sum = 0; - sum += SimpleSerialization::EstimateBufferSize(MajorVersion()); - sum += SimpleSerialization::EstimateBufferSize(MirrorVersion()); - - sum += SimpleSerialization::EstimateBufferSize(m_status); - - sum += sizeof(std::uint32_t); - for (const auto& indexRes : m_allIndexResults) - { - sum += SimpleSerialization::EstimateBufferSize(indexRes.m_indexName); - sum += sizeof(std::uint32_t); - sum += sizeof(bool); - - for (const auto& res : indexRes.m_results) - { - sum += SimpleSerialization::EstimateBufferSize(res.VID); - sum += SimpleSerialization::EstimateBufferSize(res.Dist); - } - - if (indexRes.m_results.WithMeta()) - { - for (int i = 0; i < indexRes.m_results.GetResultNum(); ++i) - { - sum += SimpleSerialization::EstimateBufferSize(indexRes.m_results.GetMetadata(i)); - } - } - } - - return sum; -} - - -std::uint8_t* -RemoteSearchResult::Write(std::uint8_t* p_buffer) const -{ - p_buffer = SimpleSerialization::SimpleWriteBuffer(MajorVersion(), p_buffer); - p_buffer = SimpleSerialization::SimpleWriteBuffer(MirrorVersion(), p_buffer); - - p_buffer = SimpleSerialization::SimpleWriteBuffer(m_status, p_buffer); - p_buffer = SimpleSerialization::SimpleWriteBuffer(static_cast(m_allIndexResults.size()), p_buffer); - for (const auto& indexRes : m_allIndexResults) - { - p_buffer = SimpleSerialization::SimpleWriteBuffer(indexRes.m_indexName, p_buffer); - - p_buffer = SimpleSerialization::SimpleWriteBuffer(static_cast(indexRes.m_results.GetResultNum()), p_buffer); - p_buffer = SimpleSerialization::SimpleWriteBuffer(indexRes.m_results.WithMeta(), p_buffer); - - for (const auto& res : indexRes.m_results) - { - p_buffer = SimpleSerialization::SimpleWriteBuffer(res.VID, p_buffer); - p_buffer = SimpleSerialization::SimpleWriteBuffer(res.Dist, p_buffer); - } - - if (indexRes.m_results.WithMeta()) - { - for (int i = 0; i < indexRes.m_results.GetResultNum(); ++i) - { - p_buffer = SimpleSerialization::SimpleWriteBuffer(indexRes.m_results.GetMetadata(i), p_buffer); - } - } - } - - return p_buffer; -} - - -const std::uint8_t* -RemoteSearchResult::Read(const std::uint8_t* p_buffer) -{ - decltype(MajorVersion()) majorVer = 0; - decltype(MirrorVersion()) mirrorVer = 0; - - p_buffer = SimpleSerialization::SimpleReadBuffer(p_buffer, majorVer); - p_buffer = SimpleSerialization::SimpleReadBuffer(p_buffer, mirrorVer); - if (majorVer != MajorVersion()) - { - return nullptr; - } - - p_buffer = SimpleSerialization::SimpleReadBuffer(p_buffer, m_status); - - std::uint32_t len = 0; - p_buffer = SimpleSerialization::SimpleReadBuffer(p_buffer, len); - m_allIndexResults.resize(len); - - for (auto& indexRes : m_allIndexResults) - { - p_buffer = SimpleSerialization::SimpleReadBuffer(p_buffer, indexRes.m_indexName); - - std::uint32_t resNum = 0; - p_buffer = SimpleSerialization::SimpleReadBuffer(p_buffer, resNum); - - bool withMeta = false; - p_buffer = SimpleSerialization::SimpleReadBuffer(p_buffer, withMeta); - - indexRes.m_results.Init(nullptr, resNum, withMeta); - for (auto& res : indexRes.m_results) - { - p_buffer = SimpleSerialization::SimpleReadBuffer(p_buffer, res.VID); - p_buffer = SimpleSerialization::SimpleReadBuffer(p_buffer, res.Dist); - } - - if (withMeta) - { - for (int i = 0; i < indexRes.m_results.GetResultNum(); ++i) - { - ByteArray meta; - p_buffer = SimpleSerialization::SimpleReadBuffer(p_buffer, meta); - indexRes.m_results.SetMetadata(i, std::move(meta)); - } - } - } - - return p_buffer; -} diff --git a/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Server.cpp b/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Server.cpp deleted file mode 100644 index 86d60040bf..0000000000 --- a/core/src/index/thirdparty/SPTAG/AnnService/src/Socket/Server.cpp +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Socket/Server.h" - -#include - -using namespace SPTAG::Socket; - -Server::Server(const std::string& p_address, - const std::string& p_port, - const PacketHandlerMapPtr& p_handlerMap, - std::size_t p_threadNum) - : m_requestHandlerMap(p_handlerMap), - m_connectionManager(new ConnectionManager), - m_acceptor(m_ioContext) -{ - boost::asio::ip::tcp::resolver resolver(m_ioContext); - - boost::system::error_code errCode; - auto endPoints = resolver.resolve(p_address, p_port, errCode); - if (errCode) - { - fprintf(stderr, - "Failed to resolve %s %s, error: %s", - p_address.c_str(), - p_port.c_str(), - errCode.message().c_str()); - - throw std::runtime_error("Failed to resolve address."); - } - - boost::asio::ip::tcp::endpoint endpoint = *(endPoints.begin()); - m_acceptor.open(endpoint.protocol()); - m_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(false)); - - m_acceptor.bind(endpoint, errCode); - if (errCode) - { - fprintf(stderr, - "Failed to bind %s %s, error: %s", - p_address.c_str(), - p_port.c_str(), - errCode.message().c_str()); - - throw std::runtime_error("Failed to bind port."); - } - - m_acceptor.listen(boost::asio::socket_base::max_listen_connections, errCode); - if (errCode) - { - fprintf(stderr, - "Failed to listen %s %s, error: %s", - p_address.c_str(), - p_port.c_str(), - errCode.message().c_str()); - - throw std::runtime_error("Failed to listen port."); - } - - StartAccept(); - - m_threadPool.reserve(p_threadNum); - for (std::size_t i = 0; i < p_threadNum; ++i) - { - m_threadPool.emplace_back(std::move(std::thread([this]() { StartListen(); }))); - } -} - - -Server::~Server() -{ - m_acceptor.close(); - m_connectionManager->StopAll(); - while (!m_ioContext.stopped()) - { - m_ioContext.stop(); - } - - for (auto& t : m_threadPool) - { - t.join(); - } -} - - -void -Server::SetEventOnConnectionClose(std::function p_event) -{ - m_connectionManager->SetEventOnRemoving(std::move(p_event)); -} - - -void -Server::StartAccept() -{ - m_acceptor.async_accept([this](boost::system::error_code p_ec, - boost::asio::ip::tcp::socket p_socket) - { - if (!m_acceptor.is_open()) - { - return; - } - - if (!p_ec) - { - m_connectionManager->AddConnection(std::move(p_socket), - m_requestHandlerMap, - 0); - } - - StartAccept(); - }); -} - - -void -Server::StartListen() -{ - m_ioContext.run(); -} - - -void -Server::SendPacket(ConnectionID p_connection, Packet p_packet, std::function p_callback) -{ - auto connection = m_connectionManager->GetConnection(p_connection); - if (nullptr != connection) - { - connection->AsyncSend(std::move(p_packet), std::move(p_callback)); - } - else if (bool(p_callback)) - { - p_callback(false); - } -} diff --git a/core/src/index/thirdparty/SPTAG/CMakeLists.txt b/core/src/index/thirdparty/SPTAG/CMakeLists.txt deleted file mode 100644 index 44544bf7e9..0000000000 --- a/core/src/index/thirdparty/SPTAG/CMakeLists.txt +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -cmake_minimum_required (VERSION 3.12) - -project (SPTAGLib) - -function(CXX_COMPILER_DUMPVERSION _OUTPUT_VERSION) - exec_program(${CMAKE_CXX_COMPILER} - ARGS ${CMAKE_CXX_COMPILER_ARG1} -dumpversion - OUTPUT_VARIABLE COMPILER_VERSION - ) - - set(${_OUTPUT_VERSION} ${COMPILER_VERSION} PARENT_SCOPE) -endfunction() - -if(NOT WIN32) - CXX_COMPILER_DUMPVERSION(CXX_COMPILER_VERSION) -endif() - -if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") - # require at least gcc 5.0 - if (CXX_COMPILER_VERSION VERSION_LESS 5.0) - message(FATAL_ERROR "GCC version must be at least 5.0!") - endif() - set (CMAKE_CXX_FLAGS_RELEASE "-Wall -Wunreachable-code -Wno-reorder -Wno-sign-compare -Wno-unknown-pragmas -Wcast-align -lm -lrt -DNDEBUG -std=c++14 -fopenmp -march=native") - set (CMAKE_CXX_FLAGS_DEBUG "-Wall -Wunreachable-code -Wno-reorder -Wno-sign-compare -Wno-unknown-pragmas -Wcast-align -ggdb -lm -lrt -DNDEBUG -std=c++14 -fopenmp -march=native") -elseif(WIN32) - if(NOT MSVC14) - message(FATAL_ERROR "On Windows, only MSVC version 14 are supported!") - endif() -else () - message(FATAL_ERROR "Unrecognized compiler (use GCC or MSVC)!") -endif() - -if (NOT CMAKE_BUILD_TYPE) - set (CMAKE_BUILD_TYPE Release CACHE STRING "Build types: Release Debug" FORCE) -endif() -message (STATUS "Build type: ${CMAKE_BUILD_TYPE}") - -if (${CMAKE_SIZEOF_VOID_P} EQUAL "8") - set (PROJECTNAME_ARCHITECTURE "x64") -else () - set (PROJECTNAME_ARCHITECTURE "x86") -endif () -message (STATUS "Platform type: ${PROJECTNAME_ARCHITECTURE}") - -set(Boost_USE_MULTITHREADED ON) - -if (WIN32) - set(Boost_USE_STATIC_LIBS ON) - - set(CMAKE_CONFIGURATION_TYPES ${CMAKE_BUILD_TYPE}) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) - set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) - set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) - - set (LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}) - set (EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}) -else() - set (LIBRARY_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/${CMAKE_BUILD_TYPE}/") - set (EXECUTABLE_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/${CMAKE_BUILD_TYPE}/") -endif() - -set (CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) - -find_package(OpenMP) -if (OpenMP_FOUND) - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") - message (STATUS "Found openmp.") -else() - message (FATAL_ERROR "Could no find openmp!") -endif() - -find_package(Boost 1.67 COMPONENTS system thread serialization wserialization regex) -if (Boost_FOUND) - include_directories (${Boost_INCLUDE_DIR}) - link_directories (${Boost_LIBRARY_DIR} "/usr/lib") - message (STATUS "Found Boost.") - message (STATUS "Include Path: ${Boost_INCLUDE_DIRS}") - message (STATUS "Library Path: ${Boost_LIBRARY_DIRS}") - message (STATUS "Library: ${Boost_LIBRARIES}") -else() - message (FATAL_ERROR "Could not find Boost 1.67!") -endif() - -add_subdirectory (AnnService) -add_subdirectory (Wrappers) -add_subdirectory (Test) diff --git a/core/src/index/thirdparty/SPTAG/Dockerfile b/core/src/index/thirdparty/SPTAG/Dockerfile deleted file mode 100644 index 59c8c70166..0000000000 --- a/core/src/index/thirdparty/SPTAG/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -FROM ubuntu:18.04 - -WORKDIR /app -COPY CMakeLists.txt ./ -COPY AnnService ./AnnService/ -COPY Test ./Test/ -COPY Wrappers ./Wrappers/ - -SHELL ["/bin/bash", "-o", "pipefail", "-c"] - -RUN apt-get update && apt-get install -y --no-install-recommends wget build-essential \ - # remove the following if you don't want to build the wrappers - openjdk-8-jdk python3-pip swig && \ - apt-get remove --purge -y && \ - rm -rf /var/lib/apt/lists/* - -# cmake >= 3.12 is required -RUN wget "https://github.com/Kitware/CMake/releases/download/v3.14.4/cmake-3.14.4-Linux-x86_64.tar.gz" -q -O - \ - | tar -xz --strip-components=1 -C /usr/local - -# specific version of boost -RUN wget "https://dl.bintray.com/boostorg/release/1.67.0/source/boost_1_67_0.tar.gz" -q -O - \ - | tar -xz && \ - cd boost_1_67_0 && \ - ./bootstrap.sh && \ - ./b2 install && \ - # update ld cache so it finds boost in /usr/local/lib - ldconfig && \ - cd .. && rm -rf boost_1_67_0 - -# build -RUN mkdir build && cd build && cmake .. && make && cd .. - -# so python can find the SPTAG module -ENV PYTHONPATH=/app/Release \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/LICENSE b/core/src/index/thirdparty/SPTAG/LICENSE deleted file mode 100644 index d1ca00f20a..0000000000 --- a/core/src/index/thirdparty/SPTAG/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ - MIT License - - Copyright (c) Microsoft Corporation. All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/README.md b/core/src/index/thirdparty/SPTAG/README.md deleted file mode 100644 index ae4f0aab9b..0000000000 --- a/core/src/index/thirdparty/SPTAG/README.md +++ /dev/null @@ -1,144 +0,0 @@ -# SPTAG: A library for fast approximate nearest neighbor search - -[![MIT licensed](https://img.shields.io/badge/license-MIT-yellow.svg)](https://github.com/Microsoft/SPTAG/blob/master/LICENSE) -[![Build status](https://sysdnn.visualstudio.com/SPTAG/_apis/build/status/SPTAG-GITHUB)](https://sysdnn.visualstudio.com/SPTAG/_build/latest?definitionId=2) - -## **SPTAG** - SPTAG (Space Partition Tree And Graph) is a library for large scale vector approximate nearest neighbor search scenario released by [Microsoft Research (MSR)](https://www.msra.cn/) and [Microsoft Bing](http://bing.com). - -

- architecture -

- - - -## **Introduction** - -This library assumes that the samples are represented as vectors and that the vectors can be compared by L2 distances or cosine distances. -Vectors returned for a query vector are the vectors that have smallest L2 distance or cosine distances with the query vector. - -SPTAG provides two methods: kd-tree and relative neighborhood graph (SPTAG-KDT) -and balanced k-means tree and relative neighborhood graph (SPTAG-BKT). -SPTAG-KDT is advantageous in index building cost, and SPTAG-BKT is advantageous in search accuracy in very high-dimensional data. - - - -## **How it works** - -SPTAG is inspired by the NGS approach [[WangL12](#References)]. It contains two basic modules: index builder and searcher. -The RNG is built on the k-nearest neighborhood graph [[WangWZTG12](#References), [WangWJLZZH14](#References)] -for boosting the connectivity. Balanced k-means trees are used to replace kd-trees to avoid the inaccurate distance bound estimation in kd-trees for very high-dimensional vectors. -The search begins with the search in the space partition trees for -finding several seeds to start the search in the RNG. -The searches in the trees and the graph are iteratively conducted. - - ## **Highlights** - * Fresh update: Support online vector deletion and insertion - * Distributed serving: Search over multiple machines - - ## **Build** - -### **Requirements** - -* swig >= 3.0 -* cmake >= 3.12.0 -* boost >= 1.67.0 - -### **Install** - -> For Linux: -```bash -mkdir build -cd build && cmake .. && make -``` -It will generate a Release folder in the code directory which contains all the build targets. - -> For Windows: -```bash -mkdir build -cd build && cmake -A x64 .. -``` -It will generate a SPTAGLib.sln in the build directory. -Compiling the ALL_BUILD project in the Visual Studio (at least 2015) will generate a Release directory which contains all the build targets. - -> Using Docker: -```bash -docker build -t sptag . -``` -Will build a docker container with binaries in `/app/Release/`. - -### **Verify** - -Run the test (or Test.exe) in the Release folder to verify all the tests have passed. - -### **Usage** - -The detailed usage can be found in [Get started](docs/GettingStart.md). -The detailed parameters tunning can be found in [Parameters](docs/Parameters.md). - -## **References** -Please cite SPTAG in your publications if it helps your research: -``` -@manual{ChenW18, - author = {Qi Chen and - Haidong Wang and - Mingqin Li and - Gang Ren and - Scarlett Li and - Jeffery Zhu and - Jason Li and - Chuanjie Liu and - Lintao Zhang and - Jingdong Wang}, - title = {SPTAG: A library for fast approximate nearest neighbor search}, - url = {https://github.com/Microsoft/SPTAG}, - year = {2018} -} - -@inproceedings{WangL12, - author = {Jingdong Wang and - Shipeng Li}, - title = {Query-driven iterated neighborhood graph search for large scale indexing}, - booktitle = {ACM Multimedia 2012}, - pages = {179--188}, - year = {2012} -} - -@inproceedings{WangWZTGL12, - author = {Jing Wang and - Jingdong Wang and - Gang Zeng and - Zhuowen Tu and - Rui Gan and - Shipeng Li}, - title = {Scalable k-NN graph construction for visual descriptors}, - booktitle = {CVPR 2012}, - pages = {1106--1113}, - year = {2012} -} - -@article{WangWJLZZH14, - author = {Jingdong Wang and - Naiyan Wang and - You Jia and - Jian Li and - Gang Zeng and - Hongbin Zha and - Xian{-}Sheng Hua}, - title = {Trinary-Projection Trees for Approximate Nearest Neighbor Search}, - journal = {{IEEE} Trans. Pattern Anal. Mach. Intell.}, - volume = {36}, - number = {2}, - pages = {388--403}, - year = {2014 -} -``` - -## **Contribute** - -This project welcomes contributions and suggestions from all the users. - -We use [GitHub issues](https://github.com/Microsoft/SPTAG/issues) for tracking suggestions and bugs. - -## **License** -The entire codebase is under [MIT license](https://github.com/Microsoft/SPTAG/blob/master/LICENSE) diff --git a/core/src/index/thirdparty/SPTAG/SPTAG.sdf b/core/src/index/thirdparty/SPTAG/SPTAG.sdf deleted file mode 100644 index 254a44ece6de77823917e2b4a7aed9f3a9fedc6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65536 zcmeI(F-pWh6adh_$q?L13lAXT0R-C>ti6Dic3WF|1`lB45yW%Zcm{XAGDFxxu~=c{ zeMtVF$&gH5s$t0c&BJ}nV|f{i+Rh`R|#`(@0Y009C72oNAZfB*pk1PBoLR^WMBACz*}$Bgrzzk9EO z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs L0RjXF>{eg{NpuS@ diff --git a/core/src/index/thirdparty/SPTAG/SPTAG.sln b/core/src/index/thirdparty/SPTAG/SPTAG.sln deleted file mode 100644 index 5fdfd0297c..0000000000 --- a/core/src/index/thirdparty/SPTAG/SPTAG.sln +++ /dev/null @@ -1,211 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CoreLibrary", "AnnService\CoreLibrary.vcxproj", "{C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Server", "AnnService\Server.vcxproj", "{E28B1222-8BEA-4A92-8FE0-088EBDAA7FE0}" - ProjectSection(ProjectDependencies) = postProject - {F9A72303-6381-4C80-86FF-606A2F6F7B96} = {F9A72303-6381-4C80-86FF-606A2F6F7B96} - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} = {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PythonCore", "Wrappers\PythonCore.vcxproj", "{AF31947C-0495-42FE-A1AD-8F0DA2A679C7}" - ProjectSection(ProjectDependencies) = postProject - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} = {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SocketLib", "AnnService\SocketLib.vcxproj", "{F9A72303-6381-4C80-86FF-606A2F6F7B96}" - ProjectSection(ProjectDependencies) = postProject - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} = {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Client", "AnnService\Client.vcxproj", "{A89D70C3-C53B-42DE-A5CE-9A472540F5CB}" - ProjectSection(ProjectDependencies) = postProject - {F9A72303-6381-4C80-86FF-606A2F6F7B96} = {F9A72303-6381-4C80-86FF-606A2F6F7B96} - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} = {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Aggregator", "AnnService\Aggregator.vcxproj", "{D7F09A63-BDCA-4F6C-A864-8551D1FE447A}" - ProjectSection(ProjectDependencies) = postProject - {F9A72303-6381-4C80-86FF-606A2F6F7B96} = {F9A72303-6381-4C80-86FF-606A2F6F7B96} - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} = {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PythonClient", "Wrappers\PythonClient.vcxproj", "{9B014CF6-E3FB-4BD4-B3B1-D26297BB31AA}" - ProjectSection(ProjectDependencies) = postProject - {F9A72303-6381-4C80-86FF-606A2F6F7B96} = {F9A72303-6381-4C80-86FF-606A2F6F7B96} - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} = {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IndexBuilder", "AnnService\IndexBuilder.vcxproj", "{F492F794-E78B-4B1F-A556-5E045B9163D5}" - ProjectSection(ProjectDependencies) = postProject - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} = {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IndexSearcher", "AnnService\IndexSearcher.vcxproj", "{97615D3B-9FA0-469E-B229-95A91A5087E0}" - ProjectSection(ProjectDependencies) = postProject - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} = {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test\Test.vcxproj", "{29A25655-CCF2-47F8-8BC8-DFE1B5CF993C}" - ProjectSection(ProjectDependencies) = postProject - {F9A72303-6381-4C80-86FF-606A2F6F7B96} = {F9A72303-6381-4C80-86FF-606A2F6F7B96} - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} = {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JavaCore", "Wrappers\JavaCore.vcxproj", "{93FEB26B-965E-4157-8BE5-052F5CA112BB}" - ProjectSection(ProjectDependencies) = postProject - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} = {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JavaClient", "Wrappers\JavaClient.vcxproj", "{8866BF98-AA2E-450F-9F33-083E007CCA74}" - ProjectSection(ProjectDependencies) = postProject - {F9A72303-6381-4C80-86FF-606A2F6F7B96} = {F9A72303-6381-4C80-86FF-606A2F6F7B96} - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} = {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CsharpCore", "Wrappers\CsharpCore.vcxproj", "{1896C009-AD46-4A70-B83C-4652A7F37503}" - ProjectSection(ProjectDependencies) = postProject - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} = {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CsharpClient", "Wrappers\CsharpClient.vcxproj", "{363BA3BB-75C4-4CC7-AECB-28C7534B3710}" - ProjectSection(ProjectDependencies) = postProject - {F9A72303-6381-4C80-86FF-606A2F6F7B96} = {F9A72303-6381-4C80-86FF-606A2F6F7B96} - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} = {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CLRCore", "Wrappers\CLRCore.vcxproj", "{38ACBA6C-2E50-44D4-9A6D-DC735B56E38F}" - ProjectSection(ProjectDependencies) = postProject - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} = {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9} - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9}.Debug|x64.ActiveCfg = Debug|x64 - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9}.Debug|x64.Build.0 = Debug|x64 - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9}.Debug|x86.ActiveCfg = Debug|x64 - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9}.Debug|x86.Build.0 = Debug|x64 - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9}.Release|x64.ActiveCfg = Release|x64 - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9}.Release|x64.Build.0 = Release|x64 - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9}.Release|x86.ActiveCfg = Debug|x64 - {C2BC5FDE-C853-4F3D-B7E4-2C9B5524DDF9}.Release|x86.Build.0 = Debug|x64 - {E28B1222-8BEA-4A92-8FE0-088EBDAA7FE0}.Debug|x64.ActiveCfg = Debug|x64 - {E28B1222-8BEA-4A92-8FE0-088EBDAA7FE0}.Debug|x64.Build.0 = Debug|x64 - {E28B1222-8BEA-4A92-8FE0-088EBDAA7FE0}.Debug|x86.ActiveCfg = Debug|x64 - {E28B1222-8BEA-4A92-8FE0-088EBDAA7FE0}.Debug|x86.Build.0 = Debug|x64 - {E28B1222-8BEA-4A92-8FE0-088EBDAA7FE0}.Release|x64.ActiveCfg = Release|x64 - {E28B1222-8BEA-4A92-8FE0-088EBDAA7FE0}.Release|x64.Build.0 = Release|x64 - {E28B1222-8BEA-4A92-8FE0-088EBDAA7FE0}.Release|x86.ActiveCfg = Debug|x64 - {E28B1222-8BEA-4A92-8FE0-088EBDAA7FE0}.Release|x86.Build.0 = Debug|x64 - {AF31947C-0495-42FE-A1AD-8F0DA2A679C7}.Debug|x64.ActiveCfg = Debug|x64 - {AF31947C-0495-42FE-A1AD-8F0DA2A679C7}.Debug|x64.Build.0 = Debug|x64 - {AF31947C-0495-42FE-A1AD-8F0DA2A679C7}.Debug|x86.ActiveCfg = Debug|x64 - {AF31947C-0495-42FE-A1AD-8F0DA2A679C7}.Debug|x86.Build.0 = Debug|x64 - {AF31947C-0495-42FE-A1AD-8F0DA2A679C7}.Release|x64.ActiveCfg = Release|x64 - {AF31947C-0495-42FE-A1AD-8F0DA2A679C7}.Release|x64.Build.0 = Release|x64 - {AF31947C-0495-42FE-A1AD-8F0DA2A679C7}.Release|x86.ActiveCfg = Debug|x64 - {AF31947C-0495-42FE-A1AD-8F0DA2A679C7}.Release|x86.Build.0 = Debug|x64 - {F9A72303-6381-4C80-86FF-606A2F6F7B96}.Debug|x64.ActiveCfg = Debug|x64 - {F9A72303-6381-4C80-86FF-606A2F6F7B96}.Debug|x64.Build.0 = Debug|x64 - {F9A72303-6381-4C80-86FF-606A2F6F7B96}.Debug|x86.ActiveCfg = Debug|x64 - {F9A72303-6381-4C80-86FF-606A2F6F7B96}.Debug|x86.Build.0 = Debug|x64 - {F9A72303-6381-4C80-86FF-606A2F6F7B96}.Release|x64.ActiveCfg = Release|x64 - {F9A72303-6381-4C80-86FF-606A2F6F7B96}.Release|x64.Build.0 = Release|x64 - {F9A72303-6381-4C80-86FF-606A2F6F7B96}.Release|x86.ActiveCfg = Debug|x64 - {F9A72303-6381-4C80-86FF-606A2F6F7B96}.Release|x86.Build.0 = Debug|x64 - {A89D70C3-C53B-42DE-A5CE-9A472540F5CB}.Debug|x64.ActiveCfg = Debug|x64 - {A89D70C3-C53B-42DE-A5CE-9A472540F5CB}.Debug|x64.Build.0 = Debug|x64 - {A89D70C3-C53B-42DE-A5CE-9A472540F5CB}.Debug|x86.ActiveCfg = Debug|x64 - {A89D70C3-C53B-42DE-A5CE-9A472540F5CB}.Debug|x86.Build.0 = Debug|x64 - {A89D70C3-C53B-42DE-A5CE-9A472540F5CB}.Release|x64.ActiveCfg = Release|x64 - {A89D70C3-C53B-42DE-A5CE-9A472540F5CB}.Release|x64.Build.0 = Release|x64 - {A89D70C3-C53B-42DE-A5CE-9A472540F5CB}.Release|x86.ActiveCfg = Debug|x64 - {A89D70C3-C53B-42DE-A5CE-9A472540F5CB}.Release|x86.Build.0 = Debug|x64 - {D7F09A63-BDCA-4F6C-A864-8551D1FE447A}.Debug|x64.ActiveCfg = Debug|x64 - {D7F09A63-BDCA-4F6C-A864-8551D1FE447A}.Debug|x64.Build.0 = Debug|x64 - {D7F09A63-BDCA-4F6C-A864-8551D1FE447A}.Debug|x86.ActiveCfg = Debug|x64 - {D7F09A63-BDCA-4F6C-A864-8551D1FE447A}.Debug|x86.Build.0 = Debug|x64 - {D7F09A63-BDCA-4F6C-A864-8551D1FE447A}.Release|x64.ActiveCfg = Release|x64 - {D7F09A63-BDCA-4F6C-A864-8551D1FE447A}.Release|x64.Build.0 = Release|x64 - {D7F09A63-BDCA-4F6C-A864-8551D1FE447A}.Release|x86.ActiveCfg = Debug|x64 - {D7F09A63-BDCA-4F6C-A864-8551D1FE447A}.Release|x86.Build.0 = Debug|x64 - {9B014CF6-E3FB-4BD4-B3B1-D26297BB31AA}.Debug|x64.ActiveCfg = Debug|x64 - {9B014CF6-E3FB-4BD4-B3B1-D26297BB31AA}.Debug|x64.Build.0 = Debug|x64 - {9B014CF6-E3FB-4BD4-B3B1-D26297BB31AA}.Debug|x86.ActiveCfg = Debug|Win32 - {9B014CF6-E3FB-4BD4-B3B1-D26297BB31AA}.Debug|x86.Build.0 = Debug|Win32 - {9B014CF6-E3FB-4BD4-B3B1-D26297BB31AA}.Release|x64.ActiveCfg = Release|x64 - {9B014CF6-E3FB-4BD4-B3B1-D26297BB31AA}.Release|x64.Build.0 = Release|x64 - {9B014CF6-E3FB-4BD4-B3B1-D26297BB31AA}.Release|x86.ActiveCfg = Release|Win32 - {9B014CF6-E3FB-4BD4-B3B1-D26297BB31AA}.Release|x86.Build.0 = Release|Win32 - {F492F794-E78B-4B1F-A556-5E045B9163D5}.Debug|x64.ActiveCfg = Debug|x64 - {F492F794-E78B-4B1F-A556-5E045B9163D5}.Debug|x64.Build.0 = Debug|x64 - {F492F794-E78B-4B1F-A556-5E045B9163D5}.Debug|x86.ActiveCfg = Debug|Win32 - {F492F794-E78B-4B1F-A556-5E045B9163D5}.Debug|x86.Build.0 = Debug|Win32 - {F492F794-E78B-4B1F-A556-5E045B9163D5}.Release|x64.ActiveCfg = Release|x64 - {F492F794-E78B-4B1F-A556-5E045B9163D5}.Release|x64.Build.0 = Release|x64 - {F492F794-E78B-4B1F-A556-5E045B9163D5}.Release|x86.ActiveCfg = Release|Win32 - {F492F794-E78B-4B1F-A556-5E045B9163D5}.Release|x86.Build.0 = Release|Win32 - {97615D3B-9FA0-469E-B229-95A91A5087E0}.Debug|x64.ActiveCfg = Debug|x64 - {97615D3B-9FA0-469E-B229-95A91A5087E0}.Debug|x64.Build.0 = Debug|x64 - {97615D3B-9FA0-469E-B229-95A91A5087E0}.Debug|x86.ActiveCfg = Debug|Win32 - {97615D3B-9FA0-469E-B229-95A91A5087E0}.Debug|x86.Build.0 = Debug|Win32 - {97615D3B-9FA0-469E-B229-95A91A5087E0}.Release|x64.ActiveCfg = Release|x64 - {97615D3B-9FA0-469E-B229-95A91A5087E0}.Release|x64.Build.0 = Release|x64 - {97615D3B-9FA0-469E-B229-95A91A5087E0}.Release|x86.ActiveCfg = Release|Win32 - {97615D3B-9FA0-469E-B229-95A91A5087E0}.Release|x86.Build.0 = Release|Win32 - {29A25655-CCF2-47F8-8BC8-DFE1B5CF993C}.Debug|x64.ActiveCfg = Debug|x64 - {29A25655-CCF2-47F8-8BC8-DFE1B5CF993C}.Debug|x64.Build.0 = Debug|x64 - {29A25655-CCF2-47F8-8BC8-DFE1B5CF993C}.Debug|x86.ActiveCfg = Debug|Win32 - {29A25655-CCF2-47F8-8BC8-DFE1B5CF993C}.Debug|x86.Build.0 = Debug|Win32 - {29A25655-CCF2-47F8-8BC8-DFE1B5CF993C}.Release|x64.ActiveCfg = Release|x64 - {29A25655-CCF2-47F8-8BC8-DFE1B5CF993C}.Release|x64.Build.0 = Release|x64 - {29A25655-CCF2-47F8-8BC8-DFE1B5CF993C}.Release|x86.ActiveCfg = Release|Win32 - {29A25655-CCF2-47F8-8BC8-DFE1B5CF993C}.Release|x86.Build.0 = Release|Win32 - {93FEB26B-965E-4157-8BE5-052F5CA112BB}.Debug|x64.ActiveCfg = Debug|x64 - {93FEB26B-965E-4157-8BE5-052F5CA112BB}.Debug|x86.ActiveCfg = Debug|Win32 - {93FEB26B-965E-4157-8BE5-052F5CA112BB}.Release|x64.ActiveCfg = Release|x64 - {93FEB26B-965E-4157-8BE5-052F5CA112BB}.Release|x86.ActiveCfg = Release|Win32 - {8866BF98-AA2E-450F-9F33-083E007CCA74}.Debug|x64.ActiveCfg = Debug|x64 - {8866BF98-AA2E-450F-9F33-083E007CCA74}.Debug|x86.ActiveCfg = Debug|Win32 - {8866BF98-AA2E-450F-9F33-083E007CCA74}.Release|x64.ActiveCfg = Release|x64 - {8866BF98-AA2E-450F-9F33-083E007CCA74}.Release|x86.ActiveCfg = Release|Win32 - {1896C009-AD46-4A70-B83C-4652A7F37503}.Debug|x64.ActiveCfg = Debug|x64 - {1896C009-AD46-4A70-B83C-4652A7F37503}.Debug|x64.Build.0 = Debug|x64 - {1896C009-AD46-4A70-B83C-4652A7F37503}.Debug|x86.ActiveCfg = Debug|Win32 - {1896C009-AD46-4A70-B83C-4652A7F37503}.Debug|x86.Build.0 = Debug|Win32 - {1896C009-AD46-4A70-B83C-4652A7F37503}.Release|x64.ActiveCfg = Release|x64 - {1896C009-AD46-4A70-B83C-4652A7F37503}.Release|x64.Build.0 = Release|x64 - {1896C009-AD46-4A70-B83C-4652A7F37503}.Release|x86.ActiveCfg = Release|Win32 - {1896C009-AD46-4A70-B83C-4652A7F37503}.Release|x86.Build.0 = Release|Win32 - {363BA3BB-75C4-4CC7-AECB-28C7534B3710}.Debug|x64.ActiveCfg = Debug|x64 - {363BA3BB-75C4-4CC7-AECB-28C7534B3710}.Debug|x64.Build.0 = Debug|x64 - {363BA3BB-75C4-4CC7-AECB-28C7534B3710}.Debug|x86.ActiveCfg = Debug|Win32 - {363BA3BB-75C4-4CC7-AECB-28C7534B3710}.Debug|x86.Build.0 = Debug|Win32 - {363BA3BB-75C4-4CC7-AECB-28C7534B3710}.Release|x64.ActiveCfg = Release|x64 - {363BA3BB-75C4-4CC7-AECB-28C7534B3710}.Release|x64.Build.0 = Release|x64 - {363BA3BB-75C4-4CC7-AECB-28C7534B3710}.Release|x86.ActiveCfg = Release|Win32 - {363BA3BB-75C4-4CC7-AECB-28C7534B3710}.Release|x86.Build.0 = Release|Win32 - {38ACBA6C-2E50-44D4-9A6D-DC735B56E38F}.Debug|x64.ActiveCfg = Debug|x64 - {38ACBA6C-2E50-44D4-9A6D-DC735B56E38F}.Debug|x64.Build.0 = Debug|x64 - {38ACBA6C-2E50-44D4-9A6D-DC735B56E38F}.Debug|x86.ActiveCfg = Debug|Win32 - {38ACBA6C-2E50-44D4-9A6D-DC735B56E38F}.Debug|x86.Build.0 = Debug|Win32 - {38ACBA6C-2E50-44D4-9A6D-DC735B56E38F}.Release|x64.ActiveCfg = Release|x64 - {38ACBA6C-2E50-44D4-9A6D-DC735B56E38F}.Release|x64.Build.0 = Release|x64 - {38ACBA6C-2E50-44D4-9A6D-DC735B56E38F}.Release|x86.ActiveCfg = Release|Win32 - {38ACBA6C-2E50-44D4-9A6D-DC735B56E38F}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {38BDFF12-6FEC-4B67-A7BD-436D9E2544FD} - EndGlobalSection -EndGlobal diff --git a/core/src/index/thirdparty/SPTAG/Test/CMakeLists.txt b/core/src/index/thirdparty/SPTAG/Test/CMakeLists.txt deleted file mode 100644 index 39166b32a9..0000000000 --- a/core/src/index/thirdparty/SPTAG/Test/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -if(NOT WIN32) - ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK) - message (STATUS "BOOST_TEST_DYN_LINK") -endif() - -find_package(Boost 1.67 COMPONENTS system thread serialization wserialization regex filesystem unit_test_framework) -if (Boost_FOUND) - include_directories (${Boost_INCLUDE_DIR}) - link_directories (${Boost_LIBRARY_DIR}) - message (STATUS "Found Boost.") - message (STATUS "Include Path: ${Boost_INCLUDE_DIRS}") - message (STATUS "Library Path: ${Boost_LIBRARY_DIRS}") - message (STATUS "Library: ${Boost_LIBRARIES}") -else() - message (FATAL_ERROR "Could not find Boost 1.67!") -endif() - -include_directories(${PYTHON_INCLUDE_PATH} ${PROJECT_SOURCE_DIR}/AnnService ${PROJECT_SOURCE_DIR}/PythonWrapper ${PROJECT_SOURCE_DIR}/Test) - -file(GLOB TEST_HDR_FILES ${PROJECT_SOURCE_DIR}/Test/inc/Test.h) -file(GLOB TEST_SRC_FILES ${PROJECT_SOURCE_DIR}/Test/src/*.cpp) -add_executable (test ${TEST_SRC_FILES} ${TEST_HDR_FILES}) -target_link_libraries(test SPTAGLib ${Boost_LIBRARIES}) - -install(TARGETS test - RUNTIME DESTINATION bin - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib) - diff --git a/core/src/index/thirdparty/SPTAG/Test/Test.vcxproj b/core/src/index/thirdparty/SPTAG/Test/Test.vcxproj deleted file mode 100644 index c479ae5be1..0000000000 --- a/core/src/index/thirdparty/SPTAG/Test/Test.vcxproj +++ /dev/null @@ -1,183 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {29A25655-CCF2-47F8-8BC8-DFE1B5CF993C} - Test - 8.1 - - - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - - - - - - - - - - - - - - - - - - - $(SolutionDir)obj\$(Platform)_$(Configuration)\$(ProjectName)\ - $(ProjectDir);$(SolutionDir)AnnService\;$(IncludePath) - $(OutAppDir) - $(OutLibDir);$(LibraryPath) - - - false - - - - CoreLibrary.lib;%(AdditionalDependencies) - - - - - Level3 - MaxSpeed - true - true - true - true - _MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - - - true - true - - - Console - - - - - Level3 - Disabled - true - true - - - - - Level3 - Disabled - true - true - _MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - Guard - ProgramDatabase - - - Console - /guard:cf %(AdditionalOptions) - - - - - Level3 - MaxSpeed - true - true - true - true - - - true - true - - - - - - - - - - - - - - - - - Designer - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Test/Test.vcxproj.filters b/core/src/index/thirdparty/SPTAG/Test/Test.vcxproj.filters deleted file mode 100644 index a814c3ec3f..0000000000 --- a/core/src/index/thirdparty/SPTAG/Test/Test.vcxproj.filters +++ /dev/null @@ -1,48 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Test/Test.vcxproj.user b/core/src/index/thirdparty/SPTAG/Test/Test.vcxproj.user deleted file mode 100644 index 10f0fcf2d9..0000000000 --- a/core/src/index/thirdparty/SPTAG/Test/Test.vcxproj.user +++ /dev/null @@ -1,7 +0,0 @@ - - - - $(OutLibDir) - WindowsLocalDebugger - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Test/inc/Test.h b/core/src/index/thirdparty/SPTAG/Test/inc/Test.h deleted file mode 100644 index da6c096ba2..0000000000 --- a/core/src/index/thirdparty/SPTAG/Test/inc/Test.h +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#pragma once - -#include -#include diff --git a/core/src/index/thirdparty/SPTAG/Test/packages.config b/core/src/index/thirdparty/SPTAG/Test/packages.config deleted file mode 100644 index 651c754779..0000000000 --- a/core/src/index/thirdparty/SPTAG/Test/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Test/src/AlgoTest.cpp b/core/src/index/thirdparty/SPTAG/Test/src/AlgoTest.cpp deleted file mode 100644 index a93cd38bed..0000000000 --- a/core/src/index/thirdparty/SPTAG/Test/src/AlgoTest.cpp +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Test.h" -#include "inc/Helper/SimpleIniReader.h" -#include "inc/Core/VectorIndex.h" - -#include - -template -void Build(SPTAG::IndexAlgoType algo, std::string distCalcMethod, std::shared_ptr& vec, std::shared_ptr& meta, const std::string out) -{ - - std::shared_ptr vecIndex = SPTAG::VectorIndex::CreateInstance(algo, SPTAG::GetEnumValueType()); - BOOST_CHECK(nullptr != vecIndex); - - vecIndex->SetParameter("DistCalcMethod", distCalcMethod); - - BOOST_CHECK(SPTAG::ErrorCode::Success == vecIndex->BuildIndex(vec, meta)); - BOOST_CHECK(SPTAG::ErrorCode::Success == vecIndex->SaveIndex(out)); -} - -template -void BuildWithMetaMapping(SPTAG::IndexAlgoType algo, std::string distCalcMethod, std::shared_ptr& vec, std::shared_ptr& meta, const std::string out) -{ - - std::shared_ptr vecIndex = SPTAG::VectorIndex::CreateInstance(algo, SPTAG::GetEnumValueType()); - BOOST_CHECK(nullptr != vecIndex); - - vecIndex->SetParameter("DistCalcMethod", distCalcMethod); - - BOOST_CHECK(SPTAG::ErrorCode::Success == vecIndex->BuildIndex(vec, meta, true)); - BOOST_CHECK(SPTAG::ErrorCode::Success == vecIndex->SaveIndex(out)); -} - -template -void Search(const std::string folder, T* vec, SPTAG::SizeType n, int k, std::string* truthmeta) -{ - std::shared_ptr vecIndex; - BOOST_CHECK(SPTAG::ErrorCode::Success == SPTAG::VectorIndex::LoadIndex(folder, vecIndex)); - BOOST_CHECK(nullptr != vecIndex); - - for (SPTAG::SizeType i = 0; i < n; i++) - { - SPTAG::QueryResult res(vec, k, true); - vecIndex->SearchIndex(res); - std::unordered_set resmeta; - for (int j = 0; j < k; j++) - { - resmeta.insert(std::string((char*)res.GetMetadata(j).Data(), res.GetMetadata(j).Length())); - std::cout << res.GetResult(j)->Dist << "@(" << res.GetResult(j)->VID << "," << std::string((char*)res.GetMetadata(j).Data(), res.GetMetadata(j).Length()) << ") "; - } - std::cout << std::endl; - for (int j = 0; j < k; j++) - { - BOOST_CHECK(resmeta.find(truthmeta[i * k + j]) != resmeta.end()); - } - vec += vecIndex->GetFeatureDim(); - } - vecIndex.reset(); -} - -template -void Add(const std::string folder, std::shared_ptr& vec, std::shared_ptr& meta, const std::string out) -{ - std::shared_ptr vecIndex; - BOOST_CHECK(SPTAG::ErrorCode::Success == SPTAG::VectorIndex::LoadIndex(folder, vecIndex)); - BOOST_CHECK(nullptr != vecIndex); - - BOOST_CHECK(SPTAG::ErrorCode::Success == vecIndex->AddIndex(vec, meta)); - BOOST_CHECK(SPTAG::ErrorCode::Success == vecIndex->SaveIndex(out)); - vecIndex.reset(); -} - -template -void Delete(const std::string folder, T* vec, SPTAG::SizeType n, const std::string out) -{ - std::shared_ptr vecIndex; - BOOST_CHECK(SPTAG::ErrorCode::Success == SPTAG::VectorIndex::LoadIndex(folder, vecIndex)); - BOOST_CHECK(nullptr != vecIndex); - - BOOST_CHECK(SPTAG::ErrorCode::Success == vecIndex->DeleteIndex((const void*)vec, n)); - BOOST_CHECK(SPTAG::ErrorCode::Success == vecIndex->SaveIndex(out)); - vecIndex.reset(); -} - -template -void Test(SPTAG::IndexAlgoType algo, std::string distCalcMethod) -{ - SPTAG::SizeType n = 100, q = 3; - SPTAG::DimensionType m = 10; - int k = 3; - std::vector vec; - for (SPTAG::SizeType i = 0; i < n; i++) { - for (SPTAG::DimensionType j = 0; j < m; j++) { - vec.push_back((T)i); - } - } - - std::vector query; - for (SPTAG::SizeType i = 0; i < q; i++) { - for (SPTAG::DimensionType j = 0; j < m; j++) { - query.push_back((T)i*2); - } - } - - std::vector meta; - std::vector metaoffset; - for (SPTAG::SizeType i = 0; i < n; i++) { - metaoffset.push_back((std::uint64_t)meta.size()); - std::string a = std::to_string(i); - for (size_t j = 0; j < a.length(); j++) - meta.push_back(a[j]); - } - metaoffset.push_back((std::uint64_t)meta.size()); - - std::shared_ptr vecset(new SPTAG::BasicVectorSet( - SPTAG::ByteArray((std::uint8_t*)vec.data(), sizeof(T) * n * m, false), - SPTAG::GetEnumValueType(), m, n)); - - std::shared_ptr metaset(new SPTAG::MemMetadataSet( - SPTAG::ByteArray((std::uint8_t*)meta.data(), meta.size() * sizeof(char), false), - SPTAG::ByteArray((std::uint8_t*)metaoffset.data(), metaoffset.size() * sizeof(std::uint64_t), false), - n)); - - Build(algo, distCalcMethod, vecset, metaset, "testindices"); - std::string truthmeta1[] = { "0", "1", "2", "2", "1", "3", "4", "3", "5" }; - Search("testindices", query.data(), q, k, truthmeta1); - - Add("testindices", vecset, metaset, "testindices"); - std::string truthmeta2[] = { "0", "0", "1", "2", "2", "1", "4", "4", "3" }; - Search("testindices", query.data(), q, k, truthmeta2); - - Delete("testindices", query.data(), q, "testindices"); - std::string truthmeta3[] = { "1", "1", "3", "1", "3", "1", "3", "5", "3" }; - Search("testindices", query.data(), q, k, truthmeta3); - - BuildWithMetaMapping(algo, distCalcMethod, vecset, metaset, "testindices"); - std::string truthmeta4[] = { "0", "1", "2", "2", "1", "3", "4", "3", "5" }; - Search("testindices", query.data(), q, k, truthmeta4); - - Add("testindices", vecset, metaset, "testindices"); - std::string truthmeta5[] = { "0", "1", "2", "2", "1", "3", "4", "3", "5" }; - Search("testindices", query.data(), q, k, truthmeta5); -} - -BOOST_AUTO_TEST_SUITE (AlgoTest) - -BOOST_AUTO_TEST_CASE(KDTTest) -{ - Test(SPTAG::IndexAlgoType::KDT, "L2"); -} - -BOOST_AUTO_TEST_CASE(BKTTest) -{ - Test(SPTAG::IndexAlgoType::BKT, "L2"); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/core/src/index/thirdparty/SPTAG/Test/src/Base64HelperTest.cpp b/core/src/index/thirdparty/SPTAG/Test/src/Base64HelperTest.cpp deleted file mode 100644 index 2ead4753e5..0000000000 --- a/core/src/index/thirdparty/SPTAG/Test/src/Base64HelperTest.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Test.h" -#include "inc/Helper/Base64Encode.h" - -#include - -BOOST_AUTO_TEST_SUITE(Base64Test) - -BOOST_AUTO_TEST_CASE(Base64EncDec) -{ - using namespace SPTAG::Helper::Base64; - - const size_t bufferSize = 1 << 10; - std::unique_ptr rawBuffer(new uint8_t[bufferSize]); - std::unique_ptr encBuffer(new char[bufferSize]); - std::unique_ptr rawBuffer2(new uint8_t[bufferSize]); - - for (size_t inputSize = 1; inputSize < 128; ++inputSize) - { - for (size_t i = 0; i < inputSize; ++i) - { - rawBuffer[i] = static_cast(i); - } - - size_t encBufLen = CapacityForEncode(inputSize); - BOOST_CHECK(encBufLen < bufferSize); - - size_t encOutLen = 0; - BOOST_CHECK(Encode(rawBuffer.get(), inputSize, encBuffer.get(), encOutLen)); - BOOST_CHECK(encBufLen >= encOutLen); - - size_t decBufLen = CapacityForDecode(encOutLen); - BOOST_CHECK(decBufLen < bufferSize); - - size_t decOutLen = 0; - BOOST_CHECK(Decode(encBuffer.get(), encOutLen, rawBuffer.get(), decOutLen)); - BOOST_CHECK(decBufLen >= decOutLen); - } -} - -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Test/src/CommonHelperTest.cpp b/core/src/index/thirdparty/SPTAG/Test/src/CommonHelperTest.cpp deleted file mode 100644 index 17015642cc..0000000000 --- a/core/src/index/thirdparty/SPTAG/Test/src/CommonHelperTest.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Test.h" -#include "inc/Helper/CommonHelper.h" - -#include - -BOOST_AUTO_TEST_SUITE(CommonHelperTest) - - -BOOST_AUTO_TEST_CASE(ToLowerInPlaceTest) -{ - auto runTestCase = [](std::string p_input, const std::string& p_expected) - { - SPTAG::Helper::StrUtils::ToLowerInPlace(p_input); - BOOST_CHECK(p_input == p_expected); - }; - - runTestCase("abc", "abc"); - runTestCase("ABC", "abc"); - runTestCase("abC", "abc"); - runTestCase("Upper-Case", "upper-case"); - runTestCase("123!-=aBc", "123!-=abc"); -} - - -BOOST_AUTO_TEST_CASE(SplitStringTest) -{ - std::string input("seg1 seg2 seg3 seg4"); - - const auto& segs = SPTAG::Helper::StrUtils::SplitString(input, " "); - BOOST_CHECK(segs.size() == 4); - BOOST_CHECK(segs[0] == "seg1"); - BOOST_CHECK(segs[1] == "seg2"); - BOOST_CHECK(segs[2] == "seg3"); - BOOST_CHECK(segs[3] == "seg4"); -} - - -BOOST_AUTO_TEST_CASE(FindTrimmedSegmentTest) -{ - using namespace SPTAG::Helper::StrUtils; - std::string input("\t Space End \r\n\t"); - - const auto& pos = FindTrimmedSegment(input.c_str(), - input.c_str() + input.size(), - [](char p_val)->bool - { - return std::isspace(p_val) > 0; - }); - - BOOST_CHECK(pos.first == input.c_str() + 2); - BOOST_CHECK(pos.second == input.c_str() + 13); -} - - -BOOST_AUTO_TEST_CASE(StartsWithTest) -{ - using namespace SPTAG::Helper::StrUtils; - - BOOST_CHECK(StartsWith("Abcd", "A")); - BOOST_CHECK(StartsWith("Abcd", "Ab")); - BOOST_CHECK(StartsWith("Abcd", "Abc")); - BOOST_CHECK(StartsWith("Abcd", "Abcd")); - - BOOST_CHECK(!StartsWith("Abcd", "a")); - BOOST_CHECK(!StartsWith("Abcd", "F")); - BOOST_CHECK(!StartsWith("Abcd", "AF")); - BOOST_CHECK(!StartsWith("Abcd", "AbF")); - BOOST_CHECK(!StartsWith("Abcd", "AbcF")); - BOOST_CHECK(!StartsWith("Abcd", "Abcde")); -} - - -BOOST_AUTO_TEST_CASE(StrEqualIgnoreCaseTest) -{ - using namespace SPTAG::Helper::StrUtils; - - BOOST_CHECK(StrEqualIgnoreCase("Abcd", "Abcd")); - BOOST_CHECK(StrEqualIgnoreCase("Abcd", "abcd")); - BOOST_CHECK(StrEqualIgnoreCase("Abcd", "abCD")); - BOOST_CHECK(StrEqualIgnoreCase("Abcd-123", "abcd-123")); - BOOST_CHECK(StrEqualIgnoreCase(" ZZZ", " zzz")); - - BOOST_CHECK(!StrEqualIgnoreCase("abcd", "abcd1")); - BOOST_CHECK(!StrEqualIgnoreCase("Abcd", " abcd")); - BOOST_CHECK(!StrEqualIgnoreCase("000", "OOO")); -} - - - -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Test/src/DistanceTest.cpp b/core/src/index/thirdparty/SPTAG/Test/src/DistanceTest.cpp deleted file mode 100644 index 97602a2a8d..0000000000 --- a/core/src/index/thirdparty/SPTAG/Test/src/DistanceTest.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include -#include "inc/Test.h" -#include "inc/Core/Common/DistanceUtils.h" - -template -static float ComputeCosineDistance(const T *pX, const T *pY, SPTAG::DimensionType length) { - float diff = 0; - const T* pEnd1 = pX + length; - while (pX < pEnd1) diff += (*pX++) * (*pY++); - return diff; -} - -template -static float ComputeL2Distance(const T *pX, const T *pY, SPTAG::DimensionType length) -{ - float diff = 0; - const T* pEnd1 = pX + length; - while (pX < pEnd1) { - float c1 = ((float)(*pX++) - (float)(*pY++)); diff += c1 * c1; - } - return diff; -} - -template -T random(int high = RAND_MAX, int low = 0) // Generates a random value. -{ - return (T)(low + float(high - low)*(std::rand()/static_cast(RAND_MAX + 1.0))); -} - -template -void test(int high) { - SPTAG::DimensionType dimension = random(256, 2); - T *X = new T[dimension], *Y = new T[dimension]; - BOOST_ASSERT(X != nullptr && Y != nullptr); - for (SPTAG::DimensionType i = 0; i < dimension; i++) { - X[i] = random(high, -high); - Y[i] = random(high, -high); - } - BOOST_CHECK_CLOSE_FRACTION(ComputeL2Distance(X, Y, dimension), SPTAG::COMMON::DistanceUtils::ComputeL2Distance(X, Y, dimension), 1e-5); - BOOST_CHECK_CLOSE_FRACTION(high*high - ComputeCosineDistance(X, Y, dimension), SPTAG::COMMON::DistanceUtils::ComputeCosineDistance(X, Y, dimension), 1e-5); - - delete[] X; - delete[] Y; -} - -BOOST_AUTO_TEST_SUITE(DistanceTest) - -BOOST_AUTO_TEST_CASE(TestDistanceComputation) -{ - test(1); - test(127); - test(32767); -} - -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Test/src/IniReaderTest.cpp b/core/src/index/thirdparty/SPTAG/Test/src/IniReaderTest.cpp deleted file mode 100644 index c5dd0baaf5..0000000000 --- a/core/src/index/thirdparty/SPTAG/Test/src/IniReaderTest.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Test.h" -#include "inc/Helper/SimpleIniReader.h" - -#include - -BOOST_AUTO_TEST_SUITE(IniReaderTest) - -BOOST_AUTO_TEST_CASE(IniReaderLoadTest) -{ - std::ofstream tmpIni("temp.ini"); - tmpIni << "[Common]" << std::endl; - tmpIni << "; Comment " << std::endl; - tmpIni << "Param1=1" << std::endl; - tmpIni << "Param2=Exp=2" << std::endl; - - tmpIni.close(); - - SPTAG::Helper::IniReader reader; - BOOST_CHECK(SPTAG::ErrorCode::Success == reader.LoadIniFile("temp.ini")); - - BOOST_CHECK(reader.DoesSectionExist("Common")); - BOOST_CHECK(reader.DoesParameterExist("Common", "Param1")); - BOOST_CHECK(reader.DoesParameterExist("Common", "Param2")); - - BOOST_CHECK(!reader.DoesSectionExist("NotExist")); - BOOST_CHECK(!reader.DoesParameterExist("NotExist", "Param1")); - BOOST_CHECK(!reader.DoesParameterExist("Common", "ParamNotExist")); - - BOOST_CHECK(1 == reader.GetParameter("Common", "Param1", 0)); - BOOST_CHECK(0 == reader.GetParameter("Common", "ParamNotExist", 0)); - - BOOST_CHECK(std::string("Exp=2") == reader.GetParameter("Common", "Param2", std::string())); - BOOST_CHECK(std::string("1") == reader.GetParameter("Common", "Param1", std::string())); - BOOST_CHECK(std::string() == reader.GetParameter("Common", "ParamNotExist", std::string())); -} - -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Test/src/StringConvertTest.cpp b/core/src/index/thirdparty/SPTAG/Test/src/StringConvertTest.cpp deleted file mode 100644 index fa457debe2..0000000000 --- a/core/src/index/thirdparty/SPTAG/Test/src/StringConvertTest.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/Test.h" -#include "inc/Helper/StringConvert.h" - -namespace -{ - namespace Local - { - - template - void TestConvertSuccCase(ValueType p_val, const char* p_valStr) - { - using namespace SPTAG::Helper::Convert; - - std::string str = ConvertToString(p_val); - if (nullptr != p_valStr) - { - BOOST_CHECK(str == p_valStr); - } - - ValueType val; - BOOST_CHECK(ConvertStringTo(str.c_str(), val)); - BOOST_CHECK(val == p_val); - } - - } -} - -BOOST_AUTO_TEST_SUITE(StringConvertTest) - -BOOST_AUTO_TEST_CASE(ConvertInt8) -{ - Local::TestConvertSuccCase(static_cast(-1), "-1"); - Local::TestConvertSuccCase(static_cast(0), "0"); - Local::TestConvertSuccCase(static_cast(3), "3"); - Local::TestConvertSuccCase(static_cast(100), "100"); -} - -BOOST_AUTO_TEST_CASE(ConvertInt16) -{ - Local::TestConvertSuccCase(static_cast(-1), "-1"); - Local::TestConvertSuccCase(static_cast(0), "0"); - Local::TestConvertSuccCase(static_cast(3), "3"); - Local::TestConvertSuccCase(static_cast(100), "100"); -} - -BOOST_AUTO_TEST_CASE(ConvertInt32) -{ - Local::TestConvertSuccCase(static_cast(-1), "-1"); - Local::TestConvertSuccCase(static_cast(0), "0"); - Local::TestConvertSuccCase(static_cast(3), "3"); - Local::TestConvertSuccCase(static_cast(100), "100"); -} - -BOOST_AUTO_TEST_CASE(ConvertInt64) -{ - Local::TestConvertSuccCase(static_cast(-1), "-1"); - Local::TestConvertSuccCase(static_cast(0), "0"); - Local::TestConvertSuccCase(static_cast(3), "3"); - Local::TestConvertSuccCase(static_cast(100), "100"); -} - -BOOST_AUTO_TEST_CASE(ConvertUInt8) -{ - Local::TestConvertSuccCase(static_cast(0), "0"); - Local::TestConvertSuccCase(static_cast(3), "3"); - Local::TestConvertSuccCase(static_cast(100), "100"); -} - -BOOST_AUTO_TEST_CASE(ConvertUInt16) -{ - Local::TestConvertSuccCase(static_cast(0), "0"); - Local::TestConvertSuccCase(static_cast(3), "3"); - Local::TestConvertSuccCase(static_cast(100), "100"); -} - -BOOST_AUTO_TEST_CASE(ConvertUInt32) -{ - Local::TestConvertSuccCase(static_cast(0), "0"); - Local::TestConvertSuccCase(static_cast(3), "3"); - Local::TestConvertSuccCase(static_cast(100), "100"); -} - -BOOST_AUTO_TEST_CASE(ConvertUInt64) -{ - Local::TestConvertSuccCase(static_cast(0), "0"); - Local::TestConvertSuccCase(static_cast(3), "3"); - Local::TestConvertSuccCase(static_cast(100), "100"); -} - -BOOST_AUTO_TEST_CASE(ConvertFloat) -{ - Local::TestConvertSuccCase(static_cast(-1), nullptr); - Local::TestConvertSuccCase(static_cast(0), nullptr); - Local::TestConvertSuccCase(static_cast(3), nullptr); - Local::TestConvertSuccCase(static_cast(100), nullptr); -} - -BOOST_AUTO_TEST_CASE(ConvertDouble) -{ - Local::TestConvertSuccCase(static_cast(-1), nullptr); - Local::TestConvertSuccCase(static_cast(0), nullptr); - Local::TestConvertSuccCase(static_cast(3), nullptr); - Local::TestConvertSuccCase(static_cast(100), nullptr); -} - -BOOST_AUTO_TEST_CASE(ConvertIndexAlgoType) -{ - Local::TestConvertSuccCase(SPTAG::IndexAlgoType::BKT, "BKT"); - Local::TestConvertSuccCase(SPTAG::IndexAlgoType::KDT, "KDT"); -} - -BOOST_AUTO_TEST_CASE(ConvertVectorValueType) -{ - Local::TestConvertSuccCase(SPTAG::VectorValueType::Float, "Float"); - Local::TestConvertSuccCase(SPTAG::VectorValueType::Int8, "Int8"); - Local::TestConvertSuccCase(SPTAG::VectorValueType::Int16, "Int16"); -} - -BOOST_AUTO_TEST_CASE(ConvertDistCalcMethod) -{ - Local::TestConvertSuccCase(SPTAG::DistCalcMethod::Cosine, "Cosine"); - Local::TestConvertSuccCase(SPTAG::DistCalcMethod::L2, "L2"); -} - -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Test/src/main.cpp b/core/src/index/thirdparty/SPTAG/Test/src/main.cpp deleted file mode 100644 index 7bf61ea119..0000000000 --- a/core/src/index/thirdparty/SPTAG/Test/src/main.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#define BOOST_TEST_MAIN -#define BOOST_TEST_MODULE Main -#include "inc/Test.h" - -#include -#include - -using namespace boost::unit_test; - -class SPTAGVisitor : public test_tree_visitor -{ -public: - void visit(test_case const& test) - { - std::string prefix(2, '\t'); - std::cout << prefix << "Case: " << test.p_name << std::endl; - } - - bool test_suite_start(test_suite const& suite) - { - std::string prefix(1, '\t'); - std::cout << prefix << "Suite: " << suite.p_name << std::endl; - return true; - } -}; - -struct GlobalFixture -{ - GlobalFixture() - { - SPTAGVisitor visitor; - traverse_test_tree(framework::master_test_suite(), visitor, false); - } - -}; - -BOOST_TEST_GLOBAL_FIXTURE(GlobalFixture); - diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/CLRCore.vcxproj b/core/src/index/thirdparty/SPTAG/Wrappers/CLRCore.vcxproj deleted file mode 100644 index efb4d0f259..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/CLRCore.vcxproj +++ /dev/null @@ -1,141 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {38ACBA6C-2E50-44D4-9A6D-DC735B56E38F} - v4.5.2 - ManagedCProj - CLRCore - 8.1 - - - - - DynamicLibrary - true - v140 - true - Unicode - - - DynamicLibrary - false - v140 - true - Unicode - - - DynamicLibrary - true - v140 - true - MultiByte - - - DynamicLibrary - false - v140 - true - MultiByte - - - - - - - - - - - - - - - - - - - - Microsoft.ANN.SPTAGManaged - .dll - $(SolutionDir)obj\$(Platform)_$(Configuration)\$(ProjectName)\ - $(ProjectDir);$(SolutionDir)AnnService\;$(IncludePath) - $(OutLibDir);$(LibraryPath) - $(OutAppDir) - - - - true - - - true - - - false - - - false - - - - Level3 - Disabled - _DEBUG;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - NotUsing - true - - - CoreLibrary.lib;%(AdditionalDependencies) - - - - - Level3 - NDEBUG;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - NotUsing - true - - - CoreLibrary.lib;%(AdditionalDependencies) - - - - - - - - - - - - - - - - - - {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942} - - - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/CLRCore.vcxproj.filters b/core/src/index/thirdparty/SPTAG/Wrappers/CLRCore.vcxproj.filters deleted file mode 100644 index c0c35e9683..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/CLRCore.vcxproj.filters +++ /dev/null @@ -1,32 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {ba4289c4-f872-4dbc-a57f-7b415614afb3} - - - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/CMakeLists.txt b/core/src/index/thirdparty/SPTAG/Wrappers/CMakeLists.txt deleted file mode 100644 index 514367978e..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/CMakeLists.txt +++ /dev/null @@ -1,171 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -find_package(Python2 COMPONENTS Development) -if (Python2_FOUND) - include_directories (${Python2_INCLUDE_DIRS}) - link_directories (${Python2_LIBRARY_DIRS}) - set (Python_INCLUDE_DIRS ${Python2_INCLUDE_DIRS}) - set (Python_LIBRARIES ${Python2_LIBRARIES}) - set (Python_FOUND true) -else() - find_package(Python3 COMPONENTS Development) - if (Python3_FOUND) - include_directories (${Python3_INCLUDE_DIRS}) - link_directories (${Python3_LIBRARY_DIRS}) - set (Python_INCLUDE_DIRS ${Python3_INCLUDE_DIRS}) - set (Python_LIBRARIES ${Python3_LIBRARIES}) - set (Python_FOUND true) - endif() -endif() - -if (Python_FOUND) - message (STATUS "Found Python.") - message (STATUS "Include Path: ${Python_INCLUDE_DIRS}") - message (STATUS "Library Path: ${Python_LIBRARIES}") - - if (WIN32) - set(PY_SUFFIX .pyd) - else() - set(PY_SUFFIX .so) - endif() - - execute_process(COMMAND swig -python -c++ -I${PROJECT_SOURCE_DIR}/Wrappers/inc -o ${PROJECT_SOURCE_DIR}/Wrappers/inc/CoreInterface_pwrap.cpp ${PROJECT_SOURCE_DIR}/Wrappers/inc/PythonCore.i) - execute_process(COMMAND swig -python -c++ -I${PROJECT_SOURCE_DIR}/Wrappers/inc -o ${PROJECT_SOURCE_DIR}/Wrappers/inc/ClientInterface_pwrap.cpp ${PROJECT_SOURCE_DIR}/Wrappers/inc/PythonClient.i) - - include_directories(${PYTHON_INCLUDE_PATH} ${PROJECT_SOURCE_DIR}/AnnService ${PROJECT_SOURCE_DIR}/Wrappers) - - file(GLOB CORE_HDR_FILES ${PROJECT_SOURCE_DIR}/Wrappers/inc/CoreInterface.h) - file(GLOB CORE_SRC_FILES ${PROJECT_SOURCE_DIR}/Wrappers/src/CoreInterface.cpp ${PROJECT_SOURCE_DIR}/Wrappers/inc/CoreInterface_pwrap.cpp) - add_library (_SPTAG SHARED ${CORE_SRC_FILES} ${CORE_HDR_FILES}) - set_target_properties(_SPTAG PROPERTIES PREFIX "" SUFFIX ${PY_SUFFIX}) - target_link_libraries(_SPTAG SPTAGLib ${Python_LIBRARIES}) - add_custom_command(TARGET _SPTAG POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/Wrappers/inc/SPTAG.py ${EXECUTABLE_OUTPUT_PATH}) - - file(GLOB CLIENT_HDR_FILES ${PROJECT_SOURCE_DIR}/Wrappers/inc/ClientInterface.h ${PROJECT_SOURCE_DIR}/AnnService/inc/Socket/*.h ${PROJECT_SOURCE_DIR}/AnnService/inc/Client/*.h) - file(GLOB CLIENT_SRC_FILES ${PROJECT_SOURCE_DIR}/Wrappers/src/ClientInterface.cpp ${PROJECT_SOURCE_DIR}/AnnService/src/Socket/*.cpp ${PROJECT_SOURCE_DIR}/AnnService/src/Client/*.cpp ${PROJECT_SOURCE_DIR}/Wrappers/inc/ClientInterface_pwrap.cpp) - add_library (_SPTAGClient SHARED ${CLIENT_SRC_FILES} ${CLIENT_HDR_FILES}) - set_target_properties(_SPTAGClient PROPERTIES PREFIX "" SUFFIX ${PY_SUFFIX}) - target_link_libraries(_SPTAGClient SPTAGLib ${Python_LIBRARIES} ${Boost_LIBRARIES}) - add_custom_command(TARGET _SPTAGClient POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/Wrappers/inc/SPTAGClient.py ${EXECUTABLE_OUTPUT_PATH}) - - install(TARGETS _SPTAG _SPTAGClient - RUNTIME DESTINATION bin - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib) - install(FILES ${PROJECT_SOURCE_DIR}/Wrappers/src/SPTAG.py ${PROJECT_SOURCE_DIR}/Wrappers/inc/SPTAGClient.py DESTINATION bin) -else() - message (STATUS "Could not find Python.") -endif() - -find_package(JNI) -if (JNI_FOUND) - include_directories (${JNI_INCLUDE_DIRS}) - link_directories (${JNI_LIBRARY_DIRS}) - message (STATUS "Found JNI.") - message (STATUS "Include Path: ${JNI_INCLUDE_DIRS}") - message (STATUS "Library Path: ${JNI_LIBRARIES}") - - if (WIN32) - set (JAVA_SUFFIX .dll) - else() - set (JAVA_SUFFIX .so) - endif() - - execute_process(COMMAND swig -java -c++ -I${PROJECT_SOURCE_DIR}/Wrappers/inc -o ${PROJECT_SOURCE_DIR}/Wrappers/inc/CoreInterface_jwrap.cpp ${PROJECT_SOURCE_DIR}/Wrappers/inc/JavaCore.i) - execute_process(COMMAND swig -java -c++ -I${PROJECT_SOURCE_DIR}/Wrappers/inc -o ${PROJECT_SOURCE_DIR}/Wrappers/inc/ClientInterface_jwrap.cpp ${PROJECT_SOURCE_DIR}/Wrappers/inc/JavaClient.i) - - include_directories(${JNI_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/AnnService ${PROJECT_SOURCE_DIR}/Wrappers) - - file(GLOB CORE_HDR_FILES ${PROJECT_SOURCE_DIR}/Wrappers/inc/CoreInterface.h) - file(GLOB CORE_SRC_FILES ${PROJECT_SOURCE_DIR}/Wrappers/src/CoreInterface.cpp ${PROJECT_SOURCE_DIR}/Wrappers/inc/CoreInterface_jwrap.cpp) - add_library (JAVASPTAG SHARED ${CORE_SRC_FILES} ${CORE_HDR_FILES}) - set_target_properties(JAVASPTAG PROPERTIES SUFFIX ${JAVA_SUFFIX}) - target_link_libraries(JAVASPTAG SPTAGLib ${JNI_LIBRARIES}) - - file(GLOB CLIENT_HDR_FILES ${PROJECT_SOURCE_DIR}/Wrappers/inc/ClientInterface.h ${PROJECT_SOURCE_DIR}/AnnService/inc/Socket/*.h ${PROJECT_SOURCE_DIR}/AnnService/inc/Client/*.h) - file(GLOB CLIENT_SRC_FILES ${PROJECT_SOURCE_DIR}/Wrappers/src/ClientInterface.cpp ${PROJECT_SOURCE_DIR}/AnnService/src/Socket/*.cpp ${PROJECT_SOURCE_DIR}/AnnService/src/Client/*.cpp ${PROJECT_SOURCE_DIR}/Wrappers/inc/ClientInterface_jwrap.cpp) - add_library (JAVASPTAGClient SHARED ${CLIENT_SRC_FILES} ${CLIENT_HDR_FILES}) - set_target_properties(JAVASPTAGClient PROPERTIES SUFFIX ${JAVA_SUFFIX}) - target_link_libraries(JAVASPTAGClient SPTAGLib ${JNI_LIBRARIES} ${Boost_LIBRARIES}) - - file(GLOB JAVA_FILES ${PROJECT_SOURCE_DIR}/Wrappers/inc/*.java) - foreach(JAVA_FILE ${JAVA_FILES}) - message (STATUS "Add copy post-command for file " ${JAVA_FILE}) - add_custom_command(TARGET JAVASPTAGClient POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${JAVA_FILE} ${EXECUTABLE_OUTPUT_PATH}) - endforeach(JAVA_FILE) - - install(TARGETS JAVASPTAG JAVASPTAGClient - RUNTIME DESTINATION bin - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib) - install(FILES ${PROJECT_SOURCE_DIR}/Wrappers/inc/*.java DESTINATION bin) -else() - message (STATUS "Could not find JNI.") -endif() - -if (WIN32) - if (${PROJECTNAME_ARCHITECTURE} MATCHES "x64") - set (csharp_dotnet_framework_hints "$ENV{windir}\\Microsoft.NET\\Framework64") - else() - set (csharp_dotnet_framework_hints "$ENV{windir}\\Microsoft.NET\\Framework") - endif() - - file(GLOB_RECURSE csharp_dotnet_executables ${csharp_dotnet_framework_hints}/csc.exe) - list(SORT csharp_dotnet_executables) - list(REVERSE csharp_dotnet_executables) - foreach (csharp_dotnet_executable ${csharp_dotnet_executables}) - if (NOT DEFINED DOTNET_FOUND) - string(REPLACE "${csharp_dotnet_framework_hints}/" "" csharp_dotnet_version_temp ${csharp_dotnet_executable}) - string(REPLACE "/csc.exe" "" csharp_dotnet_version_temp ${csharp_dotnet_version_temp}) - - set (DOTNET_EXECUTABLE_VERSION "${csharp_dotnet_version_temp}" CACHE STRING "C# .NET compiler version" FORCE) - set (DOTNET_FOUND ${csharp_dotnet_executable}) - endif() - endforeach(csharp_dotnet_executable) -else() - FIND_PROGRAM(DOTNET_FOUND dotnet) -endif() - -if (DOTNET_FOUND) - message (STATUS "Found dotnet.") - message (STATUS "DOTNET_EXECUTABLE: " ${DOTNET_FOUND}) - - if (WIN32) - set (CSHARP_SUFFIX .dll) - else() - set (CSHARP_SUFFIX .so) - endif() - - execute_process(COMMAND swig -csharp -c++ -I${PROJECT_SOURCE_DIR}/Wrappers/inc -o ${PROJECT_SOURCE_DIR}/Wrappers/inc/CoreInterface_cwrap.cpp ${PROJECT_SOURCE_DIR}/Wrappers/inc/CsharpCore.i) - execute_process(COMMAND swig -csharp -c++ -I${PROJECT_SOURCE_DIR}/Wrappers/inc -o ${PROJECT_SOURCE_DIR}/Wrappers/inc/ClientInterface_cwrap.cpp ${PROJECT_SOURCE_DIR}/Wrappers/inc/CsharpClient.i) - - include_directories(${PROJECT_SOURCE_DIR}/AnnService ${PROJECT_SOURCE_DIR}/Wrappers) - - file(GLOB CORE_HDR_FILES ${PROJECT_SOURCE_DIR}/Wrappers/inc/CoreInterface.h) - file(GLOB CORE_SRC_FILES ${PROJECT_SOURCE_DIR}/Wrappers/src/CoreInterface.cpp ${PROJECT_SOURCE_DIR}/Wrappers/inc/CoreInterface_cwrap.cpp) - add_library (CSHARPSPTAG SHARED ${CORE_SRC_FILES} ${CORE_HDR_FILES}) - set_target_properties(CSHARPSPTAG PROPERTIES SUFFIX ${CSHARP_SUFFIX}) - target_link_libraries(CSHARPSPTAG SPTAGLib) - - file(GLOB CLIENT_HDR_FILES ${PROJECT_SOURCE_DIR}/Wrappers/inc/ClientInterface.h ${PROJECT_SOURCE_DIR}/AnnService/inc/Socket/*.h ${PROJECT_SOURCE_DIR}/AnnService/inc/Client/*.h) - file(GLOB CLIENT_SRC_FILES ${PROJECT_SOURCE_DIR}/Wrappers/src/ClientInterface.cpp ${PROJECT_SOURCE_DIR}/AnnService/src/Socket/*.cpp ${PROJECT_SOURCE_DIR}/AnnService/src/Client/*.cpp ${PROJECT_SOURCE_DIR}/Wrappers/inc/ClientInterface_cwrap.cpp) - add_library (CSHARPSPTAGClient SHARED ${CLIENT_SRC_FILES} ${CLIENT_HDR_FILES}) - set_target_properties(CSHARPSPTAGClient PROPERTIES SUFFIX ${CSHARP_SUFFIX}) - target_link_libraries(CSHARPSPTAGClient SPTAGLib ${Boost_LIBRARIES}) - - file(GLOB CSHARP_FILES ${PROJECT_SOURCE_DIR}/Wrappers/inc/*.cs) - foreach(CSHARP_FILE ${CSHARP_FILES}) - message (STATUS "Add copy post-command for file " ${CSHARP_FILE}) - add_custom_command(TARGET CSHARPSPTAGClient POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CSHARP_FILE} ${EXECUTABLE_OUTPUT_PATH}) - endforeach(CSHARP_FILE) - - install(TARGETS CSHARPSPTAG CSHARPSPTAGClient - RUNTIME DESTINATION bin - ARCHIVE DESTINATION lib - LIBRARY DESTINATION lib) - install(FILES ${PROJECT_SOURCE_DIR}/Wrappers/inc/*.cs DESTINATION bin) -else() - message (STATUS "Could not find C#.") -endif() - diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/CsharpClient.vcxproj b/core/src/index/thirdparty/SPTAG/Wrappers/CsharpClient.vcxproj deleted file mode 100644 index d7d17102d8..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/CsharpClient.vcxproj +++ /dev/null @@ -1,191 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {363BA3BB-75C4-4CC7-AECB-28C7534B3710} - CsharpClient - 8.1 - - - - - DynamicLibrary - true - v140 - MultiByte - - - DynamicLibrary - false - v140 - true - MultiByte - - - DynamicLibrary - true - v140 - MultiByte - - - DynamicLibrary - false - v140 - true - MultiByte - - - - - - - - - - - - - - - - - - - - CSHARPSPTAGClient - .dll - $(SolutionDir)obj\$(Platform)_$(Configuration)\$(ProjectName)\ - $(ProjectDir);$(SolutionDir)AnnService\;$(IncludePath) - $(OutLibDir);$(LibraryPath) - $(OutAppDir) - - - false - - - - CoreLibrary.lib;SocketLib.lib;%(AdditionalDependencies) - - - - - %(AdditionalIncludeDirectories) - - - - - Level3 - MaxSpeed - true - true - true - true - _WINDLL;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - - - true - true - - - - - Level3 - Disabled - true - true - - - - - Level3 - Disabled - true - true - _WINDLL;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - Guard - ProgramDatabase - - - /guard:cf %(AdditionalOptions) - - - - - Level3 - MaxSpeed - true - true - true - true - - - true - true - - - - - - - - - - - - - false - false - false - false - - - - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/CsharpClient.vcxproj.filters b/core/src/index/thirdparty/SPTAG/Wrappers/CsharpClient.vcxproj.filters deleted file mode 100644 index 589c50014d..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/CsharpClient.vcxproj.filters +++ /dev/null @@ -1,41 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - - - Resource Files - - - Resource Files - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/CsharpCore.vcxproj b/core/src/index/thirdparty/SPTAG/Wrappers/CsharpCore.vcxproj deleted file mode 100644 index e809d8b901..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/CsharpCore.vcxproj +++ /dev/null @@ -1,134 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {1896C009-AD46-4A70-B83C-4652A7F37503} - CsharpCore - 8.1 - - - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - DynamicLibrary - true - v140 - MultiByte - - - DynamicLibrary - false - v140 - true - MultiByte - - - - - - - - - - - - - - - - - - - - CSHARPSPTAG - .dll - $(SolutionDir)obj\$(Platform)_$(Configuration)\$(ProjectName)\ - $(ProjectDir);$(SolutionDir)AnnService\;$(IncludePath) - $(OutLibDir);$(LibraryPath) - $(OutAppDir) - - - false - - - - CoreLibrary.lib;%(AdditionalDependencies) - - - - - _WINDLL;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - %(AdditionalIncludeDirectories) - Guard - ProgramDatabase - _WINDLL;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - - - /guard:cf %(AdditionalOptions) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/CsharpCore.vcxproj.filters b/core/src/index/thirdparty/SPTAG/Wrappers/CsharpCore.vcxproj.filters deleted file mode 100644 index 51b1ec0ce6..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/CsharpCore.vcxproj.filters +++ /dev/null @@ -1,40 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {ba4289c4-f872-4dbc-a57f-7b415614afb3} - - - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - - - Resources - - - Resources - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/JavaClient.vcxproj b/core/src/index/thirdparty/SPTAG/Wrappers/JavaClient.vcxproj deleted file mode 100644 index 2ee36ac620..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/JavaClient.vcxproj +++ /dev/null @@ -1,191 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {8866BF98-AA2E-450F-9F33-083E007CCA74} - JavaClient - 8.1 - - - - - DynamicLibrary - true - v140 - MultiByte - - - DynamicLibrary - false - v140 - true - MultiByte - - - DynamicLibrary - true - v140 - MultiByte - - - DynamicLibrary - false - v140 - true - MultiByte - - - - - - - - - - - - - - - - - - - - JAVASPTAGClient - .dll - $(SolutionDir)obj\$(Platform)_$(Configuration)\$(ProjectName)\ - $(ProjectDir);$(SolutionDir)AnnService\;$(IncludePath) - $(OutLibDir);$(LibraryPath) - $(OutAppDir) - - - false - - - - $(JavaLib);CoreLibrary.lib;SocketLib.lib;%(AdditionalDependencies) - - - - - $(JavaIncDir);%(AdditionalIncludeDirectories) - - - - - Level3 - MaxSpeed - true - true - true - true - _WINDLL;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - - - true - true - - - - - Level3 - Disabled - true - true - - - - - Level3 - Disabled - true - true - _WINDLL;_SCL_SECURE_NO_WARNINGS;SWIG_JAVA_INTERPRETER_NO_DEBUG;%(PreprocessorDefinitions) - Guard - ProgramDatabase - - - /guard:cf %(AdditionalOptions) - - - - - Level3 - MaxSpeed - true - true - true - true - - - true - true - - - - - - - - - - - - - false - false - false - false - - - - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/JavaClient.vcxproj.filters b/core/src/index/thirdparty/SPTAG/Wrappers/JavaClient.vcxproj.filters deleted file mode 100644 index 0d047923aa..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/JavaClient.vcxproj.filters +++ /dev/null @@ -1,41 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - - - Resource Files - - - Resource Files - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/JavaCore.vcxproj b/core/src/index/thirdparty/SPTAG/Wrappers/JavaCore.vcxproj deleted file mode 100644 index f15c0e005f..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/JavaCore.vcxproj +++ /dev/null @@ -1,134 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {93FEB26B-965E-4157-8BE5-052F5CA112BB} - JavaCore - 8.1 - - - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - DynamicLibrary - true - v140 - MultiByte - - - DynamicLibrary - false - v140 - true - MultiByte - - - - - - - - - - - - - - - - - - - - JAVASPTAG - .dll - $(SolutionDir)obj\$(Platform)_$(Configuration)\$(ProjectName)\ - $(ProjectDir);$(SolutionDir)AnnService\;$(IncludePath) - $(OutLibDir);$(LibraryPath) - $(OutAppDir) - - - false - - - - $(JavaLib);CoreLibrary.lib;%(AdditionalDependencies) - - - - - _WINDLL;SWIG_JAVA_INTERPRETER_NO_DEBUG;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - $(JavaIncDir);%(AdditionalIncludeDirectories) - Guard - ProgramDatabase - _WINDLL;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - - - /guard:cf %(AdditionalOptions) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/JavaCore.vcxproj.filters b/core/src/index/thirdparty/SPTAG/Wrappers/JavaCore.vcxproj.filters deleted file mode 100644 index 851552684d..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/JavaCore.vcxproj.filters +++ /dev/null @@ -1,40 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {ba4289c4-f872-4dbc-a57f-7b415614afb3} - - - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - - - Resources - - - Resources - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/PythonClient.vcxproj b/core/src/index/thirdparty/SPTAG/Wrappers/PythonClient.vcxproj deleted file mode 100644 index 5cf2c2a9cf..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/PythonClient.vcxproj +++ /dev/null @@ -1,189 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {9B014CF6-E3FB-4BD4-B3B1-D26297BB31AA} - PythonClient - 8.1 - - - - - DynamicLibrary - true - v140 - MultiByte - - - DynamicLibrary - false - v140 - true - MultiByte - - - DynamicLibrary - true - v140 - MultiByte - - - DynamicLibrary - false - v140 - true - MultiByte - - - - - - - - - - - - - - - - - - - - _SPTAGClient - .pyd - $(SolutionDir)obj\$(Platform)_$(Configuration)\$(ProjectName)\ - $(ProjectDir);$(SolutionDir)AnnService\;$(IncludePath) - $(OutLibDir);$(LibraryPath) - $(OutAppDir) - - - false - - - - CoreLibrary.lib;SocketLib.lib;$(SolutionDir)packages\python2.2.7.15\tools\libs\python27.lib;%(AdditionalDependencies) - - - - - $(SolutionDir)packages\python2.2.7.15\tools\include\;%(AdditionalIncludeDirectories) - - - - - Level3 - MaxSpeed - true - true - true - true - _WINDLL;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - - - true - true - - - - - Level3 - Disabled - true - true - - - - - Level3 - Disabled - true - true - _WINDLL;_SCL_SECURE_NO_WARNINGS;SWIG_PYTHON_INTERPRETER_NO_DEBUG;%(PreprocessorDefinitions) - Guard - ProgramDatabase - - - /guard:cf %(AdditionalOptions) - - - - - Level3 - MaxSpeed - true - true - true - true - - - true - true - - - - - - - - - - - - - false - false - false - false - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/PythonClient.vcxproj.filters b/core/src/index/thirdparty/SPTAG/Wrappers/PythonClient.vcxproj.filters deleted file mode 100644 index 84c71f0977..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/PythonClient.vcxproj.filters +++ /dev/null @@ -1,41 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - - - Resource Files - - - Resource Files - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/PythonCore.vcxproj b/core/src/index/thirdparty/SPTAG/Wrappers/PythonCore.vcxproj deleted file mode 100644 index 7555ba97f4..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/PythonCore.vcxproj +++ /dev/null @@ -1,132 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {AF31947C-0495-42FE-A1AD-8F0DA2A679C7} - PythonCore - 8.1 - - - - - Application - true - v140 - MultiByte - - - Application - false - v140 - true - MultiByte - - - DynamicLibrary - true - v140 - MultiByte - - - DynamicLibrary - false - v140 - true - MultiByte - - - - - - - - - - - - - - - - - - - - _SPTAG - .pyd - $(SolutionDir)obj\$(Platform)_$(Configuration)\$(ProjectName)\ - $(ProjectDir);$(SolutionDir)AnnService\;$(IncludePath) - $(OutLibDir);$(LibraryPath) - $(OutAppDir) - - - false - - - - CoreLibrary.lib;$(SolutionDir)packages\python2.2.7.15\tools\libs\python27.lib;%(AdditionalDependencies) - - - - - _WINDLL;SWIG_PYTHON_INTERPRETER_NO_DEBUG;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - $(SolutionDir)packages\python2.2.7.15\tools\include\;%(AdditionalIncludeDirectories) - Guard - ProgramDatabase - _WINDLL;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) - - - /guard:cf %(AdditionalOptions) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/PythonCore.vcxproj.filters b/core/src/index/thirdparty/SPTAG/Wrappers/PythonCore.vcxproj.filters deleted file mode 100644 index 8d0ee1d7b9..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/PythonCore.vcxproj.filters +++ /dev/null @@ -1,40 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {ba4289c4-f872-4dbc-a57f-7b415614afb3} - - - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - - - Resources - - - Resources - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/PythonCore.vcxproj.user b/core/src/index/thirdparty/SPTAG/Wrappers/PythonCore.vcxproj.user deleted file mode 100644 index abe8dd8961..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/PythonCore.vcxproj.user +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/inc/CLRCoreInterface.h b/core/src/index/thirdparty/SPTAG/Wrappers/inc/CLRCoreInterface.h deleted file mode 100644 index 1a273ba8d7..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/inc/CLRCoreInterface.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#pragma once -#include "ManagedObject.h" -#include "inc/Core/VectorIndex.h" - -using namespace System; - -namespace Microsoft -{ - namespace ANN - { - namespace SPTAGManaged - { - - public ref class BasicResult : - public ManagedObject - { - public: - BasicResult(SPTAG::BasicResult* p_instance) : ManagedObject(p_instance) - { - } - - property int VID - { - public: - int get() - { - return m_Instance->VID; - } - private: - void set(int p_vid) - { - } - } - - property float Dist - { - public: - float get() - { - return m_Instance->Dist; - } - private: - void set(float p_dist) - { - } - } - - property array^ Meta - { - public: - array^ get() - { - array^ buf = gcnew array(m_Instance->Meta.Length()); - Marshal::Copy((IntPtr)m_Instance->Meta.Data(), buf, 0, (int)m_Instance->Meta.Length()); - return buf; - } - private: - void set(array^ p_meta) - { - } - } - }; - - public ref class AnnIndex : - public ManagedObject> - { - public: - AnnIndex(std::shared_ptr p_index); - - AnnIndex(String^ p_algoType, String^ p_valueType, int p_dimension); - - void SetBuildParam(String^ p_name, String^ p_value); - - void SetSearchParam(String^ p_name, String^ p_value); - - bool Build(array^ p_data, int p_num); - - bool BuildWithMetaData(array^ p_data, array^ p_meta, int p_num, bool p_withMetaIndex); - - array^ Search(array^ p_data, int p_resultNum); - - array^ SearchWithMetaData(array^ p_data, int p_resultNum); - - bool Save(String^ p_saveFile); - - array^>^ Dump(); - - bool Add(array^ p_data, int p_num); - - bool AddWithMetaData(array^ p_data, array^ p_meta, int p_num); - - bool Delete(array^ p_data, int p_num); - - bool DeleteByMetaData(array^ p_meta); - - static AnnIndex^ Load(String^ p_loaderFile); - - static AnnIndex^ Load(array^>^ p_index); - - static bool Merge(String^ p_indexFilePath1, String^ p_indexFilePath2); - - private: - - int m_dimension; - - size_t m_inputVectorSize; - }; - } - } -} diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/inc/ClientInterface.h b/core/src/index/thirdparty/SPTAG/Wrappers/inc/ClientInterface.h deleted file mode 100644 index 94d46bcad3..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/inc/ClientInterface.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_PW_CLIENTINTERFACE_H_ -#define _SPTAG_PW_CLIENTINTERFACE_H_ - -#include "TransferDataType.h" -#include "inc/Socket/Client.h" -#include "inc/Socket/ResourceManager.h" - -#include -#include -#include - -class AnnClient -{ -public: - AnnClient(const char* p_serverAddr, const char* p_serverPort); - - ~AnnClient(); - - void SetTimeoutMilliseconds(int p_timeout); - - void SetSearchParam(const char* p_name, const char* p_value); - - void ClearSearchParam(); - - std::shared_ptr Search(ByteArray p_data, int p_resultNum, const char* p_valueType, bool p_withMetaData); - - bool IsConnected() const; - -private: - std::string CreateSearchQuery(const ByteArray& p_data, - int p_resultNum, - bool p_extractMetadata, - SPTAG::VectorValueType p_valueType); - - SPTAG::Socket::PacketHandlerMapPtr GetHandlerMap(); - - void SearchResponseHanlder(SPTAG::Socket::ConnectionID p_localConnectionID, - SPTAG::Socket::Packet p_packet); - -private: - typedef std::function Callback; - - std::uint32_t m_timeoutInMilliseconds; - - std::string m_server; - - std::string m_port; - - std::unique_ptr m_socketClient; - - std::atomic m_connectionID; - - SPTAG::Socket::ResourceManager m_callbackManager; - - std::unordered_map m_params; - - std::mutex m_paramMutex; -}; - -#endif // _SPTAG_PW_CLIENTINTERFACE_H_ diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/inc/CoreInterface.h b/core/src/index/thirdparty/SPTAG/Wrappers/inc/CoreInterface.h deleted file mode 100644 index bc69874746..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/inc/CoreInterface.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_PW_COREINTERFACE_H_ -#define _SPTAG_PW_COREINTERFACE_H_ - -#include "TransferDataType.h" -#include "inc/Core/Common.h" -#include "inc/Core/VectorIndex.h" - -typedef int SizeType; -typedef int DimensionType; - -class AnnIndex -{ -public: - AnnIndex(DimensionType p_dimension); - - AnnIndex(const char* p_algoType, const char* p_valueType, DimensionType p_dimension); - - ~AnnIndex(); - - void SetBuildParam(const char* p_name, const char* p_value); - - void SetSearchParam(const char* p_name, const char* p_value); - - bool Build(ByteArray p_data, SizeType p_num); - - bool BuildWithMetaData(ByteArray p_data, ByteArray p_meta, SizeType p_num, bool p_withMetaIndex); - - std::shared_ptr Search(ByteArray p_data, int p_resultNum); - - std::shared_ptr SearchWithMetaData(ByteArray p_data, int p_resultNum); - - bool ReadyToServe() const; - - bool Save(const char* p_saveFile) const; - - bool Add(ByteArray p_data, SizeType p_num); - - bool AddWithMetaData(ByteArray p_data, ByteArray p_meta, SizeType p_num); - - bool Delete(ByteArray p_data, SizeType p_num); - - bool DeleteByMetaData(ByteArray p_meta); - - static AnnIndex Load(const char* p_loaderFile); - - static bool Merge(const char* p_indexFilePath1, const char* p_indexFilePath2); - -private: - AnnIndex(const std::shared_ptr& p_index); - - std::shared_ptr m_index; - - size_t m_inputVectorSize; - - DimensionType m_dimension; - - SPTAG::IndexAlgoType m_algoType; - - SPTAG::VectorValueType m_inputValueType; -}; - -#endif // _SPTAG_PW_COREINTERFACE_H_ diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/inc/CsharpClient.i b/core/src/index/thirdparty/SPTAG/Wrappers/inc/CsharpClient.i deleted file mode 100644 index 481627a97f..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/inc/CsharpClient.i +++ /dev/null @@ -1,16 +0,0 @@ -%module CSHARPSPTAGClient - -%{ -#include "inc/ClientInterface.h" -%} - -%include -%shared_ptr(AnnClient) -%shared_ptr(RemoteSearchResult) -%include "CsharpCommon.i" - -%{ -#define SWIG_FILE_WITH_INIT -%} - -%include "ClientInterface.h" diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/inc/CsharpCommon.i b/core/src/index/thirdparty/SPTAG/Wrappers/inc/CsharpCommon.i deleted file mode 100644 index 6251d6f245..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/inc/CsharpCommon.i +++ /dev/null @@ -1,125 +0,0 @@ -#ifdef SWIGCSHARP - -%{ - struct WrapperArray - { - void * _data; - size_t _size; - }; - - void deleteArrayOfWrapperArray(void* ptr) { - delete[] (WrapperArray*)ptr; - } -%} - -%pragma(csharp) imclasscode=%{ - [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] - public struct WrapperArray - { - public System.IntPtr _data; - public ulong _size; - public WrapperArray(System.IntPtr in_data, ulong in_size) { _data = in_data; _size = in_size; } - } -%} - -%apply void *VOID_INT_PTR { void * } -void deleteArrayOfWrapperArray(void* ptr); - -%typemap(ctype) ByteArray "WrapperArray" -%typemap(imtype) ByteArray "WrapperArray" -%typemap(cstype) ByteArray "byte[]" -%typemap(in) ByteArray { - $1.Set((std::uint8_t*)$input._data, $input._size, false); -} -%typemap(out) ByteArray { - $result._data = $1.Data(); - $result._size = $1.Length(); -} -%typemap(csin, - pre="unsafe { fixed(byte* ptr$csinput = $csinput) { $modulePINVOKE.WrapperArray temp$csinput = new $modulePINVOKE.WrapperArray( (System.IntPtr)ptr$csinput, (ulong)$csinput.LongLength );", - terminator="} }" - ) ByteArray %{ temp$csinput %} - -%typemap(csvarin) ByteArray %{ - set { - unsafe { fixed(byte* ptr$csinput = $csinput) - { - $modulePINVOKE.WrapperArray temp$csinput = new $modulePINVOKE.WrapperArray( (System.IntPtr)ptr$csinput, (ulong)$csinput.LongLength ); - $imcall; - } - } - } -%} - -%typemap(csout, excode=SWIGEXCODE) ByteArray %{ - $modulePINVOKE.WrapperArray data = $imcall;$excode - byte[] ret = new byte[data._size]; - System.Runtime.InteropServices.Marshal.Copy(data._data, ret, 0, (int)data._size); - return ret; -%} - -%typemap(csvarout) ByteArray %{ - get { - $modulePINVOKE.WrapperArray data = $imcall; - byte[] ret = new byte[data._size]; - System.Runtime.InteropServices.Marshal.Copy(data._data, ret, 0, (int)data._size); - return ret; - } -%} - -%typemap(ctype) std::shared_ptr "WrapperArray" -%typemap(imtype) std::shared_ptr "WrapperArray" -%typemap(cstype) std::shared_ptr "BasicResult[]" -%typemap(out) std::shared_ptr { - $result._data = new WrapperArray[$1->GetResultNum()]; - $result._size = $1->GetResultNum(); - for (int i = 0; i < $1->GetResultNum(); i++) - (((WrapperArray*)$result._data) + i)->_data = new BasicResult(*($1->GetResult(i))); -} -%typemap(csout, excode=SWIGEXCODE) std::shared_ptr { - $modulePINVOKE.WrapperArray data = $imcall; - BasicResult[] ret = new BasicResult[data._size]; - System.IntPtr ptr = data._data; - for (ulong i = 0; i < data._size; i++) { - $modulePINVOKE.WrapperArray arr = ($modulePINVOKE.WrapperArray)System.Runtime.InteropServices.Marshal.PtrToStructure(ptr, typeof($modulePINVOKE.WrapperArray)); - ret[i] = new BasicResult(arr._data, true); - ptr += sizeof($modulePINVOKE.WrapperArray); - } - $modulePINVOKE.deleteArrayOfWrapperArray(data._data); - $excode - return ret; -} - -%typemap(ctype) std::shared_ptr "WrapperArray" -%typemap(imtype) std::shared_ptr "WrapperArray" -%typemap(cstype) std::shared_ptr "BasicResult[]" -%typemap(out) std::shared_ptr { - int combinelen = 0; - int nodelen = (int)(($1->m_allIndexResults).size()); - for (int i = 0; i < nodelen; i++) { - combinelen += $1->m_allIndexResults[i].m_results.GetResultNum(); - } - $result._data = new WrapperArray[combinelen]; - $result._size = combinelen; - size_t copyed = 0; - for (int i = 0; i < nodelen; i++) { - auto& queryResult = $1->m_allIndexResults[i].m_results; - for (int j = 0; j < queryResult.GetResultNum(); j++) - (((WrapperArray*)$result._data) + copyed + j)->_data = new BasicResult(*(queryResult.GetResult(j))); - copyed += queryResult.GetResultNum(); - } -} -%typemap(csout, excode=SWIGEXCODE) std::shared_ptr { - $modulePINVOKE.WrapperArray data = $imcall; - BasicResult[] ret = new BasicResult[data._size]; - System.IntPtr ptr = data._data; - for (ulong i = 0; i < data._size; i++) { - $modulePINVOKE.WrapperArray arr = ($modulePINVOKE.WrapperArray)System.Runtime.InteropServices.Marshal.PtrToStructure(ptr, typeof($modulePINVOKE.WrapperArray)); - ret[i] = new BasicResult(arr._data, true); - ptr += sizeof($modulePINVOKE.WrapperArray); - } - $modulePINVOKE.deleteArrayOfWrapperArray(data._data); - $excode - return ret; -} -#endif diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/inc/CsharpCore.i b/core/src/index/thirdparty/SPTAG/Wrappers/inc/CsharpCore.i deleted file mode 100644 index 6434239b90..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/inc/CsharpCore.i +++ /dev/null @@ -1,17 +0,0 @@ -%module CSHARPSPTAG - -%{ -#include "inc/CoreInterface.h" -%} - -%include -%shared_ptr(AnnIndex) -%shared_ptr(QueryResult) -%include "CsharpCommon.i" - -%{ -#define SWIG_FILE_WITH_INIT -%} - -%include "CoreInterface.h" -%include "../../AnnService/inc/Core/SearchResult.h" diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/inc/JavaClient.i b/core/src/index/thirdparty/SPTAG/Wrappers/inc/JavaClient.i deleted file mode 100644 index 62a274e51a..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/inc/JavaClient.i +++ /dev/null @@ -1,16 +0,0 @@ -%module JAVASPTAGClient - -%{ -#include "inc/ClientInterface.h" -%} - -%include -%shared_ptr(AnnClient) -%shared_ptr(RemoteSearchResult) -%include "JavaCommon.i" - -%{ -#define SWIG_FILE_WITH_INIT -%} - -%include "ClientInterface.h" diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/inc/JavaCommon.i b/core/src/index/thirdparty/SPTAG/Wrappers/inc/JavaCommon.i deleted file mode 100644 index 366052d4f9..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/inc/JavaCommon.i +++ /dev/null @@ -1,60 +0,0 @@ -#ifdef SWIGJAVA - -%typemap(jni) ByteArray "jbyteArray" -%typemap(jtype) ByteArray "byte[]" -%typemap(jstype) ByteArray "byte[]" -%typemap(in) ByteArray { - $1.Set((std::uint8_t*)JCALL2(GetByteArrayElements, jenv, $input, 0), - JCALL1(GetArrayLength, jenv, $input), false); -} -%typemap(out) ByteArray { - $result = JCALL1(NewByteArray, jenv, $1.Length()); - JCALL4(SetByteArrayRegion, jenv, $result, 0, $1.Length(), (jbyte *)$1.Data()); -} -%typemap(javain) ByteArray "$javainput" -%typemap(javaout) ByteArray { return $jnicall; } - -%typemap(jni) std::shared_ptr "jobjectArray" -%typemap(jtype) std::shared_ptr "BasicResult[]" -%typemap(jstype) std::shared_ptr "BasicResult[]" -%typemap(out) std::shared_ptr { - jclass retClass = jenv->FindClass("BasicResult"); - int len = $1->GetResultNum(); - $result = jenv->NewObjectArray(len, retClass, NULL); - for (int i = 0; i < len; i++) { - auto& meta = $1->GetMetadata(i); - jbyteArray bptr = jenv->NewByteArray(meta.Length()); - jenv->SetByteArrayRegion(bptr, 0, meta.Length(), (jbyte *)meta.Data()); - jenv->SetObjectArrayElement(jresult, i, jenv->NewObject(retClass, jenv->GetMethodID(retClass, "", "(IF[B)V"), (jint)($1->GetResult(i)->VID), (jfloat)($1->GetResult(i)->Dist), bptr)); - } -} -%typemap(javaout) std::shared_ptr { return $jnicall; } - -%typemap(jni) std::shared_ptr "jobjectArray" -%typemap(jtype) std::shared_ptr "BasicResult[]" -%typemap(jstype) std::shared_ptr "BasicResult[]" -%typemap(out) std::shared_ptr { - int combinelen = 0; - int nodelen = (int)(($1->m_allIndexResults).size()); - for (int i = 0; i < nodelen; i++) { - combinelen += $1->m_allIndexResults[i].m_results.GetResultNum(); - } - jclass retClass = jenv->FindClass("BasicResult"); - $result = jenv->NewObjectArray(combinelen, retClass, NULL); - int id = 0; - for (int i = 0; i < nodelen; i++) { - for (int j = 0; j < $1->m_allIndexResults[i].m_results.GetResultNum(); j++) { - auto& ptr = $1->m_allIndexResults[i].m_results; - auto& meta = ptr.GetMetadata(j); - jbyteArray bptr = jenv->NewByteArray(meta.Length()); - jenv->SetByteArrayRegion(bptr, 0, meta.Length(), (jbyte *)meta.Data()); - jenv->SetObjectArrayElement(jresult, id, jenv->NewObject(retClass, jenv->GetMethodID(retClass, "", "(IF[B)V"), (jint)(ptr.GetResult(j)->VID), (jfloat)(ptr.GetResult(j)->Dist), bptr)); - id++; - } - } -} -%typemap(javaout) std::shared_ptr { - return $jnicall; -} - -#endif diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/inc/JavaCore.i b/core/src/index/thirdparty/SPTAG/Wrappers/inc/JavaCore.i deleted file mode 100644 index 78d9dd72e3..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/inc/JavaCore.i +++ /dev/null @@ -1,17 +0,0 @@ -%module JAVASPTAG - -%{ -#include "inc/CoreInterface.h" -%} - -%include -%shared_ptr(AnnIndex) -%shared_ptr(QueryResult) -%include "JavaCommon.i" - -%{ -#define SWIG_FILE_WITH_INIT -%} - -%include "CoreInterface.h" -%include "../../AnnService/inc/Core/SearchResult.h" diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/inc/ManagedObject.h b/core/src/index/thirdparty/SPTAG/Wrappers/inc/ManagedObject.h deleted file mode 100644 index 266d84b440..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/inc/ManagedObject.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#pragma once -#include "inc/Helper/StringConvert.h" - -using namespace System; -using namespace System::Runtime::InteropServices; - -namespace Microsoft -{ - namespace ANN - { - namespace SPTAGManaged - { - /// - /// hold a pointer to an umnanaged object from the core project - /// - template - public ref class ManagedObject - { - protected: - T* m_Instance; - - public: - ManagedObject(T* instance) - :m_Instance(instance) - { - } - - ManagedObject(T& instance) - { - m_Instance = new T(instance); - } - - /// - /// destructor, which is called whenever delete an object with delete keyword - /// - virtual ~ManagedObject() - { - if (m_Instance != nullptr) - { - delete m_Instance; - } - } - - /// - /// finalizer which is called by Garbage Collector whenever it destroys the wrapper object. - /// - !ManagedObject() - { - if (m_Instance != nullptr) - { - delete m_Instance; - } - } - - T* GetInstance() - { - return m_Instance; - } - - static const char* string_to_char_array(String^ string) - { - const char* str = (const char*)(Marshal::StringToHGlobalAnsi(string)).ToPointer(); - return str; - } - - template - static T string_to(String^ string) - { - T data; - SPTAG::Helper::Convert::ConvertStringTo(string_to_char_array(string), data); - return data; - } - }; - } - } -} - diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/inc/PythonClient.i b/core/src/index/thirdparty/SPTAG/Wrappers/inc/PythonClient.i deleted file mode 100644 index a70e2fdeb7..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/inc/PythonClient.i +++ /dev/null @@ -1,16 +0,0 @@ -%module SPTAGClient - -%{ -#include "inc/ClientInterface.h" -%} - -%include -%shared_ptr(AnnClient) -%shared_ptr(RemoteSearchResult) -%include "PythonCommon.i" - -%{ -#define SWIG_FILE_WITH_INIT -%} - -%include "ClientInterface.h" \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/inc/PythonCommon.i b/core/src/index/thirdparty/SPTAG/Wrappers/inc/PythonCommon.i deleted file mode 100644 index 7b10d50b99..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/inc/PythonCommon.i +++ /dev/null @@ -1,117 +0,0 @@ -#ifdef SWIGPYTHON - -%typemap(out) std::shared_ptr -%{ - { - $result = PyTuple_New(3); - int resNum = $1->GetResultNum(); - auto dstVecIDs = PyList_New(resNum); - auto dstVecDists = PyList_New(resNum); - auto dstMetadata = PyList_New(resNum); - int i = 0; - for (const auto& res : *($1)) - { - PyList_SetItem(dstVecIDs, i, PyInt_FromLong(res.VID)); - PyList_SetItem(dstVecDists, i, PyFloat_FromDouble(res.Dist)); - i++; - } - - if ($1->WithMeta()) - { - for (i = 0; i < resNum; ++i) - { - const auto& metadata = $1->GetMetadata(i); - PyList_SetItem(dstMetadata, i, PyBytes_FromStringAndSize(reinterpret_cast(metadata.Data()), - metadata.Length())); - } - } - - PyTuple_SetItem($result, 0, dstVecIDs); - PyTuple_SetItem($result, 1, dstVecDists); - PyTuple_SetItem($result, 2, dstMetadata); - } -%} - -%typemap(out) std::shared_ptr -%{ - { - $result = PyTuple_New(3); - auto dstVecIDs = PyList_New(0); - auto dstVecDists = PyList_New(0); - auto dstMetadata = PyList_New(0); - for (const auto& indexRes : $1->m_allIndexResults) - { - for (const auto& res : indexRes.m_results) - { - PyList_Append(dstVecIDs, PyInt_FromLong(res.VID)); - PyList_Append(dstVecDists, PyFloat_FromDouble(res.Dist)); - } - - if (indexRes.m_results.WithMeta()) - { - for (int i = 0; i < indexRes.m_results.GetResultNum(); ++i) - { - const auto& metadata = indexRes.m_results.GetMetadata(i); - PyList_Append(dstMetadata, PyBytes_FromStringAndSize(reinterpret_cast(metadata.Data()), - metadata.Length())); - } - } - } - PyTuple_SetItem($result, 0, dstVecIDs); - PyTuple_SetItem($result, 1, dstVecDists); - PyTuple_SetItem($result, 2, dstMetadata); - } -%} - - -%{ -struct PyBufferHolder -{ - PyBufferHolder() : shouldRelease(false) { } - - ~PyBufferHolder() - { - if (shouldRelease) - { - PyBuffer_Release(&buff); - } - } - - Py_buffer buff; - - bool shouldRelease; -}; -%} - -%typemap(in) ByteArray (PyBufferHolder bufferHolder) -%{ - if (PyBytes_Check($input)) - { - $1 = SPTAG::ByteArray((std::uint8_t*)PyBytes_AsString($input), PyBytes_Size($input), false); - } - else if (PyObject_CheckBuffer($input)) - { - if (PyObject_GetBuffer($input, &bufferHolder.buff, PyBUF_SIMPLE | PyBUF_C_CONTIGUOUS) == -1) - { - PyErr_SetString(PyExc_ValueError, "Failed get buffer."); - return NULL; - } - - bufferHolder.shouldRelease = true; - $1 = SPTAG::ByteArray((std::uint8_t*)bufferHolder.buff.buf, bufferHolder.buff.len, false); - } -#if (PY_VERSION_HEX >= 0x03030000) - else if (PyUnicode_Check($input)) - { - $1 = SPTAG::ByteArray((std::uint8_t*)PyUnicode_DATA($input), PyUnicode_GET_LENGTH($input), false); - } -#endif - - if (nullptr == $1.Data()) - { - PyErr_SetString(PyExc_ValueError, "Expected Bytes, Data Structure with Buffer Protocol, or Unicode String after Python 3.3 ."); - return NULL; - } -%} - -#endif diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/inc/PythonCore.i b/core/src/index/thirdparty/SPTAG/Wrappers/inc/PythonCore.i deleted file mode 100644 index d2f38ca856..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/inc/PythonCore.i +++ /dev/null @@ -1,16 +0,0 @@ -%module SPTAG - -%{ -#include "inc/CoreInterface.h" -%} - -%include -%shared_ptr(AnnIndex) -%shared_ptr(QueryResult) -%include "PythonCommon.i" - -%{ -#define SWIG_FILE_WITH_INIT -%} - -%include "CoreInterface.h" \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/inc/TransferDataType.h b/core/src/index/thirdparty/SPTAG/Wrappers/inc/TransferDataType.h deleted file mode 100644 index 51ef9614ab..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/inc/TransferDataType.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#ifndef _SPTAG_PW_TRANSFERDATATYPE_H_ -#define _SPTAG_PW_TRANSFERDATATYPE_H_ - -#include "inc/Core/CommonDataStructure.h" -#include "inc/Core/SearchQuery.h" -#include "inc/Socket/RemoteSearchQuery.h" - -typedef SPTAG::ByteArray ByteArray; - -typedef SPTAG::QueryResult QueryResult; - -typedef SPTAG::BasicResult BasicResult; - -typedef SPTAG::Socket::RemoteSearchResult RemoteSearchResult; - -#endif // _SPTAG_PW_TRANSFERDATATYPE_H_ diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/packages.config b/core/src/index/thirdparty/SPTAG/Wrappers/packages.config deleted file mode 100644 index d780ec4a8e..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/src/AssemblyInfo.cpp b/core/src/index/thirdparty/SPTAG/Wrappers/src/AssemblyInfo.cpp deleted file mode 100644 index 43759a83ef..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/src/AssemblyInfo.cpp +++ /dev/null @@ -1,36 +0,0 @@ -using namespace System; -using namespace System::Reflection; -using namespace System::Runtime::CompilerServices; -using namespace System::Runtime::InteropServices; -using namespace System::Security::Permissions; - -// -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -// -[assembly:AssemblyTitleAttribute(L"CLRCore")]; -[assembly:AssemblyDescriptionAttribute(L"")]; -[assembly:AssemblyConfigurationAttribute(L"")]; -[assembly:AssemblyCompanyAttribute(L"")]; -[assembly:AssemblyProductAttribute(L"CLRCore")]; -[assembly:AssemblyCopyrightAttribute(L"Copyright (c) 2019")]; -[assembly:AssemblyTrademarkAttribute(L"")]; -[assembly:AssemblyCultureAttribute(L"")]; - -// -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the value or you can default the Revision and Build Numbers -// by using the '*' as shown below: - -[assembly:AssemblyVersionAttribute("1.0.*")]; - -[assembly:ComVisible(false)]; - -[assembly:CLSCompliantAttribute(true)]; \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/src/CLRCoreInterface.cpp b/core/src/index/thirdparty/SPTAG/Wrappers/src/CLRCoreInterface.cpp deleted file mode 100644 index 39e62baf44..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/src/CLRCoreInterface.cpp +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/CLRCoreInterface.h" - - -namespace Microsoft -{ - namespace ANN - { - namespace SPTAGManaged - { - AnnIndex::AnnIndex(std::shared_ptr p_index) : - ManagedObject(p_index) - { - m_dimension = p_index->GetFeatureDim(); - m_inputVectorSize = SPTAG::GetValueTypeSize(p_index->GetVectorValueType()) * m_dimension; - } - - AnnIndex::AnnIndex(String^ p_algoType, String^ p_valueType, int p_dimension) : - ManagedObject(SPTAG::VectorIndex::CreateInstance(string_to(p_algoType), string_to(p_valueType))) - { - m_dimension = p_dimension; - m_inputVectorSize = SPTAG::GetValueTypeSize((*m_Instance)->GetVectorValueType()) * m_dimension; - } - - void AnnIndex::SetBuildParam(String^ p_name, String^ p_value) - { - if (m_Instance != nullptr) - (*m_Instance)->SetParameter(string_to_char_array(p_name), string_to_char_array(p_value)); - } - - void AnnIndex::SetSearchParam(String^ p_name, String^ p_value) - { - if (m_Instance != nullptr) - (*m_Instance)->SetParameter(string_to_char_array(p_name), string_to_char_array(p_value)); - } - - bool AnnIndex::Build(array^ p_data, int p_num) - { - if (m_Instance == nullptr || p_num == 0 || m_dimension == 0 || p_data->LongLength != p_num * m_inputVectorSize) - return false; - - pin_ptr ptr = &p_data[0]; - return (SPTAG::ErrorCode::Success == (*m_Instance)->BuildIndex(ptr, p_num, m_dimension)); - } - - bool AnnIndex::BuildWithMetaData(array^ p_data, array^ p_meta, int p_num, bool p_withMetaIndex) - { - if (m_Instance == nullptr || p_num == 0 || m_dimension == 0 || p_data->LongLength != p_num * m_inputVectorSize) - return false; - - pin_ptr dataptr = &p_data[0]; - std::shared_ptr vectors(new SPTAG::BasicVectorSet(SPTAG::ByteArray(dataptr, p_data->LongLength, false), (*m_Instance)->GetVectorValueType(), m_dimension, p_num)); - - pin_ptr metaptr = &p_meta[0]; - std::uint64_t* offsets = new std::uint64_t[p_num + 1]{ 0 }; - int current = 0; - for (long long i = 0; i < p_meta->LongLength; i++) { - if (((char)metaptr[i]) == '\n') - offsets[++current] = (std::uint64_t)(i + 1); - } - std::shared_ptr meta(new SPTAG::MemMetadataSet(SPTAG::ByteArray(metaptr, p_meta->LongLength, false), SPTAG::ByteArray((std::uint8_t*)offsets, (p_num + 1) * sizeof(std::uint64_t), true), p_num)); - return (SPTAG::ErrorCode::Success == (*m_Instance)->BuildIndex(vectors, meta, p_withMetaIndex)); - } - - array^ AnnIndex::Search(array^ p_data, int p_resultNum) - { - array^ res; - if (m_Instance == nullptr || m_dimension == 0 || p_data->LongLength != m_inputVectorSize) - return res; - - pin_ptr ptr = &p_data[0]; - SPTAG::QueryResult results(ptr, p_resultNum, false); - (*m_Instance)->SearchIndex(results); - - res = gcnew array(p_resultNum); - for (int i = 0; i < p_resultNum; i++) - res[i] = gcnew BasicResult(new SPTAG::BasicResult(*(results.GetResult(i)))); - - return res; - } - - array^ AnnIndex::SearchWithMetaData(array^ p_data, int p_resultNum) - { - array^ res; - if (m_Instance == nullptr || m_dimension == 0 || p_data->LongLength != m_inputVectorSize) - return res; - - pin_ptr ptr = &p_data[0]; - SPTAG::QueryResult results(ptr, p_resultNum, true); - (*m_Instance)->SearchIndex(results); - - res = gcnew array(p_resultNum); - for (int i = 0; i < p_resultNum; i++) - res[i] = gcnew BasicResult(new SPTAG::BasicResult(*(results.GetResult(i)))); - - return res; - } - - bool AnnIndex::Save(String^ p_saveFile) - { - return SPTAG::ErrorCode::Success == (*m_Instance)->SaveIndex(string_to_char_array(p_saveFile)); - } - - array^>^ AnnIndex::Dump() - { - std::shared_ptr> buffersize = (*m_Instance)->CalculateBufferSize(); - array^>^ res = gcnew array^>(buffersize->size() + 1); - std::vector indexBlobs; - for (int i = 1; i < res->Length; i++) - { - res[i] = gcnew array(buffersize->at(i-1)); - pin_ptr ptr = &res[i][0]; - indexBlobs.push_back(SPTAG::ByteArray((std::uint8_t*)ptr, res[i]->LongLength, false)); - } - std::string config; - if (SPTAG::ErrorCode::Success != (*m_Instance)->SaveIndex(config, indexBlobs)) - { - array^>^ null; - return null; - } - res[0] = gcnew array(config.size()); - Marshal::Copy(IntPtr(&config[0]), res[0], 0, config.size()); - return res; - } - - bool AnnIndex::Add(array^ p_data, int p_num) - { - if (m_Instance == nullptr || p_num == 0 || m_dimension == 0 || p_data->LongLength != p_num * m_inputVectorSize) - return false; - - pin_ptr ptr = &p_data[0]; - return (SPTAG::ErrorCode::Success == (*m_Instance)->AddIndex(ptr, p_num, m_dimension)); - } - - bool AnnIndex::AddWithMetaData(array^ p_data, array^ p_meta, int p_num) - { - if (m_Instance == nullptr || p_num == 0 || m_dimension == 0 || p_data->LongLength != p_num * m_inputVectorSize) - return false; - - pin_ptr dataptr = &p_data[0]; - std::shared_ptr vectors(new SPTAG::BasicVectorSet(SPTAG::ByteArray(dataptr, p_data->LongLength, false), (*m_Instance)->GetVectorValueType(), m_dimension, p_num)); - - pin_ptr metaptr = &p_meta[0]; - std::uint64_t* offsets = new std::uint64_t[p_num + 1]{ 0 }; - int current = 0; - for (long long i = 0; i < p_meta->LongLength; i++) { - if (((char)metaptr[i]) == '\n') - offsets[++current] = (std::uint64_t)(i + 1); - } - std::shared_ptr meta(new SPTAG::MemMetadataSet(SPTAG::ByteArray(metaptr, p_meta->LongLength, false), SPTAG::ByteArray((std::uint8_t*)offsets, (p_num + 1) * sizeof(std::uint64_t), true), p_num)); - return (SPTAG::ErrorCode::Success == (*m_Instance)->AddIndex(vectors, meta)); - } - - bool AnnIndex::Delete(array^ p_data, int p_num) - { - if (m_Instance == nullptr || p_num == 0 || m_dimension == 0 || p_data->LongLength != p_num * m_inputVectorSize) - return false; - - pin_ptr ptr = &p_data[0]; - return (SPTAG::ErrorCode::Success == (*m_Instance)->DeleteIndex(ptr, p_num)); - } - - bool AnnIndex::DeleteByMetaData(array^ p_meta) - { - if (m_Instance == nullptr) - return false; - - pin_ptr metaptr = &p_meta[0]; - return (SPTAG::ErrorCode::Success == (*m_Instance)->DeleteIndex(SPTAG::ByteArray(metaptr, p_meta->LongLength, false))); - } - - AnnIndex^ AnnIndex::Load(String^ p_loaderFile) - { - std::shared_ptr vecIndex; - AnnIndex^ res; - if (SPTAG::ErrorCode::Success != SPTAG::VectorIndex::LoadIndex(string_to_char_array(p_loaderFile), vecIndex) || nullptr == vecIndex) - { - res = gcnew AnnIndex(nullptr); - } - else { - res = gcnew AnnIndex(vecIndex); - } - return res; - } - - AnnIndex^ AnnIndex::Load(array^>^ p_index) - { - std::vector p_indexBlobs; - for (int i = 1; i < p_index->Length; i++) - { - pin_ptr ptr = &p_index[i][0]; - p_indexBlobs.push_back(SPTAG::ByteArray((std::uint8_t*)ptr, p_index[i]->LongLength, false)); - } - pin_ptr configptr = &p_index[0][0]; - - std::shared_ptr vecIndex; - if (SPTAG::ErrorCode::Success != SPTAG::VectorIndex::LoadIndex(std::string((char*)configptr, p_index[0]->LongLength), p_indexBlobs, vecIndex) || nullptr == vecIndex) - { - return gcnew AnnIndex(nullptr); - } - return gcnew AnnIndex(vecIndex); - } - - bool AnnIndex::Merge(String^ p_indexFilePath1, String^ p_indexFilePath2) - { - return (SPTAG::ErrorCode::Success == SPTAG::VectorIndex::MergeIndex(string_to_char_array(p_indexFilePath1), string_to_char_array(p_indexFilePath2))); - } - } - } -} \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/src/ClientInterface.cpp b/core/src/index/thirdparty/SPTAG/Wrappers/src/ClientInterface.cpp deleted file mode 100644 index 65a1d4cf17..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/src/ClientInterface.cpp +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/ClientInterface.h" -#include "inc/Helper/CommonHelper.h" -#include "inc/Helper/Concurrent.h" -#include "inc/Helper/Base64Encode.h" -#include "inc/Helper/StringConvert.h" - -#include - - -AnnClient::AnnClient(const char* p_serverAddr, const char* p_serverPort) - : m_connectionID(SPTAG::Socket::c_invalidConnectionID), - m_timeoutInMilliseconds(9000) -{ - using namespace SPTAG; - - m_socketClient.reset(new Socket::Client(GetHandlerMap(), 2, 30)); - - if (nullptr == p_serverAddr || nullptr == p_serverPort) - { - return; - } - - m_server = p_serverAddr; - m_port = p_serverPort; - - auto connectCallback = [this](Socket::ConnectionID p_cid, ErrorCode p_ec) - { - m_connectionID = p_cid; - - if (ErrorCode::Socket_FailedResolveEndPoint == p_ec) - { - return; - } - - while (Socket::c_invalidConnectionID == m_connectionID) - { - ErrorCode errCode; - std::this_thread::sleep_for(std::chrono::seconds(10)); - m_connectionID = m_socketClient->ConnectToServer(m_server, m_port, errCode); - } - }; - - m_socketClient->AsyncConnectToServer(m_server, m_port, connectCallback); - - m_socketClient->SetEventOnConnectionClose([this](Socket::ConnectionID p_cid) - { - ErrorCode errCode; - m_connectionID = Socket::c_invalidConnectionID; - while (Socket::c_invalidConnectionID == m_connectionID) - { - std::this_thread::sleep_for(std::chrono::seconds(10)); - m_connectionID = m_socketClient->ConnectToServer(m_server, m_port, errCode); - } - }); -} - - -AnnClient::~AnnClient() -{ -} - - -void -AnnClient::SetTimeoutMilliseconds(int p_timeout) -{ - m_timeoutInMilliseconds = p_timeout; -} - - -void -AnnClient::SetSearchParam(const char* p_name, const char* p_value) -{ - std::lock_guard guard(m_paramMutex); - - if (nullptr == p_name || '\0' == *p_name) - { - return; - } - - std::string name(p_name); - SPTAG::Helper::StrUtils::ToLowerInPlace(name); - - if (nullptr == p_value || '\0' == *p_value) - { - m_params.erase(name); - return; - } - - m_params[name] = p_value; -} - - -void -AnnClient::ClearSearchParam() -{ - std::lock_guard guard(m_paramMutex); - m_params.clear(); -} - - -std::shared_ptr -AnnClient::Search(ByteArray p_data, int p_resultNum, const char* p_valueType, bool p_withMetaData) -{ - using namespace SPTAG; - - SPTAG::Socket::RemoteSearchResult ret; - if (Socket::c_invalidConnectionID != m_connectionID) - { - - auto signal = std::make_shared(1); - - auto callback = [&ret, signal](RemoteSearchResult p_result) - { - if (RemoteSearchResult::ResultStatus::Success == p_result.m_status) - { - ret = std::move(p_result); - } - - signal->FinishOne(); - }; - - auto timeoutCallback = [this](std::shared_ptr p_callback) - { - if (nullptr != p_callback) - { - RemoteSearchResult result; - result.m_status = RemoteSearchResult::ResultStatus::Timeout; - - (*p_callback)(std::move(result)); - } - }; - - auto connectCallback = [callback, this](bool p_connectSucc) - { - if (!p_connectSucc) - { - RemoteSearchResult result; - result.m_status = RemoteSearchResult::ResultStatus::FailedNetwork; - - callback(std::move(result)); - } - }; - - Socket::Packet packet; - packet.Header().m_connectionID = Socket::c_invalidConnectionID; - packet.Header().m_packetType = Socket::PacketType::SearchRequest; - packet.Header().m_processStatus = Socket::PacketProcessStatus::Ok; - packet.Header().m_resourceID = m_callbackManager.Add(std::make_shared(std::move(callback)), - m_timeoutInMilliseconds, - std::move(timeoutCallback)); - - Socket::RemoteQuery query; - SPTAG::VectorValueType valueType; - SPTAG::Helper::Convert::ConvertStringTo(p_valueType, valueType); - query.m_queryString = CreateSearchQuery(p_data, p_resultNum, p_withMetaData, valueType); - - packet.Header().m_bodyLength = static_cast(query.EstimateBufferSize()); - packet.AllocateBuffer(packet.Header().m_bodyLength); - query.Write(packet.Body()); - packet.Header().WriteBuffer(packet.HeaderBuffer()); - - m_socketClient->SendPacket(m_connectionID, std::move(packet), connectCallback); - - signal->Wait(); - } - return std::make_shared(ret); -} - - -bool -AnnClient::IsConnected() const -{ - return m_connectionID != SPTAG::Socket::c_invalidConnectionID; -} - - -SPTAG::Socket::PacketHandlerMapPtr -AnnClient::GetHandlerMap() -{ - using namespace SPTAG; - - Socket::PacketHandlerMapPtr handlerMap(new Socket::PacketHandlerMap); - handlerMap->emplace(Socket::PacketType::SearchResponse, - std::bind(&AnnClient::SearchResponseHanlder, - this, - std::placeholders::_1, - std::placeholders::_2)); - - return handlerMap; -} - - -void -AnnClient::SearchResponseHanlder(SPTAG::Socket::ConnectionID p_localConnectionID, - SPTAG::Socket::Packet p_packet) -{ - using namespace SPTAG; - - std::shared_ptr callback = m_callbackManager.GetAndRemove(p_packet.Header().m_resourceID); - if (nullptr == callback) - { - return; - } - - if (p_packet.Header().m_processStatus != Socket::PacketProcessStatus::Ok || 0 == p_packet.Header().m_bodyLength) - { - Socket::RemoteSearchResult result; - result.m_status = Socket::RemoteSearchResult::ResultStatus::FailedExecute; - - (*callback)(std::move(result)); - } - else - { - Socket::RemoteSearchResult result; - result.Read(p_packet.Body()); - (*callback)(std::move(result)); - } -} - - -std::string -AnnClient::CreateSearchQuery(const ByteArray& p_data, - int p_resultNum, - bool p_extractMetadata, - SPTAG::VectorValueType p_valueType) -{ - std::stringstream out; - - out << "#"; - std::size_t encLen; - SPTAG::Helper::Base64::Encode(p_data.Data(), p_data.Length(), out, encLen); - - out << " $datatype:" << SPTAG::Helper::Convert::ConvertToString(p_valueType); - out << " $resultnum:" << std::to_string(p_resultNum); - out << " $extractmetadata:" << (p_extractMetadata ? "true" : "false"); - - { - std::lock_guard guard(m_paramMutex); - for (const auto& param : m_params) - { - out << " $" << param.first << ":" << param.second; - } - } - - return out.str(); -} - diff --git a/core/src/index/thirdparty/SPTAG/Wrappers/src/CoreInterface.cpp b/core/src/index/thirdparty/SPTAG/Wrappers/src/CoreInterface.cpp deleted file mode 100644 index 5a62fe0315..0000000000 --- a/core/src/index/thirdparty/SPTAG/Wrappers/src/CoreInterface.cpp +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#include "inc/CoreInterface.h" -#include "inc/Helper/StringConvert.h" - - -AnnIndex::AnnIndex(DimensionType p_dimension) - : m_algoType(SPTAG::IndexAlgoType::BKT), - m_inputValueType(SPTAG::VectorValueType::Float), - m_dimension(p_dimension) -{ - m_inputVectorSize = SPTAG::GetValueTypeSize(m_inputValueType) * m_dimension; -} - - -AnnIndex::AnnIndex(const char* p_algoType, const char* p_valueType, DimensionType p_dimension) - : m_algoType(SPTAG::IndexAlgoType::Undefined), - m_inputValueType(SPTAG::VectorValueType::Undefined), - m_dimension(p_dimension) -{ - SPTAG::Helper::Convert::ConvertStringTo(p_algoType, m_algoType); - SPTAG::Helper::Convert::ConvertStringTo(p_valueType, m_inputValueType); - m_inputVectorSize = SPTAG::GetValueTypeSize(m_inputValueType) * m_dimension; -} - - -AnnIndex::AnnIndex(const std::shared_ptr& p_index) - : m_algoType(p_index->GetIndexAlgoType()), - m_inputValueType(p_index->GetVectorValueType()), - m_dimension(p_index->GetFeatureDim()), - m_index(p_index) -{ - m_inputVectorSize = SPTAG::GetValueTypeSize(m_inputValueType) * m_dimension; -} - - -AnnIndex::~AnnIndex() -{ -} - - -bool -AnnIndex::Build(ByteArray p_data, SizeType p_num) -{ - if (nullptr == m_index) - { - m_index = SPTAG::VectorIndex::CreateInstance(m_algoType, m_inputValueType); - } - if (nullptr == m_index || p_num == 0 || m_dimension == 0 || p_data.Length() != p_num * m_inputVectorSize) - { - return false; - } - return (SPTAG::ErrorCode::Success == m_index->BuildIndex(p_data.Data(), (SPTAG::SizeType)p_num, (SPTAG::DimensionType)m_dimension)); -} - - -bool -AnnIndex::BuildWithMetaData(ByteArray p_data, ByteArray p_meta, SizeType p_num, bool p_withMetaIndex) -{ - if (nullptr == m_index) - { - m_index = SPTAG::VectorIndex::CreateInstance(m_algoType, m_inputValueType); - } - if (nullptr == m_index || p_num == 0 || m_dimension == 0 || p_data.Length() != p_num * m_inputVectorSize) - { - return false; - } - - std::shared_ptr vectors(new SPTAG::BasicVectorSet(p_data, - m_inputValueType, - static_cast(m_dimension), - static_cast(p_num))); - - std::uint64_t* offsets = new std::uint64_t[p_num + 1]{ 0 }; - SizeType current = 1; - for (size_t i = 0; i < p_meta.Length(); i++) { - if (((char)p_meta.Data()[i]) == '\n') - offsets[current++] = (std::uint64_t)(i + 1); - } - std::shared_ptr meta(new SPTAG::MemMetadataSet(p_meta, ByteArray((std::uint8_t*)offsets, (p_num + 1) * sizeof(std::uint64_t), true), (SPTAG::SizeType)p_num)); - return (SPTAG::ErrorCode::Success == m_index->BuildIndex(vectors, meta, p_withMetaIndex)); -} - - -void -AnnIndex::SetBuildParam(const char* p_name, const char* p_value) -{ - if (nullptr == m_index) - { - if (SPTAG::IndexAlgoType::Undefined == m_algoType || - SPTAG::VectorValueType::Undefined == m_inputValueType) - { - return; - } - m_index = SPTAG::VectorIndex::CreateInstance(m_algoType, m_inputValueType); - - } - m_index->SetParameter(p_name, p_value); -} - - -void -AnnIndex::SetSearchParam(const char* p_name, const char* p_value) -{ - if (nullptr != m_index) m_index->SetParameter(p_name, p_value); -} - - -std::shared_ptr -AnnIndex::Search(ByteArray p_data, int p_resultNum) -{ - std::shared_ptr results = std::make_shared(p_data.Data(), p_resultNum, false); - - if (nullptr != m_index && p_data.Length() == m_inputVectorSize) - { - m_index->SearchIndex(*results); - } - return std::move(results); -} - -std::shared_ptr -AnnIndex::SearchWithMetaData(ByteArray p_data, int p_resultNum) -{ - std::shared_ptr results = std::make_shared(p_data.Data(), p_resultNum, true); - - if (nullptr != m_index && p_data.Length() == m_inputVectorSize) - { - m_index->SearchIndex(*results); - } - return std::move(results); -} - -bool -AnnIndex::ReadyToServe() const -{ - return m_index != nullptr; -} - - -bool -AnnIndex::Save(const char* p_savefile) const -{ - return SPTAG::ErrorCode::Success == m_index->SaveIndex(p_savefile); -} - - -AnnIndex -AnnIndex::Load(const char* p_loaderFile) -{ - std::shared_ptr vecIndex; - auto ret = SPTAG::VectorIndex::LoadIndex(p_loaderFile, vecIndex); - if (SPTAG::ErrorCode::Success != ret || nullptr == vecIndex) - { - return AnnIndex(0); - } - - return AnnIndex(vecIndex); -} - - -bool -AnnIndex::Add(ByteArray p_data, SizeType p_num) -{ - if (nullptr == m_index) - { - m_index = SPTAG::VectorIndex::CreateInstance(m_algoType, m_inputValueType); - } - if (nullptr == m_index || p_num == 0 || m_dimension == 0 || p_data.Length() != p_num * m_inputVectorSize) - { - return false; - } - return (SPTAG::ErrorCode::Success == m_index->AddIndex(p_data.Data(), (SPTAG::SizeType)p_num, (SPTAG::DimensionType)m_dimension)); -} - - -bool -AnnIndex::AddWithMetaData(ByteArray p_data, ByteArray p_meta, SizeType p_num) -{ - if (nullptr == m_index) - { - m_index = SPTAG::VectorIndex::CreateInstance(m_algoType, m_inputValueType); - } - if (nullptr == m_index || p_num == 0 || m_dimension == 0 || p_data.Length() != p_num * m_inputVectorSize) - { - return false; - } - - std::shared_ptr vectors(new SPTAG::BasicVectorSet(p_data, - m_inputValueType, - static_cast(m_dimension), - static_cast(p_num))); - - std::uint64_t* offsets = new std::uint64_t[p_num + 1]{ 0 }; - SizeType current = 1; - for (size_t i = 0; i < p_meta.Length(); i++) { - if (((char)p_meta.Data()[i]) == '\n') - offsets[current++] = (std::uint64_t)(i + 1); - } - std::shared_ptr meta(new SPTAG::MemMetadataSet(p_meta, ByteArray((std::uint8_t*)offsets, (p_num + 1) * sizeof(std::uint64_t), true), (SPTAG::SizeType)p_num)); - return (SPTAG::ErrorCode::Success == m_index->AddIndex(vectors, meta)); -} - - -bool -AnnIndex::Delete(ByteArray p_data, SizeType p_num) -{ - if (nullptr == m_index || p_num == 0 || m_dimension == 0 || p_data.Length() != p_num * m_inputVectorSize) - { - return false; - } - - return (SPTAG::ErrorCode::Success == m_index->DeleteIndex(p_data.Data(), (SPTAG::SizeType)p_num)); -} - - -bool -AnnIndex::DeleteByMetaData(ByteArray p_meta) -{ - if (nullptr == m_index) return false; - - return (SPTAG::ErrorCode::Success == m_index->DeleteIndex(p_meta)); -} - - -bool -AnnIndex::Merge(const char* p_indexFilePath1, const char* p_indexFilePath2) -{ - return (SPTAG::ErrorCode::Success == SPTAG::VectorIndex::MergeIndex(p_indexFilePath1, p_indexFilePath2)); -} \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/azure-pipelines.yml b/core/src/index/thirdparty/SPTAG/azure-pipelines.yml deleted file mode 100644 index 22f697bdd4..0000000000 --- a/core/src/index/thirdparty/SPTAG/azure-pipelines.yml +++ /dev/null @@ -1,230 +0,0 @@ -resources: - -- repo: self - -phases: - -- phase: Phase_1 - - displayName: Agent job - - - - condition: succeeded() - - queue: - - name: SPTAGBuild - - steps: - - - script: | - mkdir build - cd build - cmake .. - make - cd ../Release - ./test - - displayName: 'Command Line Script' - - - - - -- phase: Phase_2 - - displayName: Agent job - - - - condition: succeeded() - - queue: - - name: Hosted - - demands: - - - msbuild - - - visualstudio - - - - steps: - - - task: NuGetToolInstaller@0 - - displayName: 'Use NuGet 4.3.0' - - - - - - - task: NuGetCommand@2 - - displayName: 'NuGet restore' - - inputs: - - restoreSolution: SPTAG.sln - - - - vstsFeed: 'bae5097f-8d64-4f8f-913e-24a4eb8302c3' - - - - - - - task: VSBuild@1 - - displayName: 'Build solution SPTAG.sln' - - inputs: - - solution: SPTAG.sln - - - - vsVersion: 14.0 - - - - platform: x64 - - - - configuration: debug - - - - msbuildArchitecture: x64 - - - - createLogFile: true - - - - - - - script: '.\x64\Debug\Test.exe' - - displayName: 'Command Line Script' - - - - - task: CopyFiles@2 - - displayName: 'Copy Files to: $(Build.ArtifactStagingDirectory)' - - inputs: - - SourceFolder: x64/Debug/ - - - - Contents: '*' - - - - TargetFolder: '$(Build.ArtifactStagingDirectory)' - - - - - - - task: PublishBuildArtifacts@1 - - displayName: 'Publish Artifact: drop' - - - - - - - task: securedevelopmentteam.vss-secure-development-tools.build-task-binskim.BinSkim@3 - - displayName: 'Run BinSkim ' - - inputs: - - InputType: Basic - - - - - - - task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@2 - - displayName: 'Run CredScan' - - inputs: - - scanFolder: AnnService - - - - suppressAsError: true - - - - verboseOutput: true - - - - debugMode: false - - - - - - - task: securedevelopmentteam.vss-secure-development-tools.build-task-autoapplicability.AutoApplicability@1 - - displayName: 'Run AutoApplicability' - - inputs: - - IsSoftware: true - - - - - - - task: securedevelopmentteam.vss-secure-development-tools.build-task-publishsecurityanalysislogs.PublishSecurityAnalysisLogs@2 - - displayName: 'Publish Security Analysis Logs' - - - - - - - task: securedevelopmentteam.vss-secure-development-tools.build-task-report.SdtReport@1 - - displayName: 'Create Security Analysis Report' - - inputs: - - AllTools: true - - - - - - - task: securedevelopmentteam.vss-secure-development-tools.build-task-postanalysis.PostAnalysis@1 - - displayName: 'Post Analysis' - - inputs: - - AllTools: true - - - - - - - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - - displayName: 'Component Detection' - diff --git a/core/src/index/thirdparty/SPTAG/docs/GettingStart.md b/core/src/index/thirdparty/SPTAG/docs/GettingStart.md deleted file mode 100644 index 9f82b680ac..0000000000 --- a/core/src/index/thirdparty/SPTAG/docs/GettingStart.md +++ /dev/null @@ -1,286 +0,0 @@ -## **Quick start** - -### **Index Build** - ```bash - Usage: - ./IndexBuiler [options] - Options: - -d, --dimension Dimension of vector, required. - -v, --vectortype Input vector data type (e.g. Float, Int8, Int16), required. - -i, --input Input raw data, required. - -o, --outputfolder Output folder, required. - -a, --algo Index Algorithm type (e.g. BKT, KDT), required. - - -t, --thread Thread Number, default is 32. - --delimiter Vector delimiter, default is |. - Index.= Set the algorithm parameter ArgName with value ArgValue. - ``` - - ### **Index Search** - ```bash - Usage: - ./IndexSearcher [options] - Options - Index.QueryFile=XXX Input Query file - Index.ResultFile=XXX Output result file - Index.TruthFile=XXX Truth file that can help to calculate the recall - Index.K=XXX How many nearest neighbors return - Index.MaxCheck=XXX The maxcheck of the search - ``` - -### ** Input File format ** -> Input raw data for index build and input query file for index search (suppose vector dimension is 3): -``` -\t||| -\t||| -... -``` -where each line represents a vector with its metadata and its value separated by a tab space. Each dimension of a vector is separated by | or use --delimiter to define the separator. - -> Truth file to calculate recall (suppose K is 2): -``` - - -... -``` -where each line represents the K nearest neighbors of a query separated by a blank space. Each neighbor is given by its vector id. - -### **Server** -```bash -Usage: -./Server [options] -Options: - -m, --mode Service mode, interactive or socket. - -c, --config Configure file of the index - -Write a server configuration file service.ini as follows: - -[Service] -ListenAddr=0.0.0.0 -ListenPort=8000 -ThreadNumber=8 -SocketThreadNumber=8 - -[QueryConfig] -DefaultMaxResultNumber=6 -DefaultSeparator=| - -[Index] -List=BKT - -[Index_BKT] -IndexFolder=BKT_gist -``` - -### **Client** -```bash -Usage: -./Client [options] -Options: --s, --server Server address --p, --port Server port --t, Search timeout --cth, Client Thread Number --sth Socket Thread Number -``` - -### **Aggregator** -```bash -Usage: -./Aggregator - -Write Aggregator.ini as follows: - -[Service] -ListenAddr=0.0.0.0 -ListenPort=8100 -ThreadNumber=8 -SocketThreadNumber=8 - -[Servers] -Number=2 - -[Server_0] -Address=127.0.0.1 -Port=8000 - -[Server_1] -Address=127.0.0.1 -Port=8010 -``` - -### **Python Support** -> Singlebox PythonWrapper - ```python - -import SPTAG -import numpy as np - -n = 100 -k = 3 -r = 3 - -def testBuild(algo, distmethod, x, out): - i = SPTAG.AnnIndex(algo, 'Float', x.shape[1]) - i.SetBuildParam("NumberOfThreads", '4') - i.SetBuildParam("DistCalcMethod", distmethod) - ret = i.Build(x, x.shape[0]) - i.Save(out) - -def testBuildWithMetaData(algo, distmethod, x, s, out): - i = SPTAG.AnnIndex(algo, 'Float', x.shape[1]) - i.SetBuildParam("NumberOfThreads", '4') - i.SetBuildParam("DistCalcMethod", distmethod) - if i.BuildWithMetaData(x, s, x.shape[0]): - i.Save(out) - -def testSearch(index, q, k): - j = SPTAG.AnnIndex.Load(index) - for t in range(q.shape[0]): - result = j.Search(q[t], k) - print (result[0]) # ids - print (result[1]) # distances - -def testSearchWithMetaData(index, q, k): - j = SPTAG.AnnIndex.Load(index) - j.SetSearchParam("MaxCheck", '1024') - for t in range(q.shape[0]): - result = j.SearchWithMetaData(q[t], k) - print (result[0]) # ids - print (result[1]) # distances - print (result[2]) # metadata - -def testAdd(index, x, out, algo, distmethod): - if index != None: - i = SPTAG.AnnIndex.Load(index) - else: - i = SPTAG.AnnIndex(algo, 'Float', x.shape[1]) - i.SetBuildParam("NumberOfThreads", '4') - i.SetBuildParam("DistCalcMethod", distmethod) - if i.Add(x, x.shape[0]): - i.Save(out) - -def testAddWithMetaData(index, x, s, out, algo, distmethod): - if index != None: - i = SPTAG.AnnIndex.Load(index) - else: - i = SPTAG.AnnIndex(algo, 'Float', x.shape[1]) - i = SPTAG.AnnIndex(algo, 'Float', x.shape[1]) - i.SetBuildParam("NumberOfThreads", '4') - i.SetBuildParam("DistCalcMethod", distmethod) - if i.AddWithMetaData(x, s, x.shape[0]): - i.Save(out) - -def testDelete(index, x, out): - i = SPTAG.AnnIndex.Load(index) - ret = i.Delete(x, x.shape[0]) - print (ret) - i.Save(out) - -def Test(algo, distmethod): - x = np.ones((n, 10), dtype=np.float32) * np.reshape(np.arange(n, dtype=np.float32), (n, 1)) - q = np.ones((r, 10), dtype=np.float32) * np.reshape(np.arange(r, dtype=np.float32), (r, 1)) * 2 - m = '' - for i in range(n): - m += str(i) + '\n' - - m = m.encode() - - print ("Build.............................") - testBuild(algo, distmethod, x, 'testindices') - testSearch('testindices', q, k) - print ("Add.............................") - testAdd('testindices', x, 'testindices', algo, distmethod) - testSearch('testindices', q, k) - print ("Delete.............................") - testDelete('testindices', q, 'testindices') - testSearch('testindices', q, k) - - print ("AddWithMetaData.............................") - testAddWithMetaData(None, x, m, 'testindices', algo, distmethod) - print ("Delete.............................") - testSearchWithMetaData('testindices', q, k) - testDelete('testindices', q, 'testindices') - testSearchWithMetaData('testindices', q, k) - -if __name__ == '__main__': - Test('BKT', 'L2') - Test('KDT', 'L2') - - ``` - - > Python Client Wrapper, Suppose there is a sever run at 127.0.0.1:8000 serving ten-dimensional vector datasets: - ```python -import SPTAGClient -import numpy as np -import time - -def testSPTAGClient(): - index = SPTAGClient.AnnClient('127.0.0.1', '8100') - while not index.IsConnected(): - time.sleep(1) - index.SetTimeoutMilliseconds(18000) - - q = np.ones((10, 10), dtype=np.float32) - for t in range(q.shape[0]): - result = index.Search(q[t], 6, 'Float', False) - print (result[0]) - print (result[1]) - -if __name__ == '__main__': - testSPTAGClient() - - ``` - - ### **C# Support** -> Singlebox CsharpWrapper - ```C# -using System; -using System.Text; - -public class test -{ - static int dimension = 10; - static int n = 10; - static int k = 3; - - static byte[] createFloatArray(int n) - { - byte[] data = new byte[n * dimension * sizeof(float)]; - for (int i = 0; i < n; i++) - for (int j = 0; j < dimension; j++) - Array.Copy(BitConverter.GetBytes((float)i), 0, data, (i * dimension + j) * sizeof(float), 4); - return data; - } - - static byte[] createMetadata(int n) - { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < n; i++) - sb.Append(i.ToString() + '\n'); - return Encoding.ASCII.GetBytes(sb.ToString()); - } - - static void Main() - { - { - AnnIndex idx = new AnnIndex("BKT", "Float", dimension); - idx.SetBuildParam("DistCalcMethod", "L2"); - byte[] data = createFloatArray(n); - byte[] meta = createMetadata(n); - idx.BuildWithMetaData(data, meta, n); - idx.Save("testcsharp"); - } - - AnnIndex index = AnnIndex.Load("testcsharp"); - BasicResult[] res = index.SearchWithMetaData(createFloatArray(1), k); - for (int i = 0; i < res.Length; i++) - Console.WriteLine("result " + i.ToString() + ":" + res[i].Dist.ToString() + "@(" + res[i].VID.ToString() + "," + Encoding.ASCII.GetString(res[i].Meta) + ")"); - Console.WriteLine("test finish!"); - } -} - - ``` - - - \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/docs/Parameters.md b/core/src/index/thirdparty/SPTAG/docs/Parameters.md deleted file mode 100644 index 9e2fa93715..0000000000 --- a/core/src/index/thirdparty/SPTAG/docs/Parameters.md +++ /dev/null @@ -1,159 +0,0 @@ -## **Parameters** - -> Common Parameters - -| ParametersName | type | default | definition| -|---|---|---|---| -| Samples | int | 1000 | how many points will be sampled to do tree node split | -|TPTNumber | int | 32 | number of TPT trees to help with graph construction | -|TPTLeafSize | int | 2000 | TPT tree leaf size | -NeighborhoodSize | int | 32 | number of neighbors each node has in the neighborhood graph | -|GraphNeighborhoodScale | int | 2 | number of neighborhood size scale in the build stage | -|CEF | int | 1000 | number of results used to construct RNG | -|MaxCheckForRefineGraph| int | 10000 | how many nodes each node will visit during graph refine in the build stage | -|NumberOfThreads | int | 1 | number of threads to uses for speed up the build | -|DistCalcMethod | string | Cosine | choose from Cosine and L2 | -|MaxCheck | int | 8192 | how many nodes will be visited for a query in the search stage - -> BKT - -| ParametersName | type | default | definition| -|---|---|---|---| -| BKTNumber | int | 1 | number of BKT trees | -| BKTKMeansK | int | 32 | how many childs each tree node has | - -> KDT - -| ParametersName | type | default | definition| -|---|---|---|---| -| KDTNumber | int | 1 | number of KDT trees | - -> Parameters that will affect the index size -* NeighborhoodSize -* BKTNumber -* KDTNumber - -> Parameters that will affect the index build time -* NumberOfThreads -* TPTNumber -* TPTLeafSize -* GraphNeighborhoodScale -* CEF -* MaxCheckForRefineGraph - -> Parameters that will affect the index quality -* TPTNumber -* TPTLeafSize -* GraphNeighborhoodScale -* CEF -* MaxCheckForRefineGraph -* NeighborhoodSize -* KDTNumber - -> Parameters that will affect search latency and recall -* MaxCheck - -## **NNI for parameters tuning** - -Prepare vector data file **data.tsv**, query data file **query.tsv**, and truth file **truth.txt** following the format introduced in the [Get Started](GettingStart.md). - -Install [microsoft nni](https://github.com/microsoft/nni) and write the following python code (nni_sptag.py), parameter search space configuration (search_space.json) and nni environment configuration (config.yml). - -> nni_sptag.py - -```Python -import nni -import os - -vector_dimension = 10 -vector_type = 'Float' -index_algo = 'BKT' -threads = 32 -k = 3 - -def main(): - para = nni.get_next_parameter() - cmd_build = "./indexbuilder -d %d -v %s -i data.tsv -o index -a %s -t %d " % (vector_dimension, vector_type, index_algo, threads) - for p, v in para.items(): - cmd_build += "Index." + p + "=" + str(v) - cmd_test = "./indexsearcher index Index.QueryFile=query.tsv Index.TruthFile=truth.txt Index.K=%d" % (k) - os.system(cmd_build) - os.system(cmd_test + " > out.txt") - with open("out.txt", "r") as fd: - lines = fd.readlines() - res = lines[-2] - segs = res.split() - recall = float(segs[-2]) - avg_latency = float(segs[-5]) - score = recall - nni.report_final_result(score) - -if __name__ == '__main__': - main() -``` -> search_space.json - -```json -{ - "BKTKmeansK": {"_type": "choice", "_value": [2, 4, 8, 16, 32]}, - "GraphNeighborhoodScale": {"_type": "choice", "_value": [2, 4, 8, 16, 32]} -} - -``` - -> config.yml - -```yaml -authorName: default - -experimentName: example_sptag - -trialConcurrency: 1 - -maxExecDuration: 1h - -maxTrialNum: 10 - -#choice: local, remote, pai - -trainingServicePlatform: local - -searchSpacePath: search_space.json - -#choice: true, false - -useAnnotation: false - -tuner: - - #choice: TPE, Random, Anneal, Evolution, BatchTuner, MetisTuner - - #SMAC (SMAC should be installed through nnictl) - - builtinTunerName: TPE - - classArgs: - - #choice: maximize, minimize - - optimize_mode: maximize - -trial: - - command: python3 nni_sptag.py - - codeDir: . - - gpuNum: 0 - -``` - -Then start the tuning (tunning results can be found in the Web UI urls in the command output): -```bash -nnictl create --config config.yml -``` - -stop the tunning: -```bash -nnictl stop -``` \ No newline at end of file diff --git a/core/src/index/thirdparty/SPTAG/docs/img/sptag.png b/core/src/index/thirdparty/SPTAG/docs/img/sptag.png deleted file mode 100644 index dc21bdf3dc156c81106aa05c3a303a7ec35b8cc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 163816 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 BL&3tYc0HTU-C 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 -#include -#include "../src/kissrandom.h" -#include "../src/annoylib.h" -#include -#include -#include -#include - - -int precision(int f=40, int n=1000000){ - std::chrono::high_resolution_clock::time_point t_start, t_end; - - std::default_random_engine generator; - std::normal_distribution distribution(0.0, 1.0); - - //****************************************************** - //Building the tree - AnnoyIndex t = AnnoyIndex(f); - - std::cout << "Building index ... be patient !!" << std::endl; - std::cout << "\"Trees that are slow to grow bear the best fruit\" (Moliere)" << std::endl; - - - - for(int i=0; i( t_end - t_start ).count(); - std::cout << " Done in "<< duration << " secs." << std::endl; - - - std::cout << "Saving index ..."; - t.save("precision.tree"); - std::cout << " Done" << std::endl; - - - - //****************************************************** - std::vector limits = {10, 100, 1000, 10000}; - int K=10; - int prec_n = 1000; - - std::map prec_sum; - std::map time_sum; - std::vector closest; - - //init precision and timers map - for(std::vector::iterator it = limits.begin(); it!=limits.end(); ++it){ - prec_sum[(*it)] = 0.0; - time_sum[(*it)] = 0.0; - } - - // doing the work - for(int i=0; i toplist; - std::vector intersection; - - for(std::vector::iterator limit = limits.begin(); limit!=limits.end(); ++limit){ - - t_start = std::chrono::high_resolution_clock::now(); - t.get_nns_by_item(j, (*limit), (size_t) -1, &toplist, nullptr); //search_k defaults to "n_trees * n" if not provided. - t_end = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast( t_end - t_start ).count(); - - //intersecting results - std::sort(closest.begin(), closest.end(), std::less()); - std::sort(toplist.begin(), toplist.end(), std::less()); - intersection.resize(std::max(closest.size(), toplist.size())); - std::vector::iterator it_set = std::set_intersection(closest.begin(), closest.end(), toplist.begin(), toplist.end(), intersection.begin()); - intersection.resize(it_set-intersection.begin()); - - // storing metrics - int found = intersection.size(); - double hitrate = found / (double) K; - prec_sum[(*limit)] += hitrate; - - time_sum[(*limit)] += duration; - - - //deallocate memory - vector().swap(intersection); - vector().swap(toplist); - } - - //print resulting metrics - for(std::vector::iterator limit = limits.begin(); limit!=limits.end(); ++limit){ - std::cout << "limit: " << (*limit) << "\tprecision: "<< std::fixed << std::setprecision(2) << (100.0 * prec_sum[(*limit)] / (i + 1)) << "% \tavg. time: "<< std::fixed<< std::setprecision(6) << (time_sum[(*limit)] / (i + 1)) * 1e-04 << "s" << std::endl; - } - - closest.clear(); vector().swap(closest); - - } - - std::cout << "\nDone" << std::endl; - return 0; -} - - -void help(){ - std::cout << "Annoy Precision C++ example" << std::endl; - std::cout << "Usage:" << std::endl; - std::cout << "(default) ./precision" << std::endl; - std::cout << "(using parameters) ./precision num_features num_nodes" << std::endl; - std::cout << std::endl; -} - -void feedback(int f, int n){ - std::cout<<"Runing precision example with:" << std::endl; - std::cout<<"num. features: "<< f << std::endl; - std::cout<<"num. nodes: "<< n << std::endl; - std::cout << std::endl; -} - - -int main(int argc, char **argv) { - int f, n; - - - if(argc == 1){ - f = 40; - n = 1000000; - - feedback(f,n); - - precision(40, 1000000); - } - else if(argc == 3){ - - f = atoi(argv[1]); - n = atoi(argv[2]); - - feedback(f,n); - - precision(f, n); - } - else { - help(); - return EXIT_FAILURE; - } - - - return EXIT_SUCCESS; -} diff --git a/core/src/index/thirdparty/annoy/examples/precision_test.py b/core/src/index/thirdparty/annoy/examples/precision_test.py deleted file mode 100644 index d179e6b9ba..0000000000 --- a/core/src/index/thirdparty/annoy/examples/precision_test.py +++ /dev/null @@ -1,46 +0,0 @@ -from __future__ import print_function -import random, time -from annoy import AnnoyIndex - -try: - xrange -except NameError: - # Python 3 compat - xrange = range - -n, f = 100000, 40 - -t = AnnoyIndex(f, 'angular') -for i in xrange(n): - v = [] - for z in xrange(f): - v.append(random.gauss(0, 1)) - t.add_item(i, v) - -t.build(2 * f) -t.save('test.tree') - -limits = [10, 100, 1000, 10000] -k = 10 -prec_sum = {} -prec_n = 1000 -time_sum = {} - -for i in xrange(prec_n): - j = random.randrange(0, n) - - closest = set(t.get_nns_by_item(j, k, n)) - for limit in limits: - t0 = time.time() - toplist = t.get_nns_by_item(j, k, limit) - T = time.time() - t0 - - found = len(closest.intersection(toplist)) - hitrate = 1.0 * found / k - prec_sum[limit] = prec_sum.get(limit, 0.0) + hitrate - time_sum[limit] = time_sum.get(limit, 0.0) + T - -for limit in limits: - print('limit: %-9d precision: %6.2f%% avg time: %.6fs' - % (limit, 100.0 * prec_sum[limit] / (i + 1), - time_sum[limit] / (i + 1))) diff --git a/core/src/index/thirdparty/annoy/examples/s_compile_cpp.sh b/core/src/index/thirdparty/annoy/examples/s_compile_cpp.sh deleted file mode 100755 index 687a6082b2..0000000000 --- a/core/src/index/thirdparty/annoy/examples/s_compile_cpp.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - - -echo "compiling precision example..." -cmd="g++ precision_test.cpp -o precision_test -std=c++11" -eval $cmd -echo "Done" diff --git a/core/src/index/thirdparty/annoy/examples/simple_test.py b/core/src/index/thirdparty/annoy/examples/simple_test.py deleted file mode 100644 index 27e0343a26..0000000000 --- a/core/src/index/thirdparty/annoy/examples/simple_test.py +++ /dev/null @@ -1,10 +0,0 @@ -from annoy import AnnoyIndex - -a = AnnoyIndex(3, 'angular') -a.add_item(0, [1, 0, 0]) -a.add_item(1, [0, 1, 0]) -a.add_item(2, [0, 0, 1]) -a.build(-1) - -print(a.get_nns_by_item(0, 100)) -print(a.get_nns_by_vector([1.0, 0.5, 0.5], 100)) diff --git a/core/src/index/thirdparty/annoy/src/annoygomodule.h b/core/src/index/thirdparty/annoy/src/annoygomodule.h deleted file mode 100644 index c5fb408419..0000000000 --- a/core/src/index/thirdparty/annoy/src/annoygomodule.h +++ /dev/null @@ -1,92 +0,0 @@ -#include "annoylib.h" -#include "kissrandom.h" - -namespace GoAnnoy { - -class AnnoyIndex { - protected: - ::AnnoyIndexInterface *ptr; - - int f; - - public: - ~AnnoyIndex() { - delete ptr; - }; - void addItem(int item, const float* w) { - ptr->add_item(item, w); - }; - void build(int q) { - ptr->build(q); - }; - bool save(const char* filename, bool prefault) { - return ptr->save(filename, prefault); - }; - bool save(const char* filename) { - return ptr->save(filename, true); - }; - void unload() { - ptr->unload(); - }; - bool load(const char* filename, bool prefault) { - return ptr->load(filename, prefault); - }; - bool load(const char* filename) { - return ptr->load(filename, true); - }; - float getDistance(int i, int j) { - return ptr->get_distance(i, j); - }; - void getNnsByItem(int item, int n, int search_k, vector* result, vector* distances) { - ptr->get_nns_by_item(item, n, search_k, result, distances); - }; - void getNnsByVector(const float* w, int n, int search_k, vector* result, vector* distances) { - ptr->get_nns_by_vector(w, n, search_k, result, distances); - }; - void getNnsByItem(int item, int n, int search_k, vector* result) { - ptr->get_nns_by_item(item, n, search_k, result, nullptr); - }; - void getNnsByVector(const float* w, int n, int search_k, vector* result) { - ptr->get_nns_by_vector(w, n, search_k, result, nullptr); - }; - - int getNItems() { - return (int)ptr->get_n_items(); - }; - void verbose(bool v) { - ptr->verbose(v); - }; - void getItem(int item, vector *v) { - v->resize(this->f); - ptr->get_item(item, &v->front()); - }; - bool onDiskBuild(const char* filename) { - return ptr->on_disk_build(filename); - }; -}; - -class AnnoyIndexAngular : public AnnoyIndex -{ - public: - AnnoyIndexAngular(int f) { - ptr = new ::AnnoyIndex(f); - this->f = f; - } -}; - -class AnnoyIndexEuclidean : public AnnoyIndex { - public: - AnnoyIndexEuclidean(int f) { - ptr = new ::AnnoyIndex(f); - this->f = f; - } -}; - -class AnnoyIndexManhattan : public AnnoyIndex { - public: - AnnoyIndexManhattan(int f) { - ptr = new ::AnnoyIndex(f); - this->f = f; - } -}; -} diff --git a/core/src/index/thirdparty/annoy/src/annoygomodule.i b/core/src/index/thirdparty/annoy/src/annoygomodule.i deleted file mode 100644 index 9882cbeb2c..0000000000 --- a/core/src/index/thirdparty/annoy/src/annoygomodule.i +++ /dev/null @@ -1,96 +0,0 @@ -%module annoyindex - -%{ -#include "annoygomodule.h" -%} - - -// const float * -%typemap(gotype) (const float *) "[]float32" - -%typemap(in) (const float *) -%{ - float *v; - vector w; - v = (float *)$input.array; - for (int i = 0; i < $input.len; i++) { - w.push_back(v[i]); - } - $1 = &w[0]; -%} - -// vector * -%typemap(gotype) (vector *) "*[]int" - -%typemap(in) (vector *) -%{ - $1 = new vector(); -%} - -%typemap(freearg) (vector *) -%{ - delete $1; -%} - -%typemap(argout) (vector *) -%{ - { - $input->len = $1->size(); - $input->cap = $1->size(); - $input->array = malloc($input->len * sizeof(intgo)); - for (int i = 0; i < $1->size(); i++) { - ((intgo *)$input->array)[i] = (intgo)(*$1)[i]; - } - } -%} - - -// vector * -%typemap(gotype) (vector *) "*[]float32" - -%typemap(in) (vector *) -%{ - $1 = new vector(); -%} - -%typemap(freearg) (vector *) -%{ - delete $1; -%} - -%typemap(argout) (vector *) -%{ - { - $input->len = $1->size(); - $input->cap = $1->size(); - $input->array = malloc($input->len * sizeof(float)); - for (int i = 0; i < $1->size(); i++) { - ((float *)$input->array)[i] = (float)(*$1)[i]; - } - } -%} - - -%typemap(gotype) (const char *) "string" - -%typemap(in) (const char *) -%{ - $1 = (char *)calloc((((_gostring_)$input).n + 1), sizeof(char)); - strncpy($1, (((_gostring_)$input).p), ((_gostring_)$input).n); -%} - -%typemap(freearg) (const char *) -%{ - free($1); -%} - - -/* Let's just grab the original header file here */ -%include "annoygomodule.h" - -%feature("notabstract") GoAnnoyIndexAngular; -%feature("notabstract") GoAnnoyIndexEuclidean; -%feature("notabstract") GoAnnoyIndexManhattan; - - - diff --git a/core/src/index/thirdparty/annoy/src/annoylib.h b/core/src/index/thirdparty/annoy/src/annoylib.h deleted file mode 100644 index 91c11e513b..0000000000 --- a/core/src/index/thirdparty/annoy/src/annoylib.h +++ /dev/null @@ -1,1411 +0,0 @@ -// Copyright (c) 2013 Spotify AB -// -// Licensed 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 limitations under -// the License. - - -#ifndef ANNOYLIB_H -#define ANNOYLIB_H - -#include -#include -#ifndef _MSC_VER -#include -#endif -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) && _MSC_VER == 1500 -typedef unsigned char uint8_t; -typedef signed __int32 int32_t; -typedef unsigned __int64 uint64_t; -typedef signed __int64 int64_t; -#else -#include -#endif - -#if defined(_MSC_VER) || defined(__MINGW32__) - // a bit hacky, but override some definitions to support 64 bit - #define off_t int64_t - #define lseek_getsize(fd) _lseeki64(fd, 0, SEEK_END) - #ifndef NOMINMAX - #define NOMINMAX - #endif - #include "mman.h" - #include -#else - #include - #define lseek_getsize(fd) lseek(fd, 0, SEEK_END) -#endif - -#include -#include -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -// Needed for Visual Studio to disable runtime checks for mempcy -#pragma runtime_checks("s", off) -#endif - -// This allows others to supply their own logger / error printer without -// requiring Annoy to import their headers. See RcppAnnoy for a use case. -#ifndef __ERROR_PRINTER_OVERRIDE__ - #define showUpdate(...) { fprintf(stderr, __VA_ARGS__ ); } -#else - #define showUpdate(...) { __ERROR_PRINTER_OVERRIDE__( __VA_ARGS__ ); } -#endif - -// Portable alloc definition, cf Writing R Extensions, Section 1.6.4 -#ifdef __GNUC__ - // Includes GCC, clang and Intel compilers - # undef alloca - # define alloca(x) __builtin_alloca((x)) -#elif defined(__sun) || defined(_AIX) - // this is necessary (and sufficient) for Solaris 10 and AIX 6: - # include -#endif - -inline void set_error_from_errno(char **error, const char* msg) { - showUpdate("%s: %s (%d)\n", msg, strerror(errno), errno); - if (error) { - *error = (char *)malloc(256); // TODO: win doesn't support snprintf - sprintf(*error, "%s: %s (%d)", msg, strerror(errno), errno); - } -} - -inline void set_error_from_string(char **error, const char* msg) { - showUpdate("%s\n", msg); - if (error) { - *error = (char *)malloc(strlen(msg) + 1); - strcpy(*error, msg); - } -} - -// We let the v array in the Node struct take whatever space is needed, so this is a mostly insignificant number. -// Compilers need *some* size defined for the v array, and some memory checking tools will flag for buffer overruns if this is set too low. -#define V_ARRAY_SIZE 65536 - -#ifndef _MSC_VER -#define popcount __builtin_popcountll -#else // See #293, #358 -#define isnan(x) _isnan(x) -#define popcount cole_popcount -#endif - -#if !defined(NO_MANUAL_VECTORIZATION) && defined(__GNUC__) && (__GNUC__ >6) && defined(__AVX512F__) // See #402 -#define USE_AVX512 -#elif !defined(NO_MANUAL_VECTORIZATION) && defined(__AVX__) && defined (__SSE__) && defined(__SSE2__) && defined(__SSE3__) -#define USE_AVX -#else -#endif - -#if defined(USE_AVX) || defined(USE_AVX512) -#if defined(_MSC_VER) -#include -#elif defined(__GNUC__) -#include -#include - -#endif -#endif - -#include -#include - -using std::vector; -using std::pair; -using std::numeric_limits; -using std::make_pair; - -inline void* remap_memory(void* _ptr, int _fd, size_t old_size, size_t new_size) { -#ifdef __linux__ - _ptr = mremap(_ptr, old_size, new_size, MREMAP_MAYMOVE); -#else - munmap(_ptr, old_size); -#ifdef MAP_POPULATE - _ptr = mmap(_ptr, new_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, _fd, 0); -#else - _ptr = mmap(_ptr, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0); -#endif -#endif - return _ptr; -} - -namespace { - -template -inline Node* get_node_ptr(const void* _nodes, const size_t _s, const S i) { - return (Node*)((uint8_t *)_nodes + (_s * i)); -} - -template -inline T dot(const T* x, const T* y, int f) { - T s = 0; - for (int z = 0; z < f; z++) { - s += (*x) * (*y); - x++; - y++; - } - return s; -} - -template -inline T manhattan_distance(const T* x, const T* y, int f) { - T d = 0.0; - for (int i = 0; i < f; i++) - d += fabs(x[i] - y[i]); - return d; -} - -template -inline T euclidean_distance(const T* x, const T* y, int f) { - // Don't use dot-product: avoid catastrophic cancellation in #314. - T d = 0.0; - for (int i = 0; i < f; ++i) { - const T tmp=*x - *y; - d += tmp * tmp; - ++x; - ++y; - } - return d; -} - -//#ifdef USE_AVX -// Horizontal single sum of 256bit vector. -#if 0 /* use FAISS distance calculation algorithm instead */ -inline float hsum256_ps_avx(__m256 v) { - const __m128 x128 = _mm_add_ps(_mm256_extractf128_ps(v, 1), _mm256_castps256_ps128(v)); - const __m128 x64 = _mm_add_ps(x128, _mm_movehl_ps(x128, x128)); - const __m128 x32 = _mm_add_ss(x64, _mm_shuffle_ps(x64, x64, 0x55)); - return _mm_cvtss_f32(x32); -} -#endif - -template<> -inline float dot(const float* x, const float *y, int f) { -#if 0 /* use FAISS distance calculation algorithm instead */ - float result = 0; - if (f > 7) { - __m256 d = _mm256_setzero_ps(); - for (; f > 7; f -= 8) { - d = _mm256_add_ps(d, _mm256_mul_ps(_mm256_loadu_ps(x), _mm256_loadu_ps(y))); - x += 8; - y += 8; - } - // Sum all floats in dot register. - result += hsum256_ps_avx(d); - } - // Don't forget the remaining values. - for (; f > 0; f--) { - result += *x * *y; - x++; - y++; - } - return result; -#else - return faiss::fvec_inner_product(x, y, (size_t)f); -#endif -} - -template<> -inline float manhattan_distance(const float* x, const float* y, int f) { -#if 0 /* use FAISS distance calculation algorithm instead */ - float result = 0; - int i = f; - if (f > 7) { - __m256 manhattan = _mm256_setzero_ps(); - __m256 minus_zero = _mm256_set1_ps(-0.0f); - for (; i > 7; i -= 8) { - const __m256 x_minus_y = _mm256_sub_ps(_mm256_loadu_ps(x), _mm256_loadu_ps(y)); - const __m256 distance = _mm256_andnot_ps(minus_zero, x_minus_y); // Absolute value of x_minus_y (forces sign bit to zero) - manhattan = _mm256_add_ps(manhattan, distance); - x += 8; - y += 8; - } - // Sum all floats in manhattan register. - result = hsum256_ps_avx(manhattan); - } - // Don't forget the remaining values. - for (; i > 0; i--) { - result += fabsf(*x - *y); - x++; - y++; - } - return result; -#else - return faiss::fvec_L1(x, y, (size_t)f); -#endif -} - -template<> -inline float euclidean_distance(const float* x, const float* y, int f) { -#if 0 /* use FAISS distance calculation algorithm instead */ - float result=0; - if (f > 7) { - __m256 d = _mm256_setzero_ps(); - for (; f > 7; f -= 8) { - const __m256 diff = _mm256_sub_ps(_mm256_loadu_ps(x), _mm256_loadu_ps(y)); - d = _mm256_add_ps(d, _mm256_mul_ps(diff, diff)); // no support for fmadd in AVX... - x += 8; - y += 8; - } - // Sum all floats in dot register. - result = hsum256_ps_avx(d); - } - // Don't forget the remaining values. - for (; f > 0; f--) { - float tmp = *x - *y; - result += tmp * tmp; - x++; - y++; - } - return result; -#else - return faiss::fvec_L2sqr(x, y, (size_t)f); -#endif -} - -//#endif - -#if 0 /* use FAISS distance calculation algorithm instead */ -#ifdef USE_AVX512 -template<> -inline float dot(const float* x, const float *y, int f) { - float result = 0; - if (f > 15) { - __m512 d = _mm512_setzero_ps(); - for (; f > 15; f -= 16) { - //AVX512F includes FMA - d = _mm512_fmadd_ps(_mm512_loadu_ps(x), _mm512_loadu_ps(y), d); - x += 16; - y += 16; - } - // Sum all floats in dot register. - result += _mm512_reduce_add_ps(d); - } - // Don't forget the remaining values. - for (; f > 0; f--) { - result += *x * *y; - x++; - y++; - } - return result; -} - -template<> -inline float manhattan_distance(const float* x, const float* y, int f) { - float result = 0; - int i = f; - if (f > 15) { - __m512 manhattan = _mm512_setzero_ps(); - for (; i > 15; i -= 16) { - const __m512 x_minus_y = _mm512_sub_ps(_mm512_loadu_ps(x), _mm512_loadu_ps(y)); - manhattan = _mm512_add_ps(manhattan, _mm512_abs_ps(x_minus_y)); - x += 16; - y += 16; - } - // Sum all floats in manhattan register. - result = _mm512_reduce_add_ps(manhattan); - } - // Don't forget the remaining values. - for (; i > 0; i--) { - result += fabsf(*x - *y); - x++; - y++; - } - return result; -} - -template<> -inline float euclidean_distance(const float* x, const float* y, int f) { - float result=0; - if (f > 15) { - __m512 d = _mm512_setzero_ps(); - for (; f > 15; f -= 16) { - const __m512 diff = _mm512_sub_ps(_mm512_loadu_ps(x), _mm512_loadu_ps(y)); - d = _mm512_fmadd_ps(diff, diff, d); - x += 16; - y += 16; - } - // Sum all floats in dot register. - result = _mm512_reduce_add_ps(d); - } - // Don't forget the remaining values. - for (; f > 0; f--) { - float tmp = *x - *y; - result += tmp * tmp; - x++; - y++; - } - return result; -} - -#endif -#endif - - -template -inline T get_norm(T* v, int f) { - return sqrt(dot(v, v, f)); -} - -template -inline void two_means(const vector& nodes, int f, Random& random, bool cosine, Node* p, Node* q) { - /* - This algorithm is a huge heuristic. Empirically it works really well, but I - can't motivate it well. The basic idea is to keep two centroids and assign - points to either one of them. We weight each centroid by the number of points - assigned to it, so to balance it. - */ - static int iteration_steps = 200; - size_t count = nodes.size(); - - size_t i = random.index(count); - size_t j = random.index(count-1); - j += (j >= i); // ensure that i != j - - Distance::template copy_node(p, nodes[i], f); - Distance::template copy_node(q, nodes[j], f); - - if (cosine) { Distance::template normalize(p, f); Distance::template normalize(q, f); } - Distance::init_node(p, f); - Distance::init_node(q, f); - - int ic = 1, jc = 1; - for (int l = 0; l < iteration_steps; l++) { - size_t k = random.index(count); - T di = ic * Distance::distance(p, nodes[k], f), - dj = jc * Distance::distance(q, nodes[k], f); - T norm = cosine ? get_norm(nodes[k]->v, f) : 1; - if (!(norm > T(0))) { - continue; - } - if (di < dj) { - for (int z = 0; z < f; z++) - p->v[z] = (p->v[z] * ic + nodes[k]->v[z] / norm) / (ic + 1); - Distance::init_node(p, f); - ic++; - } else if (dj < di) { - for (int z = 0; z < f; z++) - q->v[z] = (q->v[z] * jc + nodes[k]->v[z] / norm) / (jc + 1); - Distance::init_node(q, f); - jc++; - } - } -} -} // namespace - -struct Base { - template - static inline void preprocess(void* nodes, size_t _s, const S node_count, const int f) { - // Override this in specific metric structs below if you need to do any pre-processing - // on the entire set of nodes passed into this index. - } - - template - static inline void zero_value(Node* dest) { - // Initialize any fields that require sane defaults within this node. - } - - template - static inline void copy_node(Node* dest, const Node* source, const int f) { - memcpy(dest->v, source->v, f * sizeof(T)); - } - - template - static inline void normalize(Node* node, int f) { - T norm = get_norm(node->v, f); - if (norm > 0) { - for (int z = 0; z < f; z++) - node->v[z] /= norm; - } - } -}; - -struct Angular : Base { - template - struct Node { - /* - * We store a binary tree where each node has two things - * - A vector associated with it - * - Two children - * All nodes occupy the same amount of memory - * All nodes with n_descendants == 1 are leaf nodes. - * A memory optimization is that for nodes with 2 <= n_descendants <= K, - * we skip the vector. Instead we store a list of all descendants. K is - * determined by the number of items that fits in the space of the vector. - * For nodes with n_descendants == 1 the vector is a data point. - * For nodes with n_descendants > K the vector is the normal of the split plane. - * Note that we can't really do sizeof(node) because we cheat and allocate - * more memory to be able to fit the vector outside - */ - S n_descendants; - union { - S children[2]; // Will possibly store more than 2 - T norm; - }; - T v[V_ARRAY_SIZE]; - }; - template - static inline T distance(const Node* x, const Node* y, int f) { - // want to calculate (a/|a| - b/|b|)^2 - // = a^2 / a^2 + b^2 / b^2 - 2ab/|a||b| - // = 2 - 2cos - T pp = x->norm ? x->norm : dot(x->v, x->v, f); // For backwards compatibility reasons, we need to fall back and compute the norm here - T qq = y->norm ? y->norm : dot(y->v, y->v, f); - T pq = dot(x->v, y->v, f); - T ppqq = pp * qq; - if (ppqq > 0) return 2.0 - 2.0 * pq / sqrt(ppqq); - else return 2.0; // cos is 0 - } - template - static inline T margin(const Node* n, const T* y, int f) { - return dot(n->v, y, f); - } - template - static inline bool side(const Node* n, const T* y, int f, Random& random) { - T dot = margin(n, y, f); - if (dot != 0) - return (dot > 0); - else - return (bool)random.flip(); - } - template - static inline void create_split(const vector*>& nodes, int f, size_t s, Random& random, Node* n) { - Node* p = (Node*)alloca(s); - Node* q = (Node*)alloca(s); - two_means >(nodes, f, random, true, p, q); - for (int z = 0; z < f; z++) - n->v[z] = p->v[z] - q->v[z]; - Base::normalize >(n, f); - } - template - static inline T normalized_distance(T distance) { - // Used when requesting distances from Python layer - // Turns out sometimes the squared distance is -0.0 - // so we have to make sure it's a positive number. - return sqrt(std::max(distance, T(0))); - } - template - static inline T pq_distance(T distance, T margin, int child_nr) { - if (child_nr == 0) - margin = -margin; - return std::min(distance, margin); - } - template - static inline T pq_initial_value() { - return numeric_limits::infinity(); - } - template - static inline void init_node(Node* n, int f) { - n->norm = dot(n->v, n->v, f); - } - static const char* name() { - return "angular"; - } -}; - - -struct DotProduct : Angular { - template - struct Node { - /* - * This is an extension of the Angular node with an extra attribute for the scaled norm. - */ - S n_descendants; - S children[2]; // Will possibly store more than 2 - T dot_factor; - T v[V_ARRAY_SIZE]; - }; - - static const char* name() { - return "dot"; - } - template - static inline T distance(const Node* x, const Node* y, int f) { - return -dot(x->v, y->v, f); - } - - template - static inline void zero_value(Node* dest) { - dest->dot_factor = 0; - } - - template - static inline void init_node(Node* n, int f) { - } - - template - static inline void copy_node(Node* dest, const Node* source, const int f) { - memcpy(dest->v, source->v, f * sizeof(T)); - dest->dot_factor = source->dot_factor; - } - - template - static inline void create_split(const vector*>& nodes, int f, size_t s, Random& random, Node* n) { - Node* p = (Node*)alloca(s); - Node* q = (Node*)alloca(s); - DotProduct::zero_value(p); - DotProduct::zero_value(q); - two_means >(nodes, f, random, true, p, q); - for (int z = 0; z < f; z++) - n->v[z] = p->v[z] - q->v[z]; - n->dot_factor = p->dot_factor - q->dot_factor; - DotProduct::normalize >(n, f); - } - - template - static inline void normalize(Node* node, int f) { - T norm = sqrt(dot(node->v, node->v, f) + pow(node->dot_factor, 2)); - if (norm > 0) { - for (int z = 0; z < f; z++) - node->v[z] /= norm; - node->dot_factor /= norm; - } - } - - template - static inline T margin(const Node* n, const T* y, int f) { - return dot(n->v, y, f) + (n->dot_factor * n->dot_factor); - } - - template - static inline bool side(const Node* n, const T* y, int f, Random& random) { - T dot = margin(n, y, f); - if (dot != 0) - return (dot > 0); - else - return (bool)random.flip(); - } - - template - static inline T normalized_distance(T distance) { - return -distance; - } - - template - static inline void preprocess(void* nodes, size_t _s, const S node_count, const int f) { - // This uses a method from Microsoft Research for transforming inner product spaces to cosine/angular-compatible spaces. - // (Bachrach et al., 2014, see https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/XboxInnerProduct.pdf) - - // Step one: compute the norm of each vector and store that in its extra dimension (f-1) - for (S i = 0; i < node_count; i++) { - Node* node = get_node_ptr(nodes, _s, i); - T norm = sqrt(dot(node->v, node->v, f)); - if (isnan(norm)) norm = 0; - node->dot_factor = norm; - } - - // Step two: find the maximum norm - T max_norm = 0; - for (S i = 0; i < node_count; i++) { - Node* node = get_node_ptr(nodes, _s, i); - if (node->dot_factor > max_norm) { - max_norm = node->dot_factor; - } - } - - // Step three: set each vector's extra dimension to sqrt(max_norm^2 - norm^2) - for (S i = 0; i < node_count; i++) { - Node* node = get_node_ptr(nodes, _s, i); - T node_norm = node->dot_factor; - - T dot_factor = sqrt(pow(max_norm, static_cast(2.0)) - pow(node_norm, static_cast(2.0))); - if (isnan(dot_factor)) dot_factor = 0; - - node->dot_factor = dot_factor; - } - } -}; - -struct Hamming : Base { - template - struct Node { - S n_descendants; - S children[2]; - T v[V_ARRAY_SIZE]; - }; - - static const size_t max_iterations = 20; - - template - static inline T pq_distance(T distance, T margin, int child_nr) { - return distance - (margin != (unsigned int) child_nr); - } - - template - static inline T pq_initial_value() { - return numeric_limits::max(); - } - template - static inline int cole_popcount(T v) { - // Note: Only used with MSVC 9, which lacks intrinsics and fails to - // calculate std::bitset::count for v > 32bit. Uses the generalized - // approach by Eric Cole. - // See https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSet64 - v = v - ((v >> 1) & (T)~(T)0/3); - v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); - v = (v + (v >> 4)) & (T)~(T)0/255*15; - return (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * 8; - } - template - static inline T distance(const Node* x, const Node* y, int f) { - size_t dist = 0; - for (int i = 0; i < f; i++) { - dist += popcount(x->v[i] ^ y->v[i]); - } - return dist; - } - template - static inline bool margin(const Node* n, const T* y, int f) { - static const size_t n_bits = sizeof(T) * 8; - T chunk = n->v[0] / n_bits; - return (y[chunk] & (static_cast(1) << (n_bits - 1 - (n->v[0] % n_bits)))) != 0; - } - template - static inline bool side(const Node* n, const T* y, int f, Random& random) { - return margin(n, y, f); - } - template - static inline void create_split(const vector*>& nodes, int f, size_t s, Random& random, Node* n) { - size_t cur_size = 0; - size_t i = 0; - int dim = f * 8 * sizeof(T); - for (; i < max_iterations; i++) { - // choose random position to split at - n->v[0] = random.index(dim); - cur_size = 0; - for (typename vector*>::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { - if (margin(n, (*it)->v, f)) { - cur_size++; - } - } - if (cur_size > 0 && cur_size < nodes.size()) { - break; - } - } - // brute-force search for splitting coordinate - if (i == max_iterations) { - int j = 0; - for (; j < dim; j++) { - n->v[0] = j; - cur_size = 0; - for (typename vector*>::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { - if (margin(n, (*it)->v, f)) { - cur_size++; - } - } - if (cur_size > 0 && cur_size < nodes.size()) { - break; - } - } - } - } - template - static inline T normalized_distance(T distance) { - return distance; - } - template - static inline void init_node(Node* n, int f) { - } - static const char* name() { - return "hamming"; - } -}; - - -struct Minkowski : Base { - template - struct Node { - S n_descendants; - T a; // need an extra constant term to determine the offset of the plane - S children[2]; - T v[V_ARRAY_SIZE]; - }; - template - static inline T margin(const Node* n, const T* y, int f) { - return n->a + dot(n->v, y, f); - } - template - static inline bool side(const Node* n, const T* y, int f, Random& random) { - T dot = margin(n, y, f); - if (dot != 0) - return (dot > 0); - else - return (bool)random.flip(); - } - template - static inline T pq_distance(T distance, T margin, int child_nr) { - if (child_nr == 0) - margin = -margin; - return std::min(distance, margin); - } - template - static inline T pq_initial_value() { - return numeric_limits::infinity(); - } -}; - - -struct Euclidean : Minkowski { - template - static inline T distance(const Node* x, const Node* y, int f) { - return euclidean_distance(x->v, y->v, f); - } - template - static inline void create_split(const vector*>& nodes, int f, size_t s, Random& random, Node* n) { - Node* p = (Node*)alloca(s); - Node* q = (Node*)alloca(s); - two_means >(nodes, f, random, false, p, q); - - for (int z = 0; z < f; z++) - n->v[z] = p->v[z] - q->v[z]; - Base::normalize >(n, f); - n->a = 0.0; - for (int z = 0; z < f; z++) - n->a += -n->v[z] * (p->v[z] + q->v[z]) / 2; - } - template - static inline T normalized_distance(T distance) { - return distance; - } - template - static inline void init_node(Node* n, int f) { - } - static const char* name() { - return "euclidean"; - } - -}; - -struct Manhattan : Minkowski { - template - static inline T distance(const Node* x, const Node* y, int f) { - return manhattan_distance(x->v, y->v, f); - } - template - static inline void create_split(const vector*>& nodes, int f, size_t s, Random& random, Node* n) { - Node* p = (Node*)alloca(s); - Node* q = (Node*)alloca(s); - two_means >(nodes, f, random, false, p, q); - - for (int z = 0; z < f; z++) - n->v[z] = p->v[z] - q->v[z]; - Base::normalize >(n, f); - n->a = 0.0; - for (int z = 0; z < f; z++) - n->a += -n->v[z] * (p->v[z] + q->v[z]) / 2; - } - template - static inline T normalized_distance(T distance) { - return std::max(distance, T(0)); - } - template - static inline void init_node(Node* n, int f) { - } - static const char* name() { - return "manhattan"; - } -}; - -template -class AnnoyIndexInterface { - public: - // Note that the methods with an **error argument will allocate memory and write the pointer to that string if error is non-nullptr - virtual ~AnnoyIndexInterface() {}; - virtual bool add_item(S item, const T* w, char** error=nullptr) = 0; - virtual bool build(int q, char** error=nullptr) = 0; - virtual bool unbuild(char** error=nullptr) = 0; - virtual bool save(const char* filename, bool prefault=false, char** error=nullptr) = 0; - virtual void unload() = 0; - virtual bool load(const char* filename, bool prefault=false, char** error=nullptr) = 0; - virtual bool load_index(void* index_data, const int64_t& index_size, char** error = nullptr) = 0; - virtual T get_distance(S i, S j) const = 0; - virtual void get_nns_by_item(S item, size_t n, int64_t search_k, vector* result, vector* distances, - faiss::ConcurrentBitsetPtr& bitset = nullptr) const = 0; - virtual void get_nns_by_vector(const T* w, size_t n, int64_t search_k, vector* result, vector* distances, - faiss::ConcurrentBitsetPtr& bitset = nullptr) const = 0; - virtual S get_n_items() const = 0; - virtual S get_dim() const = 0; - virtual S get_n_trees() const = 0; - virtual int64_t get_index_length() const = 0; - virtual void* get_index() const = 0; - virtual void verbose(bool v) = 0; - virtual void get_item(S item, T* v) const = 0; - virtual void set_seed(int q) = 0; - virtual bool on_disk_build(const char* filename, char** error=nullptr) = 0; - virtual int64_t cal_size() = 0; -}; - -template - class AnnoyIndex : public AnnoyIndexInterface { - /* - * We use random projection to build a forest of binary trees of all items. - * Basically just split the hyperspace into two sides by a hyperplane, - * then recursively split each of those subtrees etc. - * We create a tree like this q times. The default q is determined automatically - * in such a way that we at most use 2x as much memory as the vectors take. - */ -public: - typedef Distance D; - typedef typename D::template Node Node; - -protected: - const int _f; - size_t _s; - S _n_items; - Random _random; - void* _nodes; // Could either be mmapped, or point to a memory buffer that we reallocate - S _n_nodes; - S _nodes_size; - vector _roots; - S _K; - bool _loaded; - bool _verbose; - int _fd; - bool _on_disk; - bool _built; -public: - - AnnoyIndex(int f) : _f(f), _random() { - _s = offsetof(Node, v) + _f * sizeof(T); // Size of each node - _verbose = false; - _built = false; - _K = (S) (((size_t) (_s - offsetof(Node, children))) / sizeof(S)); // Max number of descendants to fit into node - reinitialize(); // Reset everything - } - ~AnnoyIndex() { - unload(); - } - - int get_f() const { - return _f; - } - - bool add_item(S item, const T* w, char** error=nullptr) { - return add_item_impl(item, w, error); - } - - template - bool add_item_impl(S item, const W& w, char** error=nullptr) { - if (_loaded) { - set_error_from_string(error, "You can't add an item to a loaded index"); - return false; - } - _allocate_size(item + 1); - Node* n = _get(item); - - D::zero_value(n); - - n->children[0] = 0; - n->children[1] = 0; - n->n_descendants = 1; - - for (int z = 0; z < _f; z++) - n->v[z] = w[z]; - - D::init_node(n, _f); - - if (item >= _n_items) - _n_items = item + 1; - - return true; - } - - bool on_disk_build(const char* file, char** error=nullptr) { - _on_disk = true; - _fd = open(file, O_RDWR | O_CREAT | O_TRUNC, (int) 0600); - if (_fd == -1) { - set_error_from_errno(error, "Unable to open"); - _fd = 0; - return false; - } - _nodes_size = 1; - if (ftruncate(_fd, _s * _nodes_size) == -1) { - set_error_from_errno(error, "Unable to truncate"); - return false; - } -#ifdef MAP_POPULATE - _nodes = (Node*) mmap(0, _s * _nodes_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, _fd, 0); -#else - _nodes = (Node*) mmap(0, _s * _nodes_size, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0); -#endif - return true; - } - - bool build(int q, char** error=nullptr) { - if (_loaded) { - set_error_from_string(error, "You can't build a loaded index"); - return false; - } - - if (_built) { - set_error_from_string(error, "You can't build a built index"); - return false; - } - - D::template preprocess(_nodes, _s, _n_items, _f); - - _n_nodes = _n_items; - while (1) { - if (q == -1 && _n_nodes >= _n_items * 2) - break; - if (q != -1 && _roots.size() >= (size_t)q) - break; - if (_verbose) showUpdate("pass %zd...\n", _roots.size()); - - vector indices; - for (S i = 0; i < _n_items; i++) { - if (_get(i)->n_descendants >= 1) // Issue #223 - indices.push_back(i); - } - - _roots.push_back(_make_tree(indices, true)); - } - - // Also, copy the roots into the last segment of the array - // This way we can load them faster without reading the whole file - _allocate_size(_n_nodes + (S)_roots.size()); - for (size_t i = 0; i < _roots.size(); i++) - memcpy(_get(_n_nodes + (S)i), _get(_roots[i]), _s); - _n_nodes += _roots.size(); - - if (_verbose) showUpdate("has %ld nodes\n", _n_nodes); - - if (_on_disk) { - _nodes = remap_memory(_nodes, _fd, _s * _nodes_size, _s * _n_nodes); - if (ftruncate(_fd, _s * _n_nodes)) { - // TODO: this probably creates an index in a corrupt state... not sure what to do - set_error_from_errno(error, "Unable to truncate"); - return false; - } - _nodes_size = _n_nodes; - } - _built = true; - return true; - } - - bool unbuild(char** error=nullptr) { - if (_loaded) { - set_error_from_string(error, "You can't unbuild a loaded index"); - return false; - } - - _roots.clear(); - _n_nodes = _n_items; - _built = false; - - return true; - } - - bool save(const char* filename, bool prefault=false, char** error=nullptr) { - if (!_built) { - set_error_from_string(error, "You can't save an index that hasn't been built"); - return false; - } - if (_on_disk) { - return true; - } else { - // Delete file if it already exists (See issue #335) - unlink(filename); - - FILE *f = fopen(filename, "wb"); - if (f == nullptr) { - set_error_from_errno(error, "Unable to open"); - return false; - } - - if (fwrite(_nodes, _s, _n_nodes, f) != (size_t) _n_nodes) { - set_error_from_errno(error, "Unable to write"); - return false; - } - - if (fclose(f) == EOF) { - set_error_from_errno(error, "Unable to close"); - return false; - } - - unload(); - return load(filename, prefault, error); - } - } - - void reinitialize() { - _fd = 0; - _nodes = nullptr; - _loaded = false; - _n_items = 0; - _n_nodes = 0; - _nodes_size = 0; - _on_disk = false; - _roots.clear(); - } - - void unload() { - if (_on_disk && _fd) { - close(_fd); - munmap(_nodes, _s * _nodes_size); - } else { - if (_fd) { - // we have mmapped data - close(_fd); - munmap(_nodes, _n_nodes * _s); - } else if (_nodes) { - // We have heap allocated data - free(_nodes); - } - } - reinitialize(); - if (_verbose) showUpdate("unloaded\n"); - } - - bool load(const char* filename, bool prefault=false, char** error=nullptr) { - _fd = open(filename, O_RDONLY, (int)0400); - if (_fd == -1) { - set_error_from_errno(error, "Unable to open"); - _fd = 0; - return false; - } - off_t size = lseek_getsize(_fd); - if (size == -1) { - set_error_from_errno(error, "Unable to get size"); - return false; - } else if (size == 0) { - set_error_from_errno(error, "Size of file is zero"); - return false; - } else if (size % _s) { - // Something is fishy with this index! - set_error_from_errno(error, "Index size is not a multiple of vector size"); - return false; - } - - int flags = MAP_SHARED; - if (prefault) { -#ifdef MAP_POPULATE - flags |= MAP_POPULATE; -#else - showUpdate("prefault is set to true, but MAP_POPULATE is not defined on this platform"); -#endif - } - _nodes = (Node*)mmap(0, size, PROT_READ, flags, _fd, 0); - _n_nodes = (S)(size / _s); - - // Find the roots by scanning the end of the file and taking the nodes with most descendants - _roots.clear(); - S m = -1; - for (S i = _n_nodes - 1; i >= 0; i--) { - S k = _get(i)->n_descendants; - if (m == -1 || k == m) { - _roots.push_back(i); - m = k; - } else { - break; - } - } - // hacky fix: since the last root precedes the copy of all roots, delete it - if (_roots.size() > 1 && _get(_roots.front())->children[0] == _get(_roots.back())->children[0]) - _roots.pop_back(); - _loaded = true; - _built = true; - _n_items = m; - if (_verbose) showUpdate("found %lu roots with degree %ld\n", _roots.size(), m); - return true; - } - - bool load_index(void* index_data, const int64_t& index_size, char** error) { - if (index_size == -1) { - set_error_from_errno(error, "Unable to get size"); - return false; - } else if (index_size == 0) { - set_error_from_errno(error, "Size of file is zero"); - return false; - } else if (index_size % _s) { - // Something is fishy with this index! - set_error_from_errno(error, "Index size is not a multiple of vector size"); - return false; - } - - _n_nodes = (S)(index_size / _s); -// _nodes = (Node*)malloc(_s * _n_nodes); - _nodes = (Node*)malloc((size_t)index_size); - if (_nodes == nullptr) { - set_error_from_errno(error, "alloc failed when load_index 4 annoy"); - return false; - } - memcpy(_nodes, index_data, (size_t)index_size); - - // Find the roots by scanning the end of the file and taking the nodes with most descendants - _roots.clear(); - S m = -1; - for (S i = _n_nodes - 1; i >= 0; i--) { - S k = _get(i)->n_descendants; - if (m == -1 || k == m) { - _roots.push_back(i); - m = k; - } else { - break; - } - } - // hacky fix: since the last root precedes the copy of all roots, delete it - if (_roots.size() > 1 && _get(_roots.front())->children[0] == _get(_roots.back())->children[0]) - _roots.pop_back(); - _loaded = true; - _built = true; - _n_items = m; - if (_verbose) showUpdate("found %lu roots with degree %ld\n", _roots.size(), m); - return true; - } - - T get_distance(S i, S j) const { - return D::normalized_distance(D::distance(_get(i), _get(j), _f)); - } - - void get_nns_by_item(S item, size_t n, int64_t search_k, vector* result, vector* distances, - faiss::ConcurrentBitsetPtr& bitset) const { - // TODO: handle OOB - const Node* m = _get(item); - _get_all_nns(m->v, n, search_k, result, distances, bitset); - } - - void get_nns_by_vector(const T* w, size_t n, int64_t search_k, vector* result, vector* distances, - faiss::ConcurrentBitsetPtr& bitset) const { - _get_all_nns(w, n, search_k, result, distances, bitset); - } - - S get_n_items() const { - return _n_items; - } - - S get_dim() const { - return _f; - } - - S get_n_trees() const { - return (S)_roots.size(); - } - - int64_t get_index_length() const { - return (int64_t)_s * _n_nodes; - } - - void* get_index() const { - return _nodes; - } - - void verbose(bool v) { - _verbose = v; - } - - void get_item(S item, T* v) const { - // TODO: handle OOB - Node* m = _get(item); - memcpy(v, m->v, (_f) * sizeof(T)); - } - - void set_seed(int seed) { - _random.set_seed(seed); - } - -protected: - void _allocate_size(S n) { - if (n > _nodes_size) { - const double reallocation_factor = 1.3; - S new_nodes_size = std::max(n, (S) ((_nodes_size + 1) * reallocation_factor)); - void *old = _nodes; - - if (_on_disk) { - int rc = ftruncate(_fd, _s * new_nodes_size); - if (_verbose && rc) showUpdate("File truncation error\n"); - _nodes = remap_memory(_nodes, _fd, _s * _nodes_size, _s * new_nodes_size); - } else { - _nodes = realloc(_nodes, _s * new_nodes_size); - memset((char *) _nodes + (_nodes_size * _s) / sizeof(char), 0, (new_nodes_size - _nodes_size) * _s); - } - - _nodes_size = new_nodes_size; - if (_verbose) showUpdate("Reallocating to %ld nodes: old_address=%p, new_address=%p\n", new_nodes_size, old, _nodes); - } - } - - inline Node* _get(const S i) const { - return get_node_ptr(_nodes, _s, i); - } - - S _make_tree(const vector& indices, bool is_root) { - // The basic rule is that if we have <= _K items, then it's a leaf node, otherwise it's a split node. - // There's some regrettable complications caused by the problem that root nodes have to be "special": - // 1. We identify root nodes by the arguable logic that _n_items == n->n_descendants, regardless of how many descendants they actually have - // 2. Root nodes with only 1 child need to be a "dummy" parent - // 3. Due to the _n_items "hack", we need to be careful with the cases where _n_items <= _K or _n_items > _K - if (indices.size() == 1 && !is_root) - return indices[0]; - - if (indices.size() <= (size_t)_K && (!is_root || (size_t)_n_items <= (size_t)_K || indices.size() == 1)) { - _allocate_size(_n_nodes + 1); - S item = _n_nodes++; - Node* m = _get(item); - m->n_descendants = is_root ? _n_items : (S)indices.size(); - - // Using std::copy instead of a loop seems to resolve issues #3 and #13, - // probably because gcc 4.8 goes overboard with optimizations. - // Using memcpy instead of std::copy for MSVC compatibility. #235 - // Only copy when necessary to avoid crash in MSVC 9. #293 - if (!indices.empty()) - memcpy(m->children, &indices[0], indices.size() * sizeof(S)); - return item; - } - - vector children; - for (size_t i = 0; i < indices.size(); i++) { - S j = indices[i]; - Node* n = _get(j); - if (n) - children.push_back(n); - } - - vector children_indices[2]; - Node* m = (Node*)alloca(_s); - D::create_split(children, _f, _s, _random, m); - faiss::BuilderSuspend::check_wait(); - - for (size_t i = 0; i < indices.size(); i++) { - S j = indices[i]; - Node* n = _get(j); - if (n) { - bool side = D::side(m, n->v, _f, _random); - children_indices[side].push_back(j); - } else { - showUpdate("No node for index %ld?\n", j); - } - } - - // If we didn't find a hyperplane, just randomize sides as a last option - while (children_indices[0].size() == 0 || children_indices[1].size() == 0) { - if (_verbose) - showUpdate("\tNo hyperplane found (left has %ld children, right has %ld children)\n", - children_indices[0].size(), children_indices[1].size()); - if (_verbose && indices.size() > 100000) - showUpdate("Failed splitting %lu items\n", indices.size()); - - children_indices[0].clear(); - children_indices[1].clear(); - - // Set the vector to 0.0 - for (int z = 0; z < _f; z++) - m->v[z] = 0; - - for (size_t i = 0; i < indices.size(); i++) { - S j = indices[i]; - // Just randomize... - children_indices[_random.flip()].push_back(j); - } - } - - int flip = (children_indices[0].size() > children_indices[1].size()); - - m->n_descendants = is_root ? _n_items : (S)indices.size(); - for (int side = 0; side < 2; side++) { - // run _make_tree for the smallest child first (for cache locality) - faiss::BuilderSuspend::check_wait(); - m->children[side^flip] = _make_tree(children_indices[side^flip], false); - } - - _allocate_size(_n_nodes + 1); - S item = _n_nodes++; - memcpy(_get(item), m, _s); - - return item; - } - - void _get_all_nns(const T* v, size_t n, int64_t search_k, vector* result, vector* distances, - faiss::ConcurrentBitsetPtr& bitset) const { - Node* v_node = (Node *)alloca(_s); - D::template zero_value(v_node); - memcpy(v_node->v, v, sizeof(T) * _f); - D::init_node(v_node, _f); - - std::priority_queue > q; - - if (search_k <= 0) { - search_k = std::max(int64_t(n * _roots.size()), int64_t(_n_items * 5 / 100)); - } - - for (size_t i = 0; i < _roots.size(); i++) { - q.push(make_pair(Distance::template pq_initial_value(), _roots[i])); - } - - std::vector nns; - while (nns.size() < (size_t)search_k && !q.empty()) { - const pair& top = q.top(); - T d = top.first; - S i = top.second; - Node* nd = _get(i); - q.pop(); - if (nd->n_descendants == 1 && i < _n_items) { // raw data - if (bitset == nullptr || !bitset->test((faiss::ConcurrentBitset::id_type_t)i)) - nns.push_back(i); - } else if (nd->n_descendants <= _K) { - const S* dst = nd->children; - for (auto ii = 0; ii < nd->n_descendants; ++ ii) { - if (bitset == nullptr || !bitset->test((faiss::ConcurrentBitset::id_type_t)dst[ii])) - nns.push_back(dst[ii]); -// nns.insert(nns.end(), dst, &dst[nd->n_descendants]); - } - } else { - T margin = D::margin(nd, v, _f); - q.push(make_pair(D::pq_distance(d, margin, 1), static_cast(nd->children[1]))); - q.push(make_pair(D::pq_distance(d, margin, 0), static_cast(nd->children[0]))); - } - } - - // Get distances for all items - // To avoid calculating distance multiple times for any items, sort by id - std::sort(nns.begin(), nns.end()); - vector > nns_dist; - S last = -1; - for (size_t i = 0; i < nns.size(); i++) { - S j = nns[i]; - if (j == last) - continue; - last = j; - if (_get(j)->n_descendants == 1) // This is only to guard a really obscure case, #284 - nns_dist.push_back(make_pair(D::distance(v_node, _get(j), _f), j)); - } - - size_t m = nns_dist.size(); - size_t p = n < m ? n : m; // Return this many items - std::partial_sort(nns_dist.begin(), nns_dist.begin() + p, nns_dist.end()); - for (size_t i = 0; i < p; i++) { - if (distances) - distances->push_back(D::normalized_distance(nns_dist[i].first)); - result->push_back(nns_dist[i].second); - } - } - - int64_t cal_size() { - int64_t ret = 0; - ret += sizeof(*this); - ret += _roots.size() * sizeof(S); - ret += std::max(_n_nodes, _nodes_size) * _s; - return ret; - } -}; - -#endif -// vim: tabstop=2 shiftwidth=2 diff --git a/core/src/index/thirdparty/annoy/src/annoyluamodule.cc b/core/src/index/thirdparty/annoy/src/annoyluamodule.cc deleted file mode 100644 index 4f483d2d3d..0000000000 --- a/core/src/index/thirdparty/annoy/src/annoyluamodule.cc +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright (c) 2016 Boris Nagaev -// -// Licensed 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 limitations under -// the License. - -#include -#include - -#include - -#include "annoylib.h" -#include "kissrandom.h" - -#if LUA_VERSION_NUM == 501 -#define compat_setfuncs(L, funcs) luaL_register(L, nullptr, funcs) -#define compat_rawlen lua_objlen -#else -#define compat_setfuncs(L, funcs) luaL_setfuncs(L, funcs, 0) -#define compat_rawlen lua_rawlen -#endif - -template -class LuaAnnoy { -public: - typedef int32_t AnnoyS; - typedef float AnnoyT; - typedef AnnoyIndex Impl; - typedef LuaAnnoy ThisClass; - - class LuaArrayProxy { - public: - LuaArrayProxy(lua_State* L, int object, int f) - : L_(L) - , object_(object) - { - luaL_checktype(L, object, LUA_TTABLE); - int v_len = compat_rawlen(L, object); - luaL_argcheck(L, v_len == f, object, "Length of v != f"); - } - - double operator[](int index) const { - lua_rawgeti(L_, object_, index + 1); - double result = lua_tonumber(L_, -1); - lua_pop(L_, 1); - return result; - } - - private: - lua_State* L_; - int object_; - }; - - static void toVector(lua_State* L, int object, int f, AnnoyT* dst) { - LuaArrayProxy proxy(L, object, f); - for (int i = 0; i < f; i++) { - dst[i] = proxy[i]; - } - } - - template - static void pushVector(lua_State* L, const Vector& v) { - lua_createtable(L, v.size(), 0); - for (int j = 0; j < v.size(); j++) { - lua_pushnumber(L, v[j]); - lua_rawseti(L, -2, j + 1); - } - } - - static const char* typeAsString() { - return typeid(Impl).name(); - } - - static Impl* getAnnoy(lua_State* L, int object) { - return reinterpret_cast( - luaL_checkudata(L, object, typeAsString()) - ); - } - - static int getItemIndex(lua_State* L, int object, int size = -1) { - int item = luaL_checkinteger(L, object); - luaL_argcheck(L, item >= 0, object, "Index must be >= 0"); - if (size != -1) { - luaL_argcheck(L, item < size, object, "Index must be < size"); - } - return item; - } - - static int gc(lua_State* L) { - Impl* self = getAnnoy(L, 1); - self->~Impl(); - return 0; - } - - static int tostring(lua_State* L) { - Impl* self = getAnnoy(L, 1); - lua_pushfstring( - L, - "annoy.AnnoyIndex object (%dx%d, %s distance)", - self->get_n_items(), self->get_f(), Distance::name() - ); - return 1; - } - - static int add_item(lua_State* L) { - Impl* self = getAnnoy(L, 1); - int item = getItemIndex(L, 2); - self->add_item_impl(item, LuaArrayProxy(L, 3, self->get_f())); - return 0; - } - - static int build(lua_State* L) { - Impl* self = getAnnoy(L, 1); - int n_trees = luaL_checkinteger(L, 2); - self->build(n_trees); - lua_pushboolean(L, true); - return 1; - } - - static int on_disk_build(lua_State* L) { - Impl* self = getAnnoy(L, 1); - const char* filename = luaL_checkstring(L, 2); - self->on_disk_build(filename); - lua_pushboolean(L, true); - return 1; - } - - static int save(lua_State* L) { - int nargs = lua_gettop(L); - Impl* self = getAnnoy(L, 1); - const char* filename = luaL_checkstring(L, 2); - bool prefault = true; - if (nargs >= 3) { - prefault = lua_toboolean(L, 3); - } - self->save(filename, prefault); - lua_pushboolean(L, true); - return 1; - } - - static int load(lua_State* L) { - Impl* self = getAnnoy(L, 1); - int nargs = lua_gettop(L); - const char* filename = luaL_checkstring(L, 2); - bool prefault = true; - if (nargs >= 3) { - prefault = lua_toboolean(L, 3); - } - if (!self->load(filename, prefault)) { - return luaL_error(L, "Can't load file: %s", filename); - } - lua_pushboolean(L, true); - return 1; - } - - static int unload(lua_State* L) { - Impl* self = getAnnoy(L, 1); - self->unload(); - lua_pushboolean(L, true); - return 1; - } - - struct Searcher { - std::vector result; - std::vector distances; - Impl* self; - int n; - int search_k; - bool include_distances; - - Searcher(lua_State* L) { - int nargs = lua_gettop(L); - self = getAnnoy(L, 1); - n = luaL_checkinteger(L, 3); - search_k = -1; - if (nargs >= 4) { - search_k = luaL_checkinteger(L, 4); - } - include_distances = false; - if (nargs >= 5) { - include_distances = lua_toboolean(L, 5); - } - } - - int pushResults(lua_State* L) { - pushVector(L, result); - if (include_distances) { - pushVector(L, distances); - } - return include_distances ? 2 : 1; - } - }; - - static int get_nns_by_item(lua_State* L) { - Searcher s(L); - int item = getItemIndex(L, 2, s.self->get_n_items()); - s.self->get_nns_by_item(item, s.n, s.search_k, &s.result, - s.include_distances ? &s.distances : nullptr); - return s.pushResults(L); - } - - static int get_nns_by_vector(lua_State* L) { - Searcher s(L); - std::vector _vec(s.self->get_f()); - AnnoyT* vec = &(_vec[0]); - toVector(L, 2, s.self->get_f(), vec); - s.self->get_nns_by_vector(vec, s.n, s.search_k, &s.result, - s.include_distances ? &s.distances : nullptr); - return s.pushResults(L); - } - - static int get_item_vector(lua_State* L) { - Impl* self = getAnnoy(L, 1); - int item = getItemIndex(L, 2, self->get_n_items()); - std::vector _vec(self->get_f()); - AnnoyT* vec = &(_vec[0]); - self->get_item(item, vec); - pushVector(L, _vec); - return 1; - } - - static int get_distance(lua_State* L) { - Impl* self = getAnnoy(L, 1); - int i = getItemIndex(L, 2, self->get_n_items()); - int j = getItemIndex(L, 3, self->get_n_items()); - AnnoyT distance = self->get_distance(i, j); - lua_pushnumber(L, distance); - return 1; - } - - static int get_n_items(lua_State* L) { - Impl* self = getAnnoy(L, 1); - lua_pushnumber(L, self->get_n_items()); - return 1; - } - - static const luaL_Reg* getMetatable() { - static const luaL_Reg funcs[] = { - {"__gc", &ThisClass::gc}, - {"__tostring", &ThisClass::tostring}, - {nullptr, nullptr}, - }; - return funcs; - } - - static const luaL_Reg* getMethods() { - static const luaL_Reg funcs[] = { - {"add_item", &ThisClass::add_item}, - {"build", &ThisClass::build}, - {"save", &ThisClass::save}, - {"load", &ThisClass::load}, - {"unload", &ThisClass::unload}, - {"get_nns_by_item", &ThisClass::get_nns_by_item}, - {"get_nns_by_vector", &ThisClass::get_nns_by_vector}, - {"get_item_vector", &ThisClass::get_item_vector}, - {"get_distance", &ThisClass::get_distance}, - {"get_n_items", &ThisClass::get_n_items}, - {"on_disk_build", &ThisClass::on_disk_build}, - {nullptr, nullptr}, - }; - return funcs; - } - - static void createNew(lua_State* L, int f) { - void* self = lua_newuserdata(L, sizeof(Impl)); - if (luaL_newmetatable(L, typeAsString())) { - compat_setfuncs(L, getMetatable()); - lua_newtable(L); - compat_setfuncs(L, getMethods()); - lua_setfield(L, -2, "__index"); - } - new (self) Impl(f); - lua_setmetatable(L, -2); - } -}; - -static int lua_an_make(lua_State* L) { - int f = luaL_checkinteger(L, 1); - const char* metric = "angular"; - if (lua_gettop(L) >= 2) { - metric = luaL_checkstring(L, 2); - } - if (strcmp(metric, "angular") == 0) { - LuaAnnoy::createNew(L, f); - return 1; - } else if (strcmp(metric, "euclidean") == 0) { - LuaAnnoy::createNew(L, f); - return 1; - } else if (strcmp(metric, "manhattan") == 0) { - LuaAnnoy::createNew(L, f); - return 1; - } else { - return luaL_error(L, "Unknown metric: %s", metric); - } -} - -static const luaL_Reg LUA_ANNOY_FUNCS[] = { - {"AnnoyIndex", lua_an_make}, - {nullptr, nullptr}, -}; - -extern "C" { -int luaopen_annoy(lua_State* L) { - lua_newtable(L); - compat_setfuncs(L, LUA_ANNOY_FUNCS); - return 1; -} -} - -// vim: tabstop=2 shiftwidth=2 diff --git a/core/src/index/thirdparty/annoy/src/annoymodule.cc b/core/src/index/thirdparty/annoy/src/annoymodule.cc deleted file mode 100644 index 6121a2bc41..0000000000 --- a/core/src/index/thirdparty/annoy/src/annoymodule.cc +++ /dev/null @@ -1,632 +0,0 @@ -// Copyright (c) 2013 Spotify AB -// -// Licensed 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 limitations under -// the License. - -#include "annoylib.h" -#include "kissrandom.h" -#include "Python.h" -#include "structmember.h" -#include -#if defined(_MSC_VER) && _MSC_VER == 1500 -typedef signed __int32 int32_t; -#else -#include -#endif - - -#if defined(USE_AVX512) -#define AVX_INFO "Using 512-bit AVX instructions" -#elif defined(USE_AVX128) -#define AVX_INFO "Using 128-bit AVX instructions" -#else -#define AVX_INFO "Not using AVX instructions" -#endif - -#if defined(_MSC_VER) -#define COMPILER_INFO "Compiled using MSC" -#elif defined(__GNUC__) -#define COMPILER_INFO "Compiled on GCC" -#else -#define COMPILER_INFO "Compiled on unknown platform" -#endif - -#define ANNOY_DOC (COMPILER_INFO ". " AVX_INFO ".") - -#if PY_MAJOR_VERSION >= 3 -#define IS_PY3K -#endif - -#ifndef Py_TYPE - #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) -#endif - -#ifdef IS_PY3K - #define PyInt_FromLong PyLong_FromLong -#endif - - -template class AnnoyIndexInterface; - -class HammingWrapper : public AnnoyIndexInterface { - // Wrapper class for Hamming distance, using composition. - // This translates binary (float) vectors into packed uint64_t vectors. - // This is questionable from a performance point of view. Should reconsider this solution. -private: - int32_t _f_external, _f_internal; - AnnoyIndex _index; - void _pack(const float* src, uint64_t* dst) const { - for (int32_t i = 0; i < _f_internal; i++) { - dst[i] = 0; - for (int32_t j = 0; j < 64 && i*64+j < _f_external; j++) { - dst[i] |= (uint64_t)(src[i * 64 + j] > 0.5) << j; - } - } - }; - void _unpack(const uint64_t* src, float* dst) const { - for (int32_t i = 0; i < _f_external; i++) { - dst[i] = (src[i / 64] >> (i % 64)) & 1; - } - }; -public: - HammingWrapper(int f) : _f_external(f), _f_internal((f + 63) / 64), _index((f + 63) / 64) {}; - bool add_item(int32_t item, const float* w, char**error) { - vector w_internal(_f_internal, 0); - _pack(w, &w_internal[0]); - return _index.add_item(item, &w_internal[0], error); - }; - bool build(int q, char** error) { return _index.build(q, error); }; - bool unbuild(char** error) { return _index.unbuild(error); }; - bool save(const char* filename, bool prefault, char** error) { return _index.save(filename, prefault, error); }; - void unload() { _index.unload(); }; - bool load(const char* filename, bool prefault, char** error) { return _index.load(filename, prefault, error); }; - float get_distance(int32_t i, int32_t j) const { return _index.get_distance(i, j); }; - void get_nns_by_item(int32_t item, size_t n, int search_k, vector* result, vector* distances) const { - if (distances) { - vector distances_internal; - _index.get_nns_by_item(item, n, search_k, result, &distances_internal); - distances->insert(distances->begin(), distances_internal.begin(), distances_internal.end()); - } else { - _index.get_nns_by_item(item, n, search_k, result, nullptr); - } - }; - void get_nns_by_vector(const float* w, size_t n, int search_k, vector* result, vector* distances) const { - vector w_internal(_f_internal, 0); - _pack(w, &w_internal[0]); - if (distances) { - vector distances_internal; - _index.get_nns_by_vector(&w_internal[0], n, search_k, result, &distances_internal); - distances->insert(distances->begin(), distances_internal.begin(), distances_internal.end()); - } else { - _index.get_nns_by_vector(&w_internal[0], n, search_k, result, nullptr); - } - }; - int32_t get_n_items() const { return _index.get_n_items(); }; - int32_t get_n_trees() const { return _index.get_n_trees(); }; - void verbose(bool v) { _index.verbose(v); }; - void get_item(int32_t item, float* v) const { - vector v_internal(_f_internal, 0); - _index.get_item(item, &v_internal[0]); - _unpack(&v_internal[0], v); - }; - void set_seed(int q) { _index.set_seed(q); }; - bool on_disk_build(const char* filename, char** error) { return _index.on_disk_build(filename, error); }; -}; - -// annoy python object -typedef struct { - PyObject_HEAD - int f; - AnnoyIndexInterface* ptr; -} py_annoy; - - -static PyObject * -py_an_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) { - py_annoy *self = (py_annoy *)type->tp_alloc(type, 0); - if (self == nullptr) { - return nullptr; - } - const char *metric = nullptr; - - static char const * kwlist[] = {"f", "metric", nullptr}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|s", (char**)kwlist, &self->f, &metric)) - return nullptr; - if (!metric) { - // This keeps coming up, see #368 etc - PyErr_WarnEx(PyExc_FutureWarning, "The default argument for metric will be removed " - "in future version of Annoy. Please pass metric='angular' explicitly.", 1); - self->ptr = new AnnoyIndex(self->f); - } else if (!strcmp(metric, "angular")) { - self->ptr = new AnnoyIndex(self->f); - } else if (!strcmp(metric, "euclidean")) { - self->ptr = new AnnoyIndex(self->f); - } else if (!strcmp(metric, "manhattan")) { - self->ptr = new AnnoyIndex(self->f); - } else if (!strcmp(metric, "hamming")) { - self->ptr = new HammingWrapper(self->f); - } else if (!strcmp(metric, "dot")) { - self->ptr = new AnnoyIndex(self->f); - } else { - PyErr_SetString(PyExc_ValueError, "No such metric"); - return nullptr; - } - - return (PyObject *)self; -} - - -static int -py_an_init(py_annoy *self, PyObject *args, PyObject *kwargs) { - // Seems to be needed for Python 3 - const char *metric = nullptr; - int f; - static char const * kwlist[] = {"f", "metric", nullptr}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|s", (char**)kwlist, &f, &metric)) - return (int) nullptr; - return 0; -} - - -static void -py_an_dealloc(py_annoy* self) { - delete self->ptr; - Py_TYPE(self)->tp_free((PyObject*)self); -} - - -static PyMemberDef py_annoy_members[] = { - {(char*)"f", T_INT, offsetof(py_annoy, f), 0, - (char*)""}, - {nullptr} /* Sentinel */ -}; - - -static PyObject * -py_an_load(py_annoy *self, PyObject *args, PyObject *kwargs) { - char *filename, *error; - bool prefault = false; - if (!self->ptr) - return nullptr; - static char const * kwlist[] = {"fn", "prefault", nullptr}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|b", (char**)kwlist, &filename, &prefault)) - return nullptr; - - if (!self->ptr->load(filename, prefault, &error)) { - PyErr_SetString(PyExc_IOError, error); - free(error); - return nullptr; - } - Py_RETURN_TRUE; -} - - -static PyObject * -py_an_save(py_annoy *self, PyObject *args, PyObject *kwargs) { - char *filename, *error; - bool prefault = false; - if (!self->ptr) - return nullptr; - static char const * kwlist[] = {"fn", "prefault", nullptr}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|b", (char**)kwlist, &filename, &prefault)) - return nullptr; - - if (!self->ptr->save(filename, prefault, &error)) { - PyErr_SetString(PyExc_IOError, error); - free(error); - return nullptr; - } - Py_RETURN_TRUE; -} - - -PyObject* -get_nns_to_python(const vector& result, const vector& distances, int include_distances) { - PyObject* l = PyList_New(result.size()); - for (size_t i = 0; i < result.size(); i++) - PyList_SetItem(l, i, PyInt_FromLong(result[i])); - if (!include_distances) - return l; - - PyObject* d = PyList_New(distances.size()); - for (size_t i = 0; i < distances.size(); i++) - PyList_SetItem(d, i, PyFloat_FromDouble(distances[i])); - - PyObject* t = PyTuple_New(2); - PyTuple_SetItem(t, 0, l); - PyTuple_SetItem(t, 1, d); - - return t; -} - - -bool check_constraints(py_annoy *self, int32_t item, bool building) { - if (item < 0) { - PyErr_SetString(PyExc_IndexError, "Item index can not be negative"); - return false; - } else if (!building && item >= self->ptr->get_n_items()) { - PyErr_SetString(PyExc_IndexError, "Item index larger than the largest item index"); - return false; - } else { - return true; - } -} - -static PyObject* -py_an_get_nns_by_item(py_annoy *self, PyObject *args, PyObject *kwargs) { - int32_t item, n, search_k=-1, include_distances=0; - if (!self->ptr) - return nullptr; - - static char const * kwlist[] = {"i", "n", "search_k", "include_distances", nullptr}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ii|ii", (char**)kwlist, &item, &n, &search_k, &include_distances)) - return nullptr; - - if (!check_constraints(self, item, false)) { - return nullptr; - } - - vector result; - vector distances; - - Py_BEGIN_ALLOW_THREADS; - self->ptr->get_nns_by_item(item, n, search_k, &result, include_distances ? &distances : nullptr); - Py_END_ALLOW_THREADS; - - return get_nns_to_python(result, distances, include_distances); -} - - -bool -convert_list_to_vector(PyObject* v, int f, vector* w) { - if (PyObject_Size(v) == -1) { - char buf[256]; - snprintf(buf, 256, "Expected an iterable, got an object of type \"%s\"", v->ob_type->tp_name); - PyErr_SetString(PyExc_ValueError, buf); - return false; - } - if (PyObject_Size(v) != f) { - char buf[128]; - snprintf(buf, 128, "Vector has wrong length (expected %d, got %ld)", f, PyObject_Size(v)); - PyErr_SetString(PyExc_IndexError, buf); - return false; - } - for (int z = 0; z < f; z++) { - PyObject *key = PyInt_FromLong(z); - PyObject *pf = PyObject_GetItem(v, key); - (*w)[z] = PyFloat_AsDouble(pf); - Py_DECREF(key); - Py_DECREF(pf); - } - return true; -} - -static PyObject* -py_an_get_nns_by_vector(py_annoy *self, PyObject *args, PyObject *kwargs) { - PyObject* v; - int32_t n, search_k=-1, include_distances=0; - if (!self->ptr) - return nullptr; - - static char const * kwlist[] = {"vector", "n", "search_k", "include_distances", nullptr}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Oi|ii", (char**)kwlist, &v, &n, &search_k, &include_distances)) - return nullptr; - - vector w(self->f); - if (!convert_list_to_vector(v, self->f, &w)) { - return nullptr; - } - - vector result; - vector distances; - - Py_BEGIN_ALLOW_THREADS; - self->ptr->get_nns_by_vector(&w[0], n, search_k, &result, include_distances ? &distances : nullptr); - Py_END_ALLOW_THREADS; - - return get_nns_to_python(result, distances, include_distances); -} - - -static PyObject* -py_an_get_item_vector(py_annoy *self, PyObject *args) { - int32_t item; - if (!self->ptr) - return nullptr; - if (!PyArg_ParseTuple(args, "i", &item)) - return nullptr; - - if (!check_constraints(self, item, false)) { - return nullptr; - } - - vector v(self->f); - self->ptr->get_item(item, &v[0]); - PyObject* l = PyList_New(self->f); - for (int z = 0; z < self->f; z++) { - PyList_SetItem(l, z, PyFloat_FromDouble(v[z])); - } - - return l; -} - - -static PyObject* -py_an_add_item(py_annoy *self, PyObject *args, PyObject* kwargs) { - PyObject* v; - int32_t item; - if (!self->ptr) - return nullptr; - static char const * kwlist[] = {"i", "vector", nullptr}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO", (char**)kwlist, &item, &v)) - return nullptr; - - if (!check_constraints(self, item, true)) { - return nullptr; - } - - vector w(self->f); - if (!convert_list_to_vector(v, self->f, &w)) { - return nullptr; - } - char* error; - if (!self->ptr->add_item(item, &w[0], &error)) { - PyErr_SetString(PyExc_Exception, error); - free(error); - return nullptr; - } - - Py_RETURN_NONE; -} - -static PyObject * -py_an_on_disk_build(py_annoy *self, PyObject *args, PyObject *kwargs) { - char *filename, *error; - if (!self->ptr) - return nullptr; - static char const * kwlist[] = {"fn", nullptr}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", (char**)kwlist, &filename)) - return nullptr; - - if (!self->ptr->on_disk_build(filename, &error)) { - PyErr_SetString(PyExc_IOError, error); - free(error); - return nullptr; - } - Py_RETURN_TRUE; -} - -static PyObject * -py_an_build(py_annoy *self, PyObject *args, PyObject *kwargs) { - int q; - if (!self->ptr) - return nullptr; - static char const * kwlist[] = {"n_trees", nullptr}; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i", (char**)kwlist, &q)) - return nullptr; - - bool res; - char* error; - Py_BEGIN_ALLOW_THREADS; - res = self->ptr->build(q, &error); - Py_END_ALLOW_THREADS; - if (!res) { - PyErr_SetString(PyExc_Exception, error); - free(error); - return nullptr; - } - - Py_RETURN_TRUE; -} - - -static PyObject * -py_an_unbuild(py_annoy *self) { - if (!self->ptr) - return nullptr; - - char* error; - if (!self->ptr->unbuild(&error)) { - PyErr_SetString(PyExc_Exception, error); - free(error); - return nullptr; - } - - Py_RETURN_TRUE; -} - - -static PyObject * -py_an_unload(py_annoy *self) { - if (!self->ptr) - return nullptr; - - self->ptr->unload(); - - Py_RETURN_TRUE; -} - - -static PyObject * -py_an_get_distance(py_annoy *self, PyObject *args) { - int32_t i, j; - if (!self->ptr) - return nullptr; - if (!PyArg_ParseTuple(args, "ii", &i, &j)) - return nullptr; - - if (!check_constraints(self, i, false) || !check_constraints(self, j, false)) { - return nullptr; - } - - double d = self->ptr->get_distance(i,j); - return PyFloat_FromDouble(d); -} - - -static PyObject * -py_an_get_n_items(py_annoy *self) { - if (!self->ptr) - return nullptr; - - int32_t n = self->ptr->get_n_items(); - return PyInt_FromLong(n); -} - -static PyObject * -py_an_get_n_trees(py_annoy *self) { - if (!self->ptr) - return nullptr; - - int32_t n = self->ptr->get_n_trees(); - return PyInt_FromLong(n); -} - -static PyObject * -py_an_verbose(py_annoy *self, PyObject *args) { - int verbose; - if (!self->ptr) - return nullptr; - if (!PyArg_ParseTuple(args, "i", &verbose)) - return nullptr; - - self->ptr->verbose((bool)verbose); - - Py_RETURN_TRUE; -} - - -static PyObject * -py_an_set_seed(py_annoy *self, PyObject *args) { - int q; - if (!self->ptr) - return nullptr; - if (!PyArg_ParseTuple(args, "i", &q)) - return nullptr; - - self->ptr->set_seed(q); - - Py_RETURN_NONE; -} - - -static PyMethodDef AnnoyMethods[] = { - {"load", (PyCFunction)py_an_load, METH_VARARGS | METH_KEYWORDS, "Loads (mmaps) an index from disk."}, - {"save", (PyCFunction)py_an_save, METH_VARARGS | METH_KEYWORDS, "Saves the index to disk."}, - {"get_nns_by_item",(PyCFunction)py_an_get_nns_by_item, METH_VARARGS | METH_KEYWORDS, "Returns the `n` closest items to item `i`.\n\n:param search_k: the query will inspect up to `search_k` nodes.\n`search_k` gives you a run-time tradeoff between better accuracy and speed.\n`search_k` defaults to `n_trees * n` if not provided.\n\n:param include_distances: If `True`, this function will return a\n2 element tuple of lists. The first list contains the `n` closest items.\nThe second list contains the corresponding distances."}, - {"get_nns_by_vector",(PyCFunction)py_an_get_nns_by_vector, METH_VARARGS | METH_KEYWORDS, "Returns the `n` closest items to vector `vector`.\n\n:param search_k: the query will inspect up to `search_k` nodes.\n`search_k` gives you a run-time tradeoff between better accuracy and speed.\n`search_k` defaults to `n_trees * n` if not provided.\n\n:param include_distances: If `True`, this function will return a\n2 element tuple of lists. The first list contains the `n` closest items.\nThe second list contains the corresponding distances."}, - {"get_item_vector",(PyCFunction)py_an_get_item_vector, METH_VARARGS, "Returns the vector for item `i` that was previously added."}, - {"add_item",(PyCFunction)py_an_add_item, METH_VARARGS | METH_KEYWORDS, "Adds item `i` (any nonnegative integer) with vector `v`.\n\nNote that it will allocate memory for `max(i)+1` items."}, - {"on_disk_build",(PyCFunction)py_an_on_disk_build, METH_VARARGS | METH_KEYWORDS, "Build will be performed with storage on disk instead of RAM."}, - {"build",(PyCFunction)py_an_build, METH_VARARGS | METH_KEYWORDS, "Builds a forest of `n_trees` trees.\n\nMore trees give higher precision when querying. After calling `build`,\nno more items can be added."}, - {"unbuild",(PyCFunction)py_an_unbuild, METH_NOARGS, "Unbuilds the tree in order to allows adding new items.\n\nbuild() has to be called again afterwards in order to\nrun queries."}, - {"unload",(PyCFunction)py_an_unload, METH_NOARGS, "Unloads an index from disk."}, - {"get_distance",(PyCFunction)py_an_get_distance, METH_VARARGS, "Returns the distance between items `i` and `j`."}, - {"get_n_items",(PyCFunction)py_an_get_n_items, METH_NOARGS, "Returns the number of items in the index."}, - {"get_n_trees",(PyCFunction)py_an_get_n_trees, METH_NOARGS, "Returns the number of trees in the index."}, - {"verbose",(PyCFunction)py_an_verbose, METH_VARARGS, ""}, - {"set_seed",(PyCFunction)py_an_set_seed, METH_VARARGS, "Sets the seed of Annoy's random number generator."}, - {nullptr, nullptr, 0, nullptr} /* Sentinel */ -}; - - -static PyTypeObject PyAnnoyType = { - PyVarObject_HEAD_INIT(nullptr, 0) - "annoy.Annoy", /*tp_name*/ - sizeof(py_annoy), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)py_an_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - ANNOY_DOC, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - AnnoyMethods, /* tp_methods */ - py_annoy_members, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)py_an_init, /* tp_init */ - 0, /* tp_alloc */ - py_an_new, /* tp_new */ -}; - -static PyMethodDef module_methods[] = { - {nullptr} /* Sentinel */ -}; - -#if PY_MAJOR_VERSION >= 3 - static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "annoylib", /* m_name */ - ANNOY_DOC, /* m_doc */ - -1, /* m_size */ - module_methods, /* m_methods */ - nullptr, /* m_reload */ - nullptr, /* m_traverse */ - nullptr, /* m_clear */ - nullptr, /* m_free */ - }; -#endif - -PyObject *create_module(void) { - PyObject *m; - - if (PyType_Ready(&PyAnnoyType) < 0) - return nullptr; - -#if PY_MAJOR_VERSION >= 3 - m = PyModule_Create(&moduledef); -#else - m = Py_InitModule("annoylib", module_methods); -#endif - - if (m == nullptr) - return nullptr; - - Py_INCREF(&PyAnnoyType); - PyModule_AddObject(m, "Annoy", (PyObject *)&PyAnnoyType); - return m; -} - -#if PY_MAJOR_VERSION >= 3 - PyMODINIT_FUNC PyInit_annoylib(void) { - return create_module(); // it should return moudule object in py3 - } -#else - PyMODINIT_FUNC initannoylib(void) { - create_module(); - } -#endif - - -// vim: tabstop=2 shiftwidth=2 diff --git a/core/src/index/thirdparty/annoy/src/kissrandom.h b/core/src/index/thirdparty/annoy/src/kissrandom.h deleted file mode 100644 index 9e40110f3e..0000000000 --- a/core/src/index/thirdparty/annoy/src/kissrandom.h +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef KISSRANDOM_H -#define KISSRANDOM_H - -#if defined(_MSC_VER) && _MSC_VER == 1500 -typedef unsigned __int32 uint32_t; -typedef unsigned __int64 uint64_t; -#else -#include -#endif - -// KISS = "keep it simple, stupid", but high quality random number generator -// http://www0.cs.ucl.ac.uk/staff/d.jones/GoodPracticeRNG.pdf -> "Use a good RNG and build it into your code" -// http://mathforum.org/kb/message.jspa?messageID=6627731 -// https://de.wikipedia.org/wiki/KISS_(Zufallszahlengenerator) - -// 32 bit KISS -struct Kiss32Random { - uint32_t x; - uint32_t y; - uint32_t z; - uint32_t c; - - // seed must be != 0 - Kiss32Random(uint32_t seed = 123456789) { - x = seed; - y = 362436000; - z = 521288629; - c = 7654321; - } - - uint32_t kiss() { - // Linear congruence generator - x = 69069 * x + 12345; - - // Xor shift - y ^= y << 13; - y ^= y >> 17; - y ^= y << 5; - - // Multiply-with-carry - uint64_t t = 698769069ULL * z + c; - c = t >> 32; - z = (uint32_t) t; - - return x + y + z; - } - inline int flip() { - // Draw random 0 or 1 - return kiss() & 1; - } - inline size_t index(size_t n) { - // Draw random integer between 0 and n-1 where n is at most the number of data points you have - return kiss() % n; - } - inline void set_seed(uint32_t seed) { - x = seed; - } -}; - -// 64 bit KISS. Use this if you have more than about 2^24 data points ("big data" ;) ) -struct Kiss64Random { - uint64_t x; - uint64_t y; - uint64_t z; - uint64_t c; - - // seed must be != 0 - Kiss64Random(uint64_t seed = 1234567890987654321ULL) { - x = seed; - y = 362436362436362436ULL; - z = 1066149217761810ULL; - c = 123456123456123456ULL; - } - - uint64_t kiss() { - // Linear congruence generator - z = 6906969069LL*z+1234567; - - // Xor shift - y ^= (y<<13); - y ^= (y>>17); - y ^= (y<<43); - - // Multiply-with-carry (uint128_t t = (2^58 + 1) * x + c; c = t >> 64; x = (uint64_t) t) - uint64_t t = (x<<58)+c; - c = (x>>6); - x += t; - c += (x -#include -#include -#include - -#define PROT_NONE 0 -#define PROT_READ 1 -#define PROT_WRITE 2 -#define PROT_EXEC 4 - -#define MAP_FILE 0 -#define MAP_SHARED 1 -#define MAP_PRIVATE 2 -#define MAP_TYPE 0xf -#define MAP_FIXED 0x10 -#define MAP_ANONYMOUS 0x20 -#define MAP_ANON MAP_ANONYMOUS - -#define MAP_FAILED ((void *)-1) - -/* Flags for msync. */ -#define MS_ASYNC 1 -#define MS_SYNC 2 -#define MS_INVALIDATE 4 - -#ifndef FILE_MAP_EXECUTE -#define FILE_MAP_EXECUTE 0x0020 -#endif - -static int __map_mman_error(const DWORD err, const int deferr) -{ - if (err == 0) - return 0; - //TODO: implement - return err; -} - -static DWORD __map_mmap_prot_page(const int prot) -{ - DWORD protect = 0; - - if (prot == PROT_NONE) - return protect; - - if ((prot & PROT_EXEC) != 0) - { - protect = ((prot & PROT_WRITE) != 0) ? - PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ; - } - else - { - protect = ((prot & PROT_WRITE) != 0) ? - PAGE_READWRITE : PAGE_READONLY; - } - - return protect; -} - -static DWORD __map_mmap_prot_file(const int prot) -{ - DWORD desiredAccess = 0; - - if (prot == PROT_NONE) - return desiredAccess; - - if ((prot & PROT_READ) != 0) - desiredAccess |= FILE_MAP_READ; - if ((prot & PROT_WRITE) != 0) - desiredAccess |= FILE_MAP_WRITE; - if ((prot & PROT_EXEC) != 0) - desiredAccess |= FILE_MAP_EXECUTE; - - return desiredAccess; -} - -inline void* mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off) -{ - HANDLE fm, h; - - void * map = MAP_FAILED; - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable: 4293) -#endif - - const DWORD dwFileOffsetLow = (sizeof(off_t) <= sizeof(DWORD)) ? - (DWORD)off : (DWORD)(off & 0xFFFFFFFFL); - const DWORD dwFileOffsetHigh = (sizeof(off_t) <= sizeof(DWORD)) ? - (DWORD)0 : (DWORD)((off >> 32) & 0xFFFFFFFFL); - const DWORD protect = __map_mmap_prot_page(prot); - const DWORD desiredAccess = __map_mmap_prot_file(prot); - - const off_t maxSize = off + (off_t)len; - - const DWORD dwMaxSizeLow = (sizeof(off_t) <= sizeof(DWORD)) ? - (DWORD)maxSize : (DWORD)(maxSize & 0xFFFFFFFFL); - const DWORD dwMaxSizeHigh = (sizeof(off_t) <= sizeof(DWORD)) ? - (DWORD)0 : (DWORD)((maxSize >> 32) & 0xFFFFFFFFL); - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - - errno = 0; - - if (len == 0 - /* Unsupported flag combinations */ - || (flags & MAP_FIXED) != 0 - /* Usupported protection combinations */ - || prot == PROT_EXEC) - { - errno = EINVAL; - return MAP_FAILED; - } - - h = ((flags & MAP_ANONYMOUS) == 0) ? - (HANDLE)_get_osfhandle(fildes) : INVALID_HANDLE_VALUE; - - if ((flags & MAP_ANONYMOUS) == 0 && h == INVALID_HANDLE_VALUE) - { - errno = EBADF; - return MAP_FAILED; - } - - fm = CreateFileMapping(h, NULL, protect, dwMaxSizeHigh, dwMaxSizeLow, NULL); - - if (fm == NULL) - { - errno = __map_mman_error(GetLastError(), EPERM); - return MAP_FAILED; - } - - map = MapViewOfFile(fm, desiredAccess, dwFileOffsetHigh, dwFileOffsetLow, len); - - CloseHandle(fm); - - if (map == NULL) - { - errno = __map_mman_error(GetLastError(), EPERM); - return MAP_FAILED; - } - - return map; -} - -inline int munmap(void *addr, size_t len) -{ - if (UnmapViewOfFile(addr)) - return 0; - - errno = __map_mman_error(GetLastError(), EPERM); - - return -1; -} - -inline int mprotect(void *addr, size_t len, int prot) -{ - DWORD newProtect = __map_mmap_prot_page(prot); - DWORD oldProtect = 0; - - if (VirtualProtect(addr, len, newProtect, &oldProtect)) - return 0; - - errno = __map_mman_error(GetLastError(), EPERM); - - return -1; -} - -inline int msync(void *addr, size_t len, int flags) -{ - if (FlushViewOfFile(addr, len)) - return 0; - - errno = __map_mman_error(GetLastError(), EPERM); - - return -1; -} - -inline int mlock(const void *addr, size_t len) -{ - if (VirtualLock((LPVOID)addr, len)) - return 0; - - errno = __map_mman_error(GetLastError(), EPERM); - - return -1; -} - -inline int munlock(const void *addr, size_t len) -{ - if (VirtualUnlock((LPVOID)addr, len)) - return 0; - - errno = __map_mman_error(GetLastError(), EPERM); - - return -1; -} - -#if !defined(__MINGW32__) -inline int ftruncate(int fd, unsigned int size) { - if (fd < 0) { - errno = EBADF; - return -1; - } - - HANDLE h = (HANDLE)_get_osfhandle(fd); - unsigned int cur = SetFilePointer(h, 0, NULL, FILE_CURRENT); - if (cur == ~0 || SetFilePointer(h, size, NULL, FILE_BEGIN) == ~0 || !SetEndOfFile(h)) { - int error = GetLastError(); - switch (GetLastError()) { - case ERROR_INVALID_HANDLE: - errno = EBADF; - break; - default: - errno = EIO; - break; - } - return -1; - } - - return 0; -} -#endif - -#endif diff --git a/core/src/index/thirdparty/build.sh b/core/src/index/thirdparty/build.sh deleted file mode 100755 index a0af3349d5..0000000000 --- a/core/src/index/thirdparty/build.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash - -BUILD_TYPE="Release" - -while getopts "p:t:d:h" arg -do - case $arg in - t) - BUILD_TYPE=$OPTARG # BUILD_TYPE - ;; - h) # help - echo " - -parameter: --p: postgresql install path. --t: build type - -usage: -./build.sh -t \${BUILD_TYPE} - " - exit 0 - ;; - ?) - echo "unknown argument" - exit 1 - ;; - esac -done - -if [[ -d build ]]; then - rm ./build -r -fi - -while IFS='' read -r line || [[ -n "$line" ]]; do - cd $line - ./build.sh -t ${BUILD_TYPE} - if [ $? -ne 0 ];then - exit 1 - fi - cd ../ -done < project.conf - diff --git a/core/src/index/thirdparty/faiss/.dockerignore b/core/src/index/thirdparty/faiss/.dockerignore deleted file mode 100644 index 7763a51dc3..0000000000 --- a/core/src/index/thirdparty/faiss/.dockerignore +++ /dev/null @@ -1 +0,0 @@ -sift1M \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/.gitignore b/core/src/index/thirdparty/faiss/.gitignore deleted file mode 100644 index a25bc7f112..0000000000 --- a/core/src/index/thirdparty/faiss/.gitignore +++ /dev/null @@ -1,21 +0,0 @@ -*.swp -*.swo -*.o -*.a -*.dSYM -*.so -*.dylib -*.pyc -*~ -.DS_Store -depend -/config.* -/aclocal.m4 -/autom4te.cache/ -/makefile.inc -/bin/ -/c_api/bin/ -/c_api/gpu/bin/ -/tests/test -/tests/gtest/ -include/ diff --git a/core/src/index/thirdparty/faiss/AutoTune.cpp b/core/src/index/thirdparty/faiss/AutoTune.cpp deleted file mode 100644 index a90a6f53ea..0000000000 --- a/core/src/index/thirdparty/faiss/AutoTune.cpp +++ /dev/null @@ -1,719 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -/* - * implementation of Hyper-parameter auto-tuning - */ - -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace faiss { - - -AutoTuneCriterion::AutoTuneCriterion (idx_t nq, idx_t nnn): - nq (nq), nnn (nnn), gt_nnn (0) -{} - - -void AutoTuneCriterion::set_groundtruth ( - int gt_nnn, const float *gt_D_in, const idx_t *gt_I_in) -{ - this->gt_nnn = gt_nnn; - if (gt_D_in) { // allow null for this, as it is often not used - gt_D.resize (nq * gt_nnn); - memcpy (gt_D.data(), gt_D_in, sizeof (gt_D[0]) * nq * gt_nnn); - } - gt_I.resize (nq * gt_nnn); - memcpy (gt_I.data(), gt_I_in, sizeof (gt_I[0]) * nq * gt_nnn); -} - - - -OneRecallAtRCriterion::OneRecallAtRCriterion (idx_t nq, idx_t R): - AutoTuneCriterion(nq, R), R(R) -{} - -double OneRecallAtRCriterion::evaluate(const float* /*D*/, const idx_t* I) - const { - FAISS_THROW_IF_NOT_MSG( - (gt_I.size() == gt_nnn * nq && gt_nnn >= 1 && nnn >= R), - "ground truth not initialized"); - idx_t n_ok = 0; - for (idx_t q = 0; q < nq; q++) { - idx_t gt_nn = gt_I[q * gt_nnn]; - const idx_t* I_line = I + q * nnn; - for (int i = 0; i < R; i++) { - if (I_line[i] == gt_nn) { - n_ok++; - break; - } - } - } - return n_ok / double(nq); -} - - -IntersectionCriterion::IntersectionCriterion (idx_t nq, idx_t R): - AutoTuneCriterion(nq, R), R(R) -{} - -double IntersectionCriterion::evaluate(const float* /*D*/, const idx_t* I) - const { - FAISS_THROW_IF_NOT_MSG( - (gt_I.size() == gt_nnn * nq && gt_nnn >= R && nnn >= R), - "ground truth not initialized"); - int64_t n_ok = 0; -#pragma omp parallel for reduction(+: n_ok) - for (idx_t q = 0; q < nq; q++) { - n_ok += ranklist_intersection_size ( - R, >_I [q * gt_nnn], - R, I + q * nnn); - } - return n_ok / double (nq * R); -} - -/*************************************************************** - * OperatingPoints - ***************************************************************/ - -OperatingPoints::OperatingPoints () -{ - clear(); -} - -void OperatingPoints::clear () -{ - all_pts.clear(); - optimal_pts.clear(); - /// default point: doing nothing gives 0 performance and takes 0 time - OperatingPoint op = {0, 0, "", -1}; - optimal_pts.push_back(op); -} - -/// add a performance measure -bool OperatingPoints::add (double perf, double t, const std::string & key, - size_t cno) -{ - OperatingPoint op = {perf, t, key, int64_t(cno)}; - all_pts.push_back (op); - if (perf == 0) { - return false; // no method for 0 accuracy is faster than doing nothing - } - std::vector & a = optimal_pts; - if (perf > a.back().perf) { - // keep unconditionally - a.push_back (op); - } else if (perf == a.back().perf) { - if (t < a.back ().t) { - a.back() = op; - } else { - return false; - } - } else { - int i; - // stricto sensu this should be a bissection - for (i = 0; i < a.size(); i++) { - if (a[i].perf >= perf) break; - } - assert (i < a.size()); - if (t < a[i].t) { - if (a[i].perf == perf) { - a[i] = op; - } else { - a.insert (a.begin() + i, op); - } - } else { - return false; - } - } - { // remove non-optimal points from array - int i = a.size() - 1; - while (i > 0) { - if (a[i].t < a[i - 1].t) - a.erase (a.begin() + (i - 1)); - i--; - } - } - return true; -} - - -int OperatingPoints::merge_with (const OperatingPoints &other, - const std::string & prefix) -{ - int n_add = 0; - for (int i = 0; i < other.all_pts.size(); i++) { - const OperatingPoint & op = other.all_pts[i]; - if (add (op.perf, op.t, prefix + op.key, op.cno)) - n_add++; - } - return n_add; -} - - - -/// get time required to obtain a given performance measure -double OperatingPoints::t_for_perf (double perf) const -{ - const std::vector & a = optimal_pts; - if (perf > a.back().perf) return 1e50; - int i0 = -1, i1 = a.size() - 1; - while (i0 + 1 < i1) { - int imed = (i0 + i1 + 1) / 2; - if (a[imed].perf < perf) i0 = imed; - else i1 = imed; - } - return a[i1].t; -} - - -void OperatingPoints::all_to_gnuplot (const char *fname) const -{ - FILE *f = fopen(fname, "w"); - if (!f) { - fprintf (stderr, "cannot open %s", fname); - perror(""); - abort(); - } - for (int i = 0; i < all_pts.size(); i++) { - const OperatingPoint & op = all_pts[i]; - fprintf (f, "%g %g %s\n", op.perf, op.t, op.key.c_str()); - } - fclose(f); -} - -void OperatingPoints::optimal_to_gnuplot (const char *fname) const -{ - FILE *f = fopen(fname, "w"); - if (!f) { - fprintf (stderr, "cannot open %s", fname); - perror(""); - abort(); - } - double prev_perf = 0.0; - for (int i = 0; i < optimal_pts.size(); i++) { - const OperatingPoint & op = optimal_pts[i]; - fprintf (f, "%g %g\n", prev_perf, op.t); - fprintf (f, "%g %g %s\n", op.perf, op.t, op.key.c_str()); - prev_perf = op.perf; - } - fclose(f); -} - -void OperatingPoints::display (bool only_optimal) const -{ - const std::vector &pts = - only_optimal ? optimal_pts : all_pts; - printf("Tested %ld operating points, %ld ones are optimal:\n", - all_pts.size(), optimal_pts.size()); - - for (int i = 0; i < pts.size(); i++) { - const OperatingPoint & op = pts[i]; - const char *star = ""; - if (!only_optimal) { - for (int j = 0; j < optimal_pts.size(); j++) { - if (op.cno == optimal_pts[j].cno) { - star = "*"; - break; - } - } - } - printf ("cno=%ld key=%s perf=%.4f t=%.3f %s\n", - op.cno, op.key.c_str(), op.perf, op.t, star); - } - -} - -/*************************************************************** - * ParameterSpace - ***************************************************************/ - -ParameterSpace::ParameterSpace (): - verbose (1), n_experiments (500), - batchsize (1<<30), thread_over_batches (false), - min_test_duration (0) -{ -} - -/* not keeping this constructor as inheritors will call the parent - initialize() - */ - -#if 0 -ParameterSpace::ParameterSpace (Index *index): - verbose (1), n_experiments (500), - batchsize (1<<30), thread_over_batches (false) - -{ - initialize(index); -} -#endif - -size_t ParameterSpace::n_combinations () const -{ - size_t n = 1; - for (int i = 0; i < parameter_ranges.size(); i++) - n *= parameter_ranges[i].values.size(); - return n; -} - -/// get string representation of the combination -std::string ParameterSpace::combination_name (size_t cno) const { - char buf[1000], *wp = buf; - *wp = 0; - for (int i = 0; i < parameter_ranges.size(); i++) { - const ParameterRange & pr = parameter_ranges[i]; - size_t j = cno % pr.values.size(); - cno /= pr.values.size(); - wp += snprintf ( - wp, buf + 1000 - wp, "%s%s=%g", i == 0 ? "" : ",", - pr.name.c_str(), pr.values[j]); - } - return std::string (buf); -} - - -bool ParameterSpace::combination_ge (size_t c1, size_t c2) const -{ - for (int i = 0; i < parameter_ranges.size(); i++) { - int nval = parameter_ranges[i].values.size(); - size_t j1 = c1 % nval; - size_t j2 = c2 % nval; - if (!(j1 >= j2)) return false; - c1 /= nval; - c2 /= nval; - } - return true; -} - - - -#define DC(classname) \ - const classname *ix = dynamic_cast(index) - -static void init_pq_ParameterRange (const ProductQuantizer & pq, - ParameterRange & pr) -{ - if (pq.code_size % 4 == 0) { - // Polysemous not supported for code sizes that are not a - // multiple of 4 - for (int i = 2; i <= pq.code_size * 8 / 2; i+= 2) - pr.values.push_back(i); - } - pr.values.push_back (pq.code_size * 8); -} - -ParameterRange &ParameterSpace::add_range(const char * name) -{ - for (auto & pr : parameter_ranges) { - if (pr.name == name) { - return pr; - } - } - parameter_ranges.push_back (ParameterRange ()); - parameter_ranges.back ().name = name; - return parameter_ranges.back (); -} - - -/// initialize with reasonable parameters for the index -void ParameterSpace::initialize (const Index * index) -{ - if (DC (IndexPreTransform)) { - index = ix->index; - } - if (DC (IndexRefineFlat)) { - ParameterRange & pr = add_range("k_factor_rf"); - for (int i = 0; i <= 6; i++) { - pr.values.push_back (1 << i); - } - index = ix->base_index; - } - if (DC (IndexPreTransform)) { - index = ix->index; - } - - if (DC (IndexIVF)) { - { - ParameterRange & pr = add_range("nprobe"); - for (int i = 0; i < 13; i++) { - size_t nprobe = 1 << i; - if (nprobe >= ix->nlist) break; - pr.values.push_back (nprobe); - } - } - if (dynamic_cast(ix->quantizer)) { - ParameterRange & pr = add_range("efSearch"); - for (int i = 2; i <= 9; i++) { - pr.values.push_back (1 << i); - } - } - } - if (DC (IndexPQ)) { - ParameterRange & pr = add_range("ht"); - init_pq_ParameterRange (ix->pq, pr); - } - if (DC (IndexIVFPQ)) { - ParameterRange & pr = add_range("ht"); - init_pq_ParameterRange (ix->pq, pr); - } - - if (DC (IndexIVF)) { - const MultiIndexQuantizer *miq = - dynamic_cast (ix->quantizer); - if (miq) { - ParameterRange & pr_max_codes = add_range("max_codes"); - for (int i = 8; i < 20; i++) { - pr_max_codes.values.push_back (1 << i); - } - pr_max_codes.values.push_back ( - std::numeric_limits::infinity() - ); - } - } - if (DC (IndexIVFPQR)) { - ParameterRange & pr = add_range("k_factor"); - for (int i = 0; i <= 6; i++) { - pr.values.push_back (1 << i); - } - } - if (dynamic_cast(index)) { - ParameterRange & pr = add_range("efSearch"); - for (int i = 2; i <= 9; i++) { - pr.values.push_back (1 << i); - } - } -} - -#undef DC - -// non-const version -#define DC(classname) classname *ix = dynamic_cast(index) - - -/// set a combination of parameters on an index -void ParameterSpace::set_index_parameters (Index *index, size_t cno) const -{ - - for (int i = 0; i < parameter_ranges.size(); i++) { - const ParameterRange & pr = parameter_ranges[i]; - size_t j = cno % pr.values.size(); - cno /= pr.values.size(); - double val = pr.values [j]; - set_index_parameter (index, pr.name, val); - } -} - -/// set a combination of parameters on an index -void ParameterSpace::set_index_parameters ( - Index *index, const char *description_in) const -{ - char description[strlen(description_in) + 1]; - char *ptr; - memcpy (description, description_in, strlen(description_in) + 1); - - for (char *tok = strtok_r (description, " ,", &ptr); - tok; - tok = strtok_r (nullptr, " ,", &ptr)) { - char name[100]; - double val; - int ret = sscanf (tok, "%100[^=]=%lf", name, &val); - FAISS_THROW_IF_NOT_FMT ( - ret == 2, "could not interpret parameters %s", tok); - set_index_parameter (index, name, val); - } - -} - -void ParameterSpace::set_index_parameter ( - Index * index, const std::string & name, double val) const -{ - if (verbose > 1) - printf(" set %s=%g\n", name.c_str(), val); - - if (name == "verbose") { - index->verbose = int(val); - // and fall through to also enable it on sub-indexes - } - if (DC (IndexPreTransform)) { - set_index_parameter (ix->index, name, val); - return; - } - if (DC (IndexShards)) { - // call on all sub-indexes - auto fn = - [this, name, val](int, Index* subIndex) { - set_index_parameter(subIndex, name, val); - }; - - ix->runOnIndex(fn); - return; - } - if (DC (IndexReplicas)) { - // call on all sub-indexes - auto fn = - [this, name, val](int, Index* subIndex) { - set_index_parameter(subIndex, name, val); - }; - - ix->runOnIndex(fn); - return; - } - if (DC (IndexRefineFlat)) { - if (name == "k_factor_rf") { - ix->k_factor = int(val); - return; - } - // otherwise it is for the sub-index - set_index_parameter (&ix->refine_index, name, val); - return; - } - - if (name == "verbose") { - index->verbose = int(val); - return; // last verbose that we could find - } - - if (name == "nprobe") { - if (DC (IndexIDMap)) { - set_index_parameter (ix->index, name, val); - return; - } else if (DC (IndexIVF)) { - ix->nprobe = int(val); - return; - } - } - - if (name == "ht") { - if (DC (IndexPQ)) { - if (val >= ix->pq.code_size * 8) { - ix->search_type = IndexPQ::ST_PQ; - } else { - ix->search_type = IndexPQ::ST_polysemous; - ix->polysemous_ht = int(val); - } - return; - } else if (DC (IndexIVFPQ)) { - if (val >= ix->pq.code_size * 8) { - ix->polysemous_ht = 0; - } else { - ix->polysemous_ht = int(val); - } - return; - } - } - - if (name == "k_factor") { - if (DC (IndexIVFPQR)) { - ix->k_factor = val; - return; - } - } - if (name == "max_codes") { - if (DC (IndexIVF)) { - ix->max_codes = std::isfinite(val) ? size_t(val) : 0; - return; - } - } - - if (name == "efSearch") { - if (DC (IndexHNSW)) { - ix->hnsw.efSearch = int(val); - return; - } - if (DC (IndexIVF)) { - if (IndexHNSW *cq = - dynamic_cast(ix->quantizer)) { - cq->hnsw.efSearch = int(val); - return; - } - } - } - - FAISS_THROW_FMT ("ParameterSpace::set_index_parameter:" - "could not set parameter %s", - name.c_str()); -} - -void ParameterSpace::display () const -{ - printf ("ParameterSpace, %ld parameters, %ld combinations:\n", - parameter_ranges.size (), n_combinations ()); - for (int i = 0; i < parameter_ranges.size(); i++) { - const ParameterRange & pr = parameter_ranges[i]; - printf (" %s: ", pr.name.c_str ()); - char sep = '['; - for (int j = 0; j < pr.values.size(); j++) { - printf ("%c %g", sep, pr.values [j]); - sep = ','; - } - printf ("]\n"); - } -} - - - -void ParameterSpace::update_bounds (size_t cno, const OperatingPoint & op, - double *upper_bound_perf, - double *lower_bound_t) const -{ - if (combination_ge (cno, op.cno)) { - if (op.t > *lower_bound_t) *lower_bound_t = op.t; - } - if (combination_ge (op.cno, cno)) { - if (op.perf < *upper_bound_perf) *upper_bound_perf = op.perf; - } -} - - - -void ParameterSpace::explore (Index *index, - size_t nq, const float *xq, - const AutoTuneCriterion & crit, - OperatingPoints * ops) const -{ - FAISS_THROW_IF_NOT_MSG (nq == crit.nq, - "criterion does not have the same nb of queries"); - - size_t n_comb = n_combinations (); - - if (n_experiments == 0) { - - for (size_t cno = 0; cno < n_comb; cno++) { - set_index_parameters (index, cno); - std::vector I(nq * crit.nnn); - std::vector D(nq * crit.nnn); - - double t0 = getmillisecs (); - index->search (nq, xq, crit.nnn, D.data(), I.data()); - double t_search = (getmillisecs() - t0) / 1e3; - - double perf = crit.evaluate (D.data(), I.data()); - - bool keep = ops->add (perf, t_search, combination_name (cno), cno); - - if (verbose) - printf(" %ld/%ld: %s perf=%.3f t=%.3f s %s\n", cno, n_comb, - combination_name (cno).c_str(), perf, t_search, - keep ? "*" : ""); - } - return; - } - - int n_exp = n_experiments; - - if (n_exp > n_comb) n_exp = n_comb; - FAISS_THROW_IF_NOT (n_comb == 1 || n_exp > 2); - std::vector perm (n_comb); - // make sure the slowest and fastest experiment are run - perm[0] = 0; - if (n_comb > 1) { - perm[1] = n_comb - 1; - rand_perm (&perm[2], n_comb - 2, 1234); - for (int i = 2; i < perm.size(); i++) perm[i] ++; - } - - for (size_t xp = 0; xp < n_exp; xp++) { - size_t cno = perm[xp]; - - if (verbose) - printf(" %ld/%d: cno=%ld %s ", xp, n_exp, cno, - combination_name (cno).c_str()); - - { - double lower_bound_t = 0.0; - double upper_bound_perf = 1.0; - for (int i = 0; i < ops->all_pts.size(); i++) { - update_bounds (cno, ops->all_pts[i], - &upper_bound_perf, &lower_bound_t); - } - double best_t = ops->t_for_perf (upper_bound_perf); - if (verbose) - printf ("bounds [perf<=%.3f t>=%.3f] %s", - upper_bound_perf, lower_bound_t, - best_t <= lower_bound_t ? "skip\n" : ""); - if (best_t <= lower_bound_t) continue; - } - - set_index_parameters (index, cno); - std::vector I(nq * crit.nnn); - std::vector D(nq * crit.nnn); - - double t0 = getmillisecs (); - - int nrun = 0; - double t_search; - - do { - - if (thread_over_batches) { -#pragma omp parallel for - for (size_t q0 = 0; q0 < nq; q0 += batchsize) { - size_t q1 = q0 + batchsize; - if (q1 > nq) q1 = nq; - index->search (q1 - q0, xq + q0 * index->d, - crit.nnn, - D.data() + q0 * crit.nnn, - I.data() + q0 * crit.nnn); - } - } else { - for (size_t q0 = 0; q0 < nq; q0 += batchsize) { - size_t q1 = q0 + batchsize; - if (q1 > nq) q1 = nq; - index->search (q1 - q0, xq + q0 * index->d, - crit.nnn, - D.data() + q0 * crit.nnn, - I.data() + q0 * crit.nnn); - } - } - nrun ++; - t_search = (getmillisecs() - t0) / 1e3; - - } while (t_search < min_test_duration); - - t_search /= nrun; - - double perf = crit.evaluate (D.data(), I.data()); - - bool keep = ops->add (perf, t_search, combination_name (cno), cno); - - if (verbose) - printf(" perf %.3f t %.3f (%d runs) %s\n", - perf, t_search, nrun, - keep ? "*" : ""); - } -} - - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/AutoTune.h b/core/src/index/thirdparty/faiss/AutoTune.h deleted file mode 100644 index d7eff14e64..0000000000 --- a/core/src/index/thirdparty/faiss/AutoTune.h +++ /dev/null @@ -1,212 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_AUTO_TUNE_H -#define FAISS_AUTO_TUNE_H - -#include -#include -#include - -#include -#include - -namespace faiss { - - -/** - * Evaluation criterion. Returns a performance measure in [0,1], - * higher is better. - */ -struct AutoTuneCriterion { - typedef Index::idx_t idx_t; - idx_t nq; ///< nb of queries this criterion is evaluated on - idx_t nnn; ///< nb of NNs that the query should request - idx_t gt_nnn; ///< nb of GT NNs required to evaluate crterion - - std::vector gt_D; ///< Ground-truth distances (size nq * gt_nnn) - std::vector gt_I; ///< Ground-truth indexes (size nq * gt_nnn) - - AutoTuneCriterion (idx_t nq, idx_t nnn); - - /** Intitializes the gt_D and gt_I vectors. Must be called before evaluating - * - * @param gt_D_in size nq * gt_nnn - * @param gt_I_in size nq * gt_nnn - */ - void set_groundtruth (int gt_nnn, const float *gt_D_in, - const idx_t *gt_I_in); - - /** Evaluate the criterion. - * - * @param D size nq * nnn - * @param I size nq * nnn - * @return the criterion, between 0 and 1. Larger is better. - */ - virtual double evaluate (const float *D, const idx_t *I) const = 0; - - virtual ~AutoTuneCriterion () {} - -}; - -struct OneRecallAtRCriterion: AutoTuneCriterion { - - idx_t R; - - OneRecallAtRCriterion (idx_t nq, idx_t R); - - double evaluate(const float* D, const idx_t* I) const override; - - ~OneRecallAtRCriterion() override {} -}; - - -struct IntersectionCriterion: AutoTuneCriterion { - - idx_t R; - - IntersectionCriterion (idx_t nq, idx_t R); - - double evaluate(const float* D, const idx_t* I) const override; - - ~IntersectionCriterion() override {} -}; - -/** - * Maintains a list of experimental results. Each operating point is a - * (perf, t, key) triplet, where higher perf and lower t is - * better. The key field is an arbitrary identifier for the operating point - */ - -struct OperatingPoint { - double perf; ///< performance measure (output of a Criterion) - double t; ///< corresponding execution time (ms) - std::string key; ///< key that identifies this op pt - int64_t cno; ///< integer identifer -}; - -struct OperatingPoints { - /// all operating points - std::vector all_pts; - - /// optimal operating points, sorted by perf - std::vector optimal_pts; - - // begins with a single operating point: t=0, perf=0 - OperatingPoints (); - - /// add operating points from other to this, with a prefix to the keys - int merge_with (const OperatingPoints &other, - const std::string & prefix = ""); - - void clear (); - - /// add a performance measure. Return whether it is an optimal point - bool add (double perf, double t, const std::string & key, size_t cno = 0); - - /// get time required to obtain a given performance measure - double t_for_perf (double perf) const; - - /// easy-to-read output - void display (bool only_optimal = true) const; - - /// output to a format easy to digest by gnuplot - void all_to_gnuplot (const char *fname) const; - void optimal_to_gnuplot (const char *fname) const; - -}; - -/// possible values of a parameter, sorted from least to most expensive/accurate -struct ParameterRange { - std::string name; - std::vector values; -}; - -/** Uses a-priori knowledge on the Faiss indexes to extract tunable parameters. - */ -struct ParameterSpace { - /// all tunable parameters - std::vector parameter_ranges; - - // exploration parameters - - /// verbosity during exploration - int verbose; - - /// nb of experiments during optimization (0 = try all combinations) - int n_experiments; - - /// maximum number of queries to submit at a time. - size_t batchsize; - - /// use multithreading over batches (useful to benchmark - /// independent single-searches) - bool thread_over_batches; - - /// run tests several times until they reach at least this - /// duration (to avoid jittering in MT mode) - double min_test_duration; - - ParameterSpace (); - - /// nb of combinations, = product of values sizes - size_t n_combinations () const; - - /// returns whether combinations c1 >= c2 in the tuple sense - bool combination_ge (size_t c1, size_t c2) const; - - /// get string representation of the combination - std::string combination_name (size_t cno) const; - - /// print a description on stdout - void display () const; - - /// add a new parameter (or return it if it exists) - ParameterRange &add_range(const char * name); - - /// initialize with reasonable parameters for the index - virtual void initialize (const Index * index); - - /// set a combination of parameters on an index - void set_index_parameters (Index *index, size_t cno) const; - - /// set a combination of parameters described by a string - void set_index_parameters (Index *index, const char *param_string) const; - - /// set one of the parameters - virtual void set_index_parameter ( - Index * index, const std::string & name, double val) const; - - /** find an upper bound on the performance and a lower bound on t - * for configuration cno given another operating point op */ - void update_bounds (size_t cno, const OperatingPoint & op, - double *upper_bound_perf, - double *lower_bound_t) const; - - /** explore operating points - * @param index index to run on - * @param xq query vectors (size nq * index.d) - * @param crit selection criterion - * @param ops resulting operating points - */ - void explore (Index *index, - size_t nq, const float *xq, - const AutoTuneCriterion & crit, - OperatingPoints * ops) const; - - virtual ~ParameterSpace () {} -}; - - - -} // namespace faiss - - - -#endif diff --git a/core/src/index/thirdparty/faiss/BuilderSuspend.cpp b/core/src/index/thirdparty/faiss/BuilderSuspend.cpp deleted file mode 100644 index dd32b630f7..0000000000 --- a/core/src/index/thirdparty/faiss/BuilderSuspend.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "BuilderSuspend.h" - -namespace faiss { - -std::atomic BuilderSuspend::suspend_flag_(false); -std::mutex BuilderSuspend::mutex_; -std::condition_variable BuilderSuspend::cv_; - -void BuilderSuspend::suspend() { - suspend_flag_ = true; -} - -void BuilderSuspend::resume() { - suspend_flag_ = false; -} - -void BuilderSuspend::check_wait() { - while (suspend_flag_) { - std::unique_lock lck(mutex_); - cv_.wait_for(lck, std::chrono::seconds(5)); - } -} - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/BuilderSuspend.h b/core/src/index/thirdparty/faiss/BuilderSuspend.h deleted file mode 100644 index d5291a9628..0000000000 --- a/core/src/index/thirdparty/faiss/BuilderSuspend.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include - -namespace faiss { - -class BuilderSuspend { -public: - static void suspend(); - static void resume(); - static void check_wait(); - -private: - static std::atomic suspend_flag_; - static std::mutex mutex_; - static std::condition_variable cv_; - -}; - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/CODE_OF_CONDUCT.md b/core/src/index/thirdparty/faiss/CODE_OF_CONDUCT.md deleted file mode 100644 index ac27d8a51b..0000000000 --- a/core/src/index/thirdparty/faiss/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,2 +0,0 @@ -# Code of Conduct -Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please [read the full text](https://code.fb.com/codeofconduct) so that you can understand what actions will and will not be tolerated. \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/CONTRIBUTING.md b/core/src/index/thirdparty/faiss/CONTRIBUTING.md deleted file mode 100644 index a93141be47..0000000000 --- a/core/src/index/thirdparty/faiss/CONTRIBUTING.md +++ /dev/null @@ -1,53 +0,0 @@ -# Contributing to Faiss - -We want to make contributing to this project as easy and transparent as -possible. - -## Our Development Process - -We mainly develop Faiss within Facebook. Sometimes, we will sync the -github version of Faiss with the internal state. - -## Pull Requests - -We welcome pull requests that add significant value to Faiss. If you plan to do -a major development and contribute it back to Faiss, please contact us first before -putting too much effort into it. - -1. Fork the repo and create your branch from `master`. -2. If you've added code that should be tested, add tests. -3. If you've changed APIs, update the documentation. -4. Ensure the test suite passes. -5. Make sure your code lints. -6. If you haven't already, complete the Contributor License Agreement ("CLA"). - -There is a Facebook internal test suite for Faiss, and we need to run -all changes to Faiss through it. - -## Contributor License Agreement ("CLA") - -In order to accept your pull request, we need you to submit a CLA. You only need -to do this once to work on any of Facebook's open source projects. - -Complete your CLA here: - -## Issues - -We use GitHub issues to track public bugs. Please ensure your description is -clear and has sufficient instructions to be able to reproduce the issue. - -Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe -disclosure of security bugs. In those cases, please go through the process -outlined on that page and do not file a public issue. - -## Coding Style - -* 4 or 2 spaces for indentation in C++ (no tabs) -* 80 character line length (both for C++ and Python) -* C++ language level: C++11 - -## License - -By contributing to Faiss, you agree that your contributions will be licensed -under the LICENSE file in the root directory of this source tree. - diff --git a/core/src/index/thirdparty/faiss/Clustering.cpp b/core/src/index/thirdparty/faiss/Clustering.cpp deleted file mode 100644 index eba243d17d..0000000000 --- a/core/src/index/thirdparty/faiss/Clustering.cpp +++ /dev/null @@ -1,526 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -namespace faiss { - -ClusteringParameters::ClusteringParameters (): - niter(25), - nredo(1), - verbose(false), - spherical(false), - int_centroids(false), - update_index(false), - frozen_centroids(false), - min_points_per_centroid(39), - max_points_per_centroid(256), - seed(1234), - decode_block_size(32768) -{} -// 39 corresponds to 10000 / 256 -> to avoid warnings on PQ tests with randu10k - - -Clustering::Clustering (int d, int k): - d(d), k(k) {} - -Clustering::Clustering (int d, int k, const ClusteringParameters &cp): - ClusteringParameters (cp), d(d), k(k) {} - - - -static double imbalance_factor (int n, int k, int64_t *assign) { - std::vector hist(k, 0); - for (int i = 0; i < n; i++) - hist[assign[i]]++; - - double tot = 0, uf = 0; - - for (int i = 0 ; i < k ; i++) { - tot += hist[i]; - uf += hist[i] * (double) hist[i]; - } - uf = uf * k / (tot * tot); - - return uf; -} - -void Clustering::post_process_centroids () -{ - - if (spherical) { - fvec_renorm_L2 (d, k, centroids.data()); - } - - if (int_centroids) { - for (size_t i = 0; i < centroids.size(); i++) - centroids[i] = roundf (centroids[i]); - } -} - - -void Clustering::train (idx_t nx, const float *x_in, Index & index, - const float *weights) { - train_encoded (nx, reinterpret_cast(x_in), nullptr, - index, weights); -} - - -namespace { - -using idx_t = Clustering::idx_t; - -idx_t subsample_training_set( - const Clustering &clus, idx_t nx, const uint8_t *x, - size_t line_size, const float * weights, - uint8_t **x_out, - float **weights_out -) -{ - if (clus.verbose) { - printf("Sampling a subset of %ld / %ld for training\n", - clus.k * clus.max_points_per_centroid, nx); - } - std::vector perm (nx); - rand_perm (perm.data (), nx, clus.seed); - nx = clus.k * clus.max_points_per_centroid; - uint8_t * x_new = new uint8_t [nx * line_size]; - *x_out = x_new; - for (idx_t i = 0; i < nx; i++) { - memcpy (x_new + i * line_size, x + perm[i] * line_size, line_size); - } - if (weights) { - float *weights_new = new float[nx]; - for (idx_t i = 0; i < nx; i++) { - weights_new[i] = weights[perm[i]]; - } - *weights_out = weights_new; - } else { - *weights_out = nullptr; - } - return nx; -} - -/** compute centroids as (weighted) sum of training points - * - * @param x training vectors, size n * code_size (from codec) - * @param codec how to decode the vectors (if NULL then cast to float*) - * @param weights per-training vector weight, size n (or NULL) - * @param assign nearest centroid for each training vector, size n - * @param k_frozen do not update the k_frozen first centroids - * @param centroids centroid vectors (output only), size k * d - * @param hassign histogram of assignments per centroid (size k), - * should be 0 on input - * - */ - -void compute_centroids (size_t d, size_t k, size_t n, - size_t k_frozen, - const uint8_t * x, const Index *codec, - const int64_t * assign, - const float * weights, - float * hassign, - float * centroids) -{ - k -= k_frozen; - centroids += k_frozen * d; - - memset (centroids, 0, sizeof(*centroids) * d * k); - - size_t line_size = codec ? codec->sa_code_size() : d * sizeof (float); - -#pragma omp parallel - { - int nt = omp_get_num_threads(); - int rank = omp_get_thread_num(); - - // this thread is taking care of centroids c0:c1 - size_t c0 = (k * rank) / nt; - size_t c1 = (k * (rank + 1)) / nt; - std::vector decode_buffer (d); - - for (size_t i = 0; i < n; i++) { - int64_t ci = assign[i]; - assert (ci >= 0 && ci < k + k_frozen); - ci -= k_frozen; - if (ci >= c0 && ci < c1) { - float * c = centroids + ci * d; - const float * xi; - if (!codec) { - xi = reinterpret_cast(x + i * line_size); - } else { - float *xif = decode_buffer.data(); - codec->sa_decode (1, x + i * line_size, xif); - xi = xif; - } - if (weights) { - float w = weights[i]; - hassign[ci] += w; - for (size_t j = 0; j < d; j++) { - c[j] += xi[j] * w; - } - } else { - hassign[ci] += 1.0; - for (size_t j = 0; j < d; j++) { - c[j] += xi[j]; - } - } - } - } - - } - -#pragma omp parallel for - for (size_t ci = 0; ci < k; ci++) { - if (hassign[ci] == 0) { - continue; - } - float norm = 1 / hassign[ci]; - float * c = centroids + ci * d; - for (size_t j = 0; j < d; j++) { - c[j] *= norm; - } - } - -} - -// a bit above machine epsilon for float16 -#define EPS (1 / 1024.) - -/** Handle empty clusters by splitting larger ones. - * - * It works by slightly changing the centroids to make 2 clusters from - * a single one. Takes the same arguements as compute_centroids. - * - * @return nb of spliting operations (larger is worse) - */ -int split_clusters (size_t d, size_t k, size_t n, - size_t k_frozen, - float * hassign, - float * centroids) -{ - k -= k_frozen; - centroids += k_frozen * d; - - /* Take care of void clusters */ - size_t nsplit = 0; - RandomGenerator rng (1234); - for (size_t ci = 0; ci < k; ci++) { - if (hassign[ci] == 0) { /* need to redefine a centroid */ - size_t cj; - for (cj = 0; 1; cj = (cj + 1) % k) { - /* probability to pick this cluster for split */ - float p = (hassign[cj] - 1.0) / (float) (n - k); - float r = rng.rand_float (); - if (r < p) { - break; /* found our cluster to be split */ - } - } - memcpy (centroids+ci*d, centroids+cj*d, sizeof(*centroids) * d); - - /* small symmetric pertubation */ - for (size_t j = 0; j < d; j++) { - if (j % 2 == 0) { - centroids[ci * d + j] *= 1 + EPS; - centroids[cj * d + j] *= 1 - EPS; - } else { - centroids[ci * d + j] *= 1 - EPS; - centroids[cj * d + j] *= 1 + EPS; - } - } - - /* assume even split of the cluster */ - hassign[ci] = hassign[cj] / 2; - hassign[cj] -= hassign[ci]; - nsplit++; - } - } - - return nsplit; - -} - - - -}; - - -void Clustering::train_encoded (idx_t nx, const uint8_t *x_in, - const Index * codec, Index & index, - const float *weights) { - - FAISS_THROW_IF_NOT_FMT (nx >= k, - "Number of training points (%ld) should be at least " - "as large as number of clusters (%ld)", nx, k); - - FAISS_THROW_IF_NOT_FMT ((!codec || codec->d == d), - "Codec dimension %d not the same as data dimension %d", - int(codec->d), int(d)); - - FAISS_THROW_IF_NOT_FMT (index.d == d, - "Index dimension %d not the same as data dimension %d", - int(index.d), int(d)); - - double t0 = getmillisecs(); - - if (!codec) { - // Check for NaNs in input data. Normally it is the user's - // responsibility, but it may spare us some hard-to-debug - // reports. - const float *x = reinterpret_cast(x_in); - for (size_t i = 0; i < nx * d; i++) { - FAISS_THROW_IF_NOT_MSG (finite (x[i]), - "input contains NaN's or Inf's"); - } - } - - const uint8_t *x = x_in; - std::unique_ptr del1; - std::unique_ptr del3; - size_t line_size = codec ? codec->sa_code_size() : sizeof(float) * d; - - if (nx > k * max_points_per_centroid) { - uint8_t *x_new; - float *weights_new; - nx = subsample_training_set (*this, nx, x, line_size, weights, - &x_new, &weights_new); - del1.reset (x_new); x = x_new; - del3.reset (weights_new); weights = weights_new; - } else if (nx < k * min_points_per_centroid) { - fprintf (stderr, - "WARNING clustering %ld points to %ld centroids: " - "please provide at least %ld training points\n", - nx, k, idx_t(k) * min_points_per_centroid); - } - - if (nx == k) { - // this is a corner case, just copy training set to clusters - if (verbose) { - printf("Number of training points (%ld) same as number of " - "clusters, just copying\n", nx); - } - centroids.resize (d * k); - if (!codec) { - memcpy (centroids.data(), x_in, sizeof (float) * d * k); - } else { - codec->sa_decode (nx, x_in, centroids.data()); - } - - // one fake iteration... - ClusteringIterationStats stats = { 0.0, 0.0, 0.0, 1.0, 0 }; - iteration_stats.push_back (stats); - - index.reset(); - index.add(k, centroids.data()); - return; - } - - - if (verbose) { - printf("Clustering %d points in %ldD to %ld clusters, " - "redo %d times, %d iterations\n", - int(nx), d, k, nredo, niter); - if (codec) { - printf("Input data encoded in %ld bytes per vector\n", - codec->sa_code_size ()); - } - } - - std::unique_ptr assign(new idx_t[nx]); - std::unique_ptr dis(new float[nx]); - - // remember best iteration for redo - float best_err = HUGE_VALF; - std::vector best_obj; - std::vector best_centroids; - - // support input centroids - - FAISS_THROW_IF_NOT_MSG ( - centroids.size() % d == 0, - "size of provided input centroids not a multiple of dimension" - ); - - size_t n_input_centroids = centroids.size() / d; - - if (verbose && n_input_centroids > 0) { - printf (" Using %zd centroids provided as input (%sfrozen)\n", - n_input_centroids, frozen_centroids ? "" : "not "); - } - - double t_search_tot = 0; - if (verbose) { - printf(" Preprocessing in %.2f s\n", - (getmillisecs() - t0) / 1000.); - } - t0 = getmillisecs(); - - // temporary buffer to decode vectors during the optimization - std::vector decode_buffer - (codec ? d * decode_block_size : 0); - - for (int redo = 0; redo < nredo; redo++) { - - if (verbose && nredo > 1) { - printf("Outer iteration %d / %d\n", redo, nredo); - } - - // initialize (remaining) centroids with random points from the dataset - centroids.resize (d * k); - std::vector perm (nx); - - rand_perm (perm.data(), nx, seed + 1 + redo * 15486557L); - - if (!codec) { - for (int i = n_input_centroids; i < k ; i++) { - memcpy (¢roids[i * d], x + perm[i] * line_size, line_size); - } - } else { - for (int i = n_input_centroids; i < k ; i++) { - codec->sa_decode (1, x + perm[i] * line_size, ¢roids[i * d]); - } - } - - post_process_centroids (); - - // prepare the index - - if (index.ntotal != 0) { - index.reset(); - } - - if (!index.is_trained) { - index.train (k, centroids.data()); - } - - index.add (k, centroids.data()); - - // k-means iterations - - float err = 0; - for (int i = 0; i < niter; i++) { - double t0s = getmillisecs(); - - if (!codec) { - index.assign (nx, reinterpret_cast(x), - assign.get(), dis.get()); - } else { - // search by blocks of decode_block_size vectors - size_t code_size = codec->sa_code_size (); - for (size_t i0 = 0; i0 < nx; i0 += decode_block_size) { - size_t i1 = i0 + decode_block_size; - if (i1 > nx) { i1 = nx; } - codec->sa_decode (i1 - i0, x + code_size * i0, - decode_buffer.data ()); - index.search (i1 - i0, decode_buffer.data (), 1, - dis.get() + i0, assign.get() + i0); - } - } - - InterruptCallback::check(); - t_search_tot += getmillisecs() - t0s; - - // accumulate error - err = 0; - for (int j = 0; j < nx; j++) { - err += dis[j]; - } - - // update the centroids - std::vector hassign (k); - - size_t k_frozen = frozen_centroids ? n_input_centroids : 0; - compute_centroids ( - d, k, nx, k_frozen, - x, codec, assign.get(), weights, - hassign.data(), centroids.data() - ); - - int nsplit = split_clusters ( - d, k, nx, k_frozen, - hassign.data(), centroids.data() - ); - - // collect statistics - ClusteringIterationStats stats = - { err, (getmillisecs() - t0) / 1000.0, - t_search_tot / 1000, imbalance_factor (nx, k, assign.get()), - nsplit }; - iteration_stats.push_back(stats); - - if (verbose) { - printf (" Iteration %d (%.2f s, search %.2f s): " - "objective=%g imbalance=%.3f nsplit=%d \r", - i, stats.time, stats.time_search, stats.obj, - stats.imbalance_factor, nsplit); - fflush (stdout); - } - - post_process_centroids (); - - // add centroids to index for the next iteration (or for output) - - index.reset (); - if (update_index) { - index.train (k, centroids.data()); - } - - index.add (k, centroids.data()); - InterruptCallback::check (); - } - - if (verbose) printf("\n"); - if (nredo > 1) { - if (err < best_err) { - if (verbose) { - printf ("Objective improved: keep new clusters\n"); - } - best_centroids = centroids; - best_obj = iteration_stats; - best_err = err; - } - index.reset (); - } - } - if (nredo > 1) { - centroids = best_centroids; - iteration_stats = best_obj; - index.reset(); - index.add(k, best_centroids.data()); - } - -} - -float kmeans_clustering (size_t d, size_t n, size_t k, - const float *x, - float *centroids) -{ - Clustering clus (d, k); - clus.verbose = d * n * k > (1L << 30); - // display logs if > 1Gflop per iteration - IndexFlatL2 index (d); - clus.train (n, x, index); - memcpy(centroids, clus.centroids.data(), sizeof(*centroids) * d * k); - return clus.iteration_stats.back().obj; -} - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/Clustering.h b/core/src/index/thirdparty/faiss/Clustering.h deleted file mode 100644 index 46410af79f..0000000000 --- a/core/src/index/thirdparty/faiss/Clustering.h +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_CLUSTERING_H -#define FAISS_CLUSTERING_H -#include - -#include - -namespace faiss { - - -/** Class for the clustering parameters. Can be passed to the - * constructor of the Clustering object. - */ -struct ClusteringParameters { - int niter; ///< clustering iterations - int nredo; ///< redo clustering this many times and keep best - - bool verbose; - bool spherical; ///< do we want normalized centroids? - bool int_centroids; ///< round centroids coordinates to integer - bool update_index; ///< re-train index after each iteration? - bool frozen_centroids; ///< use the centroids provided as input and do not change them during iterations - - int min_points_per_centroid; ///< otherwise you get a warning - int max_points_per_centroid; ///< to limit size of dataset - - int seed; ///< seed for the random number generator - - size_t decode_block_size; ///< how many vectors at a time to decode - - /// sets reasonable defaults - ClusteringParameters (); -}; - - -struct ClusteringIterationStats { - float obj; ///< objective values (sum of distances reported by index) - double time; ///< seconds for iteration - double time_search; ///< seconds for just search - double imbalance_factor; ///< imbalance factor of iteration - int nsplit; ///< number of cluster splits -}; - - -/** K-means clustering based on assignment - centroid update iterations - * - * The clustering is based on an Index object that assigns training - * points to the centroids. Therefore, at each iteration the centroids - * are added to the index. - * - * On output, the centoids table is set to the latest version - * of the centroids and they are also added to the index. If the - * centroids table it is not empty on input, it is also used for - * initialization. - * - */ -struct Clustering: ClusteringParameters { - typedef Index::idx_t idx_t; - size_t d; ///< dimension of the vectors - size_t k; ///< nb of centroids - - /** centroids (k * d) - * if centroids are set on input to train, they will be used as initialization - */ - std::vector centroids; - - /// stats at every iteration of clustering - std::vector iteration_stats; - - Clustering (int d, int k); - Clustering (int d, int k, const ClusteringParameters &cp); - - /** run k-means training - * - * @param x training vectors, size n * d - * @param index index used for assignment - * @param x_weights weight associated to each vector: NULL or size n - */ - virtual void train (idx_t n, const float * x, faiss::Index & index, - const float *x_weights = nullptr); - - - /** run with encoded vectors - * - * win addition to train()'s parameters takes a codec as parameter - * to decode the input vectors. - * - * @param codec codec used to decode the vectors (nullptr = - * vectors are in fact floats) * - */ - void train_encoded (idx_t nx, const uint8_t *x_in, - const Index * codec, Index & index, - const float *weights = nullptr); - - /// Post-process the centroids after each centroid update. - /// includes optional L2 normalization and nearest integer rounding - void post_process_centroids (); - - virtual ~Clustering() {} -}; - - -/** simplified interface - * - * @param d dimension of the data - * @param n nb of training vectors - * @param k nb of output centroids - * @param x training set (size n * d) - * @param centroids output centroids (size k * d) - * @return final quantization error - */ -float kmeans_clustering (size_t d, size_t n, size_t k, - const float *x, - float *centroids); - - - -} - - -#endif diff --git a/core/src/index/thirdparty/faiss/DirectMap.cpp b/core/src/index/thirdparty/faiss/DirectMap.cpp deleted file mode 100644 index bd3cf5460f..0000000000 --- a/core/src/index/thirdparty/faiss/DirectMap.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include - -#include -#include - -namespace faiss { - -DirectMap::DirectMap(): type(NoMap) -{} - -void DirectMap::set_type (Type new_type, const InvertedLists *invlists, size_t ntotal) { - - FAISS_THROW_IF_NOT (new_type == NoMap || new_type == Array || - new_type == Hashtable); - - if (new_type == type) { - // nothing to do - return; - } - - array.clear (); - hashtable.clear (); - type = new_type; - - if (new_type == NoMap) { - return; - } else if (new_type == Array) { - array.resize (ntotal, -1); - } else if (new_type == Hashtable) { - hashtable.reserve (ntotal); - } - - for (size_t key = 0; key < invlists->nlist; key++) { - size_t list_size = invlists->list_size (key); - InvertedLists::ScopedIds idlist (invlists, key); - - if (new_type == Array) { - for (long ofs = 0; ofs < list_size; ofs++) { - FAISS_THROW_IF_NOT_MSG ( - 0 <= idlist [ofs] && idlist[ofs] < ntotal, - "direct map supported only for seuquential ids"); - array [idlist [ofs]] = lo_build(key, ofs); - } - } else if (new_type == Hashtable) { - for (long ofs = 0; ofs < list_size; ofs++) { - hashtable [idlist [ofs]] = lo_build(key, ofs); - } - } - } -} - -void DirectMap::clear() -{ - array.clear (); - hashtable.clear (); -} - - -DirectMap::idx_t DirectMap::get (idx_t key) const -{ - if (type == Array) { - FAISS_THROW_IF_NOT_MSG ( - key >= 0 && key < array.size(), "invalid key" - ); - idx_t lo = array[key]; - FAISS_THROW_IF_NOT_MSG(lo >= 0, "-1 entry in direct_map"); - return lo; - } else if (type == Hashtable) { - auto res = hashtable.find (key); - FAISS_THROW_IF_NOT_MSG (res != hashtable.end(), "key not found"); - return res->second; - } else { - FAISS_THROW_MSG ("direct map not initialized"); - } -} - - - -void DirectMap::add_single_id (idx_t id, idx_t list_no, size_t offset) -{ - if (type == NoMap) return; - - if (type == Array) { - assert (id == array.size()); - if (list_no >= 0) { - array.push_back (lo_build (list_no, offset)); - } else { - array.push_back (-1); - } - } else if (type == Hashtable) { - if (list_no >= 0) { - hashtable[id] = lo_build (list_no, offset); - } - } - -} - -void DirectMap::check_can_add (const idx_t *ids) { - if (type == Array && ids) { - FAISS_THROW_MSG ("cannot have array direct map and add with ids"); - } -} - -/********************* DirectMapAdd implementation */ - - -DirectMapAdd::DirectMapAdd (DirectMap &direct_map, size_t n, const idx_t *xids): - direct_map(direct_map), type(direct_map.type), n(n), xids(xids) -{ - if (type == DirectMap::Array) { - FAISS_THROW_IF_NOT (xids == nullptr); - ntotal = direct_map.array.size(); - direct_map.array.resize (ntotal + n, -1); - } else if (type == DirectMap::Hashtable) { - // can't parallel update hashtable so use temp array - all_ofs.resize (n, -1); - } -} - - -void DirectMapAdd::add (size_t i, idx_t list_no, size_t ofs) -{ - if (type == DirectMap::Array) { - direct_map.array [ntotal + i] = lo_build (list_no, ofs); - } else if (type == DirectMap::Hashtable) { - all_ofs [i] = lo_build (list_no, ofs); - } -} - -DirectMapAdd::~DirectMapAdd () -{ - if (type == DirectMap::Hashtable) { - for (int i = 0; i < n; i++) { - idx_t id = xids ? xids[i] : ntotal + i; - direct_map.hashtable [id] = all_ofs [i]; - } - } -} - -/********************************************************/ - -using ScopedCodes = InvertedLists::ScopedCodes; -using ScopedIds = InvertedLists::ScopedIds; - - -size_t DirectMap::remove_ids(const IDSelector& sel, InvertedLists *invlists) -{ - size_t nlist = invlists->nlist; - std::vector toremove(nlist); - - size_t nremove = 0; - - if (type == NoMap) { - // exhaustive scan of IVF -#pragma omp parallel for - for (idx_t i = 0; i < nlist; i++) { - idx_t l0 = invlists->list_size (i), l = l0, j = 0; - ScopedIds idsi (invlists, i); - while (j < l) { - if (sel.is_member (idsi[j])) { - l--; - invlists->update_entry ( - i, j, - invlists->get_single_id (i, l), - ScopedCodes (invlists, i, l).get() - ); - } else { - j++; - } - } - toremove[i] = l0 - l; - } - // this will not run well in parallel on ondisk because of - // possible shrinks - for (idx_t i = 0; i < nlist; i++) { - if (toremove[i] > 0) { - nremove += toremove[i]; - invlists->resize(i, invlists->list_size(i) - toremove[i]); - } - } - } else if (type == Hashtable) { - const IDSelectorArray *sela = - dynamic_cast(&sel); - FAISS_THROW_IF_NOT_MSG ( - sela, - "remove with hashtable works only with IDSelectorArray" - ); - - for (idx_t i = 0; i < sela->n; i++) { - idx_t id = sela->ids[i]; - auto res = hashtable.find (id); - if (res != hashtable.end()) { - size_t list_no = lo_listno (res->second); - size_t offset = lo_offset (res->second); - idx_t last = invlists->list_size (list_no) - 1; - hashtable.erase (res); - if (offset < last) { - idx_t last_id = invlists->get_single_id (list_no, last); - invlists->update_entry ( - list_no, offset, - last_id, - ScopedCodes (invlists, list_no, last).get() - ); - // update hash entry for last element - hashtable [last_id] = list_no << 32 | offset; - } - invlists->resize(list_no, last); - nremove++; - } - } - - } else { - FAISS_THROW_MSG("remove not supported with this direct_map format"); - } - return nremove; -} - -void DirectMap::update_codes (InvertedLists *invlists, - int n, const idx_t *ids, - const idx_t *assign, - const uint8_t *codes) -{ - FAISS_THROW_IF_NOT (type == Array); - - size_t code_size = invlists->code_size; - - for (size_t i = 0; i < n; i++) { - idx_t id = ids[i]; - FAISS_THROW_IF_NOT_MSG (0 <= id && id < array.size(), - "id to update out of range"); - { // remove old one - idx_t dm = array [id]; - int64_t ofs = lo_offset (dm); - int64_t il = lo_listno (dm); - size_t l = invlists->list_size (il); - if (ofs != l - 1) { // move l - 1 to ofs - int64_t id2 = invlists->get_single_id (il, l - 1); - array[id2] = lo_build (il, ofs); - invlists->update_entry (il, ofs, id2, - invlists->get_single_code (il, l - 1)); - } - invlists->resize (il, l - 1); - } - { // insert new one - int64_t il = assign[i]; - size_t l = invlists->list_size (il); - idx_t dm = lo_build (il, l); - array [id] = dm; - invlists->add_entry (il, id, codes + i * code_size); - } - } -} - - -} diff --git a/core/src/index/thirdparty/faiss/DirectMap.h b/core/src/index/thirdparty/faiss/DirectMap.h deleted file mode 100644 index 27ea1c7260..0000000000 --- a/core/src/index/thirdparty/faiss/DirectMap.h +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_DIRECT_MAP_H -#define FAISS_DIRECT_MAP_H - -#include -#include - - -namespace faiss { - -// When offsets list id + offset are encoded in an uint64 -// we call this LO = list-offset - -inline uint64_t lo_build (uint64_t list_id, uint64_t offset) { - return list_id << 32 | offset; -} - -inline uint64_t lo_listno (uint64_t lo) { - return lo >> 32; -} - -inline uint64_t lo_offset (uint64_t lo) { - return lo & 0xffffffff; -} - -/** - * Direct map: a way to map back from ids to inverted lists - */ -struct DirectMap { - typedef Index::idx_t idx_t; - - enum Type { - NoMap = 0, // default - Array = 1, // sequential ids (only for add, no add_with_ids) - Hashtable = 2 // arbitrary ids - }; - Type type; - - /// map for direct access to the elements. Map ids to LO-encoded entries. - std::vector array; - std::unordered_map hashtable; - - DirectMap(); - - /// set type and initialize - void set_type (Type new_type, const InvertedLists *invlists, size_t ntotal); - - /// get an entry - idx_t get (idx_t id) const; - - /// for quick checks - bool no () const {return type == NoMap; } - - /** - * update the direct_map - */ - - /// throw if Array and ids is not NULL - void check_can_add (const idx_t *ids); - - /// non thread-safe version - void add_single_id (idx_t id, idx_t list_no, size_t offset); - - /// remove all entries - void clear(); - - /** - * operations on inverted lists that require translation with a DirectMap - */ - - /// remove ids from the InvertedLists, possibly using the direct map - size_t remove_ids(const IDSelector& sel, InvertedLists *invlists); - - /// update entries, using the direct map - void update_codes (InvertedLists *invlists, - int n, const idx_t *ids, - const idx_t *list_nos, - const uint8_t *codes); - - - -}; - -/// Thread-safe way of updating the direct_map -struct DirectMapAdd { - - typedef Index::idx_t idx_t; - - using Type = DirectMap::Type; - - DirectMap &direct_map; - DirectMap::Type type; - size_t ntotal; - size_t n; - const idx_t *xids; - - std::vector all_ofs; - - DirectMapAdd (DirectMap &direct_map, size_t n, const idx_t *xids); - - /// add vector i (with id xids[i]) at list_no and offset - void add (size_t i, idx_t list_no, size_t offset); - - ~DirectMapAdd (); -}; - - - -} - - -#endif diff --git a/core/src/index/thirdparty/faiss/Dockerfile b/core/src/index/thirdparty/faiss/Dockerfile deleted file mode 100644 index 9da42ef70f..0000000000 --- a/core/src/index/thirdparty/faiss/Dockerfile +++ /dev/null @@ -1,29 +0,0 @@ -FROM nvidia/cuda:8.0-devel-centos7 - -# Install MKL -RUN yum-config-manager --add-repo https://yum.repos.intel.com/mkl/setup/intel-mkl.repo -RUN rpm --import https://yum.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2019.PUB -RUN yum install -y intel-mkl-2019.3-062 -ENV LD_LIBRARY_PATH /opt/intel/mkl/lib/intel64:$LD_LIBRARY_PATH -ENV LIBRARY_PATH /opt/intel/mkl/lib/intel64:$LIBRARY_PATH -ENV LD_PRELOAD /usr/lib64/libgomp.so.1:/opt/intel/mkl/lib/intel64/libmkl_def.so:\ -/opt/intel/mkl/lib/intel64/libmkl_avx2.so:/opt/intel/mkl/lib/intel64/libmkl_core.so:\ -/opt/intel/mkl/lib/intel64/libmkl_intel_lp64.so:/opt/intel/mkl/lib/intel64/libmkl_gnu_thread.so - -# Install necessary build tools -RUN yum install -y gcc-c++ make swig3 - -# Install necesary headers/libs -RUN yum install -y python-devel numpy - -COPY . /opt/faiss - -WORKDIR /opt/faiss - -# --with-cuda=/usr/local/cuda-8.0 -RUN ./configure --prefix=/usr --libdir=/usr/lib64 --without-cuda -RUN make -j "$(nproc)" -RUN make -C python -RUN make test -RUN make install -RUN make -C demos demo_ivfpq_indexing && ./demos/demo_ivfpq_indexing diff --git a/core/src/index/thirdparty/faiss/FaissHook.cpp b/core/src/index/thirdparty/faiss/FaissHook.cpp deleted file mode 100644 index e20ab37e55..0000000000 --- a/core/src/index/thirdparty/faiss/FaissHook.cpp +++ /dev/null @@ -1,109 +0,0 @@ - -// -*- c++ -*- - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { - -bool faiss_use_avx512 = true; -bool faiss_use_avx2 = true; -bool faiss_use_sse = true; - -/* set default to AVX */ -fvec_func_ptr fvec_inner_product = fvec_inner_product_avx; -fvec_func_ptr fvec_L2sqr = fvec_L2sqr_avx; -fvec_func_ptr fvec_L1 = fvec_L1_avx; -fvec_func_ptr fvec_Linf = fvec_Linf_avx; - -sq_get_distance_computer_func_ptr sq_get_distance_computer = sq_get_distance_computer_avx; -sq_sel_quantizer_func_ptr sq_sel_quantizer = sq_select_quantizer_avx; -sq_sel_inv_list_scanner_func_ptr sq_sel_inv_list_scanner = sq_select_inverted_list_scanner_avx; - -/*****************************************************************************/ - -bool support_avx512() { - if (!faiss_use_avx512) return false; - - InstructionSet& instruction_set_inst = InstructionSet::GetInstance(); - return (instruction_set_inst.AVX512F() && - instruction_set_inst.AVX512DQ() && - instruction_set_inst.AVX512BW()); -} - -bool support_avx2() { - if (!faiss_use_avx2) return false; - - InstructionSet& instruction_set_inst = InstructionSet::GetInstance(); - return (instruction_set_inst.AVX2()); -} - -bool support_sse() { - if (!faiss_use_sse) return false; - - InstructionSet& instruction_set_inst = InstructionSet::GetInstance(); - return (instruction_set_inst.SSE42()); -} - -bool hook_init(std::string& cpu_flag) { - static std::mutex hook_mutex; - std::lock_guard lock(hook_mutex); - - if (support_avx512()) { - /* for IVFFLAT */ - fvec_inner_product = fvec_inner_product_avx512; - fvec_L2sqr = fvec_L2sqr_avx512; - fvec_L1 = fvec_L1_avx512; - fvec_Linf = fvec_Linf_avx512; - - /* for IVFSQ */ - sq_get_distance_computer = sq_get_distance_computer_avx512; - sq_sel_quantizer = sq_select_quantizer_avx512; - sq_sel_inv_list_scanner = sq_select_inverted_list_scanner_avx512; - - cpu_flag = "AVX512"; - } else if (support_avx2()) { - /* for IVFFLAT */ - fvec_inner_product = fvec_inner_product_avx; - fvec_L2sqr = fvec_L2sqr_avx; - fvec_L1 = fvec_L1_avx; - fvec_Linf = fvec_Linf_avx; - - /* for IVFSQ */ - sq_get_distance_computer = sq_get_distance_computer_avx; - sq_sel_quantizer = sq_select_quantizer_avx; - sq_sel_inv_list_scanner = sq_select_inverted_list_scanner_avx; - - cpu_flag = "AVX2"; - } else if (support_sse()) { - /* for IVFFLAT */ - fvec_inner_product = fvec_inner_product_sse; - fvec_L2sqr = fvec_L2sqr_sse; - fvec_L1 = fvec_L1_sse; - fvec_Linf = fvec_Linf_sse; - - /* for IVFSQ */ - sq_get_distance_computer = sq_get_distance_computer_ref; - sq_sel_quantizer = sq_select_quantizer_ref; - sq_sel_inv_list_scanner = sq_select_inverted_list_scanner_ref; - - cpu_flag = "SSE42"; - } else { - cpu_flag = "UNSUPPORTED"; - return false; - } - - return true; -} - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/FaissHook.h b/core/src/index/thirdparty/faiss/FaissHook.h deleted file mode 100644 index f1aa98f606..0000000000 --- a/core/src/index/thirdparty/faiss/FaissHook.h +++ /dev/null @@ -1,40 +0,0 @@ - -// -*- c++ -*- - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace faiss { - -typedef float (*fvec_func_ptr)(const float*, const float*, size_t); - -typedef SQDistanceComputer* (*sq_get_distance_computer_func_ptr)(MetricType, QuantizerType, size_t, const std::vector&); -typedef Quantizer* (*sq_sel_quantizer_func_ptr)(QuantizerType, size_t, const std::vector&); -typedef InvertedListScanner* (*sq_sel_inv_list_scanner_func_ptr)(MetricType, const ScalarQuantizer*, const Index*, size_t, bool, bool); - -extern bool faiss_use_avx512; -extern bool faiss_use_avx2; -extern bool faiss_use_sse; - -extern fvec_func_ptr fvec_inner_product; -extern fvec_func_ptr fvec_L2sqr; -extern fvec_func_ptr fvec_L1; -extern fvec_func_ptr fvec_Linf; - -extern sq_get_distance_computer_func_ptr sq_get_distance_computer; -extern sq_sel_quantizer_func_ptr sq_sel_quantizer; -extern sq_sel_inv_list_scanner_func_ptr sq_sel_inv_list_scanner; - -extern bool support_avx512(); -extern bool support_avx2(); -extern bool support_sse(); - -extern bool hook_init(std::string& cpu_flag); - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/INSTALL.md b/core/src/index/thirdparty/faiss/INSTALL.md deleted file mode 100644 index 01f29e46e1..0000000000 --- a/core/src/index/thirdparty/faiss/INSTALL.md +++ /dev/null @@ -1,353 +0,0 @@ -[//]: # "**********************************************************" -[//]: # "** INSTALL file for Faiss (Fair AI Similarity Search **" -[//]: # "**********************************************************" - -INSTALL file for Faiss (Fair AI Similarity Search) -================================================== - -Install via Conda ------------------ - -The easiest way to install FAISS is from Anaconda. We regularly push stable releases to the pytorch conda channel. - -Currently we support faiss-cpu both on Linux and OSX. We also provide faiss-gpu compiled with CUDA8/CUDA9/CUDA10 on Linux systems. - -You can easily install it by - -``` -# CPU version only -conda install faiss-cpu -c pytorch - -# GPU version -conda install faiss-gpu cudatoolkit=8.0 -c pytorch # For CUDA8 -conda install faiss-gpu cudatoolkit=9.0 -c pytorch # For CUDA9 -conda install faiss-gpu cudatoolkit=10.0 -c pytorch # For CUDA10 -``` - -Compile from source -------------------- - -The Faiss compilation works in 2 steps: - -1. compile the C++ core and examples - -2. compile the Python interface - -Steps 2 depends on 1. - -It is also possible to build a pure C interface. This optional process is -described separately (please see the [C interface installation file](c_api/INSTALL.md)) - -General compilation instructions -================================ - -TL;DR: `./configure && make (&& make install)` for the C++ library, and then `cd python; make && make install` for the python interface. - -1. `./configure` - -This generates the system-dependent configuration for the `Makefile`, stored in -a file called `makefile.inc`. - -A few useful options: -- `./configure --without-cuda` in order to build the CPU part only. -- `./configure --with-cuda=/path/to/cuda-10.1` in order to hint to the path of -the cudatoolkit. -- `./configure --with-cuda-arch="-gencode=arch=compute_75,code=sm_75 -gencode=arch=compute_72,code=sm_72"` for specifying which GPU architectures to build against. -- `./configure --with-python=/path/to/python3.7` in order to build a python -interface for a different python than the default one. -- `LDFLAGS=-L/path_to_mkl/lib/ ./configure` so that configure detects the MKL BLAS imeplementation. Note that this may require to set the LD_LIBRARY_PATH at runtime. - -2. `make` - -This builds the C++ library (the whole library if a suitable cuda toolkit was -found, or the CPU part only otherwise). - -3. `make install` (optional) - -This installs the headers and libraries. - -4. `make -C python` (or `make py`) - -This builds the python interface. - -5. `make -C python install` - -This installs the python library. - - -Faiss has been tested only on x86_64 machines on Linux and Mac OS. - -Faiss requires a C++ compiler that understands: -- the Intel intrinsics for SSE instructions, -- the GCC intrinsic for the popcount instruction, -- basic OpenMP. - -There are a few examples for makefile.inc in the example_makefiles/ -subdirectory. There are also indications for specific configurations in the -troubleshooting section of the wiki. - -https://github.com/facebookresearch/faiss/wiki/Troubleshooting - -Faiss comes as a .a archive, that can be linked with executables or -dynamic libraries (useful for the Python wrapper). - - -BLAS/Lapack ------------ - -The only variables that need to be configured for the C++ Faiss are -the BLAS/Lapack flags (a linear aglebra software package). It needs a -flag telling whether BLAS/Lapack uses 32 or 64 bit integers and the -linking flags. Faiss uses the Fortran 77 interface of BLAS/Lapack and -thus does not need an include path. - -There are several BLAS implementations, depending on the OS and -machine. To have reasonable performance, the BLAS library should be -multithreaded. See the example makefile.inc's for hints and examples -on how to set the flags, or simply run the configure script: - - `./configure` - -To check that the link flags are correct, and verify whether the -implementation uses 32 or 64 bit integers, you can - - `make misc/test_blas` - -and run - - `./misc/test_blas` - - -Testing Faiss -------------- - -A basic usage example is in - - `demos/demo_ivfpq_indexing` - -which you can build by calling - `make -C demos demo_ivfpq_indexing` - -It makes a small index, stores it and performs some searches. A normal -runtime is around 20s. With a fast machine and Intel MKL's BLAS it -runs in 2.5s. - -To run the whole test suite: - - `make test` (for the CPU part) - - `make test_gpu` (for the GPU part) - - -A real-life benchmark ---------------------- - -A bit longer example runs and evaluates Faiss on the SIFT1M -dataset. To run it, please download the ANN_SIFT1M dataset from - -http://corpus-texmex.irisa.fr/ - -and unzip it to the subdirectory `sift1M` at the root of the source -directory for this repository. - -Then compile and run the following (after ensuring you have installed faiss): - -``` -make demos -./demos/demo_sift1M -``` - -This is a demonstration of the high-level auto-tuning API. You can try -setting a different index_key to find the indexing structure that -gives the best performance. - - -The Python interface -====================================== - -The Python interface is compiled with - - `make -C python` (or `make py`) - -How it works ------------- - -The Python interface is provided via SWIG (Simple Wrapper and -Interface Generator) and an additional level of manual wrappers (in python/faiss.py). - -SWIG generates two wrapper files: a Python file (`python/swigfaiss.py`) and a -C++ file that must be compiled to a dynamic library (`python/_swigfaiss.so`). - -Testing the Python wrapper --------------------------- - -Often, a successful compile does not mean that the library works, -because missing symbols are detected only at runtime. You should be -able to load the Faiss dynamic library: - - `python -c "import faiss"` - -In case of failure, it reports the first missing symbol. To see all -missing symbols (on Linux), use - - `ldd -r _swigfaiss.so` - -Sometimes, problems (eg with BLAS libraries) appear only when actually -calling a BLAS function. A simple way to check this - -```python -python -c "import faiss, numpy -faiss.Kmeans(10, 20).train(numpy.random.rand(1000, 10).astype('float32')) -``` - - -Real-life test --------------- - -The following script extends the demo_sift1M test to several types of -indexes. This must be run from the root of the source directory for this -repository: - -``` -mkdir tmp # graphs of the output will be written here -PYTHONPATH=. python demos/demo_auto_tune.py -``` - -It will cycle through a few types of indexes and find optimal -operating points. You can play around with the types of indexes. - - -Step 3: Compiling the GPU implementation -======================================== - -The GPU version is a superset of the CPU version. In addition it -requires the cuda compiler and related libraries (Cublas) - -The nvcc-specific flags to pass to the compiler, based on your desired -compute capability can be customized by providing the `--with-cuda-arch` to -`./configure`. Only compute capability 3.5+ is supported. For example, we enable -by default: - -``` --gencode=arch=compute_35,code=compute_35 --gencode=arch=compute_52,code=compute_52 --gencode=arch=compute_60,code=compute_60 --gencode=arch=compute_61,code=compute_61 --gencode=arch=compute_70,code=compute_70 --gencode=arch=compute_75,code=compute_75 -``` - -However, look at https://developer.nvidia.com/cuda-gpus to determine -what compute capability you need to use, and replace our gencode -specifications with the one(s) you need. - -Most other flags are related to the C++11 compiler used by nvcc to -complile the actual C++ code. They are normally just transmitted by -nvcc, except some of them that are not recognized and that should be -escaped by prefixing them with -Xcompiler. Also link flags that are -prefixed with -Wl, should be passed with -Xlinker. - -You may want to add `-j 10` to use 10 threads during compile. - -Testing the GPU implementation ------------------------------- - -Compile the example with - - `make -C gpu/test demo_ivfpq_indexing_gpu` - -This produce the GPU code equivalent to the CPU -demo_ivfpq_indexing. It also shows how to translate indexed from/to -the GPU. - - -Python example with GPU support -------------------------------- - -The auto-tuning example above also runs on the GPU. Edit -`demos/demo_auto_tune.py` at line 100 with the values - -```python -keys_to_test = keys_gpu -use_gpu = True -``` - -and you can run - -``` -export PYTHONPATH=. -python demos/demo_auto_tune.py -``` - -to test the GPU code. - - -Docker instructions -=================== - -For using GPU capabilities of Faiss, you'll need to run "nvidia-docker" -rather than "docker". Make sure that docker -(https://docs.docker.com/engine/installation/) and nvidia-docker -(https://github.com/NVIDIA/nvidia-docker) are installed on your system - -To build the "faiss" image, run - - `nvidia-docker build -t faiss .` - -or if you don't want/need to clone the sources, just run - - `nvidia-docker build -t faiss github.com/facebookresearch/faiss` - -If you want to run the tests during the docker build, uncomment the -last 3 "RUN" steps in the Dockerfile. But you might want to run the -tests by yourself, so just run - - `nvidia-docker run -ti --name faiss faiss bash` - -and run what you want. If you need a dataset (like sift1M), download it -inside the created container, or better, mount a directory from the host - - nvidia-docker run -ti --name faiss -v /my/host/data/folder/ann_dataset/sift/:/opt/faiss/sift1M faiss bash - - -How to use Faiss in your own projects -===================================== - -C++ ---- - -The makefile generates a static and a dynamic library - -``` -libfaiss.a -libfaiss.so (or libfaiss.dylib) -``` - -the executable should be linked to one of these. If you use -the static version (.a), add the LDFLAGS used in the Makefile. - -For binary-only distributions, the headers should be under -a `faiss/` directory, so that they can be included as - -```c++ -#include -#include -``` - -Python ------- - -To import Faiss in your own Python project, you need the files - -``` -__init__.py -swigfaiss.py -_swigfaiss.so -``` -to be present in a `faiss/` directory visible in the PYTHONPATH or in the -current directory. -Then Faiss can be used in python with - -```python -import faiss -``` diff --git a/core/src/index/thirdparty/faiss/IVFlib.cpp b/core/src/index/thirdparty/faiss/IVFlib.cpp deleted file mode 100644 index 098b729357..0000000000 --- a/core/src/index/thirdparty/faiss/IVFlib.cpp +++ /dev/null @@ -1,364 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include - -#include -#include -#include -#include - - -namespace faiss { namespace ivflib { - - -void check_compatible_for_merge (const Index * index0, - const Index * index1) -{ - - const faiss::IndexPreTransform *pt0 = - dynamic_cast(index0); - - if (pt0) { - const faiss::IndexPreTransform *pt1 = - dynamic_cast(index1); - FAISS_THROW_IF_NOT_MSG (pt1, "both indexes should be pretransforms"); - - FAISS_THROW_IF_NOT (pt0->chain.size() == pt1->chain.size()); - for (int i = 0; i < pt0->chain.size(); i++) { - FAISS_THROW_IF_NOT (typeid(pt0->chain[i]) == typeid(pt1->chain[i])); - } - - index0 = pt0->index; - index1 = pt1->index; - } - FAISS_THROW_IF_NOT (typeid(index0) == typeid(index1)); - FAISS_THROW_IF_NOT (index0->d == index1->d && - index0->metric_type == index1->metric_type); - - const faiss::IndexIVF *ivf0 = dynamic_cast(index0); - if (ivf0) { - const faiss::IndexIVF *ivf1 = - dynamic_cast(index1); - FAISS_THROW_IF_NOT (ivf1); - - ivf0->check_compatible_for_merge (*ivf1); - } - - // TODO: check as thoroughfully for other index types - -} - -const IndexIVF * try_extract_index_ivf (const Index * index) -{ - if (auto *pt = - dynamic_cast(index)) { - index = pt->index; - } - - if (auto *idmap = - dynamic_cast(index)) { - index = idmap->index; - } - if (auto *idmap = - dynamic_cast(index)) { - index = idmap->index; - } - - auto *ivf = dynamic_cast(index); - - return ivf; -} - -IndexIVF * try_extract_index_ivf (Index * index) { - return const_cast (try_extract_index_ivf ((const Index*)(index))); -} - -const IndexIVF * extract_index_ivf (const Index * index) -{ - const IndexIVF *ivf = try_extract_index_ivf (index); - FAISS_THROW_IF_NOT (ivf); - return ivf; -} - -IndexIVF * extract_index_ivf (Index * index) { - return const_cast (extract_index_ivf ((const Index*)(index))); -} - - -void merge_into(faiss::Index *index0, faiss::Index *index1, bool shift_ids) { - - check_compatible_for_merge (index0, index1); - IndexIVF * ivf0 = extract_index_ivf (index0); - IndexIVF * ivf1 = extract_index_ivf (index1); - - ivf0->merge_from (*ivf1, shift_ids ? ivf0->ntotal : 0); - - // useful for IndexPreTransform - index0->ntotal = ivf0->ntotal; - index1->ntotal = ivf1->ntotal; -} - - - -void search_centroid(faiss::Index *index, - const float* x, int n, - idx_t* centroid_ids) -{ - std::unique_ptr del; - if (auto index_pre = dynamic_cast(index)) { - x = index_pre->apply_chain(n, x); - del.reset((float*)x); - index = index_pre->index; - } - faiss::IndexIVF* index_ivf = dynamic_cast(index); - assert(index_ivf); - index_ivf->quantizer->assign(n, x, centroid_ids); -} - - - -void search_and_return_centroids(faiss::Index *index, - size_t n, - const float* xin, - long k, - float *distances, - idx_t* labels, - idx_t* query_centroid_ids, - idx_t* result_centroid_ids) -{ - const float *x = xin; - std::unique_ptr del; - if (auto index_pre = dynamic_cast(index)) { - x = index_pre->apply_chain(n, x); - del.reset((float*)x); - index = index_pre->index; - } - faiss::IndexIVF* index_ivf = dynamic_cast(index); - assert(index_ivf); - - size_t nprobe = index_ivf->nprobe; - std::vector cent_nos (n * nprobe); - std::vector cent_dis (n * nprobe); - index_ivf->quantizer->search( - n, x, nprobe, cent_dis.data(), cent_nos.data()); - - if (query_centroid_ids) { - for (size_t i = 0; i < n; i++) - query_centroid_ids[i] = cent_nos[i * nprobe]; - } - - index_ivf->search_preassigned (n, x, k, - cent_nos.data(), cent_dis.data(), - distances, labels, true); - - for (size_t i = 0; i < n * k; i++) { - idx_t label = labels[i]; - if (label < 0) { - if (result_centroid_ids) - result_centroid_ids[i] = -1; - } else { - long list_no = lo_listno (label); - long list_index = lo_offset (label); - if (result_centroid_ids) - result_centroid_ids[i] = list_no; - labels[i] = index_ivf->invlists->get_single_id(list_no, list_index); - } - } -} - - -SlidingIndexWindow::SlidingIndexWindow (Index *index): index (index) { - n_slice = 0; - IndexIVF* index_ivf = const_cast(extract_index_ivf (index)); - ils = dynamic_cast (index_ivf->invlists); - nlist = ils->nlist; - FAISS_THROW_IF_NOT_MSG (ils, - "only supports indexes with ArrayInvertedLists"); - sizes.resize(nlist); -} - -template -static void shift_and_add (std::vector & dst, - size_t remove, - const std::vector & src) -{ - if (remove > 0) - memmove (dst.data(), dst.data() + remove, - (dst.size() - remove) * sizeof (T)); - size_t insert_point = dst.size() - remove; - dst.resize (insert_point + src.size()); - memcpy (dst.data() + insert_point, src.data (), src.size() * sizeof(T)); -} - -template -static void remove_from_begin (std::vector & v, - size_t remove) -{ - if (remove > 0) - v.erase (v.begin(), v.begin() + remove); -} - -void SlidingIndexWindow::step(const Index *sub_index, bool remove_oldest) { - - FAISS_THROW_IF_NOT_MSG (!remove_oldest || n_slice > 0, - "cannot remove slice: there is none"); - - const ArrayInvertedLists *ils2 = nullptr; - if(sub_index) { - check_compatible_for_merge (index, sub_index); - ils2 = dynamic_cast( - extract_index_ivf (sub_index)->invlists); - FAISS_THROW_IF_NOT_MSG (ils2, "supports only ArrayInvertedLists"); - } - IndexIVF *index_ivf = extract_index_ivf (index); - - if (remove_oldest && ils2) { - for (int i = 0; i < nlist; i++) { - std::vector & sizesi = sizes[i]; - size_t amount_to_remove = sizesi[0]; - index_ivf->ntotal += ils2->ids[i].size() - amount_to_remove; - - shift_and_add (ils->ids[i], amount_to_remove, ils2->ids[i]); - shift_and_add (ils->codes[i], amount_to_remove * ils->code_size, - ils2->codes[i]); - for (int j = 0; j + 1 < n_slice; j++) { - sizesi[j] = sizesi[j + 1] - amount_to_remove; - } - sizesi[n_slice - 1] = ils->ids[i].size(); - } - } else if (ils2) { - for (int i = 0; i < nlist; i++) { - index_ivf->ntotal += ils2->ids[i].size(); - shift_and_add (ils->ids[i], 0, ils2->ids[i]); - shift_and_add (ils->codes[i], 0, ils2->codes[i]); - sizes[i].push_back(ils->ids[i].size()); - } - n_slice++; - } else if (remove_oldest) { - for (int i = 0; i < nlist; i++) { - size_t amount_to_remove = sizes[i][0]; - index_ivf->ntotal -= amount_to_remove; - remove_from_begin (ils->ids[i], amount_to_remove); - remove_from_begin (ils->codes[i], - amount_to_remove * ils->code_size); - for (int j = 0; j + 1 < n_slice; j++) { - sizes[i][j] = sizes[i][j + 1] - amount_to_remove; - } - sizes[i].pop_back (); - } - n_slice--; - } else { - FAISS_THROW_MSG ("nothing to do???"); - } - index->ntotal = index_ivf->ntotal; -} - - - -// Get a subset of inverted lists [i0, i1). Works on IndexIVF's and -// IndexIVF's embedded in a IndexPreTransform - -ArrayInvertedLists * -get_invlist_range (const Index *index, long i0, long i1) -{ - const IndexIVF *ivf = extract_index_ivf (index); - - FAISS_THROW_IF_NOT (0 <= i0 && i0 <= i1 && i1 <= ivf->nlist); - - const InvertedLists *src = ivf->invlists; - - ArrayInvertedLists * il = new ArrayInvertedLists(i1 - i0, src->code_size); - - for (long i = i0; i < i1; i++) { - il->add_entries(i - i0, src->list_size(i), - InvertedLists::ScopedIds (src, i).get(), - InvertedLists::ScopedCodes (src, i).get()); - } - return il; -} - - - -void set_invlist_range (Index *index, long i0, long i1, - ArrayInvertedLists * src) -{ - IndexIVF *ivf = extract_index_ivf (index); - - FAISS_THROW_IF_NOT (0 <= i0 && i0 <= i1 && i1 <= ivf->nlist); - - ArrayInvertedLists *dst = dynamic_cast(ivf->invlists); - FAISS_THROW_IF_NOT_MSG (dst, "only ArrayInvertedLists supported"); - FAISS_THROW_IF_NOT (src->nlist == i1 - i0 && - dst->code_size == src->code_size); - - size_t ntotal = index->ntotal; - for (long i = i0 ; i < i1; i++) { - ntotal -= dst->list_size (i); - ntotal += src->list_size (i - i0); - std::swap (src->codes[i - i0], dst->codes[i]); - std::swap (src->ids[i - i0], dst->ids[i]); - } - ivf->ntotal = index->ntotal = ntotal; -} - - -void search_with_parameters (const Index *index, - idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, - IVFSearchParameters *params, - size_t *nb_dis_ptr) -{ - FAISS_THROW_IF_NOT (params); - const float *prev_x = x; - ScopeDeleter del; - - if (auto ip = dynamic_cast (index)) { - x = ip->apply_chain (n, x); - if (x != prev_x) { - del.set(x); - } - index = ip->index; - } - - std::vector Iq(params->nprobe * n); - std::vector Dq(params->nprobe * n); - - const IndexIVF *index_ivf = dynamic_cast(index); - FAISS_THROW_IF_NOT (index_ivf); - - double t0 = getmillisecs(); - index_ivf->quantizer->search(n, x, params->nprobe, - Dq.data(), Iq.data()); - double t1 = getmillisecs(); - indexIVF_stats.quantization_time += t1 - t0; - - if (nb_dis_ptr) { - size_t nb_dis = 0; - const InvertedLists *il = index_ivf->invlists; - for (idx_t i = 0; i < n * params->nprobe; i++) { - if (Iq[i] >= 0) { - nb_dis += il->list_size(Iq[i]); - } - } - *nb_dis_ptr = nb_dis; - } - - index_ivf->search_preassigned(n, x, k, Iq.data(), Dq.data(), - distances, labels, - false, params); - double t2 = getmillisecs(); - indexIVF_stats.search_time += t2 - t1; -} - - - -} } // namespace faiss::ivflib diff --git a/core/src/index/thirdparty/faiss/IVFlib.h b/core/src/index/thirdparty/faiss/IVFlib.h deleted file mode 100644 index 879fd19086..0000000000 --- a/core/src/index/thirdparty/faiss/IVFlib.h +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_IVFLIB_H -#define FAISS_IVFLIB_H - -/** Since IVF (inverted file) indexes are of so much use for - * large-scale use cases, we group a few functions related to them in - * this small library. Most functions work both on IndexIVFs and - * IndexIVFs embedded within an IndexPreTransform. - */ - -#include -#include - -namespace faiss { namespace ivflib { - - -/** check if two indexes have the same parameters and are trained in - * the same way, otherwise throw. */ -void check_compatible_for_merge (const Index * index1, - const Index * index2); - -/** get an IndexIVF from an index. The index may be an IndexIVF or - * some wrapper class that encloses an IndexIVF - * - * throws an exception if this is not the case. - */ -const IndexIVF * extract_index_ivf (const Index * index); -IndexIVF * extract_index_ivf (Index * index); - -/// same as above but returns nullptr instead of throwing on failure -const IndexIVF * try_extract_index_ivf (const Index * index); -IndexIVF * try_extract_index_ivf (Index * index); - -/** Merge index1 into index0. Works on IndexIVF's and IndexIVF's - * embedded in a IndexPreTransform. On output, the index1 is empty. - * - * @param shift_ids: translate the ids from index1 to index0->prev_ntotal - */ -void merge_into(Index *index0, Index *index1, bool shift_ids); - -typedef Index::idx_t idx_t; - -/* Returns the cluster the embeddings belong to. - * - * @param index Index, which should be an IVF index - * (otherwise there are no clusters) - * @param embeddings object descriptors for which the centroids should be found, - * size num_objects * d - * @param centroid_ids - * cluster id each object belongs to, size num_objects - */ -void search_centroid(Index *index, - const float* x, int n, - idx_t* centroid_ids); - -/* Returns the cluster the embeddings belong to. - * - * @param index Index, which should be an IVF index - * (otherwise there are no clusters) - * @param query_centroid_ids - * centroid ids corresponding to the query vectors (size n) - * @param result_centroid_ids - * centroid ids corresponding to the results (size n * k) - * other arguments are the same as the standard search function - */ -void search_and_return_centroids(Index *index, - size_t n, - const float* xin, - long k, - float *distances, - idx_t* labels, - idx_t* query_centroid_ids, - idx_t* result_centroid_ids); - - -/** A set of IndexIVFs concatenated together in a FIFO fashion. - * at each "step", the oldest index slice is removed and a new index is added. - */ -struct SlidingIndexWindow { - /// common index that contains the sliding window - Index * index; - - /// InvertedLists of index - ArrayInvertedLists *ils; - - /// number of slices currently in index - int n_slice; - - /// same as index->nlist - size_t nlist; - - /// cumulative list sizes at each slice - std::vector > sizes; - - /// index should be initially empty and trained - SlidingIndexWindow (Index *index); - - /** Add one index to the current index and remove the oldest one. - * - * @param sub_index slice to swap in (can be NULL) - * @param remove_oldest if true, remove the oldest slices */ - void step(const Index *sub_index, bool remove_oldest); - -}; - - -/// Get a subset of inverted lists [i0, i1) -ArrayInvertedLists * get_invlist_range (const Index *index, - long i0, long i1); - -/// Set a subset of inverted lists -void set_invlist_range (Index *index, long i0, long i1, - ArrayInvertedLists * src); - -// search an IndexIVF, possibly embedded in an IndexPreTransform with -// given parameters. Optionally returns the number of distances -// computed -void search_with_parameters (const Index *index, - idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, - IVFSearchParameters *params, - size_t *nb_dis = nullptr); - - - -} } // namespace faiss::ivflib - -#endif diff --git a/core/src/index/thirdparty/faiss/Index.cpp b/core/src/index/thirdparty/faiss/Index.cpp deleted file mode 100644 index d5748f719f..0000000000 --- a/core/src/index/thirdparty/faiss/Index.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include -#include -#include - -#include - - -namespace faiss { - -Index::~Index () -{ -} - - -void Index::train(idx_t /*n*/, const float* /*x*/) { - // does nothing by default -} - - -void Index::range_search (idx_t , const float *, float, - RangeSearchResult *, - ConcurrentBitsetPtr) const -{ - FAISS_THROW_MSG ("range search not implemented"); -} - -void Index::assign (idx_t n, const float* x, idx_t* labels, float* distance) -{ - float *dis_inner = (distance == nullptr) ? new float[n] : distance; - search (n, x, 1, dis_inner, labels); - if (distance == nullptr) { - delete[] dis_inner; - } -} - -void Index::add_with_ids( - idx_t /*n*/, - const float* /*x*/, - const idx_t* /*xids*/) { - FAISS_THROW_MSG ("add_with_ids not implemented for this type of index"); -} - -#if 0 -void Index::get_vector_by_id (idx_t n, const idx_t *xid, float *x, ConcurrentBitsetPtr bitset) { - FAISS_THROW_MSG ("get_vector_by_id not implemented for this type of index"); -} - -void Index::search_by_id (idx_t n, const idx_t *xid, idx_t k, float *distances, idx_t *labels, - ConcurrentBitsetPtr bitset) { - FAISS_THROW_MSG ("search_by_id not implemented for this type of index"); -} -#endif - -size_t Index::remove_ids(const IDSelector& /*sel*/) { - FAISS_THROW_MSG ("remove_ids not implemented for this type of index"); - return -1; -} - - -void Index::reconstruct (idx_t, float * ) const { - FAISS_THROW_MSG ("reconstruct not implemented for this type of index"); -} - - -void Index::reconstruct_n (idx_t i0, idx_t ni, float *recons) const { - for (idx_t i = 0; i < ni; i++) { - reconstruct (i0 + i, recons + i * d); - } -} - - -void Index::search_and_reconstruct (idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, - float *recons) const { - search (n, x, k, distances, labels); - for (idx_t i = 0; i < n; ++i) { - for (idx_t j = 0; j < k; ++j) { - idx_t ij = i * k + j; - idx_t key = labels[ij]; - float* reconstructed = recons + ij * d; - if (key < 0) { - // Fill with NaNs - memset(reconstructed, -1, sizeof(*reconstructed) * d); - } else { - reconstruct (key, reconstructed); - } - } - } -} - -void Index::compute_residual (const float * x, - float * residual, idx_t key) const { - reconstruct (key, residual); - for (size_t i = 0; i < d; i++) { - residual[i] = x[i] - residual[i]; - } -} - -void Index::compute_residual_n (idx_t n, const float* xs, - float* residuals, - const idx_t* keys) const { -#pragma omp parallel for - for (idx_t i = 0; i < n; ++i) { - compute_residual(&xs[i * d], &residuals[i * d], keys[i]); - } -} - - - -size_t Index::sa_code_size () const -{ - FAISS_THROW_MSG ("standalone codec not implemented for this type of index"); -} - -void Index::sa_encode (idx_t, const float *, - uint8_t *) const -{ - FAISS_THROW_MSG ("standalone codec not implemented for this type of index"); -} - -void Index::sa_decode (idx_t, const uint8_t *, - float *) const -{ - FAISS_THROW_MSG ("standalone codec not implemented for this type of index"); -} - - -namespace { - - -// storage that explicitly reconstructs vectors before computing distances -struct GenericDistanceComputer : DistanceComputer { - size_t d; - const Index& storage; - std::vector buf; - const float *q; - - explicit GenericDistanceComputer(const Index& storage) - : storage(storage) { - d = storage.d; - buf.resize(d * 2); - } - - float operator () (idx_t i) override { - storage.reconstruct(i, buf.data()); - return fvec_L2sqr(q, buf.data(), d); - } - - float symmetric_dis(idx_t i, idx_t j) override { - storage.reconstruct(i, buf.data()); - storage.reconstruct(j, buf.data() + d); - return fvec_L2sqr(buf.data() + d, buf.data(), d); - } - - void set_query(const float *x) override { - q = x; - } - -}; - - -} // namespace - - -DistanceComputer * Index::get_distance_computer() const { - if (metric_type == METRIC_L2) { - return new GenericDistanceComputer(*this); - } else { - FAISS_THROW_MSG ("get_distance_computer() not implemented"); - } -} - - -} diff --git a/core/src/index/thirdparty/faiss/Index.h b/core/src/index/thirdparty/faiss/Index.h deleted file mode 100644 index 9a0967962e..0000000000 --- a/core/src/index/thirdparty/faiss/Index.h +++ /dev/null @@ -1,272 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_INDEX_H -#define FAISS_INDEX_H - -#include -#include -#include -#include -#include -#include - -#define FAISS_VERSION_MAJOR 1 -#define FAISS_VERSION_MINOR 6 -#define FAISS_VERSION_PATCH 3 - -/** - * @namespace faiss - * - * Throughout the library, vectors are provided as float * pointers. - * Most algorithms can be optimized when several vectors are processed - * (added/searched) together in a batch. In this case, they are passed - * in as a matrix. When n vectors of size d are provided as float * x, - * component j of vector i is - * - * x[ i * d + j ] - * - * where 0 <= i < n and 0 <= j < d. In other words, matrices are - * always compact. When specifying the size of the matrix, we call it - * an n*d matrix, which implies a row-major storage. - */ - - -namespace faiss { - -/// Forward declarations see AuxIndexStructures.h -struct IDSelector; -struct RangeSearchResult; -struct DistanceComputer; - -/** Abstract structure for an index, supports adding vectors and searching them. - * - * All vectors provided at add or search time are 32-bit float arrays, - * although the internal representation may vary. - */ -struct Index { - using idx_t = int64_t; ///< all indices are this type - using component_t = float; - using distance_t = float; - - int d; ///< vector dimension - idx_t ntotal; ///< total nb of indexed vectors - bool verbose; ///< verbosity level - - /// set if the Index does not require training, or if training is - /// done already - bool is_trained; - - /// type of metric this index uses for search - MetricType metric_type; - float metric_arg; ///< argument of the metric type - - explicit Index (idx_t d = 0, MetricType metric = METRIC_L2): - d(d), - ntotal(0), - verbose(false), - is_trained(true), - metric_type (metric), - metric_arg(0) {} - - virtual ~Index (); - - - /** Perform training on a representative set of vectors - * - * @param n nb of training vectors - * @param x training vecors, size n * d - */ - virtual void train(idx_t n, const float* x); - - /** Add n vectors of dimension d to the index. - * - * Vectors are implicitly assigned labels ntotal .. ntotal + n - 1 - * This function slices the input vectors in chuncks smaller than - * blocksize_add and calls add_core. - * @param x input matrix, size n * d - */ - virtual void add (idx_t n, const float *x) = 0; - - /** Same as add, but stores xids instead of sequential ids. - * - * The default implementation fails with an assertion, as it is - * not supported by all indexes. - * - * @param xids if non-null, ids to store for the vectors (size n) - */ - virtual void add_with_ids (idx_t n, const float * x, const idx_t *xids); - - /** query n vectors of dimension d to the index. - * - * return at most k vectors. If there are not enough results for a - * query, the result array is padded with -1s. - * - * @param x input vectors to search, size n * d - * @param labels output labels of the NNs, size n*k - * @param distances output pairwise distances, size n*k - * @param bitset flags to check the validity of vectors - */ - virtual void search (idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, - ConcurrentBitsetPtr bitset = nullptr) const = 0; - -#if 0 - /** query n raw vectors from the index by ids. - * - * return n raw vectors. - * - * @param n input num of xid - * @param xid input labels of the NNs, size n - * @param x output raw vectors, size n * d - * @param bitset flags to check the validity of vectors - */ - virtual void get_vector_by_id (idx_t n, const idx_t *xid, float *x, ConcurrentBitsetPtr bitset = nullptr); - - /** query n vectors of dimension d to the index by ids. - * - * return at most k vectors. If there are not enough results for a - * query, the result array is padded with -1s. - * - * @param xid input ids to search, size n - * @param labels output labels of the NNs, size n*k - * @param distances output pairwise distances, size n*k - * @param bitset flags to check the validity of vectors - */ - virtual void search_by_id (idx_t n, const idx_t *xid, idx_t k, float *distances, idx_t *labels, - ConcurrentBitsetPtr bitset = nullptr); -#endif - - /** query n vectors of dimension d to the index. - * - * return all vectors with distance < radius. Note that many - * indexes do not implement the range_search (only the k-NN search - * is mandatory). - * - * @param x input vectors to search, size n * d - * @param radius search radius - * @param result result table - */ - virtual void range_search (idx_t n, const float *x, float radius, - RangeSearchResult *result, - ConcurrentBitsetPtr bitset = nullptr) const; - - /** return the indexes of the k vectors closest to the query x. - * - * This function is identical as search but only return labels of neighbors. - * @param x input vectors to search, size n * d - * @param labels output labels of the NNs, size n - */ - virtual void assign (idx_t n, const float* x, idx_t* labels, float* distance = nullptr); - - /// removes all elements from the database. - virtual void reset() = 0; - - /** removes IDs from the index. Not supported by all - * indexes. Returns the number of elements removed. - */ - virtual size_t remove_ids (const IDSelector & sel); - - /** Reconstruct a stored vector (or an approximation if lossy coding) - * - * this function may not be defined for some indexes - * @param key id of the vector to reconstruct - * @param recons reconstucted vector (size d) - */ - virtual void reconstruct (idx_t key, float * recons) const; - - /** Reconstruct vectors i0 to i0 + ni - 1 - * - * this function may not be defined for some indexes - * @param recons reconstucted vector (size ni * d) - */ - virtual void reconstruct_n (idx_t i0, idx_t ni, float *recons) const; - - /** Similar to search, but also reconstructs the stored vectors (or an - * approximation in the case of lossy coding) for the search results. - * - * If there are not enough results for a query, the resulting arrays - * is padded with -1s. - * - * @param recons reconstructed vectors size (n, k, d) - **/ - virtual void search_and_reconstruct (idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, - float *recons) const; - - /** Computes a residual vector after indexing encoding. - * - * The residual vector is the difference between a vector and the - * reconstruction that can be decoded from its representation in - * the index. The residual can be used for multiple-stage indexing - * methods, like IndexIVF's methods. - * - * @param x input vector, size d - * @param residual output residual vector, size d - * @param key encoded index, as returned by search and assign - */ - virtual void compute_residual (const float * x, - float * residual, idx_t key) const; - - /** Computes a residual vector after indexing encoding (batch form). - * Equivalent to calling compute_residual for each vector. - * - * The residual vector is the difference between a vector and the - * reconstruction that can be decoded from its representation in - * the index. The residual can be used for multiple-stage indexing - * methods, like IndexIVF's methods. - * - * @param n number of vectors - * @param xs input vectors, size (n x d) - * @param residuals output residual vectors, size (n x d) - * @param keys encoded index, as returned by search and assign - */ - virtual void compute_residual_n (idx_t n, const float* xs, - float* residuals, - const idx_t* keys) const; - - /** Get a DistanceComputer (defined in AuxIndexStructures) object - * for this kind of index. - * - * DistanceComputer is implemented for indexes that support random - * access of their vectors. - */ - virtual DistanceComputer * get_distance_computer() const; - - - /* The standalone codec interface */ - - /** size of the produced codes in bytes */ - virtual size_t sa_code_size () const; - - /** encode a set of vectors - * - * @param n number of vectors - * @param x input vectors, size n * d - * @param bytes output encoded vectors, size n * sa_code_size() - */ - virtual void sa_encode (idx_t n, const float *x, - uint8_t *bytes) const; - - /** encode a set of vectors - * - * @param n number of vectors - * @param bytes input encoded vectors, size n * sa_code_size() - * @param x output vectors, size n * d - */ - virtual void sa_decode (idx_t n, const uint8_t *bytes, - float *x) const; - - -}; - -} - - -#endif diff --git a/core/src/index/thirdparty/faiss/Index2Layer.cpp b/core/src/index/thirdparty/faiss/Index2Layer.cpp deleted file mode 100644 index cbdfd75426..0000000000 --- a/core/src/index/thirdparty/faiss/Index2Layer.cpp +++ /dev/null @@ -1,437 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include -#include -#include - -#ifdef __SSE__ -#include -#endif - -#include - -#include - -#include -#include -#include -#include -#include -#include - -/* -#include - -#include - -#include - - -*/ - - -namespace faiss { - - -/************************************* - * Index2Layer implementation - *************************************/ - - -Index2Layer::Index2Layer (Index * quantizer, size_t nlist, - int M, int nbit, - MetricType metric): - Index (quantizer->d, metric), - q1 (quantizer, nlist), - pq (quantizer->d, M, nbit) -{ - is_trained = false; - for (int nbyte = 0; nbyte < 7; nbyte++) { - if ((1L << (8 * nbyte)) >= nlist) { - code_size_1 = nbyte; - break; - } - } - code_size_2 = pq.code_size; - code_size = code_size_1 + code_size_2; -} - -Index2Layer::Index2Layer () -{ - code_size = code_size_1 = code_size_2 = 0; -} - -Index2Layer::~Index2Layer () -{} - -void Index2Layer::train(idx_t n, const float* x) -{ - if (verbose) { - printf ("training level-1 quantizer %ld vectors in %dD\n", - n, d); - } - - q1.train_q1 (n, x, verbose, metric_type); - - if (verbose) { - printf("computing residuals\n"); - } - - const float * x_in = x; - - x = fvecs_maybe_subsample ( - d, (size_t*)&n, pq.cp.max_points_per_centroid * pq.ksub, - x, verbose, pq.cp.seed); - - ScopeDeleter del_x (x_in == x ? nullptr : x); - - std::vector assign(n); // assignement to coarse centroids - q1.quantizer->assign (n, x, assign.data()); - std::vector residuals(n * d); - for (idx_t i = 0; i < n; i++) { - q1.quantizer->compute_residual ( - x + i * d, residuals.data() + i * d, assign[i]); - } - - if (verbose) - printf ("training %zdx%zd product quantizer on %ld vectors in %dD\n", - pq.M, pq.ksub, n, d); - pq.verbose = verbose; - pq.train (n, residuals.data()); - - is_trained = true; -} - -void Index2Layer::add(idx_t n, const float* x) -{ - idx_t bs = 32768; - if (n > bs) { - for (idx_t i0 = 0; i0 < n; i0 += bs) { - idx_t i1 = std::min(i0 + bs, n); - if (verbose) { - printf("Index2Layer::add: adding %ld:%ld / %ld\n", - i0, i1, n); - } - add (i1 - i0, x + i0 * d); - } - return; - } - - std::vector codes1 (n); - q1.quantizer->assign (n, x, codes1.data()); - std::vector residuals(n * d); - for (idx_t i = 0; i < n; i++) { - q1.quantizer->compute_residual ( - x + i * d, residuals.data() + i * d, codes1[i]); - } - std::vector codes2 (n * code_size_2); - - pq.compute_codes (residuals.data(), codes2.data(), n); - - codes.resize ((ntotal + n) * code_size); - uint8_t *wp = &codes[ntotal * code_size]; - - { - int i = 0x11223344; - const char *ip = (char*)&i; - FAISS_THROW_IF_NOT_MSG (ip[0] == 0x44, - "works only on a little-endian CPU"); - } - - // copy to output table - for (idx_t i = 0; i < n; i++) { - memcpy (wp, &codes1[i], code_size_1); - wp += code_size_1; - memcpy (wp, &codes2[i * code_size_2], code_size_2); - wp += code_size_2; - } - - ntotal += n; - -} - -void Index2Layer::search( - idx_t /*n*/, - const float* /*x*/, - idx_t /*k*/, - float* /*distances*/, - idx_t* /*labels*/, - ConcurrentBitsetPtr) const { - FAISS_THROW_MSG("not implemented"); -} - - -void Index2Layer::reconstruct_n(idx_t i0, idx_t ni, float* recons) const -{ - float recons1[d]; - FAISS_THROW_IF_NOT (i0 >= 0 && i0 + ni <= ntotal); - const uint8_t *rp = &codes[i0 * code_size]; - - for (idx_t i = 0; i < ni; i++) { - idx_t key = 0; - memcpy (&key, rp, code_size_1); - q1.quantizer->reconstruct (key, recons1); - rp += code_size_1; - pq.decode (rp, recons); - for (idx_t j = 0; j < d; j++) { - recons[j] += recons1[j]; - } - rp += code_size_2; - recons += d; - } -} - -void Index2Layer::transfer_to_IVFPQ (IndexIVFPQ & other) const -{ - FAISS_THROW_IF_NOT (other.nlist == q1.nlist); - FAISS_THROW_IF_NOT (other.code_size == code_size_2); - FAISS_THROW_IF_NOT (other.ntotal == 0); - - const uint8_t *rp = codes.data(); - - for (idx_t i = 0; i < ntotal; i++) { - idx_t key = 0; - memcpy (&key, rp, code_size_1); - rp += code_size_1; - other.invlists->add_entry (key, i, rp); - rp += code_size_2; - } - - other.ntotal = ntotal; - -} - - - -void Index2Layer::reconstruct(idx_t key, float* recons) const -{ - reconstruct_n (key, 1, recons); -} - -void Index2Layer::reset() -{ - ntotal = 0; - codes.clear (); -} - - -namespace { - - -struct Distance2Level : DistanceComputer { - size_t d; - const Index2Layer& storage; - std::vector buf; - const float *q; - - const float *pq_l1_tab, *pq_l2_tab; - - explicit Distance2Level(const Index2Layer& storage) - : storage(storage) { - d = storage.d; - FAISS_ASSERT(storage.pq.dsub == 4); - pq_l2_tab = storage.pq.centroids.data(); - buf.resize(2 * d); - } - - float symmetric_dis(idx_t i, idx_t j) override { - storage.reconstruct(i, buf.data()); - storage.reconstruct(j, buf.data() + d); - return fvec_L2sqr(buf.data() + d, buf.data(), d); - } - - void set_query(const float *x) override { - q = x; - } -}; - -// well optimized for xNN+PQNN -struct DistanceXPQ4 : Distance2Level { - - int M, k; - - explicit DistanceXPQ4(const Index2Layer& storage) - : Distance2Level (storage) { - const IndexFlat *quantizer = - dynamic_cast (storage.q1.quantizer); - - FAISS_ASSERT(quantizer); - M = storage.pq.M; - pq_l1_tab = quantizer->xb.data(); - } - - float operator () (idx_t i) override { -#ifdef __SSE__ - const uint8_t *code = storage.codes.data() + i * storage.code_size; - long key = 0; - memcpy (&key, code, storage.code_size_1); - code += storage.code_size_1; - - // walking pointers - const float *qa = q; - const __m128 *l1_t = (const __m128 *)(pq_l1_tab + d * key); - const __m128 *pq_l2_t = (const __m128 *)pq_l2_tab; - __m128 accu = _mm_setzero_ps(); - - for (int m = 0; m < M; m++) { - __m128 qi = _mm_loadu_ps(qa); - __m128 recons = l1_t[m] + pq_l2_t[*code++]; - __m128 diff = qi - recons; - accu += diff * diff; - pq_l2_t += 256; - qa += 4; - } - - accu = _mm_hadd_ps (accu, accu); - accu = _mm_hadd_ps (accu, accu); - return _mm_cvtss_f32 (accu); -#else - FAISS_THROW_MSG("not implemented for non-x64 platforms"); -#endif - } - -}; - -// well optimized for 2xNN+PQNN -struct Distance2xXPQ4 : Distance2Level { - - int M_2, mi_nbits; - - explicit Distance2xXPQ4(const Index2Layer& storage) - : Distance2Level(storage) { - const MultiIndexQuantizer *mi = - dynamic_cast (storage.q1.quantizer); - - FAISS_ASSERT(mi); - FAISS_ASSERT(storage.pq.M % 2 == 0); - M_2 = storage.pq.M / 2; - mi_nbits = mi->pq.nbits; - pq_l1_tab = mi->pq.centroids.data(); - } - - float operator () (idx_t i) override { - const uint8_t *code = storage.codes.data() + i * storage.code_size; - long key01 = 0; - memcpy (&key01, code, storage.code_size_1); - code += storage.code_size_1; -#ifdef __SSE__ - - // walking pointers - const float *qa = q; - const __m128 *pq_l1_t = (const __m128 *)pq_l1_tab; - const __m128 *pq_l2_t = (const __m128 *)pq_l2_tab; - __m128 accu = _mm_setzero_ps(); - - for (int mi_m = 0; mi_m < 2; mi_m++) { - long l1_idx = key01 & ((1L << mi_nbits) - 1); - const __m128 * pq_l1 = pq_l1_t + M_2 * l1_idx; - - for (int m = 0; m < M_2; m++) { - __m128 qi = _mm_loadu_ps(qa); - __m128 recons = pq_l1[m] + pq_l2_t[*code++]; - __m128 diff = qi - recons; - accu += diff * diff; - pq_l2_t += 256; - qa += 4; - } - pq_l1_t += M_2 << mi_nbits; - key01 >>= mi_nbits; - } - accu = _mm_hadd_ps (accu, accu); - accu = _mm_hadd_ps (accu, accu); - return _mm_cvtss_f32 (accu); -#else - FAISS_THROW_MSG("not implemented for non-x64 platforms"); -#endif - } - -}; - - -} // namespace - - -DistanceComputer * Index2Layer::get_distance_computer() const { -#ifdef __SSE__ - const MultiIndexQuantizer *mi = - dynamic_cast (q1.quantizer); - - if (mi && pq.M % 2 == 0 && pq.dsub == 4) { - return new Distance2xXPQ4(*this); - } - - const IndexFlat *fl = - dynamic_cast (q1.quantizer); - - if (fl && pq.dsub == 4) { - return new DistanceXPQ4(*this); - } -#endif - - return Index::get_distance_computer(); -} - - -/* The standalone codec interface */ -size_t Index2Layer::sa_code_size () const -{ - return code_size; -} - -void Index2Layer::sa_encode (idx_t n, const float *x, uint8_t *bytes) const -{ - FAISS_THROW_IF_NOT (is_trained); - std::unique_ptr list_nos (new int64_t [n]); - q1.quantizer->assign (n, x, list_nos.get()); - std::vector residuals(n * d); - for (idx_t i = 0; i < n; i++) { - q1.quantizer->compute_residual ( - x + i * d, residuals.data() + i * d, list_nos[i]); - } - pq.compute_codes (residuals.data(), bytes, n); - - for (idx_t i = n - 1; i >= 0; i--) { - uint8_t * code = bytes + i * code_size; - memmove (code + code_size_1, - bytes + i * code_size_2, code_size_2); - q1.encode_listno (list_nos[i], code); - } - -} - -void Index2Layer::sa_decode (idx_t n, const uint8_t *bytes, float *x) const -{ - -#pragma omp parallel - { - std::vector residual (d); - -#pragma omp for - for (size_t i = 0; i < n; i++) { - const uint8_t *code = bytes + i * code_size; - int64_t list_no = q1.decode_listno (code); - float *xi = x + i * d; - pq.decode (code + code_size_1, xi); - q1.quantizer->reconstruct (list_no, residual.data()); - for (size_t j = 0; j < d; j++) { - xi[j] += residual[j]; - } - } - } - -} - - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/Index2Layer.h b/core/src/index/thirdparty/faiss/Index2Layer.h deleted file mode 100644 index 7062ff3690..0000000000 --- a/core/src/index/thirdparty/faiss/Index2Layer.h +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#pragma once - -#include - -#include -#include - -namespace faiss { - -struct IndexIVFPQ; - - -/** Same as an IndexIVFPQ without the inverted lists: codes are stored sequentially - * - * The class is mainly inteded to store encoded vectors that can be - * accessed randomly, the search function is not implemented. - */ -struct Index2Layer: Index { - /// first level quantizer - Level1Quantizer q1; - - /// second level quantizer is always a PQ - ProductQuantizer pq; - - /// Codes. Size ntotal * code_size. - std::vector codes; - - /// size of the code for the first level (ceil(log8(q1.nlist))) - size_t code_size_1; - - /// size of the code for the second level - size_t code_size_2; - - /// code_size_1 + code_size_2 - size_t code_size; - - Index2Layer (Index * quantizer, size_t nlist, - int M, int nbit = 8, - MetricType metric = METRIC_L2); - - Index2Layer (); - ~Index2Layer (); - - void train(idx_t n, const float* x) override; - - void add(idx_t n, const float* x) override; - - /// not implemented - void search( - idx_t n, - const float* x, - idx_t k, - float* distances, - idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - void reconstruct_n(idx_t i0, idx_t ni, float* recons) const override; - - void reconstruct(idx_t key, float* recons) const override; - - void reset() override; - - DistanceComputer * get_distance_computer() const override; - - /// transfer the flat codes to an IVFPQ index - void transfer_to_IVFPQ(IndexIVFPQ & other) const; - - - /* The standalone codec interface */ - size_t sa_code_size () const override; - void sa_encode (idx_t n, const float *x, uint8_t *bytes) const override; - void sa_decode (idx_t n, const uint8_t *bytes, float *x) const override; - -}; - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexBinary.cpp b/core/src/index/thirdparty/faiss/IndexBinary.cpp deleted file mode 100644 index fc41fe481c..0000000000 --- a/core/src/index/thirdparty/faiss/IndexBinary.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include -#include - -#include - -namespace faiss { - -IndexBinary::~IndexBinary() {} - -void IndexBinary::train(idx_t, const uint8_t *) { - // Does nothing by default. -} - -void IndexBinary::range_search(idx_t, const uint8_t *, int, - RangeSearchResult *, - ConcurrentBitsetPtr) const { - FAISS_THROW_MSG("range search not implemented"); -} - -void IndexBinary::assign(idx_t n, const uint8_t *x, idx_t *labels, idx_t k) { - int *distances = new int[n * k]; - ScopeDeleter del(distances); - search(n, x, k, distances, labels); -} - -void IndexBinary::add_with_ids(idx_t, const uint8_t *, const idx_t *) { - FAISS_THROW_MSG("add_with_ids not implemented for this type of index"); -} - -#if 0 -void IndexBinary::get_vector_by_id (idx_t n, const idx_t *xid, uint8_t *x, ConcurrentBitsetPtr bitset) { - FAISS_THROW_MSG("get_vector_by_id not implemented for this type of index"); -} - -void IndexBinary::search_by_id (idx_t n, const idx_t *xid, idx_t k, int32_t *distances, idx_t *labels, - ConcurrentBitsetPtr bitset) { - FAISS_THROW_MSG("search_by_id not implemented for this type of index"); -} -#endif - -size_t IndexBinary::remove_ids(const IDSelector&) { - FAISS_THROW_MSG("remove_ids not implemented for this type of index"); - return 0; -} - -void IndexBinary::reconstruct(idx_t, uint8_t *) const { - FAISS_THROW_MSG("reconstruct not implemented for this type of index"); -} - -void IndexBinary::reconstruct_n(idx_t i0, idx_t ni, uint8_t *recons) const { - for (idx_t i = 0; i < ni; i++) { - reconstruct(i0 + i, recons + i * d); - } -} - -void IndexBinary::search_and_reconstruct(idx_t n, const uint8_t *x, idx_t k, - int32_t *distances, idx_t *labels, - uint8_t *recons) const { - search(n, x, k, distances, labels); - for (idx_t i = 0; i < n; ++i) { - for (idx_t j = 0; j < k; ++j) { - idx_t ij = i * k + j; - idx_t key = labels[ij]; - uint8_t *reconstructed = recons + ij * d; - if (key < 0) { - // Fill with NaNs - memset(reconstructed, -1, sizeof(*reconstructed) * d); - } else { - reconstruct(key, reconstructed); - } - } - } -} - -void IndexBinary::display() const { - printf("Index: %s -> %ld elements\n", typeid (*this).name(), ntotal); -} - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexBinary.h b/core/src/index/thirdparty/faiss/IndexBinary.h deleted file mode 100644 index 4141a7d63c..0000000000 --- a/core/src/index/thirdparty/faiss/IndexBinary.h +++ /dev/null @@ -1,196 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_INDEX_BINARY_H -#define FAISS_INDEX_BINARY_H - -#include -#include -#include -#include - -#include -#include - - -namespace faiss { - - -/// Forward declarations see AuxIndexStructures.h -struct IDSelector; -struct RangeSearchResult; - -/** Abstract structure for a binary index. - * - * Supports adding vertices and searching them. - * - * All queries are symmetric because there is no distinction between codes and - * vectors. - */ -struct IndexBinary { - using idx_t = Index::idx_t; ///< all indices are this type - using component_t = uint8_t; - using distance_t = int32_t; - - int d; ///< vector dimension - int code_size; ///< number of bytes per vector ( = d / 8 ) - idx_t ntotal; ///< total nb of indexed vectors - bool verbose; ///< verbosity level - - /// set if the Index does not require training, or if training is done already - bool is_trained; - - /// type of metric this index uses for search - MetricType metric_type; - - explicit IndexBinary(idx_t d = 0, MetricType metric = METRIC_L2) - : d(d), - code_size(d / 8), - ntotal(0), - verbose(false), - is_trained(true), - metric_type(metric) { - FAISS_THROW_IF_NOT(d % 8 == 0); - } - - virtual ~IndexBinary(); - - - /** Perform training on a representative set of vectors. - * - * @param n nb of training vectors - * @param x training vecors, size n * d / 8 - */ - virtual void train(idx_t n, const uint8_t *x); - - /** Add n vectors of dimension d to the index. - * - * Vectors are implicitly assigned labels ntotal .. ntotal + n - 1 - * @param x input matrix, size n * d / 8 - */ - virtual void add(idx_t n, const uint8_t *x) = 0; - - /** Same as add, but stores xids instead of sequential ids. - * - * The default implementation fails with an assertion, as it is - * not supported by all indexes. - * - * @param xids if non-null, ids to store for the vectors (size n) - */ - virtual void add_with_ids(idx_t n, const uint8_t *x, const idx_t *xids); - - /** Query n vectors of dimension d to the index. - * - * return at most k vectors. If there are not enough results for a - * query, the result array is padded with -1s. - * - * @param x input vectors to search, size n * d / 8 - * @param labels output labels of the NNs, size n*k - * @param distances output pairwise distances, size n*k - * @param bitset flags to check the validity of vectors - */ - virtual void search(idx_t n, const uint8_t *x, idx_t k, - int32_t *distances, idx_t *labels, - ConcurrentBitsetPtr bitset = nullptr) const = 0; - -#if 0 - /** Query n raw vectors from the index by ids. - * - * return n raw vectors. - * - * @param n input num of xid - * @param xid input labels of the NNs, size n - * @param x output raw vectors, size n * d - * @param bitset flags to check the validity of vectors - */ - virtual void get_vector_by_id (idx_t n, const idx_t *xid, uint8_t *x, ConcurrentBitsetPtr bitset = nullptr); - - /** query n vectors of dimension d to the index by ids. - * - * return at most k vectors. If there are not enough results for a - * query, the result array is padded with -1s. - * - * @param xid input ids to search, size n - * @param labels output labels of the NNs, size n*k - * @param distances output pairwise distances, size n*k - * @param bitset flags to check the validity of vectors - */ - virtual void search_by_id (idx_t n, const idx_t *xid, idx_t k, int32_t *distances, idx_t *labels, - ConcurrentBitsetPtr bitset = nullptr); -#endif - - /** Query n vectors of dimension d to the index. - * - * return all vectors with distance < radius. Note that many indexes - * do not implement the range_search (only the k-NN search is - * mandatory). The distances are converted to float to reuse the - * RangeSearchResult structure, but they are integer. By convention, - * only distances < radius (strict comparison) are returned, - * ie. radius = 0 does not return any result and 1 returns only - * exact same vectors. - * - * @param x input vectors to search, size n * d / 8 - * @param radius search radius - * @param result result table - */ - virtual void range_search(idx_t n, const uint8_t *x, int radius, - RangeSearchResult *result, - ConcurrentBitsetPtr bitset = nullptr) const; - - /** Return the indexes of the k vectors closest to the query x. - * - * This function is identical to search but only returns labels of neighbors. - * @param x input vectors to search, size n * d / 8 - * @param labels output labels of the NNs, size n*k - */ - void assign(idx_t n, const uint8_t *x, idx_t *labels, idx_t k = 1); - - /// Removes all elements from the database. - virtual void reset() = 0; - - /** Removes IDs from the index. Not supported by all indexes. - */ - virtual size_t remove_ids(const IDSelector& sel); - - /** Reconstruct a stored vector. - * - * This function may not be defined for some indexes. - * @param key id of the vector to reconstruct - * @param recons reconstucted vector (size d / 8) - */ - virtual void reconstruct(idx_t key, uint8_t *recons) const; - - - /** Reconstruct vectors i0 to i0 + ni - 1. - * - * This function may not be defined for some indexes. - * @param recons reconstucted vectors (size ni * d / 8) - */ - virtual void reconstruct_n(idx_t i0, idx_t ni, uint8_t *recons) const; - - /** Similar to search, but also reconstructs the stored vectors (or an - * approximation in the case of lossy coding) for the search results. - * - * If there are not enough results for a query, the resulting array - * is padded with -1s. - * - * @param recons reconstructed vectors size (n, k, d) - **/ - virtual void search_and_reconstruct(idx_t n, const uint8_t *x, idx_t k, - int32_t *distances, idx_t *labels, - uint8_t *recons) const; - - /** Display the actual class name and some more info. */ - void display() const; -}; - - -} // namespace faiss - -#endif // FAISS_INDEX_BINARY_H diff --git a/core/src/index/thirdparty/faiss/IndexBinaryFlat.cpp b/core/src/index/thirdparty/faiss/IndexBinaryFlat.cpp deleted file mode 100644 index f301376cb7..0000000000 --- a/core/src/index/thirdparty/faiss/IndexBinaryFlat.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { - -IndexBinaryFlat::IndexBinaryFlat(idx_t d) - : IndexBinary(d) {} - -IndexBinaryFlat::IndexBinaryFlat(idx_t d, MetricType metric) - : IndexBinary(d, metric) {} - -void IndexBinaryFlat::add(idx_t n, const uint8_t *x) { - xb.insert(xb.end(), x, x + n * code_size); - ntotal += n; -} - -void IndexBinaryFlat::reset() { - xb.clear(); - ntotal = 0; -} - -void IndexBinaryFlat::search(idx_t n, const uint8_t *x, idx_t k, - int32_t *distances, idx_t *labels, - ConcurrentBitsetPtr bitset) const { - const idx_t block_size = query_batch_size; - if (metric_type == METRIC_Jaccard || metric_type == METRIC_Tanimoto) { - float *D = reinterpret_cast(distances); - for (idx_t s = 0; s < n; s += block_size) { - idx_t nn = block_size; - if (s + block_size > n) { - nn = n - s; - } - - // We see the distances and labels as heaps. - float_maxheap_array_t res = { - size_t(nn), size_t(k), labels + s * k, D + s * k - }; - - binary_distence_knn_hc(metric_type, &res, x + s * code_size, xb.data(), ntotal, code_size, - /* ordered = */ true, bitset); - - } - if (metric_type == METRIC_Tanimoto) { - for (int i = 0; i < k * n; i++) { - D[i] = -log2(1-D[i]); - } - } - } else if (metric_type == METRIC_Substructure || metric_type == METRIC_Superstructure) { - float *D = reinterpret_cast(distances); - for (idx_t s = 0; s < n; s += block_size) { - idx_t nn = block_size; - if (s + block_size > n) { - nn = n - s; - } - - // only match ids will be chosed, not to use heap - binary_distence_knn_mc(metric_type, x + s * code_size, xb.data(), nn, ntotal, k, code_size, - D + s * k, labels + s * k, bitset); - } - } else { - for (idx_t s = 0; s < n; s += block_size) { - idx_t nn = block_size; - if (s + block_size > n) { - nn = n - s; - } - if (use_heap) { - // We see the distances and labels as heaps. - int_maxheap_array_t res = { - size_t(nn), size_t(k), labels + s * k, distances + s * k - }; - - hammings_knn_hc(&res, x + s * code_size, xb.data(), ntotal, code_size, - /* ordered = */ true, bitset); - } else { - hammings_knn_mc(x + s * code_size, xb.data(), nn, ntotal, k, code_size, - distances + s * k, labels + s * k, bitset); - } - } - } -} - -size_t IndexBinaryFlat::remove_ids(const IDSelector& sel) { - idx_t j = 0; - for (idx_t i = 0; i < ntotal; i++) { - if (sel.is_member(i)) { - // should be removed - } else { - if (i > j) { - memmove(&xb[code_size * j], &xb[code_size * i], sizeof(xb[0]) * code_size); - } - j++; - } - } - long nremove = ntotal - j; - if (nremove > 0) { - ntotal = j; - xb.resize(ntotal * code_size); - } - return nremove; -} - -void IndexBinaryFlat::reconstruct(idx_t key, uint8_t *recons) const { - memcpy(recons, &(xb[code_size * key]), sizeof(*recons) * code_size); -} - -void IndexBinaryFlat::range_search(idx_t n, const uint8_t *x, int radius, - RangeSearchResult *result, - ConcurrentBitsetPtr bitset) const -{ - hamming_range_search (x, xb.data(), n, ntotal, radius, code_size, result); -} - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexBinaryFlat.h b/core/src/index/thirdparty/faiss/IndexBinaryFlat.h deleted file mode 100644 index 012b9b43f4..0000000000 --- a/core/src/index/thirdparty/faiss/IndexBinaryFlat.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef INDEX_BINARY_FLAT_H -#define INDEX_BINARY_FLAT_H - -#include - -#include - -namespace faiss { - - -/** Index that stores the full vectors and performs exhaustive search. */ -struct IndexBinaryFlat : IndexBinary { - /// database vectors, size ntotal * d / 8 - std::vector xb; - - /** Select between using a heap or counting to select the k smallest values - * when scanning inverted lists. - */ - bool use_heap = true; - - size_t query_batch_size = 32; - - explicit IndexBinaryFlat(idx_t d); - - IndexBinaryFlat(idx_t d, MetricType metric); - - void add(idx_t n, const uint8_t *x) override; - - void reset() override; - - void search(idx_t n, const uint8_t *x, idx_t k, - int32_t *distances, idx_t *labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - void range_search(idx_t n, const uint8_t *x, int radius, - RangeSearchResult *result, - ConcurrentBitsetPtr bitset = nullptr) const override; - - void reconstruct(idx_t key, uint8_t *recons) const override; - - /** Remove some ids. Note that because of the indexing structure, - * the semantics of this operation are different from the usual ones: - * the new ids are shifted. */ - size_t remove_ids(const IDSelector& sel) override; - - IndexBinaryFlat() {} -}; - - -} // namespace faiss - -#endif // INDEX_BINARY_FLAT_H diff --git a/core/src/index/thirdparty/faiss/IndexBinaryFromFloat.cpp b/core/src/index/thirdparty/faiss/IndexBinaryFromFloat.cpp deleted file mode 100644 index 67bd9a28dc..0000000000 --- a/core/src/index/thirdparty/faiss/IndexBinaryFromFloat.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include - -namespace faiss { - - -IndexBinaryFromFloat::IndexBinaryFromFloat() {} - -IndexBinaryFromFloat::IndexBinaryFromFloat(Index *index) - : IndexBinary(index->d), - index(index), - own_fields(false) { - is_trained = index->is_trained; - ntotal = index->ntotal; -} - -IndexBinaryFromFloat::~IndexBinaryFromFloat() { - if (own_fields) { - delete index; - } -} - -void IndexBinaryFromFloat::add(idx_t n, const uint8_t *x) { - constexpr idx_t bs = 32768; - std::unique_ptr xf(new float[bs * d]); - - for (idx_t b = 0; b < n; b += bs) { - idx_t bn = std::min(bs, n - b); - binary_to_real(bn * d, x + b * code_size, xf.get()); - - index->add(bn, xf.get()); - } - ntotal = index->ntotal; -} - -void IndexBinaryFromFloat::reset() { - index->reset(); - ntotal = index->ntotal; -} - -void IndexBinaryFromFloat::search(idx_t n, const uint8_t *x, idx_t k, - int32_t *distances, idx_t *labels, - ConcurrentBitsetPtr bitset) const { - constexpr idx_t bs = 32768; - std::unique_ptr xf(new float[bs * d]); - std::unique_ptr df(new float[bs * k]); - - for (idx_t b = 0; b < n; b += bs) { - idx_t bn = std::min(bs, n - b); - binary_to_real(bn * d, x + b * code_size, xf.get()); - - index->search(bn, xf.get(), k, df.get(), labels + b * k); - for (int i = 0; i < bn * k; ++i) { - distances[b * k + i] = int32_t(std::round(df[i] / 4.0)); - } - } -} - -void IndexBinaryFromFloat::train(idx_t n, const uint8_t *x) { - std::unique_ptr xf(new float[n * d]); - binary_to_real(n * d, x, xf.get()); - - index->train(n, xf.get()); - is_trained = true; - ntotal = index->ntotal; -} - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexBinaryFromFloat.h b/core/src/index/thirdparty/faiss/IndexBinaryFromFloat.h deleted file mode 100644 index b630c832e4..0000000000 --- a/core/src/index/thirdparty/faiss/IndexBinaryFromFloat.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_INDEX_BINARY_FROM_FLOAT_H -#define FAISS_INDEX_BINARY_FROM_FLOAT_H - -#include - - -namespace faiss { - - -struct Index; - -/** IndexBinary backed by a float Index. - * - * Supports adding vertices and searching them. - * - * All queries are symmetric because there is no distinction between codes and - * vectors. - */ -struct IndexBinaryFromFloat : IndexBinary { - Index *index = nullptr; - - bool own_fields = false; ///< Whether object owns the index pointer. - - IndexBinaryFromFloat(); - - explicit IndexBinaryFromFloat(Index *index); - - ~IndexBinaryFromFloat(); - - void add(idx_t n, const uint8_t *x) override; - - void reset() override; - - void search(idx_t n, const uint8_t *x, idx_t k, - int32_t *distances, idx_t *labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - void train(idx_t n, const uint8_t *x) override; -}; - - -} // namespace faiss - -#endif // FAISS_INDEX_BINARY_FROM_FLOAT_H diff --git a/core/src/index/thirdparty/faiss/IndexBinaryHNSW.cpp b/core/src/index/thirdparty/faiss/IndexBinaryHNSW.cpp deleted file mode 100644 index 87234e4aac..0000000000 --- a/core/src/index/thirdparty/faiss/IndexBinaryHNSW.cpp +++ /dev/null @@ -1,326 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace faiss { - - -/************************************************************** - * add / search blocks of descriptors - **************************************************************/ - -namespace { - - -void hnsw_add_vertices(IndexBinaryHNSW& index_hnsw, - size_t n0, - size_t n, const uint8_t *x, - bool verbose, - bool preset_levels = false) { - HNSW& hnsw = index_hnsw.hnsw; - size_t ntotal = n0 + n; - double t0 = getmillisecs(); - if (verbose) { - printf("hnsw_add_vertices: adding %ld elements on top of %ld " - "(preset_levels=%d)\n", - n, n0, int(preset_levels)); - } - - int max_level = hnsw.prepare_level_tab(n, preset_levels); - - if (verbose) { - printf(" max_level = %d\n", max_level); - } - - std::vector locks(ntotal); - for(int i = 0; i < ntotal; i++) { - omp_init_lock(&locks[i]); - } - - // add vectors from highest to lowest level - std::vector hist; - std::vector order(n); - - { // make buckets with vectors of the same level - - // build histogram - for (int i = 0; i < n; i++) { - HNSW::storage_idx_t pt_id = i + n0; - int pt_level = hnsw.levels[pt_id] - 1; - while (pt_level >= hist.size()) { - hist.push_back(0); - } - hist[pt_level] ++; - } - - // accumulate - std::vector offsets(hist.size() + 1, 0); - for (int i = 0; i < hist.size() - 1; i++) { - offsets[i + 1] = offsets[i] + hist[i]; - } - - // bucket sort - for (int i = 0; i < n; i++) { - HNSW::storage_idx_t pt_id = i + n0; - int pt_level = hnsw.levels[pt_id] - 1; - order[offsets[pt_level]++] = pt_id; - } - } - - { // perform add - RandomGenerator rng2(789); - - int i1 = n; - - for (int pt_level = hist.size() - 1; pt_level >= 0; pt_level--) { - int i0 = i1 - hist[pt_level]; - - if (verbose) { - printf("Adding %d elements at level %d\n", - i1 - i0, pt_level); - } - - // random permutation to get rid of dataset order bias - for (int j = i0; j < i1; j++) { - std::swap(order[j], order[j + rng2.rand_int(i1 - j)]); - } - -#pragma omp parallel - { - VisitedTable vt (ntotal); - - std::unique_ptr dis( - index_hnsw.get_distance_computer() - ); - int prev_display = verbose && omp_get_thread_num() == 0 ? 0 : -1; - -#pragma omp for schedule(dynamic) - for (int i = i0; i < i1; i++) { - HNSW::storage_idx_t pt_id = order[i]; - dis->set_query((float *)(x + (pt_id - n0) * index_hnsw.code_size)); - - hnsw.add_with_locks(*dis, pt_level, pt_id, locks, vt); - - if (prev_display >= 0 && i - i0 > prev_display + 10000) { - prev_display = i - i0; - printf(" %d / %d\r", i - i0, i1 - i0); - fflush(stdout); - } - } - } - i1 = i0; - } - FAISS_ASSERT(i1 == 0); - } - if (verbose) { - printf("Done in %.3f ms\n", getmillisecs() - t0); - } - - for(int i = 0; i < ntotal; i++) - omp_destroy_lock(&locks[i]); -} - - -} // anonymous namespace - - -/************************************************************** - * IndexBinaryHNSW implementation - **************************************************************/ - -IndexBinaryHNSW::IndexBinaryHNSW() -{ - is_trained = true; -} - -IndexBinaryHNSW::IndexBinaryHNSW(int d, int M) - : IndexBinary(d), - hnsw(M), - own_fields(true), - storage(new IndexBinaryFlat(d)) -{ - is_trained = true; -} - -IndexBinaryHNSW::IndexBinaryHNSW(IndexBinary *storage, int M) - : IndexBinary(storage->d), - hnsw(M), - own_fields(false), - storage(storage) -{ - is_trained = true; -} - -IndexBinaryHNSW::~IndexBinaryHNSW() { - if (own_fields) { - delete storage; - } -} - -void IndexBinaryHNSW::train(idx_t n, const uint8_t *x) -{ - // hnsw structure does not require training - storage->train(n, x); - is_trained = true; -} - -void IndexBinaryHNSW::search(idx_t n, const uint8_t *x, idx_t k, - int32_t *distances, idx_t *labels, - ConcurrentBitsetPtr bitset) const -{ -#pragma omp parallel - { - VisitedTable vt(ntotal); - std::unique_ptr dis(get_distance_computer()); - -#pragma omp for - for(idx_t i = 0; i < n; i++) { - idx_t *idxi = labels + i * k; - float *simi = (float *)(distances + i * k); - - dis->set_query((float *)(x + i * code_size)); - - maxheap_heapify(k, simi, idxi); - hnsw.search(*dis, k, idxi, simi, vt); - maxheap_reorder(k, simi, idxi); - } - } - -#pragma omp parallel for - for (int i = 0; i < n * k; ++i) { - distances[i] = std::round(((float *)distances)[i]); - } -} - - -void IndexBinaryHNSW::add(idx_t n, const uint8_t *x) -{ - FAISS_THROW_IF_NOT(is_trained); - int n0 = ntotal; - storage->add(n, x); - ntotal = storage->ntotal; - - hnsw_add_vertices(*this, n0, n, x, verbose, - hnsw.levels.size() == ntotal); -} - -void IndexBinaryHNSW::reset() -{ - hnsw.reset(); - storage->reset(); - ntotal = 0; -} - -void IndexBinaryHNSW::reconstruct(idx_t key, uint8_t *recons) const -{ - storage->reconstruct(key, recons); -} - - -namespace { - - -template -struct FlatHammingDis : DistanceComputer { - const int code_size; - const uint8_t *b; - size_t ndis; - HammingComputer hc; - - float operator () (idx_t i) override { - ndis++; - return hc.hamming(b + i * code_size); - } - - float symmetric_dis(idx_t i, idx_t j) override { - return HammingComputerDefault(b + j * code_size, code_size) - .hamming(b + i * code_size); - } - - - explicit FlatHammingDis(const IndexBinaryFlat& storage) - : code_size(storage.code_size), - b(storage.xb.data()), - ndis(0), - hc() {} - - // NOTE: Pointers are cast from float in order to reuse the floating-point - // DistanceComputer. - void set_query(const float *x) override { - hc.set((uint8_t *)x, code_size); - } - - ~FlatHammingDis() override { -#pragma omp critical - { - hnsw_stats.ndis += ndis; - } - } -}; - - -} // namespace - - -DistanceComputer *IndexBinaryHNSW::get_distance_computer() const { - IndexBinaryFlat *flat_storage = dynamic_cast(storage); - - FAISS_ASSERT(flat_storage != nullptr); - - switch(code_size) { - case 4: - return new FlatHammingDis(*flat_storage); - case 8: - return new FlatHammingDis(*flat_storage); - case 16: - return new FlatHammingDis(*flat_storage); - case 20: - return new FlatHammingDis(*flat_storage); - case 32: - return new FlatHammingDis(*flat_storage); - case 64: - return new FlatHammingDis(*flat_storage); - default: - if (code_size % 8 == 0) { - return new FlatHammingDis(*flat_storage); - } else if (code_size % 4 == 0) { - return new FlatHammingDis(*flat_storage); - } - } - - return new FlatHammingDis(*flat_storage); -} - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexBinaryHNSW.h b/core/src/index/thirdparty/faiss/IndexBinaryHNSW.h deleted file mode 100644 index be10fee692..0000000000 --- a/core/src/index/thirdparty/faiss/IndexBinaryHNSW.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#pragma once - -#include -#include -#include - - -namespace faiss { - - -/** The HNSW index is a normal random-access index with a HNSW - * link structure built on top */ - -struct IndexBinaryHNSW : IndexBinary { - typedef HNSW::storage_idx_t storage_idx_t; - - // the link strcuture - HNSW hnsw; - - // the sequential storage - bool own_fields; - IndexBinary *storage; - - explicit IndexBinaryHNSW(); - explicit IndexBinaryHNSW(int d, int M = 32); - explicit IndexBinaryHNSW(IndexBinary *storage, int M = 32); - - ~IndexBinaryHNSW() override; - - DistanceComputer *get_distance_computer() const; - - void add(idx_t n, const uint8_t *x) override; - - /// Trains the storage if needed - void train(idx_t n, const uint8_t* x) override; - - /// entry point for search - void search(idx_t n, const uint8_t *x, idx_t k, - int32_t *distances, idx_t *labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - void reconstruct(idx_t key, uint8_t* recons) const override; - - void reset() override; -}; - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexBinaryHash.cpp b/core/src/index/thirdparty/faiss/IndexBinaryHash.cpp deleted file mode 100644 index 008da09455..0000000000 --- a/core/src/index/thirdparty/faiss/IndexBinaryHash.cpp +++ /dev/null @@ -1,496 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved -// -*- c++ -*- - -#include - -#include -#include - -#include -#include - -#include -#include - - -namespace faiss { - -void IndexBinaryHash::InvertedList::add ( - idx_t id, size_t code_size, const uint8_t *code) -{ - ids.push_back(id); - vecs.insert(vecs.end(), code, code + code_size); -} - -IndexBinaryHash::IndexBinaryHash(int d, int b): - IndexBinary(d), b(b), nflip(0) -{ - is_trained = true; -} - -IndexBinaryHash::IndexBinaryHash(): b(0), nflip(0) -{ - is_trained = true; -} - -void IndexBinaryHash::reset() -{ - invlists.clear(); - ntotal = 0; -} - - -void IndexBinaryHash::add(idx_t n, const uint8_t *x) -{ - add_with_ids(n, x, nullptr); -} - -void IndexBinaryHash::add_with_ids(idx_t n, const uint8_t *x, const idx_t *xids) -{ - uint64_t mask = ((uint64_t)1 << b) - 1; - // simplistic add function. Cannot really be parallelized. - - for (idx_t i = 0; i < n; i++) { - idx_t id = xids ? xids[i] : ntotal + i; - const uint8_t * xi = x + i * code_size; - idx_t hash = *((uint64_t*)xi) & mask; - invlists[hash].add(id, code_size, xi); - } - ntotal += n; -} - -namespace { - - -/** Enumerate all bit vectors of size nbit with up to maxflip 1s - * test in P127257851 P127258235 - */ -struct FlipEnumerator { - int nbit, nflip, maxflip; - uint64_t mask, x; - - FlipEnumerator (int nbit, int maxflip): nbit(nbit), maxflip(maxflip) { - nflip = 0; - mask = 0; - x = 0; - } - - bool next() { - if (x == mask) { - if (nflip == maxflip) { - return false; - } - // increase Hamming radius - nflip++; - mask = (((uint64_t)1 << nflip) - 1); - x = mask << (nbit - nflip); - return true; - } - - int i = __builtin_ctzll(x); - - if (i > 0) { - x ^= (uint64_t)3 << (i - 1); - } else { - // nb of LSB 1s - int n1 = __builtin_ctzll(~x); - // clear them - x &= ((uint64_t)(-1) << n1); - int n2 = __builtin_ctzll(x); - x ^= (((uint64_t)1 << (n1 + 2)) - 1) << (n2 - n1 - 1); - } - return true; - } - -}; - -using idx_t = Index::idx_t; - - -struct RangeSearchResults { - int radius; - RangeQueryResult &qres; - - inline void add (float dis, idx_t id) { - if (dis < radius) { - qres.add (dis, id); - } - } - -}; - -struct KnnSearchResults { - // heap params - idx_t k; - int32_t * heap_sim; - idx_t * heap_ids; - - using C = CMax; - - inline void add (float dis, idx_t id) { - if (dis < heap_sim[0]) { - heap_pop (k, heap_sim, heap_ids); - heap_push (k, heap_sim, heap_ids, dis, id); - } - } - -}; - -template -void -search_single_query_template(const IndexBinaryHash & index, const uint8_t *q, - SearchResults &res, - size_t &n0, size_t &nlist, size_t &ndis) -{ - size_t code_size = index.code_size; - uint64_t mask = ((uint64_t)1 << index.b) - 1; - uint64_t qhash = *((uint64_t*)q) & mask; - HammingComputer hc (q, code_size); - FlipEnumerator fe(index.b, index.nflip); - - // loop over neighbors that are at most at nflip bits - do { - uint64_t hash = qhash ^ fe.x; - auto it = index.invlists.find (hash); - - if (it == index.invlists.end()) { - continue; - } - - const IndexBinaryHash::InvertedList &il = it->second; - - size_t nv = il.ids.size(); - - if (nv == 0) { - n0++; - } else { - const uint8_t *codes = il.vecs.data(); - for (size_t i = 0; i < nv; i++) { - int dis = hc.hamming (codes); - res.add(dis, il.ids[i]); - codes += code_size; - } - ndis += nv; - nlist++; - } - } while(fe.next()); -} - -template -void -search_single_query(const IndexBinaryHash & index, const uint8_t *q, - SearchResults &res, - size_t &n0, size_t &nlist, size_t &ndis) -{ -#define HC(name) search_single_query_template(index, q, res, n0, nlist, ndis); - switch(index.code_size) { - case 4: HC(HammingComputer4); break; - case 8: HC(HammingComputer8); break; - case 16: HC(HammingComputer16); break; - case 20: HC(HammingComputer20); break; - case 32: HC(HammingComputer32); break; - default: - if (index.code_size % 8 == 0) { - HC(HammingComputerM8); - } else { - HC(HammingComputerDefault); - } - } -#undef HC -} - - -} // anonymous namespace - - - -void IndexBinaryHash::range_search(idx_t n, const uint8_t *x, int radius, - RangeSearchResult *result, - ConcurrentBitsetPtr bitset) const -{ - - size_t nlist = 0, ndis = 0, n0 = 0; - -#pragma omp parallel if(n > 100) reduction(+: ndis, n0, nlist) - { - RangeSearchPartialResult pres (result); - -#pragma omp for - for (size_t i = 0; i < n; i++) { // loop queries - RangeQueryResult & qres = pres.new_result (i); - RangeSearchResults res = {radius, qres}; - const uint8_t *q = x + i * code_size; - - search_single_query (*this, q, res, n0, nlist, ndis); - - } - pres.finalize (); - } - indexBinaryHash_stats.nq += n; - indexBinaryHash_stats.n0 += n0; - indexBinaryHash_stats.nlist += nlist; - indexBinaryHash_stats.ndis += ndis; -} - -void IndexBinaryHash::search(idx_t n, const uint8_t *x, idx_t k, - int32_t *distances, idx_t *labels, - ConcurrentBitsetPtr bitset) const -{ - - using HeapForL2 = CMax; - size_t nlist = 0, ndis = 0, n0 = 0; - -#pragma omp parallel for if(n > 100) reduction(+: nlist, ndis, n0) - for (size_t i = 0; i < n; i++) { - int32_t * simi = distances + k * i; - idx_t * idxi = labels + k * i; - - heap_heapify (k, simi, idxi); - KnnSearchResults res = {k, simi, idxi}; - const uint8_t *q = x + i * code_size; - - search_single_query (*this, q, res, n0, nlist, ndis); - - } - indexBinaryHash_stats.nq += n; - indexBinaryHash_stats.n0 += n0; - indexBinaryHash_stats.nlist += nlist; - indexBinaryHash_stats.ndis += ndis; -} - -size_t IndexBinaryHash::hashtable_size() const -{ - return invlists.size(); -} - - -void IndexBinaryHash::display() const -{ - for (auto it = invlists.begin(); it != invlists.end(); ++it) { - printf("%ld: [", it->first); - const std::vector & v = it->second.ids; - for (auto x: v) { - printf("%ld ", 0 + x); - } - printf("]\n"); - - } -} - - -void IndexBinaryHashStats::reset() -{ - memset ((void*)this, 0, sizeof (*this)); -} - -IndexBinaryHashStats indexBinaryHash_stats; - -/******************************************************* - * IndexBinaryMultiHash implementation - ******************************************************/ - - -IndexBinaryMultiHash::IndexBinaryMultiHash(int d, int nhash, int b): - IndexBinary(d), - storage(new IndexBinaryFlat(d)), own_fields(true), - maps(nhash), nhash(nhash), b(b), nflip(0) -{ - FAISS_THROW_IF_NOT(nhash * b <= d); -} - -IndexBinaryMultiHash::IndexBinaryMultiHash(): - storage(nullptr), own_fields(true), - nhash(0), b(0), nflip(0) -{} - -IndexBinaryMultiHash::~IndexBinaryMultiHash() -{ - if (own_fields) { - delete storage; - } -} - - -void IndexBinaryMultiHash::reset() -{ - storage->reset(); - ntotal = 0; - for(auto map: maps) { - map.clear(); - } -} - -void IndexBinaryMultiHash::add(idx_t n, const uint8_t *x) -{ - storage->add(n, x); - // populate maps - uint64_t mask = ((uint64_t)1 << b) - 1; - - for(idx_t i = 0; i < n; i++) { - const uint8_t *xi = x + i * code_size; - int ho = 0; - for(int h = 0; h < nhash; h++) { - uint64_t hash = *(uint64_t*)(xi + (ho >> 3)) >> (ho & 7); - hash &= mask; - maps[h][hash].push_back(i + ntotal); - ho += b; - } - } - ntotal += n; -} - - -namespace { - -template -static -void verify_shortlist( - const IndexBinaryFlat & index, - const uint8_t * q, - const std::unordered_set & shortlist, - SearchResults &res) -{ - size_t code_size = index.code_size; - size_t nlist = 0, ndis = 0, n0 = 0; - - HammingComputer hc (q, code_size); - const uint8_t *codes = index.xb.data(); - - for (auto i: shortlist) { - int dis = hc.hamming (codes + i * code_size); - res.add(dis, i); - } -} - -template -void -search_1_query_multihash(const IndexBinaryMultiHash & index, const uint8_t *xi, - SearchResults &res, - size_t &n0, size_t &nlist, size_t &ndis) -{ - - std::unordered_set shortlist; - int b = index.b; - uint64_t mask = ((uint64_t)1 << b) - 1; - - int ho = 0; - for(int h = 0; h < index.nhash; h++) { - uint64_t qhash = *(uint64_t*)(xi + (ho >> 3)) >> (ho & 7); - qhash &= mask; - const IndexBinaryMultiHash::Map & map = index.maps[h]; - - FlipEnumerator fe(index.b, index.nflip); - // loop over neighbors that are at most at nflip bits - do { - uint64_t hash = qhash ^ fe.x; - auto it = map.find (hash); - - if (it != map.end()) { - const std::vector & v = it->second; - for (auto i: v) { - shortlist.insert(i); - } - nlist++; - } else { - n0++; - } - } while(fe.next()); - - ho += b; - } - ndis += shortlist.size(); - - // verify shortlist - -#define HC(name) verify_shortlist (*index.storage, xi, shortlist, res) - switch(index.code_size) { - case 4: HC(HammingComputer4); break; - case 8: HC(HammingComputer8); break; - case 16: HC(HammingComputer16); break; - case 20: HC(HammingComputer20); break; - case 32: HC(HammingComputer32); break; - default: - if (index.code_size % 8 == 0) { - HC(HammingComputerM8); - } else { - HC(HammingComputerDefault); - } - } -#undef HC -} - -} // anonymous namespace - -void IndexBinaryMultiHash::range_search(idx_t n, const uint8_t *x, int radius, - RangeSearchResult *result, - ConcurrentBitsetPtr bitset) const -{ - - size_t nlist = 0, ndis = 0, n0 = 0; - -#pragma omp parallel if(n > 100) reduction(+: ndis, n0, nlist) - { - RangeSearchPartialResult pres (result); - -#pragma omp for - for (size_t i = 0; i < n; i++) { // loop queries - RangeQueryResult & qres = pres.new_result (i); - RangeSearchResults res = {radius, qres}; - const uint8_t *q = x + i * code_size; - - search_1_query_multihash (*this, q, res, n0, nlist, ndis); - - } - pres.finalize (); - } - indexBinaryHash_stats.nq += n; - indexBinaryHash_stats.n0 += n0; - indexBinaryHash_stats.nlist += nlist; - indexBinaryHash_stats.ndis += ndis; -} - -void IndexBinaryMultiHash::search(idx_t n, const uint8_t *x, idx_t k, - int32_t *distances, idx_t *labels, - ConcurrentBitsetPtr bitset) const -{ - - using HeapForL2 = CMax; - size_t nlist = 0, ndis = 0, n0 = 0; - -#pragma omp parallel for if(n > 100) reduction(+: nlist, ndis, n0) - for (size_t i = 0; i < n; i++) { - int32_t * simi = distances + k * i; - idx_t * idxi = labels + k * i; - - heap_heapify (k, simi, idxi); - KnnSearchResults res = {k, simi, idxi}; - const uint8_t *q = x + i * code_size; - - search_1_query_multihash (*this, q, res, n0, nlist, ndis); - - } - indexBinaryHash_stats.nq += n; - indexBinaryHash_stats.n0 += n0; - indexBinaryHash_stats.nlist += nlist; - indexBinaryHash_stats.ndis += ndis; -} - -size_t IndexBinaryMultiHash::hashtable_size() const -{ - size_t tot = 0; - for (auto map: maps) { - tot += map.size(); - } - - return tot; -} - - -} diff --git a/core/src/index/thirdparty/faiss/IndexBinaryHash.h b/core/src/index/thirdparty/faiss/IndexBinaryHash.h deleted file mode 100644 index 5dbcad626d..0000000000 --- a/core/src/index/thirdparty/faiss/IndexBinaryHash.h +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_BINARY_HASH_H -#define FAISS_BINARY_HASH_H - - - -#include -#include - -#include -#include -#include - - -namespace faiss { - -struct RangeSearchResult; - - -/** just uses the b first bits as a hash value */ -struct IndexBinaryHash : IndexBinary { - - struct InvertedList { - std::vector ids; - std::vector vecs; - - void add (idx_t id, size_t code_size, const uint8_t *code); - }; - - using InvertedListMap = std::unordered_map; - InvertedListMap invlists; - - int b, nflip; - - IndexBinaryHash(int d, int b); - - IndexBinaryHash(); - - void reset() override; - - void add(idx_t n, const uint8_t *x) override; - - void add_with_ids(idx_t n, const uint8_t *x, const idx_t *xids) override; - - void range_search(idx_t n, const uint8_t *x, int radius, - RangeSearchResult *result, - ConcurrentBitsetPtr bitset = nullptr) const override; - - void search(idx_t n, const uint8_t *x, idx_t k, - int32_t *distances, idx_t *labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - void display() const; - size_t hashtable_size() const; - -}; - -struct IndexBinaryHashStats { - size_t nq; // nb of queries run - size_t n0; // nb of empty lists - size_t nlist; // nb of non-empty inverted lists scanned - size_t ndis; // nb of distancs computed - - IndexBinaryHashStats () {reset (); } - void reset (); -}; - -extern IndexBinaryHashStats indexBinaryHash_stats; - - -/** just uses the b first bits as a hash value */ -struct IndexBinaryMultiHash: IndexBinary { - - // where the vectors are actually stored - IndexBinaryFlat *storage; - bool own_fields; - - // maps hash values to the ids that hash to them - using Map = std::unordered_map >; - - // the different hashes, size nhash - std::vector maps; - - int nhash; ///< nb of hash maps - int b; ///< nb bits per hash map - int nflip; ///< nb bit flips to use at search time - - IndexBinaryMultiHash(int d, int nhash, int b); - - IndexBinaryMultiHash(); - - ~IndexBinaryMultiHash(); - - void reset() override; - - void add(idx_t n, const uint8_t *x) override; - - void range_search(idx_t n, const uint8_t *x, int radius, - RangeSearchResult *result, - ConcurrentBitsetPtr bitset = nullptr) const override; - - void search(idx_t n, const uint8_t *x, idx_t k, - int32_t *distances, idx_t *labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - size_t hashtable_size() const; - -}; - -} - -#endif diff --git a/core/src/index/thirdparty/faiss/IndexBinaryIVF.cpp b/core/src/index/thirdparty/faiss/IndexBinaryIVF.cpp deleted file mode 100644 index 57ab21dd80..0000000000 --- a/core/src/index/thirdparty/faiss/IndexBinaryIVF.cpp +++ /dev/null @@ -1,975 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved -// -*- c++ -*- - -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace faiss { - -IndexBinaryIVF::IndexBinaryIVF(IndexBinary *quantizer, size_t d, size_t nlist) - : IndexBinary(d), - invlists(new ArrayInvertedLists(nlist, code_size)), - own_invlists(true), - nprobe(1), - max_codes(0), - quantizer(quantizer), - nlist(nlist), - own_fields(false), - clustering_index(nullptr) -{ - FAISS_THROW_IF_NOT (d == quantizer->d); - is_trained = quantizer->is_trained && (quantizer->ntotal == nlist); - - cp.niter = 10; -} - -IndexBinaryIVF::IndexBinaryIVF(IndexBinary *quantizer, size_t d, size_t nlist, MetricType metric) - : IndexBinary(d, metric), - invlists(new ArrayInvertedLists(nlist, code_size)), - own_invlists(true), - nprobe(1), - max_codes(0), - quantizer(quantizer), - nlist(nlist), - own_fields(false), - clustering_index(nullptr) -{ - FAISS_THROW_IF_NOT (d == quantizer->d); - is_trained = quantizer->is_trained && (quantizer->ntotal == nlist); - - cp.niter = 10; -} - -IndexBinaryIVF::IndexBinaryIVF() - : invlists(nullptr), - own_invlists(false), - nprobe(1), - max_codes(0), - quantizer(nullptr), - nlist(0), - own_fields(false), - clustering_index(nullptr) -{} - -void IndexBinaryIVF::add(idx_t n, const uint8_t *x) { - add_with_ids(n, x, nullptr); -} - -void IndexBinaryIVF::add_with_ids(idx_t n, const uint8_t *x, const idx_t *xids) { - add_core(n, x, xids, nullptr); -} - -void IndexBinaryIVF::add_core(idx_t n, const uint8_t *x, const idx_t *xids, - const idx_t *precomputed_idx) { - FAISS_THROW_IF_NOT(is_trained); - assert(invlists); - direct_map.check_can_add (xids); - - const idx_t * idx; - - std::unique_ptr scoped_idx; - - if (precomputed_idx) { - idx = precomputed_idx; - } else { - scoped_idx.reset(new idx_t[n]); - quantizer->assign(n, x, scoped_idx.get()); - idx = scoped_idx.get(); - } - - long n_add = 0; - for (size_t i = 0; i < n; i++) { - idx_t id = xids ? xids[i] : ntotal + i; - idx_t list_no = idx[i]; - - if (list_no < 0) { - direct_map.add_single_id (id, -1, 0); - } else { - const uint8_t *xi = x + i * code_size; - size_t offset = invlists->add_entry(list_no, id, xi); - - direct_map.add_single_id (id, list_no, offset); - } - - n_add++; - } - if (verbose) { - printf("IndexBinaryIVF::add_with_ids: added %ld / %ld vectors\n", - n_add, n); - } - ntotal += n_add; -} - -void IndexBinaryIVF::make_direct_map (bool b) -{ - if (b) { - direct_map.set_type (DirectMap::Array, invlists, ntotal); - } else { - direct_map.set_type (DirectMap::NoMap, invlists, ntotal); - } -} - -void IndexBinaryIVF::set_direct_map_type (DirectMap::Type type) -{ - direct_map.set_type (type, invlists, ntotal); -} - - -void IndexBinaryIVF::search(idx_t n, const uint8_t *x, idx_t k, - int32_t *distances, idx_t *labels, - ConcurrentBitsetPtr bitset) const { - std::unique_ptr idx(new idx_t[n * nprobe]); - std::unique_ptr coarse_dis(new int32_t[n * nprobe]); - - double t0 = getmillisecs(); - quantizer->search(n, x, nprobe, coarse_dis.get(), idx.get()); - indexIVF_stats.quantization_time += getmillisecs() - t0; - - t0 = getmillisecs(); - invlists->prefetch_lists(idx.get(), n * nprobe); - - search_preassigned(n, x, k, idx.get(), coarse_dis.get(), - distances, labels, false, nullptr, bitset); - indexIVF_stats.search_time += getmillisecs() - t0; -} - -#if 0 -void IndexBinaryIVF::get_vector_by_id(idx_t n, const idx_t *xid, uint8_t *x, ConcurrentBitsetPtr bitset) { - make_direct_map(true); - - /* only get vector by 1 id */ - FAISS_ASSERT(n == 1); - if (!bitset || !bitset->test(xid[0])) { - reconstruct(xid[0], x + 0 * d); - } else { - memset(x, UINT8_MAX, d * sizeof(uint8_t)); - } -} - -void IndexBinaryIVF::search_by_id (idx_t n, const idx_t *xid, idx_t k, int32_t *distances, idx_t *labels, - ConcurrentBitsetPtr bitset) { - make_direct_map(true); - - auto x = new uint8_t[n * d]; - for (idx_t i = 0; i < n; ++i) { - reconstruct(xid[i], x + i * d); - } - - search(n, x, k, distances, labels, bitset); - delete []x; -} -#endif - -void IndexBinaryIVF::reconstruct(idx_t key, uint8_t *recons) const { - idx_t lo = direct_map.get (key); - reconstruct_from_offset (lo_listno(lo), lo_offset(lo), recons); -} - -void IndexBinaryIVF::reconstruct_n(idx_t i0, idx_t ni, uint8_t *recons) const { - FAISS_THROW_IF_NOT(ni == 0 || (i0 >= 0 && i0 + ni <= ntotal)); - - for (idx_t list_no = 0; list_no < nlist; list_no++) { - size_t list_size = invlists->list_size(list_no); - const Index::idx_t *idlist = invlists->get_ids(list_no); - - for (idx_t offset = 0; offset < list_size; offset++) { - idx_t id = idlist[offset]; - if (!(id >= i0 && id < i0 + ni)) { - continue; - } - - uint8_t *reconstructed = recons + (id - i0) * d; - reconstruct_from_offset(list_no, offset, reconstructed); - } - } -} - -void IndexBinaryIVF::search_and_reconstruct(idx_t n, const uint8_t *x, idx_t k, - int32_t *distances, idx_t *labels, - uint8_t *recons) const { - std::unique_ptr idx(new idx_t[n * nprobe]); - std::unique_ptr coarse_dis(new int32_t[n * nprobe]); - - quantizer->search(n, x, nprobe, coarse_dis.get(), idx.get()); - - invlists->prefetch_lists(idx.get(), n * nprobe); - - // search_preassigned() with `store_pairs` enabled to obtain the list_no - // and offset into `codes` for reconstruction - search_preassigned(n, x, k, idx.get(), coarse_dis.get(), - distances, labels, /* store_pairs */true); - for (idx_t i = 0; i < n; ++i) { - for (idx_t j = 0; j < k; ++j) { - idx_t ij = i * k + j; - idx_t key = labels[ij]; - uint8_t *reconstructed = recons + ij * d; - if (key < 0) { - // Fill with NaNs - memset(reconstructed, -1, sizeof(*reconstructed) * d); - } else { - int list_no = key >> 32; - int offset = key & 0xffffffff; - - // Update label to the actual id - labels[ij] = invlists->get_single_id(list_no, offset); - - reconstruct_from_offset(list_no, offset, reconstructed); - } - } - } -} - -void IndexBinaryIVF::reconstruct_from_offset(idx_t list_no, idx_t offset, - uint8_t *recons) const { - memcpy(recons, invlists->get_single_code(list_no, offset), code_size); -} - -void IndexBinaryIVF::reset() { - direct_map.clear(); - invlists->reset(); - ntotal = 0; -} - -size_t IndexBinaryIVF::remove_ids(const IDSelector& sel) { - size_t nremove = direct_map.remove_ids (sel, invlists); - ntotal -= nremove; - return nremove; -} - -void IndexBinaryIVF::train(idx_t n, const uint8_t *x) { - if (verbose) { - printf("Training quantizer\n"); - } - - if (quantizer->is_trained && (quantizer->ntotal == nlist)) { - if (verbose) { - printf("IVF quantizer does not need training.\n"); - } - } else { - if (verbose) { - printf("Training quantizer on %ld vectors in %dD\n", n, d); - } - - Clustering clus(d, nlist, cp); - quantizer->reset(); - - IndexFlat index_tmp; - - if (metric_type == METRIC_Jaccard || metric_type == METRIC_Tanimoto) { - index_tmp = IndexFlat(d, METRIC_Jaccard); - } else if (metric_type == METRIC_Substructure || metric_type == METRIC_Superstructure) { - // unsupported - FAISS_THROW_MSG("IVF not to support Substructure and Superstructure."); - } else { - index_tmp = IndexFlat(d, METRIC_L2); - } - - if (clustering_index && verbose) { - printf("using clustering_index of dimension %d to do the clustering\n", - clustering_index->d); - } - - // LSH codec that is able to convert the binary vectors to floats. - IndexLSH codec(d, d, false, false); - - clus.train_encoded (n, x, &codec, clustering_index ? *clustering_index : index_tmp); - - // convert clusters to binary - std::unique_ptr x_b(new uint8_t[clus.k * code_size]); - real_to_binary(d * clus.k, clus.centroids.data(), x_b.get()); - - quantizer->add(clus.k, x_b.get()); - quantizer->is_trained = true; - } - - is_trained = true; -} - -void IndexBinaryIVF::merge_from(IndexBinaryIVF &other, idx_t add_id) { - // minimal sanity checks - FAISS_THROW_IF_NOT(other.d == d); - FAISS_THROW_IF_NOT(other.nlist == nlist); - FAISS_THROW_IF_NOT(other.code_size == code_size); - FAISS_THROW_IF_NOT_MSG(direct_map.no() && other.direct_map.no(), - "direct map copy not implemented"); - FAISS_THROW_IF_NOT_MSG(typeid (*this) == typeid (other), - "can only merge indexes of the same type"); - - invlists->merge_from (other.invlists, add_id); - - ntotal += other.ntotal; - other.ntotal = 0; -} - -void IndexBinaryIVF::replace_invlists(InvertedLists *il, bool own) { - FAISS_THROW_IF_NOT(il->nlist == nlist && - il->code_size == code_size); - if (own_invlists) { - delete invlists; - } - invlists = il; - own_invlists = own; -} - - -namespace { - -using idx_t = Index::idx_t; - - -template -struct IVFBinaryScannerL2: BinaryInvertedListScanner { - - HammingComputer hc; - size_t code_size; - bool store_pairs; - - IVFBinaryScannerL2 (size_t code_size, bool store_pairs): - code_size (code_size), store_pairs(store_pairs) - {} - - void set_query (const uint8_t *query_vector) override { - hc.set (query_vector, code_size); - } - - idx_t list_no; - void set_list (idx_t list_no, uint8_t /* coarse_dis */) override { - this->list_no = list_no; - } - - uint32_t distance_to_code (const uint8_t *code) const override { - return hc.hamming (code); - } - - size_t scan_codes (size_t n, - const uint8_t *codes, - const idx_t *ids, - int32_t *simi, idx_t *idxi, - size_t k, - ConcurrentBitsetPtr bitset) const override - { - using C = CMax; - - size_t nup = 0; - for (size_t j = 0; j < n; j++) { - if (!bitset || !bitset->test(ids[j])) { - uint32_t dis = hc.hamming (codes); - if (dis < simi[0]) { - idx_t id = store_pairs ? (list_no << 32 | j) : ids[j]; - heap_swap_top (k, simi, idxi, dis, id); - nup++; - } - } - codes += code_size; - } - return nup; - } - - void scan_codes_range (size_t n, - const uint8_t *codes, - const idx_t *ids, - int radius, - RangeQueryResult &result) const - { - size_t nup = 0; - for (size_t j = 0; j < n; j++) { - uint32_t dis = hc.hamming (codes); - if (dis < radius) { - int64_t id = store_pairs ? lo_build (list_no, j) : ids[j]; - result.add (dis, id); - } - codes += code_size; - } - } -}; - -template -struct IVFBinaryScannerJaccard: BinaryInvertedListScanner { - DistanceComputer hc; - size_t code_size; - - IVFBinaryScannerJaccard (size_t code_size): code_size (code_size) - {} - - void set_query (const uint8_t *query_vector) override { - hc.set (query_vector, code_size); - } - - idx_t list_no; - void set_list (idx_t list_no, uint8_t /* coarse_dis */) override { - this->list_no = list_no; - } - - uint32_t distance_to_code (const uint8_t *code) const override { - } - - size_t scan_codes (size_t n, - const uint8_t *codes, - const idx_t *ids, - int32_t *simi, idx_t *idxi, - size_t k, - ConcurrentBitsetPtr bitset = nullptr) const override - { - using C = CMax; - float* psimi = (float*)simi; - size_t nup = 0; - for (size_t j = 0; j < n; j++) { - if(!bitset || !bitset->test(ids[j])){ - float dis = hc.compute (codes); - - if (dis < psimi[0]) { - idx_t id = store_pairs ? (list_no << 32 | j) : ids[j]; - heap_swap_top (k, psimi, idxi, dis, id); - nup++; - } - } - codes += code_size; - } - return nup; - } - - void scan_codes_range (size_t n, - const uint8_t *codes, - const idx_t *ids, - int radius, - RangeQueryResult &result) const override { - // not yet - } -}; - -template -BinaryInvertedListScanner *select_IVFBinaryScannerL2 (size_t code_size) { -#define HC(name) return new IVFBinaryScannerL2 (code_size, store_pairs) - switch (code_size) { - case 4: HC(HammingComputer4); - case 8: HC(HammingComputer8); - case 16: HC(HammingComputer16); - case 20: HC(HammingComputer20); - case 32: HC(HammingComputer32); - case 64: HC(HammingComputer64); - default: - if (code_size % 8 == 0) { - HC(HammingComputerM8); - } else if (code_size % 4 == 0) { - HC(HammingComputerM4); - } else { - HC(HammingComputerDefault); - } - } -#undef HC -} - -template -BinaryInvertedListScanner *select_IVFBinaryScannerJaccard (size_t code_size) { - switch (code_size) { -#define HANDLE_CS(cs) \ - case cs: \ - return new IVFBinaryScannerJaccard (cs); - HANDLE_CS(16) - HANDLE_CS(32) - HANDLE_CS(64) - HANDLE_CS(128) - HANDLE_CS(256) - HANDLE_CS(512) -#undef HANDLE_CS - default: - return new IVFBinaryScannerJaccard(code_size); - } -} - -void search_knn_hamming_heap(const IndexBinaryIVF& ivf, - size_t n, - const uint8_t *x, - idx_t k, - const idx_t *keys, - const int32_t * coarse_dis, - int32_t *distances, idx_t *labels, - bool store_pairs, - const IVFSearchParameters *params, - ConcurrentBitsetPtr bitset = nullptr) -{ - long nprobe = params ? params->nprobe : ivf.nprobe; - long max_codes = params ? params->max_codes : ivf.max_codes; - MetricType metric_type = ivf.metric_type; - - // almost verbatim copy from IndexIVF::search_preassigned - - size_t nlistv = 0, ndis = 0, nheap = 0; - using HeapForIP = CMin; - using HeapForL2 = CMax; - -#pragma omp parallel if(n > 1) reduction(+: nlistv, ndis, nheap) - { - std::unique_ptr scanner - (ivf.get_InvertedListScanner (store_pairs)); - -#pragma omp for - for (size_t i = 0; i < n; i++) { - const uint8_t *xi = x + i * ivf.code_size; - scanner->set_query(xi); - - const idx_t * keysi = keys + i * nprobe; - int32_t * simi = distances + k * i; - idx_t * idxi = labels + k * i; - - if (metric_type == METRIC_INNER_PRODUCT) { - heap_heapify (k, simi, idxi); - } else { - heap_heapify (k, simi, idxi); - } - - size_t nscan = 0; - - for (size_t ik = 0; ik < nprobe; ik++) { - idx_t key = keysi[ik]; /* select the list */ - if (key < 0) { - // not enough centroids for multiprobe - continue; - } - FAISS_THROW_IF_NOT_FMT - (key < (idx_t) ivf.nlist, - "Invalid key=%ld at ik=%ld nlist=%ld\n", - key, ik, ivf.nlist); - - scanner->set_list (key, coarse_dis[i * nprobe + ik]); - - nlistv++; - - size_t list_size = ivf.invlists->list_size(key); - InvertedLists::ScopedCodes scodes (ivf.invlists, key); - std::unique_ptr sids; - const Index::idx_t * ids = nullptr; - - if (!store_pairs) { - sids.reset (new InvertedLists::ScopedIds (ivf.invlists, key)); - ids = sids->get(); - } - - nheap += scanner->scan_codes (list_size, scodes.get(), - ids, simi, idxi, k, bitset); - - nscan += list_size; - if (max_codes && nscan >= max_codes) - break; - } - - ndis += nscan; - if (metric_type == METRIC_INNER_PRODUCT) { - heap_reorder (k, simi, idxi); - } else { - heap_reorder (k, simi, idxi); - } - - } // parallel for - } // parallel - - indexIVF_stats.nq += n; - indexIVF_stats.nlist += nlistv; - indexIVF_stats.ndis += ndis; - indexIVF_stats.nheap_updates += nheap; - -} - -void search_knn_binary_dis_heap(const IndexBinaryIVF& ivf, - size_t n, - const uint8_t *x, - idx_t k, - const idx_t *keys, - const float * coarse_dis, - float *distances, - idx_t *labels, - bool store_pairs, - const IVFSearchParameters *params, - ConcurrentBitsetPtr bitset = nullptr) -{ - long nprobe = params ? params->nprobe : ivf.nprobe; - long max_codes = params ? params->max_codes : ivf.max_codes; - MetricType metric_type = ivf.metric_type; - - // almost verbatim copy from IndexIVF::search_preassigned - - size_t nlistv = 0, ndis = 0, nheap = 0; - using HeapForJaccard = CMax; - -#pragma omp parallel if(n > 1) reduction(+: nlistv, ndis, nheap) - { - std::unique_ptr scanner - (ivf.get_InvertedListScanner(store_pairs)); - -#pragma omp for - for (size_t i = 0; i < n; i++) { - const uint8_t *xi = x + i * ivf.code_size; - scanner->set_query(xi); - - const idx_t * keysi = keys + i * nprobe; - float * simi = distances + k * i; - idx_t * idxi = labels + k * i; - - heap_heapify (k, simi, idxi); - - size_t nscan = 0; - - for (size_t ik = 0; ik < nprobe; ik++) { - idx_t key = keysi[ik]; /* select the list */ - if (key < 0) { - // not enough centroids for multiprobe - continue; - } - FAISS_THROW_IF_NOT_FMT - (key < (idx_t) ivf.nlist, - "Invalid key=%ld at ik=%ld nlist=%ld\n", - key, ik, ivf.nlist); - - scanner->set_list (key, (int32_t)coarse_dis[i * nprobe + ik]); - - nlistv++; - - size_t list_size = ivf.invlists->list_size(key); - InvertedLists::ScopedCodes scodes (ivf.invlists, key); - std::unique_ptr sids; - const Index::idx_t * ids = nullptr; - - if (!store_pairs) { - sids.reset (new InvertedLists::ScopedIds (ivf.invlists, key)); - ids = sids->get(); - } - - nheap += scanner->scan_codes (list_size, scodes.get(), - ids, (int32_t*)simi, idxi, k, bitset); - - nscan += list_size; - if (max_codes && nscan >= max_codes) - break; - } - - ndis += nscan; - heap_reorder (k, simi, idxi); - - } // parallel for - } // parallel - - indexIVF_stats.nq += n; - indexIVF_stats.nlist += nlistv; - indexIVF_stats.ndis += ndis; - indexIVF_stats.nheap_updates += nheap; -} - -template -void search_knn_hamming_count(const IndexBinaryIVF& ivf, - size_t nx, - const uint8_t *x, - const idx_t *keys, - int k, - int32_t *distances, - idx_t *labels, - const IVFSearchParameters *params, - ConcurrentBitsetPtr bitset = nullptr) { - const int nBuckets = ivf.d + 1; - std::vector all_counters(nx * nBuckets, 0); - std::unique_ptr all_ids_per_dis(new idx_t[nx * nBuckets * k]); - - long nprobe = params ? params->nprobe : ivf.nprobe; - long max_codes = params ? params->max_codes : ivf.max_codes; - - std::vector> cs; - for (size_t i = 0; i < nx; ++i) { - cs.push_back(HCounterState( - all_counters.data() + i * nBuckets, - all_ids_per_dis.get() + i * nBuckets * k, - x + i * ivf.code_size, - ivf.d, - k - )); - } - - size_t nlistv = 0, ndis = 0; - -#pragma omp parallel for reduction(+: nlistv, ndis) - for (size_t i = 0; i < nx; i++) { - const idx_t * keysi = keys + i * nprobe; - HCounterState& csi = cs[i]; - - size_t nscan = 0; - - for (size_t ik = 0; ik < nprobe; ik++) { - idx_t key = keysi[ik]; /* select the list */ - if (key < 0) { - // not enough centroids for multiprobe - continue; - } - FAISS_THROW_IF_NOT_FMT ( - key < (idx_t) ivf.nlist, - "Invalid key=%ld at ik=%ld nlist=%ld\n", - key, ik, ivf.nlist); - - nlistv++; - size_t list_size = ivf.invlists->list_size(key); - InvertedLists::ScopedCodes scodes (ivf.invlists, key); - const uint8_t *list_vecs = scodes.get(); - const Index::idx_t *ids = store_pairs - ? nullptr - : ivf.invlists->get_ids(key); - - for (size_t j = 0; j < list_size; j++) { - if (!bitset || !bitset->test(ids[j])) { - const uint8_t *yj = list_vecs + ivf.code_size * j; - idx_t id = store_pairs ? (key << 32 | j) : ids[j]; - csi.update_counter(yj, id); - } - } - if (ids) - ivf.invlists->release_ids (key, ids); - - nscan += list_size; - if (max_codes && nscan >= max_codes) - break; - } - ndis += nscan; - - int nres = 0; - for (int b = 0; b < nBuckets && nres < k; b++) { - for (int l = 0; l < csi.counters[b] && nres < k; l++) { - labels[i * k + nres] = csi.ids_per_dis[b * k + l]; - distances[i * k + nres] = b; - nres++; - } - } - while (nres < k) { - labels[i * k + nres] = -1; - distances[i * k + nres] = std::numeric_limits::max(); - ++nres; - } - } - - indexIVF_stats.nq += nx; - indexIVF_stats.nlist += nlistv; - indexIVF_stats.ndis += ndis; -} - - - -template -void search_knn_hamming_count_1 ( - const IndexBinaryIVF& ivf, - size_t nx, - const uint8_t *x, - const idx_t *keys, - int k, - int32_t *distances, - idx_t *labels, - const IVFSearchParameters *params, - ConcurrentBitsetPtr bitset = nullptr) { - switch (ivf.code_size) { -#define HANDLE_CS(cs) \ - case cs: \ - search_knn_hamming_count( \ - ivf, nx, x, keys, k, distances, labels, params, bitset); \ - break; - HANDLE_CS(4); - HANDLE_CS(8); - HANDLE_CS(16); - HANDLE_CS(20); - HANDLE_CS(32); - HANDLE_CS(64); -#undef HANDLE_CS - default: - if (ivf.code_size % 8 == 0) { - search_knn_hamming_count - (ivf, nx, x, keys, k, distances, labels, params, bitset); - } else if (ivf.code_size % 4 == 0) { - search_knn_hamming_count - (ivf, nx, x, keys, k, distances, labels, params, bitset); - } else { - search_knn_hamming_count - (ivf, nx, x, keys, k, distances, labels, params, bitset); - } - break; - } -} - -} // namespace - -BinaryInvertedListScanner *IndexBinaryIVF::get_InvertedListScanner - (bool store_pairs) const -{ - switch (metric_type) { - case METRIC_Jaccard: - case METRIC_Tanimoto: - if (store_pairs) { - return select_IVFBinaryScannerJaccard (code_size); - } else { - return select_IVFBinaryScannerJaccard (code_size); - } - case METRIC_Substructure: - case METRIC_Superstructure: - // unsupported - return nullptr; - default: - if (store_pairs) { - return select_IVFBinaryScannerL2(code_size); - } else { - return select_IVFBinaryScannerL2(code_size); - } - } -} - -void IndexBinaryIVF::search_preassigned(idx_t n, const uint8_t *x, idx_t k, - const idx_t *idx, - const int32_t * coarse_dis, - int32_t *distances, idx_t *labels, - bool store_pairs, - const IVFSearchParameters *params, - ConcurrentBitsetPtr bitset - ) const { - if (metric_type == METRIC_Jaccard || metric_type == METRIC_Tanimoto) { - if (use_heap) { - float *D = new float[k * n]; - float *c_dis = new float [n * nprobe]; - memcpy(c_dis, coarse_dis, sizeof(float) * n * nprobe); - search_knn_binary_dis_heap(*this, n, x, k, idx, c_dis , - D, labels, store_pairs, - params, bitset); - if (metric_type == METRIC_Tanimoto) { - for (int i = 0; i < k * n; i++) { - D[i] = -log2(1-D[i]); - } - } - memcpy(distances, D, sizeof(float) * n * k); - delete [] D; - delete [] c_dis; - } else { - //not implemented - } - } else if (metric_type == METRIC_Substructure || metric_type == METRIC_Superstructure) { - // unsupported - } else { - if (use_heap) { - search_knn_hamming_heap (*this, n, x, k, idx, coarse_dis, - distances, labels, store_pairs, - params, bitset); - } else { - if (store_pairs) { - search_knn_hamming_count_1 - (*this, n, x, idx, k, distances, labels, params, bitset); - } else { - search_knn_hamming_count_1 - (*this, n, x, idx, k, distances, labels, params, bitset); - } - } - } -} - -void IndexBinaryIVF::range_search( - idx_t n, const uint8_t *x, int radius, - RangeSearchResult *res, - ConcurrentBitsetPtr bitset) const -{ - std::unique_ptr idx(new idx_t[n * nprobe]); - std::unique_ptr coarse_dis(new int32_t[n * nprobe]); - - double t0 = getmillisecs(); - quantizer->search(n, x, nprobe, coarse_dis.get(), idx.get()); - indexIVF_stats.quantization_time += getmillisecs() - t0; - - t0 = getmillisecs(); - invlists->prefetch_lists(idx.get(), n * nprobe); - - bool store_pairs = false; - size_t nlistv = 0, ndis = 0; - - std::vector all_pres (omp_get_max_threads()); - -#pragma omp parallel reduction(+: nlistv, ndis) - { - RangeSearchPartialResult pres(res); - std::unique_ptr scanner - (get_InvertedListScanner(store_pairs)); - FAISS_THROW_IF_NOT (scanner.get ()); - - all_pres[omp_get_thread_num()] = &pres; - - auto scan_list_func = [&](size_t i, size_t ik, RangeQueryResult &qres) - { - - idx_t key = idx[i * nprobe + ik]; /* select the list */ - if (key < 0) return; - FAISS_THROW_IF_NOT_FMT ( - key < (idx_t) nlist, - "Invalid key=%ld at ik=%ld nlist=%ld\n", - key, ik, nlist); - const size_t list_size = invlists->list_size(key); - - if (list_size == 0) return; - - InvertedLists::ScopedCodes scodes (invlists, key); - InvertedLists::ScopedIds ids (invlists, key); - - scanner->set_list (key, coarse_dis[i * nprobe + ik]); - nlistv++; - ndis += list_size; - scanner->scan_codes_range (list_size, scodes.get(), - ids.get(), radius, qres); - }; - -#pragma omp for - for (size_t i = 0; i < n; i++) { - scanner->set_query (x + i * code_size); - - RangeQueryResult & qres = pres.new_result (i); - - for (size_t ik = 0; ik < nprobe; ik++) { - scan_list_func (i, ik, qres); - } - - } - - pres.finalize(); - - } - indexIVF_stats.nq += n; - indexIVF_stats.nlist += nlistv; - indexIVF_stats.ndis += ndis; - indexIVF_stats.search_time += getmillisecs() - t0; - -} - - - - -IndexBinaryIVF::~IndexBinaryIVF() { - if (own_invlists) { - delete invlists; - } - - if (own_fields) { - delete quantizer; - } -} - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexBinaryIVF.h b/core/src/index/thirdparty/faiss/IndexBinaryIVF.h deleted file mode 100644 index c3cd7e7443..0000000000 --- a/core/src/index/thirdparty/faiss/IndexBinaryIVF.h +++ /dev/null @@ -1,234 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_INDEX_BINARY_IVF_H -#define FAISS_INDEX_BINARY_IVF_H - - -#include - -#include -#include -#include -#include - - -namespace faiss { - -struct BinaryInvertedListScanner; - -/** Index based on a inverted file (IVF) - * - * In the inverted file, the quantizer (an IndexBinary instance) provides a - * quantization index for each vector to be added. The quantization - * index maps to a list (aka inverted list or posting list), where the - * id of the vector is stored. - * - * Otherwise the object is similar to the IndexIVF - */ -struct IndexBinaryIVF : IndexBinary { - /// Acess to the actual data - InvertedLists *invlists; - bool own_invlists; - - size_t nprobe; ///< number of probes at query time - size_t max_codes; ///< max nb of codes to visit to do a query - - /** Select between using a heap or counting to select the k smallest values - * when scanning inverted lists. - */ - bool use_heap = true; - - /// map for direct access to the elements. Enables reconstruct(). - DirectMap direct_map; - - IndexBinary *quantizer; ///< quantizer that maps vectors to inverted lists - size_t nlist; ///< number of possible key values - - bool own_fields; ///< whether object owns the quantizer - - ClusteringParameters cp; ///< to override default clustering params - Index *clustering_index; ///< to override index used during clustering - - /** The Inverted file takes a quantizer (an IndexBinary) on input, - * which implements the function mapping a vector to a list - * identifier. The pointer is borrowed: the quantizer should not - * be deleted while the IndexBinaryIVF is in use. - */ - IndexBinaryIVF(IndexBinary *quantizer, size_t d, size_t nlist); - - IndexBinaryIVF(IndexBinary *quantizer, size_t d, size_t nlist, MetricType metric); - - IndexBinaryIVF(); - - ~IndexBinaryIVF() override; - - void reset() override; - - /// Trains the quantizer - void train(idx_t n, const uint8_t *x) override; - - void add(idx_t n, const uint8_t *x) override; - - void add_with_ids(idx_t n, const uint8_t *x, const idx_t *xids) override; - - /// same as add_with_ids, with precomputed coarse quantizer - void add_core (idx_t n, const uint8_t * x, const idx_t *xids, - const idx_t *precomputed_idx); - - /** Search a set of vectors, that are pre-quantized by the IVF - * quantizer. Fill in the corresponding heaps with the query - * results. search() calls this. - * - * @param n nb of vectors to query - * @param x query vectors, size nx * d - * @param assign coarse quantization indices, size nx * nprobe - * @param centroid_dis - * distances to coarse centroids, size nx * nprobe - * @param distance - * output distances, size n * k - * @param labels output labels, size n * k - * @param store_pairs store inv list index + inv list offset - * instead in upper/lower 32 bit of result, - * instead of ids (used for reranking). - * @param params used to override the object's search parameters - */ - void search_preassigned(idx_t n, const uint8_t *x, idx_t k, - const idx_t *assign, - const int32_t *centroid_dis, - int32_t *distances, idx_t *labels, - bool store_pairs, - const IVFSearchParameters *params=nullptr, - ConcurrentBitsetPtr bitset = nullptr - ) const; - - virtual BinaryInvertedListScanner *get_InvertedListScanner ( - bool store_pairs=false) const; - - /** assign the vectors, then call search_preassign */ - void search(idx_t n, const uint8_t *x, idx_t k, - int32_t *distances, idx_t *labels, ConcurrentBitsetPtr bitset = nullptr) const override; - -#if 0 - /** get raw vectors by ids */ - void get_vector_by_id(idx_t n, const idx_t *xid, uint8_t *x, ConcurrentBitsetPtr bitset = nullptr) override; - - void search_by_id (idx_t n, const idx_t *xid, idx_t k, int32_t *distances, idx_t *labels, - ConcurrentBitsetPtr bitset = nullptr) override; -#endif - - void range_search(idx_t n, const uint8_t *x, int radius, - RangeSearchResult *result, - ConcurrentBitsetPtr bitset = nullptr) const override; - - void reconstruct(idx_t key, uint8_t *recons) const override; - - /** Reconstruct a subset of the indexed vectors. - * - * Overrides default implementation to bypass reconstruct() which requires - * direct_map to be maintained. - * - * @param i0 first vector to reconstruct - * @param ni nb of vectors to reconstruct - * @param recons output array of reconstructed vectors, size ni * d / 8 - */ - void reconstruct_n(idx_t i0, idx_t ni, uint8_t *recons) const override; - - /** Similar to search, but also reconstructs the stored vectors (or an - * approximation in the case of lossy coding) for the search results. - * - * Overrides default implementation to avoid having to maintain direct_map - * and instead fetch the code offsets through the `store_pairs` flag in - * search_preassigned(). - * - * @param recons reconstructed vectors size (n, k, d / 8) - */ - void search_and_reconstruct(idx_t n, const uint8_t *x, idx_t k, - int32_t *distances, idx_t *labels, - uint8_t *recons) const override; - - /** Reconstruct a vector given the location in terms of (inv list index + - * inv list offset) instead of the id. - * - * Useful for reconstructing when the direct_map is not maintained and - * the inv list offset is computed by search_preassigned() with - * `store_pairs` set. - */ - virtual void reconstruct_from_offset(idx_t list_no, idx_t offset, - uint8_t* recons) const; - - - /// Dataset manipulation functions - size_t remove_ids(const IDSelector& sel) override; - - /** moves the entries from another dataset to self. On output, - * other is empty. add_id is added to all moved ids (for - * sequential ids, this would be this->ntotal */ - virtual void merge_from(IndexBinaryIVF& other, idx_t add_id); - - size_t get_list_size(size_t list_no) const - { return invlists->list_size(list_no); } - - /** intialize a direct map - * - * @param new_maintain_direct_map if true, create a direct map, - * else clear it - */ - void make_direct_map(bool new_maintain_direct_map=true); - - void set_direct_map_type (DirectMap::Type type); - - void replace_invlists(InvertedLists *il, bool own=false); -}; - - -struct BinaryInvertedListScanner { - - using idx_t = Index::idx_t; - - /// from now on we handle this query. - virtual void set_query (const uint8_t *query_vector) = 0; - - /// following codes come from this inverted list - virtual void set_list (idx_t list_no, uint8_t coarse_dis) = 0; - - /// compute a single query-to-code distance - virtual uint32_t distance_to_code (const uint8_t *code) const = 0; - - /** compute the distances to codes. (distances, labels) should be - * organized as a min- or max-heap - * - * @param n number of codes to scan - * @param codes codes to scan (n * code_size) - * @param ids corresponding ids (ignored if store_pairs) - * @param distances heap distances (size k) - * @param labels heap labels (size k) - * @param k heap size - */ - virtual size_t scan_codes (size_t n, - const uint8_t *codes, - const idx_t *ids, - int32_t *distances, idx_t *labels, - size_t k, - ConcurrentBitsetPtr bitset = nullptr) const = 0; - - virtual void scan_codes_range (size_t n, - const uint8_t *codes, - const idx_t *ids, - int radius, - RangeQueryResult &result) const = 0; - - virtual ~BinaryInvertedListScanner () {} - -}; - - -} // namespace faiss - -#endif // FAISS_INDEX_BINARY_IVF_H diff --git a/core/src/index/thirdparty/faiss/IndexFlat.cpp b/core/src/index/thirdparty/faiss/IndexFlat.cpp deleted file mode 100644 index 7780650da3..0000000000 --- a/core/src/index/thirdparty/faiss/IndexFlat.cpp +++ /dev/null @@ -1,540 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { - -IndexFlat::IndexFlat (idx_t d, MetricType metric): - Index(d, metric) -{ -} - - - -void IndexFlat::add (idx_t n, const float *x) { - xb.insert(xb.end(), x, x + n * d); - ntotal += n; -} - - -void IndexFlat::reset() { - xb.clear(); - ntotal = 0; -} - - -void IndexFlat::search (idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, - ConcurrentBitsetPtr bitset) const -{ - // we see the distances and labels as heaps - - if (metric_type == METRIC_INNER_PRODUCT) { - float_minheap_array_t res = { - size_t(n), size_t(k), labels, distances}; - knn_inner_product (x, xb.data(), d, n, ntotal, &res, bitset); - } else if (metric_type == METRIC_L2) { - float_maxheap_array_t res = { - size_t(n), size_t(k), labels, distances}; - knn_L2sqr (x, xb.data(), d, n, ntotal, &res, bitset); - } else if (metric_type == METRIC_Jaccard) { - float_maxheap_array_t res = { - size_t(n), size_t(k), labels, distances}; - knn_jaccard(x, xb.data(), d, n, ntotal, &res, bitset); - } else { - float_maxheap_array_t res = { - size_t(n), size_t(k), labels, distances}; - knn_extra_metrics (x, xb.data(), d, n, ntotal, - metric_type, metric_arg, - &res, bitset); - } -} - -void IndexFlat::assign(idx_t n, const float * x, idx_t * labels, float* distances) -{ - // usually used in IVF k-means algorithm - float *dis_inner = (distances == nullptr) ? new float[n] : distances; - switch (metric_type) { - case METRIC_INNER_PRODUCT: - case METRIC_L2: { - // ignore the metric_type, both use L2 - elkan_L2_sse(x, xb.data(), d, n, ntotal, labels, dis_inner); - break; - } - default: { - // binary metrics - // There may be something wrong, but maintain the original logic now. - Index::assign(n, x, labels, dis_inner); - break; - } - } - if (distances == nullptr) { - delete[] dis_inner; - } -} - -void IndexFlat::range_search (idx_t n, const float *x, float radius, - RangeSearchResult *result, - ConcurrentBitsetPtr bitset) const -{ - switch (metric_type) { - case METRIC_INNER_PRODUCT: - range_search_inner_product (x, xb.data(), d, n, ntotal, - radius, result); - break; - case METRIC_L2: - range_search_L2sqr (x, xb.data(), d, n, ntotal, radius, result); - break; - default: - FAISS_THROW_MSG("metric type not supported"); - } -} - - -void IndexFlat::compute_distance_subset ( - idx_t n, - const float *x, - idx_t k, - float *distances, - const idx_t *labels) const -{ - switch (metric_type) { - case METRIC_INNER_PRODUCT: - fvec_inner_products_by_idx ( - distances, - x, xb.data(), labels, d, n, k); - break; - case METRIC_L2: - fvec_L2sqr_by_idx ( - distances, - x, xb.data(), labels, d, n, k); - break; - default: - FAISS_THROW_MSG("metric type not supported"); - } - -} - -size_t IndexFlat::remove_ids (const IDSelector & sel) -{ - idx_t j = 0; - for (idx_t i = 0; i < ntotal; i++) { - if (sel.is_member (i)) { - // should be removed - } else { - if (i > j) { - memmove (&xb[d * j], &xb[d * i], sizeof(xb[0]) * d); - } - j++; - } - } - size_t nremove = ntotal - j; - if (nremove > 0) { - ntotal = j; - xb.resize (ntotal * d); - } - return nremove; -} - - -namespace { - - -struct FlatL2Dis : DistanceComputer { - size_t d; - Index::idx_t nb; - const float *q; - const float *b; - size_t ndis; - - float operator () (idx_t i) override { - ndis++; - return fvec_L2sqr(q, b + i * d, d); - } - - float symmetric_dis(idx_t i, idx_t j) override { - return fvec_L2sqr(b + j * d, b + i * d, d); - } - - explicit FlatL2Dis(const IndexFlat& storage, const float *q = nullptr) - : d(storage.d), - nb(storage.ntotal), - q(q), - b(storage.xb.data()), - ndis(0) {} - - void set_query(const float *x) override { - q = x; - } -}; - -struct FlatIPDis : DistanceComputer { - size_t d; - Index::idx_t nb; - const float *q; - const float *b; - size_t ndis; - - float operator () (idx_t i) override { - ndis++; - return fvec_inner_product (q, b + i * d, d); - } - - float symmetric_dis(idx_t i, idx_t j) override { - return fvec_inner_product (b + j * d, b + i * d, d); - } - - explicit FlatIPDis(const IndexFlat& storage, const float *q = nullptr) - : d(storage.d), - nb(storage.ntotal), - q(q), - b(storage.xb.data()), - ndis(0) {} - - void set_query(const float *x) override { - q = x; - } -}; - - - - -} // namespace - - -DistanceComputer * IndexFlat::get_distance_computer() const { - if (metric_type == METRIC_L2) { - return new FlatL2Dis(*this); - } else if (metric_type == METRIC_INNER_PRODUCT) { - return new FlatIPDis(*this); - } else { - return get_extra_distance_computer (d, metric_type, metric_arg, - ntotal, xb.data()); - } -} - - -void IndexFlat::reconstruct (idx_t key, float * recons) const -{ - memcpy (recons, &(xb[key * d]), sizeof(*recons) * d); -} - - -/* The standalone codec interface */ -size_t IndexFlat::sa_code_size () const -{ - return sizeof(float) * d; -} - -void IndexFlat::sa_encode (idx_t n, const float *x, uint8_t *bytes) const -{ - memcpy (bytes, x, sizeof(float) * d * n); -} - -void IndexFlat::sa_decode (idx_t n, const uint8_t *bytes, float *x) const -{ - memcpy (x, bytes, sizeof(float) * d * n); -} - - - - -/*************************************************** - * IndexFlatL2BaseShift - ***************************************************/ - -IndexFlatL2BaseShift::IndexFlatL2BaseShift (idx_t d, size_t nshift, const float *shift): - IndexFlatL2 (d), shift (nshift) -{ - memcpy (this->shift.data(), shift, sizeof(float) * nshift); -} - -void IndexFlatL2BaseShift::search ( - idx_t n, - const float *x, - idx_t k, - float *distances, - idx_t *labels, - ConcurrentBitsetPtr bitset) const -{ - FAISS_THROW_IF_NOT (shift.size() == ntotal); - - float_maxheap_array_t res = { - size_t(n), size_t(k), labels, distances}; - knn_L2sqr_base_shift (x, xb.data(), d, n, ntotal, &res, shift.data()); -} - - - -/*************************************************** - * IndexRefineFlat - ***************************************************/ - -IndexRefineFlat::IndexRefineFlat (Index *base_index): - Index (base_index->d, base_index->metric_type), - refine_index (base_index->d, base_index->metric_type), - base_index (base_index), own_fields (false), - k_factor (1) -{ - is_trained = base_index->is_trained; - FAISS_THROW_IF_NOT_MSG (base_index->ntotal == 0, - "base_index should be empty in the beginning"); -} - -IndexRefineFlat::IndexRefineFlat () { - base_index = nullptr; - own_fields = false; - k_factor = 1; -} - - -void IndexRefineFlat::train (idx_t n, const float *x) -{ - base_index->train (n, x); - is_trained = true; -} - -void IndexRefineFlat::add (idx_t n, const float *x) { - FAISS_THROW_IF_NOT (is_trained); - base_index->add (n, x); - refine_index.add (n, x); - ntotal = refine_index.ntotal; -} - -void IndexRefineFlat::reset () -{ - base_index->reset (); - refine_index.reset (); - ntotal = 0; -} - -namespace { -typedef faiss::Index::idx_t idx_t; - -template -static void reorder_2_heaps ( - idx_t n, - idx_t k, idx_t *labels, float *distances, - idx_t k_base, const idx_t *base_labels, const float *base_distances) -{ -#pragma omp parallel for - for (idx_t i = 0; i < n; i++) { - idx_t *idxo = labels + i * k; - float *diso = distances + i * k; - const idx_t *idxi = base_labels + i * k_base; - const float *disi = base_distances + i * k_base; - - heap_heapify (k, diso, idxo, disi, idxi, k); - if (k_base != k) { // add remaining elements - heap_addn (k, diso, idxo, disi + k, idxi + k, k_base - k); - } - heap_reorder (k, diso, idxo); - } -} - - -} - - -void IndexRefineFlat::search ( - idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, - ConcurrentBitsetPtr bitset) const -{ - FAISS_THROW_IF_NOT (is_trained); - idx_t k_base = idx_t (k * k_factor); - idx_t * base_labels = labels; - float * base_distances = distances; - ScopeDeleter del1; - ScopeDeleter del2; - - - if (k != k_base) { - base_labels = new idx_t [n * k_base]; - del1.set (base_labels); - base_distances = new float [n * k_base]; - del2.set (base_distances); - } - - base_index->search (n, x, k_base, base_distances, base_labels); - - for (int i = 0; i < n * k_base; i++) - assert (base_labels[i] >= -1 && - base_labels[i] < ntotal); - - // compute refined distances - refine_index.compute_distance_subset ( - n, x, k_base, base_distances, base_labels); - - // sort and store result - if (metric_type == METRIC_L2) { - typedef CMax C; - reorder_2_heaps ( - n, k, labels, distances, - k_base, base_labels, base_distances); - - } else if (metric_type == METRIC_INNER_PRODUCT) { - typedef CMin C; - reorder_2_heaps ( - n, k, labels, distances, - k_base, base_labels, base_distances); - } else { - FAISS_THROW_MSG("Metric type not supported"); - } - -} - - - -IndexRefineFlat::~IndexRefineFlat () -{ - if (own_fields) delete base_index; -} - -/*************************************************** - * IndexFlat1D - ***************************************************/ - - -IndexFlat1D::IndexFlat1D (bool continuous_update): - IndexFlatL2 (1), - continuous_update (continuous_update) -{ -} - -/// if not continuous_update, call this between the last add and -/// the first search -void IndexFlat1D::update_permutation () -{ - perm.resize (ntotal); - if (ntotal < 1000000) { - fvec_argsort (ntotal, xb.data(), (size_t*)perm.data()); - } else { - fvec_argsort_parallel (ntotal, xb.data(), (size_t*)perm.data()); - } -} - -void IndexFlat1D::add (idx_t n, const float *x) -{ - IndexFlatL2::add (n, x); - if (continuous_update) - update_permutation(); -} - -void IndexFlat1D::reset() -{ - IndexFlatL2::reset(); - perm.clear(); -} - -void IndexFlat1D::search ( - idx_t n, - const float *x, - idx_t k, - float *distances, - idx_t *labels, - ConcurrentBitsetPtr bitset) const -{ - FAISS_THROW_IF_NOT_MSG (perm.size() == ntotal, - "Call update_permutation before search"); - -#pragma omp parallel for - for (idx_t i = 0; i < n; i++) { - - float q = x[i]; // query - float *D = distances + i * k; - idx_t *I = labels + i * k; - - // binary search - idx_t i0 = 0, i1 = ntotal; - idx_t wp = 0; - - if (xb[perm[i0]] > q) { - i1 = 0; - goto finish_right; - } - - if (xb[perm[i1 - 1]] <= q) { - i0 = i1 - 1; - goto finish_left; - } - - while (i0 + 1 < i1) { - idx_t imed = (i0 + i1) / 2; - if (xb[perm[imed]] <= q) i0 = imed; - else i1 = imed; - } - - // query is between xb[perm[i0]] and xb[perm[i1]] - // expand to nearest neighs - - while (wp < k) { - float xleft = xb[perm[i0]]; - float xright = xb[perm[i1]]; - - if (q - xleft < xright - q) { - D[wp] = q - xleft; - I[wp] = perm[i0]; - i0--; wp++; - if (i0 < 0) { goto finish_right; } - } else { - D[wp] = xright - q; - I[wp] = perm[i1]; - i1++; wp++; - if (i1 >= ntotal) { goto finish_left; } - } - } - goto done; - - finish_right: - // grow to the right from i1 - while (wp < k) { - if (i1 < ntotal) { - D[wp] = xb[perm[i1]] - q; - I[wp] = perm[i1]; - i1++; - } else { - D[wp] = std::numeric_limits::infinity(); - I[wp] = -1; - } - wp++; - } - goto done; - - finish_left: - // grow to the left from i0 - while (wp < k) { - if (i0 >= 0) { - D[wp] = q - xb[perm[i0]]; - I[wp] = perm[i0]; - i0--; - } else { - D[wp] = std::numeric_limits::infinity(); - I[wp] = -1; - } - wp++; - } - done: ; - } - -} - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexFlat.h b/core/src/index/thirdparty/faiss/IndexFlat.h deleted file mode 100644 index a04d32a614..0000000000 --- a/core/src/index/thirdparty/faiss/IndexFlat.h +++ /dev/null @@ -1,187 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef INDEX_FLAT_H -#define INDEX_FLAT_H - -#include - -#include - - -namespace faiss { - -/** Index that stores the full vectors and performs exhaustive search */ -struct IndexFlat: Index { - - /// database vectors, size ntotal * d - std::vector xb; - - explicit IndexFlat (idx_t d, MetricType metric = METRIC_L2); - - void add(idx_t n, const float* x) override; - - void reset() override; - - void search( - idx_t n, - const float* x, - idx_t k, - float* distances, - idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - void assign ( - idx_t n, - const float * x, - idx_t * labels, - float* distances = nullptr) override; - - void range_search( - idx_t n, - const float* x, - float radius, - RangeSearchResult* result, - ConcurrentBitsetPtr bitset = nullptr) const override; - - void reconstruct(idx_t key, float* recons) const override; - - /** compute distance with a subset of vectors - * - * @param x query vectors, size n * d - * @param labels indices of the vectors that should be compared - * for each query vector, size n * k - * @param distances - * corresponding output distances, size n * k - */ - void compute_distance_subset ( - idx_t n, - const float *x, - idx_t k, - float *distances, - const idx_t *labels) const; - - /** remove some ids. NB that Because of the structure of the - * indexing structure, the semantics of this operation are - * different from the usual ones: the new ids are shifted */ - size_t remove_ids(const IDSelector& sel) override; - - IndexFlat () {} - - DistanceComputer * get_distance_computer() const override; - - /* The stanadlone codec interface (just memcopies in this case) */ - size_t sa_code_size () const override; - - void sa_encode (idx_t n, const float *x, - uint8_t *bytes) const override; - - void sa_decode (idx_t n, const uint8_t *bytes, - float *x) const override; - -}; - - - -struct IndexFlatIP:IndexFlat { - explicit IndexFlatIP (idx_t d): IndexFlat (d, METRIC_INNER_PRODUCT) {} - IndexFlatIP () {} -}; - - -struct IndexFlatL2:IndexFlat { - explicit IndexFlatL2 (idx_t d): IndexFlat (d, METRIC_L2) {} - IndexFlatL2 () {} -}; - - -// same as an IndexFlatL2 but a value is subtracted from each distance -struct IndexFlatL2BaseShift: IndexFlatL2 { - std::vector shift; - - IndexFlatL2BaseShift (idx_t d, size_t nshift, const float *shift); - - void search( - idx_t n, - const float* x, - idx_t k, - float* distances, - idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; -}; - - -/** Index that queries in a base_index (a fast one) and refines the - * results with an exact search, hopefully improving the results. - */ -struct IndexRefineFlat: Index { - - /// storage for full vectors - IndexFlat refine_index; - - /// faster index to pre-select the vectors that should be filtered - Index *base_index; - bool own_fields; ///< should the base index be deallocated? - - /// factor between k requested in search and the k requested from - /// the base_index (should be >= 1) - float k_factor; - - explicit IndexRefineFlat (Index *base_index); - - IndexRefineFlat (); - - void train(idx_t n, const float* x) override; - - void add(idx_t n, const float* x) override; - - void reset() override; - - void search( - idx_t n, - const float* x, - idx_t k, - float* distances, - idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - ~IndexRefineFlat() override; -}; - - -/// optimized version for 1D "vectors". -struct IndexFlat1D:IndexFlatL2 { - bool continuous_update; ///< is the permutation updated continuously? - - std::vector perm; ///< sorted database indices - - explicit IndexFlat1D (bool continuous_update=true); - - /// if not continuous_update, call this between the last add and - /// the first search - void update_permutation (); - - void add(idx_t n, const float* x) override; - - void reset() override; - - /// Warn: the distances returned are L1 not L2 - void search( - idx_t n, - const float* x, - idx_t k, - float* distances, - idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; -}; - - -} - -#endif diff --git a/core/src/index/thirdparty/faiss/IndexHNSW.cpp b/core/src/index/thirdparty/faiss/IndexHNSW.cpp deleted file mode 100644 index c06f9840e2..0000000000 --- a/core/src/index/thirdparty/faiss/IndexHNSW.cpp +++ /dev/null @@ -1,1142 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#ifdef __SSE__ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern "C" { - -/* declare BLAS functions, see http://www.netlib.org/clapack/cblas/ */ - -int sgemm_ (const char *transa, const char *transb, FINTEGER *m, FINTEGER * - n, FINTEGER *k, const float *alpha, const float *a, - FINTEGER *lda, const float *b, FINTEGER * - ldb, float *beta, float *c, FINTEGER *ldc); - -} - -namespace faiss { - -using idx_t = Index::idx_t; -using MinimaxHeap = HNSW::MinimaxHeap; -using storage_idx_t = HNSW::storage_idx_t; -using NodeDistFarther = HNSW::NodeDistFarther; - -HNSWStats hnsw_stats; - -/************************************************************** - * add / search blocks of descriptors - **************************************************************/ - -namespace { - - -/* Wrap the distance computer into one that negates the - distances. This makes supporting INNER_PRODUCE search easier */ - -struct NegativeDistanceComputer: DistanceComputer { - - /// owned by this - DistanceComputer *basedis; - - explicit NegativeDistanceComputer(DistanceComputer *basedis): - basedis(basedis) - {} - - void set_query(const float *x) override { - basedis->set_query(x); - } - - /// compute distance of vector i to current query - float operator () (idx_t i) override { - return -(*basedis)(i); - } - - /// compute distance between two stored vectors - float symmetric_dis (idx_t i, idx_t j) override { - return -basedis->symmetric_dis(i, j); - } - - virtual ~NegativeDistanceComputer () - { - delete basedis; - } - -}; - -DistanceComputer *storage_distance_computer(const Index *storage) -{ - if (storage->metric_type == METRIC_INNER_PRODUCT) { - return new NegativeDistanceComputer(storage->get_distance_computer()); - } else { - return storage->get_distance_computer(); - } -} - - - -void hnsw_add_vertices(IndexHNSW &index_hnsw, - size_t n0, - size_t n, const float *x, - bool verbose, - bool preset_levels = false) { - size_t d = index_hnsw.d; - HNSW & hnsw = index_hnsw.hnsw; - size_t ntotal = n0 + n; - double t0 = getmillisecs(); - if (verbose) { - printf("hnsw_add_vertices: adding %ld elements on top of %ld " - "(preset_levels=%d)\n", - n, n0, int(preset_levels)); - } - - if (n == 0) { - return; - } - - int max_level = hnsw.prepare_level_tab(n, preset_levels); - - if (verbose) { - printf(" max_level = %d\n", max_level); - } - - std::vector locks(ntotal); - for(int i = 0; i < ntotal; i++) - omp_init_lock(&locks[i]); - - // add vectors from highest to lowest level - std::vector hist; - std::vector order(n); - - { // make buckets with vectors of the same level - - // build histogram - for (int i = 0; i < n; i++) { - storage_idx_t pt_id = i + n0; - int pt_level = hnsw.levels[pt_id] - 1; - while (pt_level >= hist.size()) - hist.push_back(0); - hist[pt_level] ++; - } - - // accumulate - std::vector offsets(hist.size() + 1, 0); - for (int i = 0; i < hist.size() - 1; i++) { - offsets[i + 1] = offsets[i] + hist[i]; - } - - // bucket sort - for (int i = 0; i < n; i++) { - storage_idx_t pt_id = i + n0; - int pt_level = hnsw.levels[pt_id] - 1; - order[offsets[pt_level]++] = pt_id; - } - } - - idx_t check_period = InterruptCallback::get_period_hint - (max_level * index_hnsw.d * hnsw.efConstruction); - - { // perform add - RandomGenerator rng2(789); - - int i1 = n; - - for (int pt_level = hist.size() - 1; pt_level >= 0; pt_level--) { - int i0 = i1 - hist[pt_level]; - - if (verbose) { - printf("Adding %d elements at level %d\n", - i1 - i0, pt_level); - } - - // random permutation to get rid of dataset order bias - for (int j = i0; j < i1; j++) - std::swap(order[j], order[j + rng2.rand_int(i1 - j)]); - - bool interrupt = false; - -#pragma omp parallel if(i1 > i0 + 100) - { - VisitedTable vt (ntotal); - - DistanceComputer *dis = - storage_distance_computer (index_hnsw.storage); - ScopeDeleter1 del(dis); - int prev_display = verbose && omp_get_thread_num() == 0 ? 0 : -1; - size_t counter = 0; - -#pragma omp for schedule(dynamic) - for (int i = i0; i < i1; i++) { - storage_idx_t pt_id = order[i]; - dis->set_query (x + (pt_id - n0) * d); - - // cannot break - if (interrupt) { - continue; - } - - hnsw.add_with_locks(*dis, pt_level, pt_id, locks, vt); - - if (prev_display >= 0 && i - i0 > prev_display + 10000) { - prev_display = i - i0; - printf(" %d / %d\r", i - i0, i1 - i0); - fflush(stdout); - } - - if (counter % check_period == 0) { - if (InterruptCallback::is_interrupted ()) { - interrupt = true; - } - } - counter++; - } - - } - if (interrupt) { - FAISS_THROW_MSG ("computation interrupted"); - } - i1 = i0; - } - FAISS_ASSERT(i1 == 0); - } - if (verbose) { - printf("Done in %.3f ms\n", getmillisecs() - t0); - } - - for(int i = 0; i < ntotal; i++) { - omp_destroy_lock(&locks[i]); - } -} - - -} // namespace - - - - -/************************************************************** - * IndexHNSW implementation - **************************************************************/ - -IndexHNSW::IndexHNSW(int d, int M, MetricType metric): - Index(d, metric), - hnsw(M), - own_fields(false), - storage(nullptr), - reconstruct_from_neighbors(nullptr) -{} - -IndexHNSW::IndexHNSW(Index *storage, int M): - Index(storage->d, storage->metric_type), - hnsw(M), - own_fields(false), - storage(storage), - reconstruct_from_neighbors(nullptr) -{} - -IndexHNSW::~IndexHNSW() { - if (own_fields) { - delete storage; - } -} - -void IndexHNSW::train(idx_t n, const float* x) -{ - FAISS_THROW_IF_NOT_MSG(storage, - "Please use IndexHSNWFlat (or variants) instead of IndexHNSW directly"); - // hnsw structure does not require training - storage->train (n, x); - is_trained = true; -} - -void IndexHNSW::search (idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, ConcurrentBitsetPtr bitset) const - -{ - FAISS_THROW_IF_NOT_MSG(storage, - "Please use IndexHSNWFlat (or variants) instead of IndexHNSW directly"); - size_t nreorder = 0; - - idx_t check_period = InterruptCallback::get_period_hint ( - hnsw.max_level * d * hnsw.efSearch); - - for (idx_t i0 = 0; i0 < n; i0 += check_period) { - idx_t i1 = std::min(i0 + check_period, n); - -#pragma omp parallel reduction(+ : nreorder) - { - VisitedTable vt (ntotal); - - DistanceComputer *dis = storage_distance_computer(storage); - ScopeDeleter1 del(dis); - -#pragma omp for - for(idx_t i = i0; i < i1; i++) { - idx_t * idxi = labels + i * k; - float * simi = distances + i * k; - dis->set_query(x + i * d); - - maxheap_heapify (k, simi, idxi); - hnsw.search(*dis, k, idxi, simi, vt); - - maxheap_reorder (k, simi, idxi); - - if (reconstruct_from_neighbors && - reconstruct_from_neighbors->k_reorder != 0) { - int k_reorder = reconstruct_from_neighbors->k_reorder; - if (k_reorder == -1 || k_reorder > k) k_reorder = k; - - nreorder += reconstruct_from_neighbors->compute_distances( - k_reorder, idxi, x + i * d, simi); - - // sort top k_reorder - maxheap_heapify (k_reorder, simi, idxi, simi, idxi, k_reorder); - maxheap_reorder (k_reorder, simi, idxi); - } - - } - - } - InterruptCallback::check (); - } - - if (metric_type == METRIC_INNER_PRODUCT) { - // we need to revert the negated distances - for (size_t i = 0; i < k * n; i++) { - distances[i] = -distances[i]; - } - } - - hnsw_stats.nreorder += nreorder; -} - - -void IndexHNSW::add(idx_t n, const float *x) -{ - FAISS_THROW_IF_NOT_MSG(storage, - "Please use IndexHSNWFlat (or variants) instead of IndexHNSW directly"); - FAISS_THROW_IF_NOT(is_trained); - int n0 = ntotal; - storage->add(n, x); - ntotal = storage->ntotal; - - hnsw_add_vertices (*this, n0, n, x, verbose, - hnsw.levels.size() == ntotal); -} - -void IndexHNSW::reset() -{ - hnsw.reset(); - storage->reset(); - ntotal = 0; -} - -void IndexHNSW::reconstruct (idx_t key, float* recons) const -{ - storage->reconstruct(key, recons); -} - -void IndexHNSW::shrink_level_0_neighbors(int new_size) -{ -#pragma omp parallel - { - DistanceComputer *dis = storage_distance_computer(storage); - ScopeDeleter1 del(dis); - -#pragma omp for - for (idx_t i = 0; i < ntotal; i++) { - - size_t begin, end; - hnsw.neighbor_range(i, 0, &begin, &end); - - std::priority_queue initial_list; - - for (size_t j = begin; j < end; j++) { - int v1 = hnsw.neighbors[j]; - if (v1 < 0) break; - initial_list.emplace(dis->symmetric_dis(i, v1), v1); - - // initial_list.emplace(qdis(v1), v1); - } - - std::vector shrunk_list; - HNSW::shrink_neighbor_list(*dis, initial_list, - shrunk_list, new_size); - - for (size_t j = begin; j < end; j++) { - if (j - begin < shrunk_list.size()) - hnsw.neighbors[j] = shrunk_list[j - begin].id; - else - hnsw.neighbors[j] = -1; - } - } - } - -} - -void IndexHNSW::search_level_0( - idx_t n, const float *x, idx_t k, - const storage_idx_t *nearest, const float *nearest_d, - float *distances, idx_t *labels, int nprobe, - int search_type) const -{ - - storage_idx_t ntotal = hnsw.levels.size(); -#pragma omp parallel - { - DistanceComputer *qdis = storage_distance_computer(storage); - ScopeDeleter1 del(qdis); - - VisitedTable vt (ntotal); - -#pragma omp for - for(idx_t i = 0; i < n; i++) { - idx_t * idxi = labels + i * k; - float * simi = distances + i * k; - - qdis->set_query(x + i * d); - maxheap_heapify (k, simi, idxi); - - if (search_type == 1) { - - int nres = 0; - - for(int j = 0; j < nprobe; j++) { - storage_idx_t cj = nearest[i * nprobe + j]; - - if (cj < 0) break; - - if (vt.get(cj)) continue; - - int candidates_size = std::max(hnsw.efSearch, int(k)); - MinimaxHeap candidates(candidates_size); - - candidates.push(cj, nearest_d[i * nprobe + j]); - - nres = hnsw.search_from_candidates( - *qdis, k, idxi, simi, - candidates, vt, 0, nres - ); - } - } else if (search_type == 2) { - - int candidates_size = std::max(hnsw.efSearch, int(k)); - candidates_size = std::max(candidates_size, nprobe); - - MinimaxHeap candidates(candidates_size); - for(int j = 0; j < nprobe; j++) { - storage_idx_t cj = nearest[i * nprobe + j]; - - if (cj < 0) break; - candidates.push(cj, nearest_d[i * nprobe + j]); - } - hnsw.search_from_candidates( - *qdis, k, idxi, simi, - candidates, vt, 0 - ); - - } - vt.advance(); - - maxheap_reorder (k, simi, idxi); - - } - } - - -} - -void IndexHNSW::init_level_0_from_knngraph( - int k, const float *D, const idx_t *I) -{ - int dest_size = hnsw.nb_neighbors (0); - -#pragma omp parallel for - for (idx_t i = 0; i < ntotal; i++) { - DistanceComputer *qdis = storage_distance_computer(storage); - float vec[d]; - storage->reconstruct(i, vec); - qdis->set_query(vec); - - std::priority_queue initial_list; - - for (size_t j = 0; j < k; j++) { - int v1 = I[i * k + j]; - if (v1 == i) continue; - if (v1 < 0) break; - initial_list.emplace(D[i * k + j], v1); - } - - std::vector shrunk_list; - HNSW::shrink_neighbor_list(*qdis, initial_list, shrunk_list, dest_size); - - size_t begin, end; - hnsw.neighbor_range(i, 0, &begin, &end); - - for (size_t j = begin; j < end; j++) { - if (j - begin < shrunk_list.size()) - hnsw.neighbors[j] = shrunk_list[j - begin].id; - else - hnsw.neighbors[j] = -1; - } - } -} - - - -void IndexHNSW::init_level_0_from_entry_points( - int n, const storage_idx_t *points, - const storage_idx_t *nearests) -{ - - std::vector locks(ntotal); - for(int i = 0; i < ntotal; i++) - omp_init_lock(&locks[i]); - -#pragma omp parallel - { - VisitedTable vt (ntotal); - - DistanceComputer *dis = storage_distance_computer(storage); - ScopeDeleter1 del(dis); - float vec[storage->d]; - -#pragma omp for schedule(dynamic) - for (int i = 0; i < n; i++) { - storage_idx_t pt_id = points[i]; - storage_idx_t nearest = nearests[i]; - storage->reconstruct (pt_id, vec); - dis->set_query (vec); - - hnsw.add_links_starting_from(*dis, pt_id, - nearest, (*dis)(nearest), - 0, locks.data(), vt); - - if (verbose && i % 10000 == 0) { - printf(" %d / %d\r", i, n); - fflush(stdout); - } - } - } - if (verbose) { - printf("\n"); - } - - for(int i = 0; i < ntotal; i++) - omp_destroy_lock(&locks[i]); -} - -void IndexHNSW::reorder_links() -{ - int M = hnsw.nb_neighbors(0); - -#pragma omp parallel - { - std::vector distances (M); - std::vector order (M); - std::vector tmp (M); - DistanceComputer *dis = storage_distance_computer(storage); - ScopeDeleter1 del(dis); - -#pragma omp for - for(storage_idx_t i = 0; i < ntotal; i++) { - - size_t begin, end; - hnsw.neighbor_range(i, 0, &begin, &end); - - for (size_t j = begin; j < end; j++) { - storage_idx_t nj = hnsw.neighbors[j]; - if (nj < 0) { - end = j; - break; - } - distances[j - begin] = dis->symmetric_dis(i, nj); - tmp [j - begin] = nj; - } - - fvec_argsort (end - begin, distances.data(), order.data()); - for (size_t j = begin; j < end; j++) { - hnsw.neighbors[j] = tmp[order[j - begin]]; - } - } - - } -} - - -void IndexHNSW::link_singletons() -{ - printf("search for singletons\n"); - - std::vector seen(ntotal); - - for (size_t i = 0; i < ntotal; i++) { - size_t begin, end; - hnsw.neighbor_range(i, 0, &begin, &end); - for (size_t j = begin; j < end; j++) { - storage_idx_t ni = hnsw.neighbors[j]; - if (ni >= 0) seen[ni] = true; - } - } - - int n_sing = 0, n_sing_l1 = 0; - std::vector singletons; - for (storage_idx_t i = 0; i < ntotal; i++) { - if (!seen[i]) { - singletons.push_back(i); - n_sing++; - if (hnsw.levels[i] > 1) - n_sing_l1++; - } - } - - printf(" Found %d / %ld singletons (%d appear in a level above)\n", - n_sing, ntotal, n_sing_l1); - - std::vectorrecons(singletons.size() * d); - for (int i = 0; i < singletons.size(); i++) { - - FAISS_ASSERT(!"not implemented"); - - } - - -} - - -/************************************************************** - * ReconstructFromNeighbors implementation - **************************************************************/ - - -ReconstructFromNeighbors::ReconstructFromNeighbors( - const IndexHNSW & index, size_t k, size_t nsq): - index(index), k(k), nsq(nsq) { - M = index.hnsw.nb_neighbors(0); - FAISS_ASSERT(k <= 256); - code_size = k == 1 ? 0 : nsq; - ntotal = 0; - d = index.d; - FAISS_ASSERT(d % nsq == 0); - dsub = d / nsq; - k_reorder = -1; -} - -void ReconstructFromNeighbors::reconstruct(storage_idx_t i, float *x, float *tmp) const -{ - - - const HNSW & hnsw = index.hnsw; - size_t begin, end; - hnsw.neighbor_range(i, 0, &begin, &end); - - if (k == 1 || nsq == 1) { - const float * beta; - if (k == 1) { - beta = codebook.data(); - } else { - int idx = codes[i]; - beta = codebook.data() + idx * (M + 1); - } - - float w0 = beta[0]; // weight of image itself - index.storage->reconstruct(i, tmp); - - for (int l = 0; l < d; l++) - x[l] = w0 * tmp[l]; - - for (size_t j = begin; j < end; j++) { - - storage_idx_t ji = hnsw.neighbors[j]; - if (ji < 0) ji = i; - float w = beta[j - begin + 1]; - index.storage->reconstruct(ji, tmp); - for (int l = 0; l < d; l++) - x[l] += w * tmp[l]; - } - } else if (nsq == 2) { - int idx0 = codes[2 * i]; - int idx1 = codes[2 * i + 1]; - - const float *beta0 = codebook.data() + idx0 * (M + 1); - const float *beta1 = codebook.data() + (idx1 + k) * (M + 1); - - index.storage->reconstruct(i, tmp); - - float w0; - - w0 = beta0[0]; - for (int l = 0; l < dsub; l++) - x[l] = w0 * tmp[l]; - - w0 = beta1[0]; - for (int l = dsub; l < d; l++) - x[l] = w0 * tmp[l]; - - for (size_t j = begin; j < end; j++) { - storage_idx_t ji = hnsw.neighbors[j]; - if (ji < 0) ji = i; - index.storage->reconstruct(ji, tmp); - float w; - w = beta0[j - begin + 1]; - for (int l = 0; l < dsub; l++) - x[l] += w * tmp[l]; - - w = beta1[j - begin + 1]; - for (int l = dsub; l < d; l++) - x[l] += w * tmp[l]; - } - } else { - const float *betas[nsq]; - { - const float *b = codebook.data(); - const uint8_t *c = &codes[i * code_size]; - for (int sq = 0; sq < nsq; sq++) { - betas[sq] = b + (*c++) * (M + 1); - b += (M + 1) * k; - } - } - - index.storage->reconstruct(i, tmp); - { - int d0 = 0; - for (int sq = 0; sq < nsq; sq++) { - float w = *(betas[sq]++); - int d1 = d0 + dsub; - for (int l = d0; l < d1; l++) { - x[l] = w * tmp[l]; - } - d0 = d1; - } - } - - for (size_t j = begin; j < end; j++) { - storage_idx_t ji = hnsw.neighbors[j]; - if (ji < 0) ji = i; - - index.storage->reconstruct(ji, tmp); - int d0 = 0; - for (int sq = 0; sq < nsq; sq++) { - float w = *(betas[sq]++); - int d1 = d0 + dsub; - for (int l = d0; l < d1; l++) { - x[l] += w * tmp[l]; - } - d0 = d1; - } - } - } -} - -void ReconstructFromNeighbors::reconstruct_n(storage_idx_t n0, - storage_idx_t ni, - float *x) const -{ -#pragma omp parallel - { - std::vector tmp(index.d); -#pragma omp for - for (storage_idx_t i = 0; i < ni; i++) { - reconstruct(n0 + i, x + i * index.d, tmp.data()); - } - } -} - -size_t ReconstructFromNeighbors::compute_distances( - size_t n, const idx_t *shortlist, - const float *query, float *distances) const -{ - std::vector tmp(2 * index.d); - size_t ncomp = 0; - for (int i = 0; i < n; i++) { - if (shortlist[i] < 0) break; - reconstruct(shortlist[i], tmp.data(), tmp.data() + index.d); - distances[i] = fvec_L2sqr(query, tmp.data(), index.d); - ncomp++; - } - return ncomp; -} - -void ReconstructFromNeighbors::get_neighbor_table(storage_idx_t i, float *tmp1) const -{ - const HNSW & hnsw = index.hnsw; - size_t begin, end; - hnsw.neighbor_range(i, 0, &begin, &end); - size_t d = index.d; - - index.storage->reconstruct(i, tmp1); - - for (size_t j = begin; j < end; j++) { - storage_idx_t ji = hnsw.neighbors[j]; - if (ji < 0) ji = i; - index.storage->reconstruct(ji, tmp1 + (j - begin + 1) * d); - } - -} - - -/// called by add_codes -void ReconstructFromNeighbors::estimate_code( - const float *x, storage_idx_t i, uint8_t *code) const -{ - - // fill in tmp table with the neighbor values - float *tmp1 = new float[d * (M + 1) + (d * k)]; - float *tmp2 = tmp1 + d * (M + 1); - ScopeDeleter del(tmp1); - - // collect coordinates of base - get_neighbor_table (i, tmp1); - - for (size_t sq = 0; sq < nsq; sq++) { - int d0 = sq * dsub; - - { - FINTEGER ki = k, di = d, m1 = M + 1; - FINTEGER dsubi = dsub; - float zero = 0, one = 1; - - sgemm_ ("N", "N", &dsubi, &ki, &m1, &one, - tmp1 + d0, &di, - codebook.data() + sq * (m1 * k), &m1, - &zero, tmp2, &dsubi); - } - - float min = HUGE_VAL; - int argmin = -1; - for (size_t j = 0; j < k; j++) { - float dis = fvec_L2sqr(x + d0, tmp2 + j * dsub, dsub); - if (dis < min) { - min = dis; - argmin = j; - } - } - code[sq] = argmin; - } - -} - -void ReconstructFromNeighbors::add_codes(size_t n, const float *x) -{ - if (k == 1) { // nothing to encode - ntotal += n; - return; - } - codes.resize(codes.size() + code_size * n); -#pragma omp parallel for - for (int i = 0; i < n; i++) { - estimate_code(x + i * index.d, ntotal + i, - codes.data() + (ntotal + i) * code_size); - } - ntotal += n; - FAISS_ASSERT (codes.size() == ntotal * code_size); -} - - -/************************************************************** - * IndexHNSWFlat implementation - **************************************************************/ - - -IndexHNSWFlat::IndexHNSWFlat() -{ - is_trained = true; -} - -IndexHNSWFlat::IndexHNSWFlat(int d, int M, MetricType metric): - IndexHNSW(new IndexFlat(d, metric), M) -{ - own_fields = true; - is_trained = true; -} - - -/************************************************************** - * IndexHNSWPQ implementation - **************************************************************/ - - -IndexHNSWPQ::IndexHNSWPQ() {} - -IndexHNSWPQ::IndexHNSWPQ(int d, int pq_m, int M): - IndexHNSW(new IndexPQ(d, pq_m, 8), M) -{ - own_fields = true; - is_trained = false; -} - -void IndexHNSWPQ::train(idx_t n, const float* x) -{ - IndexHNSW::train (n, x); - (dynamic_cast (storage))->pq.compute_sdc_table(); -} - - -/************************************************************** - * IndexHNSWSQ implementation - **************************************************************/ - - -IndexHNSWSQ::IndexHNSWSQ(int d, QuantizerType qtype, int M, - MetricType metric): - IndexHNSW (new IndexScalarQuantizer (d, qtype, metric), M) -{ - is_trained = false; - own_fields = true; -} - -IndexHNSWSQ::IndexHNSWSQ() {} - - -/************************************************************** - * IndexHNSW2Level implementation - **************************************************************/ - - -IndexHNSW2Level::IndexHNSW2Level(Index *quantizer, size_t nlist, int m_pq, int M): - IndexHNSW (new Index2Layer (quantizer, nlist, m_pq), M) -{ - own_fields = true; - is_trained = false; -} - -IndexHNSW2Level::IndexHNSW2Level() {} - - -namespace { - - -// same as search_from_candidates but uses v -// visno -> is in result list -// visno + 1 -> in result list + in candidates -int search_from_candidates_2(const HNSW & hnsw, - DistanceComputer & qdis, int k, - idx_t *I, float * D, - MinimaxHeap &candidates, - VisitedTable &vt, - int level, int nres_in = 0) -{ - int nres = nres_in; - int ndis = 0; - for (int i = 0; i < candidates.size(); i++) { - idx_t v1 = candidates.ids[i]; - FAISS_ASSERT(v1 >= 0); - vt.visited[v1] = vt.visno + 1; - } - - int nstep = 0; - - while (candidates.size() > 0) { - float d0 = 0; - int v0 = candidates.pop_min(&d0); - - size_t begin, end; - hnsw.neighbor_range(v0, level, &begin, &end); - - for (size_t j = begin; j < end; j++) { - int v1 = hnsw.neighbors[j]; - if (v1 < 0) break; - if (vt.visited[v1] == vt.visno + 1) { - // nothing to do - } else { - ndis++; - float d = qdis(v1); - candidates.push(v1, d); - - // never seen before --> add to heap - if (vt.visited[v1] < vt.visno) { - if (nres < k) { - faiss::maxheap_push (++nres, D, I, d, v1); - } else if (d < D[0]) { - faiss::maxheap_pop (nres--, D, I); - faiss::maxheap_push (++nres, D, I, d, v1); - } - } - vt.visited[v1] = vt.visno + 1; - } - } - - nstep++; - if (nstep > hnsw.efSearch) { - break; - } - } - - if (level == 0) { -#pragma omp critical - { - hnsw_stats.n1 ++; - if (candidates.size() == 0) - hnsw_stats.n2 ++; - } - } - - - return nres; -} - - -} // namespace - -void IndexHNSW2Level::search (idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, ConcurrentBitsetPtr bitset) const -{ - if (dynamic_cast(storage)) { - IndexHNSW::search (n, x, k, distances, labels); - - } else { // "mixed" search - - const IndexIVFPQ *index_ivfpq = - dynamic_cast(storage); - - int nprobe = index_ivfpq->nprobe; - - std::unique_ptr coarse_assign(new idx_t[n * nprobe]); - std::unique_ptr coarse_dis(new float[n * nprobe]); - - index_ivfpq->quantizer->search (n, x, nprobe, coarse_dis.get(), - coarse_assign.get()); - - index_ivfpq->search_preassigned (n, x, k, coarse_assign.get(), - coarse_dis.get(), distances, labels, - false); - -#pragma omp parallel - { - VisitedTable vt (ntotal); - DistanceComputer *dis = storage_distance_computer(storage); - ScopeDeleter1 del(dis); - - int candidates_size = hnsw.upper_beam; - MinimaxHeap candidates(candidates_size); - -#pragma omp for - for(idx_t i = 0; i < n; i++) { - idx_t * idxi = labels + i * k; - float * simi = distances + i * k; - dis->set_query(x + i * d); - - // mark all inverted list elements as visited - - for (int j = 0; j < nprobe; j++) { - idx_t key = coarse_assign[j + i * nprobe]; - if (key < 0) break; - size_t list_length = index_ivfpq->get_list_size (key); - const idx_t * ids = index_ivfpq->invlists->get_ids (key); - - for (int jj = 0; jj < list_length; jj++) { - vt.set (ids[jj]); - } - } - - candidates.clear(); - // copy the upper_beam elements to candidates list - - int search_policy = 2; - - if (search_policy == 1) { - - for (int j = 0 ; j < hnsw.upper_beam && j < k; j++) { - if (idxi[j] < 0) break; - candidates.push (idxi[j], simi[j]); - // search_from_candidates adds them back - idxi[j] = -1; - simi[j] = HUGE_VAL; - } - - // reorder from sorted to heap - maxheap_heapify (k, simi, idxi, simi, idxi, k); - - hnsw.search_from_candidates( - *dis, k, idxi, simi, - candidates, vt, 0, k - ); - - vt.advance(); - - } else if (search_policy == 2) { - - for (int j = 0 ; j < hnsw.upper_beam && j < k; j++) { - if (idxi[j] < 0) break; - candidates.push (idxi[j], simi[j]); - } - - // reorder from sorted to heap - maxheap_heapify (k, simi, idxi, simi, idxi, k); - - search_from_candidates_2 ( - hnsw, *dis, k, idxi, simi, - candidates, vt, 0, k); - vt.advance (); - vt.advance (); - - } - - maxheap_reorder (k, simi, idxi); - } - } - } - - -} - - -void IndexHNSW2Level::flip_to_ivf () -{ - Index2Layer *storage2l = - dynamic_cast(storage); - - FAISS_THROW_IF_NOT (storage2l); - - IndexIVFPQ * index_ivfpq = - new IndexIVFPQ (storage2l->q1.quantizer, - d, storage2l->q1.nlist, - storage2l->pq.M, 8); - index_ivfpq->pq = storage2l->pq; - index_ivfpq->is_trained = storage2l->is_trained; - index_ivfpq->precompute_table(); - index_ivfpq->own_fields = storage2l->q1.own_fields; - storage2l->transfer_to_IVFPQ(*index_ivfpq); - index_ivfpq->make_direct_map (true); - - storage = index_ivfpq; - delete storage2l; - -} - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexHNSW.h b/core/src/index/thirdparty/faiss/IndexHNSW.h deleted file mode 100644 index a8cb10512f..0000000000 --- a/core/src/index/thirdparty/faiss/IndexHNSW.h +++ /dev/null @@ -1,171 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#pragma once - -#include - -#include -#include -#include -#include -#include - - -namespace faiss { - -struct IndexHNSW; - -struct ReconstructFromNeighbors { - typedef Index::idx_t idx_t; - typedef HNSW::storage_idx_t storage_idx_t; - - const IndexHNSW & index; - size_t M; // number of neighbors - size_t k; // number of codebook entries - size_t nsq; // number of subvectors - size_t code_size; - int k_reorder; // nb to reorder. -1 = all - - std::vector codebook; // size nsq * k * (M + 1) - - std::vector codes; // size ntotal * code_size - size_t ntotal; - size_t d, dsub; // derived values - - explicit ReconstructFromNeighbors(const IndexHNSW& index, - size_t k=256, size_t nsq=1); - - /// codes must be added in the correct order and the IndexHNSW - /// must be populated and sorted - void add_codes(size_t n, const float *x); - - size_t compute_distances(size_t n, const idx_t *shortlist, - const float *query, float *distances) const; - - /// called by add_codes - void estimate_code(const float *x, storage_idx_t i, uint8_t *code) const; - - /// called by compute_distances - void reconstruct(storage_idx_t i, float *x, float *tmp) const; - - void reconstruct_n(storage_idx_t n0, storage_idx_t ni, float *x) const; - - /// get the M+1 -by-d table for neighbor coordinates for vector i - void get_neighbor_table(storage_idx_t i, float *out) const; - -}; - - -/** The HNSW index is a normal random-access index with a HNSW - * link structure built on top */ - -struct IndexHNSW : Index { - - typedef HNSW::storage_idx_t storage_idx_t; - - // the link strcuture - HNSW hnsw; - - // the sequential storage - bool own_fields; - Index *storage; - - ReconstructFromNeighbors *reconstruct_from_neighbors; - - explicit IndexHNSW (int d = 0, int M = 32, MetricType metric = METRIC_L2); - explicit IndexHNSW (Index *storage, int M = 32); - - ~IndexHNSW() override; - - void add(idx_t n, const float *x) override; - - /// Trains the storage if needed - void train(idx_t n, const float* x) override; - - /// entry point for search - void search (idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - void reconstruct(idx_t key, float* recons) const override; - - void reset () override; - - void shrink_level_0_neighbors(int size); - - /** Perform search only on level 0, given the starting points for - * each vertex. - * - * @param search_type 1:perform one search per nprobe, 2: enqueue - * all entry points - */ - void search_level_0(idx_t n, const float *x, idx_t k, - const storage_idx_t *nearest, const float *nearest_d, - float *distances, idx_t *labels, int nprobe = 1, - int search_type = 1) const; - - /// alternative graph building - void init_level_0_from_knngraph( - int k, const float *D, const idx_t *I); - - /// alternative graph building - void init_level_0_from_entry_points( - int npt, const storage_idx_t *points, - const storage_idx_t *nearests); - - // reorder links from nearest to farthest - void reorder_links(); - - void link_singletons(); -}; - - -/** Flat index topped with with a HNSW structure to access elements - * more efficiently. - */ - -struct IndexHNSWFlat : IndexHNSW { - IndexHNSWFlat(); - IndexHNSWFlat(int d, int M, MetricType metric = METRIC_L2); -}; - -/** PQ index topped with with a HNSW structure to access elements - * more efficiently. - */ -struct IndexHNSWPQ : IndexHNSW { - IndexHNSWPQ(); - IndexHNSWPQ(int d, int pq_m, int M); - void train(idx_t n, const float* x) override; -}; - -/** SQ index topped with with a HNSW structure to access elements - * more efficiently. - */ -struct IndexHNSWSQ : IndexHNSW { - IndexHNSWSQ(); - IndexHNSWSQ(int d, QuantizerType qtype, int M, MetricType metric = METRIC_L2); -}; - -/** 2-level code structure with fast random access - */ -struct IndexHNSW2Level : IndexHNSW { - IndexHNSW2Level(); - IndexHNSW2Level(Index *quantizer, size_t nlist, int m_pq, int M); - - void flip_to_ivf(); - - /// entry point for search - void search (idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, - ConcurrentBitsetPtr bitset = nullptr) const override; -}; - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexIVF.cpp b/core/src/index/thirdparty/faiss/IndexIVF.cpp deleted file mode 100644 index 7342374327..0000000000 --- a/core/src/index/thirdparty/faiss/IndexIVF.cpp +++ /dev/null @@ -1,1013 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - - -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -namespace faiss { - -using ScopedIds = InvertedLists::ScopedIds; -using ScopedCodes = InvertedLists::ScopedCodes; - -/***************************************** - * Level1Quantizer implementation - ******************************************/ - - -Level1Quantizer::Level1Quantizer (Index * quantizer, size_t nlist): - quantizer (quantizer), - nlist (nlist), - quantizer_trains_alone (0), - own_fields (false), - clustering_index (nullptr) -{ - // here we set a low # iterations because this is typically used - // for large clusterings (nb this is not used for the MultiIndex, - // for which quantizer_trains_alone = true) - cp.niter = 10; -} - -Level1Quantizer::Level1Quantizer (): - quantizer (nullptr), - nlist (0), - quantizer_trains_alone (0), own_fields (false), - clustering_index (nullptr) -{} - -Level1Quantizer::~Level1Quantizer () -{ - if (own_fields) { - if(quantizer == quantizer_backup) { - if(quantizer != nullptr) { - delete quantizer; - } - } else { - if(quantizer != nullptr) { - delete quantizer; - } - - if(quantizer_backup != nullptr) { - delete quantizer_backup; - } - } - quantizer = nullptr; - quantizer_backup = nullptr; - } -} - -void Level1Quantizer::train_q1 (size_t n, const float *x, bool verbose, MetricType metric_type) -{ - size_t d = quantizer->d; - if (quantizer->is_trained && (quantizer->ntotal == nlist)) { - if (verbose) - printf ("IVF quantizer does not need training.\n"); - } else if (quantizer_trains_alone == 1) { - if (verbose) - printf ("IVF quantizer trains alone...\n"); - quantizer->train (n, x); - quantizer->verbose = verbose; - FAISS_THROW_IF_NOT_MSG (quantizer->ntotal == nlist, - "nlist not consistent with quantizer size"); - } else if (quantizer_trains_alone == 0) { - if (verbose) - printf ("Training level-1 quantizer on %ld vectors in %ldD\n", - n, d); - - Clustering clus (d, nlist, cp); - quantizer->reset(); - if (clustering_index) { - clus.train (n, x, *clustering_index); - quantizer->add (nlist, clus.centroids.data()); - } else { - clus.train (n, x, *quantizer); - } - quantizer->is_trained = true; - } else if (quantizer_trains_alone == 2) { - if (verbose) - printf ( - "Training L2 quantizer on %ld vectors in %ldD%s\n", - n, d, - clustering_index ? "(user provided index)" : ""); - FAISS_THROW_IF_NOT (metric_type == METRIC_L2); - Clustering clus (d, nlist, cp); - if (!clustering_index) { - IndexFlatL2 assigner (d); - clus.train(n, x, assigner); - } else { - clus.train(n, x, *clustering_index); - } - if (verbose) - printf ("Adding centroids to quantizer\n"); - quantizer->add (nlist, clus.centroids.data()); - } -} - -size_t Level1Quantizer::coarse_code_size () const -{ - size_t nl = nlist - 1; - size_t nbyte = 0; - while (nl > 0) { - nbyte ++; - nl >>= 8; - } - return nbyte; -} - -void Level1Quantizer::encode_listno (Index::idx_t list_no, uint8_t *code) const -{ - // little endian - size_t nl = nlist - 1; - while (nl > 0) { - *code++ = list_no & 0xff; - list_no >>= 8; - nl >>= 8; - } -} - -Index::idx_t Level1Quantizer::decode_listno (const uint8_t *code) const -{ - size_t nl = nlist - 1; - int64_t list_no = 0; - int nbit = 0; - while (nl > 0) { - list_no |= int64_t(*code++) << nbit; - nbit += 8; - nl >>= 8; - } - FAISS_THROW_IF_NOT (list_no >= 0 && list_no < nlist); - return list_no; -} - - - -/***************************************** - * IndexIVF implementation - ******************************************/ - - -IndexIVF::IndexIVF (Index * quantizer, size_t d, - size_t nlist, size_t code_size, - MetricType metric): - Index (d, metric), - Level1Quantizer (quantizer, nlist), - invlists (new ArrayInvertedLists (nlist, code_size)), - own_invlists (true), - code_size (code_size), - nprobe (1), - max_codes (0), - parallel_mode (0) -{ - FAISS_THROW_IF_NOT (d == quantizer->d); - is_trained = quantizer->is_trained && (quantizer->ntotal == nlist); - // Spherical by default if the metric is inner_product - if (metric_type == METRIC_INNER_PRODUCT) { - cp.spherical = true; - } - -} - -IndexIVF::IndexIVF (): - invlists (nullptr), own_invlists (false), - code_size (0), - nprobe (1), max_codes (0), parallel_mode (0) -{} - -void IndexIVF::add (idx_t n, const float * x) -{ - add_with_ids (n, x, nullptr); -} - - -void IndexIVF::add_with_ids (idx_t n, const float * x, const idx_t *xids) -{ - // do some blocking to avoid excessive allocs - idx_t bs = 65536; - if (n > bs) { - for (idx_t i0 = 0; i0 < n; i0 += bs) { - idx_t i1 = std::min (n, i0 + bs); - if (verbose) { - printf(" IndexIVF::add_with_ids %ld:%ld\n", i0, i1); - } - add_with_ids (i1 - i0, x + i0 * d, - xids ? xids + i0 : nullptr); - } - return; - } - - FAISS_THROW_IF_NOT (is_trained); - direct_map.check_can_add (xids); - - std::unique_ptr idx(new idx_t[n]); - quantizer->assign (n, x, idx.get()); - size_t nadd = 0, nminus1 = 0; - - for (size_t i = 0; i < n; i++) { - if (idx[i] < 0) nminus1++; - } - - std::unique_ptr flat_codes(new uint8_t [n * code_size]); - encode_vectors (n, x, idx.get(), flat_codes.get()); - - DirectMapAdd dm_adder(direct_map, n, xids); - -#pragma omp parallel reduction(+: nadd) - { - int nt = omp_get_num_threads(); - int rank = omp_get_thread_num(); - - // each thread takes care of a subset of lists - for (size_t i = 0; i < n; i++) { - idx_t list_no = idx [i]; - if (list_no >= 0 && list_no % nt == rank) { - idx_t id = xids ? xids[i] : ntotal + i; - size_t ofs = invlists->add_entry ( - list_no, id, - flat_codes.get() + i * code_size - ); - - dm_adder.add (i, list_no, ofs); - - nadd++; - } else if (rank == 0 && list_no == -1) { - dm_adder.add (i, -1, 0); - } - } - } - - - if (verbose) { - printf(" added %ld / %ld vectors (%ld -1s)\n", nadd, n, nminus1); - } - - ntotal += n; -} - -void IndexIVF::to_readonly() { - if (is_readonly()) return; - auto readonly_lists = this->invlists->to_readonly(); - if (!readonly_lists) return; - this->replace_invlists(readonly_lists, true); -} - -bool IndexIVF::is_readonly() const { - return this->invlists->is_readonly(); -} - -void IndexIVF::backup_quantizer() { - this->quantizer_backup = quantizer; -} - -void IndexIVF::restore_quantizer() { - if(this->quantizer_backup != nullptr) { - quantizer = this->quantizer_backup; - } -} - -void IndexIVF::make_direct_map (bool b) -{ - if (b) { - direct_map.set_type (DirectMap::Array, invlists, ntotal); - } else { - direct_map.set_type (DirectMap::NoMap, invlists, ntotal); - } -} - -void IndexIVF::set_direct_map_type (DirectMap::Type type) -{ - direct_map.set_type (type, invlists, ntotal); -} - - -void IndexIVF::search (idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, - ConcurrentBitsetPtr bitset) const -{ - std::unique_ptr idx(new idx_t[n * nprobe]); - std::unique_ptr coarse_dis(new float[n * nprobe]); - - double t0 = getmillisecs(); - quantizer->search (n, x, nprobe, coarse_dis.get(), idx.get()); - indexIVF_stats.quantization_time += getmillisecs() - t0; - - t0 = getmillisecs(); - invlists->prefetch_lists (idx.get(), n * nprobe); - - search_preassigned (n, x, k, idx.get(), coarse_dis.get(), - distances, labels, false, nullptr, bitset); - indexIVF_stats.search_time += getmillisecs() - t0; - - // string - if (LOG_DEBUG_) { - auto ids = idx.get(); - for (size_t i = 0; i < n; i++) { - std::stringstream ss; - ss << "Query #" << i << ", nprobe list: "; - for (size_t j = 0; j < nprobe; j++) { - if (j != 0) { - ss << ","; - } - ss << ids[i * nprobe + j]; - } - (*LOG_DEBUG_)(ss.str()); - } - } -} - -#if 0 -void IndexIVF::get_vector_by_id (idx_t n, const idx_t *xid, float *x, ConcurrentBitsetPtr bitset) { - make_direct_map(true); - - /* only get vector by 1 id */ - FAISS_ASSERT(n == 1); - if (!bitset || !bitset->test(xid[0])) { - reconstruct(xid[0], x + 0 * d); - } else { - memset(x, UINT8_MAX, d * sizeof(float)); - } -} - -void IndexIVF::search_by_id (idx_t n, const idx_t *xid, idx_t k, float *distances, idx_t *labels, - ConcurrentBitsetPtr bitset) { - make_direct_map(true); - - auto x = new float[n * d]; - for (idx_t i = 0; i < n; ++i) { - reconstruct(xid[i], x + i * d); - } - - search(n, x, k, distances, labels, bitset); - delete []x; -} -#endif - -void IndexIVF::search_preassigned (idx_t n, const float *x, idx_t k, - const idx_t *keys, - const float *coarse_dis , - float *distances, idx_t *labels, - bool store_pairs, - const IVFSearchParameters *params, - ConcurrentBitsetPtr bitset) const -{ - long nprobe = params ? params->nprobe : this->nprobe; - long max_codes = params ? params->max_codes : this->max_codes; - - size_t nlistv = 0, ndis = 0, nheap = 0; - - using HeapForIP = CMin; - using HeapForL2 = CMax; - - bool interrupt = false; - - int pmode = this->parallel_mode & ~PARALLEL_MODE_NO_HEAP_INIT; - bool do_heap_init = !(this->parallel_mode & PARALLEL_MODE_NO_HEAP_INIT); - - // don't start parallel section if single query - bool do_parallel = - pmode == 0 ? n > 1 : - pmode == 1 ? nprobe > 1 : - nprobe * n > 1; - -#pragma omp parallel if(do_parallel) reduction(+: nlistv, ndis, nheap) - { - InvertedListScanner *scanner = get_InvertedListScanner(store_pairs); - ScopeDeleter1 del(scanner); - - /***************************************************** - * Depending on parallel_mode, there are two possible ways - * to organize the search. Here we define local functions - * that are in common between the two - ******************************************************/ - - // intialize + reorder a result heap - - auto init_result = [&](float *simi, idx_t *idxi) { - if (!do_heap_init) return; - if (metric_type == METRIC_INNER_PRODUCT) { - heap_heapify (k, simi, idxi); - } else { - heap_heapify (k, simi, idxi); - } - }; - - auto reorder_result = [&] (float *simi, idx_t *idxi) { - if (!do_heap_init) return; - if (metric_type == METRIC_INNER_PRODUCT) { - heap_reorder (k, simi, idxi); - } else { - heap_reorder (k, simi, idxi); - } - }; - - // single list scan using the current scanner (with query - // set porperly) and storing results in simi and idxi - auto scan_one_list = [&] (idx_t key, float coarse_dis_i, - float *simi, idx_t *idxi, - ConcurrentBitsetPtr bitset) { - - if (key < 0) { - // not enough centroids for multiprobe - return (size_t)0; - } - FAISS_THROW_IF_NOT_FMT (key < (idx_t) nlist, - "Invalid key=%ld nlist=%ld\n", - key, nlist); - - size_t list_size = invlists->list_size(key); - - // don't waste time on empty lists - if (list_size == 0) { - return (size_t)0; - } - - scanner->set_list (key, coarse_dis_i); - - nlistv++; - - InvertedLists::ScopedCodes scodes (invlists, key); - - std::unique_ptr sids; - const Index::idx_t * ids = nullptr; - - if (!store_pairs) { - sids.reset (new InvertedLists::ScopedIds (invlists, key)); - ids = sids->get(); - } - - nheap += scanner->scan_codes (list_size, scodes.get(), - ids, simi, idxi, k, bitset); - - return list_size; - }; - - /**************************************************** - * Actual loops, depending on parallel_mode - ****************************************************/ - - if (pmode == 0) { - -#pragma omp for - for (size_t i = 0; i < n; i++) { - - if (interrupt) { - continue; - } - - // loop over queries - scanner->set_query (x + i * d); - float * simi = distances + i * k; - idx_t * idxi = labels + i * k; - - init_result (simi, idxi); - - long nscan = 0; - - // loop over probes - for (size_t ik = 0; ik < nprobe; ik++) { - - nscan += scan_one_list ( - keys [i * nprobe + ik], - coarse_dis[i * nprobe + ik], - simi, idxi, bitset - ); - - if (max_codes && nscan >= max_codes) { - break; - } - } - - ndis += nscan; - reorder_result (simi, idxi); - - if (InterruptCallback::is_interrupted ()) { - interrupt = true; - } - - } // parallel for - } else if (pmode == 1) { - std::vector local_idx (k); - std::vector local_dis (k); - - for (size_t i = 0; i < n; i++) { - scanner->set_query (x + i * d); - init_result (local_dis.data(), local_idx.data()); - -#pragma omp for schedule(dynamic) - for (size_t ik = 0; ik < nprobe; ik++) { - ndis += scan_one_list - (keys [i * nprobe + ik], - coarse_dis[i * nprobe + ik], - local_dis.data(), local_idx.data(), bitset); - - // can't do the test on max_codes - } - // merge thread-local results - - float * simi = distances + i * k; - idx_t * idxi = labels + i * k; -#pragma omp single - init_result (simi, idxi); - -#pragma omp barrier -#pragma omp critical - { - if (metric_type == METRIC_INNER_PRODUCT) { - heap_addn - (k, simi, idxi, - local_dis.data(), local_idx.data(), k); - } else { - heap_addn - (k, simi, idxi, - local_dis.data(), local_idx.data(), k); - } - } -#pragma omp barrier -#pragma omp single - reorder_result (simi, idxi); - } - } else { - FAISS_THROW_FMT ("parallel_mode %d not supported\n", - pmode); - } - } // parallel section - - if (interrupt) { - FAISS_THROW_MSG ("computation interrupted"); - } - - indexIVF_stats.nq += n; - indexIVF_stats.nlist += nlistv; - indexIVF_stats.ndis += ndis; - indexIVF_stats.nheap_updates += nheap; - -} - - - - -void IndexIVF::range_search (idx_t nx, const float *x, float radius, - RangeSearchResult *result, - ConcurrentBitsetPtr bitset) const -{ - std::unique_ptr keys (new idx_t[nx * nprobe]); - std::unique_ptr coarse_dis (new float[nx * nprobe]); - - double t0 = getmillisecs(); - quantizer->search (nx, x, nprobe, coarse_dis.get (), keys.get ()); - indexIVF_stats.quantization_time += getmillisecs() - t0; - - t0 = getmillisecs(); - invlists->prefetch_lists (keys.get(), nx * nprobe); - - range_search_preassigned (nx, x, radius, keys.get (), coarse_dis.get (), - result, bitset); - - indexIVF_stats.search_time += getmillisecs() - t0; -} - -void IndexIVF::range_search_preassigned ( - idx_t nx, const float *x, float radius, - const idx_t *keys, const float *coarse_dis, - RangeSearchResult *result, - ConcurrentBitsetPtr bitset) const -{ - - size_t nlistv = 0, ndis = 0; - bool store_pairs = false; - - std::vector all_pres (omp_get_max_threads()); - -#pragma omp parallel reduction(+: nlistv, ndis) - { - RangeSearchPartialResult pres(result); - std::unique_ptr scanner - (get_InvertedListScanner(store_pairs)); - FAISS_THROW_IF_NOT (scanner.get ()); - all_pres[omp_get_thread_num()] = &pres; - - // prepare the list scanning function - - auto scan_list_func = [&](size_t i, size_t ik, RangeQueryResult &qres) { - - idx_t key = keys[i * nprobe + ik]; /* select the list */ - if (key < 0) return; - FAISS_THROW_IF_NOT_FMT ( - key < (idx_t) nlist, - "Invalid key=%ld at ik=%ld nlist=%ld\n", - key, ik, nlist); - const size_t list_size = invlists->list_size(key); - - if (list_size == 0) return; - - InvertedLists::ScopedCodes scodes (invlists, key); - InvertedLists::ScopedIds ids (invlists, key); - - scanner->set_list (key, coarse_dis[i * nprobe + ik]); - nlistv++; - ndis += list_size; - scanner->scan_codes_range (list_size, scodes.get(), - ids.get(), radius, qres, bitset); - }; - - if (parallel_mode == 0) { - -#pragma omp for - for (size_t i = 0; i < nx; i++) { - scanner->set_query (x + i * d); - - RangeQueryResult & qres = pres.new_result (i); - - for (size_t ik = 0; ik < nprobe; ik++) { - scan_list_func (i, ik, qres); - } - - } - - } else if (parallel_mode == 1) { - - for (size_t i = 0; i < nx; i++) { - scanner->set_query (x + i * d); - - RangeQueryResult & qres = pres.new_result (i); - -#pragma omp for schedule(dynamic) - for (size_t ik = 0; ik < nprobe; ik++) { - scan_list_func (i, ik, qres); - } - } - } else if (parallel_mode == 2) { - std::vector all_qres (nx); - RangeQueryResult *qres = nullptr; - -#pragma omp for schedule(dynamic) - for (size_t iik = 0; iik < nx * nprobe; iik++) { - size_t i = iik / nprobe; - size_t ik = iik % nprobe; - if (qres == nullptr || qres->qno != i) { - FAISS_ASSERT (!qres || i > qres->qno); - qres = &pres.new_result (i); - scanner->set_query (x + i * d); - } - scan_list_func (i, ik, *qres); - } - } else { - FAISS_THROW_FMT ("parallel_mode %d not supported\n", parallel_mode); - } - if (parallel_mode == 0) { - pres.finalize (); - } else { -#pragma omp barrier -#pragma omp single - RangeSearchPartialResult::merge (all_pres, false); -#pragma omp barrier - - } - } - indexIVF_stats.nq += nx; - indexIVF_stats.nlist += nlistv; - indexIVF_stats.ndis += ndis; -} - - -InvertedListScanner *IndexIVF::get_InvertedListScanner ( - bool /*store_pairs*/) const -{ - return nullptr; -} - -void IndexIVF::reconstruct (idx_t key, float* recons) const -{ - idx_t lo = direct_map.get (key); - reconstruct_from_offset (lo_listno(lo), lo_offset(lo), recons); -} - - -void IndexIVF::reconstruct_n (idx_t i0, idx_t ni, float* recons) const -{ - FAISS_THROW_IF_NOT (ni == 0 || (i0 >= 0 && i0 + ni <= ntotal)); - - for (idx_t list_no = 0; list_no < nlist; list_no++) { - size_t list_size = invlists->list_size (list_no); - ScopedIds idlist (invlists, list_no); - - for (idx_t offset = 0; offset < list_size; offset++) { - idx_t id = idlist[offset]; - if (!(id >= i0 && id < i0 + ni)) { - continue; - } - - float* reconstructed = recons + (id - i0) * d; - reconstruct_from_offset (list_no, offset, reconstructed); - } - } -} - - -/* standalone codec interface */ -size_t IndexIVF::sa_code_size () const -{ - size_t coarse_size = coarse_code_size(); - return code_size + coarse_size; -} - -void IndexIVF::sa_encode (idx_t n, const float *x, - uint8_t *bytes) const -{ - FAISS_THROW_IF_NOT (is_trained); - std::unique_ptr idx (new int64_t [n]); - quantizer->assign (n, x, idx.get()); - encode_vectors (n, x, idx.get(), bytes, true); -} - - -void IndexIVF::search_and_reconstruct (idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, - float *recons) const -{ - idx_t * idx = new idx_t [n * nprobe]; - ScopeDeleter del (idx); - float * coarse_dis = new float [n * nprobe]; - ScopeDeleter del2 (coarse_dis); - - quantizer->search (n, x, nprobe, coarse_dis, idx); - - invlists->prefetch_lists (idx, n * nprobe); - - // search_preassigned() with `store_pairs` enabled to obtain the list_no - // and offset into `codes` for reconstruction - search_preassigned (n, x, k, idx, coarse_dis, - distances, labels, true /* store_pairs */); - for (idx_t i = 0; i < n; ++i) { - for (idx_t j = 0; j < k; ++j) { - idx_t ij = i * k + j; - idx_t key = labels[ij]; - float* reconstructed = recons + ij * d; - if (key < 0) { - // Fill with NaNs - memset(reconstructed, -1, sizeof(*reconstructed) * d); - } else { - int list_no = lo_listno (key); - int offset = lo_offset (key); - - // Update label to the actual id - labels[ij] = invlists->get_single_id (list_no, offset); - - reconstruct_from_offset (list_no, offset, reconstructed); - } - } - } -} - -void IndexIVF::reconstruct_from_offset( - int64_t /*list_no*/, - int64_t /*offset*/, - float* /*recons*/) const { - FAISS_THROW_MSG ("reconstruct_from_offset not implemented"); -} - -void IndexIVF::reset () -{ - direct_map.clear (); - invlists->reset (); - ntotal = 0; -} - - -size_t IndexIVF::remove_ids (const IDSelector & sel) -{ - size_t nremove = direct_map.remove_ids (sel, invlists); - ntotal -= nremove; - return nremove; -} - - -void IndexIVF::update_vectors (int n, const idx_t *new_ids, const float *x) -{ - - if (direct_map.type == DirectMap::Hashtable) { - // just remove then add - IDSelectorArray sel(n, new_ids); - size_t nremove = remove_ids (sel); - FAISS_THROW_IF_NOT_MSG (nremove == n, - "did not find all entries to remove"); - add_with_ids (n, x, new_ids); - return; - } - - FAISS_THROW_IF_NOT (direct_map.type == DirectMap::Array); - // here it is more tricky because we don't want to introduce holes - // in continuous range of ids - - FAISS_THROW_IF_NOT (is_trained); - std::vector assign (n); - quantizer->assign (n, x, assign.data()); - - std::vector flat_codes (n * code_size); - encode_vectors (n, x, assign.data(), flat_codes.data()); - - direct_map.update_codes (invlists, n, new_ids, assign.data(), flat_codes.data()); - -} - - - - -void IndexIVF::train (idx_t n, const float *x) -{ - if (verbose) - printf ("Training level-1 quantizer\n"); - - train_q1 (n, x, verbose, metric_type); - - if (verbose) - printf ("Training IVF residual\n"); - - train_residual (n, x); - is_trained = true; - -} - -void IndexIVF::train_residual(idx_t /*n*/, const float* /*x*/) { - if (verbose) - printf("IndexIVF: no residual training\n"); - // does nothing by default -} - - -void IndexIVF::check_compatible_for_merge (const IndexIVF &other) const -{ - // minimal sanity checks - FAISS_THROW_IF_NOT (other.d == d); - FAISS_THROW_IF_NOT (other.nlist == nlist); - FAISS_THROW_IF_NOT (other.code_size == code_size); - FAISS_THROW_IF_NOT_MSG (typeid (*this) == typeid (other), - "can only merge indexes of the same type"); - FAISS_THROW_IF_NOT_MSG (this->direct_map.no() && other.direct_map.no(), - "merge direct_map not implemented"); -} - - -void IndexIVF::merge_from (IndexIVF &other, idx_t add_id) -{ - check_compatible_for_merge (other); - - invlists->merge_from (other.invlists, add_id); - - ntotal += other.ntotal; - other.ntotal = 0; -} - - -void IndexIVF::replace_invlists (InvertedLists *il, bool own) -{ - if (own_invlists) { - delete invlists; - } - // FAISS_THROW_IF_NOT (ntotal == 0); - if (il) { - FAISS_THROW_IF_NOT (il->nlist == nlist && - il->code_size == code_size); - } - invlists = il; - own_invlists = own; -} - - -void IndexIVF::copy_subset_to (IndexIVF & other, int subset_type, - idx_t a1, idx_t a2) const -{ - - FAISS_THROW_IF_NOT (nlist == other.nlist); - FAISS_THROW_IF_NOT (code_size == other.code_size); - FAISS_THROW_IF_NOT (other.direct_map.no()); - FAISS_THROW_IF_NOT_FMT ( - subset_type == 0 || subset_type == 1 || subset_type == 2, - "subset type %d not implemented", subset_type); - - size_t accu_n = 0; - size_t accu_a1 = 0; - size_t accu_a2 = 0; - - InvertedLists *oivf = other.invlists; - - for (idx_t list_no = 0; list_no < nlist; list_no++) { - size_t n = invlists->list_size (list_no); - ScopedIds ids_in (invlists, list_no); - - if (subset_type == 0) { - for (idx_t i = 0; i < n; i++) { - idx_t id = ids_in[i]; - if (a1 <= id && id < a2) { - oivf->add_entry (list_no, - invlists->get_single_id (list_no, i), - ScopedCodes (invlists, list_no, i).get()); - other.ntotal++; - } - } - } else if (subset_type == 1) { - for (idx_t i = 0; i < n; i++) { - idx_t id = ids_in[i]; - if (id % a1 == a2) { - oivf->add_entry (list_no, - invlists->get_single_id (list_no, i), - ScopedCodes (invlists, list_no, i).get()); - other.ntotal++; - } - } - } else if (subset_type == 2) { - // see what is allocated to a1 and to a2 - size_t next_accu_n = accu_n + n; - size_t next_accu_a1 = next_accu_n * a1 / ntotal; - size_t i1 = next_accu_a1 - accu_a1; - size_t next_accu_a2 = next_accu_n * a2 / ntotal; - size_t i2 = next_accu_a2 - accu_a2; - - for (idx_t i = i1; i < i2; i++) { - oivf->add_entry (list_no, - invlists->get_single_id (list_no, i), - ScopedCodes (invlists, list_no, i).get()); - } - - other.ntotal += i2 - i1; - accu_a1 = next_accu_a1; - accu_a2 = next_accu_a2; - } - accu_n += n; - } - FAISS_ASSERT(accu_n == ntotal); - -} - -void -IndexIVF::dump() { - for (auto i = 0; i < invlists->nlist; ++ i) { - auto numVecs = invlists->list_size(i); - auto ids = invlists->get_ids(i); - auto codes = invlists->get_codes(i); - int code_size = invlists->code_size; - - - std::cout << "Bucket ID: " << i << ", with code size: " << code_size << ", vectors number: " << numVecs << std::endl; - if(code_size == 8) { - // int8 types - for (auto j=0; j < numVecs; ++j) { - std::cout << *(ids+j) << ": " << std::endl; - for(int k = 0; k < this->d; ++ k) { - printf("%u ", (uint8_t)(codes[j * d + k])); - } - std::cout << std::endl; - } - } - std::cout << "Bucket End." << std::endl; - } -} - - -IndexIVF::~IndexIVF() -{ - if (own_invlists) { - delete invlists; - } -} - - -void IndexIVFStats::reset() -{ - memset ((void*)this, 0, sizeof (*this)); -} - - -IndexIVFStats indexIVF_stats; - -void InvertedListScanner::scan_codes_range (size_t , - const uint8_t *, - const idx_t *, - float , - RangeQueryResult &, - ConcurrentBitsetPtr) const -{ - FAISS_THROW_MSG ("scan_codes_range not implemented"); -} - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexIVF.h b/core/src/index/thirdparty/faiss/IndexIVF.h deleted file mode 100644 index 744f27f333..0000000000 --- a/core/src/index/thirdparty/faiss/IndexIVF.h +++ /dev/null @@ -1,396 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_INDEX_IVF_H -#define FAISS_INDEX_IVF_H - - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace faiss { - - -/** Encapsulates a quantizer object for the IndexIVF - * - * The class isolates the fields that are independent of the storage - * of the lists (especially training) - */ -struct Level1Quantizer { - Index * quantizer = nullptr; ///< quantizer that maps vectors to inverted lists - Index * quantizer_backup = nullptr; ///< quantizer for backup - size_t nlist; ///< number of possible key values - - /** - * = 0: use the quantizer as index in a kmeans training - * = 1: just pass on the training set to the train() of the quantizer - * = 2: kmeans training on a flat index + add the centroids to the quantizer - */ - char quantizer_trains_alone; - bool own_fields; ///< whether object owns the quantizer - - ClusteringParameters cp; ///< to override default clustering params - Index *clustering_index; ///< to override index used during clustering - - /// Trains the quantizer and calls train_residual to train sub-quantizers - void train_q1 (size_t n, const float *x, bool verbose, - MetricType metric_type); - - - /// compute the number of bytes required to store list ids - size_t coarse_code_size () const; - void encode_listno (Index::idx_t list_no, uint8_t *code) const; - Index::idx_t decode_listno (const uint8_t *code) const; - - Level1Quantizer (Index * quantizer, size_t nlist); - - Level1Quantizer (); - - ~Level1Quantizer (); - -}; - - - -struct IVFSearchParameters { - size_t nprobe; ///< number of probes at query time - size_t max_codes; ///< max nb of codes to visit to do a query - virtual ~IVFSearchParameters () {} -}; - - - -struct InvertedListScanner; - -/** Index based on a inverted file (IVF) - * - * In the inverted file, the quantizer (an Index instance) provides a - * quantization index for each vector to be added. The quantization - * index maps to a list (aka inverted list or posting list), where the - * id of the vector is stored. - * - * The inverted list object is required only after trainng. If none is - * set externally, an ArrayInvertedLists is used automatically. - * - * At search time, the vector to be searched is also quantized, and - * only the list corresponding to the quantization index is - * searched. This speeds up the search by making it - * non-exhaustive. This can be relaxed using multi-probe search: a few - * (nprobe) quantization indices are selected and several inverted - * lists are visited. - * - * Sub-classes implement a post-filtering of the index that refines - * the distance estimation from the query to databse vectors. - */ -struct IndexIVF: Index, Level1Quantizer { - /// Acess to the actual data - InvertedLists *invlists; - bool own_invlists; - - size_t code_size; ///< code size per vector in bytes - - size_t nprobe; ///< number of probes at query time - size_t max_codes; ///< max nb of codes to visit to do a query - - /** Parallel mode determines how queries are parallelized with OpenMP - * - * 0 (default): parallelize over queries - * 1: parallelize over inverted lists - * 2: parallelize over both - * - * PARALLEL_MODE_NO_HEAP_INIT: binary or with the previous to - * prevent the heap to be initialized and finalized - */ - int parallel_mode; - const int PARALLEL_MODE_NO_HEAP_INIT = 1024; - - /** optional map that maps back ids to invlist entries. This - * enables reconstruct() */ - DirectMap direct_map; - - /** The Inverted file takes a quantizer (an Index) on input, - * which implements the function mapping a vector to a list - * identifier. The pointer is borrowed: the quantizer should not - * be deleted while the IndexIVF is in use. - */ - IndexIVF (Index * quantizer, size_t d, - size_t nlist, size_t code_size, - MetricType metric = METRIC_L2); - - void reset() override; - - /// Trains the quantizer and calls train_residual to train sub-quantizers - void train(idx_t n, const float* x) override; - - /// Calls add_with_ids with NULL ids - void add(idx_t n, const float* x) override; - - /// default implementation that calls encode_vectors - void add_with_ids(idx_t n, const float* x, const idx_t* xids) override; - - /** Encodes a set of vectors as they would appear in the inverted lists - * - * @param list_nos inverted list ids as returned by the - * quantizer (size n). -1s are ignored. - * @param codes output codes, size n * code_size - * @param include_listno - * include the list ids in the code (in this case add - * ceil(log8(nlist)) to the code size) - */ - virtual void encode_vectors(idx_t n, const float* x, - const idx_t *list_nos, - uint8_t * codes, - bool include_listno = false) const = 0; - - /// Sub-classes that encode the residuals can train their encoders here - /// does nothing by default - virtual void train_residual (idx_t n, const float *x); - - /** search a set of vectors, that are pre-quantized by the IVF - * quantizer. Fill in the corresponding heaps with the query - * results. The default implementation uses InvertedListScanners - * to do the search. - * - * @param n nb of vectors to query - * @param x query vectors, size nx * d - * @param assign coarse quantization indices, size nx * nprobe - * @param centroid_dis - * distances to coarse centroids, size nx * nprobe - * @param distance - * output distances, size n * k - * @param labels output labels, size n * k - * @param store_pairs store inv list index + inv list offset - * instead in upper/lower 32 bit of result, - * instead of ids (used for reranking). - * @param params used to override the object's search parameters - */ - virtual void search_preassigned (idx_t n, const float *x, idx_t k, - const idx_t *assign, - const float *centroid_dis, - float *distances, idx_t *labels, - bool store_pairs, - const IVFSearchParameters *params=nullptr, - ConcurrentBitsetPtr bitset = nullptr - ) const; - - /** assign the vectors, then call search_preassign */ - void search (idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - -#if 0 - /** get raw vectors by ids */ - void get_vector_by_id (idx_t n, const idx_t *xid, float *x, ConcurrentBitsetPtr bitset = nullptr) override; - - void search_by_id (idx_t n, const idx_t *xid, idx_t k, float *distances, idx_t *labels, - ConcurrentBitsetPtr bitset = nullptr) override; -#endif - - void range_search (idx_t n, const float* x, float radius, - RangeSearchResult* result, - ConcurrentBitsetPtr bitset = nullptr) const override; - - void range_search_preassigned(idx_t nx, const float *x, float radius, - const idx_t *keys, const float *coarse_dis, - RangeSearchResult *result, - ConcurrentBitsetPtr bitset = nullptr) const; - - /// get a scanner for this index (store_pairs means ignore labels) - virtual InvertedListScanner *get_InvertedListScanner ( - bool store_pairs=false) const; - - /** reconstruct a vector. Works only if maintain_direct_map is set to 1 or 2 */ - void reconstruct (idx_t key, float* recons) const override; - - /** Update a subset of vectors. - * - * The index must have a direct_map - * - * @param nv nb of vectors to update - * @param idx vector indices to update, size nv - * @param v vectors of new values, size nv*d - */ - virtual void update_vectors (int nv, const idx_t *idx, const float *v); - - /** Reconstruct a subset of the indexed vectors. - * - * Overrides default implementation to bypass reconstruct() which requires - * direct_map to be maintained. - * - * @param i0 first vector to reconstruct - * @param ni nb of vectors to reconstruct - * @param recons output array of reconstructed vectors, size ni * d - */ - void reconstruct_n(idx_t i0, idx_t ni, float* recons) const override; - - /** Similar to search, but also reconstructs the stored vectors (or an - * approximation in the case of lossy coding) for the search results. - * - * Overrides default implementation to avoid having to maintain direct_map - * and instead fetch the code offsets through the `store_pairs` flag in - * search_preassigned(). - * - * @param recons reconstructed vectors size (n, k, d) - */ - void search_and_reconstruct (idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, - float *recons) const override; - - /** Reconstruct a vector given the location in terms of (inv list index + - * inv list offset) instead of the id. - * - * Useful for reconstructing when the direct_map is not maintained and - * the inv list offset is computed by search_preassigned() with - * `store_pairs` set. - */ - virtual void reconstruct_from_offset (int64_t list_no, int64_t offset, - float* recons) const; - - - /// Dataset manipulation functions - - size_t remove_ids(const IDSelector& sel) override; - - /** check that the two indexes are compatible (ie, they are - * trained in the same way and have the same - * parameters). Otherwise throw. */ - void check_compatible_for_merge (const IndexIVF &other) const; - - /** moves the entries from another dataset to self. On output, - * other is empty. add_id is added to all moved ids (for - * sequential ids, this would be this->ntotal */ - virtual void merge_from (IndexIVF &other, idx_t add_id); - - /** copy a subset of the entries index to the other index - * - * if subset_type == 0: copies ids in [a1, a2) - * if subset_type == 1: copies ids if id % a1 == a2 - * if subset_type == 2: copies inverted lists such that a1 - * elements are left before and a2 elements are after - */ - virtual void copy_subset_to (IndexIVF & other, int subset_type, - idx_t a1, idx_t a2) const; - - virtual void to_readonly(); - virtual bool is_readonly() const; - - virtual void backup_quantizer(); - - virtual void restore_quantizer(); - - ~IndexIVF() override; - - size_t get_list_size (size_t list_no) const - { return invlists->list_size(list_no); } - - /** intialize a direct map - * - * @param new_maintain_direct_map if true, create a direct map, - * else clear it - */ - void make_direct_map (bool new_maintain_direct_map=true); - - void set_direct_map_type (DirectMap::Type type); - - - /// replace the inverted lists, old one is deallocated if own_invlists - void replace_invlists (InvertedLists *il, bool own=false); - - /* The standalone codec interface (except sa_decode that is specific) */ - size_t sa_code_size () const override; - - void sa_encode (idx_t n, const float *x, - uint8_t *bytes) const override; - - void dump(); - - IndexIVF (); -}; - -struct RangeQueryResult; - -/** Object that handles a query. The inverted lists to scan are - * provided externally. The object has a lot of state, but - * distance_to_code and scan_codes can be called in multiple - * threads */ -struct InvertedListScanner { - - using idx_t = Index::idx_t; - - /// from now on we handle this query. - virtual void set_query (const float *query_vector) = 0; - - /// following codes come from this inverted list - virtual void set_list (idx_t list_no, float coarse_dis) = 0; - - /// compute a single query-to-code distance - virtual float distance_to_code (const uint8_t *code) const = 0; - - /** scan a set of codes, compute distances to current query and - * update heap of results if necessary. - * - * @param n number of codes to scan - * @param codes codes to scan (n * code_size) - * @param ids corresponding ids (ignored if store_pairs) - * @param distances heap distances (size k) - * @param labels heap labels (size k) - * @param k heap size - * @return number of heap updates performed - */ - virtual size_t scan_codes (size_t n, - const uint8_t *codes, - const idx_t *ids, - float *distances, idx_t *labels, - size_t k, - ConcurrentBitsetPtr bitset = nullptr) const = 0; - - /** scan a set of codes, compute distances to current query and - * update results if distances are below radius - * - * (default implementation fails) */ - virtual void scan_codes_range (size_t n, - const uint8_t *codes, - const idx_t *ids, - float radius, - RangeQueryResult &result, - ConcurrentBitsetPtr bitset = nullptr) const; - - virtual ~InvertedListScanner () {} - -}; - - -struct IndexIVFStats { - size_t nq; // nb of queries run - size_t nlist; // nb of inverted lists scanned - size_t ndis; // nb of distancs computed - size_t nheap_updates; // nb of times the heap was updated - double quantization_time; // time spent quantizing vectors (in ms) - double search_time; // time spent searching lists (in ms) - - IndexIVFStats () {reset (); } - void reset (); -}; - -// global var that collects them all -extern IndexIVFStats indexIVF_stats; - - -} // namespace faiss - - -#endif diff --git a/core/src/index/thirdparty/faiss/IndexIVFFlat.cpp b/core/src/index/thirdparty/faiss/IndexIVFFlat.cpp deleted file mode 100644 index 2846990f9f..0000000000 --- a/core/src/index/thirdparty/faiss/IndexIVFFlat.cpp +++ /dev/null @@ -1,472 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include - -#include - -#include -#include -#include -#include -#include - -namespace faiss { - - -/***************************************** - * IndexIVFFlat implementation - ******************************************/ - -IndexIVFFlat::IndexIVFFlat (Index * quantizer, - size_t d, size_t nlist, MetricType metric): - IndexIVF (quantizer, d, nlist, sizeof(float) * d, metric) -{ - code_size = sizeof(float) * d; -} - - -void IndexIVFFlat::add_with_ids (idx_t n, const float * x, const idx_t *xids) -{ - add_core (n, x, xids, nullptr); -} - -void IndexIVFFlat::add_core (idx_t n, const float * x, const int64_t *xids, - const int64_t *precomputed_idx) - -{ - FAISS_THROW_IF_NOT (is_trained); - assert (invlists); - direct_map.check_can_add (xids); - const int64_t * idx; - ScopeDeleter del; - - if (precomputed_idx) { - idx = precomputed_idx; - } else { - int64_t * idx0 = new int64_t [n]; - del.set (idx0); - quantizer->assign (n, x, idx0); - idx = idx0; - } - int64_t n_add = 0; - for (size_t i = 0; i < n; i++) { - idx_t id = xids ? xids[i] : ntotal + i; - idx_t list_no = idx [i]; - size_t offset; - - if (list_no >= 0) { - const float *xi = x + i * d; - offset = invlists->add_entry ( - list_no, id, (const uint8_t*) xi); - n_add++; - } else { - offset = 0; - } - direct_map.add_single_id (id, list_no, offset); - } - - if (verbose) { - printf("IndexIVFFlat::add_core: added %ld / %ld vectors\n", - n_add, n); - } - ntotal += n; -} - -void IndexIVFFlat::encode_vectors(idx_t n, const float* x, - const idx_t * list_nos, - uint8_t * codes, - bool include_listnos) const -{ - if (!include_listnos) { - memcpy (codes, x, code_size * n); - } else { - size_t coarse_size = coarse_code_size (); - for (size_t i = 0; i < n; i++) { - int64_t list_no = list_nos [i]; - uint8_t *code = codes + i * (code_size + coarse_size); - const float *xi = x + i * d; - if (list_no >= 0) { - encode_listno (list_no, code); - memcpy (code + coarse_size, xi, code_size); - } else { - memset (code, 0, code_size + coarse_size); - } - - } - } -} - -void IndexIVFFlat::sa_decode (idx_t n, const uint8_t *bytes, - float *x) const -{ - size_t coarse_size = coarse_code_size (); - for (size_t i = 0; i < n; i++) { - const uint8_t *code = bytes + i * (code_size + coarse_size); - float *xi = x + i * d; - memcpy (xi, code + coarse_size, code_size); - } -} - - -namespace { - - -template -struct IVFFlatScanner: InvertedListScanner { - size_t d; - bool store_pairs; - - IVFFlatScanner(size_t d, bool store_pairs): - d(d), store_pairs(store_pairs) {} - - const float *xi; - void set_query (const float *query) override { - this->xi = query; - } - - idx_t list_no; - void set_list (idx_t list_no, float /* coarse_dis */) override { - this->list_no = list_no; - } - - float distance_to_code (const uint8_t *code) const override { - const float *yj = (float*)code; - float dis = metric == METRIC_INNER_PRODUCT ? - fvec_inner_product (xi, yj, d) : fvec_L2sqr (xi, yj, d); - return dis; - } - - size_t scan_codes (size_t list_size, - const uint8_t *codes, - const idx_t *ids, - float *simi, idx_t *idxi, - size_t k, - ConcurrentBitsetPtr bitset) const override - { - const float *list_vecs = (const float*)codes; - size_t nup = 0; - for (size_t j = 0; j < list_size; j++) { - if (!bitset || !bitset->test(ids[j])) { - const float * yj = list_vecs + d * j; - float dis = metric == METRIC_INNER_PRODUCT ? - fvec_inner_product (xi, yj, d) : fvec_L2sqr (xi, yj, d); - if (C::cmp (simi[0], dis)) { - int64_t id = store_pairs ? (list_no << 32 | j) : ids[j]; - heap_swap_top (k, simi, idxi, dis, id); - nup++; - } - } - } - return nup; - } - - void scan_codes_range (size_t list_size, - const uint8_t *codes, - const idx_t *ids, - float radius, - RangeQueryResult & res, - ConcurrentBitsetPtr bitset = nullptr) const override - { - const float *list_vecs = (const float*)codes; - for (size_t j = 0; j < list_size; j++) { - const float * yj = list_vecs + d * j; - float dis = metric == METRIC_INNER_PRODUCT ? - fvec_inner_product (xi, yj, d) : fvec_L2sqr (xi, yj, d); - if (C::cmp (radius, dis)) { - int64_t id = store_pairs ? lo_build (list_no, j) : ids[j]; - res.add (dis, id); - } - } - } - - -}; - - -} // anonymous namespace - - - -InvertedListScanner* IndexIVFFlat::get_InvertedListScanner - (bool store_pairs) const -{ - if (metric_type == METRIC_INNER_PRODUCT) { - return new IVFFlatScanner< - METRIC_INNER_PRODUCT, CMin > (d, store_pairs); - } else if (metric_type == METRIC_L2) { - return new IVFFlatScanner< - METRIC_L2, CMax >(d, store_pairs); - } else { - FAISS_THROW_MSG("metric type not supported"); - } - return nullptr; -} - - - - -void IndexIVFFlat::reconstruct_from_offset (int64_t list_no, int64_t offset, - float* recons) const -{ - memcpy (recons, invlists->get_single_code (list_no, offset), code_size); -} - -/***************************************** - * IndexIVFFlatDedup implementation - ******************************************/ - -IndexIVFFlatDedup::IndexIVFFlatDedup ( - Index * quantizer, size_t d, size_t nlist_, - MetricType metric_type): - IndexIVFFlat (quantizer, d, nlist_, metric_type) -{} - - -void IndexIVFFlatDedup::train(idx_t n, const float* x) -{ - std::unordered_map map; - float * x2 = new float [n * d]; - ScopeDeleter del (x2); - - int64_t n2 = 0; - for (int64_t i = 0; i < n; i++) { - uint64_t hash = hash_bytes((uint8_t *)(x + i * d), code_size); - if (map.count(hash) && - !memcmp (x2 + map[hash] * d, x + i * d, code_size)) { - // is duplicate, skip - } else { - map [hash] = n2; - memcpy (x2 + n2 * d, x + i * d, code_size); - n2 ++; - } - } - if (verbose) { - printf ("IndexIVFFlatDedup::train: train on %ld points after dedup " - "(was %ld points)\n", n2, n); - } - IndexIVFFlat::train (n2, x2); -} - - - -void IndexIVFFlatDedup::add_with_ids( - idx_t na, const float* x, const idx_t* xids) -{ - - FAISS_THROW_IF_NOT (is_trained); - assert (invlists); - FAISS_THROW_IF_NOT_MSG (direct_map.no(), - "IVFFlatDedup not implemented with direct_map"); - int64_t * idx = new int64_t [na]; - ScopeDeleter del (idx); - quantizer->assign (na, x, idx); - - int64_t n_add = 0, n_dup = 0; - // TODO make a omp loop with this - for (size_t i = 0; i < na; i++) { - idx_t id = xids ? xids[i] : ntotal + i; - int64_t list_no = idx [i]; - - if (list_no < 0) { - continue; - } - const float *xi = x + i * d; - - // search if there is already an entry with that id - InvertedLists::ScopedCodes codes (invlists, list_no); - - int64_t n = invlists->list_size (list_no); - int64_t offset = -1; - for (int64_t o = 0; o < n; o++) { - if (!memcmp (codes.get() + o * code_size, - xi, code_size)) { - offset = o; - break; - } - } - - if (offset == -1) { // not found - invlists->add_entry (list_no, id, (const uint8_t*) xi); - } else { - // mark equivalence - idx_t id2 = invlists->get_single_id (list_no, offset); - std::pair pair (id2, id); - instances.insert (pair); - n_dup ++; - } - n_add++; - } - if (verbose) { - printf("IndexIVFFlat::add_with_ids: added %ld / %ld vectors" - " (out of which %ld are duplicates)\n", - n_add, na, n_dup); - } - ntotal += n_add; -} - -void IndexIVFFlatDedup::search_preassigned ( - idx_t n, const float *x, idx_t k, - const idx_t *assign, - const float *centroid_dis, - float *distances, idx_t *labels, - bool store_pairs, - const IVFSearchParameters *params, - ConcurrentBitsetPtr bitset) const -{ - FAISS_THROW_IF_NOT_MSG ( - !store_pairs, "store_pairs not supported in IVFDedup"); - - IndexIVFFlat::search_preassigned (n, x, k, assign, centroid_dis, - distances, labels, false, - params); - - std::vector labels2 (k); - std::vector dis2 (k); - - for (int64_t i = 0; i < n; i++) { - idx_t *labels1 = labels + i * k; - float *dis1 = distances + i * k; - int64_t j = 0; - for (; j < k; j++) { - if (instances.find (labels1[j]) != instances.end ()) { - // a duplicate: special handling - break; - } - } - if (j < k) { - // there are duplicates, special handling - int64_t j0 = j; - int64_t rp = j; - while (j < k) { - auto range = instances.equal_range (labels1[rp]); - float dis = dis1[rp]; - labels2[j] = labels1[rp]; - dis2[j] = dis; - j ++; - for (auto it = range.first; j < k && it != range.second; ++it) { - labels2[j] = it->second; - dis2[j] = dis; - j++; - } - rp++; - } - memcpy (labels1 + j0, labels2.data() + j0, - sizeof(labels1[0]) * (k - j0)); - memcpy (dis1 + j0, dis2.data() + j0, - sizeof(dis2[0]) * (k - j0)); - } - } - -} - - -size_t IndexIVFFlatDedup::remove_ids(const IDSelector& sel) -{ - std::unordered_map replace; - std::vector > toadd; - for (auto it = instances.begin(); it != instances.end(); ) { - if (sel.is_member(it->first)) { - // then we erase this entry - if (!sel.is_member(it->second)) { - // if the second is not erased - if (replace.count(it->first) == 0) { - replace[it->first] = it->second; - } else { // remember we should add an element - std::pair new_entry ( - replace[it->first], it->second); - toadd.push_back(new_entry); - } - } - it = instances.erase(it); - } else { - if (sel.is_member(it->second)) { - it = instances.erase(it); - } else { - ++it; - } - } - } - - instances.insert (toadd.begin(), toadd.end()); - - // mostly copied from IndexIVF.cpp - - FAISS_THROW_IF_NOT_MSG (direct_map.no(), - "direct map remove not implemented"); - - std::vector toremove(nlist); - -#pragma omp parallel for - for (int64_t i = 0; i < nlist; i++) { - int64_t l0 = invlists->list_size (i), l = l0, j = 0; - InvertedLists::ScopedIds idsi (invlists, i); - while (j < l) { - if (sel.is_member (idsi[j])) { - if (replace.count(idsi[j]) == 0) { - l--; - invlists->update_entry ( - i, j, - invlists->get_single_id (i, l), - InvertedLists::ScopedCodes (invlists, i, l).get()); - } else { - invlists->update_entry ( - i, j, - replace[idsi[j]], - InvertedLists::ScopedCodes (invlists, i, j).get()); - j++; - } - } else { - j++; - } - } - toremove[i] = l0 - l; - } - // this will not run well in parallel on ondisk because of possible shrinks - int64_t nremove = 0; - for (int64_t i = 0; i < nlist; i++) { - if (toremove[i] > 0) { - nremove += toremove[i]; - invlists->resize( - i, invlists->list_size(i) - toremove[i]); - } - } - ntotal -= nremove; - return nremove; -} - - -void IndexIVFFlatDedup::range_search( - idx_t , - const float* , - float , - RangeSearchResult* , - ConcurrentBitsetPtr) const -{ - FAISS_THROW_MSG ("not implemented"); -} - -void IndexIVFFlatDedup::update_vectors (int , const idx_t *, const float *) -{ - FAISS_THROW_MSG ("not implemented"); -} - - -void IndexIVFFlatDedup::reconstruct_from_offset ( - int64_t , int64_t , float* ) const -{ - FAISS_THROW_MSG ("not implemented"); -} - - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexIVFFlat.h b/core/src/index/thirdparty/faiss/IndexIVFFlat.h deleted file mode 100644 index 3c5777a1c2..0000000000 --- a/core/src/index/thirdparty/faiss/IndexIVFFlat.h +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_INDEX_IVF_FLAT_H -#define FAISS_INDEX_IVF_FLAT_H - -#include -#include - -#include - - -namespace faiss { - -/** Inverted file with stored vectors. Here the inverted file - * pre-selects the vectors to be searched, but they are not otherwise - * encoded, the code array just contains the raw float entries. - */ -struct IndexIVFFlat: IndexIVF { - - IndexIVFFlat ( - Index * quantizer, size_t d, size_t nlist_, - MetricType = METRIC_L2); - - /// same as add_with_ids, with precomputed coarse quantizer - virtual void add_core (idx_t n, const float * x, const int64_t *xids, - const int64_t *precomputed_idx); - - /// implemented for all IndexIVF* classes - void add_with_ids(idx_t n, const float* x, const idx_t* xids) override; - - void encode_vectors(idx_t n, const float* x, - const idx_t *list_nos, - uint8_t * codes, - bool include_listnos=false) const override; - - - InvertedListScanner *get_InvertedListScanner (bool store_pairs) - const override; - - - void reconstruct_from_offset (int64_t list_no, int64_t offset, - float* recons) const override; - - void sa_decode (idx_t n, const uint8_t *bytes, - float *x) const override; - - IndexIVFFlat () {} -}; - - -struct IndexIVFFlatDedup: IndexIVFFlat { - - /** Maps ids stored in the index to the ids of vectors that are - * the same. When a vector is unique, it does not appear in the - * instances map */ - std::unordered_multimap instances; - - IndexIVFFlatDedup ( - Index * quantizer, size_t d, size_t nlist_, - MetricType = METRIC_L2); - - /// also dedups the training set - void train(idx_t n, const float* x) override; - - /// implemented for all IndexIVF* classes - void add_with_ids(idx_t n, const float* x, const idx_t* xids) override; - - void search_preassigned (idx_t n, const float *x, idx_t k, - const idx_t *assign, - const float *centroid_dis, - float *distances, idx_t *labels, - bool store_pairs, - const IVFSearchParameters *params=nullptr, - ConcurrentBitsetPtr bitset = nullptr - ) const override; - - size_t remove_ids(const IDSelector& sel) override; - - /// not implemented - void range_search( - idx_t n, - const float* x, - float radius, - RangeSearchResult* result, - ConcurrentBitsetPtr bitset = nullptr) const override; - - /// not implemented - void update_vectors (int nv, const idx_t *idx, const float *v) override; - - /// not implemented - void reconstruct_from_offset (int64_t list_no, int64_t offset, - float* recons) const override; - - IndexIVFFlatDedup () {} - - -}; - - - -} // namespace faiss - -#endif diff --git a/core/src/index/thirdparty/faiss/IndexIVFPQ.cpp b/core/src/index/thirdparty/faiss/IndexIVFPQ.cpp deleted file mode 100644 index fb786cc375..0000000000 --- a/core/src/index/thirdparty/faiss/IndexIVFPQ.cpp +++ /dev/null @@ -1,1239 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include - -#include - -#include - -#include - -namespace faiss { - -/***************************************** - * IndexIVFPQ implementation - ******************************************/ - -IndexIVFPQ::IndexIVFPQ (Index * quantizer, size_t d, size_t nlist, - size_t M, size_t nbits_per_idx, MetricType metric): - IndexIVF (quantizer, d, nlist, 0, metric), - pq (d, M, nbits_per_idx) -{ - FAISS_THROW_IF_NOT (nbits_per_idx <= 8); - code_size = pq.code_size; - invlists->code_size = code_size; - is_trained = false; - by_residual = true; - use_precomputed_table = 0; - scan_table_threshold = 0; - - polysemous_training = nullptr; - do_polysemous_training = false; - polysemous_ht = 0; - -} - - -/**************************************************************** - * training */ - -void IndexIVFPQ::train_residual (idx_t n, const float *x) -{ - train_residual_o (n, x, nullptr); -} - - -void IndexIVFPQ::train_residual_o (idx_t n, const float *x, float *residuals_2) -{ - const float * x_in = x; - - x = fvecs_maybe_subsample ( - d, (size_t*)&n, pq.cp.max_points_per_centroid * pq.ksub, - x, verbose, pq.cp.seed); - - ScopeDeleter del_x (x_in == x ? nullptr : x); - - const float *trainset; - ScopeDeleter del_residuals; - if (by_residual) { - if(verbose) printf("computing residuals\n"); - idx_t * assign = new idx_t [n]; // assignement to coarse centroids - ScopeDeleter del (assign); - quantizer->assign (n, x, assign); - float *residuals = new float [n * d]; - del_residuals.set (residuals); - for (idx_t i = 0; i < n; i++) - quantizer->compute_residual (x + i * d, residuals+i*d, assign[i]); - - trainset = residuals; - } else { - trainset = x; - } - if (verbose) - printf ("training %zdx%zd product quantizer on %ld vectors in %dD\n", - pq.M, pq.ksub, n, d); - pq.verbose = verbose; - pq.train (n, trainset); - - if (do_polysemous_training) { - if (verbose) - printf("doing polysemous training for PQ\n"); - PolysemousTraining default_pt; - PolysemousTraining *pt = polysemous_training; - if (!pt) pt = &default_pt; - pt->optimize_pq_for_hamming (pq, n, trainset); - } - - // prepare second-level residuals for refine PQ - if (residuals_2) { - uint8_t *train_codes = new uint8_t [pq.code_size * n]; - ScopeDeleter del (train_codes); - pq.compute_codes (trainset, train_codes, n); - - for (idx_t i = 0; i < n; i++) { - const float *xx = trainset + i * d; - float * res = residuals_2 + i * d; - pq.decode (train_codes + i * pq.code_size, res); - for (int j = 0; j < d; j++) - res[j] = xx[j] - res[j]; - } - - } - - if (by_residual) { - precompute_table (); - } - -} - - - - - - -/**************************************************************** - * IVFPQ as codec */ - - -/* produce a binary signature based on the residual vector */ -void IndexIVFPQ::encode (idx_t key, const float * x, uint8_t * code) const -{ - if (by_residual) { - float residual_vec[d]; - quantizer->compute_residual (x, residual_vec, key); - pq.compute_code (residual_vec, code); - } - else pq.compute_code (x, code); -} - -void IndexIVFPQ::encode_multiple (size_t n, idx_t *keys, - const float * x, uint8_t * xcodes, - bool compute_keys) const -{ - if (compute_keys) - quantizer->assign (n, x, keys); - - encode_vectors (n, x, keys, xcodes); -} - -void IndexIVFPQ::decode_multiple (size_t n, const idx_t *keys, - const uint8_t * xcodes, float * x) const -{ - pq.decode (xcodes, x, n); - if (by_residual) { - std::vector centroid (d); - for (size_t i = 0; i < n; i++) { - quantizer->reconstruct (keys[i], centroid.data()); - float *xi = x + i * d; - for (size_t j = 0; j < d; j++) { - xi [j] += centroid [j]; - } - } - } -} - - - - -/**************************************************************** - * add */ - - -void IndexIVFPQ::add_with_ids (idx_t n, const float * x, const idx_t *xids) -{ - add_core_o (n, x, xids, nullptr); -} - - -static float * compute_residuals ( - const Index *quantizer, - Index::idx_t n, const float* x, - const Index::idx_t *list_nos) -{ - size_t d = quantizer->d; - float *residuals = new float [n * d]; - // TODO: parallelize? - for (size_t i = 0; i < n; i++) { - if (list_nos[i] < 0) - memset (residuals + i * d, 0, sizeof(*residuals) * d); - else - quantizer->compute_residual ( - x + i * d, residuals + i * d, list_nos[i]); - } - return residuals; -} - -void IndexIVFPQ::encode_vectors(idx_t n, const float* x, - const idx_t *list_nos, - uint8_t * codes, - bool include_listnos) const -{ - if (by_residual) { - float *to_encode = compute_residuals (quantizer, n, x, list_nos); - ScopeDeleter del (to_encode); - pq.compute_codes (to_encode, codes, n); - } else { - pq.compute_codes (x, codes, n); - } - - if (include_listnos) { - size_t coarse_size = coarse_code_size(); - for (idx_t i = n - 1; i >= 0; i--) { - uint8_t * code = codes + i * (coarse_size + code_size); - memmove (code + coarse_size, - codes + i * code_size, code_size); - encode_listno (list_nos[i], code); - } - } -} - - - -void IndexIVFPQ::sa_decode (idx_t n, const uint8_t *codes, - float *x) const -{ - size_t coarse_size = coarse_code_size (); - -#pragma omp parallel - { - std::vector residual (d); - -#pragma omp for - for (size_t i = 0; i < n; i++) { - const uint8_t *code = codes + i * (code_size + coarse_size); - int64_t list_no = decode_listno (code); - float *xi = x + i * d; - pq.decode (code + coarse_size, xi); - if (by_residual) { - quantizer->reconstruct (list_no, residual.data()); - for (size_t j = 0; j < d; j++) { - xi[j] += residual[j]; - } - } - } - } -} - - -void IndexIVFPQ::add_core_o (idx_t n, const float * x, const idx_t *xids, - float *residuals_2, const idx_t *precomputed_idx) -{ - - idx_t bs = 32768; - if (n > bs) { - for (idx_t i0 = 0; i0 < n; i0 += bs) { - idx_t i1 = std::min(i0 + bs, n); - if (verbose) { - printf("IndexIVFPQ::add_core_o: adding %ld:%ld / %ld\n", - i0, i1, n); - } - add_core_o (i1 - i0, x + i0 * d, - xids ? xids + i0 : nullptr, - residuals_2 ? residuals_2 + i0 * d : nullptr, - precomputed_idx ? precomputed_idx + i0 : nullptr); - } - return; - } - - InterruptCallback::check(); - - direct_map.check_can_add (xids); - - FAISS_THROW_IF_NOT (is_trained); - double t0 = getmillisecs (); - const idx_t * idx; - ScopeDeleter del_idx; - - if (precomputed_idx) { - idx = precomputed_idx; - } else { - idx_t * idx0 = new idx_t [n]; - del_idx.set (idx0); - quantizer->assign (n, x, idx0); - idx = idx0; - } - - double t1 = getmillisecs (); - uint8_t * xcodes = new uint8_t [n * code_size]; - ScopeDeleter del_xcodes (xcodes); - - const float *to_encode = nullptr; - ScopeDeleter del_to_encode; - - if (by_residual) { - to_encode = compute_residuals (quantizer, n, x, idx); - del_to_encode.set (to_encode); - } else { - to_encode = x; - } - pq.compute_codes (to_encode, xcodes, n); - - double t2 = getmillisecs (); - // TODO: parallelize? - size_t n_ignore = 0; - for (size_t i = 0; i < n; i++) { - idx_t key = idx[i]; - idx_t id = xids ? xids[i] : ntotal + i; - if (key < 0) { - direct_map.add_single_id (id, -1, 0); - n_ignore ++; - if (residuals_2) - memset (residuals_2, 0, sizeof(*residuals_2) * d); - continue; - } - - uint8_t *code = xcodes + i * code_size; - size_t offset = invlists->add_entry (key, id, code); - - if (residuals_2) { - float *res2 = residuals_2 + i * d; - const float *xi = to_encode + i * d; - pq.decode (code, res2); - for (int j = 0; j < d; j++) - res2[j] = xi[j] - res2[j]; - } - - direct_map.add_single_id (id, key, offset); - } - - double t3 = getmillisecs (); - if(verbose) { - char comment[100] = {0}; - if (n_ignore > 0) - snprintf (comment, 100, "(%ld vectors ignored)", n_ignore); - printf(" add_core times: %.3f %.3f %.3f %s\n", - t1 - t0, t2 - t1, t3 - t2, comment); - } - ntotal += n; -} - - -void IndexIVFPQ::reconstruct_from_offset (int64_t list_no, int64_t offset, - float* recons) const -{ - const uint8_t* code = invlists->get_single_code (list_no, offset); - - if (by_residual) { - std::vector centroid(d); - quantizer->reconstruct (list_no, centroid.data()); - - pq.decode (code, recons); - for (int i = 0; i < d; ++i) { - recons[i] += centroid[i]; - } - } else { - pq.decode (code, recons); - } -} - - - -/// 2G by default, accommodates tables up to PQ32 w/ 65536 centroids -size_t IndexIVFPQ::precomputed_table_max_bytes = ((size_t)1) << 31; - -/** Precomputed tables for residuals - * - * During IVFPQ search with by_residual, we compute - * - * d = || x - y_C - y_R ||^2 - * - * where x is the query vector, y_C the coarse centroid, y_R the - * refined PQ centroid. The expression can be decomposed as: - * - * d = || x - y_C ||^2 + || y_R ||^2 + 2 * (y_C|y_R) - 2 * (x|y_R) - * --------------- --------------------------- ------- - * term 1 term 2 term 3 - * - * When using multiprobe, we use the following decomposition: - * - term 1 is the distance to the coarse centroid, that is computed - * during the 1st stage search. - * - term 2 can be precomputed, as it does not involve x. However, - * because of the PQ, it needs nlist * M * ksub storage. This is why - * use_precomputed_table is off by default - * - term 3 is the classical non-residual distance table. - * - * Since y_R defined by a product quantizer, it is split across - * subvectors and stored separately for each subvector. If the coarse - * quantizer is a MultiIndexQuantizer then the table can be stored - * more compactly. - * - * At search time, the tables for term 2 and term 3 are added up. This - * is faster when the length of the lists is > ksub * M. - */ - -void IndexIVFPQ::precompute_table () -{ - if (use_precomputed_table == -1) - return; - - if (use_precomputed_table == 0) { // then choose the type of table - if (quantizer->metric_type == METRIC_INNER_PRODUCT) { - if (verbose) { - printf("IndexIVFPQ::precompute_table: precomputed " - "tables not needed for inner product quantizers\n"); - } - return; - } - const MultiIndexQuantizer *miq = - dynamic_cast (quantizer); - if (miq && pq.M % miq->pq.M == 0) - use_precomputed_table = 2; - else { - size_t table_size = pq.M * pq.ksub * nlist * sizeof(float); - if (table_size > precomputed_table_max_bytes) { - if (verbose) { - printf( - "IndexIVFPQ::precompute_table: not precomputing table, " - "it would be too big: %ld bytes (max %ld)\n", - table_size, precomputed_table_max_bytes); - use_precomputed_table = 0; - } - return; - } - use_precomputed_table = 1; - } - } // otherwise assume user has set appropriate flag on input - - if (verbose) { - printf ("precomputing IVFPQ tables type %d\n", - use_precomputed_table); - } - - // squared norms of the PQ centroids - std::vector r_norms (pq.M * pq.ksub, NAN); - for (int m = 0; m < pq.M; m++) - for (int j = 0; j < pq.ksub; j++) - r_norms [m * pq.ksub + j] = - fvec_norm_L2sqr (pq.get_centroids (m, j), pq.dsub); - - if (use_precomputed_table == 1) { - - precomputed_table.resize (nlist * pq.M * pq.ksub); - std::vector centroid (d); - - for (size_t i = 0; i < nlist; i++) { - quantizer->reconstruct (i, centroid.data()); - - float *tab = &precomputed_table[i * pq.M * pq.ksub]; - pq.compute_inner_prod_table (centroid.data(), tab); - fvec_madd (pq.M * pq.ksub, r_norms.data(), 2.0, tab, tab); - } - } else if (use_precomputed_table == 2) { - const MultiIndexQuantizer *miq = - dynamic_cast (quantizer); - FAISS_THROW_IF_NOT (miq); - const ProductQuantizer &cpq = miq->pq; - FAISS_THROW_IF_NOT (pq.M % cpq.M == 0); - - precomputed_table.resize(cpq.ksub * pq.M * pq.ksub); - - // reorder PQ centroid table - std::vector centroids (d * cpq.ksub, NAN); - - for (int m = 0; m < cpq.M; m++) { - for (size_t i = 0; i < cpq.ksub; i++) { - memcpy (centroids.data() + i * d + m * cpq.dsub, - cpq.get_centroids (m, i), - sizeof (*centroids.data()) * cpq.dsub); - } - } - - pq.compute_inner_prod_tables (cpq.ksub, centroids.data (), - precomputed_table.data ()); - - for (size_t i = 0; i < cpq.ksub; i++) { - float *tab = &precomputed_table[i * pq.M * pq.ksub]; - fvec_madd (pq.M * pq.ksub, r_norms.data(), 2.0, tab, tab); - } - - } - -} - -namespace { - -using idx_t = Index::idx_t; - - -#define TIC t0 = get_cycles() -#define TOC get_cycles () - t0 - - - -/** QueryTables manages the various ways of searching an - * IndexIVFPQ. The code contains a lot of branches, depending on: - * - metric_type: are we computing L2 or Inner product similarity? - * - by_residual: do we encode raw vectors or residuals? - * - use_precomputed_table: are x_R|x_C tables precomputed? - * - polysemous_ht: are we filtering with polysemous codes? - */ -struct QueryTables { - - /***************************************************** - * General data from the IVFPQ - *****************************************************/ - - const IndexIVFPQ & ivfpq; - const IVFSearchParameters *params; - - // copied from IndexIVFPQ for easier access - int d; - const ProductQuantizer & pq; - MetricType metric_type; - bool by_residual; - int use_precomputed_table; - int polysemous_ht; - - // pre-allocated data buffers - float * sim_table, * sim_table_2; - float * residual_vec, *decoded_vec; - - // single data buffer - std::vector mem; - - // for table pointers - std::vector sim_table_ptrs; - - explicit QueryTables (const IndexIVFPQ & ivfpq, - const IVFSearchParameters *params): - ivfpq(ivfpq), - d(ivfpq.d), - pq (ivfpq.pq), - metric_type (ivfpq.metric_type), - by_residual (ivfpq.by_residual), - use_precomputed_table (ivfpq.use_precomputed_table) - { - mem.resize (pq.ksub * pq.M * 2 + d * 2); - sim_table = mem.data (); - sim_table_2 = sim_table + pq.ksub * pq.M; - residual_vec = sim_table_2 + pq.ksub * pq.M; - decoded_vec = residual_vec + d; - - // for polysemous - polysemous_ht = ivfpq.polysemous_ht; - if (auto ivfpq_params = - dynamic_cast(params)) { - polysemous_ht = ivfpq_params->polysemous_ht; - } - if (polysemous_ht != 0) { - q_code.resize (pq.code_size); - } - init_list_cycles = 0; - sim_table_ptrs.resize (pq.M); - } - - /***************************************************** - * What we do when query is known - *****************************************************/ - - // field specific to query - const float * qi; - - // query-specific intialization - void init_query (const float * qi) { - this->qi = qi; - if (metric_type == METRIC_INNER_PRODUCT) - init_query_IP (); - else - init_query_L2 (); - if (!by_residual && polysemous_ht != 0) - pq.compute_code (qi, q_code.data()); - } - - void init_query_IP () { - // precompute some tables specific to the query qi - pq.compute_inner_prod_table (qi, sim_table); - } - - void init_query_L2 () { - if (!by_residual) { - pq.compute_distance_table (qi, sim_table); - } else if (use_precomputed_table) { - pq.compute_inner_prod_table (qi, sim_table_2); - } - } - - /***************************************************** - * When inverted list is known: prepare computations - *****************************************************/ - - // fields specific to list - Index::idx_t key; - float coarse_dis; - std::vector q_code; - - uint64_t init_list_cycles; - - /// once we know the query and the centroid, we can prepare the - /// sim_table that will be used for accumulation - /// and dis0, the initial value - float precompute_list_tables () { - float dis0 = 0; - uint64_t t0; TIC; - if (by_residual) { - if (metric_type == METRIC_INNER_PRODUCT) - dis0 = precompute_list_tables_IP (); - else - dis0 = precompute_list_tables_L2 (); - } - init_list_cycles += TOC; - return dis0; - } - - float precompute_list_table_pointers () { - float dis0 = 0; - uint64_t t0; TIC; - if (by_residual) { - if (metric_type == METRIC_INNER_PRODUCT) - FAISS_THROW_MSG ("not implemented"); - else - dis0 = precompute_list_table_pointers_L2 (); - } - init_list_cycles += TOC; - return dis0; - } - - /***************************************************** - * compute tables for inner prod - *****************************************************/ - - float precompute_list_tables_IP () - { - // prepare the sim_table that will be used for accumulation - // and dis0, the initial value - ivfpq.quantizer->reconstruct (key, decoded_vec); - // decoded_vec = centroid - float dis0 = fvec_inner_product (qi, decoded_vec, d); - - if (polysemous_ht) { - for (int i = 0; i < d; i++) { - residual_vec [i] = qi[i] - decoded_vec[i]; - } - pq.compute_code (residual_vec, q_code.data()); - } - return dis0; - } - - - /***************************************************** - * compute tables for L2 distance - *****************************************************/ - - float precompute_list_tables_L2 () - { - float dis0 = 0; - - if (use_precomputed_table == 0 || use_precomputed_table == -1) { - ivfpq.quantizer->compute_residual (qi, residual_vec, key); - pq.compute_distance_table (residual_vec, sim_table); - - if (polysemous_ht != 0) { - pq.compute_code (residual_vec, q_code.data()); - } - - } else if (use_precomputed_table == 1) { - dis0 = coarse_dis; - - fvec_madd (pq.M * pq.ksub, - &ivfpq.precomputed_table [key * pq.ksub * pq.M], - -2.0, sim_table_2, - sim_table); - - - if (polysemous_ht != 0) { - ivfpq.quantizer->compute_residual (qi, residual_vec, key); - pq.compute_code (residual_vec, q_code.data()); - } - - } else if (use_precomputed_table == 2) { - dis0 = coarse_dis; - - const MultiIndexQuantizer *miq = - dynamic_cast (ivfpq.quantizer); - FAISS_THROW_IF_NOT (miq); - const ProductQuantizer &cpq = miq->pq; - int Mf = pq.M / cpq.M; - - const float *qtab = sim_table_2; // query-specific table - float *ltab = sim_table; // (output) list-specific table - - long k = key; - for (int cm = 0; cm < cpq.M; cm++) { - // compute PQ index - int ki = k & ((uint64_t(1) << cpq.nbits) - 1); - k >>= cpq.nbits; - - // get corresponding table - const float *pc = &ivfpq.precomputed_table - [(ki * pq.M + cm * Mf) * pq.ksub]; - - if (polysemous_ht == 0) { - - // sum up with query-specific table - fvec_madd (Mf * pq.ksub, - pc, - -2.0, qtab, - ltab); - ltab += Mf * pq.ksub; - qtab += Mf * pq.ksub; - } else { - for (int m = cm * Mf; m < (cm + 1) * Mf; m++) { - q_code[m] = fvec_madd_and_argmin - (pq.ksub, pc, -2, qtab, ltab); - pc += pq.ksub; - ltab += pq.ksub; - qtab += pq.ksub; - } - } - - } - } - - return dis0; - } - - float precompute_list_table_pointers_L2 () - { - float dis0 = 0; - - if (use_precomputed_table == 1) { - dis0 = coarse_dis; - - const float * s = &ivfpq.precomputed_table [key * pq.ksub * pq.M]; - for (int m = 0; m < pq.M; m++) { - sim_table_ptrs [m] = s; - s += pq.ksub; - } - } else if (use_precomputed_table == 2) { - dis0 = coarse_dis; - - const MultiIndexQuantizer *miq = - dynamic_cast (ivfpq.quantizer); - FAISS_THROW_IF_NOT (miq); - const ProductQuantizer &cpq = miq->pq; - int Mf = pq.M / cpq.M; - - long k = key; - int m0 = 0; - for (int cm = 0; cm < cpq.M; cm++) { - int ki = k & ((uint64_t(1) << cpq.nbits) - 1); - k >>= cpq.nbits; - - const float *pc = &ivfpq.precomputed_table - [(ki * pq.M + cm * Mf) * pq.ksub]; - - for (int m = m0; m < m0 + Mf; m++) { - sim_table_ptrs [m] = pc; - pc += pq.ksub; - } - m0 += Mf; - } - } else { - FAISS_THROW_MSG ("need precomputed tables"); - } - - if (polysemous_ht) { - FAISS_THROW_MSG ("not implemented"); - // Not clear that it makes sense to implemente this, - // because it costs M * ksub, which is what we wanted to - // avoid with the tables pointers. - } - - return dis0; - } - - -}; - - - -template -struct KnnSearchResults { - idx_t key; - const idx_t *ids; - - // heap params - size_t k; - float * heap_sim; - idx_t * heap_ids; - - size_t nup; - - inline void add (idx_t j, float dis, ConcurrentBitsetPtr bitset = nullptr) { - if (C::cmp (heap_sim[0], dis)) { - idx_t id = ids ? ids[j] : lo_build (key, j); - if (bitset != nullptr && bitset->test((faiss::ConcurrentBitset::id_type_t)id)) - return; - heap_swap_top (k, heap_sim, heap_ids, dis, id); - nup++; - } - } - -}; - -template -struct RangeSearchResults { - idx_t key; - const idx_t *ids; - - // wrapped result structure - float radius; - RangeQueryResult & rres; - - inline void add (idx_t j, float dis, faiss::ConcurrentBitsetPtr bitset = nullptr) { - if (C::cmp (radius, dis)) { - idx_t id = ids ? ids[j] : lo_build (key, j); - rres.add (dis, id); - } - } -}; - - - -/***************************************************** - * Scaning the codes. - * The scanning functions call their favorite precompute_* - * function to precompute the tables they need. - *****************************************************/ -template -struct IVFPQScannerT: QueryTables { - - const uint8_t * list_codes; - const IDType * list_ids; - size_t list_size; - - IVFPQScannerT (const IndexIVFPQ & ivfpq, const IVFSearchParameters *params): - QueryTables (ivfpq, params) - { - assert(METRIC_TYPE == metric_type); - } - - float dis0; - - void init_list (idx_t list_no, float coarse_dis, - int mode) { - this->key = list_no; - this->coarse_dis = coarse_dis; - - if (mode == 2) { - dis0 = precompute_list_tables (); - } else if (mode == 1) { - dis0 = precompute_list_table_pointers (); - } - } - - /***************************************************** - * Scaning the codes: simple PQ scan. - *****************************************************/ - - /// version of the scan where we use precomputed tables - template - void scan_list_with_table (size_t ncode, const uint8_t *codes, - SearchResultType & res, - ConcurrentBitsetPtr bitset = nullptr) const - { - for (size_t j = 0; j < ncode; j++) { - PQDecoder decoder(codes, pq.nbits); - codes += pq.code_size; - float dis = dis0; - const float *tab = sim_table; - - for (size_t m = 0; m < pq.M; m++) { - dis += tab[decoder.decode()]; - tab += pq.ksub; - } - - res.add(j, dis, bitset); - } - } - - - /// tables are not precomputed, but pointers are provided to the - /// relevant X_c|x_r tables - template - void scan_list_with_pointer (size_t ncode, const uint8_t *codes, - SearchResultType & res, - faiss::ConcurrentBitsetPtr bitset = nullptr) const - { - for (size_t j = 0; j < ncode; j++) { - PQDecoder decoder(codes, pq.nbits); - codes += pq.code_size; - - float dis = dis0; - const float *tab = sim_table_2; - - for (size_t m = 0; m < pq.M; m++) { - int ci = decoder.decode(); - dis += sim_table_ptrs [m][ci] - 2 * tab [ci]; - tab += pq.ksub; - } - res.add (j, dis, bitset); - } - } - - - /// nothing is precomputed: access residuals on-the-fly - template - void scan_on_the_fly_dist (size_t ncode, const uint8_t *codes, - SearchResultType &res, - faiss::ConcurrentBitsetPtr bitset = nullptr) const - { - const float *dvec; - float dis0 = 0; - if (by_residual) { - if (METRIC_TYPE == METRIC_INNER_PRODUCT) { - ivfpq.quantizer->reconstruct (key, residual_vec); - dis0 = fvec_inner_product (residual_vec, qi, d); - } else { - ivfpq.quantizer->compute_residual (qi, residual_vec, key); - } - dvec = residual_vec; - } else { - dvec = qi; - dis0 = 0; - } - - for (size_t j = 0; j < ncode; j++) { - - pq.decode (codes, decoded_vec); - codes += pq.code_size; - - float dis; - if (METRIC_TYPE == METRIC_INNER_PRODUCT) { - dis = dis0 + fvec_inner_product (decoded_vec, qi, d); - } else { - dis = fvec_L2sqr (decoded_vec, dvec, d); - } - res.add (j, dis, bitset); - } - } - - /***************************************************** - * Scanning codes with polysemous filtering - *****************************************************/ - - template - void scan_list_polysemous_hc ( - size_t ncode, const uint8_t *codes, - SearchResultType & res, - faiss::ConcurrentBitsetPtr bitset = nullptr) const - { - int ht = ivfpq.polysemous_ht; - size_t n_hamming_pass = 0, nup = 0; - - int code_size = pq.code_size; - - HammingComputer hc (q_code.data(), code_size); - - for (size_t j = 0; j < ncode; j++) { - const uint8_t *b_code = codes; - int hd = hc.hamming (b_code); - if (hd < ht) { - n_hamming_pass ++; - PQDecoder decoder(codes, pq.nbits); - - float dis = dis0; - const float *tab = sim_table; - - for (size_t m = 0; m < pq.M; m++) { - dis += tab[decoder.decode()]; - tab += pq.ksub; - } - - res.add (j, dis, bitset); - } - codes += code_size; - } -#pragma omp critical - { - indexIVFPQ_stats.n_hamming_pass += n_hamming_pass; - } - } - - template - void scan_list_polysemous ( - size_t ncode, const uint8_t *codes, - SearchResultType &res, - faiss::ConcurrentBitsetPtr bitset = nullptr) const - { - switch (pq.code_size) { -#define HANDLE_CODE_SIZE(cs) \ - case cs: \ - scan_list_polysemous_hc \ - \ - (ncode, codes, res, bitset); \ - break - HANDLE_CODE_SIZE(4); - HANDLE_CODE_SIZE(8); - HANDLE_CODE_SIZE(16); - HANDLE_CODE_SIZE(20); - HANDLE_CODE_SIZE(32); - HANDLE_CODE_SIZE(64); -#undef HANDLE_CODE_SIZE - default: - if (pq.code_size % 8 == 0) - scan_list_polysemous_hc - - (ncode, codes, res, bitset); - else - scan_list_polysemous_hc - - (ncode, codes, res, bitset); - break; - } - } - -}; - - -/* We put as many parameters as possible in template. Hopefully the - * gain in runtime is worth the code bloat. C is the comparator < or - * >, it is directly related to METRIC_TYPE. precompute_mode is how - * much we precompute (2 = precompute distance tables, 1 = precompute - * pointers to distances, 0 = compute distances one by one). - * Currently only 2 is supported */ -template -struct IVFPQScanner: - IVFPQScannerT, - InvertedListScanner -{ - bool store_pairs; - int precompute_mode; - - IVFPQScanner(const IndexIVFPQ & ivfpq, bool store_pairs, - int precompute_mode): - IVFPQScannerT(ivfpq, nullptr), - store_pairs(store_pairs), precompute_mode(precompute_mode) - { - } - - void set_query (const float *query) override { - this->init_query (query); - } - - void set_list (idx_t list_no, float coarse_dis) override { - this->init_list (list_no, coarse_dis, precompute_mode); - } - - float distance_to_code (const uint8_t *code) const override { - assert(precompute_mode == 2); - float dis = this->dis0; - const float *tab = this->sim_table; - PQDecoder decoder(code, this->pq.nbits); - - for (size_t m = 0; m < this->pq.M; m++) { - dis += tab[decoder.decode()]; - tab += this->pq.ksub; - } - return dis; - } - - size_t scan_codes (size_t ncode, - const uint8_t *codes, - const idx_t *ids, - float *heap_sim, idx_t *heap_ids, - size_t k, - faiss::ConcurrentBitsetPtr bitset) const override - { - KnnSearchResults res = { - /* key */ this->key, - /* ids */ this->store_pairs ? nullptr : ids, - /* k */ k, - /* heap_sim */ heap_sim, - /* heap_ids */ heap_ids, - /* nup */ 0 - }; - - if (this->polysemous_ht > 0) { - assert(precompute_mode == 2); - this->scan_list_polysemous (ncode, codes, res, bitset); - } else if (precompute_mode == 2) { - this->scan_list_with_table (ncode, codes, res, bitset); - } else if (precompute_mode == 1) { - this->scan_list_with_pointer (ncode, codes, res, bitset); - } else if (precompute_mode == 0) { - this->scan_on_the_fly_dist (ncode, codes, res, bitset); - } else { - FAISS_THROW_MSG("bad precomp mode"); - } - return res.nup; - } - - void scan_codes_range (size_t ncode, - const uint8_t *codes, - const idx_t *ids, - float radius, - RangeQueryResult & rres, - faiss::ConcurrentBitsetPtr bitset = nullptr) const override - { - RangeSearchResults res = { - /* key */ this->key, - /* ids */ this->store_pairs ? nullptr : ids, - /* radius */ radius, - /* rres */ rres - }; - - if (this->polysemous_ht > 0) { - assert(precompute_mode == 2); - this->scan_list_polysemous (ncode, codes, res, bitset); - } else if (precompute_mode == 2) { - this->scan_list_with_table (ncode, codes, res, bitset); - } else if (precompute_mode == 1) { - this->scan_list_with_pointer (ncode, codes, res, bitset); - } else if (precompute_mode == 0) { - this->scan_on_the_fly_dist (ncode, codes, res, bitset); - } else { - FAISS_THROW_MSG("bad precomp mode"); - } - - } -}; - -template -InvertedListScanner *get_InvertedListScanner1 (const IndexIVFPQ &index, - bool store_pairs) -{ - - if (index.metric_type == METRIC_INNER_PRODUCT) { - return new IVFPQScanner - , PQDecoder> - (index, store_pairs, 2); - } else if (index.metric_type == METRIC_L2) { - return new IVFPQScanner - , PQDecoder> - (index, store_pairs, 2); - } - return nullptr; -} - - -} // anonymous namespace - -InvertedListScanner * -IndexIVFPQ::get_InvertedListScanner (bool store_pairs) const -{ - - if (pq.nbits == 8) { - return get_InvertedListScanner1 (*this, store_pairs); - } else if (pq.nbits == 16) { - return get_InvertedListScanner1 (*this, store_pairs); - } else { - return get_InvertedListScanner1 (*this, store_pairs); - } - return nullptr; - -} - - - -IndexIVFPQStats indexIVFPQ_stats; - -void IndexIVFPQStats::reset () { - memset (this, 0, sizeof (*this)); -} - - - -IndexIVFPQ::IndexIVFPQ () -{ - // initialize some runtime values - use_precomputed_table = 0; - scan_table_threshold = 0; - do_polysemous_training = false; - polysemous_ht = 0; - polysemous_training = nullptr; -} - - -struct CodeCmp { - const uint8_t *tab; - size_t code_size; - bool operator () (int a, int b) const { - return cmp (a, b) > 0; - } - int cmp (int a, int b) const { - return memcmp (tab + a * code_size, tab + b * code_size, - code_size); - } -}; - - -size_t IndexIVFPQ::find_duplicates (idx_t *dup_ids, size_t *lims) const -{ - size_t ngroup = 0; - lims[0] = 0; - for (size_t list_no = 0; list_no < nlist; list_no++) { - size_t n = invlists->list_size (list_no); - std::vector ord (n); - for (int i = 0; i < n; i++) ord[i] = i; - InvertedLists::ScopedCodes codes (invlists, list_no); - CodeCmp cs = { codes.get(), code_size }; - std::sort (ord.begin(), ord.end(), cs); - - InvertedLists::ScopedIds list_ids (invlists, list_no); - int prev = -1; // all elements from prev to i-1 are equal - for (int i = 0; i < n; i++) { - if (prev >= 0 && cs.cmp (ord [prev], ord [i]) == 0) { - // same as previous => remember - if (prev + 1 == i) { // start new group - ngroup++; - lims[ngroup] = lims[ngroup - 1]; - dup_ids [lims [ngroup]++] = list_ids [ord [prev]]; - } - dup_ids [lims [ngroup]++] = list_ids [ord [i]]; - } else { // not same as previous. - prev = i; - } - } - } - return ngroup; -} - - - - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexIVFPQ.h b/core/src/index/thirdparty/faiss/IndexIVFPQ.h deleted file mode 100644 index 4ca04e9ef9..0000000000 --- a/core/src/index/thirdparty/faiss/IndexIVFPQ.h +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_INDEX_IVFPQ_H -#define FAISS_INDEX_IVFPQ_H - - -#include - -#include -#include - - -namespace faiss { - -struct IVFPQSearchParameters: IVFSearchParameters { - size_t scan_table_threshold; ///< use table computation or on-the-fly? - int polysemous_ht; ///< Hamming thresh for polysemous filtering - ~IVFPQSearchParameters () {} -}; - - -/** Inverted file with Product Quantizer encoding. Each residual - * vector is encoded as a product quantizer code. - */ -struct IndexIVFPQ: IndexIVF { - bool by_residual; ///< Encode residual or plain vector? - - ProductQuantizer pq; ///< produces the codes - - bool do_polysemous_training; ///< reorder PQ centroids after training? - PolysemousTraining *polysemous_training; ///< if NULL, use default - - // search-time parameters - size_t scan_table_threshold; ///< use table computation or on-the-fly? - int polysemous_ht; ///< Hamming thresh for polysemous filtering - - /** Precompute table that speed up query preprocessing at some - * memory cost (used only for by_residual with L2 metric) - * =-1: force disable - * =0: decide heuristically (default: use tables only if they are - * < precomputed_tables_max_bytes) - * =1: tables that work for all quantizers (size 256 * nlist * M) - * =2: specific version for MultiIndexQuantizer (much more compact) - */ - int use_precomputed_table; - static size_t precomputed_table_max_bytes; - - /// if use_precompute_table - /// size nlist * pq.M * pq.ksub - std::vector precomputed_table; - - IndexIVFPQ ( - Index * quantizer, size_t d, size_t nlist, - size_t M, size_t nbits_per_idx, MetricType metric = METRIC_L2); - - void add_with_ids(idx_t n, const float* x, const idx_t* xids = nullptr) - override; - - void encode_vectors(idx_t n, const float* x, - const idx_t *list_nos, - uint8_t * codes, - bool include_listnos = false) const override; - - void sa_decode (idx_t n, const uint8_t *bytes, - float *x) const override; - - - /// same as add_core, also: - /// - output 2nd level residuals if residuals_2 != NULL - /// - use precomputed list numbers if precomputed_idx != NULL - void add_core_o (idx_t n, const float *x, - const idx_t *xids, float *residuals_2, - const idx_t *precomputed_idx = nullptr); - - /// trains the product quantizer - void train_residual(idx_t n, const float* x) override; - - /// same as train_residual, also output 2nd level residuals - void train_residual_o (idx_t n, const float *x, float *residuals_2); - - void reconstruct_from_offset (int64_t list_no, int64_t offset, - float* recons) const override; - - /** Find exact duplicates in the dataset. - * - * the duplicates are returned in pre-allocated arrays (see the - * max sizes). - * - * @param lims limits between groups of duplicates - * (max size ntotal / 2 + 1) - * @param ids ids[lims[i]] : ids[lims[i+1]-1] is a group of - * duplicates (max size ntotal) - * @return n number of groups found - */ - size_t find_duplicates (idx_t *ids, size_t *lims) const; - - // map a vector to a binary code knowning the index - void encode (idx_t key, const float * x, uint8_t * code) const; - - /** Encode multiple vectors - * - * @param n nb vectors to encode - * @param keys posting list ids for those vectors (size n) - * @param x vectors (size n * d) - * @param codes output codes (size n * code_size) - * @param compute_keys if false, assume keys are precomputed, - * otherwise compute them - */ - void encode_multiple (size_t n, idx_t *keys, - const float * x, uint8_t * codes, - bool compute_keys = false) const; - - /// inverse of encode_multiple - void decode_multiple (size_t n, const idx_t *keys, - const uint8_t * xcodes, float * x) const; - - InvertedListScanner *get_InvertedListScanner (bool store_pairs) - const override; - - /// build precomputed table - void precompute_table (); - - IndexIVFPQ (); - -}; - - -/// statistics are robust to internal threading, but not if -/// IndexIVFPQ::search_preassigned is called by multiple threads -struct IndexIVFPQStats { - size_t nrefine; ///< nb of refines (IVFPQR) - - size_t n_hamming_pass; - ///< nb of passed Hamming distance tests (for polysemous) - - // timings measured with the CPU RTC on all threads - size_t search_cycles; - size_t refine_cycles; ///< only for IVFPQR - - IndexIVFPQStats () {reset (); } - void reset (); -}; - -// global var that collects them all -extern IndexIVFPQStats indexIVFPQ_stats; - - - - -} // namespace faiss - - -#endif diff --git a/core/src/index/thirdparty/faiss/IndexIVFPQR.cpp b/core/src/index/thirdparty/faiss/IndexIVFPQR.cpp deleted file mode 100644 index 20d849210c..0000000000 --- a/core/src/index/thirdparty/faiss/IndexIVFPQR.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include -#include - -#include -#include - -namespace faiss { - -/***************************************** - * IndexIVFPQR implementation - ******************************************/ - -IndexIVFPQR::IndexIVFPQR ( - Index * quantizer, size_t d, size_t nlist, - size_t M, size_t nbits_per_idx, - size_t M_refine, size_t nbits_per_idx_refine): - IndexIVFPQ (quantizer, d, nlist, M, nbits_per_idx), - refine_pq (d, M_refine, nbits_per_idx_refine), - k_factor (4) -{ - by_residual = true; -} - -IndexIVFPQR::IndexIVFPQR (): - k_factor (1) -{ - by_residual = true; -} - - - -void IndexIVFPQR::reset() -{ - IndexIVFPQ::reset(); - refine_codes.clear(); -} - - - - -void IndexIVFPQR::train_residual (idx_t n, const float *x) -{ - - float * residual_2 = new float [n * d]; - ScopeDeleter del(residual_2); - - train_residual_o (n, x, residual_2); - - if (verbose) - printf ("training %zdx%zd 2nd level PQ quantizer on %ld %dD-vectors\n", - refine_pq.M, refine_pq.ksub, n, d); - - refine_pq.cp.max_points_per_centroid = 1000; - refine_pq.cp.verbose = verbose; - - refine_pq.train (n, residual_2); - -} - - -void IndexIVFPQR::add_with_ids (idx_t n, const float *x, const idx_t *xids) { - add_core (n, x, xids, nullptr); -} - -void IndexIVFPQR::add_core (idx_t n, const float *x, const idx_t *xids, - const idx_t *precomputed_idx) { - - float * residual_2 = new float [n * d]; - ScopeDeleter del(residual_2); - - idx_t n0 = ntotal; - - add_core_o (n, x, xids, residual_2, precomputed_idx); - - refine_codes.resize (ntotal * refine_pq.code_size); - - refine_pq.compute_codes ( - residual_2, &refine_codes[n0 * refine_pq.code_size], n); - - -} -#define TIC t0 = get_cycles() -#define TOC get_cycles () - t0 - - -void IndexIVFPQR::search_preassigned (idx_t n, const float *x, idx_t k, - const idx_t *idx, - const float *L1_dis, - float *distances, idx_t *labels, - bool store_pairs, - const IVFSearchParameters *params, - ConcurrentBitsetPtr bitset - ) const -{ - uint64_t t0; - TIC; - size_t k_coarse = long(k * k_factor); - idx_t *coarse_labels = new idx_t [k_coarse * n]; - ScopeDeleter del1 (coarse_labels); - { // query with quantizer levels 1 and 2. - float *coarse_distances = new float [k_coarse * n]; - ScopeDeleter del(coarse_distances); - - IndexIVFPQ::search_preassigned ( - n, x, k_coarse, - idx, L1_dis, coarse_distances, coarse_labels, - true, params); - } - - - indexIVFPQ_stats.search_cycles += TOC; - - TIC; - - // 3rd level refinement - size_t n_refine = 0; -#pragma omp parallel reduction(+ : n_refine) - { - // tmp buffers - float *residual_1 = new float [2 * d]; - ScopeDeleter del (residual_1); - float *residual_2 = residual_1 + d; -#pragma omp for - for (idx_t i = 0; i < n; i++) { - const float *xq = x + i * d; - const idx_t * shortlist = coarse_labels + k_coarse * i; - float * heap_sim = distances + k * i; - idx_t * heap_ids = labels + k * i; - maxheap_heapify (k, heap_sim, heap_ids); - - for (int j = 0; j < k_coarse; j++) { - idx_t sl = shortlist[j]; - - if (sl == -1) continue; - - int list_no = lo_listno(sl); - int ofs = lo_offset(sl); - - assert (list_no >= 0 && list_no < nlist); - assert (ofs >= 0 && ofs < invlists->list_size (list_no)); - - // 1st level residual - quantizer->compute_residual (xq, residual_1, list_no); - - // 2nd level residual - const uint8_t * l2code = - invlists->get_single_code (list_no, ofs); - - pq.decode (l2code, residual_2); - for (int l = 0; l < d; l++) - residual_2[l] = residual_1[l] - residual_2[l]; - - // 3rd level residual's approximation - idx_t id = invlists->get_single_id (list_no, ofs); - assert (0 <= id && id < ntotal); - refine_pq.decode (&refine_codes [id * refine_pq.code_size], - residual_1); - - float dis = fvec_L2sqr (residual_1, residual_2, d); - - if (dis < heap_sim[0]) { - idx_t id_or_pair = store_pairs ? sl : id; - maxheap_swap_top (k, heap_sim, heap_ids, dis, id_or_pair); - } - n_refine ++; - } - maxheap_reorder (k, heap_sim, heap_ids); - } - } - indexIVFPQ_stats.nrefine += n_refine; - indexIVFPQ_stats.refine_cycles += TOC; -} - -void IndexIVFPQR::reconstruct_from_offset (int64_t list_no, int64_t offset, - float* recons) const -{ - IndexIVFPQ::reconstruct_from_offset (list_no, offset, recons); - - idx_t id = invlists->get_single_id (list_no, offset); - assert (0 <= id && id < ntotal); - - std::vector r3(d); - refine_pq.decode (&refine_codes [id * refine_pq.code_size], r3.data()); - for (int i = 0; i < d; ++i) { - recons[i] += r3[i]; - } -} - -void IndexIVFPQR::merge_from (IndexIVF &other_in, idx_t add_id) -{ - IndexIVFPQR *other = dynamic_cast (&other_in); - FAISS_THROW_IF_NOT(other); - - IndexIVF::merge_from (other_in, add_id); - - refine_codes.insert (refine_codes.end(), - other->refine_codes.begin(), - other->refine_codes.end()); - other->refine_codes.clear(); -} - -size_t IndexIVFPQR::remove_ids(const IDSelector& /*sel*/) { - FAISS_THROW_MSG("not implemented"); - return 0; -} - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexIVFPQR.h b/core/src/index/thirdparty/faiss/IndexIVFPQR.h deleted file mode 100644 index 38177bda41..0000000000 --- a/core/src/index/thirdparty/faiss/IndexIVFPQR.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#pragma once - -#include - -#include - - -namespace faiss { - - - -/** Index with an additional level of PQ refinement */ -struct IndexIVFPQR: IndexIVFPQ { - ProductQuantizer refine_pq; ///< 3rd level quantizer - std::vector refine_codes; ///< corresponding codes - - /// factor between k requested in search and the k requested from the IVFPQ - float k_factor; - - IndexIVFPQR ( - Index * quantizer, size_t d, size_t nlist, - size_t M, size_t nbits_per_idx, - size_t M_refine, size_t nbits_per_idx_refine); - - void reset() override; - - size_t remove_ids(const IDSelector& sel) override; - - /// trains the two product quantizers - void train_residual(idx_t n, const float* x) override; - - void add_with_ids(idx_t n, const float* x, const idx_t* xids) override; - - /// same as add_with_ids, but optionally use the precomputed list ids - void add_core (idx_t n, const float *x, const idx_t *xids, - const idx_t *precomputed_idx = nullptr); - - void reconstruct_from_offset (int64_t list_no, int64_t offset, - float* recons) const override; - - void merge_from (IndexIVF &other, idx_t add_id) override; - - - void search_preassigned (idx_t n, const float *x, idx_t k, - const idx_t *assign, - const float *centroid_dis, - float *distances, idx_t *labels, - bool store_pairs, - const IVFSearchParameters *params=nullptr, - ConcurrentBitsetPtr bitset = nullptr - ) const override; - - IndexIVFPQR(); -}; - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexIVFSpectralHash.cpp b/core/src/index/thirdparty/faiss/IndexIVFSpectralHash.cpp deleted file mode 100644 index 4e27500a34..0000000000 --- a/core/src/index/thirdparty/faiss/IndexIVFSpectralHash.cpp +++ /dev/null @@ -1,333 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace faiss { - - -IndexIVFSpectralHash::IndexIVFSpectralHash ( - Index * quantizer, size_t d, size_t nlist, - int nbit, float period): - IndexIVF (quantizer, d, nlist, (nbit + 7) / 8, METRIC_L2), - nbit (nbit), period (period), threshold_type (Thresh_global) -{ - FAISS_THROW_IF_NOT (code_size % 4 == 0); - RandomRotationMatrix *rr = new RandomRotationMatrix (d, nbit); - rr->init (1234); - vt = rr; - own_fields = true; - is_trained = false; -} - -IndexIVFSpectralHash::IndexIVFSpectralHash(): - IndexIVF(), vt(nullptr), own_fields(false), - nbit(0), period(0), threshold_type(Thresh_global) -{} - -IndexIVFSpectralHash::~IndexIVFSpectralHash () -{ - if (own_fields) { - delete vt; - } -} - -namespace { - - -float median (size_t n, float *x) { - std::sort(x, x + n); - if (n % 2 == 1) { - return x [n / 2]; - } else { - return (x [n / 2 - 1] + x [n / 2]) / 2; - } -} - -} - - -void IndexIVFSpectralHash::train_residual (idx_t n, const float *x) -{ - if (!vt->is_trained) { - vt->train (n, x); - } - - if (threshold_type == Thresh_global) { - // nothing to do - return; - } else if (threshold_type == Thresh_centroid || - threshold_type == Thresh_centroid_half) { - // convert all centroids with vt - std::vector centroids (nlist * d); - quantizer->reconstruct_n (0, nlist, centroids.data()); - trained.resize(nlist * nbit); - vt->apply_noalloc (nlist, centroids.data(), trained.data()); - if (threshold_type == Thresh_centroid_half) { - for (size_t i = 0; i < nlist * nbit; i++) { - trained[i] -= 0.25 * period; - } - } - return; - } - // otherwise train medians - - // assign - std::unique_ptr idx (new idx_t [n]); - quantizer->assign (n, x, idx.get()); - - std::vector sizes(nlist + 1); - for (size_t i = 0; i < n; i++) { - FAISS_THROW_IF_NOT (idx[i] >= 0); - sizes[idx[i]]++; - } - - size_t ofs = 0; - for (int j = 0; j < nlist; j++) { - size_t o0 = ofs; - ofs += sizes[j]; - sizes[j] = o0; - } - - // transform - std::unique_ptr xt (vt->apply (n, x)); - - // transpose + reorder - std::unique_ptr xo (new float[n * nbit]); - - for (size_t i = 0; i < n; i++) { - size_t idest = sizes[idx[i]]++; - for (size_t j = 0; j < nbit; j++) { - xo[idest + n * j] = xt[i * nbit + j]; - } - } - - trained.resize (n * nbit); - // compute medians -#pragma omp for - for (int i = 0; i < nlist; i++) { - size_t i0 = i == 0 ? 0 : sizes[i - 1]; - size_t i1 = sizes[i]; - for (int j = 0; j < nbit; j++) { - float *xoi = xo.get() + i0 + n * j; - if (i0 == i1) { // nothing to train - trained[i * nbit + j] = 0.0; - } else if (i1 == i0 + 1) { - trained[i * nbit + j] = xoi[0]; - } else { - trained[i * nbit + j] = median(i1 - i0, xoi); - } - } - } -} - - -namespace { - -void binarize_with_freq(size_t nbit, float freq, - const float *x, const float *c, - uint8_t *codes) -{ - memset (codes, 0, (nbit + 7) / 8); - for (size_t i = 0; i < nbit; i++) { - float xf = (x[i] - c[i]); - int xi = int(floor(xf * freq)); - int bit = xi & 1; - codes[i >> 3] |= bit << (i & 7); - } -} - - -}; - - - -void IndexIVFSpectralHash::encode_vectors(idx_t n, const float* x_in, - const idx_t *list_nos, - uint8_t * codes, - bool include_listnos) const -{ - FAISS_THROW_IF_NOT (is_trained); - float freq = 2.0 / period; - - FAISS_THROW_IF_NOT_MSG (!include_listnos, "listnos encoding not supported"); - - // transform with vt - std::unique_ptr x (vt->apply (n, x_in)); - -#pragma omp parallel - { - std::vector zero (nbit); - - // each thread takes care of a subset of lists -#pragma omp for - for (size_t i = 0; i < n; i++) { - int64_t list_no = list_nos [i]; - - if (list_no >= 0) { - const float *c; - if (threshold_type == Thresh_global) { - c = zero.data(); - } else { - c = trained.data() + list_no * nbit; - } - binarize_with_freq (nbit, freq, - x.get() + i * nbit, c, - codes + i * code_size) ; - } - } - } -} - -namespace { - - -template -struct IVFScanner: InvertedListScanner { - - // copied from index structure - const IndexIVFSpectralHash *index; - size_t code_size; - size_t nbit; - bool store_pairs; - - float period, freq; - std::vector q; - std::vector zero; - std::vector qcode; - HammingComputer hc; - - using idx_t = Index::idx_t; - - IVFScanner (const IndexIVFSpectralHash * index, - bool store_pairs): - index (index), - code_size(index->code_size), - nbit(index->nbit), - store_pairs(store_pairs), - period(index->period), freq(2.0 / index->period), - q(nbit), zero(nbit), qcode(code_size), - hc(qcode.data(), code_size) - { - } - - - void set_query (const float *query) override { - FAISS_THROW_IF_NOT(query); - FAISS_THROW_IF_NOT(q.size() == nbit); - index->vt->apply_noalloc (1, query, q.data()); - - if (index->threshold_type == - IndexIVFSpectralHash::Thresh_global) { - binarize_with_freq - (nbit, freq, q.data(), zero.data(), qcode.data()); - hc.set (qcode.data(), code_size); - } - } - - idx_t list_no; - - void set_list (idx_t list_no, float /*coarse_dis*/) override { - this->list_no = list_no; - if (index->threshold_type != IndexIVFSpectralHash::Thresh_global) { - const float *c = index->trained.data() + list_no * nbit; - binarize_with_freq (nbit, freq, q.data(), c, qcode.data()); - hc.set (qcode.data(), code_size); - } - } - - float distance_to_code (const uint8_t *code) const final { - return hc.hamming (code); - } - - size_t scan_codes (size_t list_size, - const uint8_t *codes, - const idx_t *ids, - float *simi, idx_t *idxi, - size_t k, - ConcurrentBitsetPtr bitset) const override - { - size_t nup = 0; - for (size_t j = 0; j < list_size; j++) { - if (!bitset || !bitset->test(ids[j])) { - float dis = hc.hamming (codes); - - if (dis < simi [0]) { - int64_t id = store_pairs ? (list_no << 32 | j) : ids[j]; - maxheap_swap_top (k, simi, idxi, dis, id); - nup++; - } - } - codes += code_size; - } - return nup; - } - - void scan_codes_range (size_t list_size, - const uint8_t *codes, - const idx_t *ids, - float radius, - RangeQueryResult & res, - ConcurrentBitsetPtr bitset = nullptr) const override - { - for (size_t j = 0; j < list_size; j++) { - float dis = hc.hamming (codes); - if (dis < radius) { - int64_t id = store_pairs ? lo_build (list_no, j) : ids[j]; - res.add (dis, id); - } - codes += code_size; - } - } - - -}; - -} // anonymous namespace - -InvertedListScanner* IndexIVFSpectralHash::get_InvertedListScanner - (bool store_pairs) const -{ - switch (code_size) { -#define HANDLE_CODE_SIZE(cs) \ - case cs: \ - return new IVFScanner (this, store_pairs) - HANDLE_CODE_SIZE(4); - HANDLE_CODE_SIZE(8); - HANDLE_CODE_SIZE(16); - HANDLE_CODE_SIZE(20); - HANDLE_CODE_SIZE(32); - HANDLE_CODE_SIZE(64); -#undef HANDLE_CODE_SIZE - default: - if (code_size % 8 == 0) { - return new IVFScanner(this, store_pairs); - } else if (code_size % 4 == 0) { - return new IVFScanner(this, store_pairs); - } else { - FAISS_THROW_MSG("not supported"); - } - } - -} - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexIVFSpectralHash.h b/core/src/index/thirdparty/faiss/IndexIVFSpectralHash.h deleted file mode 100644 index ee01ac81cd..0000000000 --- a/core/src/index/thirdparty/faiss/IndexIVFSpectralHash.h +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_INDEX_IVFSH_H -#define FAISS_INDEX_IVFSH_H - - -#include - -#include - - -namespace faiss { - -struct VectorTransform; - -/** Inverted list that stores binary codes of size nbit. Before the - * binary conversion, the dimension of the vectors is transformed from - * dim d into dim nbit by vt (a random rotation by default). - * - * Each coordinate is subtracted from a value determined by - * threshold_type, and split into intervals of size period. Half of - * the interval is a 0 bit, the other half a 1. - */ -struct IndexIVFSpectralHash: IndexIVF { - - VectorTransform *vt; // transformation from d to nbit dim - bool own_fields; - - int nbit; - float period; - - enum ThresholdType { - Thresh_global, - Thresh_centroid, - Thresh_centroid_half, - Thresh_median - }; - ThresholdType threshold_type; - - // size nlist * nbit or 0 if Thresh_global - std::vector trained; - - IndexIVFSpectralHash (Index * quantizer, size_t d, size_t nlist, - int nbit, float period); - - IndexIVFSpectralHash (); - - void train_residual(idx_t n, const float* x) override; - - void encode_vectors(idx_t n, const float* x, - const idx_t *list_nos, - uint8_t * codes, - bool include_listnos = false) const override; - - InvertedListScanner *get_InvertedListScanner (bool store_pairs) - const override; - - ~IndexIVFSpectralHash () override; - -}; - - - - -}; // namespace faiss - - -#endif diff --git a/core/src/index/thirdparty/faiss/IndexLSH.cpp b/core/src/index/thirdparty/faiss/IndexLSH.cpp deleted file mode 100644 index 1a780a2e7d..0000000000 --- a/core/src/index/thirdparty/faiss/IndexLSH.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include - -#include - -#include -#include -#include - - -namespace faiss { - -/*************************************************************** - * IndexLSH - ***************************************************************/ - - -IndexLSH::IndexLSH (idx_t d, int nbits, bool rotate_data, bool train_thresholds): - Index(d), nbits(nbits), rotate_data(rotate_data), - train_thresholds (train_thresholds), rrot(d, nbits) -{ - is_trained = !train_thresholds; - - bytes_per_vec = (nbits + 7) / 8; - - if (rotate_data) { - rrot.init(5); - } else { - FAISS_THROW_IF_NOT (d >= nbits); - } -} - -IndexLSH::IndexLSH (): - nbits (0), bytes_per_vec(0), rotate_data (false), train_thresholds (false) -{ -} - - -const float * IndexLSH::apply_preprocess (idx_t n, const float *x) const -{ - - float *xt = nullptr; - if (rotate_data) { - // also applies bias if exists - xt = rrot.apply (n, x); - } else if (d != nbits) { - assert (nbits < d); - xt = new float [nbits * n]; - float *xp = xt; - for (idx_t i = 0; i < n; i++) { - const float *xl = x + i * d; - for (int j = 0; j < nbits; j++) - *xp++ = xl [j]; - } - } - - if (train_thresholds) { - - if (xt == NULL) { - xt = new float [nbits * n]; - memcpy (xt, x, sizeof(*x) * n * nbits); - } - - float *xp = xt; - for (idx_t i = 0; i < n; i++) - for (int j = 0; j < nbits; j++) - *xp++ -= thresholds [j]; - } - - return xt ? xt : x; -} - - - -void IndexLSH::train (idx_t n, const float *x) -{ - if (train_thresholds) { - thresholds.resize (nbits); - train_thresholds = false; - const float *xt = apply_preprocess (n, x); - ScopeDeleter del (xt == x ? nullptr : xt); - train_thresholds = true; - - float * transposed_x = new float [n * nbits]; - ScopeDeleter del2 (transposed_x); - - for (idx_t i = 0; i < n; i++) - for (idx_t j = 0; j < nbits; j++) - transposed_x [j * n + i] = xt [i * nbits + j]; - - for (idx_t i = 0; i < nbits; i++) { - float *xi = transposed_x + i * n; - // std::nth_element - std::sort (xi, xi + n); - if (n % 2 == 1) - thresholds [i] = xi [n / 2]; - else - thresholds [i] = (xi [n / 2 - 1] + xi [n / 2]) / 2; - - } - } - is_trained = true; -} - - -void IndexLSH::add (idx_t n, const float *x) -{ - FAISS_THROW_IF_NOT (is_trained); - codes.resize ((ntotal + n) * bytes_per_vec); - - sa_encode (n, x, &codes[ntotal * bytes_per_vec]); - - ntotal += n; -} - - -void IndexLSH::search ( - idx_t n, - const float *x, - idx_t k, - float *distances, - idx_t *labels, - ConcurrentBitsetPtr bitset) const -{ - FAISS_THROW_IF_NOT (is_trained); - const float *xt = apply_preprocess (n, x); - ScopeDeleter del (xt == x ? nullptr : xt); - - uint8_t * qcodes = new uint8_t [n * bytes_per_vec]; - ScopeDeleter del2 (qcodes); - - fvecs2bitvecs (xt, qcodes, nbits, n); - - int * idistances = new int [n * k]; - ScopeDeleter del3 (idistances); - - int_maxheap_array_t res = { size_t(n), size_t(k), labels, idistances}; - - hammings_knn_hc (&res, qcodes, codes.data(), - ntotal, bytes_per_vec, true); - - - // convert distances to floats - for (int i = 0; i < k * n; i++) - distances[i] = idistances[i]; - -} - - -void IndexLSH::transfer_thresholds (LinearTransform *vt) { - if (!train_thresholds) return; - FAISS_THROW_IF_NOT (nbits == vt->d_out); - if (!vt->have_bias) { - vt->b.resize (nbits, 0); - vt->have_bias = true; - } - for (int i = 0; i < nbits; i++) - vt->b[i] -= thresholds[i]; - train_thresholds = false; - thresholds.clear(); -} - -void IndexLSH::reset() { - codes.clear(); - ntotal = 0; -} - - -size_t IndexLSH::sa_code_size () const -{ - return bytes_per_vec; -} - -void IndexLSH::sa_encode (idx_t n, const float *x, - uint8_t *bytes) const -{ - FAISS_THROW_IF_NOT (is_trained); - const float *xt = apply_preprocess (n, x); - ScopeDeleter del (xt == x ? nullptr : xt); - fvecs2bitvecs (xt, bytes, nbits, n); -} - -void IndexLSH::sa_decode (idx_t n, const uint8_t *bytes, - float *x) const -{ - float *xt = x; - ScopeDeleter del; - if (rotate_data || nbits != d) { - xt = new float [n * nbits]; - del.set(xt); - } - bitvecs2fvecs (bytes, xt, nbits, n); - - if (train_thresholds) { - float *xp = xt; - for (idx_t i = 0; i < n; i++) { - for (int j = 0; j < nbits; j++) { - *xp++ += thresholds [j]; - } - } - } - - if (rotate_data) { - rrot.reverse_transform (n, xt, x); - } else if (nbits != d) { - for (idx_t i = 0; i < n; i++) { - memcpy (x + i * d, xt + i * nbits, - nbits * sizeof(xt[0])); - } - } -} - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexLSH.h b/core/src/index/thirdparty/faiss/IndexLSH.h deleted file mode 100644 index 7bcc9c5f84..0000000000 --- a/core/src/index/thirdparty/faiss/IndexLSH.h +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef INDEX_LSH_H -#define INDEX_LSH_H - -#include - -#include -#include - -namespace faiss { - - -/** The sign of each vector component is put in a binary signature */ -struct IndexLSH:Index { - typedef unsigned char uint8_t; - - int nbits; ///< nb of bits per vector - int bytes_per_vec; ///< nb of 8-bits per encoded vector - bool rotate_data; ///< whether to apply a random rotation to input - bool train_thresholds; ///< whether we train thresholds or use 0 - - RandomRotationMatrix rrot; ///< optional random rotation - - std::vector thresholds; ///< thresholds to compare with - - /// encoded dataset - std::vector codes; - - IndexLSH ( - idx_t d, int nbits, - bool rotate_data = true, - bool train_thresholds = false); - - /** Preprocesses and resizes the input to the size required to - * binarize the data - * - * @param x input vectors, size n * d - * @return output vectors, size n * bits. May be the same pointer - * as x, otherwise it should be deleted by the caller - */ - const float *apply_preprocess (idx_t n, const float *x) const; - - void train(idx_t n, const float* x) override; - - void add(idx_t n, const float* x) override; - - void search( - idx_t n, - const float* x, - idx_t k, - float* distances, - idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - void reset() override; - - /// transfer the thresholds to a pre-processing stage (and unset - /// train_thresholds) - void transfer_thresholds (LinearTransform * vt); - - ~IndexLSH() override {} - - IndexLSH (); - - /* standalone codec interface. - * - * The vectors are decoded to +/- 1 (not 0, 1) */ - - size_t sa_code_size () const override; - - void sa_encode (idx_t n, const float *x, - uint8_t *bytes) const override; - - void sa_decode (idx_t n, const uint8_t *bytes, - float *x) const override; - -}; - - -} - - -#endif diff --git a/core/src/index/thirdparty/faiss/IndexLattice.cpp b/core/src/index/thirdparty/faiss/IndexLattice.cpp deleted file mode 100644 index 5c7be9fcbc..0000000000 --- a/core/src/index/thirdparty/faiss/IndexLattice.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - - -#include -#include // for the bitstring routines -#include -#include - -namespace faiss { - - -IndexLattice::IndexLattice (idx_t d, int nsq, int scale_nbit, int r2): - Index (d), - nsq (nsq), - dsq (d / nsq), - zn_sphere_codec (dsq, r2), - scale_nbit (scale_nbit) -{ - FAISS_THROW_IF_NOT (d % nsq == 0); - - lattice_nbit = 0; - while (!( ((uint64_t)1 << lattice_nbit) >= zn_sphere_codec.nv)) { - lattice_nbit++; - } - - int total_nbit = (lattice_nbit + scale_nbit) * nsq; - - code_size = (total_nbit + 7) / 8; - - is_trained = false; -} - -void IndexLattice::train(idx_t n, const float* x) -{ - // compute ranges per sub-block - trained.resize (nsq * 2); - float * mins = trained.data(); - float * maxs = trained.data() + nsq; - for (int sq = 0; sq < nsq; sq++) { - mins[sq] = HUGE_VAL; - maxs[sq] = -1; - } - - for (idx_t i = 0; i < n; i++) { - for (int sq = 0; sq < nsq; sq++) { - float norm2 = fvec_norm_L2sqr (x + i * d + sq * dsq, dsq); - if (norm2 > maxs[sq]) maxs[sq] = norm2; - if (norm2 < mins[sq]) mins[sq] = norm2; - } - } - - for (int sq = 0; sq < nsq; sq++) { - mins[sq] = sqrtf (mins[sq]); - maxs[sq] = sqrtf (maxs[sq]); - } - - is_trained = true; -} - -/* The standalone codec interface */ -size_t IndexLattice::sa_code_size () const -{ - return code_size; -} - - - -void IndexLattice::sa_encode (idx_t n, const float *x, uint8_t *codes) const -{ - - const float * mins = trained.data(); - const float * maxs = mins + nsq; - int64_t sc = int64_t(1) << scale_nbit; - -#pragma omp parallel for - for (idx_t i = 0; i < n; i++) { - BitstringWriter wr(codes + i * code_size, code_size); - const float *xi = x + i * d; - for (int j = 0; j < nsq; j++) { - float nj = - (sqrtf(fvec_norm_L2sqr(xi, dsq)) - mins[j]) - * sc / (maxs[j] - mins[j]); - if (nj < 0) nj = 0; - if (nj >= sc) nj = sc - 1; - wr.write((int64_t)nj, scale_nbit); - wr.write(zn_sphere_codec.encode(xi), lattice_nbit); - xi += dsq; - } - } -} - -void IndexLattice::sa_decode (idx_t n, const uint8_t *codes, float *x) const -{ - const float * mins = trained.data(); - const float * maxs = mins + nsq; - float sc = int64_t(1) << scale_nbit; - float r = sqrtf(zn_sphere_codec.r2); - -#pragma omp parallel for - for (idx_t i = 0; i < n; i++) { - BitstringReader rd(codes + i * code_size, code_size); - float *xi = x + i * d; - for (int j = 0; j < nsq; j++) { - float norm = - (rd.read (scale_nbit) + 0.5) * - (maxs[j] - mins[j]) / sc + mins[j]; - norm /= r; - zn_sphere_codec.decode (rd.read (lattice_nbit), xi); - for (int l = 0; l < dsq; l++) { - xi[l] *= norm; - } - xi += dsq; - } - } -} - -void IndexLattice::add(idx_t , const float* ) -{ - FAISS_THROW_MSG("not implemented"); -} - - -void IndexLattice::search(idx_t , const float* , idx_t , - float* , idx_t* , ConcurrentBitsetPtr ) const -{ - FAISS_THROW_MSG("not implemented"); -} - - -void IndexLattice::reset() -{ - FAISS_THROW_MSG("not implemented"); -} - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexLattice.h b/core/src/index/thirdparty/faiss/IndexLattice.h deleted file mode 100644 index e946fac40a..0000000000 --- a/core/src/index/thirdparty/faiss/IndexLattice.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_INDEX_LATTICE_H -#define FAISS_INDEX_LATTICE_H - - -#include - -#include -#include - -namespace faiss { - - - - - -/** Index that encodes a vector with a series of Zn lattice quantizers - */ -struct IndexLattice: Index { - - /// number of sub-vectors - int nsq; - /// dimension of sub-vectors - size_t dsq; - - /// the lattice quantizer - ZnSphereCodecAlt zn_sphere_codec; - - /// nb bits used to encode the scale, per subvector - int scale_nbit, lattice_nbit; - /// total, in bytes - size_t code_size; - - /// mins and maxes of the vector norms, per subquantizer - std::vector trained; - - IndexLattice (idx_t d, int nsq, int scale_nbit, int r2); - - void train(idx_t n, const float* x) override; - - /* The standalone codec interface */ - size_t sa_code_size () const override; - - void sa_encode (idx_t n, const float *x, - uint8_t *bytes) const override; - - void sa_decode (idx_t n, const uint8_t *bytes, - float *x) const override; - - /// not implemented - void add(idx_t n, const float* x) override; - void search(idx_t n, const float* x, idx_t k, - float* distances, idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - void reset() override; - -}; - -} // namespace faiss - -#endif diff --git a/core/src/index/thirdparty/faiss/IndexPQ.cpp b/core/src/index/thirdparty/faiss/IndexPQ.cpp deleted file mode 100644 index 6e50ba1a2c..0000000000 --- a/core/src/index/thirdparty/faiss/IndexPQ.cpp +++ /dev/null @@ -1,1190 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - - -#include -#include -#include -#include - -#include - -#include -#include -#include - -namespace faiss { - -/********************************************************* - * IndexPQ implementation - ********************************************************/ - - -IndexPQ::IndexPQ (int d, size_t M, size_t nbits, MetricType metric): - Index(d, metric), pq(d, M, nbits) -{ - is_trained = false; - do_polysemous_training = false; - polysemous_ht = nbits * M + 1; - search_type = ST_PQ; - encode_signs = false; -} - -IndexPQ::IndexPQ () -{ - metric_type = METRIC_L2; - is_trained = false; - do_polysemous_training = false; - polysemous_ht = pq.nbits * pq.M + 1; - search_type = ST_PQ; - encode_signs = false; -} - - -void IndexPQ::train (idx_t n, const float *x) -{ - if (!do_polysemous_training) { // standard training - pq.train(n, x); - } else { - idx_t ntrain_perm = polysemous_training.ntrain_permutation; - - if (ntrain_perm > n / 4) - ntrain_perm = n / 4; - if (verbose) { - printf ("PQ training on %ld points, remains %ld points: " - "training polysemous on %s\n", - n - ntrain_perm, ntrain_perm, - ntrain_perm == 0 ? "centroids" : "these"); - } - pq.train(n - ntrain_perm, x); - - polysemous_training.optimize_pq_for_hamming ( - pq, ntrain_perm, x + (n - ntrain_perm) * d); - } - is_trained = true; -} - - -void IndexPQ::add (idx_t n, const float *x) -{ - FAISS_THROW_IF_NOT (is_trained); - codes.resize ((n + ntotal) * pq.code_size); - pq.compute_codes (x, &codes[ntotal * pq.code_size], n); - ntotal += n; -} - - -size_t IndexPQ::remove_ids (const IDSelector & sel) -{ - idx_t j = 0; - for (idx_t i = 0; i < ntotal; i++) { - if (sel.is_member (i)) { - // should be removed - } else { - if (i > j) { - memmove (&codes[pq.code_size * j], &codes[pq.code_size * i], pq.code_size); - } - j++; - } - } - size_t nremove = ntotal - j; - if (nremove > 0) { - ntotal = j; - codes.resize (ntotal * pq.code_size); - } - return nremove; -} - - -void IndexPQ::reset() -{ - codes.clear(); - ntotal = 0; -} - -void IndexPQ::reconstruct_n (idx_t i0, idx_t ni, float *recons) const -{ - FAISS_THROW_IF_NOT (ni == 0 || (i0 >= 0 && i0 + ni <= ntotal)); - for (idx_t i = 0; i < ni; i++) { - const uint8_t * code = &codes[(i0 + i) * pq.code_size]; - pq.decode (code, recons + i * d); - } -} - - -void IndexPQ::reconstruct (idx_t key, float * recons) const -{ - FAISS_THROW_IF_NOT (key >= 0 && key < ntotal); - pq.decode (&codes[key * pq.code_size], recons); -} - - -namespace { - - -struct PQDis: DistanceComputer { - size_t d; - Index::idx_t nb; - const uint8_t *codes; - size_t code_size; - const ProductQuantizer & pq; - const float *sdc; - std::vector precomputed_table; - size_t ndis; - - float operator () (idx_t i) override - { - const uint8_t *code = codes + i * code_size; - const float *dt = precomputed_table.data(); - float accu = 0; - for (int j = 0; j < pq.M; j++) { - accu += dt[*code++]; - dt += 256; - } - ndis++; - return accu; - } - - float symmetric_dis(idx_t i, idx_t j) override - { - const float * sdci = sdc; - float accu = 0; - const uint8_t *codei = codes + i * code_size; - const uint8_t *codej = codes + j * code_size; - - for (int l = 0; l < pq.M; l++) { - accu += sdci[(*codei++) + (*codej++) * 256]; - sdci += 256 * 256; - } - return accu; - } - - explicit PQDis(const IndexPQ& storage, const float* /*q*/ = nullptr) - : pq(storage.pq) { - precomputed_table.resize(pq.M * pq.ksub); - nb = storage.ntotal; - d = storage.d; - codes = storage.codes.data(); - code_size = pq.code_size; - FAISS_ASSERT(pq.ksub == 256); - FAISS_ASSERT(pq.sdc_table.size() == pq.ksub * pq.ksub * pq.M); - sdc = pq.sdc_table.data(); - ndis = 0; - } - - void set_query(const float *x) override { - pq.compute_distance_table(x, precomputed_table.data()); - } -}; - - -} // namespace - - -DistanceComputer * IndexPQ::get_distance_computer() const { - FAISS_THROW_IF_NOT(pq.nbits == 8); - return new PQDis(*this); -} - - -/***************************************** - * IndexPQ polysemous search routines - ******************************************/ - - - - - -void IndexPQ::search (idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, - ConcurrentBitsetPtr bitset) const -{ - FAISS_THROW_IF_NOT (is_trained); - if (search_type == ST_PQ) { // Simple PQ search - - if (metric_type == METRIC_L2) { - float_maxheap_array_t res = { - size_t(n), size_t(k), labels, distances }; - pq.search (x, n, codes.data(), ntotal, &res, true); - } else { - float_minheap_array_t res = { - size_t(n), size_t(k), labels, distances }; - pq.search_ip (x, n, codes.data(), ntotal, &res, true); - } - indexPQ_stats.nq += n; - indexPQ_stats.ncode += n * ntotal; - - } else if (search_type == ST_polysemous || - search_type == ST_polysemous_generalize) { - - FAISS_THROW_IF_NOT (metric_type == METRIC_L2); - - search_core_polysemous (n, x, k, distances, labels); - - } else { // code-to-code distances - - uint8_t * q_codes = new uint8_t [n * pq.code_size]; - ScopeDeleter del (q_codes); - - - if (!encode_signs) { - pq.compute_codes (x, q_codes, n); - } else { - FAISS_THROW_IF_NOT (d == pq.nbits * pq.M); - memset (q_codes, 0, n * pq.code_size); - for (size_t i = 0; i < n; i++) { - const float *xi = x + i * d; - uint8_t *code = q_codes + i * pq.code_size; - for (int j = 0; j < d; j++) - if (xi[j] > 0) code [j>>3] |= 1 << (j & 7); - } - } - - if (search_type == ST_SDC) { - - float_maxheap_array_t res = { - size_t(n), size_t(k), labels, distances}; - - pq.search_sdc (q_codes, n, codes.data(), ntotal, &res, true); - - } else { - int * idistances = new int [n * k]; - ScopeDeleter del (idistances); - - int_maxheap_array_t res = { - size_t (n), size_t (k), labels, idistances}; - - if (search_type == ST_HE) { - - hammings_knn_hc (&res, q_codes, codes.data(), - ntotal, pq.code_size, true); - - } else if (search_type == ST_generalized_HE) { - - generalized_hammings_knn_hc (&res, q_codes, codes.data(), - ntotal, pq.code_size, true); - } - - // convert distances to floats - for (int i = 0; i < k * n; i++) - distances[i] = idistances[i]; - - } - - - indexPQ_stats.nq += n; - indexPQ_stats.ncode += n * ntotal; - } -} - - - - - -void IndexPQStats::reset() -{ - nq = ncode = n_hamming_pass = 0; -} - -IndexPQStats indexPQ_stats; - - -template -static size_t polysemous_inner_loop ( - const IndexPQ & index, - const float *dis_table_qi, const uint8_t *q_code, - size_t k, float *heap_dis, int64_t *heap_ids) -{ - - int M = index.pq.M; - int code_size = index.pq.code_size; - int ksub = index.pq.ksub; - size_t ntotal = index.ntotal; - int ht = index.polysemous_ht; - - const uint8_t *b_code = index.codes.data(); - - size_t n_pass_i = 0; - - HammingComputer hc (q_code, code_size); - - for (int64_t bi = 0; bi < ntotal; bi++) { - int hd = hc.hamming (b_code); - - if (hd < ht) { - n_pass_i ++; - - float dis = 0; - const float * dis_table = dis_table_qi; - for (int m = 0; m < M; m++) { - dis += dis_table [b_code[m]]; - dis_table += ksub; - } - - if (dis < heap_dis[0]) { - maxheap_swap_top (k, heap_dis, heap_ids, dis, bi); - } - } - b_code += code_size; - } - return n_pass_i; -} - - -void IndexPQ::search_core_polysemous (idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels) const -{ - FAISS_THROW_IF_NOT (pq.nbits == 8); - - // PQ distance tables - float * dis_tables = new float [n * pq.ksub * pq.M]; - ScopeDeleter del (dis_tables); - pq.compute_distance_tables (n, x, dis_tables); - - // Hamming embedding queries - uint8_t * q_codes = new uint8_t [n * pq.code_size]; - ScopeDeleter del2 (q_codes); - - if (false) { - pq.compute_codes (x, q_codes, n); - } else { -#pragma omp parallel for - for (idx_t qi = 0; qi < n; qi++) { - pq.compute_code_from_distance_table - (dis_tables + qi * pq.M * pq.ksub, - q_codes + qi * pq.code_size); - } - } - - size_t n_pass = 0; - -#pragma omp parallel for reduction (+: n_pass) - for (idx_t qi = 0; qi < n; qi++) { - const uint8_t * q_code = q_codes + qi * pq.code_size; - - const float * dis_table_qi = dis_tables + qi * pq.M * pq.ksub; - - int64_t * heap_ids = labels + qi * k; - float *heap_dis = distances + qi * k; - maxheap_heapify (k, heap_dis, heap_ids); - - if (search_type == ST_polysemous) { - - switch (pq.code_size) { - case 4: - n_pass += polysemous_inner_loop - (*this, dis_table_qi, q_code, k, heap_dis, heap_ids); - break; - case 8: - n_pass += polysemous_inner_loop - (*this, dis_table_qi, q_code, k, heap_dis, heap_ids); - break; - case 16: - n_pass += polysemous_inner_loop - (*this, dis_table_qi, q_code, k, heap_dis, heap_ids); - break; - case 32: - n_pass += polysemous_inner_loop - (*this, dis_table_qi, q_code, k, heap_dis, heap_ids); - break; - case 20: - n_pass += polysemous_inner_loop - (*this, dis_table_qi, q_code, k, heap_dis, heap_ids); - break; - default: - if (pq.code_size % 8 == 0) { - n_pass += polysemous_inner_loop - (*this, dis_table_qi, q_code, k, heap_dis, heap_ids); - } else if (pq.code_size % 4 == 0) { - n_pass += polysemous_inner_loop - (*this, dis_table_qi, q_code, k, heap_dis, heap_ids); - } else { - FAISS_THROW_FMT( - "code size %zd not supported for polysemous", - pq.code_size); - } - break; - } - } else { - switch (pq.code_size) { - case 8: - n_pass += polysemous_inner_loop - (*this, dis_table_qi, q_code, k, heap_dis, heap_ids); - break; - case 16: - n_pass += polysemous_inner_loop - (*this, dis_table_qi, q_code, k, heap_dis, heap_ids); - break; - case 32: - n_pass += polysemous_inner_loop - (*this, dis_table_qi, q_code, k, heap_dis, heap_ids); - break; - default: - if (pq.code_size % 8 == 0) { - n_pass += polysemous_inner_loop - (*this, dis_table_qi, q_code, k, heap_dis, heap_ids); - } else { - FAISS_THROW_FMT( - "code size %zd not supported for polysemous", - pq.code_size); - } - break; - } - } - maxheap_reorder (k, heap_dis, heap_ids); - } - - indexPQ_stats.nq += n; - indexPQ_stats.ncode += n * ntotal; - indexPQ_stats.n_hamming_pass += n_pass; - - -} - - -/* The standalone codec interface (just remaps to the PQ functions) */ -size_t IndexPQ::sa_code_size () const -{ - return pq.code_size; -} - -void IndexPQ::sa_encode (idx_t n, const float *x, uint8_t *bytes) const -{ - pq.compute_codes (x, bytes, n); -} - -void IndexPQ::sa_decode (idx_t n, const uint8_t *bytes, float *x) const -{ - pq.decode (bytes, x, n); -} - - - - -/***************************************** - * Stats of IndexPQ codes - ******************************************/ - - - - -void IndexPQ::hamming_distance_table (idx_t n, const float *x, - int32_t *dis) const -{ - uint8_t * q_codes = new uint8_t [n * pq.code_size]; - ScopeDeleter del (q_codes); - - pq.compute_codes (x, q_codes, n); - - hammings (q_codes, codes.data(), n, ntotal, pq.code_size, dis); -} - - -void IndexPQ::hamming_distance_histogram (idx_t n, const float *x, - idx_t nb, const float *xb, - int64_t *hist) -{ - FAISS_THROW_IF_NOT (metric_type == METRIC_L2); - FAISS_THROW_IF_NOT (pq.code_size % 8 == 0); - FAISS_THROW_IF_NOT (pq.nbits == 8); - - // Hamming embedding queries - uint8_t * q_codes = new uint8_t [n * pq.code_size]; - ScopeDeleter del (q_codes); - pq.compute_codes (x, q_codes, n); - - uint8_t * b_codes ; - ScopeDeleter del_b_codes; - - if (xb) { - b_codes = new uint8_t [nb * pq.code_size]; - del_b_codes.set (b_codes); - pq.compute_codes (xb, b_codes, nb); - } else { - nb = ntotal; - b_codes = codes.data(); - } - int nbits = pq.M * pq.nbits; - memset (hist, 0, sizeof(*hist) * (nbits + 1)); - size_t bs = 256; - -#pragma omp parallel - { - std::vector histi (nbits + 1); - hamdis_t *distances = new hamdis_t [nb * bs]; - ScopeDeleter del (distances); -#pragma omp for - for (size_t q0 = 0; q0 < n; q0 += bs) { - // printf ("dis stats: %ld/%ld\n", q0, n); - size_t q1 = q0 + bs; - if (q1 > n) q1 = n; - - hammings (q_codes + q0 * pq.code_size, b_codes, - q1 - q0, nb, - pq.code_size, distances); - - for (size_t i = 0; i < nb * (q1 - q0); i++) - histi [distances [i]]++; - } -#pragma omp critical - { - for (int i = 0; i <= nbits; i++) - hist[i] += histi[i]; - } - } - -} - - - - - - - - - - - - - - - - - - - - -/***************************************** - * MultiIndexQuantizer - ******************************************/ - -namespace { - -template -struct PreSortedArray { - - const T * x; - int N; - - explicit PreSortedArray (int N): N(N) { - } - void init (const T*x) { - this->x = x; - } - // get smallest value - T get_0 () { - return x[0]; - } - - // get delta between n-smallest and n-1 -smallest - T get_diff (int n) { - return x[n] - x[n - 1]; - } - - // remap orders counted from smallest to indices in array - int get_ord (int n) { - return n; - } - -}; - -template -struct ArgSort { - const T * x; - bool operator() (size_t i, size_t j) { - return x[i] < x[j]; - } -}; - - -/** Array that maintains a permutation of its elements so that the - * array's elements are sorted - */ -template -struct SortedArray { - const T * x; - int N; - std::vector perm; - - explicit SortedArray (int N) { - this->N = N; - perm.resize (N); - } - - void init (const T*x) { - this->x = x; - for (int n = 0; n < N; n++) - perm[n] = n; - ArgSort cmp = {x }; - std::sort (perm.begin(), perm.end(), cmp); - } - - // get smallest value - T get_0 () { - return x[perm[0]]; - } - - // get delta between n-smallest and n-1 -smallest - T get_diff (int n) { - return x[perm[n]] - x[perm[n - 1]]; - } - - // remap orders counted from smallest to indices in array - int get_ord (int n) { - return perm[n]; - } -}; - - - -/** Array has n values. Sort the k first ones and copy the other ones - * into elements k..n-1 - */ -template -void partial_sort (int k, int n, - const typename C::T * vals, typename C::TI * perm) { - // insert first k elts in heap - for (int i = 1; i < k; i++) { - indirect_heap_push (i + 1, vals, perm, perm[i]); - } - - // insert next n - k elts in heap - for (int i = k; i < n; i++) { - typename C::TI id = perm[i]; - typename C::TI top = perm[0]; - - if (C::cmp(vals[top], vals[id])) { - indirect_heap_pop (k, vals, perm); - indirect_heap_push (k, vals, perm, id); - perm[i] = top; - } else { - // nothing, elt at i is good where it is. - } - } - - // order the k first elements in heap - for (int i = k - 1; i > 0; i--) { - typename C::TI top = perm[0]; - indirect_heap_pop (i + 1, vals, perm); - perm[i] = top; - } -} - -/** same as SortedArray, but only the k first elements are sorted */ -template -struct SemiSortedArray { - const T * x; - int N; - - // type of the heap: CMax = sort ascending - typedef CMax HC; - std::vector perm; - - int k; // k elements are sorted - - int initial_k, k_factor; - - explicit SemiSortedArray (int N) { - this->N = N; - perm.resize (N); - perm.resize (N); - initial_k = 3; - k_factor = 4; - } - - void init (const T*x) { - this->x = x; - for (int n = 0; n < N; n++) - perm[n] = n; - k = 0; - grow (initial_k); - } - - /// grow the sorted part of the array to size next_k - void grow (int next_k) { - if (next_k < N) { - partial_sort (next_k - k, N - k, x, &perm[k]); - k = next_k; - } else { // full sort of remainder of array - ArgSort cmp = {x }; - std::sort (perm.begin() + k, perm.end(), cmp); - k = N; - } - } - - // get smallest value - T get_0 () { - return x[perm[0]]; - } - - // get delta between n-smallest and n-1 -smallest - T get_diff (int n) { - if (n >= k) { - // want to keep powers of 2 - 1 - int next_k = (k + 1) * k_factor - 1; - grow (next_k); - } - return x[perm[n]] - x[perm[n - 1]]; - } - - // remap orders counted from smallest to indices in array - int get_ord (int n) { - assert (n < k); - return perm[n]; - } -}; - - - -/***************************************** - * Find the k smallest sums of M terms, where each term is taken in a - * table x of n values. - * - * A combination of terms is encoded as a scalar 0 <= t < n^M. The - * combination t0 ... t(M-1) that correspond to the sum - * - * sum = x[0, t0] + x[1, t1] + .... + x[M-1, t(M-1)] - * - * is encoded as - * - * t = t0 + t1 * n + t2 * n^2 + ... + t(M-1) * n^(M-1) - * - * MinSumK is an object rather than a function, so that storage can be - * re-used over several computations with the same sizes. use_seen is - * good when there may be ties in the x array and it is a concern if - * occasionally several t's are returned. - * - * @param x size M * n, values to add up - * @parms k nb of results to retrieve - * @param M nb of terms - * @param n nb of distinct values - * @param sums output, size k, sorted - * @prarm terms output, size k, with encoding as above - * - ******************************************/ -template -struct MinSumK { - int K; ///< nb of sums to return - int M; ///< nb of elements to sum up - int nbit; ///< nb of bits to encode one entry - int N; ///< nb of possible elements for each of the M terms - - /** the heap. - * We use a heap to maintain a queue of sums, with the associated - * terms involved in the sum. - */ - typedef CMin HC; - size_t heap_capacity, heap_size; - T *bh_val; - int64_t *bh_ids; - - std::vector ssx; - - // all results get pushed several times. When there are ties, they - // are popped interleaved with others, so it is not easy to - // identify them. Therefore, this bit array just marks elements - // that were seen before. - std::vector seen; - - MinSumK (int K, int M, int nbit, int N): - K(K), M(M), nbit(nbit), N(N) { - heap_capacity = K * M; - assert (N <= (1 << nbit)); - - // we'll do k steps, each step pushes at most M vals - bh_val = new T[heap_capacity]; - bh_ids = new int64_t[heap_capacity]; - - if (use_seen) { - int64_t n_ids = weight(M); - seen.resize ((n_ids + 7) / 8); - } - - for (int m = 0; m < M; m++) - ssx.push_back (SSA(N)); - - } - - int64_t weight (int i) { - return 1 << (i * nbit); - } - - bool is_seen (int64_t i) { - return (seen[i >> 3] >> (i & 7)) & 1; - } - - void mark_seen (int64_t i) { - if (use_seen) - seen [i >> 3] |= 1 << (i & 7); - } - - void run (const T *x, int64_t ldx, - T * sums, int64_t * terms) { - heap_size = 0; - - for (int m = 0; m < M; m++) { - ssx[m].init(x); - x += ldx; - } - - { // intial result: take min for all elements - T sum = 0; - terms[0] = 0; - mark_seen (0); - for (int m = 0; m < M; m++) { - sum += ssx[m].get_0(); - } - sums[0] = sum; - for (int m = 0; m < M; m++) { - heap_push (++heap_size, bh_val, bh_ids, - sum + ssx[m].get_diff(1), - weight(m)); - } - } - - for (int k = 1; k < K; k++) { - // pop smallest value from heap - if (use_seen) {// skip already seen elements - while (is_seen (bh_ids[0])) { - assert (heap_size > 0); - heap_pop (heap_size--, bh_val, bh_ids); - } - } - assert (heap_size > 0); - - T sum = sums[k] = bh_val[0]; - int64_t ti = terms[k] = bh_ids[0]; - - if (use_seen) { - mark_seen (ti); - heap_pop (heap_size--, bh_val, bh_ids); - } else { - do { - heap_pop (heap_size--, bh_val, bh_ids); - } while (heap_size > 0 && bh_ids[0] == ti); - } - - // enqueue followers - int64_t ii = ti; - for (int m = 0; m < M; m++) { - int64_t n = ii & ((1L << nbit) - 1); - ii >>= nbit; - if (n + 1 >= N) continue; - - enqueue_follower (ti, m, n, sum); - } - } - - /* - for (int k = 0; k < K; k++) - for (int l = k + 1; l < K; l++) - assert (terms[k] != terms[l]); - */ - - // convert indices by applying permutation - for (int k = 0; k < K; k++) { - int64_t ii = terms[k]; - if (use_seen) { - // clear seen for reuse at next loop - seen[ii >> 3] = 0; - } - int64_t ti = 0; - for (int m = 0; m < M; m++) { - int64_t n = ii & ((1L << nbit) - 1); - ti += int64_t(ssx[m].get_ord(n)) << (nbit * m); - ii >>= nbit; - } - terms[k] = ti; - } - } - - - void enqueue_follower (int64_t ti, int m, int n, T sum) { - T next_sum = sum + ssx[m].get_diff(n + 1); - int64_t next_ti = ti + weight(m); - heap_push (++heap_size, bh_val, bh_ids, next_sum, next_ti); - } - - ~MinSumK () { - delete [] bh_ids; - delete [] bh_val; - } -}; - -} // anonymous namespace - - -MultiIndexQuantizer::MultiIndexQuantizer (int d, - size_t M, - size_t nbits): - Index(d, METRIC_L2), pq(d, M, nbits) -{ - is_trained = false; - pq.verbose = verbose; -} - - - -void MultiIndexQuantizer::train(idx_t n, const float *x) -{ - pq.verbose = verbose; - pq.train (n, x); - is_trained = true; - // count virtual elements in index - ntotal = 1; - for (int m = 0; m < pq.M; m++) - ntotal *= pq.ksub; -} - - -void MultiIndexQuantizer::search (idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, - ConcurrentBitsetPtr bitset) const { - if (n == 0) return; - - // the allocation just below can be severe... - idx_t bs = 32768; - if (n > bs) { - for (idx_t i0 = 0; i0 < n; i0 += bs) { - idx_t i1 = std::min(i0 + bs, n); - if (verbose) { - printf("MultiIndexQuantizer::search: %ld:%ld / %ld\n", - i0, i1, n); - } - search (i1 - i0, x + i0 * d, k, - distances + i0 * k, - labels + i0 * k); - } - return; - } - - float * dis_tables = new float [n * pq.ksub * pq.M]; - ScopeDeleter del (dis_tables); - - pq.compute_distance_tables (n, x, dis_tables); - - if (k == 1) { - // simple version that just finds the min in each table - -#pragma omp parallel for - for (int i = 0; i < n; i++) { - const float * dis_table = dis_tables + i * pq.ksub * pq.M; - float dis = 0; - idx_t label = 0; - - for (int s = 0; s < pq.M; s++) { - float vmin = HUGE_VALF; - idx_t lmin = -1; - - for (idx_t j = 0; j < pq.ksub; j++) { - if (dis_table[j] < vmin) { - vmin = dis_table[j]; - lmin = j; - } - } - dis += vmin; - label |= lmin << (s * pq.nbits); - dis_table += pq.ksub; - } - - distances [i] = dis; - labels [i] = label; - } - - - } else { - -#pragma omp parallel if(n > 1) - { - MinSumK , false> - msk(k, pq.M, pq.nbits, pq.ksub); -#pragma omp for - for (int i = 0; i < n; i++) { - msk.run (dis_tables + i * pq.ksub * pq.M, pq.ksub, - distances + i * k, labels + i * k); - - } - } - } - -} - - -void MultiIndexQuantizer::reconstruct (idx_t key, float * recons) const -{ - - int64_t jj = key; - for (int m = 0; m < pq.M; m++) { - int64_t n = jj & ((1L << pq.nbits) - 1); - jj >>= pq.nbits; - memcpy(recons, pq.get_centroids(m, n), sizeof(recons[0]) * pq.dsub); - recons += pq.dsub; - } -} - -void MultiIndexQuantizer::add(idx_t /*n*/, const float* /*x*/) { - FAISS_THROW_MSG( - "This index has virtual elements, " - "it does not support add"); -} - -void MultiIndexQuantizer::reset () -{ - FAISS_THROW_MSG ( "This index has virtual elements, " - "it does not support reset"); -} - - - - - - - - - - -/***************************************** - * MultiIndexQuantizer2 - ******************************************/ - - - -MultiIndexQuantizer2::MultiIndexQuantizer2 ( - int d, size_t M, size_t nbits, - Index **indexes): - MultiIndexQuantizer (d, M, nbits) -{ - assign_indexes.resize (M); - for (int i = 0; i < M; i++) { - FAISS_THROW_IF_NOT_MSG( - indexes[i]->d == pq.dsub, - "Provided sub-index has incorrect size"); - assign_indexes[i] = indexes[i]; - } - own_fields = false; -} - -MultiIndexQuantizer2::MultiIndexQuantizer2 ( - int d, size_t nbits, - Index *assign_index_0, - Index *assign_index_1): - MultiIndexQuantizer (d, 2, nbits) -{ - FAISS_THROW_IF_NOT_MSG( - assign_index_0->d == pq.dsub && - assign_index_1->d == pq.dsub, - "Provided sub-index has incorrect size"); - assign_indexes.resize (2); - assign_indexes [0] = assign_index_0; - assign_indexes [1] = assign_index_1; - own_fields = false; -} - -void MultiIndexQuantizer2::train(idx_t n, const float* x) -{ - MultiIndexQuantizer::train(n, x); - // add centroids to sub-indexes - for (int i = 0; i < pq.M; i++) { - assign_indexes[i]->add(pq.ksub, pq.get_centroids(i, 0)); - } -} - - -void MultiIndexQuantizer2::search( - idx_t n, const float* x, idx_t K, - float* distances, idx_t* labels, - ConcurrentBitsetPtr bitset) const -{ - - if (n == 0) return; - - int k2 = std::min(K, int64_t(pq.ksub)); - - int64_t M = pq.M; - int64_t dsub = pq.dsub, ksub = pq.ksub; - - // size (M, n, k2) - std::vector sub_ids(n * M * k2); - std::vector sub_dis(n * M * k2); - std::vector xsub(n * dsub); - - for (int m = 0; m < M; m++) { - float *xdest = xsub.data(); - const float *xsrc = x + m * dsub; - for (int j = 0; j < n; j++) { - memcpy(xdest, xsrc, dsub * sizeof(xdest[0])); - xsrc += d; - xdest += dsub; - } - - assign_indexes[m]->search( - n, xsub.data(), k2, - &sub_dis[k2 * n * m], - &sub_ids[k2 * n * m]); - } - - if (K == 1) { - // simple version that just finds the min in each table - assert (k2 == 1); - - for (int i = 0; i < n; i++) { - float dis = 0; - idx_t label = 0; - - for (int m = 0; m < M; m++) { - float vmin = sub_dis[i + m * n]; - idx_t lmin = sub_ids[i + m * n]; - dis += vmin; - label |= lmin << (m * pq.nbits); - } - distances [i] = dis; - labels [i] = label; - } - - } else { - -#pragma omp parallel if(n > 1) - { - MinSumK , false> - msk(K, pq.M, pq.nbits, k2); -#pragma omp for - for (int i = 0; i < n; i++) { - idx_t *li = labels + i * K; - msk.run (&sub_dis[i * k2], k2 * n, - distances + i * K, li); - - // remap ids - - const idx_t *idmap0 = sub_ids.data() + i * k2; - int64_t ld_idmap = k2 * n; - int64_t mask1 = ksub - 1L; - - for (int k = 0; k < K; k++) { - const idx_t *idmap = idmap0; - int64_t vin = li[k]; - int64_t vout = 0; - int bs = 0; - for (int m = 0; m < M; m++) { - int64_t s = vin & mask1; - vin >>= pq.nbits; - vout |= idmap[s] << bs; - bs += pq.nbits; - idmap += ld_idmap; - } - li[k] = vout; - } - } - } - } -} - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexPQ.h b/core/src/index/thirdparty/faiss/IndexPQ.h deleted file mode 100644 index 25a643efe2..0000000000 --- a/core/src/index/thirdparty/faiss/IndexPQ.h +++ /dev/null @@ -1,202 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_INDEX_PQ_H -#define FAISS_INDEX_PQ_H - -#include - -#include - -#include -#include -#include - -namespace faiss { - - -/** Index based on a product quantizer. Stored vectors are - * approximated by PQ codes. */ -struct IndexPQ: Index { - - /// The product quantizer used to encode the vectors - ProductQuantizer pq; - - /// Codes. Size ntotal * pq.code_size - std::vector codes; - - /** Constructor. - * - * @param d dimensionality of the input vectors - * @param M number of subquantizers - * @param nbits number of bit per subvector index - */ - IndexPQ (int d, ///< dimensionality of the input vectors - size_t M, ///< number of subquantizers - size_t nbits, ///< number of bit per subvector index - MetricType metric = METRIC_L2); - - IndexPQ (); - - void train(idx_t n, const float* x) override; - - void add(idx_t n, const float* x) override; - - void search( - idx_t n, - const float* x, - idx_t k, - float* distances, - idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - void reset() override; - - void reconstruct_n(idx_t i0, idx_t ni, float* recons) const override; - - void reconstruct(idx_t key, float* recons) const override; - - size_t remove_ids(const IDSelector& sel) override; - - /* The standalone codec interface */ - size_t sa_code_size () const override; - - void sa_encode (idx_t n, const float *x, - uint8_t *bytes) const override; - - void sa_decode (idx_t n, const uint8_t *bytes, - float *x) const override; - - - DistanceComputer * get_distance_computer() const override; - - /****************************************************** - * Polysemous codes implementation - ******************************************************/ - bool do_polysemous_training; ///< false = standard PQ - - /// parameters used for the polysemous training - PolysemousTraining polysemous_training; - - /// how to perform the search in search_core - enum Search_type_t { - ST_PQ, ///< asymmetric product quantizer (default) - ST_HE, ///< Hamming distance on codes - ST_generalized_HE, ///< nb of same codes - ST_SDC, ///< symmetric product quantizer (SDC) - ST_polysemous, ///< HE filter (using ht) + PQ combination - ST_polysemous_generalize, ///< Filter on generalized Hamming - }; - - Search_type_t search_type; - - // just encode the sign of the components, instead of using the PQ encoder - // used only for the queries - bool encode_signs; - - /// Hamming threshold used for polysemy - int polysemous_ht; - - // actual polysemous search - void search_core_polysemous (idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels) const; - - /// prepare query for a polysemous search, but instead of - /// computing the result, just get the histogram of Hamming - /// distances. May be computed on a provided dataset if xb != NULL - /// @param dist_histogram (M * nbits + 1) - void hamming_distance_histogram (idx_t n, const float *x, - idx_t nb, const float *xb, - int64_t *dist_histogram); - - /** compute pairwise distances between queries and database - * - * @param n nb of query vectors - * @param x query vector, size n * d - * @param dis output distances, size n * ntotal - */ - void hamming_distance_table (idx_t n, const float *x, - int32_t *dis) const; - -}; - - -/// statistics are robust to internal threading, but not if -/// IndexPQ::search is called by multiple threads -struct IndexPQStats { - size_t nq; // nb of queries run - size_t ncode; // nb of codes visited - - size_t n_hamming_pass; // nb of passed Hamming distance tests (for polysemy) - - IndexPQStats () {reset (); } - void reset (); -}; - -extern IndexPQStats indexPQ_stats; - - - -/** Quantizer where centroids are virtual: they are the Cartesian - * product of sub-centroids. */ -struct MultiIndexQuantizer: Index { - ProductQuantizer pq; - - MultiIndexQuantizer (int d, ///< dimension of the input vectors - size_t M, ///< number of subquantizers - size_t nbits); ///< number of bit per subvector index - - void train(idx_t n, const float* x) override; - - void search( - idx_t n, const float* x, idx_t k, - float* distances, idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - /// add and reset will crash at runtime - void add(idx_t n, const float* x) override; - void reset() override; - - MultiIndexQuantizer () {} - - void reconstruct(idx_t key, float* recons) const override; -}; - - -/** MultiIndexQuantizer where the PQ assignmnet is performed by sub-indexes - */ -struct MultiIndexQuantizer2: MultiIndexQuantizer { - - /// M Indexes on d / M dimensions - std::vector assign_indexes; - bool own_fields; - - MultiIndexQuantizer2 ( - int d, size_t M, size_t nbits, - Index **indexes); - - MultiIndexQuantizer2 ( - int d, size_t nbits, - Index *assign_index_0, - Index *assign_index_1); - - void train(idx_t n, const float* x) override; - - void search( - idx_t n, const float* x, idx_t k, - float* distances, idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - -}; - - -} // namespace faiss - - -#endif diff --git a/core/src/index/thirdparty/faiss/IndexPreTransform.cpp b/core/src/index/thirdparty/faiss/IndexPreTransform.cpp deleted file mode 100644 index 9172978df9..0000000000 --- a/core/src/index/thirdparty/faiss/IndexPreTransform.cpp +++ /dev/null @@ -1,289 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include -#include -#include - -#include - -namespace faiss { - -/********************************************* - * IndexPreTransform - *********************************************/ - -IndexPreTransform::IndexPreTransform (): - index(nullptr), own_fields (false) -{ -} - - -IndexPreTransform::IndexPreTransform ( - Index * index): - Index (index->d, index->metric_type), - index (index), own_fields (false) -{ - is_trained = index->is_trained; - ntotal = index->ntotal; -} - - -IndexPreTransform::IndexPreTransform ( - VectorTransform * ltrans, - Index * index): - Index (index->d, index->metric_type), - index (index), own_fields (false) -{ - is_trained = index->is_trained; - ntotal = index->ntotal; - prepend_transform (ltrans); -} - -void IndexPreTransform::prepend_transform (VectorTransform *ltrans) -{ - FAISS_THROW_IF_NOT (ltrans->d_out == d); - is_trained = is_trained && ltrans->is_trained; - chain.insert (chain.begin(), ltrans); - d = ltrans->d_in; -} - - -IndexPreTransform::~IndexPreTransform () -{ - if (own_fields) { - for (int i = 0; i < chain.size(); i++) - delete chain[i]; - delete index; - } -} - - - - -void IndexPreTransform::train (idx_t n, const float *x) -{ - int last_untrained = 0; - if (!index->is_trained) { - last_untrained = chain.size(); - } else { - for (int i = chain.size() - 1; i >= 0; i--) { - if (!chain[i]->is_trained) { - last_untrained = i; - break; - } - } - } - const float *prev_x = x; - ScopeDeleter del; - - if (verbose) { - printf("IndexPreTransform::train: training chain 0 to %d\n", - last_untrained); - } - - for (int i = 0; i <= last_untrained; i++) { - - if (i < chain.size()) { - VectorTransform *ltrans = chain [i]; - if (!ltrans->is_trained) { - if (verbose) { - printf(" Training chain component %d/%zd\n", - i, chain.size()); - if (OPQMatrix *opqm = dynamic_cast(ltrans)) { - opqm->verbose = true; - } - } - ltrans->train (n, prev_x); - } - } else { - if (verbose) { - printf(" Training sub-index\n"); - } - index->train (n, prev_x); - } - if (i == last_untrained) break; - if (verbose) { - printf(" Applying transform %d/%zd\n", - i, chain.size()); - } - - float * xt = chain[i]->apply (n, prev_x); - - if (prev_x != x) delete [] prev_x; - prev_x = xt; - del.set(xt); - } - - is_trained = true; -} - - -const float *IndexPreTransform::apply_chain (idx_t n, const float *x) const -{ - const float *prev_x = x; - ScopeDeleter del; - - for (int i = 0; i < chain.size(); i++) { - float * xt = chain[i]->apply (n, prev_x); - ScopeDeleter del2 (xt); - del2.swap (del); - prev_x = xt; - } - del.release (); - return prev_x; -} - -void IndexPreTransform::reverse_chain (idx_t n, const float* xt, float* x) const -{ - const float* next_x = xt; - ScopeDeleter del; - - for (int i = chain.size() - 1; i >= 0; i--) { - float* prev_x = (i == 0) ? x : new float [n * chain[i]->d_in]; - ScopeDeleter del2 ((prev_x == x) ? nullptr : prev_x); - chain [i]->reverse_transform (n, next_x, prev_x); - del2.swap (del); - next_x = prev_x; - } -} - -void IndexPreTransform::add (idx_t n, const float *x) -{ - FAISS_THROW_IF_NOT (is_trained); - const float *xt = apply_chain (n, x); - ScopeDeleter del(xt == x ? nullptr : xt); - index->add (n, xt); - ntotal = index->ntotal; -} - -void IndexPreTransform::add_with_ids (idx_t n, const float * x, - const idx_t *xids) -{ - FAISS_THROW_IF_NOT (is_trained); - const float *xt = apply_chain (n, x); - ScopeDeleter del(xt == x ? nullptr : xt); - index->add_with_ids (n, xt, xids); - ntotal = index->ntotal; -} - - - - -void IndexPreTransform::search (idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, - ConcurrentBitsetPtr bitset) const -{ - FAISS_THROW_IF_NOT (is_trained); - const float *xt = apply_chain (n, x); - ScopeDeleter del(xt == x ? nullptr : xt); - index->search (n, xt, k, distances, labels); -} - -void IndexPreTransform::range_search (idx_t n, const float* x, float radius, - RangeSearchResult* result, - ConcurrentBitsetPtr bitset) const -{ - FAISS_THROW_IF_NOT (is_trained); - const float *xt = apply_chain (n, x); - ScopeDeleter del(xt == x ? nullptr : xt); - index->range_search (n, xt, radius, result); -} - - - -void IndexPreTransform::reset () { - index->reset(); - ntotal = 0; -} - -size_t IndexPreTransform::remove_ids (const IDSelector & sel) { - size_t nremove = index->remove_ids (sel); - ntotal = index->ntotal; - return nremove; -} - - -void IndexPreTransform::reconstruct (idx_t key, float * recons) const -{ - float *x = chain.empty() ? recons : new float [index->d]; - ScopeDeleter del (recons == x ? nullptr : x); - // Initial reconstruction - index->reconstruct (key, x); - - // Revert transformations from last to first - reverse_chain (1, x, recons); -} - - -void IndexPreTransform::reconstruct_n (idx_t i0, idx_t ni, float *recons) const -{ - float *x = chain.empty() ? recons : new float [ni * index->d]; - ScopeDeleter del (recons == x ? nullptr : x); - // Initial reconstruction - index->reconstruct_n (i0, ni, x); - - // Revert transformations from last to first - reverse_chain (ni, x, recons); -} - - -void IndexPreTransform::search_and_reconstruct ( - idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, float* recons) const -{ - FAISS_THROW_IF_NOT (is_trained); - - const float* xt = apply_chain (n, x); - ScopeDeleter del ((xt == x) ? nullptr : xt); - - float* recons_temp = chain.empty() ? recons : new float [n * k * index->d]; - ScopeDeleter del2 ((recons_temp == recons) ? nullptr : recons_temp); - index->search_and_reconstruct (n, xt, k, distances, labels, recons_temp); - - // Revert transformations from last to first - reverse_chain (n * k, recons_temp, recons); -} - -size_t IndexPreTransform::sa_code_size () const -{ - return index->sa_code_size (); -} - -void IndexPreTransform::sa_encode (idx_t n, const float *x, - uint8_t *bytes) const -{ - if (chain.empty()) { - index->sa_encode (n, x, bytes); - } else { - const float *xt = apply_chain (n, x); - ScopeDeleter del(xt == x ? nullptr : xt); - index->sa_encode (n, xt, bytes); - } -} - -void IndexPreTransform::sa_decode (idx_t n, const uint8_t *bytes, - float *x) const -{ - if (chain.empty()) { - index->sa_decode (n, bytes, x); - } else { - std::unique_ptr x1 (new float [index->d * n]); - index->sa_decode (n, bytes, x1.get()); - // Revert transformations from last to first - reverse_chain (n, x1.get(), x); - } -} - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexPreTransform.h b/core/src/index/thirdparty/faiss/IndexPreTransform.h deleted file mode 100644 index 605ada9fa4..0000000000 --- a/core/src/index/thirdparty/faiss/IndexPreTransform.h +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#pragma once - - - -#include -#include - -namespace faiss { - -/** Index that applies a LinearTransform transform on vectors before - * handing them over to a sub-index */ -struct IndexPreTransform: Index { - - std::vector chain; ///! chain of tranforms - Index * index; ///! the sub-index - - bool own_fields; ///! whether pointers are deleted in destructor - - explicit IndexPreTransform (Index *index); - - IndexPreTransform (); - - /// ltrans is the last transform before the index - IndexPreTransform (VectorTransform * ltrans, Index * index); - - void prepend_transform (VectorTransform * ltrans); - - void train(idx_t n, const float* x) override; - - void add(idx_t n, const float* x) override; - - void add_with_ids(idx_t n, const float* x, const idx_t* xids) override; - - void reset() override; - - /** removes IDs from the index. Not supported by all indexes. - */ - size_t remove_ids(const IDSelector& sel) override; - - void search( - idx_t n, - const float* x, - idx_t k, - float* distances, - idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - - /* range search, no attempt is done to change the radius */ - void range_search (idx_t n, const float* x, float radius, - RangeSearchResult* result, - ConcurrentBitsetPtr bitset = nullptr) const override; - - - void reconstruct (idx_t key, float * recons) const override; - - void reconstruct_n (idx_t i0, idx_t ni, float *recons) - const override; - - void search_and_reconstruct (idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, - float *recons) const override; - - /// apply the transforms in the chain. The returned float * may be - /// equal to x, otherwise it should be deallocated. - const float * apply_chain (idx_t n, const float *x) const; - - /// Reverse the transforms in the chain. May not be implemented for - /// all transforms in the chain or may return approximate results. - void reverse_chain (idx_t n, const float* xt, float* x) const; - - - /* standalone codec interface */ - size_t sa_code_size () const override; - void sa_encode (idx_t n, const float *x, - uint8_t *bytes) const override; - void sa_decode (idx_t n, const uint8_t *bytes, - float *x) const override; - - ~IndexPreTransform() override; -}; - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexReplicas.cpp b/core/src/index/thirdparty/faiss/IndexReplicas.cpp deleted file mode 100644 index 8749ab6cc5..0000000000 --- a/core/src/index/thirdparty/faiss/IndexReplicas.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -namespace faiss { - -template -IndexReplicasTemplate::IndexReplicasTemplate(bool threaded) - : ThreadedIndex(threaded) { -} - -template -IndexReplicasTemplate::IndexReplicasTemplate(idx_t d, bool threaded) - : ThreadedIndex(d, threaded) { -} - -template -IndexReplicasTemplate::IndexReplicasTemplate(int d, bool threaded) - : ThreadedIndex(d, threaded) { -} - -template -void -IndexReplicasTemplate::onAfterAddIndex(IndexT* index) { - // Make sure that the parameters are the same for all prior indices, unless - // we're the first index to be added - if (this->count() > 0 && this->at(0) != index) { - auto existing = this->at(0); - - FAISS_THROW_IF_NOT_FMT(index->ntotal == existing->ntotal, - "IndexReplicas: newly added index does " - "not have same number of vectors as prior index; " - "prior index has %ld vectors, new index has %ld", - existing->ntotal, index->ntotal); - - FAISS_THROW_IF_NOT_MSG(index->is_trained == existing->is_trained, - "IndexReplicas: newly added index does " - "not have same train status as prior index"); - } else { - // Set our parameters based on the first index we're adding - // (dimension is handled in ThreadedIndex) - this->ntotal = index->ntotal; - this->verbose = index->verbose; - this->is_trained = index->is_trained; - this->metric_type = index->metric_type; - } -} - -template -void -IndexReplicasTemplate::train(idx_t n, const component_t* x) { - this->runOnIndex([n, x](int, IndexT* index){ index->train(n, x); }); -} - -template -void -IndexReplicasTemplate::add(idx_t n, const component_t* x) { - this->runOnIndex([n, x](int, IndexT* index){ index->add(n, x); }); - this->ntotal += n; -} - -template -void -IndexReplicasTemplate::reconstruct(idx_t n, component_t* x) const { - FAISS_THROW_IF_NOT_MSG(this->count() > 0, "no replicas in index"); - - // Just pass to the first replica - this->at(0)->reconstruct(n, x); -} - -template -void -IndexReplicasTemplate::search(idx_t n, - const component_t* x, - idx_t k, - distance_t* distances, - idx_t* labels, - ConcurrentBitsetPtr bitset) const { - FAISS_THROW_IF_NOT_MSG(this->count() > 0, "no replicas in index"); - - if (n == 0) { - return; - } - - auto dim = this->d; - size_t componentsPerVec = - sizeof(component_t) == 1 ? (dim + 7) / 8 : dim; - - // Partition the query by the number of indices we have - faiss::Index::idx_t queriesPerIndex = - (faiss::Index::idx_t) (n + this->count() - 1) / - (faiss::Index::idx_t) this->count(); - FAISS_ASSERT(n / queriesPerIndex <= this->count()); - - auto fn = - [queriesPerIndex, componentsPerVec, - n, x, k, distances, labels](int i, const IndexT* index) { - faiss::Index::idx_t base = (faiss::Index::idx_t) i * queriesPerIndex; - - if (base < n) { - auto numForIndex = std::min(queriesPerIndex, n - base); - - index->search(numForIndex, - x + base * componentsPerVec, - k, - distances + base * k, - labels + base * k); - } - }; - - this->runOnIndex(fn); -} - -// explicit instantiations -template struct IndexReplicasTemplate; -template struct IndexReplicasTemplate; - -} // namespace diff --git a/core/src/index/thirdparty/faiss/IndexReplicas.h b/core/src/index/thirdparty/faiss/IndexReplicas.h deleted file mode 100644 index a98c28cea5..0000000000 --- a/core/src/index/thirdparty/faiss/IndexReplicas.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include - -namespace faiss { - -/// Takes individual faiss::Index instances, and splits queries for -/// sending to each Index instance, and joins the results together -/// when done. -/// Each index is managed by a separate CPU thread. -template -class IndexReplicasTemplate : public ThreadedIndex { - public: - using idx_t = typename IndexT::idx_t; - using component_t = typename IndexT::component_t; - using distance_t = typename IndexT::distance_t; - - /// The dimension that all sub-indices must share will be the dimension of the - /// first sub-index added - /// @param threaded do we use one thread per sub-index or do queries - /// sequentially? - explicit IndexReplicasTemplate(bool threaded = true); - - /// @param d the dimension that all sub-indices must share - /// @param threaded do we use one thread per sub index or do queries - /// sequentially? - explicit IndexReplicasTemplate(idx_t d, bool threaded = true); - - /// int version due to the implicit bool conversion ambiguity of int as - /// dimension - explicit IndexReplicasTemplate(int d, bool threaded = true); - - /// Alias for addIndex() - void add_replica(IndexT* index) { this->addIndex(index); } - - /// Alias for removeIndex() - void remove_replica(IndexT* index) { this->removeIndex(index); } - - /// faiss::Index API - /// All indices receive the same call - void train(idx_t n, const component_t* x) override; - - /// faiss::Index API - /// All indices receive the same call - void add(idx_t n, const component_t* x) override; - - /// faiss::Index API - /// Query is partitioned into a slice for each sub-index - /// split by ceil(n / #indices) for our sub-indices - void search(idx_t n, - const component_t* x, - idx_t k, - distance_t* distances, - idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - /// reconstructs from the first index - void reconstruct(idx_t, component_t *v) const override; - - protected: - /// Called just after an index is added - void onAfterAddIndex(IndexT* index) override; -}; - -using IndexReplicas = IndexReplicasTemplate; -using IndexBinaryReplicas = IndexReplicasTemplate; - -} // namespace diff --git a/core/src/index/thirdparty/faiss/IndexSQHybrid.cpp b/core/src/index/thirdparty/faiss/IndexSQHybrid.cpp deleted file mode 100644 index 8376ca8b31..0000000000 --- a/core/src/index/thirdparty/faiss/IndexSQHybrid.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include -#include - -#include - -#include -#include -#include -#include -#include - -namespace faiss { - -/******************************************************************* - * IndexIVFSQHybrid implementation - ********************************************************************/ - -IndexIVFSQHybrid::IndexIVFSQHybrid ( - Index *quantizer, size_t d, size_t nlist, - QuantizerType qtype, - MetricType metric, bool encode_residual) - : IndexIVF(quantizer, d, nlist, 0, metric), - sq(d, qtype), - by_residual(encode_residual) -{ - code_size = sq.code_size; - // was not known at construction time - invlists->code_size = code_size; - is_trained = false; -} - -IndexIVFSQHybrid::IndexIVFSQHybrid (): - IndexIVF(), - by_residual(true) -{ -} - -void IndexIVFSQHybrid::train_residual (idx_t n, const float *x) -{ - sq.train_residual(n, x, quantizer, by_residual, verbose); -} - -void IndexIVFSQHybrid::encode_vectors(idx_t n, const float* x, - const idx_t *list_nos, - uint8_t * codes, - bool include_listnos) const -{ - std::unique_ptr squant (sq.select_quantizer ()); - size_t coarse_size = include_listnos ? coarse_code_size () : 0; - memset(codes, 0, (code_size + coarse_size) * n); - -#pragma omp parallel if(n > 1) - { - std::vector residual (d); - -#pragma omp for - for (size_t i = 0; i < n; i++) { - int64_t list_no = list_nos [i]; - if (list_no >= 0) { - const float *xi = x + i * d; - uint8_t *code = codes + i * (code_size + coarse_size); - if (by_residual) { - quantizer->compute_residual ( - xi, residual.data(), list_no); - xi = residual.data (); - } - if (coarse_size) { - encode_listno (list_no, code); - } - squant->encode_vector (xi, code + coarse_size); - } - } - } -} - -void IndexIVFSQHybrid::sa_decode (idx_t n, const uint8_t *codes, - float *x) const -{ - std::unique_ptr squant (sq.select_quantizer ()); - size_t coarse_size = coarse_code_size (); - -#pragma omp parallel if(n > 1) - { - std::vector residual (d); - -#pragma omp for - for (size_t i = 0; i < n; i++) { - const uint8_t *code = codes + i * (code_size + coarse_size); - int64_t list_no = decode_listno (code); - float *xi = x + i * d; - squant->decode_vector (code + coarse_size, xi); - if (by_residual) { - quantizer->reconstruct (list_no, residual.data()); - for (size_t j = 0; j < d; j++) { - xi[j] += residual[j]; - } - } - } - } -} - - - -void IndexIVFSQHybrid::add_with_ids - (idx_t n, const float * x, const idx_t *xids) -{ - FAISS_THROW_IF_NOT (is_trained); - std::unique_ptr idx (new int64_t [n]); - quantizer->assign (n, x, idx.get()); - size_t nadd = 0; - std::unique_ptr squant(sq.select_quantizer ()); - -#pragma omp parallel reduction(+: nadd) - { - std::vector residual (d); - std::vector one_code (code_size); - int nt = omp_get_num_threads(); - int rank = omp_get_thread_num(); - - // each thread takes care of a subset of lists - for (size_t i = 0; i < n; i++) { - int64_t list_no = idx [i]; - if (list_no >= 0 && list_no % nt == rank) { - int64_t id = xids ? xids[i] : ntotal + i; - - const float * xi = x + i * d; - if (by_residual) { - quantizer->compute_residual (xi, residual.data(), list_no); - xi = residual.data(); - } - - memset (one_code.data(), 0, code_size); - squant->encode_vector (xi, one_code.data()); - - invlists->add_entry (list_no, id, one_code.data()); - - nadd++; - - } - } - } - ntotal += n; -} - - - - - -InvertedListScanner* IndexIVFSQHybrid::get_InvertedListScanner - (bool store_pairs) const -{ - return sq.select_InvertedListScanner (metric_type, quantizer, store_pairs, - by_residual); -} - - -void IndexIVFSQHybrid::reconstruct_from_offset (int64_t list_no, - int64_t offset, - float* recons) const -{ - std::vector centroid(d); - quantizer->reconstruct (list_no, centroid.data()); - - const uint8_t* code = invlists->get_single_code (list_no, offset); - sq.decode (code, recons, 1); - for (int i = 0; i < d; ++i) { - recons[i] += centroid[i]; - } -} - - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexSQHybrid.h b/core/src/index/thirdparty/faiss/IndexSQHybrid.h deleted file mode 100644 index c3bf599b08..0000000000 --- a/core/src/index/thirdparty/faiss/IndexSQHybrid.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_INDEX_SQ_HYBRID_H -#define FAISS_INDEX_SQ_HYBRID_H - -#include -#include - -#include -#include -#include - - -namespace faiss { - - /** An IVF implementation where the components of the residuals are - * encoded with a scalar uniform quantizer. All distance computations - * are asymmetric, so the encoded vectors are decoded and approximate - * distances are computed. - */ - -struct IndexIVFSQHybrid: IndexIVF { - ScalarQuantizer sq; - bool by_residual; - - IndexIVFSQHybrid(Index *quantizer, size_t d, size_t nlist, - QuantizerType qtype, - MetricType metric = METRIC_L2, - bool encode_residual = true); - - IndexIVFSQHybrid(); - - void train_residual(idx_t n, const float* x) override; - - void encode_vectors(idx_t n, const float* x, - const idx_t *list_nos, - uint8_t * codes, - bool include_listnos=false) const override; - - void add_with_ids(idx_t n, const float* x, const idx_t* xids) override; - - InvertedListScanner *get_InvertedListScanner (bool store_pairs) - const override; - - - void reconstruct_from_offset (int64_t list_no, int64_t offset, - float* recons) const override; - - /* standalone codec interface */ - void sa_decode (idx_t n, const uint8_t *bytes, - float *x) const override; - -}; - - -} - - -#endif diff --git a/core/src/index/thirdparty/faiss/IndexScalarQuantizer.cpp b/core/src/index/thirdparty/faiss/IndexScalarQuantizer.cpp deleted file mode 100644 index d96612daef..0000000000 --- a/core/src/index/thirdparty/faiss/IndexScalarQuantizer.cpp +++ /dev/null @@ -1,325 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include - -namespace faiss { - - - -/******************************************************************* - * IndexScalarQuantizer implementation - ********************************************************************/ - -IndexScalarQuantizer::IndexScalarQuantizer - (int d, QuantizerType qtype, - MetricType metric): - Index(d, metric), - sq (d, qtype) -{ - is_trained = - qtype == QuantizerType::QT_fp16 || - qtype == QuantizerType::QT_8bit_direct; - code_size = sq.code_size; -} - - -IndexScalarQuantizer::IndexScalarQuantizer (): - IndexScalarQuantizer(0, QuantizerType::QT_8bit) -{} - -void IndexScalarQuantizer::train(idx_t n, const float* x) -{ - sq.train(n, x); - is_trained = true; -} - -void IndexScalarQuantizer::add(idx_t n, const float* x) -{ - FAISS_THROW_IF_NOT (is_trained); - codes.resize ((n + ntotal) * code_size); - sq.compute_codes (x, &codes[ntotal * code_size], n); - ntotal += n; -} - - -void IndexScalarQuantizer::search( - idx_t n, - const float* x, - idx_t k, - float* distances, - idx_t* labels, - ConcurrentBitsetPtr bitset) const -{ - FAISS_THROW_IF_NOT (is_trained); - FAISS_THROW_IF_NOT (metric_type == METRIC_L2 || - metric_type == METRIC_INNER_PRODUCT); - -#pragma omp parallel - { - InvertedListScanner* scanner = sq.select_InvertedListScanner - (metric_type, nullptr, true); - ScopeDeleter1 del(scanner); - -#pragma omp for - for (size_t i = 0; i < n; i++) { - float * D = distances + k * i; - idx_t * I = labels + k * i; - // re-order heap - if (metric_type == METRIC_L2) { - maxheap_heapify (k, D, I); - } else { - minheap_heapify (k, D, I); - } - scanner->set_query (x + i * d); - scanner->scan_codes (ntotal, codes.data(), - nullptr, D, I, k); - - // re-order heap - if (metric_type == METRIC_L2) { - maxheap_reorder (k, D, I); - } else { - minheap_reorder (k, D, I); - } - } - } - -} - - -DistanceComputer *IndexScalarQuantizer::get_distance_computer () const -{ - SQDistanceComputer *dc = sq.get_distance_computer (metric_type); - dc->code_size = sq.code_size; - dc->codes = codes.data(); - return dc; -} - - -void IndexScalarQuantizer::reset() -{ - codes.clear(); - ntotal = 0; -} - -void IndexScalarQuantizer::reconstruct_n( - idx_t i0, idx_t ni, float* recons) const -{ - std::unique_ptr squant(sq.select_quantizer ()); - for (size_t i = 0; i < ni; i++) { - squant->decode_vector(&codes[(i + i0) * code_size], recons + i * d); - } -} - -void IndexScalarQuantizer::reconstruct(idx_t key, float* recons) const -{ - reconstruct_n(key, 1, recons); -} - -/* Codec interface */ -size_t IndexScalarQuantizer::sa_code_size () const -{ - return sq.code_size; -} - -void IndexScalarQuantizer::sa_encode (idx_t n, const float *x, - uint8_t *bytes) const -{ - FAISS_THROW_IF_NOT (is_trained); - sq.compute_codes (x, bytes, n); -} - -void IndexScalarQuantizer::sa_decode (idx_t n, const uint8_t *bytes, - float *x) const -{ - FAISS_THROW_IF_NOT (is_trained); - sq.decode(bytes, x, n); -} - - - -/******************************************************************* - * IndexIVFScalarQuantizer implementation - ********************************************************************/ - -IndexIVFScalarQuantizer::IndexIVFScalarQuantizer ( - Index *quantizer, size_t d, size_t nlist, - QuantizerType qtype, - MetricType metric, bool encode_residual) - : IndexIVF(quantizer, d, nlist, 0, metric), - sq(d, qtype), - by_residual(encode_residual) -{ - code_size = sq.code_size; - // was not known at construction time - invlists->code_size = code_size; - is_trained = false; -} - -IndexIVFScalarQuantizer::IndexIVFScalarQuantizer (): - IndexIVF(), - by_residual(true) -{ -} - -void IndexIVFScalarQuantizer::train_residual (idx_t n, const float *x) -{ - sq.train_residual(n, x, quantizer, by_residual, verbose); -} - -void IndexIVFScalarQuantizer::encode_vectors(idx_t n, const float* x, - const idx_t *list_nos, - uint8_t * codes, - bool include_listnos) const -{ - std::unique_ptr squant (sq.select_quantizer ()); - size_t coarse_size = include_listnos ? coarse_code_size () : 0; - memset(codes, 0, (code_size + coarse_size) * n); - -#pragma omp parallel if(n > 1) - { - std::vector residual (d); - -#pragma omp for - for (size_t i = 0; i < n; i++) { - int64_t list_no = list_nos [i]; - if (list_no >= 0) { - const float *xi = x + i * d; - uint8_t *code = codes + i * (code_size + coarse_size); - if (by_residual) { - quantizer->compute_residual ( - xi, residual.data(), list_no); - xi = residual.data (); - } - if (coarse_size) { - encode_listno (list_no, code); - } - squant->encode_vector (xi, code + coarse_size); - } - } - } -} - -void IndexIVFScalarQuantizer::sa_decode (idx_t n, const uint8_t *codes, - float *x) const -{ - std::unique_ptr squant (sq.select_quantizer ()); - size_t coarse_size = coarse_code_size (); - -#pragma omp parallel if(n > 1) - { - std::vector residual (d); - -#pragma omp for - for (size_t i = 0; i < n; i++) { - const uint8_t *code = codes + i * (code_size + coarse_size); - int64_t list_no = decode_listno (code); - float *xi = x + i * d; - squant->decode_vector (code + coarse_size, xi); - if (by_residual) { - quantizer->reconstruct (list_no, residual.data()); - for (size_t j = 0; j < d; j++) { - xi[j] += residual[j]; - } - } - } - } -} - - - -void IndexIVFScalarQuantizer::add_with_ids - (idx_t n, const float * x, const idx_t *xids) -{ - FAISS_THROW_IF_NOT (is_trained); - std::unique_ptr idx (new int64_t [n]); - quantizer->assign (n, x, idx.get()); - size_t nadd = 0; - std::unique_ptr squant(sq.select_quantizer ()); - - DirectMapAdd dm_add (direct_map, n, xids); - -#pragma omp parallel reduction(+: nadd) - { - std::vector residual (d); - std::vector one_code (code_size); - int nt = omp_get_num_threads(); - int rank = omp_get_thread_num(); - - // each thread takes care of a subset of lists - for (size_t i = 0; i < n; i++) { - int64_t list_no = idx [i]; - if (list_no >= 0 && list_no % nt == rank) { - int64_t id = xids ? xids[i] : ntotal + i; - - const float * xi = x + i * d; - if (by_residual) { - quantizer->compute_residual (xi, residual.data(), list_no); - xi = residual.data(); - } - - memset (one_code.data(), 0, code_size); - squant->encode_vector (xi, one_code.data()); - - size_t ofs = invlists->add_entry (list_no, id, one_code.data()); - - dm_add.add (i, list_no, ofs); - nadd++; - - } else if (rank == 0 && list_no == -1) { - dm_add.add (i, -1, 0); - } - } - } - - - ntotal += n; -} - - - - - -InvertedListScanner* IndexIVFScalarQuantizer::get_InvertedListScanner - (bool store_pairs) const -{ - return sq.select_InvertedListScanner (metric_type, quantizer, store_pairs, - by_residual); -} - - -void IndexIVFScalarQuantizer::reconstruct_from_offset (int64_t list_no, - int64_t offset, - float* recons) const -{ - std::vector centroid(d); - quantizer->reconstruct (list_no, centroid.data()); - - const uint8_t* code = invlists->get_single_code (list_no, offset); - sq.decode (code, recons, 1); - for (int i = 0; i < d; ++i) { - recons[i] += centroid[i]; - } -} - - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexScalarQuantizer.h b/core/src/index/thirdparty/faiss/IndexScalarQuantizer.h deleted file mode 100644 index feb0e8314f..0000000000 --- a/core/src/index/thirdparty/faiss/IndexScalarQuantizer.h +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_INDEX_SCALAR_QUANTIZER_H -#define FAISS_INDEX_SCALAR_QUANTIZER_H - -#include -#include - -#include -#include -#include - -namespace faiss { - -/** - * The uniform quantizer has a range [vmin, vmax]. The range can be - * the same for all dimensions (uniform) or specific per dimension - * (default). - */ - - - - -struct IndexScalarQuantizer: Index { - /// Used to encode the vectors - ScalarQuantizer sq; - - /// Codes. Size ntotal * pq.code_size - std::vector codes; - - size_t code_size; - - /** Constructor. - * - * @param d dimensionality of the input vectors - * @param M number of subquantizers - * @param nbits number of bit per subvector index - */ - IndexScalarQuantizer (int d, - QuantizerType qtype, - MetricType metric = METRIC_L2); - - IndexScalarQuantizer (); - - void train(idx_t n, const float* x) override; - - void add(idx_t n, const float* x) override; - - void search( - idx_t n, - const float* x, - idx_t k, - float* distances, - idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - void reset() override; - - void reconstruct_n(idx_t i0, idx_t ni, float* recons) const override; - - void reconstruct(idx_t key, float* recons) const override; - - DistanceComputer *get_distance_computer () const override; - - /* standalone codec interface */ - size_t sa_code_size () const override; - - void sa_encode (idx_t n, const float *x, - uint8_t *bytes) const override; - - void sa_decode (idx_t n, const uint8_t *bytes, - float *x) const override; - - -}; - - - /** An IVF implementation where the components of the residuals are - * encoded with a scalar uniform quantizer. All distance computations - * are asymmetric, so the encoded vectors are decoded and approximate - * distances are computed. - */ - -struct IndexIVFScalarQuantizer: IndexIVF { - ScalarQuantizer sq; - bool by_residual; - - IndexIVFScalarQuantizer(Index *quantizer, size_t d, size_t nlist, - QuantizerType qtype, - MetricType metric = METRIC_L2, - bool encode_residual = true); - - IndexIVFScalarQuantizer(); - - void train_residual(idx_t n, const float* x) override; - - void encode_vectors(idx_t n, const float* x, - const idx_t *list_nos, - uint8_t * codes, - bool include_listnos=false) const override; - - void add_with_ids(idx_t n, const float* x, const idx_t* xids) override; - - InvertedListScanner *get_InvertedListScanner (bool store_pairs) - const override; - - - void reconstruct_from_offset (int64_t list_no, int64_t offset, - float* recons) const override; - - /* standalone codec interface */ - void sa_decode (idx_t n, const uint8_t *bytes, - float *x) const override; - -}; - - -} - - -#endif diff --git a/core/src/index/thirdparty/faiss/IndexShards.cpp b/core/src/index/thirdparty/faiss/IndexShards.cpp deleted file mode 100644 index 0e0ac16264..0000000000 --- a/core/src/index/thirdparty/faiss/IndexShards.cpp +++ /dev/null @@ -1,318 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include - -#include -#include -#include - -namespace faiss { - -// subroutines -namespace { - -typedef Index::idx_t idx_t; - - -// add translation to all valid labels -void translate_labels (long n, idx_t *labels, long translation) -{ - if (translation == 0) return; - for (long i = 0; i < n; i++) { - if(labels[i] < 0) continue; - labels[i] += translation; - } -} - - -/** merge result tables from several shards. - * @param all_distances size nshard * n * k - * @param all_labels idem - * @param translartions label translations to apply, size nshard - */ - -template -void -merge_tables(long n, long k, long nshard, - typename IndexClass::distance_t *distances, - idx_t *labels, - const std::vector& all_distances, - const std::vector& all_labels, - const std::vector& translations) { - if (k == 0) { - return; - } - using distance_t = typename IndexClass::distance_t; - - long stride = n * k; -#pragma omp parallel - { - std::vector buf (2 * nshard); - int * pointer = buf.data(); - int * shard_ids = pointer + nshard; - std::vector buf2 (nshard); - distance_t * heap_vals = buf2.data(); -#pragma omp for - for (long i = 0; i < n; i++) { - // the heap maps values to the shard where they are - // produced. - const distance_t *D_in = all_distances.data() + i * k; - const idx_t *I_in = all_labels.data() + i * k; - int heap_size = 0; - - for (long s = 0; s < nshard; s++) { - pointer[s] = 0; - if (I_in[stride * s] >= 0) { - heap_push (++heap_size, heap_vals, shard_ids, - D_in[stride * s], s); - } - } - - distance_t *D = distances + i * k; - idx_t *I = labels + i * k; - - for (int j = 0; j < k; j++) { - if (heap_size == 0) { - I[j] = -1; - D[j] = C::neutral(); - } else { - // pop best element - int s = shard_ids[0]; - int & p = pointer[s]; - D[j] = heap_vals[0]; - I[j] = I_in[stride * s + p] + translations[s]; - - heap_pop (heap_size--, heap_vals, shard_ids); - p++; - if (p < k && I_in[stride * s + p] >= 0) { - heap_push (++heap_size, heap_vals, shard_ids, - D_in[stride * s + p], s); - } - } - } - } - } -} - -} // anonymous namespace - -template -IndexShardsTemplate::IndexShardsTemplate(idx_t d, - bool threaded, - bool successive_ids) - : ThreadedIndex(d, threaded), - successive_ids(successive_ids) { -} - -template -IndexShardsTemplate::IndexShardsTemplate(int d, - bool threaded, - bool successive_ids) - : ThreadedIndex(d, threaded), - successive_ids(successive_ids) { -} - -template -IndexShardsTemplate::IndexShardsTemplate(bool threaded, - bool successive_ids) - : ThreadedIndex(threaded), - successive_ids(successive_ids) { -} - -template -void -IndexShardsTemplate::onAfterAddIndex(IndexT* index /* unused */) { - sync_with_shard_indexes(); -} - -template -void -IndexShardsTemplate::onAfterRemoveIndex(IndexT* index /* unused */) { - sync_with_shard_indexes(); -} - -template -void -IndexShardsTemplate::sync_with_shard_indexes() { - if (!this->count()) { - this->is_trained = false; - this->ntotal = 0; - - return; - } - - auto firstIndex = this->at(0); - this->metric_type = firstIndex->metric_type; - this->is_trained = firstIndex->is_trained; - this->ntotal = firstIndex->ntotal; - - for (int i = 1; i < this->count(); ++i) { - auto index = this->at(i); - FAISS_THROW_IF_NOT(this->metric_type == index->metric_type); - FAISS_THROW_IF_NOT(this->d == index->d); - - this->ntotal += index->ntotal; - } -} - -template -void -IndexShardsTemplate::train(idx_t n, - const component_t *x) { - auto fn = - [n, x](int no, IndexT *index) { - if (index->verbose) { - printf("begin train shard %d on %ld points\n", no, n); - } - - index->train(n, x); - - if (index->verbose) { - printf("end train shard %d\n", no); - } - }; - - this->runOnIndex(fn); - sync_with_shard_indexes(); -} - -template -void -IndexShardsTemplate::add(idx_t n, - const component_t *x) { - add_with_ids(n, x, nullptr); -} - -template -void -IndexShardsTemplate::add_with_ids(idx_t n, - const component_t * x, - const idx_t *xids) { - - FAISS_THROW_IF_NOT_MSG(!(successive_ids && xids), - "It makes no sense to pass in ids and " - "request them to be shifted"); - - if (successive_ids) { - FAISS_THROW_IF_NOT_MSG(!xids, - "It makes no sense to pass in ids and " - "request them to be shifted"); - FAISS_THROW_IF_NOT_MSG(this->ntotal == 0, - "when adding to IndexShards with sucessive_ids, " - "only add() in a single pass is supported"); - } - - idx_t nshard = this->count(); - const idx_t *ids = xids; - - std::vector aids; - - if (!ids && !successive_ids) { - aids.resize(n); - - for (idx_t i = 0; i < n; i++) { - aids[i] = this->ntotal + i; - } - - ids = aids.data(); - } - - size_t components_per_vec = - sizeof(component_t) == 1 ? (this->d + 7) / 8 : this->d; - - auto fn = - [n, ids, x, nshard, components_per_vec](int no, IndexT *index) { - idx_t i0 = (idx_t) no * n / nshard; - idx_t i1 = ((idx_t) no + 1) * n / nshard; - auto x0 = x + i0 * components_per_vec; - - if (index->verbose) { - printf ("begin add shard %d on %ld points\n", no, n); - } - - if (ids) { - index->add_with_ids (i1 - i0, x0, ids + i0); - } else { - index->add (i1 - i0, x0); - } - - if (index->verbose) { - printf ("end add shard %d on %ld points\n", no, i1 - i0); - } - }; - - this->runOnIndex(fn); - - // This is safe to do here because the current thread controls execution in - // all threads, and nothing else is happening - this->ntotal += n; -} - -template -void -IndexShardsTemplate::search(idx_t n, - const component_t *x, - idx_t k, - distance_t *distances, - idx_t *labels, - ConcurrentBitsetPtr bitset) const { - long nshard = this->count(); - - std::vector all_distances(nshard * k * n); - std::vector all_labels(nshard * k * n); - - auto fn = - [n, k, x, &all_distances, &all_labels](int no, const IndexT *index) { - if (index->verbose) { - printf ("begin query shard %d on %ld points\n", no, n); - } - - index->search (n, x, k, - all_distances.data() + no * k * n, - all_labels.data() + no * k * n); - - if (index->verbose) { - printf ("end query shard %d\n", no); - } - }; - - this->runOnIndex(fn); - - std::vector translations(nshard, 0); - - // Because we just called runOnIndex above, it is safe to access the sub-index - // ntotal here - if (successive_ids) { - translations[0] = 0; - - for (int s = 0; s + 1 < nshard; s++) { - translations[s + 1] = translations[s] + this->at(s)->ntotal; - } - } - - if (this->metric_type == METRIC_L2) { - merge_tables>( - n, k, nshard, distances, labels, - all_distances, all_labels, translations); - } else { - merge_tables>( - n, k, nshard, distances, labels, - all_distances, all_labels, translations); - } -} - -// explicit instanciations -template struct IndexShardsTemplate; -template struct IndexShardsTemplate; - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/IndexShards.h b/core/src/index/thirdparty/faiss/IndexShards.h deleted file mode 100644 index 6fbca6778a..0000000000 --- a/core/src/index/thirdparty/faiss/IndexShards.h +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include - -namespace faiss { - -/** - * Index that concatenates the results from several sub-indexes - */ -template -struct IndexShardsTemplate : public ThreadedIndex { - using idx_t = typename IndexT::idx_t; - using component_t = typename IndexT::component_t; - using distance_t = typename IndexT::distance_t; - - /** - * The dimension that all sub-indices must share will be the dimension of the - * first sub-index added - * - * @param threaded do we use one thread per sub_index or do - * queries sequentially? - * @param successive_ids should we shift the returned ids by - * the size of each sub-index or return them - * as they are? - */ - explicit IndexShardsTemplate(bool threaded = false, - bool successive_ids = true); - - /** - * @param threaded do we use one thread per sub_index or do - * queries sequentially? - * @param successive_ids should we shift the returned ids by - * the size of each sub-index or return them - * as they are? - */ - explicit IndexShardsTemplate(idx_t d, - bool threaded = false, - bool successive_ids = true); - - /// int version due to the implicit bool conversion ambiguity of int as - /// dimension - explicit IndexShardsTemplate(int d, - bool threaded = false, - bool successive_ids = true); - - /// Alias for addIndex() - void add_shard(IndexT* index) { this->addIndex(index); } - - /// Alias for removeIndex() - void remove_shard(IndexT* index) { this->removeIndex(index); } - - /// supported only for sub-indices that implement add_with_ids - void add(idx_t n, const component_t* x) override; - - /** - * Cases (successive_ids, xids): - * - true, non-NULL ERROR: it makes no sense to pass in ids and - * request them to be shifted - * - true, NULL OK, but should be called only once (calls add() - * on sub-indexes). - * - false, non-NULL OK: will call add_with_ids with passed in xids - * distributed evenly over shards - * - false, NULL OK: will call add_with_ids on each sub-index, - * starting at ntotal - */ - void add_with_ids(idx_t n, const component_t* x, const idx_t* xids) override; - - void search(idx_t n, const component_t* x, idx_t k, - distance_t* distances, idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - void train(idx_t n, const component_t* x) override; - - // update metric_type and ntotal. Call if you changes something in - // the shard indexes. - void sync_with_shard_indexes(); - - bool successive_ids; - - protected: - /// Called just after an index is added - void onAfterAddIndex(IndexT* index) override; - - /// Called just after an index is removed - void onAfterRemoveIndex(IndexT* index) override; -}; - -using IndexShards = IndexShardsTemplate; -using IndexBinaryShards = IndexShardsTemplate; - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/InvertedLists.cpp b/core/src/index/thirdparty/faiss/InvertedLists.cpp deleted file mode 100644 index 59f5d1e7cb..0000000000 --- a/core/src/index/thirdparty/faiss/InvertedLists.cpp +++ /dev/null @@ -1,856 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include - -#include -#include - -#ifndef USE_CPU -#include "gpu/utils/DeviceUtils.h" -#include "cuda.h" -#include "cuda_runtime.h" - -namespace faiss { - -/* - * Use pin memory to build Readonly Inverted list will accelerate cuda memory copy, but it will downgrade cpu ivf search - * performance. read only inverted list structure will also make ivf search performance not stable. ISSUE 500 mention - * this problem. Best performance is the original inverted list with non pin memory. - */ - -PageLockMemory::PageLockMemory(size_t size) : nbytes(size) { - auto err = cudaHostAlloc(&(this->data), size, 0); - if (err) { - std::string msg = - "Fail to alloc page lock memory " + std::to_string(size) + ", err code " + std::to_string((int32_t)err); - FAISS_THROW_MSG(msg); - } -} - -PageLockMemory::~PageLockMemory() { - CUDA_VERIFY(cudaFreeHost((void*)(this->data))); -} - -PageLockMemory::PageLockMemory(const PageLockMemory& other) { - auto err = cudaHostAlloc(&(this->data), other.nbytes, 0); - if (err) { - std::string msg = "Fail to alloc page lock memory " + std::to_string(other.nbytes) + ", err code " + - std::to_string((int32_t)err); - FAISS_THROW_MSG(msg); - } - memcpy(this->data, other.data, other.nbytes); - this->nbytes = other.nbytes; -} - -PageLockMemory::PageLockMemory(PageLockMemory &&other) { - this->data = other.data; - this->nbytes = other.nbytes; - other.data = nullptr; - other.nbytes = 0; -} - -} -#endif - -namespace faiss { - - - -/***************************************** - * InvertedLists implementation - ******************************************/ - -InvertedLists::InvertedLists (size_t nlist, size_t code_size): - nlist (nlist), code_size (code_size) -{ -} - -InvertedLists::~InvertedLists () -{} - -InvertedLists::idx_t InvertedLists::get_single_id ( - size_t list_no, size_t offset) const -{ - assert (offset < list_size (list_no)); - return get_ids(list_no)[offset]; -} - - -void InvertedLists::release_codes (size_t, const uint8_t *) const -{} - -void InvertedLists::release_ids (size_t, const idx_t *) const -{} - -void InvertedLists::prefetch_lists (const idx_t *, int) const -{} - -const uint8_t * InvertedLists::get_single_code ( - size_t list_no, size_t offset) const -{ - assert (offset < list_size (list_no)); - return get_codes(list_no) + offset * code_size; -} - -size_t InvertedLists::add_entry (size_t list_no, idx_t theid, - const uint8_t *code) -{ - return add_entries (list_no, 1, &theid, code); -} - -void InvertedLists::update_entry (size_t list_no, size_t offset, - idx_t id, const uint8_t *code) -{ - update_entries (list_no, offset, 1, &id, code); -} - -InvertedLists* InvertedLists::to_readonly() { - return nullptr; -} - -bool InvertedLists::is_readonly() const { - return false; -} - -void InvertedLists::reset () { - for (size_t i = 0; i < nlist; i++) { - resize (i, 0); - } -} - -void InvertedLists::merge_from (InvertedLists *oivf, size_t add_id) { - -#pragma omp parallel for - for (idx_t i = 0; i < nlist; i++) { - size_t list_size = oivf->list_size (i); - ScopedIds ids (oivf, i); - if (add_id == 0) { - add_entries (i, list_size, ids.get (), - ScopedCodes (oivf, i).get()); - } else { - std::vector new_ids (list_size); - - for (size_t j = 0; j < list_size; j++) { - new_ids [j] = ids[j] + add_id; - } - add_entries (i, list_size, new_ids.data(), - ScopedCodes (oivf, i).get()); - } - oivf->resize (i, 0); - } -} - -double InvertedLists::imbalance_factor () const { - std::vector hist(nlist); - - for (size_t i = 0; i < nlist; i++) { - hist[i] = list_size(i); - } - - return faiss::imbalance_factor(nlist, hist.data()); -} - -void InvertedLists::print_stats () const { - std::vector sizes(40); - for (size_t i = 0; i < nlist; i++) { - for (size_t j = 0; j < sizes.size(); j++) { - if ((list_size(i) >> j) == 0) { - sizes[j]++; - break; - } - } - } - for (size_t i = 0; i < sizes.size(); i++) { - if (sizes[i]) { - printf("list size in < %d: %d instances\n", 1 << i, sizes[i]); - } - } -} - -size_t InvertedLists::compute_ntotal () const { - size_t tot = 0; - for (size_t i = 0; i < nlist; i++) { - tot += list_size(i); - } - return tot; -} - -/***************************************** - * ArrayInvertedLists implementation - ******************************************/ - -ArrayInvertedLists::ArrayInvertedLists (size_t nlist, size_t code_size): - InvertedLists (nlist, code_size) -{ - ids.resize (nlist); - codes.resize (nlist); -} - -size_t ArrayInvertedLists::add_entries ( - size_t list_no, size_t n_entry, - const idx_t* ids_in, const uint8_t *code) -{ - if (n_entry == 0) return 0; - assert (list_no < nlist); - size_t o = ids [list_no].size(); - ids [list_no].resize (o + n_entry); - memcpy (&ids[list_no][o], ids_in, sizeof (ids_in[0]) * n_entry); - codes [list_no].resize ((o + n_entry) * code_size); - memcpy (&codes[list_no][o * code_size], code, code_size * n_entry); - return o; -} - -size_t ArrayInvertedLists::list_size(size_t list_no) const -{ - assert (list_no < nlist); - return ids[list_no].size(); -} - -const uint8_t * ArrayInvertedLists::get_codes (size_t list_no) const -{ - assert (list_no < nlist); - return codes[list_no].data(); -} - - -const InvertedLists::idx_t * ArrayInvertedLists::get_ids (size_t list_no) const -{ - assert (list_no < nlist); - return ids[list_no].data(); -} - -void ArrayInvertedLists::resize (size_t list_no, size_t new_size) -{ - ids[list_no].resize (new_size); - codes[list_no].resize (new_size * code_size); -} - -void ArrayInvertedLists::update_entries ( - size_t list_no, size_t offset, size_t n_entry, - const idx_t *ids_in, const uint8_t *codes_in) -{ - assert (list_no < nlist); - assert (n_entry + offset <= ids[list_no].size()); - memcpy (&ids[list_no][offset], ids_in, sizeof(ids_in[0]) * n_entry); - memcpy (&codes[list_no][offset * code_size], codes_in, code_size * n_entry); -} - -InvertedLists* ArrayInvertedLists::to_readonly() { - ReadOnlyArrayInvertedLists* readonly = new ReadOnlyArrayInvertedLists(*this); - return readonly; -} - -ArrayInvertedLists::~ArrayInvertedLists () -{} - -/***************************************************************** - * ReadOnlyArrayInvertedLists implementations - *****************************************************************/ - -ReadOnlyArrayInvertedLists::ReadOnlyArrayInvertedLists(size_t nlist, - size_t code_size, const std::vector& list_length) - : InvertedLists (nlist, code_size), - readonly_length(list_length) { - valid = readonly_length.size() == nlist; - if (!valid) { - FAISS_THROW_MSG ("Invalid list_length"); - return; - } - auto total_size = std::accumulate(readonly_length.begin(), readonly_length.end(), 0); - readonly_offset.reserve(nlist); - -#ifdef USE_CPU - readonly_codes.reserve(total_size * code_size); - readonly_ids.reserve(total_size); -#endif - - size_t offset = 0; - for (auto i=0; icode_size) * sizeof(uint8_t); - pin_readonly_codes = std::make_shared(codes_size); - pin_readonly_ids = std::make_shared(ids_size); - - offset = 0; - for (auto i = 0; i < other.ids.size(); i++) { - auto& list_ids = other.ids[i]; - auto& list_codes = other.codes[i]; - - uint8_t* ids_ptr = (uint8_t*)(pin_readonly_ids->data) + offset * sizeof(idx_t); - memcpy(ids_ptr, list_ids.data(), list_ids.size() * sizeof(idx_t)); - - uint8_t* codes_ptr = (uint8_t*)(pin_readonly_codes->data) + offset * (this->code_size) * sizeof(uint8_t); - memcpy(codes_ptr, list_codes.data(), list_codes.size() * sizeof(uint8_t)); - - offset += list_ids.size(); - } -#endif - - valid = true; -} - -//ReadOnlyArrayInvertedLists::ReadOnlyArrayInvertedLists(const ReadOnlyArrayInvertedLists &other) -// : InvertedLists (other.nlist, other.code_size) { -// readonly_length = other.readonly_length; -// readonly_offset = other.readonly_offset; -// pin_readonly_codes = std::make_shared(*other.pin_readonly_codes); -// pin_readonly_ids = std::make_shared(*other.pin_readonly_ids); -// valid = true; -//} - -//ReadOnlyArrayInvertedLists::ReadOnlyArrayInvertedLists(ReadOnlyArrayInvertedLists &&other) -// : InvertedLists (other.nlist, other.code_size) { -// readonly_length = std::move(other.readonly_length); -// readonly_offset = std::move(other.readonly_offset); -// pin_readonly_codes = other.pin_readonly_codes; -// pin_readonly_ids = other.pin_readonly_ids; -// -// other.pin_readonly_codes = nullptr; -// other.pin_readonly_ids = nullptr; -// valid = true; -//} - -ReadOnlyArrayInvertedLists::~ReadOnlyArrayInvertedLists() { -} - -bool -ReadOnlyArrayInvertedLists::is_valid() { - return valid; -} - -size_t ReadOnlyArrayInvertedLists::add_entries ( - size_t , size_t , - const idx_t* , const uint8_t *) -{ - FAISS_THROW_MSG ("not implemented"); -} - -void ReadOnlyArrayInvertedLists::update_entries (size_t, size_t , size_t , - const idx_t *, const uint8_t *) -{ - FAISS_THROW_MSG ("not implemented"); -} - -void ReadOnlyArrayInvertedLists::resize (size_t , size_t ) -{ - FAISS_THROW_MSG ("not implemented"); -} - -size_t ReadOnlyArrayInvertedLists::list_size(size_t list_no) const -{ - FAISS_ASSERT(list_no < nlist && valid); - return readonly_length[list_no]; -} - -const uint8_t * ReadOnlyArrayInvertedLists::get_codes (size_t list_no) const -{ - FAISS_ASSERT(list_no < nlist && valid); -#ifdef USE_CPU - return readonly_codes.data() + readonly_offset[list_no] * code_size; -#else - uint8_t *pcodes = (uint8_t *)(pin_readonly_codes->data); - return pcodes + readonly_offset[list_no] * code_size; -#endif -} - -const InvertedLists::idx_t* ReadOnlyArrayInvertedLists::get_ids (size_t list_no) const -{ - FAISS_ASSERT(list_no < nlist && valid); -#ifdef USE_CPU - return readonly_ids.data() + readonly_offset[list_no]; -#else - idx_t *pids = (idx_t *)pin_readonly_ids->data; - return pids + readonly_offset[list_no]; -#endif -} - -const InvertedLists::idx_t* ReadOnlyArrayInvertedLists::get_all_ids() const { - FAISS_ASSERT(valid); -#ifdef USE_CPU - return readonly_ids.data(); -#else - return (idx_t *)(pin_readonly_ids->data); -#endif -} - -const uint8_t* ReadOnlyArrayInvertedLists::get_all_codes() const { - FAISS_ASSERT(valid); -#ifdef USE_CPU - return readonly_codes.data(); -#else - return (uint8_t *)(pin_readonly_codes->data); -#endif -} - -const std::vector& ReadOnlyArrayInvertedLists::get_list_length() const { - FAISS_ASSERT(valid); - return readonly_length; -} - -bool ReadOnlyArrayInvertedLists::is_readonly() const { - FAISS_ASSERT(valid); - return true; -} - -/***************************************************************** - * Meta-inverted list implementations - *****************************************************************/ - - -size_t ReadOnlyInvertedLists::add_entries ( - size_t , size_t , - const idx_t* , const uint8_t *) -{ - FAISS_THROW_MSG ("not implemented"); -} - -void ReadOnlyInvertedLists::update_entries (size_t, size_t , size_t , - const idx_t *, const uint8_t *) -{ - FAISS_THROW_MSG ("not implemented"); -} - -void ReadOnlyInvertedLists::resize (size_t , size_t ) -{ - FAISS_THROW_MSG ("not implemented"); -} - - - -/***************************************** - * HStackInvertedLists implementation - ******************************************/ - -HStackInvertedLists::HStackInvertedLists ( - int nil, const InvertedLists **ils_in): - ReadOnlyInvertedLists (nil > 0 ? ils_in[0]->nlist : 0, - nil > 0 ? ils_in[0]->code_size : 0) -{ - FAISS_THROW_IF_NOT (nil > 0); - for (int i = 0; i < nil; i++) { - ils.push_back (ils_in[i]); - FAISS_THROW_IF_NOT (ils_in[i]->code_size == code_size && - ils_in[i]->nlist == nlist); - } -} - -size_t HStackInvertedLists::list_size(size_t list_no) const -{ - size_t sz = 0; - for (int i = 0; i < ils.size(); i++) { - const InvertedLists *il = ils[i]; - sz += il->list_size (list_no); - } - return sz; -} - -const uint8_t * HStackInvertedLists::get_codes (size_t list_no) const -{ - uint8_t *codes = new uint8_t [code_size * list_size(list_no)], *c = codes; - - for (int i = 0; i < ils.size(); i++) { - const InvertedLists *il = ils[i]; - size_t sz = il->list_size(list_no) * code_size; - if (sz > 0) { - memcpy (c, ScopedCodes (il, list_no).get(), sz); - c += sz; - } - } - return codes; -} - -const uint8_t * HStackInvertedLists::get_single_code ( - size_t list_no, size_t offset) const -{ - for (int i = 0; i < ils.size(); i++) { - const InvertedLists *il = ils[i]; - size_t sz = il->list_size (list_no); - if (offset < sz) { - // here we have to copy the code, otherwise it will crash at dealloc - uint8_t * code = new uint8_t [code_size]; - memcpy (code, ScopedCodes (il, list_no, offset).get(), code_size); - return code; - } - offset -= sz; - } - FAISS_THROW_FMT ("offset %ld unknown", offset); -} - - -void HStackInvertedLists::release_codes (size_t, const uint8_t *codes) const { - delete [] codes; -} - -const Index::idx_t * HStackInvertedLists::get_ids (size_t list_no) const -{ - idx_t *ids = new idx_t [list_size(list_no)], *c = ids; - - for (int i = 0; i < ils.size(); i++) { - const InvertedLists *il = ils[i]; - size_t sz = il->list_size(list_no); - if (sz > 0) { - memcpy (c, ScopedIds (il, list_no).get(), sz * sizeof(idx_t)); - c += sz; - } - } - return ids; -} - -Index::idx_t HStackInvertedLists::get_single_id ( - size_t list_no, size_t offset) const -{ - - for (int i = 0; i < ils.size(); i++) { - const InvertedLists *il = ils[i]; - size_t sz = il->list_size (list_no); - if (offset < sz) { - return il->get_single_id (list_no, offset); - } - offset -= sz; - } - FAISS_THROW_FMT ("offset %ld unknown", offset); -} - - -void HStackInvertedLists::release_ids (size_t, const idx_t *ids) const { - delete [] ids; -} - -void HStackInvertedLists::prefetch_lists (const idx_t *list_nos, int nlist) const -{ - for (int i = 0; i < ils.size(); i++) { - const InvertedLists *il = ils[i]; - il->prefetch_lists (list_nos, nlist); - } -} - -/***************************************** - * SliceInvertedLists implementation - ******************************************/ - - -namespace { - - using idx_t = InvertedLists::idx_t; - - idx_t translate_list_no (const SliceInvertedLists *sil, - idx_t list_no) { - FAISS_THROW_IF_NOT (list_no >= 0 && list_no < sil->nlist); - return list_no + sil->i0; - } - -}; - - - -SliceInvertedLists::SliceInvertedLists ( - const InvertedLists *il, idx_t i0, idx_t i1): - ReadOnlyInvertedLists (i1 - i0, il->code_size), - il (il), i0(i0), i1(i1) -{ - -} - -size_t SliceInvertedLists::list_size(size_t list_no) const -{ - return il->list_size (translate_list_no (this, list_no)); -} - -const uint8_t * SliceInvertedLists::get_codes (size_t list_no) const -{ - return il->get_codes (translate_list_no (this, list_no)); -} - -const uint8_t * SliceInvertedLists::get_single_code ( - size_t list_no, size_t offset) const -{ - return il->get_single_code (translate_list_no (this, list_no), offset); -} - - -void SliceInvertedLists::release_codes ( - size_t list_no, const uint8_t *codes) const { - return il->release_codes (translate_list_no (this, list_no), codes); -} - -const Index::idx_t * SliceInvertedLists::get_ids (size_t list_no) const -{ - return il->get_ids (translate_list_no (this, list_no)); -} - -Index::idx_t SliceInvertedLists::get_single_id ( - size_t list_no, size_t offset) const -{ - return il->get_single_id (translate_list_no (this, list_no), offset); -} - - -void SliceInvertedLists::release_ids (size_t list_no, const idx_t *ids) const { - return il->release_ids (translate_list_no (this, list_no), ids); -} - -void SliceInvertedLists::prefetch_lists (const idx_t *list_nos, int nlist) const -{ - std::vector translated_list_nos; - for (int j = 0; j < nlist; j++) { - idx_t list_no = list_nos[j]; - if (list_no < 0) continue; - translated_list_nos.push_back (translate_list_no (this, list_no)); - } - il->prefetch_lists (translated_list_nos.data(), - translated_list_nos.size()); -} - - -/***************************************** - * VStackInvertedLists implementation - ******************************************/ - -namespace { - - using idx_t = InvertedLists::idx_t; - - // find the invlist this number belongs to - int translate_list_no (const VStackInvertedLists *vil, - idx_t list_no) { - FAISS_THROW_IF_NOT (list_no >= 0 && list_no < vil->nlist); - int i0 = 0, i1 = vil->ils.size(); - const idx_t *cumsz = vil->cumsz.data(); - while (i0 + 1 < i1) { - int imed = (i0 + i1) / 2; - if (list_no >= cumsz[imed]) { - i0 = imed; - } else { - i1 = imed; - } - } - assert(list_no >= cumsz[i0] && list_no < cumsz[i0 + 1]); - return i0; - } - - idx_t sum_il_sizes (int nil, const InvertedLists **ils_in) { - idx_t tot = 0; - for (int i = 0; i < nil; i++) { - tot += ils_in[i]->nlist; - } - return tot; - } - -}; - - - -VStackInvertedLists::VStackInvertedLists ( - int nil, const InvertedLists **ils_in): - ReadOnlyInvertedLists (sum_il_sizes(nil, ils_in), - nil > 0 ? ils_in[0]->code_size : 0) -{ - FAISS_THROW_IF_NOT (nil > 0); - cumsz.resize (nil + 1); - for (int i = 0; i < nil; i++) { - ils.push_back (ils_in[i]); - FAISS_THROW_IF_NOT (ils_in[i]->code_size == code_size); - cumsz[i + 1] = cumsz[i] + ils_in[i]->nlist; - } -} - -size_t VStackInvertedLists::list_size(size_t list_no) const -{ - int i = translate_list_no (this, list_no); - list_no -= cumsz[i]; - return ils[i]->list_size (list_no); -} - -const uint8_t * VStackInvertedLists::get_codes (size_t list_no) const -{ - int i = translate_list_no (this, list_no); - list_no -= cumsz[i]; - return ils[i]->get_codes (list_no); -} - -const uint8_t * VStackInvertedLists::get_single_code ( - size_t list_no, size_t offset) const -{ - int i = translate_list_no (this, list_no); - list_no -= cumsz[i]; - return ils[i]->get_single_code (list_no, offset); -} - - -void VStackInvertedLists::release_codes ( - size_t list_no, const uint8_t *codes) const { - int i = translate_list_no (this, list_no); - list_no -= cumsz[i]; - return ils[i]->release_codes (list_no, codes); -} - -const Index::idx_t * VStackInvertedLists::get_ids (size_t list_no) const -{ - int i = translate_list_no (this, list_no); - list_no -= cumsz[i]; - return ils[i]->get_ids (list_no); -} - -Index::idx_t VStackInvertedLists::get_single_id ( - size_t list_no, size_t offset) const -{ - int i = translate_list_no (this, list_no); - list_no -= cumsz[i]; - return ils[i]->get_single_id (list_no, offset); -} - - -void VStackInvertedLists::release_ids (size_t list_no, const idx_t *ids) const { - int i = translate_list_no (this, list_no); - list_no -= cumsz[i]; - return ils[i]->release_ids (list_no, ids); -} - -void VStackInvertedLists::prefetch_lists ( - const idx_t *list_nos, int nlist) const -{ - std::vector ilno (nlist, -1); - std::vector n_per_il (ils.size(), 0); - for (int j = 0; j < nlist; j++) { - idx_t list_no = list_nos[j]; - if (list_no < 0) continue; - int i = ilno[j] = translate_list_no (this, list_no); - n_per_il[i]++; - } - std::vector cum_n_per_il (ils.size() + 1, 0); - for (int j = 0; j < ils.size(); j++) { - cum_n_per_il[j + 1] = cum_n_per_il[j] + n_per_il[j]; - } - std::vector sorted_list_nos (cum_n_per_il.back()); - for (int j = 0; j < nlist; j++) { - idx_t list_no = list_nos[j]; - if (list_no < 0) continue; - int i = ilno[j]; - list_no -= cumsz[i]; - sorted_list_nos[cum_n_per_il[i]++] = list_no; - } - - int i0 = 0; - for (int j = 0; j < ils.size(); j++) { - int i1 = i0 + n_per_il[j]; - if (i1 > i0) { - ils[j]->prefetch_lists (sorted_list_nos.data() + i0, - i1 - i0); - } - i0 = i1; - } -} - - - -/***************************************** - * MaskedInvertedLists implementation - ******************************************/ - - -MaskedInvertedLists::MaskedInvertedLists (const InvertedLists *il0, - const InvertedLists *il1): - ReadOnlyInvertedLists (il0->nlist, il0->code_size), - il0 (il0), il1 (il1) -{ - FAISS_THROW_IF_NOT (il1->nlist == nlist); - FAISS_THROW_IF_NOT (il1->code_size == code_size); -} - -size_t MaskedInvertedLists::list_size(size_t list_no) const -{ - size_t sz = il0->list_size(list_no); - return sz ? sz : il1->list_size(list_no); -} - -const uint8_t * MaskedInvertedLists::get_codes (size_t list_no) const -{ - size_t sz = il0->list_size(list_no); - return (sz ? il0 : il1)->get_codes(list_no); -} - -const idx_t * MaskedInvertedLists::get_ids (size_t list_no) const -{ - size_t sz = il0->list_size (list_no); - return (sz ? il0 : il1)->get_ids (list_no); -} - -void MaskedInvertedLists::release_codes ( - size_t list_no, const uint8_t *codes) const -{ - size_t sz = il0->list_size (list_no); - (sz ? il0 : il1)->release_codes (list_no, codes); -} - -void MaskedInvertedLists::release_ids (size_t list_no, const idx_t *ids) const -{ - size_t sz = il0->list_size (list_no); - (sz ? il0 : il1)->release_ids (list_no, ids); -} - -idx_t MaskedInvertedLists::get_single_id (size_t list_no, size_t offset) const -{ - size_t sz = il0->list_size (list_no); - return (sz ? il0 : il1)->get_single_id (list_no, offset); -} - -const uint8_t * MaskedInvertedLists::get_single_code ( - size_t list_no, size_t offset) const -{ - size_t sz = il0->list_size (list_no); - return (sz ? il0 : il1)->get_single_code (list_no, offset); -} - -void MaskedInvertedLists::prefetch_lists ( - const idx_t *list_nos, int nlist) const -{ - std::vector list0, list1; - for (int i = 0; i < nlist; i++) { - idx_t list_no = list_nos[i]; - if (list_no < 0) continue; - size_t sz = il0->list_size(list_no); - (sz ? list0 : list1).push_back (list_no); - } - il0->prefetch_lists (list0.data(), list0.size()); - il1->prefetch_lists (list1.data(), list1.size()); -} - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/InvertedLists.h b/core/src/index/thirdparty/faiss/InvertedLists.h deleted file mode 100644 index ec77d2cb18..0000000000 --- a/core/src/index/thirdparty/faiss/InvertedLists.h +++ /dev/null @@ -1,409 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_INVERTEDLISTS_IVF_H -#define FAISS_INVERTEDLISTS_IVF_H - -/** - * Definition of inverted lists + a few common classes that implement - * the interface. - */ - -#include -#include -#include - -#ifndef USE_CPU -namespace faiss { - -struct PageLockMemory { -public: - PageLockMemory() : data(nullptr), nbytes(0) {} - - PageLockMemory(size_t size); - - ~PageLockMemory(); - - PageLockMemory(const PageLockMemory& other); - - PageLockMemory(PageLockMemory &&other); - - inline size_t size() { - return nbytes; - } - - void *data; - size_t nbytes; -}; -using PageLockMemoryPtr = std::shared_ptr; -} -#endif - -namespace faiss { - -/** Table of inverted lists - * multithreading rules: - * - concurrent read accesses are allowed - * - concurrent update accesses are allowed - * - for resize and add_entries, only concurrent access to different lists - * are allowed - */ -struct InvertedLists { - typedef Index::idx_t idx_t; - - size_t nlist; ///< number of possible key values - size_t code_size; ///< code size per vector in bytes - - InvertedLists (size_t nlist, size_t code_size); - - /************************* - * Read only functions */ - - /// get the size of a list - virtual size_t list_size(size_t list_no) const = 0; - - /** get the codes for an inverted list - * must be released by release_codes - * - * @return codes size list_size * code_size - */ - virtual const uint8_t * get_codes (size_t list_no) const = 0; - - /** get the ids for an inverted list - * must be released by release_ids - * - * @return ids size list_size - */ - virtual const idx_t * get_ids (size_t list_no) const = 0; - - /// release codes returned by get_codes (default implementation is nop - virtual void release_codes (size_t list_no, const uint8_t *codes) const; - - /// release ids returned by get_ids - virtual void release_ids (size_t list_no, const idx_t *ids) const; - - /// @return a single id in an inverted list - virtual idx_t get_single_id (size_t list_no, size_t offset) const; - - /// @return a single code in an inverted list - /// (should be deallocated with release_codes) - virtual const uint8_t * get_single_code ( - size_t list_no, size_t offset) const; - - /// prepare the following lists (default does nothing) - /// a list can be -1 hence the signed long - virtual void prefetch_lists (const idx_t *list_nos, int nlist) const; - - /************************* - * writing functions */ - - /// add one entry to an inverted list - virtual size_t add_entry (size_t list_no, idx_t theid, - const uint8_t *code); - - virtual size_t add_entries ( - size_t list_no, size_t n_entry, - const idx_t* ids, const uint8_t *code) = 0; - - virtual void update_entry (size_t list_no, size_t offset, - idx_t id, const uint8_t *code); - - virtual void update_entries (size_t list_no, size_t offset, size_t n_entry, - const idx_t *ids, const uint8_t *code) = 0; - - virtual void resize (size_t list_no, size_t new_size) = 0; - - virtual void reset (); - - virtual InvertedLists* to_readonly(); - - virtual bool is_readonly() const; - - /// move all entries from oivf (empty on output) - void merge_from (InvertedLists *oivf, size_t add_id); - - virtual ~InvertedLists (); - - /************************* - * statistics */ - - /// 1= perfectly balanced, >1: imbalanced - double imbalance_factor () const; - - /// display some stats about the inverted lists - void print_stats () const; - - /// sum up list sizes - size_t compute_ntotal () const; - - /************************************** - * Scoped inverted lists (for automatic deallocation) - * - * instead of writing: - * - * uint8_t * codes = invlists->get_codes (10); - * ... use codes - * invlists->release_codes(10, codes) - * - * write: - * - * ScopedCodes codes (invlists, 10); - * ... use codes.get() - * // release called automatically when codes goes out of scope - * - * the following function call also works: - * - * foo (123, ScopedCodes (invlists, 10).get(), 456); - * - */ - - struct ScopedIds { - const InvertedLists *il; - const idx_t *ids; - size_t list_no; - - ScopedIds (const InvertedLists *il, size_t list_no): - il (il), ids (il->get_ids (list_no)), list_no (list_no) - {} - - const idx_t *get() {return ids; } - - idx_t operator [] (size_t i) const { - return ids[i]; - } - - ~ScopedIds () { - il->release_ids (list_no, ids); - } - }; - - struct ScopedCodes { - const InvertedLists *il; - const uint8_t *codes; - size_t list_no; - - ScopedCodes (const InvertedLists *il, size_t list_no): - il (il), codes (il->get_codes (list_no)), list_no (list_no) - {} - - ScopedCodes (const InvertedLists *il, size_t list_no, size_t offset): - il (il), codes (il->get_single_code (list_no, offset)), - list_no (list_no) - {} - - const uint8_t *get() {return codes; } - - ~ScopedCodes () { - il->release_codes (list_no, codes); - } - }; - - -}; - - -/// simple (default) implementation as an array of inverted lists -struct ArrayInvertedLists: InvertedLists { - std::vector < std::vector > codes; // binary codes, size nlist - std::vector < std::vector > ids; ///< Inverted lists for indexes - - ArrayInvertedLists (size_t nlist, size_t code_size); - - size_t list_size(size_t list_no) const override; - const uint8_t * get_codes (size_t list_no) const override; - const idx_t * get_ids (size_t list_no) const override; - - size_t add_entries ( - size_t list_no, size_t n_entry, - const idx_t* ids, const uint8_t *code) override; - - void update_entries (size_t list_no, size_t offset, size_t n_entry, - const idx_t *ids, const uint8_t *code) override; - - void resize (size_t list_no, size_t new_size) override; - - InvertedLists* to_readonly() override; - - virtual ~ArrayInvertedLists (); -}; - -struct ReadOnlyArrayInvertedLists: InvertedLists { -#ifdef USE_CPU - std::vector readonly_codes; - std::vector readonly_ids; -#else - PageLockMemoryPtr pin_readonly_codes; - PageLockMemoryPtr pin_readonly_ids; -#endif - - std::vector readonly_length; - std::vector readonly_offset; - bool valid; - - ReadOnlyArrayInvertedLists(size_t nlist, size_t code_size, const std::vector& list_length); - explicit ReadOnlyArrayInvertedLists(const ArrayInvertedLists& other); - - // Use default copy construct, just copy pointer, DON'T COPY pin_readonly_codes AND pin_readonly_ids -// explicit ReadOnlyArrayInvertedLists(const ReadOnlyArrayInvertedLists &); -// explicit ReadOnlyArrayInvertedLists(ReadOnlyArrayInvertedLists &&); - virtual ~ReadOnlyArrayInvertedLists(); - - size_t list_size(size_t list_no) const override; - const uint8_t * get_codes (size_t list_no) const override; - const idx_t * get_ids (size_t list_no) const override; - - const uint8_t * get_all_codes() const; - const idx_t * get_all_ids() const; - const std::vector& get_list_length() const; - - size_t add_entries ( - size_t list_no, size_t n_entry, - const idx_t* ids, const uint8_t *code) override; - - void update_entries (size_t list_no, size_t offset, size_t n_entry, - const idx_t *ids, const uint8_t *code) override; - - void resize (size_t list_no, size_t new_size) override; - - bool is_readonly() const override; - - bool is_valid(); -}; - -/***************************************************************** - * Meta-inverted lists - * - * About terminology: the inverted lists are seen as a sparse matrix, - * that can be stacked horizontally, vertically and sliced. - *****************************************************************/ - -struct ReadOnlyInvertedLists: InvertedLists { - - ReadOnlyInvertedLists (size_t nlist, size_t code_size): - InvertedLists (nlist, code_size) {} - - size_t add_entries ( - size_t list_no, size_t n_entry, - const idx_t* ids, const uint8_t *code) override; - - void update_entries (size_t list_no, size_t offset, size_t n_entry, - const idx_t *ids, const uint8_t *code) override; - - void resize (size_t list_no, size_t new_size) override; - -}; - - -/// Horizontal stack of inverted lists -struct HStackInvertedLists: ReadOnlyInvertedLists { - - std::vectorils; - - /// build InvertedLists by concatenating nil of them - HStackInvertedLists (int nil, const InvertedLists **ils); - - size_t list_size(size_t list_no) const override; - const uint8_t * get_codes (size_t list_no) const override; - const idx_t * get_ids (size_t list_no) const override; - - void prefetch_lists (const idx_t *list_nos, int nlist) const override; - - void release_codes (size_t list_no, const uint8_t *codes) const override; - void release_ids (size_t list_no, const idx_t *ids) const override; - - idx_t get_single_id (size_t list_no, size_t offset) const override; - - const uint8_t * get_single_code ( - size_t list_no, size_t offset) const override; - -}; - -using ConcatenatedInvertedLists = HStackInvertedLists; - - -/// vertical slice of indexes in another InvertedLists -struct SliceInvertedLists: ReadOnlyInvertedLists { - const InvertedLists *il; - idx_t i0, i1; - - SliceInvertedLists(const InvertedLists *il, idx_t i0, idx_t i1); - - size_t list_size(size_t list_no) const override; - const uint8_t * get_codes (size_t list_no) const override; - const idx_t * get_ids (size_t list_no) const override; - - void release_codes (size_t list_no, const uint8_t *codes) const override; - void release_ids (size_t list_no, const idx_t *ids) const override; - - idx_t get_single_id (size_t list_no, size_t offset) const override; - - const uint8_t * get_single_code ( - size_t list_no, size_t offset) const override; - - void prefetch_lists (const idx_t *list_nos, int nlist) const override; -}; - - -struct VStackInvertedLists: ReadOnlyInvertedLists { - std::vectorils; - std::vector cumsz; - - /// build InvertedLists by concatenating nil of them - VStackInvertedLists (int nil, const InvertedLists **ils); - - size_t list_size(size_t list_no) const override; - const uint8_t * get_codes (size_t list_no) const override; - const idx_t * get_ids (size_t list_no) const override; - - void release_codes (size_t list_no, const uint8_t *codes) const override; - void release_ids (size_t list_no, const idx_t *ids) const override; - - idx_t get_single_id (size_t list_no, size_t offset) const override; - - const uint8_t * get_single_code ( - size_t list_no, size_t offset) const override; - - void prefetch_lists (const idx_t *list_nos, int nlist) const override; - -}; - - -/** use the first inverted lists if they are non-empty otherwise use the second - * - * This is useful if il1 has a few inverted lists that are too long, - * and that il0 has replacement lists for those, with empty lists for - * the others. */ -struct MaskedInvertedLists: ReadOnlyInvertedLists { - - const InvertedLists *il0; - const InvertedLists *il1; - - MaskedInvertedLists (const InvertedLists *il0, - const InvertedLists *il1); - - size_t list_size(size_t list_no) const override; - const uint8_t * get_codes (size_t list_no) const override; - const idx_t * get_ids (size_t list_no) const override; - - void release_codes (size_t list_no, const uint8_t *codes) const override; - void release_ids (size_t list_no, const idx_t *ids) const override; - - idx_t get_single_id (size_t list_no, size_t offset) const override; - - const uint8_t * get_single_code ( - size_t list_no, size_t offset) const override; - - void prefetch_lists (const idx_t *list_nos, int nlist) const override; - -}; - -} // namespace faiss - - -#endif diff --git a/core/src/index/thirdparty/faiss/LICENSE b/core/src/index/thirdparty/faiss/LICENSE deleted file mode 100644 index b96dcb0480..0000000000 --- a/core/src/index/thirdparty/faiss/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) Facebook, Inc. and its affiliates. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/core/src/index/thirdparty/faiss/Makefile b/core/src/index/thirdparty/faiss/Makefile deleted file mode 100644 index f81e67914c..0000000000 --- a/core/src/index/thirdparty/faiss/Makefile +++ /dev/null @@ -1,123 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - --include makefile.inc - -HEADERS = $(wildcard *.h impl/*.h utils/*.h) -SRC = $(wildcard *.cpp impl/*.cpp utils/*.cpp) -AVX_SRC = $(wildcard *avx.cpp impl/*avx.cpp utils/*avx.cpp) -AVX512_SRC = $(wildcard *avx512.cpp impl/*avx512.cpp utils/*avx512.cpp) -OBJ = $(SRC:.cpp=.o) -INSTALLDIRS = $(DESTDIR)$(libdir) $(DESTDIR)$(includedir)/faiss - -GPU_HEADERS = $(wildcard gpu/*.h gpu/impl/*.h gpu/impl/*.cuh gpu/utils/*.h gpu/utils/*.cuh) -GPU_CPPSRC = $(wildcard gpu/*.cpp gpu/impl/*.cpp gpu/utils/*.cpp) -GPU_CUSRC = $(wildcard gpu/*.cu gpu/impl/*.cu gpu/utils/*.cu \ -gpu/utils/nvidia/*.cu gpu/utils/blockselect/*.cu gpu/utils/warpselect/*.cu) -GPU_SRC = $(GPU_CPPSRC) $(GPU_CUSRC) -GPU_CPPOBJ = $(GPU_CPPSRC:.cpp=.o) -GPU_CUOBJ = $(GPU_CUSRC:.cu=.o) -GPU_OBJ = $(GPU_CPPOBJ) $(GPU_CUOBJ) - -ifneq ($(strip $(NVCC)),) - OBJ += $(GPU_OBJ) - HEADERS += $(GPU_HEADERS) -endif - -CPPFLAGS += -I. -NVCCFLAGS += -I. - -############################ -# Building - -all: libfaiss.a libfaiss.$(SHAREDEXT) - -libfaiss.a: $(OBJ) - $(AR) r $@ $^ - -libfaiss.$(SHAREDEXT): $(OBJ) - $(CXX) $(SHAREDFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) - -%.o: %.cpp - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(CPUFLAGS) -c $< -o $@ - -# support avx -%avx.o: %avx.cpp - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(CPUFLAGS) -mavx2 -c $< -o $@ - -# support avx512 -%avx512.o: %avx512.cpp - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(CPUFLAGS) -mavx512f -mavx512dq -mavx512bw -c $< -o $@ - -%.o: %.cu - $(NVCC) $(NVCCFLAGS) -c $< -o $@ - -clean: - rm -f libfaiss.a libfaiss.$(SHAREDEXT) - rm -f $(OBJ) - - -############################ -# Installing - -install: libfaiss.a libfaiss.$(SHAREDEXT) installdirs - cp libfaiss.a libfaiss.$(SHAREDEXT) $(DESTDIR)$(libdir) - tar cf - $(HEADERS) | tar xf - -C $(DESTDIR)$(includedir)/faiss/ - -installdirs: - $(MKDIR_P) $(INSTALLDIRS) - -uninstall: - rm -f $(DESTDIR)$(libdir)/libfaiss.a \ - $(DESTDIR)$(libdir)/libfaiss.$(SHAREDEXT) - rm -rf $(DESTDIR)$(includedir)/faiss - - -############################# -# Dependencies - --include depend - -depend: $(SRC) $(GPU_SRC) - for i in $^; do \ - $(CXXCPP) $(CPPFLAGS) -DCUDA_VERSION=7050 -x c++ -MM $$i; \ - done > depend - - -############################# -# Python - -py: libfaiss.a - $(MAKE) -C python - - -############################# -# Tests - -test: libfaiss.a py - $(MAKE) -C tests run - PYTHONPATH=./python/build/`ls python/build | grep lib` \ - $(PYTHON) -m unittest discover tests/ -v - -test_gpu: libfaiss.a - $(MAKE) -C gpu/test run - PYTHONPATH=./python/build/`ls python/build | grep lib` \ - $(PYTHON) -m unittest discover gpu/test/ -v - -############################# -# Demos - -demos: libfaiss.a - $(MAKE) -C demos - - -############################# -# Misc - -misc/test_blas: misc/test_blas.cpp - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) - - -.PHONY: all clean demos install installdirs py test test_gpu uninstall diff --git a/core/src/index/thirdparty/faiss/MatrixStats.cpp b/core/src/index/thirdparty/faiss/MatrixStats.cpp deleted file mode 100644 index 1862d1a52f..0000000000 --- a/core/src/index/thirdparty/faiss/MatrixStats.cpp +++ /dev/null @@ -1,252 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - - -#include - - -#include /* va_list, va_start, va_arg, va_end */ - -#include -#include -#include - -namespace faiss { - -/********************************************************************* - * MatrixStats - *********************************************************************/ - -MatrixStats::PerDimStats::PerDimStats(): - n(0), n_nan(0), n_inf(0), n0(0), - min(HUGE_VALF), max(-HUGE_VALF), - sum(0), sum2(0), - mean(NAN), stddev(NAN) -{} - - -void MatrixStats::PerDimStats::add (float x) -{ - n++; - if (std::isnan(x)) { - n_nan++; - return; - } - if (!std::isfinite(x)) { - n_inf++; - return; - } - if (x == 0) n0++; - if (x < min) min = x; - if (x > max) max = x; - sum += x; - sum2 += (double)x * (double)x; -} - -void MatrixStats::PerDimStats::compute_mean_std () -{ - n_valid = n - n_nan - n_inf; - mean = sum / n_valid; - double var = sum2 / n_valid - mean * mean; - if (var < 0) var = 0; - stddev = sqrt(var); -} - - -void MatrixStats::do_comment (const char *fmt, ...) -{ - va_list ap; - - /* Determine required size */ - va_start(ap, fmt); - size_t size = vsnprintf(buf, nbuf, fmt, ap); - va_end(ap); - - nbuf -= size; - buf += size; -} - - - -MatrixStats::MatrixStats (size_t n, size_t d, const float *x): - n(n), d(d), - n_collision(0), n_valid(0), n0(0), - min_norm2(HUGE_VAL), max_norm2(0) -{ - std::vector comment_buf (10000); - buf = comment_buf.data (); - nbuf = comment_buf.size(); - - do_comment ("analyzing %ld vectors of size %ld\n", n, d); - - if (d > 1024) { - do_comment ( - "indexing this many dimensions is hard, " - "please consider dimensionality reducution (with PCAMatrix)\n"); - } - - size_t nbytes = sizeof (x[0]) * d; - per_dim_stats.resize (d); - - for (size_t i = 0; i < n; i++) { - const float *xi = x + d * i; - double sum2 = 0; - for (size_t j = 0; j < d; j++) { - per_dim_stats[j].add (xi[j]); - sum2 += xi[j] * (double)xi[j]; - } - - if (std::isfinite (sum2)) { - n_valid++; - if (sum2 == 0) { - n0 ++; - } else { - if (sum2 < min_norm2) min_norm2 = sum2; - if (sum2 > max_norm2) max_norm2 = sum2; - } - } - - { // check hash - uint64_t hash = hash_bytes((const uint8_t*)xi, nbytes); - auto elt = occurrences.find (hash); - if (elt == occurrences.end()) { - Occurrence occ = {i, 1}; - occurrences[hash] = occ; - } else { - if (!memcmp (xi, x + elt->second.first * d, nbytes)) { - elt->second.count ++; - } else { - n_collision ++; - // we should use a list of collisions but overkill - } - } - } - } - - // invalid vecor stats - if (n_valid == n) { - do_comment ("no NaN or Infs in data\n"); - } else { - do_comment ("%ld vectors contain NaN or Inf " - "(or have too large components), " - "expect bad results with indexing!\n", n - n_valid); - } - - // copies in dataset - if (occurrences.size() == n) { - do_comment ("all vectors are distinct\n"); - } else { - do_comment ("%ld vectors are distinct (%.2f%%)\n", - occurrences.size(), - occurrences.size() * 100.0 / n); - - if (n_collision > 0) { - do_comment ("%ld collisions in hash table, " - "counts may be invalid\n", n_collision); - } - - Occurrence max = {0, 0}; - for (auto it = occurrences.begin(); - it != occurrences.end(); ++it) { - if (it->second.count > max.count) { - max = it->second; - } - } - do_comment ("vector %ld has %ld copies\n", max.first, max.count); - } - - { // norm stats - min_norm2 = sqrt (min_norm2); - max_norm2 = sqrt (max_norm2); - do_comment ("range of L2 norms=[%g, %g] (%ld null vectors)\n", - min_norm2, max_norm2, n0); - - if (max_norm2 < min_norm2 * 1.0001) { - do_comment ("vectors are normalized, inner product and " - "L2 search are equivalent\n"); - } - - if (max_norm2 > min_norm2 * 100) { - do_comment ("vectors have very large differences in norms, " - "is this normal?\n"); - } - } - - { // per dimension stats - - double max_std = 0, min_std = HUGE_VAL; - - size_t n_dangerous_range = 0, n_0_range = 0, n0 = 0; - - for (size_t j = 0; j < d; j++) { - PerDimStats &st = per_dim_stats[j]; - st.compute_mean_std (); - n0 += st.n0; - - if (st.max == st.min) { - n_0_range ++; - } else if (st.max < 1.001 * st.min) { - n_dangerous_range ++; - } - - if (st.stddev > max_std) max_std = st.stddev; - if (st.stddev < min_std) min_std = st.stddev; - } - - - - if (n0 == 0) { - do_comment ("matrix contains no 0s\n"); - } else { - do_comment ("matrix contains %.2f %% 0 entries\n", - n0 * 100.0 / (n * d)); - } - - if (n_0_range == 0) { - do_comment ("no constant dimensions\n"); - } else { - do_comment ("%ld dimensions are constant: they can be removed\n", - n_0_range); - } - - if (n_dangerous_range == 0) { - do_comment ("no dimension has a too large mean\n"); - } else { - do_comment ("%ld dimensions are too large " - "wrt. their variance, may loose precision " - "in IndexFlatL2 (use CenteringTransform)\n", - n_dangerous_range); - } - - do_comment ("stddevs per dimension are in [%g %g]\n", min_std, max_std); - - size_t n_small_var = 0; - - for (size_t j = 0; j < d; j++) { - const PerDimStats &st = per_dim_stats[j]; - if (st.stddev < max_std * 1e-4) { - n_small_var++; - } - } - - if (n_small_var > 0) { - do_comment ("%ld dimensions have negligible stddev wrt. " - "the largest dimension, they could be ignored", - n_small_var); - } - - } - comments = comment_buf.data (); - buf = nullptr; - nbuf = 0; -} - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/MatrixStats.h b/core/src/index/thirdparty/faiss/MatrixStats.h deleted file mode 100644 index 6418644c6e..0000000000 --- a/core/src/index/thirdparty/faiss/MatrixStats.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#pragma once - -#include -#include -#include -#include - - -namespace faiss { - - -/** Reports some statistics on a dataset and comments on them. - * - * It is a class rather than a function so that all stats can also be - * accessed from code */ - -struct MatrixStats { - MatrixStats (size_t n, size_t d, const float *x); - std::string comments; - - // raw statistics - size_t n, d; - size_t n_collision, n_valid, n0; - double min_norm2, max_norm2; - - struct PerDimStats { - size_t n, n_nan, n_inf, n0; - - float min, max; - double sum, sum2; - - size_t n_valid; - double mean, stddev; - - PerDimStats(); - void add (float x); - void compute_mean_std (); - }; - - std::vector per_dim_stats; - struct Occurrence { - size_t first; - size_t count; - }; - std::unordered_map occurrences; - - char *buf; - size_t nbuf; - void do_comment (const char *fmt, ...); - -}; - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/MetaIndexes.cpp b/core/src/index/thirdparty/faiss/MetaIndexes.cpp deleted file mode 100644 index 0094e17ba4..0000000000 --- a/core/src/index/thirdparty/faiss/MetaIndexes.cpp +++ /dev/null @@ -1,379 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include - -#include -#include -#include -#include - - -namespace faiss { - -namespace { - - -} // namespace - -/***************************************************** - * IndexIDMap implementation - *******************************************************/ - -template -IndexIDMapTemplate::IndexIDMapTemplate (IndexT *index): - index (index), - own_fields (false) -{ - FAISS_THROW_IF_NOT_MSG (index->ntotal == 0, "index must be empty on input"); - this->is_trained = index->is_trained; - this->metric_type = index->metric_type; - this->verbose = index->verbose; - this->d = index->d; -} - -template -void IndexIDMapTemplate::add - (idx_t, const typename IndexT::component_t *) -{ - FAISS_THROW_MSG ("add does not make sense with IndexIDMap, " - "use add_with_ids"); -} - - -template -void IndexIDMapTemplate::train - (idx_t n, const typename IndexT::component_t *x) -{ - index->train (n, x); - this->is_trained = index->is_trained; -} - -template -void IndexIDMapTemplate::reset () -{ - index->reset (); - id_map.clear(); - this->ntotal = 0; -} - - -template -void IndexIDMapTemplate::add_with_ids - (idx_t n, const typename IndexT::component_t * x, - const typename IndexT::idx_t *xids) -{ - index->add (n, x); - for (idx_t i = 0; i < n; i++) - id_map.push_back (xids[i]); - this->ntotal = index->ntotal; -} - - -template -void IndexIDMapTemplate::search - (idx_t n, const typename IndexT::component_t *x, idx_t k, - typename IndexT::distance_t *distances, typename IndexT::idx_t *labels, - ConcurrentBitsetPtr bitset) const -{ - index->search (n, x, k, distances, labels, bitset); - idx_t *li = labels; -#pragma omp parallel for - for (idx_t i = 0; i < n * k; i++) { - li[i] = li[i] < 0 ? li[i] : id_map[li[i]]; - } -} - -#if 0 -template -void IndexIDMapTemplate::get_vector_by_id(idx_t n, const idx_t *xid, component_t *x, - ConcurrentBitsetPtr bitset) -{ - /* only get vector by 1 id */ - FAISS_ASSERT(n == 1); - if (!bitset || !bitset->test(xid[0])) { - index->reconstruct(xid[0], x + 0 * IndexT::d); - } else { - memset(x, UINT8_MAX, IndexT::d * sizeof(component_t)); - } -} - -template -void IndexIDMapTemplate::search_by_id (idx_t n, const idx_t *xid, idx_t k, - typename IndexT::distance_t *distances, idx_t *labels, ConcurrentBitsetPtr bitset) -{ - auto x = new typename IndexT::component_t[n * IndexT::d]; - for (idx_t i = 0; i < n; i++) { - index->reconstruct(xid[i], x + i * IndexT::d); - } - index->search(n, x, k, distances, labels, bitset); - delete []x; -} -#endif - -template -void IndexIDMapTemplate::range_search - (typename IndexT::idx_t n, const typename IndexT::component_t *x, - typename IndexT::distance_t radius, RangeSearchResult *result, - ConcurrentBitsetPtr bitset) const -{ - index->range_search(n, x, radius, result, bitset); -#pragma omp parallel for - for (idx_t i = 0; i < result->lims[result->nq]; i++) { - result->labels[i] = result->labels[i] < 0 ? - result->labels[i] : id_map[result->labels[i]]; - } -} - -namespace { - -struct IDTranslatedSelector: IDSelector { - const std::vector & id_map; - const IDSelector & sel; - IDTranslatedSelector (const std::vector & id_map, - const IDSelector & sel): - id_map (id_map), sel (sel) - {} - bool is_member(idx_t id) const override { - return sel.is_member(id_map[id]); - } -}; - -} - -template -size_t IndexIDMapTemplate::remove_ids (const IDSelector & sel) -{ - // remove in sub-index first - IDTranslatedSelector sel2 (id_map, sel); - size_t nremove = index->remove_ids (sel2); - - int64_t j = 0; - for (idx_t i = 0; i < this->ntotal; i++) { - if (sel.is_member (id_map[i])) { - // remove - } else { - id_map[j] = id_map[i]; - j++; - } - } - FAISS_ASSERT (j == index->ntotal); - this->ntotal = j; - id_map.resize(this->ntotal); - return nremove; -} - -template -IndexIDMapTemplate::~IndexIDMapTemplate () -{ - if (own_fields) delete index; -} - - - -/***************************************************** - * IndexIDMap2 implementation - *******************************************************/ - -template -IndexIDMap2Template::IndexIDMap2Template (IndexT *index): - IndexIDMapTemplate (index) -{} - -template -void IndexIDMap2Template::add_with_ids - (idx_t n, const typename IndexT::component_t* x, - const typename IndexT::idx_t* xids) -{ - size_t prev_ntotal = this->ntotal; - IndexIDMapTemplate::add_with_ids (n, x, xids); - for (size_t i = prev_ntotal; i < this->ntotal; i++) { - rev_map [this->id_map [i]] = i; - } -} - -template -void IndexIDMap2Template::construct_rev_map () -{ - rev_map.clear (); - for (size_t i = 0; i < this->ntotal; i++) { - rev_map [this->id_map [i]] = i; - } -} - - -template -size_t IndexIDMap2Template::remove_ids(const IDSelector& sel) -{ - // This is quite inefficient - size_t nremove = IndexIDMapTemplate::remove_ids (sel); - construct_rev_map (); - return nremove; -} - -template -void IndexIDMap2Template::reconstruct - (idx_t key, typename IndexT::component_t * recons) const -{ - try { - this->index->reconstruct (rev_map.at (key), recons); - } catch (const std::out_of_range& e) { - FAISS_THROW_FMT ("key %ld not found", key); - } -} - - -// explicit template instantiations - -template struct IndexIDMapTemplate; -template struct IndexIDMapTemplate; -template struct IndexIDMap2Template; -template struct IndexIDMap2Template; - - -/***************************************************** - * IndexSplitVectors implementation - *******************************************************/ - - -IndexSplitVectors::IndexSplitVectors (idx_t d, bool threaded): - Index (d), own_fields (false), - threaded (threaded), sum_d (0) -{ - -} - -void IndexSplitVectors::add_sub_index (Index *index) -{ - sub_indexes.push_back (index); - sync_with_sub_indexes (); -} - -void IndexSplitVectors::sync_with_sub_indexes () -{ - if (sub_indexes.empty()) return; - Index * index0 = sub_indexes[0]; - sum_d = index0->d; - metric_type = index0->metric_type; - is_trained = index0->is_trained; - ntotal = index0->ntotal; - for (int i = 1; i < sub_indexes.size(); i++) { - Index * index = sub_indexes[i]; - FAISS_THROW_IF_NOT (metric_type == index->metric_type); - FAISS_THROW_IF_NOT (ntotal == index->ntotal); - sum_d += index->d; - } - -} - -void IndexSplitVectors::add(idx_t /*n*/, const float* /*x*/) { - FAISS_THROW_MSG("not implemented"); -} - - - -void IndexSplitVectors::search ( - idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels, - ConcurrentBitsetPtr bitset) const -{ - FAISS_THROW_IF_NOT_MSG (k == 1, - "search implemented only for k=1"); - FAISS_THROW_IF_NOT_MSG (sum_d == d, - "not enough indexes compared to # dimensions"); - - int64_t nshard = sub_indexes.size(); - float *all_distances = new float [nshard * k * n]; - idx_t *all_labels = new idx_t [nshard * k * n]; - ScopeDeleter del (all_distances); - ScopeDeleter del2 (all_labels); - - auto query_func = [n, x, k, distances, labels, all_distances, all_labels, this] - (int no) { - const IndexSplitVectors *index = this; - float *distances1 = no == 0 ? distances : all_distances + no * k * n; - idx_t *labels1 = no == 0 ? labels : all_labels + no * k * n; - if (index->verbose) - printf ("begin query shard %d on %ld points\n", no, n); - const Index * sub_index = index->sub_indexes[no]; - int64_t sub_d = sub_index->d, d = index->d; - idx_t ofs = 0; - for (int i = 0; i < no; i++) ofs += index->sub_indexes[i]->d; - float *sub_x = new float [sub_d * n]; - ScopeDeleter del1 (sub_x); - for (idx_t i = 0; i < n; i++) - memcpy (sub_x + i * sub_d, x + ofs + i * d, sub_d * sizeof (sub_x)); - sub_index->search (n, sub_x, k, distances1, labels1); - if (index->verbose) - printf ("end query shard %d\n", no); - }; - - if (!threaded) { - for (int i = 0; i < nshard; i++) { - query_func(i); - } - } else { - std::vector > threads; - std::vector> v; - - for (int i = 0; i < nshard; i++) { - threads.emplace_back(new WorkerThread()); - WorkerThread *wt = threads.back().get(); - v.emplace_back(wt->add([i, query_func](){query_func(i); })); - } - - // Blocking wait for completion - for (auto& func : v) { - func.get(); - } - } - - int64_t factor = 1; - for (int i = 0; i < nshard; i++) { - if (i > 0) { // results of 0 are already in the table - const float *distances_i = all_distances + i * k * n; - const idx_t *labels_i = all_labels + i * k * n; - for (int64_t j = 0; j < n; j++) { - if (labels[j] >= 0 && labels_i[j] >= 0) { - labels[j] += labels_i[j] * factor; - distances[j] += distances_i[j]; - } else { - labels[j] = -1; - distances[j] = 0.0 / 0.0; - } - } - } - factor *= sub_indexes[i]->ntotal; - } - -} - -void IndexSplitVectors::train(idx_t /*n*/, const float* /*x*/) { - FAISS_THROW_MSG("not implemented"); -} - -void IndexSplitVectors::reset () -{ - FAISS_THROW_MSG ("not implemented"); -} - - -IndexSplitVectors::~IndexSplitVectors () -{ - if (own_fields) { - for (int s = 0; s < sub_indexes.size(); s++) - delete sub_indexes [s]; - } -} - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/MetaIndexes.h b/core/src/index/thirdparty/faiss/MetaIndexes.h deleted file mode 100644 index cfac6a5572..0000000000 --- a/core/src/index/thirdparty/faiss/MetaIndexes.h +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef META_INDEXES_H -#define META_INDEXES_H - -#include -#include -#include -#include -#include - -namespace faiss { - -/** Index that translates search results to ids */ -template -struct IndexIDMapTemplate : IndexT { - using idx_t = typename IndexT::idx_t; - using component_t = typename IndexT::component_t; - using distance_t = typename IndexT::distance_t; - - IndexT * index; ///! the sub-index - bool own_fields; ///! whether pointers are deleted in destructo - std::vector id_map; - - explicit IndexIDMapTemplate (IndexT *index); - - /// @param xids if non-null, ids to store for the vectors (size n) - void add_with_ids(idx_t n, const component_t* x, const idx_t* xids) override; - - /// this will fail. Use add_with_ids - void add(idx_t n, const component_t* x) override; - - void search( - idx_t n, const component_t* x, idx_t k, - distance_t* distances, idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - -#if 0 - void get_vector_by_id(idx_t n, const idx_t *xid, component_t *x, ConcurrentBitsetPtr bitset = nullptr) override; - - void search_by_id (idx_t n, const idx_t *xid, idx_t k, distance_t *distances, idx_t *labels, - ConcurrentBitsetPtr bitset = nullptr) override; -#endif - - void train(idx_t n, const component_t* x) override; - - void reset() override; - - /// remove ids adapted to IndexFlat - size_t remove_ids(const IDSelector& sel) override; - - void range_search (idx_t n, const component_t *x, distance_t radius, - RangeSearchResult *result, - ConcurrentBitsetPtr bitset = nullptr) const override; - - ~IndexIDMapTemplate () override; - IndexIDMapTemplate () {own_fields=false; index=nullptr; } -}; - -using IndexIDMap = IndexIDMapTemplate; -using IndexBinaryIDMap = IndexIDMapTemplate; - - -/** same as IndexIDMap but also provides an efficient reconstruction - * implementation via a 2-way index */ -template -struct IndexIDMap2Template : IndexIDMapTemplate { - using idx_t = typename IndexT::idx_t; - using component_t = typename IndexT::component_t; - using distance_t = typename IndexT::distance_t; - - std::unordered_map rev_map; - - explicit IndexIDMap2Template (IndexT *index); - - /// make the rev_map from scratch - void construct_rev_map (); - - void add_with_ids(idx_t n, const component_t* x, const idx_t* xids) override; - - size_t remove_ids(const IDSelector& sel) override; - - void reconstruct (idx_t key, component_t * recons) const override; - - ~IndexIDMap2Template() override {} - IndexIDMap2Template () {} -}; - -using IndexIDMap2 = IndexIDMap2Template; -using IndexBinaryIDMap2 = IndexIDMap2Template; - - -/** splits input vectors in segments and assigns each segment to a sub-index - * used to distribute a MultiIndexQuantizer - */ -struct IndexSplitVectors: Index { - bool own_fields; - bool threaded; - std::vector sub_indexes; - idx_t sum_d; /// sum of dimensions seen so far - - explicit IndexSplitVectors (idx_t d, bool threaded = false); - - void add_sub_index (Index *); - void sync_with_sub_indexes (); - - void add(idx_t n, const float* x) override; - - void search( - idx_t n, - const float* x, - idx_t k, - float* distances, - idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - void train(idx_t n, const float* x) override; - - void reset() override; - - ~IndexSplitVectors() override; -}; - - -} // namespace faiss - - -#endif diff --git a/core/src/index/thirdparty/faiss/MetricType.h b/core/src/index/thirdparty/faiss/MetricType.h deleted file mode 100644 index 5248f5b801..0000000000 --- a/core/src/index/thirdparty/faiss/MetricType.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_METRIC_TYPE_H -#define FAISS_METRIC_TYPE_H - -namespace faiss { - -/// The metric space for vector comparison for Faiss indices and algorithms. -/// -/// Most algorithms support both inner product and L2, with the flat -/// (brute-force) indices supporting additional metric types for vector -/// comparison. -enum MetricType { - METRIC_INNER_PRODUCT = 0, ///< maximum inner product search - METRIC_L2 = 1, ///< squared L2 search - METRIC_L1, ///< L1 (aka cityblock) - METRIC_Linf, ///< infinity distance - METRIC_Lp, ///< L_p distance, p is given by a faiss::Index - /// metric_arg - METRIC_Jaccard, - METRIC_Tanimoto, - METRIC_Hamming, - METRIC_Substructure, ///< Tversky case alpha = 0, beta = 1 - METRIC_Superstructure, ///< Tversky case alpha = 1, beta = 0 - - /// some additional metrics defined in scipy.spatial.distance - METRIC_Canberra = 20, - METRIC_BrayCurtis, - METRIC_JensenShannon, -}; - -} - -#endif diff --git a/core/src/index/thirdparty/faiss/OnDiskInvertedLists.cpp b/core/src/index/thirdparty/faiss/OnDiskInvertedLists.cpp deleted file mode 100644 index 2b798123d8..0000000000 --- a/core/src/index/thirdparty/faiss/OnDiskInvertedLists.cpp +++ /dev/null @@ -1,674 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include - -#include - -#include -#include -#include - -#include -#include - - -namespace faiss { - - -/********************************************** - * LockLevels - **********************************************/ - - -struct LockLevels { - /* There n times lock1(n), one lock2 and one lock3 - * Invariants: - * a single thread can hold one lock1(n) for some n - * a single thread can hold lock2, if it holds lock1(n) for some n - * a single thread can hold lock3, if it holds lock1(n) for some n - * AND lock2 AND no other thread holds lock1(m) for m != n - */ - pthread_mutex_t mutex1; - pthread_cond_t level1_cv; - pthread_cond_t level2_cv; - pthread_cond_t level3_cv; - - std::unordered_set level1_holders; // which level1 locks are held - int n_level2; // nb threads that wait on level2 - bool level3_in_use; // a threads waits on level3 - bool level2_in_use; - - LockLevels() { - pthread_mutex_init(&mutex1, nullptr); - pthread_cond_init(&level1_cv, nullptr); - pthread_cond_init(&level2_cv, nullptr); - pthread_cond_init(&level3_cv, nullptr); - n_level2 = 0; - level2_in_use = false; - level3_in_use = false; - } - - ~LockLevels() { - pthread_cond_destroy(&level1_cv); - pthread_cond_destroy(&level2_cv); - pthread_cond_destroy(&level3_cv); - pthread_mutex_destroy(&mutex1); - } - - void lock_1(int no) { - pthread_mutex_lock(&mutex1); - while (level3_in_use || level1_holders.count(no) > 0) { - pthread_cond_wait(&level1_cv, &mutex1); - } - level1_holders.insert(no); - pthread_mutex_unlock(&mutex1); - } - - void unlock_1(int no) { - pthread_mutex_lock(&mutex1); - assert(level1_holders.count(no) == 1); - level1_holders.erase(no); - if (level3_in_use) { // a writer is waiting - pthread_cond_signal(&level3_cv); - } else { - pthread_cond_broadcast(&level1_cv); - } - pthread_mutex_unlock(&mutex1); - } - - void lock_2() { - pthread_mutex_lock(&mutex1); - n_level2 ++; - if (level3_in_use) { // tell waiting level3 that we are blocked - pthread_cond_signal(&level3_cv); - } - while (level2_in_use) { - pthread_cond_wait(&level2_cv, &mutex1); - } - level2_in_use = true; - pthread_mutex_unlock(&mutex1); - } - - void unlock_2() { - pthread_mutex_lock(&mutex1); - level2_in_use = false; - n_level2 --; - pthread_cond_signal(&level2_cv); - pthread_mutex_unlock(&mutex1); - } - - void lock_3() { - pthread_mutex_lock(&mutex1); - level3_in_use = true; - // wait until there are no level1 holders anymore except the - // ones that are waiting on level2 (we are holding lock2) - while (level1_holders.size() > n_level2) { - pthread_cond_wait(&level3_cv, &mutex1); - } - // don't release the lock! - } - - void unlock_3() { - level3_in_use = false; - // wake up all level1_holders - pthread_cond_broadcast(&level1_cv); - pthread_mutex_unlock(&mutex1); - } - - void print () { - pthread_mutex_lock(&mutex1); - printf("State: level3_in_use=%d n_level2=%d level1_holders: [", level3_in_use, n_level2); - for (int k : level1_holders) { - printf("%d ", k); - } - printf("]\n"); - pthread_mutex_unlock(&mutex1); - } - -}; - -/********************************************** - * OngoingPrefetch - **********************************************/ - -struct OnDiskInvertedLists::OngoingPrefetch { - - struct Thread { - pthread_t pth; - OngoingPrefetch *pf; - - bool one_list () { - idx_t list_no = pf->get_next_list(); - if(list_no == -1) return false; - const OnDiskInvertedLists *od = pf->od; - od->locks->lock_1 (list_no); - size_t n = od->list_size (list_no); - const Index::idx_t *idx = od->get_ids (list_no); - const uint8_t *codes = od->get_codes (list_no); - int cs = 0; - for (size_t i = 0; i < n;i++) { - cs += idx[i]; - } - const idx_t *codes8 = (const idx_t*)codes; - idx_t n8 = n * od->code_size / 8; - - for (size_t i = 0; i < n8;i++) { - cs += codes8[i]; - } - od->locks->unlock_1(list_no); - - global_cs += cs & 1; - return true; - } - - }; - - std::vector threads; - - pthread_mutex_t list_ids_mutex; - std::vector list_ids; - int cur_list; - - // mutex for the list of tasks - pthread_mutex_t mutex; - - // pretext to avoid code below to be optimized out - static int global_cs; - - const OnDiskInvertedLists *od; - - explicit OngoingPrefetch (const OnDiskInvertedLists *od): od (od) - { - pthread_mutex_init (&mutex, nullptr); - pthread_mutex_init (&list_ids_mutex, nullptr); - cur_list = 0; - } - - static void* prefetch_list (void * arg) { - Thread *th = static_cast(arg); - - while (th->one_list()) ; - - return nullptr; - } - - idx_t get_next_list () { - idx_t list_no = -1; - pthread_mutex_lock (&list_ids_mutex); - if (cur_list >= 0 && cur_list < list_ids.size()) { - list_no = list_ids[cur_list++]; - } - pthread_mutex_unlock (&list_ids_mutex); - return list_no; - } - - void prefetch_lists (const idx_t *list_nos, int n) { - pthread_mutex_lock (&mutex); - pthread_mutex_lock (&list_ids_mutex); - list_ids.clear (); - pthread_mutex_unlock (&list_ids_mutex); - for (auto &th: threads) { - pthread_join (th.pth, nullptr); - } - - threads.resize (0); - cur_list = 0; - int nt = std::min (n, od->prefetch_nthread); - - if (nt > 0) { - // prepare tasks - for (int i = 0; i < n; i++) { - idx_t list_no = list_nos[i]; - if (list_no >= 0 && od->list_size(list_no) > 0) { - list_ids.push_back (list_no); - } - } - // prepare threads - threads.resize (nt); - for (Thread &th: threads) { - th.pf = this; - pthread_create (&th.pth, nullptr, prefetch_list, &th); - } - } - pthread_mutex_unlock (&mutex); - } - - ~OngoingPrefetch () { - pthread_mutex_lock (&mutex); - for (auto &th: threads) { - pthread_join (th.pth, nullptr); - } - pthread_mutex_unlock (&mutex); - pthread_mutex_destroy (&mutex); - pthread_mutex_destroy (&list_ids_mutex); - } - -}; - -int OnDiskInvertedLists::OngoingPrefetch::global_cs = 0; - - -void OnDiskInvertedLists::prefetch_lists (const idx_t *list_nos, int n) const -{ - pf->prefetch_lists (list_nos, n); -} - - - -/********************************************** - * OnDiskInvertedLists: mmapping - **********************************************/ - - -void OnDiskInvertedLists::do_mmap () -{ - const char *rw_flags = read_only ? "r" : "r+"; - int prot = read_only ? PROT_READ : PROT_WRITE | PROT_READ; - FILE *f = fopen (filename.c_str(), rw_flags); - FAISS_THROW_IF_NOT_FMT (f, "could not open %s in mode %s: %s", - filename.c_str(), rw_flags, strerror(errno)); - - uint8_t * ptro = (uint8_t*)mmap (nullptr, totsize, - prot, MAP_SHARED, fileno (f), 0); - - FAISS_THROW_IF_NOT_FMT (ptro != MAP_FAILED, - "could not mmap %s: %s", - filename.c_str(), - strerror(errno)); - ptr = ptro; - fclose (f); - -} - -void OnDiskInvertedLists::update_totsize (size_t new_size) -{ - - // unmap file - if (ptr != nullptr) { - int err = munmap (ptr, totsize); - FAISS_THROW_IF_NOT_FMT (err == 0, "munmap error: %s", - strerror(errno)); - } - if (totsize == 0) { - // must create file before truncating it - FILE *f = fopen (filename.c_str(), "w"); - FAISS_THROW_IF_NOT_FMT (f, "could not open %s in mode W: %s", - filename.c_str(), strerror(errno)); - fclose (f); - } - - if (new_size > totsize) { - if (!slots.empty() && - slots.back().offset + slots.back().capacity == totsize) { - slots.back().capacity += new_size - totsize; - } else { - slots.push_back (Slot(totsize, new_size - totsize)); - } - } else { - assert(!"not implemented"); - } - - totsize = new_size; - - // create file - printf ("resizing %s to %ld bytes\n", filename.c_str(), totsize); - - int err = truncate (filename.c_str(), totsize); - - FAISS_THROW_IF_NOT_FMT (err == 0, "truncate %s to %ld: %s", - filename.c_str(), totsize, - strerror(errno)); - do_mmap (); -} - - - - - - -/********************************************** - * OnDiskInvertedLists - **********************************************/ - -#define INVALID_OFFSET (size_t)(-1) - -OnDiskInvertedLists::List::List (): - size (0), capacity (0), offset (INVALID_OFFSET) -{} - -OnDiskInvertedLists::Slot::Slot (size_t offset, size_t capacity): - offset (offset), capacity (capacity) -{} - -OnDiskInvertedLists::Slot::Slot (): - offset (0), capacity (0) -{} - - - -OnDiskInvertedLists::OnDiskInvertedLists ( - size_t nlist, size_t code_size, - const char *filename): - InvertedLists (nlist, code_size), - filename (filename), - totsize (0), - ptr (nullptr), - read_only (false), - locks (new LockLevels ()), - pf (new OngoingPrefetch (this)), - prefetch_nthread (32) -{ - lists.resize (nlist); - - // slots starts empty -} - -OnDiskInvertedLists::OnDiskInvertedLists (): - OnDiskInvertedLists (0, 0, "") -{ -} - -OnDiskInvertedLists::~OnDiskInvertedLists () -{ - delete pf; - - // unmap all lists - if (ptr != nullptr) { - int err = munmap (ptr, totsize); - if (err != 0) { - fprintf(stderr, "mumap error: %s", - strerror(errno)); - } - } - delete locks; -} - - - - -size_t OnDiskInvertedLists::list_size(size_t list_no) const -{ - return lists[list_no].size; -} - - -const uint8_t * OnDiskInvertedLists::get_codes (size_t list_no) const -{ - if (lists[list_no].offset == INVALID_OFFSET) { - return nullptr; - } - - return ptr + lists[list_no].offset; -} - -const Index::idx_t * OnDiskInvertedLists::get_ids (size_t list_no) const -{ - if (lists[list_no].offset == INVALID_OFFSET) { - return nullptr; - } - - return (const idx_t*)(ptr + lists[list_no].offset + - code_size * lists[list_no].capacity); -} - - -void OnDiskInvertedLists::update_entries ( - size_t list_no, size_t offset, size_t n_entry, - const idx_t *ids_in, const uint8_t *codes_in) -{ - FAISS_THROW_IF_NOT (!read_only); - if (n_entry == 0) return; - const List & l = lists[list_no]; - assert (n_entry + offset <= l.size); - idx_t *ids = const_cast(get_ids (list_no)); - memcpy (ids + offset, ids_in, sizeof(ids_in[0]) * n_entry); - uint8_t *codes = const_cast(get_codes (list_no)); - memcpy (codes + offset * code_size, codes_in, code_size * n_entry); -} - -size_t OnDiskInvertedLists::add_entries ( - size_t list_no, size_t n_entry, - const idx_t* ids, const uint8_t *code) -{ - FAISS_THROW_IF_NOT (!read_only); - locks->lock_1 (list_no); - size_t o = list_size (list_no); - resize_locked (list_no, n_entry + o); - update_entries (list_no, o, n_entry, ids, code); - locks->unlock_1 (list_no); - return o; -} - -void OnDiskInvertedLists::resize (size_t list_no, size_t new_size) -{ - FAISS_THROW_IF_NOT (!read_only); - locks->lock_1 (list_no); - resize_locked (list_no, new_size); - locks->unlock_1 (list_no); -} - - - -void OnDiskInvertedLists::resize_locked (size_t list_no, size_t new_size) -{ - List & l = lists[list_no]; - - if (new_size <= l.capacity && - new_size > l.capacity / 2) { - l.size = new_size; - return; - } - - // otherwise we release the current slot, and find a new one - - locks->lock_2 (); - free_slot (l.offset, l.capacity); - - List new_l; - - if (new_size == 0) { - new_l = List(); - } else { - new_l.size = new_size; - new_l.capacity = 1; - while (new_l.capacity < new_size) { - new_l.capacity *= 2; - } - new_l.offset = allocate_slot ( - new_l.capacity * (sizeof(idx_t) + code_size)); - } - - // copy common data - if (l.offset != new_l.offset) { - size_t n = std::min (new_size, l.size); - if (n > 0) { - memcpy (ptr + new_l.offset, get_codes(list_no), n * code_size); - memcpy (ptr + new_l.offset + new_l.capacity * code_size, - get_ids (list_no), n * sizeof(idx_t)); - } - } - - lists[list_no] = new_l; - locks->unlock_2 (); -} - -size_t OnDiskInvertedLists::allocate_slot (size_t capacity) { - // should hold lock2 - - auto it = slots.begin(); - while (it != slots.end() && it->capacity < capacity) { - it++; - } - - if (it == slots.end()) { - // not enough capacity - size_t new_size = totsize == 0 ? 32 : totsize * 2; - while (new_size - totsize < capacity) - new_size *= 2; - locks->lock_3 (); - update_totsize(new_size); - locks->unlock_3 (); - it = slots.begin(); - while (it != slots.end() && it->capacity < capacity) { - it++; - } - assert (it != slots.end()); - } - - size_t o = it->offset; - if (it->capacity == capacity) { - slots.erase (it); - } else { - // take from beginning of slot - it->capacity -= capacity; - it->offset += capacity; - } - - return o; -} - - - -void OnDiskInvertedLists::free_slot (size_t offset, size_t capacity) { - - // should hold lock2 - if (capacity == 0) return; - - auto it = slots.begin(); - while (it != slots.end() && it->offset <= offset) { - it++; - } - - size_t inf = 1UL << 60; - - size_t end_prev = inf; - if (it != slots.begin()) { - auto prev = it; - prev--; - end_prev = prev->offset + prev->capacity; - } - - size_t begin_next = 1L << 60; - if (it != slots.end()) { - begin_next = it->offset; - } - - assert (end_prev == inf || offset >= end_prev); - assert (offset + capacity <= begin_next); - - if (offset == end_prev) { - auto prev = it; - prev--; - if (offset + capacity == begin_next) { - prev->capacity += capacity + it->capacity; - slots.erase (it); - } else { - prev->capacity += capacity; - } - } else { - if (offset + capacity == begin_next) { - it->offset -= capacity; - it->capacity += capacity; - } else { - slots.insert (it, Slot (offset, capacity)); - } - } - - // TODO shrink global storage if needed -} - - -/***************************************** - * Compact form - *****************************************/ - -size_t OnDiskInvertedLists::merge_from (const InvertedLists **ils, int n_il, - bool verbose) -{ - FAISS_THROW_IF_NOT_MSG (totsize == 0, "works only on an empty InvertedLists"); - - std::vector sizes (nlist); - for (int i = 0; i < n_il; i++) { - const InvertedLists *il = ils[i]; - FAISS_THROW_IF_NOT (il->nlist == nlist && il->code_size == code_size); - - for (size_t j = 0; j < nlist; j++) { - sizes [j] += il->list_size(j); - } - } - - size_t cums = 0; - size_t ntotal = 0; - for (size_t j = 0; j < nlist; j++) { - ntotal += sizes[j]; - lists[j].size = 0; - lists[j].capacity = sizes[j]; - lists[j].offset = cums; - cums += lists[j].capacity * (sizeof(idx_t) + code_size); - } - - update_totsize (cums); - - - size_t nmerged = 0; - double t0 = getmillisecs(), last_t = t0; - -#pragma omp parallel for - for (size_t j = 0; j < nlist; j++) { - List & l = lists[j]; - for (int i = 0; i < n_il; i++) { - const InvertedLists *il = ils[i]; - size_t n_entry = il->list_size(j); - l.size += n_entry; - update_entries (j, l.size - n_entry, n_entry, - ScopedIds(il, j).get(), - ScopedCodes(il, j).get()); - } - assert (l.size == l.capacity); - if (verbose) { -#pragma omp critical - { - nmerged++; - double t1 = getmillisecs(); - if (t1 - last_t > 500) { - printf("merged %ld lists in %.3f s\r", - nmerged, (t1 - t0) / 1000.0); - fflush(stdout); - last_t = t1; - } - } - } - } - if(verbose) { - printf("\n"); - } - - return ntotal; -} - - -void OnDiskInvertedLists::crop_invlists(size_t l0, size_t l1) -{ - FAISS_THROW_IF_NOT(0 <= l0 && l0 <= l1 && l1 <= nlist); - - std::vector new_lists (l1 - l0); - memcpy (new_lists.data(), &lists[l0], (l1 - l0) * sizeof(List)); - - lists.swap(new_lists); - - nlist = l1 - l0; -} - - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/OnDiskInvertedLists.h b/core/src/index/thirdparty/faiss/OnDiskInvertedLists.h deleted file mode 100644 index 3476b48ca9..0000000000 --- a/core/src/index/thirdparty/faiss/OnDiskInvertedLists.h +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_ON_DISK_INVERTED_LISTS_H -#define FAISS_ON_DISK_INVERTED_LISTS_H - -#include -#include - -#include - -namespace faiss { - - -struct LockLevels; - -/** On-disk storage of inverted lists. - * - * The data is stored in a mmapped chunk of memory (base ptointer ptr, - * size totsize). Each list is a range of memory that contains (object - * List) that contains: - * - * - uint8_t codes[capacity * code_size] - * - followed by idx_t ids[capacity] - * - * in each of the arrays, the size <= capacity first elements are - * used, the rest is not initialized. - * - * Addition and resize are supported by: - * - roundind up the capacity of the lists to a power of two - * - maintaining a list of empty slots, sorted by size. - * - resizing the mmapped block is adjusted as needed. - * - * An OnDiskInvertedLists is compact if the size == capacity for all - * lists and there are no available slots. - * - * Addition to the invlists is slow. For incremental add it is better - * to use a default ArrayInvertedLists object and convert it to an - * OnDisk with merge_from. - * - * When it is known that a set of lists will be accessed, it is useful - * to call prefetch_lists, that launches a set of threads to read the - * lists in parallel. - */ -struct OnDiskInvertedLists: InvertedLists { - - struct List { - size_t size; // size of inverted list (entries) - size_t capacity; // allocated size (entries) - size_t offset; // offset in buffer (bytes) - List (); - }; - - // size nlist - std::vector lists; - - struct Slot { - size_t offset; // bytes - size_t capacity; // bytes - Slot (size_t offset, size_t capacity); - Slot (); - }; - - // size whatever space remains - std::list slots; - - std::string filename; - size_t totsize; - uint8_t *ptr; // mmap base pointer - bool read_only; /// are inverted lists mapped read-only - - OnDiskInvertedLists (size_t nlist, size_t code_size, - const char *filename); - - size_t list_size(size_t list_no) const override; - const uint8_t * get_codes (size_t list_no) const override; - const idx_t * get_ids (size_t list_no) const override; - - size_t add_entries ( - size_t list_no, size_t n_entry, - const idx_t* ids, const uint8_t *code) override; - - void update_entries (size_t list_no, size_t offset, size_t n_entry, - const idx_t *ids, const uint8_t *code) override; - - void resize (size_t list_no, size_t new_size) override; - - // copy all inverted lists into *this, in compact form (without - // allocating slots) - size_t merge_from (const InvertedLists **ils, int n_il, bool verbose=false); - - /// restrict the inverted lists to l0:l1 without touching the mmapped region - void crop_invlists(size_t l0, size_t l1); - - void prefetch_lists (const idx_t *list_nos, int nlist) const override; - - virtual ~OnDiskInvertedLists (); - - // private - - LockLevels * locks; - - // encapsulates the threads that are busy prefeteching - struct OngoingPrefetch; - OngoingPrefetch *pf; - int prefetch_nthread; - - void do_mmap (); - void update_totsize (size_t new_totsize); - void resize_locked (size_t list_no, size_t new_size); - size_t allocate_slot (size_t capacity); - void free_slot (size_t offset, size_t capacity); - - // empty constructor for the I/O functions - OnDiskInvertedLists (); -}; - - -} // namespace faiss - -#endif diff --git a/core/src/index/thirdparty/faiss/README.md b/core/src/index/thirdparty/faiss/README.md deleted file mode 100644 index 299ad809da..0000000000 --- a/core/src/index/thirdparty/faiss/README.md +++ /dev/null @@ -1,91 +0,0 @@ -# Faiss - -Faiss is a library for efficient similarity search and clustering of dense vectors. It contains algorithms that search in sets of vectors of any size, up to ones that possibly do not fit in RAM. It also contains supporting code for evaluation and parameter tuning. Faiss is written in C++ with complete wrappers for Python/numpy. Some of the most useful algorithms are implemented on the GPU. It is developed by [Facebook AI Research](https://research.fb.com/category/facebook-ai-research-fair/). - -## NEWS - -*NEW: version 1.6.1 (2019-11-29) bugfix.* - -*NEW: version 1.6.0 (2019-10-15) code structure reorg, support for codec interface.* - -*NEW: version 1.5.3 (2019-06-24) fix performance regression in IndexIVF.* - -*NEW: version 1.5.2 (2019-05-27) the license was relaxed to MIT from BSD+Patents. Read LICENSE for details.* - -*NEW: version 1.5.0 (2018-12-19) GPU binary flat index and binary HNSW index* - -*NEW: version 1.4.0 (2018-08-30) no more crashes in pure Python code* - -*NEW: version 1.3.0 (2018-07-12) support for binary indexes* - -*NEW: latest commit (2018-02-22) supports on-disk storage of inverted indexes, see demos/demo_ondisk_ivf.py* - -*NEW: latest commit (2018-01-09) includes an implementation of the HNSW indexing method, see benchs/bench_hnsw.py* - -*NEW: there is now a Facebook public discussion group for Faiss users at https://www.facebook.com/groups/faissusers/* - -*NEW: on 2017-07-30, the license on Faiss was relaxed to BSD from CC-BY-NC. Read LICENSE for details.* - -## Introduction - -Faiss contains several methods for similarity search. It assumes that the instances are represented as vectors and are identified by an integer, and that the vectors can be compared with L2 (Euclidean) distances or dot products. Vectors that are similar to a query vector are those that have the lowest L2 distance or the highest dot product with the query vector. It also supports cosine similarity, since this is a dot product on normalized vectors. - -Most of the methods, like those based on binary vectors and compact quantization codes, solely use a compressed representation of the vectors and do not require to keep the original vectors. This generally comes at the cost of a less precise search but these methods can scale to billions of vectors in main memory on a single server. - -The GPU implementation can accept input from either CPU or GPU memory. On a server with GPUs, the GPU indexes can be used a drop-in replacement for the CPU indexes (e.g., replace `IndexFlatL2` with `GpuIndexFlatL2`) and copies to/from GPU memory are handled automatically. Results will be faster however if both input and output remain resident on the GPU. Both single and multi-GPU usage is supported. - -## Building - -The library is mostly implemented in C++, with optional GPU support provided via CUDA, and an optional Python interface. The CPU version requires a BLAS library. It compiles with a Makefile and can be packaged in a docker image. See [INSTALL.md](INSTALL.md) for details. - -## How Faiss works - -Faiss is built around an index type that stores a set of vectors, and provides a function to search in them with L2 and/or dot product vector comparison. Some index types are simple baselines, such as exact search. Most of the available indexing structures correspond to various trade-offs with respect to - -- search time -- search quality -- memory used per index vector -- training time -- need for external data for unsupervised training - -The optional GPU implementation provides what is likely (as of March 2017) the fastest exact and approximate (compressed-domain) nearest neighbor search implementation for high-dimensional vectors, fastest Lloyd's k-means, and fastest small k-selection algorithm known. [The implementation is detailed here](https://arxiv.org/abs/1702.08734). - -## Full documentation of Faiss - -The following are entry points for documentation: - -- the full documentation, including a [tutorial](https://github.com/facebookresearch/faiss/wiki/Getting-started), a [FAQ](https://github.com/facebookresearch/faiss/wiki/FAQ) and a [troubleshooting section](https://github.com/facebookresearch/faiss/wiki/Troubleshooting) can be found on the [wiki page](http://github.com/facebookresearch/faiss/wiki) -- the [doxygen documentation](http://rawgithub.com/facebookresearch/faiss/master/docs/html/annotated.html) gives per-class information -- to reproduce results from our research papers, [Polysemous codes](https://arxiv.org/abs/1609.01882) and [Billion-scale similarity search with GPUs](https://arxiv.org/abs/1702.08734), refer to the [benchmarks README](benchs/README.md). For [ -Link and code: Fast indexing with graphs and compact regression codes](https://arxiv.org/abs/1804.09996), see the [link_and_code README](benchs/link_and_code) - -## Authors - -The main authors of Faiss are: -- [Hervé Jégou](https://github.com/jegou) initiated the Faiss project and wrote its first implementation -- [Matthijs Douze](https://github.com/mdouze) implemented most of the CPU Faiss -- [Jeff Johnson](https://github.com/wickedfoo) implemented all of the GPU Faiss -- [Lucas Hosseini](https://github.com/beauby) implemented the binary indexes - -## Reference - -Reference to cite when you use Faiss in a research paper: - -``` -@article{JDH17, - title={Billion-scale similarity search with GPUs}, - author={Johnson, Jeff and Douze, Matthijs and J{\'e}gou, Herv{\'e}}, - journal={arXiv preprint arXiv:1702.08734}, - year={2017} -} -``` - -## Join the Faiss community - -For public discussion of Faiss or for questions, there is a Facebook public discussion group at https://www.facebook.com/groups/faissusers/ - -We monitor the [issues page](http://github.com/facebookresearch/faiss/issues) of the repository. You can report bugs, ask questions, etc. - -## License - -Faiss is MIT-licensed. diff --git a/core/src/index/thirdparty/faiss/VectorTransform.cpp b/core/src/index/thirdparty/faiss/VectorTransform.cpp deleted file mode 100644 index 1a6d920171..0000000000 --- a/core/src/index/thirdparty/faiss/VectorTransform.cpp +++ /dev/null @@ -1,1158 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -using namespace faiss; - - -extern "C" { - -// this is to keep the clang syntax checker happy -#ifndef FINTEGER -#define FINTEGER int -#endif - - -/* declare BLAS functions, see http://www.netlib.org/clapack/cblas/ */ - -int sgemm_ ( - const char *transa, const char *transb, FINTEGER *m, FINTEGER * - n, FINTEGER *k, const float *alpha, const float *a, - FINTEGER *lda, const float *b, - FINTEGER *ldb, float *beta, - float *c, FINTEGER *ldc); - -int dgemm_ ( - const char *transa, const char *transb, FINTEGER *m, FINTEGER * - n, FINTEGER *k, const double *alpha, const double *a, - FINTEGER *lda, const double *b, - FINTEGER *ldb, double *beta, - double *c, FINTEGER *ldc); - -int ssyrk_ ( - const char *uplo, const char *trans, FINTEGER *n, FINTEGER *k, - float *alpha, float *a, FINTEGER *lda, - float *beta, float *c, FINTEGER *ldc); - -/* Lapack functions from http://www.netlib.org/clapack/old/single/ */ - -int ssyev_ ( - const char *jobz, const char *uplo, FINTEGER *n, float *a, - FINTEGER *lda, float *w, float *work, FINTEGER *lwork, - FINTEGER *info); - -int dsyev_ ( - const char *jobz, const char *uplo, FINTEGER *n, double *a, - FINTEGER *lda, double *w, double *work, FINTEGER *lwork, - FINTEGER *info); - -int sgesvd_( - const char *jobu, const char *jobvt, FINTEGER *m, FINTEGER *n, - float *a, FINTEGER *lda, float *s, float *u, FINTEGER *ldu, float *vt, - FINTEGER *ldvt, float *work, FINTEGER *lwork, FINTEGER *info); - - -int dgesvd_( - const char *jobu, const char *jobvt, FINTEGER *m, FINTEGER *n, - double *a, FINTEGER *lda, double *s, double *u, FINTEGER *ldu, double *vt, - FINTEGER *ldvt, double *work, FINTEGER *lwork, FINTEGER *info); - -} - -/********************************************* - * VectorTransform - *********************************************/ - - - -float * VectorTransform::apply (Index::idx_t n, const float * x) const -{ - float * xt = new float[n * d_out]; - apply_noalloc (n, x, xt); - return xt; -} - - -void VectorTransform::train (idx_t, const float *) { - // does nothing by default -} - - -void VectorTransform::reverse_transform ( - idx_t , const float *, - float *) const -{ - FAISS_THROW_MSG ("reverse transform not implemented"); -} - - - - -/********************************************* - * LinearTransform - *********************************************/ - -/// both d_in > d_out and d_out < d_in are supported -LinearTransform::LinearTransform (int d_in, int d_out, - bool have_bias): - VectorTransform (d_in, d_out), have_bias (have_bias), - is_orthonormal (false), verbose (false) -{ - is_trained = false; // will be trained when A and b are initialized -} - -void LinearTransform::apply_noalloc (Index::idx_t n, const float * x, - float * xt) const -{ - FAISS_THROW_IF_NOT_MSG(is_trained, "Transformation not trained yet"); - - float c_factor; - if (have_bias) { - FAISS_THROW_IF_NOT_MSG (b.size() == d_out, "Bias not initialized"); - float * xi = xt; - for (int i = 0; i < n; i++) - for(int j = 0; j < d_out; j++) - *xi++ = b[j]; - c_factor = 1.0; - } else { - c_factor = 0.0; - } - - FAISS_THROW_IF_NOT_MSG (A.size() == d_out * d_in, - "Transformation matrix not initialized"); - - float one = 1; - FINTEGER nbiti = d_out, ni = n, di = d_in; - sgemm_ ("Transposed", "Not transposed", - &nbiti, &ni, &di, - &one, A.data(), &di, x, &di, &c_factor, xt, &nbiti); - -} - - -void LinearTransform::transform_transpose (idx_t n, const float * y, - float *x) const -{ - if (have_bias) { // allocate buffer to store bias-corrected data - float *y_new = new float [n * d_out]; - const float *yr = y; - float *yw = y_new; - for (idx_t i = 0; i < n; i++) { - for (int j = 0; j < d_out; j++) { - *yw++ = *yr++ - b [j]; - } - } - y = y_new; - } - - { - FINTEGER dii = d_in, doi = d_out, ni = n; - float one = 1.0, zero = 0.0; - sgemm_ ("Not", "Not", &dii, &ni, &doi, - &one, A.data (), &dii, y, &doi, &zero, x, &dii); - } - - if (have_bias) delete [] y; -} - -void LinearTransform::set_is_orthonormal () -{ - if (d_out > d_in) { - // not clear what we should do in this case - is_orthonormal = false; - return; - } - if (d_out == 0) { // borderline case, unnormalized matrix - is_orthonormal = true; - return; - } - - double eps = 4e-5; - FAISS_ASSERT(A.size() >= d_out * d_in); - { - std::vector ATA(d_out * d_out); - FINTEGER dii = d_in, doi = d_out; - float one = 1.0, zero = 0.0; - - sgemm_ ("Transposed", "Not", &doi, &doi, &dii, - &one, A.data (), &dii, - A.data(), &dii, - &zero, ATA.data(), &doi); - - is_orthonormal = true; - for (long i = 0; i < d_out; i++) { - for (long j = 0; j < d_out; j++) { - float v = ATA[i + j * d_out]; - if (i == j) v-= 1; - if (fabs(v) > eps) { - is_orthonormal = false; - } - } - } - } - -} - - -void LinearTransform::reverse_transform (idx_t n, const float * xt, - float *x) const -{ - if (is_orthonormal) { - transform_transpose (n, xt, x); - } else { - FAISS_THROW_MSG ("reverse transform not implemented for non-orthonormal matrices"); - } -} - - -void LinearTransform::print_if_verbose ( - const char*name, const std::vector &mat, - int n, int d) const -{ - if (!verbose) return; - printf("matrix %s: %d*%d [\n", name, n, d); - FAISS_THROW_IF_NOT (mat.size() >= n * d); - for (int i = 0; i < n; i++) { - for (int j = 0; j < d; j++) { - printf("%10.5g ", mat[i * d + j]); - } - printf("\n"); - } - printf("]\n"); -} - -/********************************************* - * RandomRotationMatrix - *********************************************/ - -void RandomRotationMatrix::init (int seed) -{ - - if(d_out <= d_in) { - A.resize (d_out * d_in); - float *q = A.data(); - float_randn(q, d_out * d_in, seed); - matrix_qr(d_in, d_out, q); - } else { - // use tight-frame transformation - A.resize (d_out * d_out); - float *q = A.data(); - float_randn(q, d_out * d_out, seed); - matrix_qr(d_out, d_out, q); - // remove columns - int i, j; - for (i = 0; i < d_out; i++) { - for(j = 0; j < d_in; j++) { - q[i * d_in + j] = q[i * d_out + j]; - } - } - A.resize(d_in * d_out); - } - is_orthonormal = true; - is_trained = true; -} - -void RandomRotationMatrix::train (Index::idx_t /*n*/, const float */*x*/) -{ - // initialize with some arbitrary seed - init (12345); -} - - -/********************************************* - * PCAMatrix - *********************************************/ - -PCAMatrix::PCAMatrix (int d_in, int d_out, - float eigen_power, bool random_rotation): - LinearTransform(d_in, d_out, true), - eigen_power(eigen_power), random_rotation(random_rotation) -{ - is_trained = false; - max_points_per_d = 1000; - balanced_bins = 0; -} - - -namespace { - -/// Compute the eigenvalue decomposition of symmetric matrix cov, -/// dimensions d_in-by-d_in. Output eigenvectors in cov. - -void eig(size_t d_in, double *cov, double *eigenvalues, int verbose) -{ - { // compute eigenvalues and vectors - FINTEGER info = 0, lwork = -1, di = d_in; - double workq; - - dsyev_ ("Vectors as well", "Upper", - &di, cov, &di, eigenvalues, &workq, &lwork, &info); - lwork = FINTEGER(workq); - double *work = new double[lwork]; - - dsyev_ ("Vectors as well", "Upper", - &di, cov, &di, eigenvalues, work, &lwork, &info); - - delete [] work; - - if (info != 0) { - fprintf (stderr, "WARN ssyev info returns %d, " - "a very bad PCA matrix is learnt\n", - int(info)); - // do not throw exception, as the matrix could still be useful - } - - - if(verbose && d_in <= 10) { - printf("info=%ld new eigvals=[", long(info)); - for(int j = 0; j < d_in; j++) printf("%g ", eigenvalues[j]); - printf("]\n"); - - double *ci = cov; - printf("eigenvecs=\n"); - for(int i = 0; i < d_in; i++) { - for(int j = 0; j < d_in; j++) - printf("%10.4g ", *ci++); - printf("\n"); - } - } - - } - - // revert order of eigenvectors & values - - for(int i = 0; i < d_in / 2; i++) { - - std::swap(eigenvalues[i], eigenvalues[d_in - 1 - i]); - double *v1 = cov + i * d_in; - double *v2 = cov + (d_in - 1 - i) * d_in; - for(int j = 0; j < d_in; j++) - std::swap(v1[j], v2[j]); - } - -} - - -} - -void PCAMatrix::train (Index::idx_t n, const float *x) -{ - const float * x_in = x; - - x = fvecs_maybe_subsample (d_in, (size_t*)&n, - max_points_per_d * d_in, x, verbose); - - ScopeDeleter del_x (x != x_in ? x : nullptr); - - // compute mean - mean.clear(); mean.resize(d_in, 0.0); - if (have_bias) { // we may want to skip the bias - const float *xi = x; - for (int i = 0; i < n; i++) { - for(int j = 0; j < d_in; j++) - mean[j] += *xi++; - } - for(int j = 0; j < d_in; j++) - mean[j] /= n; - } - if(verbose) { - printf("mean=["); - for(int j = 0; j < d_in; j++) printf("%g ", mean[j]); - printf("]\n"); - } - - if(n >= d_in) { - // compute covariance matrix, store it in PCA matrix - PCAMat.resize(d_in * d_in); - float * cov = PCAMat.data(); - { // initialize with mean * mean^T term - float *ci = cov; - for(int i = 0; i < d_in; i++) { - for(int j = 0; j < d_in; j++) - *ci++ = - n * mean[i] * mean[j]; - } - } - { - FINTEGER di = d_in, ni = n; - float one = 1.0; - ssyrk_ ("Up", "Non transposed", - &di, &ni, &one, (float*)x, &di, &one, cov, &di); - - } - if(verbose && d_in <= 10) { - float *ci = cov; - printf("cov=\n"); - for(int i = 0; i < d_in; i++) { - for(int j = 0; j < d_in; j++) - printf("%10g ", *ci++); - printf("\n"); - } - } - - std::vector covd (d_in * d_in); - for (size_t i = 0; i < d_in * d_in; i++) covd [i] = cov [i]; - - std::vector eigenvaluesd (d_in); - - eig (d_in, covd.data (), eigenvaluesd.data (), verbose); - - for (size_t i = 0; i < d_in * d_in; i++) PCAMat [i] = covd [i]; - eigenvalues.resize (d_in); - - for (size_t i = 0; i < d_in; i++) - eigenvalues [i] = eigenvaluesd [i]; - - - } else { - - std::vector xc (n * d_in); - - for (size_t i = 0; i < n; i++) - for(size_t j = 0; j < d_in; j++) - xc [i * d_in + j] = x [i * d_in + j] - mean[j]; - - // compute Gram matrix - std::vector gram (n * n); - { - FINTEGER di = d_in, ni = n; - float one = 1.0, zero = 0.0; - ssyrk_ ("Up", "Transposed", - &ni, &di, &one, xc.data(), &di, &zero, gram.data(), &ni); - } - - if(verbose && d_in <= 10) { - float *ci = gram.data(); - printf("gram=\n"); - for(int i = 0; i < n; i++) { - for(int j = 0; j < n; j++) - printf("%10g ", *ci++); - printf("\n"); - } - } - - std::vector gramd (n * n); - for (size_t i = 0; i < n * n; i++) - gramd [i] = gram [i]; - - std::vector eigenvaluesd (n); - - // eig will fill in only the n first eigenvals - - eig (n, gramd.data (), eigenvaluesd.data (), verbose); - - PCAMat.resize(d_in * n); - - for (size_t i = 0; i < n * n; i++) - gram [i] = gramd [i]; - - eigenvalues.resize (d_in); - // fill in only the n first ones - for (size_t i = 0; i < n; i++) - eigenvalues [i] = eigenvaluesd [i]; - - { // compute PCAMat = x' * v - FINTEGER di = d_in, ni = n; - float one = 1.0; - - sgemm_ ("Non", "Non Trans", - &di, &ni, &ni, - &one, xc.data(), &di, gram.data(), &ni, - &one, PCAMat.data(), &di); - } - - if(verbose && d_in <= 10) { - float *ci = PCAMat.data(); - printf("PCAMat=\n"); - for(int i = 0; i < n; i++) { - for(int j = 0; j < d_in; j++) - printf("%10g ", *ci++); - printf("\n"); - } - } - fvec_renorm_L2 (d_in, n, PCAMat.data()); - - } - - prepare_Ab(); - is_trained = true; -} - -void PCAMatrix::copy_from (const PCAMatrix & other) -{ - FAISS_THROW_IF_NOT (other.is_trained); - mean = other.mean; - eigenvalues = other.eigenvalues; - PCAMat = other.PCAMat; - prepare_Ab (); - is_trained = true; -} - -void PCAMatrix::prepare_Ab () -{ - FAISS_THROW_IF_NOT_FMT ( - d_out * d_in <= PCAMat.size(), - "PCA matrix cannot output %d dimensions from %d ", - d_out, d_in); - - if (!random_rotation) { - A = PCAMat; - A.resize(d_out * d_in); // strip off useless dimensions - - // first scale the components - if (eigen_power != 0) { - float *ai = A.data(); - for (int i = 0; i < d_out; i++) { - float factor = pow(eigenvalues[i], eigen_power); - for(int j = 0; j < d_in; j++) - *ai++ *= factor; - } - } - - if (balanced_bins != 0) { - FAISS_THROW_IF_NOT (d_out % balanced_bins == 0); - int dsub = d_out / balanced_bins; - std::vector Ain; - std::swap(A, Ain); - A.resize(d_out * d_in); - - std::vector accu(balanced_bins); - std::vector counter(balanced_bins); - - // greedy assignment - for (int i = 0; i < d_out; i++) { - // find best bin - int best_j = -1; - float min_w = 1e30; - for (int j = 0; j < balanced_bins; j++) { - if (counter[j] < dsub && accu[j] < min_w) { - min_w = accu[j]; - best_j = j; - } - } - int row_dst = best_j * dsub + counter[best_j]; - accu[best_j] += eigenvalues[i]; - counter[best_j] ++; - memcpy (&A[row_dst * d_in], &Ain[i * d_in], - d_in * sizeof (A[0])); - } - - if (verbose) { - printf(" bin accu=["); - for (int i = 0; i < balanced_bins; i++) - printf("%g ", accu[i]); - printf("]\n"); - } - } - - - } else { - FAISS_THROW_IF_NOT_MSG (balanced_bins == 0, - "both balancing bins and applying a random rotation " - "does not make sense"); - RandomRotationMatrix rr(d_out, d_out); - - rr.init(5); - - // apply scaling on the rotation matrix (right multiplication) - if (eigen_power != 0) { - for (int i = 0; i < d_out; i++) { - float factor = pow(eigenvalues[i], eigen_power); - for(int j = 0; j < d_out; j++) - rr.A[j * d_out + i] *= factor; - } - } - - A.resize(d_in * d_out); - { - FINTEGER dii = d_in, doo = d_out; - float one = 1.0, zero = 0.0; - - sgemm_ ("Not", "Not", &dii, &doo, &doo, - &one, PCAMat.data(), &dii, rr.A.data(), &doo, &zero, - A.data(), &dii); - - } - - } - - b.clear(); b.resize(d_out); - - for (int i = 0; i < d_out; i++) { - float accu = 0; - for (int j = 0; j < d_in; j++) - accu -= mean[j] * A[j + i * d_in]; - b[i] = accu; - } - - is_orthonormal = eigen_power == 0; - -} - -/********************************************* - * ITQMatrix - *********************************************/ - -ITQMatrix::ITQMatrix (int d): - LinearTransform(d, d, false), - max_iter (50), - seed (123) -{ -} - - -/** translated from fbcode/deeplearning/catalyzer/catalyzer/quantizers.py */ -void ITQMatrix::train (Index::idx_t n, const float* xf) -{ - size_t d = d_in; - std::vector rotation (d * d); - - if (init_rotation.size() == d * d) { - memcpy (rotation.data(), init_rotation.data(), - d * d * sizeof(rotation[0])); - } else { - RandomRotationMatrix rrot (d, d); - rrot.init (seed); - for (size_t i = 0; i < d * d; i++) { - rotation[i] = rrot.A[i]; - } - } - - std::vector x (n * d); - - for (size_t i = 0; i < n * d; i++) { - x[i] = xf[i]; - } - - std::vector rotated_x (n * d), cov_mat (d * d); - std::vector u (d * d), vt (d * d), singvals (d); - - for (int i = 0; i < max_iter; i++) { - print_if_verbose ("rotation", rotation, d, d); - { // rotated_data = np.dot(training_data, rotation) - FINTEGER di = d, ni = n; - double one = 1, zero = 0; - dgemm_ ("N", "N", &di, &ni, &di, - &one, rotation.data(), &di, x.data(), &di, - &zero, rotated_x.data(), &di); - } - print_if_verbose ("rotated_x", rotated_x, n, d); - // binarize - for (size_t j = 0; j < n * d; j++) { - rotated_x[j] = rotated_x[j] < 0 ? -1 : 1; - } - // covariance matrix - { // rotated_data = np.dot(training_data, rotation) - FINTEGER di = d, ni = n; - double one = 1, zero = 0; - dgemm_ ("N", "T", &di, &di, &ni, - &one, rotated_x.data(), &di, x.data(), &di, - &zero, cov_mat.data(), &di); - } - print_if_verbose ("cov_mat", cov_mat, d, d); - // SVD - { - - FINTEGER di = d; - FINTEGER lwork = -1, info; - double lwork1; - - // workspace query - dgesvd_ ("A", "A", &di, &di, cov_mat.data(), &di, - singvals.data(), u.data(), &di, - vt.data(), &di, - &lwork1, &lwork, &info); - - FAISS_THROW_IF_NOT (info == 0); - lwork = size_t (lwork1); - std::vector work (lwork); - dgesvd_ ("A", "A", &di, &di, cov_mat.data(), &di, - singvals.data(), u.data(), &di, - vt.data(), &di, - work.data(), &lwork, &info); - FAISS_THROW_IF_NOT_FMT (info == 0, "sgesvd returned info=%d", info); - - } - print_if_verbose ("u", u, d, d); - print_if_verbose ("vt", vt, d, d); - // update rotation - { - FINTEGER di = d; - double one = 1, zero = 0; - dgemm_ ("N", "T", &di, &di, &di, - &one, u.data(), &di, vt.data(), &di, - &zero, rotation.data(), &di); - } - print_if_verbose ("final rot", rotation, d, d); - - } - A.resize (d * d); - for (size_t i = 0; i < d; i++) { - for (size_t j = 0; j < d; j++) { - A[i + d * j] = rotation[j + d * i]; - } - } - is_trained = true; - -} - -ITQTransform::ITQTransform (int d_in, int d_out, bool do_pca): - VectorTransform (d_in, d_out), - do_pca (do_pca), - itq (d_out), - pca_then_itq (d_in, d_out, false) -{ - if (!do_pca) { - FAISS_THROW_IF_NOT (d_in == d_out); - } - max_train_per_dim = 10; - is_trained = false; -} - - - - -void ITQTransform::train (idx_t n, const float *x) -{ - FAISS_THROW_IF_NOT (!is_trained); - - const float * x_in = x; - size_t max_train_points = std::max(d_in * max_train_per_dim, 32768); - x = fvecs_maybe_subsample (d_in, (size_t*)&n, max_train_points, x); - - ScopeDeleter del_x (x != x_in ? x : nullptr); - - std::unique_ptr x_norm(new float[n * d_in]); - { // normalize - int d = d_in; - - mean.resize (d, 0); - for (idx_t i = 0; i < n; i++) { - for (idx_t j = 0; j < d; j++) { - mean[j] += x[i * d + j]; - } - } - for (idx_t j = 0; j < d; j++) { - mean[j] /= n; - } - for (idx_t i = 0; i < n; i++) { - for (idx_t j = 0; j < d; j++) { - x_norm[i * d + j] = x[i * d + j] - mean[j]; - } - } - fvec_renorm_L2 (d_in, n, x_norm.get()); - } - - // train PCA - - PCAMatrix pca (d_in, d_out); - float *x_pca; - std::unique_ptr x_pca_del; - if (do_pca) { - pca.have_bias = false; // for consistency with reference implem - pca.train (n, x_norm.get()); - x_pca = pca.apply (n, x_norm.get()); - x_pca_del.reset(x_pca); - } else { - x_pca = x_norm.get(); - } - - // train ITQ - itq.train (n, x_pca); - - // merge PCA and ITQ - if (do_pca) { - FINTEGER di = d_out, dini = d_in; - float one = 1, zero = 0; - pca_then_itq.A.resize(d_in * d_out); - sgemm_ ("N", "N", &dini, &di, &di, - &one, pca.A.data(), &dini, - itq.A.data(), &di, - &zero, pca_then_itq.A.data(), &dini); - } else { - pca_then_itq.A = itq.A; - } - pca_then_itq.is_trained = true; - is_trained = true; -} - -void ITQTransform::apply_noalloc (Index::idx_t n, const float * x, - float * xt) const -{ - FAISS_THROW_IF_NOT_MSG(is_trained, "Transformation not trained yet"); - - std::unique_ptr x_norm(new float[n * d_in]); - { // normalize - int d = d_in; - for (idx_t i = 0; i < n; i++) { - for (idx_t j = 0; j < d; j++) { - x_norm[i * d + j] = x[i * d + j] - mean[j]; - } - } - // this is not really useful if we are going to binarize right - // afterwards but OK - fvec_renorm_L2 (d_in, n, x_norm.get()); - } - - pca_then_itq.apply_noalloc (n, x_norm.get(), xt); -} - -/********************************************* - * OPQMatrix - *********************************************/ - - -OPQMatrix::OPQMatrix (int d, int M, int d2): - LinearTransform (d, d2 == -1 ? d : d2, false), M(M), - niter (50), - niter_pq (4), niter_pq_0 (40), - verbose(false), - pq(nullptr) -{ - is_trained = false; - // OPQ is quite expensive to train, so set this right. - max_train_points = 256 * 256; - pq = nullptr; -} - - - -void OPQMatrix::train (Index::idx_t n, const float *x) -{ - - const float * x_in = x; - - x = fvecs_maybe_subsample (d_in, (size_t*)&n, - max_train_points, x, verbose); - - ScopeDeleter del_x (x != x_in ? x : nullptr); - - // To support d_out > d_in, we pad input vectors with 0s to d_out - size_t d = d_out <= d_in ? d_in : d_out; - size_t d2 = d_out; - -#if 0 - // what this test shows: the only way of getting bit-exact - // reproducible results with sgeqrf and sgesvd seems to be forcing - // single-threading. - { // test repro - std::vector r (d * d); - float * rotation = r.data(); - float_randn (rotation, d * d, 1234); - printf("CS0: %016lx\n", - ivec_checksum (128*128, (int*)rotation)); - matrix_qr (d, d, rotation); - printf("CS1: %016lx\n", - ivec_checksum (128*128, (int*)rotation)); - return; - } -#endif - - if (verbose) { - printf ("OPQMatrix::train: training an OPQ rotation matrix " - "for M=%d from %ld vectors in %dD -> %dD\n", - M, n, d_in, d_out); - } - - std::vector xtrain (n * d); - // center x - { - std::vector sum (d); - const float *xi = x; - for (size_t i = 0; i < n; i++) { - for (int j = 0; j < d_in; j++) - sum [j] += *xi++; - } - for (int i = 0; i < d; i++) sum[i] /= n; - float *yi = xtrain.data(); - xi = x; - for (size_t i = 0; i < n; i++) { - for (int j = 0; j < d_in; j++) - *yi++ = *xi++ - sum[j]; - yi += d - d_in; - } - } - float *rotation; - - if (A.size () == 0) { - A.resize (d * d); - rotation = A.data(); - if (verbose) - printf(" OPQMatrix::train: making random %ld*%ld rotation\n", - d, d); - float_randn (rotation, d * d, 1234); - matrix_qr (d, d, rotation); - // we use only the d * d2 upper part of the matrix - A.resize (d * d2); - } else { - FAISS_THROW_IF_NOT (A.size() == d * d2); - rotation = A.data(); - } - - std::vector - xproj (d2 * n), pq_recons (d2 * n), xxr (d * n), - tmp(d * d * 4); - - - ProductQuantizer pq_default (d2, M, 8); - ProductQuantizer &pq_regular = pq ? *pq : pq_default; - std::vector codes (pq_regular.code_size * n); - - double t0 = getmillisecs(); - for (int iter = 0; iter < niter; iter++) { - - { // torch.mm(xtrain, rotation:t()) - FINTEGER di = d, d2i = d2, ni = n; - float zero = 0, one = 1; - sgemm_ ("Transposed", "Not transposed", - &d2i, &ni, &di, - &one, rotation, &di, - xtrain.data(), &di, - &zero, xproj.data(), &d2i); - } - - pq_regular.cp.max_points_per_centroid = 1000; - pq_regular.cp.niter = iter == 0 ? niter_pq_0 : niter_pq; - pq_regular.verbose = verbose; - pq_regular.train (n, xproj.data()); - - if (verbose) { - printf(" encode / decode\n"); - } - if (pq_regular.assign_index) { - pq_regular.compute_codes_with_assign_index - (xproj.data(), codes.data(), n); - } else { - pq_regular.compute_codes (xproj.data(), codes.data(), n); - } - pq_regular.decode (codes.data(), pq_recons.data(), n); - - float pq_err = fvec_L2sqr (pq_recons.data(), xproj.data(), n * d2) / n; - - if (verbose) - printf (" Iteration %d (%d PQ iterations):" - "%.3f s, obj=%g\n", iter, pq_regular.cp.niter, - (getmillisecs () - t0) / 1000.0, pq_err); - - { - float *u = tmp.data(), *vt = &tmp [d * d]; - float *sing_val = &tmp [2 * d * d]; - FINTEGER di = d, d2i = d2, ni = n; - float one = 1, zero = 0; - - if (verbose) { - printf(" X * recons\n"); - } - // torch.mm(xtrain:t(), pq_recons) - sgemm_ ("Not", "Transposed", - &d2i, &di, &ni, - &one, pq_recons.data(), &d2i, - xtrain.data(), &di, - &zero, xxr.data(), &d2i); - - - FINTEGER lwork = -1, info = -1; - float worksz; - // workspace query - sgesvd_ ("All", "All", - &d2i, &di, xxr.data(), &d2i, - sing_val, - vt, &d2i, u, &di, - &worksz, &lwork, &info); - - lwork = int(worksz); - std::vector work (lwork); - // u and vt swapped - sgesvd_ ("All", "All", - &d2i, &di, xxr.data(), &d2i, - sing_val, - vt, &d2i, u, &di, - work.data(), &lwork, &info); - - sgemm_ ("Transposed", "Transposed", - &di, &d2i, &d2i, - &one, u, &di, vt, &d2i, - &zero, rotation, &di); - - } - pq_regular.train_type = ProductQuantizer::Train_hot_start; - } - - // revert A matrix - if (d > d_in) { - for (long i = 0; i < d_out; i++) - memmove (&A[i * d_in], &A[i * d], sizeof(A[0]) * d_in); - A.resize (d_in * d_out); - } - - is_trained = true; - is_orthonormal = true; -} - - -/********************************************* - * NormalizationTransform - *********************************************/ - -NormalizationTransform::NormalizationTransform (int d, float norm): - VectorTransform (d, d), norm (norm) -{ -} - -NormalizationTransform::NormalizationTransform (): - VectorTransform (-1, -1), norm (-1) -{ -} - -void NormalizationTransform::apply_noalloc - (idx_t n, const float* x, float* xt) const -{ - if (norm == 2.0) { - memcpy (xt, x, sizeof (x[0]) * n * d_in); - fvec_renorm_L2 (d_in, n, xt); - } else { - FAISS_THROW_MSG ("not implemented"); - } -} - -void NormalizationTransform::reverse_transform (idx_t n, const float* xt, - float* x) const -{ - memcpy (x, xt, sizeof (xt[0]) * n * d_in); -} - -/********************************************* - * CenteringTransform - *********************************************/ - -CenteringTransform::CenteringTransform (int d): - VectorTransform (d, d) -{ - is_trained = false; -} - -void CenteringTransform::train(Index::idx_t n, const float *x) { - FAISS_THROW_IF_NOT_MSG(n > 0, "need at least one training vector"); - mean.resize (d_in, 0); - for (idx_t i = 0; i < n; i++) { - for (size_t j = 0; j < d_in; j++) { - mean[j] += *x++; - } - } - - for (size_t j = 0; j < d_in; j++) { - mean[j] /= n; - } - is_trained = true; -} - - -void CenteringTransform::apply_noalloc - (idx_t n, const float* x, float* xt) const -{ - FAISS_THROW_IF_NOT (is_trained); - - for (idx_t i = 0; i < n; i++) { - for (size_t j = 0; j < d_in; j++) { - *xt++ = *x++ - mean[j]; - } - } -} - -void CenteringTransform::reverse_transform (idx_t n, const float* xt, - float* x) const -{ - FAISS_THROW_IF_NOT (is_trained); - - for (idx_t i = 0; i < n; i++) { - for (size_t j = 0; j < d_in; j++) { - *x++ = *xt++ + mean[j]; - } - } - -} - - - - - -/********************************************* - * RemapDimensionsTransform - *********************************************/ - - -RemapDimensionsTransform::RemapDimensionsTransform ( - int d_in, int d_out, const int *map_in): - VectorTransform (d_in, d_out) -{ - map.resize (d_out); - for (int i = 0; i < d_out; i++) { - map[i] = map_in[i]; - FAISS_THROW_IF_NOT (map[i] == -1 || (map[i] >= 0 && map[i] < d_in)); - } -} - -RemapDimensionsTransform::RemapDimensionsTransform ( - int d_in, int d_out, bool uniform): VectorTransform (d_in, d_out) -{ - map.resize (d_out, -1); - - if (uniform) { - if (d_in < d_out) { - for (int i = 0; i < d_in; i++) { - map [i * d_out / d_in] = i; - } - } else { - for (int i = 0; i < d_out; i++) { - map [i] = i * d_in / d_out; - } - } - } else { - for (int i = 0; i < d_in && i < d_out; i++) - map [i] = i; - } -} - - -void RemapDimensionsTransform::apply_noalloc (idx_t n, const float * x, - float *xt) const -{ - for (idx_t i = 0; i < n; i++) { - for (int j = 0; j < d_out; j++) { - xt[j] = map[j] < 0 ? 0 : x[map[j]]; - } - x += d_in; - xt += d_out; - } -} - -void RemapDimensionsTransform::reverse_transform (idx_t n, const float * xt, - float *x) const -{ - memset (x, 0, sizeof (*x) * n * d_in); - for (idx_t i = 0; i < n; i++) { - for (int j = 0; j < d_out; j++) { - if (map[j] >= 0) x[map[j]] = xt[j]; - } - x += d_in; - xt += d_out; - } -} diff --git a/core/src/index/thirdparty/faiss/VectorTransform.h b/core/src/index/thirdparty/faiss/VectorTransform.h deleted file mode 100644 index 4b55245b07..0000000000 --- a/core/src/index/thirdparty/faiss/VectorTransform.h +++ /dev/null @@ -1,322 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_VECTOR_TRANSFORM_H -#define FAISS_VECTOR_TRANSFORM_H - -/** Defines a few objects that apply transformations to a set of - * vectors Often these are pre-processing steps. - */ - -#include -#include - -#include - - -namespace faiss { - - -/** Any transformation applied on a set of vectors */ -struct VectorTransform { - - typedef Index::idx_t idx_t; - - int d_in; ///! input dimension - int d_out; ///! output dimension - - explicit VectorTransform (int d_in = 0, int d_out = 0): - d_in(d_in), d_out(d_out), is_trained(true) - {} - - - /// set if the VectorTransform does not require training, or if - /// training is done already - bool is_trained; - - - /** Perform training on a representative set of vectors. Does - * nothing by default. - * - * @param n nb of training vectors - * @param x training vecors, size n * d - */ - virtual void train (idx_t n, const float *x); - - /** apply the random roation, return new allocated matrix - * @param x size n * d_in - * @return size n * d_out - */ - float *apply (idx_t n, const float * x) const; - - /// same as apply, but result is pre-allocated - virtual void apply_noalloc (idx_t n, const float * x, - float *xt) const = 0; - - /// reverse transformation. May not be implemented or may return - /// approximate result - virtual void reverse_transform (idx_t n, const float * xt, - float *x) const; - - virtual ~VectorTransform () {} - -}; - - - -/** Generic linear transformation, with bias term applied on output - * y = A * x + b - */ -struct LinearTransform: VectorTransform { - - bool have_bias; ///! whether to use the bias term - - /// check if matrix A is orthonormal (enables reverse_transform) - bool is_orthonormal; - - /// Transformation matrix, size d_out * d_in - std::vector A; - - /// bias vector, size d_out - std::vector b; - - /// both d_in > d_out and d_out < d_in are supported - explicit LinearTransform (int d_in = 0, int d_out = 0, - bool have_bias = false); - - /// same as apply, but result is pre-allocated - void apply_noalloc(idx_t n, const float* x, float* xt) const override; - - /// compute x = A^T * (x - b) - /// is reverse transform if A has orthonormal lines - void transform_transpose (idx_t n, const float * y, - float *x) const; - - /// works only if is_orthonormal - void reverse_transform (idx_t n, const float * xt, - float *x) const override; - - /// compute A^T * A to set the is_orthonormal flag - void set_is_orthonormal (); - - bool verbose; - void print_if_verbose (const char*name, const std::vector &mat, - int n, int d) const; - - ~LinearTransform() override {} -}; - - - -/// Randomly rotate a set of vectors -struct RandomRotationMatrix: LinearTransform { - - /// both d_in > d_out and d_out < d_in are supported - RandomRotationMatrix (int d_in, int d_out): - LinearTransform(d_in, d_out, false) {} - - /// must be called before the transform is used - void init(int seed); - - // intializes with an arbitrary seed - void train(idx_t n, const float* x) override; - - RandomRotationMatrix () {} -}; - - -/** Applies a principal component analysis on a set of vectors, - * with optionally whitening and random rotation. */ -struct PCAMatrix: LinearTransform { - - /** after transformation the components are multiplied by - * eigenvalues^eigen_power - * - * =0: no whitening - * =-0.5: full whitening - */ - float eigen_power; - - /// random rotation after PCA - bool random_rotation; - - /// ratio between # training vectors and dimension - size_t max_points_per_d; - - /// try to distribute output eigenvectors in this many bins - int balanced_bins; - - /// Mean, size d_in - std::vector mean; - - /// eigenvalues of covariance matrix (= squared singular values) - std::vector eigenvalues; - - /// PCA matrix, size d_in * d_in - std::vector PCAMat; - - // the final matrix is computed after random rotation and/or whitening - explicit PCAMatrix (int d_in = 0, int d_out = 0, - float eigen_power = 0, bool random_rotation = false); - - /// train on n vectors. If n < d_in then the eigenvector matrix - /// will be completed with 0s - void train(idx_t n, const float* x) override; - - /// copy pre-trained PCA matrix - void copy_from (const PCAMatrix & other); - - /// called after mean, PCAMat and eigenvalues are computed - void prepare_Ab(); - -}; - - -/** ITQ implementation from - * - * Iterative quantization: A procrustean approach to learning binary codes - * for large-scale image retrieval, - * - * Yunchao Gong, Svetlana Lazebnik, Albert Gordo, Florent Perronnin, - * PAMI'12. - */ - -struct ITQMatrix: LinearTransform { - - int max_iter; - int seed; - - // force initialization of the rotation (for debugging) - std::vector init_rotation; - - explicit ITQMatrix (int d = 0); - - void train (idx_t n, const float* x) override; -}; - - - -/** The full ITQ transform, including normalizations and PCA transformation - */ -struct ITQTransform: VectorTransform { - - std::vector mean; - bool do_pca; - ITQMatrix itq; - - /// max training points per dimension - int max_train_per_dim; - - // concatenation of PCA + ITQ transformation - LinearTransform pca_then_itq; - - explicit ITQTransform (int d_in = 0, int d_out = 0, bool do_pca = false); - - void train (idx_t n, const float *x) override; - - void apply_noalloc (idx_t n, const float* x, float* xt) const override; - -}; - - -struct ProductQuantizer; - -/** Applies a rotation to align the dimensions with a PQ to minimize - * the reconstruction error. Can be used before an IndexPQ or an - * IndexIVFPQ. The method is the non-parametric version described in: - * - * "Optimized Product Quantization for Approximate Nearest Neighbor Search" - * Tiezheng Ge, Kaiming He, Qifa Ke, Jian Sun, CVPR'13 - * - */ -struct OPQMatrix: LinearTransform { - - int M; ///< nb of subquantizers - int niter; ///< Number of outer training iterations - int niter_pq; ///< Number of training iterations for the PQ - int niter_pq_0; ///< same, for the first outer iteration - - /// if there are too many training points, resample - size_t max_train_points; - bool verbose; - - /// if non-NULL, use this product quantizer for training - /// should be constructed with (d_out, M, _) - ProductQuantizer * pq; - - /// if d2 != -1, output vectors of this dimension - explicit OPQMatrix (int d = 0, int M = 1, int d2 = -1); - - void train(idx_t n, const float* x) override; -}; - - -/** remap dimensions for intput vectors, possibly inserting 0s - * strictly speaking this is also a linear transform but we don't want - * to compute it with matrix multiplies */ -struct RemapDimensionsTransform: VectorTransform { - - /// map from output dimension to input, size d_out - /// -1 -> set output to 0 - std::vector map; - - RemapDimensionsTransform (int d_in, int d_out, const int *map); - - /// remap input to output, skipping or inserting dimensions as needed - /// if uniform: distribute dimensions uniformly - /// otherwise just take the d_out first ones. - RemapDimensionsTransform (int d_in, int d_out, bool uniform = true); - - void apply_noalloc(idx_t n, const float* x, float* xt) const override; - - /// reverse transform correct only when the mapping is a permutation - void reverse_transform(idx_t n, const float* xt, float* x) const override; - - RemapDimensionsTransform () {} -}; - - -/** per-vector normalization */ -struct NormalizationTransform: VectorTransform { - float norm; - - explicit NormalizationTransform (int d, float norm = 2.0); - NormalizationTransform (); - - void apply_noalloc(idx_t n, const float* x, float* xt) const override; - - /// Identity transform since norm is not revertible - void reverse_transform(idx_t n, const float* xt, float* x) const override; -}; - -/** Subtract the mean of each component from the vectors. */ -struct CenteringTransform: VectorTransform { - - /// Mean, size d_in = d_out - std::vector mean; - - explicit CenteringTransform (int d = 0); - - /// train on n vectors. - void train(idx_t n, const float* x) override; - - /// subtract the mean - void apply_noalloc(idx_t n, const float* x, float* xt) const override; - - /// add the mean - void reverse_transform (idx_t n, const float * xt, - float *x) const override; - -}; - - -} // namespace faiss - - -#endif diff --git a/core/src/index/thirdparty/faiss/acinclude/ax_blas.m4 b/core/src/index/thirdparty/faiss/acinclude/ax_blas.m4 deleted file mode 100644 index ada1b17fee..0000000000 --- a/core/src/index/thirdparty/faiss/acinclude/ax_blas.m4 +++ /dev/null @@ -1,234 +0,0 @@ -# =========================================================================== -# https://www.gnu.org/software/autoconf-archive/ax_blas.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_BLAS([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) -# -# DESCRIPTION -# -# This macro looks for a library that implements the BLAS linear-algebra -# interface (see http://www.netlib.org/blas/). On success, it sets the -# BLAS_LIBS output variable to hold the requisite library linkages. -# -# To link with BLAS, you should link with: -# -# $BLAS_LIBS $LIBS $FLIBS -# -# in that order. FLIBS is the output variable of the -# AC_F77_LIBRARY_LDFLAGS macro (called if necessary by AX_BLAS), and is -# sometimes necessary in order to link with F77 libraries. Users will also -# need to use AC_F77_DUMMY_MAIN (see the autoconf manual), for the same -# reason. -# -# Many libraries are searched for, from ATLAS to CXML to ESSL. The user -# may also use --with-blas= in order to use some specific BLAS -# library . In order to link successfully, however, be aware that you -# will probably need to use the same Fortran compiler (which can be set -# via the F77 env. var.) as was used to compile the BLAS library. -# -# ACTION-IF-FOUND is a list of shell commands to run if a BLAS library is -# found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it is -# not found. If ACTION-IF-FOUND is not specified, the default action will -# define HAVE_BLAS. -# -# LICENSE -# -# Copyright (c) 2008 Steven G. Johnson -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -#serial 15 - -AU_ALIAS([ACX_BLAS], [AX_BLAS]) -AC_DEFUN([AX_BLAS], [ -AC_PREREQ(2.50) -# AC_REQUIRE([AC_F77_LIBRARY_LDFLAGS]) -AC_REQUIRE([AC_CANONICAL_HOST]) -ax_blas_ok=no - -AC_ARG_WITH(blas, - [AS_HELP_STRING([--with-blas=], [use BLAS library ])]) -case $with_blas in - yes | "") ;; - no) ax_blas_ok=disable ;; - -* | */* | *.a | *.so | *.so.* | *.o) BLAS_LIBS="$with_blas" ;; - *) BLAS_LIBS="-l$with_blas" ;; -esac - -OPENMP_LDFLAGS="$OPENMP_CXXFLAGS" - -# Get fortran linker names of BLAS functions to check for. -# AC_F77_FUNC(sgemm) -# AC_F77_FUNC(dgemm) -sgemm=sgemm_ -dgemm=dgemm_ - -ax_blas_save_LIBS="$LIBS" -LIBS="$LIBS $FLIBS" - -# First, check BLAS_LIBS environment variable -if test $ax_blas_ok = no; then -if test "x$BLAS_LIBS" != x; then - save_LIBS="$LIBS"; LIBS="$BLAS_LIBS $LIBS" - AC_MSG_CHECKING([for $sgemm in $BLAS_LIBS]) - AC_TRY_LINK_FUNC($sgemm, [ax_blas_ok=yes], [BLAS_LIBS=""]) - AC_MSG_RESULT($ax_blas_ok) - LIBS="$save_LIBS" -fi -fi - -# BLAS linked to by default? (happens on some supercomputers) -if test $ax_blas_ok = no; then - save_LIBS="$LIBS"; LIBS="$LIBS" - AC_MSG_CHECKING([if $sgemm is being linked in already]) - AC_TRY_LINK_FUNC($sgemm, [ax_blas_ok=yes]) - AC_MSG_RESULT($ax_blas_ok) - LIBS="$save_LIBS" -fi - -# BLAS in Intel MKL library? -if test $ax_blas_ok = no; then - case $host_os in - darwin*) - AC_CHECK_LIB(mkl_intel_lp64, $sgemm, - [ax_blas_ok=yes;BLAS_LIBS="-lmkl_intel_lp64 -lmkl_intel_thread -lmkl_core -liomp5 -lpthread"; OPENMP_LDFLAGS=""],, - [-lmkl_intel_lp64 -lmkl_intel_thread -lmkl_core -liomp5 -lpthread]) - ;; - *) - if test $host_cpu = x86_64; then - AC_CHECK_LIB(mkl_intel_lp64, $sgemm, - [ax_blas_ok=yes;BLAS_LIBS="-lmkl_intel_lp64 -lmkl_gnu_thread -lmkl_core -lgomp -lpthread -lm -ldl"],, - [-lmkl_intel_lp64 -lmkl_gnu_thread -lmkl_core -lgomp -lpthread -lm -ldl]) - elif test $host_cpu = i686; then - AC_CHECK_LIB(mkl_intel, $sgemm, - [ax_blas_ok=yes;BLAS_LIBS="-lmkl_intel -lmkl_gnu_thread -lmkl_core -lgomp -lpthread -lm -ldl"],, - [-lmkl_intel -lmkl_gnu_thread -lmkl_core -lgomp -lpthread -lm -ldl]) - fi - ;; - esac -fi -# Old versions of MKL -if test $ax_blas_ok = no; then - AC_CHECK_LIB(mkl, $sgemm, [ax_blas_ok=yes;BLAS_LIBS="-lmkl -lguide -lpthread"],,[-lguide -lpthread]) -fi - -# BLAS in OpenBLAS library? (http://xianyi.github.com/OpenBLAS/) -if test $ax_blas_ok = no; then - AC_CHECK_LIB(openblas, $sgemm, [ax_blas_ok=yes - BLAS_LIBS="-lopenblas"]) -fi - -# BLAS in ATLAS library? (http://math-atlas.sourceforge.net/) -if test $ax_blas_ok = no; then - AC_CHECK_LIB(atlas, ATL_xerbla, - [AC_CHECK_LIB(f77blas, $sgemm, - [AC_CHECK_LIB(cblas, cblas_dgemm, - [ax_blas_ok=yes - BLAS_LIBS="-lcblas -lf77blas -latlas"], - [], [-lf77blas -latlas])], - [], [-latlas])]) -fi - -# BLAS in PhiPACK libraries? (requires generic BLAS lib, too) -if test $ax_blas_ok = no; then - AC_CHECK_LIB(blas, $sgemm, - [AC_CHECK_LIB(dgemm, $dgemm, - [AC_CHECK_LIB(sgemm, $sgemm, - [ax_blas_ok=yes; BLAS_LIBS="-lsgemm -ldgemm -lblas"], - [], [-lblas])], - [], [-lblas])]) -fi - -# BLAS in Apple vecLib library? -if test $ax_blas_ok = no; then - save_LIBS="$LIBS"; LIBS="-framework vecLib $LIBS" - AC_MSG_CHECKING([for $sgemm in -framework vecLib]) - AC_TRY_LINK_FUNC($sgemm, [ax_blas_ok=yes;BLAS_LIBS="-framework vecLib"]) - AC_MSG_RESULT($ax_blas_ok) - LIBS="$save_LIBS" -fi - -# BLAS in Alpha CXML library? -if test $ax_blas_ok = no; then - AC_CHECK_LIB(cxml, $sgemm, [ax_blas_ok=yes;BLAS_LIBS="-lcxml"]) -fi - -# BLAS in Alpha DXML library? (now called CXML, see above) -if test $ax_blas_ok = no; then - AC_CHECK_LIB(dxml, $sgemm, [ax_blas_ok=yes;BLAS_LIBS="-ldxml"]) -fi - -# BLAS in Sun Performance library? -if test $ax_blas_ok = no; then - if test "x$GCC" != xyes; then # only works with Sun CC - AC_CHECK_LIB(sunmath, acosp, - [AC_CHECK_LIB(sunperf, $sgemm, - [BLAS_LIBS="-xlic_lib=sunperf -lsunmath" - ax_blas_ok=yes],[],[-lsunmath])]) - fi -fi - -# BLAS in SCSL library? (SGI/Cray Scientific Library) -if test $ax_blas_ok = no; then - AC_CHECK_LIB(scs, $sgemm, [ax_blas_ok=yes; BLAS_LIBS="-lscs"]) -fi - -# BLAS in SGIMATH library? -if test $ax_blas_ok = no; then - AC_CHECK_LIB(complib.sgimath, $sgemm, - [ax_blas_ok=yes; BLAS_LIBS="-lcomplib.sgimath"]) -fi - -# BLAS in IBM ESSL library? (requires generic BLAS lib, too) -if test $ax_blas_ok = no; then - AC_CHECK_LIB(blas, $sgemm, - [AC_CHECK_LIB(essl, $sgemm, - [ax_blas_ok=yes; BLAS_LIBS="-lessl -lblas"], - [], [-lblas $FLIBS])]) -fi - -# Generic BLAS library? -if test $ax_blas_ok = no; then - AC_CHECK_LIB(blas, $sgemm, [ax_blas_ok=yes; BLAS_LIBS="-lblas"]) -fi - -AC_SUBST(BLAS_LIBS) -AC_SUBST(OPENMP_LDFLAGS) - -LIBS="$ax_blas_save_LIBS" - -# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: -if test x"$ax_blas_ok" = xyes; then - ifelse([$1],,AC_DEFINE(HAVE_BLAS,1,[Define if you have a BLAS library.]),[$1]) - : -else - ax_blas_ok=no - $2 -fi -])dnl AX_BLAS diff --git a/core/src/index/thirdparty/faiss/acinclude/ax_check_cpu.m4 b/core/src/index/thirdparty/faiss/acinclude/ax_check_cpu.m4 deleted file mode 100644 index fc61fc91e9..0000000000 --- a/core/src/index/thirdparty/faiss/acinclude/ax_check_cpu.m4 +++ /dev/null @@ -1,26 +0,0 @@ -# serial 1 - -AC_DEFUN([AX_CPU_ARCH], [ - -AC_MSG_CHECKING([for cpu arch]) - - AC_CANONICAL_TARGET - - case $target in - amd64-* | x86_64-*) - ARCH_CPUFLAGS="-mpopcnt -msse4" - ARCH_CXXFLAGS="-m64" - ;; - aarch64*-*) -dnl This is an arch for Nvidia Xavier a proper detection would be nice. - ARCH_CPUFLAGS="-march=armv8.2-a" - ;; - *) ;; - esac - -AC_MSG_RESULT([$target CPUFLAGS+="$ARCH_CPUFLAGS" CXXFLAGS+="$ARCH_CXXFLAGS"]) - -AC_SUBST(ARCH_CPUFLAGS) -AC_SUBST(ARCH_CXXFLAGS) - -])dnl diff --git a/core/src/index/thirdparty/faiss/acinclude/ax_cxx_compile_stdcxx.m4 b/core/src/index/thirdparty/faiss/acinclude/ax_cxx_compile_stdcxx.m4 deleted file mode 100644 index 0b6cb3a7d7..0000000000 --- a/core/src/index/thirdparty/faiss/acinclude/ax_cxx_compile_stdcxx.m4 +++ /dev/null @@ -1,972 +0,0 @@ -# =========================================================================== -# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) -# -# DESCRIPTION -# -# Check for baseline language coverage in the compiler for the specified -# version of the C++ standard. If necessary, add switches to CXX and -# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) -# or '14' (for the C++14 standard). -# -# The second argument, if specified, indicates whether you insist on an -# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. -# -std=c++11). If neither is specified, you get whatever works, with -# preference for an extended mode. -# -# The third argument, if specified 'mandatory' or if left unspecified, -# indicates that baseline support for the specified C++ standard is -# required and that the macro should error out if no mode with that -# support is found. If specified 'optional', then configuration proceeds -# regardless, after defining HAVE_CXX${VERSION} if and only if a -# supporting mode is found. -# -# LICENSE -# -# Copyright (c) 2008 Benjamin Kosnik -# Copyright (c) 2012 Zack Weinberg -# Copyright (c) 2013 Roy Stogner -# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov -# Copyright (c) 2015 Paul Norman -# Copyright (c) 2015 Moritz Klammler -# Copyright (c) 2016, 2018 Krzesimir Nowak -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 9 - -dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro -dnl (serial version number 13). - -AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl - m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], - [$1], [14], [ax_cxx_compile_alternatives="14 1y"], - [$1], [17], [ax_cxx_compile_alternatives="17 1z"], - [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl - m4_if([$2], [], [], - [$2], [ext], [], - [$2], [noext], [], - [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl - m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], - [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], - [$3], [optional], [ax_cxx_compile_cxx$1_required=false], - [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) - AC_LANG_PUSH([C++])dnl - ac_success=no - - m4_if([$2], [noext], [], [dnl - if test x$ac_success = xno; then - for alternative in ${ax_cxx_compile_alternatives}; do - switch="-std=gnu++${alternative}" - cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) - AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, - $cachevar, - [ac_save_CXX="$CXX" - CXX="$CXX $switch" - AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], - [eval $cachevar=yes], - [eval $cachevar=no]) - CXX="$ac_save_CXX"]) - if eval test x\$$cachevar = xyes; then - CXX="$CXX $switch" - if test -n "$CXXCPP" ; then - CXXCPP="$CXXCPP $switch" - fi - ac_success=yes - break - fi - done - fi]) - - m4_if([$2], [ext], [], [dnl - if test x$ac_success = xno; then - dnl HP's aCC needs +std=c++11 according to: - dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf - dnl Cray's crayCC needs "-h std=c++11" - for alternative in ${ax_cxx_compile_alternatives}; do - for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do - cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) - AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, - $cachevar, - [ac_save_CXX="$CXX" - CXX="$CXX $switch" - AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], - [eval $cachevar=yes], - [eval $cachevar=no]) - CXX="$ac_save_CXX"]) - if eval test x\$$cachevar = xyes; then - CXX="$CXX $switch" - if test -n "$CXXCPP" ; then - CXXCPP="$CXXCPP $switch" - fi - ac_success=yes - break - fi - done - if test x$ac_success = xyes; then - break - fi - done - fi]) - AC_LANG_POP([C++]) - if test x$ax_cxx_compile_cxx$1_required = xtrue; then - if test x$ac_success = xno; then - AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) - fi - fi - if test x$ac_success = xno; then - HAVE_CXX$1=0 - AC_MSG_NOTICE([No compiler with C++$1 support was found]) - else - HAVE_CXX$1=1 - AC_DEFINE(HAVE_CXX$1,1, - [define if the compiler supports basic C++$1 syntax]) - fi - AC_SUBST(HAVE_CXX$1) -]) - - -dnl Test body for checking C++11 support - -m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], - _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 -) - - -dnl Test body for checking C++14 support - -m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], - _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 - _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 -) - -m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], - _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 - _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 - _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 -) - -dnl Tests for new features in C++11 - -m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ - -// If the compiler admits that it is not ready for C++11, why torture it? -// Hopefully, this will speed up the test. - -#ifndef __cplusplus - -#error "This is not a C++ compiler" - -#elif __cplusplus < 201103L - -#error "This is not a C++11 compiler" - -#else - -namespace cxx11 -{ - - namespace test_static_assert - { - - template - struct check - { - static_assert(sizeof(int) <= sizeof(T), "not big enough"); - }; - - } - - namespace test_final_override - { - - struct Base - { - virtual void f() {} - }; - - struct Derived : public Base - { - virtual void f() override {} - }; - - } - - namespace test_double_right_angle_brackets - { - - template < typename T > - struct check {}; - - typedef check single_type; - typedef check> double_type; - typedef check>> triple_type; - typedef check>>> quadruple_type; - - } - - namespace test_decltype - { - - int - f() - { - int a = 1; - decltype(a) b = 2; - return a + b; - } - - } - - namespace test_type_deduction - { - - template < typename T1, typename T2 > - struct is_same - { - static const bool value = false; - }; - - template < typename T > - struct is_same - { - static const bool value = true; - }; - - template < typename T1, typename T2 > - auto - add(T1 a1, T2 a2) -> decltype(a1 + a2) - { - return a1 + a2; - } - - int - test(const int c, volatile int v) - { - static_assert(is_same::value == true, ""); - static_assert(is_same::value == false, ""); - static_assert(is_same::value == false, ""); - auto ac = c; - auto av = v; - auto sumi = ac + av + 'x'; - auto sumf = ac + av + 1.0; - static_assert(is_same::value == true, ""); - static_assert(is_same::value == true, ""); - static_assert(is_same::value == true, ""); - static_assert(is_same::value == false, ""); - static_assert(is_same::value == true, ""); - return (sumf > 0.0) ? sumi : add(c, v); - } - - } - - namespace test_noexcept - { - - int f() { return 0; } - int g() noexcept { return 0; } - - static_assert(noexcept(f()) == false, ""); - static_assert(noexcept(g()) == true, ""); - - } - - namespace test_constexpr - { - - template < typename CharT > - unsigned long constexpr - strlen_c_r(const CharT *const s, const unsigned long acc) noexcept - { - return *s ? strlen_c_r(s + 1, acc + 1) : acc; - } - - template < typename CharT > - unsigned long constexpr - strlen_c(const CharT *const s) noexcept - { - return strlen_c_r(s, 0UL); - } - - static_assert(strlen_c("") == 0UL, ""); - static_assert(strlen_c("1") == 1UL, ""); - static_assert(strlen_c("example") == 7UL, ""); - static_assert(strlen_c("another\0example") == 7UL, ""); - - } - - namespace test_rvalue_references - { - - template < int N > - struct answer - { - static constexpr int value = N; - }; - - answer<1> f(int&) { return answer<1>(); } - answer<2> f(const int&) { return answer<2>(); } - answer<3> f(int&&) { return answer<3>(); } - - void - test() - { - int i = 0; - const int c = 0; - static_assert(decltype(f(i))::value == 1, ""); - static_assert(decltype(f(c))::value == 2, ""); - static_assert(decltype(f(0))::value == 3, ""); - } - - } - - namespace test_uniform_initialization - { - - struct test - { - static const int zero {}; - static const int one {1}; - }; - - static_assert(test::zero == 0, ""); - static_assert(test::one == 1, ""); - - } - - namespace test_lambdas - { - - void - test1() - { - auto lambda1 = [](){}; - auto lambda2 = lambda1; - lambda1(); - lambda2(); - } - - int - test2() - { - auto a = [](int i, int j){ return i + j; }(1, 2); - auto b = []() -> int { return '0'; }(); - auto c = [=](){ return a + b; }(); - auto d = [&](){ return c; }(); - auto e = [a, &b](int x) mutable { - const auto identity = [](int y){ return y; }; - for (auto i = 0; i < a; ++i) - a += b--; - return x + identity(a + b); - }(0); - return a + b + c + d + e; - } - - int - test3() - { - const auto nullary = [](){ return 0; }; - const auto unary = [](int x){ return x; }; - using nullary_t = decltype(nullary); - using unary_t = decltype(unary); - const auto higher1st = [](nullary_t f){ return f(); }; - const auto higher2nd = [unary](nullary_t f1){ - return [unary, f1](unary_t f2){ return f2(unary(f1())); }; - }; - return higher1st(nullary) + higher2nd(nullary)(unary); - } - - } - - namespace test_variadic_templates - { - - template - struct sum; - - template - struct sum - { - static constexpr auto value = N0 + sum::value; - }; - - template <> - struct sum<> - { - static constexpr auto value = 0; - }; - - static_assert(sum<>::value == 0, ""); - static_assert(sum<1>::value == 1, ""); - static_assert(sum<23>::value == 23, ""); - static_assert(sum<1, 2>::value == 3, ""); - static_assert(sum<5, 5, 11>::value == 21, ""); - static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); - - } - - // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae - // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function - // because of this. - namespace test_template_alias_sfinae - { - - struct foo {}; - - template - using member = typename T::member_type; - - template - void func(...) {} - - template - void func(member*) {} - - void test(); - - void test() { func(0); } - - } - -} // namespace cxx11 - -#endif // __cplusplus >= 201103L - -]]) - - -dnl Tests for new features in C++14 - -m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ - -// If the compiler admits that it is not ready for C++14, why torture it? -// Hopefully, this will speed up the test. - -#ifndef __cplusplus - -#error "This is not a C++ compiler" - -#elif __cplusplus < 201402L - -#error "This is not a C++14 compiler" - -#else - -namespace cxx14 -{ - - namespace test_polymorphic_lambdas - { - - int - test() - { - const auto lambda = [](auto&&... args){ - const auto istiny = [](auto x){ - return (sizeof(x) == 1UL) ? 1 : 0; - }; - const int aretiny[] = { istiny(args)... }; - return aretiny[0]; - }; - return lambda(1, 1L, 1.0f, '1'); - } - - } - - namespace test_binary_literals - { - - constexpr auto ivii = 0b0000000000101010; - static_assert(ivii == 42, "wrong value"); - - } - - namespace test_generalized_constexpr - { - - template < typename CharT > - constexpr unsigned long - strlen_c(const CharT *const s) noexcept - { - auto length = 0UL; - for (auto p = s; *p; ++p) - ++length; - return length; - } - - static_assert(strlen_c("") == 0UL, ""); - static_assert(strlen_c("x") == 1UL, ""); - static_assert(strlen_c("test") == 4UL, ""); - static_assert(strlen_c("another\0test") == 7UL, ""); - - } - - namespace test_lambda_init_capture - { - - int - test() - { - auto x = 0; - const auto lambda1 = [a = x](int b){ return a + b; }; - const auto lambda2 = [a = lambda1(x)](){ return a; }; - return lambda2(); - } - - } - - namespace test_digit_separators - { - - constexpr auto ten_million = 100'000'000; - static_assert(ten_million == 100000000, ""); - - } - - namespace test_return_type_deduction - { - - auto f(int& x) { return x; } - decltype(auto) g(int& x) { return x; } - - template < typename T1, typename T2 > - struct is_same - { - static constexpr auto value = false; - }; - - template < typename T > - struct is_same - { - static constexpr auto value = true; - }; - - int - test() - { - auto x = 0; - static_assert(is_same::value, ""); - static_assert(is_same::value, ""); - return x; - } - - } - -} // namespace cxx14 - -#endif // __cplusplus >= 201402L - -]]) - - -dnl Tests for new features in C++17 - -m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ - -// If the compiler admits that it is not ready for C++17, why torture it? -// Hopefully, this will speed up the test. - -#ifndef __cplusplus - -#error "This is not a C++ compiler" - -#elif __cplusplus <= 201402L - -#error "This is not a C++17 compiler" - -#else - -#if defined(__clang__) - #define REALLY_CLANG -#else - #if defined(__GNUC__) - #define REALLY_GCC - #endif -#endif - -#include -#include -#include - -namespace cxx17 -{ - -#if !defined(REALLY_CLANG) - namespace test_constexpr_lambdas - { - - // TODO: test it with clang++ from git - - constexpr int foo = [](){return 42;}(); - - } -#endif // !defined(REALLY_CLANG) - - namespace test::nested_namespace::definitions - { - - } - - namespace test_fold_expression - { - - template - int multiply(Args... args) - { - return (args * ... * 1); - } - - template - bool all(Args... args) - { - return (args && ...); - } - - } - - namespace test_extended_static_assert - { - - static_assert (true); - - } - - namespace test_auto_brace_init_list - { - - auto foo = {5}; - auto bar {5}; - - static_assert(std::is_same, decltype(foo)>::value); - static_assert(std::is_same::value); - } - - namespace test_typename_in_template_template_parameter - { - - template typename X> struct D; - - } - - namespace test_fallthrough_nodiscard_maybe_unused_attributes - { - - int f1() - { - return 42; - } - - [[nodiscard]] int f2() - { - [[maybe_unused]] auto unused = f1(); - - switch (f1()) - { - case 17: - f1(); - [[fallthrough]]; - case 42: - f1(); - } - return f1(); - } - - } - - namespace test_extended_aggregate_initialization - { - - struct base1 - { - int b1, b2 = 42; - }; - - struct base2 - { - base2() { - b3 = 42; - } - int b3; - }; - - struct derived : base1, base2 - { - int d; - }; - - derived d1 {{1, 2}, {}, 4}; // full initialization - derived d2 {{}, {}, 4}; // value-initialized bases - - } - - namespace test_general_range_based_for_loop - { - - struct iter - { - int i; - - int& operator* () - { - return i; - } - - const int& operator* () const - { - return i; - } - - iter& operator++() - { - ++i; - return *this; - } - }; - - struct sentinel - { - int i; - }; - - bool operator== (const iter& i, const sentinel& s) - { - return i.i == s.i; - } - - bool operator!= (const iter& i, const sentinel& s) - { - return !(i == s); - } - - struct range - { - iter begin() const - { - return {0}; - } - - sentinel end() const - { - return {5}; - } - }; - - void f() - { - range r {}; - - for (auto i : r) - { - [[maybe_unused]] auto v = i; - } - } - - } - - namespace test_lambda_capture_asterisk_this_by_value - { - - struct t - { - int i; - int foo() - { - return [*this]() - { - return i; - }(); - } - }; - - } - - namespace test_enum_class_construction - { - - enum class byte : unsigned char - {}; - - byte foo {42}; - - } - - namespace test_constexpr_if - { - - template - int f () - { - if constexpr(cond) - { - return 13; - } - else - { - return 42; - } - } - - } - - namespace test_selection_statement_with_initializer - { - - int f() - { - return 13; - } - - int f2() - { - if (auto i = f(); i > 0) - { - return 3; - } - - switch (auto i = f(); i + 4) - { - case 17: - return 2; - - default: - return 1; - } - } - - } - -#if !defined(REALLY_CLANG) - namespace test_template_argument_deduction_for_class_templates - { - - // TODO: test it with clang++ from git - - template - struct pair - { - pair (T1 p1, T2 p2) - : m1 {p1}, - m2 {p2} - {} - - T1 m1; - T2 m2; - }; - - void f() - { - [[maybe_unused]] auto p = pair{13, 42u}; - } - - } -#endif // !defined(REALLY_CLANG) - - namespace test_non_type_auto_template_parameters - { - - template - struct B - {}; - - B<5> b1; - B<'a'> b2; - - } - -#if !defined(REALLY_CLANG) - namespace test_structured_bindings - { - - // TODO: test it with clang++ from git - - int arr[2] = { 1, 2 }; - std::pair pr = { 1, 2 }; - - auto f1() -> int(&)[2] - { - return arr; - } - - auto f2() -> std::pair& - { - return pr; - } - - struct S - { - int x1 : 2; - volatile double y1; - }; - - S f3() - { - return {}; - } - - auto [ x1, y1 ] = f1(); - auto& [ xr1, yr1 ] = f1(); - auto [ x2, y2 ] = f2(); - auto& [ xr2, yr2 ] = f2(); - const auto [ x3, y3 ] = f3(); - - } -#endif // !defined(REALLY_CLANG) - -#if !defined(REALLY_CLANG) - namespace test_exception_spec_type_system - { - - // TODO: test it with clang++ from git - - struct Good {}; - struct Bad {}; - - void g1() noexcept; - void g2(); - - template - Bad - f(T*, T*); - - template - Good - f(T1*, T2*); - - static_assert (std::is_same_v); - - } -#endif // !defined(REALLY_CLANG) - - namespace test_inline_variables - { - - template void f(T) - {} - - template inline T g(T) - { - return T{}; - } - - template<> inline void f<>(int) - {} - - template<> int g<>(int) - { - return 5; - } - - } - -} // namespace cxx17 - -#endif // __cplusplus <= 201402L - -]]) diff --git a/core/src/index/thirdparty/faiss/acinclude/ax_lapack.m4 b/core/src/index/thirdparty/faiss/acinclude/ax_lapack.m4 deleted file mode 100644 index 4993f29b9c..0000000000 --- a/core/src/index/thirdparty/faiss/acinclude/ax_lapack.m4 +++ /dev/null @@ -1,132 +0,0 @@ -# =========================================================================== -# https://www.gnu.org/software/autoconf-archive/ax_lapack.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_LAPACK([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) -# -# DESCRIPTION -# -# This macro looks for a library that implements the LAPACK linear-algebra -# interface (see http://www.netlib.org/lapack/). On success, it sets the -# LAPACK_LIBS output variable to hold the requisite library linkages. -# -# To link with LAPACK, you should link with: -# -# $LAPACK_LIBS $BLAS_LIBS $LIBS $FLIBS -# -# in that order. BLAS_LIBS is the output variable of the AX_BLAS macro, -# called automatically. FLIBS is the output variable of the -# AC_F77_LIBRARY_LDFLAGS macro (called if necessary by AX_BLAS), and is -# sometimes necessary in order to link with F77 libraries. Users will also -# need to use AC_F77_DUMMY_MAIN (see the autoconf manual), for the same -# reason. -# -# The user may also use --with-lapack= in order to use some specific -# LAPACK library . In order to link successfully, however, be aware -# that you will probably need to use the same Fortran compiler (which can -# be set via the F77 env. var.) as was used to compile the LAPACK and BLAS -# libraries. -# -# ACTION-IF-FOUND is a list of shell commands to run if a LAPACK library -# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it -# is not found. If ACTION-IF-FOUND is not specified, the default action -# will define HAVE_LAPACK. -# -# LICENSE -# -# Copyright (c) 2009 Steven G. Johnson -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program. If not, see . -# -# As a special exception, the respective Autoconf Macro's copyright owner -# gives unlimited permission to copy, distribute and modify the configure -# scripts that are the output of Autoconf when processing the Macro. You -# need not follow the terms of the GNU General Public License when using -# or distributing such scripts, even though portions of the text of the -# Macro appear in them. The GNU General Public License (GPL) does govern -# all other use of the material that constitutes the Autoconf Macro. -# -# This special exception to the GPL applies to versions of the Autoconf -# Macro released by the Autoconf Archive. When you make and distribute a -# modified version of the Autoconf Macro, you may extend this special -# exception to the GPL to apply to your modified version as well. - -#serial 8 - -AU_ALIAS([ACX_LAPACK], [AX_LAPACK]) -AC_DEFUN([AX_LAPACK], [ -AC_REQUIRE([AX_BLAS]) -ax_lapack_ok=no - -AC_ARG_WITH(lapack, - [AS_HELP_STRING([--with-lapack=], [use LAPACK library ])]) -case $with_lapack in - yes | "") ;; - no) ax_lapack_ok=disable ;; - -* | */* | *.a | *.so | *.so.* | *.o) LAPACK_LIBS="$with_lapack" ;; - *) LAPACK_LIBS="-l$with_lapack" ;; -esac - -# Get fortran linker name of LAPACK function to check for. -# AC_F77_FUNC(cheev) -cheev=cheev_ - -# We cannot use LAPACK if BLAS is not found -if test "x$ax_blas_ok" != xyes; then - ax_lapack_ok=noblas - LAPACK_LIBS="" -fi - -# First, check LAPACK_LIBS environment variable -if test "x$LAPACK_LIBS" != x; then - save_LIBS="$LIBS"; LIBS="$LAPACK_LIBS $BLAS_LIBS $LIBS $FLIBS" - AC_MSG_CHECKING([for $cheev in $LAPACK_LIBS]) - AC_TRY_LINK_FUNC($cheev, [ax_lapack_ok=yes], [LAPACK_LIBS=""]) - AC_MSG_RESULT($ax_lapack_ok) - LIBS="$save_LIBS" - if test $ax_lapack_ok = no; then - LAPACK_LIBS="" - fi -fi - -# LAPACK linked to by default? (is sometimes included in BLAS lib) -if test $ax_lapack_ok = no; then - save_LIBS="$LIBS"; LIBS="$LIBS $BLAS_LIBS $FLIBS" - AC_CHECK_FUNC($cheev, [ax_lapack_ok=yes]) - LIBS="$save_LIBS" -fi - -# Generic LAPACK library? -for lapack in lapack lapack_rs6k; do - if test $ax_lapack_ok = no; then - save_LIBS="$LIBS"; LIBS="$BLAS_LIBS $LIBS" - AC_CHECK_LIB($lapack, $cheev, - [ax_lapack_ok=yes; LAPACK_LIBS="-l$lapack"], [], [$FLIBS]) - LIBS="$save_LIBS" - fi -done - -AC_SUBST(LAPACK_LIBS) - -# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: -if test x"$ax_lapack_ok" = xyes; then - ifelse([$1],,AC_DEFINE(HAVE_LAPACK,1,[Define if you have LAPACK library.]),[$1]) - : -else - ax_lapack_ok=no - $2 -fi -])dnl AX_LAPACK diff --git a/core/src/index/thirdparty/faiss/acinclude/fa_check_cuda.m4 b/core/src/index/thirdparty/faiss/acinclude/fa_check_cuda.m4 deleted file mode 100644 index f730bc23e2..0000000000 --- a/core/src/index/thirdparty/faiss/acinclude/fa_check_cuda.m4 +++ /dev/null @@ -1,67 +0,0 @@ -AC_DEFUN([FA_CHECK_CUDA], [ - -AC_ARG_WITH(cuda, - [AS_HELP_STRING([--with-cuda=], [prefix of the CUDA installation])]) -AC_ARG_WITH(cuda-arch, - [AS_HELP_STRING([--with-cuda-arch=], [device specific -gencode flags])], - [], - [with_cuda_arch=default]) - -if test x$with_cuda != xno; then - if test x$with_cuda != x; then - cuda_prefix=$with_cuda - AC_CHECK_PROG(NVCC, [nvcc], [$cuda_prefix/bin/nvcc], [], [$cuda_prefix/bin]) - NVCC_CPPFLAGS="-I$cuda_prefix/include" - NVCC_LDFLAGS="-L$cuda_prefix/lib64" - else - AC_CHECK_PROGS(NVCC, [nvcc /usr/local/cuda/bin/nvcc], []) - if test "x$NVCC" == "x/usr/local/cuda/bin/nvcc"; then - cuda_prefix="/usr/local/cuda" - NVCC_CPPFLAGS="-I$cuda_prefix/include" - NVCC_LDFLAGS="-L$cuda_prefix/lib64" - else - cuda_prefix="" - NVCC_CPPFLAGS="" - NVCC_LDFLAGS="" - fi - fi - - if test "x$NVCC" == x; then - AC_MSG_ERROR([Couldn't find nvcc]) - fi - - if test "x$with_cuda_arch" == xdefault; then - with_cuda_arch="-gencode=arch=compute_35,code=compute_35 \\ --gencode=arch=compute_52,code=compute_52 \\ --gencode=arch=compute_60,code=compute_60 \\ --gencode=arch=compute_61,code=compute_61 \\ --gencode=arch=compute_70,code=compute_70 \\ --gencode=arch=compute_75,code=compute_75" - fi - - fa_save_CPPFLAGS="$CPPFLAGS" - fa_save_LDFLAGS="$LDFLAGS" - fa_save_LIBS="$LIBS" - - CPPFLAGS="$NVCC_CPPFLAGS $CPPFLAGS" - LDFLAGS="$NVCC_LDFLAGS $LDFLAGS" - - AC_CHECK_HEADER([cuda.h], [], AC_MSG_FAILURE([Couldn't find cuda.h])) - AC_CHECK_LIB([cublas], [cublasAlloc], [], AC_MSG_FAILURE([Couldn't find libcublas])) - AC_CHECK_LIB([cudart], [cudaSetDevice], [], AC_MSG_FAILURE([Couldn't find libcudart])) - - NVCC_LIBS="$LIBS" - NVCC_CPPFLAGS="$CPPFLAGS" - NVCC_LDFLAGS="$LDFLAGS" - CPPFLAGS="$fa_save_CPPFLAGS" - LDFLAGS="$fa_save_LDFLAGS" - LIBS="$fa_save_LIBS" -fi - -AC_SUBST(NVCC) -AC_SUBST(NVCC_CPPFLAGS) -AC_SUBST(NVCC_LDFLAGS) -AC_SUBST(NVCC_LIBS) -AC_SUBST(CUDA_PREFIX, $cuda_prefix) -AC_SUBST(CUDA_ARCH, $with_cuda_arch) -]) diff --git a/core/src/index/thirdparty/faiss/acinclude/fa_numpy.m4 b/core/src/index/thirdparty/faiss/acinclude/fa_numpy.m4 deleted file mode 100644 index 6e3dcde531..0000000000 --- a/core/src/index/thirdparty/faiss/acinclude/fa_numpy.m4 +++ /dev/null @@ -1,20 +0,0 @@ -AC_DEFUN([FA_NUMPY], [ -AC_REQUIRE([FA_PYTHON]) - -AC_MSG_CHECKING([for numpy headers path]) - -fa_numpy_headers=`$PYTHON -c "import numpy; print(numpy.get_include())"` - -if test $? == 0; then - if test x$fa_numpy_headers != x; then - AC_MSG_RESULT($fa_numpy_headers) - AC_SUBST(NUMPY_INCLUDE, $fa_numpy_headers) - else - AC_MSG_RESULT([not found]) - AC_MSG_WARN([You won't be able to build the python interface.]) - fi -else - AC_MSG_RESULT([not found]) - AC_MSG_WARN([You won't be able to build the python interface.]) -fi -])dnl diff --git a/core/src/index/thirdparty/faiss/acinclude/fa_prog_nm.m4 b/core/src/index/thirdparty/faiss/acinclude/fa_prog_nm.m4 deleted file mode 100644 index f450ba7645..0000000000 --- a/core/src/index/thirdparty/faiss/acinclude/fa_prog_nm.m4 +++ /dev/null @@ -1,16 +0,0 @@ -dnl -dnl Check for an nm(1) utility. -dnl -AC_DEFUN([FA_PROG_NM], -[ - case "${NM-unset}" in - unset) AC_CHECK_PROGS(NM, nm, nm) ;; - *) AC_CHECK_PROGS(NM, $NM nm, nm) ;; - esac - AC_MSG_CHECKING(nm flags) - case "${NMFLAGS-unset}" in - unset) NMFLAGS= ;; - esac - AC_MSG_RESULT($NMFLAGS) - AC_SUBST(NMFLAGS) -]) diff --git a/core/src/index/thirdparty/faiss/acinclude/fa_prog_swig.m4 b/core/src/index/thirdparty/faiss/acinclude/fa_prog_swig.m4 deleted file mode 100644 index 1e6ab8e49d..0000000000 --- a/core/src/index/thirdparty/faiss/acinclude/fa_prog_swig.m4 +++ /dev/null @@ -1,11 +0,0 @@ -AC_DEFUN([FA_PROG_SWIG], [ - -AC_ARG_WITH(swig, -[AS_HELP_STRING([--with-swig=], [use SWIG binary ])]) -case $with_swig in - "") AC_CHECK_PROG(SWIG, swig, swig);; - *) SWIG="$with_swig" -esac - -AC_SUBST(SWIG) -]) diff --git a/core/src/index/thirdparty/faiss/acinclude/fa_python.m4 b/core/src/index/thirdparty/faiss/acinclude/fa_python.m4 deleted file mode 100644 index a58a9d15ec..0000000000 --- a/core/src/index/thirdparty/faiss/acinclude/fa_python.m4 +++ /dev/null @@ -1,21 +0,0 @@ -AC_DEFUN([FA_PYTHON], [ - -AC_ARG_WITH(python, - [AS_HELP_STRING([--with-python=], [use Python binary ])]) -case $with_python in - "") PYTHON_BIN=python ;; - *) PYTHON_BIN="$with_python" -esac - -AC_CHECK_PROG(PYTHON, $PYTHON_BIN, $PYTHON_BIN) -fa_python_bin=$PYTHON - -AC_MSG_CHECKING([for Python C flags]) -fa_python_cflags=`$PYTHON -c " -import sysconfig -paths = [['-I' + sysconfig.get_path(p) for p in ['include', 'platinclude']]] -print(' '.join(paths))"` -AC_MSG_RESULT($fa_python_cflags) -AC_SUBST(PYTHON_CFLAGS, "$PYTHON_CFLAGS $fa_python_cflags") - -])dnl FA_PYTHON diff --git a/core/src/index/thirdparty/faiss/benchs/README.md b/core/src/index/thirdparty/faiss/benchs/README.md deleted file mode 100644 index 7e95a7673d..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/README.md +++ /dev/null @@ -1,338 +0,0 @@ - -# Benchmarking scripts - -This directory contains benchmarking scripts that can reproduce the -numbers reported in the two papers - -``` -@inproceedings{DJP16, - Author = {Douze, Matthijs and J{\'e}gou, Herv{\'e} and Perronnin, Florent}, - Booktitle = "ECCV", - Organization = {Springer}, - Title = {Polysemous codes}, - Year = {2016} -} -``` -and - -``` -@inproceedings{JDJ17, - Author = {Jeff Johnson and Matthijs Douze and Herv{\'e} J{\'e}gou}, - journal= {arXiv:1702.08734},, - Title = {Billion-scale similarity search with GPUs}, - Year = {2017}, -} -``` - -Note that the numbers (especially timings) change slightly due to changes in the implementation, different machines, etc. - -The scripts are self-contained. They depend only on Faiss and external training data that should be stored in sub-directories. - -## SIFT1M experiments - -The script [`bench_polysemous_sift1m.py`](bench_polysemous_sift1m.py) reproduces the numbers in -Figure 3 from the "Polysemous" paper. - -### Getting SIFT1M - -To run it, please download the ANN_SIFT1M dataset from - -http://corpus-texmex.irisa.fr/ - -and unzip it to the subdirectory sift1M. - -### Result - -The output looks like: - -``` -PQ training on 100000 points, remains 0 points: training polysemous on centroids -add vectors to index -PQ baseline 7.517 ms per query, R@1 0.4474 -Polysemous 64 9.875 ms per query, R@1 0.4474 -Polysemous 62 8.358 ms per query, R@1 0.4474 -Polysemous 58 5.531 ms per query, R@1 0.4474 -Polysemous 54 3.420 ms per query, R@1 0.4478 -Polysemous 50 2.182 ms per query, R@1 0.4475 -Polysemous 46 1.621 ms per query, R@1 0.4408 -Polysemous 42 1.448 ms per query, R@1 0.4174 -Polysemous 38 1.331 ms per query, R@1 0.3563 -Polysemous 34 1.334 ms per query, R@1 0.2661 -Polysemous 30 1.272 ms per query, R@1 0.1794 -``` - - -## Experiments on 1B elements dataset - -The script [`bench_polysemous_1bn.py`](bench_polysemous_1bn.py) reproduces a few experiments on -two datasets of size 1B from the Polysemous codes" paper. - - -### Getting BIGANN - -Download the four files of ANN_SIFT1B from -http://corpus-texmex.irisa.fr/ to subdirectory bigann/ - -### Getting Deep1B - -The ground-truth and queries are available here - -https://yadi.sk/d/11eDCm7Dsn9GA - -For the learning and database vectors, use the script - -https://github.com/arbabenko/GNOIMI/blob/master/downloadDeep1B.py - -to download the data to subdirectory deep1b/, then concatenate the -database files to base.fvecs and the training files to learn.fvecs - -### Running the experiments - -These experiments are quite long. To support resuming, the script -stores the result of training to a temporary directory, `/tmp/bench_polysemous`. - -The script `bench_polysemous_1bn.py` takes at least two arguments: - -- the dataset name: SIFT1000M (aka SIFT1B, aka BIGANN) or Deep1B. SIFT1M, SIFT2M,... are also supported to make subsets of for small experiments (note that SIFT1M as a subset of SIFT1B is not the same as the SIFT1M above) - -- the type of index to build, which should be a valid [index_factory key](https://github.com/facebookresearch/faiss/wiki/High-level-interface-and-auto-tuning#index-factory) (see below for examples) - -- the remaining arguments are parsed as search-time parameters. - -### Experiments of Table 2 - -The `IMI*+PolyD+ADC` results in Table 2 can be reproduced with (for 16 bytes): - -``` -python bench_polysemous_1bn.par SIFT1000M IMI2x12,PQ16 nprobe=16,max_codes={10000,30000},ht={44..54} -``` - -Training takes about 2 minutes and adding vectors to the dataset -takes 3.1 h. These operations are multithreaded. Note that in the command -above, we use bash's [brace expansion](https://www.gnu.org/software/bash/manual/html_node/Brace-Expansion.html) to set a grid of parameters. - -The search is *not* multithreaded, and the output looks like: - -``` - R@1 R@10 R@100 time %pass -nprobe=16,max_codes=10000,ht=44 0.1779 0.2994 0.3139 0.194 12.45 -nprobe=16,max_codes=10000,ht=45 0.1859 0.3183 0.3339 0.197 14.24 -nprobe=16,max_codes=10000,ht=46 0.1930 0.3366 0.3543 0.202 16.22 -nprobe=16,max_codes=10000,ht=47 0.1993 0.3550 0.3745 0.209 18.39 -nprobe=16,max_codes=10000,ht=48 0.2033 0.3694 0.3917 0.640 20.77 -nprobe=16,max_codes=10000,ht=49 0.2070 0.3839 0.4077 0.229 23.36 -nprobe=16,max_codes=10000,ht=50 0.2101 0.3949 0.4205 0.232 26.17 -nprobe=16,max_codes=10000,ht=51 0.2120 0.4042 0.4310 0.239 29.21 -nprobe=16,max_codes=10000,ht=52 0.2134 0.4113 0.4402 0.245 32.47 -nprobe=16,max_codes=10000,ht=53 0.2157 0.4184 0.4482 0.250 35.96 -nprobe=16,max_codes=10000,ht=54 0.2170 0.4240 0.4546 0.256 39.66 -nprobe=16,max_codes=30000,ht=44 0.1882 0.3327 0.3555 0.226 11.29 -nprobe=16,max_codes=30000,ht=45 0.1964 0.3525 0.3771 0.231 13.05 -nprobe=16,max_codes=30000,ht=46 0.2039 0.3713 0.3987 0.236 15.01 -nprobe=16,max_codes=30000,ht=47 0.2103 0.3907 0.4202 0.245 17.19 -nprobe=16,max_codes=30000,ht=48 0.2145 0.4055 0.4384 0.251 19.60 -nprobe=16,max_codes=30000,ht=49 0.2179 0.4198 0.4550 0.257 22.25 -nprobe=16,max_codes=30000,ht=50 0.2208 0.4305 0.4681 0.268 25.15 -nprobe=16,max_codes=30000,ht=51 0.2227 0.4402 0.4791 0.275 28.30 -nprobe=16,max_codes=30000,ht=52 0.2241 0.4473 0.4884 0.284 31.70 -nprobe=16,max_codes=30000,ht=53 0.2265 0.4544 0.4965 0.294 35.34 -nprobe=16,max_codes=30000,ht=54 0.2278 0.4601 0.5031 0.303 39.20 -``` - -The result reported in table 2 is the one for which the %pass (percentage of code comparisons that pass the Hamming check) is around 20%, which occurs for Hamming threshold `ht=48`. - -The 8-byte results can be reproduced with the factory key `IMI2x12,PQ8` - -### Experiments of the appendix - -The experiments in the appendix are only in the ArXiv version of the paper (table 3). - -``` -python bench_polysemous_1bn.py SIFT1000M OPQ8_64,IMI2x13,PQ8 nprobe={1,2,4,8,16,32,64,128},ht={20,24,26,28,30} - - R@1 R@10 R@100 time %pass -nprobe=1,ht=20 0.0351 0.0616 0.0751 0.158 19.01 -... -nprobe=32,ht=28 0.1256 0.3563 0.5026 0.561 52.61 -... -``` -Here again the runs are not exactly the same but the original result was obtained from nprobe=32,ht=28. - -For Deep1B, we used a simple version of [auto-tuning](https://github.com/facebookresearch/faiss/wiki/High-level-interface-and-auto-tuning/_edit#auto-tuning-the-runtime-parameters) to sweep through the set of operating points: - -``` -python bench_polysemous_1bn.py Deep1B OPQ20_80,IMI2x14,PQ20 autotune -... -Done in 4067.555 s, available OPs: -Parameters 1-R@1 time - 0.0000 0.000 -nprobe=1,ht=22,max_codes=256 0.0215 3.115 -nprobe=1,ht=30,max_codes=256 0.0381 3.120 -... -nprobe=512,ht=68,max_codes=524288 0.4478 36.903 -nprobe=1024,ht=80,max_codes=131072 0.4557 46.363 -nprobe=1024,ht=78,max_codes=262144 0.4616 61.939 -... -``` -The original results were obtained with `nprobe=1024,ht=66,max_codes=262144`. - - -## GPU experiments - -The benchmarks below run 1 or 4 Titan X GPUs and reproduce the results of the "GPU paper". They are also a good starting point on how to use GPU Faiss. - -### Search on SIFT1M - -See above on how to get SIFT1M into subdirectory sift1M/. The script [`bench_gpu_sift1m.py`](bench_gpu_sift1m.py) reproduces the "exact k-NN time" plot in the ArXiv paper, and the SIFT1M numbers. - -The output is: -``` -============ Exact search -add vectors to index -warmup -benchmark -k=1 0.715 s, R@1 0.9914 -k=2 0.729 s, R@1 0.9935 -k=4 0.731 s, R@1 0.9935 -k=8 0.732 s, R@1 0.9935 -k=16 0.742 s, R@1 0.9935 -k=32 0.737 s, R@1 0.9935 -k=64 0.753 s, R@1 0.9935 -k=128 0.761 s, R@1 0.9935 -k=256 0.799 s, R@1 0.9935 -k=512 0.975 s, R@1 0.9935 -k=1024 1.424 s, R@1 0.9935 -============ Approximate search -train -WARNING clustering 100000 points to 4096 centroids: please provide at least 159744 training points -add vectors to index -WARN: increase temp memory to avoid cudaMalloc, or decrease query/add size (alloc 256000000 B, highwater 256000000 B) -warmup -benchmark -nprobe= 1 0.043 s recalls= 0.3909 0.4312 0.4312 -nprobe= 2 0.040 s recalls= 0.5041 0.5636 0.5636 -nprobe= 4 0.048 s recalls= 0.6048 0.6897 0.6897 -nprobe= 8 0.064 s recalls= 0.6879 0.8028 0.8028 -nprobe= 16 0.088 s recalls= 0.7534 0.8940 0.8940 -nprobe= 32 0.134 s recalls= 0.7957 0.9549 0.9550 -nprobe= 64 0.224 s recalls= 0.8125 0.9833 0.9834 -nprobe= 128 0.395 s recalls= 0.8205 0.9953 0.9954 -nprobe= 256 0.717 s recalls= 0.8227 0.9993 0.9994 -nprobe= 512 1.348 s recalls= 0.8228 0.9999 1.0000 -``` -The run produces two warnings: - -- the clustering complains that it does not have enough training data, there is not much we can do about this. - -- the add() function complains that there is an inefficient memory allocation, but this is a concern only when it happens often, and we are not benchmarking the add time anyways. - -To index small datasets, it is more efficient to use a `GpuIVFFlat`, which just stores the full vectors in the inverted lists. We did not mention this in the the paper because it is not as scalable. To experiment with this setting, change the `index_factory` string from "IVF4096,PQ64" to "IVF16384,Flat". This gives: - -``` -nprobe= 1 0.025 s recalls= 0.4084 0.4105 0.4105 -nprobe= 2 0.033 s recalls= 0.5235 0.5264 0.5264 -nprobe= 4 0.033 s recalls= 0.6332 0.6367 0.6367 -nprobe= 8 0.040 s recalls= 0.7358 0.7403 0.7403 -nprobe= 16 0.049 s recalls= 0.8273 0.8324 0.8324 -nprobe= 32 0.068 s recalls= 0.8957 0.9024 0.9024 -nprobe= 64 0.104 s recalls= 0.9477 0.9549 0.9549 -nprobe= 128 0.174 s recalls= 0.9760 0.9837 0.9837 -nprobe= 256 0.299 s recalls= 0.9866 0.9944 0.9944 -nprobe= 512 0.527 s recalls= 0.9907 0.9987 0.9987 -``` - -### Clustering on MNIST8m - -To get the "infinite MNIST dataset", follow the instructions on [Léon Bottou's website](http://leon.bottou.org/projects/infimnist). The script assumes the file `mnist8m-patterns-idx3-ubyte` is in subdirectory `mnist8m` - -The script [`kmeans_mnist.py`](kmeans_mnist.py) produces the following output: - -``` -python kmeans_mnist.py 1 256 -... -Clustering 8100000 points in 784D to 256 clusters, redo 1 times, 20 iterations - Preprocessing in 7.94526 s - Iteration 19 (131.697 s, search 114.78 s): objective=1.44881e+13 imbalance=1.05963 nsplit=0 -final objective: 1.449e+13 -total runtime: 140.615 s -``` - -### search on SIFT1B - -The script [`bench_gpu_1bn.py`](bench_gpu_1bn.py) runs multi-gpu searches on the two 1-billion vector datasets we considered. It is more complex than the previous scripts, because it supports many search options and decomposes the dataset build process in Python to exploit the best possible CPU/GPU parallelism and GPU distribution. - -Even on multiple GPUs, building the 1B datasets can last several hours. It is often a good idea to validate that everything is working fine on smaller datasets like SIFT1M, SIFT2M, etc. - -The search results on SIFT1B in the "GPU paper" can be obtained with - - - -``` -python bench_gpu_1bn.py SIFT1000M OPQ8_32,IVF262144,PQ8 -nnn 10 -ngpu 1 -tempmem $[1536*1024*1024] -... -0/10000 (0.024 s) probe=1 : 0.161 s 1-R@1: 0.0752 1-R@10: 0.1924 -0/10000 (0.005 s) probe=2 : 0.150 s 1-R@1: 0.0964 1-R@10: 0.2693 -0/10000 (0.005 s) probe=4 : 0.153 s 1-R@1: 0.1102 1-R@10: 0.3328 -0/10000 (0.005 s) probe=8 : 0.170 s 1-R@1: 0.1220 1-R@10: 0.3827 -0/10000 (0.005 s) probe=16 : 0.196 s 1-R@1: 0.1290 1-R@10: 0.4151 -0/10000 (0.006 s) probe=32 : 0.244 s 1-R@1: 0.1314 1-R@10: 0.4345 -0/10000 (0.006 s) probe=64 : 0.353 s 1-R@1: 0.1332 1-R@10: 0.4461 -0/10000 (0.005 s) probe=128: 0.587 s 1-R@1: 0.1341 1-R@10: 0.4502 -0/10000 (0.006 s) probe=256: 1.160 s 1-R@1: 0.1342 1-R@10: 0.4511 -``` - -We use the `-tempmem` option to reduce the temporary memory allocation to 1.5G, otherwise the dataset does not fit in GPU memory - -### search on Deep1B - -The same script generates the GPU search results on Deep1B. - -``` -python bench_gpu_1bn.py Deep1B OPQ20_80,IVF262144,PQ20 -nnn 10 -R 2 -ngpu 4 -altadd -noptables -tempmem $[1024*1024*1024] -... - -0/10000 (0.115 s) probe=1 : 0.239 s 1-R@1: 0.2387 1-R@10: 0.3420 -0/10000 (0.006 s) probe=2 : 0.103 s 1-R@1: 0.3110 1-R@10: 0.4623 -0/10000 (0.005 s) probe=4 : 0.105 s 1-R@1: 0.3772 1-R@10: 0.5862 -0/10000 (0.005 s) probe=8 : 0.116 s 1-R@1: 0.4235 1-R@10: 0.6889 -0/10000 (0.005 s) probe=16 : 0.133 s 1-R@1: 0.4517 1-R@10: 0.7693 -0/10000 (0.005 s) probe=32 : 0.168 s 1-R@1: 0.4713 1-R@10: 0.8281 -0/10000 (0.005 s) probe=64 : 0.238 s 1-R@1: 0.4841 1-R@10: 0.8649 -0/10000 (0.007 s) probe=128: 0.384 s 1-R@1: 0.4900 1-R@10: 0.8816 -0/10000 (0.005 s) probe=256: 0.736 s 1-R@1: 0.4933 1-R@10: 0.8912 -``` - -Here we are a bit tight on memory so we disable precomputed tables (`-noptables`) and restrict the amount of temporary memory. The `-altadd` option avoids GPU memory overflows during add. - - -### knn-graph on Deep1B - -The same script generates the KNN-graph on Deep1B. Note that the inverted file from above will not be re-used because the training sets are different. For the knngraph, the script will first do a pass over the whole dataset to compute the ground-truth knn for a subset of 10k nodes, for evaluation. - -``` -python bench_gpu_1bn.py Deep1B OPQ20_80,IVF262144,PQ20 -nnn 10 -altadd -knngraph -R 2 -noptables -tempmem $[1<<30] -ngpu 4 -... -CPU index contains 1000000000 vectors, move to GPU -Copy CPU index to 2 sharded GPU indexes - dispatch to GPUs 0:2 -IndexShards shard 0 indices 0:500000000 - IndexIVFPQ size 500000000 -> GpuIndexIVFPQ indicesOptions=0 usePrecomputed=0 useFloat16=0 reserveVecs=0 -IndexShards shard 1 indices 500000000:1000000000 - IndexIVFPQ size 500000000 -> GpuIndexIVFPQ indicesOptions=0 usePrecomputed=0 useFloat16=0 reserveVecs=0 - dispatch to GPUs 2:4 -IndexShards shard 0 indices 0:500000000 - IndexIVFPQ size 500000000 -> GpuIndexIVFPQ indicesOptions=0 usePrecomputed=0 useFloat16=0 reserveVecs=0 -IndexShards shard 1 indices 500000000:1000000000 - IndexIVFPQ size 500000000 -> GpuIndexIVFPQ indicesOptions=0 usePrecomputed=0 useFloat16=0 reserveVecs=0 -move to GPU done in 151.535 s -search... -999997440/1000000000 (8389.961 s, 0.3379) probe=1 : 8389.990 s rank-10 intersection results: 0.3379 -999997440/1000000000 (9205.934 s, 0.4079) probe=2 : 9205.966 s rank-10 intersection results: 0.4079 -999997440/1000000000 (9741.095 s, 0.4722) probe=4 : 9741.128 s rank-10 intersection results: 0.4722 -999997440/1000000000 (10830.420 s, 0.5256) probe=8 : 10830.455 s rank-10 intersection results: 0.5256 -999997440/1000000000 (12531.716 s, 0.5603) probe=16 : 12531.758 s rank-10 intersection results: 0.5603 -999997440/1000000000 (15922.519 s, 0.5825) probe=32 : 15922.571 s rank-10 intersection results: 0.5825 -999997440/1000000000 (22774.153 s, 0.5950) probe=64 : 22774.220 s rank-10 intersection results: 0.5950 -999997440/1000000000 (36717.207 s, 0.6015) probe=128: 36717.309 s rank-10 intersection results: 0.6015 -999997440/1000000000 (70616.392 s, 0.6047) probe=256: 70616.581 s rank-10 intersection results: 0.6047 -``` diff --git a/core/src/index/thirdparty/faiss/benchs/bench_all_ivf/README.md b/core/src/index/thirdparty/faiss/benchs/bench_all_ivf/README.md deleted file mode 100644 index 2f7c76b5ac..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/bench_all_ivf/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Benchmark of IVF variants - -This is a benchmark of IVF index variants, looking at compression vs. speed vs. accuracy. -The results are in [this wiki chapter](https://github.com/facebookresearch/faiss/wiki/Indexing-1G-vectors) - - -The code is organized as: - -- `datasets.py`: code to access the datafiles, compute the ground-truth and report accuracies - -- `bench_all_ivf.py`: evaluate one type of inverted file - -- `run_on_cluster_generic.bash`: call `bench_all_ivf.py` for all tested types of indices. -Since the number of experiments is quite large the script is structued so that the benchmark can be run on a cluster. - -- `parse_bench_all_ivf.py`: make nice tradeoff plots from all the results. - -The code depends on Faiss and can use 1 to 8 GPUs to do the k-means clustering for large vocabularies. - -It was run in October 2018 for the results in the wiki. diff --git a/core/src/index/thirdparty/faiss/benchs/bench_all_ivf/bench_all_ivf.py b/core/src/index/thirdparty/faiss/benchs/bench_all_ivf/bench_all_ivf.py deleted file mode 100644 index ee53018828..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/bench_all_ivf/bench_all_ivf.py +++ /dev/null @@ -1,308 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#!/usr/bin/env python2 - -import os -import sys -import time -import numpy as np -import faiss -import argparse -import datasets -from datasets import sanitize - -###################################################### -# Command-line parsing -###################################################### - - -parser = argparse.ArgumentParser() - -def aa(*args, **kwargs): - group.add_argument(*args, **kwargs) - - -group = parser.add_argument_group('dataset options') - -aa('--db', default='deep1M', help='dataset') -aa('--compute_gt', default=False, action='store_true', - help='compute and store the groundtruth') - -group = parser.add_argument_group('index consturction') - -aa('--indexkey', default='HNSW32', help='index_factory type') -aa('--efConstruction', default=200, type=int, - help='HNSW construction factor') -aa('--M0', default=-1, type=int, help='size of base level') -aa('--maxtrain', default=256 * 256, type=int, - help='maximum number of training points (0 to set automatically)') -aa('--indexfile', default='', help='file to read or write index from') -aa('--add_bs', default=-1, type=int, - help='add elements index by batches of this size') -aa('--no_precomputed_tables', action='store_true', default=False, - help='disable precomputed tables (uses less memory)') -aa('--clustering_niter', default=-1, type=int, - help='number of clustering iterations (-1 = leave default)') -aa('--train_on_gpu', default=False, action='store_true', - help='do training on GPU') -aa('--get_centroids_from', default='', - help='get the centroids from this index (to speed up training)') - -group = parser.add_argument_group('searching') - -aa('--k', default=100, type=int, help='nb of nearest neighbors') -aa('--searchthreads', default=-1, type=int, - help='nb of threads to use at search time') -aa('--searchparams', nargs='+', default=['autotune'], - help="search parameters to use (can be autotune or a list of params)") -aa('--n_autotune', default=500, type=int, - help="max nb of autotune experiments") -aa('--autotune_max', default=[], nargs='*', - help='set max value for autotune variables format "var:val" (exclusive)') -aa('--autotune_range', default=[], nargs='*', - help='set complete autotune range, format "var:val1,val2,..."') -aa('--min_test_duration', default=0, type=float, - help='run test at least for so long to avoid jitter') - -args = parser.parse_args() - -print("args:", args) - -os.system('echo -n "nb processors "; ' - 'cat /proc/cpuinfo | grep ^processor | wc -l; ' - 'cat /proc/cpuinfo | grep ^"model name" | tail -1') - -###################################################### -# Load dataset -###################################################### - -xt, xb, xq, gt = datasets.load_data( - dataset=args.db, compute_gt=args.compute_gt) - - -print("dataset sizes: train %s base %s query %s GT %s" % ( - xt.shape, xb.shape, xq.shape, gt.shape)) - -nq, d = xq.shape -nb, d = xb.shape - - -###################################################### -# Make index -###################################################### - -if args.indexfile and os.path.exists(args.indexfile): - - print("reading", args.indexfile) - index = faiss.read_index(args.indexfile) - - if isinstance(index, faiss.IndexPreTransform): - index_ivf = faiss.downcast_index(index.index) - else: - index_ivf = index - assert isinstance(index_ivf, faiss.IndexIVF) - vec_transform = lambda x: x - assert isinstance(index_ivf, faiss.IndexIVF) - -else: - - print("build index, key=", args.indexkey) - - index = faiss.index_factory(d, args.indexkey) - - if isinstance(index, faiss.IndexPreTransform): - index_ivf = faiss.downcast_index(index.index) - vec_transform = index.chain.at(0).apply_py - else: - index_ivf = index - vec_transform = lambda x:x - assert isinstance(index_ivf, faiss.IndexIVF) - index_ivf.verbose = True - index_ivf.quantizer.verbose = True - index_ivf.cp.verbose = True - - maxtrain = args.maxtrain - if maxtrain == 0: - if 'IMI' in args.indexkey: - maxtrain = int(256 * 2 ** (np.log2(index_ivf.nlist) / 2)) - else: - maxtrain = 50 * index_ivf.nlist - print("setting maxtrain to %d" % maxtrain) - args.maxtrain = maxtrain - - xt2 = sanitize(xt[:args.maxtrain]) - assert np.all(np.isfinite(xt2)) - - print("train, size", xt2.shape) - - if args.get_centroids_from == '': - - if args.clustering_niter >= 0: - print(("setting nb of clustering iterations to %d" % - args.clustering_niter)) - index_ivf.cp.niter = args.clustering_niter - - if args.train_on_gpu: - print("add a training index on GPU") - train_index = faiss.index_cpu_to_all_gpus(faiss.IndexFlatL2(d)) - index_ivf.clustering_index = train_index - - else: - print("Getting centroids from", args.get_centroids_from) - src_index = faiss.read_index(args.get_centroids_from) - src_quant = faiss.downcast_index(src_index.quantizer) - centroids = faiss.vector_to_array(src_quant.xb) - centroids = centroids.reshape(-1, d) - print(" centroid table shape", centroids.shape) - - if isinstance(index, faiss.IndexPreTransform): - print(" training vector transform") - assert index.chain.size() == 1 - vt = index.chain.at(0) - vt.train(xt2) - print(" transform centroids") - centroids = vt.apply_py(centroids) - - print(" add centroids to quantizer") - index_ivf.quantizer.add(centroids) - del src_index - - t0 = time.time() - index.train(xt2) - print(" train in %.3f s" % (time.time() - t0)) - - print("adding") - t0 = time.time() - if args.add_bs == -1: - index.add(sanitize(xb)) - else: - for i0 in range(0, nb, args.add_bs): - i1 = min(nb, i0 + args.add_bs) - print(" adding %d:%d / %d" % (i0, i1, nb)) - index.add(sanitize(xb[i0:i1])) - - print(" add in %.3f s" % (time.time() - t0)) - if args.indexfile: - print("storing", args.indexfile) - faiss.write_index(index, args.indexfile) - -if args.no_precomputed_tables: - if isinstance(index_ivf, faiss.IndexIVFPQ): - print("disabling precomputed table") - index_ivf.use_precomputed_table = -1 - index_ivf.precomputed_table.clear() - -if args.indexfile: - print("index size on disk: ", os.stat(args.indexfile).st_size) - -print("current RSS:", faiss.get_mem_usage_kb() * 1024) - -precomputed_table_size = 0 -if hasattr(index_ivf, 'precomputed_table'): - precomputed_table_size = index_ivf.precomputed_table.size() * 4 - -print("precomputed tables size:", precomputed_table_size) - - -############################################################# -# Index is ready -############################################################# - -xq = sanitize(xq) - -if args.searchthreads != -1: - print("Setting nb of threads to", args.searchthreads) - faiss.omp_set_num_threads(args.searchthreads) - - -ps = faiss.ParameterSpace() -ps.initialize(index) - - -parametersets = args.searchparams - -header = '%-40s R@1 R@10 R@100 time(ms/q) nb distances #runs' % "parameters" - - -def eval_setting(index, xq, gt, min_time): - nq = xq.shape[0] - ivf_stats = faiss.cvar.indexIVF_stats - ivf_stats.reset() - nrun = 0 - t0 = time.time() - while True: - D, I = index.search(xq, 100) - nrun += 1 - t1 = time.time() - if t1 - t0 > min_time: - break - ms_per_query = ((t1 - t0) * 1000.0 / nq / nrun) - for rank in 1, 10, 100: - n_ok = (I[:, :rank] == gt[:, :1]).sum() - print("%.4f" % (n_ok / float(nq)), end=' ') - print(" %8.3f " % ms_per_query, end=' ') - print("%12d " % (ivf_stats.ndis / nrun), end=' ') - print(nrun) - - -if parametersets == ['autotune']: - - ps.n_experiments = args.n_autotune - ps.min_test_duration = args.min_test_duration - - for kv in args.autotune_max: - k, vmax = kv.split(':') - vmax = float(vmax) - print("limiting %s to %g" % (k, vmax)) - pr = ps.add_range(k) - values = faiss.vector_to_array(pr.values) - values = np.array([v for v in values if v < vmax]) - faiss.copy_array_to_vector(values, pr.values) - - for kv in args.autotune_range: - k, vals = kv.split(':') - vals = np.fromstring(vals, sep=',') - print("setting %s to %s" % (k, vals)) - pr = ps.add_range(k) - faiss.copy_array_to_vector(vals, pr.values) - - # setup the Criterion object: optimize for 1-R@1 - crit = faiss.OneRecallAtRCriterion(nq, 1) - - # by default, the criterion will request only 1 NN - crit.nnn = 100 - crit.set_groundtruth(None, gt.astype('int64')) - - # then we let Faiss find the optimal parameters by itself - print("exploring operating points") - ps.display() - - t0 = time.time() - op = ps.explore(index, xq, crit) - print("Done in %.3f s, available OPs:" % (time.time() - t0)) - - op.display() - - print(header) - opv = op.optimal_pts - for i in range(opv.size()): - opt = opv.at(i) - - ps.set_index_parameters(index, opt.key) - - print("%-40s " % opt.key, end=' ') - sys.stdout.flush() - - eval_setting(index, xq, gt, args.min_test_duration) - -else: - print(header) - for param in parametersets: - print("%-40s " % param, end=' ') - sys.stdout.flush() - ps.set_index_parameters(index, param) - - eval_setting(index, xq, gt, args.min_test_duration) diff --git a/core/src/index/thirdparty/faiss/benchs/bench_all_ivf/bench_kmeans.py b/core/src/index/thirdparty/faiss/benchs/bench_all_ivf/bench_kmeans.py deleted file mode 100644 index 90cb4e83d9..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/bench_all_ivf/bench_kmeans.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#!/usr/bin/env python2 - -from __future__ import print_function -import os -import numpy as np -import faiss -import argparse -import datasets -from datasets import sanitize - -###################################################### -# Command-line parsing -###################################################### - -parser = argparse.ArgumentParser() - - -def aa(*args, **kwargs): - group.add_argument(*args, **kwargs) - - -group = parser.add_argument_group('dataset options') - -aa('--db', default='deep1M', help='dataset') -aa('--nt', default=65536, type=int) -aa('--nb', default=100000, type=int) -aa('--nt_sample', default=0, type=int) - -group = parser.add_argument_group('kmeans options') -aa('--k', default=256, type=int) -aa('--seed', default=12345, type=int) -aa('--pcadim', default=-1, type=int, help='PCA to this dimension') -aa('--niter', default=25, type=int) -aa('--eval_freq', default=100, type=int) - - -args = parser.parse_args() - -print("args:", args) - -os.system('echo -n "nb processors "; ' - 'cat /proc/cpuinfo | grep ^processor | wc -l; ' - 'cat /proc/cpuinfo | grep ^"model name" | tail -1') - -ngpu = faiss.get_num_gpus() -print("nb GPUs:", ngpu) - -###################################################### -# Load dataset -###################################################### - -xt, xb, xq, gt = datasets.load_data(dataset=args.db) - - -if args.nt_sample == 0: - xt_pca = xt[args.nt:args.nt + 10000] - xt = xt[:args.nt] -else: - xt_pca = xt[args.nt_sample:args.nt_sample + 10000] - rs = np.random.RandomState(args.seed) - idx = rs.choice(args.nt_sample, size=args.nt, replace=False) - xt = xt[idx] - -xb = xb[:args.nb] - -d = xb.shape[1] - -if args.pcadim != -1: - print("training PCA: %d -> %d" % (d, args.pcadim)) - pca = faiss.PCAMatrix(d, args.pcadim) - pca.train(sanitize(xt_pca)) - xt = pca.apply_py(sanitize(xt)) - xb = pca.apply_py(sanitize(xb)) - d = xb.shape[1] - - -###################################################### -# Run clustering -###################################################### - - -index = faiss.IndexFlatL2(d) - -if ngpu > 0: - print("moving index to GPU") - index = faiss.index_cpu_to_all_gpus(index) - - -clustering = faiss.Clustering(d, args.k) - -clustering.verbose = True -clustering.seed = args.seed -clustering.max_points_per_centroid = 10**6 -clustering.min_points_per_centroid = 1 - - -for iter0 in range(0, args.niter, args.eval_freq): - iter1 = min(args.niter, iter0 + args.eval_freq) - clustering.niter = iter1 - iter0 - - if iter0 > 0: - faiss.copy_array_to_vector(centroids.ravel(), clustering.centroids) - - clustering.train(sanitize(xt), index) - index.reset() - centroids = faiss.vector_to_array(clustering.centroids).reshape(args.k, d) - index.add(centroids) - - _, I = index.search(sanitize(xb), 1) - - error = ((xb - centroids[I.ravel()]) ** 2).sum() - - print("iter1=%d quantization error on test: %.4f" % (iter1, error)) diff --git a/core/src/index/thirdparty/faiss/benchs/bench_all_ivf/datasets.py b/core/src/index/thirdparty/faiss/benchs/bench_all_ivf/datasets.py deleted file mode 100644 index 9f90643217..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/bench_all_ivf/datasets.py +++ /dev/null @@ -1,235 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#! /usr/bin/env python2 - -""" -Common functions to load datasets and compute their ground-truth -""" - -from __future__ import print_function -import time -import numpy as np -import faiss -import sys - -# set this to the directory that contains the datafiles. -# deep1b data should be at simdir + 'deep1b' -# bigann data should be at simdir + 'bigann' -simdir = '/mnt/vol/gfsai-east/ai-group/datasets/simsearch/' - -################################################################# -# Small I/O functions -################################################################# - - -def ivecs_read(fname): - a = np.fromfile(fname, dtype='int32') - d = a[0] - return a.reshape(-1, d + 1)[:, 1:].copy() - - -def fvecs_read(fname): - return ivecs_read(fname).view('float32') - - -def ivecs_mmap(fname): - a = np.memmap(fname, dtype='int32', mode='r') - d = a[0] - return a.reshape(-1, d + 1)[:, 1:] - - -def fvecs_mmap(fname): - return ivecs_mmap(fname).view('float32') - - -def bvecs_mmap(fname): - x = np.memmap(fname, dtype='uint8', mode='r') - d = x[:4].view('int32')[0] - return x.reshape(-1, d + 4)[:, 4:] - - -def ivecs_write(fname, m): - n, d = m.shape - m1 = np.empty((n, d + 1), dtype='int32') - m1[:, 0] = d - m1[:, 1:] = m - m1.tofile(fname) - - -def fvecs_write(fname, m): - m = m.astype('float32') - ivecs_write(fname, m.view('int32')) - - - -################################################################# -# Dataset -################################################################# - -def sanitize(x): - return np.ascontiguousarray(x, dtype='float32') - - -class ResultHeap: - """ Combine query results from a sliced dataset """ - - def __init__(self, nq, k): - " nq: number of query vectors, k: number of results per query " - self.I = np.zeros((nq, k), dtype='int64') - self.D = np.zeros((nq, k), dtype='float32') - self.nq, self.k = nq, k - heaps = faiss.float_maxheap_array_t() - heaps.k = k - heaps.nh = nq - heaps.val = faiss.swig_ptr(self.D) - heaps.ids = faiss.swig_ptr(self.I) - heaps.heapify() - self.heaps = heaps - - def add_batch_result(self, D, I, i0): - assert D.shape == (self.nq, self.k) - assert I.shape == (self.nq, self.k) - I += i0 - self.heaps.addn_with_ids( - self.k, faiss.swig_ptr(D), - faiss.swig_ptr(I), self.k) - - def finalize(self): - self.heaps.reorder() - - -def compute_GT_sliced(xb, xq, k): - print("compute GT") - t0 = time.time() - nb, d = xb.shape - nq, d = xq.shape - rh = ResultHeap(nq, k) - bs = 10 ** 5 - - xqs = sanitize(xq) - - db_gt = faiss.index_cpu_to_all_gpus(faiss.IndexFlatL2(d)) - - # compute ground-truth by blocks of bs, and add to heaps - for i0 in range(0, nb, bs): - i1 = min(nb, i0 + bs) - xsl = sanitize(xb[i0:i1]) - db_gt.add(xsl) - D, I = db_gt.search(xqs, k) - rh.add_batch_result(D, I, i0) - db_gt.reset() - print("\r %d/%d, %.3f s" % (i0, nb, time.time() - t0), end=' ') - sys.stdout.flush() - print() - rh.finalize() - gt_I = rh.I - - print("GT time: %.3f s" % (time.time() - t0)) - return gt_I - - -def do_compute_gt(xb, xq, k): - print("computing GT") - nb, d = xb.shape - index = faiss.index_cpu_to_all_gpus(faiss.IndexFlatL2(d)) - if nb < 100 * 1000: - print(" add") - index.add(np.ascontiguousarray(xb, dtype='float32')) - print(" search") - D, I = index.search(np.ascontiguousarray(xq, dtype='float32'), k) - else: - I = compute_GT_sliced(xb, xq, k) - - return I.astype('int32') - - -def load_data(dataset='deep1M', compute_gt=False): - - print("load data", dataset) - - if dataset == 'sift1M': - basedir = simdir + 'sift1M/' - - xt = fvecs_read(basedir + "sift_learn.fvecs") - xb = fvecs_read(basedir + "sift_base.fvecs") - xq = fvecs_read(basedir + "sift_query.fvecs") - gt = ivecs_read(basedir + "sift_groundtruth.ivecs") - - elif dataset.startswith('bigann'): - basedir = simdir + 'bigann/' - - dbsize = 1000 if dataset == "bigann1B" else int(dataset[6:-1]) - xb = bvecs_mmap(basedir + 'bigann_base.bvecs') - xq = bvecs_mmap(basedir + 'bigann_query.bvecs') - xt = bvecs_mmap(basedir + 'bigann_learn.bvecs') - # trim xb to correct size - xb = xb[:dbsize * 1000 * 1000] - gt = ivecs_read(basedir + 'gnd/idx_%dM.ivecs' % dbsize) - - elif dataset.startswith("deep"): - basedir = simdir + 'deep1b/' - szsuf = dataset[4:] - if szsuf[-1] == 'M': - dbsize = 10 ** 6 * int(szsuf[:-1]) - elif szsuf == '1B': - dbsize = 10 ** 9 - elif szsuf[-1] == 'k': - dbsize = 1000 * int(szsuf[:-1]) - else: - assert False, "did not recognize suffix " + szsuf - - xt = fvecs_mmap(basedir + "learn.fvecs") - xb = fvecs_mmap(basedir + "base.fvecs") - xq = fvecs_read(basedir + "deep1B_queries.fvecs") - - xb = xb[:dbsize] - - gt_fname = basedir + "%s_groundtruth.ivecs" % dataset - if compute_gt: - gt = do_compute_gt(xb, xq, 100) - print("store", gt_fname) - ivecs_write(gt_fname, gt) - - gt = ivecs_read(gt_fname) - - else: - assert False - - print("dataset %s sizes: B %s Q %s T %s" % ( - dataset, xb.shape, xq.shape, xt.shape)) - - return xt, xb, xq, gt - -################################################################# -# Evaluation -################################################################# - - -def evaluate_DI(D, I, gt): - nq = gt.shape[0] - k = I.shape[1] - rank = 1 - while rank <= k: - recall = (I[:, :rank] == gt[:, :1]).sum() / float(nq) - print("R@%d: %.4f" % (rank, recall), end=' ') - rank *= 10 - - -def evaluate(xq, gt, index, k=100, endl=True): - t0 = time.time() - D, I = index.search(xq, k) - t1 = time.time() - nq = xq.shape[0] - print("\t %8.4f ms per query, " % ( - (t1 - t0) * 1000.0 / nq), end=' ') - rank = 1 - while rank <= k: - recall = (I[:, :rank] == gt[:, :1]).sum() / float(nq) - print("R@%d: %.4f" % (rank, recall), end=' ') - rank *= 10 - if endl: - print() - return D, I diff --git a/core/src/index/thirdparty/faiss/benchs/bench_all_ivf/parse_bench_all_ivf.py b/core/src/index/thirdparty/faiss/benchs/bench_all_ivf/parse_bench_all_ivf.py deleted file mode 100644 index 1a4d260ea5..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/bench_all_ivf/parse_bench_all_ivf.py +++ /dev/null @@ -1,268 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#! /usr/bin/env python2 - -import os -import numpy as np -from matplotlib import pyplot - -import re - -from argparse import Namespace - - -# the directory used in run_on_cluster.bash -basedir = '/mnt/vol/gfsai-east/ai-group/users/matthijs/bench_all_ivf/' -logdir = basedir + 'logs/' - - -# which plot to output -db = 'bigann1B' -code_size = 8 - - - -def unitsize(indexkey): - """ size of one vector in the index """ - mo = re.match('.*,PQ(\\d+)', indexkey) - if mo: - return int(mo.group(1)) - if indexkey.endswith('SQ8'): - bits_per_d = 8 - elif indexkey.endswith('SQ4'): - bits_per_d = 4 - elif indexkey.endswith('SQfp16'): - bits_per_d = 16 - else: - assert False - mo = re.match('PCAR(\\d+),.*', indexkey) - if mo: - return bits_per_d * int(mo.group(1)) / 8 - mo = re.match('OPQ\\d+_(\\d+),.*', indexkey) - if mo: - return bits_per_d * int(mo.group(1)) / 8 - mo = re.match('RR(\\d+),.*', indexkey) - if mo: - return bits_per_d * int(mo.group(1)) / 8 - assert False - - -def dbsize_from_name(dbname): - sufs = { - '1B': 10**9, - '100M': 10**8, - '10M': 10**7, - '1M': 10**6, - } - for s in sufs: - if dbname.endswith(s): - return sufs[s] - else: - assert False - - -def keep_latest_stdout(fnames): - fnames = [fname for fname in fnames if fname.endswith('.stdout')] - fnames.sort() - n = len(fnames) - fnames2 = [] - for i, fname in enumerate(fnames): - if i + 1 < n and fnames[i + 1][:-8] == fname[:-8]: - continue - fnames2.append(fname) - return fnames2 - - -def parse_result_file(fname): - # print fname - st = 0 - res = [] - keys = [] - stats = {} - stats['run_version'] = fname[-8] - for l in open(fname): - if st == 0: - if l.startswith('CHRONOS_JOB_INSTANCE_ID'): - stats['CHRONOS_JOB_INSTANCE_ID'] = l.split()[-1] - if l.startswith('index size on disk:'): - stats['index_size'] = int(l.split()[-1]) - if l.startswith('current RSS:'): - stats['RSS'] = int(l.split()[-1]) - if l.startswith('precomputed tables size:'): - stats['tables_size'] = int(l.split()[-1]) - if l.startswith('Setting nb of threads to'): - stats['n_threads'] = int(l.split()[-1]) - if l.startswith(' add in'): - stats['add_time'] = float(l.split()[-2]) - if l.startswith('args:'): - args = eval(l[l.find(' '):]) - indexkey = args.indexkey - elif 'R@1 R@10 R@100' in l: - st = 1 - elif 'index size on disk:' in l: - index_size = int(l.split()[-1]) - elif st == 1: - st = 2 - elif st == 2: - fi = l.split() - keys.append(fi[0]) - res.append([float(x) for x in fi[1:]]) - return indexkey, np.array(res), keys, stats - -# run parsing -allres = {} -allstats = {} -nts = [] -missing = [] -versions = {} - -fnames = keep_latest_stdout(os.listdir(logdir)) -# print fnames -# filenames are in the form .x.stdout -# where x is a version number (from a to z) -# keep only latest version of each name - -for fname in fnames: - if not ('db' + db in fname and fname.endswith('.stdout')): - continue - indexkey, res, _, stats = parse_result_file(logdir + fname) - if res.size == 0: - missing.append(fname) - errorline = open( - logdir + fname.replace('.stdout', '.stderr')).readlines() - if len(errorline) > 0: - errorline = errorline[-1] - else: - errorline = 'NO STDERR' - print fname, stats['CHRONOS_JOB_INSTANCE_ID'], errorline - - else: - if indexkey in allres: - if allstats[indexkey]['run_version'] > stats['run_version']: - # don't use this run - continue - n_threads = stats.get('n_threads', 1) - nts.append(n_threads) - allres[indexkey] = res - allstats[indexkey] = stats - -assert len(set(nts)) == 1 -n_threads = nts[0] - - -def plot_tradeoffs(allres, code_size, recall_rank): - dbsize = dbsize_from_name(db) - recall_idx = int(np.log10(recall_rank)) - - bigtab = [] - names = [] - - for k,v in sorted(allres.items()): - if v.ndim != 2: continue - us = unitsize(k) - if us != code_size: continue - perf = v[:, recall_idx] - times = v[:, 3] - bigtab.append( - np.vstack(( - np.ones(times.size, dtype=int) * len(names), - perf, times - )) - ) - names.append(k) - - bigtab = np.hstack(bigtab) - - perm = np.argsort(bigtab[1, :]) - bigtab = bigtab[:, perm] - - times = np.minimum.accumulate(bigtab[2, ::-1])[::-1] - selection = np.where(bigtab[2, :] == times) - - selected_methods = [names[i] for i in - np.unique(bigtab[0, selection].astype(int))] - not_selected = list(set(names) - set(selected_methods)) - - print "methods without an optimal OP: ", not_selected - - nq = 10000 - pyplot.title('database ' + db + ' code_size=%d' % code_size) - - # grayed out lines - - for k in not_selected: - v = allres[k] - if v.ndim != 2: continue - us = unitsize(k) - if us != code_size: continue - - linestyle = (':' if 'PQ' in k else - '-.' if 'SQ4' in k else - '--' if 'SQ8' in k else '-') - - pyplot.semilogy(v[:, recall_idx], v[:, 3], label=None, - linestyle=linestyle, - marker='o' if 'HNSW' in k else '+', - color='#cccccc', linewidth=0.2) - - # important methods - for k in selected_methods: - v = allres[k] - if v.ndim != 2: continue - us = unitsize(k) - if us != code_size: continue - - stats = allstats[k] - tot_size = stats['index_size'] + stats['tables_size'] - id_size = 8 # 64 bit - - addt = '' - if 'add_time' in stats: - add_time = stats['add_time'] - if add_time > 7200: - add_min = add_time / 60 - addt = ', %dh%02d' % (add_min / 60, add_min % 60) - else: - add_sec = int(add_time) - addt = ', %dm%02d' % (add_sec / 60, add_sec % 60) - - - label = k + ' (size+%.1f%%%s)' % ( - tot_size / float((code_size + id_size) * dbsize) * 100 - 100, - addt) - - linestyle = (':' if 'PQ' in k else - '-.' if 'SQ4' in k else - '--' if 'SQ8' in k else '-') - - pyplot.semilogy(v[:, recall_idx], v[:, 3], label=label, - linestyle=linestyle, - marker='o' if 'HNSW' in k else '+') - - if len(not_selected) == 0: - om = '' - else: - om = '\nomitted:' - nc = len(om) - for m in not_selected: - if nc > 80: - om += '\n' - nc = 0 - om += ' ' + m - nc += len(m) + 1 - - pyplot.xlabel('1-recall at %d %s' % (recall_rank, om) ) - pyplot.ylabel('search time per query (ms, %d threads)' % n_threads) - pyplot.legend() - pyplot.grid() - pyplot.savefig('figs/tradeoffs_%s_cs%d_r%d.png' % ( - db, code_size, recall_rank)) - return selected_methods, not_selected - - -pyplot.gcf().set_size_inches(15, 10) - -plot_tradeoffs(allres, code_size=code_size, recall_rank=1) diff --git a/core/src/index/thirdparty/faiss/benchs/bench_all_ivf/run_on_cluster_generic.bash b/core/src/index/thirdparty/faiss/benchs/bench_all_ivf/run_on_cluster_generic.bash deleted file mode 100644 index 6d88f43d9a..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/bench_all_ivf/run_on_cluster_generic.bash +++ /dev/null @@ -1,249 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -# @nolint - -# This script launches the experiments on a cluster -# It assumes two shell functions are defined: -# -# run_on_1machine: runs a command on one (full) machine on a cluster -# -# run_on_8gpu: runs a command on one machine with 8 GPUs -# -# the two functions are called as: -# -# run_on_1machine -# -# the stdout of the command should be stored in $logdir/.stdout - -function run_on_1machine () { - # To be implemented -} - -function run_on_8gpu () { - # To be implemented -} - - -# prepare output directories -# set to some directory where all indexes, can be written. -basedir=XXXXX - -logdir=$basedir/logs -indexdir=$basedir/indexes - -mkdir -p $lars $logdir $indexdir - - -############################### 1M experiments - -for db in sift1M deep1M bigann1M; do - - for coarse in IMI2x9 IMI2x10 IVF1024_HNSW32 IVF4096_HNSW32 IVF16384_HNSW32 - do - - for indexkey in \ - OPQ8_64,$coarse,PQ8 \ - PCAR16,$coarse,SQ4 \ - OPQ16_64,$coarse,PQ16 \ - PCAR32,$coarse,SQ4 \ - PCAR16,$coarse,SQ8 \ - OPQ32_128,$coarse,PQ32 \ - PCAR64,$coarse,SQ4 \ - PCAR32,$coarse,SQ8 \ - PCAR16,$coarse,SQfp16 \ - PCAR64,$coarse,SQ8 \ - PCAR32,$coarse,SQfp16 \ - PCAR128,$coarse,SQ4 - do - key=autotune.db$db.${indexkey//,/_} - run_on_1machine $key \ - python -u bench_all_ivf.py \ - --db $db \ - --indexkey $indexkey \ - --maxtrain 0 \ - --indexfile $indexdir/$key.faissindex - - done - done -done - - - -############################### 10M experiments - - -for db in deep10M bigann10M; do - - for coarse in \ - IMI2x10 IMI2x11 IMI2x12 IMI2x13 IVF4096_HNSW32 \ - IVF16384_HNSW32 IVF65536_HNSW32 IVF262144_HNSW32 - do - - for indexkey in \ - OPQ8_64,$coarse,PQ8 \ - PCAR16,$coarse,SQ4 \ - OPQ16_64,$coarse,PQ16 \ - PCAR32,$coarse,SQ4 \ - PCAR16,$coarse,SQ8 \ - OPQ32_128,$coarse,PQ32 \ - PCAR64,$coarse,SQ4 \ - PCAR32,$coarse,SQ8 \ - PCAR16,$coarse,SQfp16 \ - PCAR64,$coarse,SQ8 \ - PCAR32,$coarse,SQfp16 \ - PCAR128,$coarse,SQ4 \ - OPQ64_128,$coarse,PQ64 - do - key=autotune.db$db.${indexkey//,/_} - run_on_1machine $key \ - python -u bench_all_ivf.py \ - --db $db \ - --indexkey $indexkey \ - --maxtrain 0 \ - --indexfile $indexdir/$key.faissindex \ - --searchthreads 16 \ - --min_test_duration 3 \ - - done - done -done - - -############################### 100M experiments - -for db in deep100M bigann100M; do - - for coarse in IMI2x11 IMI2x12 IVF65536_HNSW32 IVF262144_HNSW32 - do - - for indexkey in \ - OPQ8_64,$coarse,PQ8 \ - OPQ16_64,$coarse,PQ16 \ - PCAR32,$coarse,SQ4 \ - OPQ32_128,$coarse,PQ32 \ - PCAR64,$coarse,SQ4 \ - PCAR32,$coarse,SQ8 \ - PCAR64,$coarse,SQ8 \ - PCAR32,$coarse,SQfp16 \ - PCAR128,$coarse,SQ4 \ - OPQ64_128,$coarse,PQ64 - do - key=autotune.db$db.${indexkey//,/_} - run_on_1machine $key \ - python -u bench_all_ivf.py \ - --db $db \ - --indexkey $indexkey \ - --maxtrain 0 \ - --indexfile $indexdir/$key.faissindex \ - --searchthreads 16 \ - --min_test_duration 3 \ - --add_bs 1000000 - - done - done -done - - -############################### 1B experiments - -for db in deep1B bigann1B; do - - for coarse in IMI2x12 IMI2x13 IVF262144_HNSW32 - do - - for indexkey in \ - OPQ8_64,$coarse,PQ8 \ - OPQ16_64,$coarse,PQ16 \ - PCAR32,$coarse,SQ4 \ - OPQ32_128,$coarse,PQ32 \ - PCAR64,$coarse,SQ4 \ - PCAR32,$coarse,SQ8 \ - PCAR64,$coarse,SQ8 \ - PCAR32,$coarse,SQfp16 \ - PCAR128,$coarse,SQ4 \ - PQ64_128,$coarse,PQ64 \ - RR128,$coarse,SQ4 - do - key=autotune.db$db.${indexkey//,/_} - run_on_1machine $key \ - python -u bench_all_ivf.py \ - --db $db \ - --indexkey $indexkey \ - --maxtrain 0 \ - --indexfile $indexdir/$key.faissindex \ - --searchthreads 16 \ - --min_test_duration 3 \ - --add_bs 1000000 - - done - done - -done - -############################################ -# precompute centroids on GPU for large vocabularies - - -for db in deep1M bigann1M; do - - for ncent in 1048576 4194304; do - - key=clustering.db$db.IVF$ncent - run_on_8gpu $key \ - python -u bench_all_ivf.py \ - --db $db \ - --indexkey IVF$ncent,SQ8 \ - --maxtrain 100000000 \ - --indexfile $indexdir/$key.faissindex \ - --searchthreads 16 \ - --min_test_duration 3 \ - --add_bs 1000000 \ - --train_on_gpu - - done -done - - -################################# -# Run actual experiment - -for db in deep1B bigann1B; do - - for ncent in 1048576 4194304; do - coarse=IVF${ncent}_HNSW32 - centroidsname=clustering.db${db/1B/1M}.IVF${ncent}.faissindex - - for indexkey in \ - OPQ8_64,$coarse,PQ8 \ - OPQ16_64,$coarse,PQ16 \ - PCAR32,$coarse,SQ4 \ - OPQ32_128,$coarse,PQ32 \ - PCAR64,$coarse,SQ4 \ - PCAR32,$coarse,SQ8 \ - PCAR64,$coarse,SQ8 \ - PCAR32,$coarse,SQfp16 \ - OPQ64_128,$coarse,PQ64 \ - RR128,$coarse,SQ4 \ - OPQ64_128,$coarse,PQ64 \ - RR128,$coarse,SQ4 - do - key=autotune.db$db.${indexkey//,/_} - - run_on_1machine $key.c $key \ - python -u bench_all_ivf.py \ - --db $db \ - --indexkey $indexkey \ - --maxtrain 256000 \ - --indexfile $indexdir/$key.faissindex \ - --get_centroids_from $indexdir/$centroidsname \ - --searchthreads 16 \ - --min_test_duration 3 \ - --add_bs 1000000 - - done - done - -done diff --git a/core/src/index/thirdparty/faiss/benchs/bench_for_interrupt.py b/core/src/index/thirdparty/faiss/benchs/bench_for_interrupt.py deleted file mode 100644 index b72d825ef9..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/bench_for_interrupt.py +++ /dev/null @@ -1,155 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#! /usr/bin/env python3 - -from __future__ import print_function -import numpy as np -import faiss -import time -import os -import argparse - - -parser = argparse.ArgumentParser() - -def aa(*args, **kwargs): - group.add_argument(*args, **kwargs) - -group = parser.add_argument_group('dataset options') -aa('--dim', type=int, default=64) -aa('--nb', type=int, default=int(1e6)) -aa('--subset_len', type=int, default=int(1e5)) -aa('--key', default='IVF1000,Flat') -aa('--nprobe', type=int, default=640) -aa('--no_intcallback', default=False, action='store_true') -aa('--twostage', default=False, action='store_true') -aa('--nt', type=int, default=-1) - - -args = parser.parse_args() -print("args:", args) - - -d = args.dim # dimension -nb = args.nb # database size -nq = 1000 # nb of queries -nt = 100000 -subset_len = args.subset_len - - -np.random.seed(1234) # make reproducible -xb = np.random.random((nb, d)).astype('float32') -xq = np.random.random((nq, d)).astype('float32') -xt = np.random.random((nt, d)).astype('float32') -k = 100 - -if args.no_intcallback: - faiss.InterruptCallback.clear_instance() - -if args.nt != -1: - faiss.omp_set_num_threads(args.nt) - -nprobe = args.nprobe -key = args.key -#key = 'IVF1000,Flat' -# key = 'IVF1000,PQ64' -# key = 'IVF100_HNSW32,PQ64' - -# faiss.omp_set_num_threads(1) - -pf = 'dim%d_' % d -if d == 64: - pf = '' - -basename = '/tmp/base%s%s.index' % (pf, key) - -if os.path.exists(basename): - print('load', basename) - index_1 = faiss.read_index(basename) -else: - print('train + write', basename) - index_1 = faiss.index_factory(d, key) - index_1.train(xt) - faiss.write_index(index_1, basename) - -print('add') -index_1.add(xb) - -print('set nprobe=', nprobe) -faiss.ParameterSpace().set_index_parameter(index_1, 'nprobe', nprobe) - -class ResultHeap: - """ Combine query results from a sliced dataset """ - - def __init__(self, nq, k): - " nq: number of query vectors, k: number of results per query " - self.I = np.zeros((nq, k), dtype='int64') - self.D = np.zeros((nq, k), dtype='float32') - self.nq, self.k = nq, k - heaps = faiss.float_maxheap_array_t() - heaps.k = k - heaps.nh = nq - heaps.val = faiss.swig_ptr(self.D) - heaps.ids = faiss.swig_ptr(self.I) - heaps.heapify() - self.heaps = heaps - - def add_batch_result(self, D, I, i0): - assert D.shape == (self.nq, self.k) - assert I.shape == (self.nq, self.k) - I += i0 - self.heaps.addn_with_ids( - self.k, faiss.swig_ptr(D), - faiss.swig_ptr(I), self.k) - - def finalize(self): - self.heaps.reorder() - -stats = faiss.cvar.indexIVF_stats -stats.reset() - -print('index size', index_1.ntotal, - 'imbalance', index_1.invlists.imbalance_factor()) -start = time.time() -Dref, Iref = index_1.search(xq, k) -print('time of searching: %.3f s = %.3f + %.3f ms' % ( - time.time() - start, stats.quantization_time, stats.search_time)) - -indexes = {} -if args.twostage: - - for i in range(0, nb, subset_len): - index = faiss.read_index(basename) - faiss.ParameterSpace().set_index_parameter(index, 'nprobe', nprobe) - print("add %d:%d" %(i, i+subset_len)) - index.add(xb[i:i + subset_len]) - indexes[i] = index - -rh = ResultHeap(nq, k) -sum_time = tq = ts = 0 -for i in range(0, nb, subset_len): - if not args.twostage: - index = faiss.read_index(basename) - faiss.ParameterSpace().set_index_parameter(index, 'nprobe', nprobe) - print("add %d:%d" %(i, i+subset_len)) - index.add(xb[i:i + subset_len]) - else: - index = indexes[i] - - stats.reset() - start = time.time() - Di, Ii = index.search(xq, k) - sum_time = sum_time + time.time() - start - tq += stats.quantization_time - ts += stats.search_time - rh.add_batch_result(Di, Ii, i) - -print('time of searching separately: %.3f s = %.3f + %.3f ms' % - (sum_time, tq, ts)) - -rh.finalize() - -print('diffs: %d / %d' % ((Iref != rh.I).sum(), Iref.size)) diff --git a/core/src/index/thirdparty/faiss/benchs/bench_gpu_1bn.py b/core/src/index/thirdparty/faiss/benchs/bench_gpu_1bn.py deleted file mode 100644 index c676f7c793..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/bench_gpu_1bn.py +++ /dev/null @@ -1,747 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#! /usr/bin/env python2 - -from __future__ import print_function -import numpy as np -import time -import os -import sys -import faiss -import re - -from multiprocessing.dummy import Pool as ThreadPool -from datasets import ivecs_read - -#################################################################### -# Parse command line -#################################################################### - - -def usage(): - print(""" - -Usage: bench_gpu_1bn.py dataset indextype [options] - -dataset: set of vectors to operate on. - Supported: SIFT1M, SIFT2M, ..., SIFT1000M or Deep1B - -indextype: any index type supported by index_factory that runs on GPU. - - General options - --ngpu ngpu nb of GPUs to use (default = all) --tempmem N use N bytes of temporary GPU memory --nocache do not read or write intermediate files --float16 use 16-bit floats on the GPU side - - Add options - --abs N split adds in blocks of no more than N vectors --max_add N copy sharded dataset to CPU each max_add additions - (to avoid memory overflows with geometric reallocations) --altadd Alternative add function, where the index is not stored - on GPU during add. Slightly faster for big datasets on - slow GPUs - - Search options - --R R: nb of replicas of the same dataset (the dataset - will be copied across ngpu/R, default R=1) --noptables do not use precomputed tables in IVFPQ. --qbs N split queries in blocks of no more than N vectors --nnn N search N neighbors for each query --nprobe 4,16,64 try this number of probes --knngraph instead of the standard setup for the dataset, - compute a k-nn graph with nnn neighbors per element --oI xx%d.npy output the search result indices to this numpy file, - %d will be replaced with the nprobe --oD xx%d.npy output the search result distances to this file - -""", file=sys.stderr) - sys.exit(1) - - -# default values - -dbname = None -index_key = None - -ngpu = faiss.get_num_gpus() - -replicas = 1 # nb of replicas of sharded dataset -add_batch_size = 32768 -query_batch_size = 16384 -nprobes = [1 << l for l in range(9)] -knngraph = False -use_precomputed_tables = True -tempmem = -1 # if -1, use system default -max_add = -1 -use_float16 = False -use_cache = True -nnn = 10 -altadd = False -I_fname = None -D_fname = None - -args = sys.argv[1:] - -while args: - a = args.pop(0) - if a == '-h': usage() - elif a == '-ngpu': ngpu = int(args.pop(0)) - elif a == '-R': replicas = int(args.pop(0)) - elif a == '-noptables': use_precomputed_tables = False - elif a == '-abs': add_batch_size = int(args.pop(0)) - elif a == '-qbs': query_batch_size = int(args.pop(0)) - elif a == '-nnn': nnn = int(args.pop(0)) - elif a == '-tempmem': tempmem = int(args.pop(0)) - elif a == '-nocache': use_cache = False - elif a == '-knngraph': knngraph = True - elif a == '-altadd': altadd = True - elif a == '-float16': use_float16 = True - elif a == '-nprobe': nprobes = [int(x) for x in args.pop(0).split(',')] - elif a == '-max_add': max_add = int(args.pop(0)) - elif not dbname: dbname = a - elif not index_key: index_key = a - else: - print("argument %s unknown" % a, file=sys.stderr) - sys.exit(1) - -cacheroot = '/tmp/bench_gpu_1bn' - -if not os.path.isdir(cacheroot): - print("%s does not exist, creating it" % cacheroot) - os.mkdir(cacheroot) - -################################################################# -# Small Utility Functions -################################################################# - -# we mem-map the biggest files to avoid having them in memory all at -# once - -def mmap_fvecs(fname): - x = np.memmap(fname, dtype='int32', mode='r') - d = x[0] - return x.view('float32').reshape(-1, d + 1)[:, 1:] - -def mmap_bvecs(fname): - x = np.memmap(fname, dtype='uint8', mode='r') - d = x[:4].view('int32')[0] - return x.reshape(-1, d + 4)[:, 4:] - - -def rate_limited_imap(f, l): - """A threaded imap that does not produce elements faster than they - are consumed""" - pool = ThreadPool(1) - res = None - for i in l: - res_next = pool.apply_async(f, (i, )) - if res: - yield res.get() - res = res_next - yield res.get() - - -class IdentPreproc: - """a pre-processor is either a faiss.VectorTransform or an IndentPreproc""" - - def __init__(self, d): - self.d_in = self.d_out = d - - def apply_py(self, x): - return x - - -def sanitize(x): - """ convert array to a c-contiguous float array """ - return np.ascontiguousarray(x.astype('float32')) - - -def dataset_iterator(x, preproc, bs): - """ iterate over the lines of x in blocks of size bs""" - - nb = x.shape[0] - block_ranges = [(i0, min(nb, i0 + bs)) - for i0 in range(0, nb, bs)] - - def prepare_block(i01): - i0, i1 = i01 - xb = sanitize(x[i0:i1]) - return i0, preproc.apply_py(xb) - - return rate_limited_imap(prepare_block, block_ranges) - - -def eval_intersection_measure(gt_I, I): - """ measure intersection measure (used for knngraph)""" - inter = 0 - rank = I.shape[1] - assert gt_I.shape[1] >= rank - for q in range(nq_gt): - inter += faiss.ranklist_intersection_size( - rank, faiss.swig_ptr(gt_I[q, :]), - rank, faiss.swig_ptr(I[q, :].astype('int64'))) - return inter / float(rank * nq_gt) - - -################################################################# -# Prepare dataset -################################################################# - -print("Preparing dataset", dbname) - -if dbname.startswith('SIFT'): - # SIFT1M to SIFT1000M - dbsize = int(dbname[4:-1]) - xb = mmap_bvecs('bigann/bigann_base.bvecs') - xq = mmap_bvecs('bigann/bigann_query.bvecs') - xt = mmap_bvecs('bigann/bigann_learn.bvecs') - - # trim xb to correct size - xb = xb[:dbsize * 1000 * 1000] - - gt_I = ivecs_read('bigann/gnd/idx_%dM.ivecs' % dbsize) - -elif dbname == 'Deep1B': - xb = mmap_fvecs('deep1b/base.fvecs') - xq = mmap_fvecs('deep1b/deep1B_queries.fvecs') - xt = mmap_fvecs('deep1b/learn.fvecs') - # deep1B's train is is outrageously big - xt = xt[:10 * 1000 * 1000] - gt_I = ivecs_read('deep1b/deep1B_groundtruth.ivecs') - -else: - print('unknown dataset', dbname, file=sys.stderr) - sys.exit(1) - - -if knngraph: - # convert to knn-graph dataset - xq = xb - xt = xb - # we compute the ground-truth on this number of queries for validation - nq_gt = 10000 - gt_sl = 100 - - # ground truth will be computed below - gt_I = None - - -print("sizes: B %s Q %s T %s gt %s" % ( - xb.shape, xq.shape, xt.shape, - gt_I.shape if gt_I is not None else None)) - - - -################################################################# -# Parse index_key and set cache files -# -# The index_key is a valid factory key that would work, but we -# decompose the training to do it faster -################################################################# - - -pat = re.compile('(OPQ[0-9]+(_[0-9]+)?,|PCAR[0-9]+,)?' + - '(IVF[0-9]+),' + - '(PQ[0-9]+|Flat)') - -matchobject = pat.match(index_key) - -assert matchobject, 'could not parse ' + index_key - -mog = matchobject.groups() - -preproc_str = mog[0] -ivf_str = mog[2] -pqflat_str = mog[3] - -ncent = int(ivf_str[3:]) - -prefix = '' - -if knngraph: - gt_cachefile = '%s/BK_gt_%s.npy' % (cacheroot, dbname) - prefix = 'BK_' - # files must be kept distinct because the training set is not the - # same for the knngraph - -if preproc_str: - preproc_cachefile = '%s/%spreproc_%s_%s.vectrans' % ( - cacheroot, prefix, dbname, preproc_str[:-1]) -else: - preproc_cachefile = None - preproc_str = '' - -cent_cachefile = '%s/%scent_%s_%s%s.npy' % ( - cacheroot, prefix, dbname, preproc_str, ivf_str) - -index_cachefile = '%s/%s%s_%s%s,%s.index' % ( - cacheroot, prefix, dbname, preproc_str, ivf_str, pqflat_str) - - -if not use_cache: - preproc_cachefile = None - cent_cachefile = None - index_cachefile = None - -print("cachefiles:") -print(preproc_cachefile) -print(cent_cachefile) -print(index_cachefile) - - -################################################################# -# Wake up GPUs -################################################################# - -print("preparing resources for %d GPUs" % ngpu) - -gpu_resources = [] - -for i in range(ngpu): - res = faiss.StandardGpuResources() - if tempmem >= 0: - res.setTempMemory(tempmem) - gpu_resources.append(res) - - -def make_vres_vdev(i0=0, i1=-1): - " return vectors of device ids and resources useful for gpu_multiple" - vres = faiss.GpuResourcesVector() - vdev = faiss.IntVector() - if i1 == -1: - i1 = ngpu - for i in range(i0, i1): - vdev.push_back(i) - vres.push_back(gpu_resources[i]) - return vres, vdev - - -################################################################# -# Prepare ground truth (for the knngraph) -################################################################# - - -def compute_GT(): - print("compute GT") - t0 = time.time() - - gt_I = np.zeros((nq_gt, gt_sl), dtype='int64') - gt_D = np.zeros((nq_gt, gt_sl), dtype='float32') - heaps = faiss.float_maxheap_array_t() - heaps.k = gt_sl - heaps.nh = nq_gt - heaps.val = faiss.swig_ptr(gt_D) - heaps.ids = faiss.swig_ptr(gt_I) - heaps.heapify() - bs = 10 ** 5 - - n, d = xb.shape - xqs = sanitize(xq[:nq_gt]) - - db_gt = faiss.IndexFlatL2(d) - vres, vdev = make_vres_vdev() - db_gt_gpu = faiss.index_cpu_to_gpu_multiple( - vres, vdev, db_gt) - - # compute ground-truth by blocks of bs, and add to heaps - for i0, xsl in dataset_iterator(xb, IdentPreproc(d), bs): - db_gt_gpu.add(xsl) - D, I = db_gt_gpu.search(xqs, gt_sl) - I += i0 - heaps.addn_with_ids( - gt_sl, faiss.swig_ptr(D), faiss.swig_ptr(I), gt_sl) - db_gt_gpu.reset() - print("\r %d/%d, %.3f s" % (i0, n, time.time() - t0), end=' ') - print() - heaps.reorder() - - print("GT time: %.3f s" % (time.time() - t0)) - return gt_I - - -if knngraph: - - if gt_cachefile and os.path.exists(gt_cachefile): - print("load GT", gt_cachefile) - gt_I = np.load(gt_cachefile) - else: - gt_I = compute_GT() - if gt_cachefile: - print("store GT", gt_cachefile) - np.save(gt_cachefile, gt_I) - -################################################################# -# Prepare the vector transformation object (pure CPU) -################################################################# - - -def train_preprocessor(): - print("train preproc", preproc_str) - d = xt.shape[1] - t0 = time.time() - if preproc_str.startswith('OPQ'): - fi = preproc_str[3:-1].split('_') - m = int(fi[0]) - dout = int(fi[1]) if len(fi) == 2 else d - preproc = faiss.OPQMatrix(d, m, dout) - elif preproc_str.startswith('PCAR'): - dout = int(preproc_str[4:-1]) - preproc = faiss.PCAMatrix(d, dout, 0, True) - else: - assert False - preproc.train(sanitize(xt[:1000000])) - print("preproc train done in %.3f s" % (time.time() - t0)) - return preproc - - -def get_preprocessor(): - if preproc_str: - if not preproc_cachefile or not os.path.exists(preproc_cachefile): - preproc = train_preprocessor() - if preproc_cachefile: - print("store", preproc_cachefile) - faiss.write_VectorTransform(preproc, preproc_cachefile) - else: - print("load", preproc_cachefile) - preproc = faiss.read_VectorTransform(preproc_cachefile) - else: - d = xb.shape[1] - preproc = IdentPreproc(d) - return preproc - - -################################################################# -# Prepare the coarse quantizer -################################################################# - - -def train_coarse_quantizer(x, k, preproc): - d = preproc.d_out - clus = faiss.Clustering(d, k) - clus.verbose = True - # clus.niter = 2 - clus.max_points_per_centroid = 10000000 - - print("apply preproc on shape", x.shape, 'k=', k) - t0 = time.time() - x = preproc.apply_py(sanitize(x)) - print(" preproc %.3f s output shape %s" % ( - time.time() - t0, x.shape)) - - vres, vdev = make_vres_vdev() - index = faiss.index_cpu_to_gpu_multiple( - vres, vdev, faiss.IndexFlatL2(d)) - - clus.train(x, index) - centroids = faiss.vector_float_to_array(clus.centroids) - - return centroids.reshape(k, d) - - -def prepare_coarse_quantizer(preproc): - - if cent_cachefile and os.path.exists(cent_cachefile): - print("load centroids", cent_cachefile) - centroids = np.load(cent_cachefile) - else: - nt = max(1000000, 256 * ncent) - print("train coarse quantizer...") - t0 = time.time() - centroids = train_coarse_quantizer(xt[:nt], ncent, preproc) - print("Coarse train time: %.3f s" % (time.time() - t0)) - if cent_cachefile: - print("store centroids", cent_cachefile) - np.save(cent_cachefile, centroids) - - coarse_quantizer = faiss.IndexFlatL2(preproc.d_out) - coarse_quantizer.add(centroids) - - return coarse_quantizer - - -################################################################# -# Make index and add elements to it -################################################################# - - -def prepare_trained_index(preproc): - - coarse_quantizer = prepare_coarse_quantizer(preproc) - d = preproc.d_out - if pqflat_str == 'Flat': - print("making an IVFFlat index") - idx_model = faiss.IndexIVFFlat(coarse_quantizer, d, ncent, - faiss.METRIC_L2) - else: - m = int(pqflat_str[2:]) - assert m < 56 or use_float16, "PQ%d will work only with -float16" % m - print("making an IVFPQ index, m = ", m) - idx_model = faiss.IndexIVFPQ(coarse_quantizer, d, ncent, m, 8) - - coarse_quantizer.this.disown() - idx_model.own_fields = True - - # finish training on CPU - t0 = time.time() - print("Training vector codes") - x = preproc.apply_py(sanitize(xt[:1000000])) - idx_model.train(x) - print(" done %.3f s" % (time.time() - t0)) - - return idx_model - - -def compute_populated_index(preproc): - """Add elements to a sharded index. Return the index and if available - a sharded gpu_index that contains the same data. """ - - indexall = prepare_trained_index(preproc) - - co = faiss.GpuMultipleClonerOptions() - co.useFloat16 = use_float16 - co.useFloat16CoarseQuantizer = False - co.usePrecomputed = use_precomputed_tables - co.indicesOptions = faiss.INDICES_CPU - co.verbose = True - co.reserveVecs = max_add if max_add > 0 else xb.shape[0] - co.shard = True - assert co.shard_type in (0, 1, 2) - vres, vdev = make_vres_vdev() - gpu_index = faiss.index_cpu_to_gpu_multiple( - vres, vdev, indexall, co) - - print("add...") - t0 = time.time() - nb = xb.shape[0] - for i0, xs in dataset_iterator(xb, preproc, add_batch_size): - i1 = i0 + xs.shape[0] - gpu_index.add_with_ids(xs, np.arange(i0, i1)) - if max_add > 0 and gpu_index.ntotal > max_add: - print("Flush indexes to CPU") - for i in range(ngpu): - index_src_gpu = faiss.downcast_index(gpu_index.at(i)) - index_src = faiss.index_gpu_to_cpu(index_src_gpu) - print(" index %d size %d" % (i, index_src.ntotal)) - index_src.copy_subset_to(indexall, 0, 0, nb) - index_src_gpu.reset() - index_src_gpu.reserveMemory(max_add) - gpu_index.sync_with_shard_indexes() - - print('\r%d/%d (%.3f s) ' % ( - i0, nb, time.time() - t0), end=' ') - sys.stdout.flush() - print("Add time: %.3f s" % (time.time() - t0)) - - print("Aggregate indexes to CPU") - t0 = time.time() - - if hasattr(gpu_index, 'at'): - # it is a sharded index - for i in range(ngpu): - index_src = faiss.index_gpu_to_cpu(gpu_index.at(i)) - print(" index %d size %d" % (i, index_src.ntotal)) - index_src.copy_subset_to(indexall, 0, 0, nb) - else: - # simple index - index_src = faiss.index_gpu_to_cpu(gpu_index) - index_src.copy_subset_to(indexall, 0, 0, nb) - - print(" done in %.3f s" % (time.time() - t0)) - - if max_add > 0: - # it does not contain all the vectors - gpu_index = None - - return gpu_index, indexall - -def compute_populated_index_2(preproc): - - indexall = prepare_trained_index(preproc) - - # set up a 3-stage pipeline that does: - # - stage 1: load + preproc - # - stage 2: assign on GPU - # - stage 3: add to index - - stage1 = dataset_iterator(xb, preproc, add_batch_size) - - vres, vdev = make_vres_vdev() - coarse_quantizer_gpu = faiss.index_cpu_to_gpu_multiple( - vres, vdev, indexall.quantizer) - - def quantize(args): - (i0, xs) = args - _, assign = coarse_quantizer_gpu.search(xs, 1) - return i0, xs, assign.ravel() - - stage2 = rate_limited_imap(quantize, stage1) - - print("add...") - t0 = time.time() - nb = xb.shape[0] - - for i0, xs, assign in stage2: - i1 = i0 + xs.shape[0] - if indexall.__class__ == faiss.IndexIVFPQ: - indexall.add_core_o(i1 - i0, faiss.swig_ptr(xs), - None, None, faiss.swig_ptr(assign)) - elif indexall.__class__ == faiss.IndexIVFFlat: - indexall.add_core(i1 - i0, faiss.swig_ptr(xs), None, - faiss.swig_ptr(assign)) - else: - assert False - - print('\r%d/%d (%.3f s) ' % ( - i0, nb, time.time() - t0), end=' ') - sys.stdout.flush() - print("Add time: %.3f s" % (time.time() - t0)) - - return None, indexall - - - -def get_populated_index(preproc): - - if not index_cachefile or not os.path.exists(index_cachefile): - if not altadd: - gpu_index, indexall = compute_populated_index(preproc) - else: - gpu_index, indexall = compute_populated_index_2(preproc) - if index_cachefile: - print("store", index_cachefile) - faiss.write_index(indexall, index_cachefile) - else: - print("load", index_cachefile) - indexall = faiss.read_index(index_cachefile) - gpu_index = None - - co = faiss.GpuMultipleClonerOptions() - co.useFloat16 = use_float16 - co.useFloat16CoarseQuantizer = False - co.usePrecomputed = use_precomputed_tables - co.indicesOptions = 0 - co.verbose = True - co.shard = True # the replicas will be made "manually" - t0 = time.time() - print("CPU index contains %d vectors, move to GPU" % indexall.ntotal) - if replicas == 1: - - if not gpu_index: - print("copying loaded index to GPUs") - vres, vdev = make_vres_vdev() - index = faiss.index_cpu_to_gpu_multiple( - vres, vdev, indexall, co) - else: - index = gpu_index - - else: - del gpu_index # We override the GPU index - - print("Copy CPU index to %d sharded GPU indexes" % replicas) - - index = faiss.IndexReplicas() - - for i in range(replicas): - gpu0 = ngpu * i / replicas - gpu1 = ngpu * (i + 1) / replicas - vres, vdev = make_vres_vdev(gpu0, gpu1) - - print(" dispatch to GPUs %d:%d" % (gpu0, gpu1)) - - index1 = faiss.index_cpu_to_gpu_multiple( - vres, vdev, indexall, co) - index1.this.disown() - index.addIndex(index1) - index.own_fields = True - del indexall - print("move to GPU done in %.3f s" % (time.time() - t0)) - return index - - - -################################################################# -# Perform search -################################################################# - - -def eval_dataset(index, preproc): - - ps = faiss.GpuParameterSpace() - ps.initialize(index) - - nq_gt = gt_I.shape[0] - print("search...") - sl = query_batch_size - nq = xq.shape[0] - for nprobe in nprobes: - ps.set_index_parameter(index, 'nprobe', nprobe) - t0 = time.time() - - if sl == 0: - D, I = index.search(preproc.apply_py(sanitize(xq)), nnn) - else: - I = np.empty((nq, nnn), dtype='int32') - D = np.empty((nq, nnn), dtype='float32') - - inter_res = '' - - for i0, xs in dataset_iterator(xq, preproc, sl): - print('\r%d/%d (%.3f s%s) ' % ( - i0, nq, time.time() - t0, inter_res), end=' ') - sys.stdout.flush() - - i1 = i0 + xs.shape[0] - Di, Ii = index.search(xs, nnn) - - I[i0:i1] = Ii - D[i0:i1] = Di - - if knngraph and not inter_res and i1 >= nq_gt: - ires = eval_intersection_measure( - gt_I[:, :nnn], I[:nq_gt]) - inter_res = ', %.4f' % ires - - t1 = time.time() - if knngraph: - ires = eval_intersection_measure(gt_I[:, :nnn], I[:nq_gt]) - print(" probe=%-3d: %.3f s rank-%d intersection results: %.4f" % ( - nprobe, t1 - t0, nnn, ires)) - else: - print(" probe=%-3d: %.3f s" % (nprobe, t1 - t0), end=' ') - gtc = gt_I[:, :1] - nq = xq.shape[0] - for rank in 1, 10, 100: - if rank > nnn: continue - nok = (I[:, :rank] == gtc).sum() - print("1-R@%d: %.4f" % (rank, nok / float(nq)), end=' ') - print() - if I_fname: - I_fname_i = I_fname % I - print("storing", I_fname_i) - np.save(I, I_fname_i) - if D_fname: - D_fname_i = I_fname % I - print("storing", D_fname_i) - np.save(D, D_fname_i) - - -################################################################# -# Driver -################################################################# - - -preproc = get_preprocessor() - -index = get_populated_index(preproc) - -eval_dataset(index, preproc) - -# make sure index is deleted before the resources -del index diff --git a/core/src/index/thirdparty/faiss/benchs/bench_gpu_sift1m.py b/core/src/index/thirdparty/faiss/benchs/bench_gpu_sift1m.py deleted file mode 100644 index 76c312b5c5..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/bench_gpu_sift1m.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#!/usr/bin/env python2 - -from __future__ import print_function -import os -import time -import numpy as np -import pdb - -import faiss -from datasets import load_sift1M, evaluate - - -print("load data") - -xb, xq, xt, gt = load_sift1M() -nq, d = xq.shape - -# we need only a StandardGpuResources per GPU -res = faiss.StandardGpuResources() - - -################################################################# -# Exact search experiment -################################################################# - -print("============ Exact search") - -flat_config = faiss.GpuIndexFlatConfig() -flat_config.device = 0 - -index = faiss.GpuIndexFlatL2(res, d, flat_config) - -print("add vectors to index") - -index.add(xb) - -print("warmup") - -index.search(xq, 123) - -print("benchmark") - -for lk in range(11): - k = 1 << lk - t, r = evaluate(index, xq, gt, k) - - # the recall should be 1 at all times - print("k=%d %.3f ms, R@1 %.4f" % (k, t, r[1])) - - -################################################################# -# Approximate search experiment -################################################################# - -print("============ Approximate search") - -index = faiss.index_factory(d, "IVF4096,PQ64") - -# faster, uses more memory -# index = faiss.index_factory(d, "IVF16384,Flat") - -co = faiss.GpuClonerOptions() - -# here we are using a 64-byte PQ, so we must set the lookup tables to -# 16 bit float (this is due to the limited temporary memory). -co.useFloat16 = True - -index = faiss.index_cpu_to_gpu(res, 0, index, co) - -print("train") - -index.train(xt) - -print("add vectors to index") - -index.add(xb) - -print("warmup") - -index.search(xq, 123) - -print("benchmark") - -for lnprobe in range(10): - nprobe = 1 << lnprobe - index.setNumProbes(nprobe) - t, r = evaluate(index, xq, gt, 100) - - print("nprobe=%4d %.3f ms recalls= %.4f %.4f %.4f" % (nprobe, t, r[1], r[10], r[100])) diff --git a/core/src/index/thirdparty/faiss/benchs/bench_hnsw.py b/core/src/index/thirdparty/faiss/benchs/bench_hnsw.py deleted file mode 100644 index dea13da8c2..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/bench_hnsw.py +++ /dev/null @@ -1,158 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#!/usr/bin/env python2 - -from __future__ import print_function -import time -import sys -import numpy as np -import faiss -from datasets import load_sift1M - - -k = int(sys.argv[1]) -todo = sys.argv[1:] - -print("load data") -xb, xq, xt, gt = load_sift1M() -nq, d = xq.shape - -if todo == []: - todo = 'hnsw hnsw_sq ivf ivf_hnsw_quantizer kmeans kmeans_hnsw'.split() - - -def evaluate(index): - # for timing with a single core - # faiss.omp_set_num_threads(1) - - t0 = time.time() - D, I = index.search(xq, k) - t1 = time.time() - - missing_rate = (I == -1).sum() / float(k * nq) - recall_at_1 = (I == gt[:, :1]).sum() / float(nq) - print("\t %7.3f ms per query, R@1 %.4f, missing rate %.4f" % ( - (t1 - t0) * 1000.0 / nq, recall_at_1, missing_rate)) - - -if 'hnsw' in todo: - - print("Testing HNSW Flat") - - index = faiss.IndexHNSWFlat(d, 32) - - # training is not needed - - # this is the default, higher is more accurate and slower to - # construct - index.hnsw.efConstruction = 40 - - print("add") - # to see progress - index.verbose = True - index.add(xb) - - print("search") - for efSearch in 16, 32, 64, 128, 256: - for bounded_queue in [True, False]: - print("efSearch", efSearch, "bounded queue", bounded_queue, end=' ') - index.hnsw.search_bounded_queue = bounded_queue - index.hnsw.efSearch = efSearch - evaluate(index) - -if 'hnsw_sq' in todo: - - print("Testing HNSW with a scalar quantizer") - # also set M so that the vectors and links both use 128 bytes per - # entry (total 256 bytes) - index = faiss.IndexHNSWSQ(d, faiss.ScalarQuantizer.QT_8bit, 16) - - print("training") - # training for the scalar quantizer - index.train(xt) - - # this is the default, higher is more accurate and slower to - # construct - index.hnsw.efConstruction = 40 - - print("add") - # to see progress - index.verbose = True - index.add(xb) - - print("search") - for efSearch in 16, 32, 64, 128, 256: - print("efSearch", efSearch, end=' ') - index.hnsw.efSearch = efSearch - evaluate(index) - -if 'ivf' in todo: - - print("Testing IVF Flat (baseline)") - quantizer = faiss.IndexFlatL2(d) - index = faiss.IndexIVFFlat(quantizer, d, 16384) - index.cp.min_points_per_centroid = 5 # quiet warning - - # to see progress - index.verbose = True - - print("training") - index.train(xt) - - print("add") - index.add(xb) - - print("search") - for nprobe in 1, 4, 16, 64, 256: - print("nprobe", nprobe, end=' ') - index.nprobe = nprobe - evaluate(index) - -if 'ivf_hnsw_quantizer' in todo: - - print("Testing IVF Flat with HNSW quantizer") - quantizer = faiss.IndexHNSWFlat(d, 32) - index = faiss.IndexIVFFlat(quantizer, d, 16384) - index.cp.min_points_per_centroid = 5 # quiet warning - index.quantizer_trains_alone = 2 - - # to see progress - index.verbose = True - - print("training") - index.train(xt) - - print("add") - index.add(xb) - - print("search") - quantizer.hnsw.efSearch = 64 - for nprobe in 1, 4, 16, 64, 256: - print("nprobe", nprobe, end=' ') - index.nprobe = nprobe - evaluate(index) - -# Bonus: 2 kmeans tests - -if 'kmeans' in todo: - print("Performing kmeans on sift1M database vectors (baseline)") - clus = faiss.Clustering(d, 16384) - clus.verbose = True - clus.niter = 10 - index = faiss.IndexFlatL2(d) - clus.train(xb, index) - - -if 'kmeans_hnsw' in todo: - print("Performing kmeans on sift1M using HNSW assignment") - clus = faiss.Clustering(d, 16384) - clus.verbose = True - clus.niter = 10 - index = faiss.IndexHNSWFlat(d, 32) - # increase the default efSearch, otherwise the number of empty - # clusters is too high. - index.hnsw.efSearch = 128 - clus.train(xb, index) diff --git a/core/src/index/thirdparty/faiss/benchs/bench_index_pq.py b/core/src/index/thirdparty/faiss/benchs/bench_index_pq.py deleted file mode 100644 index 4fd5ccfeb0..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/bench_index_pq.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -from __future__ import print_function -import faiss -from datasets import load_sift1M, evaluate - -xb, xq, xt, gt = load_sift1M() -nq, d = xq.shape - -k = 32 - -for nbits in 4, 6, 8, 10, 12: - index = faiss.IndexPQ(d, 8, nbits) - index.train(xt) - index.add(xb) - - t, r = evaluate(index, xq, gt, k) - print("\t %7.3f ms per query, R@1 %.4f" % (t, r[1])) - del index diff --git a/core/src/index/thirdparty/faiss/benchs/bench_pairwise_distances.py b/core/src/index/thirdparty/faiss/benchs/bench_pairwise_distances.py deleted file mode 100644 index bde8cc908e..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/bench_pairwise_distances.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#! /usr/bin/env python3 - -"""small test script to benchmark the SIMD implementation of the -distance computations for the additional metrics. Call eg. with L1 to -get L1 distance computations. -""" - -import faiss - -import sys -import time - -d = 64 -nq = 4096 -nb = 16384 - -print("sample") - -xq = faiss.randn((nq, d), 123) -xb = faiss.randn((nb, d), 123) - -mt_name = "L2" if len(sys.argv) < 2 else sys.argv[1] - -mt = getattr(faiss, "METRIC_" + mt_name) - -print("distances") -t0 = time.time() -dis = faiss.pairwise_distances(xq, xb, mt) -t1 = time.time() - -print("nq=%d nb=%d d=%d %s: %.3f s" % (nq, nb, d, mt_name, t1 - t0)) diff --git a/core/src/index/thirdparty/faiss/benchs/bench_polysemous_1bn.py b/core/src/index/thirdparty/faiss/benchs/bench_polysemous_1bn.py deleted file mode 100644 index 0cf3b723a1..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/bench_polysemous_1bn.py +++ /dev/null @@ -1,254 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#!/usr/bin/env python2 - -from __future__ import print_function -import os -import sys -import time -import numpy as np -import re -import faiss -from multiprocessing.dummy import Pool as ThreadPool -from datasets import ivecs_read - - -# we mem-map the biggest files to avoid having them in memory all at -# once - - -def mmap_fvecs(fname): - x = np.memmap(fname, dtype='int32', mode='r') - d = x[0] - return x.view('float32').reshape(-1, d + 1)[:, 1:] - - -def mmap_bvecs(fname): - x = np.memmap(fname, dtype='uint8', mode='r') - d = x[:4].view('int32')[0] - return x.reshape(-1, d + 4)[:, 4:] - - -################################################################# -# Bookkeeping -################################################################# - - -dbname = sys.argv[1] -index_key = sys.argv[2] -parametersets = sys.argv[3:] - - -tmpdir = '/tmp/bench_polysemous' - -if not os.path.isdir(tmpdir): - print("%s does not exist, creating it" % tmpdir) - os.mkdir(tmpdir) - - -################################################################# -# Prepare dataset -################################################################# - - -print("Preparing dataset", dbname) - -if dbname.startswith('SIFT'): - # SIFT1M to SIFT1000M - dbsize = int(dbname[4:-1]) - xb = mmap_bvecs('bigann/bigann_base.bvecs') - xq = mmap_bvecs('bigann/bigann_query.bvecs') - xt = mmap_bvecs('bigann/bigann_learn.bvecs') - - # trim xb to correct size - xb = xb[:dbsize * 1000 * 1000] - - gt = ivecs_read('bigann/gnd/idx_%dM.ivecs' % dbsize) - -elif dbname == 'Deep1B': - xb = mmap_fvecs('deep1b/base.fvecs') - xq = mmap_fvecs('deep1b/deep1B_queries.fvecs') - xt = mmap_fvecs('deep1b/learn.fvecs') - # deep1B's train is is outrageously big - xt = xt[:10 * 1000 * 1000] - gt = ivecs_read('deep1b/deep1B_groundtruth.ivecs') - -else: - print('unknown dataset', dbname, file=sys.stderr) - sys.exit(1) - - -print("sizes: B %s Q %s T %s gt %s" % ( - xb.shape, xq.shape, xt.shape, gt.shape)) - -nq, d = xq.shape -nb, d = xb.shape -assert gt.shape[0] == nq - - -################################################################# -# Training -################################################################# - - -def choose_train_size(index_key): - - # some training vectors for PQ and the PCA - n_train = 256 * 1000 - - if "IVF" in index_key: - matches = re.findall('IVF([0-9]+)', index_key) - ncentroids = int(matches[0]) - n_train = max(n_train, 100 * ncentroids) - elif "IMI" in index_key: - matches = re.findall('IMI2x([0-9]+)', index_key) - nbit = int(matches[0]) - n_train = max(n_train, 256 * (1 << nbit)) - return n_train - - -def get_trained_index(): - filename = "%s/%s_%s_trained.index" % ( - tmpdir, dbname, index_key) - - if not os.path.exists(filename): - index = faiss.index_factory(d, index_key) - - n_train = choose_train_size(index_key) - - xtsub = xt[:n_train] - print("Keeping %d train vectors" % xtsub.shape[0]) - # make sure the data is actually in RAM and in float - xtsub = xtsub.astype('float32').copy() - index.verbose = True - - t0 = time.time() - index.train(xtsub) - index.verbose = False - print("train done in %.3f s" % (time.time() - t0)) - print("storing", filename) - faiss.write_index(index, filename) - else: - print("loading", filename) - index = faiss.read_index(filename) - return index - - -################################################################# -# Adding vectors to dataset -################################################################# - -def rate_limited_imap(f, l): - 'a thread pre-processes the next element' - pool = ThreadPool(1) - res = None - for i in l: - res_next = pool.apply_async(f, (i, )) - if res: - yield res.get() - res = res_next - yield res.get() - - -def matrix_slice_iterator(x, bs): - " iterate over the lines of x in blocks of size bs" - nb = x.shape[0] - block_ranges = [(i0, min(nb, i0 + bs)) - for i0 in range(0, nb, bs)] - - return rate_limited_imap( - lambda i01: x[i01[0]:i01[1]].astype('float32').copy(), - block_ranges) - - -def get_populated_index(): - - filename = "%s/%s_%s_populated.index" % ( - tmpdir, dbname, index_key) - - if not os.path.exists(filename): - index = get_trained_index() - i0 = 0 - t0 = time.time() - for xs in matrix_slice_iterator(xb, 100000): - i1 = i0 + xs.shape[0] - print('\radd %d:%d, %.3f s' % (i0, i1, time.time() - t0), end=' ') - sys.stdout.flush() - index.add(xs) - i0 = i1 - print() - print("Add done in %.3f s" % (time.time() - t0)) - print("storing", filename) - faiss.write_index(index, filename) - else: - print("loading", filename) - index = faiss.read_index(filename) - return index - - -################################################################# -# Perform searches -################################################################# - -index = get_populated_index() - -ps = faiss.ParameterSpace() -ps.initialize(index) - -# make sure queries are in RAM -xq = xq.astype('float32').copy() - -# a static C++ object that collects statistics about searches -ivfpq_stats = faiss.cvar.indexIVFPQ_stats -ivf_stats = faiss.cvar.indexIVF_stats - - -if parametersets == ['autotune'] or parametersets == ['autotuneMT']: - - if parametersets == ['autotune']: - faiss.omp_set_num_threads(1) - - # setup the Criterion object: optimize for 1-R@1 - crit = faiss.OneRecallAtRCriterion(nq, 1) - # by default, the criterion will request only 1 NN - crit.nnn = 100 - crit.set_groundtruth(None, gt.astype('int64')) - - # then we let Faiss find the optimal parameters by itself - print("exploring operating points") - - t0 = time.time() - op = ps.explore(index, xq, crit) - print("Done in %.3f s, available OPs:" % (time.time() - t0)) - - # opv is a C++ vector, so it cannot be accessed like a Python array - opv = op.optimal_pts - print("%-40s 1-R@1 time" % "Parameters") - for i in range(opv.size()): - opt = opv.at(i) - print("%-40s %.4f %7.3f" % (opt.key, opt.perf, opt.t)) - -else: - - # we do queries in a single thread - faiss.omp_set_num_threads(1) - - print(' ' * len(parametersets[0]), '\t', 'R@1 R@10 R@100 time %pass') - - for param in parametersets: - print(param, '\t', end=' ') - sys.stdout.flush() - ps.set_index_parameters(index, param) - t0 = time.time() - ivfpq_stats.reset() - ivf_stats.reset() - D, I = index.search(xq, 100) - t1 = time.time() - for rank in 1, 10, 100: - n_ok = (I[:, :rank] == gt[:, :1]).sum() - print("%.4f" % (n_ok / float(nq)), end=' ') - print("%8.3f " % ((t1 - t0) * 1000.0 / nq), end=' ') - print("%5.2f" % (ivfpq_stats.n_hamming_pass * 100.0 / ivf_stats.ndis)) diff --git a/core/src/index/thirdparty/faiss/benchs/bench_polysemous_sift1m.py b/core/src/index/thirdparty/faiss/benchs/bench_polysemous_sift1m.py deleted file mode 100644 index f54c66bc2b..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/bench_polysemous_sift1m.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#!/usr/bin/env python2 - -from __future__ import print_function -import time -import numpy as np - -import faiss -from datasets import load_sift1M, evaluate - - -print("load data") -xb, xq, xt, gt = load_sift1M() -nq, d = xq.shape - -# index with 16 subquantizers, 8 bit each -index = faiss.IndexPQ(d, 16, 8) -index.do_polysemous_training = True -index.verbose = True - -print("train") - -index.train(xt) - -print("add vectors to index") - -index.add(xb) - -nt = 1 -faiss.omp_set_num_threads(1) - - -print("PQ baseline", end=' ') -index.search_type = faiss.IndexPQ.ST_PQ -t, r = evaluate(index, xq, gt, 1) -print("\t %7.3f ms per query, R@1 %.4f" % (t, r[1])) - -for ht in 64, 62, 58, 54, 50, 46, 42, 38, 34, 30: - print("Polysemous", ht, end=' ') - index.search_type = faiss.IndexPQ.ST_polysemous - index.polysemous_ht = ht - t, r = evaluate(index, xq, gt, 1) - print("\t %7.3f ms per query, R@1 %.4f" % (t, r[1])) diff --git a/core/src/index/thirdparty/faiss/benchs/bench_scalar_quantizer.py b/core/src/index/thirdparty/faiss/benchs/bench_scalar_quantizer.py deleted file mode 100644 index a990b485f1..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/bench_scalar_quantizer.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#!/usr/bin/env python2 - -from __future__ import print_function -import time -import numpy as np -import faiss -from datasets import load_sift1M - - -print("load data") - -xb, xq, xt, gt = load_sift1M() -nq, d = xq.shape - -ncent = 256 - -variants = [(name, getattr(faiss.ScalarQuantizer, name)) - for name in dir(faiss.ScalarQuantizer) - if name.startswith('QT_')] - -quantizer = faiss.IndexFlatL2(d) -# quantizer.add(np.zeros((1, d), dtype='float32')) - -if False: - for name, qtype in [('flat', 0)] + variants: - - print("============== test", name) - t0 = time.time() - - if name == 'flat': - index = faiss.IndexIVFFlat(quantizer, d, ncent, - faiss.METRIC_L2) - else: - index = faiss.IndexIVFScalarQuantizer(quantizer, d, ncent, - qtype, faiss.METRIC_L2) - - index.nprobe = 16 - print("[%.3f s] train" % (time.time() - t0)) - index.train(xt) - print("[%.3f s] add" % (time.time() - t0)) - index.add(xb) - print("[%.3f s] search" % (time.time() - t0)) - D, I = index.search(xq, 100) - print("[%.3f s] eval" % (time.time() - t0)) - - for rank in 1, 10, 100: - n_ok = (I[:, :rank] == gt[:, :1]).sum() - print("%.4f" % (n_ok / float(nq)), end=' ') - print() - -if True: - for name, qtype in variants: - - print("============== test", name) - - for rsname, vals in [('RS_minmax', - [-0.4, -0.2, -0.1, -0.05, 0.0, 0.1, 0.5]), - ('RS_meanstd', [0.8, 1.0, 1.5, 2.0, 3.0, 5.0, 10.0]), - ('RS_quantiles', [0.02, 0.05, 0.1, 0.15]), - ('RS_optim', [0.0])]: - for val in vals: - print("%-15s %5g " % (rsname, val), end=' ') - index = faiss.IndexIVFScalarQuantizer(quantizer, d, ncent, - qtype, faiss.METRIC_L2) - index.nprobe = 16 - index.sq.rangestat = getattr(faiss.ScalarQuantizer, - rsname) - - index.rangestat_arg = val - - index.train(xt) - index.add(xb) - t0 = time.time() - D, I = index.search(xq, 100) - t1 = time.time() - - for rank in 1, 10, 100: - n_ok = (I[:, :rank] == gt[:, :1]).sum() - print("%.4f" % (n_ok / float(nq)), end=' ') - print(" %.3f s" % (t1 - t0)) diff --git a/core/src/index/thirdparty/faiss/benchs/bench_vector_ops.py b/core/src/index/thirdparty/faiss/benchs/bench_vector_ops.py deleted file mode 100644 index 331a9923e2..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/bench_vector_ops.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#! /usr/bin/env python2 - -from __future__ import print_function -import numpy as np -import faiss -import time - -swig_ptr = faiss.swig_ptr - -if False: - a = np.arange(10, 14).astype('float32') - b = np.arange(20, 24).astype('float32') - - faiss.fvec_inner_product (swig_ptr(a), swig_ptr(b), 4) - - 1/0 - -xd = 100 -yd = 1000000 - -np.random.seed(1234) - -faiss.omp_set_num_threads(1) - -print('xd=%d yd=%d' % (xd, yd)) - -print('Running inner products test..') -for d in 3, 4, 12, 36, 64: - - x = faiss.rand(xd * d).reshape(xd, d) - y = faiss.rand(yd * d).reshape(yd, d) - - distances = np.empty((xd, yd), dtype='float32') - - t0 = time.time() - for i in range(xd): - faiss.fvec_inner_products_ny(swig_ptr(distances[i]), - swig_ptr(x[i]), - swig_ptr(y), - d, yd) - t1 = time.time() - - # sparse verification - ntry = 100 - num, denom = 0, 0 - for t in range(ntry): - xi = np.random.randint(xd) - yi = np.random.randint(yd) - num += abs(distances[xi, yi] - np.dot(x[xi], y[yi])) - denom += abs(distances[xi, yi]) - - print('d=%d t=%.3f s diff=%g' % (d, t1 - t0, num / denom)) - - -print('Running L2sqr test..') -for d in 3, 4, 12, 36, 64: - - x = faiss.rand(xd * d).reshape(xd, d) - y = faiss.rand(yd * d).reshape(yd, d) - - distances = np.empty((xd, yd), dtype='float32') - - t0 = time.time() - for i in range(xd): - faiss.fvec_L2sqr_ny(swig_ptr(distances[i]), - swig_ptr(x[i]), - swig_ptr(y), - d, yd) - t1 = time.time() - - # sparse verification - ntry = 100 - num, denom = 0, 0 - for t in range(ntry): - xi = np.random.randint(xd) - yi = np.random.randint(yd) - num += abs(distances[xi, yi] - np.sum((x[xi] - y[yi]) ** 2)) - denom += abs(distances[xi, yi]) - - print('d=%d t=%.3f s diff=%g' % (d, t1 - t0, num / denom)) diff --git a/core/src/index/thirdparty/faiss/benchs/datasets.py b/core/src/index/thirdparty/faiss/benchs/datasets.py deleted file mode 100644 index 3971f278f9..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/datasets.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -from __future__ import print_function -import sys -import time -import numpy as np - - -def ivecs_read(fname): - a = np.fromfile(fname, dtype='int32') - d = a[0] - return a.reshape(-1, d + 1)[:, 1:].copy() - - -def fvecs_read(fname): - return ivecs_read(fname).view('float32') - - -def load_sift1M(): - print("Loading sift1M...", end='', file=sys.stderr) - xt = fvecs_read("sift1M/sift_learn.fvecs") - xb = fvecs_read("sift1M/sift_base.fvecs") - xq = fvecs_read("sift1M/sift_query.fvecs") - gt = ivecs_read("sift1M/sift_groundtruth.ivecs") - print("done", file=sys.stderr) - - return xb, xq, xt, gt - - -def evaluate(index, xq, gt, k): - nq = xq.shape[0] - t0 = time.time() - D, I = index.search(xq, k) # noqa: E741 - t1 = time.time() - - recalls = {} - i = 1 - while i <= k: - recalls[i] = (I[:, :i] == gt[:, :1]).sum() / float(nq) - i *= 10 - - return (t1 - t0) * 1000.0 / nq, recalls diff --git a/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/README.md b/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/README.md deleted file mode 100644 index c2c792992b..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/README.md +++ /dev/null @@ -1,198 +0,0 @@ - -# Distributed on-disk index for 1T-scale datasets - -This is code corresponding to the description in [Indexing 1T vectors](https://github.com/facebookresearch/faiss/wiki/Indexing-1T-vectors). -All the code is in python 3 (and not compatible with Python 2). -The current code uses the Deep1B dataset for demonstration purposes, but can scale to 1000x larger. -To run it, download the Deep1B dataset as explained [here](../#getting-deep1b), and edit paths to the dataset in the scripts. - -The cluster commands are written for the Slurm batch scheduling system. -Hopefully, changing to another type of scheduler should be quite straightforward. - -## Distributed k-means - -To cluster 500M vectors to 10M centroids, it is useful to have a distriubuted k-means implementation. -The distribution simply consists in splitting the training vectors across machines (servers) and have them do the assignment. -The master/client then synthesizes the results and updates the centroids. - -The distributed k-means implementation here is based on 3 files: - -- [`rpc.py`](rpc.py) is a very simple remote procedure call implementation based on sockets and pickle. -It exposes the methods of an object on the server side so that they can be called from the client as if the object was local. - -- [`distributed_kmeans.py`](distributed_kmeans.py) contains the k-means implementation. -The main loop of k-means is re-implemented in python but follows closely the Faiss C++ implementation, and should not be significantly less efficient. -It relies on a `DatasetAssign` object that does the assignement to centrtoids, which is the bulk of the computation. -The object can be a Faiss CPU index, a GPU index or a set of remote GPU or CPU indexes. - -- [`run_on_cluster.bash`](run_on_cluster.bash) contains the shell code to run the distributed k-means on a cluster. - -The distributed k-means works with a Python install that contains faiss and scipy (for sparse matrices). -It clusters the training data of Deep1B, this can be changed easily to any file in fvecs, bvecs or npy format that contains the training set. -The training vectors may be too large to fit in RAM, but they are memory-mapped so that should not be a problem. -The file is also assumed to be accessible from all server machines with eg. a distributed file system. - -### Local tests - -Edit `distibuted_kmeans.py` to point `testdata` to your local copy of the dataset. - -Then, 4 levels of sanity check can be run: -```bash -# reference Faiss C++ run -python distributed_kmeans.py --test 0 -# using the Python implementation -python distributed_kmeans.py --test 1 -# use the dispatch object (on local datasets) -python distributed_kmeans.py --test 2 -# same, with GPUs -python distributed_kmeans.py --test 3 -``` -The output should look like [This gist](https://gist.github.com/mdouze/ffa01fe666a9325761266fe55ead72ad). - -### Distributed sanity check - -To run the distributed k-means, `distibuted_kmeans.py` has to be run both on the servers (`--server` option) and client sides (`--client` option). -Edit the top of `run_on_cluster.bash` to set the path of the data to cluster. - -Sanity checks can be run with -```bash -# non distributed baseline -bash run_on_cluster.bash test_kmeans_0 -# using all the machine's GPUs -bash run_on_cluster.bash test_kmeans_1 -# distrbuted run, with one local server per GPU -bash run_on_cluster.bash test_kmeans_2 -``` -The test `test_kmeans_2` simulates a distributed run on a single machine by starting one server process per GPU and connecting to the servers via the rpc protocol. -The output should look like [this gist](https://gist.github.com/mdouze/5b2dc69b74579ecff04e1686a277d32e). - - - -### Distributed run - -The way the script can be distributed depends on the cluster's scheduling system. -Here we use Slurm, but it should be relatively easy to adapt to any scheduler that can allocate a set of matchines and start the same exectuable on all of them. - -The command -``` -bash run_on_cluster.bash slurm_distributed_kmeans -``` -asks SLURM for 5 machines with 4 GPUs each with the `srun` command. -All 5 machines run the script with the `slurm_within_kmeans_server` option. -They determine the number of servers and their own server id via the `SLURM_NPROCS` and `SLURM_PROCID` environment variables. - -All machines start `distributed_kmeans.py` in server mode for the slice of the dataset they are responsible for. - -In addition, the machine #0 also starts the client. -The client knows who are the other servers via the variable `SLURM_JOB_NODELIST`. -It connects to all clients and performs the clustering. - -The output should look like [this gist](https://gist.github.com/mdouze/8d25e89fb4af5093057cae0f917da6cd). - -### Run used for deep1B - -For the real run, we run the clustering on 50M vectors to 1M centroids. -This is just a matter of using as many machines / GPUs as possible in setting the output centroids with the `--out filename` option. -Then run -``` -bash run_on_cluster.bash deep1b_clustering -``` - -The last lines of output read like: -``` - Iteration 19 (898.92 s, search 875.71 s): objective=1.33601e+07 imbalance=1.303 nsplit=0 - 0: writing centroids to /checkpoint/matthijs/ondisk_distributed/1M_centroids.npy -``` - -This means that the total training time was 899s, of which 876s were used for computation. -However, the computation includes the I/O overhead to the assignment servers. -In this implementation, the overhead of transmitting the data is non-negligible and so is the centroid computation stage. -This is due to the inefficient Python implementation and the RPC protocol that is not optimized for broadcast / gather (like MPI). -However, it is a simple implementation that should run on most clusters. - -## Making the trained index - -After the centroids are obtained, an empty trained index must be constructed. -This is done by: - -- applying a pre-processing stage (a random rotation) to balance the dimensions of the vectors. This can be done after clustering, the clusters are just rotated as well. - -- wrapping the centroids into a HNSW index to speed up the CPU-based assignment of vectors - -- training the 6-bit scalar quantizer used to encode the vectors - -This is performed by the script [`make_trained_index.py`](make_trained_index.py). - -## Building the index by slices - -We call the slices "vslisces" as they are vertical slices of the big matrix, see explanation in the wiki section [Split across datanbase partitions](https://github.com/facebookresearch/faiss/wiki/Indexing-1T-vectors#split-across-database-partitions). - -The script [make_index_vslice.py](make_index_vslice.py) makes an index for a subset of the vectors of the input data and stores it as an independent index. -There are 200 slices of 5M vectors each for Deep1B. -It can be run in a brute-force parallel fashion, there is no constraint on ordering. -To run the script in parallel on a slurm cluster, use: -``` -bash run_on_cluster.bash make_index_vslices -``` -For a real dataset, the data would be read from a DBMS. -In that case, reading the data and indexing it in parallel is worthwhile because reading is very slow. - -## Splitting accross inverted lists - -The 200 slices need to be merged together. -This is done with the script [merge_to_ondisk.py](merge_to_ondisk.py), that memory maps the 200 vertical slice indexes, extracts a subset of the inverted lists and writes them to a contiguous horizontal slice. -We slice the inverted lists into 50 horizontal slices. -This is run with -``` -bash run_on_cluster.bash make_index_hslices -``` - -## Querying the index - -At this point the index is ready. -The horizontal slices need to be loaded in the right order and combined into an index to be usable. -This is done in the [combined_index.py](combined_index.py) script. -It provides a `CombinedIndexDeep1B` object that contains an index object that can be searched. -To test, run: -``` -python combined_index.py -``` -The output should look like: -``` -(faiss_1.5.2) matthijs@devfair0144:~/faiss_versions/faiss_1Tcode/faiss/benchs/distributed_ondisk$ python combined_index.py -reading /checkpoint/matthijs/ondisk_distributed//hslices/slice49.faissindex -loading empty index /checkpoint/matthijs/ondisk_distributed/trained.faissindex -replace invlists -loaded index of size 1000000000 -nprobe=1 1-recall@1=0.2904 t=12.35s -nnprobe=10 1-recall@1=0.6499 t=17.67s -nprobe=100 1-recall@1=0.8673 t=29.23s -nprobe=1000 1-recall@1=0.9132 t=129.58s -``` -ie. searching is a lot slower than from RAM. - -## Distributed query - -To reduce the bandwidth required from the machine that does the queries, it is possible to split the search accross several search servers. -This way, only the effective results are returned to the main machine. - -The search client and server are implemented in [`search_server.py`](search_server.py). -It can be used as a script to start a search server for `CombinedIndexDeep1B` or as a module to load the clients. - -The search servers can be started with -``` -bash run_on_cluster.bash run_search_servers -``` -(adjust to the number of servers that can be used). - -Then an example of search client is [`distributed_query_demo.py`](distributed_query_demo.py). -It connects to the servers and assigns subsets of inverted lists to visit to each of them. - -A typical output is [this gist](https://gist.github.com/mdouze/1585b9854a9a2437d71f2b2c3c05c7c5). -The number in MiB indicates the amount of data that is read from disk to perform the search. -In this case, the scale of the dataset is too small for the distributed search to have much impact, but on datasets > 10x larger, the difference becomes more significant. - -## Conclusion - -This code contains the core components to make an index that scales up to 1T vectors. -There are a few simplifications wrt. the index that was effectively used in [Indexing 1T vectors](https://github.com/facebookresearch/faiss/wiki/Indexing-1T-vectors). diff --git a/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/combined_index.py b/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/combined_index.py deleted file mode 100644 index 3df2a0180a..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/combined_index.py +++ /dev/null @@ -1,194 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#!/usr/bin/env python3 - -import os -import faiss -import numpy as np - - -class CombinedIndex: - """ - combines a set of inverted lists into a hstack - masks part of those lists - adds these inverted lists to an empty index that contains - the info on how to perform searches - """ - - def __init__(self, invlist_fnames, empty_index_fname, - masked_index_fname=None): - - self.indexes = indexes = [] - ilv = faiss.InvertedListsPtrVector() - - for fname in invlist_fnames: - if os.path.exists(fname): - print('reading', fname, end='\r', flush=True) - index = faiss.read_index(fname) - indexes.append(index) - il = faiss.extract_index_ivf(index).invlists - else: - raise AssertionError - ilv.push_back(il) - print() - - self.big_il = faiss.VStackInvertedLists(ilv.size(), ilv.data()) - if masked_index_fname: - self.big_il_base = self.big_il - print('loading', masked_index_fname) - self.masked_index = faiss.read_index( - masked_index_fname, - faiss.IO_FLAG_MMAP | faiss.IO_FLAG_READ_ONLY) - self.big_il = faiss.MaskedInvertedLists( - faiss.extract_index_ivf(self.masked_index).invlists, - self.big_il_base) - - print('loading empty index', empty_index_fname) - self.index = faiss.read_index(empty_index_fname) - ntotal = self.big_il.compute_ntotal() - - print('replace invlists') - index_ivf = faiss.extract_index_ivf(self.index) - index_ivf.replace_invlists(self.big_il, False) - index_ivf.ntotal = self.index.ntotal = ntotal - index_ivf.parallel_mode = 1 # seems reasonable to do this all the time - - quantizer = faiss.downcast_index(index_ivf.quantizer) - quantizer.hnsw.efSearch = 1024 - - ############################################################ - # Expose fields and functions of the index as methods so that they - # can be called by RPC - - def search(self, x, k): - return self.index.search(x, k) - - def range_search(self, x, radius): - return self.index.range_search(x, radius) - - def transform_and_assign(self, xq): - index = self.index - - if isinstance(index, faiss.IndexPreTransform): - assert index.chain.size() == 1 - vt = index.chain.at(0) - xq = vt.apply_py(xq) - - # perform quantization - index_ivf = faiss.extract_index_ivf(index) - quantizer = index_ivf.quantizer - coarse_dis, list_nos = quantizer.search(xq, index_ivf.nprobe) - return xq, list_nos, coarse_dis - - - def ivf_search_preassigned(self, xq, list_nos, coarse_dis, k): - index_ivf = faiss.extract_index_ivf(self.index) - n, d = xq.shape - assert d == index_ivf.d - n2, d2 = list_nos.shape - assert list_nos.shape == coarse_dis.shape - assert n2 == n - assert d2 == index_ivf.nprobe - D = np.empty((n, k), dtype='float32') - I = np.empty((n, k), dtype='int64') - index_ivf.search_preassigned( - n, faiss.swig_ptr(xq), k, - faiss.swig_ptr(list_nos), faiss.swig_ptr(coarse_dis), - faiss.swig_ptr(D), faiss.swig_ptr(I), False) - return D, I - - - def ivf_range_search_preassigned(self, xq, list_nos, coarse_dis, radius): - index_ivf = faiss.extract_index_ivf(self.index) - n, d = xq.shape - assert d == index_ivf.d - n2, d2 = list_nos.shape - assert list_nos.shape == coarse_dis.shape - assert n2 == n - assert d2 == index_ivf.nprobe - res = faiss.RangeSearchResult(n) - - index_ivf.range_search_preassigned( - n, faiss.swig_ptr(xq), radius, - faiss.swig_ptr(list_nos), faiss.swig_ptr(coarse_dis), - res) - - lims = faiss.rev_swig_ptr(res.lims, n + 1).copy() - nd = int(lims[-1]) - D = faiss.rev_swig_ptr(res.distances, nd).copy() - I = faiss.rev_swig_ptr(res.labels, nd).copy() - return lims, D, I - - def set_nprobe(self, nprobe): - index_ivf = faiss.extract_index_ivf(self.index) - index_ivf.nprobe = nprobe - - def set_parallel_mode(self, pm): - index_ivf = faiss.extract_index_ivf(self.index) - index_ivf.parallel_mode = pm - - def get_ntotal(self): - return self.index.ntotal - - def set_prefetch_nthread(self, nt): - for idx in self.indexes: - il = faiss.downcast_InvertedLists( - faiss.extract_index_ivf(idx).invlists) - il.prefetch_nthread - il.prefetch_nthread = nt - - def set_omp_num_threads(self, nt): - faiss.omp_set_num_threads(nt) - -class CombinedIndexDeep1B(CombinedIndex): - """ loads a CombinedIndex with the data from the big photodna index """ - - def __init__(self): - # set some paths - workdir = "/checkpoint/matthijs/ondisk_distributed/" - - # empty index with the proper quantizer - indexfname = workdir + 'trained.faissindex' - - # index that has some invlists that override the big one - masked_index_fname = None - invlist_fnames = [ - '%s/hslices/slice%d.faissindex' % (workdir, i) - for i in range(50) - ] - CombinedIndex.__init__(self, invlist_fnames, indexfname, masked_index_fname) - - -def ivecs_read(fname): - a = np.fromfile(fname, dtype='int32') - d = a[0] - return a.reshape(-1, d + 1)[:, 1:].copy() - - -def fvecs_read(fname): - return ivecs_read(fname).view('float32') - - -if __name__ == '__main__': - import time - ci = CombinedIndexDeep1B() - print('loaded index of size ', ci.index.ntotal) - - deep1bdir = "/datasets01_101/simsearch/041218/deep1b/" - - xq = fvecs_read(deep1bdir + "deep1B_queries.fvecs") - gt_fname = deep1bdir + "deep1B_groundtruth.ivecs" - gt = ivecs_read(gt_fname) - - for nprobe in 1, 10, 100, 1000: - ci.set_nprobe(nprobe) - t0 = time.time() - D, I = ci.search(xq, 100) - t1 = time.time() - print('nprobe=%d 1-recall@1=%.4f t=%.2fs' % ( - nprobe, (I[:, 0] == gt[:, 0]).sum() / len(xq), - t1 - t0 - )) diff --git a/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/distributed_kmeans.py b/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/distributed_kmeans.py deleted file mode 100644 index ae7a292d3d..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/distributed_kmeans.py +++ /dev/null @@ -1,411 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#! /usr/bin/env python3 - -""" -Simple distributed kmeans implementation Relies on an abstraction -for the training matrix, that can be sharded over several machines. -""" - -import faiss -import time -import numpy as np -import sys -import pdb -import argparse - -from scipy.sparse import csc_matrix - -from multiprocessing.dummy import Pool as ThreadPool - -import rpc - - - - -class DatasetAssign: - """Wrapper for a matrix that offers a function to assign the vectors - to centroids. All other implementations offer the same interface""" - - def __init__(self, x): - self.x = np.ascontiguousarray(x, dtype='float32') - - def count(self): - return self.x.shape[0] - - def dim(self): - return self.x.shape[1] - - def get_subset(self, indices): - return self.x[indices] - - def perform_search(self, centroids): - index = faiss.IndexFlatL2(self.x.shape[1]) - index.add(centroids) - return index.search(self.x, 1) - - def assign_to(self, centroids, weights=None): - D, I = self.perform_search(centroids) - - I = I.ravel() - D = D.ravel() - n = len(self.x) - if weights is None: - weights = np.ones(n, dtype='float32') - nc = len(centroids) - m = csc_matrix((weights, I, np.arange(n + 1)), - shape=(nc, n)) - sum_per_centroid = m * self.x - - return I, D, sum_per_centroid - - -class DatasetAssignGPU(DatasetAssign): - """ GPU version of the previous """ - - def __init__(self, x, gpu_id, verbose=False): - DatasetAssign.__init__(self, x) - index = faiss.IndexFlatL2(x.shape[1]) - if gpu_id >= 0: - self.index = faiss.index_cpu_to_gpu( - faiss.StandardGpuResources(), - gpu_id, index) - else: - # -1 -> assign to all GPUs - self.index = faiss.index_cpu_to_all_gpus(index) - - - def perform_search(self, centroids): - self.index.reset() - self.index.add(centroids) - return self.index.search(self.x, 1) - - -class DatasetAssignDispatch: - """dispatches to several other DatasetAssigns and combines the - results""" - - def __init__(self, xes, in_parallel): - self.xes = xes - self.d = xes[0].dim() - if not in_parallel: - self.imap = map - else: - self.pool = ThreadPool(len(self.xes)) - self.imap = self.pool.imap - self.sizes = list(map(lambda x: x.count(), self.xes)) - self.cs = np.cumsum([0] + self.sizes) - - def count(self): - return self.cs[-1] - - def dim(self): - return self.d - - def get_subset(self, indices): - res = np.zeros((len(indices), self.d), dtype='float32') - nos = np.searchsorted(self.cs[1:], indices, side='right') - - def handle(i): - mask = nos == i - sub_indices = indices[mask] - self.cs[i] - subset = self.xes[i].get_subset(sub_indices) - res[mask] = subset - - list(self.imap(handle, range(len(self.xes)))) - return res - - def assign_to(self, centroids, weights=None): - src = self.imap( - lambda x: x.assign_to(centroids, weights), - self.xes - ) - I = [] - D = [] - sum_per_centroid = None - for Ii, Di, sum_per_centroid_i in src: - I.append(Ii) - D.append(Di) - if sum_per_centroid is None: - sum_per_centroid = sum_per_centroid_i - else: - sum_per_centroid += sum_per_centroid_i - return np.hstack(I), np.hstack(D), sum_per_centroid - - -def imbalance_factor(k , assign): - return faiss.imbalance_factor(len(assign), k, faiss.swig_ptr(assign)) - - -def reassign_centroids(hassign, centroids, rs=None): - """ reassign centroids when some of them collapse """ - if rs is None: - rs = np.random - k, d = centroids.shape - nsplit = 0 - empty_cents = np.where(hassign == 0)[0] - - if empty_cents.size == 0: - return 0 - - fac = np.ones(d) - fac[::2] += 1 / 1024. - fac[1::2] -= 1 / 1024. - - # this is a single pass unless there are more than k/2 - # empty centroids - while empty_cents.size > 0: - # choose which centroids to split - probas = hassign.astype('float') - 1 - probas[probas < 0] = 0 - probas /= probas.sum() - nnz = (probas > 0).sum() - - nreplace = min(nnz, empty_cents.size) - cjs = rs.choice(k, size=nreplace, p=probas) - - for ci, cj in zip(empty_cents[:nreplace], cjs): - - c = centroids[cj] - centroids[ci] = c * fac - centroids[cj] = c / fac - - hassign[ci] = hassign[cj] // 2 - hassign[cj] -= hassign[ci] - nsplit += 1 - - empty_cents = empty_cents[nreplace:] - - return nsplit - - -def kmeans(k, data, niter=25, seed=1234, checkpoint=None): - """Pure python kmeans implementation. Follows the Faiss C++ version - quite closely, but takes a DatasetAssign instead of a training data - matrix. Also redo is not implemented. """ - n, d = data.count(), data.dim() - - print(("Clustering %d points in %dD to %d clusters, " + - "%d iterations seed %d") % (n, d, k, niter, seed)) - - rs = np.random.RandomState(seed) - print("preproc...") - t0 = time.time() - # initialization - perm = rs.choice(n, size=k, replace=False) - centroids = data.get_subset(perm) - - print(" done") - t_search_tot = 0 - obj = [] - for i in range(niter): - t0s = time.time() - - print('assigning', end='\r', flush=True) - assign, D, sums = data.assign_to(centroids) - - print('compute centroids', end='\r', flush=True) - - # pdb.set_trace() - - t_search_tot += time.time() - t0s; - - err = D.sum() - obj.append(err) - - hassign = np.bincount(assign, minlength=k) - - fac = hassign.reshape(-1, 1).astype('float32') - fac[fac == 0] = 1 # quiet warning - - centroids = sums / fac - - nsplit = reassign_centroids(hassign, centroids, rs) - - print((" Iteration %d (%.2f s, search %.2f s): " - "objective=%g imbalance=%.3f nsplit=%d") % ( - i, (time.time() - t0), t_search_tot, - err, imbalance_factor (k, assign), - nsplit) - ) - - if checkpoint is not None: - print('storing centroids in', checkpoint) - np.save(checkpoint, centroids) - - return centroids - - -class AssignServer(rpc.Server): - """ Assign version that can be exposed via RPC """ - - def __init__(self, s, assign, log_prefix=''): - rpc.Server.__init__(self, s, log_prefix=log_prefix) - self.assign = assign - - def __getattr__(self, f): - return getattr(self.assign, f) - - - -def bvecs_mmap(fname): - x = np.memmap(fname, dtype='uint8', mode='r') - d = x[:4].view('int32')[0] - return x.reshape(-1, d + 4)[:, 4:] - - -def ivecs_mmap(fname): - a = np.memmap(fname, dtype='int32', mode='r') - d = a[0] - return a.reshape(-1, d + 1)[:, 1:] - -def fvecs_mmap(fname): - return ivecs_mmap(fname).view('float32') - - -def do_test(todo): - testdata = '/datasets01_101/simsearch/041218/bigann/bigann_learn.bvecs' - - x = bvecs_mmap(testdata) - - # bad distribution to stress-test split code - xx = x[:100000].copy() - xx[:50000] = x[0] - - todo = sys.argv[1:] - - if "0" in todo: - # reference C++ run - km = faiss.Kmeans(x.shape[1], 1000, niter=20, verbose=True) - km.train(xx.astype('float32')) - - if "1" in todo: - # using the Faiss c++ implementation - data = DatasetAssign(xx) - kmeans(1000, data, 20) - - if "2" in todo: - # use the dispatch object (on local datasets) - data = DatasetAssignDispatch([ - DatasetAssign(xx[20000 * i : 20000 * (i + 1)]) - for i in range(5) - ], False - ) - kmeans(1000, data, 20) - - if "3" in todo: - # same, with GPU - ngpu = faiss.get_num_gpus() - print('using %d GPUs' % ngpu) - data = DatasetAssignDispatch([ - DatasetAssignGPU(xx[100000 * i // ngpu: 100000 * (i + 1) // ngpu], i) - for i in range(ngpu) - ], True - ) - kmeans(1000, data, 20) - - -def main(): - parser = argparse.ArgumentParser() - - def aa(*args, **kwargs): - group.add_argument(*args, **kwargs) - - group = parser.add_argument_group('general options') - aa('--test', default='', help='perform tests (comma-separated numbers)') - - aa('--k', default=0, type=int, help='nb centroids') - aa('--seed', default=1234, type=int, help='random seed') - aa('--niter', default=20, type=int, help='nb iterations') - aa('--gpu', default=-2, type=int, help='GPU to use (-2:none, -1: all)') - - group = parser.add_argument_group('I/O options') - aa('--indata', default='', - help='data file to load (supported formats fvecs, bvecs, npy') - aa('--i0', default=0, type=int, help='first vector to keep') - aa('--i1', default=-1, type=int, help='last vec to keep + 1') - aa('--out', default='', help='file to store centroids') - aa('--store_each_iteration', default=False, action='store_true', - help='store centroid checkpoints') - - group = parser.add_argument_group('server options') - aa('--server', action='store_true', default=False, help='run server') - aa('--port', default=12345, type=int, help='server port') - aa('--when_ready', default=None, help='store host:port to this file when ready') - aa('--ipv4', default=False, action='store_true', help='force ipv4') - - group = parser.add_argument_group('client options') - aa('--client', action='store_true', default=False, help='run client') - aa('--servers', default='', help='list of server:port separated by spaces') - - args = parser.parse_args() - - if args.test: - do_test(args.test.split(',')) - return - - # prepare data matrix (either local or remote) - if args.indata: - print('loading ', args.indata) - if args.indata.endswith('.bvecs'): - x = bvecs_mmap(args.indata) - elif args.indata.endswith('.fvecs'): - x = fvecs_mmap(args.indata) - elif args.indata.endswith('.npy'): - x = np.load(args.indata, mmap_mode='r') - else: - raise AssertionError - - if args.i1 == -1: - args.i1 = len(x) - x = x[args.i0:args.i1] - if args.gpu == -2: - data = DatasetAssign(x) - else: - print('moving to GPU') - data = DatasetAssignGPU(x, args.gpu) - - elif args.client: - print('connecting to servers') - - def connect_client(hostport): - host, port = hostport.split(':') - port = int(port) - print('connecting %s:%d' % (host, port)) - client = rpc.Client(host, port, v6=not args.ipv4) - print('client %s:%d ready' % (host, port)) - return client - - hostports = args.servers.strip().split(' ') - # pool = ThreadPool(len(hostports)) - - data = DatasetAssignDispatch( - list(map(connect_client, hostports)), - True - ) - else: - raise AssertionError - - - if args.server: - print('starting server') - log_prefix = f"{rpc.socket.gethostname()}:{args.port}" - rpc.run_server( - lambda s: AssignServer(s, data, log_prefix=log_prefix), - args.port, report_to_file=args.when_ready, - v6=not args.ipv4) - - else: - print('running kmeans') - centroids = kmeans(args.k, data, niter=args.niter, seed=args.seed, - checkpoint=args.out if args.store_each_iteration else None) - if args.out != '': - print('writing centroids to', args.out) - np.save(args.out, centroids) - - -if __name__ == '__main__': - main() diff --git a/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/distributed_query_demo.py b/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/distributed_query_demo.py deleted file mode 100644 index 9453c0ec27..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/distributed_query_demo.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -import os -import faiss -import numpy as np -import time -import rpc -import sys - -import combined_index -import search_server - -hostnames = sys.argv[1:] - -print("Load local index") -ci = combined_index.CombinedIndexDeep1B() - -print("connect to clients") -clients = [] -for host in hostnames: - client = rpc.Client(host, 12012, v6=False) - clients.append(client) - -# check if all servers respond -print("sizes seen by servers:", [cl.get_ntotal() for cl in clients]) - - -# aggregate all clients into a one that uses them all for speed -# note that it also requires a local index ci -sindex = search_server.SplitPerListIndex(ci, clients) -sindex.verbose = True - -# set reasonable parameters -ci.set_parallel_mode(1) -ci.set_prefetch_nthread(0) -ci.set_omp_num_threads(64) - -# initialize params -sindex.set_parallel_mode(1) -sindex.set_prefetch_nthread(0) -sindex.set_omp_num_threads(64) - -def ivecs_read(fname): - a = np.fromfile(fname, dtype='int32') - d = a[0] - return a.reshape(-1, d + 1)[:, 1:].copy() - -def fvecs_read(fname): - return ivecs_read(fname).view('float32') - - -deep1bdir = "/datasets01_101/simsearch/041218/deep1b/" - -xq = fvecs_read(deep1bdir + "deep1B_queries.fvecs") -gt_fname = deep1bdir + "deep1B_groundtruth.ivecs" -gt = ivecs_read(gt_fname) - - -for nprobe in 1, 10, 100, 1000: - sindex.set_nprobe(nprobe) - t0 = time.time() - D, I = sindex.search(xq, 100) - t1 = time.time() - print('nprobe=%d 1-recall@1=%.4f t=%.2fs' % ( - nprobe, (I[:, 0] == gt[:, 0]).sum() / len(xq), - t1 - t0 - )) diff --git a/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/make_index_vslice.py b/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/make_index_vslice.py deleted file mode 100644 index ca58425b25..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/make_index_vslice.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -import os -import time -import numpy as np -import faiss -import argparse -from multiprocessing.dummy import Pool as ThreadPool - -def ivecs_mmap(fname): - a = np.memmap(fname, dtype='int32', mode='r') - d = a[0] - return a.reshape(-1, d + 1)[:, 1:] - -def fvecs_mmap(fname): - return ivecs_mmap(fname).view('float32') - - -def produce_batches(args): - - x = fvecs_mmap(args.input) - - if args.i1 == -1: - args.i1 = len(x) - - print("Iterating on vectors %d:%d from %s by batches of size %d" % ( - args.i0, args.i1, args.input, args.bs)) - - for j0 in range(args.i0, args.i1, args.bs): - j1 = min(j0 + args.bs, args.i1) - yield np.arange(j0, j1), x[j0:j1] - - -def rate_limited_iter(l): - 'a thread pre-processes the next element' - pool = ThreadPool(1) - res = None - - def next_or_None(): - try: - return next(l) - except StopIteration: - return None - - while True: - res_next = pool.apply_async(next_or_None) - if res is not None: - res = res.get() - if res is None: - return - yield res - res = res_next - -deep1bdir = "/datasets01_101/simsearch/041218/deep1b/" -workdir = "/checkpoint/matthijs/ondisk_distributed/" - -def main(): - parser = argparse.ArgumentParser( - description='make index for a subset of the data') - - def aa(*args, **kwargs): - group.add_argument(*args, **kwargs) - - group = parser.add_argument_group('index type') - aa('--inputindex', - default=workdir + 'trained.faissindex', - help='empty input index to fill in') - aa('--nt', default=-1, type=int, help='nb of openmp threads to use') - - group = parser.add_argument_group('db options') - aa('--input', default=deep1bdir + "base.fvecs") - aa('--bs', default=2**18, type=int, - help='batch size for db access') - aa('--i0', default=0, type=int, help='lower bound to index') - aa('--i1', default=-1, type=int, help='upper bound of vectors to index') - - group = parser.add_argument_group('output') - aa('-o', default='/tmp/x', help='output index') - aa('--keepquantizer', default=False, action='store_true', - help='by default we remove the data from the quantizer to save space') - - args = parser.parse_args() - print('args=', args) - - print('start accessing data') - src = produce_batches(args) - - print('loading index', args.inputindex) - index = faiss.read_index(args.inputindex) - - if args.nt != -1: - faiss.omp_set_num_threads(args.nt) - - t0 = time.time() - ntot = 0 - for ids, x in rate_limited_iter(src): - print('add %d:%d (%.3f s)' % (ntot, ntot + ids.size, time.time() - t0)) - index.add_with_ids(np.ascontiguousarray(x, dtype='float32'), ids) - ntot += ids.size - - index_ivf = faiss.extract_index_ivf(index) - print('invlists stats: imbalance %.3f' % index_ivf.invlists.imbalance_factor()) - index_ivf.invlists.print_stats() - - if not args.keepquantizer: - print('resetting quantizer content') - index_ivf = faiss.extract_index_ivf(index) - index_ivf.quantizer.reset() - - print('store output', args.o) - faiss.write_index(index, args.o) - -if __name__ == '__main__': - main() diff --git a/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/make_trained_index.py b/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/make_trained_index.py deleted file mode 100644 index 50e4668f1b..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/make_trained_index.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -import numpy as np -import faiss - -deep1bdir = "/datasets01_101/simsearch/041218/deep1b/" -workdir = "/checkpoint/matthijs/ondisk_distributed/" - - -print('Load centroids') -centroids = np.load(workdir + '1M_centroids.npy') -ncent, d = centroids.shape - - -print('apply random rotation') -rrot = faiss.RandomRotationMatrix(d, d) -rrot.init(1234) -centroids = rrot.apply_py(centroids) - -print('make HNSW index as quantizer') -quantizer = faiss.IndexHNSWFlat(d, 32) -quantizer.hnsw.efSearch = 1024 -quantizer.hnsw.efConstruction = 200 -quantizer.add(centroids) - -print('build index') -index = faiss.IndexPreTransform( - rrot, - faiss.IndexIVFScalarQuantizer( - quantizer, d, ncent, faiss.ScalarQuantizer.QT_6bit - ) - ) - -def ivecs_mmap(fname): - a = np.memmap(fname, dtype='int32', mode='r') - d = a[0] - return a.reshape(-1, d + 1)[:, 1:] - -def fvecs_mmap(fname): - return ivecs_mmap(fname).view('float32') - - -print('finish training index') -xt = fvecs_mmap(deep1bdir + 'learn.fvecs') -xt = np.ascontiguousarray(xt[:256 * 1000], dtype='float32') -index.train(xt) - -print('write output') -faiss.write_index(index, workdir + 'trained.faissindex') diff --git a/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/merge_to_ondisk.py b/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/merge_to_ondisk.py deleted file mode 100644 index 5c8f3ace94..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/merge_to_ondisk.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -import os -import faiss -import argparse -from multiprocessing.dummy import Pool as ThreadPool - -if __name__ == '__main__': - - parser = argparse.ArgumentParser() - - parser.add_argument('--inputs', nargs='*', required=True, - help='input indexes to merge') - parser.add_argument('--l0', type=int, default=0) - parser.add_argument('--l1', type=int, default=-1) - - parser.add_argument('--nt', default=-1, - help='nb threads') - - parser.add_argument('--output', required=True, - help='output index filename') - parser.add_argument('--outputIL', - help='output invfile filename') - - args = parser.parse_args() - - if args.nt != -1: - print('set nb of threads to', args.nt) - - - ils = faiss.InvertedListsPtrVector() - ils_dont_dealloc = [] - - pool = ThreadPool(20) - - def load_index(fname): - print("loading", fname) - try: - index = faiss.read_index(fname, faiss.IO_FLAG_MMAP | faiss.IO_FLAG_READ_ONLY) - except RuntimeError as e: - print('could not load %s: %s' % (fname, e)) - return fname, None - - print(" %d entries" % index.ntotal) - return fname, index - - index0 = None - - for _, index in pool.imap(load_index, args.inputs): - if index is None: - continue - index_ivf = faiss.extract_index_ivf(index) - il = faiss.downcast_InvertedLists(index_ivf.invlists) - index_ivf.invlists = None - il.this.own() - ils_dont_dealloc.append(il) - if (args.l0, args.l1) != (0, -1): - print('restricting to lists %d:%d' % (args.l0, args.l1)) - # il = faiss.SliceInvertedLists(il, args.l0, args.l1) - - il.crop_invlists(args.l0, args.l1) - ils_dont_dealloc.append(il) - ils.push_back(il) - - if index0 is None: - index0 = index - - print("loaded %d invlists" % ils.size()) - - if not args.outputIL: - args.outputIL = args.output + '_invlists' - - il0 = ils.at(0) - - il = faiss.OnDiskInvertedLists( - il0.nlist, il0.code_size, - args.outputIL) - - print("perform merge") - - ntotal = il.merge_from(ils.data(), ils.size(), True) - - print("swap into index0") - - index0_ivf = faiss.extract_index_ivf(index0) - index0_ivf.nlist = il0.nlist - index0_ivf.ntotal = index0.ntotal = ntotal - index0_ivf.invlists = il - index0_ivf.own_invlists = False - - print("write", args.output) - - faiss.write_index(index0, args.output) diff --git a/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/rpc.py b/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/rpc.py deleted file mode 100644 index 7b248ea0a1..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/distributed_ondisk/rpc.py +++ /dev/null @@ -1,252 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#!/usr/bin/env python3 - -""" -Simplistic RPC implementation. -Exposes all functions of a Server object. - -Uses pickle for serialization and the socket interface. -""" - -import os,pdb,pickle,time,errno,sys,_thread,traceback,socket,threading,gc - - -# default -PORT=12032 - - -######################################################################### -# simple I/O functions - - - -def inline_send_handle(f, conn): - st = os.fstat(f.fileno()) - size = st.st_size - pickle.dump(size, conn) - conn.write(f.read(size)) - -def inline_send_string(s, conn): - size = len(s) - pickle.dump(size, conn) - conn.write(s) - - -class FileSock: - " wraps a socket so that it is usable by pickle/cPickle " - - def __init__(self,sock): - self.sock = sock - self.nr=0 - - def write(self, buf): - # print("sending %d bytes"%len(buf)) - #self.sock.sendall(buf) - # print("...done") - bs = 512 * 1024 - ns = 0 - while ns < len(buf): - sent = self.sock.send(buf[ns:ns + bs]) - ns += sent - - - def read(self,bs=512*1024): - #if self.nr==10000: pdb.set_trace() - self.nr+=1 - # print("read bs=%d"%bs) - b = [] - nb = 0 - while len(b) $workdir/vslices/slice$i.bash < $workdir/hslices/slice$i.bash <0 nodes have 32 links (theses ones are "cheap" to store - because there are fewer nodes in the upper levels. - -- `--indexfile $bdir/deep1M_PQ36_M6.index`: name of the index file - (without information for the L&C extension) - -- `--beta_nsq 4`: number of bytes to allocate for the codes (M in the - paper) - -- `--beta_centroids $bdir/deep1M_PQ36_M6_nsq4.npy`: filename to store - the trained beta centroids - -- `--neigh_recons_codes $bdir/deep1M_PQ36_M6_nsq4_codes.npy`: filename - for the encoded weights (beta) of the combination - -- `--k_reorder 0,5`: number of restults to reorder. 0 = baseline - without reordering, 5 = value used throughout the paper - -- `--efSearch 1,1024`: number of nodes to visit (T in the paper) - -The script will proceed with the following steps: - -0. load dataset (and possibly compute the ground-truth if the -ground-truth file is not provided) - -1. train the OPQ encoder - -2. build the index and store it - -3. compute the residuals and train the beta vocabulary to do the reconstuction - -4. encode the vertices - -5. search and evaluate the search results. - -With option `--exhaustive` the results of the exhaustive column can be -obtained. - -The run above should output: -``` -... -setting k_reorder=5 -... -efSearch=1024 0.3132 ms per query, R@1: 0.4283 R@10: 0.6337 R@100: 0.6520 ndis 40941919 nreorder 50000 - -``` -which matches the paper's table 2. - -Note that in multi-threaded mode, the building of the HNSW strcuture -is not deterministic. Therefore, the results across runs may not be exactly the same. - -Reproducing Figure 5 in the paper ---------------------------------- - -Figure 5 just evaluates the combination of HNSW and PQ. For example, -the operating point L6&OPQ40 can be obtained with - -``` -python bench_link_and_code.py \ - --db deep1M \ - --M0 6 \ - --indexkey OPQ40_160,HNSW32_PQ40 \ - --indexfile $bdir/deep1M_PQ40_M6.index \ - --beta_nsq 1 --beta_k 1 \ - --beta_centroids $bdir/deep1M_PQ40_M6_nsq0.npy \ - --neigh_recons_codes $bdir/deep1M_PQ36_M6_nsq0_codes.npy \ - --k_reorder 0 --efSearch 16,64,256,1024 -``` - -The arguments are similar to the previous table. Note that nsq = 0 is -simulated by setting beta_nsq = 1 and beta_k = 1 (ie a code with a single -reproduction value). - -The output should look like: - -``` -setting k_reorder=0 -efSearch=16 0.0147 ms per query, R@1: 0.3409 R@10: 0.4388 R@100: 0.4394 ndis 2629735 nreorder 0 -efSearch=64 0.0122 ms per query, R@1: 0.4836 R@10: 0.6490 R@100: 0.6509 ndis 4623221 nreorder 0 -efSearch=256 0.0344 ms per query, R@1: 0.5730 R@10: 0.7915 R@100: 0.7951 ndis 11090176 nreorder 0 -efSearch=1024 0.2656 ms per query, R@1: 0.6212 R@10: 0.8722 R@100: 0.8765 ndis 33501951 nreorder 0 -``` - -The results with k_reorder=5 are not reported in the paper, they -represent the performance of a "free coding" version of the algorithm. diff --git a/core/src/index/thirdparty/faiss/benchs/link_and_code/bench_link_and_code.py b/core/src/index/thirdparty/faiss/benchs/link_and_code/bench_link_and_code.py deleted file mode 100644 index 0b055169e4..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/link_and_code/bench_link_and_code.py +++ /dev/null @@ -1,304 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#!/usr/bin/env python2 - -import os -import sys -import time -import numpy as np -import re -import faiss -from multiprocessing.dummy import Pool as ThreadPool -import pdb -import argparse -import datasets -from datasets import sanitize -import neighbor_codec - -###################################################### -# Command-line parsing -###################################################### - - -parser = argparse.ArgumentParser() - -def aa(*args, **kwargs): - group.add_argument(*args, **kwargs) - -group = parser.add_argument_group('dataset options') - -aa('--db', default='deep1M', help='dataset') -aa( '--compute_gt', default=False, action='store_true', - help='compute and store the groundtruth') - -group = parser.add_argument_group('index consturction') - -aa('--indexkey', default='HNSW32', help='index_factory type') -aa('--efConstruction', default=200, type=int, - help='HNSW construction factor') -aa('--M0', default=-1, type=int, help='size of base level') -aa('--maxtrain', default=256 * 256, type=int, - help='maximum number of training points') -aa('--indexfile', default='', help='file to read or write index from') -aa('--add_bs', default=-1, type=int, - help='add elements index by batches of this size') -aa('--link_singletons', default=False, action='store_true', - help='do a pass to link in the singletons') - -group = parser.add_argument_group( - 'searching (reconstruct_from_neighbors options)') - -aa('--beta_centroids', default='', - help='file with codebook') -aa('--neigh_recons_codes', default='', - help='file with codes for reconstruction') -aa('--beta_ntrain', default=250000, type=int, help='') -aa('--beta_k', default=256, type=int, help='beta codebook size') -aa('--beta_nsq', default=1, type=int, help='number of beta sub-vectors') -aa('--beta_niter', default=10, type=int, help='') -aa('--k_reorder', default='-1', help='') - -group = parser.add_argument_group('searching') - -aa('--k', default=100, type=int, help='nb of nearest neighbors') -aa('--exhaustive', default=False, action='store_true', - help='report the exhaustive search topline') -aa('--searchthreads', default=-1, type=int, - help='nb of threads to use at search time') -aa('--efSearch', default='', type=str, - help='comma-separated values of efSearch to try') - -args = parser.parse_args() - -print "args:", args - - -###################################################### -# Load dataset -###################################################### - -xt, xb, xq, gt = datasets.load_data( - dataset=args.db, compute_gt=args.compute_gt) - -nq, d = xq.shape -nb, d = xb.shape - - -###################################################### -# Make index -###################################################### - -if os.path.exists(args.indexfile): - - print "reading", args.indexfile - index = faiss.read_index(args.indexfile) - - if isinstance(index, faiss.IndexPreTransform): - index_hnsw = faiss.downcast_index(index.index) - vec_transform = index.chain.at(0).apply_py - else: - index_hnsw = index - vec_transform = lambda x:x - - hnsw = index_hnsw.hnsw - hnsw_stats = faiss.cvar.hnsw_stats - -else: - - print "build index, key=", args.indexkey - - index = faiss.index_factory(d, args.indexkey) - - if isinstance(index, faiss.IndexPreTransform): - index_hnsw = faiss.downcast_index(index.index) - vec_transform = index.chain.at(0).apply_py - else: - index_hnsw = index - vec_transform = lambda x:x - - hnsw = index_hnsw.hnsw - hnsw.efConstruction = args.efConstruction - hnsw_stats = faiss.cvar.hnsw_stats - index.verbose = True - index_hnsw.verbose = True - index_hnsw.storage.verbose = True - - if args.M0 != -1: - print "set level 0 nb of neighbors to", args.M0 - hnsw.set_nb_neighbors(0, args.M0) - - xt2 = sanitize(xt[:args.maxtrain]) - assert np.all(np.isfinite(xt2)) - - print "train, size", xt.shape - t0 = time.time() - index.train(xt2) - print " train in %.3f s" % (time.time() - t0) - - print "adding" - t0 = time.time() - if args.add_bs == -1: - index.add(sanitize(xb)) - else: - for i0 in range(0, nb, args.add_bs): - i1 = min(nb, i0 + args.add_bs) - print " adding %d:%d / %d" % (i0, i1, nb) - index.add(sanitize(xb[i0:i1])) - - print " add in %.3f s" % (time.time() - t0) - print "storing", args.indexfile - faiss.write_index(index, args.indexfile) - - -###################################################### -# Train beta centroids and encode dataset -###################################################### - -if args.beta_centroids: - print "reordering links" - index_hnsw.reorder_links() - - if os.path.exists(args.beta_centroids): - print "load", args.beta_centroids - beta_centroids = np.load(args.beta_centroids) - nsq, k, M1 = beta_centroids.shape - assert M1 == hnsw.nb_neighbors(0) + 1 - - rfn = faiss.ReconstructFromNeighbors(index_hnsw, k, nsq) - else: - print "train beta centroids" - rfn = faiss.ReconstructFromNeighbors( - index_hnsw, args.beta_k, args.beta_nsq) - - xb_full = vec_transform(sanitize(xb[:args.beta_ntrain])) - - beta_centroids = neighbor_codec.train_beta_codebook( - rfn, xb_full, niter=args.beta_niter) - - print " storing", args.beta_centroids - np.save(args.beta_centroids, beta_centroids) - - - faiss.copy_array_to_vector(beta_centroids.ravel(), - rfn.codebook) - index_hnsw.reconstruct_from_neighbors = rfn - - if rfn.k == 1: - pass # no codes to take care of - elif os.path.exists(args.neigh_recons_codes): - print "loading neigh codes", args.neigh_recons_codes - codes = np.load(args.neigh_recons_codes) - assert codes.size == rfn.code_size * index.ntotal - faiss.copy_array_to_vector(codes.astype('uint8'), - rfn.codes) - rfn.ntotal = index.ntotal - else: - print "encoding neigh codes" - t0 = time.time() - - bs = 1000000 if args.add_bs == -1 else args.add_bs - - for i0 in range(0, nb, bs): - i1 = min(i0 + bs, nb) - print " encode %d:%d / %d [%.3f s]\r" % ( - i0, i1, nb, time.time() - t0), - sys.stdout.flush() - xbatch = vec_transform(sanitize(xb[i0:i1])) - rfn.add_codes(i1 - i0, faiss.swig_ptr(xbatch)) - print - - print "storing %s" % args.neigh_recons_codes - codes = faiss.vector_to_array(rfn.codes) - np.save(args.neigh_recons_codes, codes) - -###################################################### -# Exhaustive evaluation -###################################################### - -if args.exhaustive: - print "exhaustive evaluation" - xq_tr = vec_transform(sanitize(xq)) - index2 = faiss.IndexFlatL2(index_hnsw.d) - accu_recons_error = 0.0 - - if faiss.get_num_gpus() > 0: - print "do eval on GPU" - co = faiss.GpuMultipleClonerOptions() - co.shard = False - index2 = faiss.index_cpu_to_all_gpus(index2, co) - - # process in batches in case the dataset does not fit in RAM - rh = datasets.ResultHeap(xq_tr.shape[0], 100) - t0 = time.time() - bs = 500000 - for i0 in range(0, nb, bs): - i1 = min(nb, i0 + bs) - print ' handling batch %d:%d' % (i0, i1) - - xb_recons = np.empty( - (i1 - i0, index_hnsw.d), dtype='float32') - rfn.reconstruct_n(i0, i1 - i0, faiss.swig_ptr(xb_recons)) - - accu_recons_error += ( - (vec_transform(sanitize(xb[i0:i1])) - - xb_recons)**2).sum() - - index2.reset() - index2.add(xb_recons) - D, I = index2.search(xq_tr, 100) - rh.add_batch_result(D, I, i0) - - rh.finalize() - del index2 - t1 = time.time() - print "done in %.3f s" % (t1 - t0) - print "total reconstruction error: ", accu_recons_error - print "eval retrieval:" - datasets.evaluate_DI(rh.D, rh.I, gt) - - -def get_neighbors(hnsw, i, level): - " list the neighbors for node i at level " - assert i < hnsw.levels.size() - assert level < hnsw.levels.at(i) - be = np.empty(2, 'uint64') - hnsw.neighbor_range(i, level, faiss.swig_ptr(be), faiss.swig_ptr(be[1:])) - return [hnsw.neighbors.at(j) for j in range(be[0], be[1])] - - -############################################################# -# Index is ready -############################################################# - -xq = sanitize(xq) - -if args.searchthreads != -1: - print "Setting nb of threads to", args.searchthreads - faiss.omp_set_num_threads(args.searchthreads) - - -if gt is None: - print "no valid groundtruth -- exit" - sys.exit() - - -k_reorders = [int(x) for x in args.k_reorder.split(',')] -efSearchs = [int(x) for x in args.efSearch.split(',')] - - -for k_reorder in k_reorders: - - if index_hnsw.reconstruct_from_neighbors: - print "setting k_reorder=%d" % k_reorder - index_hnsw.reconstruct_from_neighbors.k_reorder = k_reorder - - for efSearch in efSearchs: - print "efSearch=%-4d" % efSearch, - hnsw.efSearch = efSearch - hnsw_stats.reset() - datasets.evaluate(xq, gt, index, k=args.k, endl=False) - - print "ndis %d nreorder %d" % (hnsw_stats.ndis, hnsw_stats.nreorder) diff --git a/core/src/index/thirdparty/faiss/benchs/link_and_code/datasets.py b/core/src/index/thirdparty/faiss/benchs/link_and_code/datasets.py deleted file mode 100644 index ce1379f408..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/link_and_code/datasets.py +++ /dev/null @@ -1,235 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#! /usr/bin/env python2 - -""" -Common functions to load datasets and compute their ground-truth -""" - -import time -import numpy as np -import faiss -import pdb -import sys - -# set this to the directory that contains the datafiles. -# deep1b data should be at simdir + 'deep1b' -# bigann data should be at simdir + 'bigann' -simdir = '/mnt/vol/gfsai-east/ai-group/datasets/simsearch/' - -################################################################# -# Small I/O functions -################################################################# - - -def ivecs_read(fname): - a = np.fromfile(fname, dtype='int32') - d = a[0] - return a.reshape(-1, d + 1)[:, 1:].copy() - - -def fvecs_read(fname): - return ivecs_read(fname).view('float32') - - -def ivecs_mmap(fname): - a = np.memmap(fname, dtype='int32', mode='r') - d = a[0] - return a.reshape(-1, d + 1)[:, 1:] - - -def fvecs_mmap(fname): - return ivecs_mmap(fname).view('float32') - - -def bvecs_mmap(fname): - x = np.memmap(fname, dtype='uint8', mode='r') - d = x[:4].view('int32')[0] - return x.reshape(-1, d + 4)[:, 4:] - - -def ivecs_write(fname, m): - n, d = m.shape - m1 = np.empty((n, d + 1), dtype='int32') - m1[:, 0] = d - m1[:, 1:] = m - m1.tofile(fname) - - -def fvecs_write(fname, m): - m = m.astype('float32') - ivecs_write(fname, m.view('int32')) - - -################################################################# -# Dataset -################################################################# - -def sanitize(x): - return np.ascontiguousarray(x, dtype='float32') - - -class ResultHeap: - """ Combine query results from a sliced dataset """ - - def __init__(self, nq, k): - " nq: number of query vectors, k: number of results per query " - self.I = np.zeros((nq, k), dtype='int64') - self.D = np.zeros((nq, k), dtype='float32') - self.nq, self.k = nq, k - heaps = faiss.float_maxheap_array_t() - heaps.k = k - heaps.nh = nq - heaps.val = faiss.swig_ptr(self.D) - heaps.ids = faiss.swig_ptr(self.I) - heaps.heapify() - self.heaps = heaps - - def add_batch_result(self, D, I, i0): - assert D.shape == (self.nq, self.k) - assert I.shape == (self.nq, self.k) - I += i0 - self.heaps.addn_with_ids( - self.k, faiss.swig_ptr(D), - faiss.swig_ptr(I), self.k) - - def finalize(self): - self.heaps.reorder() - - - -def compute_GT_sliced(xb, xq, k): - print "compute GT" - t0 = time.time() - nb, d = xb.shape - nq, d = xq.shape - rh = ResultHeap(nq, k) - bs = 10 ** 5 - - xqs = sanitize(xq) - - db_gt = faiss.index_cpu_to_all_gpus(faiss.IndexFlatL2(d)) - - # compute ground-truth by blocks of bs, and add to heaps - for i0 in range(0, nb, bs): - i1 = min(nb, i0 + bs) - xsl = sanitize(xb[i0:i1]) - db_gt.add(xsl) - D, I = db_gt.search(xqs, k) - rh.add_batch_result(D, I, i0) - db_gt.reset() - print "\r %d/%d, %.3f s" % (i0, nb, time.time() - t0), - sys.stdout.flush() - print - rh.finalize() - gt_I = rh.I - - print "GT time: %.3f s" % (time.time() - t0) - return gt_I - - -def do_compute_gt(xb, xq, k): - print "computing GT" - nb, d = xb.shape - index = faiss.index_cpu_to_all_gpus(faiss.IndexFlatL2(d)) - if nb < 100 * 1000: - print " add" - index.add(np.ascontiguousarray(xb, dtype='float32')) - print " search" - D, I = index.search(np.ascontiguousarray(xq, dtype='float32'), k) - else: - I = compute_GT_sliced(xb, xq, k) - - return I.astype('int32') - - -def load_data(dataset='deep1M', compute_gt=False): - - print "load data", dataset - - if dataset == 'sift1M': - basedir = simdir + 'sift1M/' - - xt = fvecs_read(basedir + "sift_learn.fvecs") - xb = fvecs_read(basedir + "sift_base.fvecs") - xq = fvecs_read(basedir + "sift_query.fvecs") - gt = ivecs_read(basedir + "sift_groundtruth.ivecs") - - elif dataset.startswith('bigann'): - basedir = simdir + 'bigann/' - - dbsize = 1000 if dataset == "bigann1B" else int(dataset[6:-1]) - xb = bvecs_mmap(basedir + 'bigann_base.bvecs') - xq = bvecs_mmap(basedir + 'bigann_query.bvecs') - xt = bvecs_mmap(basedir + 'bigann_learn.bvecs') - # trim xb to correct size - xb = xb[:dbsize * 1000 * 1000] - gt = ivecs_read(basedir + 'gnd/idx_%dM.ivecs' % dbsize) - - elif dataset.startswith("deep"): - basedir = simdir + 'deep1b/' - szsuf = dataset[4:] - if szsuf[-1] == 'M': - dbsize = 10 ** 6 * int(szsuf[:-1]) - elif szsuf == '1B': - dbsize = 10 ** 9 - elif szsuf[-1] == 'k': - dbsize = 1000 * int(szsuf[:-1]) - else: - assert False, "did not recognize suffix " + szsuf - - xt = fvecs_mmap(basedir + "learn.fvecs") - xb = fvecs_mmap(basedir + "base.fvecs") - xq = fvecs_read(basedir + "deep1B_queries.fvecs") - - xb = xb[:dbsize] - - gt_fname = basedir + "%s_groundtruth.ivecs" % dataset - if compute_gt: - gt = do_compute_gt(xb, xq, 100) - print "store", gt_fname - ivecs_write(gt_fname, gt) - - gt = ivecs_read(gt_fname) - - else: - assert False - - print "dataset %s sizes: B %s Q %s T %s" % ( - dataset, xb.shape, xq.shape, xt.shape) - - return xt, xb, xq, gt - -################################################################# -# Evaluation -################################################################# - - -def evaluate_DI(D, I, gt): - nq = gt.shape[0] - k = I.shape[1] - rank = 1 - while rank <= k: - recall = (I[:, :rank] == gt[:, :1]).sum() / float(nq) - print "R@%d: %.4f" % (rank, recall), - rank *= 10 - - -def evaluate(xq, gt, index, k=100, endl=True): - t0 = time.time() - D, I = index.search(xq, k) - t1 = time.time() - nq = xq.shape[0] - print "\t %8.4f ms per query, " % ( - (t1 - t0) * 1000.0 / nq), - rank = 1 - while rank <= k: - recall = (I[:, :rank] == gt[:, :1]).sum() / float(nq) - print "R@%d: %.4f" % (rank, recall), - rank *= 10 - if endl: - print - return D, I diff --git a/core/src/index/thirdparty/faiss/benchs/link_and_code/neighbor_codec.py b/core/src/index/thirdparty/faiss/benchs/link_and_code/neighbor_codec.py deleted file mode 100644 index 3869a2c109..0000000000 --- a/core/src/index/thirdparty/faiss/benchs/link_and_code/neighbor_codec.py +++ /dev/null @@ -1,239 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#! /usr/bin/env python2 - -""" -This is the training code for the link and code. Especially the -neighbors_kmeans function implements the EM-algorithm to find the -appropriate weightings and cluster them. -""" - -import time -import numpy as np -import faiss - -#---------------------------------------------------------- -# Utils -#---------------------------------------------------------- - -def sanitize(x): - return np.ascontiguousarray(x, dtype='float32') - - -def train_kmeans(x, k, ngpu, max_points_per_centroid=256): - "Runs kmeans on one or several GPUs" - d = x.shape[1] - clus = faiss.Clustering(d, k) - clus.verbose = True - clus.niter = 20 - clus.max_points_per_centroid = max_points_per_centroid - - if ngpu == 0: - index = faiss.IndexFlatL2(d) - else: - res = [faiss.StandardGpuResources() for i in range(ngpu)] - - flat_config = [] - for i in range(ngpu): - cfg = faiss.GpuIndexFlatConfig() - cfg.useFloat16 = False - cfg.device = i - flat_config.append(cfg) - - if ngpu == 1: - index = faiss.GpuIndexFlatL2(res[0], d, flat_config[0]) - else: - indexes = [faiss.GpuIndexFlatL2(res[i], d, flat_config[i]) - for i in range(ngpu)] - index = faiss.IndexReplicas() - for sub_index in indexes: - index.addIndex(sub_index) - - # perform the training - clus.train(x, index) - centroids = faiss.vector_float_to_array(clus.centroids) - - obj = faiss.vector_float_to_array(clus.obj) - print "final objective: %.4g" % obj[-1] - - return centroids.reshape(k, d) - - -#---------------------------------------------------------- -# Learning the codebook from neighbors -#---------------------------------------------------------- - - -# works with both a full Inn table and dynamically generated neighbors - -def get_Inn_shape(Inn): - if type(Inn) != tuple: - return Inn.shape - return Inn[:2] - -def get_neighbor_table(x_coded, Inn, i): - if type(Inn) != tuple: - return x_coded[Inn[i,:],:] - rfn = x_coded - M, d = rfn.M, rfn.index.d - out = np.zeros((M + 1, d), dtype='float32') - rfn.get_neighbor_table(i, faiss.swig_ptr(out)) - _, _, sq = Inn - return out[:, sq * rfn.dsub : (sq + 1) * rfn.dsub] - - -# Function that produces the best regression values from the vector -# and its neighbors -def regress_from_neighbors (x, x_coded, Inn): - (N, knn) = get_Inn_shape(Inn) - betas = np.zeros((N,knn)) - t0 = time.time() - for i in xrange (N): - xi = x[i,:] - NNi = get_neighbor_table(x_coded, Inn, i) - betas[i,:] = np.linalg.lstsq(NNi.transpose(), xi, rcond=0.01)[0] - if i % (N / 10) == 0: - print ("[%d:%d] %6.3fs" % (i, i + N / 10, time.time() - t0)) - return betas - - - -# find the best beta minimizing ||x-x_coded[Inn,:]*beta||^2 -def regress_opt_beta (x, x_coded, Inn): - (N, knn) = get_Inn_shape(Inn) - d = x.shape[1] - - # construct the linear system to be solved - X = np.zeros ((d*N)) - Y = np.zeros ((d*N, knn)) - for i in xrange (N): - X[i*d:(i+1)*d] = x[i,:] - neighbor_table = get_neighbor_table(x_coded, Inn, i) - Y[i*d:(i+1)*d, :] = neighbor_table.transpose() - beta_opt = np.linalg.lstsq(Y, X, rcond=0.01)[0] - return beta_opt - - -# Find the best encoding by minimizing the reconstruction error using -# a set of pre-computed beta values -def assign_beta (beta_centroids, x, x_coded, Inn, verbose=True): - if type(Inn) == tuple: - return assign_beta_2(beta_centroids, x, x_coded, Inn) - (N, knn) = Inn.shape - x_ibeta = np.zeros ((N), dtype='int32') - t0= time.time() - for i in xrange (N): - NNi = x_coded[Inn[i,:]] - # Consider all possible betas for the encoding and compute the - # encoding error - x_reg_all = np.dot (beta_centroids, NNi) - err = ((x_reg_all - x[i,:]) ** 2).sum(axis=1) - x_ibeta[i] = err.argmin() - if verbose: - if i % (N / 10) == 0: - print ("[%d:%d] %6.3fs" % (i, i + N / 10, time.time() - t0)) - return x_ibeta - - -# Reconstruct a set of vectors using the beta_centroids, the -# assignment, the encoded neighbors identified by the list Inn (which -# includes the vector itself) -def recons_from_neighbors (beta_centroids, x_ibeta, x_coded, Inn): - (N, knn) = Inn.shape - x_rec = np.zeros(x_coded.shape) - t0= time.time() - for i in xrange (N): - NNi = x_coded[Inn[i,:]] - x_rec[i, :] = np.dot (beta_centroids[x_ibeta[i]], NNi) - if i % (N / 10) == 0: - print ("[%d:%d] %6.3fs" % (i, i + N / 10, time.time() - t0)) - return x_rec - - -# Compute a EM-like algorithm trying at optimizing the beta such as they -# minimize the reconstruction error from the neighbors -def neighbors_kmeans (x, x_coded, Inn, K, ngpus=1, niter=5): - # First compute centroids using a regular k-means algorithm - betas = regress_from_neighbors (x, x_coded, Inn) - beta_centroids = train_kmeans( - sanitize(betas), K, ngpus, max_points_per_centroid=1000000) - _, knn = get_Inn_shape(Inn) - d = x.shape[1] - - rs = np.random.RandomState() - for iter in range(niter): - print 'iter', iter - idx = assign_beta (beta_centroids, x, x_coded, Inn, verbose=False) - - hist = np.bincount(idx) - for cl0 in np.where(hist == 0)[0]: - print " cluster %d empty, split" % cl0, - cl1 = idx[np.random.randint(idx.size)] - pos = np.nonzero (idx == cl1)[0] - pos = rs.choice(pos, pos.size / 2) - print " cl %d -> %d + %d" % (cl1, len(pos), hist[cl1] - len(pos)) - idx[pos] = cl0 - hist = np.bincount(idx) - - tot_err = 0 - for k in range (K): - pos = np.nonzero (idx == k)[0] - npos = pos.shape[0] - - X = np.zeros (d*npos) - Y = np.zeros ((d*npos, knn)) - - for i in range(npos): - X[i*d:(i+1)*d] = x[pos[i],:] - neighbor_table = get_neighbor_table(x_coded, Inn, pos[i]) - Y[i*d:(i+1)*d, :] = neighbor_table.transpose() - sol, residuals, _, _ = np.linalg.lstsq(Y, X, rcond=0.01) - if residuals.size > 0: - tot_err += residuals.sum() - beta_centroids[k, :] = sol - print ' err=%g' % tot_err - return beta_centroids - - -# assign the betas in C++ -def assign_beta_2(beta_centroids, x, rfn, Inn): - _, _, sq = Inn - if rfn.k == 1: - return np.zeros(x.shape[0], dtype=int) - # add dummy dimensions to beta_centroids and x - all_beta_centroids = np.zeros( - (rfn.nsq, rfn.k, rfn.M + 1), dtype='float32') - all_beta_centroids[sq] = beta_centroids - all_x = np.zeros((len(x), rfn.d), dtype='float32') - all_x[:, sq * rfn.dsub : (sq + 1) * rfn.dsub] = x - rfn.codes.clear() - rfn.ntotal = 0 - faiss.copy_array_to_vector( - all_beta_centroids.ravel(), rfn.codebook) - rfn.add_codes(len(x), faiss.swig_ptr(all_x)) - codes = faiss.vector_to_array(rfn.codes) - codes = codes.reshape(-1, rfn.nsq) - return codes[:, sq] - - -####################################################### -# For usage from bench_storages.py - -def train_beta_codebook(rfn, xb_full, niter=10): - beta_centroids = [] - for sq in range(rfn.nsq): - d0, d1 = sq * rfn.dsub, (sq + 1) * rfn.dsub - print "training subquantizer %d/%d on dimensions %d:%d" % ( - sq, rfn.nsq, d0, d1) - beta_centroids_i = neighbors_kmeans( - xb_full[:, d0:d1], rfn, (xb_full.shape[0], rfn.M + 1, sq), - rfn.k, - ngpus=0, niter=niter) - beta_centroids.append(beta_centroids_i) - rfn.ntotal = 0 - rfn.codes.clear() - rfn.codebook.clear() - return np.stack(beta_centroids) diff --git a/core/src/index/thirdparty/faiss/build-aux/config.guess b/core/src/index/thirdparty/faiss/build-aux/config.guess deleted file mode 100755 index 2193702b12..0000000000 --- a/core/src/index/thirdparty/faiss/build-aux/config.guess +++ /dev/null @@ -1,1473 +0,0 @@ -#! /bin/sh -# Attempt to guess a canonical system name. -# Copyright 1992-2017 Free Software Foundation, Inc. - -timestamp='2017-05-27' - -# This file is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see . -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that -# program. This Exception is an additional permission under section 7 -# of the GNU General Public License, version 3 ("GPLv3"). -# -# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. -# -# You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess -# -# Please send patches to . - - -me=`echo "$0" | sed -e 's,.*/,,'` - -usage="\ -Usage: $0 [OPTION] - -Output the configuration name of the system \`$me' is run on. - -Operation modes: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.guess ($timestamp) - -Originally written by Per Bothner. -Copyright 1992-2017 Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit ;; - --version | -v ) - echo "$version" ; exit ;; - --help | --h* | -h ) - echo "$usage"; exit ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" >&2 - exit 1 ;; - * ) - break ;; - esac -done - -if test $# != 0; then - echo "$me: too many arguments$help" >&2 - exit 1 -fi - -trap 'exit 1' 1 2 15 - -# CC_FOR_BUILD -- compiler used by this script. Note that the use of a -# compiler to aid in system detection is discouraged as it requires -# temporary files to be created and, as you can see below, it is a -# headache to deal with in a portable fashion. - -# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still -# use `HOST_CC' if defined, but it is deprecated. - -# Portable tmp directory creation inspired by the Autoconf team. - -set_cc_for_build=' -trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; -trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; -: ${TMPDIR=/tmp} ; - { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || - { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || - { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || - { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; -dummy=$tmp/dummy ; -tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; -case $CC_FOR_BUILD,$HOST_CC,$CC in - ,,) echo "int x;" > $dummy.c ; - for c in cc gcc c89 c99 ; do - if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then - CC_FOR_BUILD="$c"; break ; - fi ; - done ; - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found ; - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; -esac ; set_cc_for_build= ;' - -# This is needed to find uname on a Pyramid OSx when run in the BSD universe. -# (ghazi@noc.rutgers.edu 1994-08-24) -if (test -f /.attbin/uname) >/dev/null 2>&1 ; then - PATH=$PATH:/.attbin ; export PATH -fi - -UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown -UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown -UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown -UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown - -case "${UNAME_SYSTEM}" in -Linux|GNU|GNU/*) - # If the system lacks a compiler, then just pick glibc. - # We could probably try harder. - LIBC=gnu - - eval $set_cc_for_build - cat <<-EOF > $dummy.c - #include - #if defined(__UCLIBC__) - LIBC=uclibc - #elif defined(__dietlibc__) - LIBC=dietlibc - #else - LIBC=gnu - #endif - EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` - ;; -esac - -# Note: order is significant - the case branches are not exclusive. - -case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in - *:NetBSD:*:*) - # NetBSD (nbsd) targets should (where applicable) match one or - # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, - # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently - # switched to ELF, *-*-netbsd* would select the old - # object file format. This provides both forward - # compatibility and a consistent mechanism for selecting the - # object file format. - # - # Note: NetBSD doesn't particularly care about the vendor - # portion of the name. We always set it to "unknown". - sysctl="sysctl -n hw.machine_arch" - UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ - /sbin/$sysctl 2>/dev/null || \ - /usr/sbin/$sysctl 2>/dev/null || \ - echo unknown)` - case "${UNAME_MACHINE_ARCH}" in - armeb) machine=armeb-unknown ;; - arm*) machine=arm-unknown ;; - sh3el) machine=shl-unknown ;; - sh3eb) machine=sh-unknown ;; - sh5el) machine=sh5le-unknown ;; - earmv*) - arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'` - endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'` - machine=${arch}${endian}-unknown - ;; - *) machine=${UNAME_MACHINE_ARCH}-unknown ;; - esac - # The Operating System including object format, if it has switched - # to ELF recently (or will in the future) and ABI. - case "${UNAME_MACHINE_ARCH}" in - earm*) - os=netbsdelf - ;; - arm*|i386|m68k|ns32k|sh3*|sparc|vax) - eval $set_cc_for_build - if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ELF__ - then - # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). - # Return netbsd for either. FIX? - os=netbsd - else - os=netbsdelf - fi - ;; - *) - os=netbsd - ;; - esac - # Determine ABI tags. - case "${UNAME_MACHINE_ARCH}" in - earm*) - expr='s/^earmv[0-9]/-eabi/;s/eb$//' - abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"` - ;; - esac - # The OS release - # Debian GNU/NetBSD machines have a different userland, and - # thus, need a distinct triplet. However, they do not need - # kernel version information, so it can be replaced with a - # suitable tag, in the style of linux-gnu. - case "${UNAME_VERSION}" in - Debian*) - release='-gnu' - ;; - *) - release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2` - ;; - esac - # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: - # contains redundant information, the shorter form: - # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "${machine}-${os}${release}${abi}" - exit ;; - *:Bitrig:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} - exit ;; - *:OpenBSD:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} - exit ;; - *:LibertyBSD:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE} - exit ;; - *:ekkoBSD:*:*) - echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} - exit ;; - *:SolidBSD:*:*) - echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} - exit ;; - macppc:MirBSD:*:*) - echo powerpc-unknown-mirbsd${UNAME_RELEASE} - exit ;; - *:MirBSD:*:*) - echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} - exit ;; - *:Sortix:*:*) - echo ${UNAME_MACHINE}-unknown-sortix - exit ;; - alpha:OSF1:*:*) - case $UNAME_RELEASE in - *4.0) - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` - ;; - *5.*) - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` - ;; - esac - # According to Compaq, /usr/sbin/psrinfo has been available on - # OSF/1 and Tru64 systems produced since 1995. I hope that - # covers most systems running today. This code pipes the CPU - # types through head -n 1, so we only detect the type of CPU 0. - ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` - case "$ALPHA_CPU_TYPE" in - "EV4 (21064)") - UNAME_MACHINE=alpha ;; - "EV4.5 (21064)") - UNAME_MACHINE=alpha ;; - "LCA4 (21066/21068)") - UNAME_MACHINE=alpha ;; - "EV5 (21164)") - UNAME_MACHINE=alphaev5 ;; - "EV5.6 (21164A)") - UNAME_MACHINE=alphaev56 ;; - "EV5.6 (21164PC)") - UNAME_MACHINE=alphapca56 ;; - "EV5.7 (21164PC)") - UNAME_MACHINE=alphapca57 ;; - "EV6 (21264)") - UNAME_MACHINE=alphaev6 ;; - "EV6.7 (21264A)") - UNAME_MACHINE=alphaev67 ;; - "EV6.8CB (21264C)") - UNAME_MACHINE=alphaev68 ;; - "EV6.8AL (21264B)") - UNAME_MACHINE=alphaev68 ;; - "EV6.8CX (21264D)") - UNAME_MACHINE=alphaev68 ;; - "EV6.9A (21264/EV69A)") - UNAME_MACHINE=alphaev69 ;; - "EV7 (21364)") - UNAME_MACHINE=alphaev7 ;; - "EV7.9 (21364A)") - UNAME_MACHINE=alphaev79 ;; - esac - # A Pn.n version is a patched version. - # A Vn.n version is a released version. - # A Tn.n version is a released field test version. - # A Xn.n version is an unreleased experimental baselevel. - # 1.2 uses "1.2" for uname -r. - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` - # Reset EXIT trap before exiting to avoid spurious non-zero exit code. - exitcode=$? - trap '' 0 - exit $exitcode ;; - Alpha\ *:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # Should we change UNAME_MACHINE based on the output of uname instead - # of the specific Alpha model? - echo alpha-pc-interix - exit ;; - 21064:Windows_NT:50:3) - echo alpha-dec-winnt3.5 - exit ;; - Amiga*:UNIX_System_V:4.0:*) - echo m68k-unknown-sysv4 - exit ;; - *:[Aa]miga[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-amigaos - exit ;; - *:[Mm]orph[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-morphos - exit ;; - *:OS/390:*:*) - echo i370-ibm-openedition - exit ;; - *:z/VM:*:*) - echo s390-ibm-zvmoe - exit ;; - *:OS400:*:*) - echo powerpc-ibm-os400 - exit ;; - arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix${UNAME_RELEASE} - exit ;; - arm*:riscos:*:*|arm*:RISCOS:*:*) - echo arm-unknown-riscos - exit ;; - SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) - echo hppa1.1-hitachi-hiuxmpp - exit ;; - Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) - # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "`(/bin/universe) 2>/dev/null`" = att ; then - echo pyramid-pyramid-sysv3 - else - echo pyramid-pyramid-bsd - fi - exit ;; - NILE*:*:*:dcosx) - echo pyramid-pyramid-svr4 - exit ;; - DRS?6000:unix:4.0:6*) - echo sparc-icl-nx6 - exit ;; - DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) - case `/usr/bin/uname -p` in - sparc) echo sparc-icl-nx7; exit ;; - esac ;; - s390x:SunOS:*:*) - echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) - echo i386-pc-auroraux${UNAME_RELEASE} - exit ;; - i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) - eval $set_cc_for_build - SUN_ARCH=i386 - # If there is a compiler, see if it is configured for 64-bit objects. - # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. - # This test works for both compilers. - if [ "$CC_FOR_BUILD" != no_compiler_found ]; then - if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - SUN_ARCH=x86_64 - fi - fi - echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4*:SunOS:6*:*) - # According to config.sub, this is the proper way to canonicalize - # SunOS6. Hard to guess exactly what SunOS6 will be like, but - # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4*:SunOS:*:*) - case "`/usr/bin/arch -k`" in - Series*|S4*) - UNAME_RELEASE=`uname -v` - ;; - esac - # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` - exit ;; - sun3*:SunOS:*:*) - echo m68k-sun-sunos${UNAME_RELEASE} - exit ;; - sun*:*:4.2BSD:*) - UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x${UNAME_RELEASE}" = x && UNAME_RELEASE=3 - case "`/bin/arch`" in - sun3) - echo m68k-sun-sunos${UNAME_RELEASE} - ;; - sun4) - echo sparc-sun-sunos${UNAME_RELEASE} - ;; - esac - exit ;; - aushp:SunOS:*:*) - echo sparc-auspex-sunos${UNAME_RELEASE} - exit ;; - # The situation for MiNT is a little confusing. The machine name - # can be virtually everything (everything which is not - # "atarist" or "atariste" at least should have a processor - # > m68000). The system name ranges from "MiNT" over "FreeMiNT" - # to the lowercase version "mint" (or "freemint"). Finally - # the system name "TOS" denotes a system which is actually not - # MiNT. But MiNT is downward compatible to TOS, so this should - # be no problem. - atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; - atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; - *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; - milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint${UNAME_RELEASE} - exit ;; - hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint${UNAME_RELEASE} - exit ;; - *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint${UNAME_RELEASE} - exit ;; - m68k:machten:*:*) - echo m68k-apple-machten${UNAME_RELEASE} - exit ;; - powerpc:machten:*:*) - echo powerpc-apple-machten${UNAME_RELEASE} - exit ;; - RISC*:Mach:*:*) - echo mips-dec-mach_bsd4.3 - exit ;; - RISC*:ULTRIX:*:*) - echo mips-dec-ultrix${UNAME_RELEASE} - exit ;; - VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix${UNAME_RELEASE} - exit ;; - 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix${UNAME_RELEASE} - exit ;; - mips:*:*:UMIPS | mips:*:*:RISCos) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c -#ifdef __cplusplus -#include /* for printf() prototype */ - int main (int argc, char *argv[]) { -#else - int main (argc, argv) int argc; char *argv[]; { -#endif - #if defined (host_mips) && defined (MIPSEB) - #if defined (SYSTYPE_SYSV) - printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_SVR4) - printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) - printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); - #endif - #endif - exit (-1); - } -EOF - $CC_FOR_BUILD -o $dummy $dummy.c && - dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && - SYSTEM_NAME=`$dummy $dummyarg` && - { echo "$SYSTEM_NAME"; exit; } - echo mips-mips-riscos${UNAME_RELEASE} - exit ;; - Motorola:PowerMAX_OS:*:*) - echo powerpc-motorola-powermax - exit ;; - Motorola:*:4.3:PL8-*) - echo powerpc-harris-powermax - exit ;; - Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) - echo powerpc-harris-powermax - exit ;; - Night_Hawk:Power_UNIX:*:*) - echo powerpc-harris-powerunix - exit ;; - m88k:CX/UX:7*:*) - echo m88k-harris-cxux7 - exit ;; - m88k:*:4*:R4*) - echo m88k-motorola-sysv4 - exit ;; - m88k:*:3*:R3*) - echo m88k-motorola-sysv3 - exit ;; - AViiON:dgux:*:*) - # DG/UX returns AViiON for all architectures - UNAME_PROCESSOR=`/usr/bin/uname -p` - if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] - then - if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ - [ ${TARGET_BINARY_INTERFACE}x = x ] - then - echo m88k-dg-dgux${UNAME_RELEASE} - else - echo m88k-dg-dguxbcs${UNAME_RELEASE} - fi - else - echo i586-dg-dgux${UNAME_RELEASE} - fi - exit ;; - M88*:DolphinOS:*:*) # DolphinOS (SVR3) - echo m88k-dolphin-sysv3 - exit ;; - M88*:*:R3*:*) - # Delta 88k system running SVR3 - echo m88k-motorola-sysv3 - exit ;; - XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) - echo m88k-tektronix-sysv3 - exit ;; - Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) - echo m68k-tektronix-bsd - exit ;; - *:IRIX*:*:*) - echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` - exit ;; - ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. - echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id - exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' - i*86:AIX:*:*) - echo i386-ibm-aix - exit ;; - ia64:AIX:*:*) - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` - else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} - fi - echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} - exit ;; - *:AIX:2:3) - if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - - main() - { - if (!__power_pc()) - exit(1); - puts("powerpc-ibm-aix3.2.5"); - exit(0); - } -EOF - if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` - then - echo "$SYSTEM_NAME" - else - echo rs6000-ibm-aix3.2.5 - fi - elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then - echo rs6000-ibm-aix3.2.4 - else - echo rs6000-ibm-aix3.2 - fi - exit ;; - *:AIX:*:[4567]) - IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` - if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then - IBM_ARCH=rs6000 - else - IBM_ARCH=powerpc - fi - if [ -x /usr/bin/lslpp ] ; then - IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | - awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` - else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} - fi - echo ${IBM_ARCH}-ibm-aix${IBM_REV} - exit ;; - *:AIX:*:*) - echo rs6000-ibm-aix - exit ;; - ibmrt:4.4BSD:*|romp-ibm:BSD:*) - echo romp-ibm-bsd4.4 - exit ;; - ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to - exit ;; # report: romp-ibm BSD 4.3 - *:BOSX:*:*) - echo rs6000-bull-bosx - exit ;; - DPX/2?00:B.O.S.:*:*) - echo m68k-bull-sysv3 - exit ;; - 9000/[34]??:4.3bsd:1.*:*) - echo m68k-hp-bsd - exit ;; - hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) - echo m68k-hp-bsd4.4 - exit ;; - 9000/[34678]??:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - case "${UNAME_MACHINE}" in - 9000/31? ) HP_ARCH=m68000 ;; - 9000/[34]?? ) HP_ARCH=m68k ;; - 9000/[678][0-9][0-9]) - if [ -x /usr/bin/getconf ]; then - sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` - sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` - case "${sc_cpu_version}" in - 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 - 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 - 532) # CPU_PA_RISC2_0 - case "${sc_kernel_bits}" in - 32) HP_ARCH=hppa2.0n ;; - 64) HP_ARCH=hppa2.0w ;; - '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 - esac ;; - esac - fi - if [ "${HP_ARCH}" = "" ]; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - - #define _HPUX_SOURCE - #include - #include - - int main () - { - #if defined(_SC_KERNEL_BITS) - long bits = sysconf(_SC_KERNEL_BITS); - #endif - long cpu = sysconf (_SC_CPU_VERSION); - - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1"); break; - case CPU_PA_RISC2_0: - #if defined(_SC_KERNEL_BITS) - switch (bits) - { - case 64: puts ("hppa2.0w"); break; - case 32: puts ("hppa2.0n"); break; - default: puts ("hppa2.0"); break; - } break; - #else /* !defined(_SC_KERNEL_BITS) */ - puts ("hppa2.0"); break; - #endif - default: puts ("hppa1.0"); break; - } - exit (0); - } -EOF - (CCOPTS="" $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` - test -z "$HP_ARCH" && HP_ARCH=hppa - fi ;; - esac - if [ ${HP_ARCH} = hppa2.0w ] - then - eval $set_cc_for_build - - # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating - # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler - # generating 64-bit code. GNU and HP use different nomenclature: - # - # $ CC_FOR_BUILD=cc ./config.guess - # => hppa2.0w-hp-hpux11.23 - # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess - # => hppa64-hp-hpux11.23 - - if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | - grep -q __LP64__ - then - HP_ARCH=hppa2.0w - else - HP_ARCH=hppa64 - fi - fi - echo ${HP_ARCH}-hp-hpux${HPUX_REV} - exit ;; - ia64:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - echo ia64-hp-hpux${HPUX_REV} - exit ;; - 3050*:HI-UX:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - int - main () - { - long cpu = sysconf (_SC_CPU_VERSION); - /* The order matters, because CPU_IS_HP_MC68K erroneously returns - true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct - results, however. */ - if (CPU_IS_PA_RISC (cpu)) - { - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; - case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; - default: puts ("hppa-hitachi-hiuxwe2"); break; - } - } - else if (CPU_IS_HP_MC68K (cpu)) - puts ("m68k-hitachi-hiuxwe2"); - else puts ("unknown-hitachi-hiuxwe2"); - exit (0); - } -EOF - $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && - { echo "$SYSTEM_NAME"; exit; } - echo unknown-hitachi-hiuxwe2 - exit ;; - 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) - echo hppa1.1-hp-bsd - exit ;; - 9000/8??:4.3bsd:*:*) - echo hppa1.0-hp-bsd - exit ;; - *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) - echo hppa1.0-hp-mpeix - exit ;; - hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) - echo hppa1.1-hp-osf - exit ;; - hp8??:OSF1:*:*) - echo hppa1.0-hp-osf - exit ;; - i*86:OSF1:*:*) - if [ -x /usr/sbin/sysversion ] ; then - echo ${UNAME_MACHINE}-unknown-osf1mk - else - echo ${UNAME_MACHINE}-unknown-osf1 - fi - exit ;; - parisc*:Lites*:*:*) - echo hppa1.1-hp-lites - exit ;; - C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) - echo c1-convex-bsd - exit ;; - C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit ;; - C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) - echo c34-convex-bsd - exit ;; - C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) - echo c38-convex-bsd - exit ;; - C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) - echo c4-convex-bsd - exit ;; - CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*[A-Z]90:*:*:*) - echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ - | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ - -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ - -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*TS:*:*:*) - echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*T3E:*:*:*) - echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*SV1:*:*:*) - echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - *:UNICOS/mp:*:*) - echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` - FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` - echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; - 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` - echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; - i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} - exit ;; - sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi${UNAME_RELEASE} - exit ;; - *:BSD/OS:*:*) - echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} - exit ;; - *:FreeBSD:*:*) - UNAME_PROCESSOR=`/usr/bin/uname -p` - case ${UNAME_PROCESSOR} in - amd64) - UNAME_PROCESSOR=x86_64 ;; - i386) - UNAME_PROCESSOR=i586 ;; - esac - echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` - exit ;; - i*:CYGWIN*:*) - echo ${UNAME_MACHINE}-pc-cygwin - exit ;; - *:MINGW64*:*) - echo ${UNAME_MACHINE}-pc-mingw64 - exit ;; - *:MINGW*:*) - echo ${UNAME_MACHINE}-pc-mingw32 - exit ;; - *:MSYS*:*) - echo ${UNAME_MACHINE}-pc-msys - exit ;; - i*:windows32*:*) - # uname -m includes "-pc" on this system. - echo ${UNAME_MACHINE}-mingw32 - exit ;; - i*:PW*:*) - echo ${UNAME_MACHINE}-pc-pw32 - exit ;; - *:Interix*:*) - case ${UNAME_MACHINE} in - x86) - echo i586-pc-interix${UNAME_RELEASE} - exit ;; - authenticamd | genuineintel | EM64T) - echo x86_64-unknown-interix${UNAME_RELEASE} - exit ;; - IA64) - echo ia64-unknown-interix${UNAME_RELEASE} - exit ;; - esac ;; - [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) - echo i${UNAME_MACHINE}-pc-mks - exit ;; - 8664:Windows_NT:*) - echo x86_64-pc-mks - exit ;; - i*:Windows_NT*:* | Pentium*:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we - # UNAME_MACHINE based on the output of uname instead of i386? - echo i586-pc-interix - exit ;; - i*:UWIN*:*) - echo ${UNAME_MACHINE}-pc-uwin - exit ;; - amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) - echo x86_64-unknown-cygwin - exit ;; - p*:CYGWIN*:*) - echo powerpcle-unknown-cygwin - exit ;; - prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - *:GNU:*:*) - # the GNU system - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` - exit ;; - *:GNU/*:*:*) - # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} - exit ;; - i*86:Minix:*:*) - echo ${UNAME_MACHINE}-pc-minix - exit ;; - aarch64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - aarch64_be:Linux:*:*) - UNAME_MACHINE=aarch64_be - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - alpha:Linux:*:*) - case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in - EV5) UNAME_MACHINE=alphaev5 ;; - EV56) UNAME_MACHINE=alphaev56 ;; - PCA56) UNAME_MACHINE=alphapca56 ;; - PCA57) UNAME_MACHINE=alphapca56 ;; - EV6) UNAME_MACHINE=alphaev6 ;; - EV67) UNAME_MACHINE=alphaev67 ;; - EV68*) UNAME_MACHINE=alphaev68 ;; - esac - objdump --private-headers /bin/sh | grep -q ld.so.1 - if test "$?" = 0 ; then LIBC=gnulibc1 ; fi - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - arc:Linux:*:* | arceb:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - arm*:Linux:*:*) - eval $set_cc_for_build - if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ARM_EABI__ - then - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - else - if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ARM_PCS_VFP - then - echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi - else - echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf - fi - fi - exit ;; - avr32*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - cris:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-${LIBC} - exit ;; - crisv32:Linux:*:*) - echo ${UNAME_MACHINE}-axis-linux-${LIBC} - exit ;; - e2k:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - frv:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - hexagon:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - i*86:Linux:*:*) - echo ${UNAME_MACHINE}-pc-linux-${LIBC} - exit ;; - ia64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - k1om:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - m32r*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - m68*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - mips:Linux:*:* | mips64:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #undef CPU - #undef ${UNAME_MACHINE} - #undef ${UNAME_MACHINE}el - #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=${UNAME_MACHINE}el - #else - #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=${UNAME_MACHINE} - #else - CPU= - #endif - #endif -EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } - ;; - mips64el:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - openrisc*:Linux:*:*) - echo or1k-unknown-linux-${LIBC} - exit ;; - or32:Linux:*:* | or1k*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - padre:Linux:*:*) - echo sparc-unknown-linux-${LIBC} - exit ;; - parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-${LIBC} - exit ;; - parisc:Linux:*:* | hppa:Linux:*:*) - # Look for CPU level - case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; - PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; - *) echo hppa-unknown-linux-${LIBC} ;; - esac - exit ;; - ppc64:Linux:*:*) - echo powerpc64-unknown-linux-${LIBC} - exit ;; - ppc:Linux:*:*) - echo powerpc-unknown-linux-${LIBC} - exit ;; - ppc64le:Linux:*:*) - echo powerpc64le-unknown-linux-${LIBC} - exit ;; - ppcle:Linux:*:*) - echo powerpcle-unknown-linux-${LIBC} - exit ;; - riscv32:Linux:*:* | riscv64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - s390:Linux:*:* | s390x:Linux:*:*) - echo ${UNAME_MACHINE}-ibm-linux-${LIBC} - exit ;; - sh64*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - sh*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - sparc:Linux:*:* | sparc64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - tile*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - vax:Linux:*:*) - echo ${UNAME_MACHINE}-dec-linux-${LIBC} - exit ;; - x86_64:Linux:*:*) - echo ${UNAME_MACHINE}-pc-linux-${LIBC} - exit ;; - xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-${LIBC} - exit ;; - i*86:DYNIX/ptx:4*:*) - # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. - # earlier versions are messed up and put the nodename in both - # sysname and nodename. - echo i386-sequent-sysv4 - exit ;; - i*86:UNIX_SV:4.2MP:2.*) - # Unixware is an offshoot of SVR4, but it has its own version - # number series starting with 2... - # I am not positive that other SVR4 systems won't match this, - # I just have to hope. -- rms. - # Use sysv4.2uw... so that sysv4* matches it. - echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} - exit ;; - i*86:OS/2:*:*) - # If we were able to find `uname', then EMX Unix compatibility - # is probably installed. - echo ${UNAME_MACHINE}-pc-os2-emx - exit ;; - i*86:XTS-300:*:STOP) - echo ${UNAME_MACHINE}-unknown-stop - exit ;; - i*86:atheos:*:*) - echo ${UNAME_MACHINE}-unknown-atheos - exit ;; - i*86:syllable:*:*) - echo ${UNAME_MACHINE}-pc-syllable - exit ;; - i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) - echo i386-unknown-lynxos${UNAME_RELEASE} - exit ;; - i*86:*DOS:*:*) - echo ${UNAME_MACHINE}-pc-msdosdjgpp - exit ;; - i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) - UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` - if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} - else - echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} - fi - exit ;; - i*86:*:5:[678]*) - # UnixWare 7.x, OpenUNIX and OpenServer 6. - case `/bin/uname -X | grep "^Machine"` in - *486*) UNAME_MACHINE=i486 ;; - *Pentium) UNAME_MACHINE=i586 ;; - *Pent*|*Celeron) UNAME_MACHINE=i686 ;; - esac - echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} - exit ;; - i*86:*:3.2:*) - if test -f /usr/options/cb.name; then - UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then - UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` - (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 - (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ - && UNAME_MACHINE=i586 - (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ - && UNAME_MACHINE=i686 - (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ - && UNAME_MACHINE=i686 - echo ${UNAME_MACHINE}-pc-sco$UNAME_REL - else - echo ${UNAME_MACHINE}-pc-sysv32 - fi - exit ;; - pc:*:*:*) - # Left here for compatibility: - # uname -m prints for DJGPP always 'pc', but it prints nothing about - # the processor, so we play safe by assuming i586. - # Note: whatever this is, it MUST be the same as what config.sub - # prints for the "djgpp" host, or else GDB configure will decide that - # this is a cross-build. - echo i586-pc-msdosdjgpp - exit ;; - Intel:Mach:3*:*) - echo i386-pc-mach3 - exit ;; - paragon:*:*:*) - echo i860-intel-osf1 - exit ;; - i860:*:4.*:*) # i860-SVR4 - if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 - else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 - fi - exit ;; - mini*:CTIX:SYS*5:*) - # "miniframe" - echo m68010-convergent-sysv - exit ;; - mc68k:UNIX:SYSTEM5:3.51m) - echo m68k-convergent-sysv - exit ;; - M680?0:D-NIX:5.3:*) - echo m68k-diab-dnix - exit ;; - M68*:*:R3V[5678]*:*) - test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; - 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) - OS_REL='' - test -r /etc/.relid \ - && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } - /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; - 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4; exit; } ;; - NCR*:*:4.2:* | MPRAS*:*:4.2:*) - OS_REL='.3' - test -r /etc/.relid \ - && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } - /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } - /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; - m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-unknown-lynxos${UNAME_RELEASE} - exit ;; - mc68030:UNIX_System_V:4.*:*) - echo m68k-atari-sysv4 - exit ;; - TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos${UNAME_RELEASE} - exit ;; - rs6000:LynxOS:2.*:*) - echo rs6000-unknown-lynxos${UNAME_RELEASE} - exit ;; - PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) - echo powerpc-unknown-lynxos${UNAME_RELEASE} - exit ;; - SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv${UNAME_RELEASE} - exit ;; - RM*:ReliantUNIX-*:*:*) - echo mips-sni-sysv4 - exit ;; - RM*:SINIX-*:*:*) - echo mips-sni-sysv4 - exit ;; - *:SINIX-*:*:*) - if uname -p 2>/dev/null >/dev/null ; then - UNAME_MACHINE=`(uname -p) 2>/dev/null` - echo ${UNAME_MACHINE}-sni-sysv4 - else - echo ns32k-sni-sysv - fi - exit ;; - PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort - # says - echo i586-unisys-sysv4 - exit ;; - *:UNIX_System_V:4*:FTX*) - # From Gerald Hewes . - # How about differentiating between stratus architectures? -djm - echo hppa1.1-stratus-sysv4 - exit ;; - *:*:*:FTX*) - # From seanf@swdc.stratus.com. - echo i860-stratus-sysv4 - exit ;; - i*86:VOS:*:*) - # From Paul.Green@stratus.com. - echo ${UNAME_MACHINE}-stratus-vos - exit ;; - *:VOS:*:*) - # From Paul.Green@stratus.com. - echo hppa1.1-stratus-vos - exit ;; - mc68*:A/UX:*:*) - echo m68k-apple-aux${UNAME_RELEASE} - exit ;; - news*:NEWS-OS:6*:*) - echo mips-sony-newsos6 - exit ;; - R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if [ -d /usr/nec ]; then - echo mips-nec-sysv${UNAME_RELEASE} - else - echo mips-unknown-sysv${UNAME_RELEASE} - fi - exit ;; - BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. - echo powerpc-be-beos - exit ;; - BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. - echo powerpc-apple-beos - exit ;; - BePC:BeOS:*:*) # BeOS running on Intel PC compatible. - echo i586-pc-beos - exit ;; - BePC:Haiku:*:*) # Haiku running on Intel PC compatible. - echo i586-pc-haiku - exit ;; - x86_64:Haiku:*:*) - echo x86_64-unknown-haiku - exit ;; - SX-4:SUPER-UX:*:*) - echo sx4-nec-superux${UNAME_RELEASE} - exit ;; - SX-5:SUPER-UX:*:*) - echo sx5-nec-superux${UNAME_RELEASE} - exit ;; - SX-6:SUPER-UX:*:*) - echo sx6-nec-superux${UNAME_RELEASE} - exit ;; - SX-7:SUPER-UX:*:*) - echo sx7-nec-superux${UNAME_RELEASE} - exit ;; - SX-8:SUPER-UX:*:*) - echo sx8-nec-superux${UNAME_RELEASE} - exit ;; - SX-8R:SUPER-UX:*:*) - echo sx8r-nec-superux${UNAME_RELEASE} - exit ;; - SX-ACE:SUPER-UX:*:*) - echo sxace-nec-superux${UNAME_RELEASE} - exit ;; - Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody${UNAME_RELEASE} - exit ;; - *:Rhapsody:*:*) - echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} - exit ;; - *:Darwin:*:*) - UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown - eval $set_cc_for_build - if test "$UNAME_PROCESSOR" = unknown ; then - UNAME_PROCESSOR=powerpc - fi - if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then - if [ "$CC_FOR_BUILD" != no_compiler_found ]; then - if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - case $UNAME_PROCESSOR in - i386) UNAME_PROCESSOR=x86_64 ;; - powerpc) UNAME_PROCESSOR=powerpc64 ;; - esac - fi - # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc - if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_PPC >/dev/null - then - UNAME_PROCESSOR=powerpc - fi - fi - elif test "$UNAME_PROCESSOR" = i386 ; then - # Avoid executing cc on OS X 10.9, as it ships with a stub - # that puts up a graphical alert prompting to install - # developer tools. Any system running Mac OS X 10.7 or - # later (Darwin 11 and later) is required to have a 64-bit - # processor. This is not true of the ARM version of Darwin - # that Apple uses in portable devices. - UNAME_PROCESSOR=x86_64 - fi - echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} - exit ;; - *:procnto*:*:* | *:QNX:[0123456789]*:*) - UNAME_PROCESSOR=`uname -p` - if test "$UNAME_PROCESSOR" = x86; then - UNAME_PROCESSOR=i386 - UNAME_MACHINE=pc - fi - echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} - exit ;; - *:QNX:*:4*) - echo i386-pc-qnx - exit ;; - NEO-*:NONSTOP_KERNEL:*:*) - echo neo-tandem-nsk${UNAME_RELEASE} - exit ;; - NSE-*:NONSTOP_KERNEL:*:*) - echo nse-tandem-nsk${UNAME_RELEASE} - exit ;; - NSR-*:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk${UNAME_RELEASE} - exit ;; - NSX-*:NONSTOP_KERNEL:*:*) - echo nsx-tandem-nsk${UNAME_RELEASE} - exit ;; - *:NonStop-UX:*:*) - echo mips-compaq-nonstopux - exit ;; - BS2000:POSIX*:*:*) - echo bs2000-siemens-sysv - exit ;; - DS/*:UNIX_System_V:*:*) - echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} - exit ;; - *:Plan9:*:*) - # "uname -m" is not consistent, so use $cputype instead. 386 - # is converted to i386 for consistency with other x86 - # operating systems. - if test "$cputype" = 386; then - UNAME_MACHINE=i386 - else - UNAME_MACHINE="$cputype" - fi - echo ${UNAME_MACHINE}-unknown-plan9 - exit ;; - *:TOPS-10:*:*) - echo pdp10-unknown-tops10 - exit ;; - *:TENEX:*:*) - echo pdp10-unknown-tenex - exit ;; - KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) - echo pdp10-dec-tops20 - exit ;; - XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) - echo pdp10-xkl-tops20 - exit ;; - *:TOPS-20:*:*) - echo pdp10-unknown-tops20 - exit ;; - *:ITS:*:*) - echo pdp10-unknown-its - exit ;; - SEI:*:*:SEIUX) - echo mips-sei-seiux${UNAME_RELEASE} - exit ;; - *:DragonFly:*:*) - echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` - exit ;; - *:*VMS:*:*) - UNAME_MACHINE=`(uname -p) 2>/dev/null` - case "${UNAME_MACHINE}" in - A*) echo alpha-dec-vms ; exit ;; - I*) echo ia64-dec-vms ; exit ;; - V*) echo vax-dec-vms ; exit ;; - esac ;; - *:XENIX:*:SysV) - echo i386-pc-xenix - exit ;; - i*86:skyos:*:*) - echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE} | sed -e 's/ .*$//'` - exit ;; - i*86:rdos:*:*) - echo ${UNAME_MACHINE}-pc-rdos - exit ;; - i*86:AROS:*:*) - echo ${UNAME_MACHINE}-pc-aros - exit ;; - x86_64:VMkernel:*:*) - echo ${UNAME_MACHINE}-unknown-esx - exit ;; - amd64:Isilon\ OneFS:*:*) - echo x86_64-unknown-onefs - exit ;; -esac - -cat >&2 </dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null` - -hostinfo = `(hostinfo) 2>/dev/null` -/bin/universe = `(/bin/universe) 2>/dev/null` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` -/bin/arch = `(/bin/arch) 2>/dev/null` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` - -UNAME_MACHINE = ${UNAME_MACHINE} -UNAME_RELEASE = ${UNAME_RELEASE} -UNAME_SYSTEM = ${UNAME_SYSTEM} -UNAME_VERSION = ${UNAME_VERSION} -EOF - -exit 1 - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/core/src/index/thirdparty/faiss/build-aux/config.sub b/core/src/index/thirdparty/faiss/build-aux/config.sub deleted file mode 100755 index 40ea5dfe11..0000000000 --- a/core/src/index/thirdparty/faiss/build-aux/config.sub +++ /dev/null @@ -1,1836 +0,0 @@ -#! /bin/sh -# Configuration validation subroutine script. -# Copyright 1992-2017 Free Software Foundation, Inc. - -timestamp='2017-04-02' - -# This file is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see . -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that -# program. This Exception is an additional permission under section 7 -# of the GNU General Public License, version 3 ("GPLv3"). - - -# Please send patches to . -# -# Configuration subroutine to validate and canonicalize a configuration type. -# Supply the specified configuration type as an argument. -# If it is invalid, we print an error message on stderr and exit with code 1. -# Otherwise, we print the canonical config type on stdout and succeed. - -# You can get the latest version of this script from: -# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub - -# This file is supposed to be the same for all GNU packages -# and recognize all the CPU types, system types and aliases -# that are meaningful with *any* GNU software. -# Each package is responsible for reporting which valid configurations -# it does not support. The user should be able to distinguish -# a failure to support a valid configuration from a meaningless -# configuration. - -# The goal of this file is to map all the various variations of a given -# machine specification into a single specification in the form: -# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM -# or in some cases, the newer four-part form: -# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM -# It is wrong to echo any other type of specification. - -me=`echo "$0" | sed -e 's,.*/,,'` - -usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS - -Canonicalize a configuration name. - -Operation modes: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.sub ($timestamp) - -Copyright 1992-2017 Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit ;; - --version | -v ) - echo "$version" ; exit ;; - --help | --h* | -h ) - echo "$usage"; exit ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" - exit 1 ;; - - *local*) - # First pass through any local machine types. - echo $1 - exit ;; - - * ) - break ;; - esac -done - -case $# in - 0) echo "$me: missing argument$help" >&2 - exit 1;; - 1) ;; - *) echo "$me: too many arguments$help" >&2 - exit 1;; -esac - -# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). -# Here we must recognize all the valid KERNEL-OS combinations. -maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` -case $maybe_os in - nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ - linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ - knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ - kopensolaris*-gnu* | cloudabi*-eabi* | \ - storm-chaos* | os2-emx* | rtmk-nova*) - os=-$maybe_os - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` - ;; - android-linux) - os=-linux-android - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown - ;; - *) - basic_machine=`echo $1 | sed 's/-[^-]*$//'` - if [ $basic_machine != $1 ] - then os=`echo $1 | sed 's/.*-/-/'` - else os=; fi - ;; -esac - -### Let's recognize common machines as not being operating systems so -### that things like config.sub decstation-3100 work. We also -### recognize some manufacturers as not being operating systems, so we -### can provide default operating systems below. -case $os in - -sun*os*) - # Prevent following clause from handling this invalid input. - ;; - -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ - -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ - -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ - -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ - -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ - -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ - -apple | -axis | -knuth | -cray | -microblaze*) - os= - basic_machine=$1 - ;; - -bluegene*) - os=-cnk - ;; - -sim | -cisco | -oki | -wec | -winbond) - os= - basic_machine=$1 - ;; - -scout) - ;; - -wrs) - os=-vxworks - basic_machine=$1 - ;; - -chorusos*) - os=-chorusos - basic_machine=$1 - ;; - -chorusrdb) - os=-chorusrdb - basic_machine=$1 - ;; - -hiux*) - os=-hiuxwe2 - ;; - -sco6) - os=-sco5v6 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco5) - os=-sco3.2v5 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco4) - os=-sco3.2v4 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2.[4-9]*) - os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2v[4-9]*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco5v6*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco*) - os=-sco3.2v2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -udk*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -isc) - os=-isc2.2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -clix*) - basic_machine=clipper-intergraph - ;; - -isc*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -lynx*178) - os=-lynxos178 - ;; - -lynx*5) - os=-lynxos5 - ;; - -lynx*) - os=-lynxos - ;; - -ptx*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` - ;; - -windowsnt*) - os=`echo $os | sed -e 's/windowsnt/winnt/'` - ;; - -psos*) - os=-psos - ;; - -mint | -mint[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; -esac - -# Decode aliases for certain CPU-COMPANY combinations. -case $basic_machine in - # Recognize the basic CPU types without company name. - # Some are omitted here because they have special meanings below. - 1750a | 580 \ - | a29k \ - | aarch64 | aarch64_be \ - | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ - | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ - | am33_2.0 \ - | arc | arceb \ - | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ - | avr | avr32 \ - | ba \ - | be32 | be64 \ - | bfin \ - | c4x | c8051 | clipper \ - | d10v | d30v | dlx | dsp16xx \ - | e2k | epiphany \ - | fido | fr30 | frv | ft32 \ - | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ - | hexagon \ - | i370 | i860 | i960 | ia16 | ia64 \ - | ip2k | iq2000 \ - | k1om \ - | le32 | le64 \ - | lm32 \ - | m32c | m32r | m32rle | m68000 | m68k | m88k \ - | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ - | mips | mipsbe | mipseb | mipsel | mipsle \ - | mips16 \ - | mips64 | mips64el \ - | mips64octeon | mips64octeonel \ - | mips64orion | mips64orionel \ - | mips64r5900 | mips64r5900el \ - | mips64vr | mips64vrel \ - | mips64vr4100 | mips64vr4100el \ - | mips64vr4300 | mips64vr4300el \ - | mips64vr5000 | mips64vr5000el \ - | mips64vr5900 | mips64vr5900el \ - | mipsisa32 | mipsisa32el \ - | mipsisa32r2 | mipsisa32r2el \ - | mipsisa32r6 | mipsisa32r6el \ - | mipsisa64 | mipsisa64el \ - | mipsisa64r2 | mipsisa64r2el \ - | mipsisa64r6 | mipsisa64r6el \ - | mipsisa64sb1 | mipsisa64sb1el \ - | mipsisa64sr71k | mipsisa64sr71kel \ - | mipsr5900 | mipsr5900el \ - | mipstx39 | mipstx39el \ - | mn10200 | mn10300 \ - | moxie \ - | mt \ - | msp430 \ - | nds32 | nds32le | nds32be \ - | nios | nios2 | nios2eb | nios2el \ - | ns16k | ns32k \ - | open8 | or1k | or1knd | or32 \ - | pdp10 | pdp11 | pj | pjl \ - | powerpc | powerpc64 | powerpc64le | powerpcle \ - | pru \ - | pyramid \ - | riscv32 | riscv64 \ - | rl78 | rx \ - | score \ - | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ - | sh64 | sh64le \ - | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ - | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ - | spu \ - | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ - | ubicom32 \ - | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ - | visium \ - | wasm32 \ - | we32k \ - | x86 | xc16x | xstormy16 | xtensa \ - | z8k | z80) - basic_machine=$basic_machine-unknown - ;; - c54x) - basic_machine=tic54x-unknown - ;; - c55x) - basic_machine=tic55x-unknown - ;; - c6x) - basic_machine=tic6x-unknown - ;; - leon|leon[3-9]) - basic_machine=sparc-$basic_machine - ;; - m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) - basic_machine=$basic_machine-unknown - os=-none - ;; - m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) - ;; - ms1) - basic_machine=mt-unknown - ;; - - strongarm | thumb | xscale) - basic_machine=arm-unknown - ;; - xgate) - basic_machine=$basic_machine-unknown - os=-none - ;; - xscaleeb) - basic_machine=armeb-unknown - ;; - - xscaleel) - basic_machine=armel-unknown - ;; - - # We use `pc' rather than `unknown' - # because (1) that's what they normally are, and - # (2) the word "unknown" tends to confuse beginning users. - i*86 | x86_64) - basic_machine=$basic_machine-pc - ;; - # Object if more than one company name word. - *-*-*) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; - # Recognize the basic CPU types with company name. - 580-* \ - | a29k-* \ - | aarch64-* | aarch64_be-* \ - | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ - | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ - | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ - | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ - | avr-* | avr32-* \ - | ba-* \ - | be32-* | be64-* \ - | bfin-* | bs2000-* \ - | c[123]* | c30-* | [cjt]90-* | c4x-* \ - | c8051-* | clipper-* | craynv-* | cydra-* \ - | d10v-* | d30v-* | dlx-* \ - | e2k-* | elxsi-* \ - | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ - | h8300-* | h8500-* \ - | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ - | hexagon-* \ - | i*86-* | i860-* | i960-* | ia16-* | ia64-* \ - | ip2k-* | iq2000-* \ - | k1om-* \ - | le32-* | le64-* \ - | lm32-* \ - | m32c-* | m32r-* | m32rle-* \ - | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ - | microblaze-* | microblazeel-* \ - | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ - | mips16-* \ - | mips64-* | mips64el-* \ - | mips64octeon-* | mips64octeonel-* \ - | mips64orion-* | mips64orionel-* \ - | mips64r5900-* | mips64r5900el-* \ - | mips64vr-* | mips64vrel-* \ - | mips64vr4100-* | mips64vr4100el-* \ - | mips64vr4300-* | mips64vr4300el-* \ - | mips64vr5000-* | mips64vr5000el-* \ - | mips64vr5900-* | mips64vr5900el-* \ - | mipsisa32-* | mipsisa32el-* \ - | mipsisa32r2-* | mipsisa32r2el-* \ - | mipsisa32r6-* | mipsisa32r6el-* \ - | mipsisa64-* | mipsisa64el-* \ - | mipsisa64r2-* | mipsisa64r2el-* \ - | mipsisa64r6-* | mipsisa64r6el-* \ - | mipsisa64sb1-* | mipsisa64sb1el-* \ - | mipsisa64sr71k-* | mipsisa64sr71kel-* \ - | mipsr5900-* | mipsr5900el-* \ - | mipstx39-* | mipstx39el-* \ - | mmix-* \ - | mt-* \ - | msp430-* \ - | nds32-* | nds32le-* | nds32be-* \ - | nios-* | nios2-* | nios2eb-* | nios2el-* \ - | none-* | np1-* | ns16k-* | ns32k-* \ - | open8-* \ - | or1k*-* \ - | orion-* \ - | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ - | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ - | pru-* \ - | pyramid-* \ - | riscv32-* | riscv64-* \ - | rl78-* | romp-* | rs6000-* | rx-* \ - | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ - | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ - | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ - | sparclite-* \ - | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ - | tahoe-* \ - | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ - | tile*-* \ - | tron-* \ - | ubicom32-* \ - | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ - | vax-* \ - | visium-* \ - | wasm32-* \ - | we32k-* \ - | x86-* | x86_64-* | xc16x-* | xps100-* \ - | xstormy16-* | xtensa*-* \ - | ymp-* \ - | z8k-* | z80-*) - ;; - # Recognize the basic CPU types without company name, with glob match. - xtensa*) - basic_machine=$basic_machine-unknown - ;; - # Recognize the various machine names and aliases which stand - # for a CPU type and a company and sometimes even an OS. - 386bsd) - basic_machine=i386-unknown - os=-bsd - ;; - 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - basic_machine=m68000-att - ;; - 3b*) - basic_machine=we32k-att - ;; - a29khif) - basic_machine=a29k-amd - os=-udi - ;; - abacus) - basic_machine=abacus-unknown - ;; - adobe68k) - basic_machine=m68010-adobe - os=-scout - ;; - alliant | fx80) - basic_machine=fx80-alliant - ;; - altos | altos3068) - basic_machine=m68k-altos - ;; - am29k) - basic_machine=a29k-none - os=-bsd - ;; - amd64) - basic_machine=x86_64-pc - ;; - amd64-*) - basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - amdahl) - basic_machine=580-amdahl - os=-sysv - ;; - amiga | amiga-*) - basic_machine=m68k-unknown - ;; - amigaos | amigados) - basic_machine=m68k-unknown - os=-amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - os=-sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - os=-sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - os=-bsd - ;; - aros) - basic_machine=i386-pc - os=-aros - ;; - asmjs) - basic_machine=asmjs-unknown - ;; - aux) - basic_machine=m68k-apple - os=-aux - ;; - balance) - basic_machine=ns32k-sequent - os=-dynix - ;; - blackfin) - basic_machine=bfin-unknown - os=-linux - ;; - blackfin-*) - basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - bluegene*) - basic_machine=powerpc-ibm - os=-cnk - ;; - c54x-*) - basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c55x-*) - basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c6x-*) - basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - c90) - basic_machine=c90-cray - os=-unicos - ;; - cegcc) - basic_machine=arm-unknown - os=-cegcc - ;; - convex-c1) - basic_machine=c1-convex - os=-bsd - ;; - convex-c2) - basic_machine=c2-convex - os=-bsd - ;; - convex-c32) - basic_machine=c32-convex - os=-bsd - ;; - convex-c34) - basic_machine=c34-convex - os=-bsd - ;; - convex-c38) - basic_machine=c38-convex - os=-bsd - ;; - cray | j90) - basic_machine=j90-cray - os=-unicos - ;; - craynv) - basic_machine=craynv-cray - os=-unicosmp - ;; - cr16 | cr16-*) - basic_machine=cr16-unknown - os=-elf - ;; - crds | unos) - basic_machine=m68k-crds - ;; - crisv32 | crisv32-* | etraxfs*) - basic_machine=crisv32-axis - ;; - cris | cris-* | etrax*) - basic_machine=cris-axis - ;; - crx) - basic_machine=crx-unknown - os=-elf - ;; - da30 | da30-*) - basic_machine=m68k-da30 - ;; - decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) - basic_machine=mips-dec - ;; - decsystem10* | dec10*) - basic_machine=pdp10-dec - os=-tops10 - ;; - decsystem20* | dec20*) - basic_machine=pdp10-dec - os=-tops20 - ;; - delta | 3300 | motorola-3300 | motorola-delta \ - | 3300-motorola | delta-motorola) - basic_machine=m68k-motorola - ;; - delta88) - basic_machine=m88k-motorola - os=-sysv3 - ;; - dicos) - basic_machine=i686-pc - os=-dicos - ;; - djgpp) - basic_machine=i586-pc - os=-msdosdjgpp - ;; - dpx20 | dpx20-*) - basic_machine=rs6000-bull - os=-bosx - ;; - dpx2* | dpx2*-bull) - basic_machine=m68k-bull - os=-sysv3 - ;; - e500v[12]) - basic_machine=powerpc-unknown - os=$os"spe" - ;; - e500v[12]-*) - basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` - os=$os"spe" - ;; - ebmon29k) - basic_machine=a29k-amd - os=-ebmon - ;; - elxsi) - basic_machine=elxsi-elxsi - os=-bsd - ;; - encore | umax | mmax) - basic_machine=ns32k-encore - ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - os=-ose - ;; - fx2800) - basic_machine=i860-alliant - ;; - genix) - basic_machine=ns32k-ns - ;; - gmicro) - basic_machine=tron-gmicro - os=-sysv - ;; - go32) - basic_machine=i386-pc - os=-go32 - ;; - h3050r* | hiux*) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - h8300hms) - basic_machine=h8300-hitachi - os=-hms - ;; - h8300xray) - basic_machine=h8300-hitachi - os=-xray - ;; - h8500hms) - basic_machine=h8500-hitachi - os=-hms - ;; - harris) - basic_machine=m88k-harris - os=-sysv3 - ;; - hp300-*) - basic_machine=m68k-hp - ;; - hp300bsd) - basic_machine=m68k-hp - os=-bsd - ;; - hp300hpux) - basic_machine=m68k-hp - os=-hpux - ;; - hp3k9[0-9][0-9] | hp9[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k2[0-9][0-9] | hp9k31[0-9]) - basic_machine=m68000-hp - ;; - hp9k3[2-9][0-9]) - basic_machine=m68k-hp - ;; - hp9k6[0-9][0-9] | hp6[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k7[0-79][0-9] | hp7[0-79][0-9]) - basic_machine=hppa1.1-hp - ;; - hp9k78[0-9] | hp78[0-9]) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][13679] | hp8[0-9][13679]) - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][0-9] | hp8[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hppa-next) - os=-nextstep3 - ;; - hppaosf) - basic_machine=hppa1.1-hp - os=-osf - ;; - hppro) - basic_machine=hppa1.1-hp - os=-proelf - ;; - i370-ibm* | ibm*) - basic_machine=i370-ibm - ;; - i*86v32) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv32 - ;; - i*86v4*) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv4 - ;; - i*86v) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv - ;; - i*86sol2) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-solaris2 - ;; - i386mach) - basic_machine=i386-mach - os=-mach - ;; - i386-vsta | vsta) - basic_machine=i386-unknown - os=-vsta - ;; - iris | iris4d) - basic_machine=mips-sgi - case $os in - -irix*) - ;; - *) - os=-irix4 - ;; - esac - ;; - isi68 | isi) - basic_machine=m68k-isi - os=-sysv - ;; - leon-*|leon[3-9]-*) - basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` - ;; - m68knommu) - basic_machine=m68k-unknown - os=-linux - ;; - m68knommu-*) - basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - m88k-omron*) - basic_machine=m88k-omron - ;; - magnum | m3230) - basic_machine=mips-mips - os=-sysv - ;; - merlin) - basic_machine=ns32k-utek - os=-sysv - ;; - microblaze*) - basic_machine=microblaze-xilinx - ;; - mingw64) - basic_machine=x86_64-pc - os=-mingw64 - ;; - mingw32) - basic_machine=i686-pc - os=-mingw32 - ;; - mingw32ce) - basic_machine=arm-unknown - os=-mingw32ce - ;; - miniframe) - basic_machine=m68000-convergent - ;; - *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; - mips3*-*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` - ;; - mips3*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown - ;; - monitor) - basic_machine=m68k-rom68k - os=-coff - ;; - morphos) - basic_machine=powerpc-unknown - os=-morphos - ;; - moxiebox) - basic_machine=moxie-unknown - os=-moxiebox - ;; - msdos) - basic_machine=i386-pc - os=-msdos - ;; - ms1-*) - basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` - ;; - msys) - basic_machine=i686-pc - os=-msys - ;; - mvs) - basic_machine=i370-ibm - os=-mvs - ;; - nacl) - basic_machine=le32-unknown - os=-nacl - ;; - ncr3000) - basic_machine=i486-ncr - os=-sysv4 - ;; - netbsd386) - basic_machine=i386-unknown - os=-netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - os=-linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - os=-newsos - ;; - news1000) - basic_machine=m68030-sony - os=-newsos - ;; - news-3600 | risc-news) - basic_machine=mips-sony - os=-newsos - ;; - necv70) - basic_machine=v70-nec - os=-sysv - ;; - next | m*-next ) - basic_machine=m68k-next - case $os in - -nextstep* ) - ;; - -ns2*) - os=-nextstep2 - ;; - *) - os=-nextstep3 - ;; - esac - ;; - nh3000) - basic_machine=m68k-harris - os=-cxux - ;; - nh[45]000) - basic_machine=m88k-harris - os=-cxux - ;; - nindy960) - basic_machine=i960-intel - os=-nindy - ;; - mon960) - basic_machine=i960-intel - os=-mon960 - ;; - nonstopux) - basic_machine=mips-compaq - os=-nonstopux - ;; - np1) - basic_machine=np1-gould - ;; - neo-tandem) - basic_machine=neo-tandem - ;; - nse-tandem) - basic_machine=nse-tandem - ;; - nsr-tandem) - basic_machine=nsr-tandem - ;; - nsx-tandem) - basic_machine=nsx-tandem - ;; - op50n-* | op60c-*) - basic_machine=hppa1.1-oki - os=-proelf - ;; - openrisc | openrisc-*) - basic_machine=or32-unknown - ;; - os400) - basic_machine=powerpc-ibm - os=-os400 - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - os=-ose - ;; - os68k) - basic_machine=m68k-none - os=-os68k - ;; - pa-hitachi) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - paragon) - basic_machine=i860-intel - os=-osf - ;; - parisc) - basic_machine=hppa-unknown - os=-linux - ;; - parisc-*) - basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - pbd) - basic_machine=sparc-tti - ;; - pbb) - basic_machine=m68k-tti - ;; - pc532 | pc532-*) - basic_machine=ns32k-pc532 - ;; - pc98) - basic_machine=i386-pc - ;; - pc98-*) - basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentium | p5 | k5 | k6 | nexgen | viac3) - basic_machine=i586-pc - ;; - pentiumpro | p6 | 6x86 | athlon | athlon_*) - basic_machine=i686-pc - ;; - pentiumii | pentium2 | pentiumiii | pentium3) - basic_machine=i686-pc - ;; - pentium4) - basic_machine=i786-pc - ;; - pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) - basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumpro-* | p6-* | 6x86-* | athlon-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentium4-*) - basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pn) - basic_machine=pn-gould - ;; - power) basic_machine=power-ibm - ;; - ppc | ppcbe) basic_machine=powerpc-unknown - ;; - ppc-* | ppcbe-*) - basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppcle | powerpclittle) - basic_machine=powerpcle-unknown - ;; - ppcle-* | powerpclittle-*) - basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64) basic_machine=powerpc64-unknown - ;; - ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64le | powerpc64little) - basic_machine=powerpc64le-unknown - ;; - ppc64le-* | powerpc64little-*) - basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ps2) - basic_machine=i386-ibm - ;; - pw32) - basic_machine=i586-unknown - os=-pw32 - ;; - rdos | rdos64) - basic_machine=x86_64-pc - os=-rdos - ;; - rdos32) - basic_machine=i386-pc - os=-rdos - ;; - rom68k) - basic_machine=m68k-rom68k - os=-coff - ;; - rm[46]00) - basic_machine=mips-siemens - ;; - rtpc | rtpc-*) - basic_machine=romp-ibm - ;; - s390 | s390-*) - basic_machine=s390-ibm - ;; - s390x | s390x-*) - basic_machine=s390x-ibm - ;; - sa29200) - basic_machine=a29k-amd - os=-udi - ;; - sb1) - basic_machine=mipsisa64sb1-unknown - ;; - sb1el) - basic_machine=mipsisa64sb1el-unknown - ;; - sde) - basic_machine=mipsisa32-sde - os=-elf - ;; - sei) - basic_machine=mips-sei - os=-seiux - ;; - sequent) - basic_machine=i386-sequent - ;; - sh) - basic_machine=sh-hitachi - os=-hms - ;; - sh5el) - basic_machine=sh5le-unknown - ;; - sh64) - basic_machine=sh64-unknown - ;; - sparclite-wrs | simso-wrs) - basic_machine=sparclite-wrs - os=-vxworks - ;; - sps7) - basic_machine=m68k-bull - os=-sysv2 - ;; - spur) - basic_machine=spur-unknown - ;; - st2000) - basic_machine=m68k-tandem - ;; - stratus) - basic_machine=i860-stratus - os=-sysv4 - ;; - strongarm-* | thumb-*) - basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - sun2) - basic_machine=m68000-sun - ;; - sun2os3) - basic_machine=m68000-sun - os=-sunos3 - ;; - sun2os4) - basic_machine=m68000-sun - os=-sunos4 - ;; - sun3os3) - basic_machine=m68k-sun - os=-sunos3 - ;; - sun3os4) - basic_machine=m68k-sun - os=-sunos4 - ;; - sun4os3) - basic_machine=sparc-sun - os=-sunos3 - ;; - sun4os4) - basic_machine=sparc-sun - os=-sunos4 - ;; - sun4sol2) - basic_machine=sparc-sun - os=-solaris2 - ;; - sun3 | sun3-*) - basic_machine=m68k-sun - ;; - sun4) - basic_machine=sparc-sun - ;; - sun386 | sun386i | roadrunner) - basic_machine=i386-sun - ;; - sv1) - basic_machine=sv1-cray - os=-unicos - ;; - symmetry) - basic_machine=i386-sequent - os=-dynix - ;; - t3e) - basic_machine=alphaev5-cray - os=-unicos - ;; - t90) - basic_machine=t90-cray - os=-unicos - ;; - tile*) - basic_machine=$basic_machine-unknown - os=-linux-gnu - ;; - tx39) - basic_machine=mipstx39-unknown - ;; - tx39el) - basic_machine=mipstx39el-unknown - ;; - toad1) - basic_machine=pdp10-xkl - os=-tops20 - ;; - tower | tower-32) - basic_machine=m68k-ncr - ;; - tpf) - basic_machine=s390x-ibm - os=-tpf - ;; - udi29k) - basic_machine=a29k-amd - os=-udi - ;; - ultra3) - basic_machine=a29k-nyu - os=-sym1 - ;; - v810 | necv810) - basic_machine=v810-nec - os=-none - ;; - vaxv) - basic_machine=vax-dec - os=-sysv - ;; - vms) - basic_machine=vax-dec - os=-vms - ;; - vpp*|vx|vx-*) - basic_machine=f301-fujitsu - ;; - vxworks960) - basic_machine=i960-wrs - os=-vxworks - ;; - vxworks68) - basic_machine=m68k-wrs - os=-vxworks - ;; - vxworks29k) - basic_machine=a29k-wrs - os=-vxworks - ;; - wasm32) - basic_machine=wasm32-unknown - ;; - w65*) - basic_machine=w65-wdc - os=-none - ;; - w89k-*) - basic_machine=hppa1.1-winbond - os=-proelf - ;; - xbox) - basic_machine=i686-pc - os=-mingw32 - ;; - xps | xps100) - basic_machine=xps100-honeywell - ;; - xscale-* | xscalee[bl]-*) - basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` - ;; - ymp) - basic_machine=ymp-cray - os=-unicos - ;; - z8k-*-coff) - basic_machine=z8k-unknown - os=-sim - ;; - z80-*-coff) - basic_machine=z80-unknown - os=-sim - ;; - none) - basic_machine=none-none - os=-none - ;; - -# Here we handle the default manufacturer of certain CPU types. It is in -# some cases the only manufacturer, in others, it is the most popular. - w89k) - basic_machine=hppa1.1-winbond - ;; - op50n) - basic_machine=hppa1.1-oki - ;; - op60c) - basic_machine=hppa1.1-oki - ;; - romp) - basic_machine=romp-ibm - ;; - mmix) - basic_machine=mmix-knuth - ;; - rs6000) - basic_machine=rs6000-ibm - ;; - vax) - basic_machine=vax-dec - ;; - pdp10) - # there are many clones, so DEC is not a safe bet - basic_machine=pdp10-unknown - ;; - pdp11) - basic_machine=pdp11-dec - ;; - we32k) - basic_machine=we32k-att - ;; - sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) - basic_machine=sh-unknown - ;; - sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) - basic_machine=sparc-sun - ;; - cydra) - basic_machine=cydra-cydrome - ;; - orion) - basic_machine=orion-highlevel - ;; - orion105) - basic_machine=clipper-highlevel - ;; - mac | mpw | mac-mpw) - basic_machine=m68k-apple - ;; - pmac | pmac-mpw) - basic_machine=powerpc-apple - ;; - *-unknown) - # Make sure to match an already-canonicalized machine name. - ;; - *) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; -esac - -# Here we canonicalize certain aliases for manufacturers. -case $basic_machine in - *-digital*) - basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` - ;; - *-commodore*) - basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` - ;; - *) - ;; -esac - -# Decode manufacturer-specific aliases for certain operating systems. - -if [ x"$os" != x"" ] -then -case $os in - # First match some system type aliases - # that might get confused with valid system types. - # -solaris* is a basic system type, with this one exception. - -auroraux) - os=-auroraux - ;; - -solaris1 | -solaris1.*) - os=`echo $os | sed -e 's|solaris1|sunos4|'` - ;; - -solaris) - os=-solaris2 - ;; - -svr4*) - os=-sysv4 - ;; - -unixware*) - os=-sysv4.2uw - ;; - -gnu/linux*) - os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` - ;; - # First accept the basic system types. - # The portable systems comes first. - # Each alternative MUST END IN A *, to match a version number. - # -sysv* is not here because it comes later, after sysvr4. - -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ - | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ - | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ - | -sym* | -kopensolaris* | -plan9* \ - | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ - | -aos* | -aros* | -cloudabi* | -sortix* \ - | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ - | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ - | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ - | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ - | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ - | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ - | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ - | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ - | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \ - | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ - | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ - | -linux-newlib* | -linux-musl* | -linux-uclibc* \ - | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ - | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ - | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ - | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ - | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ - | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ - | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ - | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ - | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox*) - # Remember, each alternative MUST END IN *, to match a version number. - ;; - -qnx*) - case $basic_machine in - x86-* | i*86-*) - ;; - *) - os=-nto$os - ;; - esac - ;; - -nto-qnx*) - ;; - -nto*) - os=`echo $os | sed -e 's|nto|nto-qnx|'` - ;; - -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ - | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ - | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) - ;; - -mac*) - os=`echo $os | sed -e 's|mac|macos|'` - ;; - -linux-dietlibc) - os=-linux-dietlibc - ;; - -linux*) - os=`echo $os | sed -e 's|linux|linux-gnu|'` - ;; - -sunos5*) - os=`echo $os | sed -e 's|sunos5|solaris2|'` - ;; - -sunos6*) - os=`echo $os | sed -e 's|sunos6|solaris3|'` - ;; - -opened*) - os=-openedition - ;; - -os400*) - os=-os400 - ;; - -wince*) - os=-wince - ;; - -osfrose*) - os=-osfrose - ;; - -osf*) - os=-osf - ;; - -utek*) - os=-bsd - ;; - -dynix*) - os=-bsd - ;; - -acis*) - os=-aos - ;; - -atheos*) - os=-atheos - ;; - -syllable*) - os=-syllable - ;; - -386bsd) - os=-bsd - ;; - -ctix* | -uts*) - os=-sysv - ;; - -nova*) - os=-rtmk-nova - ;; - -ns2 ) - os=-nextstep2 - ;; - -nsk*) - os=-nsk - ;; - # Preserve the version number of sinix5. - -sinix5.*) - os=`echo $os | sed -e 's|sinix|sysv|'` - ;; - -sinix*) - os=-sysv4 - ;; - -tpf*) - os=-tpf - ;; - -triton*) - os=-sysv3 - ;; - -oss*) - os=-sysv3 - ;; - -svr4) - os=-sysv4 - ;; - -svr3) - os=-sysv3 - ;; - -sysvr4) - os=-sysv4 - ;; - # This must come after -sysvr4. - -sysv*) - ;; - -ose*) - os=-ose - ;; - -es1800*) - os=-ose - ;; - -xenix) - os=-xenix - ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - os=-mint - ;; - -aros*) - os=-aros - ;; - -zvmoe) - os=-zvmoe - ;; - -dicos*) - os=-dicos - ;; - -nacl*) - ;; - -ios) - ;; - -none) - ;; - *) - # Get rid of the `-' at the beginning of $os. - os=`echo $os | sed 's/[^-]*-//'` - echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 - exit 1 - ;; -esac -else - -# Here we handle the default operating systems that come with various machines. -# The value should be what the vendor currently ships out the door with their -# machine or put another way, the most popular os provided with the machine. - -# Note that if you're going to try to match "-MANUFACTURER" here (say, -# "-sun"), then you have to tell the case statement up towards the top -# that MANUFACTURER isn't an operating system. Otherwise, code above -# will signal an error saying that MANUFACTURER isn't an operating -# system, and we'll never get to this point. - -case $basic_machine in - score-*) - os=-elf - ;; - spu-*) - os=-elf - ;; - *-acorn) - os=-riscix1.2 - ;; - arm*-rebel) - os=-linux - ;; - arm*-semi) - os=-aout - ;; - c4x-* | tic4x-*) - os=-coff - ;; - c8051-*) - os=-elf - ;; - hexagon-*) - os=-elf - ;; - tic54x-*) - os=-coff - ;; - tic55x-*) - os=-coff - ;; - tic6x-*) - os=-coff - ;; - # This must come before the *-dec entry. - pdp10-*) - os=-tops20 - ;; - pdp11-*) - os=-none - ;; - *-dec | vax-*) - os=-ultrix4.2 - ;; - m68*-apollo) - os=-domain - ;; - i386-sun) - os=-sunos4.0.2 - ;; - m68000-sun) - os=-sunos3 - ;; - m68*-cisco) - os=-aout - ;; - mep-*) - os=-elf - ;; - mips*-cisco) - os=-elf - ;; - mips*-*) - os=-elf - ;; - or32-*) - os=-coff - ;; - *-tti) # must be before sparc entry or we get the wrong os. - os=-sysv3 - ;; - sparc-* | *-sun) - os=-sunos4.1.1 - ;; - pru-*) - os=-elf - ;; - *-be) - os=-beos - ;; - *-haiku) - os=-haiku - ;; - *-ibm) - os=-aix - ;; - *-knuth) - os=-mmixware - ;; - *-wec) - os=-proelf - ;; - *-winbond) - os=-proelf - ;; - *-oki) - os=-proelf - ;; - *-hp) - os=-hpux - ;; - *-hitachi) - os=-hiux - ;; - i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) - os=-sysv - ;; - *-cbm) - os=-amigaos - ;; - *-dg) - os=-dgux - ;; - *-dolphin) - os=-sysv3 - ;; - m68k-ccur) - os=-rtu - ;; - m88k-omron*) - os=-luna - ;; - *-next ) - os=-nextstep - ;; - *-sequent) - os=-ptx - ;; - *-crds) - os=-unos - ;; - *-ns) - os=-genix - ;; - i370-*) - os=-mvs - ;; - *-next) - os=-nextstep3 - ;; - *-gould) - os=-sysv - ;; - *-highlevel) - os=-bsd - ;; - *-encore) - os=-bsd - ;; - *-sgi) - os=-irix - ;; - *-siemens) - os=-sysv4 - ;; - *-masscomp) - os=-rtu - ;; - f30[01]-fujitsu | f700-fujitsu) - os=-uxpv - ;; - *-rom68k) - os=-coff - ;; - *-*bug) - os=-coff - ;; - *-apple) - os=-macos - ;; - *-atari*) - os=-mint - ;; - *) - os=-none - ;; -esac -fi - -# Here we handle the case where we know the os, and the CPU type, but not the -# manufacturer. We pick the logical manufacturer. -vendor=unknown -case $basic_machine in - *-unknown) - case $os in - -riscix*) - vendor=acorn - ;; - -sunos*) - vendor=sun - ;; - -cnk*|-aix*) - vendor=ibm - ;; - -beos*) - vendor=be - ;; - -hpux*) - vendor=hp - ;; - -mpeix*) - vendor=hp - ;; - -hiux*) - vendor=hitachi - ;; - -unos*) - vendor=crds - ;; - -dgux*) - vendor=dg - ;; - -luna*) - vendor=omron - ;; - -genix*) - vendor=ns - ;; - -mvs* | -opened*) - vendor=ibm - ;; - -os400*) - vendor=ibm - ;; - -ptx*) - vendor=sequent - ;; - -tpf*) - vendor=ibm - ;; - -vxsim* | -vxworks* | -windiss*) - vendor=wrs - ;; - -aux*) - vendor=apple - ;; - -hms*) - vendor=hitachi - ;; - -mpw* | -macos*) - vendor=apple - ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - vendor=atari - ;; - -vos*) - vendor=stratus - ;; - esac - basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` - ;; -esac - -echo $basic_machine$os -exit - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/core/src/index/thirdparty/faiss/build-aux/install-sh b/core/src/index/thirdparty/faiss/build-aux/install-sh deleted file mode 100755 index 0360b79e7d..0000000000 --- a/core/src/index/thirdparty/faiss/build-aux/install-sh +++ /dev/null @@ -1,501 +0,0 @@ -#!/bin/sh -# install - install a program, script, or datafile - -scriptversion=2016-01-11.22; # UTC - -# This originates from X11R5 (mit/util/scripts/install.sh), which was -# later released in X11R6 (xc/config/util/install.sh) with the -# following copyright and license. -# -# Copyright (C) 1994 X Consortium -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to -# deal in the Software without restriction, including without limitation the -# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -# sell copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN -# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- -# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# -# Except as contained in this notice, the name of the X Consortium shall not -# be used in advertising or otherwise to promote the sale, use or other deal- -# ings in this Software without prior written authorization from the X Consor- -# tium. -# -# -# FSF changes to this file are in the public domain. -# -# Calling this script install-sh is preferred over install.sh, to prevent -# 'make' implicit rules from creating a file called install from it -# when there is no Makefile. -# -# This script is compatible with the BSD install script, but was written -# from scratch. - -tab=' ' -nl=' -' -IFS=" $tab$nl" - -# Set DOITPROG to "echo" to test this script. - -doit=${DOITPROG-} -doit_exec=${doit:-exec} - -# Put in absolute file names if you don't have them in your path; -# or use environment vars. - -chgrpprog=${CHGRPPROG-chgrp} -chmodprog=${CHMODPROG-chmod} -chownprog=${CHOWNPROG-chown} -cmpprog=${CMPPROG-cmp} -cpprog=${CPPROG-cp} -mkdirprog=${MKDIRPROG-mkdir} -mvprog=${MVPROG-mv} -rmprog=${RMPROG-rm} -stripprog=${STRIPPROG-strip} - -posix_mkdir= - -# Desired mode of installed file. -mode=0755 - -chgrpcmd= -chmodcmd=$chmodprog -chowncmd= -mvcmd=$mvprog -rmcmd="$rmprog -f" -stripcmd= - -src= -dst= -dir_arg= -dst_arg= - -copy_on_change=false -is_target_a_directory=possibly - -usage="\ -Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE - or: $0 [OPTION]... SRCFILES... DIRECTORY - or: $0 [OPTION]... -t DIRECTORY SRCFILES... - or: $0 [OPTION]... -d DIRECTORIES... - -In the 1st form, copy SRCFILE to DSTFILE. -In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. -In the 4th, create DIRECTORIES. - -Options: - --help display this help and exit. - --version display version info and exit. - - -c (ignored) - -C install only if different (preserve the last data modification time) - -d create directories instead of installing files. - -g GROUP $chgrpprog installed files to GROUP. - -m MODE $chmodprog installed files to MODE. - -o USER $chownprog installed files to USER. - -s $stripprog installed files. - -t DIRECTORY install into DIRECTORY. - -T report an error if DSTFILE is a directory. - -Environment variables override the default commands: - CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG - RMPROG STRIPPROG -" - -while test $# -ne 0; do - case $1 in - -c) ;; - - -C) copy_on_change=true;; - - -d) dir_arg=true;; - - -g) chgrpcmd="$chgrpprog $2" - shift;; - - --help) echo "$usage"; exit $?;; - - -m) mode=$2 - case $mode in - *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) - echo "$0: invalid mode: $mode" >&2 - exit 1;; - esac - shift;; - - -o) chowncmd="$chownprog $2" - shift;; - - -s) stripcmd=$stripprog;; - - -t) - is_target_a_directory=always - dst_arg=$2 - # Protect names problematic for 'test' and other utilities. - case $dst_arg in - -* | [=\(\)!]) dst_arg=./$dst_arg;; - esac - shift;; - - -T) is_target_a_directory=never;; - - --version) echo "$0 $scriptversion"; exit $?;; - - --) shift - break;; - - -*) echo "$0: invalid option: $1" >&2 - exit 1;; - - *) break;; - esac - shift -done - -# We allow the use of options -d and -T together, by making -d -# take the precedence; this is for compatibility with GNU install. - -if test -n "$dir_arg"; then - if test -n "$dst_arg"; then - echo "$0: target directory not allowed when installing a directory." >&2 - exit 1 - fi -fi - -if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then - # When -d is used, all remaining arguments are directories to create. - # When -t is used, the destination is already specified. - # Otherwise, the last argument is the destination. Remove it from $@. - for arg - do - if test -n "$dst_arg"; then - # $@ is not empty: it contains at least $arg. - set fnord "$@" "$dst_arg" - shift # fnord - fi - shift # arg - dst_arg=$arg - # Protect names problematic for 'test' and other utilities. - case $dst_arg in - -* | [=\(\)!]) dst_arg=./$dst_arg;; - esac - done -fi - -if test $# -eq 0; then - if test -z "$dir_arg"; then - echo "$0: no input file specified." >&2 - exit 1 - fi - # It's OK to call 'install-sh -d' without argument. - # This can happen when creating conditional directories. - exit 0 -fi - -if test -z "$dir_arg"; then - if test $# -gt 1 || test "$is_target_a_directory" = always; then - if test ! -d "$dst_arg"; then - echo "$0: $dst_arg: Is not a directory." >&2 - exit 1 - fi - fi -fi - -if test -z "$dir_arg"; then - do_exit='(exit $ret); exit $ret' - trap "ret=129; $do_exit" 1 - trap "ret=130; $do_exit" 2 - trap "ret=141; $do_exit" 13 - trap "ret=143; $do_exit" 15 - - # Set umask so as not to create temps with too-generous modes. - # However, 'strip' requires both read and write access to temps. - case $mode in - # Optimize common cases. - *644) cp_umask=133;; - *755) cp_umask=22;; - - *[0-7]) - if test -z "$stripcmd"; then - u_plus_rw= - else - u_plus_rw='% 200' - fi - cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; - *) - if test -z "$stripcmd"; then - u_plus_rw= - else - u_plus_rw=,u+rw - fi - cp_umask=$mode$u_plus_rw;; - esac -fi - -for src -do - # Protect names problematic for 'test' and other utilities. - case $src in - -* | [=\(\)!]) src=./$src;; - esac - - if test -n "$dir_arg"; then - dst=$src - dstdir=$dst - test -d "$dstdir" - dstdir_status=$? - else - - # Waiting for this to be detected by the "$cpprog $src $dsttmp" command - # might cause directories to be created, which would be especially bad - # if $src (and thus $dsttmp) contains '*'. - if test ! -f "$src" && test ! -d "$src"; then - echo "$0: $src does not exist." >&2 - exit 1 - fi - - if test -z "$dst_arg"; then - echo "$0: no destination specified." >&2 - exit 1 - fi - dst=$dst_arg - - # If destination is a directory, append the input filename; won't work - # if double slashes aren't ignored. - if test -d "$dst"; then - if test "$is_target_a_directory" = never; then - echo "$0: $dst_arg: Is a directory" >&2 - exit 1 - fi - dstdir=$dst - dst=$dstdir/`basename "$src"` - dstdir_status=0 - else - dstdir=`dirname "$dst"` - test -d "$dstdir" - dstdir_status=$? - fi - fi - - obsolete_mkdir_used=false - - if test $dstdir_status != 0; then - case $posix_mkdir in - '') - # Create intermediate dirs using mode 755 as modified by the umask. - # This is like FreeBSD 'install' as of 1997-10-28. - umask=`umask` - case $stripcmd.$umask in - # Optimize common cases. - *[2367][2367]) mkdir_umask=$umask;; - .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; - - *[0-7]) - mkdir_umask=`expr $umask + 22 \ - - $umask % 100 % 40 + $umask % 20 \ - - $umask % 10 % 4 + $umask % 2 - `;; - *) mkdir_umask=$umask,go-w;; - esac - - # With -d, create the new directory with the user-specified mode. - # Otherwise, rely on $mkdir_umask. - if test -n "$dir_arg"; then - mkdir_mode=-m$mode - else - mkdir_mode= - fi - - posix_mkdir=false - case $umask in - *[123567][0-7][0-7]) - # POSIX mkdir -p sets u+wx bits regardless of umask, which - # is incompatible with FreeBSD 'install' when (umask & 300) != 0. - ;; - *) - tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ - trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 - - if (umask $mkdir_umask && - exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 - then - if test -z "$dir_arg" || { - # Check for POSIX incompatibilities with -m. - # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or - # other-writable bit of parent directory when it shouldn't. - # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. - ls_ld_tmpdir=`ls -ld "$tmpdir"` - case $ls_ld_tmpdir in - d????-?r-*) different_mode=700;; - d????-?--*) different_mode=755;; - *) false;; - esac && - $mkdirprog -m$different_mode -p -- "$tmpdir" && { - ls_ld_tmpdir_1=`ls -ld "$tmpdir"` - test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" - } - } - then posix_mkdir=: - fi - rmdir "$tmpdir/d" "$tmpdir" - else - # Remove any dirs left behind by ancient mkdir implementations. - rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null - fi - trap '' 0;; - esac;; - esac - - if - $posix_mkdir && ( - umask $mkdir_umask && - $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" - ) - then : - else - - # The umask is ridiculous, or mkdir does not conform to POSIX, - # or it failed possibly due to a race condition. Create the - # directory the slow way, step by step, checking for races as we go. - - case $dstdir in - /*) prefix='/';; - [-=\(\)!]*) prefix='./';; - *) prefix='';; - esac - - oIFS=$IFS - IFS=/ - set -f - set fnord $dstdir - shift - set +f - IFS=$oIFS - - prefixes= - - for d - do - test X"$d" = X && continue - - prefix=$prefix$d - if test -d "$prefix"; then - prefixes= - else - if $posix_mkdir; then - (umask=$mkdir_umask && - $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break - # Don't fail if two instances are running concurrently. - test -d "$prefix" || exit 1 - else - case $prefix in - *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; - *) qprefix=$prefix;; - esac - prefixes="$prefixes '$qprefix'" - fi - fi - prefix=$prefix/ - done - - if test -n "$prefixes"; then - # Don't fail if two instances are running concurrently. - (umask $mkdir_umask && - eval "\$doit_exec \$mkdirprog $prefixes") || - test -d "$dstdir" || exit 1 - obsolete_mkdir_used=true - fi - fi - fi - - if test -n "$dir_arg"; then - { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && - { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && - { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || - test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 - else - - # Make a couple of temp file names in the proper directory. - dsttmp=$dstdir/_inst.$$_ - rmtmp=$dstdir/_rm.$$_ - - # Trap to clean up those temp files at exit. - trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 - - # Copy the file name to the temp name. - (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && - - # and set any options; do chmod last to preserve setuid bits. - # - # If any of these fail, we abort the whole thing. If we want to - # ignore errors from any of these, just make sure not to ignore - # errors from the above "$doit $cpprog $src $dsttmp" command. - # - { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && - { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && - { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && - { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && - - # If -C, don't bother to copy if it wouldn't change the file. - if $copy_on_change && - old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && - new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && - set -f && - set X $old && old=:$2:$4:$5:$6 && - set X $new && new=:$2:$4:$5:$6 && - set +f && - test "$old" = "$new" && - $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 - then - rm -f "$dsttmp" - else - # Rename the file to the real destination. - $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || - - # The rename failed, perhaps because mv can't rename something else - # to itself, or perhaps because mv is so ancient that it does not - # support -f. - { - # Now remove or move aside any old file at destination location. - # We try this two ways since rm can't unlink itself on some - # systems and the destination file might be busy for other - # reasons. In this case, the final cleanup might fail but the new - # file should still install successfully. - { - test ! -f "$dst" || - $doit $rmcmd -f "$dst" 2>/dev/null || - { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && - { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } - } || - { echo "$0: cannot unlink or rename $dst" >&2 - (exit 1); exit 1 - } - } && - - # Now rename the file to the real destination. - $doit $mvcmd "$dsttmp" "$dst" - } - fi || exit 1 - - trap '' 0 - fi -done - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "scriptversion=" -# time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC0" -# time-stamp-end: "; # UTC" -# End: diff --git a/core/src/index/thirdparty/faiss/build.sh b/core/src/index/thirdparty/faiss/build.sh deleted file mode 100755 index ea6b4c0c7d..0000000000 --- a/core/src/index/thirdparty/faiss/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#./configure CPUFLAGS='-mavx -mf16c -msse4 -mpopcnt' CXXFLAGS='-O0 -g -fPIC -m64 -Wno-sign-compare -Wall -Wextra' --prefix=$PWD --with-cuda-arch=-gencode=arch=compute_75,code=sm_75 --with-cuda=/usr/local/cuda -./configure --prefix=$PWD CFLAGS='-g -fPIC' CXXFLAGS='-O0 -g -fPIC -DELPP_THREAD_SAFE -fopenmp -g -fPIC -mf16c -O3' --without-python --with-cuda=/usr/local/cuda --with-cuda-arch='-gencode=arch=compute_60,code=sm_60 -gencode=arch=compute_61,code=sm_61 -gencode=arch=compute_70,code=sm_70 -gencode=arch=compute_75,code=sm_75' -make install -j8 diff --git a/core/src/index/thirdparty/faiss/c_api/AutoTune_c.cpp b/core/src/index/thirdparty/faiss/c_api/AutoTune_c.cpp deleted file mode 100644 index 2f412d6aaa..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/AutoTune_c.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#include -#include "AutoTune.h" -#include "AutoTune_c.h" -#include "macros_impl.h" - -using faiss::Index; -using faiss::ParameterRange; -using faiss::ParameterSpace; - -const char* faiss_ParameterRange_name(const FaissParameterRange* range) { - return reinterpret_cast(range)->name.c_str(); -} - -void faiss_ParameterRange_values(FaissParameterRange* range, double** p_values, size_t* p_size) { - auto& values = reinterpret_cast(range)->values; - *p_values = values.data(); - *p_size = values.size(); -} - -int faiss_ParameterSpace_new(FaissParameterSpace** space) { - try { - auto new_space = new ParameterSpace(); - *space = reinterpret_cast(new_space); - } CATCH_AND_HANDLE -} - -DEFINE_DESTRUCTOR(ParameterSpace) - -size_t faiss_ParameterSpace_n_combinations(const FaissParameterSpace* space) { - return reinterpret_cast(space)->n_combinations(); -} - -int faiss_ParameterSpace_combination_name(const FaissParameterSpace* space, size_t cno, char* char_buffer, size_t size) { - try { - auto rep = reinterpret_cast(space)->combination_name(cno); - strncpy(char_buffer, rep.c_str(), size); - } CATCH_AND_HANDLE -} - -int faiss_ParameterSpace_set_index_parameters(const FaissParameterSpace* space, FaissIndex* cindex, const char* param_string) { - try { - auto index = reinterpret_cast(cindex); - reinterpret_cast(space)->set_index_parameters(index, param_string); - } CATCH_AND_HANDLE -} - -/// set a combination of parameters on an index -int faiss_ParameterSpace_set_index_parameters_cno(const FaissParameterSpace* space, FaissIndex* cindex, size_t cno) { - try { - auto index = reinterpret_cast(cindex); - reinterpret_cast(space)->set_index_parameters(index, cno); - } CATCH_AND_HANDLE -} - -int faiss_ParameterSpace_set_index_parameter(const FaissParameterSpace* space, FaissIndex* cindex, const char * name, double value) { - try { - auto index = reinterpret_cast(cindex); - reinterpret_cast(space)->set_index_parameter(index, name, value); - } CATCH_AND_HANDLE -} - -void faiss_ParameterSpace_display(const FaissParameterSpace* space) { - reinterpret_cast(space)->display(); -} - -int faiss_ParameterSpace_add_range(FaissParameterSpace* space, const char* name, FaissParameterRange** p_range) { - try { - ParameterRange& range = reinterpret_cast(space)->add_range(name); - if (p_range) { - *p_range = reinterpret_cast(&range); - } - } CATCH_AND_HANDLE -} \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/c_api/AutoTune_c.h b/core/src/index/thirdparty/faiss/c_api/AutoTune_c.h deleted file mode 100644 index d870921c04..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/AutoTune_c.h +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c -*- - -#ifndef FAISS_AUTO_TUNE_C_H -#define FAISS_AUTO_TUNE_C_H - -#include "faiss_c.h" -#include "Index_c.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/// possible values of a parameter, sorted from least to most expensive/accurate -FAISS_DECLARE_CLASS(ParameterRange) - -FAISS_DECLARE_GETTER(ParameterRange, const char*, name) - -/// Getter for the values in the range. The output values are invalidated -/// upon any other modification of the range. -void faiss_ParameterRange_values(FaissParameterRange*, double**, size_t*); - -/** Uses a-priori knowledge on the Faiss indexes to extract tunable parameters. - */ -FAISS_DECLARE_CLASS(ParameterSpace) - -/// Parameter space default constructor -int faiss_ParameterSpace_new(FaissParameterSpace** space); - -/// nb of combinations, = product of values sizes -size_t faiss_ParameterSpace_n_combinations(const FaissParameterSpace*); - -/// get string representation of the combination -/// by writing it to the given character buffer. -/// A buffer size of 1000 ensures that the full name is collected. -int faiss_ParameterSpace_combination_name(const FaissParameterSpace*, size_t, char*, size_t); - -/// set a combination of parameters described by a string -int faiss_ParameterSpace_set_index_parameters(const FaissParameterSpace*, FaissIndex*, const char *); - -/// set a combination of parameters on an index -int faiss_ParameterSpace_set_index_parameters_cno(const FaissParameterSpace*, FaissIndex*, size_t); - -/// set one of the parameters -int faiss_ParameterSpace_set_index_parameter(const FaissParameterSpace*, FaissIndex*, const char *, double); - -/// print a description on stdout -void faiss_ParameterSpace_display(const FaissParameterSpace*); - -/// add a new parameter (or return it if it exists) -int faiss_ParameterSpace_add_range(FaissParameterSpace*, const char*, FaissParameterRange**); - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/c_api/Clustering_c.cpp b/core/src/index/thirdparty/faiss/c_api/Clustering_c.cpp deleted file mode 100644 index e4541458c0..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/Clustering_c.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#include "Clustering_c.h" -#include "Clustering.h" -#include "Index.h" -#include -#include "macros_impl.h" - -extern "C" { - -using faiss::Clustering; -using faiss::ClusteringParameters; -using faiss::Index; -using faiss::ClusteringIterationStats; - -DEFINE_GETTER(Clustering, int, niter) -DEFINE_GETTER(Clustering, int, nredo) -DEFINE_GETTER(Clustering, int, verbose) -DEFINE_GETTER(Clustering, int, spherical) -DEFINE_GETTER(Clustering, int, update_index) -DEFINE_GETTER(Clustering, int, frozen_centroids) - -DEFINE_GETTER(Clustering, int, min_points_per_centroid) -DEFINE_GETTER(Clustering, int, max_points_per_centroid) - -DEFINE_GETTER(Clustering, int, seed) - -/// getter for d -DEFINE_GETTER(Clustering, size_t, d) - -/// getter for k -DEFINE_GETTER(Clustering, size_t, k) - -DEFINE_GETTER(ClusteringIterationStats, float, obj) -DEFINE_GETTER(ClusteringIterationStats, double, time) -DEFINE_GETTER(ClusteringIterationStats, double, time_search) -DEFINE_GETTER(ClusteringIterationStats, double, imbalance_factor) -DEFINE_GETTER(ClusteringIterationStats, int, nsplit) - -void faiss_ClusteringParameters_init(FaissClusteringParameters* params) { - ClusteringParameters d; - params->frozen_centroids = d.frozen_centroids; - params->max_points_per_centroid = d.max_points_per_centroid; - params->min_points_per_centroid = d.min_points_per_centroid; - params->niter = d.niter; - params->nredo = d.nredo; - params->seed = d.seed; - params->spherical = d.spherical; - params->update_index = d.update_index; - params->verbose = d.verbose; -} - -// This conversion is required because the two types are not memory-compatible -inline ClusteringParameters from_faiss_c(const FaissClusteringParameters* params) { - ClusteringParameters o; - o.frozen_centroids = params->frozen_centroids; - o.max_points_per_centroid = params->max_points_per_centroid; - o.min_points_per_centroid = params->min_points_per_centroid; - o.niter = params->niter; - o.nredo = params->nredo; - o.seed = params->seed; - o.spherical = params->spherical; - o.update_index = params->update_index; - o.verbose = params->verbose; - return o; -} - -/// getter for centroids (size = k * d) -void faiss_Clustering_centroids( - FaissClustering* clustering, float** centroids, size_t* size) { - std::vector& v = reinterpret_cast(clustering)->centroids; - if (centroids) { - *centroids = v.data(); - } - if (size) { - *size = v.size(); - } -} - -/// getter for iteration stats -void faiss_Clustering_iteration_stats( - FaissClustering* clustering, FaissClusteringIterationStats** iteration_stats, size_t* size) { - std::vector& v = reinterpret_cast(clustering)->iteration_stats; - if (iteration_stats) { - *iteration_stats = reinterpret_cast(v.data()); - } - if (size) { - *size = v.size(); - } -} - -/// the only mandatory parameters are k and d -int faiss_Clustering_new(FaissClustering** p_clustering, int d, int k) { - try { - Clustering* c = new Clustering(d, k); - *p_clustering = reinterpret_cast(c); - return 0; - } CATCH_AND_HANDLE -} - -int faiss_Clustering_new_with_params( - FaissClustering** p_clustering, int d, int k, const FaissClusteringParameters* cp) { - try { - Clustering* c = new Clustering(d, k, from_faiss_c(cp)); - *p_clustering = reinterpret_cast(c); - return 0; - } CATCH_AND_HANDLE -} - -/// Index is used during the assignment stage -int faiss_Clustering_train( - FaissClustering* clustering, idx_t n, const float* x, FaissIndex* index) { - try { - reinterpret_cast(clustering)->train( - n, x, *reinterpret_cast(index)); - return 0; - } CATCH_AND_HANDLE -} - -void faiss_Clustering_free(FaissClustering* clustering) { - delete reinterpret_cast(clustering); -} - -int faiss_kmeans_clustering (size_t d, size_t n, size_t k, - const float *x, - float *centroids, - float *q_error) { - try { - float out = faiss::kmeans_clustering(d, n, k, x, centroids); - if (q_error) { - *q_error = out; - } - return 0; - } CATCH_AND_HANDLE -} - -} diff --git a/core/src/index/thirdparty/faiss/c_api/Clustering_c.h b/core/src/index/thirdparty/faiss/c_api/Clustering_c.h deleted file mode 100644 index af82152e60..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/Clustering_c.h +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved -// -*- c -*- - -#ifndef FAISS_CLUSTERING_C_H -#define FAISS_CLUSTERING_C_H - -#include "Index_c.h" -#include "faiss_c.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** Class for the clustering parameters. Can be passed to the - * constructor of the Clustering object. - */ -typedef struct FaissClusteringParameters { - int niter; ///< clustering iterations - int nredo; ///< redo clustering this many times and keep best - - int verbose; ///< (bool) - int spherical; ///< (bool) do we want normalized centroids? - int update_index; ///< (bool) update index after each iteration? - int frozen_centroids; ///< (bool) use the centroids provided as input and do not change them during iterations - - int min_points_per_centroid; ///< otherwise you get a warning - int max_points_per_centroid; ///< to limit size of dataset - - int seed; ///< seed for the random number generator -} FaissClusteringParameters; - - -/// Sets the ClusteringParameters object with reasonable defaults -void faiss_ClusteringParameters_init(FaissClusteringParameters* params); - - -/** clustering based on assignment - centroid update iterations - * - * The clustering is based on an Index object that assigns training - * points to the centroids. Therefore, at each iteration the centroids - * are added to the index. - * - * On output, the centroids table is set to the latest version - * of the centroids and they are also added to the index. If the - * centroids table it is not empty on input, it is also used for - * initialization. - * - * To do several clusterings, just call train() several times on - * different training sets, clearing the centroid table in between. - */ -FAISS_DECLARE_CLASS(Clustering) - -FAISS_DECLARE_GETTER(Clustering, int, niter) -FAISS_DECLARE_GETTER(Clustering, int, nredo) -FAISS_DECLARE_GETTER(Clustering, int, verbose) -FAISS_DECLARE_GETTER(Clustering, int, spherical) -FAISS_DECLARE_GETTER(Clustering, int, update_index) -FAISS_DECLARE_GETTER(Clustering, int, frozen_centroids) - -FAISS_DECLARE_GETTER(Clustering, int, min_points_per_centroid) -FAISS_DECLARE_GETTER(Clustering, int, max_points_per_centroid) - -FAISS_DECLARE_GETTER(Clustering, int, seed) - -/// getter for d -FAISS_DECLARE_GETTER(Clustering, size_t, d) - -/// getter for k -FAISS_DECLARE_GETTER(Clustering, size_t, k) - -FAISS_DECLARE_CLASS(ClusteringIterationStats) -FAISS_DECLARE_GETTER(ClusteringIterationStats, float, obj) -FAISS_DECLARE_GETTER(ClusteringIterationStats, double, time) -FAISS_DECLARE_GETTER(ClusteringIterationStats, double, time_search) -FAISS_DECLARE_GETTER(ClusteringIterationStats, double, imbalance_factor) -FAISS_DECLARE_GETTER(ClusteringIterationStats, int, nsplit) - -/// getter for centroids (size = k * d) -void faiss_Clustering_centroids( - FaissClustering* clustering, float** centroids, size_t* size); - -/// getter for iteration stats -void faiss_Clustering_iteration_stats( - FaissClustering* clustering, FaissClusteringIterationStats** iteration_stats, size_t* size); - -/// the only mandatory parameters are k and d -int faiss_Clustering_new(FaissClustering** p_clustering, int d, int k); - -int faiss_Clustering_new_with_params( - FaissClustering** p_clustering, int d, int k, const FaissClusteringParameters* cp); - -int faiss_Clustering_train( - FaissClustering* clustering, idx_t n, const float* x, FaissIndex* index); - -void faiss_Clustering_free(FaissClustering* clustering); - -/** simplified interface - * - * @param d dimension of the data - * @param n nb of training vectors - * @param k nb of output centroids - * @param x training set (size n * d) - * @param centroids output centroids (size k * d) - * @param q_error final quantization error - * @return error code - */ -int faiss_kmeans_clustering (size_t d, size_t n, size_t k, - const float *x, - float *centroids, - float *q_error); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/core/src/index/thirdparty/faiss/c_api/INSTALL.md b/core/src/index/thirdparty/faiss/c_api/INSTALL.md deleted file mode 100644 index b640d7db73..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/INSTALL.md +++ /dev/null @@ -1,100 +0,0 @@ -Faiss C API -=========== - -Faiss provides a pure C interface, which can subsequently be used either in pure C programs or to produce bindings for programming languages with Foreign Function Interface (FFI) support. Although this is not required for the Python interface, some other programming languages (e.g. Rust and Julia) do not have SWIG support. - -Compilation instructions ------------------------- - -The full contents of the pure C API are in the ["c_api"](c_api/) folder. -Please be sure to follow the instructions on [building the main C++ library](../INSTALL.md#step-1-compiling-the-c-faiss) first. -Then, enter the [c_api](c_api/) directory and run - - `make` - -This builds the dynamic library "faiss_c", containing the full implementation of Faiss and the necessary wrappers for the C interface. It does not depend on libfaiss.a or the C++ standard library. It will also build an example program `bin/example_c`. - -Using the API -------------- - -The C API is composed of: - -- A set of C header files comprising the main Faiss interfaces, converted for use in C. Each file follows the format `«name»_c.h`, where `«name»` is the respective name from the C++ API. For example, the file [Index_c.h](./Index_c.h) file corresponds to the base `Index` API. Functions are declared with the `faiss_` prefix (e.g. `faiss_IndexFlat_new`), whereas new types have the `Faiss` prefix (e.g. `FaissIndex`, `FaissMetricType`, ...). -- A dynamic library, compiled from the sources in the same folder, encloses the implementation of the library and wrapper functions. - -The index factory is available via the `faiss_index_factory` function in `AutoTune_c.h`: - -```c -FaissIndex* index = NULL; -int c = faiss_index_factory(&index, 64, "Flat", METRIC_L2); -if (c) { - // operation failed -} -``` - -Most operations that you would find as member functions are available with the format `faiss_«classname»_«member»`. - -```c -idx_t ntotal = faiss_Index_ntotal(index); -``` - -Since this is C, the index needs to be freed manually in the end: - -```c -faiss_Index_free(index); -``` - -Error handling is done by examining the error code returned by operations with recoverable errors. -The code identifies the type of exception that rose from the implementation. Fetching the -corresponding error message can be done by calling the function `faiss_get_last_error()` from -`error_c.h`. Getter functions and `free` functions do not return an error code. - -```c -int c = faiss_Index_add(index, nb, xb); -if (c) { - printf("%s", faiss_get_last_error()); - exit(-1); -} -``` - -An example is included, which is built automatically for the target `all`. It can also be built separately: - - `make bin/example_c` - -Building with GPU support -------------------------- - -For GPU support, a separate dynamic library in the "c_api/gpu" directory needs to be built. - - `make` - -The "gpufaiss_c" dynamic library contains the GPU and CPU implementations of Faiss, which means that -it can be used in place of "faiss_c". The same library will dynamically link with the CUDA runtime -and cuBLAS. - -Using the GPU with the C API ----------------------------- - -A standard GPU resurces object can be obtained by the name `FaissStandardGpuResources`: - -```c -FaissStandardGpuResources* gpu_res = NULL; -int c = faiss_StandardGpuResources_new(&gpu_res); -if (c) { - printf("%s", faiss_get_last_error()); - exit(-1); -} -``` - -Similarly to the C++ API, a CPU index can be converted to a GPU index: - -```c -FaissIndex* cpu_index = NULL; -int c = faiss_index_factory(&cpu_index, d, "Flat", METRIC_L2); -if (c) { /* ... */ } -FaissGpuIndex* gpu_index = NULL; -c = faiss_index_cpu_to_gpu(gpu_res, 0, cpu_index, &gpu_index); -if (c) { /* ... */ } -``` - -A more complete example is available by the name `bin/example_gpu_c`. diff --git a/core/src/index/thirdparty/faiss/c_api/IndexFlat_c.cpp b/core/src/index/thirdparty/faiss/c_api/IndexFlat_c.cpp deleted file mode 100644 index 4b741922e8..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/IndexFlat_c.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#include "IndexFlat_c.h" -#include "IndexFlat.h" -#include "Index.h" -#include "macros_impl.h" - -extern "C" { - -using faiss::Index; -using faiss::IndexFlat; -using faiss::IndexFlatIP; -using faiss::IndexFlatL2; -using faiss::IndexFlatL2BaseShift; -using faiss::IndexRefineFlat; -using faiss::IndexFlat1D; - -DEFINE_DESTRUCTOR(IndexFlat) -DEFINE_INDEX_DOWNCAST(IndexFlat) - -int faiss_IndexFlat_new(FaissIndexFlat** p_index) { - try { - *p_index = reinterpret_cast(new IndexFlat()); - return 0; - } CATCH_AND_HANDLE -} - -int faiss_IndexFlat_new_with(FaissIndexFlat** p_index, idx_t d, FaissMetricType metric) { - try { - IndexFlat* index = new IndexFlat(d, static_cast(metric)); - *p_index = reinterpret_cast(index); - return 0; - } CATCH_AND_HANDLE -} - -void faiss_IndexFlat_xb(FaissIndexFlat* index, float** p_xb, size_t* p_size) { - auto& xb = reinterpret_cast(index)->xb; - *p_xb = xb.data(); - if (p_size) { - *p_size = xb.size(); - } -} - -int faiss_IndexFlat_compute_distance_subset( - FaissIndex* index, - idx_t n, - const float *x, - idx_t k, - float *distances, - const idx_t *labels) { - try { - reinterpret_cast(index)->compute_distance_subset( - n, x, k, distances, labels); - return 0; - } CATCH_AND_HANDLE -} - -int faiss_IndexFlatIP_new(FaissIndexFlatIP** p_index) { - try { - IndexFlatIP* index = new IndexFlatIP(); - *p_index = reinterpret_cast(index); - return 0; - } CATCH_AND_HANDLE -} - -int faiss_IndexFlatIP_new_with(FaissIndexFlatIP** p_index, idx_t d) { - try { - IndexFlatIP* index = new IndexFlatIP(d); - *p_index = reinterpret_cast(index); - return 0; - } CATCH_AND_HANDLE -} - -int faiss_IndexFlatL2_new(FaissIndexFlatL2** p_index) { - try { - IndexFlatL2* index = new IndexFlatL2(); - *p_index = reinterpret_cast(index); - return 0; - } CATCH_AND_HANDLE -} - -int faiss_IndexFlatL2_new_with(FaissIndexFlatL2** p_index, idx_t d) { - try { - IndexFlatL2* index = new IndexFlatL2(d); - *p_index = reinterpret_cast(index); - return 0; - } CATCH_AND_HANDLE -} - -int faiss_IndexFlatL2BaseShift_new(FaissIndexFlatL2BaseShift** p_index, idx_t d, size_t nshift, const float *shift) { - try { - IndexFlatL2BaseShift* index = new IndexFlatL2BaseShift(d, nshift, shift); - *p_index = reinterpret_cast(index); - return 0; - } CATCH_AND_HANDLE -} - -int faiss_IndexRefineFlat_new(FaissIndexRefineFlat** p_index, FaissIndex* base_index) { - try { - IndexRefineFlat* index = new IndexRefineFlat( - reinterpret_cast(base_index)); - *p_index = reinterpret_cast(index); - return 0; - } CATCH_AND_HANDLE -} - -DEFINE_DESTRUCTOR(IndexRefineFlat) - -int faiss_IndexFlat1D_new(FaissIndexFlat1D** p_index) { - try { - IndexFlat1D* index = new IndexFlat1D(); - *p_index = reinterpret_cast(index); - return 0; - } CATCH_AND_HANDLE -} - -int faiss_IndexFlat1D_new_with(FaissIndexFlat1D** p_index, int continuous_update) { - try { - IndexFlat1D* index = new IndexFlat1D(static_cast(continuous_update)); - *p_index = reinterpret_cast(index); - return 0; - } CATCH_AND_HANDLE -} - -int faiss_IndexFlat1D_update_permutation(FaissIndexFlat1D* index) { - try { - reinterpret_cast(index)->update_permutation(); - return 0; - } CATCH_AND_HANDLE -} - -} diff --git a/core/src/index/thirdparty/faiss/c_api/IndexFlat_c.h b/core/src/index/thirdparty/faiss/c_api/IndexFlat_c.h deleted file mode 100644 index 072ba7dcf3..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/IndexFlat_c.h +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved -// -*- c -*- - -#ifndef FAISS_INDEX_FLAT_C_H -#define FAISS_INDEX_FLAT_C_H - -#include "Index_c.h" -#include "faiss_c.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// forward declaration -typedef enum FaissMetricType FaissMetricType; - -/** Opaque type for IndexFlat */ -FAISS_DECLARE_CLASS_INHERITED(IndexFlat, Index) - -int faiss_IndexFlat_new(FaissIndexFlat** p_index); - -int faiss_IndexFlat_new_with(FaissIndexFlat** p_index, idx_t d, FaissMetricType metric); - -/** get a pointer to the index's internal data (the `xb` field). The outputs - * become invalid after any data addition or removal operation. - * - * @param index opaque pointer to index object - * @param p_xb output, the pointer to the beginning of `xb`. - * @param p_size output, the current size of `sb` in number of float values. - */ -void faiss_IndexFlat_xb(FaissIndexFlat* index, float** p_xb, size_t* p_size); - -/** attempt a dynamic cast to a flat index, thus checking - * check whether the underlying index type is `IndexFlat`. - * - * @param index opaque pointer to index object - * @return the same pointer if the index is a flat index, NULL otherwise - */ -FAISS_DECLARE_INDEX_DOWNCAST(IndexFlat) - -FAISS_DECLARE_DESTRUCTOR(IndexFlat) - -/** compute distance with a subset of vectors - * - * @param index opaque pointer to index object - * @param x query vectors, size n * d - * @param labels indices of the vectors that should be compared - * for each query vector, size n * k - * @param distances - * corresponding output distances, size n * k - */ -int faiss_IndexFlat_compute_distance_subset( - FaissIndex *index, - idx_t n, - const float *x, - idx_t k, - float *distances, - const idx_t *labels); - -/** Opaque type for IndexFlatIP */ -FAISS_DECLARE_CLASS_INHERITED(IndexFlatIP, Index) - -int faiss_IndexFlatIP_new(FaissIndexFlatIP** p_index); - -int faiss_IndexFlatIP_new_with(FaissIndexFlatIP** p_index, idx_t d); - -/** Opaque type for IndexFlatL2 */ -FAISS_DECLARE_CLASS_INHERITED(IndexFlatL2, Index) - -int faiss_IndexFlatL2_new(FaissIndexFlatL2** p_index); - -int faiss_IndexFlatL2_new_with(FaissIndexFlatL2** p_index, idx_t d); - -/** Opaque type for IndexFlatL2BaseShift - * - * same as an IndexFlatL2 but a value is subtracted from each distance - */ -FAISS_DECLARE_CLASS_INHERITED(IndexFlatL2BaseShift, Index) - -int faiss_IndexFlatL2BaseShift_new(FaissIndexFlatL2BaseShift** p_index, idx_t d, size_t nshift, const float *shift); - -/** Opaque type for IndexRefineFlat - * - * Index that queries in a base_index (a fast one) and refines the - * results with an exact search, hopefully improving the results. - */ -FAISS_DECLARE_CLASS_INHERITED(IndexRefineFlat, Index) - -int faiss_IndexRefineFlat_new(FaissIndexRefineFlat** p_index, FaissIndex* base_index); - -FAISS_DECLARE_DESTRUCTOR(IndexRefineFlat) - -/** Opaque type for IndexFlat1D - * - * optimized version for 1D "vectors" - */ -FAISS_DECLARE_CLASS_INHERITED(IndexFlat1D, Index) - -int faiss_IndexFlat1D_new(FaissIndexFlat1D** p_index); -int faiss_IndexFlat1D_new_with(FaissIndexFlat1D** p_index, int continuous_update); - -int faiss_IndexFlat1D_update_permutation(FaissIndexFlat1D* index); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/core/src/index/thirdparty/faiss/c_api/IndexIVFFlat_c.cpp b/core/src/index/thirdparty/faiss/c_api/IndexIVFFlat_c.cpp deleted file mode 100644 index 410e39a6c5..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/IndexIVFFlat_c.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#include "Index_c.h" -#include "Clustering_c.h" -#include "IndexIVFFlat_c.h" -#include "IndexIVFFlat.h" -#include "macros_impl.h" - -using faiss::Index; -using faiss::IndexIVFFlat; -using faiss::MetricType; - -DEFINE_DESTRUCTOR(IndexIVFFlat) -DEFINE_INDEX_DOWNCAST(IndexIVFFlat) - -int faiss_IndexIVFFlat_new(FaissIndexIVFFlat** p_index) { - try { - *p_index = reinterpret_cast(new IndexIVFFlat()); - } CATCH_AND_HANDLE -} - -int faiss_IndexIVFFlat_new_with(FaissIndexIVFFlat** p_index, - FaissIndex* quantizer, size_t d, size_t nlist) -{ - try { - auto q = reinterpret_cast(quantizer); - *p_index = reinterpret_cast(new IndexIVFFlat(q, d, nlist)); - } CATCH_AND_HANDLE -} - -int faiss_IndexIVFFlat_new_with_metric( - FaissIndexIVFFlat** p_index, FaissIndex* quantizer, size_t d, size_t nlist, - FaissMetricType metric) -{ - try { - auto q = reinterpret_cast(quantizer); - auto m = static_cast(metric); - *p_index = reinterpret_cast(new IndexIVFFlat(q, d, nlist, m)); - } CATCH_AND_HANDLE -} - -int faiss_IndexIVFFlat_add_core(FaissIndexIVFFlat* index, idx_t n, - const float * x, const idx_t *xids, const int64_t *precomputed_idx) -{ - try { - reinterpret_cast(index)->add_core(n, x, xids, precomputed_idx); - } CATCH_AND_HANDLE -} - -int faiss_IndexIVFFlat_update_vectors(FaissIndexIVFFlat* index, int nv, - idx_t *idx, const float *v) -{ - try { - reinterpret_cast(index)->update_vectors(nv, idx, v); - } CATCH_AND_HANDLE -} diff --git a/core/src/index/thirdparty/faiss/c_api/IndexIVFFlat_c.h b/core/src/index/thirdparty/faiss/c_api/IndexIVFFlat_c.h deleted file mode 100644 index 4c5f3ec25b..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/IndexIVFFlat_c.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c -*- - -#ifndef FAISS_INDEX_IVF_FLAT_C_H -#define FAISS_INDEX_IVF_FLAT_C_H - -#include "faiss_c.h" -#include "Index_c.h" -#include "Clustering_c.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** Inverted file with stored vectors. Here the inverted file - * pre-selects the vectors to be searched, but they are not otherwise - * encoded, the code array just contains the raw float entries. - */ -FAISS_DECLARE_CLASS(IndexIVFFlat) -FAISS_DECLARE_DESTRUCTOR(IndexIVFFlat) -FAISS_DECLARE_INDEX_DOWNCAST(IndexIVFFlat) - -int faiss_IndexIVFFlat_new(FaissIndexIVFFlat** p_index); - -int faiss_IndexIVFFlat_new_with(FaissIndexIVFFlat** p_index, - FaissIndex* quantizer, size_t d, size_t nlist); - -int faiss_IndexIVFFlat_new_with_metric( - FaissIndexIVFFlat** p_index, FaissIndex* quantizer, size_t d, size_t nlist, - FaissMetricType metric); - -int faiss_IndexIVFFlat_add_core(FaissIndexIVFFlat* index, idx_t n, - const float * x, const idx_t *xids, const int64_t *precomputed_idx); - -/** Update a subset of vectors. - * - * The index must have a direct_map - * - * @param nv nb of vectors to update - * @param idx vector indices to update, size nv - * @param v vectors of new values, size nv*d - */ -int faiss_IndexIVFFlat_update_vectors(FaissIndexIVFFlat* index, int nv, - idx_t *idx, const float *v); - -#ifdef __cplusplus -} -#endif - - -#endif diff --git a/core/src/index/thirdparty/faiss/c_api/IndexIVF_c.cpp b/core/src/index/thirdparty/faiss/c_api/IndexIVF_c.cpp deleted file mode 100644 index 4f7983723b..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/IndexIVF_c.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#include "Index_c.h" -#include "Clustering_c.h" -#include "IndexIVF_c.h" -#include "IndexIVF.h" -#include "macros_impl.h" - -using faiss::IndexIVF; -using faiss::IndexIVFStats; - -DEFINE_DESTRUCTOR(IndexIVF) -DEFINE_INDEX_DOWNCAST(IndexIVF) - -/// number of possible key values -DEFINE_GETTER(IndexIVF, size_t, nlist) -/// number of probes at query time -DEFINE_GETTER(IndexIVF, size_t, nprobe) -/// quantizer that maps vectors to inverted lists -DEFINE_GETTER_PERMISSIVE(IndexIVF, FaissIndex*, quantizer) - -/** - * = 0: use the quantizer as index in a kmeans training - * = 1: just pass on the training set to the train() of the quantizer - * = 2: kmeans training on a flat index + add the centroids to the quantizer - */ -DEFINE_GETTER(IndexIVF, char, quantizer_trains_alone) - -/// whether object owns the quantizer -DEFINE_GETTER(IndexIVF, int, own_fields) - -using faiss::IndexIVF; - -int faiss_IndexIVF_merge_from( - FaissIndexIVF* index, FaissIndexIVF* other, idx_t add_id) { - try { - reinterpret_cast(index)->merge_from( - *reinterpret_cast(other), add_id); - } CATCH_AND_HANDLE -} - -int faiss_IndexIVF_copy_subset_to( - const FaissIndexIVF* index, FaissIndexIVF* other, int subset_type, idx_t a1, - idx_t a2) { - try { - reinterpret_cast(index)->copy_subset_to( - *reinterpret_cast(other), subset_type, a1, a2); - } CATCH_AND_HANDLE -} - -int faiss_IndexIVF_search_preassigned (const FaissIndexIVF* index, - idx_t n, const float *x, idx_t k, const idx_t *assign, - const float *centroid_dis, float *distances, idx_t *labels, - int store_pairs) { - try { - reinterpret_cast(index)->search_preassigned( - n, x, k, assign, centroid_dis, distances, labels, store_pairs); - } CATCH_AND_HANDLE -} - -size_t faiss_IndexIVF_get_list_size(const FaissIndexIVF* index, size_t list_no) { - return reinterpret_cast(index)->get_list_size(list_no); -} - -int faiss_IndexIVF_make_direct_map(FaissIndexIVF* index, - int new_maintain_direct_map) { - try { - reinterpret_cast(index)->make_direct_map( - static_cast(new_maintain_direct_map)); - } CATCH_AND_HANDLE -} - -double faiss_IndexIVF_imbalance_factor (const FaissIndexIVF* index) { - return reinterpret_cast(index)->invlists->imbalance_factor(); -} - -/// display some stats about the inverted lists -void faiss_IndexIVF_print_stats (const FaissIndexIVF* index) { - reinterpret_cast(index)->invlists->print_stats(); -} - -/// get inverted lists ids -void faiss_IndexIVF_invlists_get_ids (const FaissIndexIVF* index, size_t list_no, idx_t* invlist) { - const idx_t* list = reinterpret_cast(index)->invlists->get_ids(list_no); - size_t list_size = reinterpret_cast(index)->get_list_size(list_no); - memcpy(invlist, list, list_size*sizeof(idx_t)); -} - -void faiss_IndexIVFStats_reset(FaissIndexIVFStats* stats) { - reinterpret_cast(stats)->reset(); -} diff --git a/core/src/index/thirdparty/faiss/c_api/IndexIVF_c.h b/core/src/index/thirdparty/faiss/c_api/IndexIVF_c.h deleted file mode 100644 index 5aa907c8c2..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/IndexIVF_c.h +++ /dev/null @@ -1,142 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c -*- - -#ifndef FAISS_INDEX_IVF_C_H -#define FAISS_INDEX_IVF_C_H - -#include "faiss_c.h" -#include "Index_c.h" -#include "Clustering_c.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** Index based on a inverted file (IVF) - * - * In the inverted file, the quantizer (an Index instance) provides a - * quantization index for each vector to be added. The quantization - * index maps to a list (aka inverted list or posting list), where the - * id of the vector is then stored. - * - * At search time, the vector to be searched is also quantized, and - * only the list corresponding to the quantization index is - * searched. This speeds up the search by making it - * non-exhaustive. This can be relaxed using multi-probe search: a few - * (nprobe) quantization indices are selected and several inverted - * lists are visited. - * - * Sub-classes implement a post-filtering of the index that refines - * the distance estimation from the query to databse vectors. - */ -FAISS_DECLARE_CLASS_INHERITED(IndexIVF, Index) -FAISS_DECLARE_DESTRUCTOR(IndexIVF) -FAISS_DECLARE_INDEX_DOWNCAST(IndexIVF) - -/// number of possible key values -FAISS_DECLARE_GETTER(IndexIVF, size_t, nlist) -/// number of probes at query time -FAISS_DECLARE_GETTER(IndexIVF, size_t, nprobe) -/// quantizer that maps vectors to inverted lists -FAISS_DECLARE_GETTER(IndexIVF, FaissIndex*, quantizer) -/** - * = 0: use the quantizer as index in a kmeans training - * = 1: just pass on the training set to the train() of the quantizer - * = 2: kmeans training on a flat index + add the centroids to the quantizer - */ -FAISS_DECLARE_GETTER(IndexIVF, char, quantizer_trains_alone) - -/// whether object owns the quantizer -FAISS_DECLARE_GETTER(IndexIVF, int, own_fields) - -/** moves the entries from another dataset to self. On output, - * other is empty. add_id is added to all moved ids (for - * sequential ids, this would be this->ntotal */ -int faiss_IndexIVF_merge_from( - FaissIndexIVF* index, FaissIndexIVF* other, idx_t add_id); - -/** copy a subset of the entries index to the other index - * - * if subset_type == 0: copies ids in [a1, a2) - * if subset_type == 1: copies ids if id % a1 == a2 - * if subset_type == 2: copies inverted lists such that a1 - * elements are left before and a2 elements are after - */ -int faiss_IndexIVF_copy_subset_to( - const FaissIndexIVF* index, FaissIndexIVF* other, int subset_type, idx_t a1, - idx_t a2); - -/** search a set of vectors, that are pre-quantized by the IVF - * quantizer. Fill in the corresponding heaps with the query - * results. search() calls this. - * - * @param n nb of vectors to query - * @param x query vectors, size nx * d - * @param assign coarse quantization indices, size nx * nprobe - * @param centroid_dis - * distances to coarse centroids, size nx * nprobe - * @param distance - * output distances, size n * k - * @param labels output labels, size n * k - * @param store_pairs store inv list index + inv list offset - * instead in upper/lower 32 bit of result, - * instead of ids (used for reranking). - */ -int faiss_IndexIVF_search_preassigned (const FaissIndexIVF* index, - idx_t n, const float *x, idx_t k, const idx_t *assign, - const float *centroid_dis, float *distances, idx_t *labels, - int store_pairs); - -size_t faiss_IndexIVF_get_list_size(const FaissIndexIVF* index, - size_t list_no); - -/** intialize a direct map - * - * @param new_maintain_direct_map if true, create a direct map, - * else clear it - */ -int faiss_IndexIVF_make_direct_map(FaissIndexIVF* index, - int new_maintain_direct_map); - -/** Check the inverted lists' imbalance factor. - * - * 1= perfectly balanced, >1: imbalanced - */ -double faiss_IndexIVF_imbalance_factor (const FaissIndexIVF* index); - -/// display some stats about the inverted lists of the index -void faiss_IndexIVF_print_stats (const FaissIndexIVF* index); - -/// Get the IDs in an inverted list. IDs are written to `invlist`, which must be large enough -//// to accommodate the full list. -/// -/// @param list_no the list ID -/// @param invlist output pointer to a slice of memory, at least as long as the list's size -/// @see faiss_IndexIVF_get_list_size(size_t) -void faiss_IndexIVF_invlists_get_ids (const FaissIndexIVF* index, size_t list_no, idx_t* invlist); - -typedef struct FaissIndexIVFStats { - size_t nq; // nb of queries run - size_t nlist; // nb of inverted lists scanned - size_t ndis; // nb of distancs computed -} FaissIndexIVFStats; - -void faiss_IndexIVFStats_reset(FaissIndexIVFStats* stats); - -inline void faiss_IndexIVFStats_init(FaissIndexIVFStats* stats) { - faiss_IndexIVFStats_reset(stats); -} - -#ifdef __cplusplus -} -#endif - - -#endif diff --git a/core/src/index/thirdparty/faiss/c_api/IndexLSH_c.cpp b/core/src/index/thirdparty/faiss/c_api/IndexLSH_c.cpp deleted file mode 100644 index 39a348f807..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/IndexLSH_c.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#include "IndexLSH_c.h" -#include "IndexLSH.h" -#include "macros_impl.h" - -using faiss::Index; -using faiss::IndexLSH; - -DEFINE_DESTRUCTOR(IndexLSH) -DEFINE_INDEX_DOWNCAST(IndexLSH) - -DEFINE_GETTER(IndexLSH, int, nbits) -DEFINE_GETTER(IndexLSH, int, bytes_per_vec) -DEFINE_GETTER_PERMISSIVE(IndexLSH, int, rotate_data) -DEFINE_GETTER_PERMISSIVE(IndexLSH, int, train_thresholds) - -int faiss_IndexLSH_new(FaissIndexLSH** p_index, idx_t d, int nbits) { - try { - *p_index = reinterpret_cast(new IndexLSH(d, nbits)); - } CATCH_AND_HANDLE -} - -int faiss_IndexLSH_new_with_options(FaissIndexLSH** p_index, idx_t d, int nbits, int rotate_data, int train_thresholds) { - try { - *p_index = reinterpret_cast( - new IndexLSH(d, nbits, static_cast(rotate_data), static_cast(train_thresholds))); - } CATCH_AND_HANDLE -} diff --git a/core/src/index/thirdparty/faiss/c_api/IndexLSH_c.h b/core/src/index/thirdparty/faiss/c_api/IndexLSH_c.h deleted file mode 100644 index 4a3dab418d..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/IndexLSH_c.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#ifndef INDEX_LSH_C_H -#define INDEX_LSH_C_H - -#include "faiss_c.h" -#include "Index_c.h" -#include "Clustering_c.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** The sign of each vector component is put in a binary signature */ -FAISS_DECLARE_CLASS_INHERITED(IndexLSH, Index) -FAISS_DECLARE_DESTRUCTOR(IndexLSH) -FAISS_DECLARE_INDEX_DOWNCAST(IndexLSH) - -FAISS_DECLARE_GETTER(IndexLSH, int, nbits) -FAISS_DECLARE_GETTER(IndexLSH, int, bytes_per_vec) -FAISS_DECLARE_GETTER(IndexLSH, int, rotate_data) -FAISS_DECLARE_GETTER(IndexLSH, int, train_thresholds) - -int faiss_IndexLSH_new(FaissIndexLSH** p_index, idx_t d, int nbits); - -int faiss_IndexLSH_new_with_options(FaissIndexLSH** p_index, idx_t d, int nbits, int rotate_data, int train_thresholds); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/core/src/index/thirdparty/faiss/c_api/IndexPreTransform_c.cpp b/core/src/index/thirdparty/faiss/c_api/IndexPreTransform_c.cpp deleted file mode 100644 index 7d99602edd..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/IndexPreTransform_c.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#include "IndexPreTransform_c.h" -#include "IndexPreTransform.h" -#include "macros_impl.h" - -using faiss::Index; -using faiss::IndexPreTransform; - -DEFINE_DESTRUCTOR(IndexPreTransform) -DEFINE_INDEX_DOWNCAST(IndexPreTransform) - -DEFINE_GETTER_PERMISSIVE(IndexPreTransform, FaissIndex*, index) diff --git a/core/src/index/thirdparty/faiss/c_api/IndexPreTransform_c.h b/core/src/index/thirdparty/faiss/c_api/IndexPreTransform_c.h deleted file mode 100644 index c6d34b23c7..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/IndexPreTransform_c.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c -*- - -#ifndef FAISS_INDEX_PRETRANSFORM_C_H -#define FAISS_INDEX_PRETRANSFORM_C_H - -#include "faiss_c.h" -#include "Index_c.h" - -#ifdef __cplusplus -extern "C" { -#endif - -FAISS_DECLARE_CLASS(IndexPreTransform) -FAISS_DECLARE_DESTRUCTOR(IndexPreTransform) -FAISS_DECLARE_INDEX_DOWNCAST(IndexPreTransform) - -FAISS_DECLARE_GETTER(IndexPreTransform, FaissIndex*, index) - -#ifdef __cplusplus -} -#endif - - -#endif diff --git a/core/src/index/thirdparty/faiss/c_api/IndexShards_c.cpp b/core/src/index/thirdparty/faiss/c_api/IndexShards_c.cpp deleted file mode 100644 index e66aeb7ed0..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/IndexShards_c.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "IndexShards_c.h" -#include "IndexShards.h" -#include "macros_impl.h" - -using faiss::Index; -using faiss::IndexShards; - -DEFINE_GETTER(IndexShards, int, own_fields) -DEFINE_SETTER(IndexShards, int, own_fields) - -DEFINE_GETTER(IndexShards, int, successive_ids) -DEFINE_SETTER(IndexShards, int, successive_ids) - -int faiss_IndexShards_new(FaissIndexShards** p_index, idx_t d) { - try { - auto out = new IndexShards(d); - *p_index = reinterpret_cast(out); - } CATCH_AND_HANDLE -} - -int faiss_IndexShards_new_with_options(FaissIndexShards** p_index, idx_t d, int threaded, int successive_ids) { - try { - auto out = new IndexShards(d, static_cast(threaded), static_cast(successive_ids)); - *p_index = reinterpret_cast(out); - } CATCH_AND_HANDLE -} - -int faiss_IndexShards_add_shard(FaissIndexShards* index, FaissIndex* shard) { - try { - reinterpret_cast(index)->add_shard( - reinterpret_cast(shard)); - } CATCH_AND_HANDLE -} - -int faiss_IndexShards_sync_with_shard_indexes(FaissIndexShards* index) { - try { - reinterpret_cast(index)->sync_with_shard_indexes(); - } CATCH_AND_HANDLE -} - -FaissIndex* faiss_IndexShards_at(FaissIndexShards* index, int i) { - auto shard = reinterpret_cast(index)->at(i); - return reinterpret_cast(shard); -} diff --git a/core/src/index/thirdparty/faiss/c_api/IndexShards_c.h b/core/src/index/thirdparty/faiss/c_api/IndexShards_c.h deleted file mode 100644 index 7e6a30b2a9..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/IndexShards_c.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#ifndef INDEXSHARDS_C_H -#define INDEXSHARDS_C_H - -#include "faiss_c.h" -#include "Index_c.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** Index that concatenates the results from several sub-indexes - */ -FAISS_DECLARE_CLASS_INHERITED(IndexShards, Index) - -FAISS_DECLARE_GETTER_SETTER(IndexShards, int, own_fields) -FAISS_DECLARE_GETTER_SETTER(IndexShards, int, successive_ids) - -int faiss_IndexShards_new(FaissIndexShards** p_index, idx_t d); - -int faiss_IndexShards_new_with_options(FaissIndexShards** p_index, idx_t d, int threaded, int successive_ids); - -int faiss_IndexShards_add_shard(FaissIndexShards* index, FaissIndex* shard); - -/// update metric_type and ntotal -int faiss_IndexShards_sync_with_shard_indexes(FaissIndexShards* index); - -FaissIndex* faiss_IndexShards_at(FaissIndexShards* index, int i); - -#ifdef __cplusplus -} -#endif -#endif \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/c_api/Index_c.cpp b/core/src/index/thirdparty/faiss/c_api/Index_c.cpp deleted file mode 100644 index 38263f4333..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/Index_c.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#include "Index_c.h" -#include "Index.h" -#include "macros_impl.h" - -extern "C" { - -DEFINE_DESTRUCTOR(Index) - -DEFINE_GETTER(Index, int, d) - -DEFINE_GETTER(Index, int, is_trained) - -DEFINE_GETTER(Index, idx_t, ntotal) - -DEFINE_GETTER(Index, FaissMetricType, metric_type) - -int faiss_Index_train(FaissIndex* index, idx_t n, const float* x) { - try { - reinterpret_cast(index)->train(n, x); - } CATCH_AND_HANDLE -} - -int faiss_Index_add(FaissIndex* index, idx_t n, const float* x) { - try { - reinterpret_cast(index)->add(n, x); - } CATCH_AND_HANDLE -} - -int faiss_Index_add_with_ids(FaissIndex* index, idx_t n, const float* x, const idx_t* xids) { - try { - reinterpret_cast(index)->add_with_ids(n, x, xids); - } CATCH_AND_HANDLE -} - -int faiss_Index_search(const FaissIndex* index, idx_t n, const float* x, idx_t k, - float* distances, idx_t* labels) { - try { - reinterpret_cast(index)->search(n, x, k, distances, labels); - } CATCH_AND_HANDLE -} - -int faiss_Index_range_search(const FaissIndex* index, idx_t n, const float* x, float radius, - FaissRangeSearchResult* result) { - try { - reinterpret_cast(index)->range_search( - n, x, radius, reinterpret_cast(result)); - } CATCH_AND_HANDLE -} - -int faiss_Index_assign(FaissIndex* index, idx_t n, const float * x, idx_t * labels) { - try { - reinterpret_cast(index)->assign(n, x, labels); - } CATCH_AND_HANDLE -} - -int faiss_Index_reset(FaissIndex* index) { - try { - reinterpret_cast(index)->reset(); - } CATCH_AND_HANDLE -} - -int faiss_Index_remove_ids(FaissIndex* index, const FaissIDSelector* sel, size_t* n_removed) { - try { - size_t n {reinterpret_cast(index)->remove_ids( - *reinterpret_cast(sel))}; - if (n_removed) { - *n_removed = n; - } - } CATCH_AND_HANDLE -} - -int faiss_Index_reconstruct(const FaissIndex* index, idx_t key, float* recons) { - try { - reinterpret_cast(index)->reconstruct(key, recons); - } CATCH_AND_HANDLE -} - -int faiss_Index_reconstruct_n (const FaissIndex* index, idx_t i0, idx_t ni, float* recons) { - try { - reinterpret_cast(index)->reconstruct_n(i0, ni, recons); - } CATCH_AND_HANDLE -} - -int faiss_Index_compute_residual(const FaissIndex* index, const float* x, float* residual, idx_t key) { - try { - reinterpret_cast(index)->compute_residual(x, residual, key); - } CATCH_AND_HANDLE -} - -int faiss_Index_compute_residual_n(const FaissIndex* index, idx_t n, const float* x, float* residuals, const idx_t* keys) { - try { - reinterpret_cast(index)->compute_residual_n(n, x, residuals, keys); - } CATCH_AND_HANDLE -} -} \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/c_api/Index_c.h b/core/src/index/thirdparty/faiss/c_api/Index_c.h deleted file mode 100644 index 4b6a30c7cd..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/Index_c.h +++ /dev/null @@ -1,183 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved -// -*- c -*- - -#ifndef FAISS_INDEX_C_H -#define FAISS_INDEX_C_H - -#include -#include "faiss_c.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// forward declaration required here -FAISS_DECLARE_CLASS(RangeSearchResult) - -//typedef struct FaissRangeSearchResult_H FaissRangeSearchResult; -typedef struct FaissIDSelector_H FaissIDSelector; - -/// Some algorithms support both an inner product version and a L2 search version. -typedef enum FaissMetricType { - METRIC_INNER_PRODUCT = 0, ///< maximum inner product search - METRIC_L2 = 1, ///< squared L2 search - METRIC_L1, ///< L1 (aka cityblock) - METRIC_Linf, ///< infinity distance - METRIC_Lp, ///< L_p distance, p is given by metric_arg - - /// some additional metrics defined in scipy.spatial.distance - METRIC_Canberra = 20, - METRIC_BrayCurtis, - METRIC_JensenShannon, -} FaissMetricType; - -/// Opaque type for referencing to an index object -FAISS_DECLARE_CLASS(Index) -FAISS_DECLARE_DESTRUCTOR(Index) - -/// Getter for d -FAISS_DECLARE_GETTER(Index, int, d) - -/// Getter for is_trained -FAISS_DECLARE_GETTER(Index, int, is_trained) - -/// Getter for ntotal -FAISS_DECLARE_GETTER(Index, idx_t, ntotal) - -/// Getter for metric_type -FAISS_DECLARE_GETTER(Index, FaissMetricType, metric_type) - -/** Perform training on a representative set of vectors - * - * @param index opaque pointer to index object - * @param n nb of training vectors - * @param x training vecors, size n * d - */ -int faiss_Index_train(FaissIndex* index, idx_t n, const float* x); - -/** Add n vectors of dimension d to the index. - * - * Vectors are implicitly assigned labels ntotal .. ntotal + n - 1 - * This function slices the input vectors in chuncks smaller than - * blocksize_add and calls add_core. - * @param index opaque pointer to index object - * @param x input matrix, size n * d - */ -int faiss_Index_add(FaissIndex* index, idx_t n, const float* x); - -/** Same as add, but stores xids instead of sequential ids. - * - * The default implementation fails with an assertion, as it is - * not supported by all indexes. - * - * @param index opaque pointer to index object - * @param xids if non-null, ids to store for the vectors (size n) - */ -int faiss_Index_add_with_ids(FaissIndex* index, idx_t n, const float* x, const idx_t* xids); - -/** query n vectors of dimension d to the index. - * - * return at most k vectors. If there are not enough results for a - * query, the result array is padded with -1s. - * - * @param index opaque pointer to index object - * @param x input vectors to search, size n * d - * @param labels output labels of the NNs, size n*k - * @param distances output pairwise distances, size n*k - */ -int faiss_Index_search(const FaissIndex* index, idx_t n, const float* x, idx_t k, - float* distances, idx_t* labels); - -/** query n vectors of dimension d to the index. - * - * return all vectors with distance < radius. Note that many - * indexes do not implement the range_search (only the k-NN search - * is mandatory). - * - * @param index opaque pointer to index object - * @param x input vectors to search, size n * d - * @param radius search radius - * @param result result table - */ -int faiss_Index_range_search(const FaissIndex* index, idx_t n, const float* x, - float radius, FaissRangeSearchResult* result); - -/** return the indexes of the k vectors closest to the query x. - * - * This function is identical as search but only return labels of neighbors. - * @param index opaque pointer to index object - * @param x input vectors to search, size n * d - * @param labels output labels of the NNs, size n - */ -int faiss_Index_assign(FaissIndex* index, idx_t n, const float * x, idx_t * labels); - -/** removes all elements from the database. - * @param index opaque pointer to index object - */ -int faiss_Index_reset(FaissIndex* index); - -/** removes IDs from the index. Not supported by all indexes - * @param index opaque pointer to index object - * @param nremove output for the number of IDs removed - */ -int faiss_Index_remove_ids(FaissIndex* index, const FaissIDSelector* sel, size_t* n_removed); - -/** Reconstruct a stored vector (or an approximation if lossy coding) - * - * this function may not be defined for some indexes - * @param index opaque pointer to index object - * @param key id of the vector to reconstruct - * @param recons reconstucted vector (size d) - */ -int faiss_Index_reconstruct(const FaissIndex* index, idx_t key, float* recons); - -/** Reconstruct vectors i0 to i0 + ni - 1 - * - * this function may not be defined for some indexes - * @param index opaque pointer to index object - * @param recons reconstucted vector (size ni * d) - */ -int faiss_Index_reconstruct_n (const FaissIndex* index, idx_t i0, idx_t ni, float* recons); - -/** Computes a residual vector after indexing encoding. - * - * The residual vector is the difference between a vector and the - * reconstruction that can be decoded from its representation in - * the index. The residual can be used for multiple-stage indexing - * methods, like IndexIVF's methods. - * - * @param index opaque pointer to index object - * @param x input vector, size d - * @param residual output residual vector, size d - * @param key encoded index, as returned by search and assign - */ -int faiss_Index_compute_residual(const FaissIndex* index, const float* x, float* residual, idx_t key); - -/** Computes a residual vector after indexing encoding. - * - * The residual vector is the difference between a vector and the - * reconstruction that can be decoded from its representation in - * the index. The residual can be used for multiple-stage indexing - * methods, like IndexIVF's methods. - * - * @param index opaque pointer to index object - * @param n number of vectors - * @param x input vector, size (n x d) - * @param residuals output residual vectors, size (n x d) - * @param keys encoded index, as returned by search and assign - */ -int faiss_Index_compute_residual_n(const FaissIndex* index, idx_t n, const float* x, float* residuals, const idx_t* keys); - - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/c_api/Makefile b/core/src/index/thirdparty/faiss/c_api/Makefile deleted file mode 100644 index c47c465f00..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/Makefile +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -.SUFFIXES: .cpp .o - -# C API - -include ../makefile.inc -DEBUGFLAG=-DNDEBUG # no debugging - -LIBNAME=libfaiss -CLIBNAME=libfaiss_c -LIBCOBJ=error_impl.o Index_c.o IndexFlat_c.o Clustering_c.o AutoTune_c.o \ - impl/AuxIndexStructures_c.o IndexIVF_c.o IndexIVFFlat_c.o IndexLSH_c.o \ - index_io_c.o MetaIndexes_c.o IndexShards_c.o index_factory_c.o \ - clone_index_c.o IndexPreTransform_c.o -CFLAGS=-fPIC -m64 -Wno-sign-compare -g -O3 -Wall -Wextra - -# Build static and shared object files by default -all: $(CLIBNAME).a $(CLIBNAME).$(SHAREDEXT) - -# Build static object file containing the wrapper implementation only. -# Consumers are required to link with libfaiss.a and libstdc++. -$(CLIBNAME).a: $(LIBCOBJ) - ar r $@ $^ - -# Build dynamic library (independent object) -$(CLIBNAME).$(SHAREDEXT): $(LIBCOBJ) ../$(LIBNAME).a - $(CXX) $(LDFLAGS) $(SHAREDFLAGS) -o $@ \ - -Wl,--whole-archive $^ -Wl,--no-whole-archive $(LIBS) -static-libstdc++ - -bin/example_c: example_c.c $(CLIBNAME).$(SHAREDEXT) - $(CC) $(CFLAGS) -std=c99 -I. -I.. -L. -o $@ example_c.c \ - $(LDFLAGS) -lm -lfaiss_c - -clean: - rm -f $(CLIBNAME).a $(CLIBNAME).$(SHAREDEXT)* *.o bin/example_c - -%.o: %.cpp - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(CPUFLAGS) -c $< -o $@ - -# Dependencies - -error_impl.o: CXXFLAGS += -I.. -I ../impl $(DEBUGFLAG) -error_impl.o: error_impl.cpp error_c.h error_impl.h macros_impl.h - -index_io_c.o: CXXFLAGS += -I.. -I ../impl $(DEBUGFLAG) -index_io_c.o: index_io_c.cpp error_impl.cpp ../index_io.h macros_impl.h - -index_factory_c.o: CXXFLAGS += -I.. -I ../impl $(DEBUGFLAG) -index_factory_c.o: index_factory_c.cpp error_impl.cpp ../index_io.h macros_impl.h - -clone_index_c.o: CXXFLAGS += -I.. -I ../impl $(DEBUGFLAG) -clone_index_c.o: index_factory_c.cpp error_impl.cpp ../index_io.h macros_impl.h - -Index_c.o: CXXFLAGS += -I.. -I ../impl $(DEBUGFLAG) -Index_c.o: Index_c.cpp Index_c.h ../Index.h macros_impl.h - -IndexFlat_c.o: CXXFLAGS += -I.. -I ../impl $(DEBUGFLAG) -IndexFlat_c.o: IndexFlat_c.cpp IndexFlat_c.h ../IndexFlat.h macros_impl.h - -IndexIVF_c.o: CXXFLAGS += -I.. -I ../impl $(DEBUGFLAG) -IndexIVF_c.o: IndexIVF_c.cpp IndexIVF_c.h ../IndexIVF.h macros_impl.h - -IndexIVFFlat_c.o: CXXFLAGS += -I.. -I ../impl $(DEBUGFLAG) -IndexIVFFlat_c.o: IndexIVFFlat_c.cpp IndexIVFFlat_c.h ../IndexIVFFlat.h macros_impl.h - -IndexLSH_c.o: CXXFLAGS += -I.. -I ../impl $(DEBUGFLAG) -IndexLSH_c.o: IndexLSH_c.cpp IndexLSH_c.h ../IndexLSH.h macros_impl.h - -IndexShards_c.o: CXXFLAGS += -I.. -I ../impl $(DEBUGFLAG) -IndexShards_c.o: IndexShards_c.cpp IndexShards_c.h ../Index.h ../IndexShards.h macros_impl.h - -Clustering_c.o: CXXFLAGS += -I.. -I ../impl $(DEBUGFLAG) -Clustering_c.o: Clustering_c.cpp Clustering_c.h ../Clustering.h macros_impl.h - -AutoTune_c.o: CXXFLAGS += -I.. -I ../impl $(DEBUGFLAG) -AutoTune_c.o: AutoTune_c.cpp AutoTune_c.h ../AutoTune.h macros_impl.h - -impl/AuxIndexStructures_c.o: CXXFLAGS += -I.. -I ../impl $(DEBUGFLAG) -impl/AuxIndexStructures_c.o: impl/AuxIndexStructures_c.cpp impl/AuxIndexStructures_c.h ../impl/AuxIndexStructures.h macros_impl.h - -MetaIndexes_c.o: CXXFLAGS += -I.. -I ../impl $(DEBUGFLAG) -MetaIndexes_c.o: MetaIndexes_c.cpp MetaIndexes_c.h ../MetaIndexes.h macros_impl.h - -IndexPreTransform_c.o: CXXFLAGS += -I.. -I ../impl $(DEBUGFLAG) -IndexPreTransform_c.o: IndexPreTransform_c.cpp IndexPreTransform_c.h ../IndexPreTransform.h macros_impl.h diff --git a/core/src/index/thirdparty/faiss/c_api/MetaIndexes_c.cpp b/core/src/index/thirdparty/faiss/c_api/MetaIndexes_c.cpp deleted file mode 100644 index 72abd9e793..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/MetaIndexes_c.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#include "MetaIndexes_c.h" -#include "MetaIndexes.h" -#include "macros_impl.h" - -using faiss::Index; -using faiss::IndexIDMap; -using faiss::IndexIDMap2; - -DEFINE_GETTER(IndexIDMap, int, own_fields) -DEFINE_SETTER(IndexIDMap, int, own_fields) - -int faiss_IndexIDMap_new(FaissIndexIDMap** p_index, FaissIndex* index) { - try { - auto out = new IndexIDMap(reinterpret_cast(index)); - *p_index = reinterpret_cast(out); - } CATCH_AND_HANDLE -} - -void faiss_IndexIDMap_id_map(FaissIndexIDMap* index, idx_t** p_id_map, size_t* p_size) { - auto idx = reinterpret_cast(index); - if (p_id_map) - *p_id_map = idx->id_map.data(); - if (p_size) - *p_size = idx->id_map.size(); -} - -int faiss_IndexIDMap2_new(FaissIndexIDMap2** p_index, FaissIndex* index) { - try { - auto out = new IndexIDMap2(reinterpret_cast(index)); - *p_index = reinterpret_cast(out); - } CATCH_AND_HANDLE -} - -int faiss_IndexIDMap2_construct_rev_map(FaissIndexIDMap2* index) { - try { - reinterpret_cast(index)->construct_rev_map(); - } CATCH_AND_HANDLE -} - diff --git a/core/src/index/thirdparty/faiss/c_api/MetaIndexes_c.h b/core/src/index/thirdparty/faiss/c_api/MetaIndexes_c.h deleted file mode 100644 index 940394f92f..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/MetaIndexes_c.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#ifndef METAINDEXES_C_H -#define METAINDEXES_C_H - -#include "faiss_c.h" -#include "Index_c.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** Index that translates search results to ids */ -FAISS_DECLARE_CLASS_INHERITED(IndexIDMap, Index) - -FAISS_DECLARE_GETTER_SETTER(IndexIDMap, int, own_fields) - -int faiss_IndexIDMap_new(FaissIndexIDMap** p_index, FaissIndex* index); - -/** get a pointer to the index map's internal ID vector (the `id_map` field). The - * outputs of this function become invalid after any operation that can modify the index. - * - * @param index opaque pointer to index object - * @param p_id_map output, the pointer to the beginning of `id_map`. - * @param p_size output, the current length of `id_map`. - */ -void faiss_IndexIDMap_id_map(FaissIndexIDMap* index, idx_t** p_id_map, size_t* p_size); - -/** same as IndexIDMap but also provides an efficient reconstruction - implementation via a 2-way index */ -FAISS_DECLARE_CLASS_INHERITED(IndexIDMap2, IndexIDMap) - -int faiss_IndexIDMap2_new(FaissIndexIDMap2** p_index, FaissIndex* index); - -/// make the rev_map from scratch -int faiss_IndexIDMap2_construct_rev_map(FaissIndexIDMap2* index); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/core/src/index/thirdparty/faiss/c_api/clone_index_c.cpp b/core/src/index/thirdparty/faiss/c_api/clone_index_c.cpp deleted file mode 100644 index 999b139a7c..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/clone_index_c.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved -// -*- c++ -*- -// I/O code for indexes - -#include "clone_index_c.h" -#include "clone_index.h" -#include "macros_impl.h" - -using faiss::Index; - -int faiss_clone_index (const FaissIndex *idx, FaissIndex **p_out) { - try { - auto out = faiss::clone_index(reinterpret_cast(idx)); - *p_out = reinterpret_cast(out); - } CATCH_AND_HANDLE -} \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/c_api/clone_index_c.h b/core/src/index/thirdparty/faiss/c_api/clone_index_c.h deleted file mode 100644 index 3cf7e1a658..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/clone_index_c.h +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved -// -*- c++ -*- -// I/O code for indexes - - -#ifndef FAISS_CLONE_INDEX_C_H -#define FAISS_CLONE_INDEX_C_H - -#include -#include "faiss_c.h" -#include "Index_c.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* cloning functions */ - -/** Clone an index. This is equivalent to `faiss::clone_index` */ -int faiss_clone_index (const FaissIndex *, FaissIndex ** p_out); - -#ifdef __cplusplus -} -#endif -#endif \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/c_api/error_c.h b/core/src/index/thirdparty/faiss/c_api/error_c.h deleted file mode 100644 index 5aa5664feb..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/error_c.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c -*- - -#ifndef FAISS_ERROR_C_H -#define FAISS_ERROR_C_H - -#ifdef __cplusplus -extern "C" { -#endif - -/// An error code which depends on the exception thrown from the previous -/// operation. See `faiss_get_last_error` to retrieve the error message. -typedef enum FaissErrorCode { - /// No error - OK = 0, - /// Any exception other than Faiss or standard C++ library exceptions - UNKNOWN_EXCEPT = -1, - /// Faiss library exception - FAISS_EXCEPT = -2, - /// Standard C++ library exception - STD_EXCEPT = -4 -} FaissErrorCode; - -/** - * Get the error message of the last failed operation performed by Faiss. - * The given pointer is only invalid until another Faiss function is - * called. - */ -const char* faiss_get_last_error(); - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/c_api/error_impl.cpp b/core/src/index/thirdparty/faiss/c_api/error_impl.cpp deleted file mode 100644 index 25793eb0e8..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/error_impl.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#include "error_c.h" -#include "error_impl.h" -#include "FaissException.h" -#include - -thread_local std::exception_ptr faiss_last_exception; - -const char* faiss_get_last_error() { - if (faiss_last_exception) { - try { - std::rethrow_exception(faiss_last_exception); - } catch (std::exception& e) { - return e.what(); - } - } - return nullptr; -} diff --git a/core/src/index/thirdparty/faiss/c_api/error_impl.h b/core/src/index/thirdparty/faiss/c_api/error_impl.h deleted file mode 100644 index b44254ad94..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/error_impl.h +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#include - -/** global variable for holding the last exception thrown by - * calls to Faiss functions through the C API - */ -extern thread_local std::exception_ptr faiss_last_exception; diff --git a/core/src/index/thirdparty/faiss/c_api/example_c.c b/core/src/index/thirdparty/faiss/c_api/example_c.c deleted file mode 100644 index 2e9a78a1ad..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/example_c.c +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c -*- - -#include -#include -#include - -#include "error_c.h" -#include "index_io_c.h" -#include "Index_c.h" -#include "IndexFlat_c.h" -#include "AutoTune_c.h" -#include "clone_index_c.h" - -#define FAISS_TRY(C) \ - { \ - if (C) { \ - fprintf(stderr, "%s", faiss_get_last_error()); \ - exit(-1); \ - } \ - } - -double drand() { - return (double)rand() / (double)RAND_MAX; -} - -int main() { - time_t seed = time(NULL); - srand(seed); - printf("Generating some data...\n"); - int d = 128; // dimension - int nb = 100000; // database size - int nq = 10000; // nb of queries - float *xb = malloc(d * nb * sizeof(float)); - float *xq = malloc(d * nq * sizeof(float)); - - for(int i = 0; i < nb; i++) { - for(int j = 0; j < d; j++) xb[d * i + j] = drand(); - xb[d * i] += i / 1000.; - } - for(int i = 0; i < nq; i++) { - for(int j = 0; j < d; j++) xq[d * i + j] = drand(); - xq[d * i] += i / 1000.; - } - - printf("Building an index...\n"); - - FaissIndex* index = NULL; - FAISS_TRY(faiss_index_factory(&index, d, "Flat", METRIC_L2)); // use factory to create index - printf("is_trained = %s\n", faiss_Index_is_trained(index) ? "true" : "false"); - FAISS_TRY(faiss_Index_add(index, nb, xb)); // add vectors to the index - printf("ntotal = %ld\n", faiss_Index_ntotal(index)); - - printf("Searching...\n"); - int k = 5; - - { // sanity check: search 5 first vectors of xb - idx_t *I = malloc(k * 5 * sizeof(idx_t)); - float *D = malloc(k * 5 * sizeof(float)); - FAISS_TRY(faiss_Index_search(index, 5, xb, k, D, I)); - printf("I=\n"); - for(int i = 0; i < 5; i++) { - for(int j = 0; j < k; j++) printf("%5ld (d=%2.3f) ", I[i * k + j], D[i * k + j]); - printf("\n"); - } - free(I); - free(D); - } - { // search xq - idx_t *I = malloc(k * nq * sizeof(idx_t)); - float *D = malloc(k * nq * sizeof(float)); - FAISS_TRY(faiss_Index_search(index, 5, xb, k, D, I)); - printf("I=\n"); - for(int i = 0; i < 5; i++) { - for(int j = 0; j < k; j++) printf("%5ld (d=%2.3f) ", I[i * k + j], D[i * k + j]); - printf("\n"); - } - free(I); - free(D); - } - - printf("Saving index to disk...\n"); - FAISS_TRY(faiss_write_index_fname(index, "example.index")); - - printf("Freeing index...\n"); - faiss_Index_free(index); - printf("Done.\n"); - - return 0; -} diff --git a/core/src/index/thirdparty/faiss/c_api/faiss_c.h b/core/src/index/thirdparty/faiss/c_api/faiss_c.h deleted file mode 100644 index 2357f71327..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/faiss_c.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c -*- - -/// Macros and typedefs for C wrapper API declarations - -#ifndef FAISS_C_H -#define FAISS_C_H - -#include - -typedef int64_t faiss_idx_t; ///< all indices are this type -typedef faiss_idx_t idx_t; -typedef float faiss_component_t; ///< all vector components are this type -typedef float faiss_distance_t; ///< all distances between vectors are this type - -/// Declare an opaque type for a class type `clazz`. -#define FAISS_DECLARE_CLASS(clazz) \ - typedef struct Faiss ## clazz ## _H Faiss ## clazz; - -/// Declare an opaque type for a class type `clazz`, while -/// actually aliasing it to an existing parent class type `parent`. -#define FAISS_DECLARE_CLASS_INHERITED(clazz, parent) \ - typedef struct Faiss ## parent ## _H Faiss ## clazz; - -/// Declare a dynamic downcast operation from a base `FaissIndex*` pointer -/// type to a more specific index type. The function returns the same pointer -/// if the downcast is valid, and `NULL` otherwise. -#define FAISS_DECLARE_INDEX_DOWNCAST(clazz) \ - Faiss ## clazz * faiss_ ## clazz ## _cast (FaissIndex*); - -/// Declare a getter for the field `name` in class `clazz`, -/// of return type `ty` -#define FAISS_DECLARE_GETTER(clazz, ty, name) \ - ty faiss_ ## clazz ## _ ## name (const Faiss ## clazz *); - -/// Declare a setter for the field `name` in class `clazz`, -/// in which the user provides a value of type `ty` -#define FAISS_DECLARE_SETTER(clazz, ty, name) \ - void faiss_ ## clazz ## _set_ ## name (Faiss ## clazz *, ty); - -/// Declare a getter and setter for the field `name` in class `clazz`. -#define FAISS_DECLARE_GETTER_SETTER(clazz, ty, name) \ - FAISS_DECLARE_GETTER(clazz, ty, name) \ - FAISS_DECLARE_SETTER(clazz, ty, name) - -/// Declare a destructor function which frees an object of -/// type `clazz`. -#define FAISS_DECLARE_DESTRUCTOR(clazz) \ - void faiss_ ## clazz ## _free (Faiss ## clazz *obj); - -#endif diff --git a/core/src/index/thirdparty/faiss/c_api/gpu/GpuAutoTune_c.cpp b/core/src/index/thirdparty/faiss/c_api/gpu/GpuAutoTune_c.cpp deleted file mode 100644 index 7336d5d7d3..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/gpu/GpuAutoTune_c.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#include "GpuAutoTune_c.h" -#include "GpuClonerOptions_c.h" -#include "macros_impl.h" -#include "Index.h" -#include "gpu/GpuAutoTune.h" -#include "gpu/GpuClonerOptions.h" -#include - -using faiss::Index; -using faiss::gpu::GpuResources; -using faiss::gpu::GpuClonerOptions; -using faiss::gpu::GpuMultipleClonerOptions; - -int faiss_index_gpu_to_cpu(const FaissIndex* gpu_index, FaissIndex** p_out) { - try { - auto cpu_index = faiss::gpu::index_gpu_to_cpu( - reinterpret_cast(gpu_index) - ); - *p_out = reinterpret_cast(cpu_index); - } CATCH_AND_HANDLE -} - -/// converts any CPU index that can be converted to GPU -int faiss_index_cpu_to_gpu(FaissGpuResources* resources, int device, const FaissIndex *index, FaissGpuIndex** p_out) { - try { - auto res = reinterpret_cast(resources); - auto gpu_index = faiss::gpu::index_cpu_to_gpu( - res, device, reinterpret_cast(index) - ); - *p_out = reinterpret_cast(gpu_index); - } CATCH_AND_HANDLE -} - -int faiss_index_cpu_to_gpu_with_options( - FaissGpuResources* resources, int device, - const FaissIndex *index, const FaissGpuClonerOptions* options, - FaissGpuIndex** p_out) -{ - try { - auto res = reinterpret_cast(resources); - auto gpu_index = faiss::gpu::index_cpu_to_gpu( - res, device, reinterpret_cast(index), - reinterpret_cast(options)); - *p_out = reinterpret_cast(gpu_index); - } CATCH_AND_HANDLE -} - -int faiss_index_cpu_to_gpu_multiple( - FaissGpuResources* const* resources_vec, - const int* devices, size_t devices_size, - const FaissIndex* index, FaissGpuIndex** p_out) -{ - try { - std::vector res(devices_size); - for (auto i = 0u; i < devices_size; ++i) { - res[i] = reinterpret_cast(resources_vec[i]); - } - - std::vector dev(devices, devices + devices_size); - - auto gpu_index = faiss::gpu::index_cpu_to_gpu_multiple( - res, dev, reinterpret_cast(index)); - *p_out = reinterpret_cast(gpu_index); - } CATCH_AND_HANDLE -} - -int faiss_index_cpu_to_gpu_multiple_with_options( - FaissGpuResources** resources_vec, size_t resources_vec_size, - int* devices, size_t devices_size, - const FaissIndex* index, const FaissGpuMultipleClonerOptions* options, - FaissGpuIndex** p_out) -{ - try { - std::vector res(resources_vec_size); - for (auto i = 0u; i < resources_vec_size; ++i) { - res[i] = reinterpret_cast(resources_vec[i]); - } - - std::vector dev(devices, devices + devices_size); - - auto gpu_index = faiss::gpu::index_cpu_to_gpu_multiple( - res, dev, reinterpret_cast(index), - reinterpret_cast(options)); - *p_out = reinterpret_cast(gpu_index); - } CATCH_AND_HANDLE -} diff --git a/core/src/index/thirdparty/faiss/c_api/gpu/GpuAutoTune_c.h b/core/src/index/thirdparty/faiss/c_api/gpu/GpuAutoTune_c.h deleted file mode 100644 index 5dbd15c977..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/gpu/GpuAutoTune_c.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c -*- - -#ifndef FAISS_GPU_AUTO_TUNE_C_H -#define FAISS_GPU_AUTO_TUNE_C_H - -#include -#include "faiss_c.h" -#include "GpuClonerOptions_c.h" -#include "GpuResources_c.h" -#include "GpuIndex_c.h" -#include "Index_c.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/// converts any GPU index inside gpu_index to a CPU index -int faiss_index_gpu_to_cpu(const FaissIndex* gpu_index, FaissIndex** p_out); - -/// converts any CPU index that can be converted to GPU -int faiss_index_cpu_to_gpu( - FaissGpuResources* resources, int device, - const FaissIndex *index, FaissGpuIndex** p_out); - -/// converts any CPU index that can be converted to GPU -int faiss_index_cpu_to_gpu_with_options( - FaissGpuResources* resources, int device, - const FaissIndex *index, const FaissGpuClonerOptions* options, - FaissGpuIndex** p_out); - -/// converts any CPU index that can be converted to GPU -int faiss_index_cpu_to_gpu_multiple( - FaissGpuResources* const* resources_vec, const int* devices, size_t devices_size, - const FaissIndex* index, FaissGpuIndex** p_out); - -/// converts any CPU index that can be converted to GPU -int faiss_index_cpu_to_gpu_multiple_with_options( - FaissGpuResources* const* resources_vec, const int* devices, size_t devices_size, - const FaissIndex* index, const FaissGpuMultipleClonerOptions* options, - FaissGpuIndex** p_out); - -/// parameter space and setters for GPU indexes -FAISS_DECLARE_CLASS_INHERITED(GpuParameterSpace, ParameterSpace) - -#ifdef __cplusplus -} -#endif -#endif diff --git a/core/src/index/thirdparty/faiss/c_api/gpu/GpuClonerOptions_c.cpp b/core/src/index/thirdparty/faiss/c_api/gpu/GpuClonerOptions_c.cpp deleted file mode 100644 index c61fc5e34c..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/gpu/GpuClonerOptions_c.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#include "GpuClonerOptions_c.h" -#include "gpu/GpuClonerOptions.h" -#include "macros_impl.h" - -using faiss::gpu::IndicesOptions; -using faiss::gpu::GpuClonerOptions; -using faiss::gpu::GpuMultipleClonerOptions; - -int faiss_GpuClonerOptions_new(FaissGpuClonerOptions** p) { - try { - *p = reinterpret_cast(new GpuClonerOptions()); - } CATCH_AND_HANDLE -} - -int faiss_GpuMultipleClonerOptions_new(FaissGpuMultipleClonerOptions** p) { - try { - *p = reinterpret_cast(new GpuMultipleClonerOptions()); - } CATCH_AND_HANDLE -} - -DEFINE_DESTRUCTOR(GpuClonerOptions) -DEFINE_DESTRUCTOR(GpuMultipleClonerOptions) - -DEFINE_GETTER(GpuClonerOptions, FaissIndicesOptions, indicesOptions) -DEFINE_GETTER(GpuClonerOptions, int, useFloat16CoarseQuantizer) -DEFINE_GETTER(GpuClonerOptions, int, useFloat16) -DEFINE_GETTER(GpuClonerOptions, int, usePrecomputed) -DEFINE_GETTER(GpuClonerOptions, long, reserveVecs) -DEFINE_GETTER(GpuClonerOptions, int, storeTransposed) -DEFINE_GETTER(GpuClonerOptions, int, verbose) -DEFINE_GETTER(GpuMultipleClonerOptions, int, shard) -DEFINE_GETTER(GpuMultipleClonerOptions, int, shard_type) - -DEFINE_SETTER_STATIC(GpuClonerOptions, IndicesOptions, FaissIndicesOptions, indicesOptions) -DEFINE_SETTER_STATIC(GpuClonerOptions, bool, int, useFloat16CoarseQuantizer) -DEFINE_SETTER_STATIC(GpuClonerOptions, bool, int, useFloat16) -DEFINE_SETTER_STATIC(GpuClonerOptions, bool, int, usePrecomputed) -DEFINE_SETTER(GpuClonerOptions, long, reserveVecs) -DEFINE_SETTER_STATIC(GpuClonerOptions, bool, int, storeTransposed) -DEFINE_SETTER_STATIC(GpuClonerOptions, bool, int, verbose) -DEFINE_SETTER_STATIC(GpuMultipleClonerOptions, bool, int, shard) -DEFINE_SETTER(GpuMultipleClonerOptions, int, shard_type) diff --git a/core/src/index/thirdparty/faiss/c_api/gpu/GpuClonerOptions_c.h b/core/src/index/thirdparty/faiss/c_api/gpu/GpuClonerOptions_c.h deleted file mode 100644 index 94ff403e7a..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/gpu/GpuClonerOptions_c.h +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c -*- - -#ifndef FAISS_GPU_CLONER_OPTIONS_C_H -#define FAISS_GPU_CLONER_OPTIONS_C_H - -#include "faiss_c.h" -#include "GpuIndicesOptions_c.h" - -#ifdef __cplusplus -extern "C" { -#endif - -FAISS_DECLARE_CLASS(GpuClonerOptions) - -FAISS_DECLARE_DESTRUCTOR(GpuClonerOptions) - -/// Default constructor for GpuClonerOptions -int faiss_GpuClonerOptions_new(FaissGpuClonerOptions**); - -/// how should indices be stored on index types that support indices -/// (anything but GpuIndexFlat*)? -FAISS_DECLARE_GETTER_SETTER(GpuClonerOptions, FaissIndicesOptions, indicesOptions) - -/// (boolean) is the coarse quantizer in float16? -FAISS_DECLARE_GETTER_SETTER(GpuClonerOptions, int, useFloat16CoarseQuantizer) - -/// (boolean) for GpuIndexIVFFlat, is storage in float16? -/// for GpuIndexIVFPQ, are intermediate calculations in float16? -FAISS_DECLARE_GETTER_SETTER(GpuClonerOptions, int, useFloat16) - -/// (boolean) use precomputed tables? -FAISS_DECLARE_GETTER_SETTER(GpuClonerOptions, int, usePrecomputed) - -/// reserve vectors in the invfiles? -FAISS_DECLARE_GETTER_SETTER(GpuClonerOptions, long, reserveVecs) - -/// (boolean) For GpuIndexFlat, store data in transposed layout? -FAISS_DECLARE_GETTER_SETTER(GpuClonerOptions, int, storeTransposed) - -/// (boolean) Set verbose options on the index -FAISS_DECLARE_GETTER_SETTER(GpuClonerOptions, int, verbose) - -FAISS_DECLARE_CLASS_INHERITED(GpuMultipleClonerOptions, GpuClonerOptions) - -FAISS_DECLARE_DESTRUCTOR(GpuMultipleClonerOptions) - -/// Default constructor for GpuMultipleClonerOptions -int faiss_GpuMultipleClonerOptions_new(FaissGpuMultipleClonerOptions**); - -/// (boolean) Whether to shard the index across GPUs, versus replication -/// across GPUs -FAISS_DECLARE_GETTER_SETTER(GpuMultipleClonerOptions, int, shard) - -/// IndexIVF::copy_subset_to subset type -FAISS_DECLARE_GETTER_SETTER(GpuMultipleClonerOptions, int, shard_type) - -#ifdef __cplusplus -} -#endif -#endif \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/c_api/gpu/GpuIndex_c.cpp b/core/src/index/thirdparty/faiss/c_api/gpu/GpuIndex_c.cpp deleted file mode 100644 index bdef82766e..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/gpu/GpuIndex_c.cpp +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#include "gpu/GpuIndex.h" -#include "GpuIndex_c.h" -#include "macros_impl.h" - -using faiss::gpu::GpuIndexConfig; - -DEFINE_GETTER(GpuIndexConfig, int, device) diff --git a/core/src/index/thirdparty/faiss/c_api/gpu/GpuIndex_c.h b/core/src/index/thirdparty/faiss/c_api/gpu/GpuIndex_c.h deleted file mode 100644 index 664c76101f..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/gpu/GpuIndex_c.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c -*- - -#ifndef FAISS_GPU_INDEX_C_H -#define FAISS_GPU_INDEX_C_H - -#include "faiss_c.h" - -#ifdef __cplusplus -extern "C" { -#endif - -FAISS_DECLARE_CLASS(GpuIndexConfig) - -FAISS_DECLARE_GETTER(GpuIndexConfig, int, device) - -FAISS_DECLARE_CLASS_INHERITED(GpuIndex, Index) - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/core/src/index/thirdparty/faiss/c_api/gpu/GpuIndicesOptions_c.h b/core/src/index/thirdparty/faiss/c_api/gpu/GpuIndicesOptions_c.h deleted file mode 100644 index 6a49773bc6..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/gpu/GpuIndicesOptions_c.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c -*- - -#ifndef FAISS_GPU_INDICES_OPTIONS_C_H -#define FAISS_GPU_INDICES_OPTIONS_C_H - -#ifdef __cplusplus -extern "C" { -#endif - -/// How user vector index data is stored on the GPU -typedef enum FaissIndicesOptions { - /// The user indices are only stored on the CPU; the GPU returns - /// (inverted list, offset) to the CPU which is then translated to - /// the real user index. - INDICES_CPU = 0, - /// The indices are not stored at all, on either the CPU or - /// GPU. Only (inverted list, offset) is returned to the user as the - /// index. - INDICES_IVF = 1, - /// Indices are stored as 32 bit integers on the GPU, but returned - /// as 64 bit integers - INDICES_32_BIT = 2, - /// Indices are stored as 64 bit integers on the GPU - INDICES_64_BIT = 3, -} FaissIndicesOptions; - -#ifdef __cplusplus -} -#endif -#endif \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/c_api/gpu/GpuResources_c.cpp b/core/src/index/thirdparty/faiss/c_api/gpu/GpuResources_c.cpp deleted file mode 100644 index 3f6525125d..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/gpu/GpuResources_c.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#include "gpu/GpuResources_c.h" -#include "gpu/GpuResources.h" -#include "macros_impl.h" - -using faiss::gpu::GpuResources; - -DEFINE_DESTRUCTOR(GpuResources) - -int faiss_GpuResources_initializeForDevice(FaissGpuResources* res, int device) { - try { - reinterpret_cast(res)->initializeForDevice(device); - } CATCH_AND_HANDLE -} - -int faiss_GpuResources_getBlasHandle(FaissGpuResources* res, int device, cublasHandle_t* out) { - try { - auto o = reinterpret_cast(res)->getBlasHandle(device); - *out = o; - } CATCH_AND_HANDLE -} - -int faiss_GpuResources_getDefaultStream(FaissGpuResources* res, int device, cudaStream_t* out) { - try { - auto o = reinterpret_cast(res)->getDefaultStream(device); - *out = o; - } CATCH_AND_HANDLE -} - -int faiss_GpuResources_getPinnedMemory(FaissGpuResources* res, void** p_buffer, size_t* p_size) { - try { - auto o = reinterpret_cast(res)->getPinnedMemory(); - *p_buffer = o.first; - *p_size = o.second; - } CATCH_AND_HANDLE -} - -int faiss_GpuResources_getAsyncCopyStream(FaissGpuResources* res, int device, cudaStream_t* out) { - try { - auto o = reinterpret_cast(res)->getAsyncCopyStream(device); - *out = o; - } CATCH_AND_HANDLE -} - -int faiss_GpuResources_getBlasHandleCurrentDevice(FaissGpuResources* res, cublasHandle_t* out) { - try { - auto o = reinterpret_cast(res)->getBlasHandleCurrentDevice(); - *out = o; - } CATCH_AND_HANDLE -} - -int faiss_GpuResources_getDefaultStreamCurrentDevice(FaissGpuResources* res, cudaStream_t* out) { - try { - auto o = reinterpret_cast(res)->getDefaultStreamCurrentDevice(); - *out = o; - } CATCH_AND_HANDLE -} - -int faiss_GpuResources_syncDefaultStream(FaissGpuResources* res, int device) { - try { - reinterpret_cast(res)->syncDefaultStream(device); - } CATCH_AND_HANDLE -} - -int faiss_GpuResources_syncDefaultStreamCurrentDevice(FaissGpuResources* res) { - try { - reinterpret_cast(res)->syncDefaultStreamCurrentDevice(); - } CATCH_AND_HANDLE -} - -int faiss_GpuResources_getAsyncCopyStreamCurrentDevice(FaissGpuResources* res, cudaStream_t* out) { - try { - auto o = reinterpret_cast(res)->getAsyncCopyStreamCurrentDevice(); - *out = o; - } CATCH_AND_HANDLE -} - diff --git a/core/src/index/thirdparty/faiss/c_api/gpu/GpuResources_c.h b/core/src/index/thirdparty/faiss/c_api/gpu/GpuResources_c.h deleted file mode 100644 index bb9cefde36..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/gpu/GpuResources_c.h +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c -*- - -#ifndef FAISS_GPU_RESOURCES_C_H -#define FAISS_GPU_RESOURCES_C_H - -#include -#include -#include "faiss_c.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/// Base class of GPU-side resource provider; hides provision of -/// cuBLAS handles, CUDA streams and a temporary memory manager -FAISS_DECLARE_CLASS(GpuResources) - -FAISS_DECLARE_DESTRUCTOR(GpuResources) - -/// Call to pre-allocate resources for a particular device. If this is -/// not called, then resources will be allocated at the first time -/// of demand -int faiss_GpuResources_initializeForDevice(FaissGpuResources*, int); - -/// Returns the cuBLAS handle that we use for the given device -int faiss_GpuResources_getBlasHandle(FaissGpuResources*, int, cublasHandle_t*); - -/// Returns the stream that we order all computation on for the -/// given device -int faiss_GpuResources_getDefaultStream(FaissGpuResources*, int, cudaStream_t*); - -/// Returns the available CPU pinned memory buffer -int faiss_GpuResources_getPinnedMemory(FaissGpuResources*, void**, size_t*); - -/// Returns the stream on which we perform async CPU <-> GPU copies -int faiss_GpuResources_getAsyncCopyStream(FaissGpuResources*, int, cudaStream_t*); - -/// Calls getBlasHandle with the current device -int faiss_GpuResources_getBlasHandleCurrentDevice(FaissGpuResources*, cublasHandle_t*); - -/// Calls getDefaultStream with the current device -int faiss_GpuResources_getDefaultStreamCurrentDevice(FaissGpuResources*, cudaStream_t*); - -/// Synchronizes the CPU with respect to the default stream for the -/// given device -// equivalent to cudaDeviceSynchronize(getDefaultStream(device)) -int faiss_GpuResources_syncDefaultStream(FaissGpuResources*, int); - -/// Calls syncDefaultStream for the current device -int faiss_GpuResources_syncDefaultStreamCurrentDevice(FaissGpuResources*); - -/// Calls getAsyncCopyStream for the current device -int faiss_GpuResources_getAsyncCopyStreamCurrentDevice(FaissGpuResources*, cudaStream_t*); - -#ifdef __cplusplus -} -#endif -#endif \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/c_api/gpu/Makefile b/core/src/index/thirdparty/faiss/c_api/gpu/Makefile deleted file mode 100644 index ab1f707cee..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/gpu/Makefile +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -.SUFFIXES: .cpp .o - -# C API with GPU support - -include ../../makefile.inc -DEBUGFLAG=-DNDEBUG # no debugging - -LIBNAME=libgpufaiss -CLIBNAME=libgpufaiss_c -LIBGPUCOBJ=GpuAutoTune_c.o GpuClonerOptions_c.o GpuIndex_c.o GpuResources_c.o \ - StandardGpuResources_c.o -LIBCOBJ=../libfaiss_c.a -CFLAGS=-fPIC -m64 -Wno-sign-compare -g -O3 -Wall -Wextra -CUDACFLAGS=-I$(CUDA_ROOT)/include - -# Build shared object file by default -all: $(CLIBNAME).$(SHAREDEXT) - -# Build static object file containing the wrapper implementation only. -# Consumers are required to link with the C++ standard library and remaining -# portions of this library: libfaiss_c.a, libfaiss.a, libgpufaiss.a, and libstdc++. -$(CLIBNAME).a: $(LIBGPUCOBJ) ../../gpu/$(LIBNAME).a - ar r $@ $^ - -# Build dynamic library -$(CLIBNAME).$(SHAREDEXT): $(LIBCOBJ) $(LIBGPUCOBJ) ../../libfaiss.a ../../gpu/$(LIBNAME).a - $(CXX) $(LDFLAGS) $(SHAREDFLAGS) $(CUDACFLAGS) -o $@ \ - -Wl,--whole-archive $(LIBCOBJ) ../../libfaiss.a \ - -Wl,--no-whole-archive -static-libstdc++ $(LIBGPUCOBJ) $(LIBS) ../../gpu/$(LIBNAME).a \ - $(NVCCLDFLAGS) $(NVCCLIBS) - -# Build GPU example -bin/example_gpu_c: example_gpu_c.c $(CLIBNAME).$(SHAREDEXT) - $(CC) $(CFLAGS) $(CUDACFLAGS) $(NVCCLIBS) -std=c99 -I. -I.. -o $@ example_gpu_c.c \ - -L. -lgpufaiss_c - -clean: - rm -f $(CLIBNAME).a $(CLIBNAME).$(SHAREDEXT)* *.o bin/example_gpu_c - -%.o: %.cpp - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(CPUFLAGS) -c $< -o $@ - -# Dependencies - -GpuAutoTune_c.o: CXXFLAGS += -I.. -I../.. $(CUDACFLAGS) $(DEBUGFLAG) -GpuAutoTune_c.o: GpuAutoTune_c.cpp GpuAutoTune_c.h ../../gpu/GpuAutoTune.h ../Index_c.h ../macros_impl.h - -GpuClonerOptions_c.o: CXXFLAGS += -I.. -I../.. $(CUDACFLAGS) $(DEBUGFLAG) -GpuClonerOptions_c.o: GpuClonerOptions_c.cpp GpuClonerOptions_c.h GpuIndicesOptions_c.h ../../gpu/GpuClonerOptions.h ../macros_impl.h - -GpuIndex_c.o: CXXFLAGS += -I.. -I../.. $(CUDACFLAGS) $(DEBUGFLAG) -GpuIndex_c.o: GpuIndex_c.cpp GpuIndex_c.h ../../gpu/GpuIndex.h ../macros_impl.h - -GpuResources_c.o: CXXFLAGS += -I.. -I../.. $(CUDACFLAGS) $(DEBUGFLAG) -GpuResources_c.o: GpuResources_c.cpp GpuResources_c.h ../../gpu/GpuResources.h ../macros_impl.h - -StandardGpuResources_c.o: CXXFLAGS += -I.. -I../.. $(CUDACFLAGS) $(DEBUGFLAG) -StandardGpuResources_c.o: StandardGpuResources_c.cpp StandardGpuResources_c.h ../../gpu/StandardGpuResources.h ../macros_impl.h diff --git a/core/src/index/thirdparty/faiss/c_api/gpu/StandardGpuResources_c.cpp b/core/src/index/thirdparty/faiss/c_api/gpu/StandardGpuResources_c.cpp deleted file mode 100644 index 84afb027eb..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/gpu/StandardGpuResources_c.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#include "gpu/StandardGpuResources_c.h" -#include "gpu/StandardGpuResources.h" -#include "macros_impl.h" - -using faiss::gpu::StandardGpuResources; - -DEFINE_DESTRUCTOR(StandardGpuResources) - -int faiss_StandardGpuResources_new(FaissStandardGpuResources** p_res) { - try { - auto p = new StandardGpuResources(); - *p_res = reinterpret_cast(p); - } CATCH_AND_HANDLE -} - -int faiss_StandardGpuResources_noTempMemory(FaissStandardGpuResources* res) { - try { - reinterpret_cast(res)->noTempMemory(); - } CATCH_AND_HANDLE -} - -int faiss_StandardGpuResources_setTempMemory(FaissStandardGpuResources* res, size_t size) { - try { - reinterpret_cast(res)->setTempMemory(size); - } CATCH_AND_HANDLE -} - -int faiss_StandardGpuResources_setPinnedMemory(FaissStandardGpuResources* res, size_t size) { - try { - reinterpret_cast(res)->setPinnedMemory(size); - } CATCH_AND_HANDLE -} - -int faiss_StandardGpuResources_setDefaultStream(FaissStandardGpuResources* res, int device, cudaStream_t stream) { - try { - reinterpret_cast(res)->setDefaultStream(device, stream); - } CATCH_AND_HANDLE -} - -int faiss_StandardGpuResources_setDefaultNullStreamAllDevices(FaissStandardGpuResources* res) { - try { - reinterpret_cast(res)->setDefaultNullStreamAllDevices(); - } CATCH_AND_HANDLE -} diff --git a/core/src/index/thirdparty/faiss/c_api/gpu/StandardGpuResources_c.h b/core/src/index/thirdparty/faiss/c_api/gpu/StandardGpuResources_c.h deleted file mode 100644 index f9a3c854f0..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/gpu/StandardGpuResources_c.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c -*- - -#ifndef FAISS_STANDARD_GPURESOURCES_C_H -#define FAISS_STANDARD_GPURESOURCES_C_H - -#include -#include "faiss_c.h" -#include "gpu/GpuResources_c.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/// Default implementation of GpuResources that allocates a cuBLAS -/// stream and 2 streams for use, as well as temporary memory -FAISS_DECLARE_CLASS_INHERITED(StandardGpuResources, GpuResources) - -FAISS_DECLARE_DESTRUCTOR(StandardGpuResources) - -/// Default constructor for StandardGpuResources -int faiss_StandardGpuResources_new(FaissStandardGpuResources**); - -/// Disable allocation of temporary memory; all temporary memory -/// requests will call cudaMalloc / cudaFree at the point of use -int faiss_StandardGpuResources_noTempMemory(FaissStandardGpuResources*); - -/// Specify that we wish to use a certain fixed size of memory on -/// all devices as temporary memory -int faiss_StandardGpuResources_setTempMemory(FaissStandardGpuResources*, size_t size); - -/// Set amount of pinned memory to allocate, for async GPU <-> CPU -/// transfers -int faiss_StandardGpuResources_setPinnedMemory(FaissStandardGpuResources*, size_t size); - -/// Called to change the stream for work ordering -int faiss_StandardGpuResources_setDefaultStream(FaissStandardGpuResources*, int device, cudaStream_t stream); - -/// Called to change the work ordering streams to the null stream -/// for all devices -int faiss_StandardGpuResources_setDefaultNullStreamAllDevices(FaissStandardGpuResources*); - -#ifdef __cplusplus -} -#endif -#endif \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/c_api/gpu/example_gpu_c.c b/core/src/index/thirdparty/faiss/c_api/gpu/example_gpu_c.c deleted file mode 100644 index c2a10a2e30..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/gpu/example_gpu_c.c +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c -*- - -#include -#include -#include - -#include "error_c.h" -#include "Index_c.h" -#include "AutoTune_c.h" -#include "GpuAutoTune_c.h" -#include "StandardGpuResources_c.h" - -#define FAISS_TRY(C) \ - { \ - if (C) { \ - fprintf(stderr, "%s", faiss_get_last_error()); \ - exit(-1); \ - } \ - } - -double drand() { - return (double)rand() / (double)RAND_MAX; -} - -int main() { - time_t seed = time(NULL); - srand(seed); - printf("Generating some data...\n"); - int d = 128; // dimension - int nb = 100000; // database size - int nq = 10000; // nb of queries - float *xb = malloc(d * nb * sizeof(float)); - float *xq = malloc(d * nq * sizeof(float)); - - for(int i = 0; i < nb; i++) { - for(int j = 0; j < d; j++) xb[d * i + j] = drand(); - xb[d * i] += i / 1000.; - } - for(int i = 0; i < nq; i++) { - for(int j = 0; j < d; j++) xq[d * i + j] = drand(); - xq[d * i] += i / 1000.; - } - - printf("Loading standard GPU resources...\n"); - FaissStandardGpuResources* gpu_res = NULL; - FAISS_TRY(faiss_StandardGpuResources_new(&gpu_res)); - - printf("Building an index...\n"); - FaissIndex* cpu_index = NULL; - FAISS_TRY(faiss_index_factory(&cpu_index, d, "Flat", METRIC_L2)); // use factory to create index - - printf("Moving index to the GPU...\n"); - FaissGpuIndex* index = NULL; - FaissGpuClonerOptions* options = NULL; - FAISS_TRY(faiss_GpuClonerOptions_new(&options)); - FAISS_TRY(faiss_index_cpu_to_gpu_with_options(gpu_res, 0, cpu_index, options, &index)); - - printf("is_trained = %s\n", faiss_Index_is_trained(index) ? "true" : "false"); - FAISS_TRY(faiss_Index_add(index, nb, xb)); // add vectors to the index - printf("ntotal = %ld\n", faiss_Index_ntotal(index)); - - printf("Searching...\n"); - int k = 5; - - { // sanity check: search 5 first vectors of xb - idx_t *I = malloc(k * 5 * sizeof(idx_t)); - float *D = malloc(k * 5 * sizeof(float)); - FAISS_TRY(faiss_Index_search(index, 5, xb, k, D, I)); - printf("I=\n"); - for(int i = 0; i < 5; i++) { - for(int j = 0; j < k; j++) printf("%5ld (d=%2.3f) ", I[i * k + j], D[i * k + j]); - printf("\n"); - } - free(I); - free(D); - } - { // search xq - idx_t *I = malloc(k * nq * sizeof(idx_t)); - float *D = malloc(k * nq * sizeof(float)); - FAISS_TRY(faiss_Index_search(index, 5, xb, k, D, I)); - printf("I=\n"); - for(int i = 0; i < 5; i++) { - for(int j = 0; j < k; j++) printf("%5ld (d=%2.3f) ", I[i * k + j], D[i * k + j]); - printf("\n"); - } - free(I); - free(D); - } - - printf("Freeing index...\n"); - faiss_Index_free(index); - printf("Freeing GPU resources...\n"); - faiss_GpuResources_free(gpu_res); - faiss_GpuClonerOptions_free(options); - printf("Done.\n"); - - return 0; -} \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/c_api/gpu/macros_impl.h b/core/src/index/thirdparty/faiss/c_api/gpu/macros_impl.h deleted file mode 100644 index 3f6ea5844a..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/gpu/macros_impl.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#ifndef GPU_MACROS_IMPL_H -#define GPU_MACROS_IMPL_H -#include "../macros_impl.h" - -#undef DEFINE_GETTER -#define DEFINE_GETTER(clazz, ty, name) \ - ty faiss_ ## clazz ## _ ## name (const Faiss ## clazz *obj) { \ - return static_cast< ty >( \ - reinterpret_cast< const faiss::gpu::clazz *>(obj)-> name \ - ); \ - } - -#undef DEFINE_SETTER -#define DEFINE_SETTER(clazz, ty, name) \ - void faiss_ ## clazz ## _set_ ## name (Faiss ## clazz *obj, ty val) { \ - reinterpret_cast< faiss::gpu::clazz *>(obj)-> name = val; \ - } - -#undef DEFINE_SETTER_STATIC -#define DEFINE_SETTER_STATIC(clazz, ty_to, ty_from, name) \ - void faiss_ ## clazz ## _set_ ## name (Faiss ## clazz *obj, ty_from val) { \ - reinterpret_cast< faiss::gpu::clazz *>(obj)-> name = \ - static_cast< ty_to >(val); \ - } - -#undef DEFINE_DESTRUCTOR -#define DEFINE_DESTRUCTOR(clazz) \ - void faiss_ ## clazz ## _free (Faiss ## clazz *obj) { \ - delete reinterpret_cast(obj); \ - } - -#endif diff --git a/core/src/index/thirdparty/faiss/c_api/impl/AuxIndexStructures_c.cpp b/core/src/index/thirdparty/faiss/c_api/impl/AuxIndexStructures_c.cpp deleted file mode 100644 index 4b3fec8fb7..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/impl/AuxIndexStructures_c.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#include "AuxIndexStructures_c.h" -#include "../../impl/AuxIndexStructures.h" -#include "../macros_impl.h" -#include - -using faiss::BufferList; -using faiss::IDSelector; -using faiss::IDSelectorBatch; -using faiss::IDSelectorRange; -using faiss::RangeSearchResult; -using faiss::RangeSearchPartialResult; -using faiss::RangeQueryResult; -using faiss::DistanceComputer; - -DEFINE_GETTER(RangeSearchResult, size_t, nq) - -int faiss_RangeSearchResult_new(FaissRangeSearchResult** p_rsr, idx_t nq) { - try { - *p_rsr = reinterpret_cast( - new RangeSearchResult(nq)); - return 0; - } CATCH_AND_HANDLE -} - -int faiss_RangeSearchResult_new_with(FaissRangeSearchResult** p_rsr, idx_t nq, int alloc_lims) { - try { - *p_rsr = reinterpret_cast( - new RangeSearchResult(nq, static_cast(alloc_lims))); - return 0; - } CATCH_AND_HANDLE -} - -/// called when lims contains the nb of elements result entries -/// for each query -int faiss_RangeSearchResult_do_allocation(FaissRangeSearchResult* rsr) { - try { - reinterpret_cast(rsr)->do_allocation(); - return 0; - } CATCH_AND_HANDLE -} - -DEFINE_DESTRUCTOR(RangeSearchResult) - -/// getter for buffer_size -DEFINE_GETTER(RangeSearchResult, size_t, buffer_size) - -/// getter for lims: size (nq + 1) -void faiss_RangeSearchResult_lims(FaissRangeSearchResult* rsr, size_t** lims) { - *lims = reinterpret_cast(rsr)->lims; -} - -/// getter for labels and respective distances (not sorted): -/// result for query i is labels[lims[i]:lims[i+1]] -void faiss_RangeSearchResult_labels(FaissRangeSearchResult* rsr, idx_t** labels, float** distances) { - auto sr = reinterpret_cast(rsr); - *labels = sr->labels; - *distances = sr->distances; -} - -DEFINE_DESTRUCTOR(IDSelector) - -int faiss_IDSelector_is_member(const FaissIDSelector* sel, idx_t id) { - return reinterpret_cast(sel)->is_member(id); -} - -DEFINE_DESTRUCTOR(IDSelectorRange) - -DEFINE_GETTER(IDSelectorRange, idx_t, imin) -DEFINE_GETTER(IDSelectorRange, idx_t, imax) - -int faiss_IDSelectorRange_new(FaissIDSelectorRange** p_sel, idx_t imin, idx_t imax) { - try { - *p_sel = reinterpret_cast( - new IDSelectorRange(imin, imax) - ); - return 0; - } CATCH_AND_HANDLE -} - -DEFINE_GETTER(IDSelectorBatch, int, nbits) -DEFINE_GETTER(IDSelectorBatch, idx_t, mask) - -int faiss_IDSelectorBatch_new(FaissIDSelectorBatch** p_sel, size_t n, const idx_t* indices) { - try { - *p_sel = reinterpret_cast( - new IDSelectorBatch(n, indices) - ); - return 0; - } CATCH_AND_HANDLE -} - -// Below are structures used only by Index implementations - -DEFINE_DESTRUCTOR(BufferList) - -DEFINE_GETTER(BufferList, size_t, buffer_size) -DEFINE_GETTER(BufferList, size_t, wp) - -int faiss_BufferList_append_buffer(FaissBufferList* bl) { - try { - reinterpret_cast(bl)->append_buffer(); - return 0; - } CATCH_AND_HANDLE -} - -int faiss_BufferList_new(FaissBufferList** p_bl, size_t buffer_size) { - try { - *p_bl = reinterpret_cast( - new BufferList(buffer_size) - ); - return 0; - } CATCH_AND_HANDLE -} - -int faiss_BufferList_add(FaissBufferList* bl, idx_t id, float dis) { - try { - reinterpret_cast(bl)->add(id, dis); - return 0; - } CATCH_AND_HANDLE -} - -/// copy elemnts ofs:ofs+n-1 seen as linear data in the buffers to -/// tables dest_ids, dest_dis -int faiss_BufferList_copy_range( - FaissBufferList* bl, size_t ofs, size_t n, idx_t *dest_ids, float *dest_dis) { - try { - reinterpret_cast(bl)->copy_range(ofs, n, dest_ids, dest_dis); - return 0; - } CATCH_AND_HANDLE -} - -DEFINE_GETTER(RangeQueryResult, idx_t, qno) -DEFINE_GETTER(RangeQueryResult, size_t, nres) -DEFINE_GETTER_PERMISSIVE(RangeQueryResult, FaissRangeSearchPartialResult*, pres) - -int faiss_RangeQueryResult_add(FaissRangeQueryResult* qr, float dis, idx_t id) { - try { - reinterpret_cast(qr)->add(dis, id); - return 0; - } CATCH_AND_HANDLE -} - -DEFINE_GETTER_PERMISSIVE(RangeSearchPartialResult, FaissRangeSearchResult*, res) - -int faiss_RangeSearchPartialResult_new( - FaissRangeSearchPartialResult** p_res, FaissRangeSearchResult* res_in) { - try { - *p_res = reinterpret_cast( - new RangeSearchPartialResult( - reinterpret_cast(res_in)) - ); - return 0; - } CATCH_AND_HANDLE -} - -int faiss_RangeSearchPartialResult_finalize( - FaissRangeSearchPartialResult* res) { - try { - reinterpret_cast(res)->finalize(); - return 0; - } CATCH_AND_HANDLE -} - -/// called by range_search before do_allocation -int faiss_RangeSearchPartialResult_set_lims( - FaissRangeSearchPartialResult* res) { - try { - reinterpret_cast(res)->set_lims(); - return 0; - } CATCH_AND_HANDLE -} - -int faiss_RangeSearchPartialResult_new_result( - FaissRangeSearchPartialResult* res, idx_t qno, FaissRangeQueryResult** qr) { - - try { - auto q = - &reinterpret_cast(res)->new_result(qno); - if (qr) { - *qr = reinterpret_cast(&q); - } - return 0; - } CATCH_AND_HANDLE -} - -DEFINE_DESTRUCTOR(DistanceComputer) - -int faiss_DistanceComputer_set_query(FaissDistanceComputer *dc, const float *x) { - try { - reinterpret_cast(dc)->set_query(x); - return 0; - } - CATCH_AND_HANDLE -} - -int faiss_DistanceComputer_vector_to_query_dis(FaissDistanceComputer *dc, idx_t i, float *qd) { - try { - *qd = reinterpret_cast(dc)->operator()(i); - return 0; - } - CATCH_AND_HANDLE -} - -int faiss_DistanceComputer_symmetric_dis(FaissDistanceComputer *dc, idx_t i, idx_t j, float *vd) { - try { - *vd = reinterpret_cast(dc)->symmetric_dis(i, j); - return 0; - } - CATCH_AND_HANDLE -} \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/c_api/impl/AuxIndexStructures_c.h b/core/src/index/thirdparty/faiss/c_api/impl/AuxIndexStructures_c.h deleted file mode 100644 index 1d66b0aac0..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/impl/AuxIndexStructures_c.h +++ /dev/null @@ -1,149 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c -*- - -#ifndef FAISS_AUX_INDEX_STRUCTURES_C_H -#define FAISS_AUX_INDEX_STRUCTURES_C_H - -#include "../Index_c.h" -#include "../faiss_c.h" - -#ifdef __cplusplus -extern "C" { -#endif - -FAISS_DECLARE_CLASS(RangeSearchResult) - -FAISS_DECLARE_GETTER(RangeSearchResult, size_t, nq) - -int faiss_RangeSearchResult_new(FaissRangeSearchResult** p_rsr, idx_t nq); - -int faiss_RangeSearchResult_new_with(FaissRangeSearchResult** p_rsr, idx_t nq, int alloc_lims); - -/// called when lims contains the nb of elements result entries -/// for each query -int faiss_RangeSearchResult_do_allocation(FaissRangeSearchResult* rsr); - -FAISS_DECLARE_DESTRUCTOR(RangeSearchResult) - -/// getter for buffer_size -FAISS_DECLARE_GETTER(RangeSearchResult, size_t, buffer_size) - -/// getter for lims: size (nq + 1) -void faiss_RangeSearchResult_lims( - FaissRangeSearchResult* rsr, size_t** lims); - -/// getter for labels and respective distances (not sorted): -/// result for query i is labels[lims[i]:lims[i+1]] -void faiss_RangeSearchResult_labels( - FaissRangeSearchResult* rsr, idx_t** labels, float** distances); - - -/** Encapsulates a set of ids to remove. */ -FAISS_DECLARE_CLASS(IDSelector) -FAISS_DECLARE_DESTRUCTOR(IDSelector) - -int faiss_IDSelector_is_member(const FaissIDSelector* sel, idx_t id); - -/** remove ids between [imni, imax) */ -FAISS_DECLARE_CLASS(IDSelectorRange) -FAISS_DECLARE_DESTRUCTOR(IDSelectorRange) - -FAISS_DECLARE_GETTER(IDSelectorRange, idx_t, imin) -FAISS_DECLARE_GETTER(IDSelectorRange, idx_t, imax) - -int faiss_IDSelectorRange_new(FaissIDSelectorRange** p_sel, idx_t imin, idx_t imax); - -/** Remove ids from a set. Repetitions of ids in the indices set - * passed to the constructor does not hurt performance. The hash - * function used for the bloom filter and GCC's implementation of - * unordered_set are just the least significant bits of the id. This - * works fine for random ids or ids in sequences but will produce many - * hash collisions if lsb's are always the same */ -FAISS_DECLARE_CLASS(IDSelectorBatch) - -FAISS_DECLARE_GETTER(IDSelectorBatch, int, nbits) -FAISS_DECLARE_GETTER(IDSelectorBatch, idx_t, mask) - -int faiss_IDSelectorBatch_new(FaissIDSelectorBatch** p_sel, size_t n, const idx_t* indices); - -// Below are structures used only by Index implementations - -/** List of temporary buffers used to store results before they are - * copied to the RangeSearchResult object. */ -FAISS_DECLARE_CLASS(BufferList) -FAISS_DECLARE_DESTRUCTOR(BufferList) - -FAISS_DECLARE_GETTER(BufferList, size_t, buffer_size) -FAISS_DECLARE_GETTER(BufferList, size_t, wp) - -typedef struct FaissBuffer { - idx_t *ids; - float *dis; -} FaissBuffer; - -int faiss_BufferList_append_buffer(FaissBufferList* bl); - -int faiss_BufferList_new(FaissBufferList** p_bl, size_t buffer_size); - -int faiss_BufferList_add(FaissBufferList* bl, idx_t id, float dis); - -/// copy elemnts ofs:ofs+n-1 seen as linear data in the buffers to -/// tables dest_ids, dest_dis -int faiss_BufferList_copy_range( - FaissBufferList* bl, size_t ofs, size_t n, idx_t *dest_ids, float *dest_dis); - -/// the entries in the buffers are split per query -FAISS_DECLARE_CLASS(RangeSearchPartialResult) - -/// result structure for a single query -FAISS_DECLARE_CLASS(RangeQueryResult) -FAISS_DECLARE_GETTER(RangeQueryResult, idx_t, qno) -FAISS_DECLARE_GETTER(RangeQueryResult, size_t, nres) -FAISS_DECLARE_GETTER(RangeQueryResult, FaissRangeSearchPartialResult*, pres) - -int faiss_RangeQueryResult_add(FaissRangeQueryResult* qr, float dis, idx_t id); - - -FAISS_DECLARE_GETTER(RangeSearchPartialResult, FaissRangeSearchResult*, res) - -int faiss_RangeSearchPartialResult_new( - FaissRangeSearchPartialResult** p_res, FaissRangeSearchResult* res_in); - -int faiss_RangeSearchPartialResult_finalize( - FaissRangeSearchPartialResult* res); - -/// called by range_search before do_allocation -int faiss_RangeSearchPartialResult_set_lims( - FaissRangeSearchPartialResult* res); - -int faiss_RangeSearchPartialResult_new_result( - FaissRangeSearchPartialResult* res, idx_t qno, FaissRangeQueryResult** qr); - - -FAISS_DECLARE_CLASS(DistanceComputer) -/// called before computing distances -int faiss_DistanceComputer_set_query(FaissDistanceComputer *dc, const float *x); - -/** - * Compute distance of vector i to current query. - * This function corresponds to the function call operator: DistanceComputer::operator() - */ -int faiss_DistanceComputer_vector_to_query_dis( FaissDistanceComputer *dc, idx_t i, float *qd); -/// compute distance between two stored vectors -int faiss_DistanceComputer_symmetric_dis(FaissDistanceComputer *dc, idx_t i, idx_t j, float *vd); - -FAISS_DECLARE_DESTRUCTOR(DistanceComputer) - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/core/src/index/thirdparty/faiss/c_api/index_factory_c.cpp b/core/src/index/thirdparty/faiss/c_api/index_factory_c.cpp deleted file mode 100644 index f7f00c4132..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/index_factory_c.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -#include -#include "index_factory.h" -#include "index_factory_c.h" -#include "macros_impl.h" - -using faiss::Index; - -/** Build and index with the sequence of processing steps described in - * the string. - */ -int faiss_index_factory(FaissIndex** p_index, int d, const char* description, FaissMetricType metric) { - try { - *p_index = reinterpret_cast(faiss::index_factory( - d, description, static_cast(metric))); - } CATCH_AND_HANDLE -} \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/c_api/index_factory_c.h b/core/src/index/thirdparty/faiss/c_api/index_factory_c.h deleted file mode 100644 index 4262fe09a2..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/index_factory_c.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c -*- - -#ifndef FAISS_INDEX_FACTORY_C_H -#define FAISS_INDEX_FACTORY_C_H - -#include "faiss_c.h" -#include "Index_c.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** Build and index with the sequence of processing steps described in - * the string. - */ -int faiss_index_factory(FaissIndex** p_index, int d, const char* description, FaissMetricType metric); - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/c_api/index_io_c.cpp b/core/src/index/thirdparty/faiss/c_api/index_io_c.cpp deleted file mode 100644 index 8c0ca4420e..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/index_io_c.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved -// -*- c++ -*- -// I/O code for indexes - -#include "index_io_c.h" -#include "index_io.h" -#include "macros_impl.h" - -using faiss::Index; - -int faiss_write_index(const FaissIndex *idx, FILE *f) { - try { - faiss::write_index(reinterpret_cast(idx), f); - } CATCH_AND_HANDLE -} - -int faiss_write_index_fname(const FaissIndex *idx, const char *fname) { - try { - faiss::write_index(reinterpret_cast(idx), fname); - } CATCH_AND_HANDLE -} - -int faiss_read_index(FILE *f, int io_flags, FaissIndex **p_out) { - try { - auto out = faiss::read_index(f, io_flags); - *p_out = reinterpret_cast(out); - } CATCH_AND_HANDLE -} - -int faiss_read_index_fname(const char *fname, int io_flags, FaissIndex **p_out) { - try { - auto out = faiss::read_index(fname, io_flags); - *p_out = reinterpret_cast(out); - } CATCH_AND_HANDLE -} \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/c_api/index_io_c.h b/core/src/index/thirdparty/faiss/c_api/index_io_c.h deleted file mode 100644 index f703e491ca..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/index_io_c.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved -// -*- c++ -*- -// I/O code for indexes - - -#ifndef FAISS_INDEX_IO_C_H -#define FAISS_INDEX_IO_C_H - -#include -#include "faiss_c.h" -#include "Index_c.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** Write index to a file. - * This is equivalent to `faiss::write_index` when a file descriptor is provided. - */ -int faiss_write_index(const FaissIndex *idx, FILE *f); - -/** Write index to a file. - * This is equivalent to `faiss::write_index` when a file path is provided. - */ -int faiss_write_index_fname(const FaissIndex *idx, const char *fname); - -#define FAISS_IO_FLAG_MMAP 1 -#define FAISS_IO_FLAG_READ_ONLY 2 - -/** Read index from a file. - * This is equivalent to `faiss:read_index` when a file descriptor is given. - */ -int faiss_read_index(FILE *f, int io_flags, FaissIndex **p_out); - -/** Read index from a file. - * This is equivalent to `faiss:read_index` when a file path is given. - */ -int faiss_read_index_fname(const char *fname, int io_flags, FaissIndex **p_out); - -#ifdef __cplusplus -} -#endif -#endif \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/c_api/macros_impl.h b/core/src/index/thirdparty/faiss/c_api/macros_impl.h deleted file mode 100644 index af07938018..0000000000 --- a/core/src/index/thirdparty/faiss/c_api/macros_impl.h +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved. -// -*- c++ -*- - -/// Utility macros for the C wrapper implementation. - -#ifndef MACROS_IMPL_H -#define MACROS_IMPL_H - -#include "faiss_c.h" -#include "FaissException.h" -#include "error_impl.h" -#include -#include - -#ifdef NDEBUG -#define CATCH_AND_HANDLE \ - catch (faiss::FaissException& e) { \ - faiss_last_exception = \ - std::make_exception_ptr(e); \ - return -2; \ - } catch (std::exception& e) { \ - faiss_last_exception = \ - std::make_exception_ptr(e); \ - return -4; \ - } catch (...) { \ - faiss_last_exception = \ - std::make_exception_ptr( \ - std::runtime_error("Unknown error")); \ - return -1; \ - } return 0; -#else -#define CATCH_AND_HANDLE \ - catch (faiss::FaissException& e) { \ - std::cerr << e.what() << '\n'; \ - faiss_last_exception = \ - std::make_exception_ptr(e); \ - return -2; \ - } catch (std::exception& e) { \ - std::cerr << e.what() << '\n'; \ - faiss_last_exception = \ - std::make_exception_ptr(e); \ - return -4; \ - } catch (...) { \ - std::cerr << "Unrecognized exception!\n"; \ - faiss_last_exception = \ - std::make_exception_ptr( \ - std::runtime_error("Unknown error")); \ - return -1; \ - } return 0; -#endif - -#define DEFINE_GETTER(clazz, ty, name) \ - ty faiss_ ## clazz ## _ ## name (const Faiss ## clazz *obj) { \ - return static_cast< ty >( \ - reinterpret_cast< const faiss::clazz *>(obj)-> name \ - ); \ - } - -#define DEFINE_GETTER_SUBCLASS(clazz, parent, ty, name) \ - ty faiss_ ## clazz ## _ ## name (const Faiss ## clazz *obj) { \ - return static_cast< ty >( \ - reinterpret_cast(obj)-> name \ - ); \ - } - -#define DEFINE_GETTER_PERMISSIVE(clazz, ty, name) \ - ty faiss_ ## clazz ## _ ## name (const Faiss ## clazz *obj) { \ - return ( ty ) ( \ - reinterpret_cast(obj)-> name \ - ); \ - } - -#define DEFINE_GETTER_SUBCLASS_PERMISSIVE(clazz, parent, ty, name) \ - ty faiss_ ## clazz ## _ ## name (const Faiss ## clazz *obj) { \ - return ( ty ) ( \ - reinterpret_cast(obj)-> name \ - ); \ - } - -#define DEFINE_SETTER(clazz, ty, name) \ - void faiss_ ## clazz ## _set_ ## name (Faiss ## clazz *obj, ty val) { \ - reinterpret_cast< faiss::clazz *>(obj)-> name = val; \ - } - -#define DEFINE_SETTER_STATIC(clazz, ty_to, ty_from, name) \ - void faiss_ ## clazz ## _set_ ## name (Faiss ## clazz *obj, ty_from val) { \ - reinterpret_cast< faiss::clazz *>(obj)-> name = \ - static_cast< ty_to >(val); \ - } - -#define DEFINE_DESTRUCTOR(clazz) \ - void faiss_ ## clazz ## _free (Faiss ## clazz *obj) { \ - delete reinterpret_cast(obj); \ - } - -#define DEFINE_INDEX_DOWNCAST(clazz) \ - Faiss ## clazz * faiss_ ## clazz ## _cast (FaissIndex* index) { \ - return reinterpret_cast( \ - dynamic_cast< faiss::clazz *>( \ - reinterpret_cast(index))); \ - } - -#endif diff --git a/core/src/index/thirdparty/faiss/clone_index.cpp b/core/src/index/thirdparty/faiss/clone_index.cpp deleted file mode 100644 index ca9809d284..0000000000 --- a/core/src/index/thirdparty/faiss/clone_index.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { - -/************************************************************* - * cloning functions - **************************************************************/ - - - -Index * clone_index (const Index *index) -{ - Cloner cl; - return cl.clone_Index (index); -} - -// assumes there is a copy constructor ready. Always try from most -// specific to most general. Most indexes don't have complicated -// structs, the default copy constructor often just works. -#define TRYCLONE(classname, obj) \ - if (const classname *clo = dynamic_cast(obj)) { \ - return new classname(*clo); \ - } else - -VectorTransform *Cloner::clone_VectorTransform (const VectorTransform *vt) -{ - TRYCLONE (RemapDimensionsTransform, vt) - TRYCLONE (OPQMatrix, vt) - TRYCLONE (PCAMatrix, vt) - TRYCLONE (ITQMatrix, vt) - TRYCLONE (RandomRotationMatrix, vt) - TRYCLONE (LinearTransform, vt) - { - FAISS_THROW_MSG("clone not supported for this type of VectorTransform"); - } - return nullptr; -} - -IndexIVF * Cloner::clone_IndexIVF (const IndexIVF *ivf) -{ - TRYCLONE (IndexIVFPQR, ivf) - TRYCLONE (IndexIVFPQ, ivf) - TRYCLONE (IndexIVFFlat, ivf) - TRYCLONE (IndexIVFScalarQuantizer, ivf) - TRYCLONE (IndexIVFSQHybrid, ivf) - { - FAISS_THROW_MSG("clone not supported for this type of IndexIVF"); - } - return nullptr; -} - -Index *Cloner::clone_Index (IndexComposition* index_composition) { - FAISS_THROW_MSG( "Not implemented"); -} - -Index *Cloner::clone_Index (const Index *index) -{ - TRYCLONE (IndexPQ, index) - TRYCLONE (IndexLSH, index) - TRYCLONE (IndexFlatL2, index) - TRYCLONE (IndexFlatIP, index) - TRYCLONE (IndexFlat, index) - TRYCLONE (IndexLattice, index) - TRYCLONE (IndexScalarQuantizer, index) - TRYCLONE (MultiIndexQuantizer, index) - if (const IndexIVF * ivf = dynamic_cast(index)) { - IndexIVF *res = clone_IndexIVF (ivf); - if (ivf->invlists == nullptr) { - res->invlists = nullptr; - } else if (auto *ails = dynamic_cast - (ivf->invlists)) { - res->invlists = new ArrayInvertedLists(*ails); - res->own_invlists = true; - } else if (auto *ails = dynamic_cast(ivf->invlists)) { - res->invlists = new ReadOnlyArrayInvertedLists(*ails); - res->own_invlists = true; - } else { - FAISS_THROW_MSG( "clone not supported for this type of inverted lists"); - } - res->own_fields = true; - res->quantizer = clone_Index (ivf->quantizer); - return res; - } else if (const IndexPreTransform * ipt = - dynamic_cast (index)) { - IndexPreTransform *res = new IndexPreTransform (); - res->d = ipt->d; - res->ntotal = ipt->ntotal; - res->is_trained = ipt->is_trained; - res->metric_type = ipt->metric_type; - res->metric_arg = ipt->metric_arg; - - - res->index = clone_Index (ipt->index); - for (int i = 0; i < ipt->chain.size(); i++) - res->chain.push_back (clone_VectorTransform (ipt->chain[i])); - res->own_fields = true; - return res; - } else if (const IndexIDMap *idmap = - dynamic_cast (index)) { - IndexIDMap *res = new IndexIDMap (*idmap); - res->own_fields = true; - res->index = clone_Index (idmap->index); - return res; - } else if (const IndexHNSW *ihnsw = - dynamic_cast (index)) { - IndexHNSW *res = new IndexHNSW (*ihnsw); - res->own_fields = true; - res->storage = clone_Index (ihnsw->storage); - return res; - } else if (const Index2Layer *i2l = - dynamic_cast (index)) { - Index2Layer *res = new Index2Layer (*i2l); - res->q1.own_fields = true; - res->q1.quantizer = clone_Index (i2l->q1.quantizer); - return res; - } else { - FAISS_THROW_MSG( "clone not supported for this type of Index"); - } - return nullptr; -} - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/clone_index.h b/core/src/index/thirdparty/faiss/clone_index.h deleted file mode 100644 index 45990c93f7..0000000000 --- a/core/src/index/thirdparty/faiss/clone_index.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -// I/O code for indexes - -#pragma once - - - -namespace faiss { - -struct Index; -struct IndexIVF; -struct VectorTransform; - -namespace gpu { -struct GpuIndexFlat; -} - -/* cloning functions */ -Index *clone_index (const Index *); - -struct IndexComposition { - Index *index = nullptr; - gpu::GpuIndexFlat *quantizer = nullptr; - long mode = 0; // 0: all data, 1: copy quantizer, 2: copy data -}; - -/** Cloner class, useful to override classes with other cloning - * functions. The cloning function above just calls - * Cloner::clone_Index. */ -struct Cloner { - virtual VectorTransform *clone_VectorTransform (const VectorTransform *); - virtual Index *clone_Index (const Index *); - virtual Index *clone_Index (IndexComposition* index_composition); - virtual IndexIVF *clone_IndexIVF (const IndexIVF *); - virtual ~Cloner() {} -}; - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/conda/Dockerfile b/core/src/index/thirdparty/faiss/conda/Dockerfile deleted file mode 100644 index 9184e8fea3..0000000000 --- a/core/src/index/thirdparty/faiss/conda/Dockerfile +++ /dev/null @@ -1,33 +0,0 @@ -FROM soumith/conda-cuda:latest - -COPY ./ faiss -WORKDIR /faiss/conda - -ENV FAISS_BUILD_VERSION 1.5.3 -ENV FAISS_BUILD_NUMBER 0 -RUN conda build faiss --no-anaconda-upload -c pytorch -RUN CUDA_ROOT=/usr/local/cuda-8.0 \ - CUDA_ARCH="-gencode=arch=compute_35,code=compute_35 \ - -gencode=arch=compute_52,code=compute_52 \ - -gencode=arch=compute_60,code=compute_60 \ - -gencode=arch=compute_61,code=compute_61" \ - conda build faiss-gpu --variants '{ "cudatoolkit": "8.0" }' \ - --no-anaconda-upload -c pytorch --no-test -RUN CUDA_ROOT=/usr/local/cuda-9.0 \ - CUDA_ARCH="-gencode=arch=compute_35,code=compute_35 \ - -gencode=arch=compute_52,code=compute_52 \ - -gencode=arch=compute_60,code=compute_60 \ - -gencode=arch=compute_61,code=compute_61 \ - -gencode=arch=compute_70,code=compute_70" \ - conda build faiss-gpu --variants '{ "cudatoolkit": "9.0" }' \ - --no-anaconda-upload -c pytorch --no-test -RUN CUDA_ROOT=/usr/local/cuda-10.0 \ - CUDA_ARCH="-gencode=arch=compute_35,code=compute_35 \ - -gencode=arch=compute_52,code=compute_52 \ - -gencode=arch=compute_60,code=compute_60 \ - -gencode=arch=compute_61,code=compute_61 \ - -gencode=arch=compute_70,code=compute_70 \ - -gencode=arch=compute_72,code=compute_72 \ - -gencode=arch=compute_75,code=compute_75" \ - conda build faiss-gpu --variants '{ "cudatoolkit": "10.0" }' \ - --no-anaconda-upload -c pytorch --no-test diff --git a/core/src/index/thirdparty/faiss/conda/conda_build_config.yaml b/core/src/index/thirdparty/faiss/conda/conda_build_config.yaml deleted file mode 100644 index e9f0a51d26..0000000000 --- a/core/src/index/thirdparty/faiss/conda/conda_build_config.yaml +++ /dev/null @@ -1,7 +0,0 @@ -CONDA_BUILD_SYSROOT: - - /opt/MacOSX10.9.sdk # [osx] -python: - - 2.7 - - 3.5 - - 3.6 - - 3.7 diff --git a/core/src/index/thirdparty/faiss/conda/faiss-gpu/build.sh b/core/src/index/thirdparty/faiss/conda/faiss-gpu/build.sh deleted file mode 100644 index 25326c90d9..0000000000 --- a/core/src/index/thirdparty/faiss/conda/faiss-gpu/build.sh +++ /dev/null @@ -1,16 +0,0 @@ -# Build avx2 version -CXXFLAGS="-mavx2 -mf16c" ./configure --with-cuda=$CUDA_ROOT --with-cuda-arch="$CUDA_ARCH" -make -j $CPU_COUNT -make -C python _swigfaiss_avx2.so -make clean - -# Build vanilla version (no avx) -./configure --with-cuda=$CUDA_ROOT --with-cuda-arch="$CUDA_ARCH" -make -j $CPU_COUNT -make -C python _swigfaiss.so - -make -C python build - -cd python - -$PYTHON setup.py install --single-version-externally-managed --record=record.txt diff --git a/core/src/index/thirdparty/faiss/conda/faiss-gpu/conda_build_config.yaml b/core/src/index/thirdparty/faiss/conda/faiss-gpu/conda_build_config.yaml deleted file mode 100644 index da98e5d414..0000000000 --- a/core/src/index/thirdparty/faiss/conda/faiss-gpu/conda_build_config.yaml +++ /dev/null @@ -1,11 +0,0 @@ -cxx_compiler_version: - - 5.4 -cudatoolkit: - - 8.0 - - 9.0 - - 9.2 - - 10.0 - - 10.1 -pin_run_as_build: - cudatoolkit: - max_pin: x.x diff --git a/core/src/index/thirdparty/faiss/conda/faiss-gpu/meta.yaml b/core/src/index/thirdparty/faiss/conda/faiss-gpu/meta.yaml deleted file mode 100644 index 886531bafc..0000000000 --- a/core/src/index/thirdparty/faiss/conda/faiss-gpu/meta.yaml +++ /dev/null @@ -1,41 +0,0 @@ -package: - name: faiss-gpu - version: "{{ FAISS_BUILD_VERSION }}" - -source: - git_url: ../../ - -requirements: - build: - - {{ compiler('cxx') }} - - llvm-openmp # [osx] - - setuptools - - swig - - host: - - python {{ python }} - - intel-openmp # [osx] - - numpy 1.11.* - - mkl >=2018 - - cudatoolkit {{ cudatoolkit }} - - run: - - python {{ python }} - - intel-openmp # [osx] - - numpy >=1.11 - - mkl >=2018 - - blas=*=mkl - - {{ pin_compatible('cudatoolkit') }} - -build: - number: {{ FAISS_BUILD_NUMBER }} - script_env: - - CUDA_ROOT - - CUDA_ARCH - -about: - home: https://github.com/facebookresearch/faiss - license: MIT - license_family: MIT - license_file: LICENSE - summary: A library for efficient similarity search and clustering of dense vectors. diff --git a/core/src/index/thirdparty/faiss/conda/faiss-gpu/run_test.py b/core/src/index/thirdparty/faiss/conda/faiss-gpu/run_test.py deleted file mode 100644 index 68e0bbc3e3..0000000000 --- a/core/src/index/thirdparty/faiss/conda/faiss-gpu/run_test.py +++ /dev/null @@ -1,16 +0,0 @@ -import faiss -import numpy as np - -d = 128 -n = 100 - -rs = np.random.RandomState(1337) -x = rs.rand(n, d).astype(np.float32) - -index = faiss.IndexFlatL2(d) - -res = faiss.StandardGpuResources() -gpu_index = faiss.index_cpu_to_gpu(res, 0, index) -gpu_index.add(x) - -D, I = index.search(x, 10) diff --git a/core/src/index/thirdparty/faiss/conda/faiss/build.sh b/core/src/index/thirdparty/faiss/conda/faiss/build.sh deleted file mode 100644 index 87ccb4cad0..0000000000 --- a/core/src/index/thirdparty/faiss/conda/faiss/build.sh +++ /dev/null @@ -1,16 +0,0 @@ -# Build avx2 version -CXXFLAGS="-mavx2 -mf16c" ./configure --without-cuda -make -j $CPU_COUNT -make -C python _swigfaiss_avx2.so -make clean - -# Build vanilla version (no avx) -./configure --without-cuda -make -j $CPU_COUNT -make -C python _swigfaiss.so - -make -C python build - -cd python - -$PYTHON setup.py install --single-version-externally-managed --record=record.txt diff --git a/core/src/index/thirdparty/faiss/conda/faiss/meta.yaml b/core/src/index/thirdparty/faiss/conda/faiss/meta.yaml deleted file mode 100644 index e765cf388d..0000000000 --- a/core/src/index/thirdparty/faiss/conda/faiss/meta.yaml +++ /dev/null @@ -1,36 +0,0 @@ -package: - name: faiss-cpu - version: "{{ FAISS_BUILD_VERSION }}" - -source: - git_url: ../../ - -requirements: - build: - - {{ compiler('cxx') }} - - llvm-openmp # [osx] - - setuptools - - swig - - host: - - python {{ python }} - - intel-openmp # [osx] - - numpy 1.11.* - - mkl >=2018 - - run: - - python {{ python }} - - intel-openmp # [osx] - - numpy >=1.11 - - blas=*=mkl - - mkl >=2018 - -build: - number: {{ FAISS_BUILD_NUMBER }} - -about: - home: https://github.com/facebookresearch/faiss - license: MIT - license_family: MIT - license_file: LICENSE - summary: A library for efficient similarity search and clustering of dense vectors. diff --git a/core/src/index/thirdparty/faiss/conda/faiss/run_test.py b/core/src/index/thirdparty/faiss/conda/faiss/run_test.py deleted file mode 100644 index 57e6d7d92c..0000000000 --- a/core/src/index/thirdparty/faiss/conda/faiss/run_test.py +++ /dev/null @@ -1,14 +0,0 @@ -import faiss -import numpy as np - -d = 128 -# NOTE: BLAS kicks in only when n > distance_compute_blas_threshold = 20 -n = 100 - -rs = np.random.RandomState(1337) -x = rs.rand(n, d).astype(np.float32) - -index = faiss.IndexFlatL2(d) -index.add(x) - -D, I = index.search(x, 10) diff --git a/core/src/index/thirdparty/faiss/configure b/core/src/index/thirdparty/faiss/configure deleted file mode 100755 index ed40daefd9..0000000000 --- a/core/src/index/thirdparty/faiss/configure +++ /dev/null @@ -1,7998 +0,0 @@ -#! /bin/sh -# Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for faiss 1.0. -# -# -# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. -# -# -# This configure script is free software; the Free Software Foundation -# gives unlimited permission to copy, distribute and modify it. -# -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. -## -------------------- ## -## M4sh Initialization. ## -## -------------------- ## - -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - -as_nl=' -' -export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -case $0 in #(( - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break - done -IFS=$as_save_IFS - - ;; -esac -# We did not find ourselves, most probably we were run as `sh COMMAND' -# in which case we are not to be found in the path. -if test "x$as_myself" = x; then - as_myself=$0 -fi -if test ! -f "$as_myself"; then - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -# Use a proper internal environment variable to ensure we don't fall - # into an infinite loop, continuously re-executing ourselves. - if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then - _as_can_reexec=no; export _as_can_reexec; - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -as_fn_exit 255 - fi - # We don't want this to propagate to other subprocesses. - { _as_can_reexec=; unset _as_can_reexec;} -if test "x$CONFIG_SHELL" = x; then - as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which - # is contrary to our usage. Disable this feature. - alias -g '\${1+\"\$@\"}'='\"\$@\"' - setopt NO_GLOB_SUBST -else - case \`(set -o) 2>/dev/null\` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi -" - as_required="as_fn_return () { (exit \$1); } -as_fn_success () { as_fn_return 0; } -as_fn_failure () { as_fn_return 1; } -as_fn_ret_success () { return 0; } -as_fn_ret_failure () { return 1; } - -exitcode=0 -as_fn_success || { exitcode=1; echo as_fn_success failed.; } -as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } -as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } -as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } -if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : - -else - exitcode=1; echo positional parameters were not saved. -fi -test x\$exitcode = x0 || exit 1 -test -x / || exit 1" - as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO - as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO - eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && - test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 -test \$(( 1 + 1 )) = 2 || exit 1" - if (eval "$as_required") 2>/dev/null; then : - as_have_required=yes -else - as_have_required=no -fi - if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : - -else - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -as_found=false -for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - as_found=: - case $as_dir in #( - /*) - for as_base in sh bash ksh sh5; do - # Try only shells that exist, to save several forks. - as_shell=$as_dir/$as_base - if { test -f "$as_shell" || test -f "$as_shell.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : - CONFIG_SHELL=$as_shell as_have_required=yes - if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : - break 2 -fi -fi - done;; - esac - as_found=false -done -$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : - CONFIG_SHELL=$SHELL as_have_required=yes -fi; } -IFS=$as_save_IFS - - - if test "x$CONFIG_SHELL" != x; then : - export CONFIG_SHELL - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -exit 255 -fi - - if test x$as_have_required = xno; then : - $as_echo "$0: This script requires a shell more modern than all" - $as_echo "$0: the shells that I found on your system." - if test x${ZSH_VERSION+set} = xset ; then - $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" - $as_echo "$0: be upgraded to zsh 4.3.4 or later." - else - $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, -$0: including any error possibly output before this -$0: message. Then install a modern shell, or manually run -$0: the script under such a shell if you do have one." - fi - exit 1 -fi -fi -fi -SHELL=${CONFIG_SHELL-/bin/sh} -export SHELL -# Unset more variables known to interfere with behavior of common tools. -CLICOLOR_FORCE= GREP_OPTIONS= -unset CLICOLOR_FORCE GREP_OPTIONS - -## --------------------- ## -## M4sh Shell Functions. ## -## --------------------- ## -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - $as_echo "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - - - as_lineno_1=$LINENO as_lineno_1a=$LINENO - as_lineno_2=$LINENO as_lineno_2a=$LINENO - eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && - test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { - # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) - sed -n ' - p - /[$]LINENO/= - ' <$as_myself | - sed ' - s/[$]LINENO.*/&-/ - t lineno - b - :lineno - N - :loop - s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ - t loop - s/-\n.*// - ' >$as_me.lineno && - chmod +x "$as_me.lineno" || - { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } - - # If we had to re-execute with $CONFIG_SHELL, we're ensured to have - # already done that, so ensure we don't try to do so again and fall - # in an infinite loop. This has already happened in practice. - _as_can_reexec=no; export _as_can_reexec - # Don't try to exec as it changes $[0], causing all sort of problems - # (the dirname of $[0] is not the place where we might find the - # original and so on. Autoconf is especially sensitive to this). - . "./$as_me.lineno" - # Exit status is that of the last command. - exit -} - -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -pR' - fi -else - as_ln_s='cp -pR' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - -as_test_x='test -x' -as_executable_p=as_fn_executable_p - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -test -n "$DJDIR" || exec 7<&0 &1 - -# Name of the host. -# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, -# so uname gets run too. -ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` - -# -# Initializations. -# -ac_default_prefix=/usr/local -ac_clean_files= -ac_config_libobj_dir=. -LIBOBJS= -cross_compiling=no -subdirs= -MFLAGS= -MAKEFLAGS= - -# Identity of this package. -PACKAGE_NAME='faiss' -PACKAGE_TARNAME='faiss' -PACKAGE_VERSION='1.0' -PACKAGE_STRING='faiss 1.0' -PACKAGE_BUGREPORT='' -PACKAGE_URL='' - -ac_unique_file="Index.h" -# Factoring default headers for most tests. -ac_includes_default="\ -#include -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_SYS_STAT_H -# include -#endif -#ifdef STDC_HEADERS -# include -# include -#else -# ifdef HAVE_STDLIB_H -# include -# endif -#endif -#ifdef HAVE_STRING_H -# if !defined STDC_HEADERS && defined HAVE_MEMORY_H -# include -# endif -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif -#ifdef HAVE_INTTYPES_H -# include -#endif -#ifdef HAVE_STDINT_H -# include -#endif -#ifdef HAVE_UNISTD_H -# include -#endif" - -ac_header_list= -ac_subst_vars='LTLIBOBJS -ARCH_CXXFLAGS -ARCH_CPUFLAGS -target_os -target_vendor -target_cpu -target -LAPACK_LIBS -OPENMP_LDFLAGS -BLAS_LIBS -host_os -host_vendor -host_cpu -host -build_os -build_vendor -build_cpu -build -OPENMP_CXXFLAGS -LIBOBJS -CUDA_ARCH -CUDA_PREFIX -NVCC_LIBS -NVCC_LDFLAGS -NVCC_CPPFLAGS -EGREP -GREP -CXXCPP -NVCC -SWIG -NUMPY_INCLUDE -PYTHON_CFLAGS -PYTHON -MKDIR_P -SET_MAKE -CPP -ac_ct_CC -CFLAGS -CC -HAVE_CXX11 -OBJEXT -EXEEXT -ac_ct_CXX -CPPFLAGS -LDFLAGS -CXXFLAGS -CXX -target_alias -host_alias -build_alias -LIBS -ECHO_T -ECHO_N -ECHO_C -DEFS -mandir -localedir -libdir -psdir -pdfdir -dvidir -htmldir -infodir -docdir -oldincludedir -includedir -localstatedir -sharedstatedir -sysconfdir -datadir -datarootdir -libexecdir -sbindir -bindir -program_transform_name -prefix -exec_prefix -PACKAGE_URL -PACKAGE_BUGREPORT -PACKAGE_STRING -PACKAGE_VERSION -PACKAGE_TARNAME -PACKAGE_NAME -PATH_SEPARATOR -SHELL' -ac_subst_files='' -ac_user_opts=' -enable_option_checking -with_python -with_swig -with_cuda -with_cuda_arch -enable_openmp -with_blas -with_lapack -' - ac_precious_vars='build_alias -host_alias -target_alias -CXX -CXXFLAGS -LDFLAGS -LIBS -CPPFLAGS -CCC -CC -CFLAGS -CPP -CXXCPP' - - -# Initialize some variables set by options. -ac_init_help= -ac_init_version=false -ac_unrecognized_opts= -ac_unrecognized_sep= -# The variables have the same names as the options, with -# dashes changed to underlines. -cache_file=/dev/null -exec_prefix=NONE -no_create= -no_recursion= -prefix=NONE -program_prefix=NONE -program_suffix=NONE -program_transform_name=s,x,x, -silent= -site= -srcdir= -verbose= -x_includes=NONE -x_libraries=NONE - -# Installation directory options. -# These are left unexpanded so users can "make install exec_prefix=/foo" -# and all the variables that are supposed to be based on exec_prefix -# by default will actually change. -# Use braces instead of parens because sh, perl, etc. also accept them. -# (The list follows the same order as the GNU Coding Standards.) -bindir='${exec_prefix}/bin' -sbindir='${exec_prefix}/sbin' -libexecdir='${exec_prefix}/libexec' -datarootdir='${prefix}/share' -datadir='${datarootdir}' -sysconfdir='${prefix}/etc' -sharedstatedir='${prefix}/com' -localstatedir='${prefix}/var' -includedir='${prefix}/include' -oldincludedir='/usr/include' -docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' -infodir='${datarootdir}/info' -htmldir='${docdir}' -dvidir='${docdir}' -pdfdir='${docdir}' -psdir='${docdir}' -libdir='${exec_prefix}/lib' -localedir='${datarootdir}/locale' -mandir='${datarootdir}/man' - -ac_prev= -ac_dashdash= -for ac_option -do - # If the previous option needs an argument, assign it. - if test -n "$ac_prev"; then - eval $ac_prev=\$ac_option - ac_prev= - continue - fi - - case $ac_option in - *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; - *=) ac_optarg= ;; - *) ac_optarg=yes ;; - esac - - # Accept the important Cygnus configure options, so we can diagnose typos. - - case $ac_dashdash$ac_option in - --) - ac_dashdash=yes ;; - - -bindir | --bindir | --bindi | --bind | --bin | --bi) - ac_prev=bindir ;; - -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) - bindir=$ac_optarg ;; - - -build | --build | --buil | --bui | --bu) - ac_prev=build_alias ;; - -build=* | --build=* | --buil=* | --bui=* | --bu=*) - build_alias=$ac_optarg ;; - - -cache-file | --cache-file | --cache-fil | --cache-fi \ - | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) - ac_prev=cache_file ;; - -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ - | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) - cache_file=$ac_optarg ;; - - --config-cache | -C) - cache_file=config.cache ;; - - -datadir | --datadir | --datadi | --datad) - ac_prev=datadir ;; - -datadir=* | --datadir=* | --datadi=* | --datad=*) - datadir=$ac_optarg ;; - - -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ - | --dataroo | --dataro | --datar) - ac_prev=datarootdir ;; - -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ - | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) - datarootdir=$ac_optarg ;; - - -disable-* | --disable-*) - ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=no ;; - - -docdir | --docdir | --docdi | --doc | --do) - ac_prev=docdir ;; - -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) - docdir=$ac_optarg ;; - - -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) - ac_prev=dvidir ;; - -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) - dvidir=$ac_optarg ;; - - -enable-* | --enable-*) - ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=\$ac_optarg ;; - - -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ - | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ - | --exec | --exe | --ex) - ac_prev=exec_prefix ;; - -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ - | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ - | --exec=* | --exe=* | --ex=*) - exec_prefix=$ac_optarg ;; - - -gas | --gas | --ga | --g) - # Obsolete; use --with-gas. - with_gas=yes ;; - - -help | --help | --hel | --he | -h) - ac_init_help=long ;; - -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) - ac_init_help=recursive ;; - -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) - ac_init_help=short ;; - - -host | --host | --hos | --ho) - ac_prev=host_alias ;; - -host=* | --host=* | --hos=* | --ho=*) - host_alias=$ac_optarg ;; - - -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) - ac_prev=htmldir ;; - -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ - | --ht=*) - htmldir=$ac_optarg ;; - - -includedir | --includedir | --includedi | --included | --include \ - | --includ | --inclu | --incl | --inc) - ac_prev=includedir ;; - -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ - | --includ=* | --inclu=* | --incl=* | --inc=*) - includedir=$ac_optarg ;; - - -infodir | --infodir | --infodi | --infod | --info | --inf) - ac_prev=infodir ;; - -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) - infodir=$ac_optarg ;; - - -libdir | --libdir | --libdi | --libd) - ac_prev=libdir ;; - -libdir=* | --libdir=* | --libdi=* | --libd=*) - libdir=$ac_optarg ;; - - -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ - | --libexe | --libex | --libe) - ac_prev=libexecdir ;; - -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ - | --libexe=* | --libex=* | --libe=*) - libexecdir=$ac_optarg ;; - - -localedir | --localedir | --localedi | --localed | --locale) - ac_prev=localedir ;; - -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) - localedir=$ac_optarg ;; - - -localstatedir | --localstatedir | --localstatedi | --localstated \ - | --localstate | --localstat | --localsta | --localst | --locals) - ac_prev=localstatedir ;; - -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ - | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) - localstatedir=$ac_optarg ;; - - -mandir | --mandir | --mandi | --mand | --man | --ma | --m) - ac_prev=mandir ;; - -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) - mandir=$ac_optarg ;; - - -nfp | --nfp | --nf) - # Obsolete; use --without-fp. - with_fp=no ;; - - -no-create | --no-create | --no-creat | --no-crea | --no-cre \ - | --no-cr | --no-c | -n) - no_create=yes ;; - - -no-recursion | --no-recursion | --no-recursio | --no-recursi \ - | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) - no_recursion=yes ;; - - -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ - | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ - | --oldin | --oldi | --old | --ol | --o) - ac_prev=oldincludedir ;; - -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ - | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ - | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) - oldincludedir=$ac_optarg ;; - - -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) - ac_prev=prefix ;; - -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) - prefix=$ac_optarg ;; - - -program-prefix | --program-prefix | --program-prefi | --program-pref \ - | --program-pre | --program-pr | --program-p) - ac_prev=program_prefix ;; - -program-prefix=* | --program-prefix=* | --program-prefi=* \ - | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) - program_prefix=$ac_optarg ;; - - -program-suffix | --program-suffix | --program-suffi | --program-suff \ - | --program-suf | --program-su | --program-s) - ac_prev=program_suffix ;; - -program-suffix=* | --program-suffix=* | --program-suffi=* \ - | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) - program_suffix=$ac_optarg ;; - - -program-transform-name | --program-transform-name \ - | --program-transform-nam | --program-transform-na \ - | --program-transform-n | --program-transform- \ - | --program-transform | --program-transfor \ - | --program-transfo | --program-transf \ - | --program-trans | --program-tran \ - | --progr-tra | --program-tr | --program-t) - ac_prev=program_transform_name ;; - -program-transform-name=* | --program-transform-name=* \ - | --program-transform-nam=* | --program-transform-na=* \ - | --program-transform-n=* | --program-transform-=* \ - | --program-transform=* | --program-transfor=* \ - | --program-transfo=* | --program-transf=* \ - | --program-trans=* | --program-tran=* \ - | --progr-tra=* | --program-tr=* | --program-t=*) - program_transform_name=$ac_optarg ;; - - -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) - ac_prev=pdfdir ;; - -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) - pdfdir=$ac_optarg ;; - - -psdir | --psdir | --psdi | --psd | --ps) - ac_prev=psdir ;; - -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) - psdir=$ac_optarg ;; - - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - silent=yes ;; - - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) - ac_prev=sbindir ;; - -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ - | --sbi=* | --sb=*) - sbindir=$ac_optarg ;; - - -sharedstatedir | --sharedstatedir | --sharedstatedi \ - | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ - | --sharedst | --shareds | --shared | --share | --shar \ - | --sha | --sh) - ac_prev=sharedstatedir ;; - -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ - | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ - | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ - | --sha=* | --sh=*) - sharedstatedir=$ac_optarg ;; - - -site | --site | --sit) - ac_prev=site ;; - -site=* | --site=* | --sit=*) - site=$ac_optarg ;; - - -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) - ac_prev=srcdir ;; - -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) - srcdir=$ac_optarg ;; - - -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ - | --syscon | --sysco | --sysc | --sys | --sy) - ac_prev=sysconfdir ;; - -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ - | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) - sysconfdir=$ac_optarg ;; - - -target | --target | --targe | --targ | --tar | --ta | --t) - ac_prev=target_alias ;; - -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) - target_alias=$ac_optarg ;; - - -v | -verbose | --verbose | --verbos | --verbo | --verb) - verbose=yes ;; - - -version | --version | --versio | --versi | --vers | -V) - ac_init_version=: ;; - - -with-* | --with-*) - ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=\$ac_optarg ;; - - -without-* | --without-*) - ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=no ;; - - --x) - # Obsolete; use --with-x. - with_x=yes ;; - - -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ - | --x-incl | --x-inc | --x-in | --x-i) - ac_prev=x_includes ;; - -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ - | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) - x_includes=$ac_optarg ;; - - -x-libraries | --x-libraries | --x-librarie | --x-librari \ - | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) - ac_prev=x_libraries ;; - -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ - | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) - x_libraries=$ac_optarg ;; - - -*) as_fn_error $? "unrecognized option: \`$ac_option' -Try \`$0 --help' for more information" - ;; - - *=*) - ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` - # Reject names that are not valid shell variable names. - case $ac_envvar in #( - '' | [0-9]* | *[!_$as_cr_alnum]* ) - as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; - esac - eval $ac_envvar=\$ac_optarg - export $ac_envvar ;; - - *) - # FIXME: should be removed in autoconf 3.0. - $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 - expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && - $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 - : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" - ;; - - esac -done - -if test -n "$ac_prev"; then - ac_option=--`echo $ac_prev | sed 's/_/-/g'` - as_fn_error $? "missing argument to $ac_option" -fi - -if test -n "$ac_unrecognized_opts"; then - case $enable_option_checking in - no) ;; - fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; - *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; - esac -fi - -# Check all directory arguments for consistency. -for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ - datadir sysconfdir sharedstatedir localstatedir includedir \ - oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir -do - eval ac_val=\$$ac_var - # Remove trailing slashes. - case $ac_val in - */ ) - ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` - eval $ac_var=\$ac_val;; - esac - # Be sure to have absolute directory names. - case $ac_val in - [\\/$]* | ?:[\\/]* ) continue;; - NONE | '' ) case $ac_var in *prefix ) continue;; esac;; - esac - as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" -done - -# There might be people who depend on the old broken behavior: `$host' -# used to hold the argument of --host etc. -# FIXME: To remove some day. -build=$build_alias -host=$host_alias -target=$target_alias - -# FIXME: To remove some day. -if test "x$host_alias" != x; then - if test "x$build_alias" = x; then - cross_compiling=maybe - elif test "x$build_alias" != "x$host_alias"; then - cross_compiling=yes - fi -fi - -ac_tool_prefix= -test -n "$host_alias" && ac_tool_prefix=$host_alias- - -test "$silent" = yes && exec 6>/dev/null - - -ac_pwd=`pwd` && test -n "$ac_pwd" && -ac_ls_di=`ls -di .` && -ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || - as_fn_error $? "working directory cannot be determined" -test "X$ac_ls_di" = "X$ac_pwd_ls_di" || - as_fn_error $? "pwd does not report name of working directory" - - -# Find the source files, if location was not specified. -if test -z "$srcdir"; then - ac_srcdir_defaulted=yes - # Try the directory containing this script, then the parent directory. - ac_confdir=`$as_dirname -- "$as_myself" || -$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_myself" : 'X\(//\)[^/]' \| \ - X"$as_myself" : 'X\(//\)$' \| \ - X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_myself" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - srcdir=$ac_confdir - if test ! -r "$srcdir/$ac_unique_file"; then - srcdir=.. - fi -else - ac_srcdir_defaulted=no -fi -if test ! -r "$srcdir/$ac_unique_file"; then - test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." - as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" -fi -ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" -ac_abs_confdir=`( - cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" - pwd)` -# When building in place, set srcdir=. -if test "$ac_abs_confdir" = "$ac_pwd"; then - srcdir=. -fi -# Remove unnecessary trailing slashes from srcdir. -# Double slashes in file names in object file debugging info -# mess up M-x gdb in Emacs. -case $srcdir in -*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; -esac -for ac_var in $ac_precious_vars; do - eval ac_env_${ac_var}_set=\${${ac_var}+set} - eval ac_env_${ac_var}_value=\$${ac_var} - eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} - eval ac_cv_env_${ac_var}_value=\$${ac_var} -done - -# -# Report the --help message. -# -if test "$ac_init_help" = "long"; then - # Omit some internal or obsolete options to make the list less imposing. - # This message is too long to be a string in the A/UX 3.1 sh. - cat <<_ACEOF -\`configure' configures faiss 1.0 to adapt to many kinds of systems. - -Usage: $0 [OPTION]... [VAR=VALUE]... - -To assign environment variables (e.g., CC, CFLAGS...), specify them as -VAR=VALUE. See below for descriptions of some of the useful variables. - -Defaults for the options are specified in brackets. - -Configuration: - -h, --help display this help and exit - --help=short display options specific to this package - --help=recursive display the short help of all the included packages - -V, --version display version information and exit - -q, --quiet, --silent do not print \`checking ...' messages - --cache-file=FILE cache test results in FILE [disabled] - -C, --config-cache alias for \`--cache-file=config.cache' - -n, --no-create do not create output files - --srcdir=DIR find the sources in DIR [configure dir or \`..'] - -Installation directories: - --prefix=PREFIX install architecture-independent files in PREFIX - [$ac_default_prefix] - --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX - [PREFIX] - -By default, \`make install' will install all the files in -\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify -an installation prefix other than \`$ac_default_prefix' using \`--prefix', -for instance \`--prefix=\$HOME'. - -For better control, use the options below. - -Fine tuning of the installation directories: - --bindir=DIR user executables [EPREFIX/bin] - --sbindir=DIR system admin executables [EPREFIX/sbin] - --libexecdir=DIR program executables [EPREFIX/libexec] - --sysconfdir=DIR read-only single-machine data [PREFIX/etc] - --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] - --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --libdir=DIR object code libraries [EPREFIX/lib] - --includedir=DIR C header files [PREFIX/include] - --oldincludedir=DIR C header files for non-gcc [/usr/include] - --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] - --datadir=DIR read-only architecture-independent data [DATAROOTDIR] - --infodir=DIR info documentation [DATAROOTDIR/info] - --localedir=DIR locale-dependent data [DATAROOTDIR/locale] - --mandir=DIR man documentation [DATAROOTDIR/man] - --docdir=DIR documentation root [DATAROOTDIR/doc/faiss] - --htmldir=DIR html documentation [DOCDIR] - --dvidir=DIR dvi documentation [DOCDIR] - --pdfdir=DIR pdf documentation [DOCDIR] - --psdir=DIR ps documentation [DOCDIR] -_ACEOF - - cat <<\_ACEOF - -System types: - --build=BUILD configure for building on BUILD [guessed] - --host=HOST cross-compile to build programs to run on HOST [BUILD] - --target=TARGET configure for building compilers for TARGET [HOST] -_ACEOF -fi - -if test -n "$ac_init_help"; then - case $ac_init_help in - short | recursive ) echo "Configuration of faiss 1.0:";; - esac - cat <<\_ACEOF - -Optional Features: - --disable-option-checking ignore unrecognized --enable/--with options - --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) - --enable-FEATURE[=ARG] include FEATURE [ARG=yes] - --disable-openmp do not use OpenMP - -Optional Packages: - --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] - --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) - --with-python= use Python binary - --with-swig= use SWIG binary - --with-cuda= prefix of the CUDA installation - --with-cuda-arch= - device specific -gencode flags - --with-blas= use BLAS library - --with-lapack= use LAPACK library - -Some influential environment variables: - CXX C++ compiler command - CXXFLAGS C++ compiler flags - LDFLAGS linker flags, e.g. -L if you have libraries in a - nonstandard directory - LIBS libraries to pass to the linker, e.g. -l - CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if - you have headers in a nonstandard directory - CC C compiler command - CFLAGS C compiler flags - CPP C preprocessor - CXXCPP C++ preprocessor - -Use these variables to override the choices made by `configure' or to help -it to find libraries and programs with nonstandard names/locations. - -Report bugs to the package provider. -_ACEOF -ac_status=$? -fi - -if test "$ac_init_help" = "recursive"; then - # If there are subdirs, report their specific --help. - for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue - test -d "$ac_dir" || - { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || - continue - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - cd "$ac_dir" || { ac_status=$?; continue; } - # Check for guested configure. - if test -f "$ac_srcdir/configure.gnu"; then - echo && - $SHELL "$ac_srcdir/configure.gnu" --help=recursive - elif test -f "$ac_srcdir/configure"; then - echo && - $SHELL "$ac_srcdir/configure" --help=recursive - else - $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 - fi || ac_status=$? - cd "$ac_pwd" || { ac_status=$?; break; } - done -fi - -test -n "$ac_init_help" && exit $ac_status -if $ac_init_version; then - cat <<\_ACEOF -faiss configure 1.0 -generated by GNU Autoconf 2.69 - -Copyright (C) 2012 Free Software Foundation, Inc. -This configure script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it. - -Copyright (c) Facebook, Inc. and its affiliates. - -This source code is licensed under the MIT license found in the -LICENSE file in the root directory of this source tree. -_ACEOF - exit -fi - -## ------------------------ ## -## Autoconf initialization. ## -## ------------------------ ## - -# ac_fn_cxx_try_compile LINENO -# ---------------------------- -# Try to compile conftest.$ac_ext, and return whether this succeeded. -ac_fn_cxx_try_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext - if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_cxx_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_cxx_try_compile - -# ac_fn_c_try_compile LINENO -# -------------------------- -# Try to compile conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext - if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_compile - -# ac_fn_c_try_cpp LINENO -# ---------------------- -# Try to preprocess conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_cpp () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } > conftest.i && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_cpp - -# ac_fn_cxx_try_cpp LINENO -# ------------------------ -# Try to preprocess conftest.$ac_ext, and return whether this succeeded. -ac_fn_cxx_try_cpp () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } > conftest.i && { - test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || - test ! -s conftest.err - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_cxx_try_cpp - -# ac_fn_cxx_check_header_mongrel LINENO HEADER VAR INCLUDES -# --------------------------------------------------------- -# Tests whether HEADER exists, giving a warning if it cannot be compiled using -# the include files in INCLUDES and setting the cache variable VAR -# accordingly. -ac_fn_cxx_check_header_mongrel () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if eval \${$3+:} false; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -else - # Is the header compilable? -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 -$as_echo_n "checking $2 usability... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -#include <$2> -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_header_compiler=yes -else - ac_header_compiler=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 -$as_echo "$ac_header_compiler" >&6; } - -# Is the header present? -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 -$as_echo_n "checking $2 presence... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include <$2> -_ACEOF -if ac_fn_cxx_try_cpp "$LINENO"; then : - ac_header_preproc=yes -else - ac_header_preproc=no -fi -rm -f conftest.err conftest.i conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 -$as_echo "$ac_header_preproc" >&6; } - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in #(( - yes:no: ) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 -$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 -$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} - ;; - no:yes:* ) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 -$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 -$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 -$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 -$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 -$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} - ;; -esac - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - eval "$3=\$ac_header_compiler" -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_cxx_check_header_mongrel - -# ac_fn_cxx_try_run LINENO -# ------------------------ -# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes -# that executables *can* be run. -ac_fn_cxx_try_run () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then : - ac_retval=0 -else - $as_echo "$as_me: program exited with status $ac_status" >&5 - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=$ac_status -fi - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_cxx_try_run - -# ac_fn_cxx_check_header_compile LINENO HEADER VAR INCLUDES -# --------------------------------------------------------- -# Tests whether HEADER exists and can be compiled using the include files in -# INCLUDES, setting the cache variable VAR accordingly. -ac_fn_cxx_check_header_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -#include <$2> -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - eval "$3=yes" -else - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_cxx_check_header_compile - -# ac_fn_cxx_try_link LINENO -# ------------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. -ac_fn_cxx_try_link () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext conftest$ac_exeext - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_cxx_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && { - test "$cross_compiling" = yes || - test -x conftest$ac_exeext - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information - # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would - # interfere with the next link command; also delete a directory that is - # left behind by Apple's compiler. We do this before executing the actions. - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_cxx_try_link - -# ac_fn_cxx_check_type LINENO TYPE VAR INCLUDES -# --------------------------------------------- -# Tests whether TYPE exists after having included INCLUDES, setting cache -# variable VAR accordingly. -ac_fn_cxx_check_type () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - eval "$3=no" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -if (sizeof ($2)) - return 0; - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -if (sizeof (($2))) - return 0; - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - -else - eval "$3=yes" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_cxx_check_type - -# ac_fn_c_find_intX_t LINENO BITS VAR -# ----------------------------------- -# Finds a signed integer type with width BITS, setting cache variable VAR -# accordingly. -ac_fn_c_find_intX_t () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5 -$as_echo_n "checking for int$2_t... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - eval "$3=no" - # Order is important - never check a type that is potentially smaller - # than half of the expected target width. - for ac_type in int$2_t 'int' 'long int' \ - 'long long int' 'short int' 'signed char'; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes_default - enum { N = $2 / 2 - 1 }; -int -main () -{ -static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))]; -test_array [0] = 0; -return test_array [0]; - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes_default - enum { N = $2 / 2 - 1 }; -int -main () -{ -static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1) - < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))]; -test_array [0] = 0; -return test_array [0]; - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - -else - case $ac_type in #( - int$2_t) : - eval "$3=yes" ;; #( - *) : - eval "$3=\$ac_type" ;; -esac -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - if eval test \"x\$"$3"\" = x"no"; then : - -else - break -fi - done -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_find_intX_t - -# ac_fn_c_find_uintX_t LINENO BITS VAR -# ------------------------------------ -# Finds an unsigned integer type with width BITS, setting cache variable VAR -# accordingly. -ac_fn_c_find_uintX_t () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 -$as_echo_n "checking for uint$2_t... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - eval "$3=no" - # Order is important - never check a type that is potentially smaller - # than half of the expected target width. - for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \ - 'unsigned long long int' 'unsigned short int' 'unsigned char'; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes_default -int -main () -{ -static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; -test_array [0] = 0; -return test_array [0]; - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - case $ac_type in #( - uint$2_t) : - eval "$3=yes" ;; #( - *) : - eval "$3=\$ac_type" ;; -esac -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - if eval test \"x\$"$3"\" = x"no"; then : - -else - break -fi - done -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_find_uintX_t - -# ac_fn_cxx_check_func LINENO FUNC VAR -# ------------------------------------ -# Tests whether FUNC exists, setting the cache variable VAR accordingly -ac_fn_cxx_check_func () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -/* Define $2 to an innocuous variant, in case declares $2. - For example, HP-UX 11i declares gettimeofday. */ -#define $2 innocuous_$2 - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $2 (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef $2 - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $2 (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined __stub_$2 || defined __stub___$2 -choke me -#endif - -int -main () -{ -return $2 (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$3=yes" -else - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_cxx_check_func -cat >config.log <<_ACEOF -This file contains any messages produced by compilers while -running configure, to aid debugging if configure makes a mistake. - -It was created by faiss $as_me 1.0, which was -generated by GNU Autoconf 2.69. Invocation command line was - - $ $0 $@ - -_ACEOF -exec 5>>config.log -{ -cat <<_ASUNAME -## --------- ## -## Platform. ## -## --------- ## - -hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` - -/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` -/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` -/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` -/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` - -_ASUNAME - -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - $as_echo "PATH: $as_dir" - done -IFS=$as_save_IFS - -} >&5 - -cat >&5 <<_ACEOF - - -## ----------- ## -## Core tests. ## -## ----------- ## - -_ACEOF - - -# Keep a trace of the command line. -# Strip out --no-create and --no-recursion so they do not pile up. -# Strip out --silent because we don't want to record it for future runs. -# Also quote any args containing shell meta-characters. -# Make two passes to allow for proper duplicate-argument suppression. -ac_configure_args= -ac_configure_args0= -ac_configure_args1= -ac_must_keep_next=false -for ac_pass in 1 2 -do - for ac_arg - do - case $ac_arg in - -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - continue ;; - *\'*) - ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - case $ac_pass in - 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; - 2) - as_fn_append ac_configure_args1 " '$ac_arg'" - if test $ac_must_keep_next = true; then - ac_must_keep_next=false # Got value, back to normal. - else - case $ac_arg in - *=* | --config-cache | -C | -disable-* | --disable-* \ - | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ - | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ - | -with-* | --with-* | -without-* | --without-* | --x) - case "$ac_configure_args0 " in - "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; - esac - ;; - -* ) ac_must_keep_next=true ;; - esac - fi - as_fn_append ac_configure_args " '$ac_arg'" - ;; - esac - done -done -{ ac_configure_args0=; unset ac_configure_args0;} -{ ac_configure_args1=; unset ac_configure_args1;} - -# When interrupted or exit'd, cleanup temporary files, and complete -# config.log. We remove comments because anyway the quotes in there -# would cause problems or look ugly. -# WARNING: Use '\'' to represent an apostrophe within the trap. -# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. -trap 'exit_status=$? - # Save into config.log some information that might help in debugging. - { - echo - - $as_echo "## ---------------- ## -## Cache variables. ## -## ---------------- ##" - echo - # The following way of writing the cache mishandles newlines in values, -( - for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - (set) 2>&1 | - case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - sed -n \ - "s/'\''/'\''\\\\'\'''\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" - ;; #( - *) - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) - echo - - $as_echo "## ----------------- ## -## Output variables. ## -## ----------------- ##" - echo - for ac_var in $ac_subst_vars - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - $as_echo "$ac_var='\''$ac_val'\''" - done | sort - echo - - if test -n "$ac_subst_files"; then - $as_echo "## ------------------- ## -## File substitutions. ## -## ------------------- ##" - echo - for ac_var in $ac_subst_files - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - $as_echo "$ac_var='\''$ac_val'\''" - done | sort - echo - fi - - if test -s confdefs.h; then - $as_echo "## ----------- ## -## confdefs.h. ## -## ----------- ##" - echo - cat confdefs.h - echo - fi - test "$ac_signal" != 0 && - $as_echo "$as_me: caught signal $ac_signal" - $as_echo "$as_me: exit $exit_status" - } >&5 - rm -f core *.core core.conftest.* && - rm -f -r conftest* confdefs* conf$$* $ac_clean_files && - exit $exit_status -' 0 -for ac_signal in 1 2 13 15; do - trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal -done -ac_signal=0 - -# confdefs.h avoids OS command line length limits that DEFS can exceed. -rm -f -r conftest* confdefs.h - -$as_echo "/* confdefs.h */" > confdefs.h - -# Predefined preprocessor variables. - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_NAME "$PACKAGE_NAME" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_TARNAME "$PACKAGE_TARNAME" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_VERSION "$PACKAGE_VERSION" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_STRING "$PACKAGE_STRING" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_URL "$PACKAGE_URL" -_ACEOF - - -# Let the site file select an alternate cache file if it wants to. -# Prefer an explicitly selected file to automatically selected ones. -ac_site_file1=NONE -ac_site_file2=NONE -if test -n "$CONFIG_SITE"; then - # We do not want a PATH search for config.site. - case $CONFIG_SITE in #(( - -*) ac_site_file1=./$CONFIG_SITE;; - */*) ac_site_file1=$CONFIG_SITE;; - *) ac_site_file1=./$CONFIG_SITE;; - esac -elif test "x$prefix" != xNONE; then - ac_site_file1=$prefix/share/config.site - ac_site_file2=$prefix/etc/config.site -else - ac_site_file1=$ac_default_prefix/share/config.site - ac_site_file2=$ac_default_prefix/etc/config.site -fi -for ac_site_file in "$ac_site_file1" "$ac_site_file2" -do - test "x$ac_site_file" = xNONE && continue - if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 -$as_echo "$as_me: loading site script $ac_site_file" >&6;} - sed 's/^/| /' "$ac_site_file" >&5 - . "$ac_site_file" \ - || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "failed to load site script $ac_site_file -See \`config.log' for more details" "$LINENO" 5; } - fi -done - -if test -r "$cache_file"; then - # Some versions of bash will fail to source /dev/null (special files - # actually), so we avoid doing that. DJGPP emulates it as a regular file. - if test /dev/null != "$cache_file" && test -f "$cache_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 -$as_echo "$as_me: loading cache $cache_file" >&6;} - case $cache_file in - [\\/]* | ?:[\\/]* ) . "$cache_file";; - *) . "./$cache_file";; - esac - fi -else - { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 -$as_echo "$as_me: creating cache $cache_file" >&6;} - >$cache_file -fi - -as_fn_append ac_header_list " stdlib.h" -as_fn_append ac_header_list " unistd.h" -as_fn_append ac_header_list " sys/param.h" -# Check that the precious variables saved in the cache have kept the same -# value. -ac_cache_corrupted=false -for ac_var in $ac_precious_vars; do - eval ac_old_set=\$ac_cv_env_${ac_var}_set - eval ac_new_set=\$ac_env_${ac_var}_set - eval ac_old_val=\$ac_cv_env_${ac_var}_value - eval ac_new_val=\$ac_env_${ac_var}_value - case $ac_old_set,$ac_new_set in - set,) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 -$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,set) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 -$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,);; - *) - if test "x$ac_old_val" != "x$ac_new_val"; then - # differences in whitespace do not lead to failure. - ac_old_val_w=`echo x $ac_old_val` - ac_new_val_w=`echo x $ac_new_val` - if test "$ac_old_val_w" != "$ac_new_val_w"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 -$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} - ac_cache_corrupted=: - else - { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 -$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} - eval $ac_var=\$ac_old_val - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 -$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 -$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} - fi;; - esac - # Pass precious variables to config.status. - if test "$ac_new_set" = set; then - case $ac_new_val in - *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; - *) ac_arg=$ac_var=$ac_new_val ;; - esac - case " $ac_configure_args " in - *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. - *) as_fn_append ac_configure_args " '$ac_arg'" ;; - esac - fi -done -if $ac_cache_corrupted; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 -$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} - as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 -fi -## -------------------- ## -## Main body of script. ## -## -------------------- ## - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - - - -ac_aux_dir= -for ac_dir in build-aux "$srcdir"/build-aux; do - if test -f "$ac_dir/install-sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install-sh -c" - break - elif test -f "$ac_dir/install.sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install.sh -c" - break - elif test -f "$ac_dir/shtool"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/shtool install -c" - break - fi -done -if test -z "$ac_aux_dir"; then - as_fn_error $? "cannot find install-sh, install.sh, or shtool in build-aux \"$srcdir\"/build-aux" "$LINENO" 5 -fi - -# These three variables are undocumented and unsupported, -# and are intended to be withdrawn in a future Autoconf release. -# They can cause serious problems if a builder's source tree is in a directory -# whose full name contains unusual characters. -ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. -ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. -ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. - - - - -: ${CXXFLAGS="-g -O3 -Wall -Wextra"} - -# Checks for programs. -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu -if test -z "$CXX"; then - if test -n "$CCC"; then - CXX=$CCC - else - if test -n "$ac_tool_prefix"; then - for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CXX"; then - ac_cv_prog_CXX="$CXX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CXX=$ac_cv_prog_CXX -if test -n "$CXX"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 -$as_echo "$CXX" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CXX" && break - done -fi -if test -z "$CXX"; then - ac_ct_CXX=$CXX - for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CXX"; then - ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CXX="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CXX=$ac_cv_prog_ac_ct_CXX -if test -n "$ac_ct_CXX"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 -$as_echo "$ac_ct_CXX" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_CXX" && break -done - - if test "x$ac_ct_CXX" = x; then - CXX="g++" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CXX=$ac_ct_CXX - fi -fi - - fi -fi -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done - -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" -# Try to create an executable without -o first, disregard a.out. -# It will help us diagnose broken compilers, and finding out an intuition -# of exeext. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5 -$as_echo_n "checking whether the C++ compiler works... " >&6; } -ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` - -# The possible output files: -ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" - -ac_rmfiles= -for ac_file in $ac_files -do - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - * ) ac_rmfiles="$ac_rmfiles $ac_file";; - esac -done -rm -f $ac_rmfiles - -if { { ac_try="$ac_link_default" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link_default") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. -# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' -# in a Makefile. We should not override ac_cv_exeext if it was cached, -# so that the user can short-circuit this test for compilers unknown to -# Autoconf. -for ac_file in $ac_files '' -do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) - ;; - [ab].out ) - # We found the default executable, but exeext='' is most - # certainly right. - break;; - *.* ) - if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; - then :; else - ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - fi - # We set ac_cv_exeext here because the later test for it is not - # safe: cross compilers may not add the suffix if given an `-o' - # argument, so we may need to know it at that point already. - # Even if this section looks crufty: it has the advantage of - # actually working. - break;; - * ) - break;; - esac -done -test "$ac_cv_exeext" = no && ac_cv_exeext= - -else - ac_file='' -fi -if test -z "$ac_file"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -$as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "C++ compiler cannot create executables -See \`config.log' for more details" "$LINENO" 5; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5 -$as_echo_n "checking for C++ compiler default output file name... " >&6; } -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 -$as_echo "$ac_file" >&6; } -ac_exeext=$ac_cv_exeext - -rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out -ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 -$as_echo_n "checking for suffix of executables... " >&6; } -if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - # If both `conftest.exe' and `conftest' are `present' (well, observable) -# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will -# work properly (i.e., refer to `conftest.exe'), while it won't with -# `rm'. -for ac_file in conftest.exe conftest conftest.*; do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - break;; - * ) break;; - esac -done -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest conftest$ac_cv_exeext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 -$as_echo "$ac_cv_exeext" >&6; } - -rm -f conftest.$ac_ext -EXEEXT=$ac_cv_exeext -ac_exeext=$EXEEXT -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -FILE *f = fopen ("conftest.out", "w"); - return ferror (f) || fclose (f) != 0; - - ; - return 0; -} -_ACEOF -ac_clean_files="$ac_clean_files conftest.out" -# Check that the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 -$as_echo_n "checking whether we are cross compiling... " >&6; } -if test "$cross_compiling" != yes; then - { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - if { ac_try='./conftest$ac_cv_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then - cross_compiling=no - else - if test "$cross_compiling" = maybe; then - cross_compiling=yes - else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot run C++ compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details" "$LINENO" 5; } - fi - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 -$as_echo "$cross_compiling" >&6; } - -rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out -ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 -$as_echo_n "checking for suffix of object files... " >&6; } -if ${ac_cv_objext+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.o conftest.obj -if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - for ac_file in conftest.o conftest.obj conftest.*; do - test -f "$ac_file" || continue; - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; - *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` - break;; - esac -done -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of object files: cannot compile -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest.$ac_cv_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 -$as_echo "$ac_cv_objext" >&6; } -OBJEXT=$ac_cv_objext -ac_objext=$OBJEXT -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 -$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } -if ${ac_cv_cxx_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_cxx_compiler_gnu=$ac_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 -$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GXX=yes -else - GXX= -fi -ac_test_CXXFLAGS=${CXXFLAGS+set} -ac_save_CXXFLAGS=$CXXFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 -$as_echo_n "checking whether $CXX accepts -g... " >&6; } -if ${ac_cv_prog_cxx_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_cxx_werror_flag=$ac_cxx_werror_flag - ac_cxx_werror_flag=yes - ac_cv_prog_cxx_g=no - CXXFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cv_prog_cxx_g=yes -else - CXXFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - -else - ac_cxx_werror_flag=$ac_save_cxx_werror_flag - CXXFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cv_prog_cxx_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_cxx_werror_flag=$ac_save_cxx_werror_flag -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 -$as_echo "$ac_cv_prog_cxx_g" >&6; } -if test "$ac_test_CXXFLAGS" = set; then - CXXFLAGS=$ac_save_CXXFLAGS -elif test $ac_cv_prog_cxx_g = yes; then - if test "$GXX" = yes; then - CXXFLAGS="-g -O2" - else - CXXFLAGS="-g" - fi -else - if test "$GXX" = yes; then - CXXFLAGS="-O2" - else - CXXFLAGS= - fi -fi -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - - ax_cxx_compile_alternatives="11 0x" ax_cxx_compile_cxx11_required=true - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - ac_success=no - - - - if test x$ac_success = xno; then - for alternative in ${ax_cxx_compile_alternatives}; do - for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do - cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh` - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5 -$as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; } -if eval \${$cachevar+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_CXX="$CXX" - CXX="$CXX $switch" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - -// If the compiler admits that it is not ready for C++11, why torture it? -// Hopefully, this will speed up the test. - -#ifndef __cplusplus - -#error "This is not a C++ compiler" - -#elif __cplusplus < 201103L - -#error "This is not a C++11 compiler" - -#else - -namespace cxx11 -{ - - namespace test_static_assert - { - - template - struct check - { - static_assert(sizeof(int) <= sizeof(T), "not big enough"); - }; - - } - - namespace test_final_override - { - - struct Base - { - virtual void f() {} - }; - - struct Derived : public Base - { - virtual void f() override {} - }; - - } - - namespace test_double_right_angle_brackets - { - - template < typename T > - struct check {}; - - typedef check single_type; - typedef check> double_type; - typedef check>> triple_type; - typedef check>>> quadruple_type; - - } - - namespace test_decltype - { - - int - f() - { - int a = 1; - decltype(a) b = 2; - return a + b; - } - - } - - namespace test_type_deduction - { - - template < typename T1, typename T2 > - struct is_same - { - static const bool value = false; - }; - - template < typename T > - struct is_same - { - static const bool value = true; - }; - - template < typename T1, typename T2 > - auto - add(T1 a1, T2 a2) -> decltype(a1 + a2) - { - return a1 + a2; - } - - int - test(const int c, volatile int v) - { - static_assert(is_same::value == true, ""); - static_assert(is_same::value == false, ""); - static_assert(is_same::value == false, ""); - auto ac = c; - auto av = v; - auto sumi = ac + av + 'x'; - auto sumf = ac + av + 1.0; - static_assert(is_same::value == true, ""); - static_assert(is_same::value == true, ""); - static_assert(is_same::value == true, ""); - static_assert(is_same::value == false, ""); - static_assert(is_same::value == true, ""); - return (sumf > 0.0) ? sumi : add(c, v); - } - - } - - namespace test_noexcept - { - - int f() { return 0; } - int g() noexcept { return 0; } - - static_assert(noexcept(f()) == false, ""); - static_assert(noexcept(g()) == true, ""); - - } - - namespace test_constexpr - { - - template < typename CharT > - unsigned long constexpr - strlen_c_r(const CharT *const s, const unsigned long acc) noexcept - { - return *s ? strlen_c_r(s + 1, acc + 1) : acc; - } - - template < typename CharT > - unsigned long constexpr - strlen_c(const CharT *const s) noexcept - { - return strlen_c_r(s, 0UL); - } - - static_assert(strlen_c("") == 0UL, ""); - static_assert(strlen_c("1") == 1UL, ""); - static_assert(strlen_c("example") == 7UL, ""); - static_assert(strlen_c("another\0example") == 7UL, ""); - - } - - namespace test_rvalue_references - { - - template < int N > - struct answer - { - static constexpr int value = N; - }; - - answer<1> f(int&) { return answer<1>(); } - answer<2> f(const int&) { return answer<2>(); } - answer<3> f(int&&) { return answer<3>(); } - - void - test() - { - int i = 0; - const int c = 0; - static_assert(decltype(f(i))::value == 1, ""); - static_assert(decltype(f(c))::value == 2, ""); - static_assert(decltype(f(0))::value == 3, ""); - } - - } - - namespace test_uniform_initialization - { - - struct test - { - static const int zero {}; - static const int one {1}; - }; - - static_assert(test::zero == 0, ""); - static_assert(test::one == 1, ""); - - } - - namespace test_lambdas - { - - void - test1() - { - auto lambda1 = [](){}; - auto lambda2 = lambda1; - lambda1(); - lambda2(); - } - - int - test2() - { - auto a = [](int i, int j){ return i + j; }(1, 2); - auto b = []() -> int { return '0'; }(); - auto c = [=](){ return a + b; }(); - auto d = [&](){ return c; }(); - auto e = [a, &b](int x) mutable { - const auto identity = [](int y){ return y; }; - for (auto i = 0; i < a; ++i) - a += b--; - return x + identity(a + b); - }(0); - return a + b + c + d + e; - } - - int - test3() - { - const auto nullary = [](){ return 0; }; - const auto unary = [](int x){ return x; }; - using nullary_t = decltype(nullary); - using unary_t = decltype(unary); - const auto higher1st = [](nullary_t f){ return f(); }; - const auto higher2nd = [unary](nullary_t f1){ - return [unary, f1](unary_t f2){ return f2(unary(f1())); }; - }; - return higher1st(nullary) + higher2nd(nullary)(unary); - } - - } - - namespace test_variadic_templates - { - - template - struct sum; - - template - struct sum - { - static constexpr auto value = N0 + sum::value; - }; - - template <> - struct sum<> - { - static constexpr auto value = 0; - }; - - static_assert(sum<>::value == 0, ""); - static_assert(sum<1>::value == 1, ""); - static_assert(sum<23>::value == 23, ""); - static_assert(sum<1, 2>::value == 3, ""); - static_assert(sum<5, 5, 11>::value == 21, ""); - static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); - - } - - // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae - // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function - // because of this. - namespace test_template_alias_sfinae - { - - struct foo {}; - - template - using member = typename T::member_type; - - template - void func(...) {} - - template - void func(member*) {} - - void test(); - - void test() { func(0); } - - } - -} // namespace cxx11 - -#endif // __cplusplus >= 201103L - - - -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - eval $cachevar=yes -else - eval $cachevar=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - CXX="$ac_save_CXX" -fi -eval ac_res=\$$cachevar - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - if eval test x\$$cachevar = xyes; then - CXX="$CXX $switch" - if test -n "$CXXCPP" ; then - CXXCPP="$CXXCPP $switch" - fi - ac_success=yes - break - fi - done - if test x$ac_success = xyes; then - break - fi - done - fi - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - if test x$ax_cxx_compile_cxx11_required = xtrue; then - if test x$ac_success = xno; then - as_fn_error $? "*** A compiler with support for C++11 language features is required." "$LINENO" 5 - fi - fi - if test x$ac_success = xno; then - HAVE_CXX11=0 - { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++11 support was found" >&5 -$as_echo "$as_me: No compiler with C++11 support was found" >&6;} - else - HAVE_CXX11=1 - -$as_echo "#define HAVE_CXX11 1" >>confdefs.h - - fi - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -else - CC="$ac_cv_prog_CC" -fi - -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - fi -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" - fi -fi -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl.exe - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl.exe -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_CC" && break -done - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -fi - -fi - - -test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } - -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 -$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if ${ac_cv_c_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 -$as_echo "$ac_cv_c_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GCC=yes -else - GCC= -fi -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 -$as_echo_n "checking whether $CC accepts -g... " >&6; } -if ${ac_cv_prog_cc_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_c_werror_flag=$ac_c_werror_flag - ac_c_werror_flag=yes - ac_cv_prog_cc_g=no - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -else - CFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - -else - ac_c_werror_flag=$ac_save_c_werror_flag - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 -$as_echo "$ac_cv_prog_cc_g" >&6; } -if test "$ac_test_CFLAGS" = set; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 -$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } -if ${ac_cv_prog_cc_c89+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_prog_cc_c89=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -struct stat; -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; - -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) 'x' -int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -int -main () -{ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; - ; - return 0; -} -_ACEOF -for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ - -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_c89=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext - test "x$ac_cv_prog_cc_c89" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC - -fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c89" in - x) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -$as_echo "none needed" >&6; } ;; - xno) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -$as_echo "unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c89" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 -$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; -esac -if test "x$ac_cv_prog_cc_c89" != xno; then : - -fi - -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 -$as_echo_n "checking how to run the C preprocessor... " >&6; } -# On Suns, sometimes $CPP names a directory. -if test -n "$CPP" && test -d "$CPP"; then - CPP= -fi -if test -z "$CPP"; then - if ${ac_cv_prog_CPP+:} false; then : - $as_echo_n "(cached) " >&6 -else - # Double quotes because CPP needs to be expanded - for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" - do - ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - break -fi - - done - ac_cv_prog_CPP=$CPP - -fi - CPP=$ac_cv_prog_CPP -else - ac_cv_prog_CPP=$CPP -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 -$as_echo "$CPP" >&6; } -ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } -fi - -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 -$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } -set x ${MAKE-make} -ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` -if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat >conftest.make <<\_ACEOF -SHELL = /bin/sh -all: - @echo '@@@%%%=$(MAKE)=@@@%%%' -_ACEOF -# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. -case `${MAKE-make} -f conftest.make 2>/dev/null` in - *@@@%%%=?*=@@@%%%*) - eval ac_cv_prog_make_${ac_make}_set=yes;; - *) - eval ac_cv_prog_make_${ac_make}_set=no;; -esac -rm -f conftest.make -fi -if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - SET_MAKE= -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - SET_MAKE="MAKE=${MAKE-make}" -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 -$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } -if test -z "$MKDIR_P"; then - if ${ac_cv_path_mkdir+:} false; then : - $as_echo_n "(cached) " >&6 -else - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in mkdir gmkdir; do - for ac_exec_ext in '' $ac_executable_extensions; do - as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue - case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( - 'mkdir (GNU coreutils) '* | \ - 'mkdir (coreutils) '* | \ - 'mkdir (fileutils) '4.1*) - ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext - break 3;; - esac - done - done - done -IFS=$as_save_IFS - -fi - - test -d ./--version && rmdir ./--version - if test "${ac_cv_path_mkdir+set}" = set; then - MKDIR_P="$ac_cv_path_mkdir -p" - else - # As a last resort, use the slow shell script. Don't cache a - # value for MKDIR_P within a source directory, because that will - # break other packages using the cache if that directory is - # removed, or if the value is a relative name. - MKDIR_P="$ac_install_sh -d" - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 -$as_echo "$MKDIR_P" >&6; } - - - - - -# Check whether --with-python was given. -if test "${with_python+set}" = set; then : - withval=$with_python; -fi - -case $with_python in - "") PYTHON_BIN=python ;; - *) PYTHON_BIN="$with_python" -esac - -# Extract the first word of "$PYTHON_BIN", so it can be a program name with args. -set dummy $PYTHON_BIN; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_PYTHON+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$PYTHON"; then - ac_cv_prog_PYTHON="$PYTHON" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_PYTHON="$PYTHON_BIN" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -PYTHON=$ac_cv_prog_PYTHON -if test -n "$PYTHON"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PYTHON" >&5 -$as_echo "$PYTHON" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fa_python_bin=$PYTHON - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python C flags" >&5 -$as_echo_n "checking for Python C flags... " >&6; } -fa_python_cflags=`$PYTHON -c " -import sysconfig -paths = ['-I' + sysconfig.get_path(p) for p in ['include', 'platinclude']] -print(' '.join(paths))"` -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $fa_python_cflags" >&5 -$as_echo "$fa_python_cflags" >&6; } -PYTHON_CFLAGS="$PYTHON_CFLAGS $fa_python_cflags" - - - - -if test x$PYTHON != x; then - - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for numpy headers path" >&5 -$as_echo_n "checking for numpy headers path... " >&6; } - -fa_numpy_headers=`$PYTHON -c "import numpy; print(numpy.get_include())"` - -if test $? == 0; then - if test x$fa_numpy_headers != x; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $fa_numpy_headers" >&5 -$as_echo "$fa_numpy_headers" >&6; } - NUMPY_INCLUDE=$fa_numpy_headers - - else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 -$as_echo "not found" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: You won't be able to build the python interface." >&5 -$as_echo "$as_me: WARNING: You won't be able to build the python interface." >&2;} - fi -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 -$as_echo "not found" >&6; } - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: You won't be able to build the python interface." >&5 -$as_echo "$as_me: WARNING: You won't be able to build the python interface." >&2;} -fi - -fi - - - - -# Check whether --with-swig was given. -if test "${with_swig+set}" = set; then : - withval=$with_swig; -fi - -case $with_swig in - "") # Extract the first word of "swig", so it can be a program name with args. -set dummy swig; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_SWIG+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$SWIG"; then - ac_cv_prog_SWIG="$SWIG" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_SWIG="swig" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -SWIG=$ac_cv_prog_SWIG -if test -n "$SWIG"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SWIG" >&5 -$as_echo "$SWIG" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - -;; - *) SWIG="$with_swig" -esac - - - - -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 -$as_echo_n "checking how to run the C++ preprocessor... " >&6; } -if test -z "$CXXCPP"; then - if ${ac_cv_prog_CXXCPP+:} false; then : - $as_echo_n "(cached) " >&6 -else - # Double quotes because CXXCPP needs to be expanded - for CXXCPP in "$CXX -E" "/lib/cpp" - do - ac_preproc_ok=false -for ac_cxx_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_cxx_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_cxx_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - break -fi - - done - ac_cv_prog_CXXCPP=$CXXCPP - -fi - CXXCPP=$ac_cv_prog_CXXCPP -else - ac_cv_prog_CXXCPP=$CXXCPP -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 -$as_echo "$CXXCPP" >&6; } -ac_preproc_ok=false -for ac_cxx_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_cxx_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_cxx_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } -fi - -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 -$as_echo_n "checking for grep that handles long lines and -e... " >&6; } -if ${ac_cv_path_GREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -z "$GREP"; then - ac_path_GREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in grep ggrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_GREP" || continue -# Check for GNU ac_path_GREP and select it if it is found. - # Check for GNU $ac_path_GREP -case `"$ac_path_GREP" --version 2>&1` in -*GNU*) - ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'GREP' >> "conftest.nl" - "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_GREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_GREP="$ac_path_GREP" - ac_path_GREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_GREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_GREP"; then - as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_GREP=$GREP -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 -$as_echo "$ac_cv_path_GREP" >&6; } - GREP="$ac_cv_path_GREP" - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 -$as_echo_n "checking for egrep... " >&6; } -if ${ac_cv_path_EGREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 - then ac_cv_path_EGREP="$GREP -E" - else - if test -z "$EGREP"; then - ac_path_EGREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in egrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_EGREP" || continue -# Check for GNU ac_path_EGREP and select it if it is found. - # Check for GNU $ac_path_EGREP -case `"$ac_path_EGREP" --version 2>&1` in -*GNU*) - ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'EGREP' >> "conftest.nl" - "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_EGREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_EGREP="$ac_path_EGREP" - ac_path_EGREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_EGREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_EGREP"; then - as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_EGREP=$EGREP -fi - - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 -$as_echo "$ac_cv_path_EGREP" >&6; } - EGREP="$ac_cv_path_EGREP" - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 -$as_echo_n "checking for ANSI C header files... " >&6; } -if ${ac_cv_header_stdc+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#include -#include - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cv_header_stdc=yes -else - ac_cv_header_stdc=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - -if test $ac_cv_header_stdc = yes; then - # SunOS 4.x string.h does not declare mem*, contrary to ANSI. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "memchr" >/dev/null 2>&1; then : - -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "free" >/dev/null 2>&1; then : - -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. - if test "$cross_compiling" = yes; then : - : -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#if ((' ' & 0x0FF) == 0x020) -# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') -# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) -#else -# define ISLOWER(c) \ - (('a' <= (c) && (c) <= 'i') \ - || ('j' <= (c) && (c) <= 'r') \ - || ('s' <= (c) && (c) <= 'z')) -# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) -#endif - -#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) -int -main () -{ - int i; - for (i = 0; i < 256; i++) - if (XOR (islower (i), ISLOWER (i)) - || toupper (i) != TOUPPER (i)) - return 2; - return 0; -} -_ACEOF -if ac_fn_cxx_try_run "$LINENO"; then : - -else - ac_cv_header_stdc=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 -$as_echo "$ac_cv_header_stdc" >&6; } -if test $ac_cv_header_stdc = yes; then - -$as_echo "#define STDC_HEADERS 1" >>confdefs.h - -fi - -# On IRIX 5.3, sys/types and inttypes.h are conflicting. -for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ - inttypes.h stdint.h unistd.h -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_cxx_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default -" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - - - - -# Check whether --with-cuda was given. -if test "${with_cuda+set}" = set; then : - withval=$with_cuda; -fi - - -# Check whether --with-cuda-arch was given. -if test "${with_cuda_arch+set}" = set; then : - withval=$with_cuda_arch; -else - with_cuda_arch=default -fi - - -if test x$with_cuda != xno; then - if test x$with_cuda != x; then - cuda_prefix=$with_cuda - # Extract the first word of "nvcc", so it can be a program name with args. -set dummy nvcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_NVCC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$NVCC"; then - ac_cv_prog_NVCC="$NVCC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $cuda_prefix/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_NVCC="$cuda_prefix/bin/nvcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -NVCC=$ac_cv_prog_NVCC -if test -n "$NVCC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NVCC" >&5 -$as_echo "$NVCC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - NVCC_CPPFLAGS="-I$cuda_prefix/include" - NVCC_LDFLAGS="-L$cuda_prefix/lib64" - else - for ac_prog in nvcc /usr/local/cuda/bin/nvcc -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_NVCC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$NVCC"; then - ac_cv_prog_NVCC="$NVCC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_NVCC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -NVCC=$ac_cv_prog_NVCC -if test -n "$NVCC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NVCC" >&5 -$as_echo "$NVCC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$NVCC" && break -done - - if test "x$NVCC" == "x/usr/local/cuda/bin/nvcc"; then - cuda_prefix="/usr/local/cuda" - NVCC_CPPFLAGS="-I$cuda_prefix/include" - NVCC_LDFLAGS="-L$cuda_prefix/lib64" - else - cuda_prefix="" - NVCC_CPPFLAGS="" - NVCC_LDFLAGS="" - fi - fi - - if test "x$NVCC" == x; then - as_fn_error $? "Couldn't find nvcc" "$LINENO" 5 - fi - - if test "x$with_cuda_arch" == xdefault; then - with_cuda_arch="-gencode=arch=compute_35,code=compute_35 \\ --gencode=arch=compute_52,code=compute_52 \\ --gencode=arch=compute_60,code=compute_60 \\ --gencode=arch=compute_61,code=compute_61 \\ --gencode=arch=compute_70,code=compute_70 \\ --gencode=arch=compute_75,code=compute_75" - fi - - fa_save_CPPFLAGS="$CPPFLAGS" - fa_save_LDFLAGS="$LDFLAGS" - fa_save_LIBS="$LIBS" - - CPPFLAGS="$NVCC_CPPFLAGS $CPPFLAGS" - LDFLAGS="$NVCC_LDFLAGS $LDFLAGS" - - ac_fn_cxx_check_header_mongrel "$LINENO" "cuda.h" "ac_cv_header_cuda_h" "$ac_includes_default" -if test "x$ac_cv_header_cuda_h" = xyes; then : - -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "Couldn't find cuda.h -See \`config.log' for more details" "$LINENO" 5; } -fi - - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cublasAlloc in -lcublas" >&5 -$as_echo_n "checking for cublasAlloc in -lcublas... " >&6; } -if ${ac_cv_lib_cublas_cublasAlloc+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lcublas $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char cublasAlloc (); -int -main () -{ -return cublasAlloc (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - ac_cv_lib_cublas_cublasAlloc=yes -else - ac_cv_lib_cublas_cublasAlloc=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cublas_cublasAlloc" >&5 -$as_echo "$ac_cv_lib_cublas_cublasAlloc" >&6; } -if test "x$ac_cv_lib_cublas_cublasAlloc" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBCUBLAS 1 -_ACEOF - - LIBS="-lcublas $LIBS" - -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "Couldn't find libcublas -See \`config.log' for more details" "$LINENO" 5; } -fi - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cudaSetDevice in -lcudart" >&5 -$as_echo_n "checking for cudaSetDevice in -lcudart... " >&6; } -if ${ac_cv_lib_cudart_cudaSetDevice+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lcudart $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char cudaSetDevice (); -int -main () -{ -return cudaSetDevice (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - ac_cv_lib_cudart_cudaSetDevice=yes -else - ac_cv_lib_cudart_cudaSetDevice=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cudart_cudaSetDevice" >&5 -$as_echo "$ac_cv_lib_cudart_cudaSetDevice" >&6; } -if test "x$ac_cv_lib_cudart_cudaSetDevice" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_LIBCUDART 1 -_ACEOF - - LIBS="-lcudart $LIBS" - -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "Couldn't find libcudart -See \`config.log' for more details" "$LINENO" 5; } -fi - - - NVCC_LIBS="$LIBS" - NVCC_CPPFLAGS="$CPPFLAGS" - NVCC_LDFLAGS="$LDFLAGS" - CPPFLAGS="$fa_save_CPPFLAGS" - LDFLAGS="$fa_save_LDFLAGS" - LIBS="$fa_save_LIBS" -fi - - - - - -CUDA_PREFIX=$cuda_prefix - -CUDA_ARCH=$with_cuda_arch - - - - -# Checks for header files. -for ac_header in float.h limits.h stddef.h stdint.h stdlib.h string.h sys/time.h unistd.h -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - -# Checks for typedefs, structures, and compiler characteristics. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdbool.h that conforms to C99" >&5 -$as_echo_n "checking for stdbool.h that conforms to C99... " >&6; } -if ${ac_cv_header_stdbool_h+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - #include - #ifndef bool - "error: bool is not defined" - #endif - #ifndef false - "error: false is not defined" - #endif - #if false - "error: false is not 0" - #endif - #ifndef true - "error: true is not defined" - #endif - #if true != 1 - "error: true is not 1" - #endif - #ifndef __bool_true_false_are_defined - "error: __bool_true_false_are_defined is not defined" - #endif - - struct s { _Bool s: 1; _Bool t; } s; - - char a[true == 1 ? 1 : -1]; - char b[false == 0 ? 1 : -1]; - char c[__bool_true_false_are_defined == 1 ? 1 : -1]; - char d[(bool) 0.5 == true ? 1 : -1]; - /* See body of main program for 'e'. */ - char f[(_Bool) 0.0 == false ? 1 : -1]; - char g[true]; - char h[sizeof (_Bool)]; - char i[sizeof s.t]; - enum { j = false, k = true, l = false * true, m = true * 256 }; - /* The following fails for - HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */ - _Bool n[m]; - char o[sizeof n == m * sizeof n[0] ? 1 : -1]; - char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1]; - /* Catch a bug in an HP-UX C compiler. See - http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html - http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html - */ - _Bool q = true; - _Bool *pq = &q; - -int -main () -{ - - bool e = &s; - *pq |= q; - *pq |= ! q; - /* Refer to every declared value, to avoid compiler optimizations. */ - return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l - + !m + !n + !o + !p + !q + !pq); - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cv_header_stdbool_h=yes -else - ac_cv_header_stdbool_h=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdbool_h" >&5 -$as_echo "$ac_cv_header_stdbool_h" >&6; } - ac_fn_cxx_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default" -if test "x$ac_cv_type__Bool" = xyes; then : - -cat >>confdefs.h <<_ACEOF -#define HAVE__BOOL 1 -_ACEOF - - -fi - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 -$as_echo_n "checking for inline... " >&6; } -if ${ac_cv_c_inline+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_c_inline=no -for ac_kw in inline __inline__ __inline; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifndef __cplusplus -typedef int foo_t; -static $ac_kw foo_t static_foo () {return 0; } -$ac_kw foo_t foo () {return 0; } -#endif - -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cv_c_inline=$ac_kw -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - test "$ac_cv_c_inline" != no && break -done - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 -$as_echo "$ac_cv_c_inline" >&6; } - -case $ac_cv_c_inline in - inline | yes) ;; - *) - case $ac_cv_c_inline in - no) ac_val=;; - *) ac_val=$ac_cv_c_inline;; - esac - cat >>confdefs.h <<_ACEOF -#ifndef __cplusplus -#define inline $ac_val -#endif -_ACEOF - ;; -esac - -ac_fn_c_find_intX_t "$LINENO" "32" "ac_cv_c_int32_t" -case $ac_cv_c_int32_t in #( - no|yes) ;; #( - *) - -cat >>confdefs.h <<_ACEOF -#define int32_t $ac_cv_c_int32_t -_ACEOF -;; -esac - -ac_fn_c_find_intX_t "$LINENO" "64" "ac_cv_c_int64_t" -case $ac_cv_c_int64_t in #( - no|yes) ;; #( - *) - -cat >>confdefs.h <<_ACEOF -#define int64_t $ac_cv_c_int64_t -_ACEOF -;; -esac - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C/C++ restrict keyword" >&5 -$as_echo_n "checking for C/C++ restrict keyword... " >&6; } -if ${ac_cv_c_restrict+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_c_restrict=no - # The order here caters to the fact that C++ does not require restrict. - for ac_kw in __restrict __restrict__ _Restrict restrict; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -typedef int * int_ptr; - int foo (int_ptr $ac_kw ip) { - return ip[0]; - } -int -main () -{ -int s[1]; - int * $ac_kw t = s; - t[0] = 0; - return foo(t) - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cv_c_restrict=$ac_kw -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - test "$ac_cv_c_restrict" != no && break - done - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_restrict" >&5 -$as_echo "$ac_cv_c_restrict" >&6; } - - case $ac_cv_c_restrict in - restrict) ;; - no) $as_echo "#define restrict /**/" >>confdefs.h - ;; - *) cat >>confdefs.h <<_ACEOF -#define restrict $ac_cv_c_restrict -_ACEOF - ;; - esac - -ac_fn_cxx_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" -if test "x$ac_cv_type_size_t" = xyes; then : - -else - -cat >>confdefs.h <<_ACEOF -#define size_t unsigned int -_ACEOF - -fi - -ac_fn_c_find_uintX_t "$LINENO" "16" "ac_cv_c_uint16_t" -case $ac_cv_c_uint16_t in #( - no|yes) ;; #( - *) - - -cat >>confdefs.h <<_ACEOF -#define uint16_t $ac_cv_c_uint16_t -_ACEOF -;; - esac - -ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" -case $ac_cv_c_uint32_t in #( - no|yes) ;; #( - *) - -$as_echo "#define _UINT32_T 1" >>confdefs.h - - -cat >>confdefs.h <<_ACEOF -#define uint32_t $ac_cv_c_uint32_t -_ACEOF -;; - esac - -ac_fn_c_find_uintX_t "$LINENO" "64" "ac_cv_c_uint64_t" -case $ac_cv_c_uint64_t in #( - no|yes) ;; #( - *) - -$as_echo "#define _UINT64_T 1" >>confdefs.h - - -cat >>confdefs.h <<_ACEOF -#define uint64_t $ac_cv_c_uint64_t -_ACEOF -;; - esac - -ac_fn_c_find_uintX_t "$LINENO" "8" "ac_cv_c_uint8_t" -case $ac_cv_c_uint8_t in #( - no|yes) ;; #( - *) - -$as_echo "#define _UINT8_T 1" >>confdefs.h - - -cat >>confdefs.h <<_ACEOF -#define uint8_t $ac_cv_c_uint8_t -_ACEOF -;; - esac - - -# Checks for library functions. -for ac_header in stdlib.h -do : - ac_fn_cxx_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default" -if test "x$ac_cv_header_stdlib_h" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_STDLIB_H 1 -_ACEOF - -fi - -done - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible malloc" >&5 -$as_echo_n "checking for GNU libc compatible malloc... " >&6; } -if ${ac_cv_func_malloc_0_nonnull+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "$cross_compiling" = yes; then : - ac_cv_func_malloc_0_nonnull=no -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#if defined STDC_HEADERS || defined HAVE_STDLIB_H -# include -#else -char *malloc (); -#endif - -int -main () -{ -return ! malloc (0); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_run "$LINENO"; then : - ac_cv_func_malloc_0_nonnull=yes -else - ac_cv_func_malloc_0_nonnull=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_malloc_0_nonnull" >&5 -$as_echo "$ac_cv_func_malloc_0_nonnull" >&6; } -if test $ac_cv_func_malloc_0_nonnull = yes; then : - -$as_echo "#define HAVE_MALLOC 1" >>confdefs.h - -else - $as_echo "#define HAVE_MALLOC 0" >>confdefs.h - - case " $LIBOBJS " in - *" malloc.$ac_objext "* ) ;; - *) LIBOBJS="$LIBOBJS malloc.$ac_objext" - ;; -esac - - -$as_echo "#define malloc rpl_malloc" >>confdefs.h - -fi - - - - - - for ac_header in $ac_header_list -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_cxx_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default -" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - - - - - - - -for ac_func in getpagesize -do : - ac_fn_cxx_check_func "$LINENO" "getpagesize" "ac_cv_func_getpagesize" -if test "x$ac_cv_func_getpagesize" = xyes; then : - cat >>confdefs.h <<_ACEOF -#define HAVE_GETPAGESIZE 1 -_ACEOF - -fi -done - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working mmap" >&5 -$as_echo_n "checking for working mmap... " >&6; } -if ${ac_cv_func_mmap_fixed_mapped+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "$cross_compiling" = yes; then : - ac_cv_func_mmap_fixed_mapped=no -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes_default -/* malloc might have been renamed as rpl_malloc. */ -#undef malloc - -/* Thanks to Mike Haertel and Jim Avera for this test. - Here is a matrix of mmap possibilities: - mmap private not fixed - mmap private fixed at somewhere currently unmapped - mmap private fixed at somewhere already mapped - mmap shared not fixed - mmap shared fixed at somewhere currently unmapped - mmap shared fixed at somewhere already mapped - For private mappings, we should verify that changes cannot be read() - back from the file, nor mmap's back from the file at a different - address. (There have been systems where private was not correctly - implemented like the infamous i386 svr4.0, and systems where the - VM page cache was not coherent with the file system buffer cache - like early versions of FreeBSD and possibly contemporary NetBSD.) - For shared mappings, we should conversely verify that changes get - propagated back to all the places they're supposed to be. - - Grep wants private fixed already mapped. - The main things grep needs to know about mmap are: - * does it exist and is it safe to write into the mmap'd area - * how to use it (BSD variants) */ - -#include -#include - -#if !defined STDC_HEADERS && !defined HAVE_STDLIB_H -char *malloc (); -#endif - -/* This mess was copied from the GNU getpagesize.h. */ -#ifndef HAVE_GETPAGESIZE -# ifdef _SC_PAGESIZE -# define getpagesize() sysconf(_SC_PAGESIZE) -# else /* no _SC_PAGESIZE */ -# ifdef HAVE_SYS_PARAM_H -# include -# ifdef EXEC_PAGESIZE -# define getpagesize() EXEC_PAGESIZE -# else /* no EXEC_PAGESIZE */ -# ifdef NBPG -# define getpagesize() NBPG * CLSIZE -# ifndef CLSIZE -# define CLSIZE 1 -# endif /* no CLSIZE */ -# else /* no NBPG */ -# ifdef NBPC -# define getpagesize() NBPC -# else /* no NBPC */ -# ifdef PAGESIZE -# define getpagesize() PAGESIZE -# endif /* PAGESIZE */ -# endif /* no NBPC */ -# endif /* no NBPG */ -# endif /* no EXEC_PAGESIZE */ -# else /* no HAVE_SYS_PARAM_H */ -# define getpagesize() 8192 /* punt totally */ -# endif /* no HAVE_SYS_PARAM_H */ -# endif /* no _SC_PAGESIZE */ - -#endif /* no HAVE_GETPAGESIZE */ - -int -main () -{ - char *data, *data2, *data3; - const char *cdata2; - int i, pagesize; - int fd, fd2; - - pagesize = getpagesize (); - - /* First, make a file with some known garbage in it. */ - data = (char *) malloc (pagesize); - if (!data) - return 1; - for (i = 0; i < pagesize; ++i) - *(data + i) = rand (); - umask (0); - fd = creat ("conftest.mmap", 0600); - if (fd < 0) - return 2; - if (write (fd, data, pagesize) != pagesize) - return 3; - close (fd); - - /* Next, check that the tail of a page is zero-filled. File must have - non-zero length, otherwise we risk SIGBUS for entire page. */ - fd2 = open ("conftest.txt", O_RDWR | O_CREAT | O_TRUNC, 0600); - if (fd2 < 0) - return 4; - cdata2 = ""; - if (write (fd2, cdata2, 1) != 1) - return 5; - data2 = (char *) mmap (0, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd2, 0L); - if (data2 == MAP_FAILED) - return 6; - for (i = 0; i < pagesize; ++i) - if (*(data2 + i)) - return 7; - close (fd2); - if (munmap (data2, pagesize)) - return 8; - - /* Next, try to mmap the file at a fixed address which already has - something else allocated at it. If we can, also make sure that - we see the same garbage. */ - fd = open ("conftest.mmap", O_RDWR); - if (fd < 0) - return 9; - if (data2 != mmap (data2, pagesize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_FIXED, fd, 0L)) - return 10; - for (i = 0; i < pagesize; ++i) - if (*(data + i) != *(data2 + i)) - return 11; - - /* Finally, make sure that changes to the mapped area do not - percolate back to the file as seen by read(). (This is a bug on - some variants of i386 svr4.0.) */ - for (i = 0; i < pagesize; ++i) - *(data2 + i) = *(data2 + i) + 1; - data3 = (char *) malloc (pagesize); - if (!data3) - return 12; - if (read (fd, data3, pagesize) != pagesize) - return 13; - for (i = 0; i < pagesize; ++i) - if (*(data + i) != *(data3 + i)) - return 14; - close (fd); - return 0; -} -_ACEOF -if ac_fn_cxx_try_run "$LINENO"; then : - ac_cv_func_mmap_fixed_mapped=yes -else - ac_cv_func_mmap_fixed_mapped=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_mmap_fixed_mapped" >&5 -$as_echo "$ac_cv_func_mmap_fixed_mapped" >&6; } -if test $ac_cv_func_mmap_fixed_mapped = yes; then - -$as_echo "#define HAVE_MMAP 1" >>confdefs.h - -fi -rm -f conftest.mmap conftest.txt - -for ac_func in clock_gettime floor gettimeofday memmove memset munmap pow sqrt strerror strstr -do : - as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` -ac_fn_cxx_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF - -fi -done - - - - OPENMP_CXXFLAGS= - # Check whether --enable-openmp was given. -if test "${enable_openmp+set}" = set; then : - enableval=$enable_openmp; -fi - - if test "$enable_openmp" != no; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CXX option to support OpenMP" >&5 -$as_echo_n "checking for $CXX option to support OpenMP... " >&6; } -if ${ac_cv_prog_cxx_openmp+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#ifndef _OPENMP - choke me -#endif -#include -int main () { return omp_get_num_threads (); } - -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - ac_cv_prog_cxx_openmp='none needed' -else - ac_cv_prog_cxx_openmp='unsupported' - for ac_option in -fopenmp -xopenmp -openmp -mp -omp -qsmp=omp -homp \ - -Popenmp --openmp; do - ac_save_CXXFLAGS=$CXXFLAGS - CXXFLAGS="$CXXFLAGS $ac_option" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#ifndef _OPENMP - choke me -#endif -#include -int main () { return omp_get_num_threads (); } - -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - ac_cv_prog_cxx_openmp=$ac_option -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - CXXFLAGS=$ac_save_CXXFLAGS - if test "$ac_cv_prog_cxx_openmp" != unsupported; then - break - fi - done -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_openmp" >&5 -$as_echo "$ac_cv_prog_cxx_openmp" >&6; } - case $ac_cv_prog_cxx_openmp in #( - "none needed" | unsupported) - ;; #( - *) - OPENMP_CXXFLAGS=$ac_cv_prog_cxx_openmp ;; - esac - fi - - - -# Make sure we can run config.sub. -$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || - as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 -$as_echo_n "checking build system type... " >&6; } -if ${ac_cv_build+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_build_alias=$build_alias -test "x$ac_build_alias" = x && - ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` -test "x$ac_build_alias" = x && - as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 -ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 -$as_echo "$ac_cv_build" >&6; } -case $ac_cv_build in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; -esac -build=$ac_cv_build -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_build -shift -build_cpu=$1 -build_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -build_os=$* -IFS=$ac_save_IFS -case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 -$as_echo_n "checking host system type... " >&6; } -if ${ac_cv_host+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "x$host_alias" = x; then - ac_cv_host=$ac_cv_build -else - ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 -$as_echo "$ac_cv_host" >&6; } -case $ac_cv_host in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; -esac -host=$ac_cv_host -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_host -shift -host_cpu=$1 -host_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -host_os=$* -IFS=$ac_save_IFS -case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac - - - - -# AC_REQUIRE([AC_F77_LIBRARY_LDFLAGS]) - -ax_blas_ok=no - - -# Check whether --with-blas was given. -if test "${with_blas+set}" = set; then : - withval=$with_blas; -fi - -case $with_blas in - yes | "") ;; - no) ax_blas_ok=disable ;; - -* | */* | *.a | *.so | *.so.* | *.o) BLAS_LIBS="$with_blas" ;; - *) BLAS_LIBS="-l$with_blas" ;; -esac - -OPENMP_LDFLAGS="$OPENMP_CXXFLAGS" - -# Get fortran linker names of BLAS functions to check for. -# AC_F77_FUNC(sgemm) -# AC_F77_FUNC(dgemm) -sgemm=sgemm_ -dgemm=dgemm_ - -ax_blas_save_LIBS="$LIBS" -LIBS="$LIBS $FLIBS" - -# First, check BLAS_LIBS environment variable -if test $ax_blas_ok = no; then -if test "x$BLAS_LIBS" != x; then - save_LIBS="$LIBS"; LIBS="$BLAS_LIBS $LIBS" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $sgemm in $BLAS_LIBS" >&5 -$as_echo_n "checking for $sgemm in $BLAS_LIBS... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $sgemm (); -int -main () -{ -return $sgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - ax_blas_ok=yes -else - BLAS_LIBS="" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_blas_ok" >&5 -$as_echo "$ax_blas_ok" >&6; } - LIBS="$save_LIBS" -fi -fi - -# BLAS linked to by default? (happens on some supercomputers) -if test $ax_blas_ok = no; then - save_LIBS="$LIBS"; LIBS="$LIBS" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $sgemm is being linked in already" >&5 -$as_echo_n "checking if $sgemm is being linked in already... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $sgemm (); -int -main () -{ -return $sgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - ax_blas_ok=yes -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_blas_ok" >&5 -$as_echo "$ax_blas_ok" >&6; } - LIBS="$save_LIBS" -fi - -# BLAS in Intel MKL library? -if test $ax_blas_ok = no; then - case $host_os in - darwin*) - as_ac_Lib=`$as_echo "ac_cv_lib_mkl_intel_lp64_$sgemm" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $sgemm in -lmkl_intel_lp64" >&5 -$as_echo_n "checking for $sgemm in -lmkl_intel_lp64... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lmkl_intel_lp64 -lmkl_intel_lp64 -lmkl_intel_thread -lmkl_core -liomp5 -lpthread $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $sgemm (); -int -main () -{ -return $sgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - ax_blas_ok=yes;BLAS_LIBS="-lmkl_intel_lp64 -lmkl_intel_thread -lmkl_core -liomp5 -lpthread"; OPENMP_LDFLAGS="" -fi - - ;; - *) - if test $host_cpu = x86_64; then - as_ac_Lib=`$as_echo "ac_cv_lib_mkl_intel_lp64_$sgemm" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $sgemm in -lmkl_intel_lp64" >&5 -$as_echo_n "checking for $sgemm in -lmkl_intel_lp64... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lmkl_intel_lp64 -lmkl_intel_lp64 -lmkl_gnu_thread -lmkl_core -lgomp -lpthread -lm -ldl $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $sgemm (); -int -main () -{ -return $sgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - ax_blas_ok=yes;BLAS_LIBS="-lmkl_intel_lp64 -lmkl_gnu_thread -lmkl_core -lgomp -lpthread -lm -ldl" -fi - - elif test $host_cpu = i686; then - as_ac_Lib=`$as_echo "ac_cv_lib_mkl_intel_$sgemm" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $sgemm in -lmkl_intel" >&5 -$as_echo_n "checking for $sgemm in -lmkl_intel... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lmkl_intel -lmkl_intel -lmkl_gnu_thread -lmkl_core -lgomp -lpthread -lm -ldl $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $sgemm (); -int -main () -{ -return $sgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - ax_blas_ok=yes;BLAS_LIBS="-lmkl_intel -lmkl_gnu_thread -lmkl_core -lgomp -lpthread -lm -ldl" -fi - - fi - ;; - esac -fi -# Old versions of MKL -if test $ax_blas_ok = no; then - as_ac_Lib=`$as_echo "ac_cv_lib_mkl_$sgemm" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $sgemm in -lmkl" >&5 -$as_echo_n "checking for $sgemm in -lmkl... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lmkl -lguide -lpthread $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $sgemm (); -int -main () -{ -return $sgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - ax_blas_ok=yes;BLAS_LIBS="-lmkl -lguide -lpthread" -fi - -fi - -# BLAS in OpenBLAS library? (http://xianyi.github.com/OpenBLAS/) -if test $ax_blas_ok = no; then - as_ac_Lib=`$as_echo "ac_cv_lib_openblas_$sgemm" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $sgemm in -lopenblas" >&5 -$as_echo_n "checking for $sgemm in -lopenblas... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lopenblas $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $sgemm (); -int -main () -{ -return $sgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - ax_blas_ok=yes - BLAS_LIBS="-lopenblas" -fi - -fi - -# BLAS in ATLAS library? (http://math-atlas.sourceforge.net/) -if test $ax_blas_ok = no; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ATL_xerbla in -latlas" >&5 -$as_echo_n "checking for ATL_xerbla in -latlas... " >&6; } -if ${ac_cv_lib_atlas_ATL_xerbla+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-latlas $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char ATL_xerbla (); -int -main () -{ -return ATL_xerbla (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - ac_cv_lib_atlas_ATL_xerbla=yes -else - ac_cv_lib_atlas_ATL_xerbla=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_atlas_ATL_xerbla" >&5 -$as_echo "$ac_cv_lib_atlas_ATL_xerbla" >&6; } -if test "x$ac_cv_lib_atlas_ATL_xerbla" = xyes; then : - as_ac_Lib=`$as_echo "ac_cv_lib_f77blas_$sgemm" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $sgemm in -lf77blas" >&5 -$as_echo_n "checking for $sgemm in -lf77blas... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lf77blas -latlas $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $sgemm (); -int -main () -{ -return $sgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cblas_dgemm in -lcblas" >&5 -$as_echo_n "checking for cblas_dgemm in -lcblas... " >&6; } -if ${ac_cv_lib_cblas_cblas_dgemm+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lcblas -lf77blas -latlas $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char cblas_dgemm (); -int -main () -{ -return cblas_dgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - ac_cv_lib_cblas_cblas_dgemm=yes -else - ac_cv_lib_cblas_cblas_dgemm=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cblas_cblas_dgemm" >&5 -$as_echo "$ac_cv_lib_cblas_cblas_dgemm" >&6; } -if test "x$ac_cv_lib_cblas_cblas_dgemm" = xyes; then : - ax_blas_ok=yes - BLAS_LIBS="-lcblas -lf77blas -latlas" -fi - -fi - -fi - -fi - -# BLAS in PhiPACK libraries? (requires generic BLAS lib, too) -if test $ax_blas_ok = no; then - as_ac_Lib=`$as_echo "ac_cv_lib_blas_$sgemm" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $sgemm in -lblas" >&5 -$as_echo_n "checking for $sgemm in -lblas... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lblas $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $sgemm (); -int -main () -{ -return $sgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - as_ac_Lib=`$as_echo "ac_cv_lib_dgemm_$dgemm" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $dgemm in -ldgemm" >&5 -$as_echo_n "checking for $dgemm in -ldgemm... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldgemm -lblas $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $dgemm (); -int -main () -{ -return $dgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - as_ac_Lib=`$as_echo "ac_cv_lib_sgemm_$sgemm" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $sgemm in -lsgemm" >&5 -$as_echo_n "checking for $sgemm in -lsgemm... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lsgemm -lblas $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $sgemm (); -int -main () -{ -return $sgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - ax_blas_ok=yes; BLAS_LIBS="-lsgemm -ldgemm -lblas" -fi - -fi - -fi - -fi - -# BLAS in Apple vecLib library? -if test $ax_blas_ok = no; then - save_LIBS="$LIBS"; LIBS="-framework vecLib $LIBS" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $sgemm in -framework vecLib" >&5 -$as_echo_n "checking for $sgemm in -framework vecLib... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $sgemm (); -int -main () -{ -return $sgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - ax_blas_ok=yes;BLAS_LIBS="-framework vecLib" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_blas_ok" >&5 -$as_echo "$ax_blas_ok" >&6; } - LIBS="$save_LIBS" -fi - -# BLAS in Alpha CXML library? -if test $ax_blas_ok = no; then - as_ac_Lib=`$as_echo "ac_cv_lib_cxml_$sgemm" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $sgemm in -lcxml" >&5 -$as_echo_n "checking for $sgemm in -lcxml... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lcxml $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $sgemm (); -int -main () -{ -return $sgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - ax_blas_ok=yes;BLAS_LIBS="-lcxml" -fi - -fi - -# BLAS in Alpha DXML library? (now called CXML, see above) -if test $ax_blas_ok = no; then - as_ac_Lib=`$as_echo "ac_cv_lib_dxml_$sgemm" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $sgemm in -ldxml" >&5 -$as_echo_n "checking for $sgemm in -ldxml... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldxml $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $sgemm (); -int -main () -{ -return $sgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - ax_blas_ok=yes;BLAS_LIBS="-ldxml" -fi - -fi - -# BLAS in Sun Performance library? -if test $ax_blas_ok = no; then - if test "x$GCC" != xyes; then # only works with Sun CC - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for acosp in -lsunmath" >&5 -$as_echo_n "checking for acosp in -lsunmath... " >&6; } -if ${ac_cv_lib_sunmath_acosp+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lsunmath $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char acosp (); -int -main () -{ -return acosp (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - ac_cv_lib_sunmath_acosp=yes -else - ac_cv_lib_sunmath_acosp=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sunmath_acosp" >&5 -$as_echo "$ac_cv_lib_sunmath_acosp" >&6; } -if test "x$ac_cv_lib_sunmath_acosp" = xyes; then : - as_ac_Lib=`$as_echo "ac_cv_lib_sunperf_$sgemm" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $sgemm in -lsunperf" >&5 -$as_echo_n "checking for $sgemm in -lsunperf... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lsunperf -lsunmath $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $sgemm (); -int -main () -{ -return $sgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - BLAS_LIBS="-xlic_lib=sunperf -lsunmath" - ax_blas_ok=yes -fi - -fi - - fi -fi - -# BLAS in SCSL library? (SGI/Cray Scientific Library) -if test $ax_blas_ok = no; then - as_ac_Lib=`$as_echo "ac_cv_lib_scs_$sgemm" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $sgemm in -lscs" >&5 -$as_echo_n "checking for $sgemm in -lscs... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lscs $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $sgemm (); -int -main () -{ -return $sgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - ax_blas_ok=yes; BLAS_LIBS="-lscs" -fi - -fi - -# BLAS in SGIMATH library? -if test $ax_blas_ok = no; then - as_ac_Lib=`$as_echo "ac_cv_lib_complib.sgimath_$sgemm" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $sgemm in -lcomplib.sgimath" >&5 -$as_echo_n "checking for $sgemm in -lcomplib.sgimath... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lcomplib.sgimath $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $sgemm (); -int -main () -{ -return $sgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - ax_blas_ok=yes; BLAS_LIBS="-lcomplib.sgimath" -fi - -fi - -# BLAS in IBM ESSL library? (requires generic BLAS lib, too) -if test $ax_blas_ok = no; then - as_ac_Lib=`$as_echo "ac_cv_lib_blas_$sgemm" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $sgemm in -lblas" >&5 -$as_echo_n "checking for $sgemm in -lblas... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lblas $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $sgemm (); -int -main () -{ -return $sgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - as_ac_Lib=`$as_echo "ac_cv_lib_essl_$sgemm" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $sgemm in -lessl" >&5 -$as_echo_n "checking for $sgemm in -lessl... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lessl -lblas $FLIBS $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $sgemm (); -int -main () -{ -return $sgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - ax_blas_ok=yes; BLAS_LIBS="-lessl -lblas" -fi - -fi - -fi - -# Generic BLAS library? -if test $ax_blas_ok = no; then - as_ac_Lib=`$as_echo "ac_cv_lib_blas_$sgemm" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $sgemm in -lblas" >&5 -$as_echo_n "checking for $sgemm in -lblas... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lblas $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $sgemm (); -int -main () -{ -return $sgemm (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - ax_blas_ok=yes; BLAS_LIBS="-lblas" -fi - -fi - - - - -LIBS="$ax_blas_save_LIBS" - -# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: -if test x"$ax_blas_ok" = xyes; then - -$as_echo "#define HAVE_BLAS 1" >>confdefs.h - - : -else - ax_blas_ok=no - -fi - -if test "x$ax_blas_ok" == "xno"; then - as_fn_error $? "An implementation of BLAS is required but none was found." "$LINENO" 5 -fi - - - -ax_lapack_ok=no - - -# Check whether --with-lapack was given. -if test "${with_lapack+set}" = set; then : - withval=$with_lapack; -fi - -case $with_lapack in - yes | "") ;; - no) ax_lapack_ok=disable ;; - -* | */* | *.a | *.so | *.so.* | *.o) LAPACK_LIBS="$with_lapack" ;; - *) LAPACK_LIBS="-l$with_lapack" ;; -esac - -# Get fortran linker name of LAPACK function to check for. -# AC_F77_FUNC(cheev) -cheev=cheev_ - -# We cannot use LAPACK if BLAS is not found -if test "x$ax_blas_ok" != xyes; then - ax_lapack_ok=noblas - LAPACK_LIBS="" -fi - -# First, check LAPACK_LIBS environment variable -if test "x$LAPACK_LIBS" != x; then - save_LIBS="$LIBS"; LIBS="$LAPACK_LIBS $BLAS_LIBS $LIBS $FLIBS" - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $cheev in $LAPACK_LIBS" >&5 -$as_echo_n "checking for $cheev in $LAPACK_LIBS... " >&6; } - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $cheev (); -int -main () -{ -return $cheev (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - ax_lapack_ok=yes -else - LAPACK_LIBS="" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_lapack_ok" >&5 -$as_echo "$ax_lapack_ok" >&6; } - LIBS="$save_LIBS" - if test $ax_lapack_ok = no; then - LAPACK_LIBS="" - fi -fi - -# LAPACK linked to by default? (is sometimes included in BLAS lib) -if test $ax_lapack_ok = no; then - save_LIBS="$LIBS"; LIBS="$LIBS $BLAS_LIBS $FLIBS" - as_ac_var=`$as_echo "ac_cv_func_$cheev" | $as_tr_sh` -ac_fn_cxx_check_func "$LINENO" "$cheev" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : - ax_lapack_ok=yes -fi - - LIBS="$save_LIBS" -fi - -# Generic LAPACK library? -for lapack in lapack lapack_rs6k; do - if test $ax_lapack_ok = no; then - save_LIBS="$LIBS"; LIBS="$BLAS_LIBS $LIBS" - as_ac_Lib=`$as_echo "ac_cv_lib_$lapack''_$cheev" | $as_tr_sh` -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $cheev in -l$lapack" >&5 -$as_echo_n "checking for $cheev in -l$lapack... " >&6; } -if eval \${$as_ac_Lib+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-l$lapack $FLIBS $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $cheev (); -int -main () -{ -return $cheev (); - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_link "$LINENO"; then : - eval "$as_ac_Lib=yes" -else - eval "$as_ac_Lib=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -eval ac_res=\$$as_ac_Lib - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : - ax_lapack_ok=yes; LAPACK_LIBS="-l$lapack" -fi - - LIBS="$save_LIBS" - fi -done - - - -# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: -if test x"$ax_lapack_ok" = xyes; then - -$as_echo "#define HAVE_LAPACK 1" >>confdefs.h - - : -else - ax_lapack_ok=no - -fi - -if test "x$ax_lapack_ok" == "xno"; then - as_fn_error $? "An implementation of LAPACK is required but none was found." "$LINENO" 5 -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5 -$as_echo_n "checking target system type... " >&6; } -if ${ac_cv_target+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "x$target_alias" = x; then - ac_cv_target=$ac_cv_host -else - ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $target_alias failed" "$LINENO" 5 -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5 -$as_echo "$ac_cv_target" >&6; } -case $ac_cv_target in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;; -esac -target=$ac_cv_target -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_target -shift -target_cpu=$1 -target_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -target_os=$* -IFS=$ac_save_IFS -case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac - - -# The aliases save the names the user supplied, while $host etc. -# will get canonicalized. -test -n "$target_alias" && - test "$program_prefix$program_suffix$program_transform_name" = \ - NONENONEs,x,x, && - program_prefix=${target_alias}- - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for cpu arch" >&5 -$as_echo_n "checking for cpu arch... " >&6; } - - - - case $target in - amd64-* | x86_64-*) - ARCH_CPUFLAGS="-mpopcnt -msse4" - ARCH_CXXFLAGS="-m64" - ;; - aarch64*-*) - ARCH_CPUFLAGS="-march=armv8.2-a" - ;; - *) ;; - esac - -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $target CPUFLAGS+=\"$ARCH_CPUFLAGS\" CXXFLAGS+=\"$ARCH_CXXFLAGS\"" >&5 -$as_echo "$target CPUFLAGS+=\"$ARCH_CPUFLAGS\" CXXFLAGS+=\"$ARCH_CXXFLAGS\"" >&6; } - - - - - - -ac_config_files="$ac_config_files makefile.inc" - -cat >confcache <<\_ACEOF -# This file is a shell script that caches the results of configure -# tests run on this system so they can be shared between configure -# scripts and configure runs, see configure's option --config-cache. -# It is not useful on other systems. If it contains results you don't -# want to keep, you may remove or edit it. -# -# config.status only pays attention to the cache file if you give it -# the --recheck option to rerun configure. -# -# `ac_cv_env_foo' variables (set or unset) will be overridden when -# loading this file, other *unset* `ac_cv_foo' will be assigned the -# following values. - -_ACEOF - -# The following way of writing the cache mishandles newlines in values, -# but we know of no workaround that is simple, portable, and efficient. -# So, we kill variables containing newlines. -# Ultrix sh set writes to stderr and can't be redirected directly, -# and sets the high bit in the cache file unless we assign to the vars. -( - for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - - (set) 2>&1 | - case $as_nl`(ac_space=' '; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - # `set' does not quote correctly, so add quotes: double-quote - # substitution turns \\\\ into \\, and sed turns \\ into \. - sed -n \ - "s/'/'\\\\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" - ;; #( - *) - # `set' quotes correctly as required by POSIX, so do not add quotes. - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) | - sed ' - /^ac_cv_env_/b end - t clear - :clear - s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ - t end - s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ - :end' >>confcache -if diff "$cache_file" confcache >/dev/null 2>&1; then :; else - if test -w "$cache_file"; then - if test "x$cache_file" != "x/dev/null"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 -$as_echo "$as_me: updating cache $cache_file" >&6;} - if test ! -f "$cache_file" || test -h "$cache_file"; then - cat confcache >"$cache_file" - else - case $cache_file in #( - */* | ?:*) - mv -f confcache "$cache_file"$$ && - mv -f "$cache_file"$$ "$cache_file" ;; #( - *) - mv -f confcache "$cache_file" ;; - esac - fi - fi - else - { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 -$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} - fi -fi -rm -f confcache - -test "x$prefix" = xNONE && prefix=$ac_default_prefix -# Let make expand exec_prefix. -test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' - -# Transform confdefs.h into DEFS. -# Protect against shell expansion while executing Makefile rules. -# Protect against Makefile macro expansion. -# -# If the first sed substitution is executed (which looks for macros that -# take arguments), then branch to the quote section. Otherwise, -# look for a macro that doesn't take arguments. -ac_script=' -:mline -/\\$/{ - N - s,\\\n,, - b mline -} -t clear -:clear -s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g -t quote -s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g -t quote -b any -:quote -s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g -s/\[/\\&/g -s/\]/\\&/g -s/\$/$$/g -H -:any -${ - g - s/^\n// - s/\n/ /g - p -} -' -DEFS=`sed -n "$ac_script" confdefs.h` - - -ac_libobjs= -ac_ltlibobjs= -U= -for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue - # 1. Remove the extension, and $U if already installed. - ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' - ac_i=`$as_echo "$ac_i" | sed "$ac_script"` - # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR - # will be set to the directory where LIBOBJS objects are built. - as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" - as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' -done -LIBOBJS=$ac_libobjs - -LTLIBOBJS=$ac_ltlibobjs - - - -: "${CONFIG_STATUS=./config.status}" -ac_write_fail=0 -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files $CONFIG_STATUS" -{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 -$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} -as_write_fail=0 -cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 -#! $SHELL -# Generated by $as_me. -# Run this file to recreate the current configuration. -# Compiler output produced by configure, useful for debugging -# configure, is in config.log if it exists. - -debug=false -ac_cs_recheck=false -ac_cs_silent=false - -SHELL=\${CONFIG_SHELL-$SHELL} -export SHELL -_ASEOF -cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 -## -------------------- ## -## M4sh Initialization. ## -## -------------------- ## - -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - -as_nl=' -' -export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -case $0 in #(( - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break - done -IFS=$as_save_IFS - - ;; -esac -# We did not find ourselves, most probably we were run as `sh COMMAND' -# in which case we are not to be found in the path. -if test "x$as_myself" = x; then - as_myself=$0 -fi -if test ! -f "$as_myself"; then - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - $as_echo "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit - -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -pR' - fi -else - as_ln_s='cp -pR' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -as_test_x='test -x' -as_executable_p=as_fn_executable_p - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -exec 6>&1 -## ----------------------------------- ## -## Main body of $CONFIG_STATUS script. ## -## ----------------------------------- ## -_ASEOF -test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# Save the log message, to keep $0 and so on meaningful, and to -# report actual input values of CONFIG_FILES etc. instead of their -# values after options handling. -ac_log=" -This file was extended by faiss $as_me 1.0, which was -generated by GNU Autoconf 2.69. Invocation command line was - - CONFIG_FILES = $CONFIG_FILES - CONFIG_HEADERS = $CONFIG_HEADERS - CONFIG_LINKS = $CONFIG_LINKS - CONFIG_COMMANDS = $CONFIG_COMMANDS - $ $0 $@ - -on `(hostname || uname -n) 2>/dev/null | sed 1q` -" - -_ACEOF - -case $ac_config_files in *" -"*) set x $ac_config_files; shift; ac_config_files=$*;; -esac - - - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -# Files that config.status was made for. -config_files="$ac_config_files" - -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -ac_cs_usage="\ -\`$as_me' instantiates files and other configuration actions -from templates according to the current configuration. Unless the files -and actions are specified as TAGs, all are instantiated by default. - -Usage: $0 [OPTION]... [TAG]... - - -h, --help print this help, then exit - -V, --version print version number and configuration settings, then exit - --config print configuration, then exit - -q, --quiet, --silent - do not print progress messages - -d, --debug don't remove temporary files - --recheck update $as_me by reconfiguring in the same conditions - --file=FILE[:TEMPLATE] - instantiate the configuration file FILE - -Configuration files: -$config_files - -Report bugs to the package provider." - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" -ac_cs_version="\\ -faiss config.status 1.0 -configured by $0, generated by GNU Autoconf 2.69, - with options \\"\$ac_cs_config\\" - -Copyright (C) 2012 Free Software Foundation, Inc. -This config.status script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it." - -ac_pwd='$ac_pwd' -srcdir='$srcdir' -MKDIR_P='$MKDIR_P' -test -n "\$AWK" || AWK=awk -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# The default lists apply if the user does not specify any file. -ac_need_defaults=: -while test $# != 0 -do - case $1 in - --*=?*) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` - ac_shift=: - ;; - --*=) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg= - ac_shift=: - ;; - *) - ac_option=$1 - ac_optarg=$2 - ac_shift=shift - ;; - esac - - case $ac_option in - # Handling of the options. - -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) - ac_cs_recheck=: ;; - --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) - $as_echo "$ac_cs_version"; exit ;; - --config | --confi | --conf | --con | --co | --c ) - $as_echo "$ac_cs_config"; exit ;; - --debug | --debu | --deb | --de | --d | -d ) - debug=: ;; - --file | --fil | --fi | --f ) - $ac_shift - case $ac_optarg in - *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; - '') as_fn_error $? "missing file argument" ;; - esac - as_fn_append CONFIG_FILES " '$ac_optarg'" - ac_need_defaults=false;; - --he | --h | --help | --hel | -h ) - $as_echo "$ac_cs_usage"; exit ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil | --si | --s) - ac_cs_silent=: ;; - - # This is an error. - -*) as_fn_error $? "unrecognized option: \`$1' -Try \`$0 --help' for more information." ;; - - *) as_fn_append ac_config_targets " $1" - ac_need_defaults=false ;; - - esac - shift -done - -ac_configure_extra_args= - -if $ac_cs_silent; then - exec 6>/dev/null - ac_configure_extra_args="$ac_configure_extra_args --silent" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -if \$ac_cs_recheck; then - set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion - shift - \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 - CONFIG_SHELL='$SHELL' - export CONFIG_SHELL - exec "\$@" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -exec 5>>config.log -{ - echo - sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX -## Running $as_me. ## -_ASBOX - $as_echo "$ac_log" -} >&5 - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - -# Handling of arguments. -for ac_config_target in $ac_config_targets -do - case $ac_config_target in - "makefile.inc") CONFIG_FILES="$CONFIG_FILES makefile.inc" ;; - - *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; - esac -done - - -# If the user did not use the arguments to specify the items to instantiate, -# then the envvar interface is used. Set only those that are not. -# We use the long form for the default assignment because of an extremely -# bizarre bug on SunOS 4.1.3. -if $ac_need_defaults; then - test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files -fi - -# Have a temporary directory for convenience. Make it in the build tree -# simply because there is no reason against having it here, and in addition, -# creating and moving files from /tmp can sometimes cause problems. -# Hook for its removal unless debugging. -# Note that there is a small window in which the directory will not be cleaned: -# after its creation but before its name has been assigned to `$tmp'. -$debug || -{ - tmp= ac_tmp= - trap 'exit_status=$? - : "${ac_tmp:=$tmp}" - { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status -' 0 - trap 'as_fn_exit 1' 1 2 13 15 -} -# Create a (secure) tmp directory for tmp files. - -{ - tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && - test -d "$tmp" -} || -{ - tmp=./conf$$-$RANDOM - (umask 077 && mkdir "$tmp") -} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 -ac_tmp=$tmp - -# Set up the scripts for CONFIG_FILES section. -# No need to generate them if there are no CONFIG_FILES. -# This happens for instance with `./config.status config.h'. -if test -n "$CONFIG_FILES"; then - - -ac_cr=`echo X | tr X '\015'` -# On cygwin, bash can eat \r inside `` if the user requested igncr. -# But we know of no other shell where ac_cr would be empty at this -# point, so we can use a bashism as a fallback. -if test "x$ac_cr" = x; then - eval ac_cr=\$\'\\r\' -fi -ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` -if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then - ac_cs_awk_cr='\\r' -else - ac_cs_awk_cr=$ac_cr -fi - -echo 'BEGIN {' >"$ac_tmp/subs1.awk" && -_ACEOF - - -{ - echo "cat >conf$$subs.awk <<_ACEOF" && - echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && - echo "_ACEOF" -} >conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 -ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` -ac_delim='%!_!# ' -for ac_last_try in false false false false false :; do - . ./conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - - ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` - if test $ac_delim_n = $ac_delim_num; then - break - elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - else - ac_delim="$ac_delim!$ac_delim _$ac_delim!! " - fi -done -rm -f conf$$subs.sh - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && -_ACEOF -sed -n ' -h -s/^/S["/; s/!.*/"]=/ -p -g -s/^[^!]*!// -:repl -t repl -s/'"$ac_delim"'$// -t delim -:nl -h -s/\(.\{148\}\)..*/\1/ -t more1 -s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ -p -n -b repl -:more1 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t nl -:delim -h -s/\(.\{148\}\)..*/\1/ -t more2 -s/["\\]/\\&/g; s/^/"/; s/$/"/ -p -b -:more2 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t delim -' >$CONFIG_STATUS || ac_write_fail=1 -rm -f conf$$subs.awk -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -_ACAWK -cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && - for (key in S) S_is_set[key] = 1 - FS = "" - -} -{ - line = $ 0 - nfields = split(line, field, "@") - substed = 0 - len = length(field[1]) - for (i = 2; i < nfields; i++) { - key = field[i] - keylen = length(key) - if (S_is_set[key]) { - value = S[key] - line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) - len += length(value) + length(field[++i]) - substed = 1 - } else - len += 1 + keylen - } - - print line -} - -_ACAWK -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then - sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" -else - cat -fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ - || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 -_ACEOF - -# VPATH may cause trouble with some makes, so we remove sole $(srcdir), -# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and -# trailing colons and then remove the whole line if VPATH becomes empty -# (actually we leave an empty line to preserve line numbers). -if test "x$srcdir" = x.; then - ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ -h -s/// -s/^/:/ -s/[ ]*$/:/ -s/:\$(srcdir):/:/g -s/:\${srcdir}:/:/g -s/:@srcdir@:/:/g -s/^:*// -s/:*$// -x -s/\(=[ ]*\).*/\1/ -G -s/\n// -s/^[^=]*=[ ]*$// -}' -fi - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -fi # test -n "$CONFIG_FILES" - - -eval set X " :F $CONFIG_FILES " -shift -for ac_tag -do - case $ac_tag in - :[FHLC]) ac_mode=$ac_tag; continue;; - esac - case $ac_mode$ac_tag in - :[FHL]*:*);; - :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; - :[FH]-) ac_tag=-:-;; - :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; - esac - ac_save_IFS=$IFS - IFS=: - set x $ac_tag - IFS=$ac_save_IFS - shift - ac_file=$1 - shift - - case $ac_mode in - :L) ac_source=$1;; - :[FH]) - ac_file_inputs= - for ac_f - do - case $ac_f in - -) ac_f="$ac_tmp/stdin";; - *) # Look for the file first in the build tree, then in the source tree - # (if the path is not absolute). The absolute path cannot be DOS-style, - # because $ac_f cannot contain `:'. - test -f "$ac_f" || - case $ac_f in - [\\/$]*) false;; - *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; - esac || - as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; - esac - case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac - as_fn_append ac_file_inputs " '$ac_f'" - done - - # Let's still pretend it is `configure' which instantiates (i.e., don't - # use $as_me), people would be surprised to read: - # /* config.h. Generated by config.status. */ - configure_input='Generated from '` - $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' - `' by configure.' - if test x"$ac_file" != x-; then - configure_input="$ac_file. $configure_input" - { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 -$as_echo "$as_me: creating $ac_file" >&6;} - fi - # Neutralize special characters interpreted by sed in replacement strings. - case $configure_input in #( - *\&* | *\|* | *\\* ) - ac_sed_conf_input=`$as_echo "$configure_input" | - sed 's/[\\\\&|]/\\\\&/g'`;; #( - *) ac_sed_conf_input=$configure_input;; - esac - - case $ac_tag in - *:-:* | *:-) cat >"$ac_tmp/stdin" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; - esac - ;; - esac - - ac_dir=`$as_dirname -- "$ac_file" || -$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$ac_file" : 'X\(//\)[^/]' \| \ - X"$ac_file" : 'X\(//\)$' \| \ - X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$ac_file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - as_dir="$ac_dir"; as_fn_mkdir_p - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - - case $ac_mode in - :F) - # - # CONFIG_FILE - # - - ac_MKDIR_P=$MKDIR_P - case $MKDIR_P in - [\\/$]* | ?:[\\/]* ) ;; - */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; - esac -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# If the template does not know about datarootdir, expand it. -# FIXME: This hack should be removed a few years after 2.60. -ac_datarootdir_hack=; ac_datarootdir_seen= -ac_sed_dataroot=' -/datarootdir/ { - p - q -} -/@datadir@/p -/@docdir@/p -/@infodir@/p -/@localedir@/p -/@mandir@/p' -case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in -*datarootdir*) ac_datarootdir_seen=yes;; -*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 -$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 - ac_datarootdir_hack=' - s&@datadir@&$datadir&g - s&@docdir@&$docdir&g - s&@infodir@&$infodir&g - s&@localedir@&$localedir&g - s&@mandir@&$mandir&g - s&\\\${datarootdir}&$datarootdir&g' ;; -esac -_ACEOF - -# Neutralize VPATH when `$srcdir' = `.'. -# Shell code in configure.ac might set extrasub. -# FIXME: do we really want to maintain this feature? -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_sed_extra="$ac_vpsub -$extrasub -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -:t -/@[a-zA-Z_][a-zA-Z_0-9]*@/!b -s|@configure_input@|$ac_sed_conf_input|;t t -s&@top_builddir@&$ac_top_builddir_sub&;t t -s&@top_build_prefix@&$ac_top_build_prefix&;t t -s&@srcdir@&$ac_srcdir&;t t -s&@abs_srcdir@&$ac_abs_srcdir&;t t -s&@top_srcdir@&$ac_top_srcdir&;t t -s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t -s&@builddir@&$ac_builddir&;t t -s&@abs_builddir@&$ac_abs_builddir&;t t -s&@abs_top_builddir@&$ac_abs_top_builddir&;t t -s&@MKDIR_P@&$ac_MKDIR_P&;t t -$ac_datarootdir_hack -" -eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ - >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - -test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && - { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && - { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ - "$ac_tmp/out"`; test -z "$ac_out"; } && - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&5 -$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&2;} - - rm -f "$ac_tmp/stdin" - case $ac_file in - -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; - *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; - esac \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - ;; - - - - esac - -done # for ac_tag - - -as_fn_exit 0 -_ACEOF -ac_clean_files=$ac_clean_files_save - -test $ac_write_fail = 0 || - as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 - - -# configure is writing to config.log, and then calls config.status. -# config.status does its own redirection, appending to config.log. -# Unfortunately, on DOS this fails, as config.log is still kept open -# by configure, so config.status won't be able to write to it; its -# output is simply discarded. So we exec the FD to /dev/null, -# effectively closing config.log, so it can be properly (re)opened and -# appended to by config.status. When coming back to configure, we -# need to make the FD available again. -if test "$no_create" != yes; then - ac_cs_success=: - ac_config_status_args= - test "$silent" = yes && - ac_config_status_args="$ac_config_status_args --quiet" - exec 5>/dev/null - $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false - exec 5>>config.log - # Use ||, not &&, to avoid exiting from the if with $? = 1, which - # would make configure fail if this is the last instruction. - $ac_cs_success || as_fn_exit 1 -fi -if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 -$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} -fi - diff --git a/core/src/index/thirdparty/faiss/configure.ac b/core/src/index/thirdparty/faiss/configure.ac deleted file mode 100644 index 31b587b86d..0000000000 --- a/core/src/index/thirdparty/faiss/configure.ac +++ /dev/null @@ -1,70 +0,0 @@ -# -*- Autoconf -*- -# Process this file with autoconf to produce a configure script. - -AC_PREREQ([2.69]) -AC_INIT([faiss], [1.0]) -AC_COPYRIGHT([Copyright (c) Facebook, Inc. and its affiliates. - -This source code is licensed under the MIT license found in the -LICENSE file in the root directory of this source tree.]) -AC_CONFIG_SRCDIR([Index.h]) -AC_CONFIG_AUX_DIR([build-aux]) -AC_CONFIG_MACRO_DIR([acinclude]) - -: ${CXXFLAGS="-g -O3 -Wall -Wextra"} - -# Checks for programs. -AC_LANG(C++) -AC_PROG_CXX -AX_CXX_COMPILE_STDCXX([11], [noext], [mandatory]) -AC_PROG_CPP -AC_PROG_MAKE_SET -AC_PROG_MKDIR_P - -FA_PYTHON - -if test x$PYTHON != x; then - FA_NUMPY -fi - -FA_PROG_SWIG - -FA_CHECK_CUDA - - -# Checks for header files. -AC_CHECK_HEADERS([float.h limits.h stddef.h stdint.h stdlib.h string.h sys/time.h unistd.h]) - -# Checks for typedefs, structures, and compiler characteristics. -AC_CHECK_HEADER_STDBOOL -AC_C_INLINE -AC_TYPE_INT32_T -AC_TYPE_INT64_T -AC_C_RESTRICT -AC_TYPE_SIZE_T -AC_TYPE_UINT16_T -AC_TYPE_UINT32_T -AC_TYPE_UINT64_T -AC_TYPE_UINT8_T - -# Checks for library functions. -AC_FUNC_MALLOC -AC_FUNC_MMAP -AC_CHECK_FUNCS([clock_gettime floor gettimeofday memmove memset munmap pow sqrt strerror strstr]) - -AC_OPENMP - -AX_BLAS -if test "x$ax_blas_ok" == "xno"; then - AC_MSG_ERROR([An implementation of BLAS is required but none was found.]) -fi - -AX_LAPACK -if test "x$ax_lapack_ok" == "xno"; then - AC_MSG_ERROR([An implementation of LAPACK is required but none was found.]) -fi - -AX_CPU_ARCH - -AC_CONFIG_FILES([makefile.inc]) -AC_OUTPUT diff --git a/core/src/index/thirdparty/faiss/demos/Makefile b/core/src/index/thirdparty/faiss/demos/Makefile deleted file mode 100644 index 9d871697a9..0000000000 --- a/core/src/index/thirdparty/faiss/demos/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - --include ../makefile.inc - -DEMOS_SRC=$(wildcard demo_*.cpp) -DEMOS=$(DEMOS_SRC:.cpp=) - - -all: $(DEMOS) - -clean: - rm -f $(DEMOS) - -%: %.cpp - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(CPUFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) -lfaiss - - -.PHONY: all clean diff --git a/core/src/index/thirdparty/faiss/demos/README.md b/core/src/index/thirdparty/faiss/demos/README.md deleted file mode 100644 index 71a23f272e..0000000000 --- a/core/src/index/thirdparty/faiss/demos/README.md +++ /dev/null @@ -1,28 +0,0 @@ - - -Demos for a few Faiss functionalities -===================================== - - -demo_auto_tune.py ------------------ - -Demonstrates the auto-tuning functionality of Faiss - - -demo_ondisk_ivf.py ------------------- - -Shows how to construct a Faiss index that stores the inverted file -data on disk, eg. when it does not fit in RAM. The script works on a -small dataset (sift1M) for demonstration and proceeds in stages: - -0: train on the dataset - -1-4: build 4 indexes, each containing 1/4 of the dataset. This can be -done in parallel on several machines - -5: merge the 4 indexes into one that is written directly to disk -(needs not to fit in RAM) - -6: load and test the index diff --git a/core/src/index/thirdparty/faiss/demos/demo_auto_tune.py b/core/src/index/thirdparty/faiss/demos/demo_auto_tune.py deleted file mode 100644 index eb7c709a1b..0000000000 --- a/core/src/index/thirdparty/faiss/demos/demo_auto_tune.py +++ /dev/null @@ -1,170 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#!/usr/bin/env python2 - -from __future__ import print_function -import os -import time -import numpy as np - -try: - import matplotlib - matplotlib.use('Agg') - from matplotlib import pyplot - graphical_output = True -except ImportError: - graphical_output = False - -import faiss - -################################################################# -# Small I/O functions -################################################################# - -def ivecs_read(fname): - a = np.fromfile(fname, dtype="int32") - d = a[0] - return a.reshape(-1, d + 1)[:, 1:].copy() - -def fvecs_read(fname): - return ivecs_read(fname).view('float32') - - -def plot_OperatingPoints(ops, nq, **kwargs): - ops = ops.optimal_pts - n = ops.size() * 2 - 1 - pyplot.plot([ops.at( i // 2).perf for i in range(n)], - [ops.at((i + 1) // 2).t / nq * 1000 for i in range(n)], - **kwargs) - - -################################################################# -# prepare common data for all indexes -################################################################# - - - -t0 = time.time() - -print("load data") - -xt = fvecs_read("sift1M/sift_learn.fvecs") -xb = fvecs_read("sift1M/sift_base.fvecs") -xq = fvecs_read("sift1M/sift_query.fvecs") - -d = xt.shape[1] - -print("load GT") - -gt = ivecs_read("sift1M/sift_groundtruth.ivecs") -gt = gt.astype('int64') -k = gt.shape[1] - -print("prepare criterion") - -# criterion = 1-recall at 1 -crit = faiss.OneRecallAtRCriterion(xq.shape[0], 1) -crit.set_groundtruth(None, gt) -crit.nnn = k - -# indexes that are useful when there is no limitation on memory usage -unlimited_mem_keys = [ - "IMI2x10,Flat", "IMI2x11,Flat", - "IVF4096,Flat", "IVF16384,Flat", - "PCA64,IMI2x10,Flat"] - -# memory limited to 16 bytes / vector -keys_mem_16 = [ - 'IMI2x10,PQ16', 'IVF4096,PQ16', - 'IMI2x10,PQ8+8', 'OPQ16_64,IMI2x10,PQ16' - ] - -# limited to 32 bytes / vector -keys_mem_32 = [ - 'IMI2x10,PQ32', 'IVF4096,PQ32', 'IVF16384,PQ32', - 'IMI2x10,PQ16+16', - 'OPQ32,IVF4096,PQ32', 'IVF4096,PQ16+16', 'OPQ16,IMI2x10,PQ16+16' - ] - -# indexes that can run on the GPU -keys_gpu = [ - "PCA64,IVF4096,Flat", - "PCA64,Flat", "Flat", "IVF4096,Flat", "IVF16384,Flat", - "IVF4096,PQ32"] - - -keys_to_test = unlimited_mem_keys -use_gpu = False - - -if use_gpu: - # if this fails, it means that the GPU version was not comp - assert faiss.StandardGpuResources, \ - "FAISS was not compiled with GPU support, or loading _swigfaiss_gpu.so failed" - res = faiss.StandardGpuResources() - dev_no = 0 - -# remember results from other index types -op_per_key = [] - - -# keep track of optimal operating points seen so far -op = faiss.OperatingPoints() - - -for index_key in keys_to_test: - - print("============ key", index_key) - - # make the index described by the key - index = faiss.index_factory(d, index_key) - - - if use_gpu: - # transfer to GPU (may be partial) - index = faiss.index_cpu_to_gpu(res, dev_no, index) - params = faiss.GpuParameterSpace() - else: - params = faiss.ParameterSpace() - - params.initialize(index) - - print("[%.3f s] train & add" % (time.time() - t0)) - - index.train(xt) - index.add(xb) - - print("[%.3f s] explore op points" % (time.time() - t0)) - - # find operating points for this index - opi = params.explore(index, xq, crit) - - print("[%.3f s] result operating points:" % (time.time() - t0)) - opi.display() - - # update best operating points so far - op.merge_with(opi, index_key + " ") - - op_per_key.append((index_key, opi)) - - if graphical_output: - # graphical output (to tmp/ subdirectory) - - fig = pyplot.figure(figsize=(12, 9)) - pyplot.xlabel("1-recall at 1") - pyplot.ylabel("search time (ms/query, %d threads)" % faiss.omp_get_max_threads()) - pyplot.gca().set_yscale('log') - pyplot.grid() - for i2, opi2 in op_per_key: - plot_OperatingPoints(opi2, crit.nq, label = i2, marker = 'o') - # plot_OperatingPoints(op, crit.nq, label = 'best', marker = 'o', color = 'r') - pyplot.legend(loc=2) - fig.savefig('tmp/demo_auto_tune.png') - - -print("[%.3f s] final result:" % (time.time() - t0)) - -op.display() diff --git a/core/src/index/thirdparty/faiss/demos/demo_imi_flat.cpp b/core/src/index/thirdparty/faiss/demos/demo_imi_flat.cpp deleted file mode 100644 index b037817321..0000000000 --- a/core/src/index/thirdparty/faiss/demos/demo_imi_flat.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - - -#include -#include -#include - -#include - - -#include -#include -#include -#include - -double elapsed () -{ - struct timeval tv; - gettimeofday (&tv, nullptr); - return tv.tv_sec + tv.tv_usec * 1e-6; -} - - -int main () -{ - double t0 = elapsed(); - - // dimension of the vectors to index - int d = 128; - - // size of the database we plan to index - size_t nb = 1000 * 1000; - - // make a set of nt training vectors in the unit cube - // (could be the database) - size_t nt = 100 * 1000; - - //--------------------------------------------------------------- - // Define the core quantizer - // We choose a multiple inverted index for faster training with less data - // and because it usually offers best accuracy/speed trade-offs - // - // We here assume that its lifespan of this coarse quantizer will cover the - // lifespan of the inverted-file quantizer IndexIVFFlat below - // With dynamic allocation, one may give the responsability to free the - // quantizer to the inverted-file index (with attribute do_delete_quantizer) - // - // Note: a regular clustering algorithm would be defined as: - // faiss::IndexFlatL2 coarse_quantizer (d); - // - // Use nhash=2 subquantizers used to define the product coarse quantizer - // Number of bits: we will have 2^nbits_coarse centroids per subquantizer - // meaning (2^12)^nhash distinct inverted lists - size_t nhash = 2; - size_t nbits_subq = int (log2 (nb+1) / 2); // good choice in general - size_t ncentroids = 1 << (nhash * nbits_subq); // total # of centroids - - faiss::MultiIndexQuantizer coarse_quantizer (d, nhash, nbits_subq); - - printf ("IMI (%ld,%ld): %ld virtual centroids (target: %ld base vectors)", - nhash, nbits_subq, ncentroids, nb); - - // the coarse quantizer should not be dealloced before the index - // 4 = nb of bytes per code (d must be a multiple of this) - // 8 = nb of bits per sub-code (almost always 8) - faiss::MetricType metric = faiss::METRIC_L2; // can be METRIC_INNER_PRODUCT - faiss::IndexIVFFlat index (&coarse_quantizer, d, ncentroids, metric); - index.quantizer_trains_alone = true; - - // define the number of probes. 2048 is for high-dim, overkilled in practice - // Use 4-1024 depending on the trade-off speed accuracy that you want - index.nprobe = 2048; - - - { // training - printf ("[%.3f s] Generating %ld vectors in %dD for training\n", - elapsed() - t0, nt, d); - - std::vector trainvecs (nt * d); - for (size_t i = 0; i < nt * d; i++) { - trainvecs[i] = drand48(); - } - - printf ("[%.3f s] Training the index\n", elapsed() - t0); - index.verbose = true; - index.train (nt, trainvecs.data()); - } - - size_t nq; - std::vector queries; - - { // populating the database - printf ("[%.3f s] Building a dataset of %ld vectors to index\n", - elapsed() - t0, nb); - - std::vector database (nb * d); - for (size_t i = 0; i < nb * d; i++) { - database[i] = drand48(); - } - - printf ("[%.3f s] Adding the vectors to the index\n", elapsed() - t0); - - index.add (nb, database.data()); - - // remember a few elements from the database as queries - int i0 = 1234; - int i1 = 1244; - - nq = i1 - i0; - queries.resize (nq * d); - for (int i = i0; i < i1; i++) { - for (int j = 0; j < d; j++) { - queries [(i - i0) * d + j] = database [i * d + j]; - } - } - } - - { // searching the database - int k = 5; - printf ("[%.3f s] Searching the %d nearest neighbors " - "of %ld vectors in the index\n", - elapsed() - t0, k, nq); - - std::vector nns (k * nq); - std::vector dis (k * nq); - - index.search (nq, queries.data(), k, dis.data(), nns.data()); - - printf ("[%.3f s] Query results (vector ids, then distances):\n", - elapsed() - t0); - - for (int i = 0; i < nq; i++) { - printf ("query %2d: ", i); - for (int j = 0; j < k; j++) { - printf ("%7ld ", nns[j + i * k]); - } - printf ("\n dis: "); - for (int j = 0; j < k; j++) { - printf ("%7g ", dis[j + i * k]); - } - printf ("\n"); - } - } - return 0; -} diff --git a/core/src/index/thirdparty/faiss/demos/demo_imi_pq.cpp b/core/src/index/thirdparty/faiss/demos/demo_imi_pq.cpp deleted file mode 100644 index ea6f998c6e..0000000000 --- a/core/src/index/thirdparty/faiss/demos/demo_imi_pq.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - - -#include -#include -#include - -#include - - -#include -#include -#include -#include - -double elapsed () -{ - struct timeval tv; - gettimeofday (&tv, nullptr); - return tv.tv_sec + tv.tv_usec * 1e-6; -} - - -int main () -{ - double t0 = elapsed(); - - // dimension of the vectors to index - int d = 64; - - // size of the database we plan to index - size_t nb = 1000 * 1000; - size_t add_bs = 10000; // # size of the blocks to add - - // make a set of nt training vectors in the unit cube - // (could be the database) - size_t nt = 100 * 1000; - - //--------------------------------------------------------------- - // Define the core quantizer - // We choose a multiple inverted index for faster training with less data - // and because it usually offers best accuracy/speed trade-offs - // - // We here assume that its lifespan of this coarse quantizer will cover the - // lifespan of the inverted-file quantizer IndexIVFFlat below - // With dynamic allocation, one may give the responsability to free the - // quantizer to the inverted-file index (with attribute do_delete_quantizer) - // - // Note: a regular clustering algorithm would be defined as: - // faiss::IndexFlatL2 coarse_quantizer (d); - // - // Use nhash=2 subquantizers used to define the product coarse quantizer - // Number of bits: we will have 2^nbits_coarse centroids per subquantizer - // meaning (2^12)^nhash distinct inverted lists - // - // The parameter bytes_per_code is determined by the memory - // constraint, the dataset will use nb * (bytes_per_code + 8) - // bytes. - // - // The parameter nbits_subq is determined by the size of the dataset to index. - // - size_t nhash = 2; - size_t nbits_subq = 9; - size_t ncentroids = 1 << (nhash * nbits_subq); // total # of centroids - int bytes_per_code = 16; - - faiss::MultiIndexQuantizer coarse_quantizer (d, nhash, nbits_subq); - - printf ("IMI (%ld,%ld): %ld virtual centroids (target: %ld base vectors)", - nhash, nbits_subq, ncentroids, nb); - - // the coarse quantizer should not be dealloced before the index - // 4 = nb of bytes per code (d must be a multiple of this) - // 8 = nb of bits per sub-code (almost always 8) - faiss::MetricType metric = faiss::METRIC_L2; // can be METRIC_INNER_PRODUCT - faiss::IndexIVFPQ index (&coarse_quantizer, d, ncentroids, bytes_per_code, 8); - index.quantizer_trains_alone = true; - - // define the number of probes. 2048 is for high-dim, overkill in practice - // Use 4-1024 depending on the trade-off speed accuracy that you want - index.nprobe = 2048; - - - { // training. - - // The distribution of the training vectors should be the same - // as the database vectors. It could be a sub-sample of the - // database vectors, if sampling is not biased. Here we just - // randomly generate the vectors. - - printf ("[%.3f s] Generating %ld vectors in %dD for training\n", - elapsed() - t0, nt, d); - - std::vector trainvecs (nt * d); - for (size_t i = 0; i < nt; i++) { - for (size_t j = 0; j < d; j++) { - trainvecs[i * d + j] = drand48(); - } - } - - printf ("[%.3f s] Training the index\n", elapsed() - t0); - index.verbose = true; - index.train (nt, trainvecs.data()); - } - - // the index can be re-loaded later with - // faiss::Index * idx = faiss::read_index("/tmp/trained_index.faissindex"); - faiss::write_index(&index, "/tmp/trained_index.faissindex"); - - size_t nq; - std::vector queries; - - { // populating the database - printf ("[%.3f s] Building a dataset of %ld vectors to index\n", - elapsed() - t0, nb); - - std::vector database (nb * d); - std::vector ids (nb); - for (size_t i = 0; i < nb; i++) { - for (size_t j = 0; j < d; j++) { - database[i * d + j] = drand48(); - } - ids[i] = 8760000000L + i; - } - - printf ("[%.3f s] Adding the vectors to the index\n", elapsed() - t0); - - for (size_t begin = 0; begin < nb; begin += add_bs) { - size_t end = std::min (begin + add_bs, nb); - index.add_with_ids (end - begin, - database.data() + d * begin, - ids.data() + begin); - } - - // remember a few elements from the database as queries - int i0 = 1234; - int i1 = 1244; - - nq = i1 - i0; - queries.resize (nq * d); - for (int i = i0; i < i1; i++) { - for (int j = 0; j < d; j++) { - queries [(i - i0) * d + j] = database [i * d + j]; - } - } - } - - // A few notes on the internal format of the index: - // - // - the positing lists for PQ codes are index.codes, which is a - // std::vector < std::vector > - // if n is the length of posting list #i, codes[i] has length bytes_per_code * n - // - // - the corresponding ids are stored in index.ids - // - // - given a vector float *x, finding which k centroids are - // closest to it (ie to find the nearest neighbors) can be done with - // - // long *centroid_ids = new long[k]; - // float *distances = new float[k]; - // index.quantizer->search (1, x, k, dis, centroids_ids); - // - - faiss::write_index(&index, "/tmp/populated_index.faissindex"); - - { // searching the database - int k = 5; - printf ("[%.3f s] Searching the %d nearest neighbors " - "of %ld vectors in the index\n", - elapsed() - t0, k, nq); - - std::vector nns (k * nq); - std::vector dis (k * nq); - - index.search (nq, queries.data(), k, dis.data(), nns.data()); - - printf ("[%.3f s] Query results (vector ids, then distances):\n", - elapsed() - t0); - - for (int i = 0; i < nq; i++) { - printf ("query %2d: ", i); - for (int j = 0; j < k; j++) { - printf ("%7ld ", nns[j + i * k]); - } - printf ("\n dis: "); - for (int j = 0; j < k; j++) { - printf ("%7g ", dis[j + i * k]); - } - printf ("\n"); - } - } - return 0; -} diff --git a/core/src/index/thirdparty/faiss/demos/demo_ivfpq_indexing.cpp b/core/src/index/thirdparty/faiss/demos/demo_ivfpq_indexing.cpp deleted file mode 100644 index 743395ec2f..0000000000 --- a/core/src/index/thirdparty/faiss/demos/demo_ivfpq_indexing.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - - -#include -#include -#include - -#include - - -#include -#include -#include - -double elapsed () -{ - struct timeval tv; - gettimeofday (&tv, NULL); - return tv.tv_sec + tv.tv_usec * 1e-6; -} - - -int main () -{ - - double t0 = elapsed(); - - // dimension of the vectors to index - int d = 128; - - // size of the database we plan to index - size_t nb = 200 * 1000; - - // make a set of nt training vectors in the unit cube - // (could be the database) - size_t nt = 100 * 1000; - - // make the index object and train it - faiss::IndexFlatL2 coarse_quantizer (d); - - // a reasonable number of centroids to index nb vectors - int ncentroids = int (4 * sqrt (nb)); - - // the coarse quantizer should not be dealloced before the index - // 4 = nb of bytes per code (d must be a multiple of this) - // 8 = nb of bits per sub-code (almost always 8) - faiss::IndexIVFPQ index (&coarse_quantizer, d, - ncentroids, 4, 8); - - - { // training - printf ("[%.3f s] Generating %ld vectors in %dD for training\n", - elapsed() - t0, nt, d); - - std::vector trainvecs (nt * d); - for (size_t i = 0; i < nt * d; i++) { - trainvecs[i] = drand48(); - } - - printf ("[%.3f s] Training the index\n", - elapsed() - t0); - index.verbose = true; - - index.train (nt, trainvecs.data()); - } - - { // I/O demo - const char *outfilename = "/tmp/index_trained.faissindex"; - printf ("[%.3f s] storing the pre-trained index to %s\n", - elapsed() - t0, outfilename); - - write_index (&index, outfilename); - } - - size_t nq; - std::vector queries; - - { // populating the database - printf ("[%.3f s] Building a dataset of %ld vectors to index\n", - elapsed() - t0, nb); - - std::vector database (nb * d); - for (size_t i = 0; i < nb * d; i++) { - database[i] = drand48(); - } - - printf ("[%.3f s] Adding the vectors to the index\n", - elapsed() - t0); - - index.add (nb, database.data()); - - printf ("[%.3f s] imbalance factor: %g\n", - elapsed() - t0, index.invlists->imbalance_factor ()); - - // remember a few elements from the database as queries - int i0 = 1234; - int i1 = 1243; - - nq = i1 - i0; - queries.resize (nq * d); - for (int i = i0; i < i1; i++) { - for (int j = 0; j < d; j++) { - queries [(i - i0) * d + j] = database [i * d + j]; - } - } - - } - - { // searching the database - int k = 5; - printf ("[%.3f s] Searching the %d nearest neighbors " - "of %ld vectors in the index\n", - elapsed() - t0, k, nq); - - std::vector nns (k * nq); - std::vector dis (k * nq); - - index.search (nq, queries.data(), k, dis.data(), nns.data()); - - printf ("[%.3f s] Query results (vector ids, then distances):\n", - elapsed() - t0); - - for (int i = 0; i < nq; i++) { - printf ("query %2d: ", i); - for (int j = 0; j < k; j++) { - printf ("%7ld ", nns[j + i * k]); - } - printf ("\n dis: "); - for (int j = 0; j < k; j++) { - printf ("%7g ", dis[j + i * k]); - } - printf ("\n"); - } - - printf ("note that the nearest neighbor is not at " - "distance 0 due to quantization errors\n"); - } - - return 0; -} diff --git a/core/src/index/thirdparty/faiss/demos/demo_ondisk_ivf.py b/core/src/index/thirdparty/faiss/demos/demo_ondisk_ivf.py deleted file mode 100644 index c89acc8402..0000000000 --- a/core/src/index/thirdparty/faiss/demos/demo_ondisk_ivf.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#!/usr/bin/env python2 - -import sys -import numpy as np -import faiss - - -################################################################# -# Small I/O functions -################################################################# - - -def ivecs_read(fname): - a = np.fromfile(fname, dtype='int32') - d = a[0] - return a.reshape(-1, d + 1)[:, 1:].copy() - - -def fvecs_read(fname): - return ivecs_read(fname).view('float32') - - -################################################################# -# Main program -################################################################# - -stage = int(sys.argv[1]) - -tmpdir = '/tmp/' - -if stage == 0: - # train the index - xt = fvecs_read("sift1M/sift_learn.fvecs") - index = faiss.index_factory(xt.shape[1], "IVF4096,Flat") - print("training index") - index.train(xt) - print("write " + tmpdir + "trained.index") - faiss.write_index(index, tmpdir + "trained.index") - - -if 1 <= stage <= 4: - # add 1/4 of the database to 4 independent indexes - bno = stage - 1 - xb = fvecs_read("sift1M/sift_base.fvecs") - i0, i1 = int(bno * xb.shape[0] / 4), int((bno + 1) * xb.shape[0] / 4) - index = faiss.read_index(tmpdir + "trained.index") - print("adding vectors %d:%d" % (i0, i1)) - index.add_with_ids(xb[i0:i1], np.arange(i0, i1)) - print("write " + tmpdir + "block_%d.index" % bno) - faiss.write_index(index, tmpdir + "block_%d.index" % bno) - - -if stage == 5: - # merge the images into an on-disk index - # first load the inverted lists - ivfs = [] - for bno in range(4): - # the IO_FLAG_MMAP is to avoid actually loading the data thus - # the total size of the inverted lists can exceed the - # available RAM - print("read " + tmpdir + "block_%d.index" % bno) - index = faiss.read_index(tmpdir + "block_%d.index" % bno, - faiss.IO_FLAG_MMAP) - ivfs.append(index.invlists) - - # avoid that the invlists get deallocated with the index - index.own_invlists = False - - # construct the output index - index = faiss.read_index(tmpdir + "trained.index") - - # prepare the output inverted lists. They will be written - # to merged_index.ivfdata - invlists = faiss.OnDiskInvertedLists( - index.nlist, index.code_size, - tmpdir + "merged_index.ivfdata") - - # merge all the inverted lists - ivf_vector = faiss.InvertedListsPtrVector() - for ivf in ivfs: - ivf_vector.push_back(ivf) - - print("merge %d inverted lists " % ivf_vector.size()) - ntotal = invlists.merge_from(ivf_vector.data(), ivf_vector.size()) - - # now replace the inverted lists in the output index - index.ntotal = ntotal - index.replace_invlists(invlists) - - print("write " + tmpdir + "populated.index") - faiss.write_index(index, tmpdir + "populated.index") - - -if stage == 6: - # perform a search from disk - print("read " + tmpdir + "populated.index") - index = faiss.read_index(tmpdir + "populated.index") - index.nprobe = 16 - - # load query vectors and ground-truth - xq = fvecs_read("sift1M/sift_query.fvecs") - gt = ivecs_read("sift1M/sift_groundtruth.ivecs") - - D, I = index.search(xq, 5) - - recall_at_1 = (I[:, :1] == gt[:, :1]).sum() / float(xq.shape[0]) - print("recall@1: %.3f" % recall_at_1) diff --git a/core/src/index/thirdparty/faiss/demos/demo_sift1M.cpp b/core/src/index/thirdparty/faiss/demos/demo_sift1M.cpp deleted file mode 100644 index dd91c59080..0000000000 --- a/core/src/index/thirdparty/faiss/demos/demo_sift1M.cpp +++ /dev/null @@ -1,252 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include - -/** - * To run this demo, please download the ANN_SIFT1M dataset from - * - * http://corpus-texmex.irisa.fr/ - * - * and unzip it to the sudirectory sift1M. - **/ - -/***************************************************** - * I/O functions for fvecs and ivecs - *****************************************************/ - - -float * fvecs_read (const char *fname, - size_t *d_out, size_t *n_out) -{ - FILE *f = fopen(fname, "r"); - if(!f) { - fprintf(stderr, "could not open %s\n", fname); - perror(""); - abort(); - } - int d; - fread(&d, 1, sizeof(int), f); - assert((d > 0 && d < 1000000) || !"unreasonable dimension"); - fseek(f, 0, SEEK_SET); - struct stat st; - fstat(fileno(f), &st); - size_t sz = st.st_size; - assert(sz % ((d + 1) * 4) == 0 || !"weird file size"); - size_t n = sz / ((d + 1) * 4); - - *d_out = d; *n_out = n; - float *x = new float[n * (d + 1)]; - size_t nr = fread(x, sizeof(float), n * (d + 1), f); - assert(nr == n * (d + 1) || !"could not read whole file"); - - // shift array to remove row headers - for(size_t i = 0; i < n; i++) - memmove(x + i * d, x + 1 + i * (d + 1), d * sizeof(*x)); - - fclose(f); - return x; -} - -// not very clean, but works as long as sizeof(int) == sizeof(float) -int *ivecs_read(const char *fname, size_t *d_out, size_t *n_out) -{ - return (int*)fvecs_read(fname, d_out, n_out); -} - -double elapsed () -{ - struct timeval tv; - gettimeofday (&tv, nullptr); - return tv.tv_sec + tv.tv_usec * 1e-6; -} - - - -int main() -{ - double t0 = elapsed(); - - // this is typically the fastest one. - const char *index_key = "IVF4096,Flat"; - - // these ones have better memory usage - // const char *index_key = "Flat"; - // const char *index_key = "PQ32"; - // const char *index_key = "PCA80,Flat"; - // const char *index_key = "IVF4096,PQ8+16"; - // const char *index_key = "IVF4096,PQ32"; - // const char *index_key = "IMI2x8,PQ32"; - // const char *index_key = "IMI2x8,PQ8+16"; - // const char *index_key = "OPQ16_64,IMI2x8,PQ8+16"; - - faiss::Index * index; - - size_t d; - - { - printf ("[%.3f s] Loading train set\n", elapsed() - t0); - - size_t nt; - float *xt = fvecs_read("sift1M/sift_learn.fvecs", &d, &nt); - - printf ("[%.3f s] Preparing index \"%s\" d=%ld\n", - elapsed() - t0, index_key, d); - index = faiss::index_factory(d, index_key); - - printf ("[%.3f s] Training on %ld vectors\n", elapsed() - t0, nt); - - index->train(nt, xt); - delete [] xt; - } - - - { - printf ("[%.3f s] Loading database\n", elapsed() - t0); - - size_t nb, d2; - float *xb = fvecs_read("sift1M/sift_base.fvecs", &d2, &nb); - assert(d == d2 || !"dataset does not have same dimension as train set"); - - printf ("[%.3f s] Indexing database, size %ld*%ld\n", - elapsed() - t0, nb, d); - - index->add(nb, xb); - - delete [] xb; - } - - size_t nq; - float *xq; - - { - printf ("[%.3f s] Loading queries\n", elapsed() - t0); - - size_t d2; - xq = fvecs_read("sift1M/sift_query.fvecs", &d2, &nq); - assert(d == d2 || !"query does not have same dimension as train set"); - - } - - size_t k; // nb of results per query in the GT - faiss::Index::idx_t *gt; // nq * k matrix of ground-truth nearest-neighbors - - { - printf ("[%.3f s] Loading ground truth for %ld queries\n", - elapsed() - t0, nq); - - // load ground-truth and convert int to long - size_t nq2; - int *gt_int = ivecs_read("sift1M/sift_groundtruth.ivecs", &k, &nq2); - assert(nq2 == nq || !"incorrect nb of ground truth entries"); - - gt = new faiss::Index::idx_t[k * nq]; - for(int i = 0; i < k * nq; i++) { - gt[i] = gt_int[i]; - } - delete [] gt_int; - } - - // Result of the auto-tuning - std::string selected_params; - - { // run auto-tuning - - printf ("[%.3f s] Preparing auto-tune criterion 1-recall at 1 " - "criterion, with k=%ld nq=%ld\n", elapsed() - t0, k, nq); - - faiss::OneRecallAtRCriterion crit(nq, 1); - crit.set_groundtruth (k, nullptr, gt); - crit.nnn = k; // by default, the criterion will request only 1 NN - - printf ("[%.3f s] Preparing auto-tune parameters\n", elapsed() - t0); - - faiss::ParameterSpace params; - params.initialize(index); - - printf ("[%.3f s] Auto-tuning over %ld parameters (%ld combinations)\n", - elapsed() - t0, params.parameter_ranges.size(), - params.n_combinations()); - - faiss::OperatingPoints ops; - params.explore (index, nq, xq, crit, &ops); - - printf ("[%.3f s] Found the following operating points: \n", - elapsed() - t0); - - ops.display (); - - // keep the first parameter that obtains > 0.5 1-recall@1 - for (int i = 0; i < ops.optimal_pts.size(); i++) { - if (ops.optimal_pts[i].perf > 0.5) { - selected_params = ops.optimal_pts[i].key; - break; - } - } - assert (selected_params.size() >= 0 || - !"could not find good enough op point"); - } - - - { // Use the found configuration to perform a search - - faiss::ParameterSpace params; - - printf ("[%.3f s] Setting parameter configuration \"%s\" on index\n", - elapsed() - t0, selected_params.c_str()); - - params.set_index_parameters (index, selected_params.c_str()); - - printf ("[%.3f s] Perform a search on %ld queries\n", - elapsed() - t0, nq); - - // output buffers - faiss::Index::idx_t *I = new faiss::Index::idx_t[nq * k]; - float *D = new float[nq * k]; - - index->search(nq, xq, k, D, I); - - printf ("[%.3f s] Compute recalls\n", elapsed() - t0); - - // evaluate result by hand. - int n_1 = 0, n_10 = 0, n_100 = 0; - for(int i = 0; i < nq; i++) { - int gt_nn = gt[i * k]; - for(int j = 0; j < k; j++) { - if (I[i * k + j] == gt_nn) { - if(j < 1) n_1++; - if(j < 10) n_10++; - if(j < 100) n_100++; - } - } - } - printf("R@1 = %.4f\n", n_1 / float(nq)); - printf("R@10 = %.4f\n", n_10 / float(nq)); - printf("R@100 = %.4f\n", n_100 / float(nq)); - - } - - delete [] xq; - delete [] gt; - delete index; - return 0; -} diff --git a/core/src/index/thirdparty/faiss/demos/demo_weighted_kmeans.cpp b/core/src/index/thirdparty/faiss/demos/demo_weighted_kmeans.cpp deleted file mode 100644 index eee188e4b3..0000000000 --- a/core/src/index/thirdparty/faiss/demos/demo_weighted_kmeans.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -#include -#include -#include -#include -#include - - -namespace { - - -enum WeightedKMeansType { - WKMT_FlatL2, - WKMT_FlatIP, - WKMT_FlatIP_spherical, - WKMT_HNSW, -}; - - -float weighted_kmeans_clustering (size_t d, size_t n, size_t k, - const float *input, - const float *weights, - float *centroids, - WeightedKMeansType index_num) -{ - using namespace faiss; - Clustering clus (d, k); - clus.verbose = true; - - std::unique_ptr index; - - switch (index_num) { - case WKMT_FlatL2: - index.reset(new IndexFlatL2 (d)); - break; - case WKMT_FlatIP: - index.reset(new IndexFlatIP (d)); - break; - case WKMT_FlatIP_spherical: - index.reset(new IndexFlatIP (d)); - clus.spherical = true; - break; - case WKMT_HNSW: - IndexHNSWFlat *ihnsw = new IndexHNSWFlat (d, 32); - ihnsw->hnsw.efSearch = 128; - index.reset(ihnsw); - break; - } - - clus.train(n, input, *index.get(), weights); - // on output the index contains the centroids. - memcpy(centroids, clus.centroids.data(), sizeof(*centroids) * d * k); - return clus.iteration_stats.back().obj; -} - - -int d = 32; -float sigma = 0.1; - -#define BIGTEST - -#ifdef BIGTEST -// the production setup = setting of https://fb.quip.com/CWgnAAYbwtgs -int nc = 200000; -int n_big = 4; -int n_small = 2; -#else -int nc = 5; -int n_big = 100; -int n_small = 10; -#endif - -int n; // number of training points - -void generate_trainset (std::vector & ccent, - std::vector & x, - std::vector & weights) -{ - // same sampling as test_build_blocks.py test_weighted - - ccent.resize (d * 2 * nc); - faiss::float_randn (ccent.data(), d * 2 * nc, 123); - faiss::fvec_renorm_L2 (d, 2 * nc, ccent.data()); - n = nc * n_big + nc * n_small; - x.resize(d * n); - weights.resize(n); - faiss::float_randn (x.data(), x.size(), 1234); - - float *xi = x.data(); - float *w = weights.data(); - for (int ci = 0; ci < nc * 2; ci++) { // loop over centroids - int np = ci < nc ? n_big : n_small; // nb of points around this centroid - for (int i = 0; i < np; i++) { - for (int j = 0; j < d; j++) { - xi[j] = xi[j] * sigma + ccent[ci * d + j]; - } - *w++ = ci < nc ? 0.1 : 10; - xi += d; - } - } -} - -} - - -int main(int argc, char **argv) { - std::vector ccent; - std::vector x; - std::vector weights; - - printf("generate training set\n"); - generate_trainset(ccent, x, weights); - - std::vector centroids; - centroids.resize(nc * d); - - int the_index_num = -1; - int the_with_weights = -1; - - if (argc == 3) { - the_index_num = atoi(argv[1]); - the_with_weights = atoi(argv[2]); - } - - - for (int index_num = WKMT_FlatL2; - index_num <= WKMT_HNSW; - index_num++) { - - if (the_index_num >= 0 && index_num != the_index_num) { - continue; - } - - for (int with_weights = 0; with_weights <= 1; with_weights++) { - if (the_with_weights >= 0 && with_weights != the_with_weights) { - continue; - } - - printf("=================== index_num=%d Run %s weights\n", - index_num, with_weights ? "with" : "without"); - - weighted_kmeans_clustering ( - d, n, nc, x.data(), - with_weights ? weights.data() : nullptr, - centroids.data(), (WeightedKMeansType)index_num - ); - - { // compute distance of points to centroids - faiss::IndexFlatL2 cent_index(d); - cent_index.add(nc, centroids.data()); - std::vector dis (n); - std::vector idx (n); - - cent_index.search (nc * 2, ccent.data(), 1, - dis.data(), idx.data()); - - float dis1 = 0, dis2 = 0; - for (int i = 0; i < nc ; i++) { - dis1 += dis[i]; - } - printf("average distance of points from big clusters: %g\n", - dis1 / nc); - - for (int i = 0; i < nc ; i++) { - dis2 += dis[i + nc]; - } - - printf("average distance of points from small clusters: %g\n", - dis2 / nc); - - } - - } - } - return 0; -} diff --git a/core/src/index/thirdparty/faiss/example_makefiles/makefile.inc.Linux b/core/src/index/thirdparty/faiss/example_makefiles/makefile.inc.Linux deleted file mode 100644 index 12da227039..0000000000 --- a/core/src/index/thirdparty/faiss/example_makefiles/makefile.inc.Linux +++ /dev/null @@ -1,140 +0,0 @@ -# -*- makefile -*- -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -# tested on CentOS 7, Ubuntu 16 and Ubuntu 14, see below to adjust flags to distribution. - - -CXX = g++ -std=c++11 -CXXFLAGS = -fPIC -m64 -Wall -g -O3 -fopenmp -Wno-sign-compare -CPUFLAGS = -mavx -msse4 -mpopcnt -LDFLAGS = -fPIC -fopenmp - -# common linux flags -SHAREDEXT = so -SHAREDFLAGS = -shared -MKDIR_P = mkdir -p - -prefix ?= /usr/local -exec_prefix ?= ${prefix} -libdir = ${exec_prefix}/lib -includedir = ${prefix}/include - -########################################################################## -# Uncomment one of the 4 BLAS/Lapack implementation options -# below. They are sorted # from fastest to slowest (in our -# experiments). -########################################################################## - -# -# 1. Intel MKL -# -# This is the fastest BLAS implementation we tested. Unfortunately it -# is not open-source and determining the correct linking flags is a -# nightmare. See -# -# https://software.intel.com/en-us/articles/intel-mkl-link-line-advisor -# -# The latest tested version is MKL 2017.0.098 (2017 Initial Release) and can -# be downloaded here: -# -# https://registrationcenter.intel.com/en/forms/?productid=2558&licensetype=2 -# -# The following settings are working if MKL is installed on its default folder: -# -# MKLROOT = /opt/intel/compilers_and_libraries/linux/mkl/ -# -# LDFLAGS += -Wl,--no-as-needed -L$(MKLROOT)/lib/intel64 -# LIBS += -lmkl_intel_ilp64 -lmkl_core -lmkl_gnu_thread -ldl -lpthread -# -# CPPFLAGS += -DFINTEGER=long -# -# You may have to set the LD_LIBRARY_PATH=$MKLROOT/lib/intel64 at runtime. -# -# If at runtime you get the error: -# Intel MKL FATAL ERROR: Cannot load libmkl_avx2.so or libmkl_def.so -# you may set -# LD_PRELOAD=$MKLROOT/lib/intel64/libmkl_core.so:$MKLROOT/lib/intel64/libmkl_sequential.so -# at runtime as well. - -# -# 2. Openblas -# -# The library contains both BLAS and Lapack. About 30% slower than MKL. Please see -# https://github.com/facebookresearch/faiss/wiki/Troubleshooting#slow-brute-force-search-with-openblas -# to fix performance problemes with OpenBLAS - -# for Ubuntu 16: -# sudo apt-get install libopenblas-dev python-numpy python-dev - -# for Ubuntu 14: -# sudo apt-get install libopenblas-dev liblapack3 python-numpy python-dev - -CPPFLAGS += -DFINTEGER=int -LIBS += -lopenblas -llapack - -# 3. Atlas -# -# Automatically tuned linear algebra package. As the name indicates, -# it is tuned automatically for a give architecture, and in Linux -# distributions, it the architecture is typically indicated by the -# directory name, eg. atlas-sse3 = optimized for SSE3 architecture. -# -# BLASCFLAGS=-DFINTEGER=int -# BLASLDFLAGS=/usr/lib64/atlas-sse3/libptf77blas.so.3 /usr/lib64/atlas-sse3/liblapack.so -# -# 4. reference implementation -# -# This is just a compiled version of the reference BLAS -# implementation, that is not optimized at all. -# -# CPPFLAGS += -DFINTEGER=int -# LIBS += /usr/lib64/libblas.so.3 /usr/lib64/liblapack.so.3.2 -# - - -########################################################################## -# SWIG and Python flags -########################################################################## - -# SWIG executable. This should be at least version 3.x -SWIG = swig - -# The Python include directories for a given python executable can -# typically be found with -# -# python -c "import distutils.sysconfig; print distutils.sysconfig.get_python_inc()" -# python -c "import numpy ; print numpy.get_include()" -# -# or, for Python 3, with -# -# python3 -c "import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())" -# python3 -c "import numpy ; print(numpy.get_include())" -# - -PYTHONCFLAGS = -I/usr/include/python2.7/ -I/usr/lib64/python2.7/site-packages/numpy/core/include/ -PYTHONLIB = -lpython -PYTHON = /usr/bin/python - -########################################################################### -# Cuda GPU flags -########################################################################### - - - -# root of the cuda 8 installation -CUDAROOT = /usr/local/cuda-8.0 -NVCC = $(CUDAROOT)/bin/nvcc -NVCCLDFLAGS = -L$(CUDAROOT)/lib64 -NVCCLIBS = -lcudart -lcublas -lcuda -CUDACFLAGS = -I$(CUDAROOT)/include -NVCCFLAGS = -I $(CUDAROOT)/targets/x86_64-linux/include/ \ --Xcompiler -fPIC \ --Xcudafe --diag_suppress=unrecognized_attribute \ --gencode arch=compute_35,code="compute_35" \ --gencode arch=compute_52,code="compute_52" \ --gencode arch=compute_60,code="compute_60" \ --lineinfo \ --ccbin $(CXX) -DFAISS_USE_FLOAT16 diff --git a/core/src/index/thirdparty/faiss/example_makefiles/makefile.inc.Mac.brew b/core/src/index/thirdparty/faiss/example_makefiles/makefile.inc.Mac.brew deleted file mode 100644 index 8fa6fe7616..0000000000 --- a/core/src/index/thirdparty/faiss/example_makefiles/makefile.inc.Mac.brew +++ /dev/null @@ -1,99 +0,0 @@ -# -*- makefile -*- -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - - -# Tested on macOS Sierra (10.12.2) with llvm installed using Homebrew (https://brew.sh) -# brew install llvm -CXX = /usr/local/opt/llvm/bin/clang++ -std=c++11 -CXXFLAGS = -fPIC -m64 -Wall -g -O3 -fopenmp -Wno-sign-compare -I/usr/local/opt/llvm/include -CPUFLAGS = -msse4 -mpopcnt -LLVM_VERSION_PATH=$(shell ls -rt /usr/local/Cellar/llvm/ | tail -n1) -LDFLAGS = -fPIC -fopenmp -L/usr/local/opt/llvm/lib -L/usr/local/Cellar/llvm/${LLVM_VERSION_PATH}/lib - -# common mac flags -SHAREDEXT = dylib -SHAREDFLAGS = -dynamiclib -MKDIR_P = mkdir -p - -prefix ?= /usr/local -exec_prefix ?= ${prefix} -libdir = ${exec_prefix}/lib -includedir = ${prefix}/include - -########################################################################## -# Uncomment one of the 4 BLAS/Lapack implementation options -# below. They are sorted # from fastest to slowest (in our -# experiments). -########################################################################## - -# -# 1. Intel MKL -# -# This is the fastest BLAS implementation we tested. Unfortunately it -# is not open-source and determining the correct linking flags is a -# nightmare. See -# -# https://software.intel.com/en-us/articles/intel-mkl-link-line-advisor -# -# The latest tested version is MKL 2017.0.098 (2017 Initial Release) and can -# be downloaded here: -# -# https://registrationcenter.intel.com/en/forms/?productid=2558&licensetype=2 -# -# The following settings are working if MKL is installed on its default folder: -# -# MKLROOT = /opt/intel/compilers_and_libraries/linux/mkl/ -# -# LDFLAGS += -Wl,--no-as-needed -L$(MKLROOT)/lib/intel64 -# LIBS += -lmkl_intel_ilp64 -lmkl_core -lmkl_gnu_thread -ldl -lpthread -# -# CPPFLAGS += -DFINTEGER=long -# -# You may have to set the LD_LIBRARY_PATH=$MKLROOT/lib/intel64 at runtime. - -# -# 2. Openblas -# -# The library contains both BLAS and Lapack. Install with brew install OpenBLAS -# -# CPPFLAGS += -DFINTEGER=int -# LIBS += /usr/local/opt/openblas/lib/libblas.dylib -# - -# -# 3. Apple's framework accelerate -# -# This has the advantage that it does not require to install anything, -# as it is provided by default on the mac. It is not very fast, though. -# - -CPPFLAGS += -DFINTEGER=int -LIBS += -framework Accelerate - - - -########################################################################## -# SWIG and Python flags -########################################################################## - -# SWIG executable. This should be at least version 3.x -# brew install swig - -SWIG = /usr/local/bin/swig - -# The Python include directories for the current python executable - -PYTHON_INC=$(shell python -c "import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())") -NUMPY_INC=$(shell python -c "import numpy ; print(numpy.get_include())") -PYTHONCFLAGS=-I${PYTHON_INC} -I${NUMPY_INC} -PYTHONLIB=-lpython - -########################################################################## -# Faiss GPU -########################################################################## - -# As we don't have access to a Mac with nvidia GPUs installed, we -# could not validate the GPU compile of Faiss. diff --git a/core/src/index/thirdparty/faiss/example_makefiles/makefile.inc.Mac.port b/core/src/index/thirdparty/faiss/example_makefiles/makefile.inc.Mac.port deleted file mode 100644 index 6b2c292220..0000000000 --- a/core/src/index/thirdparty/faiss/example_makefiles/makefile.inc.Mac.port +++ /dev/null @@ -1,106 +0,0 @@ -# -*- makefile -*- -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. -# -# tested on Mac OS X 10.12.2 Sierra with additional software installed via macports - - -# The system default clang does not support openmp -# You can install an openmp compatible g++ with macports: -# port install g++-mp-6 -CXX = /opt/local/bin/g++-mp-6 -std=c++11 -CXXFLAGS = -fPIC -m64 -Wall -g -O3 -fopenmp -Wno-sign-compare -CPUFLAGS = -msse4 -mpopcnt -LDFLAGS = -g -fPIC -fopenmp - -# common linux flags -SHAREDEXT = dylib -SHAREDFLAGS = -dynamiclib -MKDIR_P = mkdir -p - -prefix ?= /usr/local -exec_prefix ?= ${prefix} -libdir = ${exec_prefix}/lib -includedir = ${prefix}/include - -########################################################################## -# Uncomment one of the 4 BLAS/Lapack implementation options -# below. They are sorted # from fastest to slowest (in our -# experiments). -########################################################################## - -# -# 1. Intel MKL -# -# This is the fastest BLAS implementation we tested. Unfortunately it -# is not open-source and determining the correct linking flags is a -# nightmare. See -# -# https://software.intel.com/en-us/articles/intel-mkl-link-line-advisor -# -# The latest tested version is MKL 2017.0.098 (2017 Initial Release) and can -# be downloaded here: -# -# https://registrationcenter.intel.com/en/forms/?productid=2558&licensetype=2 -# -# The following settings are working if MKL is installed on its default folder: -# -# MKLROOT = /opt/intel/compilers_and_libraries/linux/mkl/ -# -# LDFLAGS += -Wl,--no-as-needed -L$(MKLROOT)/lib/intel64 -# LIBS += -lmkl_intel_ilp64 -lmkl_core -lmkl_gnu_thread -ldl -lpthread -# -# CPPFLAGS += -DFINTEGER=long -# -# You may have to set the LD_LIBRARY_PATH=$MKLROOT/lib/intel64 at runtime. - -# -# 2. Openblas -# -# The library contains both BLAS and Lapack. Install with port install OpenBLAS -# -# CPPFLAGS += -DFINTEGER=int -# LIBS += /opt/local/lib/libopenblas.dylib -# - -# -# 3. Apple's framework accelerate -# -# This has the advantage that it does not require to install anything, -# as it is provided by default on the mac. It is not very fast, though. -# - -CPPFLAGS += -DFINTEGER=int -LIBS += -framework Accelerate - - - -########################################################################## -# SWIG and Python flags -########################################################################## - -# SWIG executable. This should be at least version 3.x -# port install swig swig-python - -SWIG = /opt/local/bin/swig - -# The Python include directories for the current python executable can -# typically be found with -# -# python -c "import distutils.sysconfig; print distutils.sysconfig.get_python_inc()" -# python -c "import numpy ; print numpy.get_include()" -# -# the paths below are for the system python (not the macports one) - -PYTHONCFLAGS=-I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 \ --I/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/numpy/core/include -PYTHONLIB=-lpython - -########################################################################## -# Faiss GPU -########################################################################## - -# As we don't have access to a Mac with nvidia GPUs installed, we -# could not validate the GPU compile of Faiss. diff --git a/core/src/index/thirdparty/faiss/faiss b/core/src/index/thirdparty/faiss/faiss deleted file mode 120000 index 6a043149e8..0000000000 --- a/core/src/index/thirdparty/faiss/faiss +++ /dev/null @@ -1 +0,0 @@ -./ \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/gpu/GpuAutoTune.cpp b/core/src/index/thirdparty/faiss/gpu/GpuAutoTune.cpp deleted file mode 100644 index c734fdabb5..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuAutoTune.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - - -using namespace ::faiss; - -/********************************************************** - * Parameters to auto-tune on GpuIndex'es - **********************************************************/ - -#define DC(classname) auto ix = dynamic_cast(index) - - -void GpuParameterSpace::initialize (const Index * index) -{ - if (DC (IndexPreTransform)) { - index = ix->index; - } - if (DC (IndexReplicas)) { - if (ix->count() == 0) return; - index = ix->at(0); - } - if (DC (IndexShards)) { - if (ix->count() == 0) return; - index = ix->at(0); - } - if (DC (GpuIndexIVF)) { - ParameterRange & pr = add_range("nprobe"); - for (int i = 0; i < 12; i++) { - size_t nprobe = 1 << i; - if (nprobe >= ix->getNumLists() || - nprobe > getMaxKSelection()) break; - pr.values.push_back (nprobe); - } - } - // not sure we should call the parent initializer -} - - - -#undef DC -// non-const version -#define DC(classname) auto *ix = dynamic_cast(index) - - - -void GpuParameterSpace::set_index_parameter ( - Index * index, const std::string & name, double val) const -{ - if (DC (IndexReplicas)) { - for (int i = 0; i < ix->count(); i++) - set_index_parameter (ix->at(i), name, val); - return; - } - if (name == "nprobe") { - if (DC (GpuIndexIVF)) { - ix->setNumProbes (int (val)); - return; - } - } - if (name == "use_precomputed_table") { - if (DC (GpuIndexIVFPQ)) { - ix->setPrecomputedCodes(bool (val)); - return; - } - } - - // maybe normal index parameters apply? - ParameterSpace::set_index_parameter (index, name, val); -} - - - - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuAutoTune.h b/core/src/index/thirdparty/faiss/gpu/GpuAutoTune.h deleted file mode 100644 index 1bcc9205d8..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuAutoTune.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include - -namespace faiss { namespace gpu { - - -/// parameter space and setters for GPU indexes -struct GpuParameterSpace: faiss::ParameterSpace { - /// initialize with reasonable parameters for the index - void initialize (const faiss::Index * index) override; - - /// set a combination of parameters on an index - void set_index_parameter ( - faiss::Index * index, const std::string & name, - double val) const override; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuCloner.cpp b/core/src/index/thirdparty/faiss/gpu/GpuCloner.cpp deleted file mode 100644 index 192c02db42..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuCloner.cpp +++ /dev/null @@ -1,478 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - - -/********************************************************** - * Cloning to CPU - **********************************************************/ - -void ToCPUCloner::merge_index(Index *dst, Index *src, bool successive_ids) -{ - if (auto ifl = dynamic_cast(dst)) { - auto ifl2 = dynamic_cast(src); - FAISS_ASSERT(ifl2); - FAISS_ASSERT(successive_ids); - ifl->add(ifl2->ntotal, ifl2->xb.data()); - } else if(auto ifl = dynamic_cast(dst)) { - auto ifl2 = dynamic_cast(src); - FAISS_ASSERT(ifl2); - ifl->merge_from(*ifl2, successive_ids ? ifl->ntotal : 0); - } else if(auto ifl = dynamic_cast(dst)) { - auto ifl2 = dynamic_cast(src); - FAISS_ASSERT(ifl2); - ifl->merge_from(*ifl2, successive_ids ? ifl->ntotal : 0); - } else if(auto ifl = dynamic_cast(dst)) { - auto ifl2 = dynamic_cast(src); - FAISS_ASSERT(ifl2); - ifl->merge_from(*ifl2, successive_ids ? ifl->ntotal : 0); - } else { - FAISS_ASSERT(!"merging not implemented for this type of class"); - } -} - - -Index *ToCPUCloner::clone_Index(const Index *index) -{ - if(auto ifl = dynamic_cast(index)) { - IndexFlat *res = new IndexFlat(); - ifl->copyTo(res); - return res; - } else if(auto ifl = dynamic_cast(index)) { - IndexIVFFlat *res = new IndexIVFFlat(); - ifl->copyTo(res); - return res; - } else if(auto ifl = - dynamic_cast(index)) { - IndexIVFScalarQuantizer *res = new IndexIVFScalarQuantizer(); - ifl->copyTo(res); - return res; - } else if(auto ifl = - dynamic_cast(index)) { - IndexIVFSQHybrid *res = new IndexIVFSQHybrid(); - ifl->copyTo(res); - return res; - } else if(auto ipq = dynamic_cast(index)) { - IndexIVFPQ *res = new IndexIVFPQ(); - ipq->copyTo(res); - return res; - - // for IndexShards and IndexReplicas we assume that the - // objective is to make a single component out of them - // (inverse op of ToGpuClonerMultiple) - - } else if(auto ish = dynamic_cast(index)) { - int nshard = ish->count(); - FAISS_ASSERT(nshard > 0); - Index *res = clone_Index(ish->at(0)); - for(int i = 1; i < ish->count(); i++) { - Index *res_i = clone_Index(ish->at(i)); - merge_index(res, res_i, ish->successive_ids); - delete res_i; - } - return res; - } else if(auto ipr = dynamic_cast(index)) { - // just clone one of the replicas - FAISS_ASSERT(ipr->count() > 0); - return clone_Index(ipr->at(0)); - } else { - return Cloner::clone_Index(index); - } -} - -faiss::Index * index_gpu_to_cpu(const faiss::Index *gpu_index) -{ - ToCPUCloner cl; - return cl.clone_Index(gpu_index); -} - - - - -/********************************************************** - * Cloning to 1 GPU - **********************************************************/ - -ToGpuCloner::ToGpuCloner(GpuResources *resources, int device, - const GpuClonerOptions &options): - GpuClonerOptions(options), resources(resources), device(device) -{} - -Index *ToGpuCloner::clone_Index (IndexComposition* index_composition) { - Index* index = index_composition->index; - - if(auto ifl = dynamic_cast(index)) { - gpu::GpuIndexFlat *&quantizer = index_composition->quantizer; - long mode = index_composition->mode; - - GpuIndexIVFSQHybridConfig config; - config.device = device; - config.indicesOptions = indicesOptions; - config.flatConfig.useFloat16 = useFloat16CoarseQuantizer; - config.flatConfig.storeTransposed = storeTransposed; - - GpuIndexIVFSQHybrid *res = - new GpuIndexIVFSQHybrid(resources, - ifl->d, - ifl->nlist, - ifl->sq.qtype, - ifl->metric_type, - ifl->by_residual, - config); - if(reserveVecs > 0 && ifl->ntotal == 0) { - res->reserveMemory(reserveVecs); - } - - res->copyFrom(ifl, quantizer, mode); - return res; - } else { - return clone_Index(index); - } -} - -Index *ToGpuCloner::clone_Index(const Index *index) -{ - auto ivf_sqh = dynamic_cast(index); - if(ivf_sqh) { - auto ifl = ivf_sqh; - GpuIndexIVFSQHybridConfig config; - config.device = device; - config.indicesOptions = indicesOptions; - config.flatConfig.useFloat16 = useFloat16CoarseQuantizer; - config.flatConfig.storeTransposed = storeTransposed; - - GpuIndexIVFSQHybrid *res = - new GpuIndexIVFSQHybrid(resources, - ifl->d, - ifl->nlist, - ifl->sq.qtype, - ifl->metric_type, - ifl->by_residual, - config); - if(reserveVecs > 0 && ifl->ntotal == 0) { - res->reserveMemory(reserveVecs); - } - - res->copyFrom(ifl); - return res; - } else if(auto ifl = dynamic_cast(index)) { - GpuIndexFlatConfig config; - config.device = device; - config.useFloat16 = useFloat16; - config.storeTransposed = storeTransposed; - config.storeInCpu = storeInCpu; - - return new GpuIndexFlat(resources, ifl, config); - } else if(auto ifl = dynamic_cast(index)) { - GpuIndexIVFFlatConfig config; - config.device = device; - config.indicesOptions = indicesOptions; - config.flatConfig.useFloat16 = useFloat16CoarseQuantizer; - config.flatConfig.storeTransposed = storeTransposed; - - GpuIndexIVFFlat *res = - new GpuIndexIVFFlat(resources, - ifl->d, - ifl->nlist, - ifl->metric_type, - config); - if(reserveVecs > 0 && ifl->ntotal == 0) { - res->reserveMemory(reserveVecs); - } - - res->copyFrom(ifl); - return res; - } else if(auto ifl = - dynamic_cast(index)) { - GpuIndexIVFScalarQuantizerConfig config; - config.device = device; - config.indicesOptions = indicesOptions; - config.flatConfig.useFloat16 = useFloat16CoarseQuantizer; - config.flatConfig.storeTransposed = storeTransposed; - - GpuIndexIVFScalarQuantizer *res = - new GpuIndexIVFScalarQuantizer(resources, - ifl->d, - ifl->nlist, - ifl->sq.qtype, - ifl->metric_type, - ifl->by_residual, - config); - if(reserveVecs > 0 && ifl->ntotal == 0) { - res->reserveMemory(reserveVecs); - } - - res->copyFrom(ifl); - return res; - } else if(auto ipq = dynamic_cast(index)) { - if(verbose) - printf(" IndexIVFPQ size %ld -> GpuIndexIVFPQ " - "indicesOptions=%d " - "usePrecomputed=%d useFloat16=%d reserveVecs=%ld\n", - ipq->ntotal, indicesOptions, usePrecomputed, - useFloat16, reserveVecs); - GpuIndexIVFPQConfig config; - config.device = device; - config.indicesOptions = indicesOptions; - config.flatConfig.useFloat16 = useFloat16CoarseQuantizer; - config.flatConfig.storeTransposed = storeTransposed; - config.useFloat16LookupTables = useFloat16; - config.usePrecomputedTables = usePrecomputed; - - GpuIndexIVFPQ *res = new GpuIndexIVFPQ(resources, ipq, config); - - if(reserveVecs > 0 && ipq->ntotal == 0) { - res->reserveMemory(reserveVecs); - } - - return res; - } else { - return Cloner::clone_Index(index); - } -} - - -faiss::Index * index_cpu_to_gpu( - GpuResources* resources, int device, - const faiss::Index *index, - const GpuClonerOptions *options) -{ - GpuClonerOptions defaults; - ToGpuCloner cl(resources, device, options ? *options : defaults); - return cl.clone_Index(index); -} - -faiss::Index * index_cpu_to_gpu( - GpuResources* resources, int device, - IndexComposition* index_composition, - const GpuClonerOptions *options) { - GpuClonerOptions defaults; - ToGpuCloner cl(resources, device, options ? *options : defaults); - return cl.clone_Index(index_composition); -} - -/********************************************************** - * Cloning to multiple GPUs - **********************************************************/ - -ToGpuClonerMultiple::ToGpuClonerMultiple( - std::vector & resources, - std::vector& devices, - const GpuMultipleClonerOptions &options): - GpuMultipleClonerOptions(options) -{ - FAISS_ASSERT(resources.size() == devices.size()); - for(int i = 0; i < resources.size(); i++) { - sub_cloners.push_back(ToGpuCloner(resources[i], devices[i], options)); - } -} - - -ToGpuClonerMultiple::ToGpuClonerMultiple( - const std::vector & sub_cloners, - const GpuMultipleClonerOptions &options): - GpuMultipleClonerOptions(options), - sub_cloners(sub_cloners) -{} - - -void ToGpuClonerMultiple::copy_ivf_shard ( - const IndexIVF *index_ivf, IndexIVF *idx2, - long n, long i) -{ - if (shard_type == 2) { - long i0 = i * index_ivf->ntotal / n; - long i1 = (i + 1) * index_ivf->ntotal / n; - - if(verbose) - printf("IndexShards shard %ld indices %ld:%ld\n", - i, i0, i1); - index_ivf->copy_subset_to(*idx2, 2, i0, i1); - FAISS_ASSERT(idx2->ntotal == i1 - i0); - } else if (shard_type == 1) { - if(verbose) - printf("IndexShards shard %ld select modulo %ld = %ld\n", - i, n, i); - index_ivf->copy_subset_to(*idx2, 1, n, i); - } else { - FAISS_THROW_FMT ("shard_type %d not implemented", shard_type); - } - -} - -Index * ToGpuClonerMultiple::clone_Index_to_shards (const Index *index) -{ - long n = sub_cloners.size(); - - auto index_ivfpq = - dynamic_cast(index); - auto index_ivfflat = - dynamic_cast(index); - auto index_ivfsq = - dynamic_cast(index); - auto index_flat = - dynamic_cast(index); - FAISS_THROW_IF_NOT_MSG ( - index_ivfpq || index_ivfflat || index_flat || index_ivfsq, - "IndexShards implemented only for " - "IndexIVFFlat, IndexIVFScalarQuantizer, " - "IndexFlat and IndexIVFPQ"); - - std::vector shards(n); - - for(long i = 0; i < n; i++) { - // make a shallow copy - if(reserveVecs) - sub_cloners[i].reserveVecs = - (reserveVecs + n - 1) / n; - - if (index_ivfpq) { - faiss::IndexIVFPQ idx2( - index_ivfpq->quantizer, index_ivfpq->d, - index_ivfpq->nlist, index_ivfpq->code_size, - index_ivfpq->pq.nbits); - idx2.metric_type = index_ivfpq->metric_type; - idx2.pq = index_ivfpq->pq; - idx2.nprobe = index_ivfpq->nprobe; - idx2.use_precomputed_table = 0; - idx2.is_trained = index->is_trained; - copy_ivf_shard (index_ivfpq, &idx2, n, i); - shards[i] = sub_cloners[i].clone_Index(&idx2); - } else if (index_ivfflat) { - faiss::IndexIVFFlat idx2( - index_ivfflat->quantizer, index->d, - index_ivfflat->nlist, index_ivfflat->metric_type); - idx2.nprobe = index_ivfflat->nprobe; - idx2.is_trained = index->is_trained; - copy_ivf_shard (index_ivfflat, &idx2, n, i); - shards[i] = sub_cloners[i].clone_Index(&idx2); - } else if (index_ivfsq) { - faiss::IndexIVFScalarQuantizer idx2( - index_ivfsq->quantizer, index->d, index_ivfsq->nlist, - index_ivfsq->sq.qtype, - index_ivfsq->metric_type, - index_ivfsq->by_residual); - - idx2.nprobe = index_ivfsq->nprobe; - idx2.is_trained = index->is_trained; - idx2.sq = index_ivfsq->sq; - copy_ivf_shard (index_ivfsq, &idx2, n, i); - shards[i] = sub_cloners[i].clone_Index(&idx2); - } else if (index_flat) { - faiss::IndexFlat idx2 ( - index->d, index->metric_type); - shards[i] = sub_cloners[i].clone_Index(&idx2); - if (index->ntotal > 0) { - long i0 = index->ntotal * i / n; - long i1 = index->ntotal * (i + 1) / n; - shards[i]->add (i1 - i0, - index_flat->xb.data() + i0 * index->d); - } - } - } - - bool successive_ids = index_flat != nullptr; - faiss::IndexShards *res = - new faiss::IndexShards(index->d, true, - successive_ids); - - for (int i = 0; i < n; i++) { - res->add_shard(shards[i]); - } - res->own_fields = true; - FAISS_ASSERT(index->ntotal == res->ntotal); - return res; -} - -Index *ToGpuClonerMultiple::clone_Index(const Index *index) -{ - long n = sub_cloners.size(); - if (n == 1) - return sub_cloners[0].clone_Index(index); - - if(dynamic_cast(index) || - dynamic_cast(index) || - dynamic_cast(index) || - dynamic_cast(index)) { - if(!shard) { - IndexReplicas * res = new IndexReplicas(); - for(auto & sub_cloner: sub_cloners) { - res->addIndex(sub_cloner.clone_Index(index)); - } - res->own_fields = true; - return res; - } else { - return clone_Index_to_shards (index); - } - } else if(auto miq = dynamic_cast(index)) { - if (verbose) { - printf("cloning MultiIndexQuantizer: " - "will be valid only for search k=1\n"); - } - const ProductQuantizer & pq = miq->pq; - IndexSplitVectors *splitv = new IndexSplitVectors(pq.d, true); - splitv->own_fields = true; - - for (int m = 0; m < pq.M; m++) { - // which GPU(s) will be assigned to this sub-quantizer - - long i0 = m * n / pq.M; - long i1 = pq.M <= n ? (m + 1) * n / pq.M : i0 + 1; - std::vector sub_cloners_2; - sub_cloners_2.insert( - sub_cloners_2.begin(), sub_cloners.begin() + i0, - sub_cloners.begin() + i1); - ToGpuClonerMultiple cm(sub_cloners_2, *this); - IndexFlatL2 idxc (pq.dsub); - idxc.add (pq.ksub, pq.centroids.data() + m * pq.d * pq.ksub); - Index *idx2 = cm.clone_Index(&idxc); - splitv->add_sub_index(idx2); - } - return splitv; - } else { - return Cloner::clone_Index(index); - } -} - - - -faiss::Index * index_cpu_to_gpu_multiple( - std::vector & resources, - std::vector &devices, - const faiss::Index *index, - const GpuMultipleClonerOptions *options) -{ - GpuMultipleClonerOptions defaults; - ToGpuClonerMultiple cl(resources, devices, options ? *options : defaults); - return cl.clone_Index(index); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuCloner.h b/core/src/index/thirdparty/faiss/gpu/GpuCloner.h deleted file mode 100644 index f2c5388d93..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuCloner.h +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -class GpuResources; - - -/// Cloner specialized for GPU -> CPU -struct ToCPUCloner: faiss::Cloner { - void merge_index(Index *dst, Index *src, bool successive_ids); - Index *clone_Index(const Index *index) override; -}; - - -/// Cloner specialized for CPU -> 1 GPU -struct ToGpuCloner: faiss::Cloner, GpuClonerOptions { - GpuResources *resources; - int device; - - ToGpuCloner(GpuResources *resources, int device, - const GpuClonerOptions &options); - - Index *clone_Index(const Index *index) override; - - Index *clone_Index (IndexComposition* index_composition) override; -}; - -/// Cloner specialized for CPU -> multiple GPUs -struct ToGpuClonerMultiple: faiss::Cloner, GpuMultipleClonerOptions { - std::vector sub_cloners; - - ToGpuClonerMultiple(std::vector & resources, - std::vector& devices, - const GpuMultipleClonerOptions &options); - - ToGpuClonerMultiple(const std::vector & sub_cloners, - const GpuMultipleClonerOptions &options); - - void copy_ivf_shard (const IndexIVF *index_ivf, IndexIVF *idx2, - long n, long i); - - Index * clone_Index_to_shards (const Index *index); - - /// main function - Index *clone_Index(const Index *index) override; -}; - - - - -/// converts any GPU index inside gpu_index to a CPU index -faiss::Index * index_gpu_to_cpu(const faiss::Index *gpu_index); - -/// converts any CPU index that can be converted to GPU -faiss::Index * index_cpu_to_gpu( - GpuResources* resources, int device, - const faiss::Index *index, - const GpuClonerOptions *options = nullptr); - -faiss::Index * index_cpu_to_gpu( - GpuResources* resources, int device, - IndexComposition* index_composition, - const GpuClonerOptions *options = nullptr); - -faiss::Index * index_cpu_to_gpu_multiple( - std::vector & resources, - std::vector &devices, - const faiss::Index *index, - const GpuMultipleClonerOptions *options = nullptr); - - - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuClonerOptions.cpp b/core/src/index/thirdparty/faiss/gpu/GpuClonerOptions.cpp deleted file mode 100644 index 4e0b40bd84..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuClonerOptions.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -GpuClonerOptions::GpuClonerOptions() - : indicesOptions(INDICES_64_BIT), - useFloat16CoarseQuantizer(false), - useFloat16(false), - usePrecomputed(false), - reserveVecs(0), - storeTransposed(false), - storeInCpu(false), - allInGpu(false), - verbose(false) { -} - -GpuMultipleClonerOptions::GpuMultipleClonerOptions() - : shard(false), - shard_type(1) -{ -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuClonerOptions.h b/core/src/index/thirdparty/faiss/gpu/GpuClonerOptions.h deleted file mode 100644 index b56a33d8d7..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuClonerOptions.h +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -namespace faiss { namespace gpu { - -/// set some options on how to copy to GPU -struct GpuClonerOptions { - GpuClonerOptions(); - - /// how should indices be stored on index types that support indices - /// (anything but GpuIndexFlat*)? - IndicesOptions indicesOptions; - - /// is the coarse quantizer in float16? - bool useFloat16CoarseQuantizer; - - /// for GpuIndexIVFFlat, is storage in float16? - /// for GpuIndexIVFPQ, are intermediate calculations in float16? - bool useFloat16; - - /// use precomputed tables? - bool usePrecomputed; - - /// reserve vectors in the invfiles? - long reserveVecs; - - /// For GpuIndexFlat, store data in transposed layout? - bool storeTransposed; - - bool storeInCpu; - - /// For IndexIVFScalarQuantizer - bool allInGpu; - - /// Set verbose options on the index - bool verbose; -}; - -struct GpuMultipleClonerOptions : public GpuClonerOptions { - GpuMultipleClonerOptions (); - - /// Whether to shard the index across GPUs, versus replication - /// across GPUs - bool shard; - - /// IndexIVF::copy_subset_to subset type - int shard_type; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuDistance.cu b/core/src/index/thirdparty/faiss/gpu/GpuDistance.cu deleted file mode 100644 index f5ce8aa24e..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuDistance.cu +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -template -void bfKnnConvert(GpuResources* resources, const GpuDistanceParams& args) { - auto device = getCurrentDevice(); - auto stream = resources->getDefaultStreamCurrentDevice(); - auto& mem = resources->getMemoryManagerCurrentDevice(); - - auto tVectors = - toDevice(resources, - device, - const_cast(reinterpret_cast(args.vectors)), - stream, - {args.vectorsRowMajor ? args.numVectors : args.dims, - args.vectorsRowMajor ? args.dims : args.numVectors}); - auto tQueries = - toDevice(resources, - device, - const_cast(reinterpret_cast(args.queries)), - stream, - {args.queriesRowMajor ? args.numQueries : args.dims, - args.queriesRowMajor ? args.dims : args.numQueries}); - - DeviceTensor tVectorNorms; - if (args.vectorNorms) { - tVectorNorms = toDevice(resources, - device, - const_cast(args.vectorNorms), - stream, - {args.numVectors}); - } - - auto tOutDistances = - toDevice(resources, - device, - args.outDistances, - stream, - {args.numQueries, args.k}); - - // The brute-force API only supports an interface for integer indices - DeviceTensor - tOutIntIndices(mem, {args.numQueries, args.k}, stream); - - // Empty bitset - auto bitsetDevice = toDevice(resources, device, nullptr, stream, {0}); - - // Since we've guaranteed that all arguments are on device, call the - // implementation - bfKnnOnDevice(resources, - device, - stream, - tVectors, - args.vectorsRowMajor, - args.vectorNorms ? &tVectorNorms : nullptr, - tQueries, - args.queriesRowMajor, - bitsetDevice, - args.k, - args.metric, - args.metricArg, - tOutDistances, - tOutIntIndices, - args.ignoreOutDistances); - - // Convert and copy int indices out - auto tOutIndices = - toDevice(resources, - device, - args.outIndices, - stream, - {args.numQueries, args.k}); - - // Convert int to idx_t - convertTensor(stream, - tOutIntIndices, - tOutIndices); - - // Copy back if necessary - fromDevice(tOutDistances, args.outDistances, stream); - fromDevice(tOutIndices, args.outIndices, stream); -} - -void -bfKnn(GpuResources* resources, const GpuDistanceParams& args) { - // For now, both vectors and queries must be of the same data type - FAISS_THROW_IF_NOT_MSG( - args.vectorType == args.queryType, - "limitation: both vectorType and queryType must currently " - "be the same (F32 or F16"); - - if (args.vectorType == DistanceDataType::F32) { - bfKnnConvert(resources, args); - } else if (args.vectorType == DistanceDataType::F16) { - bfKnnConvert(resources, args); - } else { - FAISS_THROW_MSG("unknown vectorType"); - } -} - -// legacy version -void -bruteForceKnn(GpuResources* resources, - faiss::MetricType metric, - // A region of memory size numVectors x dims, with dims - // innermost - const float* vectors, - bool vectorsRowMajor, - int numVectors, - // A region of memory size numQueries x dims, with dims - // innermost - const float* queries, - bool queriesRowMajor, - int numQueries, - int dims, - int k, - // A region of memory size numQueries x k, with k - // innermost - float* outDistances, - // A region of memory size numQueries x k, with k - // innermost - faiss::Index::idx_t* outIndices) { - std::cerr << "bruteForceKnn is deprecated; call bfKnn instead" << std::endl; - - GpuDistanceParams args; - args.metric = metric; - args.k = k; - args.dims = dims; - args.vectors = vectors; - args.vectorsRowMajor = vectorsRowMajor; - args.numVectors = numVectors; - args.queries = queries; - args.queriesRowMajor = queriesRowMajor; - args.numQueries = numQueries; - args.outDistances = outDistances; - args.outIndices = outIndices; - - bfKnn(resources, args); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuDistance.h b/core/src/index/thirdparty/faiss/gpu/GpuDistance.h deleted file mode 100644 index 05667e70f7..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuDistance.h +++ /dev/null @@ -1,145 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include - -namespace faiss { namespace gpu { - -class GpuResources; - -// Scalar type of the vector data -enum class DistanceDataType { - F32 = 1, - F16, -}; - -/// Arguments to brute-force GPU k-nearest neighbor searching -struct GpuDistanceParams { - GpuDistanceParams() - : metric(faiss::MetricType::METRIC_L2), - metricArg(0), - k(0), - dims(0), - vectors(nullptr), - vectorType(DistanceDataType::F32), - vectorsRowMajor(true), - numVectors(0), - vectorNorms(nullptr), - queries(nullptr), - queryType(DistanceDataType::F32), - queriesRowMajor(true), - numQueries(0), - outDistances(nullptr), - ignoreOutDistances(false), - outIndices(nullptr) { - } - - // - // Search parameters - // - - // Search parameter: distance metric - faiss::MetricType metric; - - // Search parameter: distance metric argument (if applicable) - // For metric == METRIC_Lp, this is the p-value - float metricArg; - - // Search parameter: return k nearest neighbors - int k; - - // Vector dimensionality - int dims; - - // - // Vectors being queried - // - - // If vectorsRowMajor is true, this is - // numVectors x dims, with dims innermost; otherwise, - // dims x numVectors, with numVectors innermost - const void* vectors; - DistanceDataType vectorType; - bool vectorsRowMajor; - int numVectors; - - // Precomputed L2 norms for each vector in `vectors`, which can be optionally - // provided in advance to speed computation for METRIC_L2 - const float* vectorNorms; - - // - // The query vectors (i.e., find k-nearest neighbors in `vectors` for each of - // the `queries` - // - - // If queriesRowMajor is true, this is - // numQueries x dims, with dims innermost; otherwise, - // dims x numQueries, with numQueries innermost - const void* queries; - DistanceDataType queryType; - bool queriesRowMajor; - int numQueries; - - // - // Output results - // - - // A region of memory size numQueries x k, with k - // innermost (row major) - float* outDistances; - - // Do we only care abouty the indices reported, rather than the output - // distances? - bool ignoreOutDistances; - - // A region of memory size numQueries x k, with k - // innermost (row major) - faiss::Index::idx_t* outIndices; -}; - -/// A wrapper for gpu/impl/Distance.cuh to expose direct brute-force k-nearest -/// neighbor searches on an externally-provided region of memory (e.g., from a -/// pytorch tensor). -/// The data (vectors, queries, outDistances, outIndices) can be resident on the -/// GPU or the CPU, but all calculations are performed on the GPU. If the result -/// buffers are on the CPU, results will be copied back when done. -/// -/// All GPU computation is performed on the current CUDA device, and ordered -/// with respect to resources->getDefaultStreamCurrentDevice(). -/// -/// For each vector in `queries`, searches all of `vectors` to find its k -/// nearest neighbors with respect to the given metric -void bfKnn(GpuResources* resources, const GpuDistanceParams& args); - -/// Deprecated legacy implementation -void bruteForceKnn(GpuResources* resources, - faiss::MetricType metric, - // If vectorsRowMajor is true, this is - // numVectors x dims, with dims innermost; otherwise, - // dims x numVectors, with numVectors innermost - const float* vectors, - bool vectorsRowMajor, - int numVectors, - // If queriesRowMajor is true, this is - // numQueries x dims, with dims innermost; otherwise, - // dims x numQueries, with numQueries innermost - const float* queries, - bool queriesRowMajor, - int numQueries, - int dims, - int k, - // A region of memory size numQueries x k, with k - // innermost (row major) - float* outDistances, - // A region of memory size numQueries x k, with k - // innermost (row major) - faiss::Index::idx_t* outIndices); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuFaissAssert.h b/core/src/index/thirdparty/faiss/gpu/GpuFaissAssert.h deleted file mode 100644 index 1931b916cc..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuFaissAssert.h +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#ifndef GPU_FAISS_ASSERT_INCLUDED -#define GPU_FAISS_ASSERT_INCLUDED - -#include -#include - -/// -/// Assertions -/// - -#ifdef __CUDA_ARCH__ -#define GPU_FAISS_ASSERT(X) assert(X) -#define GPU_FAISS_ASSERT_MSG(X, MSG) assert(X) -#define GPU_FAISS_ASSERT_FMT(X, FMT, ...) assert(X) -#else -#define GPU_FAISS_ASSERT(X) FAISS_ASSERT(X) -#define GPU_FAISS_ASSERT_MSG(X, MSG) FAISS_ASSERT_MSG(X, MSG) -#define GPU_FAISS_ASSERT_FMT(X, FMT, ...) FAISS_ASSERT_FMT(X, FMT, __VA_ARGS) -#endif // __CUDA_ARCH__ - -#endif diff --git a/core/src/index/thirdparty/faiss/gpu/GpuIndex.cu b/core/src/index/thirdparty/faiss/gpu/GpuIndex.cu deleted file mode 100644 index 173b3206f2..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuIndex.cu +++ /dev/null @@ -1,485 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -/// Default CPU search size for which we use paged copies -constexpr size_t kMinPageSize = (size_t) 256 * 1024 * 1024; - -/// Size above which we page copies from the CPU to GPU (non-paged -/// memory usage) -constexpr size_t kNonPinnedPageSize = (size_t) 256 * 1024 * 1024; - -// Default size for which we page add or search -constexpr size_t kAddPageSize = (size_t) 256 * 1024 * 1024; - -// Or, maximum number of vectors to consider per page of add or search -constexpr size_t kAddVecSize = (size_t) 512 * 1024; - -// Use a smaller search size, as precomputed code usage on IVFPQ -// requires substantial amounts of memory -// FIXME: parameterize based on algorithm need -constexpr size_t kSearchVecSize = (size_t) 32 * 1024; - -GpuIndex::GpuIndex(GpuResources* resources, - int dims, - faiss::MetricType metric, - float metricArg, - GpuIndexConfig config) : - Index(dims, metric), - resources_(resources), - device_(config.device), - memorySpace_(config.memorySpace), - minPagedSize_(kMinPageSize) { - FAISS_THROW_IF_NOT_FMT(device_ < getNumDevices(), - "Invalid GPU device %d", device_); - - FAISS_THROW_IF_NOT_MSG(dims > 0, "Invalid number of dimensions"); - -#ifdef FAISS_UNIFIED_MEM - FAISS_THROW_IF_NOT_FMT( - memorySpace_ == MemorySpace::Device || - (memorySpace_ == MemorySpace::Unified && - getFullUnifiedMemSupport(device_)), - "Device %d does not support full CUDA 8 Unified Memory (CC 6.0+)", - config.device); -#else - FAISS_THROW_IF_NOT_MSG(memorySpace_ == MemorySpace::Device, - "Must compile with CUDA 8+ for Unified Memory support"); -#endif - - metric_arg = metricArg; - - FAISS_ASSERT(resources_); - resources_->initializeForDevice(device_); -} - -void -GpuIndex::copyFrom(const faiss::Index* index) { - d = index->d; - metric_type = index->metric_type; - metric_arg = index->metric_arg; - ntotal = index->ntotal; - is_trained = index->is_trained; -} - -void -GpuIndex::copyTo(faiss::Index* index) const { - index->d = d; - index->metric_type = metric_type; - index->metric_arg = metric_arg; - index->ntotal = ntotal; - index->is_trained = is_trained; -} - -void -GpuIndex::setMinPagingSize(size_t size) { - minPagedSize_ = size; -} - -size_t -GpuIndex::getMinPagingSize() const { - return minPagedSize_; -} - -void -GpuIndex::add(Index::idx_t n, const float* x) { - // Pass to add_with_ids - add_with_ids(n, x, nullptr); -} - -void -GpuIndex::add_with_ids(Index::idx_t n, - const float* x, - const Index::idx_t* ids) { - FAISS_THROW_IF_NOT_MSG(this->is_trained, "Index not trained"); - - // For now, only support <= max int results - FAISS_THROW_IF_NOT_FMT(n <= (Index::idx_t) std::numeric_limits::max(), - "GPU index only supports up to %d indices", - std::numeric_limits::max()); - - if (n == 0) { - // nothing to add - return; - } - - std::vector generatedIds; - - // Generate IDs if we need them - if (!ids && addImplRequiresIDs_()) { - generatedIds = std::vector(n); - - for (Index::idx_t i = 0; i < n; ++i) { - generatedIds[i] = this->ntotal + i; - } - } - - DeviceScope scope(device_); - addPaged_((int) n, x, ids ? ids : generatedIds.data()); -} - -void -GpuIndex::addPaged_(int n, - const float* x, - const Index::idx_t* ids) { - if (n > 0) { - size_t totalSize = (size_t) n * this->d * sizeof(float); - - if (totalSize > kAddPageSize || n > kAddVecSize) { - // How many vectors fit into kAddPageSize? - size_t maxNumVecsForPageSize = - kAddPageSize / ((size_t) this->d * sizeof(float)); - - // Always add at least 1 vector, if we have huge vectors - maxNumVecsForPageSize = std::max(maxNumVecsForPageSize, (size_t) 1); - - size_t tileSize = std::min((size_t) n, maxNumVecsForPageSize); - tileSize = std::min(tileSize, kSearchVecSize); - - for (size_t i = 0; i < (size_t) n; i += tileSize) { - size_t curNum = std::min(tileSize, n - i); - - addPage_(curNum, - x + i * (size_t) this->d, - ids ? ids + i : nullptr); - } - } else { - addPage_(n, x, ids); - } - } -} - -void -GpuIndex::addPage_(int n, - const float* x, - const Index::idx_t* ids) { - // At this point, `x` can be resident on CPU or GPU, and `ids` may be resident - // on CPU, GPU or may be null. - // - // Before continuing, we guarantee that all data will be resident on the GPU. - auto stream = resources_->getDefaultStreamCurrentDevice(); - - auto vecs = toDevice(resources_, - device_, - const_cast(x), - stream, - {n, this->d}); - - if (ids) { - auto indices = toDevice(resources_, - device_, - const_cast(ids), - stream, - {n}); - - addImpl_(n, vecs.data(), ids ? indices.data() : nullptr); - } else { - addImpl_(n, vecs.data(), nullptr); - } -} - -void -GpuIndex::search(Index::idx_t n, - const float* x, - Index::idx_t k, - float* distances, - Index::idx_t* labels, - ConcurrentBitsetPtr bitset) const { - FAISS_THROW_IF_NOT_MSG(this->is_trained, "Index not trained"); - - // For now, only support <= max int results - FAISS_THROW_IF_NOT_FMT(n <= (Index::idx_t) std::numeric_limits::max(), - "GPU index only supports up to %d indices", - std::numeric_limits::max()); - - // Maximum k-selection supported is based on the CUDA SDK - FAISS_THROW_IF_NOT_FMT(k <= (Index::idx_t) getMaxKSelection(), - "GPU index only supports k <= %d (requested %d)", - getMaxKSelection(), - (int) k); // select limitation - - if (n == 0 || k == 0) { - // nothing to search - return; - } - - DeviceScope scope(device_); - auto stream = resources_->getDefaultStream(device_); - - // We guarantee that the searchImpl_ will be called with device-resident - // pointers. - - // The input vectors may be too large for the GPU, but we still - // assume that the output distances and labels are not. - // Go ahead and make space for output distances and labels on the - // GPU. - // If we reach a point where all inputs are too big, we can add - // another level of tiling. - auto outDistances = - toDevice(resources_, device_, distances, stream, - {(int) n, (int) k}); - - auto outLabels = - toDevice(resources_, device_, labels, stream, - {(int) n, (int) k}); - - bool usePaged = false; - - if (getDeviceForAddress(x) == -1) { - // It is possible that the user is querying for a vector set size - // `x` that won't fit on the GPU. - // In this case, we will have to handle paging of the data from CPU - // -> GPU. - // Currently, we don't handle the case where the output data won't - // fit on the GPU (e.g., n * k is too large for the GPU memory). - size_t dataSize = (size_t) n * this->d * sizeof(float); - - if (dataSize >= minPagedSize_) { - searchFromCpuPaged_(n, x, k, - outDistances.data(), - outLabels.data(), - bitset); - usePaged = true; - } - } - - if (!usePaged) { - searchNonPaged_(n, x, k, - outDistances.data(), - outLabels.data(), - bitset); - } - - // Copy back if necessary - fromDevice(outDistances, distances, stream); - fromDevice(outLabels, labels, stream); -} - -void -GpuIndex::searchNonPaged_(int n, - const float* x, - int k, - float* outDistancesData, - Index::idx_t* outIndicesData, - ConcurrentBitsetPtr bitset) const { - auto stream = resources_->getDefaultStream(device_); - - // Make sure arguments are on the device we desire; use temporary - // memory allocations to move it if necessary - auto vecs = toDevice(resources_, - device_, - const_cast(x), - stream, - {n, (int) this->d}); - - searchImpl_(n, vecs.data(), k, outDistancesData, outIndicesData, bitset); -} - -void -GpuIndex::searchFromCpuPaged_(int n, - const float* x, - int k, - float* outDistancesData, - Index::idx_t* outIndicesData, - ConcurrentBitsetPtr bitset) const { - Tensor outDistances(outDistancesData, {n, k}); - Tensor outIndices(outIndicesData, {n, k}); - - // Is pinned memory available? - auto pinnedAlloc = resources_->getPinnedMemory(); - int pageSizeInVecs = - (int) ((pinnedAlloc.second / 2) / (sizeof(float) * this->d)); - - if (!pinnedAlloc.first || pageSizeInVecs < 1) { - // Just page without overlapping copy with compute - int batchSize = utils::nextHighestPowerOf2( - (int) ((size_t) kNonPinnedPageSize / - (sizeof(float) * this->d))); - - for (int cur = 0; cur < n; cur += batchSize) { - int num = std::min(batchSize, n - cur); - - auto outDistancesSlice = outDistances.narrowOutermost(cur, num); - auto outIndicesSlice = outIndices.narrowOutermost(cur, num); - - searchNonPaged_(num, - x + (size_t) cur * this->d, - k, - outDistancesSlice.data(), - outIndicesSlice.data(), - bitset); - } - - return; - } - - // - // Pinned memory is available, so we can overlap copy with compute. - // We use two pinned memory buffers, and triple-buffer the - // procedure: - // - // 1 CPU copy -> pinned - // 2 pinned copy -> GPU - // 3 GPU compute - // - // 1 2 3 1 2 3 ... (pinned buf A) - // 1 2 3 1 2 ... (pinned buf B) - // 1 2 3 1 ... (pinned buf A) - // time -> - // - auto defaultStream = resources_->getDefaultStream(device_); - auto copyStream = resources_->getAsyncCopyStream(device_); - - FAISS_ASSERT((size_t) pageSizeInVecs * this->d <= - (size_t) std::numeric_limits::max()); - - float* bufPinnedA = (float*) pinnedAlloc.first; - float* bufPinnedB = bufPinnedA + (size_t) pageSizeInVecs * this->d; - float* bufPinned[2] = {bufPinnedA, bufPinnedB}; - - // Reserve space on the GPU for the destination of the pinned buffer - // copy - DeviceTensor bufGpuA( - resources_->getMemoryManagerCurrentDevice(), - {(int) pageSizeInVecs, (int) this->d}, - defaultStream); - DeviceTensor bufGpuB( - resources_->getMemoryManagerCurrentDevice(), - {(int) pageSizeInVecs, (int) this->d}, - defaultStream); - DeviceTensor* bufGpus[2] = {&bufGpuA, &bufGpuB}; - - // Copy completion events for the pinned buffers - std::unique_ptr eventPinnedCopyDone[2]; - - // Execute completion events for the GPU buffers - std::unique_ptr eventGpuExecuteDone[2]; - - // All offsets are in terms of number of vectors; they remain within - // int bounds (as this function only handles max in vectors) - - // Current start offset for buffer 1 - int cur1 = 0; - int cur1BufIndex = 0; - - // Current start offset for buffer 2 - int cur2 = -1; - int cur2BufIndex = 0; - - // Current start offset for buffer 3 - int cur3 = -1; - int cur3BufIndex = 0; - - while (cur3 < n) { - // Start async pinned -> GPU copy first (buf 2) - if (cur2 != -1 && cur2 < n) { - // Copy pinned to GPU - int numToCopy = std::min(pageSizeInVecs, n - cur2); - - // Make sure any previous execution has completed before continuing - auto& eventPrev = eventGpuExecuteDone[cur2BufIndex]; - if (eventPrev.get()) { - eventPrev->streamWaitOnEvent(copyStream); - } - - CUDA_VERIFY(cudaMemcpyAsync(bufGpus[cur2BufIndex]->data(), - bufPinned[cur2BufIndex], - (size_t) numToCopy * this->d * sizeof(float), - cudaMemcpyHostToDevice, - copyStream)); - - // Mark a completion event in this stream - eventPinnedCopyDone[cur2BufIndex] = - std::move(std::unique_ptr(new CudaEvent(copyStream))); - - // We pick up from here - cur3 = cur2; - cur2 += numToCopy; - cur2BufIndex = (cur2BufIndex == 0) ? 1 : 0; - } - - if (cur3 != -1 && cur3 < n) { - // Process on GPU - int numToProcess = std::min(pageSizeInVecs, n - cur3); - - // Make sure the previous copy has completed before continuing - auto& eventPrev = eventPinnedCopyDone[cur3BufIndex]; - FAISS_ASSERT(eventPrev.get()); - - eventPrev->streamWaitOnEvent(defaultStream); - - // Create tensor wrappers - // DeviceTensor input(bufGpus[cur3BufIndex]->data(), - // {numToProcess, this->d}); - auto outDistancesSlice = outDistances.narrowOutermost(cur3, numToProcess); - auto outIndicesSlice = outIndices.narrowOutermost(cur3, numToProcess); - - searchImpl_(numToProcess, - bufGpus[cur3BufIndex]->data(), - k, - outDistancesSlice.data(), - outIndicesSlice.data(), - bitset); - - // Create completion event - eventGpuExecuteDone[cur3BufIndex] = - std::move(std::unique_ptr(new CudaEvent(defaultStream))); - - // We pick up from here - cur3BufIndex = (cur3BufIndex == 0) ? 1 : 0; - cur3 += numToProcess; - } - - if (cur1 < n) { - // Copy CPU mem to CPU pinned - int numToCopy = std::min(pageSizeInVecs, n - cur1); - - // Make sure any previous copy has completed before continuing - auto& eventPrev = eventPinnedCopyDone[cur1BufIndex]; - if (eventPrev.get()) { - eventPrev->cpuWaitOnEvent(); - } - - memcpy(bufPinned[cur1BufIndex], - x + (size_t) cur1 * this->d, - (size_t) numToCopy * this->d * sizeof(float)); - - // We pick up from here - cur2 = cur1; - cur1 += numToCopy; - cur1BufIndex = (cur1BufIndex == 0) ? 1 : 0; - } - } -} - -void -GpuIndex::compute_residual(const float* x, - float* residual, - Index::idx_t key) const { - FAISS_THROW_MSG("compute_residual not implemented for this type of index"); -} - -void -GpuIndex::compute_residual_n(Index::idx_t n, - const float* xs, - float* residuals, - const Index::idx_t* keys) const { - FAISS_THROW_MSG("compute_residual_n not implemented for this type of index"); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuIndex.h b/core/src/index/thirdparty/faiss/gpu/GpuIndex.h deleted file mode 100644 index ae902f57a8..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuIndex.h +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include - -namespace faiss { namespace gpu { - -class GpuResources; - -struct GpuIndexConfig { - inline GpuIndexConfig() - : device(0), - memorySpace(MemorySpace::Device) { - } - - /// GPU device on which the index is resident - int device; - - /// What memory space to use for primary storage. - /// On Pascal and above (CC 6+) architectures, allows GPUs to use - /// more memory than is available on the GPU. - MemorySpace memorySpace; -}; - -class GpuIndex : public faiss::Index { - public: - GpuIndex(GpuResources* resources, - int dims, - faiss::MetricType metric, - float metricArg, - GpuIndexConfig config); - - inline int getDevice() const { - return device_; - } - - inline GpuResources* getResources() { - return resources_; - } - - /// Set the minimum data size for searches (in MiB) for which we use - /// CPU -> GPU paging - void setMinPagingSize(size_t size); - - /// Returns the current minimum data size for paged searches - size_t getMinPagingSize() const; - - /// `x` can be resident on the CPU or any GPU; copies are performed - /// as needed - /// Handles paged adds if the add set is too large; calls addInternal_ - void add(faiss::Index::idx_t, const float* x) override; - - /// `x` and `ids` can be resident on the CPU or any GPU; copies are - /// performed as needed - /// Handles paged adds if the add set is too large; calls addInternal_ - void add_with_ids(Index::idx_t n, - const float* x, - const Index::idx_t* ids) override; - - /// `x`, `distances` and `labels` can be resident on the CPU or any - /// GPU; copies are performed as needed - void search(Index::idx_t n, - const float* x, - Index::idx_t k, - float* distances, - Index::idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - /// Overridden to force GPU indices to provide their own GPU-friendly - /// implementation - void compute_residual(const float* x, - float* residual, - Index::idx_t key) const override; - - /// Overridden to force GPU indices to provide their own GPU-friendly - /// implementation - void compute_residual_n(Index::idx_t n, - const float* xs, - float* residuals, - const Index::idx_t* keys) const override; - - protected: - /// Copy what we need from the CPU equivalent - void copyFrom(const faiss::Index* index); - - /// Copy what we have to the CPU equivalent - void copyTo(faiss::Index* index) const; - - /// Does addImpl_ require IDs? If so, and no IDs are provided, we will - /// generate them sequentially based on the order in which the IDs are added - virtual bool addImplRequiresIDs_() const = 0; - - /// Overridden to actually perform the add - /// All data is guaranteed to be resident on our device - virtual void addImpl_(int n, - const float* x, - const Index::idx_t* ids) = 0; - - /// Overridden to actually perform the search - /// All data is guaranteed to be resident on our device - virtual void searchImpl_(int n, - const float* x, - int k, - float* distances, - Index::idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const = 0; - -private: - /// Handles paged adds if the add set is too large, passes to - /// addImpl_ to actually perform the add for the current page - void addPaged_(int n, - const float* x, - const Index::idx_t* ids); - - /// Calls addImpl_ for a single page of GPU-resident data - void addPage_(int n, - const float* x, - const Index::idx_t* ids); - - /// Calls searchImpl_ for a single page of GPU-resident data - void searchNonPaged_(int n, - const float* x, - int k, - float* outDistancesData, - Index::idx_t* outIndicesData, - ConcurrentBitsetPtr bitset = nullptr) const; - - /// Calls searchImpl_ for a single page of GPU-resident data, - /// handling paging of the data and copies from the CPU - void searchFromCpuPaged_(int n, - const float* x, - int k, - float* outDistancesData, - Index::idx_t* outIndicesData, - ConcurrentBitsetPtr bitset = nullptr) const; - - protected: - /// Manages streams, cuBLAS handles and scratch memory for devices - GpuResources* resources_; - - /// The GPU device we are resident on - const int device_; - - /// The memory space of our primary storage on the GPU - const MemorySpace memorySpace_; - - /// Size above which we page copies from the CPU to GPU - size_t minPagedSize_; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuIndexBinaryFlat.cu b/core/src/index/thirdparty/faiss/gpu/GpuIndexBinaryFlat.cu deleted file mode 100644 index cd412be944..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuIndexBinaryFlat.cu +++ /dev/null @@ -1,290 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -/// Default CPU search size for which we use paged copies -constexpr size_t kMinPageSize = (size_t) 256 * 1024 * 1024; - -GpuIndexBinaryFlat::GpuIndexBinaryFlat(GpuResources* resources, - const faiss::IndexBinaryFlat* index, - GpuIndexBinaryFlatConfig config) - : IndexBinary(index->d), - resources_(resources), - config_(std::move(config)), - data_(nullptr) { - FAISS_THROW_IF_NOT_FMT(this->d % 8 == 0, - "vector dimension (number of bits) " - "must be divisible by 8 (passed %d)", - this->d); - - // Flat index doesn't need training - this->is_trained = true; - - copyFrom(index); -} - - -GpuIndexBinaryFlat::GpuIndexBinaryFlat(GpuResources* resources, - int dims, - GpuIndexBinaryFlatConfig config) - : IndexBinary(dims), - resources_(resources), - config_(std::move(config)), - data_(nullptr) { - FAISS_THROW_IF_NOT_FMT(this->d % 8 == 0, - "vector dimension (number of bits) " - "must be divisible by 8 (passed %d)", - this->d); - - // Flat index doesn't need training - this->is_trained = true; - - // Construct index - DeviceScope scope(config_.device); - data_ = new BinaryFlatIndex(resources, - this->d, - config_.memorySpace); -} - -GpuIndexBinaryFlat::~GpuIndexBinaryFlat() { - delete data_; -} - -void -GpuIndexBinaryFlat::copyFrom(const faiss::IndexBinaryFlat* index) { - DeviceScope scope(config_.device); - - this->d = index->d; - - // GPU code has 32 bit indices - FAISS_THROW_IF_NOT_FMT(index->ntotal <= - (faiss::Index::idx_t) std::numeric_limits::max(), - "GPU index only supports up to %zu indices; " - "attempting to copy CPU index with %zu parameters", - (size_t) std::numeric_limits::max(), - (size_t) index->ntotal); - this->ntotal = index->ntotal; - - delete data_; - data_ = new BinaryFlatIndex(resources_, - this->d, - config_.memorySpace); - - // The index could be empty - if (index->ntotal > 0) { - data_->add(index->xb.data(), - index->ntotal, - resources_->getDefaultStream(config_.device)); - } -} - -void -GpuIndexBinaryFlat::copyTo(faiss::IndexBinaryFlat* index) const { - DeviceScope scope(config_.device); - - index->d = this->d; - index->ntotal = this->ntotal; - - FAISS_ASSERT(data_); - FAISS_ASSERT(data_->getSize() == this->ntotal); - index->xb.resize(this->ntotal * (this->d / 8)); - - if (this->ntotal > 0) { - fromDevice(data_->getVectorsRef(), - index->xb.data(), - resources_->getDefaultStream(config_.device)); - } -} - -void -GpuIndexBinaryFlat::add(faiss::IndexBinary::idx_t n, - const uint8_t* x) { - DeviceScope scope(config_.device); - - // To avoid multiple re-allocations, ensure we have enough storage - // available - data_->reserve(n, resources_->getDefaultStream(config_.device)); - - // Due to GPU indexing in int32, we can't store more than this - // number of vectors on a GPU - FAISS_THROW_IF_NOT_FMT(this->ntotal + n <= - (faiss::Index::idx_t) std::numeric_limits::max(), - "GPU index only supports up to %zu indices", - (size_t) std::numeric_limits::max()); - - data_->add((const unsigned char*) x, - n, - resources_->getDefaultStream(config_.device)); - this->ntotal += n; -} - -void -GpuIndexBinaryFlat::reset() { - DeviceScope scope(config_.device); - - // Free the underlying memory - data_->reset(); - this->ntotal = 0; -} - -void -GpuIndexBinaryFlat::search(faiss::IndexBinary::idx_t n, - const uint8_t* x, - faiss::IndexBinary::idx_t k, - int32_t* distances, - faiss::IndexBinary::idx_t* labels, - ConcurrentBitsetPtr bitset) const { - if (n == 0) { - return; - } - - // For now, only support <= max int results - FAISS_THROW_IF_NOT_FMT(n <= (Index::idx_t) std::numeric_limits::max(), - "GPU index only supports up to %zu indices", - (size_t) std::numeric_limits::max()); - FAISS_THROW_IF_NOT_FMT(k <= (Index::idx_t) getMaxKSelection(), - "GPU only supports k <= %d (requested %d)", - getMaxKSelection(), - (int) k); // select limitation - - DeviceScope scope(config_.device); - auto stream = resources_->getDefaultStream(config_.device); - - // The input vectors may be too large for the GPU, but we still - // assume that the output distances and labels are not. - // Go ahead and make space for output distances and labels on the - // GPU. - // If we reach a point where all inputs are too big, we can add - // another level of tiling. - auto outDistances = toDevice(resources_, - config_.device, - distances, - stream, - {(int) n, (int) k}); - - // FlatIndex only supports an interface returning int indices - DeviceTensor outIntIndices( - resources_->getMemoryManagerCurrentDevice(), - {(int) n, (int) k}, stream); - - bool usePaged = false; - - if (getDeviceForAddress(x) == -1) { - // It is possible that the user is querying for a vector set size - // `x` that won't fit on the GPU. - // In this case, we will have to handle paging of the data from CPU - // -> GPU. - // Currently, we don't handle the case where the output data won't - // fit on the GPU (e.g., n * k is too large for the GPU memory). - size_t dataSize = (size_t) n * (this->d / 8) * sizeof(uint8_t); - - if (dataSize >= kMinPageSize) { - searchFromCpuPaged_(n, x, k, - outDistances.data(), - outIntIndices.data()); - usePaged = true; - } - } - - if (!usePaged) { - searchNonPaged_(n, x, k, - outDistances.data(), - outIntIndices.data()); - } - - // Convert and copy int indices out - auto outIndices = toDevice(resources_, - config_.device, - labels, - stream, - {(int) n, (int) k}); - - // Convert int to long - convertTensor(stream, - outIntIndices, - outIndices); - - // Copy back if necessary - fromDevice(outDistances, distances, stream); - fromDevice(outIndices, labels, stream); -} - -void -GpuIndexBinaryFlat::searchNonPaged_(int n, - const uint8_t* x, - int k, - int32_t* outDistancesData, - int* outIndicesData) const { - Tensor outDistances(outDistancesData, {n, k}); - Tensor outIndices(outIndicesData, {n, k}); - - auto stream = resources_->getDefaultStream(config_.device); - - // Make sure arguments are on the device we desire; use temporary - // memory allocations to move it if necessary - auto vecs = toDevice(resources_, - config_.device, - const_cast(x), - stream, - {n, (int) (this->d / 8)}); - - data_->query(vecs, k, outDistances, outIndices); -} - -void -GpuIndexBinaryFlat::searchFromCpuPaged_(int n, - const uint8_t* x, - int k, - int32_t* outDistancesData, - int* outIndicesData) const { - Tensor outDistances(outDistancesData, {n, k}); - Tensor outIndices(outIndicesData, {n, k}); - - auto vectorSize = sizeof(uint8_t) * (this->d / 8); - - // Just page without overlapping copy with compute (as GpuIndexFlat does) - int batchSize = utils::nextHighestPowerOf2( - (int) ((size_t) kMinPageSize / vectorSize)); - - for (int cur = 0; cur < n; cur += batchSize) { - int num = std::min(batchSize, n - cur); - - auto outDistancesSlice = outDistances.narrowOutermost(cur, num); - auto outIndicesSlice = outIndices.narrowOutermost(cur, num); - - searchNonPaged_(num, - x + (size_t) cur * (this->d / 8), - k, - outDistancesSlice.data(), - outIndicesSlice.data()); - } -} - -void -GpuIndexBinaryFlat::reconstruct(faiss::IndexBinary::idx_t key, - uint8_t* out) const { - DeviceScope scope(config_.device); - - FAISS_THROW_IF_NOT_MSG(key < this->ntotal, "index out of bounds"); - auto stream = resources_->getDefaultStream(config_.device); - - auto& vecs = data_->getVectorsRef(); - auto vec = vecs[key]; - - fromDevice(vec.data(), out, vecs.getSize(1), stream); -} - -} } // namespace gpu diff --git a/core/src/index/thirdparty/faiss/gpu/GpuIndexBinaryFlat.h b/core/src/index/thirdparty/faiss/gpu/GpuIndexBinaryFlat.h deleted file mode 100644 index da559cb4f1..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuIndexBinaryFlat.h +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include - -namespace faiss { namespace gpu { - -class BinaryFlatIndex; -class GpuResources; - -struct GpuIndexBinaryFlatConfig : public GpuIndexConfig { -}; - -/// A GPU version of IndexBinaryFlat for brute-force comparison of bit vectors -/// via Hamming distance -class GpuIndexBinaryFlat : public IndexBinary { - public: - /// Construct from a pre-existing faiss::IndexBinaryFlat instance, copying - /// data over to the given GPU - GpuIndexBinaryFlat(GpuResources* resources, - const faiss::IndexBinaryFlat* index, - GpuIndexBinaryFlatConfig config = - GpuIndexBinaryFlatConfig()); - - /// Construct an empty instance that can be added to - GpuIndexBinaryFlat(GpuResources* resources, - int dims, - GpuIndexBinaryFlatConfig config = - GpuIndexBinaryFlatConfig()); - - ~GpuIndexBinaryFlat() override; - - /// Initialize ourselves from the given CPU index; will overwrite - /// all data in ourselves - void copyFrom(const faiss::IndexBinaryFlat* index); - - /// Copy ourselves to the given CPU index; will overwrite all data - /// in the index instance - void copyTo(faiss::IndexBinaryFlat* index) const; - - void add(faiss::IndexBinary::idx_t n, - const uint8_t* x) override; - - void reset() override; - - void search(faiss::IndexBinary::idx_t n, - const uint8_t* x, - faiss::IndexBinary::idx_t k, - int32_t* distances, - faiss::IndexBinary::idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - void reconstruct(faiss::IndexBinary::idx_t key, - uint8_t* recons) const override; - - protected: - /// Called from search when the input data is on the CPU; - /// potentially allows for pinned memory usage - void searchFromCpuPaged_(int n, - const uint8_t* x, - int k, - int32_t* outDistancesData, - int* outIndicesData) const; - - void searchNonPaged_(int n, - const uint8_t* x, - int k, - int32_t* outDistancesData, - int* outIndicesData) const; - - protected: - /// Manages streans, cuBLAS handles and scratch memory for devices - GpuResources* resources_; - - /// Configuration options - GpuIndexBinaryFlatConfig config_; - - /// Holds our GPU data containing the list of vectors; is managed via raw - /// pointer so as to allow non-CUDA compilers to see this header - BinaryFlatIndex* data_; -}; - -} } // namespace gpu diff --git a/core/src/index/thirdparty/faiss/gpu/GpuIndexFlat.cu b/core/src/index/thirdparty/faiss/gpu/GpuIndexFlat.cu deleted file mode 100644 index 5f4893586e..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuIndexFlat.cu +++ /dev/null @@ -1,386 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -GpuIndexFlat::GpuIndexFlat(GpuResources* resources, - const faiss::IndexFlat* index, - GpuIndexFlatConfig config) : - GpuIndex(resources, - index->d, - index->metric_type, - index->metric_arg, - config), - config_(std::move(config)), - data_(nullptr) { - // Flat index doesn't need training - this->is_trained = true; - - copyFrom(index); -} - -GpuIndexFlat::GpuIndexFlat(GpuResources* resources, - int dims, - faiss::MetricType metric, - GpuIndexFlatConfig config) : - GpuIndex(resources, dims, metric, 0, config), - config_(std::move(config)), - data_(nullptr) { - // Flat index doesn't need training - this->is_trained = true; - - // Construct index - DeviceScope scope(device_); - data_ = new FlatIndex(resources, - dims, - config_.useFloat16, - config_.storeTransposed, - memorySpace_); -} - -GpuIndexFlat::~GpuIndexFlat() { - delete data_; -} - -void -GpuIndexFlat::copyFrom(const faiss::IndexFlat* index) { - DeviceScope scope(device_); - - GpuIndex::copyFrom(index); - - // GPU code has 32 bit indices - FAISS_THROW_IF_NOT_FMT(index->ntotal <= - (faiss::Index::idx_t) std::numeric_limits::max(), - "GPU index only supports up to %zu indices; " - "attempting to copy CPU index with %zu parameters", - (size_t) std::numeric_limits::max(), - (size_t) index->ntotal); - - delete data_; - data_ = new FlatIndex(resources_, - this->d, - config_.useFloat16, - config_.storeTransposed, - memorySpace_); - - // The index could be empty - if (index->ntotal > 0) { - data_->add(index->xb.data(), - index->ntotal, - resources_->getDefaultStream(device_)); - } - - xb_.clear(); - - if (config_.storeInCpu) { - xb_ = index->xb; - } -} - -void -GpuIndexFlat::copyTo(faiss::IndexFlat* index) const { - DeviceScope scope(device_); - - GpuIndex::copyTo(index); - - FAISS_ASSERT(data_); - FAISS_ASSERT(data_->getSize() == this->ntotal); - index->xb.resize(this->ntotal * this->d); - - auto stream = resources_->getDefaultStream(device_); - - if (this->ntotal > 0) { - if (config_.useFloat16) { - auto vecFloat32 = data_->getVectorsFloat32Copy(stream); - fromDevice(vecFloat32, index->xb.data(), stream); - } else { - fromDevice(data_->getVectorsFloat32Ref(), index->xb.data(), stream); - } - } -} - -size_t -GpuIndexFlat::getNumVecs() const { - return this->ntotal; -} - -void -GpuIndexFlat::reset() { - DeviceScope scope(device_); - - // Free the underlying memory - data_->reset(); - this->ntotal = 0; -} - -void -GpuIndexFlat::train(Index::idx_t n, const float* x) { - // nothing to do -} - -void -GpuIndexFlat::add(Index::idx_t n, const float* x) { - FAISS_THROW_IF_NOT_MSG(this->is_trained, "Index not trained"); - - // For now, only support <= max int results - FAISS_THROW_IF_NOT_FMT(n <= (Index::idx_t) std::numeric_limits::max(), - "GPU index only supports up to %d indices", - std::numeric_limits::max()); - - if (n == 0) { - // nothing to add - return; - } - - DeviceScope scope(device_); - - // To avoid multiple re-allocations, ensure we have enough storage - // available - data_->reserve(n, resources_->getDefaultStream(device_)); - - // If we're not operating in float16 mode, we don't need the input - // data to be resident on our device; we can add directly. - if (!config_.useFloat16) { - addImpl_(n, x, nullptr); - } else { - // Otherwise, perform the paging - GpuIndex::add(n, x); - } -} - -bool -GpuIndexFlat::addImplRequiresIDs_() const { - return false; -} - -void -GpuIndexFlat::addImpl_(int n, - const float* x, - const Index::idx_t* ids) { - FAISS_ASSERT(data_); - FAISS_ASSERT(n > 0); - - // We do not support add_with_ids - FAISS_THROW_IF_NOT_MSG(!ids, "add_with_ids not supported"); - - // Due to GPU indexing in int32, we can't store more than this - // number of vectors on a GPU - FAISS_THROW_IF_NOT_FMT(this->ntotal + n <= - (faiss::Index::idx_t) std::numeric_limits::max(), - "GPU index only supports up to %zu indices", - (size_t) std::numeric_limits::max()); - - data_->add(x, n, resources_->getDefaultStream(device_)); - this->ntotal += n; -} - -void -GpuIndexFlat::searchImpl_(int n, - const float* x, - int k, - float* distances, - Index::idx_t* labels, - ConcurrentBitsetPtr bitset) const { - auto stream = resources_->getDefaultStream(device_); - - // Input and output data are already resident on the GPU - Tensor queries(const_cast(x), {n, (int) this->d}); - Tensor outDistances(distances, {n, k}); - Tensor outLabels(labels, {n, k}); - - // FlatIndex only supports int indices - DeviceTensor outIntLabels( - resources_->getMemoryManagerCurrentDevice(), {n, k}, stream); - - // Copy bitset to GPU - if (!bitset) { - auto bitsetDevice = toDevice(resources_, device_, nullptr, stream, {0}); - data_->query(queries, bitsetDevice, k, metric_type, metric_arg, outDistances, outIntLabels, true); - } else { - auto bitsetDevice = toDevice(resources_, device_, - const_cast(bitset->data()), stream, - {(int) bitset->size()}); - data_->query(queries, bitsetDevice, k, metric_type, metric_arg, outDistances, outIntLabels, true); - } - - // Convert int to idx_t - convertTensor(stream, - outIntLabels, - outLabels); -} - -void -GpuIndexFlat::reconstruct(faiss::Index::idx_t key, - float* out) const { - if (config_.storeInCpu && xb_.size() > 0) { - memcpy (out, &(this->xb_[key * this->d]), sizeof(*out) * this->d); - return; - } - - DeviceScope scope(device_); - - FAISS_THROW_IF_NOT_MSG(key < this->ntotal, "index out of bounds"); - auto stream = resources_->getDefaultStream(device_); - - if (config_.useFloat16) { - // FIXME jhj: kernel for copy - auto vec = data_->getVectorsFloat32Copy(key, 1, stream); - fromDevice(vec.data(), out, this->d, stream); - } else { - auto vec = data_->getVectorsFloat32Ref()[key]; - fromDevice(vec.data(), out, this->d, stream); - } -} - -void -GpuIndexFlat::reconstruct_n(faiss::Index::idx_t i0, - faiss::Index::idx_t num, - float* out) const { - DeviceScope scope(device_); - - FAISS_THROW_IF_NOT_MSG(i0 < this->ntotal, "index out of bounds"); - FAISS_THROW_IF_NOT_MSG(i0 + num - 1 < this->ntotal, "num out of bounds"); - auto stream = resources_->getDefaultStream(device_); - - if (config_.useFloat16) { - // FIXME jhj: kernel for copy - auto vec = data_->getVectorsFloat32Copy(i0, num, stream); - fromDevice(vec.data(), out, num * this->d, stream); - } else { - auto vec = data_->getVectorsFloat32Ref()[i0]; - fromDevice(vec.data(), out, this->d * num, stream); - } -} - -void -GpuIndexFlat::compute_residual(const float* x, - float* residual, - faiss::Index::idx_t key) const { - compute_residual_n(1, x, residual, &key); -} - -void -GpuIndexFlat::compute_residual_n(faiss::Index::idx_t n, - const float* xs, - float* residuals, - const faiss::Index::idx_t* keys) const { - FAISS_THROW_IF_NOT_FMT(n <= - (faiss::Index::idx_t) std::numeric_limits::max(), - "GPU index only supports up to %zu indices", - (size_t) std::numeric_limits::max()); - - auto stream = resources_->getDefaultStream(device_); - - DeviceScope scope(device_); - - auto vecsDevice = - toDevice(resources_, device_, - const_cast(xs), stream, - {(int) n, (int) this->d}); - auto idsDevice = - toDevice(resources_, device_, - const_cast(keys), - stream, - {(int) n}); - auto residualDevice = - toDevice(resources_, device_, residuals, stream, - {(int) n, (int) this->d}); - - // Convert idx_t to int - auto keysInt = - convertTensor(resources_, stream, idsDevice); - - FAISS_ASSERT(data_); - data_->computeResidual(vecsDevice, - keysInt, - residualDevice); - - fromDevice(residualDevice, residuals, stream); -} - -// -// GpuIndexFlatL2 -// - -GpuIndexFlatL2::GpuIndexFlatL2(GpuResources* resources, - faiss::IndexFlatL2* index, - GpuIndexFlatConfig config) : - GpuIndexFlat(resources, index, config) { -} - -GpuIndexFlatL2::GpuIndexFlatL2(GpuResources* resources, - int dims, - GpuIndexFlatConfig config) : - GpuIndexFlat(resources, dims, faiss::METRIC_L2, config) { -} - -void -GpuIndexFlatL2::copyFrom(faiss::IndexFlat* index) { - FAISS_THROW_IF_NOT_MSG(index->metric_type == metric_type, - "Cannot copy a GpuIndexFlatL2 from an index of " - "different metric_type"); - - GpuIndexFlat::copyFrom(index); -} - -void -GpuIndexFlatL2::copyTo(faiss::IndexFlat* index) { - FAISS_THROW_IF_NOT_MSG(index->metric_type == metric_type, - "Cannot copy a GpuIndexFlatL2 to an index of " - "different metric_type"); - - GpuIndexFlat::copyTo(index); -} - -// -// GpuIndexFlatIP -// - -GpuIndexFlatIP::GpuIndexFlatIP(GpuResources* resources, - faiss::IndexFlatIP* index, - GpuIndexFlatConfig config) : - GpuIndexFlat(resources, index, config) { -} - -GpuIndexFlatIP::GpuIndexFlatIP(GpuResources* resources, - int dims, - GpuIndexFlatConfig config) : - GpuIndexFlat(resources, dims, faiss::METRIC_INNER_PRODUCT, config) { -} - -void -GpuIndexFlatIP::copyFrom(faiss::IndexFlat* index) { - FAISS_THROW_IF_NOT_MSG(index->metric_type == metric_type, - "Cannot copy a GpuIndexFlatIP from an index of " - "different metric_type"); - - GpuIndexFlat::copyFrom(index); -} - -void -GpuIndexFlatIP::copyTo(faiss::IndexFlat* index) { - // The passed in index must be IP - FAISS_THROW_IF_NOT_MSG(index->metric_type == metric_type, - "Cannot copy a GpuIndexFlatIP to an index of " - "different metric_type"); - - GpuIndexFlat::copyTo(index); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuIndexFlat.h b/core/src/index/thirdparty/faiss/gpu/GpuIndexFlat.h deleted file mode 100644 index 90823d69a4..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuIndexFlat.h +++ /dev/null @@ -1,188 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include - -namespace faiss { - -struct IndexFlat; -struct IndexFlatL2; -struct IndexFlatIP; - -} - -namespace faiss { namespace gpu { - -struct FlatIndex; - -struct GpuIndexFlatConfig : public GpuIndexConfig { - inline GpuIndexFlatConfig() - : useFloat16(false), - storeTransposed(false), - storeInCpu(false) { - } - - /// Whether or not data is stored as float16 - bool useFloat16; - - /// Whether or not data is stored (transparently) in a transposed - /// layout, enabling use of the NN GEMM call, which is ~10% faster. - /// This will improve the speed of the flat index, but will - /// substantially slow down any add() calls made, as all data must - /// be transposed, and will increase storage requirements (we store - /// data in both transposed and non-transposed layouts). - bool storeTransposed; - - bool storeInCpu; -}; - -/// Wrapper around the GPU implementation that looks like -/// faiss::IndexFlat; copies over centroid data from a given -/// faiss::IndexFlat -class GpuIndexFlat : public GpuIndex { - public: - /// Construct from a pre-existing faiss::IndexFlat instance, copying - /// data over to the given GPU - GpuIndexFlat(GpuResources* resources, - const faiss::IndexFlat* index, - GpuIndexFlatConfig config = GpuIndexFlatConfig()); - - /// Construct an empty instance that can be added to - GpuIndexFlat(GpuResources* resources, - int dims, - faiss::MetricType metric, - GpuIndexFlatConfig config = GpuIndexFlatConfig()); - - ~GpuIndexFlat() override; - - /// Initialize ourselves from the given CPU index; will overwrite - /// all data in ourselves - void copyFrom(const faiss::IndexFlat* index); - - /// Copy ourselves to the given CPU index; will overwrite all data - /// in the index instance - void copyTo(faiss::IndexFlat* index) const; - - /// Returns the number of vectors we contain - size_t getNumVecs() const; - - /// Clears all vectors from this index - void reset() override; - - /// This index is not trained, so this does nothing - void train(Index::idx_t n, const float* x) override; - - /// Overrides to avoid excessive copies - void add(faiss::Index::idx_t, const float* x) override; - - /// Reconstruction methods; prefer the batch reconstruct as it will - /// be more efficient - void reconstruct(faiss::Index::idx_t key, float* out) const override; - - /// Batch reconstruction method - void reconstruct_n(faiss::Index::idx_t i0, - faiss::Index::idx_t num, - float* out) const override; - - /// Compute residual - void compute_residual(const float* x, - float* residual, - faiss::Index::idx_t key) const override; - - /// Compute residual (batch mode) - void compute_residual_n(faiss::Index::idx_t n, - const float* xs, - float* residuals, - const faiss::Index::idx_t* keys) const override; - - /// For internal access - inline FlatIndex* getGpuData() { return data_; } - - protected: - /// Flat index does not require IDs as there is no storage available for them - bool addImplRequiresIDs_() const override; - - /// Called from GpuIndex for add - void addImpl_(int n, - const float* x, - const Index::idx_t* ids) override; - - /// Called from GpuIndex for search - void searchImpl_(int n, - const float* x, - int k, - float* distances, - faiss::Index::idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - protected: - /// Our config object - const GpuIndexFlatConfig config_; - - /// Holds our GPU data containing the list of vectors; is managed via raw - /// pointer so as to allow non-CUDA compilers to see this header - FlatIndex* data_; - - std::vector xb_; -}; - -/// Wrapper around the GPU implementation that looks like -/// faiss::IndexFlatL2; copies over centroid data from a given -/// faiss::IndexFlat -class GpuIndexFlatL2 : public GpuIndexFlat { - public: - /// Construct from a pre-existing faiss::IndexFlatL2 instance, copying - /// data over to the given GPU - GpuIndexFlatL2(GpuResources* resources, - faiss::IndexFlatL2* index, - GpuIndexFlatConfig config = GpuIndexFlatConfig()); - - /// Construct an empty instance that can be added to - GpuIndexFlatL2(GpuResources* resources, - int dims, - GpuIndexFlatConfig config = GpuIndexFlatConfig()); - - /// Initialize ourselves from the given CPU index; will overwrite - /// all data in ourselves - void copyFrom(faiss::IndexFlat* index); - - /// Copy ourselves to the given CPU index; will overwrite all data - /// in the index instance - void copyTo(faiss::IndexFlat* index); -}; - -/// Wrapper around the GPU implementation that looks like -/// faiss::IndexFlatIP; copies over centroid data from a given -/// faiss::IndexFlat -class GpuIndexFlatIP : public GpuIndexFlat { - public: - /// Construct from a pre-existing faiss::IndexFlatIP instance, copying - /// data over to the given GPU - GpuIndexFlatIP(GpuResources* resources, - faiss::IndexFlatIP* index, - GpuIndexFlatConfig config = GpuIndexFlatConfig()); - - /// Construct an empty instance that can be added to - GpuIndexFlatIP(GpuResources* resources, - int dims, - GpuIndexFlatConfig config = GpuIndexFlatConfig()); - - /// Initialize ourselves from the given CPU index; will overwrite - /// all data in ourselves - void copyFrom(faiss::IndexFlat* index); - - /// Copy ourselves to the given CPU index; will overwrite all data - /// in the index instance - void copyTo(faiss::IndexFlat* index); -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVF.cu b/core/src/index/thirdparty/faiss/gpu/GpuIndexIVF.cu deleted file mode 100644 index 130e95f866..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVF.cu +++ /dev/null @@ -1,324 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -GpuIndexIVF::GpuIndexIVF(GpuResources* resources, - int dims, - faiss::MetricType metric, - float metricArg, - int nlistIn, - GpuIndexIVFConfig config) : - GpuIndex(resources, dims, metric, metricArg, config), - ivfConfig_(std::move(config)), - nlist(nlistIn), - nprobe(1), - quantizer(nullptr) { - - init_(); - - // Only IP and L2 are supported for now - if (!(metric_type == faiss::METRIC_L2 || - metric_type == faiss::METRIC_INNER_PRODUCT)) { - FAISS_THROW_FMT("unsupported metric type %d", (int) metric_type); - } -} - -void -GpuIndexIVF::init_() { - FAISS_THROW_IF_NOT_MSG(nlist > 0, "nlist must be > 0"); - - // Spherical by default if the metric is inner_product - if (metric_type == faiss::METRIC_INNER_PRODUCT) { - cp.spherical = true; - } - - // here we set a low # iterations because this is typically used - // for large clusterings - cp.niter = 10; - cp.verbose = verbose; - - if (!quantizer) { - // Construct an empty quantizer - GpuIndexFlatConfig config = ivfConfig_.flatConfig; - // FIXME: inherit our same device - config.device = device_; - - if (metric_type == faiss::METRIC_L2) { - quantizer = new GpuIndexFlatL2(resources_, d, config); - } else if (metric_type == faiss::METRIC_INNER_PRODUCT) { - quantizer = new GpuIndexFlatIP(resources_, d, config); - } else { - // unknown metric type - FAISS_THROW_FMT("unsupported metric type %d", (int) metric_type); - } - } -} - -GpuIndexIVF::~GpuIndexIVF() { - if (remove_quantizer == 1) { - delete quantizer; - } -} - -GpuIndexFlat* -GpuIndexIVF::getQuantizer() { - return quantizer; -} - -void -GpuIndexIVF::copyFrom(const faiss::IndexIVF* index) { - DeviceScope scope(device_); - - GpuIndex::copyFrom(index); - - FAISS_ASSERT(index->nlist > 0); - FAISS_THROW_IF_NOT_FMT(index->nlist <= - (faiss::Index::idx_t) std::numeric_limits::max(), - "GPU index only supports %zu inverted lists", - (size_t) std::numeric_limits::max()); - nlist = index->nlist; - - FAISS_THROW_IF_NOT_FMT(index->nprobe > 0 && - index->nprobe <= getMaxKSelection(), - "GPU index only supports nprobe <= %zu; passed %zu", - (size_t) getMaxKSelection(), - index->nprobe); - nprobe = index->nprobe; - - // The metric type may have changed as well, so we might have to - // change our quantizer - delete quantizer; - quantizer = nullptr; - - // Construct an empty quantizer - GpuIndexFlatConfig config = ivfConfig_.flatConfig; - // FIXME: inherit our same device - config.device = device_; - - if (index->metric_type == faiss::METRIC_L2) { - // FIXME: 2 different float16 options? - quantizer = new GpuIndexFlatL2(resources_, this->d, config); - } else if (index->metric_type == faiss::METRIC_INNER_PRODUCT) { - // FIXME: 2 different float16 options? - quantizer = new GpuIndexFlatIP(resources_, this->d, config); - } else { - // unknown metric type - FAISS_ASSERT(false); - } - - if (!index->is_trained) { - // copied in GpuIndex::copyFrom - FAISS_ASSERT(!is_trained && ntotal == 0); - return; - } - - // copied in GpuIndex::copyFrom - // ntotal can exceed max int, but the number of vectors per inverted - // list cannot exceed this. We check this in the subclasses. - FAISS_ASSERT(is_trained && (ntotal == index->ntotal)); - - // Since we're trained, the quantizer must have data - FAISS_ASSERT(index->quantizer->ntotal > 0); - - // Right now, we can only handle IndexFlat or derived classes - auto qFlat = dynamic_cast(index->quantizer); - FAISS_THROW_IF_NOT_MSG(qFlat, - "Only IndexFlat is supported for the coarse quantizer " - "for copying from an IndexIVF into a GpuIndexIVF"); - - quantizer->copyFrom(qFlat); -} - -void -GpuIndexIVF::copyFrom(faiss::IndexIVF* index, gpu::GpuIndexFlat *&qt, int64_t mode) { - DeviceScope scope(device_); - - this->d = index->d; - this->metric_type = index->metric_type; - - FAISS_ASSERT(index->nlist > 0); - FAISS_THROW_IF_NOT_FMT(index->nlist <= - (faiss::Index::idx_t) std::numeric_limits::max(), - "GPU index only supports %zu inverted lists", - (size_t) std::numeric_limits::max()); - nlist = index->nlist; - - FAISS_THROW_IF_NOT_FMT(index->nprobe > 0 && - index->nprobe <= getMaxKSelection(), - "GPU index only supports nprobe <= %zu; passed %zu", - (size_t) getMaxKSelection(), - index->nprobe); - nprobe = index->nprobe; - - // The metric type may have changed as well, so we might have to - // change our quantizer - delete quantizer; - quantizer = nullptr; - - // Construct an empty quantizer - GpuIndexFlatConfig config = ivfConfig_.flatConfig; - // FIXME: inherit our same device - config.device = device_; - config.storeInCpu = true; - - if(qt == nullptr) { - if (index->metric_type == faiss::METRIC_L2) { - // FIXME: 2 different float16 options? - quantizer = new GpuIndexFlatL2(resources_, this->d, config); - } else if (index->metric_type == faiss::METRIC_INNER_PRODUCT) { - // FIXME: 2 different float16 options? - quantizer = new GpuIndexFlatIP(resources_, this->d, config); - } else { - // unknown metric type - FAISS_ASSERT(false); - } - } - - if (!index->is_trained) { - this->is_trained = false; - this->ntotal = 0; - return; - } - - // Otherwise, we can populate ourselves from the other index - this->is_trained = true; - - // restore quantizer from backup ptr - index->restore_quantizer(); - - // ntotal can exceed max int, but the number of vectors per inverted - // list cannot exceed this. We check this in the subclasses. - this->ntotal = index->ntotal; - - // Since we're trained, the quantizer must have data - FAISS_ASSERT(index->quantizer->ntotal > 0); - - if(qt == nullptr) { - // Right now, we can only handle IndexFlat or derived classes - auto qFlat = dynamic_cast(index->quantizer); - FAISS_THROW_IF_NOT_MSG(qFlat, - "Only IndexFlat is supported for the coarse quantizer " - "for copying from an IndexIVF into a GpuIndexIVF"); - quantizer->copyFrom(qFlat); - qt = quantizer; - } else { - quantizer = qt; - } - remove_quantizer = 0; -} - -void -GpuIndexIVF::copyTo(faiss::IndexIVF* index) const { - DeviceScope scope(device_); - - // - // Index information - // - GpuIndex::copyTo(index); - - // - // IndexIVF information - // - index->nlist = nlist; - index->nprobe = nprobe; - - // Construct and copy the appropriate quantizer - faiss::IndexFlat* q = nullptr; - - if (this->metric_type == faiss::METRIC_L2) { - q = new faiss::IndexFlatL2(this->d); - - } else if (this->metric_type == faiss::METRIC_INNER_PRODUCT) { - q = new faiss::IndexFlatIP(this->d); - - } else { - // we should have one of the above metrics - FAISS_ASSERT(false); - } - - FAISS_ASSERT(quantizer); - quantizer->copyTo(q); - - if (index->own_fields) { - delete index->quantizer; - } - - index->quantizer = q; - index->quantizer_trains_alone = 0; - index->own_fields = true; - index->cp = this->cp; - index->make_direct_map(false); -} - -int -GpuIndexIVF::getNumLists() const { - return nlist; -} - -void -GpuIndexIVF::setNumProbes(int nprobe) { - FAISS_THROW_IF_NOT_FMT(nprobe > 0 && nprobe <= getMaxKSelection(), - "GPU index only supports nprobe <= %d; passed %d", - getMaxKSelection(), - nprobe); - this->nprobe = nprobe; -} - -int -GpuIndexIVF::getNumProbes() const { - return nprobe; -} - -bool -GpuIndexIVF::addImplRequiresIDs_() const { - // All IVF indices have storage for IDs - return true; -} - -void -GpuIndexIVF::trainQuantizer_(faiss::Index::idx_t n, const float* x) { - if (n == 0) { - // nothing to do - return; - } - - if (quantizer->is_trained && (quantizer->ntotal == nlist)) { - if (this->verbose) { - printf ("IVF quantizer does not need training.\n"); - } - - return; - } - - if (this->verbose) { - printf ("Training IVF quantizer on %ld vectors in %dD\n", n, d); - } - - DeviceScope scope(device_); - - // leverage the CPU-side k-means code, which works for the GPU - // flat index as well - quantizer->reset(); - Clustering clus(this->d, nlist, this->cp); - clus.verbose = verbose; - clus.train(n, x, *quantizer); - quantizer->is_trained = true; - - FAISS_ASSERT(quantizer->ntotal == nlist); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVF.h b/core/src/index/thirdparty/faiss/gpu/GpuIndexIVF.h deleted file mode 100644 index bc0dddc9a6..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVF.h +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include - -namespace faiss { struct IndexIVF; } - -namespace faiss { namespace gpu { - -class GpuIndexFlat; -class GpuResources; - -struct GpuIndexIVFConfig : public GpuIndexConfig { - inline GpuIndexIVFConfig() - : indicesOptions(INDICES_64_BIT) { - } - - /// Index storage options for the GPU - IndicesOptions indicesOptions; - - /// Configuration for the coarse quantizer object - GpuIndexFlatConfig flatConfig; -}; - -class GpuIndexIVF : public GpuIndex { - public: - GpuIndexIVF(GpuResources* resources, - int dims, - faiss::MetricType metric, - float metricArg, - int nlist, - GpuIndexIVFConfig config = GpuIndexIVFConfig()); - - ~GpuIndexIVF() override; - - private: - /// Shared initialization functions - void init_(); - - public: - /// Copy what we need from the CPU equivalent - void copyFrom(const faiss::IndexIVF* index); - - void copyFrom(faiss::IndexIVF* index, gpu::GpuIndexFlat *&qt, int64_t mode); - - /// Copy what we have to the CPU equivalent - void copyTo(faiss::IndexIVF* index) const; - - /// Returns the number of inverted lists we're managing - int getNumLists() const; - - /// Return the quantizer we're using - GpuIndexFlat* getQuantizer(); - - /// Sets the number of list probes per query - void setNumProbes(int nprobe); - - /// Returns our current number of list probes per query - int getNumProbes() const; - - protected: - bool addImplRequiresIDs_() const override; - void trainQuantizer_(faiss::Index::idx_t n, const float* x); - - public: - /// Exposing this like the CPU version for manipulation - ClusteringParameters cp; - - /// Exposing this like the CPU version for query - int nlist; - - /// Exposing this like the CPU version for manipulation - int nprobe; - - /// Exposeing this like the CPU version for query - GpuIndexFlat* quantizer; - - int remove_quantizer = 1; - - protected: - GpuIndexIVFConfig ivfConfig_; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFFlat.cu b/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFFlat.cu deleted file mode 100644 index e806ea49fa..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFFlat.cu +++ /dev/null @@ -1,264 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace faiss { namespace gpu { - -GpuIndexIVFFlat::GpuIndexIVFFlat(GpuResources* resources, - const faiss::IndexIVFFlat* index, - GpuIndexIVFFlatConfig config) : - GpuIndexIVF(resources, - index->d, - index->metric_type, - index->metric_arg, - index->nlist, - config), - ivfFlatConfig_(config), - reserveMemoryVecs_(0), - index_(nullptr) { - copyFrom(index); -} - -GpuIndexIVFFlat::GpuIndexIVFFlat(GpuResources* resources, - int dims, - int nlist, - faiss::MetricType metric, - GpuIndexIVFFlatConfig config) : - GpuIndexIVF(resources, dims, metric, 0, nlist, config), - ivfFlatConfig_(config), - reserveMemoryVecs_(0), - index_(nullptr) { - - // faiss::Index params - this->is_trained = false; - - // We haven't trained ourselves, so don't construct the IVFFlat - // index yet -} - -GpuIndexIVFFlat::~GpuIndexIVFFlat() { - delete index_; -} - -void -GpuIndexIVFFlat::reserveMemory(size_t numVecs) { - reserveMemoryVecs_ = numVecs; - if (index_) { - DeviceScope scope(device_); - index_->reserveMemory(numVecs); - } -} - -void -GpuIndexIVFFlat::copyFrom(const faiss::IndexIVFFlat* index) { - DeviceScope scope(device_); - - GpuIndexIVF::copyFrom(index); - - // Clear out our old data - delete index_; - index_ = nullptr; - - // The other index might not be trained - if (!index->is_trained) { - FAISS_ASSERT(!is_trained); - return; - } - - // Otherwise, we can populate ourselves from the other index - FAISS_ASSERT(is_trained); - - // Copy our lists as well - index_ = new IVFFlat(resources_, - quantizer->getGpuData(), - index->metric_type, - index->metric_arg, - false, // no residual - nullptr, // no scalar quantizer - ivfFlatConfig_.indicesOptions, - memorySpace_); - InvertedLists *ivf = index->invlists; - - if (ReadOnlyArrayInvertedLists* rol = dynamic_cast(ivf)) { - index_->copyCodeVectorsFromCpu((const float* )(rol->pin_readonly_codes->data), - (const long *)(rol->pin_readonly_ids->data), rol->readonly_length); - /* double t0 = getmillisecs(); */ - /* std::cout << "Readonly Takes " << getmillisecs() - t0 << " ms" << std::endl; */ - } else { - for (size_t i = 0; i < ivf->nlist; ++i) { - auto numVecs = ivf->list_size(i); - - // GPU index can only support max int entries per list - FAISS_THROW_IF_NOT_FMT(numVecs <= - (size_t) std::numeric_limits::max(), - "GPU inverted list can only support " - "%zu entries; %zu found", - (size_t) std::numeric_limits::max(), - numVecs); - - index_->addCodeVectorsFromCpu(i, - (const unsigned char*)(ivf->get_codes(i)), - ivf->get_ids(i), - numVecs); - } - } -} - -void -GpuIndexIVFFlat::copyTo(faiss::IndexIVFFlat* index) const { - DeviceScope scope(device_); - - // We must have the indices in order to copy to ourselves - FAISS_THROW_IF_NOT_MSG(ivfFlatConfig_.indicesOptions != INDICES_IVF, - "Cannot copy to CPU as GPU index doesn't retain " - "indices (INDICES_IVF)"); - - GpuIndexIVF::copyTo(index); - index->code_size = this->d * sizeof(float); - - InvertedLists *ivf = new ArrayInvertedLists(nlist, index->code_size); - index->replace_invlists(ivf, true); - - // Copy the inverted lists - if (index_) { - for (int i = 0; i < nlist; ++i) { - auto listIndices = index_->getListIndices(i); - auto listData = index_->getListVectors(i); - - ivf->add_entries(i, - listIndices.size(), - listIndices.data(), - (const uint8_t*) listData.data()); - } - } -} - -size_t -GpuIndexIVFFlat::reclaimMemory() { - if (index_) { - DeviceScope scope(device_); - - return index_->reclaimMemory(); - } - - return 0; -} - -void -GpuIndexIVFFlat::reset() { - if (index_) { - DeviceScope scope(device_); - - index_->reset(); - this->ntotal = 0; - } else { - FAISS_ASSERT(this->ntotal == 0); - } -} - -void -GpuIndexIVFFlat::train(Index::idx_t n, const float* x) { - DeviceScope scope(device_); - - if (this->is_trained) { - FAISS_ASSERT(quantizer->is_trained); - FAISS_ASSERT(quantizer->ntotal == nlist); - FAISS_ASSERT(index_); - return; - } - - FAISS_ASSERT(!index_); - - trainQuantizer_(n, x); - - // The quantizer is now trained; construct the IVF index - index_ = new IVFFlat(resources_, - quantizer->getGpuData(), - this->metric_type, - this->metric_arg, - false, // no residual - nullptr, // no scalar quantizer - ivfFlatConfig_.indicesOptions, - memorySpace_); - - if (reserveMemoryVecs_) { - index_->reserveMemory(reserveMemoryVecs_); - } - - this->is_trained = true; -} - -void -GpuIndexIVFFlat::addImpl_(int n, - const float* x, - const Index::idx_t* xids) { - // Device is already set in GpuIndex::add - FAISS_ASSERT(index_); - FAISS_ASSERT(n > 0); - - auto stream = resources_->getDefaultStream(device_); - - // Data is already resident on the GPU - Tensor data(const_cast(x), {n, (int) this->d}); - - static_assert(sizeof(long) == sizeof(Index::idx_t), "size mismatch"); - Tensor labels(const_cast(xids), {n}); - - // Not all vectors may be able to be added (some may contain NaNs etc) - index_->classifyAndAddVectors(data, labels); - - // but keep the ntotal based on the total number of vectors that we attempted - // to add - ntotal += n; -} - -void -GpuIndexIVFFlat::searchImpl_(int n, - const float* x, - int k, - float* distances, - Index::idx_t* labels, - ConcurrentBitsetPtr bitset) const { - // Device is already set in GpuIndex::search - FAISS_ASSERT(index_); - FAISS_ASSERT(n > 0); - - auto stream = resources_->getDefaultStream(device_); - - // Data is already resident on the GPU - Tensor queries(const_cast(x), {n, (int) this->d}); - Tensor outDistances(distances, {n, k}); - - static_assert(sizeof(long) == sizeof(Index::idx_t), "size mismatch"); - Tensor outLabels(const_cast(labels), {n, k}); - - if (!bitset) { - auto bitsetDevice = toDevice(resources_, device_, nullptr, stream, {0}); - index_->query(queries, bitsetDevice, nprobe, k, outDistances, outLabels); - } else { - auto bitsetDevice = toDevice(resources_, device_, - const_cast(bitset->data()), stream, - {(int) bitset->size()}); - index_->query(queries, bitsetDevice, nprobe, k, outDistances, outLabels); - } -} - - -} } // namespace - diff --git a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFFlat.h b/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFFlat.h deleted file mode 100644 index a7328c31e3..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFFlat.h +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include - -namespace faiss { struct IndexIVFFlat; } - -namespace faiss { namespace gpu { - -class IVFFlat; -class GpuIndexFlat; - -struct GpuIndexIVFFlatConfig : public GpuIndexIVFConfig { -}; - -/// Wrapper around the GPU implementation that looks like -/// faiss::IndexIVFFlat -class GpuIndexIVFFlat : public GpuIndexIVF { - public: - /// Construct from a pre-existing faiss::IndexIVFFlat instance, copying - /// data over to the given GPU, if the input index is trained. - GpuIndexIVFFlat(GpuResources* resources, - const faiss::IndexIVFFlat* index, - GpuIndexIVFFlatConfig config = GpuIndexIVFFlatConfig()); - - /// Constructs a new instance with an empty flat quantizer; the user - /// provides the number of lists desired. - GpuIndexIVFFlat(GpuResources* resources, - int dims, - int nlist, - faiss::MetricType metric, - GpuIndexIVFFlatConfig config = GpuIndexIVFFlatConfig()); - - ~GpuIndexIVFFlat() override; - - /// Reserve GPU memory in our inverted lists for this number of vectors - void reserveMemory(size_t numVecs); - - /// Initialize ourselves from the given CPU index; will overwrite - /// all data in ourselves - void copyFrom(const faiss::IndexIVFFlat* index); - - /// Copy ourselves to the given CPU index; will overwrite all data - /// in the index instance - void copyTo(faiss::IndexIVFFlat* index) const; - - /// After adding vectors, one can call this to reclaim device memory - /// to exactly the amount needed. Returns space reclaimed in bytes - size_t reclaimMemory(); - - void reset() override; - - void train(Index::idx_t n, const float* x) override; - - protected: - /// Called from GpuIndex for add/add_with_ids - void addImpl_(int n, - const float* x, - const Index::idx_t* ids) override; - - /// Called from GpuIndex for search - void searchImpl_(int n, - const float* x, - int k, - float* distances, - Index::idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - private: - GpuIndexIVFFlatConfig ivfFlatConfig_; - - /// Desired inverted list memory reservation - size_t reserveMemoryVecs_; - - /// Instance that we own; contains the inverted list - IVFFlat* index_; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFPQ.cu b/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFPQ.cu deleted file mode 100644 index d6095a58e8..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFPQ.cu +++ /dev/null @@ -1,471 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace faiss { namespace gpu { - -GpuIndexIVFPQ::GpuIndexIVFPQ(GpuResources* resources, - const faiss::IndexIVFPQ* index, - GpuIndexIVFPQConfig config) : - GpuIndexIVF(resources, - index->d, - index->metric_type, - index->metric_arg, - index->nlist, - config), - ivfpqConfig_(config), - subQuantizers_(0), - bitsPerCode_(0), - reserveMemoryVecs_(0), - index_(nullptr) { -#ifndef FAISS_USE_FLOAT16 - FAISS_ASSERT(!ivfpqConfig_.useFloat16LookupTables); -#endif - - copyFrom(index); -} - -GpuIndexIVFPQ::GpuIndexIVFPQ(GpuResources* resources, - int dims, - int nlist, - int subQuantizers, - int bitsPerCode, - faiss::MetricType metric, - GpuIndexIVFPQConfig config) : - GpuIndexIVF(resources, - dims, - metric, - 0, - nlist, - config), - ivfpqConfig_(config), - subQuantizers_(subQuantizers), - bitsPerCode_(bitsPerCode), - reserveMemoryVecs_(0), - index_(nullptr) { -#ifndef FAISS_USE_FLOAT16 - FAISS_ASSERT(!config.useFloat16LookupTables); -#endif - - verifySettings_(); - - // We haven't trained ourselves, so don't construct the PQ index yet - this->is_trained = false; -} - -GpuIndexIVFPQ::~GpuIndexIVFPQ() { - delete index_; -} - -void -GpuIndexIVFPQ::copyFrom(const faiss::IndexIVFPQ* index) { - DeviceScope scope(device_); - - GpuIndexIVF::copyFrom(index); - - // Clear out our old data - delete index_; - index_ = nullptr; - - subQuantizers_ = index->pq.M; - bitsPerCode_ = index->pq.nbits; - - // We only support this - FAISS_THROW_IF_NOT_MSG(index->pq.nbits == 8, - "GPU: only pq.nbits == 8 is supported"); - FAISS_THROW_IF_NOT_MSG(index->by_residual, - "GPU: only by_residual = true is supported"); - FAISS_THROW_IF_NOT_MSG(index->polysemous_ht == 0, - "GPU: polysemous codes not supported"); - - verifySettings_(); - - // The other index might not be trained - if (!index->is_trained) { - // copied in GpuIndex::copyFrom - FAISS_ASSERT(!is_trained); - return; - } - - // Copy our lists as well - // The product quantizer must have data in it - FAISS_ASSERT(index->pq.centroids.size() > 0); - index_ = new IVFPQ(resources_, - index->metric_type, - index->metric_arg, - quantizer->getGpuData(), - subQuantizers_, - bitsPerCode_, - (float*) index->pq.centroids.data(), - ivfpqConfig_.indicesOptions, - ivfpqConfig_.useFloat16LookupTables, - memorySpace_); - // Doesn't make sense to reserve memory here - index_->setPrecomputedCodes(ivfpqConfig_.usePrecomputedTables); - - // Copy database vectors, if any - const InvertedLists *ivf = index->invlists; - size_t nlist = ivf ? ivf->nlist : 0; - for (size_t i = 0; i < nlist; ++i) { - size_t list_size = ivf->list_size(i); - - // GPU index can only support max int entries per list - FAISS_THROW_IF_NOT_FMT(list_size <= - (size_t) std::numeric_limits::max(), - "GPU inverted list can only support " - "%zu entries; %zu found", - (size_t) std::numeric_limits::max(), - list_size); - - index_->addCodeVectorsFromCpu( - i, ivf->get_codes(i), ivf->get_ids(i), list_size); - } -} - -void -GpuIndexIVFPQ::copyTo(faiss::IndexIVFPQ* index) const { - DeviceScope scope(device_); - - // We must have the indices in order to copy to ourselves - FAISS_THROW_IF_NOT_MSG(ivfpqConfig_.indicesOptions != INDICES_IVF, - "Cannot copy to CPU as GPU index doesn't retain " - "indices (INDICES_IVF)"); - - GpuIndexIVF::copyTo(index); - - // - // IndexIVFPQ information - // - index->by_residual = true; - index->use_precomputed_table = 0; - index->code_size = subQuantizers_; - index->pq = faiss::ProductQuantizer(this->d, subQuantizers_, bitsPerCode_); - - index->do_polysemous_training = false; - index->polysemous_training = nullptr; - - index->scan_table_threshold = 0; - index->max_codes = 0; - index->polysemous_ht = 0; - index->precomputed_table.clear(); - - InvertedLists *ivf = new ArrayInvertedLists( - nlist, index->code_size); - - index->replace_invlists(ivf, true); - - if (index_) { - // Copy the inverted lists - for (int i = 0; i < nlist; ++i) { - auto ids = getListIndices(i); - auto codes = getListCodes(i); - index->invlists->add_entries (i, ids.size(), ids.data(), codes.data()); - } - - // Copy PQ centroids - auto devPQCentroids = index_->getPQCentroids(); - index->pq.centroids.resize(devPQCentroids.numElements()); - - fromDevice(devPQCentroids, - index->pq.centroids.data(), - resources_->getDefaultStream(device_)); - - if (ivfpqConfig_.usePrecomputedTables) { - index->precompute_table(); - } - } -} - -void -GpuIndexIVFPQ::reserveMemory(size_t numVecs) { - reserveMemoryVecs_ = numVecs; - if (index_) { - DeviceScope scope(device_); - index_->reserveMemory(numVecs); - } -} - -void -GpuIndexIVFPQ::setPrecomputedCodes(bool enable) { - ivfpqConfig_.usePrecomputedTables = enable; - if (index_) { - DeviceScope scope(device_); - index_->setPrecomputedCodes(enable); - } - - verifySettings_(); -} - -bool -GpuIndexIVFPQ::getPrecomputedCodes() const { - return ivfpqConfig_.usePrecomputedTables; -} - -int -GpuIndexIVFPQ::getNumSubQuantizers() const { - return subQuantizers_; -} - -int -GpuIndexIVFPQ::getBitsPerCode() const { - return bitsPerCode_; -} - -int -GpuIndexIVFPQ::getCentroidsPerSubQuantizer() const { - return utils::pow2(bitsPerCode_); -} - -size_t -GpuIndexIVFPQ::reclaimMemory() { - if (index_) { - DeviceScope scope(device_); - return index_->reclaimMemory(); - } - - return 0; -} - -void -GpuIndexIVFPQ::reset() { - if (index_) { - DeviceScope scope(device_); - - index_->reset(); - this->ntotal = 0; - } else { - FAISS_ASSERT(this->ntotal == 0); - } -} - -void -GpuIndexIVFPQ::trainResidualQuantizer_(Index::idx_t n, const float* x) { - // Code largely copied from faiss::IndexIVFPQ - // FIXME: GPUize more of this - n = std::min(n, (Index::idx_t) (1 << bitsPerCode_) * 64); - - if (this->verbose) { - printf("computing residuals\n"); - } - - std::vector assign(n); - quantizer->assign (n, x, assign.data()); - - std::vector residuals(n * d); - - // FIXME jhj convert to _n version - for (idx_t i = 0; i < n; i++) { - quantizer->compute_residual(x + i * d, &residuals[i * d], assign[i]); - } - - if (this->verbose) { - printf("training %d x %d product quantizer on %ld vectors in %dD\n", - subQuantizers_, getCentroidsPerSubQuantizer(), n, this->d); - } - - // Just use the CPU product quantizer to determine sub-centroids - faiss::ProductQuantizer pq(this->d, subQuantizers_, bitsPerCode_); - pq.verbose = this->verbose; - pq.train(n, residuals.data()); - - index_ = new IVFPQ(resources_, - metric_type, - metric_arg, - quantizer->getGpuData(), - subQuantizers_, - bitsPerCode_, - pq.centroids.data(), - ivfpqConfig_.indicesOptions, - ivfpqConfig_.useFloat16LookupTables, - memorySpace_); - if (reserveMemoryVecs_) { - index_->reserveMemory(reserveMemoryVecs_); - } - - index_->setPrecomputedCodes(ivfpqConfig_.usePrecomputedTables); -} - -void -GpuIndexIVFPQ::train(Index::idx_t n, const float* x) { - DeviceScope scope(device_); - - if (this->is_trained) { - FAISS_ASSERT(quantizer->is_trained); - FAISS_ASSERT(quantizer->ntotal == nlist); - FAISS_ASSERT(index_); - return; - } - - FAISS_ASSERT(!index_); - - // FIXME: GPUize more of this - // First, make sure that the data is resident on the CPU, if it is not on the - // CPU, as we depend upon parts of the CPU code - auto hostData = toHost((float*) x, - resources_->getDefaultStream(device_), - {(int) n, (int) this->d}); - - trainQuantizer_(n, hostData.data()); - trainResidualQuantizer_(n, hostData.data()); - - FAISS_ASSERT(index_); - - this->is_trained = true; -} - -void -GpuIndexIVFPQ::addImpl_(int n, - const float* x, - const Index::idx_t* xids) { - // Device is already set in GpuIndex::add - FAISS_ASSERT(index_); - FAISS_ASSERT(n > 0); - - auto stream = resources_->getDefaultStream(device_); - - // Data is already resident on the GPU - Tensor data(const_cast(x), {n, (int) this->d}); - - static_assert(sizeof(long) == sizeof(Index::idx_t), "size mismatch"); - Tensor labels(const_cast(xids), {n}); - - // Not all vectors may be able to be added (some may contain NaNs etc) - index_->classifyAndAddVectors(data, labels); - - // but keep the ntotal based on the total number of vectors that we attempted - // to add - ntotal += n; -} - -void -GpuIndexIVFPQ::searchImpl_(int n, - const float* x, - int k, - float* distances, - Index::idx_t* labels, - ConcurrentBitsetPtr bitset) const { - // Device is already set in GpuIndex::search - FAISS_ASSERT(index_); - FAISS_ASSERT(n > 0); - - auto stream = resources_->getDefaultStream(device_); - - // Data is already resident on the GPU - Tensor queries(const_cast(x), {n, (int) this->d}); - Tensor outDistances(distances, {n, k}); - - static_assert(sizeof(long) == sizeof(Index::idx_t), "size mismatch"); - Tensor outLabels(const_cast(labels), {n, k}); - - if (!bitset) { - auto bitsetDevice = toDevice(resources_, device_, nullptr, stream, {0}); - index_->query(queries, bitsetDevice, nprobe, k, outDistances, outLabels); - } else { - auto bitsetDevice = toDevice(resources_, device_, - const_cast(bitset->data()), stream, - {(int) bitset->size()}); - index_->query(queries, bitsetDevice, nprobe, k, outDistances, outLabels); - } -} - -int -GpuIndexIVFPQ::getListLength(int listId) const { - FAISS_ASSERT(index_); - return index_->getListLength(listId); -} - -std::vector -GpuIndexIVFPQ::getListCodes(int listId) const { - FAISS_ASSERT(index_); - DeviceScope scope(device_); - - return index_->getListCodes(listId); -} - -std::vector -GpuIndexIVFPQ::getListIndices(int listId) const { - FAISS_ASSERT(index_); - DeviceScope scope(device_); - - return index_->getListIndices(listId); -} - -void -GpuIndexIVFPQ::verifySettings_() const { - // Our implementation has these restrictions: - - // Must have some number of lists - FAISS_THROW_IF_NOT_MSG(nlist > 0, "nlist must be >0"); - - // up to a single byte per code - FAISS_THROW_IF_NOT_FMT(bitsPerCode_ <= 8, - "Bits per code must be <= 8 (passed %d)", bitsPerCode_); - - // Sub-quantizers must evenly divide dimensions available - FAISS_THROW_IF_NOT_FMT(this->d % subQuantizers_ == 0, - "Number of sub-quantizers (%d) must be an " - "even divisor of the number of dimensions (%d)", - subQuantizers_, this->d); - - // The number of bytes per encoded vector must be one we support - FAISS_THROW_IF_NOT_FMT(IVFPQ::isSupportedPQCodeLength(subQuantizers_), - "Number of bytes per encoded vector / sub-quantizers (%d) " - "is not supported", - subQuantizers_); - - // We must have enough shared memory on the current device to store - // our lookup distances - int lookupTableSize = sizeof(float); -#ifdef FAISS_USE_FLOAT16 - if (ivfpqConfig_.useFloat16LookupTables) { - lookupTableSize = sizeof(half); - } -#endif - - // 64 bytes per code is only supported with usage of float16, at 2^8 - // codes per subquantizer - size_t requiredSmemSize = - lookupTableSize * subQuantizers_ * utils::pow2(bitsPerCode_); - size_t smemPerBlock = getMaxSharedMemPerBlock(device_); - - FAISS_THROW_IF_NOT_FMT(requiredSmemSize - <= getMaxSharedMemPerBlock(device_), - "Device %d has %zu bytes of shared memory, while " - "%d bits per code and %d sub-quantizers requires %zu " - "bytes. Consider useFloat16LookupTables and/or " - "reduce parameters", - device_, smemPerBlock, bitsPerCode_, subQuantizers_, - requiredSmemSize); - - // If precomputed codes are disabled, we have an extra limitation in - // terms of the number of dimensions per subquantizer - FAISS_THROW_IF_NOT_FMT(ivfpqConfig_.usePrecomputedTables || - IVFPQ::isSupportedNoPrecomputedSubDimSize( - this->d / subQuantizers_), - "Number of dimensions per sub-quantizer (%d) " - "is not currently supported without precomputed codes. " - "Only 1, 2, 3, 4, 6, 8, 10, 12, 16, 20, 24, 28, 32 dims " - "per sub-quantizer are currently supported with no " - "precomputed codes. " - "Precomputed codes supports any number of dimensions, but " - "will involve memory overheads.", - this->d / subQuantizers_); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFPQ.h b/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFPQ.h deleted file mode 100644 index 54b65980e8..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFPQ.h +++ /dev/null @@ -1,145 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include - -namespace faiss { struct IndexIVFPQ; } - -namespace faiss { namespace gpu { - -class GpuIndexFlat; -class IVFPQ; - -struct GpuIndexIVFPQConfig : public GpuIndexIVFConfig { - inline GpuIndexIVFPQConfig() - : useFloat16LookupTables(false), - usePrecomputedTables(false) { - } - - /// Whether or not float16 residual distance tables are used in the - /// list scanning kernels. When subQuantizers * 2^bitsPerCode > - /// 16384, this is required. - bool useFloat16LookupTables; - - /// Whether or not we enable the precomputed table option for - /// search, which can substantially increase the memory requirement. - bool usePrecomputedTables; -}; - -/// IVFPQ index for the GPU -class GpuIndexIVFPQ : public GpuIndexIVF { - public: - /// Construct from a pre-existing faiss::IndexIVFPQ instance, copying - /// data over to the given GPU, if the input index is trained. - GpuIndexIVFPQ(GpuResources* resources, - const faiss::IndexIVFPQ* index, - GpuIndexIVFPQConfig config = GpuIndexIVFPQConfig()); - - /// Construct an empty index - GpuIndexIVFPQ(GpuResources* resources, - int dims, - int nlist, - int subQuantizers, - int bitsPerCode, - faiss::MetricType metric, - GpuIndexIVFPQConfig config = GpuIndexIVFPQConfig()); - - ~GpuIndexIVFPQ() override; - - /// Reserve space on the GPU for the inverted lists for `num` - /// vectors, assumed equally distributed among - - /// Initialize ourselves from the given CPU index; will overwrite - /// all data in ourselves - void copyFrom(const faiss::IndexIVFPQ* index); - - /// Copy ourselves to the given CPU index; will overwrite all data - /// in the index instance - void copyTo(faiss::IndexIVFPQ* index) const; - - /// Reserve GPU memory in our inverted lists for this number of vectors - void reserveMemory(size_t numVecs); - - /// Enable or disable pre-computed codes - void setPrecomputedCodes(bool enable); - - /// Are pre-computed codes enabled? - bool getPrecomputedCodes() const; - - /// Return the number of sub-quantizers we are using - int getNumSubQuantizers() const; - - /// Return the number of bits per PQ code - int getBitsPerCode() const; - - /// Return the number of centroids per PQ code (2^bits per code) - int getCentroidsPerSubQuantizer() const; - - /// After adding vectors, one can call this to reclaim device memory - /// to exactly the amount needed. Returns space reclaimed in bytes - size_t reclaimMemory(); - - /// Clears out all inverted lists, but retains the coarse and - /// product centroid information - void reset() override; - - void train(Index::idx_t n, const float* x) override; - - /// For debugging purposes, return the list length of a particular - /// list - int getListLength(int listId) const; - - /// For debugging purposes, return the list codes of a particular - /// list - std::vector getListCodes(int listId) const; - - /// For debugging purposes, return the list indices of a particular - /// list - std::vector getListIndices(int listId) const; - - protected: - /// Called from GpuIndex for add/add_with_ids - void addImpl_(int n, - const float* x, - const Index::idx_t* ids) override; - - /// Called from GpuIndex for search - void searchImpl_(int n, - const float* x, - int k, - float* distances, - Index::idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - private: - void verifySettings_() const; - - void trainResidualQuantizer_(Index::idx_t n, const float* x); - - private: - GpuIndexIVFPQConfig ivfpqConfig_; - - /// Number of sub-quantizers per encoded vector - int subQuantizers_; - - /// Bits per sub-quantizer code - int bitsPerCode_; - - /// Desired inverted list memory reservation - size_t reserveMemoryVecs_; - - /// The product quantizer instance that we own; contains the - /// inverted lists - IVFPQ* index_; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFSQHybrid.cu b/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFSQHybrid.cu deleted file mode 100644 index 27da743cb4..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFSQHybrid.cu +++ /dev/null @@ -1,357 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -GpuIndexIVFSQHybrid::GpuIndexIVFSQHybrid( - GpuResources* resources, - faiss::IndexIVFSQHybrid* index, - GpuIndexIVFSQHybridConfig config) : - GpuIndexIVF(resources, - index->d, - index->metric_type, - index->metric_arg, - index->nlist, - config), - ivfSQConfig_(config), - sq(index->sq), - by_residual(index->by_residual), - reserveMemoryVecs_(0), - index_(nullptr) { - gpu::GpuIndexFlat *quantizer = nullptr; - copyFrom(index, quantizer, 0); - - FAISS_THROW_IF_NOT_MSG(isSQSupported(sq.qtype), - "Unsupported QuantizerType on GPU"); -} - -GpuIndexIVFSQHybrid::GpuIndexIVFSQHybrid( - GpuResources* resources, - int dims, - int nlist, - faiss::QuantizerType qtype, - faiss::MetricType metric, - bool encodeResidual, - GpuIndexIVFSQHybridConfig config) : - GpuIndexIVF(resources, dims, metric, 0, nlist, config), - ivfSQConfig_(config), - sq(dims, qtype), - by_residual(encodeResidual), - reserveMemoryVecs_(0), - index_(nullptr) { - - // faiss::Index params - this->is_trained = false; - - // We haven't trained ourselves, so don't construct the IVFFlat - // index yet - FAISS_THROW_IF_NOT_MSG(isSQSupported(sq.qtype), - "Unsupported QuantizerType on GPU"); -} - -GpuIndexIVFSQHybrid::~GpuIndexIVFSQHybrid() { - delete index_; -} - -void -GpuIndexIVFSQHybrid::reserveMemory(size_t numVecs) { - reserveMemoryVecs_ = numVecs; - if (index_) { - index_->reserveMemory(numVecs); - } -} - -void -GpuIndexIVFSQHybrid::copyFrom( - const faiss::IndexIVFSQHybrid* index) { - DeviceScope scope(device_); - - // Clear out our old data - delete index_; - index_ = nullptr; - - // Copy what we need from the CPU index - GpuIndexIVF::copyFrom(index); - sq = index->sq; - by_residual = index->by_residual; - - // The other index might not be trained, in which case we don't need to copy - // over the lists - if (!index->is_trained) { - return; - } - - // Otherwise, we can populate ourselves from the other index - this->is_trained = true; - - // Copy our lists as well - index_ = new IVFFlat(resources_, - quantizer->getGpuData(), - index->metric_type, - index->metric_arg, - by_residual, - &sq, - ivfSQConfig_.indicesOptions, - memorySpace_); - - InvertedLists* ivf = index->invlists; - if(ReadOnlyArrayInvertedLists* rol = dynamic_cast(ivf)) { - index_->copyCodeVectorsFromCpu((const float* )(rol->pin_readonly_codes->data), - (const long *)(rol->pin_readonly_ids->data), rol->readonly_length); - } else { - for (size_t i = 0; i < ivf->nlist; ++i) { - auto numVecs = ivf->list_size(i); - - // GPU index can only support max int entries per list - FAISS_THROW_IF_NOT_FMT(numVecs <= - (size_t) std::numeric_limits::max(), - "GPU inverted list can only support " - "%zu entries; %zu found", - (size_t) std::numeric_limits::max(), - numVecs); - - index_->addCodeVectorsFromCpu( - i, - (const unsigned char*) ivf->get_codes(i), - ivf->get_ids(i), - numVecs); - } - } -} - -void -GpuIndexIVFSQHybrid::copyFrom( - faiss::IndexIVFSQHybrid* index, - gpu::GpuIndexFlat *&qt, - long mode) { - DeviceScope scope(device_); - - // Clear out our old data - delete index_; - index_ = nullptr; - - GpuIndexIVF::copyFrom(index, qt, mode); - if(mode == 1) { - // Only copy quantizer - return ; - } - - sq = index->sq; - by_residual = index->by_residual; - - // The other index might not be trained, in which case we don't need to copy - // over the lists - if (!index->is_trained) { - return; - } - - // Otherwise, we can populate ourselves from the other index - this->is_trained = true; - - // Copy our lists as well - index_ = new IVFFlat(resources_, - quantizer->getGpuData(), - index->metric_type, - index->metric_arg, - by_residual, - &sq, - ivfSQConfig_.indicesOptions, - memorySpace_); - - InvertedLists* ivf = index->invlists; - if(ReadOnlyArrayInvertedLists* rol = dynamic_cast(ivf)) { - index_->copyCodeVectorsFromCpu((const float* )(rol->pin_readonly_codes->data), - (const long *)(rol->pin_readonly_ids->data), rol->readonly_length); - } else { - for (size_t i = 0; i < ivf->nlist; ++i) { - auto numVecs = ivf->list_size(i); - - // GPU index can only support max int entries per list - FAISS_THROW_IF_NOT_FMT(numVecs <= - (size_t) std::numeric_limits::max(), - "GPU inverted list can only support " - "%zu entries; %zu found", - (size_t) std::numeric_limits::max(), - numVecs); - - index_->addCodeVectorsFromCpu( - i, - (const unsigned char*) ivf->get_codes(i), - ivf->get_ids(i), - numVecs); - } - } -} - -void -GpuIndexIVFSQHybrid::copyTo( - faiss::IndexIVFSQHybrid* index) const { - DeviceScope scope(device_); - - // We must have the indices in order to copy to ourselves - FAISS_THROW_IF_NOT_MSG( - ivfSQConfig_.indicesOptions != INDICES_IVF, - "Cannot copy to CPU as GPU index doesn't retain " - "indices (INDICES_IVF)"); - - GpuIndexIVF::copyTo(index); - index->sq = sq; - index->by_residual = by_residual; - index->code_size = sq.code_size; - - InvertedLists* ivf = new ArrayInvertedLists(nlist, index->code_size); - index->replace_invlists(ivf, true); - - // Copy the inverted lists - if (index_) { - for (int i = 0; i < nlist; ++i) { - auto listIndices = index_->getListIndices(i); - auto listData = index_->getListVectors(i); - - ivf->add_entries(i, - listIndices.size(), - listIndices.data(), - (const uint8_t*) listData.data()); - } - } -} - -size_t -GpuIndexIVFSQHybrid::reclaimMemory() { - if (index_) { - DeviceScope scope(device_); - - return index_->reclaimMemory(); - } - - return 0; -} - -void -GpuIndexIVFSQHybrid::reset() { - if (index_) { - DeviceScope scope(device_); - - index_->reset(); - this->ntotal = 0; - } else { - FAISS_ASSERT(this->ntotal == 0); - } -} - -void -GpuIndexIVFSQHybrid::trainResiduals_(Index::idx_t n, const float* x) { - // The input is already guaranteed to be on the CPU - sq.train_residual(n, x, quantizer, by_residual, verbose); -} - -void -GpuIndexIVFSQHybrid::train(Index::idx_t n, const float* x) { - DeviceScope scope(device_); - - if (this->is_trained) { - FAISS_ASSERT(quantizer->is_trained); - FAISS_ASSERT(quantizer->ntotal == nlist); - FAISS_ASSERT(index_); - return; - } - - FAISS_ASSERT(!index_); - - // FIXME: GPUize more of this - // First, make sure that the data is resident on the CPU, if it is not on the - // CPU, as we depend upon parts of the CPU code - auto hostData = toHost((float*) x, - resources_->getDefaultStream(device_), - {(int) n, (int) this->d}); - - trainQuantizer_(n, hostData.data()); - trainResiduals_(n, hostData.data()); - - // The quantizer is now trained; construct the IVF index - index_ = new IVFFlat(resources_, - quantizer->getGpuData(), - this->metric_type, - this->metric_arg, - by_residual, - &sq, - ivfSQConfig_.indicesOptions, - memorySpace_); - - if (reserveMemoryVecs_) { - index_->reserveMemory(reserveMemoryVecs_); - } - - this->is_trained = true; -} - -void -GpuIndexIVFSQHybrid::addImpl_(int n, - const float* x, - const Index::idx_t* xids) { - // Device is already set in GpuIndex::add - FAISS_ASSERT(index_); - FAISS_ASSERT(n > 0); - - auto stream = resources_->getDefaultStream(device_); - - // Data is already resident on the GPU - Tensor data(const_cast(x), {n, (int) this->d}); - - static_assert(sizeof(long) == sizeof(Index::idx_t), "size mismatch"); - Tensor labels(const_cast(xids), {n}); - - // Not all vectors may be able to be added (some may contain NaNs etc) - index_->classifyAndAddVectors(data, labels); - - // but keep the ntotal based on the total number of vectors that we attempted - // to add - ntotal += n; -} - -void -GpuIndexIVFSQHybrid::searchImpl_(int n, - const float* x, - int k, - float* distances, - Index::idx_t* labels, - ConcurrentBitsetPtr bitset) const { - // Device is already set in GpuIndex::search - FAISS_ASSERT(index_); - FAISS_ASSERT(n > 0); - - auto stream = resources_->getDefaultStream(device_); - - // Data is already resident on the GPU - Tensor queries(const_cast(x), {n, (int) this->d}); - Tensor outDistances(distances, {n, k}); - - static_assert(sizeof(long) == sizeof(Index::idx_t), "size mismatch"); - Tensor outLabels(const_cast(labels), {n, k}); - - if (!bitset) { - auto bitsetDevice = toDevice(resources_, device_, nullptr, stream, {0}); - index_->query(queries, bitsetDevice, nprobe, k, outDistances, outLabels); - } else { - auto bitsetDevice = toDevice(resources_, device_, - const_cast(bitset->data()), stream,{(int) bitset->size()}); - index_->query(queries, bitsetDevice, nprobe, k, outDistances, outLabels); - } -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFSQHybrid.h b/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFSQHybrid.h deleted file mode 100644 index 049372c10f..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFSQHybrid.h +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include - -namespace faiss { namespace gpu { - -class IVFFlat; -class GpuIndexFlat; - -struct GpuIndexIVFSQHybridConfig : public GpuIndexIVFConfig { -}; - -/// Wrapper around the GPU implementation that looks like -/// faiss::IndexIVFSQHybrid -class GpuIndexIVFSQHybrid : public GpuIndexIVF { - public: - /// Construct from a pre-existing faiss::IndexIVFSQHybrid instance, - /// copying data over to the given GPU, if the input index is trained. - GpuIndexIVFSQHybrid( - GpuResources* resources, - faiss::IndexIVFSQHybrid* index, - GpuIndexIVFSQHybridConfig config = - GpuIndexIVFSQHybridConfig()); - - /// Constructs a new instance with an empty flat quantizer; the user - /// provides the number of lists desired. - GpuIndexIVFSQHybrid( - GpuResources* resources, - int dims, - int nlist, - faiss::QuantizerType qtype, - faiss::MetricType metric = MetricType::METRIC_L2, - bool encodeResidual = true, - GpuIndexIVFSQHybridConfig config = - GpuIndexIVFSQHybridConfig()); - - ~GpuIndexIVFSQHybrid() override; - - /// Reserve GPU memory in our inverted lists for this number of vectors - void reserveMemory(size_t numVecs); - - /// Initialize ourselves from the given CPU index; will overwrite - /// all data in ourselves - void copyFrom(const faiss::IndexIVFSQHybrid* index); - - /// Initialize ourselves from the given CPU index; will overwrite - /// all data in ourselves - void copyFrom(faiss::IndexIVFSQHybrid* index, gpu::GpuIndexFlat *&quantizer, int64_t mode); - - /// Copy ourselves to the given CPU index; will overwrite all data - /// in the index instance - void copyTo(faiss::IndexIVFSQHybrid* index) const; - - /// After adding vectors, one can call this to reclaim device memory - /// to exactly the amount needed. Returns space reclaimed in bytes - size_t reclaimMemory(); - - void reset() override; - - void train(Index::idx_t n, const float* x) override; - - protected: - /// Called from GpuIndex for add/add_with_ids - void addImpl_(int n, - const float* x, - const Index::idx_t* ids) override; - - /// Called from GpuIndex for search - void searchImpl_(int n, - const float* x, - int k, - float* distances, - Index::idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - /// Called from train to handle SQ residual training - void trainResiduals_(Index::idx_t n, const float* x); - - public: - /// Exposed like the CPU version - faiss::ScalarQuantizer sq; - - /// Exposed like the CPU version - bool by_residual; - - private: - GpuIndexIVFSQHybridConfig ivfSQConfig_; - - /// Desired inverted list memory reservation - size_t reserveMemoryVecs_; - - /// Instance that we own; contains the inverted list - IVFFlat* index_; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFScalarQuantizer.cu b/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFScalarQuantizer.cu deleted file mode 100644 index 6be3bb1f79..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFScalarQuantizer.cu +++ /dev/null @@ -1,295 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -GpuIndexIVFScalarQuantizer::GpuIndexIVFScalarQuantizer( - GpuResources* resources, - const faiss::IndexIVFScalarQuantizer* index, - GpuIndexIVFScalarQuantizerConfig config) : - GpuIndexIVF(resources, - index->d, - index->metric_type, - index->metric_arg, - index->nlist, - config), - ivfSQConfig_(config), - sq(index->sq), - by_residual(index->by_residual), - reserveMemoryVecs_(0), - index_(nullptr) { - copyFrom(index); - - FAISS_THROW_IF_NOT_MSG(isSQSupported(sq.qtype), - "Unsupported QuantizerType on GPU"); -} - -GpuIndexIVFScalarQuantizer::GpuIndexIVFScalarQuantizer( - GpuResources* resources, - int dims, - int nlist, - faiss::QuantizerType qtype, - faiss::MetricType metric, - bool encodeResidual, - GpuIndexIVFScalarQuantizerConfig config) : - GpuIndexIVF(resources, dims, metric, 0, nlist, config), - ivfSQConfig_(config), - sq(dims, qtype), - by_residual(encodeResidual), - reserveMemoryVecs_(0), - index_(nullptr) { - - // faiss::Index params - this->is_trained = false; - - // We haven't trained ourselves, so don't construct the IVFFlat - // index yet - FAISS_THROW_IF_NOT_MSG(isSQSupported(sq.qtype), - "Unsupported QuantizerType on GPU"); -} - -GpuIndexIVFScalarQuantizer::~GpuIndexIVFScalarQuantizer() { - delete index_; -} - -void -GpuIndexIVFScalarQuantizer::reserveMemory(size_t numVecs) { - reserveMemoryVecs_ = numVecs; - if (index_) { - DeviceScope scope(device_); - index_->reserveMemory(numVecs); - } -} - -void -GpuIndexIVFScalarQuantizer::copyFrom( - const faiss::IndexIVFScalarQuantizer* index) { - DeviceScope scope(device_); - - // Clear out our old data - delete index_; - index_ = nullptr; - - // Copy what we need from the CPU index - GpuIndexIVF::copyFrom(index); - sq = index->sq; - by_residual = index->by_residual; - - // The other index might not be trained, in which case we don't need to copy - // over the lists - if (!index->is_trained) { - return; - } - - // Otherwise, we can populate ourselves from the other index - this->is_trained = true; - - // Copy our lists as well - index_ = new IVFFlat(resources_, - quantizer->getGpuData(), - index->metric_type, - index->metric_arg, - by_residual, - &sq, - ivfSQConfig_.indicesOptions, - memorySpace_); - - InvertedLists* ivf = index->invlists; - - if(ReadOnlyArrayInvertedLists* rol = dynamic_cast(ivf)) { - index_->copyCodeVectorsFromCpu((const float* )(rol->pin_readonly_codes->data), - (const long *)(rol->pin_readonly_ids->data), rol->readonly_length); - } else { - for (size_t i = 0; i < ivf->nlist; ++i) { - auto numVecs = ivf->list_size(i); - - // GPU index can only support max int entries per list - FAISS_THROW_IF_NOT_FMT(numVecs <= - (size_t) std::numeric_limits::max(), - "GPU inverted list can only support " - "%zu entries; %zu found", - (size_t) std::numeric_limits::max(), - numVecs); - - index_->addCodeVectorsFromCpu( - i, - (const unsigned char*) ivf->get_codes(i), - ivf->get_ids(i), - numVecs); - } - } -} - -void -GpuIndexIVFScalarQuantizer::copyTo( - faiss::IndexIVFScalarQuantizer* index) const { - DeviceScope scope(device_); - - // We must have the indices in order to copy to ourselves - FAISS_THROW_IF_NOT_MSG( - ivfSQConfig_.indicesOptions != INDICES_IVF, - "Cannot copy to CPU as GPU index doesn't retain " - "indices (INDICES_IVF)"); - - GpuIndexIVF::copyTo(index); - index->sq = sq; - index->code_size = sq.code_size; - index->by_residual = by_residual; - index->code_size = sq.code_size; - - InvertedLists* ivf = new ArrayInvertedLists(nlist, index->code_size); - index->replace_invlists(ivf, true); - - // Copy the inverted lists - if (index_) { - for (int i = 0; i < nlist; ++i) { - auto listIndices = index_->getListIndices(i); - auto listData = index_->getListVectors(i); - - ivf->add_entries(i, - listIndices.size(), - listIndices.data(), - (const uint8_t*) listData.data()); - } - } -} - -size_t -GpuIndexIVFScalarQuantizer::reclaimMemory() { - if (index_) { - DeviceScope scope(device_); - - return index_->reclaimMemory(); - } - - return 0; -} - -void -GpuIndexIVFScalarQuantizer::reset() { - if (index_) { - DeviceScope scope(device_); - - index_->reset(); - this->ntotal = 0; - } else { - FAISS_ASSERT(this->ntotal == 0); - } -} - -void -GpuIndexIVFScalarQuantizer::trainResiduals_(Index::idx_t n, const float* x) { - // The input is already guaranteed to be on the CPU - sq.train_residual(n, x, quantizer, by_residual, verbose); -} - -void -GpuIndexIVFScalarQuantizer::train(Index::idx_t n, const float* x) { - DeviceScope scope(device_); - - if (this->is_trained) { - FAISS_ASSERT(quantizer->is_trained); - FAISS_ASSERT(quantizer->ntotal == nlist); - FAISS_ASSERT(index_); - return; - } - - FAISS_ASSERT(!index_); - - // FIXME: GPUize more of this - // First, make sure that the data is resident on the CPU, if it is not on the - // CPU, as we depend upon parts of the CPU code - auto hostData = toHost((float*) x, - resources_->getDefaultStream(device_), - {(int) n, (int) this->d}); - - trainQuantizer_(n, hostData.data()); - trainResiduals_(n, hostData.data()); - - // The quantizer is now trained; construct the IVF index - index_ = new IVFFlat(resources_, - quantizer->getGpuData(), - this->metric_type, - this->metric_arg, - by_residual, - &sq, - ivfSQConfig_.indicesOptions, - memorySpace_); - - if (reserveMemoryVecs_) { - index_->reserveMemory(reserveMemoryVecs_); - } - - this->is_trained = true; -} - -void -GpuIndexIVFScalarQuantizer::addImpl_(int n, - const float* x, - const Index::idx_t* xids) { - // Device is already set in GpuIndex::add - FAISS_ASSERT(index_); - FAISS_ASSERT(n > 0); - - auto stream = resources_->getDefaultStream(device_); - - // Data is already resident on the GPU - Tensor data(const_cast(x), {n, (int) this->d}); - - static_assert(sizeof(long) == sizeof(Index::idx_t), "size mismatch"); - Tensor labels(const_cast(xids), {n}); - - // Not all vectors may be able to be added (some may contain NaNs etc) - index_->classifyAndAddVectors(data, labels); - - // but keep the ntotal based on the total number of vectors that we attempted - // to add - ntotal += n; -} - -void -GpuIndexIVFScalarQuantizer::searchImpl_(int n, - const float* x, - int k, - float* distances, - Index::idx_t* labels, - ConcurrentBitsetPtr bitset) const { - // Device is already set in GpuIndex::search - FAISS_ASSERT(index_); - FAISS_ASSERT(n > 0); - - auto stream = resources_->getDefaultStream(device_); - - // Data is already resident on the GPU - Tensor queries(const_cast(x), {n, (int) this->d}); - Tensor outDistances(distances, {n, k}); - - static_assert(sizeof(long) == sizeof(Index::idx_t), "size mismatch"); - Tensor outLabels(const_cast(labels), {n, k}); - - if (!bitset) { - auto bitsetDevice = toDevice(resources_, device_, nullptr, stream, {0}); - index_->query(queries, bitsetDevice, nprobe, k, outDistances, outLabels); - } else { - auto bitsetDevice = toDevice(resources_, device_, - const_cast(bitset->data()), stream, - {(int) bitset->size()}); - index_->query(queries, bitsetDevice, nprobe, k, outDistances, outLabels); - } -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFScalarQuantizer.h b/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFScalarQuantizer.h deleted file mode 100644 index 47b8de249f..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuIndexIVFScalarQuantizer.h +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include - -namespace faiss { namespace gpu { - -class IVFFlat; -class GpuIndexFlat; - -struct GpuIndexIVFScalarQuantizerConfig : public GpuIndexIVFConfig { -}; - -/// Wrapper around the GPU implementation that looks like -/// faiss::IndexIVFScalarQuantizer -class GpuIndexIVFScalarQuantizer : public GpuIndexIVF { - public: - /// Construct from a pre-existing faiss::IndexIVFScalarQuantizer instance, - /// copying data over to the given GPU, if the input index is trained. - GpuIndexIVFScalarQuantizer( - GpuResources* resources, - const faiss::IndexIVFScalarQuantizer* index, - GpuIndexIVFScalarQuantizerConfig config = - GpuIndexIVFScalarQuantizerConfig()); - - /// Constructs a new instance with an empty flat quantizer; the user - /// provides the number of lists desired. - GpuIndexIVFScalarQuantizer( - GpuResources* resources, - int dims, - int nlist, - faiss::QuantizerType qtype, - faiss::MetricType metric = MetricType::METRIC_L2, - bool encodeResidual = true, - GpuIndexIVFScalarQuantizerConfig config = - GpuIndexIVFScalarQuantizerConfig()); - - ~GpuIndexIVFScalarQuantizer() override; - - /// Reserve GPU memory in our inverted lists for this number of vectors - void reserveMemory(size_t numVecs); - - /// Initialize ourselves from the given CPU index; will overwrite - /// all data in ourselves - void copyFrom(const faiss::IndexIVFScalarQuantizer* index); - - /// Copy ourselves to the given CPU index; will overwrite all data - /// in the index instance - void copyTo(faiss::IndexIVFScalarQuantizer* index) const; - - /// After adding vectors, one can call this to reclaim device memory - /// to exactly the amount needed. Returns space reclaimed in bytes - size_t reclaimMemory(); - - void reset() override; - - void train(Index::idx_t n, const float* x) override; - - protected: - /// Called from GpuIndex for add/add_with_ids - void addImpl_(int n, - const float* x, - const Index::idx_t* ids) override; - - /// Called from GpuIndex for search - void searchImpl_(int n, - const float* x, - int k, - float* distances, - Index::idx_t* labels, - ConcurrentBitsetPtr bitset = nullptr) const override; - - /// Called from train to handle SQ residual training - void trainResiduals_(Index::idx_t n, const float* x); - - public: - /// Exposed like the CPU version - faiss::ScalarQuantizer sq; - - /// Exposed like the CPU version - bool by_residual; - - private: - GpuIndexIVFScalarQuantizerConfig ivfSQConfig_; - - /// Desired inverted list memory reservation - size_t reserveMemoryVecs_; - - /// Instance that we own; contains the inverted list - IVFFlat* index_; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuIndicesOptions.h b/core/src/index/thirdparty/faiss/gpu/GpuIndicesOptions.h deleted file mode 100644 index 768f981f71..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuIndicesOptions.h +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -namespace faiss { namespace gpu { - -/// How user vector index data is stored on the GPU -enum IndicesOptions { - /// The user indices are only stored on the CPU; the GPU returns - /// (inverted list, offset) to the CPU which is then translated to - /// the real user index. - INDICES_CPU = 0, - /// The indices are not stored at all, on either the CPU or - /// GPU. Only (inverted list, offset) is returned to the user as the - /// index. - INDICES_IVF = 1, - /// Indices are stored as 32 bit integers on the GPU, but returned - /// as 64 bit integers - INDICES_32_BIT = 2, - /// Indices are stored as 64 bit integers on the GPU - INDICES_64_BIT = 3, -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuResources.cpp b/core/src/index/thirdparty/faiss/gpu/GpuResources.cpp deleted file mode 100644 index fe386c2cf8..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuResources.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include - -namespace faiss { namespace gpu { - -GpuResources::~GpuResources() { -} - -cublasHandle_t -GpuResources::getBlasHandleCurrentDevice() { - return getBlasHandle(getCurrentDevice()); -} - -cudaStream_t -GpuResources::getDefaultStreamCurrentDevice() { - return getDefaultStream(getCurrentDevice()); -} - -std::vector -GpuResources::getAlternateStreamsCurrentDevice() { - return getAlternateStreams(getCurrentDevice()); -} - -DeviceMemory& -GpuResources::getMemoryManagerCurrentDevice() { - return getMemoryManager(getCurrentDevice()); -} - -cudaStream_t -GpuResources::getAsyncCopyStreamCurrentDevice() { - return getAsyncCopyStream(getCurrentDevice()); -} - -void -GpuResources::syncDefaultStream(int device) { - CUDA_VERIFY(cudaStreamSynchronize(getDefaultStream(device))); -} - -void -GpuResources::syncDefaultStreamCurrentDevice() { - syncDefaultStream(getCurrentDevice()); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/GpuResources.h b/core/src/index/thirdparty/faiss/gpu/GpuResources.h deleted file mode 100644 index bdea4f630a..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/GpuResources.h +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -/// Base class of GPU-side resource provider; hides provision of -/// cuBLAS handles, CUDA streams and a temporary memory manager -class GpuResources { - public: - virtual ~GpuResources(); - - /// Call to pre-allocate resources for a particular device. If this is - /// not called, then resources will be allocated at the first time - /// of demand - virtual void initializeForDevice(int device) = 0; - - /// Returns the cuBLAS handle that we use for the given device - virtual cublasHandle_t getBlasHandle(int device) = 0; - - /// Returns the stream that we order all computation on for the - /// given device - virtual cudaStream_t getDefaultStream(int device) = 0; - - /// Returns the set of alternative streams that we use for the given device - virtual std::vector getAlternateStreams(int device) = 0; - - /// Returns the temporary memory manager for the given device - virtual DeviceMemory& getMemoryManager(int device) = 0; - - /// Returns the available CPU pinned memory buffer - virtual std::pair getPinnedMemory() = 0; - - /// Returns the stream on which we perform async CPU <-> GPU copies - virtual cudaStream_t getAsyncCopyStream(int device) = 0; - - /// Calls getBlasHandle with the current device - cublasHandle_t getBlasHandleCurrentDevice(); - - /// Calls getDefaultStream with the current device - cudaStream_t getDefaultStreamCurrentDevice(); - - /// Synchronizes the CPU with respect to the default stream for the - /// given device - // equivalent to cudaDeviceSynchronize(getDefaultStream(device)) - void syncDefaultStream(int device); - - /// Calls syncDefaultStream for the current device - void syncDefaultStreamCurrentDevice(); - - /// Calls getAlternateStreams for the current device - std::vector getAlternateStreamsCurrentDevice(); - - /// Calls getMemoryManager for the current device - DeviceMemory& getMemoryManagerCurrentDevice(); - - /// Calls getAsyncCopyStream for the current device - cudaStream_t getAsyncCopyStreamCurrentDevice(); -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/StandardGpuResources.cpp b/core/src/index/thirdparty/faiss/gpu/StandardGpuResources.cpp deleted file mode 100644 index e564f8e367..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/StandardGpuResources.cpp +++ /dev/null @@ -1,303 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -namespace { - -// How many streams per device we allocate by default (for multi-streaming) -constexpr int kNumStreams = 2; - -// Use 256 MiB of pinned memory for async CPU <-> GPU copies by default -constexpr size_t kDefaultPinnedMemoryAllocation = (size_t) 256 * 1024 * 1024; - -// Default temporary memory allocation for <= 4 GiB memory GPUs -constexpr size_t k4GiBTempMem = (size_t) 512 * 1024 * 1024; - -// Default temporary memory allocation for <= 8 GiB memory GPUs -constexpr size_t k8GiBTempMem = (size_t) 1024 * 1024 * 1024; - -// Maximum temporary memory allocation for all GPUs -constexpr size_t kMaxTempMem = (size_t) 1536 * 1024 * 1024; - -} - -StandardGpuResources::StandardGpuResources() : - pinnedMemAlloc_(nullptr), - pinnedMemAllocSize_(0), - // let the adjustment function determine the memory size for us by passing - // in a huge value that will then be adjusted - tempMemSize_(getDefaultTempMemForGPU(-1, - std::numeric_limits::max())), - pinnedMemSize_(kDefaultPinnedMemoryAllocation), - cudaMallocWarning_(true) { -} - -StandardGpuResources::~StandardGpuResources() { - for (auto& entry : defaultStreams_) { - DeviceScope scope(entry.first); - - auto it = userDefaultStreams_.find(entry.first); - if (it == userDefaultStreams_.end()) { - // The user did not specify this stream, thus we are the ones - // who have created it - CUDA_VERIFY(cudaStreamDestroy(entry.second)); - } - } - - for (auto& entry : alternateStreams_) { - DeviceScope scope(entry.first); - - for (auto stream : entry.second) { - CUDA_VERIFY(cudaStreamDestroy(stream)); - } - } - - for (auto& entry : asyncCopyStreams_) { - DeviceScope scope(entry.first); - - CUDA_VERIFY(cudaStreamDestroy(entry.second)); - } - - for (auto& entry : blasHandles_) { - DeviceScope scope(entry.first); - - auto blasStatus = cublasDestroy(entry.second); - FAISS_ASSERT(blasStatus == CUBLAS_STATUS_SUCCESS); - } - - if (pinnedMemAlloc_) { - freeMemorySpace(MemorySpace::HostPinned, pinnedMemAlloc_); - } -} - -size_t -StandardGpuResources::getDefaultTempMemForGPU(int device, - size_t requested) { - auto totalMem = device != -1 ? - getDeviceProperties(device).totalGlobalMem : - std::numeric_limits::max(); - - if (totalMem <= (size_t) 4 * 1024 * 1024 * 1024) { - // If the GPU has <= 4 GiB of memory, reserve 512 MiB - - if (requested > k4GiBTempMem) { - return k4GiBTempMem; - } - } else if (totalMem <= (size_t) 8 * 1024 * 1024 * 1024) { - // If the GPU has <= 8 GiB of memory, reserve 1 GiB - - if (requested > k8GiBTempMem) { - return k8GiBTempMem; - } - } else { - // Never use more than 1.5 GiB - if (requested > kMaxTempMem) { - return kMaxTempMem; - } - } - - // use whatever lower limit the user requested - return requested; -} - -void -StandardGpuResources::noTempMemory() { - setTempMemory(0); - setCudaMallocWarning(false); -} - -void -StandardGpuResources::setTempMemory(size_t size) { - if (tempMemSize_ != size) { - // adjust based on general limits - tempMemSize_ = getDefaultTempMemForGPU(-1, size); - - // We need to re-initialize memory resources for all current devices that - // have been initialized. - // This should be safe to do, even if we are currently running work, because - // the cudaFree call that this implies will force-synchronize all GPUs with - // the CPU - for (auto& p : memory_) { - int device = p.first; - // Free the existing memory first - p.second.reset(); - - // Allocate new - p.second = std::unique_ptr( - new StackDeviceMemory(p.first, - // adjust for this specific device - getDefaultTempMemForGPU(device, tempMemSize_))); - } - } -} - -void -StandardGpuResources::setPinnedMemory(size_t size) { - // Should not call this after devices have been initialized - FAISS_ASSERT(defaultStreams_.size() == 0); - FAISS_ASSERT(!pinnedMemAlloc_); - - pinnedMemSize_ = size; -} - -void -StandardGpuResources::setDefaultStream(int device, cudaStream_t stream) { - auto it = defaultStreams_.find(device); - if (it != defaultStreams_.end()) { - // Replace this stream with the user stream - CUDA_VERIFY(cudaStreamDestroy(it->second)); - it->second = stream; - } - - userDefaultStreams_[device] = stream; -} - -void -StandardGpuResources::setDefaultNullStreamAllDevices() { - for (int dev = 0; dev < getNumDevices(); ++dev) { - setDefaultStream(dev, nullptr); - } -} - -void -StandardGpuResources::setCudaMallocWarning(bool b) { - cudaMallocWarning_ = b; - - for (auto& v : memory_) { - v.second->setCudaMallocWarning(b); - } -} - -bool -StandardGpuResources::isInitialized(int device) const { - // Use default streams as a marker for whether or not a certain - // device has been initialized - return defaultStreams_.count(device) != 0; -} - -void -StandardGpuResources::initializeForDevice(int device) { - if (isInitialized(device)) { - return; - } - - // If this is the first device that we're initializing, create our - // pinned memory allocation - if (defaultStreams_.empty() && pinnedMemSize_ > 0) { - allocMemorySpace(MemorySpace::HostPinned, &pinnedMemAlloc_, pinnedMemSize_); - pinnedMemAllocSize_ = pinnedMemSize_; - } - - FAISS_ASSERT(device < getNumDevices()); - DeviceScope scope(device); - - // Make sure that device properties for all devices are cached - auto& prop = getDeviceProperties(device); - - // Also check to make sure we meet our minimum compute capability (3.0) - FAISS_ASSERT_FMT(prop.major >= 3, - "Device id %d with CC %d.%d not supported, " - "need 3.0+ compute capability", - device, prop.major, prop.minor); - - // Create streams - cudaStream_t defaultStream = 0; - auto it = userDefaultStreams_.find(device); - if (it != userDefaultStreams_.end()) { - // We already have a stream provided by the user - defaultStream = it->second; - } else { - CUDA_VERIFY(cudaStreamCreateWithFlags(&defaultStream, - cudaStreamNonBlocking)); - } - - defaultStreams_[device] = defaultStream; - - cudaStream_t asyncCopyStream = 0; - CUDA_VERIFY(cudaStreamCreateWithFlags(&asyncCopyStream, - cudaStreamNonBlocking)); - - asyncCopyStreams_[device] = asyncCopyStream; - - std::vector deviceStreams; - for (int j = 0; j < kNumStreams; ++j) { - cudaStream_t stream = 0; - CUDA_VERIFY(cudaStreamCreateWithFlags(&stream, - cudaStreamNonBlocking)); - - deviceStreams.push_back(stream); - } - - alternateStreams_[device] = std::move(deviceStreams); - - // Create cuBLAS handle - cublasHandle_t blasHandle = 0; - auto blasStatus = cublasCreate(&blasHandle); - FAISS_ASSERT(blasStatus == CUBLAS_STATUS_SUCCESS); - blasHandles_[device] = blasHandle; - - // Enable tensor core support if available -#if CUDA_VERSION >= 9000 - if (getTensorCoreSupport(device)) { - cublasSetMathMode(blasHandle, CUBLAS_TENSOR_OP_MATH); - } -#endif - - FAISS_ASSERT(memory_.count(device) == 0); - - auto mem = std::unique_ptr( - new StackDeviceMemory(device, - // adjust for this specific device - getDefaultTempMemForGPU(device, tempMemSize_))); - mem->setCudaMallocWarning(cudaMallocWarning_); - - memory_.emplace(device, std::move(mem)); -} - -cublasHandle_t -StandardGpuResources::getBlasHandle(int device) { - initializeForDevice(device); - return blasHandles_[device]; -} - -cudaStream_t -StandardGpuResources::getDefaultStream(int device) { - initializeForDevice(device); - return defaultStreams_[device]; -} - -std::vector -StandardGpuResources::getAlternateStreams(int device) { - initializeForDevice(device); - return alternateStreams_[device]; -} - -DeviceMemory& StandardGpuResources::getMemoryManager(int device) { - initializeForDevice(device); - return *memory_[device]; -} - -std::pair -StandardGpuResources::getPinnedMemory() { - return std::make_pair(pinnedMemAlloc_, pinnedMemAllocSize_); -} - -cudaStream_t -StandardGpuResources::getAsyncCopyStream(int device) { - initializeForDevice(device); - return asyncCopyStreams_[device]; -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/StandardGpuResources.h b/core/src/index/thirdparty/faiss/gpu/StandardGpuResources.h deleted file mode 100644 index 9d4ffa4c44..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/StandardGpuResources.h +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -/// Default implementation of GpuResources that allocates a cuBLAS -/// stream and 2 streams for use, as well as temporary memory -class StandardGpuResources : public GpuResources { - public: - StandardGpuResources(); - - ~StandardGpuResources() override; - - /// Disable allocation of temporary memory; all temporary memory - /// requests will call cudaMalloc / cudaFree at the point of use - void noTempMemory(); - - /// Specify that we wish to use a certain fixed size of memory on - /// all devices as temporary memory. This is the upper bound for the GPU - /// memory that we will reserve. We will never go above 1.5 GiB on any GPU; - /// smaller GPUs (with <= 4 GiB or <= 8 GiB) will use less memory than that. - /// To avoid any temporary memory allocation, pass 0. - void setTempMemory(size_t size); - - /// Set amount of pinned memory to allocate, for async GPU <-> CPU - /// transfers - void setPinnedMemory(size_t size); - - /// Called to change the stream for work ordering - void setDefaultStream(int device, cudaStream_t stream); - - /// Called to change the work ordering streams to the null stream - /// for all devices - void setDefaultNullStreamAllDevices(); - - /// Enable or disable the warning about not having enough temporary memory - /// when cudaMalloc gets called - void setCudaMallocWarning(bool b); - - public: - /// Internal system calls - - /// Initialize resources for this device - void initializeForDevice(int device) override; - - cublasHandle_t getBlasHandle(int device) override; - - cudaStream_t getDefaultStream(int device) override; - - std::vector getAlternateStreams(int device) override; - - DeviceMemory& getMemoryManager(int device) override; - - std::pair getPinnedMemory() override; - - cudaStream_t getAsyncCopyStream(int device) override; - - private: - /// Have GPU resources been initialized for this device yet? - bool isInitialized(int device) const; - - /// Adjust the default temporary memory allocation based on the total GPU - /// memory size - static size_t getDefaultTempMemForGPU(int device, size_t requested); - - private: - /// Our default stream that work is ordered on, one per each device - std::unordered_map defaultStreams_; - - /// This contains particular streams as set by the user for - /// ordering, if any - std::unordered_map userDefaultStreams_; - - /// Other streams we can use, per each device - std::unordered_map > alternateStreams_; - - /// Async copy stream to use for GPU <-> CPU pinned memory copies - std::unordered_map asyncCopyStreams_; - - /// cuBLAS handle for each device - std::unordered_map blasHandles_; - - /// Temporary memory provider, per each device - std::unordered_map > memory_; - - /// Pinned memory allocation for use with this GPU - void* pinnedMemAlloc_; - size_t pinnedMemAllocSize_; - - /// Another option is to use a specified amount of memory on all - /// devices - size_t tempMemSize_; - - /// Amount of pinned memory we should allocate - size_t pinnedMemSize_; - - /// Whether or not a warning upon cudaMalloc is generated - bool cudaMallocWarning_; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/BinaryDistance.cu b/core/src/index/thirdparty/faiss/gpu/impl/BinaryDistance.cu deleted file mode 100644 index 9c91ae2182..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/BinaryDistance.cu +++ /dev/null @@ -1,316 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -// Number of warps that the kernel is instantiated with -constexpr int kWarps = 8; -constexpr int kLanes = kWarpSize; - -constexpr int kMaxDistance = std::numeric_limits::max(); - -// Performs a binary matrix multiplication, returning the lowest k results in -// `vecs` for each `query` in terms of Hamming distance (a fused kernel) -// Each warp calculates distance for a single query -template -__launch_bounds__(kWarps * kLanes) -__global__ void binaryDistanceAnySize(const Tensor vecs, - const Tensor query, - Tensor outK, - Tensor outV, - int k) { - // A matrix tile (query, k) - __shared__ BinaryType queryTile[kWarps][kLanes + 1]; // avoid bank conflict - - // B matrix tile (vec, k) - __shared__ BinaryType vecTile[kLanes][kLanes + 1]; // avoid bank conflict - - WarpSelect, - NumWarpQ, NumThreadQ, kWarps * kLanes> - heap(kMaxDistance, -1, k); - - int warpId = threadIdx.y; - int laneId = threadIdx.x; - - // Each warp handles a single query - int warpQuery = blockIdx.x * kWarps + warpId; - bool queryInBounds = warpQuery < query.getSize(0); - - // Each warp loops through the entire chunk of vectors - for (int blockVec = 0; blockVec < vecs.getSize(0); blockVec += kLanes) { - int threadDistance = 0; - - // Reduction dimension - for (int blockK = 0; blockK < vecs.getSize(1); blockK += kLanes) { - int laneK = blockK + laneId; - bool kInBounds = laneK < vecs.getSize(1); - - queryTile[warpId][laneId] = queryInBounds && kInBounds ? - query[warpQuery][laneK] : 0; - - // kWarps warps are responsible for loading 32 vecs -#pragma unroll - for (int i = 0; i < kLanes / kWarps; ++i) { - int warpVec = i * kWarps + warpId; - int vec = blockVec + warpVec; - bool vecInBounds = vec < vecs.getSize(0); - - vecTile[warpVec][laneId] = vecInBounds && kInBounds ? - vecs[vec][laneK] : 0; - } - - __syncthreads(); - - // Compare distances -#pragma unroll - for (int i = 0; i < kLanes; ++i) { - threadDistance += __popc(queryTile[warpId][i] ^ vecTile[laneId][i]); - } - - __syncthreads(); - } - - // Lanes within a warp are different vec results against the same query - // Only submit distances which represent real (query, vec) pairs - bool valInBounds = queryInBounds && (blockVec + laneId < vecs.getSize(0)); - threadDistance = valInBounds ? threadDistance : kMaxDistance; - int id = valInBounds ? blockVec + laneId : -1; - - heap.add(threadDistance, id); - } - - heap.reduce(); - - if (warpQuery < query.getSize(0)) { - heap.writeOut(outK[warpQuery].data(), - outV[warpQuery].data(), - k); - } -} - -// Version of the kernel that avoids a loop over the reduction dimension, and -// thus avoids reloading the query vectors -template -__global__ void -__launch_bounds__(kWarps * kLanes) -binaryDistanceLimitSize(const Tensor vecs, - const Tensor query, - Tensor outK, - Tensor outV, - int k) { - // A matrix tile (query, k) - __shared__ BinaryType queryTile[kWarps][kLanes + 1]; // avoid bank conflict - - // B matrix tile (vec, k) - __shared__ BinaryType vecTile[kLanes][kLanes + 1]; // avoid bank conflict - - WarpSelect, - NumWarpQ, NumThreadQ, kWarps * kLanes> - heap(kMaxDistance, -1, k); - - int warpId = threadIdx.y; - int laneId = threadIdx.x; - - // Each warp handles a single query - int laneK = laneId; - int warpQuery = blockIdx.x * kWarps + warpId; - bool kInBounds = laneK < vecs.getSize(1); - bool queryInBounds = warpQuery < query.getSize(0); - - - queryTile[warpId][laneId] = queryInBounds && kInBounds ? - query[warpQuery][laneK] : 0; - - // Each warp loops through the entire chunk of vectors - for (int blockVec = 0; blockVec < vecs.getSize(0); blockVec += kLanes) { - int threadDistance = 0; - - // kWarps warps are responsible for loading 32 vecs -#pragma unroll - for (int i = 0; i < kLanes / kWarps; ++i) { - int warpVec = i * kWarps + warpId; - int vec = blockVec + warpVec; - bool vecInBounds = vec < vecs.getSize(0); - - vecTile[warpVec][laneId] = vecInBounds && kInBounds ? - vecs[vec][laneK] : 0; - } - - __syncthreads(); - - // Compare distances -#pragma unroll - for (int i = 0; i < ReductionLimit; ++i) { - threadDistance += __popc(queryTile[warpId][i] ^ vecTile[laneId][i]); - } - - __syncthreads(); - - // Lanes within a warp are different vec results against the same query - // Only submit distances which represent real (query, vec) pairs - bool valInBounds = queryInBounds && (blockVec + laneId < vecs.getSize(0)); - threadDistance = valInBounds ? threadDistance : kMaxDistance; - int id = valInBounds ? blockVec + laneId : -1; - - heap.add(threadDistance, id); - } - - heap.reduce(); - - if (warpQuery < query.getSize(0)) { - heap.writeOut(outK[warpQuery].data(), - outV[warpQuery].data(), - k); - } -} - -template -void runBinaryDistanceAnySize(Tensor& vecs, - Tensor& query, - Tensor& outK, - Tensor& outV, - int k, cudaStream_t stream) { - dim3 grid(utils::divUp(query.getSize(0), kWarps)); - dim3 block(kLanes, kWarps); - - if (k == 1) { - binaryDistanceAnySize<1, 1, BinaryType> - <<>>( - vecs, query, outK, outV, k); - } else if (k <= 32) { - binaryDistanceAnySize<32, 2, BinaryType> - <<>>( - vecs, query, outK, outV, k); - } else if (k <= 64) { - binaryDistanceAnySize<64, 3, BinaryType> - <<>>( - vecs, query, outK, outV, k); - } else if (k <= 128) { - binaryDistanceAnySize<128, 3, BinaryType> - <<>>( - vecs, query, outK, outV, k); - } else if (k <= 256) { - binaryDistanceAnySize<256, 4, BinaryType> - <<>>( - vecs, query, outK, outV, k); - } else if (k <= 512) { - binaryDistanceAnySize<512, 8, BinaryType> - <<>>( - vecs, query, outK, outV, k); - } else if (k <= 1024) { - binaryDistanceAnySize<1024, 8, BinaryType> - <<>>( - vecs, query, outK, outV, k); - } -#if GPU_MAX_SELECTION_K >= 2048 - else if (k <= 2048) { - binaryDistanceAnySize<2048, 8, BinaryType> - <<>>( - vecs, query, outK, outV, k); - } -#endif -} - -template -void runBinaryDistanceLimitSize(Tensor& vecs, - Tensor& query, - Tensor& outK, - Tensor& outV, - int k, cudaStream_t stream) { - dim3 grid(utils::divUp(query.getSize(0), kWarps)); - dim3 block(kLanes, kWarps); - - if (k == 1) { - binaryDistanceLimitSize<1, 1, BinaryType, ReductionLimit> - <<>>( - vecs, query, outK, outV, k); - } else if (k <= 32) { - binaryDistanceLimitSize<32, 2, BinaryType, ReductionLimit> - <<>>( - vecs, query, outK, outV, k); - } else if (k <= 64) { - binaryDistanceLimitSize<64, 3, BinaryType, ReductionLimit> - <<>>( - vecs, query, outK, outV, k); - } else if (k <= 128) { - binaryDistanceLimitSize<128, 3, BinaryType, ReductionLimit> - <<>>( - vecs, query, outK, outV, k); - } else if (k <= 256) { - binaryDistanceLimitSize<256, 4, BinaryType, ReductionLimit> - <<>>( - vecs, query, outK, outV, k); - } else if (k <= 512) { - binaryDistanceLimitSize<512, 8, BinaryType, ReductionLimit> - <<>>( - vecs, query, outK, outV, k); - } else if (k <= 1024) { - binaryDistanceLimitSize<1024, 8, BinaryType, ReductionLimit> - <<>>( - vecs, query, outK, outV, k); - } -#if GPU_MAX_SELECTION_K >= 2048 - else if (k <= 2048) { - binaryDistanceLimitSize<2048, 8, BinaryType, ReductionLimit> - <<>>( - vecs, query, outK, outV, k); - } -#endif -} - -void runBinaryDistance(Tensor& vecs, - Tensor& query, - Tensor& outK, - Tensor& outV, - int k, cudaStream_t stream) { - FAISS_ASSERT(k <= GPU_MAX_SELECTION_K); - FAISS_ASSERT(vecs.getSize(1) == query.getSize(1)); - - FAISS_ASSERT(outK.getSize(1) == k); - FAISS_ASSERT(outV.getSize(1) == k); - - // For the optimized uint32 kernel, we handle 32 * 8 = 256 max dims - constexpr int kReductionLimit32 = 8; - - // For the optimized uint8 kernel, we handle 8 * 16 = 128 max dims - constexpr int kReductionLimit8 = 16; - - // All other cases (large or small) go through the general kernel - - if (vecs.getSize(1) % sizeof(unsigned int) == 0 && - (vecs.getSize(1) / sizeof(unsigned int)) <= kReductionLimit32) { - auto vecs32 = vecs.castResize(); - auto query32 = query.castResize(); - - // Optimize for vectors with dimensions a multiple of 32 that are less than - // 32 * kReductionLimit (256) dimensions in size - runBinaryDistanceLimitSize( - vecs32, query32, outK, outV, k, stream); - - } else if (vecs.getSize(1) <= kReductionLimit8) { - // Optimize for vectors with dimensions a multiple of 32 that are less than - // 32 * kReductionLimit (256) dimensions in size - runBinaryDistanceLimitSize( - vecs, query, outK, outV, k, stream); - } else { - // Arbitrary size kernel - runBinaryDistanceAnySize( - vecs, query, outK, outV, k, stream); - } -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/BinaryDistance.cuh b/core/src/index/thirdparty/faiss/gpu/impl/BinaryDistance.cuh deleted file mode 100644 index 149accc016..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/BinaryDistance.cuh +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include - -namespace faiss { namespace gpu { - -// Performs brute-force k-NN comparison between `vecs` and `query`, where they -// are encoded as binary vectors -void runBinaryDistance(Tensor& vecs, - Tensor& query, - Tensor& outK, - Tensor& outV, - int k, cudaStream_t stream); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/BinaryFlatIndex.cu b/core/src/index/thirdparty/faiss/gpu/impl/BinaryFlatIndex.cu deleted file mode 100644 index dd38fdd7dd..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/BinaryFlatIndex.cu +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -BinaryFlatIndex::BinaryFlatIndex(GpuResources* res, - int dim, - MemorySpace space) : - resources_(res), - dim_(dim), - space_(space), - num_(0), - rawData_(space) { - FAISS_ASSERT(dim % 8 == 0); -} - -/// Returns the number of vectors we contain -int BinaryFlatIndex::getSize() const { - return vectors_.getSize(0); -} - -int BinaryFlatIndex::getDim() const { - return vectors_.getSize(1) * 8; -} - -void -BinaryFlatIndex::reserve(size_t numVecs, cudaStream_t stream) { - rawData_.reserve(numVecs * (dim_ / 8) * sizeof(unsigned int), stream); -} - -Tensor& -BinaryFlatIndex::getVectorsRef() { - return vectors_; -} - -void -BinaryFlatIndex::query(Tensor& input, - int k, - Tensor& outDistances, - Tensor& outIndices) { - auto stream = resources_->getDefaultStreamCurrentDevice(); - - runBinaryDistance(vectors_, - input, - outDistances, - outIndices, - k, - stream); -} - -void -BinaryFlatIndex::add(const unsigned char* data, - int numVecs, - cudaStream_t stream) { - if (numVecs == 0) { - return; - } - - rawData_.append((char*) data, - (size_t) (dim_ / 8) * numVecs * sizeof(unsigned char), - stream, - true /* reserve exactly */); - - num_ += numVecs; - - DeviceTensor vectors( - (unsigned char*) rawData_.data(), {(int) num_, (dim_ / 8)}, space_); - vectors_ = std::move(vectors); -} - -void -BinaryFlatIndex::reset() { - rawData_.clear(); - vectors_ = std::move(DeviceTensor()); - num_ = 0; -} - -} } diff --git a/core/src/index/thirdparty/faiss/gpu/impl/BinaryFlatIndex.cuh b/core/src/index/thirdparty/faiss/gpu/impl/BinaryFlatIndex.cuh deleted file mode 100644 index c99afc45a7..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/BinaryFlatIndex.cuh +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include - -namespace faiss { namespace gpu { - -class GpuResources; - -/// Holder of GPU resources for a particular flat index -class BinaryFlatIndex { - public: - BinaryFlatIndex(GpuResources* res, - int dim, - MemorySpace space); - - /// Returns the number of vectors we contain - int getSize() const; - - int getDim() const; - - /// Reserve storage that can contain at least this many vectors - void reserve(size_t numVecs, cudaStream_t stream); - - /// Returns a reference to our vectors currently in use - Tensor& getVectorsRef(); - - void query(Tensor& vecs, - int k, - Tensor& outDistances, - Tensor& outIndices); - - /// Add vectors to ourselves; the pointer passed can be on the host - /// or the device - void add(const unsigned char* data, int numVecs, cudaStream_t stream); - - /// Free all storage - void reset(); - - private: - /// Collection of GPU resources that we use - GpuResources* resources_; - - /// Dimensionality of our vectors - const int dim_; - - /// Memory space for our allocations - MemorySpace space_; - - /// How many vectors we have - int num_; - - /// The underlying expandable storage - DeviceVector rawData_; - - /// Vectors currently in rawData_ - DeviceTensor vectors_; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/BroadcastSum.cu b/core/src/index/thirdparty/faiss/gpu/impl/BroadcastSum.cu deleted file mode 100644 index e9f7548e25..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/BroadcastSum.cu +++ /dev/null @@ -1,360 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include - -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -template -__global__ void sumAlongColumns(Tensor input, - Tensor output) { - static_assert(kRowsPerBlock % kRowUnroll == 0, "must fit rows"); - - // blockIdx.x: which chunk of rows we are responsible for updating - // blockIdx.y: which chunk of columns we are responsible for - // updating - int rowStart = blockIdx.x * kRowsPerBlock; - int rowEnd = rowStart + kRowsPerBlock; - int colStart = blockIdx.y * blockDim.x * kColLoad; - - // FIXME: if we have exact multiples, don't need this - bool endRow = (blockIdx.x == gridDim.x - 1); - bool endCol = (blockIdx.y == gridDim.y - 1); - - if (endRow) { - if (output.getSize(0) % kRowsPerBlock == 0) { - endRow = false; - } - } - - if (endCol) { - for (int col = colStart + threadIdx.x; - col < input.getSize(0); col += blockDim.x) { - T val = input[col]; - - if (endRow) { - for (int row = rowStart; row < output.getSize(0); ++row) { - T out = output[row][col]; - out = Math::add(out, val); - output[row][col] = out; - } - } else { - T rows[kRowUnroll]; - - for (int row = rowStart; row < rowEnd; row += kRowUnroll) { -#pragma unroll - for (int i = 0; i < kRowUnroll; ++i) { - rows[i] = output[row + i][col]; - } - -#pragma unroll - for (int i = 0; i < kRowUnroll; ++i) { - rows[i] = Math::add(rows[i], val); - } - -#pragma unroll - for (int i = 0; i < kRowUnroll; ++i) { - output[row + i][col] = rows[i]; - } - } - } - } - } else { - int col = colStart + threadIdx.x; - - T val[kColLoad]; - -#pragma unroll - for (int i = 0; i < kColLoad; ++i) { - val[i] = input[col + i * blockDim.x]; - } - - if (endRow) { - for (int row = rowStart; row < output.getSize(0); ++row) { -#pragma unroll - for (int i = 0; i < kColLoad; ++i) { - T out = output[row][col + i * blockDim.x]; - out = Math::add(out, val[i]); - output[row][col + i * blockDim.x] = out; - } - } - } else { - T rows[kRowUnroll * kColLoad]; - - for (int row = rowStart; row < rowEnd; row += kRowUnroll) { -#pragma unroll - for (int i = 0; i < kRowUnroll; ++i) { -#pragma unroll - for (int j = 0; j < kColLoad; ++j) { - rows[i * kColLoad + j] = - output[row + i][col + j * blockDim.x]; - } - } - -#pragma unroll - for (int i = 0; i < kRowUnroll; ++i) { -#pragma unroll - for (int j = 0; j < kColLoad; ++j) { - rows[i * kColLoad + j] = - Math::add(rows[i * kColLoad + j], val[j]); - } - } - -#pragma unroll - for (int i = 0; i < kRowUnroll; ++i) { -#pragma unroll - for (int j = 0; j < kColLoad; ++j) { - output[row + i][col + j * blockDim.x] = - rows[i * kColLoad + j]; - } - } - } - } - } -} - -template -__global__ void assignAlongColumns(Tensor input, - Tensor output) { - static_assert(kRowsPerBlock % kRowUnroll == 0, "must fit rows"); - - // blockIdx.x: which chunk of rows we are responsible for updating - // blockIdx.y: which chunk of columns we are responsible for - // updating - int rowStart = blockIdx.x * kRowsPerBlock; - int rowEnd = rowStart + kRowsPerBlock; - int colStart = blockIdx.y * blockDim.x * kColLoad; - - // FIXME: if we have exact multiples, don't need this - bool endRow = (blockIdx.x == gridDim.x - 1); - bool endCol = (blockIdx.y == gridDim.y - 1); - - if (endRow) { - if (output.getSize(0) % kRowsPerBlock == 0) { - endRow = false; - } - } - - if (endCol) { - for (int col = colStart + threadIdx.x; - col < input.getSize(0); col += blockDim.x) { - T val = input[col]; - - if (endRow) { - for (int row = rowStart; row < output.getSize(0); ++row) { - output[row][col] = val; - } - } else { - for (int row = rowStart; row < rowEnd; row += kRowUnroll) { -#pragma unroll - for (int i = 0; i < kRowUnroll; ++i) { - output[row + i][col] = val; - } - } - } - } - } else { - int col = colStart + threadIdx.x; - - T val[kColLoad]; - -#pragma unroll - for (int i = 0; i < kColLoad; ++i) { - val[i] = input[col + i * blockDim.x]; - } - - if (endRow) { - for (int row = rowStart; row < output.getSize(0); ++row) { -#pragma unroll - for (int i = 0; i < kColLoad; ++i) { - output[row][col + i * blockDim.x] = val[i]; - } - } - } else { - for (int row = rowStart; row < rowEnd; row += kRowUnroll) { -#pragma unroll - for (int i = 0; i < kRowUnroll; ++i) { -#pragma unroll - for (int j = 0; j < kColLoad; ++j) { - output[row + i][col + j * blockDim.x] = val[j]; - } - } - } - } - } -} - -template -__global__ void sumAlongRows(Tensor input, - Tensor output) { - __shared__ T sval; - - int row = blockIdx.x; - - if (threadIdx.x == 0) { - sval = input[row]; - } - - __syncthreads(); - - T val = sval; - - // FIXME: speed up - for (int i = threadIdx.x; i < output.getSize(1); i += blockDim.x) { - T out = output[row][i]; - out = Math::add(out, val); - out = Math::lt(out, Math::zero()) ? Math::zero() : out; - - output[row][i] = out; - } -} - -template -void runSumAlongColumns(Tensor& input, - Tensor& output, - cudaStream_t stream) { - FAISS_ASSERT(input.getSize(0) == output.getSize(1)); - - int threadsPerBlock = 256; - constexpr int kRowUnroll = 4; - constexpr int kRowsPerBlock = kRowUnroll * 4; - constexpr int kColLoad = 4; - - auto block = dim3(threadsPerBlock); - - if (input.template canCastResize() && - output.template canCastResize()) { - auto inputV = input.template castResize(); - auto outputV = output.template castResize(); - - auto grid = - dim3(utils::divUp(outputV.getSize(0), kRowsPerBlock), - utils::divUp(outputV.getSize(1), threadsPerBlock * kColLoad)); - - sumAlongColumns - <<>>(inputV, outputV); - } else { - auto grid = - dim3(utils::divUp(output.getSize(0), kRowsPerBlock), - utils::divUp(output.getSize(1), threadsPerBlock * kColLoad)); - - sumAlongColumns - <<>>(input, output); - } - - CUDA_TEST_ERROR(); -} - -void runSumAlongColumns(Tensor& input, - Tensor& output, - cudaStream_t stream) { - runSumAlongColumns(input, output, stream); -} - -#ifdef FAISS_USE_FLOAT16 -void runSumAlongColumns(Tensor& input, - Tensor& output, - cudaStream_t stream) { - runSumAlongColumns(input, output, stream); -} -#endif - -template -void runAssignAlongColumns(Tensor& input, - Tensor& output, - cudaStream_t stream) { - FAISS_ASSERT(input.getSize(0) == output.getSize(1)); - - int threadsPerBlock = 256; - constexpr int kRowUnroll = 4; - constexpr int kRowsPerBlock = kRowUnroll * 4; - constexpr int kColLoad = 4; - - auto block = dim3(threadsPerBlock); - - if (input.template canCastResize() && - output.template canCastResize()) { - auto inputV = input.template castResize(); - auto outputV = output.template castResize(); - - auto grid = - dim3(utils::divUp(outputV.getSize(0), kRowsPerBlock), - utils::divUp(outputV.getSize(1), threadsPerBlock * kColLoad)); - - assignAlongColumns - <<>>(inputV, outputV); - } else { - auto grid = - dim3(utils::divUp(output.getSize(0), kRowsPerBlock), - utils::divUp(output.getSize(1), threadsPerBlock * kColLoad)); - - assignAlongColumns - <<>>(input, output); - } - - CUDA_TEST_ERROR(); -} - -void runAssignAlongColumns(Tensor& input, - Tensor& output, - cudaStream_t stream) { - runAssignAlongColumns(input, output, stream); -} - -#ifdef FAISS_USE_FLOAT16 -void runAssignAlongColumns(Tensor& input, - Tensor& output, - cudaStream_t stream) { - runAssignAlongColumns(input, output, stream); -} -#endif - -template -void runSumAlongRows(Tensor& input, - Tensor& output, - bool zeroClamp, - cudaStream_t stream) { - FAISS_ASSERT(input.getSize(0) == output.getSize(0)); - - int threadsPerBlock = - std::min(output.getSize(1), getMaxThreadsCurrentDevice()); - auto grid = dim3(output.getSize(0)); - auto block = dim3(threadsPerBlock); - - if (zeroClamp) { - sumAlongRows<<>>(input, output); - } else { - sumAlongRows<<>>(input, output); - } - - CUDA_TEST_ERROR(); -} - -void runSumAlongRows(Tensor& input, - Tensor& output, - bool zeroClamp, - cudaStream_t stream) { - runSumAlongRows(input, output, zeroClamp, stream); -} - -#ifdef FAISS_USE_FLOAT16 -void runSumAlongRows(Tensor& input, - Tensor& output, - bool zeroClamp, - cudaStream_t stream) { - runSumAlongRows(input, output, zeroClamp, stream); -} -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/BroadcastSum.cuh b/core/src/index/thirdparty/faiss/gpu/impl/BroadcastSum.cuh deleted file mode 100644 index 6641aadd40..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/BroadcastSum.cuh +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include - -namespace faiss { namespace gpu { - -// output[x][i] += input[i] for all x -void runSumAlongColumns(Tensor& input, - Tensor& output, - cudaStream_t stream); - -#ifdef FAISS_USE_FLOAT16 -void runSumAlongColumns(Tensor& input, - Tensor& output, - cudaStream_t stream); -#endif - -// output[x][i] = input[i] for all x -void runAssignAlongColumns(Tensor& input, - Tensor& output, - cudaStream_t stream); - -#ifdef FAISS_USE_FLOAT16 -void runAssignAlongColumns(Tensor& input, - Tensor& output, - cudaStream_t stream); -#endif - -// output[i][x] += input[i] for all x -// If zeroClamp, output[i][x] = max(output[i][x] + input[i], 0) for all x -void runSumAlongRows(Tensor& input, - Tensor& output, - bool zeroClamp, - cudaStream_t stream); - -#ifdef FAISS_USE_FLOAT16 -void runSumAlongRows(Tensor& input, - Tensor& output, - bool zeroClamp, - cudaStream_t stream); - -#endif -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/Distance.cu b/core/src/index/thirdparty/faiss/gpu/impl/Distance.cu deleted file mode 100644 index 0856396cc1..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/Distance.cu +++ /dev/null @@ -1,448 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -template -void runDistance(bool computeL2, - GpuResources* resources, - Tensor& centroids, - bool centroidsRowMajor, - Tensor* centroidNorms, - Tensor& queries, - bool queriesRowMajor, - Tensor& bitset, - int k, - Tensor& outDistances, - Tensor& outIndices, - bool ignoreOutDistances) { - // The # of centroids in `centroids` based on memory layout - auto numCentroids = centroids.getSize(centroidsRowMajor ? 0 : 1); - - // The # of queries in `queries` based on memory layout - auto numQueries = queries.getSize(queriesRowMajor ? 0 : 1); - - // The dimensions of the vectors to consider - auto dim = queries.getSize(queriesRowMajor ? 1 : 0); - FAISS_ASSERT((numQueries == 0 || numCentroids == 0) || - dim == centroids.getSize(centroidsRowMajor ? 1 : 0)); - - FAISS_ASSERT(outDistances.getSize(0) == numQueries); - FAISS_ASSERT(outIndices.getSize(0) == numQueries); - FAISS_ASSERT(outDistances.getSize(1) == k); - FAISS_ASSERT(outIndices.getSize(1) == k); - - auto& mem = resources->getMemoryManagerCurrentDevice(); - auto defaultStream = resources->getDefaultStreamCurrentDevice(); - - // If we're quering against a 0 sized set, just return empty results - if (centroids.numElements() == 0) { - thrust::fill(thrust::cuda::par.on(defaultStream), - outDistances.data(), outDistances.end(), - Limits::getMax()); - - thrust::fill(thrust::cuda::par.on(defaultStream), - outIndices.data(), outIndices.end(), - -1); - - return; - } - - // L2: If ||c||^2 is not pre-computed, calculate it - DeviceTensor cNorms; - if (computeL2 && !centroidNorms) { - cNorms = - std::move(DeviceTensor( - mem, {numCentroids}, defaultStream)); - runL2Norm(centroids, centroidsRowMajor, cNorms, true, defaultStream); - centroidNorms = &cNorms; - } - - // - // Prepare norm vector ||q||^2; ||c||^2 is already pre-computed - // - int qNormSize[1] = {numQueries}; - DeviceTensor queryNorms(mem, qNormSize, defaultStream); - - // ||q||^2 - if (computeL2) { - runL2Norm(queries, queriesRowMajor, queryNorms, true, defaultStream); - } - - // By default, aim to use up to 512 MB of memory for the processing, with both - // number of queries and number of centroids being at least 512. - int tileRows = 0; - int tileCols = 0; - chooseTileSize(numQueries, - numCentroids, - dim, - sizeof(T), - mem.getSizeAvailable(), - tileRows, - tileCols); - - int numColTiles = utils::divUp(numCentroids, tileCols); - - // We can have any number of vectors to query against, even less than k, in - // which case we'll return -1 for the index - FAISS_ASSERT(k <= GPU_MAX_SELECTION_K); // select limitation - - // Temporary output memory space we'll use - DeviceTensor distanceBuf1( - mem, {tileRows, tileCols}, defaultStream); - DeviceTensor distanceBuf2( - mem, {tileRows, tileCols}, defaultStream); - DeviceTensor* distanceBufs[2] = - {&distanceBuf1, &distanceBuf2}; - - DeviceTensor outDistanceBuf1( - mem, {tileRows, numColTiles * k}, defaultStream); - DeviceTensor outDistanceBuf2( - mem, {tileRows, numColTiles * k}, defaultStream); - DeviceTensor* outDistanceBufs[2] = - {&outDistanceBuf1, &outDistanceBuf2}; - - DeviceTensor outIndexBuf1( - mem, {tileRows, numColTiles * k}, defaultStream); - DeviceTensor outIndexBuf2( - mem, {tileRows, numColTiles * k}, defaultStream); - DeviceTensor* outIndexBufs[2] = - {&outIndexBuf1, &outIndexBuf2}; - - auto streams = resources->getAlternateStreamsCurrentDevice(); - streamWait(streams, {defaultStream}); - - int curStream = 0; - bool interrupt = false; - - // Tile over the input queries - for (int i = 0; i < numQueries; i += tileRows) { - if (interrupt || InterruptCallback::is_interrupted()) { - interrupt = true; - break; - } - - int curQuerySize = std::min(tileRows, numQueries - i); - - auto outDistanceView = - outDistances.narrow(0, i, curQuerySize); - auto outIndexView = - outIndices.narrow(0, i, curQuerySize); - - auto queryView = - queries.narrow(queriesRowMajor ? 0 : 1, i, curQuerySize); - auto queryNormNiew = - queryNorms.narrow(0, i, curQuerySize); - - auto outDistanceBufRowView = - outDistanceBufs[curStream]->narrow(0, 0, curQuerySize); - auto outIndexBufRowView = - outIndexBufs[curStream]->narrow(0, 0, curQuerySize); - - // Tile over the centroids - for (int j = 0; j < numCentroids; j += tileCols) { - if (InterruptCallback::is_interrupted()) { - interrupt = true; - break; - } - - int curCentroidSize = std::min(tileCols, numCentroids - j); - int curColTile = j / tileCols; - - auto centroidsView = - sliceCentroids(centroids, centroidsRowMajor, j, curCentroidSize); - - auto distanceBufView = distanceBufs[curStream]-> - narrow(0, 0, curQuerySize).narrow(1, 0, curCentroidSize); - - auto outDistanceBufColView = - outDistanceBufRowView.narrow(1, k * curColTile, k); - auto outIndexBufColView = - outIndexBufRowView.narrow(1, k * curColTile, k); - - // L2: distance is ||c||^2 - 2qc + ||q||^2, we compute -2qc - // IP: just compute qc - // (query id x dim) x (centroid id, dim)' = (query id, centroid id) - runMatrixMult(distanceBufView, - false, // not transposed - queryView, - !queriesRowMajor, // transposed MM if col major - centroidsView, - centroidsRowMajor, // transposed MM if row major - computeL2 ? -2.0f : 1.0f, - 0.0f, - resources->getBlasHandleCurrentDevice(), - streams[curStream]); - - if (computeL2) { - // For L2 distance, we use this fused kernel that performs both - // adding ||c||^2 to -2qc and k-selection, so we only need two - // passes (one write by the gemm, one read here) over the huge - // region of output memory - // - // If we aren't tiling along the number of centroids, we can perform the - // output work directly - if (tileCols == numCentroids) { - // Write into the final output - runL2SelectMin(distanceBufView, - *centroidNorms, - bitset, - outDistanceView, - outIndexView, - k, - streams[curStream]); - - if (!ignoreOutDistances) { - // expand (query id) to (query id, k) by duplicating along rows - // top-k ||c||^2 - 2qc + ||q||^2 in the form (query id, k) - runSumAlongRows(queryNormNiew, - outDistanceView, - true, // L2 distances should not go below zero due - // to roundoff error - streams[curStream]); - } - } else { - auto centroidNormsView = centroidNorms->narrow(0, j, curCentroidSize); - - // Write into our intermediate output - runL2SelectMin(distanceBufView, - centroidNormsView, - bitset, - outDistanceBufColView, - outIndexBufColView, - k, - streams[curStream]); - - if (!ignoreOutDistances) { - // expand (query id) to (query id, k) by duplicating along rows - // top-k ||c||^2 - 2qc + ||q||^2 in the form (query id, k) - runSumAlongRows(queryNormNiew, - outDistanceBufColView, - true, // L2 distances should not go below zero due - // to roundoff error - streams[curStream]); - } - } - } else { - // For IP, just k-select the output for this tile - if (tileCols == numCentroids) { - // Write into the final output - runBlockSelect(distanceBufView, - bitset, - outDistanceView, - outIndexView, - true, k, streams[curStream]); - } else { - // Write into the intermediate output - runBlockSelect(distanceBufView, - bitset, - outDistanceBufColView, - outIndexBufColView, - true, k, streams[curStream]); - } - } - } - - // As we're finished with processing a full set of centroids, perform the - // final k-selection - if (tileCols != numCentroids) { - // The indices are tile-relative; for each tile of k, we need to add - // tileCols to the index - runIncrementIndex(outIndexBufRowView, k, tileCols, streams[curStream]); - - runBlockSelectPair(outDistanceBufRowView, - outIndexBufRowView, - bitset, - outDistanceView, - outIndexView, - computeL2 ? false : true, k, streams[curStream]); - } - - curStream = (curStream + 1) % 2; - } - - // Have the desired ordering stream wait on the multi-stream - streamWait({defaultStream}, streams); - - if (interrupt) { - FAISS_THROW_MSG("interrupted"); - } -} - -template -void runL2Distance(GpuResources* resources, - Tensor& centroids, - bool centroidsRowMajor, - Tensor* centroidNorms, - Tensor& queries, - bool queriesRowMajor, - Tensor& bitset, - int k, - Tensor& outDistances, - Tensor& outIndices, - bool ignoreOutDistances = false) { - runDistance(true, // L2 - resources, - centroids, - centroidsRowMajor, - centroidNorms, - queries, - queriesRowMajor, - bitset, - k, - outDistances, - outIndices, - ignoreOutDistances); -} - -template -void runIPDistance(GpuResources* resources, - Tensor& centroids, - bool centroidsRowMajor, - Tensor& queries, - bool queriesRowMajor, - Tensor& bitset, - int k, - Tensor& outDistances, - Tensor& outIndices) { - runDistance(false, // IP - resources, - centroids, - centroidsRowMajor, - nullptr, // no centroid norms provided - queries, - queriesRowMajor, - bitset, - k, - outDistances, - outIndices, - false); -} - -// -// Instantiations of the distance templates -// - -void -runIPDistance(GpuResources* resources, - Tensor& vectors, - bool vectorsRowMajor, - Tensor& queries, - bool queriesRowMajor, - Tensor& bitset, - int k, - Tensor& outDistances, - Tensor& outIndices) { - runIPDistance(resources, - vectors, - vectorsRowMajor, - queries, - queriesRowMajor, - bitset, - k, - outDistances, - outIndices); -} - -#ifdef FAISS_USE_FLOAT16 -void -runIPDistance(GpuResources* resources, - Tensor& vectors, - bool vectorsRowMajor, - Tensor& queries, - bool queriesRowMajor, - Tensor& bitset, - int k, - Tensor& outDistances, - Tensor& outIndices) { - runIPDistance(resources, - vectors, - vectorsRowMajor, - queries, - queriesRowMajor, - bitset, - k, - outDistances, - outIndices); -} -#endif - -void -runL2Distance(GpuResources* resources, - Tensor& vectors, - bool vectorsRowMajor, - Tensor* vectorNorms, - Tensor& queries, - bool queriesRowMajor, - Tensor& bitset, - int k, - Tensor& outDistances, - Tensor& outIndices, - bool ignoreOutDistances) { - runL2Distance(resources, - vectors, - vectorsRowMajor, - vectorNorms, - queries, - queriesRowMajor, - bitset, - k, - outDistances, - outIndices, - ignoreOutDistances); -} - -#ifdef FAISS_USE_FLOAT16 -void -runL2Distance(GpuResources* resources, - Tensor& vectors, - bool vectorsRowMajor, - Tensor* vectorNorms, - Tensor& queries, - bool queriesRowMajor, - Tensor& bitset, - int k, - Tensor& outDistances, - Tensor& outIndices, - bool ignoreOutDistances) { - runL2Distance(resources, - vectors, - vectorsRowMajor, - vectorNorms, - queries, - queriesRowMajor, - bitset, - k, - outDistances, - outIndices, - ignoreOutDistances); -} -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/Distance.cuh b/core/src/index/thirdparty/faiss/gpu/impl/Distance.cuh deleted file mode 100644 index 3430ddf87f..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/Distance.cuh +++ /dev/null @@ -1,216 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -class GpuResources; - -/// Calculates brute-force L2 distance between `vectors` and -/// `queries`, returning the k closest results seen -void runL2Distance(GpuResources* resources, - Tensor& vectors, - bool vectorsRowMajor, - // can be optionally pre-computed; nullptr if we - // have to compute it upon the call - Tensor* vectorNorms, - Tensor& queries, - bool queriesRowMajor, - Tensor& bitset, - int k, - Tensor& outDistances, - Tensor& outIndices, - // Do we care about `outDistances`? If not, we can - // take shortcuts. - bool ignoreOutDistances = false); - -/// Calculates brute-force inner product distance between `vectors` -/// and `queries`, returning the k closest results seen -void runIPDistance(GpuResources* resources, - Tensor& vectors, - bool vectorsRowMajor, - Tensor& queries, - bool queriesRowMajor, - Tensor& bitset, - int k, - Tensor& outDistances, - Tensor& outIndices); - -void runIPDistance(GpuResources* resources, - Tensor& vectors, - bool vectorsRowMajor, - Tensor& queries, - bool queriesRowMajor, - Tensor& bitset, - int k, - Tensor& outDistances, - Tensor& outIndices); - - -void runL2Distance(GpuResources* resources, - Tensor& vectors, - bool vectorsRowMajor, - Tensor* vectorNorms, - Tensor& queries, - bool queriesRowMajor, - Tensor& bitset, - int k, - Tensor& outDistances, - Tensor& outIndices, - bool ignoreOutDistances = false); - -// -// General distance implementation, assumes that all arguments are on the -// device. This is the top-level internal distance function to call to dispatch -// based on metric type. -// -template -void bfKnnOnDevice(GpuResources* resources, - int device, - cudaStream_t stream, - Tensor& vectors, - bool vectorsRowMajor, - Tensor* vectorNorms, - Tensor& queries, - bool queriesRowMajor, - Tensor& bitset, - int k, - faiss::MetricType metric, - float metricArg, - Tensor& outDistances, - Tensor& outIndices, - bool ignoreOutDistances) { - // We are guaranteed that all data arguments are resident on our preferred - // `device` here, and are ordered wrt `stream` - - // L2 and IP are specialized to use GEMM and an optimized L2 + selection or - // pure k-selection kernel. - if ((metric == faiss::MetricType::METRIC_L2) || - (metric == faiss::MetricType::METRIC_Lp && - metricArg == 2)) { - runL2Distance(resources, - vectors, - vectorsRowMajor, - vectorNorms, - queries, - queriesRowMajor, - bitset, - k, - outDistances, - outIndices); - } else if (metric == faiss::MetricType::METRIC_INNER_PRODUCT) { - runIPDistance(resources, - vectors, - vectorsRowMajor, - queries, - queriesRowMajor, - bitset, - k, - outDistances, - outIndices); - } else { - // - // General pairwise distance kernel - // - // The general distance kernel does not have specializations for - // transpositions (NN, NT, TN); instead, the transposition is just handled - // upon data load for now, which could result in poor data loading behavior - // for NT / TN. This can be fixed at a later date if desired, but efficiency - // is low versus GEMM anyways. - // - - Tensor tVectorsDimInnermost = - vectorsRowMajor ? - vectors.transposeInnermost(1) : - vectors.transposeInnermost(0); - Tensor tQueriesDimInnermost = - queriesRowMajor ? - queries.transposeInnermost(1) : - queries.transposeInnermost(0); - - if ((metric == faiss::MetricType::METRIC_L1) || - (metric == faiss::MetricType::METRIC_Lp && - metricArg == 1)) { - runGeneralDistance(resources, - tVectorsDimInnermost, - tQueriesDimInnermost, - bitset, - k, - L1Distance(), - outDistances, - outIndices); - } else if (metric == faiss::MetricType::METRIC_Lp && - metricArg == -1) { - // A way to test L2 distance - runGeneralDistance(resources, - tVectorsDimInnermost, - tQueriesDimInnermost, - bitset, - k, - L2Distance(), - outDistances, - outIndices); - } else if (metric == faiss::MetricType::METRIC_Lp) { - runGeneralDistance(resources, - tVectorsDimInnermost, - tQueriesDimInnermost, - bitset, - k, - LpDistance(metricArg), - outDistances, - outIndices); - } else if (metric == faiss::MetricType::METRIC_Linf) { - runGeneralDistance(resources, - tVectorsDimInnermost, - tQueriesDimInnermost, - bitset, - k, - LinfDistance(), - outDistances, - outIndices); - } else if (metric == faiss::MetricType::METRIC_Canberra) { - runGeneralDistance(resources, - tVectorsDimInnermost, - tQueriesDimInnermost, - bitset, - k, - CanberraDistance(), - outDistances, - outIndices); - } else if (metric == faiss::MetricType::METRIC_BrayCurtis) { - runGeneralDistance(resources, - tVectorsDimInnermost, - tQueriesDimInnermost, - bitset, - k, - BrayCurtisDistance(), - outDistances, - outIndices); - } else if (metric == faiss::MetricType::METRIC_JensenShannon) { - runGeneralDistance(resources, - tVectorsDimInnermost, - tQueriesDimInnermost, - bitset, - k, - JensenShannonDistance(), - outDistances, - outIndices); - } else { - FAISS_THROW_FMT("unsupported metric type %d", metric); - } - } -} - - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/DistanceUtils.cuh b/core/src/index/thirdparty/faiss/gpu/impl/DistanceUtils.cuh deleted file mode 100644 index 42d815a5f3..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/DistanceUtils.cuh +++ /dev/null @@ -1,343 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include - -// -// Shared utilities for brute-force distance calculations -// - -namespace faiss { namespace gpu { - -struct IPDistance { - __host__ __device__ IPDistance() : dist(0) {} - - static constexpr bool kDirection = true; // maximize - static constexpr float kIdentityData = 0; - static constexpr float kMaxDistance = -std::numeric_limits::max(); - - __host__ __device__ void handle(float a, float b) { - dist += a * b; - } - - __host__ __device__ float reduce() { - return dist; - } - - __host__ __device__ void combine(const IPDistance& v) { - dist += v.dist; - } - - __host__ __device__ IPDistance zero() const { - return IPDistance(); - } - - float dist; -}; - -struct L1Distance { - __host__ __device__ L1Distance() : dist(0) {} - - static constexpr bool kDirection = false; // minimize - static constexpr float kIdentityData = 0; - static constexpr float kMaxDistance = std::numeric_limits::max(); - - __host__ __device__ void handle(float a, float b) { - dist += fabsf(a - b); - } - - __host__ __device__ float reduce() { - return dist; - } - - __host__ __device__ void combine(const L1Distance& v) { - dist += v.dist; - } - - __host__ __device__ L1Distance zero() const { - return L1Distance(); - } - - float dist; -}; - -struct L2Distance { - __host__ __device__ L2Distance() : dist(0) {} - - static constexpr bool kDirection = false; // minimize - static constexpr float kIdentityData = 0; - static constexpr float kMaxDistance = std::numeric_limits::max(); - - __host__ __device__ void handle(float a, float b) { - float v = a - b; - dist += v * v; - } - - __host__ __device__ float reduce() { - return dist; - } - - __host__ __device__ void combine(const L2Distance& v) { - dist += v.dist; - } - - __host__ __device__ L2Distance zero() const { - return L2Distance(); - } - - float dist; -}; - -struct LpDistance { - __host__ __device__ LpDistance() - : p(2), dist(0) {} - - __host__ __device__ LpDistance(float arg) - : p(arg), dist(0) {} - - __host__ __device__ LpDistance(const LpDistance& v) - : p(v.p), dist(v.dist) {} - - __host__ __device__ LpDistance& operator=(const LpDistance& v) { - p = v.p; - dist = v.dist; - return *this; - } - - static constexpr bool kDirection = false; // minimize - static constexpr float kIdentityData = 0; - static constexpr float kMaxDistance = std::numeric_limits::max(); - - __host__ __device__ void handle(float a, float b) { - dist += powf(fabsf(a - b), p); - } - - __host__ __device__ float reduce() { - return dist; - } - - __host__ __device__ void combine(const LpDistance& v) { - dist += v.dist; - } - - __host__ __device__ LpDistance zero() const { - return LpDistance(p); - } - - float p; - float dist; -}; - -struct LinfDistance { - __host__ __device__ LinfDistance() : dist(0) {} - - static constexpr bool kDirection = false; // minimize - static constexpr float kIdentityData = 0; - static constexpr float kMaxDistance = std::numeric_limits::max(); - - __host__ __device__ void handle(float a, float b) { - dist = fmaxf(dist, fabsf(a - b)); - } - - __host__ __device__ float reduce() { - return dist; - } - - __host__ __device__ void combine(const LinfDistance& v) { - dist = fmaxf(dist, v.dist); - } - - __host__ __device__ LinfDistance zero() const { - return LinfDistance(); - } - - float dist; -}; - -struct CanberraDistance { - __host__ __device__ CanberraDistance() : dist(0) {} - - static constexpr bool kDirection = false; // minimize - static constexpr float kIdentityData = 0; - static constexpr float kMaxDistance = std::numeric_limits::max(); - - __host__ __device__ void handle(float a, float b) { - float denom = fabsf(a) + fabsf(b); - dist += fabsf(a - b) / denom; - } - - __host__ __device__ float reduce() { - return dist; - } - - __host__ __device__ void combine(const CanberraDistance& v) { - dist += v.dist; - } - - __host__ __device__ CanberraDistance zero() const { - return CanberraDistance(); - } - - float dist; -}; - -struct BrayCurtisDistance { - __host__ __device__ BrayCurtisDistance() - : numerator(0), denominator(0) {} - - static constexpr bool kDirection = false; // minimize - static constexpr float kIdentityData = 0; - static constexpr float kMaxDistance = std::numeric_limits::max(); - - __host__ __device__ void handle(float a, float b) { - numerator += fabsf(a - b); - denominator += fabsf(a + b); - } - - __host__ __device__ float reduce() { - return (numerator / denominator); - } - - __host__ __device__ void combine(const BrayCurtisDistance& v) { - numerator += v.numerator; - denominator += v.denominator; - } - - __host__ __device__ BrayCurtisDistance zero() const { - return BrayCurtisDistance(); - } - - float numerator; - float denominator; -}; - -struct JensenShannonDistance { - __host__ __device__ JensenShannonDistance() - : dist(0) {} - - static constexpr bool kDirection = false; // minimize - static constexpr float kIdentityData = 0; - static constexpr float kMaxDistance = std::numeric_limits::max(); - - __host__ __device__ void handle(float a, float b) { - float m = 0.5f * (a + b); - - float x = m / a; - float y = m / b; - - float kl1 = -a * log(x); - float kl2 = -b * log(y); - - dist += kl1 + kl2; - } - - __host__ __device__ float reduce() { - return 0.5 * dist; - } - - __host__ __device__ void combine(const JensenShannonDistance& v) { - dist += v.dist; - } - - __host__ __device__ JensenShannonDistance zero() const { - return JensenShannonDistance(); - } - - float dist; -}; - -template -Tensor sliceCentroids(Tensor& centroids, - bool centroidsRowMajor, - int startCentroid, - int num) { - // Row major is (num, dim) - // Col major is (dim, num) - if (startCentroid == 0 && - num == centroids.getSize(centroidsRowMajor ? 0 : 1)) { - return centroids; - } - - return centroids.narrow(centroidsRowMajor ? 0 : 1, startCentroid, num); -} - -// For each chunk of k indices, increment the index by chunk * increment -template -__global__ void incrementIndex(Tensor indices, - int k, - int increment) { - for (int i = threadIdx.x; i < k; i += blockDim.x) { - indices[blockIdx.y][blockIdx.x * k + i] += blockIdx.x * increment; - } -} - -// Used to update result indices in distance computation where the number of -// centroids is high, and is tiled -template -void runIncrementIndex(Tensor& indices, - int k, - int increment, - cudaStream_t stream) { - dim3 grid(indices.getSize(1) / k, indices.getSize(0)); - int block = std::min(k, 512); - - // should be exact - FAISS_ASSERT(grid.x * k == indices.getSize(1)); - - incrementIndex<<>>(indices, k, increment); -} - -// If the inner size (dim) of the vectors is small, we want a larger query tile -// size, like 1024 -inline void chooseTileSize(int numQueries, - int numCentroids, - int dim, - int elementSize, - size_t tempMemAvailable, - int& tileRows, - int& tileCols) { - // The matrix multiplication should be large enough to be efficient, but if it - // is too large, we seem to lose efficiency as opposed to double-streaming. - // Each tile size here defines 1/2 of the memory use due to double streaming. - // We ignore available temporary memory, as that is adjusted independently by - // the user and can thus meet these requirements (or not). - // For <= 4 GB GPUs, prefer 512 MB of usage. - // For <= 8 GB GPUs, prefer 768 MB of usage. - // Otherwise, prefer 1 GB of usage. - auto totalMem = getCurrentDeviceProperties().totalGlobalMem; - - int targetUsage = 0; - - if (totalMem <= ((size_t) 4) * 1024 * 1024 * 1024) { - targetUsage = 512 * 1024 * 1024; - } else if (totalMem <= ((size_t) 8) * 1024 * 1024 * 1024) { - targetUsage = 768 * 1024 * 1024; - } else { - targetUsage = 1024 * 1024 * 1024; - } - - targetUsage /= 2 * elementSize; - - // 512 seems to be a batch size sweetspot for float32. - // If we are on float16, increase to 512. - // If the k size (vec dim) of the matrix multiplication is small (<= 32), - // increase to 1024. - int preferredTileRows = 512; - if (dim <= 32) { - preferredTileRows = 1024; - } - - tileRows = std::min(preferredTileRows, numQueries); - - // tileCols is the remainder size - tileCols = std::min(targetUsage / preferredTileRows, numCentroids); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/FlatIndex.cu b/core/src/index/thirdparty/faiss/gpu/impl/FlatIndex.cu deleted file mode 100644 index 29480fa84f..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/FlatIndex.cu +++ /dev/null @@ -1,388 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -FlatIndex::FlatIndex(GpuResources* res, - int dim, - bool useFloat16, - bool storeTransposed, - MemorySpace space) : - resources_(res), - dim_(dim), - useFloat16_(useFloat16), - storeTransposed_(storeTransposed), - space_(space), - num_(0), - rawData_(space) { -#ifndef FAISS_USE_FLOAT16 - FAISS_ASSERT(!useFloat16_); -#endif -} - -bool -FlatIndex::getUseFloat16() const { - return useFloat16_; -} - -/// Returns the number of vectors we contain -int FlatIndex::getSize() const { -#ifdef FAISS_USE_FLOAT16 - if (useFloat16_) { - return vectorsHalf_.getSize(0); - } else { - return vectors_.getSize(0); - } -#else - return vectors_.getSize(0); -#endif -} - -int FlatIndex::getDim() const { -#ifdef FAISS_USE_FLOAT16 - if (useFloat16_) { - return vectorsHalf_.getSize(1); - } else { - return vectors_.getSize(1); - } -#else - return vectors_.getSize(1); -#endif -} - -void -FlatIndex::reserve(size_t numVecs, cudaStream_t stream) { -#ifdef FAISS_USE_FLOAT16 - if (useFloat16_) { - rawData_.reserve(numVecs * dim_ * sizeof(half), stream); - } else { - rawData_.reserve(numVecs * dim_ * sizeof(float), stream); - } -#else - rawData_.reserve(numVecs * dim_ * sizeof(float), stream); -#endif -} - -template <> -Tensor& -FlatIndex::getVectorsRef() { - // Should not call this unless we are in float32 mode - FAISS_ASSERT(!useFloat16_); - return getVectorsFloat32Ref(); -} - -#ifdef FAISS_USE_FLOAT16 -template <> -Tensor& -FlatIndex::getVectorsRef() { - // Should not call this unless we are in float16 mode - FAISS_ASSERT(useFloat16_); - return getVectorsFloat16Ref(); -} -#endif - -Tensor& -FlatIndex::getVectorsFloat32Ref() { - // Should not call this unless we are in float32 mode - FAISS_ASSERT(!useFloat16_); - - return vectors_; -} - -#ifdef FAISS_USE_FLOAT16 -Tensor& -FlatIndex::getVectorsFloat16Ref() { - // Should not call this unless we are in float16 mode - FAISS_ASSERT(useFloat16_); - - return vectorsHalf_; -} -#endif - -DeviceTensor -FlatIndex::getVectorsFloat32Copy(cudaStream_t stream) { - return getVectorsFloat32Copy(0, num_, stream); -} - -DeviceTensor -FlatIndex::getVectorsFloat32Copy(int from, int num, cudaStream_t stream) { - DeviceTensor vecFloat32({num, dim_}, space_); - -#ifdef FAISS_USE_FLOAT16 - if (useFloat16_) { - auto halfNarrow = vectorsHalf_.narrowOutermost(from, num); - convertTensor(stream, halfNarrow, vecFloat32); - } else { - vectors_.copyTo(vecFloat32, stream); - } -#else - vectors_.copyTo(vecFloat32, stream); -#endif - - return vecFloat32; -} - -void -FlatIndex::query(Tensor& input, - Tensor& bitset, - int k, - faiss::MetricType metric, - float metricArg, - Tensor& outDistances, - Tensor& outIndices, - bool exactDistance) { - auto stream = resources_->getDefaultStreamCurrentDevice(); - auto& mem = resources_->getMemoryManagerCurrentDevice(); - -#ifdef FAISS_USE_FLOAT16 - if (useFloat16_) { - // We need to convert the input to float16 for comparison to ourselves - - auto inputHalf = - convertTensor(resources_, stream, input); - - query(inputHalf, bitset, k, metric, metricArg, - outDistances, outIndices, exactDistance); - - } else { - bfKnnOnDevice(resources_, - getCurrentDevice(), - stream, - storeTransposed_ ? vectorsTransposed_ : vectors_, - !storeTransposed_, // is vectors row major? - &norms_, - input, - true, // input is row major - bitset, - k, - metric, - metricArg, - outDistances, - outIndices, - !exactDistance); - } -#else - bfKnnOnDevice(resources_, - getCurrentDevice(), - stream, - storeTransposed_ ? vectorsTransposed_ : vectors_, - !storeTransposed_, // is vectors row major? - &norms_, - input, - true, // input is row major - bitset, - k, - metric, - metricArg, - outDistances, - outIndices, - !exactDistance); -#endif -} - -#ifdef FAISS_USE_FLOAT16 -void -FlatIndex::query(Tensor& input, - Tensor& bitset, - int k, - faiss::MetricType metric, - float metricArg, - Tensor& outDistances, - Tensor& outIndices, - bool exactDistance) { - FAISS_ASSERT(useFloat16_); - - bfKnnOnDevice(resources_, - getCurrentDevice(), - resources_->getDefaultStreamCurrentDevice(), - storeTransposed_ ? vectorsHalfTransposed_ : vectorsHalf_, - !storeTransposed_, // is vectors row major? - &norms_, - input, - true, // input is row major - bitset, - k, - metric, - metricArg, - outDistances, - outIndices, - !exactDistance); -} -#endif - -void -FlatIndex::computeResidual(Tensor& vecs, - Tensor& listIds, - Tensor& residuals) { -#ifdef FAISS_USE_FLOAT16 - if (useFloat16_) { - runCalcResidual(vecs, - getVectorsFloat16Ref(), - listIds, - residuals, - resources_->getDefaultStreamCurrentDevice()); - } else { - runCalcResidual(vecs, - getVectorsFloat32Ref(), - listIds, - residuals, - resources_->getDefaultStreamCurrentDevice()); - } -#else - runCalcResidual(vecs, - getVectorsFloat32Ref(), - listIds, - residuals, - resources_->getDefaultStreamCurrentDevice()); -#endif -} - -void -FlatIndex::reconstruct(Tensor& listIds, - Tensor& vecs) { -#ifdef FAISS_USE_FLOAT16 - if (useFloat16_) { - runReconstruct(listIds, - getVectorsFloat16Ref(), - vecs, - resources_->getDefaultStreamCurrentDevice()); - } else { - runReconstruct(listIds, - getVectorsFloat32Ref(), - vecs, - resources_->getDefaultStreamCurrentDevice()); - } -#else - runReconstruct(listIds, - getVectorsFloat32Ref(), - vecs, - resources_->getDefaultStreamCurrentDevice()); -#endif -} -void -FlatIndex::reconstruct(Tensor& listIds, - Tensor& vecs) { - auto listIds1 = listIds.downcastOuter<1>(); - auto vecs2 = vecs.downcastOuter<2>(); - - reconstruct(listIds1, vecs2); -} - -void -FlatIndex::add(const float* data, int numVecs, cudaStream_t stream) { - if (numVecs == 0) { - return; - } - -#ifdef FAISS_USE_FLOAT16 - if (useFloat16_) { - // Make sure that `data` is on our device; we'll run the - // conversion on our device - auto devData = toDevice(resources_, - getCurrentDevice(), - (float*) data, - stream, - {numVecs, dim_}); - - auto devDataHalf = - convertTensor(resources_, stream, devData); - - rawData_.append((char*) devDataHalf.data(), - devDataHalf.getSizeInBytes(), - stream, - true /* reserve exactly */); - } else { - rawData_.append((char*) data, - (size_t) dim_ * numVecs * sizeof(float), - stream, - true /* reserve exactly */); - } - -#else - rawData_.append((char*) data, - (size_t) dim_ * numVecs * sizeof(float), - stream, - true /* reserve exactly */); -#endif - num_ += numVecs; - -#ifdef FAISS_USE_FLOAT16 - if (useFloat16_) { - DeviceTensor vectorsHalf( - (half*) rawData_.data(), {(int) num_, dim_}, space_); - vectorsHalf_ = std::move(vectorsHalf); - } else { - DeviceTensor vectors( - (float*) rawData_.data(), {(int) num_, dim_}, space_); - vectors_ = std::move(vectors); - } -#else - DeviceTensor vectors( - (float*) rawData_.data(), {(int) num_, dim_}, space_); - vectors_ = std::move(vectors); -#endif - - if (storeTransposed_) { -#ifdef FAISS_USE_FLOAT16 - if (useFloat16_) { - vectorsHalfTransposed_ = - std::move(DeviceTensor({dim_, (int) num_}, space_)); - runTransposeAny(vectorsHalf_, 0, 1, vectorsHalfTransposed_, stream); - } else { - vectorsTransposed_ = - std::move(DeviceTensor({dim_, (int) num_}, space_)); - runTransposeAny(vectors_, 0, 1, vectorsTransposed_, stream); - } -#else - vectorsTransposed_ = - std::move(DeviceTensor({dim_, (int) num_}, space_)); - runTransposeAny(vectors_, 0, 1, vectorsTransposed_, stream); -#endif - } - - // Precompute L2 norms of our database -#ifdef FAISS_USE_FLOAT16 - if (useFloat16_) { - DeviceTensor norms({(int) num_}, space_); - runL2Norm(vectorsHalf_, true, norms, true, stream); - norms_ = std::move(norms); - } else { - DeviceTensor norms({(int) num_}, space_); - runL2Norm(vectors_, true, norms, true, stream); - norms_ = std::move(norms); - } -#else - DeviceTensor norms({(int) num_}, space_); - runL2Norm(vectors_, true, norms, true, stream); - norms_ = std::move(norms); -#endif -} - -void -FlatIndex::reset() { - rawData_.clear(); - vectors_ = std::move(DeviceTensor()); - vectorsTransposed_ = std::move(DeviceTensor()); -#ifdef FAISS_USE_FLOAT16 - vectorsHalf_ = std::move(DeviceTensor()); - vectorsHalfTransposed_ = std::move(DeviceTensor()); -#endif - norms_ = std::move(DeviceTensor()); - num_ = 0; -} - -} } diff --git a/core/src/index/thirdparty/faiss/gpu/impl/FlatIndex.cuh b/core/src/index/thirdparty/faiss/gpu/impl/FlatIndex.cuh deleted file mode 100644 index eef07df24c..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/FlatIndex.cuh +++ /dev/null @@ -1,139 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -class GpuResources; - -/// Holder of GPU resources for a particular flat index -class FlatIndex { - public: - FlatIndex(GpuResources* res, - int dim, - bool useFloat16, - bool storeTransposed, - MemorySpace space); - - /// Whether or not this flat index primarily stores data in float16 - bool getUseFloat16() const; - - /// Returns the number of vectors we contain - int getSize() const; - - /// Returns the dimensionality of the vectors - int getDim() const; - - /// Reserve storage that can contain at least this many vectors - void reserve(size_t numVecs, cudaStream_t stream); - - /// Returns the vectors based on the type desired; the FlatIndex must be of - /// the same type (float16 or float32) to not assert - template - Tensor& getVectorsRef(); - - /// Returns a reference to our vectors currently in use - Tensor& getVectorsFloat32Ref(); - - /// Returns a reference to our vectors currently in use (useFloat16 mode) -#ifdef FAISS_USE_FLOAT16 - Tensor& getVectorsFloat16Ref(); -#endif - - /// Performs a copy of the vectors on the given device, converting - /// as needed from float16 - DeviceTensor getVectorsFloat32Copy(cudaStream_t stream); - - /// Returns only a subset of the vectors - DeviceTensor getVectorsFloat32Copy(int from, - int num, - cudaStream_t stream); - - void query(Tensor& vecs, - Tensor& bitset, - int k, - faiss::MetricType metric, - float metricArg, - Tensor& outDistances, - Tensor& outIndices, - bool exactDistance); - -#ifdef FAISS_USE_FLOAT16 - void query(Tensor& vecs, - Tensor& bitset, - int k, - faiss::MetricType metric, - float metricArg, - Tensor& outDistances, - Tensor& outIndices, - bool exactDistance); -#endif - - /// Compute residual for set of vectors - void computeResidual(Tensor& vecs, - Tensor& listIds, - Tensor& residuals); - - /// Gather vectors given the set of IDs - void reconstruct(Tensor& listIds, - Tensor& vecs); - - void reconstruct(Tensor& listIds, - Tensor& vecs); - - /// Add vectors to ourselves; the pointer passed can be on the host - /// or the device - void add(const float* data, int numVecs, cudaStream_t stream); - - /// Free all storage - void reset(); - - private: - /// Collection of GPU resources that we use - GpuResources* resources_; - - /// Dimensionality of our vectors - const int dim_; - - /// Float16 data format - const bool useFloat16_; - - /// Store vectors in transposed layout for speed; makes addition to - /// the index slower - const bool storeTransposed_; - - /// Memory space for our allocations - MemorySpace space_; - - /// How many vectors we have - int num_; - - /// The underlying expandable storage - DeviceVector rawData_; - - /// Vectors currently in rawData_ - DeviceTensor vectors_; - DeviceTensor vectorsTransposed_; - - /// Vectors currently in rawData_, float16 form -#ifdef FAISS_USE_FLOAT16 - DeviceTensor vectorsHalf_; - DeviceTensor vectorsHalfTransposed_; -#endif - - /// Precomputed L2 norms - DeviceTensor norms_; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/GeneralDistance.cuh b/core/src/index/thirdparty/faiss/gpu/impl/GeneralDistance.cuh deleted file mode 100644 index 5dae58638c..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/GeneralDistance.cuh +++ /dev/null @@ -1,432 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -// -// Kernels for non-L2 / inner product distances -// - -namespace faiss { namespace gpu { - -// Reduction tree operator -template -struct ReduceDistanceOp { - __device__ static DistanceOp reduce(DistanceOp ops[N]) { - DistanceOp vals[N/2]; -#pragma unroll - for (int i = 0; i < N / 2; ++i) { - vals[i] = ops[i * 2]; - vals[i].combine(ops[i * 2 + 1]); - } - - return ReduceDistanceOp::reduce(vals); - } -}; - -template -struct ReduceDistanceOp { - __device__ static DistanceOp reduce(DistanceOp ops[1]) { - return ops[0]; - } -}; - -// Implements a pairwise reduction tree -template -inline __device__ DistanceOp -reduce(const DistanceOp& in, - const T queryTile[kWarpSize][DimMultiple * kWarpSize + 1], - const T vecTile[kWarpSize][DimMultiple * kWarpSize + 1]) { - DistanceOp accs[Unroll]; -#pragma unroll - for (int i = 0; i < Unroll; ++i) { - accs[i] = in.zero(); - } - - auto vecTileBase = vecTile[threadIdx.x]; - auto queryTileBase = queryTile[threadIdx.y]; - -#pragma unroll - for (int i = 0; i < Unroll; ++i) { -#pragma unroll - for (int j = 0; j < (kWarpSize * DimMultiple / Unroll); ++j) { - int idx = i * (kWarpSize * DimMultiple / Unroll) + j; - accs[i].handle(ConvertTo::to(queryTileBase[idx]), - ConvertTo::to(vecTileBase[idx])); - } - } - - return ReduceDistanceOp::reduce(accs); -} - -// Our general distance matrix "multiplication" kernel -template -__launch_bounds__(kWarpSize * kWarpSize) -__global__ void -generalDistance(Tensor query, // m x k - Tensor vec, // n x k - DistanceOp op, - Tensor out) { // m x n - constexpr int kDimMultiple = 1; - - __shared__ T queryTile[kWarpSize][kWarpSize * kDimMultiple + 1]; - __shared__ T vecTile[kWarpSize][kWarpSize * kDimMultiple + 1]; - - // block y -> query - // block x -> vector - - int queryBlock = blockIdx.y * kWarpSize; - int queryThread = queryBlock + threadIdx.y; - - int vecBlock = blockIdx.x * kWarpSize; - int vecThreadLoad = vecBlock + threadIdx.y; - int vecThreadSave = vecBlock + threadIdx.x; - - DistanceOp acc = op.zero(); - - auto queryTileBase = queryTile[threadIdx.y]; - auto vecTileBase = vecTile[threadIdx.y]; - - auto queryBase = query[queryThread]; - auto vecBase = vec[vecThreadLoad]; - - if ((blockIdx.x != (gridDim.x - 1)) && (blockIdx.y != (gridDim.y - 1))) { - // - // Interior tile - // - int limit = utils::roundDown(query.getSize(1), kWarpSize * kDimMultiple); - - for (int k = threadIdx.x; k < limit; k += kWarpSize * kDimMultiple) { - // Load query tile -#pragma unroll - for (int i = 0; i < kDimMultiple; ++i) { - queryTileBase[threadIdx.x + i * kWarpSize] = - queryBase[k + i * kWarpSize]; - vecTileBase[threadIdx.x + i * kWarpSize] = - vecBase[k + i * kWarpSize]; - } - - __syncthreads(); - - // thread (y, x) does (query y, vec x) - acc.combine( - reduce(op, queryTile, vecTile)); - - __syncthreads(); - } - - // Handle remainder - if (limit < query.getSize(1)) { -#pragma unroll - for (int i = 0; i < kDimMultiple; ++i) { - int k = limit + threadIdx.x + i * kWarpSize; - bool kInBounds = k < query.getSize(1); - - queryTileBase[threadIdx.x + i * kWarpSize] = - kInBounds ? - queryBase[k] : (T) 0; //DistanceOp::kIdentityData; - - vecTileBase[threadIdx.x + i * kWarpSize] = - kInBounds ? - vecBase[k] : (T) 0; // DistanceOp::kIdentityData; - } - - __syncthreads(); - - int remainder = query.getSize(1) - limit; - - // thread (y, x) does (query y, vec x) -#pragma unroll - for (int i = 0; i < remainder; ++i) { - acc.handle(ConvertTo::to(queryTileBase[i]), - ConvertTo::to(vecTile[threadIdx.x][i])); - } - } - - // Write out results - out[queryThread][vecThreadSave] = acc.reduce(); - } else { - // - // Otherwise, we're an exterior tile - // - - bool queryThreadInBounds = queryThread < query.getSize(0); - bool vecThreadInBoundsLoad = vecThreadLoad < vec.getSize(0); - bool vecThreadInBoundsSave = vecThreadSave < vec.getSize(0); - int limit = utils::roundDown(query.getSize(1), kWarpSize); - - for (int k = threadIdx.x; k < limit; k += kWarpSize) { - // Load query tile - queryTileBase[threadIdx.x] = - queryThreadInBounds ? - queryBase[k] : (T) 0; // DistanceOp::kIdentityData; - - vecTileBase[threadIdx.x] = - vecThreadInBoundsLoad ? - vecBase[k] : (T) 0; // DistanceOp::kIdentityData; - - __syncthreads(); - - // thread (y, x) does (query y, vec x) -#pragma unroll - for (int i = 0; i < kWarpSize; ++i) { - acc.handle(ConvertTo::to(queryTileBase[i]), - ConvertTo::to(vecTile[threadIdx.x][i])); - } - - __syncthreads(); - } - - // Handle remainder - if (limit < query.getSize(1)) { - int k = limit + threadIdx.x; - bool kInBounds = k < query.getSize(1); - - // Load query tile - queryTileBase[threadIdx.x] = - queryThreadInBounds && kInBounds ? - queryBase[k] : (T) 0; // DistanceOp::kIdentityData; - - vecTileBase[threadIdx.x] = - vecThreadInBoundsLoad && kInBounds ? - vecBase[k] : (T) 0; // DistanceOp::kIdentityData; - - __syncthreads(); - - int remainder = query.getSize(1) - limit; - - // thread (y, x) does (query y, vec x) - for (int i = 0; i < remainder; ++i) { - acc.handle(ConvertTo::to(queryTileBase[i]), - ConvertTo::to(vecTile[threadIdx.x][i])); - } - } - - // Write out results - if (queryThreadInBounds && vecThreadInBoundsSave) { - out[queryThread][vecThreadSave] = acc.reduce(); - } - } -} - - -template -void runGeneralDistanceKernel(Tensor& vecs, - Tensor& query, - Tensor& out, - const DistanceOp& op, - cudaStream_t stream) { - FAISS_ASSERT(vecs.getSize(1) == query.getSize(1)); - FAISS_ASSERT(out.getSize(0) == query.getSize(0)); - FAISS_ASSERT(out.getSize(1) == vecs.getSize(0)); - - dim3 grid(utils::divUp(vecs.getSize(0), kWarpSize), - utils::divUp(query.getSize(0), kWarpSize)); - dim3 block(kWarpSize, kWarpSize); - - generalDistance<<>>(query, vecs, op, out); -} - -template -void runGeneralDistance(GpuResources* resources, - Tensor& centroids, - Tensor& queries, - Tensor& bitset, - int k, - const DistanceOp& op, - Tensor& outDistances, - Tensor& outIndices) { - // The # of centroids in `centroids` based on memory layout - auto numCentroids = centroids.getSize(0); - - // The # of queries in `queries` based on memory layout - auto numQueries = queries.getSize(0); - - // The dimensions of the vectors to consider - auto dim = queries.getSize(1); - FAISS_ASSERT((numQueries == 0 || numCentroids == 0) || - dim == centroids.getSize(1)); - - FAISS_ASSERT(outDistances.getSize(0) == numQueries); - FAISS_ASSERT(outIndices.getSize(0) == numQueries); - FAISS_ASSERT(outDistances.getSize(1) == k); - FAISS_ASSERT(outIndices.getSize(1) == k); - - auto& mem = resources->getMemoryManagerCurrentDevice(); - auto defaultStream = resources->getDefaultStreamCurrentDevice(); - - // If we're quering against a 0 sized set, just return empty results - if (centroids.numElements() == 0) { - thrust::fill(thrust::cuda::par.on(defaultStream), - outDistances.data(), outDistances.end(), - Limits::getMax()); - - thrust::fill(thrust::cuda::par.on(defaultStream), - outIndices.data(), outIndices.end(), - -1); - - return; - } - - // By default, aim to use up to 512 MB of memory for the processing, with both - // number of queries and number of centroids being at least 512. - int tileRows = 0; - int tileCols = 0; - chooseTileSize(numQueries, - numCentroids, - dim, - sizeof(T), - mem.getSizeAvailable(), - tileRows, - tileCols); - - int numColTiles = utils::divUp(numCentroids, tileCols); - - // We can have any number of vectors to query against, even less than k, in - // which case we'll return -1 for the index - FAISS_ASSERT(k <= GPU_MAX_SELECTION_K); // select limitation - - // Temporary output memory space we'll use - DeviceTensor distanceBuf1( - mem, {tileRows, tileCols}, defaultStream); - DeviceTensor distanceBuf2( - mem, {tileRows, tileCols}, defaultStream); - DeviceTensor* distanceBufs[2] = - {&distanceBuf1, &distanceBuf2}; - - DeviceTensor outDistanceBuf1( - mem, {tileRows, numColTiles * k}, defaultStream); - DeviceTensor outDistanceBuf2( - mem, {tileRows, numColTiles * k}, defaultStream); - DeviceTensor* outDistanceBufs[2] = - {&outDistanceBuf1, &outDistanceBuf2}; - - DeviceTensor outIndexBuf1( - mem, {tileRows, numColTiles * k}, defaultStream); - DeviceTensor outIndexBuf2( - mem, {tileRows, numColTiles * k}, defaultStream); - DeviceTensor* outIndexBufs[2] = - {&outIndexBuf1, &outIndexBuf2}; - - auto streams = resources->getAlternateStreamsCurrentDevice(); - streamWait(streams, {defaultStream}); - - int curStream = 0; - bool interrupt = false; - - // Tile over the input queries - for (int i = 0; i < numQueries; i += tileRows) { - if (interrupt || InterruptCallback::is_interrupted()) { - interrupt = true; - break; - } - - int curQuerySize = std::min(tileRows, numQueries - i); - - auto outDistanceView = - outDistances.narrow(0, i, curQuerySize); - auto outIndexView = - outIndices.narrow(0, i, curQuerySize); - - auto queryView = - queries.narrow(0, i, curQuerySize); - - auto outDistanceBufRowView = - outDistanceBufs[curStream]->narrow(0, 0, curQuerySize); - auto outIndexBufRowView = - outIndexBufs[curStream]->narrow(0, 0, curQuerySize); - - // Tile over the centroids - for (int j = 0; j < numCentroids; j += tileCols) { - if (InterruptCallback::is_interrupted()) { - interrupt = true; - break; - } - - int curCentroidSize = std::min(tileCols, numCentroids - j); - int curColTile = j / tileCols; - - auto centroidsView = - sliceCentroids(centroids, true, j, curCentroidSize); - - auto distanceBufView = distanceBufs[curStream]-> - narrow(0, 0, curQuerySize).narrow(1, 0, curCentroidSize); - - auto outDistanceBufColView = - outDistanceBufRowView.narrow(1, k * curColTile, k); - auto outIndexBufColView = - outIndexBufRowView.narrow(1, k * curColTile, k); - - runGeneralDistanceKernel(centroidsView, - queryView, - distanceBufView, - op, - streams[curStream]); - - // For IP, just k-select the output for this tile - if (tileCols == numCentroids) { - // Write into the final output - runBlockSelect(distanceBufView, - bitset, - outDistanceView, - outIndexView, - DistanceOp::kDirection, k, streams[curStream]); - } else { - // Write into the intermediate output - runBlockSelect(distanceBufView, - bitset, - outDistanceBufColView, - outIndexBufColView, - DistanceOp::kDirection, k, streams[curStream]); - } - } - - // As we're finished with processing a full set of centroids, perform the - // final k-selection - if (tileCols != numCentroids) { - // The indices are tile-relative; for each tile of k, we need to add - // tileCols to the index - runIncrementIndex(outIndexBufRowView, k, tileCols, streams[curStream]); - - runBlockSelectPair(outDistanceBufRowView, - outIndexBufRowView, - bitset, - outDistanceView, - outIndexView, - DistanceOp::kDirection, k, streams[curStream]); - } - - curStream = (curStream + 1) % 2; - } - - // Have the desired ordering stream wait on the multi-stream - streamWait({defaultStream}, streams); - - if (interrupt) { - FAISS_THROW_MSG("interrupted"); - } - - CUDA_TEST_ERROR(); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/GpuScalarQuantizer.cuh b/core/src/index/thirdparty/faiss/gpu/impl/GpuScalarQuantizer.cuh deleted file mode 100644 index 32675a5a4e..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/GpuScalarQuantizer.cuh +++ /dev/null @@ -1,607 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -inline bool isSQSupported(QuantizerType qtype) { - switch (qtype) { - case QuantizerType::QT_8bit: - case QuantizerType::QT_8bit_uniform: - case QuantizerType::QT_8bit_direct: - case QuantizerType::QT_4bit: - case QuantizerType::QT_4bit_uniform: - case QuantizerType::QT_fp16: - return true; - default: - return false; - } -} - -// Wrapper around the CPU ScalarQuantizer that allows storage of parameters in -// GPU memory -struct GpuScalarQuantizer : public ScalarQuantizer { - GpuScalarQuantizer(const ScalarQuantizer& sq) - : ScalarQuantizer(sq), - gpuTrained(DeviceTensor({(int) sq.trained.size()})) { - HostTensor - cpuTrained((float*) sq.trained.data(), {(int) sq.trained.size()}); - - // Just use the default stream, as we're allocating memory above in any case - gpuTrained.copyFrom(cpuTrained, 0); - CUDA_VERIFY(cudaStreamSynchronize(0)); - } - - // ScalarQuantizer::trained copied to GPU memory - DeviceTensor gpuTrained; -}; - -// -// Quantizer codecs -// - -// QT is the quantizer type implemented -// DimMultiple is the minimum guaranteed dimension multiple of the vectors -// encoded (used for ensuring alignment for memory load/stores) -template -struct Codec { }; - -///// -// -// 32 bit encodings -// (does not use qtype) -// -///// - -struct CodecFloat { - /// How many dimensions per iteration we are handling for encoding or decoding - static constexpr int kDimPerIter = 1; - - CodecFloat(int vecBytes) : bytesPerVec(vecBytes) { } - - size_t getSmemSize(int dim) { return 0; } - inline __device__ void setSmem(float* smem, int dim) { } - - inline __device__ void decode(void* data, int vec, int d, - float* out) const { - float* p = (float*) &((uint8_t*) data)[vec * bytesPerVec]; - out[0] = p[d]; - } - - inline __device__ float decodePartial(void* data, int vec, int d, - int subD) const { - // doesn't need implementing (kDimPerIter == 1) - return 0.0f; - } - - inline __device__ void encode(void* data, int vec, int d, - float v[kDimPerIter]) const { - float* p = (float*) &((uint8_t*) data)[vec * bytesPerVec]; - p[d] = v[0]; - } - - inline __device__ void encodePartial(void* data, int vec, int d, - int remaining, - float v[kDimPerIter]) const { - // doesn't need implementing (kDimPerIter == 1) - } - - int bytesPerVec; -}; - -///// -// -// 16 bit encodings -// -///// - -// Arbitrary dimension fp16 -template <> -struct Codec<(int)QuantizerType::QT_fp16, 1> { - /// How many dimensions per iteration we are handling for encoding or decoding - static constexpr int kDimPerIter = 1; - - Codec(int vecBytes) : bytesPerVec(vecBytes) { } - - size_t getSmemSize(int dim) { return 0; } - inline __device__ void setSmem(float* smem, int dim) { } - - inline __device__ void decode(void* data, int vec, int d, - float* out) const { - half* p = (half*) &((uint8_t*) data)[vec * bytesPerVec]; - out[0] = Convert()(p[d]); - } - - inline __device__ float decodePartial(void* data, int vec, int d, - int subD) const { - // doesn't need implementing (kDimPerIter == 1) - return 0.0f; - } - - inline __device__ void encode(void* data, int vec, int d, - float v[kDimPerIter]) const { - half* p = (half*) &((uint8_t*) data)[vec * bytesPerVec]; - p[d] = Convert()(v[0]); - } - - inline __device__ void encodePartial(void* data, int vec, int d, - int remaining, - float v[kDimPerIter]) const { - // doesn't need implementing (kDimPerIter == 1) - } - - int bytesPerVec; -}; - -// dim % 2 == 0, ensures uint32 alignment -template <> -struct Codec<(int)QuantizerType::QT_fp16, 2> { - /// How many dimensions per iteration we are handling for encoding or decoding - static constexpr int kDimPerIter = 2; - - Codec(int vecBytes) : bytesPerVec(vecBytes) { } - - size_t getSmemSize(int dim) { return 0; } - inline __device__ void setSmem(float* smem, int dim) { } - - inline __device__ void decode(void* data, int vec, int d, - float* out) const { - half2* p = (half2*) &((uint8_t*) data)[vec * bytesPerVec]; - half2 pd = p[d]; - - out[0] = Convert()(__low2half(pd)); - out[1] = Convert()(__high2half(pd)); - } - - inline __device__ float decodePartial(void* data, int vec, int d, - int subD) const { - // should not be called - assert(false); - return 0; - } - - inline __device__ void encode(void* data, int vec, int d, - float v[kDimPerIter]) const { - half2* p = (half2*) &((uint8_t*) data)[vec * bytesPerVec]; - half h0 = Convert()(v[0]); - half h1 = Convert()(v[1]); - - p[d] = __halves2half2(h0, h1); - } - - inline __device__ void encodePartial(void* data, int vec, int d, - int remaining, - float v[kDimPerIter]) const { - // should not be called - assert(false); - } - - int bytesPerVec; -}; - -///// -// -// 8 bit encodings -// -///// - -template -struct Get8BitType { }; - -template <> -struct Get8BitType<1> { using T = uint8_t; }; - -template <> -struct Get8BitType<2> { using T = uint16_t; }; - -template <> -struct Get8BitType<4> { using T = uint32_t; }; - -// Uniform quantization across all dimensions -template -struct Codec<(int)QuantizerType::QT_8bit_uniform, DimMultiple> { - /// How many dimensions per iteration we are handling for encoding or decoding - static constexpr int kDimPerIter = DimMultiple; - using MemT = typename Get8BitType::T; - - Codec(int vecBytes, float min, float diff) - : bytesPerVec(vecBytes), vmin(min), vdiff(diff) { - } - - size_t getSmemSize(int dim) { return 0; } - inline __device__ void setSmem(float* smem, int dim) { } - - inline __device__ float decodeHelper(uint8_t v) const { - float x = (((float) v) + 0.5f) / 255.0f; - return vmin + x * vdiff; - } - - inline __device__ void decode(void* data, int vec, int d, - float* out) const { - MemT* p = (MemT*) &((uint8_t*) data)[vec * bytesPerVec]; - MemT pv = p[d]; - - uint8_t x[kDimPerIter]; -#pragma unroll - for (int i = 0; i < kDimPerIter; ++i) { - x[i] = (uint8_t) ((pv >> (i * 8)) & 0xffU); - } - - float xDec[kDimPerIter]; -#pragma unroll - for (int i = 0; i < kDimPerIter; ++i) { - xDec[i] = decodeHelper(x[i]); - } - - #pragma unroll - for (int i = 0; i < kDimPerIter; ++i) { - out[i] = xDec[i]; - } - } - - inline __device__ float decodePartial(void* data, int vec, int d, - int subD) const { - if (DimMultiple > 1) { - // should not be called - assert(false); - } - - // otherwise does not need implementing - return 0; - } - - inline __device__ uint8_t encodeHelper(float v) const { - float x = (v - vmin) / vdiff; - x = fminf(1.0f, fmaxf(0.0f, x)); - return (uint8_t) (255 * x); - } - - inline __device__ void encode(void* data, int vec, int d, - float v[kDimPerIter]) const { - MemT* p = (MemT*) &((uint8_t*) data)[vec * bytesPerVec]; - - MemT x[kDimPerIter]; -#pragma unroll - for (int i = 0; i < kDimPerIter; ++i) { - x[i] = encodeHelper(v[i]); - } - - MemT out = 0; -#pragma unroll - for (int i = 0; i < kDimPerIter; ++i) { - out |= (x[i] << (i * 8)); - } - - p[d] = out; - } - - inline __device__ void encodePartial(void* data, int vec, int d, - int remaining, - float v[kDimPerIter]) const { - if (DimMultiple > 1) { - // should not be called - assert(false); - } - - // otherwise does not need implementing - } - - int bytesPerVec; - const float vmin; - const float vdiff; -}; - -// Uniform quantization per each dimension -template -struct Codec<(int)QuantizerType::QT_8bit, DimMultiple> { - /// How many dimensions per iteration we are handling for encoding or decoding - static constexpr int kDimPerIter = DimMultiple; - using MemT = typename Get8BitType::T; - - Codec(int vecBytes, float* min, float* diff) - : bytesPerVec(vecBytes), vmin(min), vdiff(diff), - smemVmin(nullptr), - smemVdiff(nullptr) { - } - - size_t getSmemSize(int dim) { - return sizeof(float) * dim * 2; - } - - inline __device__ void setSmem(float* smem, int dim) { - smemVmin = smem; - smemVdiff = smem + dim; - - for (int i = threadIdx.x; i < dim; i += blockDim.x) { - smemVmin[i] = vmin[i]; - smemVdiff[i] = vdiff[i]; - } - } - - inline __device__ float decodeHelper(uint8_t v, int realDim) const { - float x = (((float) v) + 0.5f) / 255.0f; - return smemVmin[realDim] + x * smemVdiff[realDim]; - } - - inline __device__ void decode(void* data, int vec, int d, - float* out) const { - MemT* p = (MemT*) &((uint8_t*) data)[vec * bytesPerVec]; - MemT pv = p[d]; - int realDim = d * kDimPerIter; - - uint8_t x[kDimPerIter]; -#pragma unroll - for (int i = 0; i < kDimPerIter; ++i) { - x[i] = (uint8_t) ((pv >> (i * 8)) & 0xffU); - } - - float xDec[kDimPerIter]; -#pragma unroll - for (int i = 0; i < kDimPerIter; ++i) { - xDec[i] = decodeHelper(x[i], realDim + i); - } - - #pragma unroll - for (int i = 0; i < kDimPerIter; ++i) { - out[i] = xDec[i]; - } - } - - inline __device__ float decodePartial(void* data, int vec, int d, - int subD) const { - if (DimMultiple > 1) { - // should not be called - assert(false); - } - - // otherwise does not need implementing - return 0; - } - - inline __device__ uint8_t encodeHelper(float v, int realDim) const { - float x = (v - vmin[realDim]) / vdiff[realDim]; - x = fminf(1.0f, fmaxf(0.0f, x)); - return (uint8_t) (255 * x); - } - - inline __device__ void encode(void* data, int vec, int d, - float v[kDimPerIter]) const { - MemT* p = (MemT*) &((uint8_t*) data)[vec * bytesPerVec]; - int realDim = d * kDimPerIter; - - MemT x[kDimPerIter]; -#pragma unroll - for (int i = 0; i < kDimPerIter; ++i) { - x[i] = encodeHelper(v[i], realDim + i); - } - - MemT out = 0; -#pragma unroll - for (int i = 0; i < kDimPerIter; ++i) { - out |= (x[i] << (i * 8)); - } - - p[d] = out; - } - - inline __device__ void encodePartial(void* data, int vec, int d, - int remaining, - float v[kDimPerIter]) const { - if (DimMultiple > 1) { - // should not be called - assert(false); - } - - // otherwise does not need implementing - } - - int bytesPerVec; - - // gmem pointers - const float* vmin; - const float* vdiff; - - // smem pointers (configured in the kernel) - float* smemVmin; - float* smemVdiff; -}; - -template <> -struct Codec<(int)QuantizerType::QT_8bit_direct, 1> { - /// How many dimensions per iteration we are handling for encoding or decoding - static constexpr int kDimPerIter = 1; - - Codec(int vecBytes) : bytesPerVec(vecBytes) { } - - size_t getSmemSize(int dim) { return 0; } - inline __device__ void setSmem(float* smem, int dim) { } - - inline __device__ void decode(void* data, int vec, int d, - float* out) const { - uint8_t* p = &((uint8_t*) data)[vec * bytesPerVec]; - out[0] = (float) p[d]; - } - - inline __device__ float decodePartial(void* data, int vec, int d, - int subD) const { - // doesn't need implementing (kDimPerIter == 1) - return 0.0f; - } - - inline __device__ void encode(void* data, int vec, int d, - float v[kDimPerIter]) const { - uint8_t* p = &((uint8_t*) data)[vec * bytesPerVec]; - p[d] = (uint8_t) v[0]; - } - - inline __device__ void encodePartial(void* data, int vec, int d, - int remaining, - float v[kDimPerIter]) const { - // doesn't need implementing (kDimPerIter == 1) - } - - int bytesPerVec; -}; - -///// -// -// 4 bit encodings -// -///// - -// Uniform quantization across all dimensions -template <> -struct Codec<(int)QuantizerType::QT_4bit_uniform, 1> { - /// How many dimensions per iteration we are handling for encoding or decoding - static constexpr int kDimPerIter = 2; - - Codec(int vecBytes, float min, float diff) - : bytesPerVec(vecBytes), vmin(min), vdiff(diff) { - } - - size_t getSmemSize(int dim) { return 0; } - inline __device__ void setSmem(float* smem, int dim) { } - - inline __device__ float decodeHelper(uint8_t v) const { - float x = (((float) v) + 0.5f) / 15.0f; - return vmin + x * vdiff; - } - - inline __device__ void decode(void* data, int vec, int d, - float* out) const { - uint8_t* p = &((uint8_t*) data)[vec * bytesPerVec]; - uint8_t pv = p[d]; - - out[0] = decodeHelper(pv & 0xf); - out[1] = decodeHelper(pv >> 4); - } - - inline __device__ float decodePartial(void* data, int vec, int d, - int subD /* unused */) const { - // We can only be called for a single input - uint8_t* p = &((uint8_t*) data)[vec * bytesPerVec]; - uint8_t pv = p[d]; - - return decodeHelper(pv & 0xf); - } - - inline __device__ uint8_t encodeHelper(float v) const { - float x = (v - vmin) / vdiff; - x = fminf(1.0f, fmaxf(0.0f, x)); - return (uint8_t) (x * 15.0f); - } - - inline __device__ void encode(void* data, int vec, int d, - float v[kDimPerIter]) const { - uint8_t* p = &((uint8_t*) data)[vec * bytesPerVec]; - p[d] = encodeHelper(v[0]) | (encodeHelper(v[1]) << 4); - } - - inline __device__ void encodePartial(void* data, int vec, int d, - int remaining, /* unused */ - float v[kDimPerIter]) const { - // We can only be called for a single output - uint8_t* p = &((uint8_t*) data)[vec * bytesPerVec]; - p[d] = encodeHelper(v[0]); - } - - int bytesPerVec; - const float vmin; - const float vdiff; -}; - -template <> -struct Codec<(int)QuantizerType::QT_4bit, 1> { - /// How many dimensions per iteration we are handling for encoding or decoding - static constexpr int kDimPerIter = 2; - - Codec(int vecBytes, float* min, float* diff) - : bytesPerVec(vecBytes), vmin(min), vdiff(diff), - smemVmin(nullptr), - smemVdiff(nullptr) { - } - - size_t getSmemSize(int dim) { - return sizeof(float) * dim * 2; - } - - inline __device__ void setSmem(float* smem, int dim) { - smemVmin = smem; - smemVdiff = smem + dim; - - for (int i = threadIdx.x; i < dim; i += blockDim.x) { - smemVmin[i] = vmin[i]; - smemVdiff[i] = vdiff[i]; - } - } - - inline __device__ float decodeHelper(uint8_t v, int realDim) const { - float x = (((float) v) + 0.5f) / 15.0f; - return smemVmin[realDim] + x * smemVdiff[realDim]; - } - - inline __device__ void decode(void* data, int vec, int d, - float* out) const { - uint8_t* p = &((uint8_t*) data)[vec * bytesPerVec]; - uint8_t pv = p[d]; - int realDim = d * kDimPerIter; - - out[0] = decodeHelper(pv & 0xf, realDim); - out[1] = decodeHelper(pv >> 4, realDim + 1); - } - - inline __device__ float decodePartial(void* data, int vec, int d, - int subD /* unused */) const { - // We can only be called for a single input - uint8_t* p = &((uint8_t*) data)[vec * bytesPerVec]; - uint8_t pv = p[d]; - int realDim = d * kDimPerIter; - - return decodeHelper(pv & 0xf, realDim); - } - - inline __device__ uint8_t encodeHelper(float v, int realDim) const { - float x = (v - vmin[realDim]) / vdiff[realDim]; - x = fminf(1.0f, fmaxf(0.0f, x)); - return (uint8_t) (x * 15.0f); - } - - inline __device__ void encode(void* data, int vec, int d, - float v[kDimPerIter]) const { - uint8_t* p = &((uint8_t*) data)[vec * bytesPerVec]; - int realDim = d * kDimPerIter; - p[d] = encodeHelper(v[0], realDim) | (encodeHelper(v[1], realDim + 1) << 4); - } - - inline __device__ void encodePartial(void* data, int vec, int d, - int remaining, /* unused */ - float v[kDimPerIter]) const { - // We can only be called for a single output - uint8_t* p = &((uint8_t*) data)[vec * bytesPerVec]; - int realDim = d * kDimPerIter; - - p[d] = encodeHelper(v[0], realDim); - } - - int bytesPerVec; - - // gmem pointers - const float* vmin; - const float* vdiff; - - // smem pointers - float* smemVmin; - float* smemVdiff; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/IVFAppend.cu b/core/src/index/thirdparty/faiss/gpu/impl/IVFAppend.cu deleted file mode 100644 index ace37549b9..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/IVFAppend.cu +++ /dev/null @@ -1,369 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -// -// IVF list length update -// - -__global__ void -runUpdateListPointers(Tensor listIds, - Tensor newListLength, - Tensor newCodePointers, - Tensor newIndexPointers, - int* listLengths, - void** listCodes, - void** listIndices) { - int i = blockIdx.x * blockDim.x + threadIdx.x; - - if (i < listIds.getSize(0)) { - int listId = listIds[i]; - listLengths[listId] = newListLength[i]; - listCodes[listId] = newCodePointers[i]; - listIndices[listId] = newIndexPointers[i]; - } -} - -void -runUpdateListPointers(Tensor& listIds, - Tensor& newListLength, - Tensor& newCodePointers, - Tensor& newIndexPointers, - thrust::device_vector& listLengths, - thrust::device_vector& listCodes, - thrust::device_vector& listIndices, - cudaStream_t stream) { - int numThreads = std::min(listIds.getSize(0), getMaxThreadsCurrentDevice()); - int numBlocks = utils::divUp(listIds.getSize(0), numThreads); - - dim3 grid(numBlocks); - dim3 block(numThreads); - - runUpdateListPointers<<>>( - listIds, newListLength, newCodePointers, newIndexPointers, - listLengths.data().get(), - listCodes.data().get(), - listIndices.data().get()); - - CUDA_TEST_ERROR(); -} - -// -// IVF PQ append -// - -template -__global__ void -ivfpqInvertedListAppend(Tensor listIds, - Tensor listOffset, - Tensor encodings, - Tensor indices, - void** listCodes, - void** listIndices) { - int encodingToAdd = blockIdx.x * blockDim.x + threadIdx.x; - - if (encodingToAdd >= listIds.getSize(0)) { - return; - } - - int listId = listIds[encodingToAdd]; - int offset = listOffset[encodingToAdd]; - - // Add vector could be invalid (contains NaNs etc) - if (listId == -1 || offset == -1) { - return; - } - - auto encoding = encodings[encodingToAdd]; - long index = indices[encodingToAdd]; - - if (Opt == INDICES_32_BIT) { - // FIXME: there could be overflow here, but where should we check this? - ((int*) listIndices[listId])[offset] = (int) index; - } else if (Opt == INDICES_64_BIT) { - ((long*) listIndices[listId])[offset] = (long) index; - } else { - // INDICES_CPU or INDICES_IVF; no indices are being stored - } - - unsigned char* codeStart = - ((unsigned char*) listCodes[listId]) + offset * encodings.getSize(1); - - // FIXME: slow - for (int i = 0; i < encodings.getSize(1); ++i) { - codeStart[i] = (unsigned char) encoding[i]; - } -} - -void -runIVFPQInvertedListAppend(Tensor& listIds, - Tensor& listOffset, - Tensor& encodings, - Tensor& indices, - thrust::device_vector& listCodes, - thrust::device_vector& listIndices, - IndicesOptions indicesOptions, - cudaStream_t stream) { - int numThreads = std::min(listIds.getSize(0), getMaxThreadsCurrentDevice()); - int numBlocks = utils::divUp(listIds.getSize(0), numThreads); - - dim3 grid(numBlocks); - dim3 block(numThreads); - -#define RUN_APPEND(IND) \ - do { \ - ivfpqInvertedListAppend<<>>( \ - listIds, listOffset, encodings, indices, \ - listCodes.data().get(), \ - listIndices.data().get()); \ - } while (0) - - if ((indicesOptions == INDICES_CPU) || (indicesOptions == INDICES_IVF)) { - // no need to maintain indices on the GPU - RUN_APPEND(INDICES_IVF); - } else if (indicesOptions == INDICES_32_BIT) { - RUN_APPEND(INDICES_32_BIT); - } else if (indicesOptions == INDICES_64_BIT) { - RUN_APPEND(INDICES_64_BIT); - } else { - // unknown index storage type - FAISS_ASSERT(false); - } - - CUDA_TEST_ERROR(); - -#undef RUN_APPEND -} - -// -// IVF flat append -// - -__global__ void -ivfFlatIndicesAppend(Tensor listIds, - Tensor listOffset, - Tensor indices, - IndicesOptions opt, - void** listIndices) { - int vec = blockIdx.x * blockDim.x + threadIdx.x; - - if (vec >= listIds.getSize(0)) { - return; - } - - int listId = listIds[vec]; - int offset = listOffset[vec]; - - // Add vector could be invalid (contains NaNs etc) - if (listId == -1 || offset == -1) { - return; - } - - long index = indices[vec]; - - if (opt == INDICES_32_BIT) { - // FIXME: there could be overflow here, but where should we check this? - ((int*) listIndices[listId])[offset] = (int) index; - } else if (opt == INDICES_64_BIT) { - ((long*) listIndices[listId])[offset] = (long) index; - } -} - -template -__global__ void -ivfFlatInvertedListAppend(Tensor listIds, - Tensor listOffset, - Tensor vecs, - void** listData, - Codec codec) { - int vec = blockIdx.x; - - int listId = listIds[vec]; - int offset = listOffset[vec]; - - // Add vector could be invalid (contains NaNs etc) - if (listId == -1 || offset == -1) { - return; - } - - // Handle whole encoding (only thread 0 will handle the remainder) - int limit = utils::divDown(vecs.getSize(1), Codec::kDimPerIter); - - int i; - for (i = threadIdx.x; i < limit; i += blockDim.x) { - int realDim = i * Codec::kDimPerIter; - float toEncode[Codec::kDimPerIter]; - -#pragma unroll - for (int j = 0; j < Codec::kDimPerIter; ++j) { - toEncode[j] = vecs[vec][realDim + j]; - } - - codec.encode(listData[listId], offset, i, toEncode); - } - - // Handle remainder with a single thread, if any - if (Codec::kDimPerIter > 1) { - int realDim = limit * Codec::kDimPerIter; - - // Was there any remainder? - if (realDim < vecs.getSize(1)) { - if (threadIdx.x == 0) { - float toEncode[Codec::kDimPerIter]; - - // How many remaining that we need to encode - int remaining = vecs.getSize(1) - realDim; - -#pragma unroll - for (int j = 0; j < Codec::kDimPerIter; ++j) { - int idx = realDim + j; - toEncode[j] = idx < vecs.getSize(1) ? vecs[vec][idx] : 0.0f; - } - - codec.encodePartial(listData[listId], offset, i, remaining, toEncode); - } - } - } -} - -void -runIVFFlatInvertedListAppend(Tensor& listIds, - Tensor& listOffset, - Tensor& vecs, - Tensor& indices, - bool useResidual, - Tensor& residuals, - GpuScalarQuantizer* scalarQ, - thrust::device_vector& listData, - thrust::device_vector& listIndices, - IndicesOptions indicesOptions, - cudaStream_t stream) { - int dim = vecs.getSize(1); - int maxThreads = getMaxThreadsCurrentDevice(); - - // First, append the indices that we're about to add, if any - if (indicesOptions != INDICES_CPU && indicesOptions != INDICES_IVF) { - int blocks = utils::divUp(vecs.getSize(0), maxThreads); - - ivfFlatIndicesAppend<<>>( - listIds, - listOffset, - indices, - indicesOptions, - listIndices.data().get()); - } - - // Each block will handle appending a single vector -#define RUN_APPEND \ - do { \ - dim3 grid(vecs.getSize(0)); \ - dim3 block(std::min(dim / codec.kDimPerIter, maxThreads)); \ - \ - ivfFlatInvertedListAppend \ - <<>>( \ - listIds, \ - listOffset, \ - useResidual ? residuals : vecs, \ - listData.data().get(), \ - codec); \ - } while (0) - - if (!scalarQ) { - CodecFloat codec(dim * sizeof(float)); - RUN_APPEND; - } else { - switch (scalarQ->qtype) { - case QuantizerType::QT_8bit: - { - if (false) { -// if (dim % 4 == 0) { - Codec<(int)QuantizerType::QT_8bit, 4> - codec(scalarQ->code_size, - scalarQ->gpuTrained.data(), - scalarQ->gpuTrained.data() + dim); - RUN_APPEND; - } else { - Codec<(int)QuantizerType::QT_8bit, 1> - codec(scalarQ->code_size, - scalarQ->gpuTrained.data(), - scalarQ->gpuTrained.data() + dim); - RUN_APPEND; - } - } - break; - case QuantizerType::QT_8bit_uniform: - { -// if (dim % 4 == 0) { - if (false) { - Codec<(int)QuantizerType::QT_8bit_uniform, 4> - codec(scalarQ->code_size, scalarQ->trained[0], scalarQ->trained[1]); - RUN_APPEND; - } else { - Codec<(int)QuantizerType::QT_8bit_uniform, 1> - codec(scalarQ->code_size, scalarQ->trained[0], scalarQ->trained[1]); - RUN_APPEND; - } - } - break; - case QuantizerType::QT_fp16: - { -// if (dim % 2 == 0) { - if (false) { - Codec<(int)QuantizerType::QT_fp16, 2> - codec(scalarQ->code_size); - RUN_APPEND; - } else { - Codec<(int)QuantizerType::QT_fp16, 1> - codec(scalarQ->code_size); - RUN_APPEND; - } - } - break; - case QuantizerType::QT_8bit_direct: - { - Codec<(int)QuantizerType::QT_8bit_direct, 1> - codec(scalarQ->code_size); - RUN_APPEND; - } - break; - case QuantizerType::QT_4bit: - { - Codec<(int)QuantizerType::QT_4bit, 1> - codec(scalarQ->code_size, - scalarQ->gpuTrained.data(), - scalarQ->gpuTrained.data() + dim); - RUN_APPEND; - } - break; - case QuantizerType::QT_4bit_uniform: - { - Codec<(int)QuantizerType::QT_4bit_uniform, 1> - codec(scalarQ->code_size, scalarQ->trained[0], scalarQ->trained[1]); - RUN_APPEND; - } - break; - default: - // unimplemented, should be handled at a higher level - FAISS_ASSERT(false); - } - } - - CUDA_TEST_ERROR(); - -#undef RUN_APPEND -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/IVFAppend.cuh b/core/src/index/thirdparty/faiss/gpu/impl/IVFAppend.cuh deleted file mode 100644 index 3d61248082..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/IVFAppend.cuh +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -/// Update device-side list pointers in a batch -void runUpdateListPointers(Tensor& listIds, - Tensor& newListLength, - Tensor& newCodePointers, - Tensor& newIndexPointers, - thrust::device_vector& listLengths, - thrust::device_vector& listCodes, - thrust::device_vector& listIndices, - cudaStream_t stream); - -/// Actually append the new codes / vector indices to the individual lists - -/// IVFPQ -void runIVFPQInvertedListAppend(Tensor& listIds, - Tensor& listOffset, - Tensor& encodings, - Tensor& indices, - thrust::device_vector& listCodes, - thrust::device_vector& listIndices, - IndicesOptions indicesOptions, - cudaStream_t stream); - -/// IVF flat storage -void runIVFFlatInvertedListAppend(Tensor& listIds, - Tensor& listOffset, - Tensor& vecs, - Tensor& indices, - bool useResidual, - Tensor& residuals, - GpuScalarQuantizer* scalarQ, - thrust::device_vector& listData, - thrust::device_vector& listIndices, - IndicesOptions indicesOptions, - cudaStream_t stream); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/IVFBase.cu b/core/src/index/thirdparty/faiss/gpu/impl/IVFBase.cu deleted file mode 100644 index 48c362e36e..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/IVFBase.cu +++ /dev/null @@ -1,379 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -IVFBase::IVFBase(GpuResources* resources, - faiss::MetricType metric, - float metricArg, - FlatIndex* quantizer, - int bytesPerVector, - IndicesOptions indicesOptions, - MemorySpace space) : - resources_(resources), - metric_(metric), - metricArg_(metricArg), - quantizer_(quantizer), - bytesPerVector_(bytesPerVector), - indicesOptions_(indicesOptions), - space_(space), - dim_(quantizer->getDim()), - numLists_(quantizer->getSize()), - maxListLength_(0) { - reset(); -} - -IVFBase::~IVFBase() { -} - -void -IVFBase::reserveMemory(size_t numVecs) { - size_t vecsPerList = numVecs / deviceListData_.size(); - if (vecsPerList < 1) { - return; - } - - auto stream = resources_->getDefaultStreamCurrentDevice(); - - size_t bytesPerDataList = vecsPerList * bytesPerVector_; - for (auto& list : deviceListData_) { - list->reserve(bytesPerDataList, stream); - } - - if ((indicesOptions_ == INDICES_32_BIT) || - (indicesOptions_ == INDICES_64_BIT)) { - // Reserve for index lists as well - size_t bytesPerIndexList = vecsPerList * - (indicesOptions_ == INDICES_32_BIT ? sizeof(int) : sizeof(long)); - - for (auto& list : deviceListIndices_) { - list->reserve(bytesPerIndexList, stream); - } - } - - // Update device info for all lists, since the base pointers may - // have changed - updateDeviceListInfo_(stream); -} - -void -IVFBase::reset() { - deviceListData_.clear(); - deviceListIndices_.clear(); - deviceListDataPointers_.clear(); - deviceListIndexPointers_.clear(); - deviceListLengths_.clear(); - listOffsetToUserIndex_.clear(); - - deviceListData_.reserve(numLists_); - deviceListIndices_.reserve(numLists_); - listOffsetToUserIndex_.resize(numLists_); - - for (size_t i = 0; i < numLists_; ++i) { - deviceListData_.emplace_back( - std::unique_ptr>( - new DeviceVector(space_))); - deviceListIndices_.emplace_back( - std::unique_ptr>( - new DeviceVector(space_))); - listOffsetToUserIndex_.emplace_back(std::vector()); - } - - deviceListDataPointers_.resize(numLists_, nullptr); - deviceListIndexPointers_.resize(numLists_, nullptr); - deviceListLengths_.resize(numLists_, 0); - maxListLength_ = 0; - - deviceData_.reset(new DeviceVector(space_)); - deviceIndices_.reset(new DeviceVector(space_)); - deviceTrained_.reset(new DeviceVector(space_)); -} - -int -IVFBase::getDim() const { - return dim_; -} - -size_t -IVFBase::reclaimMemory() { - // Reclaim all unused memory exactly - return reclaimMemory_(true); -} - -size_t -IVFBase::reclaimMemory_(bool exact) { - auto stream = resources_->getDefaultStreamCurrentDevice(); - - size_t totalReclaimed = 0; - - for (int i = 0; i < deviceListData_.size(); ++i) { - auto& data = deviceListData_[i]; - totalReclaimed += data->reclaim(exact, stream); - - deviceListDataPointers_[i] = data->data(); - } - - for (int i = 0; i < deviceListIndices_.size(); ++i) { - auto& indices = deviceListIndices_[i]; - totalReclaimed += indices->reclaim(exact, stream); - - deviceListIndexPointers_[i] = indices->data(); - } - - // Update device info for all lists, since the base pointers may - // have changed - updateDeviceListInfo_(stream); - - return totalReclaimed; -} - -void -IVFBase::updateDeviceListInfo_(cudaStream_t stream) { - std::vector listIds(deviceListData_.size()); - for (int i = 0; i < deviceListData_.size(); ++i) { - listIds[i] = i; - } - - updateDeviceListInfo_(listIds, stream); -} - -void -IVFBase::updateDeviceListInfo_(const std::vector& listIds, - cudaStream_t stream) { - auto& mem = resources_->getMemoryManagerCurrentDevice(); - - HostTensor - hostListsToUpdate({(int) listIds.size()}); - HostTensor - hostNewListLength({(int) listIds.size()}); - HostTensor - hostNewDataPointers({(int) listIds.size()}); - HostTensor - hostNewIndexPointers({(int) listIds.size()}); - - for (int i = 0; i < listIds.size(); ++i) { - auto listId = listIds[i]; - auto& data = deviceListData_[listId]; - auto& indices = deviceListIndices_[listId]; - - hostListsToUpdate[i] = listId; - hostNewListLength[i] = data->size() / bytesPerVector_; - hostNewDataPointers[i] = data->data(); - hostNewIndexPointers[i] = indices->data(); - } - - // Copy the above update sets to the GPU - DeviceTensor listsToUpdate( - mem, hostListsToUpdate, stream); - DeviceTensor newListLength( - mem, hostNewListLength, stream); - DeviceTensor newDataPointers( - mem, hostNewDataPointers, stream); - DeviceTensor newIndexPointers( - mem, hostNewIndexPointers, stream); - - // Update all pointers to the lists on the device that may have - // changed - runUpdateListPointers(listsToUpdate, - newListLength, - newDataPointers, - newIndexPointers, - deviceListLengths_, - deviceListDataPointers_, - deviceListIndexPointers_, - stream); -} - -size_t -IVFBase::getNumLists() const { - return numLists_; -} - -int -IVFBase::getListLength(int listId) const { - FAISS_ASSERT(listId < deviceListLengths_.size()); - - return deviceListLengths_[listId]; -} - -std::vector -IVFBase::getListIndices(int listId) const { - FAISS_ASSERT(listId < numLists_); - - if (indicesOptions_ == INDICES_32_BIT) { - FAISS_ASSERT(listId < deviceListIndices_.size()); - - auto intInd = deviceListIndices_[listId]->copyToHost( - resources_->getDefaultStreamCurrentDevice()); - - std::vector out(intInd.size()); - for (size_t i = 0; i < intInd.size(); ++i) { - out[i] = (long) intInd[i]; - } - - return out; - } else if (indicesOptions_ == INDICES_64_BIT) { - FAISS_ASSERT(listId < deviceListIndices_.size()); - - return deviceListIndices_[listId]->copyToHost( - resources_->getDefaultStreamCurrentDevice()); - } else if (indicesOptions_ == INDICES_CPU) { - FAISS_ASSERT(listId < deviceListData_.size()); - FAISS_ASSERT(listId < listOffsetToUserIndex_.size()); - - auto& userIds = listOffsetToUserIndex_[listId]; - FAISS_ASSERT(userIds.size() == - deviceListData_[listId]->size() / bytesPerVector_); - - // this will return a copy - return userIds; - } else { - // unhandled indices type (includes INDICES_IVF) - FAISS_ASSERT(false); - return std::vector(); - } -} - -std::vector -IVFBase::getListVectors(int listId) const { - FAISS_ASSERT(listId < deviceListData_.size()); - auto& list = *deviceListData_[listId]; - auto stream = resources_->getDefaultStreamCurrentDevice(); - - return list.copyToHost(stream); -} - -void -IVFBase::copyIndicesFromCpu_(const long* indices, - const std::vector& list_length) { - FAISS_ASSERT_FMT(list_length.size() == this->getNumLists(), "Expect list size %zu but %zu received!", - this->getNumLists(), list_length.size()); - auto numVecs = std::accumulate(list_length.begin(), list_length.end(), 0); - - auto stream = resources_->getDefaultStreamCurrentDevice(); - int bytesPerRecord; - - if (indicesOptions_ == INDICES_32_BIT) { - std::vector indices32(numVecs); - for (size_t i = 0; i < numVecs; ++i) { - auto ind = indices[i]; - FAISS_ASSERT(ind <= (long) std::numeric_limits::max()); - indices32[i] = (int) ind; - } - - bytesPerRecord = sizeof(int); - - deviceIndices_->append((unsigned char*) indices32.data(), - numVecs * bytesPerRecord, - stream, - true); - } else if (indicesOptions_ == INDICES_64_BIT) { - bytesPerRecord = sizeof(long); - deviceIndices_->append((unsigned char*) indices, - numVecs * bytesPerRecord, - stream, - true); - } else if (indicesOptions_ == INDICES_CPU) { - FAISS_ASSERT(false); - size_t listId = 0; - auto curr_indices = indices; - for (auto& userIndices : listOffsetToUserIndex_) { - userIndices.insert(userIndices.begin(), curr_indices, curr_indices + list_length[listId]); - curr_indices += list_length[listId]; - listId++; - } - } else { - // indices are not stored - FAISS_ASSERT(indicesOptions_ == INDICES_IVF); - } - - size_t listId = 0; - size_t pos = 0; - size_t size = 0; - - thrust::host_vector hostPointers(deviceListData_.size(), nullptr); - for (auto& device_indice : deviceListIndices_) { - auto data = deviceIndices_->data() + pos; - size = list_length[listId] * bytesPerRecord; - device_indice->reset(data, size, size); - hostPointers[listId] = device_indice->data(); - pos += size; - ++ listId; - } - - deviceListIndexPointers_ = hostPointers; -} - -void -IVFBase::addIndicesFromCpu_(int listId, - const long* indices, - size_t numVecs) { - auto stream = resources_->getDefaultStreamCurrentDevice(); - - auto& listIndices = deviceListIndices_[listId]; - auto prevIndicesData = listIndices->data(); - - if (indicesOptions_ == INDICES_32_BIT) { - // Make sure that all indices are in bounds - std::vector indices32(numVecs); - for (size_t i = 0; i < numVecs; ++i) { - auto ind = indices[i]; - FAISS_ASSERT(ind <= (long) std::numeric_limits::max()); - indices32[i] = (int) ind; - } - - listIndices->append((unsigned char*) indices32.data(), - numVecs * sizeof(int), - stream, - true /* exact reserved size */); - } else if (indicesOptions_ == INDICES_64_BIT) { - listIndices->append((unsigned char*) indices, - numVecs * sizeof(long), - stream, - true /* exact reserved size */); - } else if (indicesOptions_ == INDICES_CPU) { - // indices are stored on the CPU - FAISS_ASSERT(listId < listOffsetToUserIndex_.size()); - - auto& userIndices = listOffsetToUserIndex_[listId]; - userIndices.insert(userIndices.begin(), indices, indices + numVecs); - } else { - // indices are not stored - FAISS_ASSERT(indicesOptions_ == INDICES_IVF); - } - - if (prevIndicesData != listIndices->data()) { - deviceListIndexPointers_[listId] = listIndices->data(); - } -} - -void -IVFBase::addTrainedDataFromCpu_(const uint8_t* trained, - size_t numData) { - auto stream = resources_->getDefaultStreamCurrentDevice(); - - deviceTrained_->append((unsigned char*)trained, - numData, - stream, - true); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/IVFBase.cuh b/core/src/index/thirdparty/faiss/gpu/impl/IVFBase.cuh deleted file mode 100644 index 987439269d..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/IVFBase.cuh +++ /dev/null @@ -1,151 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -class GpuResources; -struct FlatIndex; - -/// Base inverted list functionality for IVFFlat and IVFPQ -class IVFBase { - public: - IVFBase(GpuResources* resources, - faiss::MetricType metric, - float metricArg, - /// We do not own this reference - FlatIndex* quantizer, - int bytesPerVector, - IndicesOptions indicesOptions, - MemorySpace space); - - virtual ~IVFBase(); - - /// Reserve GPU memory in our inverted lists for this number of vectors - void reserveMemory(size_t numVecs); - - /// Clear out all inverted lists, but retain the coarse quantizer - /// and the product quantizer info - void reset(); - - /// Return the number of dimensions we are indexing - int getDim() const; - - /// After adding vectors, one can call this to reclaim device memory - /// to exactly the amount needed. Returns space reclaimed in bytes - size_t reclaimMemory(); - - /// Returns the number of inverted lists - size_t getNumLists() const; - - /// For debugging purposes, return the list length of a particular - /// list - int getListLength(int listId) const; - - /// Return the list indices of a particular list back to the CPU - std::vector getListIndices(int listId) const; - - DeviceVector* getTrainedData() { return deviceTrained_.get(); }; - - /// Return the encoded vectors of a particular list back to the CPU - std::vector getListVectors(int listId) const; - - protected: - /// Reclaim memory consumed on the device for our inverted lists - /// `exact` means we trim exactly to the memory needed - size_t reclaimMemory_(bool exact); - - /// Update all device-side list pointer and size information - void updateDeviceListInfo_(cudaStream_t stream); - - /// For a set of list IDs, update device-side list pointer and size - /// information - void updateDeviceListInfo_(const std::vector& listIds, - cudaStream_t stream); - - /// Shared function to copy indices from CPU to GPU - void addIndicesFromCpu_(int listId, - const long* indices, - size_t numVecs); - - void copyIndicesFromCpu_(const long* indices, - const std::vector& list_length); - - void addTrainedDataFromCpu_(const uint8_t* trained, size_t numData); - - protected: - /// Collection of GPU resources that we use - GpuResources* resources_; - - /// Metric type of the index - faiss::MetricType metric_; - - /// Metric arg - float metricArg_; - - /// Quantizer object - FlatIndex* quantizer_; - - /// Expected dimensionality of the vectors - const int dim_; - - /// Number of inverted lists we maintain - const int numLists_; - - /// Number of bytes per vector in the list - const int bytesPerVector_; - - /// How are user indices stored on the GPU? - const IndicesOptions indicesOptions_; - - /// What memory space our inverted list storage is in - const MemorySpace space_; - - /// Device representation of all inverted list data - /// id -> data - thrust::device_vector deviceListDataPointers_; - - /// Device representation of all inverted list index pointers - /// id -> data - thrust::device_vector deviceListIndexPointers_; - - /// Device representation of all inverted list lengths - /// id -> length - thrust::device_vector deviceListLengths_; - - /// Maximum list length seen - int maxListLength_; - - /// Device memory for each separate list, as managed by the host. - /// Device memory as stored in DeviceVector is stored as unique_ptr - /// since deviceListSummary_ pointers must remain valid despite - /// resizing of deviceLists_ - std::vector>> deviceListData_; - std::vector>> deviceListIndices_; - - std::unique_ptr> deviceData_; - std::unique_ptr> deviceIndices_; - std::unique_ptr> deviceTrained_; - - /// If we are storing indices on the CPU (indicesOptions_ is - /// INDICES_CPU), then this maintains a CPU-side map of what - /// (inverted list id, offset) maps to which user index - std::vector> listOffsetToUserIndex_; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/IVFFlat.cu b/core/src/index/thirdparty/faiss/gpu/impl/IVFFlat.cu deleted file mode 100644 index acebb5799e..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/IVFFlat.cu +++ /dev/null @@ -1,413 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -IVFFlat::IVFFlat(GpuResources* resources, - FlatIndex* quantizer, - faiss::MetricType metric, - float metricArg, - bool useResidual, - faiss::ScalarQuantizer* scalarQ, - IndicesOptions indicesOptions, - MemorySpace space) : - IVFBase(resources, - metric, - metricArg, - quantizer, - scalarQ ? scalarQ->code_size : - sizeof(float) * quantizer->getDim(), - indicesOptions, - space), - useResidual_(useResidual), - scalarQ_(scalarQ ? new GpuScalarQuantizer(*scalarQ) : nullptr) { -} - -IVFFlat::~IVFFlat() { -} - -void -IVFFlat::copyCodeVectorsFromCpu(const float* vecs, - const long* indices, - const std::vector& list_length) { - FAISS_ASSERT_FMT(list_length.size() == this->getNumLists(), "Expect list size %zu but %zu received!", - this->getNumLists(), list_length.size()); - int64_t numVecs = std::accumulate(list_length.begin(), list_length.end(), 0); - if (numVecs == 0) { - return; - } - - auto stream = resources_->getDefaultStreamCurrentDevice(); - - deviceListLengths_ = list_length; - - int64_t lengthInBytes = numVecs * bytesPerVector_; - - // We only have int32 length representations on the GPU per each - // list; the length is in sizeof(char) - FAISS_ASSERT(deviceData_->size() + lengthInBytes <= std::numeric_limits::max()); - - deviceData_->append((unsigned char*) vecs, - lengthInBytes, - stream, - true /* exact reserved size */); - copyIndicesFromCpu_(indices, list_length); - maxListLength_ = 0; - - size_t listId = 0; - size_t pos = 0; - size_t size = 0; - thrust::host_vector hostPointers(deviceListData_.size(), nullptr); - - for (auto& device_data : deviceListData_) { - auto data = deviceData_->data() + pos; - - size = list_length[listId] * bytesPerVector_; - - device_data->reset(data, size, size); - hostPointers[listId] = device_data->data(); - maxListLength_ = std::max(maxListLength_, (int)list_length[listId]); - pos += size; - ++ listId; - } - - deviceListDataPointers_ = hostPointers; - - // device_vector add is potentially happening on a different stream - // than our default stream - if (stream != 0) { - streamWait({stream}, {0}); - } -} - -void -IVFFlat::addCodeVectorsFromCpu(int listId, - const unsigned char* vecs, - const long* indices, - size_t numVecs) { - // This list must already exist - FAISS_ASSERT(listId < deviceListData_.size()); - auto stream = resources_->getDefaultStreamCurrentDevice(); - - // If there's nothing to add, then there's nothing we have to do - if (numVecs == 0) { - return; - } - - size_t lengthInBytes = numVecs * bytesPerVector_; - - auto& listData = deviceListData_[listId]; - auto prevData = listData->data(); - - // We only have int32 length representations on the GPU per each - // list; the length is in sizeof(char) - FAISS_ASSERT(listData->size() + lengthInBytes <= - (size_t) std::numeric_limits::max()); - - listData->append(vecs, - lengthInBytes, - stream, - true /* exact reserved size */); - - // Handle the indices as well - addIndicesFromCpu_(listId, indices, numVecs); - - // This list address may have changed due to vector resizing, but - // only bother updating it on the device if it has changed - if (prevData != listData->data()) { - deviceListDataPointers_[listId] = listData->data(); - } - - // And our size has changed too - int listLength = listData->size() / bytesPerVector_; - deviceListLengths_[listId] = listLength; - - // We update this as well, since the multi-pass algorithm uses it - maxListLength_ = std::max(maxListLength_, listLength); - - // device_vector add is potentially happening on a different stream - // than our default stream - if (stream != 0) { - streamWait({stream}, {0}); - } -} - -int -IVFFlat::classifyAndAddVectors(Tensor& vecs, - Tensor& indices) { - FAISS_ASSERT(vecs.getSize(0) == indices.getSize(0)); - FAISS_ASSERT(vecs.getSize(1) == dim_); - - auto& mem = resources_->getMemoryManagerCurrentDevice(); - auto stream = resources_->getDefaultStreamCurrentDevice(); - - // Number of valid vectors that we actually add; we return this - int numAdded = 0; - - DeviceTensor - listDistance2d(mem, {vecs.getSize(0), 1}, stream); - - DeviceTensor - listIds2d(mem, {vecs.getSize(0), 1}, stream); - auto listIds = listIds2d.view<1>({vecs.getSize(0)}); - - /* pseudo bitset */ - DeviceTensor bitset(mem, {0}, stream); - quantizer_->query(vecs, bitset, 1, metric_, metricArg_, - listDistance2d, listIds2d, false); - - // Calculate residuals for these vectors, if needed - DeviceTensor - residuals(mem, {vecs.getSize(0), dim_}, stream); - - if (useResidual_) { - quantizer_->computeResidual(vecs, listIds, residuals); - } - - // Copy the lists that we wish to append to back to the CPU - // FIXME: really this can be into pinned memory and a true async - // copy on a different stream; we can start the copy early, but it's - // tiny - HostTensor listIdsHost(listIds, stream); - - // Now we add the encoded vectors to the individual lists - // First, make sure that there is space available for adding the new - // encoded vectors and indices - - // list id -> # being added - std::unordered_map assignCounts; - - // vector id -> offset in list - // (we already have vector id -> list id in listIds) - HostTensor listOffsetHost({listIdsHost.getSize(0)}); - - for (int i = 0; i < listIds.getSize(0); ++i) { - int listId = listIdsHost[i]; - - // Add vector could be invalid (contains NaNs etc) - if (listId < 0) { - listOffsetHost[i] = -1; - continue; - } - - FAISS_ASSERT(listId < numLists_); - ++numAdded; - - int offset = deviceListData_[listId]->size() / bytesPerVector_; - - auto it = assignCounts.find(listId); - if (it != assignCounts.end()) { - offset += it->second; - it->second++; - } else { - assignCounts[listId] = 1; - } - - listOffsetHost[i] = offset; - } - - // If we didn't add anything (all invalid vectors), no need to - // continue - if (numAdded == 0) { - return 0; - } - - // We need to resize the data structures for the inverted lists on - // the GPUs, which means that they might need reallocation, which - // means that their base address may change. Figure out the new base - // addresses, and update those in a batch on the device - { - for (auto& counts : assignCounts) { - auto& data = deviceListData_[counts.first]; - data->resize(data->size() + counts.second * bytesPerVector_, - stream); - int newNumVecs = (int) (data->size() / bytesPerVector_); - - auto& indices = deviceListIndices_[counts.first]; - if ((indicesOptions_ == INDICES_32_BIT) || - (indicesOptions_ == INDICES_64_BIT)) { - size_t indexSize = - (indicesOptions_ == INDICES_32_BIT) ? sizeof(int) : sizeof(long); - - indices->resize(indices->size() + counts.second * indexSize, stream); - } else if (indicesOptions_ == INDICES_CPU) { - // indices are stored on the CPU side - FAISS_ASSERT(counts.first < listOffsetToUserIndex_.size()); - - auto& userIndices = listOffsetToUserIndex_[counts.first]; - userIndices.resize(newNumVecs); - } else { - // indices are not stored on the GPU or CPU side - FAISS_ASSERT(indicesOptions_ == INDICES_IVF); - } - - // This is used by the multi-pass query to decide how much scratch - // space to allocate for intermediate results - maxListLength_ = std::max(maxListLength_, newNumVecs); - } - - // Update all pointers to the lists on the device that may have - // changed - { - std::vector listIds(assignCounts.size()); - int i = 0; - for (auto& counts : assignCounts) { - listIds[i++] = counts.first; - } - - updateDeviceListInfo_(listIds, stream); - } - } - - // If we're maintaining the indices on the CPU side, update our - // map. We already resized our map above. - if (indicesOptions_ == INDICES_CPU) { - // We need to maintain the indices on the CPU side - HostTensor hostIndices(indices, stream); - - for (int i = 0; i < hostIndices.getSize(0); ++i) { - int listId = listIdsHost[i]; - - // Add vector could be invalid (contains NaNs etc) - if (listId < 0) { - continue; - } - - int offset = listOffsetHost[i]; - - FAISS_ASSERT(listId < listOffsetToUserIndex_.size()); - auto& userIndices = listOffsetToUserIndex_[listId]; - - FAISS_ASSERT(offset < userIndices.size()); - userIndices[offset] = hostIndices[i]; - } - } - - // We similarly need to actually append the new vectors - { - DeviceTensor listOffset(mem, listOffsetHost, stream); - - // Now, for each list to which a vector is being assigned, write it - runIVFFlatInvertedListAppend(listIds, - listOffset, - vecs, - indices, - useResidual_, - residuals, - scalarQ_.get(), - deviceListDataPointers_, - deviceListIndexPointers_, - indicesOptions_, - stream); - } - - return numAdded; -} - -void -IVFFlat::query(Tensor& queries, - Tensor& bitset, - int nprobe, - int k, - Tensor& outDistances, - Tensor& outIndices) { - auto& mem = resources_->getMemoryManagerCurrentDevice(); - auto stream = resources_->getDefaultStreamCurrentDevice(); - - // These are caught at a higher level - FAISS_ASSERT(nprobe <= GPU_MAX_SELECTION_K); - FAISS_ASSERT(k <= GPU_MAX_SELECTION_K); - nprobe = std::min(nprobe, quantizer_->getSize()); - - FAISS_ASSERT(queries.getSize(1) == dim_); - - FAISS_ASSERT(outDistances.getSize(0) == queries.getSize(0)); - FAISS_ASSERT(outIndices.getSize(0) == queries.getSize(0)); - - // Reserve space for the quantized information - DeviceTensor - coarseDistances(mem, {queries.getSize(0), nprobe}, stream); - DeviceTensor - coarseIndices(mem, {queries.getSize(0), nprobe}, stream); - - DeviceTensor coarseBitset(mem, {0}, stream); - // Find the `nprobe` closest lists; we can use int indices both - // internally and externally - quantizer_->query(queries, - coarseBitset, - nprobe, - metric_, - metricArg_, - coarseDistances, - coarseIndices, - false); - - DeviceTensor - residualBase(mem, {queries.getSize(0), nprobe, dim_}, stream); - - if (useResidual_) { - // Reconstruct vectors from the quantizer - quantizer_->reconstruct(coarseIndices, residualBase); - } - - runIVFFlatScan(queries, - coarseIndices, - bitset, - deviceListDataPointers_, - deviceListIndexPointers_, - indicesOptions_, - deviceListLengths_, - maxListLength_, - k, - metric_, - useResidual_, - residualBase, - scalarQ_.get(), - outDistances, - outIndices, - resources_); - - // If the GPU isn't storing indices (they are on the CPU side), we - // need to perform the re-mapping here - // FIXME: we might ultimately be calling this function with inputs - // from the CPU, these are unnecessary copies - if (indicesOptions_ == INDICES_CPU) { - HostTensor hostOutIndices(outIndices, stream); - - ivfOffsetToUserIndex(hostOutIndices.data(), - numLists_, - hostOutIndices.getSize(0), - hostOutIndices.getSize(1), - listOffsetToUserIndex_); - - // Copy back to GPU, since the input to this function is on the - // GPU - outIndices.copyFrom(hostOutIndices, stream); - } -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/IVFFlat.cuh b/core/src/index/thirdparty/faiss/gpu/impl/IVFFlat.cuh deleted file mode 100644 index 6b29419121..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/IVFFlat.cuh +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include - -namespace faiss { namespace gpu { - -class IVFFlat : public IVFBase { - public: - /// Construct from a quantizer that has elemen - IVFFlat(GpuResources* resources, - /// We do not own this reference - FlatIndex* quantizer, - faiss::MetricType metric, - float metricArg, - bool useResidual, - /// Optional ScalarQuantizer - faiss::ScalarQuantizer* scalarQ, - IndicesOptions indicesOptions, - MemorySpace space); - - ~IVFFlat() override; - - /// Add vectors to a specific list; the input data can be on the - /// host or on our current device - void addCodeVectorsFromCpu(int listId, - const unsigned char* vecs, - const long* indices, - size_t numVecs); - - void copyCodeVectorsFromCpu(const float* vecs, - const long* indices, - const std::vector& list_length); - - /// Adds the given vectors to this index. - /// The input data must be on our current device. - /// Returns the number of vectors successfully added. Vectors may - /// not be able to be added because they contain NaNs. - int classifyAndAddVectors(Tensor& vecs, - Tensor& indices); - - /// Find the approximate k nearest neigbors for `queries` against - /// our database - void query(Tensor& queries, - Tensor& bitset, - int nprobe, - int k, - Tensor& outDistances, - Tensor& outIndices); - - private: - /// Returns the size of our stored vectors, in bytes - size_t getVectorMemorySize() const; - - private: - /// Do we encode the residual from a coarse quantizer or not? - bool useResidual_; - - /// Scalar quantizer for encoded vectors, if any - std::unique_ptr scalarQ_; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/IVFFlatScan.cu b/core/src/index/thirdparty/faiss/gpu/impl/IVFFlatScan.cu deleted file mode 100644 index 2b76e0a09b..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/IVFFlatScan.cu +++ /dev/null @@ -1,542 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -namespace { - -/// Sort direction per each metric -inline bool metricToSortDirection(MetricType mt) { - switch (mt) { - case MetricType::METRIC_INNER_PRODUCT: - // highest - return true; - case MetricType::METRIC_L2: - // lowest - return false; - default: - // unhandled metric - FAISS_ASSERT(false); - return false; - } -} - -} - -// Number of warps we create per block of IVFFlatScan -constexpr int kIVFFlatScanWarps = 4; - -// Works for any dimension size -template -struct IVFFlatScan { - static __device__ void scan(float* query, - bool useResidual, - float* residualBaseSlice, - void* vecData, - const Codec& codec, - const Metric& metric, - int numVecs, - int dim, - float* distanceOut) { - // How many separate loading points are there for the decoder? - int limit = utils::divDown(dim, Codec::kDimPerIter); - - // Each warp handles a separate chunk of vectors - int warpId = threadIdx.x / kWarpSize; - // FIXME: why does getLaneId() not work when we write out below!?!?! - int laneId = threadIdx.x % kWarpSize; // getLaneId(); - - // Divide the set of vectors among the warps - int vecsPerWarp = utils::divUp(numVecs, kIVFFlatScanWarps); - - int vecStart = vecsPerWarp * warpId; - int vecEnd = min(vecsPerWarp * (warpId + 1), numVecs); - - // Walk the list of vectors for this warp - for (int vec = vecStart; vec < vecEnd; ++vec) { - Metric dist = metric.zero(); - - // Scan the dimensions availabe that have whole units for the decoder, - // as the decoder may handle more than one dimension at once (leaving the - // remainder to be handled separately) - for (int d = laneId; d < limit; d += kWarpSize) { - int realDim = d * Codec::kDimPerIter; - float vecVal[Codec::kDimPerIter]; - - // Decode the kDimPerIter dimensions - codec.decode(vecData, vec, d, vecVal); - -#pragma unroll - for (int j = 0; j < Codec::kDimPerIter; ++j) { - vecVal[j] += useResidual ? residualBaseSlice[realDim + j] : 0.0f; - } - -#pragma unroll - for (int j = 0; j < Codec::kDimPerIter; ++j) { - dist.handle(query[realDim + j], vecVal[j]); - } - } - - // Handle remainder by a single thread, if any - // Not needed if we decode 1 dim per time - if (Codec::kDimPerIter > 1) { - int realDim = limit * Codec::kDimPerIter; - - // Was there any remainder? - if (realDim < dim) { - // Let the first threads in the block sequentially perform it - int remainderDim = realDim + laneId; - - if (remainderDim < dim) { - float vecVal = - codec.decodePartial(vecData, vec, limit, laneId); - vecVal += useResidual ? residualBaseSlice[remainderDim] : 0.0f; - dist.handle(query[remainderDim], vecVal); - } - } - } - - // Reduce distance within warp - auto warpDist = warpReduceAllSum(dist.reduce()); - - if (laneId == 0) { - distanceOut[vec] = warpDist; - } - } - } -}; - -template -__global__ void -ivfFlatScan(Tensor queries, - bool useResidual, - Tensor residualBase, - Tensor listIds, - void** allListData, - int* listLengths, - Codec codec, - Metric metric, - Tensor prefixSumOffsets, - Tensor distance) { - extern __shared__ float smem[]; - - auto queryId = blockIdx.y; - auto probeId = blockIdx.x; - - // This is where we start writing out data - // We ensure that before the array (at offset -1), there is a 0 value - int outBase = *(prefixSumOffsets[queryId][probeId].data() - 1); - - auto listId = listIds[queryId][probeId]; - // Safety guard in case NaNs in input cause no list ID to be generated - if (listId == -1) { - return; - } - - auto query = queries[queryId].data(); - auto vecs = allListData[listId]; - auto numVecs = listLengths[listId]; - auto dim = queries.getSize(1); - auto distanceOut = distance[outBase].data(); - - auto residualBaseSlice = residualBase[queryId][probeId].data(); - - codec.setSmem(smem, dim); - - IVFFlatScan::scan(query, - useResidual, - residualBaseSlice, - vecs, - codec, - metric, - numVecs, - dim, - distanceOut); -} - -void -runIVFFlatScanTile(Tensor& queries, - Tensor& listIds, - Tensor& bitset, - thrust::device_vector& listData, - thrust::device_vector& listIndices, - IndicesOptions indicesOptions, - thrust::device_vector& listLengths, - Tensor& thrustMem, - Tensor& prefixSumOffsets, - Tensor& allDistances, - Tensor& heapDistances, - Tensor& heapIndices, - int k, - faiss::MetricType metricType, - bool useResidual, - Tensor& residualBase, - GpuScalarQuantizer* scalarQ, - Tensor& outDistances, - Tensor& outIndices, - cudaStream_t stream) { - int dim = queries.getSize(1); - - // Check the amount of shared memory per block available based on our type is - // sufficient - if (scalarQ && - (scalarQ->qtype == QuantizerType::QT_8bit || - scalarQ->qtype == QuantizerType::QT_4bit)) { - int maxDim = getMaxSharedMemPerBlockCurrentDevice() / - (sizeof(float) * 2); - - FAISS_THROW_IF_NOT_FMT(dim < maxDim, - "Insufficient shared memory available on the GPU " - "for QT_8bit or QT_4bit with %d dimensions; " - "maximum dimensions possible is %d", dim, maxDim); - } - - - // Calculate offset lengths, so we know where to write out - // intermediate results - runCalcListOffsets(listIds, listLengths, prefixSumOffsets, thrustMem, stream); - - auto grid = dim3(listIds.getSize(1), listIds.getSize(0)); - auto block = dim3(kWarpSize * kIVFFlatScanWarps); - -#define RUN_IVF_FLAT \ - do { \ - ivfFlatScan \ - <<>>( \ - queries, \ - useResidual, \ - residualBase, \ - listIds, \ - listData.data().get(), \ - listLengths.data().get(), \ - codec, \ - metric, \ - prefixSumOffsets, \ - allDistances); \ - } while (0) - -#define HANDLE_METRICS \ - do { \ - if (metricType == MetricType::METRIC_L2) { \ - L2Distance metric; RUN_IVF_FLAT; \ - } else { \ - IPDistance metric; RUN_IVF_FLAT; \ - } \ - } while (0) - - if (!scalarQ) { - CodecFloat codec(dim * sizeof(float)); - HANDLE_METRICS; - } else { - switch (scalarQ->qtype) { - case QuantizerType::QT_8bit: - { - // FIXME: investigate 32 bit load perf issues -// if (dim % 4 == 0) { - if (false) { - Codec<(int)QuantizerType::QT_8bit, 4> - codec(scalarQ->code_size, - scalarQ->gpuTrained.data(), - scalarQ->gpuTrained.data() + dim); - HANDLE_METRICS; - } else { - Codec<(int)QuantizerType::QT_8bit, 1> - codec(scalarQ->code_size, - scalarQ->gpuTrained.data(), - scalarQ->gpuTrained.data() + dim); - HANDLE_METRICS; - } - } - break; - case QuantizerType::QT_8bit_uniform: - { - // FIXME: investigate 32 bit load perf issues - if (false) { -// if (dim % 4 == 0) { - Codec<(int)QuantizerType::QT_8bit_uniform, 4> - codec(scalarQ->code_size, scalarQ->trained[0], scalarQ->trained[1]); - HANDLE_METRICS; - } else { - Codec<(int)QuantizerType::QT_8bit_uniform, 1> - codec(scalarQ->code_size, scalarQ->trained[0], scalarQ->trained[1]); - HANDLE_METRICS; - } - } - break; - case QuantizerType::QT_fp16: - { - if (false) { - // FIXME: investigate 32 bit load perf issues -// if (dim % 2 == 0) { - Codec<(int)QuantizerType::QT_fp16, 2> - codec(scalarQ->code_size); - HANDLE_METRICS; - } else { - Codec<(int)QuantizerType::QT_fp16, 1> - codec(scalarQ->code_size); - HANDLE_METRICS; - } - } - break; - case QuantizerType::QT_8bit_direct: - { - Codec<(int)QuantizerType::QT_8bit_direct, 1> - codec(scalarQ->code_size); - HANDLE_METRICS; - } - break; - case QuantizerType::QT_4bit: - { - Codec<(int)QuantizerType::QT_4bit, 1> - codec(scalarQ->code_size, - scalarQ->gpuTrained.data(), - scalarQ->gpuTrained.data() + dim); - HANDLE_METRICS; - } - break; - case QuantizerType::QT_4bit_uniform: - { - Codec<(int)QuantizerType::QT_4bit_uniform, 1> - codec(scalarQ->code_size, scalarQ->trained[0], scalarQ->trained[1]); - HANDLE_METRICS; - } - break; - default: - // unimplemented, should be handled at a higher level - FAISS_ASSERT(false); - } - } - - CUDA_TEST_ERROR(); - -#undef HANDLE_METRICS -#undef RUN_IVF_FLAT - - // k-select the output in chunks, to increase parallelism - runPass1SelectLists(listIndices, - indicesOptions, - prefixSumOffsets, - listIds, - bitset, - allDistances, - listIds.getSize(1), - k, - metricToSortDirection(metricType), - heapDistances, - heapIndices, - stream); - - // k-select final output - auto flatHeapDistances = heapDistances.downcastInner<2>(); - auto flatHeapIndices = heapIndices.downcastInner<2>(); - - runPass2SelectLists(flatHeapDistances, - flatHeapIndices, - listIndices, - indicesOptions, - prefixSumOffsets, - listIds, - k, - metricToSortDirection(metricType), - outDistances, - outIndices, - stream); -} - -void -runIVFFlatScan(Tensor& queries, - Tensor& listIds, - Tensor& bitset, - thrust::device_vector& listData, - thrust::device_vector& listIndices, - IndicesOptions indicesOptions, - thrust::device_vector& listLengths, - int maxListLength, - int k, - faiss::MetricType metric, - bool useResidual, - Tensor& residualBase, - GpuScalarQuantizer* scalarQ, - // output - Tensor& outDistances, - // output - Tensor& outIndices, - GpuResources* res) { - constexpr int kMinQueryTileSize = 8; - constexpr int kMaxQueryTileSize = 128; - constexpr int kThrustMemSize = 16384; - - int nprobe = listIds.getSize(1); - - auto& mem = res->getMemoryManagerCurrentDevice(); - auto stream = res->getDefaultStreamCurrentDevice(); - - // Make a reservation for Thrust to do its dirty work (global memory - // cross-block reduction space); hopefully this is large enough. - DeviceTensor thrustMem1( - mem, {kThrustMemSize}, stream); - DeviceTensor thrustMem2( - mem, {kThrustMemSize}, stream); - DeviceTensor* thrustMem[2] = - {&thrustMem1, &thrustMem2}; - - // How much temporary storage is available? - // If possible, we'd like to fit within the space available. - size_t sizeAvailable = mem.getSizeAvailable(); - - // We run two passes of heap selection - // This is the size of the first-level heap passes - constexpr int kNProbeSplit = 8; - int pass2Chunks = std::min(nprobe, kNProbeSplit); - - size_t sizeForFirstSelectPass = - pass2Chunks * k * (sizeof(float) + sizeof(int)); - - // How much temporary storage we need per each query - size_t sizePerQuery = - 2 * // # streams - ((nprobe * sizeof(int) + sizeof(int)) + // prefixSumOffsets - nprobe * maxListLength * sizeof(float) + // allDistances - sizeForFirstSelectPass); - - int queryTileSize = (int) (sizeAvailable / sizePerQuery); - - if (queryTileSize < kMinQueryTileSize) { - queryTileSize = kMinQueryTileSize; - } else if (queryTileSize > kMaxQueryTileSize) { - queryTileSize = kMaxQueryTileSize; - } - - // FIXME: we should adjust queryTileSize to deal with this, since - // indexing is in int32 - FAISS_ASSERT(queryTileSize * nprobe * maxListLength < - std::numeric_limits::max()); - - // Temporary memory buffers - // Make sure there is space prior to the start which will be 0, and - // will handle the boundary condition without branches - DeviceTensor prefixSumOffsetSpace1( - mem, {queryTileSize * nprobe + 1}, stream); - DeviceTensor prefixSumOffsetSpace2( - mem, {queryTileSize * nprobe + 1}, stream); - - DeviceTensor prefixSumOffsets1( - prefixSumOffsetSpace1[1].data(), - {queryTileSize, nprobe}); - DeviceTensor prefixSumOffsets2( - prefixSumOffsetSpace2[1].data(), - {queryTileSize, nprobe}); - DeviceTensor* prefixSumOffsets[2] = - {&prefixSumOffsets1, &prefixSumOffsets2}; - - // Make sure the element before prefixSumOffsets is 0, since we - // depend upon simple, boundary-less indexing to get proper results - CUDA_VERIFY(cudaMemsetAsync(prefixSumOffsetSpace1.data(), - 0, - sizeof(int), - stream)); - CUDA_VERIFY(cudaMemsetAsync(prefixSumOffsetSpace2.data(), - 0, - sizeof(int), - stream)); - - DeviceTensor allDistances1( - mem, {queryTileSize * nprobe * maxListLength}, stream); - DeviceTensor allDistances2( - mem, {queryTileSize * nprobe * maxListLength}, stream); - DeviceTensor* allDistances[2] = - {&allDistances1, &allDistances2}; - - DeviceTensor heapDistances1( - mem, {queryTileSize, pass2Chunks, k}, stream); - DeviceTensor heapDistances2( - mem, {queryTileSize, pass2Chunks, k}, stream); - DeviceTensor* heapDistances[2] = - {&heapDistances1, &heapDistances2}; - - DeviceTensor heapIndices1( - mem, {queryTileSize, pass2Chunks, k}, stream); - DeviceTensor heapIndices2( - mem, {queryTileSize, pass2Chunks, k}, stream); - DeviceTensor* heapIndices[2] = - {&heapIndices1, &heapIndices2}; - - auto streams = res->getAlternateStreamsCurrentDevice(); - streamWait(streams, {stream}); - - int curStream = 0; - - for (int query = 0; query < queries.getSize(0); query += queryTileSize) { - int numQueriesInTile = - std::min(queryTileSize, queries.getSize(0) - query); - - auto prefixSumOffsetsView = - prefixSumOffsets[curStream]->narrowOutermost(0, numQueriesInTile); - - auto listIdsView = - listIds.narrowOutermost(query, numQueriesInTile); - auto queryView = - queries.narrowOutermost(query, numQueriesInTile); - auto residualBaseView = - residualBase.narrowOutermost(query, numQueriesInTile); - - auto heapDistancesView = - heapDistances[curStream]->narrowOutermost(0, numQueriesInTile); - auto heapIndicesView = - heapIndices[curStream]->narrowOutermost(0, numQueriesInTile); - - auto outDistanceView = - outDistances.narrowOutermost(query, numQueriesInTile); - auto outIndicesView = - outIndices.narrowOutermost(query, numQueriesInTile); - - runIVFFlatScanTile(queryView, - listIdsView, - bitset, - listData, - listIndices, - indicesOptions, - listLengths, - *thrustMem[curStream], - prefixSumOffsetsView, - *allDistances[curStream], - heapDistancesView, - heapIndicesView, - k, - metric, - useResidual, - residualBaseView, - scalarQ, - outDistanceView, - outIndicesView, - streams[curStream]); - - curStream = (curStream + 1) % 2; - } - - streamWait({stream}, streams); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/IVFFlatScan.cuh b/core/src/index/thirdparty/faiss/gpu/impl/IVFFlatScan.cuh deleted file mode 100644 index 2b67cba06f..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/IVFFlatScan.cuh +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -class GpuResources; - -void runIVFFlatScan(Tensor& queries, - Tensor& listIds, - Tensor& bitset, - thrust::device_vector& listData, - thrust::device_vector& listIndices, - IndicesOptions indicesOptions, - thrust::device_vector& listLengths, - int maxListLength, - int k, - faiss::MetricType metric, - bool useResidual, - Tensor& residualBase, - GpuScalarQuantizer* scalarQ, - // output - Tensor& outDistances, - // output - Tensor& outIndices, - GpuResources* res); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/IVFPQ.cu b/core/src/index/thirdparty/faiss/gpu/impl/IVFPQ.cu deleted file mode 100644 index 48254c1f5b..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/IVFPQ.cu +++ /dev/null @@ -1,811 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -IVFPQ::IVFPQ(GpuResources* resources, - faiss::MetricType metric, - float metricArg, - FlatIndex* quantizer, - int numSubQuantizers, - int bitsPerSubQuantizer, - float* pqCentroidData, - IndicesOptions indicesOptions, - bool useFloat16LookupTables, - MemorySpace space) : - IVFBase(resources, - metric, - metricArg, - quantizer, - numSubQuantizers, - indicesOptions, - space), - numSubQuantizers_(numSubQuantizers), - bitsPerSubQuantizer_(bitsPerSubQuantizer), - numSubQuantizerCodes_(utils::pow2(bitsPerSubQuantizer_)), - dimPerSubQuantizer_(dim_ / numSubQuantizers), - precomputedCodes_(false), - useFloat16LookupTables_(useFloat16LookupTables) { - FAISS_ASSERT(pqCentroidData); - - FAISS_ASSERT(bitsPerSubQuantizer_ <= 8); - FAISS_ASSERT(dim_ % numSubQuantizers_ == 0); - FAISS_ASSERT(isSupportedPQCodeLength(bytesPerVector_)); - -#ifndef FAISS_USE_FLOAT16 - FAISS_ASSERT(!useFloat16LookupTables_); -#endif - - setPQCentroids_(pqCentroidData); -} - -IVFPQ::~IVFPQ() { -} - - -bool -IVFPQ::isSupportedPQCodeLength(int size) { - switch (size) { - case 1: - case 2: - case 3: - case 4: - case 8: - case 12: - case 16: - case 20: - case 24: - case 28: - case 32: - case 40: - case 48: - case 56: // only supported with float16 - case 64: // only supported with float16 - case 96: // only supported with float16 - return true; - default: - return false; - } -} - -bool -IVFPQ::isSupportedNoPrecomputedSubDimSize(int dims) { - return faiss::gpu::isSupportedNoPrecomputedSubDimSize(dims); -} - -void -IVFPQ::setPrecomputedCodes(bool enable) { - if (enable && metric_ == MetricType::METRIC_INNER_PRODUCT) { - FAISS_THROW_MSG("Precomputed codes are not needed for GpuIndexIVFPQ " - "with METRIC_INNER_PRODUCT"); - } - - if (precomputedCodes_ != enable) { - precomputedCodes_ = enable; - - if (precomputedCodes_) { - precomputeCodes_(); - } else { - // Clear out old precomputed code data - precomputedCode_ = std::move(DeviceTensor()); -#ifdef FAISS_USE_FLOAT16 - precomputedCodeHalf_ = std::move(DeviceTensor()); -#endif - } - } -} - -int -IVFPQ::classifyAndAddVectors(Tensor& vecs, - Tensor& indices) { - FAISS_ASSERT(vecs.getSize(0) == indices.getSize(0)); - FAISS_ASSERT(vecs.getSize(1) == dim_); - - auto& mem = resources_->getMemoryManagerCurrentDevice(); - auto stream = resources_->getDefaultStreamCurrentDevice(); - - // Number of valid vectors that we actually add; we return this - int numAdded = 0; - - // We don't actually need this - DeviceTensor listDistance(mem, {vecs.getSize(0), 1}, stream); - // We use this - DeviceTensor listIds2d(mem, {vecs.getSize(0), 1}, stream); - auto listIds = listIds2d.view<1>({vecs.getSize(0)}); - - /* pseudo bitset */ - DeviceTensor bitset(mem, {0}, stream); - quantizer_->query(vecs, - bitset, - 1, - metric_, - metricArg_, - listDistance, - listIds2d, - false); - - // Copy the lists that we wish to append to back to the CPU - // FIXME: really this can be into pinned memory and a true async - // copy on a different stream; we can start the copy early, but it's - // tiny - HostTensor listIdsHost(listIds, stream); - - // Calculate the residual for each closest centroid - DeviceTensor residuals( - mem, {vecs.getSize(0), vecs.getSize(1)}, stream); - -#ifdef FAISS_USE_FLOAT16 - if (quantizer_->getUseFloat16()) { - auto& coarseCentroids = quantizer_->getVectorsFloat16Ref(); - runCalcResidual(vecs, coarseCentroids, listIds, residuals, stream); - } else { - auto& coarseCentroids = quantizer_->getVectorsFloat32Ref(); - runCalcResidual(vecs, coarseCentroids, listIds, residuals, stream); - } -#else - auto& coarseCentroids = quantizer_->getVectorsFloat32Ref(); - runCalcResidual(vecs, coarseCentroids, listIds, residuals, stream); -#endif - - // Residuals are in the form - // (vec x numSubQuantizer x dimPerSubQuantizer) - // transpose to - // (numSubQuantizer x vec x dimPerSubQuantizer) - auto residualsView = residuals.view<3>( - {residuals.getSize(0), numSubQuantizers_, dimPerSubQuantizer_}); - - DeviceTensor residualsTranspose( - mem, - {numSubQuantizers_, residuals.getSize(0), dimPerSubQuantizer_}, - stream); - - runTransposeAny(residualsView, 0, 1, residualsTranspose, stream); - - // Get the product quantizer centroids in the form - // (numSubQuantizer x numSubQuantizerCodes x dimPerSubQuantizer) - // which is pqCentroidsMiddleCode_ - - // We now have a batch operation to find the top-1 distances: - // batch size: numSubQuantizer - // centroids: (numSubQuantizerCodes x dimPerSubQuantizer) - // residuals: (vec x dimPerSubQuantizer) - // => (numSubQuantizer x vec x 1) - - DeviceTensor closestSubQDistance( - mem, {numSubQuantizers_, residuals.getSize(0), 1}, stream); - DeviceTensor closestSubQIndex( - mem, {numSubQuantizers_, residuals.getSize(0), 1}, stream); - - for (int subQ = 0; subQ < numSubQuantizers_; ++subQ) { - auto closestSubQDistanceView = closestSubQDistance[subQ].view(); - auto closestSubQIndexView = closestSubQIndex[subQ].view(); - - auto pqCentroidsMiddleCodeView = pqCentroidsMiddleCode_[subQ].view(); - auto residualsTransposeView = residualsTranspose[subQ].view(); - - runL2Distance(resources_, - pqCentroidsMiddleCodeView, - true, // pqCentroidsMiddleCodeView is row major - nullptr, // no precomputed norms - residualsTransposeView, - true, // residualsTransposeView is row major - bitset, - 1, - closestSubQDistanceView, - closestSubQIndexView, - // We don't care about distances - true); - } - - // Now, we have the nearest sub-q centroid for each slice of the - // residual vector. - auto closestSubQIndexView = closestSubQIndex.view<2>( - {numSubQuantizers_, residuals.getSize(0)}); - - // Transpose this for easy use - DeviceTensor encodings( - mem, {residuals.getSize(0), numSubQuantizers_}, stream); - - runTransposeAny(closestSubQIndexView, 0, 1, encodings, stream); - - // Now we add the encoded vectors to the individual lists - // First, make sure that there is space available for adding the new - // encoded vectors and indices - - // list id -> # being added - std::unordered_map assignCounts; - - // vector id -> offset in list - // (we already have vector id -> list id in listIds) - HostTensor listOffsetHost({listIdsHost.getSize(0)}); - - for (int i = 0; i < listIdsHost.getSize(0); ++i) { - int listId = listIdsHost[i]; - - // Add vector could be invalid (contains NaNs etc) - if (listId < 0) { - listOffsetHost[i] = -1; - continue; - } - - FAISS_ASSERT(listId < numLists_); - ++numAdded; - - int offset = deviceListData_[listId]->size() / bytesPerVector_; - - auto it = assignCounts.find(listId); - if (it != assignCounts.end()) { - offset += it->second; - it->second++; - } else { - assignCounts[listId] = 1; - } - - listOffsetHost[i] = offset; - } - - // If we didn't add anything (all invalid vectors), no need to - // continue - if (numAdded == 0) { - return 0; - } - - // We need to resize the data structures for the inverted lists on - // the GPUs, which means that they might need reallocation, which - // means that their base address may change. Figure out the new base - // addresses, and update those in a batch on the device - { - // Resize all of the lists that we are appending to - for (auto& counts : assignCounts) { - auto& codes = deviceListData_[counts.first]; - codes->resize(codes->size() + counts.second * bytesPerVector_, - stream); - int newNumVecs = (int) (codes->size() / bytesPerVector_); - - auto& indices = deviceListIndices_[counts.first]; - if ((indicesOptions_ == INDICES_32_BIT) || - (indicesOptions_ == INDICES_64_BIT)) { - size_t indexSize = - (indicesOptions_ == INDICES_32_BIT) ? sizeof(int) : sizeof(long); - - indices->resize(indices->size() + counts.second * indexSize, stream); - } else if (indicesOptions_ == INDICES_CPU) { - // indices are stored on the CPU side - FAISS_ASSERT(counts.first < listOffsetToUserIndex_.size()); - - auto& userIndices = listOffsetToUserIndex_[counts.first]; - userIndices.resize(newNumVecs); - } else { - // indices are not stored on the GPU or CPU side - FAISS_ASSERT(indicesOptions_ == INDICES_IVF); - } - - // This is used by the multi-pass query to decide how much scratch - // space to allocate for intermediate results - maxListLength_ = std::max(maxListLength_, newNumVecs); - } - - // Update all pointers and sizes on the device for lists that we - // appended to - { - std::vector listIds(assignCounts.size()); - int i = 0; - for (auto& counts : assignCounts) { - listIds[i++] = counts.first; - } - - updateDeviceListInfo_(listIds, stream); - } - } - - // If we're maintaining the indices on the CPU side, update our - // map. We already resized our map above. - if (indicesOptions_ == INDICES_CPU) { - // We need to maintain the indices on the CPU side - HostTensor hostIndices(indices, stream); - - for (int i = 0; i < hostIndices.getSize(0); ++i) { - int listId = listIdsHost[i]; - - // Add vector could be invalid (contains NaNs etc) - if (listId < 0) { - continue; - } - - int offset = listOffsetHost[i]; - - FAISS_ASSERT(listId < listOffsetToUserIndex_.size()); - auto& userIndices = listOffsetToUserIndex_[listId]; - - FAISS_ASSERT(offset < userIndices.size()); - userIndices[offset] = hostIndices[i]; - } - } - - // We similarly need to actually append the new encoded vectors - { - DeviceTensor listOffset(mem, listOffsetHost, stream); - - // This kernel will handle appending each encoded vector + index to - // the appropriate list - runIVFPQInvertedListAppend(listIds, - listOffset, - encodings, - indices, - deviceListDataPointers_, - deviceListIndexPointers_, - indicesOptions_, - stream); - } - - return numAdded; -} - -void -IVFPQ::addCodeVectorsFromCpu(int listId, - const void* codes, - const long* indices, - size_t numVecs) { - // This list must already exist - FAISS_ASSERT(listId < deviceListData_.size()); - auto stream = resources_->getDefaultStreamCurrentDevice(); - - // If there's nothing to add, then there's nothing we have to do - if (numVecs == 0) { - return; - } - - size_t lengthInBytes = numVecs * bytesPerVector_; - - auto& listCodes = deviceListData_[listId]; - auto prevCodeData = listCodes->data(); - - // We only have int32 length representations on the GPU per each - // list; the length is in sizeof(char) - FAISS_ASSERT(listCodes->size() % bytesPerVector_ == 0); - FAISS_ASSERT(listCodes->size() + lengthInBytes <= - (size_t) std::numeric_limits::max()); - - listCodes->append((unsigned char*) codes, - lengthInBytes, - stream, - true /* exact reserved size */); - - // Handle the indices as well - addIndicesFromCpu_(listId, indices, numVecs); - - // This list address may have changed due to vector resizing, but - // only bother updating it on the device if it has changed - if (prevCodeData != listCodes->data()) { - deviceListDataPointers_[listId] = listCodes->data(); - } - - // And our size has changed too - int listLength = listCodes->size() / bytesPerVector_; - deviceListLengths_[listId] = listLength; - - // We update this as well, since the multi-pass algorithm uses it - maxListLength_ = std::max(maxListLength_, listLength); - - // device_vector add is potentially happening on a different stream - // than our default stream - if (resources_->getDefaultStreamCurrentDevice() != 0) { - streamWait({stream}, {0}); - } -} - -void -IVFPQ::setPQCentroids_(float* data) { - size_t pqSize = - numSubQuantizers_ * numSubQuantizerCodes_ * dimPerSubQuantizer_; - - // Make sure the data is on the host - // FIXME: why are we doing this? - thrust::host_vector hostMemory; - hostMemory.insert(hostMemory.end(), data, data + pqSize); - - HostTensor pqHost( - hostMemory.data(), - {numSubQuantizers_, numSubQuantizerCodes_, dimPerSubQuantizer_}); - DeviceTensor pqDevice( - pqHost, - resources_->getDefaultStreamCurrentDevice()); - - DeviceTensor pqDeviceTranspose( - {numSubQuantizers_, dimPerSubQuantizer_, numSubQuantizerCodes_}); - runTransposeAny(pqDevice, 1, 2, pqDeviceTranspose, - resources_->getDefaultStreamCurrentDevice()); - - pqCentroidsInnermostCode_ = std::move(pqDeviceTranspose); - - // Also maintain the PQ centroids in the form - // (sub q)(code id)(sub dim) - DeviceTensor pqCentroidsMiddleCode( - {numSubQuantizers_, numSubQuantizerCodes_, dimPerSubQuantizer_}); - runTransposeAny(pqCentroidsInnermostCode_, 1, 2, pqCentroidsMiddleCode, - resources_->getDefaultStreamCurrentDevice()); - - pqCentroidsMiddleCode_ = std::move(pqCentroidsMiddleCode); -} - -template -void -IVFPQ::precomputeCodesT_() { - FAISS_ASSERT(metric_ == MetricType::METRIC_L2); - - // - // d = || x - y_C ||^2 + || y_R ||^2 + 2 * (y_C|y_R) - 2 * (x|y_R) - // --------------- --------------------------- ------- - // term 1 term 2 term 3 - // - - // Terms 1 and 3 are available only at query time. We compute term 2 - // here. - - // Compute ||y_R||^2 by treating - // (sub q)(code id)(sub dim) as (sub q * code id)(sub dim) - auto pqCentroidsMiddleCodeView = - pqCentroidsMiddleCode_.view<2>( - {numSubQuantizers_ * numSubQuantizerCodes_, dimPerSubQuantizer_}); - DeviceTensor subQuantizerNorms( - {numSubQuantizers_ * numSubQuantizerCodes_}); - - runL2Norm(pqCentroidsMiddleCodeView, true, - subQuantizerNorms, true, - resources_->getDefaultStreamCurrentDevice()); - - // Compute 2 * (y_C|y_R) via batch matrix multiplication - // batch size (sub q) x {(centroid id)(sub dim) x (code id)(sub dim)'} - // => (sub q) x {(centroid id)(code id)} - // => (sub q)(centroid id)(code id) - - // View (centroid id)(dim) as - // (centroid id)(sub q)(dim) - // Transpose (centroid id)(sub q)(sub dim) to - // (sub q)(centroid id)(sub dim) - auto& coarseCentroids = quantizer_->template getVectorsRef(); - auto centroidView = coarseCentroids.template view<3>( - {coarseCentroids.getSize(0), numSubQuantizers_, dimPerSubQuantizer_}); - DeviceTensor centroidsTransposed( - {numSubQuantizers_, coarseCentroids.getSize(0), dimPerSubQuantizer_}); - - runTransposeAny(centroidView, 0, 1, centroidsTransposed, - resources_->getDefaultStreamCurrentDevice()); - - DeviceTensor coarsePQProduct( - {numSubQuantizers_, coarseCentroids.getSize(0), numSubQuantizerCodes_}); - - runIteratedMatrixMult(coarsePQProduct, false, - centroidsTransposed, false, - pqCentroidsMiddleCode_, true, - 2.0f, 0.0f, - resources_->getBlasHandleCurrentDevice(), - resources_->getDefaultStreamCurrentDevice()); - - // Transpose (sub q)(centroid id)(code id) to - // (centroid id)(sub q)(code id) - DeviceTensor coarsePQProductTransposed( - {coarseCentroids.getSize(0), numSubQuantizers_, numSubQuantizerCodes_}); - runTransposeAny(coarsePQProduct, 0, 1, coarsePQProductTransposed, - resources_->getDefaultStreamCurrentDevice()); - - // View (centroid id)(sub q)(code id) as - // (centroid id)(sub q * code id) - auto coarsePQProductTransposedView = coarsePQProductTransposed.view<2>( - {coarseCentroids.getSize(0), numSubQuantizers_ * numSubQuantizerCodes_}); - - // Sum || y_R ||^2 + 2 * (y_C|y_R) - // i.e., add norms (sub q * code id) - // along columns of inner product (centroid id)(sub q * code id) - runSumAlongColumns(subQuantizerNorms, coarsePQProductTransposedView, - resources_->getDefaultStreamCurrentDevice()); - - // We added into the view, so `coarsePQProductTransposed` is now our - // precomputed term 2. -#ifdef FAISS_USE_FLOAT16 - if (useFloat16LookupTables_) { - precomputedCodeHalf_ = - convertTensor(resources_, - resources_->getDefaultStreamCurrentDevice(), - coarsePQProductTransposed); - } else { - precomputedCode_ = std::move(coarsePQProductTransposed); - } -#else - precomputedCode_ = std::move(coarsePQProductTransposed); -#endif - -} - -void -IVFPQ::precomputeCodes_() { -#ifdef FAISS_USE_FLOAT16 - if (quantizer_->getUseFloat16()) { - precomputeCodesT_(); - } else { - precomputeCodesT_(); - } -#else - precomputeCodesT_(); -#endif -} - -void -IVFPQ::query(Tensor& queries, - Tensor& bitset, - int nprobe, - int k, - Tensor& outDistances, - Tensor& outIndices) { - // These are caught at a higher level - FAISS_ASSERT(nprobe <= GPU_MAX_SELECTION_K); - FAISS_ASSERT(k <= GPU_MAX_SELECTION_K); - - auto& mem = resources_->getMemoryManagerCurrentDevice(); - auto stream = resources_->getDefaultStreamCurrentDevice(); - nprobe = std::min(nprobe, quantizer_->getSize()); - - FAISS_ASSERT(queries.getSize(1) == dim_); - FAISS_ASSERT(outDistances.getSize(0) == queries.getSize(0)); - FAISS_ASSERT(outIndices.getSize(0) == queries.getSize(0)); - - // Reserve space for the closest coarse centroids - DeviceTensor - coarseDistances(mem, {queries.getSize(0), nprobe}, stream); - DeviceTensor - coarseIndices(mem, {queries.getSize(0), nprobe}, stream); - - DeviceTensor coarseBitset(mem, {0}, stream); - // Find the `nprobe` closest coarse centroids; we can use int - // indices both internally and externally - quantizer_->query(queries, - coarseBitset, - nprobe, - metric_, - metricArg_, - coarseDistances, - coarseIndices, - true); - - if (precomputedCodes_) { - FAISS_ASSERT(metric_ == MetricType::METRIC_L2); - - runPQPrecomputedCodes_(queries, - bitset, - coarseDistances, - coarseIndices, - k, - outDistances, - outIndices); - } else { - runPQNoPrecomputedCodes_(queries, - bitset, - coarseDistances, - coarseIndices, - k, - outDistances, - outIndices); - } - - // If the GPU isn't storing indices (they are on the CPU side), we - // need to perform the re-mapping here - // FIXME: we might ultimately be calling this function with inputs - // from the CPU, these are unnecessary copies - if (indicesOptions_ == INDICES_CPU) { - HostTensor hostOutIndices(outIndices, stream); - - ivfOffsetToUserIndex(hostOutIndices.data(), - numLists_, - hostOutIndices.getSize(0), - hostOutIndices.getSize(1), - listOffsetToUserIndex_); - - // Copy back to GPU, since the input to this function is on the - // GPU - outIndices.copyFrom(hostOutIndices, stream); - } -} - -std::vector -IVFPQ::getListCodes(int listId) const { - FAISS_ASSERT(listId < deviceListData_.size()); - - return deviceListData_[listId]->copyToHost( - resources_->getDefaultStreamCurrentDevice()); -} - -Tensor -IVFPQ::getPQCentroids() { - return pqCentroidsMiddleCode_; -} - -void -IVFPQ::runPQPrecomputedCodes_( - Tensor& queries, - Tensor& bitset, - DeviceTensor& coarseDistances, - DeviceTensor& coarseIndices, - int k, - Tensor& outDistances, - Tensor& outIndices) { - FAISS_ASSERT(metric_ == MetricType::METRIC_L2); - - auto& mem = resources_->getMemoryManagerCurrentDevice(); - auto stream = resources_->getDefaultStreamCurrentDevice(); - - // Compute precomputed code term 3, - 2 * (x|y_R) - // This is done via batch MM - // {sub q} x {(query id)(sub dim) * (code id)(sub dim)'} => - // {sub q} x {(query id)(code id)} - DeviceTensor term3Transposed( - mem, - {queries.getSize(0), numSubQuantizers_, numSubQuantizerCodes_}, - stream); - - // These allocations within are only temporary, so release them when - // we're done to maximize free space - { - auto querySubQuantizerView = queries.view<3>( - {queries.getSize(0), numSubQuantizers_, dimPerSubQuantizer_}); - DeviceTensor queriesTransposed( - mem, - {numSubQuantizers_, queries.getSize(0), dimPerSubQuantizer_}, - stream); - runTransposeAny(querySubQuantizerView, 0, 1, queriesTransposed, stream); - - DeviceTensor term3( - mem, - {numSubQuantizers_, queries.getSize(0), numSubQuantizerCodes_}, - stream); - - runIteratedMatrixMult(term3, false, - queriesTransposed, false, - pqCentroidsMiddleCode_, true, - -2.0f, 0.0f, - resources_->getBlasHandleCurrentDevice(), - stream); - - runTransposeAny(term3, 0, 1, term3Transposed, stream); - } - - NoTypeTensor<3, true> term2; - NoTypeTensor<3, true> term3; -#ifdef FAISS_USE_FLOAT16 - DeviceTensor term3Half; - - if (useFloat16LookupTables_) { - term3Half = - convertTensor(resources_, stream, term3Transposed); - - term2 = NoTypeTensor<3, true>(precomputedCodeHalf_); - term3 = NoTypeTensor<3, true>(term3Half); - } -#endif - - if (!useFloat16LookupTables_) { - term2 = NoTypeTensor<3, true>(precomputedCode_); - term3 = NoTypeTensor<3, true>(term3Transposed); - } - - runPQScanMultiPassPrecomputed(queries, - coarseDistances, // term 1 - term2, // term 2 - term3, // term 3 - coarseIndices, - bitset, - useFloat16LookupTables_, - bytesPerVector_, - numSubQuantizers_, - numSubQuantizerCodes_, - deviceListDataPointers_, - deviceListIndexPointers_, - indicesOptions_, - deviceListLengths_, - maxListLength_, - k, - outDistances, - outIndices, - resources_); -} - -template -void -IVFPQ::runPQNoPrecomputedCodesT_( - Tensor& queries, - Tensor& bitset, - DeviceTensor& coarseDistances, - DeviceTensor& coarseIndices, - int k, - Tensor& outDistances, - Tensor& outIndices) { - auto& coarseCentroids = quantizer_->template getVectorsRef(); - - runPQScanMultiPassNoPrecomputed(queries, - coarseCentroids, - pqCentroidsInnermostCode_, - coarseIndices, - bitset, - useFloat16LookupTables_, - bytesPerVector_, - numSubQuantizers_, - numSubQuantizerCodes_, - deviceListDataPointers_, - deviceListIndexPointers_, - indicesOptions_, - deviceListLengths_, - maxListLength_, - k, - metric_, - outDistances, - outIndices, - resources_); -} - -void -IVFPQ::runPQNoPrecomputedCodes_( - Tensor& queries, - Tensor& bitset, - DeviceTensor& coarseDistances, - DeviceTensor& coarseIndices, - int k, - Tensor& outDistances, - Tensor& outIndices) { -#ifdef FAISS_USE_FLOAT16 - if (quantizer_->getUseFloat16()) { - runPQNoPrecomputedCodesT_(queries, - bitset, - coarseDistances, - coarseIndices, - k, - outDistances, - outIndices); - } else { - runPQNoPrecomputedCodesT_(queries, - bitset, - coarseDistances, - coarseIndices, - k, - outDistances, - outIndices); - } -#else - runPQNoPrecomputedCodesT_(queries, - bitset, - coarseDistances, - coarseIndices, - k, - outDistances, - outIndices); -#endif - -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/IVFPQ.cuh b/core/src/index/thirdparty/faiss/gpu/impl/IVFPQ.cuh deleted file mode 100644 index ad03fb4f89..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/IVFPQ.cuh +++ /dev/null @@ -1,161 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include - -namespace faiss { namespace gpu { - -/// Implementing class for IVFPQ on the GPU -class IVFPQ : public IVFBase { - public: - IVFPQ(GpuResources* resources, - faiss::MetricType metric, - float metricArg, - /// We do not own this reference - FlatIndex* quantizer, - int numSubQuantizers, - int bitsPerSubQuantizer, - float* pqCentroidData, - IndicesOptions indicesOptions, - bool useFloat16LookupTables, - MemorySpace space); - - /// Returns true if we support PQ in this size - static bool isSupportedPQCodeLength(int size); - - /// For no precomputed codes, is this a supported sub-dimension - /// size? - /// FIXME: get MM implementation working again - static bool isSupportedNoPrecomputedSubDimSize(int dims); - - ~IVFPQ() override; - - /// Enable or disable pre-computed codes - void setPrecomputedCodes(bool enable); - - /// Adds a set of codes and indices to a list; the data can be - /// resident on either the host or the device - void addCodeVectorsFromCpu(int listId, - const void* codes, - const long* indices, - size_t numVecs); - - /// Calcuates the residual and quantizes the vectors, adding them to - /// this index - /// The input data must be on our current device. - /// Returns the number of vectors successfully added. Vectors may - /// not be able to be added because they contain NaNs. - int classifyAndAddVectors(Tensor& vecs, - Tensor& indices); - - /// Find the approximate k nearest neigbors for `queries` against - /// our database - void query(Tensor& queries, - Tensor& bitset, - int nprobe, - int k, - Tensor& outDistances, - Tensor& outIndices); - - /// Return the list codes of a particular list back to the CPU - std::vector getListCodes(int listId) const; - - /// Returns our set of sub-quantizers of the form - /// (sub q)(code id)(sub dim) - Tensor getPQCentroids(); - - private: - /// Sets the current product quantizer centroids; the data can be - /// resident on either the host or the device. It will be transposed - /// into our preferred data layout - /// Data must be a row-major, 3-d array of size - /// (numSubQuantizers, numSubQuantizerCodes, dim / numSubQuantizers) - void setPQCentroids_(float* data); - - /// Calculate precomputed residual distance information - void precomputeCodes_(); - - /// Calculate precomputed residual distance information (for different coarse - /// centroid type) - template - void precomputeCodesT_(); - - /// Runs kernels for scanning inverted lists with precomputed codes - void runPQPrecomputedCodes_(Tensor& queries, - Tensor& bitset, - DeviceTensor& coarseDistances, - DeviceTensor& coarseIndices, - int k, - Tensor& outDistances, - Tensor& outIndices); - - /// Runs kernels for scanning inverted lists without precomputed codes - void runPQNoPrecomputedCodes_(Tensor& queries, - Tensor& bitset, - DeviceTensor& coarseDistances, - DeviceTensor& coarseIndices, - int k, - Tensor& outDistances, - Tensor& outIndices); - - /// Runs kernels for scanning inverted lists without precomputed codes (for - /// different coarse centroid type) - template - void runPQNoPrecomputedCodesT_(Tensor& queries, - Tensor& bitset, - DeviceTensor& coarseDistances, - DeviceTensor& coarseIndices, - int k, - Tensor& outDistances, - Tensor& outIndices); - - private: - /// Number of sub-quantizers per vector - const int numSubQuantizers_; - - /// Number of bits per sub-quantizer - const int bitsPerSubQuantizer_; - - /// Number of per sub-quantizer codes (2^bits) - const int numSubQuantizerCodes_; - - /// Number of dimensions per each sub-quantizer - const int dimPerSubQuantizer_; - - /// Do we maintain precomputed terms and lookup tables in float16 - /// form? - const bool useFloat16LookupTables_; - - /// On the GPU, we prefer different PQ centroid data layouts for - /// different purposes. - /// - /// (sub q)(sub dim)(code id) - DeviceTensor pqCentroidsInnermostCode_; - - /// (sub q)(code id)(sub dim) - DeviceTensor pqCentroidsMiddleCode_; - - /// Are precomputed codes enabled? (additional factoring and - /// precomputation of the residual distance, to reduce query-time work) - bool precomputedCodes_; - - /// Precomputed term 2 in float form - /// (centroid id)(sub q)(code id) - DeviceTensor precomputedCode_; - - /// Precomputed term 2 in half form -#ifdef FAISS_USE_FLOAT16 - DeviceTensor precomputedCodeHalf_; -#endif -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/IVFUtils.cu b/core/src/index/thirdparty/faiss/gpu/impl/IVFUtils.cu deleted file mode 100644 index fda439fea2..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/IVFUtils.cu +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -// Calculates the total number of intermediate distances to consider -// for all queries -__global__ void -getResultLengths(Tensor topQueryToCentroid, - int* listLengths, - int totalSize, - Tensor length) { - int linearThreadId = blockIdx.x * blockDim.x + threadIdx.x; - if (linearThreadId >= totalSize) { - return; - } - - int nprobe = topQueryToCentroid.getSize(1); - int queryId = linearThreadId / nprobe; - int listId = linearThreadId % nprobe; - - int centroidId = topQueryToCentroid[queryId][listId]; - - // Safety guard in case NaNs in input cause no list ID to be generated - length[queryId][listId] = (centroidId != -1) ? listLengths[centroidId] : 0; -} - -void runCalcListOffsets(Tensor& topQueryToCentroid, - thrust::device_vector& listLengths, - Tensor& prefixSumOffsets, - Tensor& thrustMem, - cudaStream_t stream) { - FAISS_ASSERT(topQueryToCentroid.getSize(0) == prefixSumOffsets.getSize(0)); - FAISS_ASSERT(topQueryToCentroid.getSize(1) == prefixSumOffsets.getSize(1)); - - int totalSize = topQueryToCentroid.numElements(); - - int numThreads = std::min(totalSize, getMaxThreadsCurrentDevice()); - int numBlocks = utils::divUp(totalSize, numThreads); - - auto grid = dim3(numBlocks); - auto block = dim3(numThreads); - - getResultLengths<<>>( - topQueryToCentroid, - listLengths.data().get(), - totalSize, - prefixSumOffsets); - CUDA_TEST_ERROR(); - - // Prefix sum of the indices, so we know where the intermediate - // results should be maintained - // Thrust wants a place for its temporary allocations, so provide - // one, so it won't call cudaMalloc/Free - GpuResourcesThrustAllocator alloc(thrustMem.data(), - thrustMem.getSizeInBytes()); - - thrust::inclusive_scan(thrust::cuda::par(alloc).on(stream), - prefixSumOffsets.data(), - prefixSumOffsets.data() + totalSize, - prefixSumOffsets.data()); - CUDA_TEST_ERROR(); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/IVFUtils.cuh b/core/src/index/thirdparty/faiss/gpu/impl/IVFUtils.cuh deleted file mode 100644 index 3eb226568d..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/IVFUtils.cuh +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include - -// A collection of utility functions for IVFPQ and IVFFlat, for -// post-processing and k-selecting the results -namespace faiss { namespace gpu { - -// This is warp divergence central, but this is really a final step -// and happening a small number of times -inline __device__ int binarySearchForBucket(int* prefixSumOffsets, - int size, - int val) { - int start = 0; - int end = size; - - while (end - start > 0) { - int mid = start + (end - start) / 2; - - int midVal = prefixSumOffsets[mid]; - - // Find the first bucket that we are <= - if (midVal <= val) { - start = mid + 1; - } else { - end = mid; - } - } - - // We must find the bucket that it is in - assert(start != size); - - return start; -} - -inline __device__ long -getListIndex(int queryId, - int offset, - void** listIndices, - Tensor& prefixSumOffsets, - Tensor& topQueryToCentroid, - IndicesOptions opt) { - long index = -1; - - // In order to determine the actual user index, we need to first - // determine what list it was in. - // We do this by binary search in the prefix sum list. - int probe = binarySearchForBucket(prefixSumOffsets[queryId].data(), - prefixSumOffsets.getSize(1), - offset); - - // This is then the probe for the query; we can find the actual - // list ID from this - int listId = topQueryToCentroid[queryId][probe]; - - // Now, we need to know the offset within the list - // We ensure that before the array (at offset -1), there is a 0 value - int listStart = *(prefixSumOffsets[queryId][probe].data() - 1); - int listOffset = offset - listStart; - - // This gives us our final index - if (opt == INDICES_32_BIT) { - index = (long) ((int*) listIndices[listId])[listOffset]; - } else if (opt == INDICES_64_BIT) { - index = ((long*) listIndices[listId])[listOffset]; - } else { - index = ((long) listId << 32 | (long) listOffset); - } - - return index; -} - -/// Function for multi-pass scanning that collects the length of -/// intermediate results for all (query, probe) pair -void runCalcListOffsets(Tensor& topQueryToCentroid, - thrust::device_vector& listLengths, - Tensor& prefixSumOffsets, - Tensor& thrustMem, - cudaStream_t stream); - -/// Performs a first pass of k-selection on the results -void runPass1SelectLists(thrust::device_vector& listIndices, - IndicesOptions indicesOptions, - Tensor& prefixSumOffsets, - Tensor& topQueryToCentroid, - Tensor& bitset, - Tensor& distance, - int nprobe, - int k, - bool chooseLargest, - Tensor& heapDistances, - Tensor& heapIndices, - cudaStream_t stream); - -/// Performs a final pass of k-selection on the results, producing the -/// final indices -void runPass2SelectLists(Tensor& heapDistances, - Tensor& heapIndices, - thrust::device_vector& listIndices, - IndicesOptions indicesOptions, - Tensor& prefixSumOffsets, - Tensor& topQueryToCentroid, - int k, - bool chooseLargest, - Tensor& outDistances, - Tensor& outIndices, - cudaStream_t stream); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/IVFUtilsSelect1.cu b/core/src/index/thirdparty/faiss/gpu/impl/IVFUtilsSelect1.cu deleted file mode 100644 index b575d3c0a4..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/IVFUtilsSelect1.cu +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include - -// -// This kernel is split into a separate compilation unit to cut down -// on compile time -// - -namespace faiss { namespace gpu { - -template -__global__ void -pass1SelectLists(void** listIndices, - Tensor prefixSumOffsets, - Tensor topQueryToCentroid, - Tensor bitset, - Tensor distance, - int nprobe, - int k, - IndicesOptions opt, - Tensor heapDistances, - Tensor heapIndices) { - constexpr int kNumWarps = ThreadsPerBlock / kWarpSize; - - __shared__ float smemK[kNumWarps * NumWarpQ]; - __shared__ int smemV[kNumWarps * NumWarpQ]; - - constexpr auto kInit = Dir ? kFloatMin : kFloatMax; - BlockSelect, - NumWarpQ, NumThreadQ, ThreadsPerBlock> - heap(kInit, -1, smemK, smemV, k); - - auto queryId = blockIdx.y; - auto sliceId = blockIdx.x; - auto numSlices = gridDim.x; - - int sliceSize = (nprobe / numSlices); - int sliceStart = sliceSize * sliceId; - int sliceEnd = sliceId == (numSlices - 1) ? nprobe : - sliceStart + sliceSize; - auto offsets = prefixSumOffsets[queryId].data(); - - // We ensure that before the array (at offset -1), there is a 0 value - int start = *(&offsets[sliceStart] - 1); - int end = offsets[sliceEnd - 1]; - - int num = end - start; - int limit = utils::roundDown(num, kWarpSize); - - int i = threadIdx.x; - auto distanceStart = distance[start].data(); - bool bitsetEmpty = (bitset.getSize(0) == 0); - long index = -1; - - // BlockSelect add cannot be used in a warp divergent circumstance; we - // handle the remainder warp below - for (; i < limit; i += blockDim.x) { - index = getListIndex(queryId, - start + i, - listIndices, - prefixSumOffsets, - topQueryToCentroid, - opt); - if (bitsetEmpty || (!(bitset[index >> 3] & (0x1 << (index & 0x7))))) { - heap.addThreadQ(distanceStart[i], start + i); - } - heap.checkThreadQ(); - } - - // Handle warp divergence separately - if (i < num) { - index = getListIndex(queryId, - start + i, - listIndices, - prefixSumOffsets, - topQueryToCentroid, - opt); - if (bitsetEmpty || (!(bitset[index >> 3] & (0x1 << (index & 0x7))))) { - heap.addThreadQ(distanceStart[i], start + i); - } - } - - // Merge all final results - heap.reduce(); - - // Write out the final k-selected values; they should be all - // together - for (int i = threadIdx.x; i < k; i += blockDim.x) { - heapDistances[queryId][sliceId][i] = smemK[i]; - heapIndices[queryId][sliceId][i] = smemV[i]; - } -} - -void -runPass1SelectLists(thrust::device_vector& listIndices, - IndicesOptions indicesOptions, - Tensor& prefixSumOffsets, - Tensor& topQueryToCentroid, - Tensor& bitset, - Tensor& distance, - int nprobe, - int k, - bool chooseLargest, - Tensor& heapDistances, - Tensor& heapIndices, - cudaStream_t stream) { - // This is caught at a higher level - FAISS_ASSERT(k <= GPU_MAX_SELECTION_K); - - auto grid = dim3(heapDistances.getSize(1), prefixSumOffsets.getSize(0)); - -#define RUN_PASS(BLOCK, NUM_WARP_Q, NUM_THREAD_Q, DIR) \ - do { \ - pass1SelectLists \ - <<>>(listIndices.data().get(), \ - prefixSumOffsets, \ - topQueryToCentroid, \ - bitset, \ - distance, \ - nprobe, \ - k, \ - indicesOptions, \ - heapDistances, \ - heapIndices); \ - CUDA_TEST_ERROR(); \ - return; /* success */ \ - } while (0) - -#if GPU_MAX_SELECTION_K >= 2048 - - // block size 128 for k <= 1024, 64 for k = 2048 -#define RUN_PASS_DIR(DIR) \ - do { \ - if (k == 1) { \ - RUN_PASS(128, 1, 1, DIR); \ - } else if (k <= 32) { \ - RUN_PASS(128, 32, 2, DIR); \ - } else if (k <= 64) { \ - RUN_PASS(128, 64, 3, DIR); \ - } else if (k <= 128) { \ - RUN_PASS(128, 128, 3, DIR); \ - } else if (k <= 256) { \ - RUN_PASS(128, 256, 4, DIR); \ - } else if (k <= 512) { \ - RUN_PASS(128, 512, 8, DIR); \ - } else if (k <= 1024) { \ - RUN_PASS(128, 1024, 8, DIR); \ - } else if (k <= 2048) { \ - RUN_PASS(64, 2048, 8, DIR); \ - } \ - } while (0) - -#else - -#define RUN_PASS_DIR(DIR) \ - do { \ - if (k == 1) { \ - RUN_PASS(128, 1, 1, DIR); \ - } else if (k <= 32) { \ - RUN_PASS(128, 32, 2, DIR); \ - } else if (k <= 64) { \ - RUN_PASS(128, 64, 3, DIR); \ - } else if (k <= 128) { \ - RUN_PASS(128, 128, 3, DIR); \ - } else if (k <= 256) { \ - RUN_PASS(128, 256, 4, DIR); \ - } else if (k <= 512) { \ - RUN_PASS(128, 512, 8, DIR); \ - } else if (k <= 1024) { \ - RUN_PASS(128, 1024, 8, DIR); \ - } \ - } while (0) - -#endif // GPU_MAX_SELECTION_K - - if (chooseLargest) { - RUN_PASS_DIR(true); - } else { - RUN_PASS_DIR(false); - } - -#undef RUN_PASS_DIR -#undef RUN_PASS -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/IVFUtilsSelect2.cu b/core/src/index/thirdparty/faiss/gpu/impl/IVFUtilsSelect2.cu deleted file mode 100644 index 8c6b9eb3b8..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/IVFUtilsSelect2.cu +++ /dev/null @@ -1,218 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include - -// -// This kernel is split into a separate compilation unit to cut down -// on compile time -// - -namespace faiss { namespace gpu { - -// This is warp divergence central, but this is really a final step -// and happening a small number of times -//inline __device__ int binarySearchForBucket(int* prefixSumOffsets, -// int size, -// int val) { -// int start = 0; -// int end = size; -// -// while (end - start > 0) { -// int mid = start + (end - start) / 2; -// -// int midVal = prefixSumOffsets[mid]; -// -// // Find the first bucket that we are <= -// if (midVal <= val) { -// start = mid + 1; -// } else { -// end = mid; -// } -// } -// -// // We must find the bucket that it is in -// assert(start != size); -// -// return start; -//} - -template -__global__ void -pass2SelectLists(Tensor heapDistances, - Tensor heapIndices, - void** listIndices, - Tensor prefixSumOffsets, - Tensor topQueryToCentroid, - int k, - IndicesOptions opt, - Tensor outDistances, - Tensor outIndices) { - constexpr int kNumWarps = ThreadsPerBlock / kWarpSize; - - __shared__ float smemK[kNumWarps * NumWarpQ]; - __shared__ int smemV[kNumWarps * NumWarpQ]; - - constexpr auto kInit = Dir ? kFloatMin : kFloatMax; - BlockSelect, - NumWarpQ, NumThreadQ, ThreadsPerBlock> - heap(kInit, -1, smemK, smemV, k); - - auto queryId = blockIdx.x; - int num = heapDistances.getSize(1); - int limit = utils::roundDown(num, kWarpSize); - - int i = threadIdx.x; - auto heapDistanceStart = heapDistances[queryId]; - - // BlockSelect add cannot be used in a warp divergent circumstance; we - // handle the remainder warp below - for (; i < limit; i += blockDim.x) { - heap.add(heapDistanceStart[i], i); - } - - // Handle warp divergence separately - if (i < num) { - heap.addThreadQ(heapDistanceStart[i], i); - } - - // Merge all final results - heap.reduce(); - - for (int i = threadIdx.x; i < k; i += blockDim.x) { - outDistances[queryId][i] = smemK[i]; - - // `v` is the index in `heapIndices` - // We need to translate this into an original user index. The - // reason why we don't maintain intermediate results in terms of - // user indices is to substantially reduce temporary memory - // requirements and global memory write traffic for the list - // scanning. - // This code is highly divergent, but it's probably ok, since this - // is the very last step and it is happening a small number of - // times (#queries x k). - int v = smemV[i]; - long index = -1; - - if (v != -1) { - // `offset` is the offset of the intermediate result, as - // calculated by the original scan. - int offset = heapIndices[queryId][v]; - - index = getListIndex(queryId, - offset, - listIndices, - prefixSumOffsets, - topQueryToCentroid, - opt); - } - - outIndices[queryId][i] = index; - } -} - -void -runPass2SelectLists(Tensor& heapDistances, - Tensor& heapIndices, - thrust::device_vector& listIndices, - IndicesOptions indicesOptions, - Tensor& prefixSumOffsets, - Tensor& topQueryToCentroid, - int k, - bool chooseLargest, - Tensor& outDistances, - Tensor& outIndices, - cudaStream_t stream) { - auto grid = dim3(topQueryToCentroid.getSize(0)); - -#define RUN_PASS(BLOCK, NUM_WARP_Q, NUM_THREAD_Q, DIR) \ - do { \ - pass2SelectLists \ - <<>>(heapDistances, \ - heapIndices, \ - listIndices.data().get(), \ - prefixSumOffsets, \ - topQueryToCentroid, \ - k, \ - indicesOptions, \ - outDistances, \ - outIndices); \ - CUDA_TEST_ERROR(); \ - return; /* success */ \ - } while (0) - -#if GPU_MAX_SELECTION_K >= 2048 - - // block size 128 for k <= 1024, 64 for k = 2048 -#define RUN_PASS_DIR(DIR) \ - do { \ - if (k == 1) { \ - RUN_PASS(128, 1, 1, DIR); \ - } else if (k <= 32) { \ - RUN_PASS(128, 32, 2, DIR); \ - } else if (k <= 64) { \ - RUN_PASS(128, 64, 3, DIR); \ - } else if (k <= 128) { \ - RUN_PASS(128, 128, 3, DIR); \ - } else if (k <= 256) { \ - RUN_PASS(128, 256, 4, DIR); \ - } else if (k <= 512) { \ - RUN_PASS(128, 512, 8, DIR); \ - } else if (k <= 1024) { \ - RUN_PASS(128, 1024, 8, DIR); \ - } else if (k <= 2048) { \ - RUN_PASS(64, 2048, 8, DIR); \ - } \ - } while (0) - -#else - -#define RUN_PASS_DIR(DIR) \ - do { \ - if (k == 1) { \ - RUN_PASS(128, 1, 1, DIR); \ - } else if (k <= 32) { \ - RUN_PASS(128, 32, 2, DIR); \ - } else if (k <= 64) { \ - RUN_PASS(128, 64, 3, DIR); \ - } else if (k <= 128) { \ - RUN_PASS(128, 128, 3, DIR); \ - } else if (k <= 256) { \ - RUN_PASS(128, 256, 4, DIR); \ - } else if (k <= 512) { \ - RUN_PASS(128, 512, 8, DIR); \ - } else if (k <= 1024) { \ - RUN_PASS(128, 1024, 8, DIR); \ - } \ - } while (0) - -#endif // GPU_MAX_SELECTION_K - - if (chooseLargest) { - RUN_PASS_DIR(true); - } else { - RUN_PASS_DIR(false); - } - - // unimplemented / too many resources - FAISS_ASSERT_FMT(false, "unimplemented k value (%d)", k); - -#undef RUN_PASS_DIR -#undef RUN_PASS -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/L2Norm.cu b/core/src/index/thirdparty/faiss/gpu/impl/L2Norm.cu deleted file mode 100644 index bdf812524e..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/L2Norm.cu +++ /dev/null @@ -1,331 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -// Input: (batch x dim) -// Output: (batch norm) -// Done under the presumption that the dimension size is not too large -// (<10k or so), since there wouldn't be enough parallelism applying a -// single block to the problem. Also that each vector is large enough -// (>64), since a single block works on multiple rows' norms at the -// same time. -// T: the type we are doing the math in (e.g., float, half) -// TVec: the potentially vectorized type we are loading in (e.g., -// float4, half2) -template -__global__ void -l2NormRowMajor(Tensor input, - Tensor output) { - extern __shared__ char smemByte[]; // #warps * RowTileSize elements - float* smem = (float*) smemByte; - - IndexType numWarps = utils::divUp(blockDim.x, kWarpSize); - IndexType laneId = getLaneId(); - IndexType warpId = threadIdx.x / kWarpSize; - - bool lastRowTile = (blockIdx.x == (gridDim.x - 1)); - IndexType rowStart = RowTileSize * blockIdx.x; - // accumulate in f32 - float rowNorm[RowTileSize]; - - if (lastRowTile) { - // We are handling the very end of the input matrix rows - for (IndexType row = 0; row < input.getSize(0) - rowStart; ++row) { - if (NormLoop) { - rowNorm[0] = 0; - - for (IndexType col = threadIdx.x; - col < input.getSize(1); col += blockDim.x) { - TVec val = input[rowStart + row][col]; - val = Math::mul(val, val); - rowNorm[0] = rowNorm[0] + Math::reduceAdd(val); - } - } else { - TVec val = input[rowStart + row][threadIdx.x]; - val = Math::mul(val, val); - rowNorm[0] = Math::reduceAdd(val); - } - - rowNorm[0] = warpReduceAllSum(rowNorm[0]); - if (laneId == 0) { - smem[row * numWarps + warpId] = rowNorm[0]; - } - } - } else { - // We are guaranteed that all RowTileSize rows are available in - // [rowStart, rowStart + RowTileSize) - - if (NormLoop) { - // A single block of threads is not big enough to span each - // vector - TVec tmp[RowTileSize]; - -#pragma unroll - for (int row = 0; row < RowTileSize; ++row) { - rowNorm[row] = 0; - } - - for (IndexType col = threadIdx.x; - col < input.getSize(1); col += blockDim.x) { -#pragma unroll - for (int row = 0; row < RowTileSize; ++row) { - tmp[row] = input[rowStart + row][col]; - } - -#pragma unroll - for (int row = 0; row < RowTileSize; ++row) { - tmp[row] = Math::mul(tmp[row], tmp[row]); - } - -#pragma unroll - for (int row = 0; row < RowTileSize; ++row) { - rowNorm[row] = rowNorm[row] + - Math::reduceAdd(tmp[row]); - } - } - } else { - TVec tmp[RowTileSize]; - - // A block of threads is the exact size of the vector -#pragma unroll - for (int row = 0; row < RowTileSize; ++row) { - tmp[row] = input[rowStart + row][threadIdx.x]; - } - -#pragma unroll - for (int row = 0; row < RowTileSize; ++row) { - tmp[row] = Math::mul(tmp[row], tmp[row]); - } - -#pragma unroll - for (int row = 0; row < RowTileSize; ++row) { - rowNorm[row] = Math::reduceAdd(tmp[row]); - } - } - - // Sum up all parts in each warp -#pragma unroll - for (int row = 0; row < RowTileSize; ++row) { - rowNorm[row] = warpReduceAllSum(rowNorm[row]); - } - - if (laneId == 0) { -#pragma unroll - for (int row = 0; row < RowTileSize; ++row) { - smem[row * numWarps + warpId] = rowNorm[row]; - } - } - } - - __syncthreads(); - - // Sum across warps - if (warpId == 0) { -#pragma unroll - for (int row = 0; row < RowTileSize; ++row) { - rowNorm[row] = laneId < numWarps ? smem[row * numWarps + laneId] : 0; - } - -#pragma unroll - for (int row = 0; row < RowTileSize; ++row) { - rowNorm[row] = warpReduceAllSum(rowNorm[row]); - } - - // Write out answer - if (laneId == 0) { -#pragma unroll - for (int row = 0; row < RowTileSize; ++row) { - int outCol = rowStart + row; - - if (lastRowTile) { - if (outCol < output.getSize(0)) { - output[outCol] = - NormSquared ? ConvertTo::to(rowNorm[row]) : - sqrtf(ConvertTo::to(rowNorm[row])); - } - } else { - output[outCol] = - NormSquared ? ConvertTo::to(rowNorm[row]) : - sqrtf(ConvertTo::to(rowNorm[row])); - } - } - } - } -} - -// Input: (dim x batch) -// Output: (batch norm) -// Handles the case where `input` is column major. A single thread calculates -// the norm of each vector instead of a block-wide reduction. -template -__global__ void -l2NormColMajor(Tensor input, - Tensor output) { - // grid-stride loop to handle all batch elements - for (IndexType batch = blockIdx.x * blockDim.x + threadIdx.x; - batch < input.getSize(1); - batch += gridDim.x * blockDim.x) { - float sum = 0; - - // This is still a coalesced load from the memory - for (IndexType dim = 0; dim < input.getSize(0); ++dim) { - // Just do the math in float32, even if the input is float16 - float v = ConvertTo::to(input[dim][batch]); - sum += v * v; - } - - if (!NormSquared) { - sum = sqrtf(sum); - } - - output[batch] = ConvertTo::to(sum); - } -} - -template -void runL2Norm(Tensor& input, - bool inputRowMajor, - Tensor& output, - bool normSquared, - cudaStream_t stream) { - IndexType maxThreads = (IndexType) getMaxThreadsCurrentDevice(); - constexpr int rowTileSize = 8; - -#define RUN_L2_ROW_MAJOR(TYPE_T, TYPE_TVEC, INPUT) \ - do { \ - if (normLoop) { \ - if (normSquared) { \ - l2NormRowMajor \ - <<>>(INPUT, output); \ - } else { \ - l2NormRowMajor \ - <<>>(INPUT, output); \ - } \ - } else { \ - if (normSquared) { \ - l2NormRowMajor \ - <<>>(INPUT, output); \ - } else { \ - l2NormRowMajor \ - <<>>(INPUT, output); \ - } \ - } \ - } while (0) - - if (inputRowMajor) { - // - // Row-major kernel - /// - - if (input.template canCastResize()) { - // Can load using the vectorized type - auto inputV = input.template castResize(); - - auto dim = inputV.getSize(1); - bool normLoop = dim > maxThreads; - auto numThreads = min(dim, maxThreads); - - auto grid = dim3(utils::divUp(inputV.getSize(0), rowTileSize)); - auto block = dim3(numThreads); - - auto smem = sizeof(float) * rowTileSize * utils::divUp(numThreads, kWarpSize); - - RUN_L2_ROW_MAJOR(T, TVec, inputV); - } else { - // Can't load using the vectorized type - - auto dim = input.getSize(1); - bool normLoop = dim > maxThreads; - auto numThreads = min(dim, maxThreads); - - auto grid = dim3(utils::divUp(input.getSize(0), rowTileSize)); - auto block = dim3(numThreads); - - auto smem = sizeof(float) * rowTileSize * utils::divUp(numThreads, kWarpSize); - - RUN_L2_ROW_MAJOR(T, T, input); - } - } else { - // - // Column-major kernel - // - - // Just use a fixed-sized block, since the kernel threads are fully - // independent - auto block = 128; - - // Cap the grid size at 2^16 since there is a grid-stride loop to handle - // processing everything - auto grid = (int) - std::min(utils::divUp(input.getSize(1), (IndexType) block), - (IndexType) 65536); - - if (normSquared) { - l2NormColMajor<<>>( - input, output); - } else { - l2NormColMajor<<>>( - input, output); - } - } - -#undef RUN_L2 - - CUDA_TEST_ERROR(); -} - -void runL2Norm(Tensor& input, - bool inputRowMajor, - Tensor& output, - bool normSquared, - cudaStream_t stream) { - if (input.canUseIndexType()) { - runL2Norm( - input, inputRowMajor, output, normSquared, stream); - } else { - auto inputCast = input.castIndexType(); - auto outputCast = output.castIndexType(); - - runL2Norm( - inputCast, inputRowMajor, outputCast, normSquared, stream); - } -} - -#ifdef FAISS_USE_FLOAT16 -void runL2Norm(Tensor& input, - bool inputRowMajor, - Tensor& output, - bool normSquared, - cudaStream_t stream) { - if (input.canUseIndexType()) { - runL2Norm( - input, inputRowMajor, output, normSquared, stream); - } else { - auto inputCast = input.castIndexType(); - auto outputCast = output.castIndexType(); - - runL2Norm( - inputCast, inputRowMajor, outputCast, normSquared, stream); - } -} -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/L2Norm.cuh b/core/src/index/thirdparty/faiss/gpu/impl/L2Norm.cuh deleted file mode 100644 index 6df3dcea58..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/L2Norm.cuh +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include - -namespace faiss { namespace gpu { - -void runL2Norm(Tensor& input, - bool inputRowMajor, - Tensor& output, - bool normSquared, - cudaStream_t stream); - -#ifdef FAISS_USE_FLOAT16 -void runL2Norm(Tensor& input, - bool inputRowMajor, - Tensor& output, - bool normSquared, - cudaStream_t stream); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/L2Select.cu b/core/src/index/thirdparty/faiss/gpu/impl/L2Select.cu deleted file mode 100644 index 9ea70ec651..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/L2Select.cu +++ /dev/null @@ -1,264 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -// L2 + select kernel for k == 1, implements re-use of ||c||^2 -template -__global__ void l2SelectMin1(Tensor productDistances, - Tensor centroidDistances, - Tensor bitset, - Tensor outDistances, - Tensor outIndices) { - // Each block handles kRowsPerBlock rows of the distances (results) - Pair threadMin[kRowsPerBlock]; - __shared__ Pair blockMin[kRowsPerBlock * (kBlockSize / kWarpSize)]; - - T distance[kRowsPerBlock]; - -#pragma unroll - for (int i = 0; i < kRowsPerBlock; ++i) { - threadMin[i].k = Limits::getMax(); - threadMin[i].v = -1; - } - - // blockIdx.x: which chunk of rows we are responsible for updating - int rowStart = blockIdx.x * kRowsPerBlock; - - // FIXME: if we have exact multiples, don't need this - bool endRow = (blockIdx.x == gridDim.x - 1); - - bool bitsetEmpty = (bitset.getSize(0) == 0); - - if (endRow) { - if (productDistances.getSize(0) % kRowsPerBlock == 0) { - endRow = false; - } - } - - if (endRow) { - for (int row = rowStart; row < productDistances.getSize(0); ++row) { - for (int col = threadIdx.x; col < productDistances.getSize(1); - col += blockDim.x) { - if (bitsetEmpty || (!(bitset[col >> 3] & (0x1 << (col & 0x7))))) { - distance[0] = Math::add(centroidDistances[col], - productDistances[row][col]); - } else { - distance[0] = (T)(1.0 / 0.0); - } - - if (Math::lt(distance[0], threadMin[0].k)) { - threadMin[0].k = distance[0]; - threadMin[0].v = col; - } - } - - // Reduce within the block - threadMin[0] = - blockReduceAll, Min>, false, false>( - threadMin[0], Min>(), blockMin); - - if (threadIdx.x == 0) { - outDistances[row][0] = threadMin[0].k; - outIndices[row][0] = threadMin[0].v; - } - - // so we can use the shared memory again - __syncthreads(); - - threadMin[0].k = Limits::getMax(); - threadMin[0].v = -1; - } - } else { - for (int col = threadIdx.x; col < productDistances.getSize(1); - col += blockDim.x) { - T centroidDistance = centroidDistances[col]; - -#pragma unroll - for (int row = 0; row < kRowsPerBlock; ++row) { - distance[row] = productDistances[rowStart + row][col]; - } - -#pragma unroll - for (int row = 0; row < kRowsPerBlock; ++row) { - distance[row] = Math::add(distance[row], centroidDistance); - } - -#pragma unroll - for (int row = 0; row < kRowsPerBlock; ++row) { - if (Math::lt(distance[row], threadMin[row].k)) { - threadMin[row].k = distance[row]; - threadMin[row].v = col; - } - } - } - - // Reduce within the block - blockReduceAll, - Min >, - false, - false>(threadMin, - Min >(), - blockMin); - - if (threadIdx.x == 0) { -#pragma unroll - for (int row = 0; row < kRowsPerBlock; ++row) { - outDistances[rowStart + row][0] = threadMin[row].k; - outIndices[rowStart + row][0] = threadMin[row].v; - } - } - } -} - -// With bitset included -// L2 + select kernel for k > 1, no re-use of ||c||^2 -template -__global__ void l2SelectMinK(Tensor productDistances, - Tensor centroidDistances, - Tensor bitset, - Tensor outDistances, - Tensor outIndices, - int k, T initK) { - // Each block handles a single row of the distances (results) - constexpr int kNumWarps = ThreadsPerBlock / kWarpSize; - - __shared__ T smemK[kNumWarps * NumWarpQ]; - __shared__ int smemV[kNumWarps * NumWarpQ]; - - BlockSelect, - NumWarpQ, NumThreadQ, ThreadsPerBlock> - heap(initK, -1, smemK, smemV, k); - - int row = blockIdx.x; - - // Whole warps must participate in the selection - int limit = utils::roundDown(productDistances.getSize(1), kWarpSize); - int i = threadIdx.x; - - bool bitsetEmpty = (bitset.getSize(0) == 0); - T v; - - for (; i < limit; i += blockDim.x) { - if (bitsetEmpty || (!(bitset[i >> 3] & (0x1 << (i & 0x7))))) { - v = Math::add(centroidDistances[i], - productDistances[row][i]); - heap.addThreadQ(v, i); - } - heap.checkThreadQ(); - } - - if (i < productDistances.getSize(1)) { - if (bitsetEmpty || (!(bitset[i >> 3] & (0x1 << (i & 0x7))))) { - v = Math::add(centroidDistances[i], - productDistances[row][i]); - heap.addThreadQ(v, i); - } - } - - heap.reduce(); - for (int i = threadIdx.x; i < k; i += blockDim.x) { - outDistances[row][i] = smemK[i]; - outIndices[row][i] = smemV[i]; - } -} - -template -void runL2SelectMin(Tensor& productDistances, - Tensor& centroidDistances, - Tensor& bitset, - Tensor& outDistances, - Tensor& outIndices, - int k, - cudaStream_t stream) { - FAISS_ASSERT(productDistances.getSize(0) == outDistances.getSize(0)); - FAISS_ASSERT(productDistances.getSize(0) == outIndices.getSize(0)); - FAISS_ASSERT(centroidDistances.getSize(0) == productDistances.getSize(1)); - FAISS_ASSERT(outDistances.getSize(1) == k); - FAISS_ASSERT(outIndices.getSize(1) == k); - FAISS_ASSERT(k <= GPU_MAX_SELECTION_K); - - if (k == 1) { - constexpr int kThreadsPerBlock = 256; - constexpr int kRowsPerBlock = 8; - - auto block = dim3(kThreadsPerBlock); - auto grid = dim3(utils::divUp(outDistances.getSize(0), kRowsPerBlock)); - - l2SelectMin1 - <<>>(productDistances, centroidDistances, bitset, - outDistances, outIndices); - } else { - auto grid = dim3(outDistances.getSize(0)); - -#define RUN_L2_SELECT(BLOCK, NUM_WARP_Q, NUM_THREAD_Q) \ - do { \ - l2SelectMinK \ - <<>>(productDistances, centroidDistances, bitset, \ - outDistances, outIndices, \ - k, Limits::getMax()); \ - } while (0) - - // block size 128 for everything <= 1024 - if (k <= 32) { - RUN_L2_SELECT(128, 32, 2); - } else if (k <= 64) { - RUN_L2_SELECT(128, 64, 3); - } else if (k <= 128) { - RUN_L2_SELECT(128, 128, 3); - } else if (k <= 256) { - RUN_L2_SELECT(128, 256, 4); - } else if (k <= 512) { - RUN_L2_SELECT(128, 512, 8); - } else if (k <= 1024) { - RUN_L2_SELECT(128, 1024, 8); - -#if GPU_MAX_SELECTION_K >= 2048 - } else if (k <= 2048) { - // smaller block for less shared memory - RUN_L2_SELECT(64, 2048, 8); -#endif - - } else { - FAISS_ASSERT(false); - } - } - - CUDA_TEST_ERROR(); -} - -void runL2SelectMin(Tensor& productDistances, - Tensor& centroidDistances, - Tensor& bitset, - Tensor& outDistances, - Tensor& outIndices, - int k, - cudaStream_t stream) { - runL2SelectMin(productDistances, - centroidDistances, - bitset, - outDistances, - outIndices, - k, - stream); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/L2Select.cuh b/core/src/index/thirdparty/faiss/gpu/impl/L2Select.cuh deleted file mode 100644 index b29552d786..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/L2Select.cuh +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include - -namespace faiss { namespace gpu { - -void runL2SelectMin(Tensor& productDistances, - Tensor& centroidDistances, - Tensor& bitset, - Tensor& outDistances, - Tensor& outIndices, - int k, - cudaStream_t stream); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/Metrics.cuh b/core/src/index/thirdparty/faiss/gpu/impl/Metrics.cuh deleted file mode 100644 index 5b9feac3ee..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/Metrics.cuh +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -namespace faiss { namespace gpu { - -/// List of supported metrics -inline bool isMetricSupported(MetricType mt) { - switch (mt) { - case MetricType::METRIC_INNER_PRODUCT: - case MetricType::METRIC_L2: - return true; - default: - return false; - } -} - -/// Sort direction per each metric -inline bool metricToSortDirection(MetricType mt) { - switch (mt) { - case MetricType::METRIC_INNER_PRODUCT: - // highest - return true; - case MetricType::METRIC_L2: - // lowest - return false; - default: - // unhandled metric - FAISS_ASSERT(false); - return false; - } -} - -struct L2Metric { - static inline __device__ float distance(float a, float b) { - float d = a - b; - return d * d; - } -}; - -struct IPMetric { - static inline __device__ float distance(float a, float b) { - return a * b; - } -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/PQCodeDistances-inl.cuh b/core/src/index/thirdparty/faiss/gpu/impl/PQCodeDistances-inl.cuh deleted file mode 100644 index 520a8bcafb..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/PQCodeDistances-inl.cuh +++ /dev/null @@ -1,574 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -// Kernel responsible for calculating distance from residual vector to -// each product quantizer code centroid -template -__global__ void -__launch_bounds__(288, 4) -pqCodeDistances(Tensor queries, - int queriesPerBlock, - Tensor coarseCentroids, - Tensor pqCentroids, - Tensor topQueryToCentroid, - // (query id)(coarse)(subquantizer)(code) -> dist - Tensor outCodeDistances) { - const auto numSubQuantizers = pqCentroids.getSize(0); - const auto dimsPerSubQuantizer = pqCentroids.getSize(1); - assert(DimsPerSubQuantizer == dimsPerSubQuantizer); - const auto codesPerSubQuantizer = pqCentroids.getSize(2); - - bool isLoadingThread = threadIdx.x >= codesPerSubQuantizer; - int loadingThreadId = threadIdx.x - codesPerSubQuantizer; - - extern __shared__ float smem[]; - - // Each thread calculates a single code - float subQuantizerData[DimsPerSubQuantizer]; - - auto code = threadIdx.x; - auto subQuantizer = blockIdx.y; - - // Each thread will load the pq centroid data for the code that it - // is processing -#pragma unroll - for (int i = 0; i < DimsPerSubQuantizer; ++i) { - subQuantizerData[i] = pqCentroids[subQuantizer][i][code].ldg(); - } - - // Where we store our query vector - float* smemQuery = smem; - - // Where we store our residual vector; this is double buffered so we - // can be loading the next one while processing the current one - float* smemResidual1 = &smemQuery[DimsPerSubQuantizer]; - float* smemResidual2 = &smemResidual1[DimsPerSubQuantizer]; - - // Where we pre-load the coarse centroid IDs - int* coarseIds = (int*) &smemResidual2[DimsPerSubQuantizer]; - - // Each thread is calculating the distance for a single code, - // performing the reductions locally - - // Handle multiple queries per block - auto startQueryId = blockIdx.x * queriesPerBlock; - auto numQueries = queries.getSize(0) - startQueryId; - if (numQueries > queriesPerBlock) { - numQueries = queriesPerBlock; - } - - for (int query = 0; query < numQueries; ++query) { - auto queryId = startQueryId + query; - - auto querySubQuantizer = - queries[queryId][subQuantizer * DimsPerSubQuantizer].data(); - - // Load current query vector - for (int i = threadIdx.x; i < DimsPerSubQuantizer; i += blockDim.x) { - smemQuery[i] = querySubQuantizer[i]; - } - - // Load list of coarse centroids found - for (int i = threadIdx.x; - i < topQueryToCentroid.getSize(1); i += blockDim.x) { - coarseIds[i] = topQueryToCentroid[queryId][i]; - } - - // We need coarseIds below - // FIXME: investigate loading separately, so we don't need this - __syncthreads(); - - // Preload first buffer of residual data - if (isLoadingThread) { - for (int i = loadingThreadId; - i < DimsPerSubQuantizer; - i += blockDim.x - codesPerSubQuantizer) { - auto coarseId = coarseIds[0]; - // In case NaNs were in the original query data - coarseId = coarseId == -1 ? 0 : coarseId; - auto coarseCentroidSubQuantizer = - coarseCentroids[coarseId][subQuantizer * dimsPerSubQuantizer].data(); - - if (L2Distance) { - smemResidual1[i] = smemQuery[i] - - ConvertTo::to(coarseCentroidSubQuantizer[i]); - } else { - smemResidual1[i] = - ConvertTo::to(coarseCentroidSubQuantizer[i]); - } - } - } - - // The block walks the list for a single query - for (int coarse = 0; coarse < topQueryToCentroid.getSize(1); ++coarse) { - // Wait for smemResidual1 to be loaded - __syncthreads(); - - if (isLoadingThread) { - // Preload second buffer of residual data - for (int i = loadingThreadId; - i < DimsPerSubQuantizer; - i += blockDim.x - codesPerSubQuantizer) { - // FIXME: try always making this centroid id 0 so we can - // terminate - if (coarse != (topQueryToCentroid.getSize(1) - 1)) { - auto coarseId = coarseIds[coarse + 1]; - // In case NaNs were in the original query data - coarseId = coarseId == -1 ? 0 : coarseId; - - auto coarseCentroidSubQuantizer = - coarseCentroids[coarseId] - [subQuantizer * dimsPerSubQuantizer].data(); - - if (L2Distance) { - smemResidual2[i] = smemQuery[i] - - ConvertTo::to(coarseCentroidSubQuantizer[i]); - } else { - smemResidual2[i] = - ConvertTo::to(coarseCentroidSubQuantizer[i]); - } - } - } - } else { - // These are the processing threads - float dist = 0.0f; - - constexpr int kUnroll = 4; - constexpr int kRemainder = DimsPerSubQuantizer % kUnroll; - constexpr int kRemainderBase = DimsPerSubQuantizer - kRemainder; - float vals[kUnroll]; - - // Calculate residual - pqCentroid for each dim that we're - // processing - - // Unrolled loop - if (L2Distance) { -#pragma unroll - for (int i = 0; i < DimsPerSubQuantizer / kUnroll; ++i) { -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - vals[j] = smemResidual1[i * kUnroll + j]; - } - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - vals[j] -= subQuantizerData[i * kUnroll + j]; - } - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - vals[j] *= vals[j]; - } - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - dist += vals[j]; - } - } - } else { - // Inner product: query slice against the reconstructed sub-quantizer - // for this coarse cell (query o (centroid + subQCentroid)) -#pragma unroll - for (int i = 0; i < DimsPerSubQuantizer / kUnroll; ++i) { -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - vals[j] = smemResidual1[i * kUnroll + j]; - } - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - vals[j] += subQuantizerData[i * kUnroll + j]; - } - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - vals[j] *= smemQuery[i * kUnroll + j]; - } - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - dist += vals[j]; - } - } - } - - // Remainder loop - if (L2Distance) { -#pragma unroll - for (int j = 0; j < kRemainder; ++j) { - vals[j] = smemResidual1[kRemainderBase + j]; - } - -#pragma unroll - for (int j = 0; j < kRemainder; ++j) { - vals[j] -= subQuantizerData[kRemainderBase + j]; - } - -#pragma unroll - for (int j = 0; j < kRemainder; ++j) { - vals[j] *= vals[j]; - } - } else { - // Inner product - // Inner product: query slice against the reconstructed sub-quantizer - // for this coarse cell (query o (centroid + subQCentroid)) -#pragma unroll - for (int j = 0; j < kRemainder; ++j) { - vals[j] = smemResidual1[kRemainderBase + j]; - } - -#pragma unroll - for (int j = 0; j < kRemainder; ++j) { - vals[j] += subQuantizerData[kRemainderBase + j]; - } - -#pragma unroll - for (int j = 0; j < kRemainder; ++j) { - vals[j] *= smemQuery[kRemainderBase + j]; - } - } - -#pragma unroll - for (int j = 0; j < kRemainder; ++j) { - dist += vals[j]; - } - - // We have the distance for our code; write it out - outCodeDistances[queryId][coarse][subQuantizer][code] = - ConvertTo::to(dist); - } // !isLoadingThread - - // Swap residual buffers - float* tmp = smemResidual1; - smemResidual1 = smemResidual2; - smemResidual2 = tmp; - } - } -} - -template -__global__ void -residualVector(Tensor queries, - Tensor coarseCentroids, - Tensor topQueryToCentroid, - int numSubDim, - // output is transposed: - // (sub q)(query id)(centroid id)(sub dim) - Tensor residual) { - // block x is query id - // block y is centroid id - // thread x is dim - auto queryId = blockIdx.x; - auto centroidId = blockIdx.y; - - int realCentroidId = topQueryToCentroid[queryId][centroidId]; - - for (int dim = threadIdx.x; dim < queries.getSize(1); dim += blockDim.x) { - float q = queries[queryId][dim]; - float c = ConvertTo::to(coarseCentroids[realCentroidId][dim]); - - residual[dim / numSubDim][queryId][centroidId][dim % numSubDim] = q - c; - } -} - -template -void -runResidualVector(Tensor& pqCentroids, - Tensor& queries, - Tensor& coarseCentroids, - Tensor& topQueryToCentroid, - Tensor& residual, - cudaStream_t stream) { - auto grid = - dim3(topQueryToCentroid.getSize(0), topQueryToCentroid.getSize(1)); - auto block = dim3(std::min(queries.getSize(1), getMaxThreadsCurrentDevice())); - - residualVector<<>>( - queries, coarseCentroids, topQueryToCentroid, pqCentroids.getSize(1), - residual); - - CUDA_TEST_ERROR(); -} - -template -void -runPQCodeDistancesMM(Tensor& pqCentroids, - Tensor& queries, - Tensor& coarseCentroids, - Tensor& topQueryToCentroid, - NoTypeTensor<4, true>& outCodeDistances, - bool useFloat16Lookup, - DeviceMemory& mem, - cublasHandle_t handle, - cudaStream_t stream) { - // Calculate (q - c) residual vector - // (sub q)(query id)(centroid id)(sub dim) - DeviceTensor residual( - mem, - {pqCentroids.getSize(0), - topQueryToCentroid.getSize(0), - topQueryToCentroid.getSize(1), - pqCentroids.getSize(1)}, - stream); - - runResidualVector(pqCentroids, queries, - coarseCentroids, topQueryToCentroid, - residual, stream); - - // Calculate ||q - c||^2 - DeviceTensor residualNorms( - mem, - {pqCentroids.getSize(0) * - topQueryToCentroid.getSize(0) * - topQueryToCentroid.getSize(1)}, - stream); - - auto residualView2 = residual.view<2>( - {pqCentroids.getSize(0) * - topQueryToCentroid.getSize(0) * - topQueryToCentroid.getSize(1), - pqCentroids.getSize(1)}); - - runL2Norm(residualView2, true, residualNorms, true, stream); - - // Perform a batch MM: - // (sub q) x {(q * c)(sub dim) x (sub dim)(code)} => - // (sub q) x {(q * c)(code)} - auto residualView3 = residual.view<3>( - {pqCentroids.getSize(0), - topQueryToCentroid.getSize(0) * topQueryToCentroid.getSize(1), - pqCentroids.getSize(1)}); - - DeviceTensor residualDistance( - mem, - {pqCentroids.getSize(0), - topQueryToCentroid.getSize(0) * topQueryToCentroid.getSize(1), - pqCentroids.getSize(2)}, - stream); - - runIteratedMatrixMult(residualDistance, false, - residualView3, false, - pqCentroids, false, - -2.0f, 0.0f, - handle, - stream); - - // Sum ||q - c||^2 along rows - auto residualDistanceView2 = residualDistance.view<2>( - {pqCentroids.getSize(0) * - topQueryToCentroid.getSize(0) * - topQueryToCentroid.getSize(1), - pqCentroids.getSize(2)}); - - runSumAlongRows(residualNorms, residualDistanceView2, false, stream); - - Tensor outCodeDistancesF; - DeviceTensor outCodeDistancesFloatMem; - - if (useFloat16Lookup) { - outCodeDistancesFloatMem = DeviceTensor( - mem, {outCodeDistances.getSize(0), - outCodeDistances.getSize(1), - outCodeDistances.getSize(2), - outCodeDistances.getSize(3)}, - stream); - - outCodeDistancesF = outCodeDistancesFloatMem; - } else { - outCodeDistancesF = outCodeDistances.toTensor(); - } - - // Transpose -2(sub q)(q * c)(code) to -2(q * c)(sub q)(code) (which - // is where we build our output distances) - auto outCodeDistancesView = outCodeDistancesF.view<3>( - {topQueryToCentroid.getSize(0) * topQueryToCentroid.getSize(1), - outCodeDistances.getSize(2), - outCodeDistances.getSize(3)}); - - runTransposeAny(residualDistance, 0, 1, outCodeDistancesView, stream); - - // Calculate code norms per each sub-dim - // (sub q)(sub dim)(code) is pqCentroids - // transpose to (sub q)(code)(sub dim) - DeviceTensor pqCentroidsTranspose( - mem, - {pqCentroids.getSize(0), pqCentroids.getSize(2), pqCentroids.getSize(1)}, - stream); - - runTransposeAny(pqCentroids, 1, 2, pqCentroidsTranspose, stream); - - auto pqCentroidsTransposeView = pqCentroidsTranspose.view<2>( - {pqCentroids.getSize(0) * pqCentroids.getSize(2), - pqCentroids.getSize(1)}); - - DeviceTensor pqCentroidsNorm( - mem, - {pqCentroids.getSize(0) * pqCentroids.getSize(2)}, - stream); - - runL2Norm(pqCentroidsTransposeView, true, pqCentroidsNorm, true, stream); - - // View output as (q * c)(sub q * code), and add centroid norm to - // each row - auto outDistancesCodeViewCols = outCodeDistancesView.view<2>( - {topQueryToCentroid.getSize(0) * topQueryToCentroid.getSize(1), - outCodeDistances.getSize(2) * outCodeDistances.getSize(3)}); - - runSumAlongColumns(pqCentroidsNorm, outDistancesCodeViewCols, stream); - -#ifdef FAISS_USE_FLOAT16 - if (useFloat16Lookup) { - // Need to convert back - auto outCodeDistancesH = outCodeDistances.toTensor(); - convertTensor(stream, - outCodeDistancesF, - outCodeDistancesH); - } -#endif -} - -template -void -runPQCodeDistances(Tensor& pqCentroids, - Tensor& queries, - Tensor& coarseCentroids, - Tensor& topQueryToCentroid, - NoTypeTensor<4, true>& outCodeDistances, - bool l2Distance, - bool useFloat16Lookup, - cudaStream_t stream) { - const auto numSubQuantizers = pqCentroids.getSize(0); - const auto dimsPerSubQuantizer = pqCentroids.getSize(1); - const auto codesPerSubQuantizer = pqCentroids.getSize(2); - - // FIXME: tune - // Reuse of pq centroid data is based on both # of queries * nprobe, - // and we should really be tiling in both dimensions - constexpr int kQueriesPerBlock = 8; - - auto grid = dim3(utils::divUp(queries.getSize(0), kQueriesPerBlock), - numSubQuantizers); - - // Reserve one block of threads for double buffering - // FIXME: probably impractical for large # of dims? - auto loadingThreads = utils::roundUp(dimsPerSubQuantizer, kWarpSize); - auto block = dim3(codesPerSubQuantizer + loadingThreads); - - auto smem = (3 * dimsPerSubQuantizer) * sizeof(float) - + topQueryToCentroid.getSize(1) * sizeof(int); - -#ifdef FAISS_USE_FLOAT16 -#define RUN_CODE(DIMS, L2) \ - do { \ - if (useFloat16Lookup) { \ - auto outCodeDistancesT = outCodeDistances.toTensor(); \ - \ - pqCodeDistances<<>>( \ - queries, kQueriesPerBlock, \ - coarseCentroids, pqCentroids, \ - topQueryToCentroid, outCodeDistancesT); \ - } else { \ - auto outCodeDistancesT = outCodeDistances.toTensor(); \ - \ - pqCodeDistances<<>>( \ - queries, kQueriesPerBlock, \ - coarseCentroids, pqCentroids, \ - topQueryToCentroid, outCodeDistancesT); \ - } \ - } while (0) -#else -#define RUN_CODE(DIMS, L2) \ - do { \ - auto outCodeDistancesT = outCodeDistances.toTensor(); \ - pqCodeDistances<<>>( \ - queries, kQueriesPerBlock, \ - coarseCentroids, pqCentroids, \ - topQueryToCentroid, outCodeDistancesT); \ - } while (0) -#endif - -#define CODE_L2(DIMS) \ - do { \ - if (l2Distance) { \ - RUN_CODE(DIMS, true); \ - } else { \ - RUN_CODE(DIMS, false); \ - } \ - } while (0) - - switch (dimsPerSubQuantizer) { - case 1: - CODE_L2(1); - break; - case 2: - CODE_L2(2); - break; - case 3: - CODE_L2(3); - break; - case 4: - CODE_L2(4); - break; - case 6: - CODE_L2(6); - break; - case 8: - CODE_L2(8); - break; - case 10: - CODE_L2(10); - break; - case 12: - CODE_L2(12); - break; - case 16: - CODE_L2(16); - break; - case 20: - CODE_L2(20); - break; - case 24: - CODE_L2(24); - break; - case 28: - CODE_L2(28); - break; - case 32: - CODE_L2(32); - break; - // FIXME: larger sizes require too many registers - we need the - // MM implementation working - default: - FAISS_THROW_MSG("Too many dimensions (>32) per subquantizer " - "not currently supported"); - } - -#undef RUN_CODE -#undef CODE_L2 - - CUDA_TEST_ERROR(); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/PQCodeDistances.cu b/core/src/index/thirdparty/faiss/gpu/impl/PQCodeDistances.cu deleted file mode 100644 index eec8852310..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/PQCodeDistances.cu +++ /dev/null @@ -1,589 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -template -struct Converter { -}; - -#ifdef FAISS_USE_FLOAT16 -template <> -struct Converter { - inline static __device__ half to(float v) { return __float2half(v); } -}; -#endif - -template <> -struct Converter { - inline static __device__ float to(float v) { return v; } -}; - -// Kernel responsible for calculating distance from residual vector to -// each product quantizer code centroid -template -__global__ void -__launch_bounds__(288, 4) -pqCodeDistances(Tensor queries, - int queriesPerBlock, - Tensor coarseCentroids, - Tensor pqCentroids, - Tensor topQueryToCentroid, - // (query id)(coarse)(subquantizer)(code) -> dist - Tensor outCodeDistances) { - const auto numSubQuantizers = pqCentroids.getSize(0); - const auto dimsPerSubQuantizer = pqCentroids.getSize(1); - assert(DimsPerSubQuantizer == dimsPerSubQuantizer); - const auto codesPerSubQuantizer = pqCentroids.getSize(2); - - bool isLoadingThread = threadIdx.x >= codesPerSubQuantizer; - int loadingThreadId = threadIdx.x - codesPerSubQuantizer; - - extern __shared__ float smem[]; - - // Each thread calculates a single code - float subQuantizerData[DimsPerSubQuantizer]; - - auto code = threadIdx.x; - auto subQuantizer = blockIdx.y; - - // Each thread will load the pq centroid data for the code that it - // is processing -#pragma unroll - for (int i = 0; i < DimsPerSubQuantizer; ++i) { - subQuantizerData[i] = pqCentroids[subQuantizer][i][code].ldg(); - } - - // Where we store our query vector - float* smemQuery = smem; - - // Where we store our residual vector; this is double buffered so we - // can be loading the next one while processing the current one - float* smemResidual1 = &smemQuery[DimsPerSubQuantizer]; - float* smemResidual2 = &smemResidual1[DimsPerSubQuantizer]; - - // Where we pre-load the coarse centroid IDs - int* coarseIds = (int*) &smemResidual2[DimsPerSubQuantizer]; - - // Each thread is calculating the distance for a single code, - // performing the reductions locally - - // Handle multiple queries per block - auto startQueryId = blockIdx.x * queriesPerBlock; - auto numQueries = queries.getSize(0) - startQueryId; - if (numQueries > queriesPerBlock) { - numQueries = queriesPerBlock; - } - - for (int query = 0; query < numQueries; ++query) { - auto queryId = startQueryId + query; - - auto querySubQuantizer = - queries[queryId][subQuantizer * DimsPerSubQuantizer].data(); - - // Load current query vector - for (int i = threadIdx.x; i < DimsPerSubQuantizer; i += blockDim.x) { - smemQuery[i] = querySubQuantizer[i]; - } - - // Load list of coarse centroids found - for (int i = threadIdx.x; - i < topQueryToCentroid.getSize(1); i += blockDim.x) { - coarseIds[i] = topQueryToCentroid[queryId][i]; - } - - // We need coarseIds below - // FIXME: investigate loading separately, so we don't need this - __syncthreads(); - - // Preload first buffer of residual data - if (isLoadingThread) { - for (int i = loadingThreadId; - i < DimsPerSubQuantizer; - i += blockDim.x - codesPerSubQuantizer) { - auto coarseId = coarseIds[0]; - // In case NaNs were in the original query data - coarseId = coarseId == -1 ? 0 : coarseId; - auto coarseCentroidSubQuantizer = - coarseCentroids[coarseId][subQuantizer * dimsPerSubQuantizer].data(); - - if (L2Distance) { - smemResidual1[i] = smemQuery[i] - coarseCentroidSubQuantizer[i]; - } else { - smemResidual1[i] = coarseCentroidSubQuantizer[i]; - } - } - } - - // The block walks the list for a single query - for (int coarse = 0; coarse < topQueryToCentroid.getSize(1); ++coarse) { - // Wait for smemResidual1 to be loaded - __syncthreads(); - - if (isLoadingThread) { - // Preload second buffer of residual data - for (int i = loadingThreadId; - i < DimsPerSubQuantizer; - i += blockDim.x - codesPerSubQuantizer) { - // FIXME: try always making this centroid id 0 so we can - // terminate - if (coarse != (topQueryToCentroid.getSize(1) - 1)) { - auto coarseId = coarseIds[coarse + 1]; - // In case NaNs were in the original query data - coarseId = coarseId == -1 ? 0 : coarseId; - - auto coarseCentroidSubQuantizer = - coarseCentroids[coarseId] - [subQuantizer * dimsPerSubQuantizer].data(); - - if (L2Distance) { - smemResidual2[i] = smemQuery[i] - coarseCentroidSubQuantizer[i]; - } else { - smemResidual2[i] = coarseCentroidSubQuantizer[i]; - } - } - } - } else { - // These are the processing threads - float dist = 0.0f; - - constexpr int kUnroll = 4; - constexpr int kRemainder = DimsPerSubQuantizer % kUnroll; - constexpr int kRemainderBase = DimsPerSubQuantizer - kRemainder; - float vals[kUnroll]; - - // Calculate residual - pqCentroid for each dim that we're - // processing - - // Unrolled loop - if (L2Distance) { -#pragma unroll - for (int i = 0; i < DimsPerSubQuantizer / kUnroll; ++i) { -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - vals[j] = smemResidual1[i * kUnroll + j]; - } - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - vals[j] -= subQuantizerData[i * kUnroll + j]; - } - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - vals[j] *= vals[j]; - } - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - dist += vals[j]; - } - } - } else { - // Inner product: query slice against the reconstructed sub-quantizer - // for this coarse cell (query o (centroid + subQCentroid)) -#pragma unroll - for (int i = 0; i < DimsPerSubQuantizer / kUnroll; ++i) { -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - vals[j] = smemResidual1[i * kUnroll + j]; - } - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - vals[j] += subQuantizerData[i * kUnroll + j]; - } - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - vals[j] *= smemQuery[i * kUnroll + j]; - } - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - dist += vals[j]; - } - } - } - - // Remainder loop - if (L2Distance) { -#pragma unroll - for (int j = 0; j < kRemainder; ++j) { - vals[j] = smemResidual1[kRemainderBase + j]; - } - -#pragma unroll - for (int j = 0; j < kRemainder; ++j) { - vals[j] -= subQuantizerData[kRemainderBase + j]; - } - -#pragma unroll - for (int j = 0; j < kRemainder; ++j) { - vals[j] *= vals[j]; - } - } else { - // Inner product - // Inner product: query slice against the reconstructed sub-quantizer - // for this coarse cell (query o (centroid + subQCentroid)) -#pragma unroll - for (int j = 0; j < kRemainder; ++j) { - vals[j] = smemResidual1[kRemainderBase + j]; - } - -#pragma unroll - for (int j = 0; j < kRemainder; ++j) { - vals[j] += subQuantizerData[kRemainderBase + j]; - } - -#pragma unroll - for (int j = 0; j < kRemainder; ++j) { - vals[j] *= smemQuery[kRemainderBase + j]; - } - } - -#pragma unroll - for (int j = 0; j < kRemainder; ++j) { - dist += vals[j]; - } - - // We have the distance for our code; write it out - outCodeDistances[queryId][coarse][subQuantizer][code] = - Converter::to(dist); - } // !isLoadingThread - - // Swap residual buffers - float* tmp = smemResidual1; - smemResidual1 = smemResidual2; - smemResidual2 = tmp; - } - } -} - -__global__ void -residualVector(Tensor queries, - Tensor coarseCentroids, - Tensor topQueryToCentroid, - int numSubDim, - // output is transposed: - // (sub q)(query id)(centroid id)(sub dim) - Tensor residual) { - // block x is query id - // block y is centroid id - // thread x is dim - auto queryId = blockIdx.x; - auto centroidId = blockIdx.y; - - int realCentroidId = topQueryToCentroid[queryId][centroidId]; - - for (int dim = threadIdx.x; dim < queries.getSize(1); dim += blockDim.x) { - float q = queries[queryId][dim]; - float c = coarseCentroids[realCentroidId][dim]; - - residual[dim / numSubDim][queryId][centroidId][dim % numSubDim] = - q - c; - } -} - -void -runResidualVector(Tensor& pqCentroids, - Tensor& queries, - Tensor& coarseCentroids, - Tensor& topQueryToCentroid, - Tensor& residual, - cudaStream_t stream) { - auto grid = - dim3(topQueryToCentroid.getSize(0), topQueryToCentroid.getSize(1)); - auto block = dim3(std::min(queries.getSize(1), getMaxThreadsCurrentDevice())); - - residualVector<<>>( - queries, coarseCentroids, topQueryToCentroid, pqCentroids.getSize(1), - residual); - - CUDA_TEST_ERROR(); -} - -void -runPQCodeDistancesMM(Tensor& pqCentroids, - Tensor& queries, - Tensor& coarseCentroids, - Tensor& topQueryToCentroid, - NoTypeTensor<4, true>& outCodeDistances, - bool useFloat16Lookup, - DeviceMemory& mem, - cublasHandle_t handle, - cudaStream_t stream) { - // Calculate (q - c) residual vector - // (sub q)(query id)(centroid id)(sub dim) - DeviceTensor residual( - mem, - {pqCentroids.getSize(0), - topQueryToCentroid.getSize(0), - topQueryToCentroid.getSize(1), - pqCentroids.getSize(1)}, - stream); - - runResidualVector(pqCentroids, queries, - coarseCentroids, topQueryToCentroid, - residual, stream); - - // Calculate ||q - c||^2 - DeviceTensor residualNorms( - mem, - {pqCentroids.getSize(0) * - topQueryToCentroid.getSize(0) * - topQueryToCentroid.getSize(1)}, - stream); - - auto residualView2 = residual.view<2>( - {pqCentroids.getSize(0) * - topQueryToCentroid.getSize(0) * - topQueryToCentroid.getSize(1), - pqCentroids.getSize(1)}); - - runL2Norm(residualView2, true, residualNorms, true, stream); - - // Perform a batch MM: - // (sub q) x {(q * c)(sub dim) x (sub dim)(code)} => - // (sub q) x {(q * c)(code)} - auto residualView3 = residual.view<3>( - {pqCentroids.getSize(0), - topQueryToCentroid.getSize(0) * topQueryToCentroid.getSize(1), - pqCentroids.getSize(1)}); - - DeviceTensor residualDistance( - mem, - {pqCentroids.getSize(0), - topQueryToCentroid.getSize(0) * topQueryToCentroid.getSize(1), - pqCentroids.getSize(2)}, - stream); - - runIteratedMatrixMult(residualDistance, false, - residualView3, false, - pqCentroids, false, - -2.0f, 0.0f, - handle, - stream); - - // Sum ||q - c||^2 along rows - auto residualDistanceView2 = residualDistance.view<2>( - {pqCentroids.getSize(0) * - topQueryToCentroid.getSize(0) * - topQueryToCentroid.getSize(1), - pqCentroids.getSize(2)}); - - runSumAlongRows(residualNorms, residualDistanceView2, false, stream); - - Tensor outCodeDistancesF; - DeviceTensor outCodeDistancesFloatMem; - -#ifdef FAISS_USE_FLOAT16 - if (useFloat16Lookup) { - outCodeDistancesFloatMem = DeviceTensor( - mem, {outCodeDistances.getSize(0), - outCodeDistances.getSize(1), - outCodeDistances.getSize(2), - outCodeDistances.getSize(3)}, - stream); - - outCodeDistancesF = outCodeDistancesFloatMem; - } else { - outCodeDistancesF = outCodeDistances.toTensor(); - } -#else - outCodeDistancesF = outCodeDistances.toTensor(); -#endif - - // Transpose -2(sub q)(q * c)(code) to -2(q * c)(sub q)(code) (which - // is where we build our output distances) - auto outCodeDistancesView = outCodeDistancesF.view<3>( - {topQueryToCentroid.getSize(0) * topQueryToCentroid.getSize(1), - outCodeDistances.getSize(2), - outCodeDistances.getSize(3)}); - - runTransposeAny(residualDistance, 0, 1, outCodeDistancesView, stream); - - // Calculate code norms per each sub-dim - // (sub q)(sub dim)(code) is pqCentroids - // transpose to (sub q)(code)(sub dim) - DeviceTensor pqCentroidsTranspose( - mem, - {pqCentroids.getSize(0), pqCentroids.getSize(2), pqCentroids.getSize(1)}, - stream); - - runTransposeAny(pqCentroids, 1, 2, pqCentroidsTranspose, stream); - - auto pqCentroidsTransposeView = pqCentroidsTranspose.view<2>( - {pqCentroids.getSize(0) * pqCentroids.getSize(2), - pqCentroids.getSize(1)}); - - DeviceTensor pqCentroidsNorm( - mem, - {pqCentroids.getSize(0) * pqCentroids.getSize(2)}, - stream); - - runL2Norm(pqCentroidsTransposeView, true, pqCentroidsNorm, true, stream); - - // View output as (q * c)(sub q * code), and add centroid norm to - // each row - auto outDistancesCodeViewCols = outCodeDistancesView.view<2>( - {topQueryToCentroid.getSize(0) * topQueryToCentroid.getSize(1), - outCodeDistances.getSize(2) * outCodeDistances.getSize(3)}); - - runSumAlongColumns(pqCentroidsNorm, outDistancesCodeViewCols, stream); - -#ifdef FAISS_USE_FLOAT16 - if (useFloat16Lookup) { - // Need to convert back - auto outCodeDistancesH = outCodeDistances.toTensor(); - convertTensor(stream, - outCodeDistancesF, - outCodeDistancesH); - } -#endif -} - -void -runPQCodeDistances(Tensor& pqCentroids, - Tensor& queries, - Tensor& coarseCentroids, - Tensor& topQueryToCentroid, - NoTypeTensor<4, true>& outCodeDistances, - bool l2Distance, - bool useFloat16Lookup, - cudaStream_t stream) { - const auto numSubQuantizers = pqCentroids.getSize(0); - const auto dimsPerSubQuantizer = pqCentroids.getSize(1); - const auto codesPerSubQuantizer = pqCentroids.getSize(2); - - // FIXME: tune - // Reuse of pq centroid data is based on both # of queries * nprobe, - // and we should really be tiling in both dimensions - constexpr int kQueriesPerBlock = 8; - - auto grid = dim3(utils::divUp(queries.getSize(0), kQueriesPerBlock), - numSubQuantizers); - - // Reserve one block of threads for double buffering - // FIXME: probably impractical for large # of dims? - auto loadingThreads = utils::roundUp(dimsPerSubQuantizer, kWarpSize); - auto block = dim3(codesPerSubQuantizer + loadingThreads); - - auto smem = (3 * dimsPerSubQuantizer) * sizeof(float) - + topQueryToCentroid.getSize(1) * sizeof(int); - -#ifdef FAISS_USE_FLOAT16 -#define RUN_CODE(DIMS, L2) \ - do { \ - if (useFloat16Lookup) { \ - auto outCodeDistancesT = outCodeDistances.toTensor(); \ - \ - pqCodeDistances<<>>( \ - queries, kQueriesPerBlock, \ - coarseCentroids, pqCentroids, \ - topQueryToCentroid, outCodeDistancesT); \ - } else { \ - auto outCodeDistancesT = outCodeDistances.toTensor(); \ - \ - pqCodeDistances<<>>( \ - queries, kQueriesPerBlock, \ - coarseCentroids, pqCentroids, \ - topQueryToCentroid, outCodeDistancesT); \ - } \ - } while (0) -#else -#define RUN_CODE(DIMS, L2) \ - do { \ - if(!useFloat16Lookup){ \ - auto outCodeDistancesT = outCodeDistances.toTensor(); \ - \ - pqCodeDistances<<>>( \ - queries, kQueriesPerBlock, \ - coarseCentroids, pqCentroids, \ - topQueryToCentroid, outCodeDistancesT); \ - } \ - } while (0) -#endif - -#define CODE_L2(DIMS) \ - do { \ - if (l2Distance) { \ - RUN_CODE(DIMS, true); \ - } else { \ - RUN_CODE(DIMS, false); \ - } \ - } while (0) - - switch (dimsPerSubQuantizer) { - case 1: - CODE_L2(1); - break; - case 2: - CODE_L2(2); - break; - case 3: - CODE_L2(3); - break; - case 4: - CODE_L2(4); - break; - case 6: - CODE_L2(6); - break; - case 8: - CODE_L2(8); - break; - case 10: - CODE_L2(10); - break; - case 12: - CODE_L2(12); - break; - case 16: - CODE_L2(16); - break; - case 20: - CODE_L2(20); - break; - case 24: - CODE_L2(24); - break; - case 28: - CODE_L2(28); - break; - case 32: - CODE_L2(32); - break; - // FIXME: larger sizes require too many registers - we need the - // MM implementation working - default: - FAISS_THROW_MSG("Too many dimensions (>32) per subquantizer " - "not currently supported"); - } - -#undef RUN_CODE -#undef CODE_L2 - - CUDA_TEST_ERROR(); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/PQCodeDistances.cuh b/core/src/index/thirdparty/faiss/gpu/impl/PQCodeDistances.cuh deleted file mode 100644 index 0add947f2c..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/PQCodeDistances.cuh +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include - -namespace faiss { namespace gpu { - -class DeviceMemory; - -/// pqCentroids is of the form (sub q)(sub dim)(code id) -/// Calculates the distance from the (query - centroid) residual to -/// each sub-code vector, for the given list of query results in -/// topQueryToCentroid -template -void runPQCodeDistances(Tensor& pqCentroids, - Tensor& queries, - Tensor& coarseCentroids, - Tensor& topQueryToCentroid, - NoTypeTensor<4, true>& outCodeDistances, - bool l2Distance, - bool useFloat16Lookup, - cudaStream_t stream); - -template -void runPQCodeDistancesMM(Tensor& pqCentroids, - Tensor& queries, - Tensor& coarseCentroids, - Tensor& topQueryToCentroid, - NoTypeTensor<4, true>& outCodeDistances, - bool useFloat16Lookup, - DeviceMemory& mem, - cublasHandle_t handle, - cudaStream_t stream); - -} } // namespace - -#include diff --git a/core/src/index/thirdparty/faiss/gpu/impl/PQCodeLoad.cuh b/core/src/index/thirdparty/faiss/gpu/impl/PQCodeLoad.cuh deleted file mode 100644 index da933b1d00..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/PQCodeLoad.cuh +++ /dev/null @@ -1,357 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include - -namespace faiss { namespace gpu { - -#if __CUDA_ARCH__ >= 350 -// Use the CC 3.5+ read-only texture cache (nc) -#define LD_NC_V1 "ld.global.cs.nc.u32" -#define LD_NC_V2 "ld.global.cs.nc.v2.u32" -#define LD_NC_V4 "ld.global.cs.nc.v4.u32" -#else -// Read normally -#define LD_NC_V1 "ld.global.cs.u32" -#define LD_NC_V2 "ld.global.cs.v2.u32" -#define LD_NC_V4 "ld.global.cs.v4.u32" -#endif // __CUDA_ARCH__ - -/// -/// This file contains loader functions for PQ codes of various byte -/// length. -/// - -// Type-specific wrappers around the PTX bfe.* instruction, for -// quantization code extraction -inline __device__ unsigned int getByte(unsigned char v, - int pos, - int width) { - return v; -} - -inline __device__ unsigned int getByte(unsigned short v, - int pos, - int width) { - return getBitfield((unsigned int) v, pos, width); -} - -inline __device__ unsigned int getByte(unsigned int v, - int pos, - int width) { - return getBitfield(v, pos, width); -} - -inline __device__ unsigned int getByte(unsigned long v, - int pos, - int width) { - return getBitfield(v, pos, width); -} - -template -struct LoadCode32 {}; - -template<> -struct LoadCode32<1> { - static inline __device__ void load(unsigned int code32[1], - unsigned char* p, - int offset) { - p += offset * 1; - asm("ld.global.cs.u8 {%0}, [%1];" : - "=r"(code32[0]) : "l"(p)); - } -}; - -template<> -struct LoadCode32<2> { - static inline __device__ void load(unsigned int code32[1], - unsigned char* p, - int offset) { - p += offset * 2; - asm("ld.global.cs.u16 {%0}, [%1];" : - "=r"(code32[0]) : "l"(p)); - } -}; - -template<> -struct LoadCode32<3> { - static inline __device__ void load(unsigned int code32[1], - unsigned char* p, - int offset) { - p += offset * 3; - unsigned int a; - unsigned int b; - unsigned int c; - - // FIXME: this is a non-coalesced, unaligned, non-vectorized load - // unfortunately need to reorganize memory layout by warp - asm("ld.global.cs.u8 {%0}, [%1 + 0];" : - "=r"(a) : "l"(p)); - asm("ld.global.cs.u8 {%0}, [%1 + 1];" : - "=r"(b) : "l"(p)); - asm("ld.global.cs.u8 {%0}, [%1 + 2];" : - "=r"(c) : "l"(p)); - - // FIXME: this is also slow, since we have to recover the - // individual bytes loaded - code32[0] = (c << 16) | (b << 8) | a; - } -}; - -template<> -struct LoadCode32<4> { - static inline __device__ void load(unsigned int code32[1], - unsigned char* p, - int offset) { - p += offset * 4; - asm("ld.global.cs.u32 {%0}, [%1];" : - "=r"(code32[0]) : "l"(p)); - } -}; - -template<> -struct LoadCode32<8> { - static inline __device__ void load(unsigned int code32[2], - unsigned char* p, - int offset) { - p += offset * 8; - asm("ld.global.cs.v2.u32 {%0, %1}, [%2];" : - "=r"(code32[0]), "=r"(code32[1]) : "l"(p)); - } -}; - -template<> -struct LoadCode32<12> { - static inline __device__ void load(unsigned int code32[3], - unsigned char* p, - int offset) { - p += offset * 12; - // FIXME: this is a non-coalesced, unaligned, non-vectorized load - // unfortunately need to reorganize memory layout by warp - asm(LD_NC_V1 " {%0}, [%1 + 0];" : - "=r"(code32[0]) : "l"(p)); - asm(LD_NC_V1 " {%0}, [%1 + 4];" : - "=r"(code32[1]) : "l"(p)); - asm(LD_NC_V1 " {%0}, [%1 + 8];" : - "=r"(code32[2]) : "l"(p)); - } -}; - -template<> -struct LoadCode32<16> { - static inline __device__ void load(unsigned int code32[4], - unsigned char* p, - int offset) { - p += offset * 16; - asm("ld.global.cs.v4.u32 {%0, %1, %2, %3}, [%4];" : - "=r"(code32[0]), "=r"(code32[1]), - "=r"(code32[2]), "=r"(code32[3]) : "l"(p)); - } -}; - -template<> -struct LoadCode32<20> { - static inline __device__ void load(unsigned int code32[5], - unsigned char* p, - int offset) { - p += offset * 20; - // FIXME: this is a non-coalesced, unaligned, non-vectorized load - // unfortunately need to reorganize memory layout by warp - asm(LD_NC_V1 " {%0}, [%1 + 0];" : - "=r"(code32[0]) : "l"(p)); - asm(LD_NC_V1 " {%0}, [%1 + 4];" : - "=r"(code32[1]) : "l"(p)); - asm(LD_NC_V1 " {%0}, [%1 + 8];" : - "=r"(code32[2]) : "l"(p)); - asm(LD_NC_V1 " {%0}, [%1 + 12];" : - "=r"(code32[3]) : "l"(p)); - asm(LD_NC_V1 " {%0}, [%1 + 16];" : - "=r"(code32[4]) : "l"(p)); - } -}; - -template<> -struct LoadCode32<24> { - static inline __device__ void load(unsigned int code32[6], - unsigned char* p, - int offset) { - p += offset * 24; - // FIXME: this is a non-coalesced, unaligned, 2-vectorized load - // unfortunately need to reorganize memory layout by warp - asm(LD_NC_V2 " {%0, %1}, [%2 + 0];" : - "=r"(code32[0]), "=r"(code32[1]) : "l"(p)); - asm(LD_NC_V2 " {%0, %1}, [%2 + 8];" : - "=r"(code32[2]), "=r"(code32[3]) : "l"(p)); - asm(LD_NC_V2 " {%0, %1}, [%2 + 16];" : - "=r"(code32[4]), "=r"(code32[5]) : "l"(p)); - } -}; - -template<> -struct LoadCode32<28> { - static inline __device__ void load(unsigned int code32[7], - unsigned char* p, - int offset) { - p += offset * 28; - // FIXME: this is a non-coalesced, unaligned, non-vectorized load - // unfortunately need to reorganize memory layout by warp - asm(LD_NC_V1 " {%0}, [%1 + 0];" : - "=r"(code32[0]) : "l"(p)); - asm(LD_NC_V1 " {%0}, [%1 + 4];" : - "=r"(code32[1]) : "l"(p)); - asm(LD_NC_V1 " {%0}, [%1 + 8];" : - "=r"(code32[2]) : "l"(p)); - asm(LD_NC_V1 " {%0}, [%1 + 12];" : - "=r"(code32[3]) : "l"(p)); - asm(LD_NC_V1 " {%0}, [%1 + 16];" : - "=r"(code32[4]) : "l"(p)); - asm(LD_NC_V1 " {%0}, [%1 + 20];" : - "=r"(code32[5]) : "l"(p)); - asm(LD_NC_V1 " {%0}, [%1 + 24];" : - "=r"(code32[6]) : "l"(p)); - } -}; - -template<> -struct LoadCode32<32> { - static inline __device__ void load(unsigned int code32[8], - unsigned char* p, - int offset) { - p += offset * 32; - // FIXME: this is a non-coalesced load - // unfortunately need to reorganize memory layout by warp - asm(LD_NC_V4 " {%0, %1, %2, %3}, [%4];" : - "=r"(code32[0]), "=r"(code32[1]), - "=r"(code32[2]), "=r"(code32[3]) : "l"(p)); - asm(LD_NC_V4 " {%0, %1, %2, %3}, [%4 + 16];" : - "=r"(code32[4]), "=r"(code32[5]), - "=r"(code32[6]), "=r"(code32[7]) : "l"(p)); - } -}; - -template<> -struct LoadCode32<40> { - static inline __device__ void load(unsigned int code32[10], - unsigned char* p, - int offset) { - p += offset * 40; - // FIXME: this is a non-coalesced, unaligned, 2-vectorized load - // unfortunately need to reorganize memory layout by warp - asm(LD_NC_V2 " {%0, %1}, [%2 + 0];" : - "=r"(code32[0]), "=r"(code32[1]) : "l"(p)); - asm(LD_NC_V2 " {%0, %1}, [%2 + 8];" : - "=r"(code32[2]), "=r"(code32[3]) : "l"(p)); - asm(LD_NC_V2 " {%0, %1}, [%2 + 16];" : - "=r"(code32[4]), "=r"(code32[5]) : "l"(p)); - asm(LD_NC_V2 " {%0, %1}, [%2 + 24];" : - "=r"(code32[6]), "=r"(code32[7]) : "l"(p)); - asm(LD_NC_V2 " {%0, %1}, [%2 + 32];" : - "=r"(code32[8]), "=r"(code32[9]) : "l"(p)); - } -}; - -template<> -struct LoadCode32<48> { - static inline __device__ void load(unsigned int code32[12], - unsigned char* p, - int offset) { - p += offset * 48; - // FIXME: this is a non-coalesced load - // unfortunately need to reorganize memory layout by warp - asm(LD_NC_V4 " {%0, %1, %2, %3}, [%4];" : - "=r"(code32[0]), "=r"(code32[1]), - "=r"(code32[2]), "=r"(code32[3]) : "l"(p)); - asm(LD_NC_V4 " {%0, %1, %2, %3}, [%4 + 16];" : - "=r"(code32[4]), "=r"(code32[5]), - "=r"(code32[6]), "=r"(code32[7]) : "l"(p)); - asm(LD_NC_V4 " {%0, %1, %2, %3}, [%4 + 32];" : - "=r"(code32[8]), "=r"(code32[9]), - "=r"(code32[10]), "=r"(code32[11]) : "l"(p)); - } -}; - -template<> -struct LoadCode32<56> { - static inline __device__ void load(unsigned int code32[14], - unsigned char* p, - int offset) { - p += offset * 56; - // FIXME: this is a non-coalesced, unaligned, 2-vectorized load - // unfortunately need to reorganize memory layout by warp - asm(LD_NC_V2 " {%0, %1}, [%2 + 0];" : - "=r"(code32[0]), "=r"(code32[1]) : "l"(p)); - asm(LD_NC_V2 " {%0, %1}, [%2 + 8];" : - "=r"(code32[2]), "=r"(code32[3]) : "l"(p)); - asm(LD_NC_V2 " {%0, %1}, [%2 + 16];" : - "=r"(code32[4]), "=r"(code32[5]) : "l"(p)); - asm(LD_NC_V2 " {%0, %1}, [%2 + 24];" : - "=r"(code32[6]), "=r"(code32[7]) : "l"(p)); - asm(LD_NC_V2 " {%0, %1}, [%2 + 32];" : - "=r"(code32[8]), "=r"(code32[9]) : "l"(p)); - asm(LD_NC_V2 " {%0, %1}, [%2 + 40];" : - "=r"(code32[10]), "=r"(code32[11]) : "l"(p)); - asm(LD_NC_V2 " {%0, %1}, [%2 + 48];" : - "=r"(code32[12]), "=r"(code32[13]) : "l"(p)); - } -}; - -template<> -struct LoadCode32<64> { - static inline __device__ void load(unsigned int code32[16], - unsigned char* p, - int offset) { - p += offset * 64; - // FIXME: this is a non-coalesced load - // unfortunately need to reorganize memory layout by warp - asm(LD_NC_V4 " {%0, %1, %2, %3}, [%4];" : - "=r"(code32[0]), "=r"(code32[1]), - "=r"(code32[2]), "=r"(code32[3]) : "l"(p)); - asm(LD_NC_V4 " {%0, %1, %2, %3}, [%4 + 16];" : - "=r"(code32[4]), "=r"(code32[5]), - "=r"(code32[6]), "=r"(code32[7]) : "l"(p)); - asm(LD_NC_V4 " {%0, %1, %2, %3}, [%4 + 32];" : - "=r"(code32[8]), "=r"(code32[9]), - "=r"(code32[10]), "=r"(code32[11]) : "l"(p)); - asm(LD_NC_V4 " {%0, %1, %2, %3}, [%4 + 48];" : - "=r"(code32[12]), "=r"(code32[13]), - "=r"(code32[14]), "=r"(code32[15]) : "l"(p)); - } -}; - -template<> -struct LoadCode32<96> { - static inline __device__ void load(unsigned int code32[24], - unsigned char* p, - int offset) { - p += offset * 96; - // FIXME: this is a non-coalesced load - // unfortunately need to reorganize memory layout by warp - asm(LD_NC_V4 " {%0, %1, %2, %3}, [%4];" : - "=r"(code32[0]), "=r"(code32[1]), - "=r"(code32[2]), "=r"(code32[3]) : "l"(p)); - asm(LD_NC_V4 " {%0, %1, %2, %3}, [%4 + 16];" : - "=r"(code32[4]), "=r"(code32[5]), - "=r"(code32[6]), "=r"(code32[7]) : "l"(p)); - asm(LD_NC_V4 " {%0, %1, %2, %3}, [%4 + 32];" : - "=r"(code32[8]), "=r"(code32[9]), - "=r"(code32[10]), "=r"(code32[11]) : "l"(p)); - asm(LD_NC_V4 " {%0, %1, %2, %3}, [%4 + 48];" : - "=r"(code32[12]), "=r"(code32[13]), - "=r"(code32[14]), "=r"(code32[15]) : "l"(p)); - asm(LD_NC_V4 " {%0, %1, %2, %3}, [%4 + 64];" : - "=r"(code32[16]), "=r"(code32[17]), - "=r"(code32[18]), "=r"(code32[19]) : "l"(p)); - asm(LD_NC_V4 " {%0, %1, %2, %3}, [%4 + 80];" : - "=r"(code32[20]), "=r"(code32[21]), - "=r"(code32[22]), "=r"(code32[23]) : "l"(p)); - } -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassNoPrecomputed-inl.cuh b/core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassNoPrecomputed-inl.cuh deleted file mode 100644 index a77e783d09..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassNoPrecomputed-inl.cuh +++ /dev/null @@ -1,623 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace faiss { namespace gpu { - -// This must be kept in sync with PQCodeDistances.cu -inline bool isSupportedNoPrecomputedSubDimSize(int dims) { - switch (dims) { - case 1: - case 2: - case 3: - case 4: - case 6: - case 8: - case 10: - case 12: - case 16: - case 20: - case 24: - case 28: - case 32: - return true; - default: - // FIXME: larger sizes require too many registers - we need the - // MM implementation working - return false; - } -} - -template -struct LoadCodeDistances { - static inline __device__ void load(LookupT* smem, - LookupT* codes, - int numCodes) { - constexpr int kWordSize = sizeof(LookupVecT) / sizeof(LookupT); - - // We can only use the vector type if the data is guaranteed to be - // aligned. The codes are innermost, so if it is evenly divisible, - // then any slice will be aligned. - if (numCodes % kWordSize == 0) { - // Load the data by float4 for efficiency, and then handle any remainder - // limitVec is the number of whole vec words we can load, in terms - // of whole blocks performing the load - constexpr int kUnroll = 2; - int limitVec = numCodes / (kUnroll * kWordSize * blockDim.x); - limitVec *= kUnroll * blockDim.x; - - LookupVecT* smemV = (LookupVecT*) smem; - LookupVecT* codesV = (LookupVecT*) codes; - - for (int i = threadIdx.x; i < limitVec; i += kUnroll * blockDim.x) { - LookupVecT vals[kUnroll]; - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - vals[j] = - LoadStore::load(&codesV[i + j * blockDim.x]); - } - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - LoadStore::store(&smemV[i + j * blockDim.x], vals[j]); - } - } - - // This is where we start loading the remainder that does not evenly - // fit into kUnroll x blockDim.x - int remainder = limitVec * kWordSize; - - for (int i = remainder + threadIdx.x; i < numCodes; i += blockDim.x) { - smem[i] = codes[i]; - } - } else { - // Potential unaligned load - constexpr int kUnroll = 4; - - int limit = utils::roundDown(numCodes, kUnroll * blockDim.x); - - int i = threadIdx.x; - for (; i < limit; i += kUnroll * blockDim.x) { - LookupT vals[kUnroll]; - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - vals[j] = codes[i + j * blockDim.x]; - } - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - smem[i + j * blockDim.x] = vals[j]; - } - } - - for (; i < numCodes; i += blockDim.x) { - smem[i] = codes[i]; - } - } - } -}; - -template -__global__ void -pqScanNoPrecomputedMultiPass(Tensor queries, - Tensor pqCentroids, - Tensor topQueryToCentroid, - Tensor codeDistances, - void** listCodes, - int* listLengths, - Tensor prefixSumOffsets, - Tensor distance) { - const auto codesPerSubQuantizer = pqCentroids.getSize(2); - - // Where the pq code -> residual distance is stored - extern __shared__ char smemCodeDistances[]; - LookupT* codeDist = (LookupT*) smemCodeDistances; - - // Each block handles a single query - auto queryId = blockIdx.y; - auto probeId = blockIdx.x; - - // This is where we start writing out data - // We ensure that before the array (at offset -1), there is a 0 value - int outBase = *(prefixSumOffsets[queryId][probeId].data() - 1); - float* distanceOut = distance[outBase].data(); - - auto listId = topQueryToCentroid[queryId][probeId]; - // Safety guard in case NaNs in input cause no list ID to be generated - if (listId == -1) { - return; - } - - unsigned char* codeList = (unsigned char*) listCodes[listId]; - int limit = listLengths[listId]; - - constexpr int kNumCode32 = NumSubQuantizers <= 4 ? 1 : - (NumSubQuantizers / 4); - unsigned int code32[kNumCode32]; - unsigned int nextCode32[kNumCode32]; - - // We double-buffer the code loading, which improves memory utilization - if (threadIdx.x < limit) { - LoadCode32::load(code32, codeList, threadIdx.x); - } - - LoadCodeDistances::load( - codeDist, - codeDistances[queryId][probeId].data(), - codeDistances.getSize(2) * codeDistances.getSize(3)); - - // Prevent WAR dependencies - __syncthreads(); - - // Each thread handles one code element in the list, with a - // block-wide stride - for (int codeIndex = threadIdx.x; - codeIndex < limit; - codeIndex += blockDim.x) { - // Prefetch next codes - if (codeIndex + blockDim.x < limit) { - LoadCode32::load( - nextCode32, codeList, codeIndex + blockDim.x); - } - - float dist = 0.0f; - -#pragma unroll - for (int word = 0; word < kNumCode32; ++word) { - constexpr int kBytesPerCode32 = - NumSubQuantizers < 4 ? NumSubQuantizers : 4; - - if (kBytesPerCode32 == 1) { - auto code = code32[0]; - dist = ConvertTo::to(codeDist[code]); - - } else { -#pragma unroll - for (int byte = 0; byte < kBytesPerCode32; ++byte) { - auto code = getByte(code32[word], byte * 8, 8); - - auto offset = - codesPerSubQuantizer * (word * kBytesPerCode32 + byte); - - dist += ConvertTo::to(codeDist[offset + code]); - } - } - } - - // Write out intermediate distance result - // We do not maintain indices here, in order to reduce global - // memory traffic. Those are recovered in the final selection step. - distanceOut[codeIndex] = dist; - - // Rotate buffers -#pragma unroll - for (int word = 0; word < kNumCode32; ++word) { - code32[word] = nextCode32[word]; - } - } -} - -template -void -runMultiPassTile(Tensor& queries, - Tensor& centroids, - Tensor& pqCentroidsInnermostCode, - NoTypeTensor<4, true>& codeDistances, - Tensor& topQueryToCentroid, - Tensor& bitset, - bool useFloat16Lookup, - int bytesPerCode, - int numSubQuantizers, - int numSubQuantizerCodes, - thrust::device_vector& listCodes, - thrust::device_vector& listIndices, - IndicesOptions indicesOptions, - thrust::device_vector& listLengths, - Tensor& thrustMem, - Tensor& prefixSumOffsets, - Tensor& allDistances, - Tensor& heapDistances, - Tensor& heapIndices, - int k, - faiss::MetricType metric, - Tensor& outDistances, - Tensor& outIndices, - cudaStream_t stream) { - // We only support two metrics at the moment - FAISS_ASSERT(metric == MetricType::METRIC_INNER_PRODUCT || - metric == MetricType::METRIC_L2); - - bool l2Distance = metric == MetricType::METRIC_L2; - - // Calculate offset lengths, so we know where to write out - // intermediate results - runCalcListOffsets(topQueryToCentroid, listLengths, prefixSumOffsets, - thrustMem, stream); - - // Calculate residual code distances, since this is without - // precomputed codes - runPQCodeDistances(pqCentroidsInnermostCode, - queries, - centroids, - topQueryToCentroid, - codeDistances, - l2Distance, - useFloat16Lookup, - stream); - - // Convert all codes to a distance, and write out (distance, - // index) values for all intermediate results - { - auto kThreadsPerBlock = 256; - - auto grid = dim3(topQueryToCentroid.getSize(1), - topQueryToCentroid.getSize(0)); - auto block = dim3(kThreadsPerBlock); - - // pq centroid distances - -#ifdef FAISS_USE_FLOAT16 - auto smem = (sizeof(float)== useFloat16Lookup) ? sizeof(half) : sizeof(float); -#else - auto smem = sizeof(float); -#endif - - smem *= numSubQuantizers * numSubQuantizerCodes; - FAISS_ASSERT(smem <= getMaxSharedMemPerBlockCurrentDevice()); - -#define RUN_PQ_OPT(NUM_SUB_Q, LOOKUP_T, LOOKUP_VEC_T) \ - do { \ - auto codeDistancesT = codeDistances.toTensor(); \ - \ - pqScanNoPrecomputedMultiPass \ - <<>>( \ - queries, \ - pqCentroidsInnermostCode, \ - topQueryToCentroid, \ - codeDistancesT, \ - listCodes.data().get(), \ - listLengths.data().get(), \ - prefixSumOffsets, \ - allDistances); \ - } while (0) - -#ifdef FAISS_USE_FLOAT16 -#define RUN_PQ(NUM_SUB_Q) \ - do { \ - if (useFloat16Lookup) { \ - RUN_PQ_OPT(NUM_SUB_Q, half, Half8); \ - } else { \ - RUN_PQ_OPT(NUM_SUB_Q, float, float4); \ - } \ - } while (0) -#else -#define RUN_PQ(NUM_SUB_Q) \ - do { \ - RUN_PQ_OPT(NUM_SUB_Q, float, float4); \ - } while (0) -#endif - - switch (bytesPerCode) { - case 1: - RUN_PQ(1); - break; - case 2: - RUN_PQ(2); - break; - case 3: - RUN_PQ(3); - break; - case 4: - RUN_PQ(4); - break; - case 8: - RUN_PQ(8); - break; - case 12: - RUN_PQ(12); - break; - case 16: - RUN_PQ(16); - break; - case 20: - RUN_PQ(20); - break; - case 24: - RUN_PQ(24); - break; - case 28: - RUN_PQ(28); - break; - case 32: - RUN_PQ(32); - break; - case 40: - RUN_PQ(40); - break; - case 48: - RUN_PQ(48); - break; - case 56: - RUN_PQ(56); - break; - case 64: - RUN_PQ(64); - break; - case 96: - RUN_PQ(96); - break; - default: - FAISS_ASSERT(false); - break; - } - -#undef RUN_PQ -#undef RUN_PQ_OPT - } - - CUDA_TEST_ERROR(); - - // k-select the output in chunks, to increase parallelism - runPass1SelectLists(listIndices, - indicesOptions, - prefixSumOffsets, - topQueryToCentroid, - bitset, - allDistances, - topQueryToCentroid.getSize(1), - k, - !l2Distance, // L2 distance chooses smallest - heapDistances, - heapIndices, - stream); - - // k-select final output - auto flatHeapDistances = heapDistances.downcastInner<2>(); - auto flatHeapIndices = heapIndices.downcastInner<2>(); - - runPass2SelectLists(flatHeapDistances, - flatHeapIndices, - listIndices, - indicesOptions, - prefixSumOffsets, - topQueryToCentroid, - k, - !l2Distance, // L2 distance chooses smallest - outDistances, - outIndices, - stream); -} - -template -void -runPQScanMultiPassNoPrecomputed(Tensor& queries, - Tensor& centroids, - Tensor& pqCentroidsInnermostCode, - Tensor& topQueryToCentroid, - Tensor& bitset, - bool useFloat16Lookup, - int bytesPerCode, - int numSubQuantizers, - int numSubQuantizerCodes, - thrust::device_vector& listCodes, - thrust::device_vector& listIndices, - IndicesOptions indicesOptions, - thrust::device_vector& listLengths, - int maxListLength, - int k, - faiss::MetricType metric, - // output - Tensor& outDistances, - // output - Tensor& outIndices, - GpuResources* res) { - constexpr int kMinQueryTileSize = 8; - constexpr int kMaxQueryTileSize = 128; - constexpr int kThrustMemSize = 16384; - - int nprobe = topQueryToCentroid.getSize(1); - - auto& mem = res->getMemoryManagerCurrentDevice(); - auto stream = res->getDefaultStreamCurrentDevice(); - - // Make a reservation for Thrust to do its dirty work (global memory - // cross-block reduction space); hopefully this is large enough. - DeviceTensor thrustMem1( - mem, {kThrustMemSize}, stream); - DeviceTensor thrustMem2( - mem, {kThrustMemSize}, stream); - DeviceTensor* thrustMem[2] = - {&thrustMem1, &thrustMem2}; - - // How much temporary storage is available? - // If possible, we'd like to fit within the space available. - size_t sizeAvailable = mem.getSizeAvailable(); - - // We run two passes of heap selection - // This is the size of the first-level heap passes - constexpr int kNProbeSplit = 8; - int pass2Chunks = std::min(nprobe, kNProbeSplit); - - size_t sizeForFirstSelectPass = - pass2Chunks * k * (sizeof(float) + sizeof(int)); - - // How much temporary storage we need per each query - size_t sizePerQuery = - 2 * // streams - ((nprobe * sizeof(int) + sizeof(int)) + // prefixSumOffsets - nprobe * maxListLength * sizeof(float) + // allDistances - // residual distances - nprobe * numSubQuantizers * numSubQuantizerCodes * sizeof(float) + - sizeForFirstSelectPass); - - int queryTileSize = (int) (sizeAvailable / sizePerQuery); - - if (queryTileSize < kMinQueryTileSize) { - queryTileSize = kMinQueryTileSize; - } else if (queryTileSize > kMaxQueryTileSize) { - queryTileSize = kMaxQueryTileSize; - } - - // FIXME: we should adjust queryTileSize to deal with this, since - // indexing is in int32 - FAISS_ASSERT(queryTileSize * nprobe * maxListLength < - std::numeric_limits::max()); - - // Temporary memory buffers - // Make sure there is space prior to the start which will be 0, and - // will handle the boundary condition without branches - DeviceTensor prefixSumOffsetSpace1( - mem, {queryTileSize * nprobe + 1}, stream); - DeviceTensor prefixSumOffsetSpace2( - mem, {queryTileSize * nprobe + 1}, stream); - - DeviceTensor prefixSumOffsets1( - prefixSumOffsetSpace1[1].data(), - {queryTileSize, nprobe}); - DeviceTensor prefixSumOffsets2( - prefixSumOffsetSpace2[1].data(), - {queryTileSize, nprobe}); - DeviceTensor* prefixSumOffsets[2] = - {&prefixSumOffsets1, &prefixSumOffsets2}; - - // Make sure the element before prefixSumOffsets is 0, since we - // depend upon simple, boundary-less indexing to get proper results - CUDA_VERIFY(cudaMemsetAsync(prefixSumOffsetSpace1.data(), - 0, - sizeof(int), - stream)); - CUDA_VERIFY(cudaMemsetAsync(prefixSumOffsetSpace2.data(), - 0, - sizeof(int), - stream)); - - int codeDistanceTypeSize = sizeof(float); -#ifdef FAISS_USE_FLOAT16 - if (useFloat16Lookup) { - codeDistanceTypeSize = sizeof(half); - } -#endif - - int totalCodeDistancesSize = - queryTileSize * nprobe * numSubQuantizers * numSubQuantizerCodes * - codeDistanceTypeSize; - - DeviceTensor codeDistances1Mem( - mem, {totalCodeDistancesSize}, stream); - NoTypeTensor<4, true> codeDistances1( - codeDistances1Mem.data(), - codeDistanceTypeSize, - {queryTileSize, nprobe, numSubQuantizers, numSubQuantizerCodes}); - - DeviceTensor codeDistances2Mem( - mem, {totalCodeDistancesSize}, stream); - NoTypeTensor<4, true> codeDistances2( - codeDistances2Mem.data(), - codeDistanceTypeSize, - {queryTileSize, nprobe, numSubQuantizers, numSubQuantizerCodes}); - - NoTypeTensor<4, true>* codeDistances[2] = - {&codeDistances1, &codeDistances2}; - - DeviceTensor allDistances1( - mem, {queryTileSize * nprobe * maxListLength}, stream); - DeviceTensor allDistances2( - mem, {queryTileSize * nprobe * maxListLength}, stream); - DeviceTensor* allDistances[2] = - {&allDistances1, &allDistances2}; - - DeviceTensor heapDistances1( - mem, {queryTileSize, pass2Chunks, k}, stream); - DeviceTensor heapDistances2( - mem, {queryTileSize, pass2Chunks, k}, stream); - DeviceTensor* heapDistances[2] = - {&heapDistances1, &heapDistances2}; - - DeviceTensor heapIndices1( - mem, {queryTileSize, pass2Chunks, k}, stream); - DeviceTensor heapIndices2( - mem, {queryTileSize, pass2Chunks, k}, stream); - DeviceTensor* heapIndices[2] = - {&heapIndices1, &heapIndices2}; - - auto streams = res->getAlternateStreamsCurrentDevice(); - streamWait(streams, {stream}); - - int curStream = 0; - - for (int query = 0; query < queries.getSize(0); query += queryTileSize) { - int numQueriesInTile = - std::min(queryTileSize, queries.getSize(0) - query); - - auto prefixSumOffsetsView = - prefixSumOffsets[curStream]->narrowOutermost(0, numQueriesInTile); - - auto codeDistancesView = - codeDistances[curStream]->narrowOutermost(0, numQueriesInTile); - auto coarseIndicesView = - topQueryToCentroid.narrowOutermost(query, numQueriesInTile); - auto queryView = - queries.narrowOutermost(query, numQueriesInTile); - - auto heapDistancesView = - heapDistances[curStream]->narrowOutermost(0, numQueriesInTile); - auto heapIndicesView = - heapIndices[curStream]->narrowOutermost(0, numQueriesInTile); - - auto outDistanceView = - outDistances.narrowOutermost(query, numQueriesInTile); - auto outIndicesView = - outIndices.narrowOutermost(query, numQueriesInTile); - - runMultiPassTile(queryView, - centroids, - pqCentroidsInnermostCode, - codeDistancesView, - coarseIndicesView, - bitset, - useFloat16Lookup, - bytesPerCode, - numSubQuantizers, - numSubQuantizerCodes, - listCodes, - listIndices, - indicesOptions, - listLengths, - *thrustMem[curStream], - prefixSumOffsetsView, - *allDistances[curStream], - heapDistancesView, - heapIndicesView, - k, - metric, - outDistanceView, - outIndicesView, - streams[curStream]); - - curStream = (curStream + 1) % 2; - } - - streamWait({stream}, streams); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassNoPrecomputed.cu b/core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassNoPrecomputed.cu deleted file mode 100644 index b4934382cb..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassNoPrecomputed.cu +++ /dev/null @@ -1,627 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace faiss { namespace gpu { - -//// This must be kept in sync with PQCodeDistances.cu -//bool isSupportedNoPrecomputedSubDimSize(int dims) { -// switch (dims) { -// case 1: -// case 2: -// case 3: -// case 4: -// case 6: -// case 8: -// case 10: -// case 12: -// case 16: -// case 20: -// case 24: -// case 28: -// case 32: -// return true; -// default: -// // FIXME: larger sizes require too many registers - we need the -// // MM implementation working -// return false; -// } -//} -// -//template -//struct LoadCodeDistances { -// static inline __device__ void load(LookupT* smem, -// LookupT* codes, -// int numCodes) { -// constexpr int kWordSize = sizeof(LookupVecT) / sizeof(LookupT); -// -// // We can only use the vector type if the data is guaranteed to be -// // aligned. The codes are innermost, so if it is evenly divisible, -// // then any slice will be aligned. -// if (numCodes % kWordSize == 0) { -// // Load the data by float4 for efficiency, and then handle any remainder -// // limitVec is the number of whole vec words we can load, in terms -// // of whole blocks performing the load -// constexpr int kUnroll = 2; -// int limitVec = numCodes / (kUnroll * kWordSize * blockDim.x); -// limitVec *= kUnroll * blockDim.x; -// -// LookupVecT* smemV = (LookupVecT*) smem; -// LookupVecT* codesV = (LookupVecT*) codes; -// -// for (int i = threadIdx.x; i < limitVec; i += kUnroll * blockDim.x) { -// LookupVecT vals[kUnroll]; -// -//#pragma unroll -// for (int j = 0; j < kUnroll; ++j) { -// vals[j] = -// LoadStore::load(&codesV[i + j * blockDim.x]); -// } -// -//#pragma unroll -// for (int j = 0; j < kUnroll; ++j) { -// LoadStore::store(&smemV[i + j * blockDim.x], vals[j]); -// } -// } -// -// // This is where we start loading the remainder that does not evenly -// // fit into kUnroll x blockDim.x -// int remainder = limitVec * kWordSize; -// -// for (int i = remainder + threadIdx.x; i < numCodes; i += blockDim.x) { -// smem[i] = codes[i]; -// } -// } else { -// // Potential unaligned load -// constexpr int kUnroll = 4; -// -// int limit = utils::roundDown(numCodes, kUnroll * blockDim.x); -// -// int i = threadIdx.x; -// for (; i < limit; i += kUnroll * blockDim.x) { -// LookupT vals[kUnroll]; -// -//#pragma unroll -// for (int j = 0; j < kUnroll; ++j) { -// vals[j] = codes[i + j * blockDim.x]; -// } -// -//#pragma unroll -// for (int j = 0; j < kUnroll; ++j) { -// smem[i + j * blockDim.x] = vals[j]; -// } -// } -// -// for (; i < numCodes; i += blockDim.x) { -// smem[i] = codes[i]; -// } -// } -// } -//}; -// -//template -//__global__ void -//pqScanNoPrecomputedMultiPass(Tensor queries, -// Tensor pqCentroids, -// Tensor topQueryToCentroid, -// Tensor codeDistances, -// void** listCodes, -// int* listLengths, -// Tensor prefixSumOffsets, -// Tensor distance) { -// const auto codesPerSubQuantizer = pqCentroids.getSize(2); -// -// // Where the pq code -> residual distance is stored -// extern __shared__ char smemCodeDistances[]; -// LookupT* codeDist = (LookupT*) smemCodeDistances; -// -// // Each block handles a single query -// auto queryId = blockIdx.y; -// auto probeId = blockIdx.x; -// -// // This is where we start writing out data -// // We ensure that before the array (at offset -1), there is a 0 value -// int outBase = *(prefixSumOffsets[queryId][probeId].data() - 1); -// float* distanceOut = distance[outBase].data(); -// -// auto listId = topQueryToCentroid[queryId][probeId]; -// // Safety guard in case NaNs in input cause no list ID to be generated -// if (listId == -1) { -// return; -// } -// -// unsigned char* codeList = (unsigned char*) listCodes[listId]; -// int limit = listLengths[listId]; -// -// constexpr int kNumCode32 = NumSubQuantizers <= 4 ? 1 : -// (NumSubQuantizers / 4); -// unsigned int code32[kNumCode32]; -// unsigned int nextCode32[kNumCode32]; -// -// // We double-buffer the code loading, which improves memory utilization -// if (threadIdx.x < limit) { -// LoadCode32::load(code32, codeList, threadIdx.x); -// } -// -// LoadCodeDistances::load( -// codeDist, -// codeDistances[queryId][probeId].data(), -// codeDistances.getSize(2) * codeDistances.getSize(3)); -// -// // Prevent WAR dependencies -// __syncthreads(); -// -// // Each thread handles one code element in the list, with a -// // block-wide stride -// for (int codeIndex = threadIdx.x; -// codeIndex < limit; -// codeIndex += blockDim.x) { -// // Prefetch next codes -// if (codeIndex + blockDim.x < limit) { -// LoadCode32::load( -// nextCode32, codeList, codeIndex + blockDim.x); -// } -// -// float dist = 0.0f; -// -//#pragma unroll -// for (int word = 0; word < kNumCode32; ++word) { -// constexpr int kBytesPerCode32 = -// NumSubQuantizers < 4 ? NumSubQuantizers : 4; -// -// if (kBytesPerCode32 == 1) { -// auto code = code32[0]; -// dist = ConvertTo::to(codeDist[code]); -// -// } else { -//#pragma unroll -// for (int byte = 0; byte < kBytesPerCode32; ++byte) { -// auto code = getByte(code32[word], byte * 8, 8); -// -// auto offset = -// codesPerSubQuantizer * (word * kBytesPerCode32 + byte); -// -// dist += ConvertTo::to(codeDist[offset + code]); -// } -// } -// } -// -// // Write out intermediate distance result -// // We do not maintain indices here, in order to reduce global -// // memory traffic. Those are recovered in the final selection step. -// distanceOut[codeIndex] = dist; -// -// // Rotate buffers -//#pragma unroll -// for (int word = 0; word < kNumCode32; ++word) { -// code32[word] = nextCode32[word]; -// } -// } -//} - -void -runMultiPassTile(Tensor& queries, - Tensor& centroids, - Tensor& pqCentroidsInnermostCode, - NoTypeTensor<4, true>& codeDistances, - Tensor& topQueryToCentroid, - Tensor& bitset, - bool useFloat16Lookup, - int bytesPerCode, - int numSubQuantizers, - int numSubQuantizerCodes, - thrust::device_vector& listCodes, - thrust::device_vector& listIndices, - IndicesOptions indicesOptions, - thrust::device_vector& listLengths, - Tensor& thrustMem, - Tensor& prefixSumOffsets, - Tensor& allDistances, - Tensor& heapDistances, - Tensor& heapIndices, - int k, - faiss::MetricType metric, - Tensor& outDistances, - Tensor& outIndices, - cudaStream_t stream) { - // We only support two metrics at the moment - FAISS_ASSERT(metric == MetricType::METRIC_INNER_PRODUCT || - metric == MetricType::METRIC_L2); - - bool l2Distance = metric == MetricType::METRIC_L2; - // Calculate offset lengths, so we know where to write out -#ifndef FAISS_USE_FLOAT16 - FAISS_ASSERT(!useFloat16Lookup); -#endif - // Calculate offset lengths, so we know where to write out - // intermediate results - runCalcListOffsets(topQueryToCentroid, listLengths, prefixSumOffsets, - thrustMem, stream); - - // Calculate residual code distances, since this is without - // precomputed codes - runPQCodeDistances(pqCentroidsInnermostCode, - queries, - centroids, - topQueryToCentroid, - codeDistances, - l2Distance, - useFloat16Lookup, - stream); - - // Convert all codes to a distance, and write out (distance, - // index) values for all intermediate results - { - auto kThreadsPerBlock = 256; - - auto grid = dim3(topQueryToCentroid.getSize(1), - topQueryToCentroid.getSize(0)); - auto block = dim3(kThreadsPerBlock); - - // pq centroid distances - //auto smem = useFloat16Lookup ? sizeof(half) : sizeof(float); - auto smem = sizeof(float); -#ifdef FAISS_USE_FLOAT16 - if (useFloat16Lookup) { - smem = sizeof(half); - } -#endif - - smem *= numSubQuantizers * numSubQuantizerCodes; - FAISS_ASSERT(smem <= getMaxSharedMemPerBlockCurrentDevice()); - -#define RUN_PQ_OPT(NUM_SUB_Q, LOOKUP_T, LOOKUP_VEC_T) \ - do { \ - auto codeDistancesT = codeDistances.toTensor(); \ - \ - pqScanNoPrecomputedMultiPass \ - <<>>( \ - queries, \ - pqCentroidsInnermostCode, \ - topQueryToCentroid, \ - codeDistancesT, \ - listCodes.data().get(), \ - listLengths.data().get(), \ - prefixSumOffsets, \ - allDistances); \ - } while (0) - -#ifdef FAISS_USE_FLOAT16 -#define RUN_PQ(NUM_SUB_Q) \ - do { \ - if (useFloat16Lookup) { \ - RUN_PQ_OPT(NUM_SUB_Q, half, Half8); \ - } else { \ - RUN_PQ_OPT(NUM_SUB_Q, float, float4); \ - } \ - } while (0) -#else -#define RUN_PQ(NUM_SUB_Q) \ - do { \ - RUN_PQ_OPT(NUM_SUB_Q, float, float4); \ - } while (0) -#endif // FAISS_USE_FLOAT16 - - switch (bytesPerCode) { - case 1: - RUN_PQ(1); - break; - case 2: - RUN_PQ(2); - break; - case 3: - RUN_PQ(3); - break; - case 4: - RUN_PQ(4); - break; - case 8: - RUN_PQ(8); - break; - case 12: - RUN_PQ(12); - break; - case 16: - RUN_PQ(16); - break; - case 20: - RUN_PQ(20); - break; - case 24: - RUN_PQ(24); - break; - case 28: - RUN_PQ(28); - break; - case 32: - RUN_PQ(32); - break; - case 40: - RUN_PQ(40); - break; - case 48: - RUN_PQ(48); - break; - case 56: - RUN_PQ(56); - break; - case 64: - RUN_PQ(64); - break; - case 96: - RUN_PQ(96); - break; - default: - FAISS_ASSERT(false); - break; - } - -#undef RUN_PQ -#undef RUN_PQ_OPT - } - - CUDA_TEST_ERROR(); - - // k-select the output in chunks, to increase parallelism - runPass1SelectLists(listIndices, - indicesOptions, - prefixSumOffsets, - topQueryToCentroid, - bitset, - allDistances, - topQueryToCentroid.getSize(1), - k, - !l2Distance, // L2 distance chooses smallest - heapDistances, - heapIndices, - stream); - - // k-select final output - auto flatHeapDistances = heapDistances.downcastInner<2>(); - auto flatHeapIndices = heapIndices.downcastInner<2>(); - - runPass2SelectLists(flatHeapDistances, - flatHeapIndices, - listIndices, - indicesOptions, - prefixSumOffsets, - topQueryToCentroid, - k, - !l2Distance, // L2 distance chooses smallest - outDistances, - outIndices, - stream); -} - -void runPQScanMultiPassNoPrecomputed(Tensor& queries, - Tensor& centroids, - Tensor& pqCentroidsInnermostCode, - Tensor& topQueryToCentroid, - Tensor& bitset, - bool useFloat16Lookup, - int bytesPerCode, - int numSubQuantizers, - int numSubQuantizerCodes, - thrust::device_vector& listCodes, - thrust::device_vector& listIndices, - IndicesOptions indicesOptions, - thrust::device_vector& listLengths, - int maxListLength, - int k, - faiss::MetricType metric, - // output - Tensor& outDistances, - // output - Tensor& outIndices, - GpuResources* res) { - constexpr int kMinQueryTileSize = 8; - constexpr int kMaxQueryTileSize = 128; - constexpr int kThrustMemSize = 16384; - - int nprobe = topQueryToCentroid.getSize(1); - - auto& mem = res->getMemoryManagerCurrentDevice(); - auto stream = res->getDefaultStreamCurrentDevice(); - - // Make a reservation for Thrust to do its dirty work (global memory - // cross-block reduction space); hopefully this is large enough. - DeviceTensor thrustMem1( - mem, {kThrustMemSize}, stream); - DeviceTensor thrustMem2( - mem, {kThrustMemSize}, stream); - DeviceTensor* thrustMem[2] = - {&thrustMem1, &thrustMem2}; - - // How much temporary storage is available? - // If possible, we'd like to fit within the space available. - size_t sizeAvailable = mem.getSizeAvailable(); - - // We run two passes of heap selection - // This is the size of the first-level heap passes - constexpr int kNProbeSplit = 8; - int pass2Chunks = std::min(nprobe, kNProbeSplit); - - size_t sizeForFirstSelectPass = - pass2Chunks * k * (sizeof(float) + sizeof(int)); - - // How much temporary storage we need per each query - size_t sizePerQuery = - 2 * // streams - ((nprobe * sizeof(int) + sizeof(int)) + // prefixSumOffsets - nprobe * maxListLength * sizeof(float) + // allDistances - // residual distances - nprobe * numSubQuantizers * numSubQuantizerCodes * sizeof(float) + - sizeForFirstSelectPass); - - int queryTileSize = (int) (sizeAvailable / sizePerQuery); - - if (queryTileSize < kMinQueryTileSize) { - queryTileSize = kMinQueryTileSize; - } else if (queryTileSize > kMaxQueryTileSize) { - queryTileSize = kMaxQueryTileSize; - } - - // FIXME: we should adjust queryTileSize to deal with this, since - // indexing is in int32 - FAISS_ASSERT(queryTileSize * nprobe * maxListLength < - std::numeric_limits::max()); - - // Temporary memory buffers - // Make sure there is space prior to the start which will be 0, and - // will handle the boundary condition without branches - DeviceTensor prefixSumOffsetSpace1( - mem, {queryTileSize * nprobe + 1}, stream); - DeviceTensor prefixSumOffsetSpace2( - mem, {queryTileSize * nprobe + 1}, stream); - - DeviceTensor prefixSumOffsets1( - prefixSumOffsetSpace1[1].data(), - {queryTileSize, nprobe}); - DeviceTensor prefixSumOffsets2( - prefixSumOffsetSpace2[1].data(), - {queryTileSize, nprobe}); - DeviceTensor* prefixSumOffsets[2] = - {&prefixSumOffsets1, &prefixSumOffsets2}; - - // Make sure the element before prefixSumOffsets is 0, since we - // depend upon simple, boundary-less indexing to get proper results - CUDA_VERIFY(cudaMemsetAsync(prefixSumOffsetSpace1.data(), - 0, - sizeof(int), - stream)); - CUDA_VERIFY(cudaMemsetAsync(prefixSumOffsetSpace2.data(), - 0, - sizeof(int), - stream)); - - int codeDistanceTypeSize = sizeof(float); -#ifdef FAISS_USE_FLOAT16 - if (useFloat16Lookup) { - codeDistanceTypeSize = sizeof(half); - } -#else - FAISS_ASSERT(!useFloat16Lookup); -#endif - - int totalCodeDistancesSize = - queryTileSize * nprobe * numSubQuantizers * numSubQuantizerCodes * - codeDistanceTypeSize; - - DeviceTensor codeDistances1Mem( - mem, {totalCodeDistancesSize}, stream); - NoTypeTensor<4, true> codeDistances1( - codeDistances1Mem.data(), - codeDistanceTypeSize, - {queryTileSize, nprobe, numSubQuantizers, numSubQuantizerCodes}); - - DeviceTensor codeDistances2Mem( - mem, {totalCodeDistancesSize}, stream); - NoTypeTensor<4, true> codeDistances2( - codeDistances2Mem.data(), - codeDistanceTypeSize, - {queryTileSize, nprobe, numSubQuantizers, numSubQuantizerCodes}); - - NoTypeTensor<4, true>* codeDistances[2] = - {&codeDistances1, &codeDistances2}; - - DeviceTensor allDistances1( - mem, {queryTileSize * nprobe * maxListLength}, stream); - DeviceTensor allDistances2( - mem, {queryTileSize * nprobe * maxListLength}, stream); - DeviceTensor* allDistances[2] = - {&allDistances1, &allDistances2}; - - DeviceTensor heapDistances1( - mem, {queryTileSize, pass2Chunks, k}, stream); - DeviceTensor heapDistances2( - mem, {queryTileSize, pass2Chunks, k}, stream); - DeviceTensor* heapDistances[2] = - {&heapDistances1, &heapDistances2}; - - DeviceTensor heapIndices1( - mem, {queryTileSize, pass2Chunks, k}, stream); - DeviceTensor heapIndices2( - mem, {queryTileSize, pass2Chunks, k}, stream); - DeviceTensor* heapIndices[2] = - {&heapIndices1, &heapIndices2}; - - auto streams = res->getAlternateStreamsCurrentDevice(); - streamWait(streams, {stream}); - - int curStream = 0; - - for (int query = 0; query < queries.getSize(0); query += queryTileSize) { - int numQueriesInTile = - std::min(queryTileSize, queries.getSize(0) - query); - - auto prefixSumOffsetsView = - prefixSumOffsets[curStream]->narrowOutermost(0, numQueriesInTile); - - auto codeDistancesView = - codeDistances[curStream]->narrowOutermost(0, numQueriesInTile); - auto coarseIndicesView = - topQueryToCentroid.narrowOutermost(query, numQueriesInTile); - auto queryView = - queries.narrowOutermost(query, numQueriesInTile); - - auto heapDistancesView = - heapDistances[curStream]->narrowOutermost(0, numQueriesInTile); - auto heapIndicesView = - heapIndices[curStream]->narrowOutermost(0, numQueriesInTile); - - auto outDistanceView = - outDistances.narrowOutermost(query, numQueriesInTile); - auto outIndicesView = - outIndices.narrowOutermost(query, numQueriesInTile); - - runMultiPassTile(queryView, - centroids, - pqCentroidsInnermostCode, - codeDistancesView, - coarseIndicesView, - bitset, - useFloat16Lookup, - bytesPerCode, - numSubQuantizers, - numSubQuantizerCodes, - listCodes, - listIndices, - indicesOptions, - listLengths, - *thrustMem[curStream], - prefixSumOffsetsView, - *allDistances[curStream], - heapDistancesView, - heapIndicesView, - k, - metric, - outDistanceView, - outIndicesView, - streams[curStream]); - - curStream = (curStream + 1) % 2; - } - - streamWait({stream}, streams); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassNoPrecomputed.cuh b/core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassNoPrecomputed.cuh deleted file mode 100644 index d3c0cc53d5..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassNoPrecomputed.cuh +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -class GpuResources; - -/// For no precomputed codes, is this a supported number of dimensions -/// per subquantizer? -bool isSupportedNoPrecomputedSubDimSize(int dims); - -template -void runPQScanMultiPassNoPrecomputed(Tensor& queries, - Tensor& centroids, - Tensor& pqCentroidsInnermostCode, - Tensor& topQueryToCentroid, - Tensor& bitset, - bool useFloat16Lookup, - int bytesPerCode, - int numSubQuantizers, - int numSubQuantizerCodes, - thrust::device_vector& listCodes, - thrust::device_vector& listIndices, - IndicesOptions indicesOptions, - thrust::device_vector& listLengths, - int maxListLength, - int k, - faiss::MetricType metric, - // output - Tensor& outDistances, - // output - Tensor& outIndices, - GpuResources* res); - -} } // namespace - -#include diff --git a/core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassPrecomputed.cu b/core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassPrecomputed.cu deleted file mode 100644 index 02e65ff32a..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassPrecomputed.cu +++ /dev/null @@ -1,573 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -// For precomputed codes, this calculates and loads code distances -// into smem -template -inline __device__ void -loadPrecomputedTerm(LookupT* smem, - LookupT* term2Start, - LookupT* term3Start, - int numCodes) { - constexpr int kWordSize = sizeof(LookupVecT) / sizeof(LookupT); - - // We can only use vector loads if the data is guaranteed to be - // aligned. The codes are innermost, so if it is evenly divisible, - // then any slice will be aligned. - if (numCodes % kWordSize == 0) { - constexpr int kUnroll = 2; - - // Load the data by float4 for efficiency, and then handle any remainder - // limitVec is the number of whole vec words we can load, in terms - // of whole blocks performing the load - int limitVec = numCodes / (kUnroll * kWordSize * blockDim.x); - limitVec *= kUnroll * blockDim.x; - - LookupVecT* smemV = (LookupVecT*) smem; - LookupVecT* term2StartV = (LookupVecT*) term2Start; - LookupVecT* term3StartV = (LookupVecT*) term3Start; - - for (int i = threadIdx.x; i < limitVec; i += kUnroll * blockDim.x) { - LookupVecT vals[kUnroll]; - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - vals[j] = - LoadStore::load(&term2StartV[i + j * blockDim.x]); - } - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - LookupVecT q = - LoadStore::load(&term3StartV[i + j * blockDim.x]); - - vals[j] = Math::add(vals[j], q); - } - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - LoadStore::store(&smemV[i + j * blockDim.x], vals[j]); - } - } - - // This is where we start loading the remainder that does not evenly - // fit into kUnroll x blockDim.x - int remainder = limitVec * kWordSize; - - for (int i = remainder + threadIdx.x; i < numCodes; i += blockDim.x) { - smem[i] = Math::add(term2Start[i], term3Start[i]); - } - } else { - // Potential unaligned load - constexpr int kUnroll = 4; - - int limit = utils::roundDown(numCodes, kUnroll * blockDim.x); - - int i = threadIdx.x; - for (; i < limit; i += kUnroll * blockDim.x) { - LookupT vals[kUnroll]; - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - vals[j] = term2Start[i + j * blockDim.x]; - } - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - vals[j] = Math::add(vals[j], term3Start[i + j * blockDim.x]); - } - -#pragma unroll - for (int j = 0; j < kUnroll; ++j) { - smem[i + j * blockDim.x] = vals[j]; - } - } - - for (; i < numCodes; i += blockDim.x) { - smem[i] = Math::add(term2Start[i], term3Start[i]); - } - } -} - -template -__global__ void -pqScanPrecomputedMultiPass(Tensor queries, - Tensor precompTerm1, - Tensor precompTerm2, - Tensor precompTerm3, - Tensor topQueryToCentroid, - void** listCodes, - int* listLengths, - Tensor prefixSumOffsets, - Tensor distance) { - // precomputed term 2 + 3 storage - // (sub q)(code id) - extern __shared__ char smemTerm23[]; - LookupT* term23 = (LookupT*) smemTerm23; - - // Each block handles a single query - auto queryId = blockIdx.y; - auto probeId = blockIdx.x; - auto codesPerSubQuantizer = precompTerm2.getSize(2); - auto precompTermSize = precompTerm2.getSize(1) * codesPerSubQuantizer; - - // This is where we start writing out data - // We ensure that before the array (at offset -1), there is a 0 value - int outBase = *(prefixSumOffsets[queryId][probeId].data() - 1); - float* distanceOut = distance[outBase].data(); - - auto listId = topQueryToCentroid[queryId][probeId]; - // Safety guard in case NaNs in input cause no list ID to be generated - if (listId == -1) { - return; - } - - unsigned char* codeList = (unsigned char*) listCodes[listId]; - int limit = listLengths[listId]; - - constexpr int kNumCode32 = NumSubQuantizers <= 4 ? 1 : - (NumSubQuantizers / 4); - unsigned int code32[kNumCode32]; - unsigned int nextCode32[kNumCode32]; - - // We double-buffer the code loading, which improves memory utilization - if (threadIdx.x < limit) { - LoadCode32::load(code32, codeList, threadIdx.x); - } - - // Load precomputed terms 1, 2, 3 - float term1 = precompTerm1[queryId][probeId]; - loadPrecomputedTerm(term23, - precompTerm2[listId].data(), - precompTerm3[queryId].data(), - precompTermSize); - - // Prevent WAR dependencies - __syncthreads(); - - // Each thread handles one code element in the list, with a - // block-wide stride - for (int codeIndex = threadIdx.x; - codeIndex < limit; - codeIndex += blockDim.x) { - // Prefetch next codes - if (codeIndex + blockDim.x < limit) { - LoadCode32::load( - nextCode32, codeList, codeIndex + blockDim.x); - } - - float dist = term1; - -#pragma unroll - for (int word = 0; word < kNumCode32; ++word) { - constexpr int kBytesPerCode32 = - NumSubQuantizers < 4 ? NumSubQuantizers : 4; - - if (kBytesPerCode32 == 1) { - auto code = code32[0]; - dist = ConvertTo::to(term23[code]); - - } else { -#pragma unroll - for (int byte = 0; byte < kBytesPerCode32; ++byte) { - auto code = getByte(code32[word], byte * 8, 8); - - auto offset = - codesPerSubQuantizer * (word * kBytesPerCode32 + byte); - - dist += ConvertTo::to(term23[offset + code]); - } - } - } - - // Write out intermediate distance result - // We do not maintain indices here, in order to reduce global - // memory traffic. Those are recovered in the final selection step. - distanceOut[codeIndex] = dist; - - // Rotate buffers -#pragma unroll - for (int word = 0; word < kNumCode32; ++word) { - code32[word] = nextCode32[word]; - } - } -} - -void -runMultiPassTile(Tensor& queries, - Tensor& precompTerm1, - NoTypeTensor<3, true>& precompTerm2, - NoTypeTensor<3, true>& precompTerm3, - Tensor& topQueryToCentroid, - Tensor& bitset, - bool useFloat16Lookup, - int bytesPerCode, - int numSubQuantizers, - int numSubQuantizerCodes, - thrust::device_vector& listCodes, - thrust::device_vector& listIndices, - IndicesOptions indicesOptions, - thrust::device_vector& listLengths, - Tensor& thrustMem, - Tensor& prefixSumOffsets, - Tensor& allDistances, - Tensor& heapDistances, - Tensor& heapIndices, - int k, - Tensor& outDistances, - Tensor& outIndices, - cudaStream_t stream) { - // Calculate offset lengths, so we know where to write out - // intermediate results - runCalcListOffsets(topQueryToCentroid, listLengths, prefixSumOffsets, - thrustMem, stream); - - // Convert all codes to a distance, and write out (distance, - // index) values for all intermediate results - { - auto kThreadsPerBlock = 256; - - auto grid = dim3(topQueryToCentroid.getSize(1), - topQueryToCentroid.getSize(0)); - auto block = dim3(kThreadsPerBlock); - - // pq precomputed terms (2 + 3) - auto smem = sizeof(float); -#ifdef FAISS_USE_FLOAT16 - if (useFloat16Lookup) { - smem = sizeof(half); - } -#endif - - smem *= numSubQuantizers * numSubQuantizerCodes; - FAISS_ASSERT(smem <= getMaxSharedMemPerBlockCurrentDevice()); - -#define RUN_PQ_OPT(NUM_SUB_Q, LOOKUP_T, LOOKUP_VEC_T) \ - do { \ - auto precompTerm2T = precompTerm2.toTensor(); \ - auto precompTerm3T = precompTerm3.toTensor(); \ - \ - pqScanPrecomputedMultiPass \ - <<>>( \ - queries, \ - precompTerm1, \ - precompTerm2T, \ - precompTerm3T, \ - topQueryToCentroid, \ - listCodes.data().get(), \ - listLengths.data().get(), \ - prefixSumOffsets, \ - allDistances); \ - } while (0) - -#ifdef FAISS_USE_FLOAT16 -#define RUN_PQ(NUM_SUB_Q) \ - do { \ - if (useFloat16Lookup) { \ - RUN_PQ_OPT(NUM_SUB_Q, half, Half8); \ - } else { \ - RUN_PQ_OPT(NUM_SUB_Q, float, float4); \ - } \ - } while (0) -#else -#define RUN_PQ(NUM_SUB_Q) \ - do { \ - RUN_PQ_OPT(NUM_SUB_Q, float, float4); \ - } while (0) -#endif - - switch (bytesPerCode) { - case 1: - RUN_PQ(1); - break; - case 2: - RUN_PQ(2); - break; - case 3: - RUN_PQ(3); - break; - case 4: - RUN_PQ(4); - break; - case 8: - RUN_PQ(8); - break; - case 12: - RUN_PQ(12); - break; - case 16: - RUN_PQ(16); - break; - case 20: - RUN_PQ(20); - break; - case 24: - RUN_PQ(24); - break; - case 28: - RUN_PQ(28); - break; - case 32: - RUN_PQ(32); - break; - case 40: - RUN_PQ(40); - break; - case 48: - RUN_PQ(48); - break; - case 56: - RUN_PQ(56); - break; - case 64: - RUN_PQ(64); - break; - case 96: - RUN_PQ(96); - break; - default: - FAISS_ASSERT(false); - break; - } - - CUDA_TEST_ERROR(); - -#undef RUN_PQ -#undef RUN_PQ_OPT - } - - // k-select the output in chunks, to increase parallelism - runPass1SelectLists(listIndices, - indicesOptions, - prefixSumOffsets, - topQueryToCentroid, - bitset, - allDistances, - topQueryToCentroid.getSize(1), - k, - false, // L2 distance chooses smallest - heapDistances, - heapIndices, - stream); - - // k-select final output - auto flatHeapDistances = heapDistances.downcastInner<2>(); - auto flatHeapIndices = heapIndices.downcastInner<2>(); - - runPass2SelectLists(flatHeapDistances, - flatHeapIndices, - listIndices, - indicesOptions, - prefixSumOffsets, - topQueryToCentroid, - k, - false, // L2 distance chooses smallest - outDistances, - outIndices, - stream); - - CUDA_TEST_ERROR(); -} - -void runPQScanMultiPassPrecomputed(Tensor& queries, - Tensor& precompTerm1, - NoTypeTensor<3, true>& precompTerm2, - NoTypeTensor<3, true>& precompTerm3, - Tensor& topQueryToCentroid, - Tensor& bitset, - bool useFloat16Lookup, - int bytesPerCode, - int numSubQuantizers, - int numSubQuantizerCodes, - thrust::device_vector& listCodes, - thrust::device_vector& listIndices, - IndicesOptions indicesOptions, - thrust::device_vector& listLengths, - int maxListLength, - int k, - // output - Tensor& outDistances, - // output - Tensor& outIndices, - GpuResources* res) { - constexpr int kMinQueryTileSize = 8; - constexpr int kMaxQueryTileSize = 128; - constexpr int kThrustMemSize = 16384; - - int nprobe = topQueryToCentroid.getSize(1); - - auto& mem = res->getMemoryManagerCurrentDevice(); - auto stream = res->getDefaultStreamCurrentDevice(); - - // Make a reservation for Thrust to do its dirty work (global memory - // cross-block reduction space); hopefully this is large enough. - DeviceTensor thrustMem1( - mem, {kThrustMemSize}, stream); - DeviceTensor thrustMem2( - mem, {kThrustMemSize}, stream); - DeviceTensor* thrustMem[2] = - {&thrustMem1, &thrustMem2}; - - // How much temporary storage is available? - // If possible, we'd like to fit within the space available. - size_t sizeAvailable = mem.getSizeAvailable(); - - // We run two passes of heap selection - // This is the size of the first-level heap passes - constexpr int kNProbeSplit = 8; - int pass2Chunks = std::min(nprobe, kNProbeSplit); - - size_t sizeForFirstSelectPass = - pass2Chunks * k * (sizeof(float) + sizeof(int)); - - // How much temporary storage we need per each query - size_t sizePerQuery = - 2 * // # streams - ((nprobe * sizeof(int) + sizeof(int)) + // prefixSumOffsets - nprobe * maxListLength * sizeof(float) + // allDistances - sizeForFirstSelectPass); - - int queryTileSize = (int) (sizeAvailable / sizePerQuery); - - if (queryTileSize < kMinQueryTileSize) { - queryTileSize = kMinQueryTileSize; - } else if (queryTileSize > kMaxQueryTileSize) { - queryTileSize = kMaxQueryTileSize; - } - - // FIXME: we should adjust queryTileSize to deal with this, since - // indexing is in int32 - FAISS_ASSERT(queryTileSize * nprobe * maxListLength <= - std::numeric_limits::max()); - - // Temporary memory buffers - // Make sure there is space prior to the start which will be 0, and - // will handle the boundary condition without branches - DeviceTensor prefixSumOffsetSpace1( - mem, {queryTileSize * nprobe + 1}, stream); - DeviceTensor prefixSumOffsetSpace2( - mem, {queryTileSize * nprobe + 1}, stream); - - DeviceTensor prefixSumOffsets1( - prefixSumOffsetSpace1[1].data(), - {queryTileSize, nprobe}); - DeviceTensor prefixSumOffsets2( - prefixSumOffsetSpace2[1].data(), - {queryTileSize, nprobe}); - DeviceTensor* prefixSumOffsets[2] = - {&prefixSumOffsets1, &prefixSumOffsets2}; - - // Make sure the element before prefixSumOffsets is 0, since we - // depend upon simple, boundary-less indexing to get proper results - CUDA_VERIFY(cudaMemsetAsync(prefixSumOffsetSpace1.data(), - 0, - sizeof(int), - stream)); - CUDA_VERIFY(cudaMemsetAsync(prefixSumOffsetSpace2.data(), - 0, - sizeof(int), - stream)); - - DeviceTensor allDistances1( - mem, {queryTileSize * nprobe * maxListLength}, stream); - DeviceTensor allDistances2( - mem, {queryTileSize * nprobe * maxListLength}, stream); - DeviceTensor* allDistances[2] = - {&allDistances1, &allDistances2}; - - DeviceTensor heapDistances1( - mem, {queryTileSize, pass2Chunks, k}, stream); - DeviceTensor heapDistances2( - mem, {queryTileSize, pass2Chunks, k}, stream); - DeviceTensor* heapDistances[2] = - {&heapDistances1, &heapDistances2}; - - DeviceTensor heapIndices1( - mem, {queryTileSize, pass2Chunks, k}, stream); - DeviceTensor heapIndices2( - mem, {queryTileSize, pass2Chunks, k}, stream); - DeviceTensor* heapIndices[2] = - {&heapIndices1, &heapIndices2}; - - auto streams = res->getAlternateStreamsCurrentDevice(); - streamWait(streams, {stream}); - - int curStream = 0; - - for (int query = 0; query < queries.getSize(0); query += queryTileSize) { - int numQueriesInTile = - std::min(queryTileSize, queries.getSize(0) - query); - - auto prefixSumOffsetsView = - prefixSumOffsets[curStream]->narrowOutermost(0, numQueriesInTile); - - auto coarseIndicesView = - topQueryToCentroid.narrowOutermost(query, numQueriesInTile); - auto queryView = - queries.narrowOutermost(query, numQueriesInTile); - auto term1View = - precompTerm1.narrowOutermost(query, numQueriesInTile); - auto term3View = - precompTerm3.narrowOutermost(query, numQueriesInTile); - - auto heapDistancesView = - heapDistances[curStream]->narrowOutermost(0, numQueriesInTile); - auto heapIndicesView = - heapIndices[curStream]->narrowOutermost(0, numQueriesInTile); - - auto outDistanceView = - outDistances.narrowOutermost(query, numQueriesInTile); - auto outIndicesView = - outIndices.narrowOutermost(query, numQueriesInTile); - - runMultiPassTile(queryView, - term1View, - precompTerm2, - term3View, - coarseIndicesView, - bitset, - useFloat16Lookup, - bytesPerCode, - numSubQuantizers, - numSubQuantizerCodes, - listCodes, - listIndices, - indicesOptions, - listLengths, - *thrustMem[curStream], - prefixSumOffsetsView, - *allDistances[curStream], - heapDistancesView, - heapIndicesView, - k, - outDistanceView, - outIndicesView, - streams[curStream]); - - curStream = (curStream + 1) % 2; - } - - streamWait({stream}, streams); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassPrecomputed.cuh b/core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassPrecomputed.cuh deleted file mode 100644 index 644ba7d99d..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/PQScanMultiPassPrecomputed.cuh +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -class GpuResources; - -void runPQScanMultiPassPrecomputed(Tensor& queries, - Tensor& precompTerm1, - NoTypeTensor<3, true>& precompTerm2, - NoTypeTensor<3, true>& precompTerm3, - Tensor& topQueryToCentroid, - Tensor& bitset, - bool useFloat16Lookup, - int bytesPerCode, - int numSubQuantizers, - int numSubQuantizerCodes, - thrust::device_vector& listCodes, - thrust::device_vector& listIndices, - IndicesOptions indicesOptions, - thrust::device_vector& listLengths, - int maxListLength, - int k, - // output - Tensor& outDistances, - // output - Tensor& outIndices, - GpuResources* res); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/RemapIndices.cpp b/core/src/index/thirdparty/faiss/gpu/impl/RemapIndices.cpp deleted file mode 100644 index a3df65c91c..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/RemapIndices.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include - -namespace faiss { namespace gpu { - -// Utility function to translate (list id, offset) to a user index on -// the CPU. In a cpp in order to use OpenMP -void ivfOffsetToUserIndex( - long* indices, - int numLists, - int queries, - int k, - const std::vector>& listOffsetToUserIndex) { - FAISS_ASSERT(numLists == listOffsetToUserIndex.size()); - -#pragma omp parallel for - for (int q = 0; q < queries; ++q) { - for (int r = 0; r < k; ++r) { - long offsetIndex = indices[q * k + r]; - - if (offsetIndex < 0) continue; - - int listId = (int) (offsetIndex >> 32); - int listOffset = (int) (offsetIndex & 0xffffffff); - - FAISS_ASSERT(listId < numLists); - auto& listIndices = listOffsetToUserIndex[listId]; - - FAISS_ASSERT(listOffset < listIndices.size()); - indices[q * k + r] = listIndices[listOffset]; - } - } -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/RemapIndices.h b/core/src/index/thirdparty/faiss/gpu/impl/RemapIndices.h deleted file mode 100644 index 234148451f..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/RemapIndices.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include - -namespace faiss { namespace gpu { - -/// Utility function to translate (list id, offset) to a user index on -/// the CPU. In a cpp in order to use OpenMP. -void ivfOffsetToUserIndex( - long* indices, - int numLists, - int queries, - int k, - const std::vector>& listOffsetToUserIndex); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/VectorResidual.cu b/core/src/index/thirdparty/faiss/gpu/impl/VectorResidual.cu deleted file mode 100644 index 980b3c3979..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/VectorResidual.cu +++ /dev/null @@ -1,148 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include -#include -#include -#include -#include -#include // in CUDA SDK, for CUDART_NAN_F - -namespace faiss { namespace gpu { - -template -__global__ void calcResidual(Tensor vecs, - Tensor centroids, - Tensor vecToCentroid, - Tensor residuals) { - auto vec = vecs[blockIdx.x]; - auto residual = residuals[blockIdx.x]; - - int centroidId = vecToCentroid[blockIdx.x]; - // Vector could be invalid (containing NaNs), so -1 was the - // classified centroid - if (centroidId == -1) { - if (LargeDim) { - for (int i = threadIdx.x; i < vecs.getSize(1); i += blockDim.x) { - residual[i] = CUDART_NAN_F; - } - } else { - residual[threadIdx.x] = CUDART_NAN_F; - } - - return; - } - - auto centroid = centroids[centroidId]; - - if (LargeDim) { - for (int i = threadIdx.x; i < vecs.getSize(1); i += blockDim.x) { - residual[i] = vec[i] - ConvertTo::to(centroid[i]); - } - } else { - residual[threadIdx.x] = vec[threadIdx.x] - - ConvertTo::to(centroid[threadIdx.x]); - } -} - -template -__global__ void gatherReconstruct(Tensor listIds, - Tensor vecs, - Tensor out) { - auto id = listIds[blockIdx.x]; - auto vec = vecs[id]; - auto outVec = out[blockIdx.x]; - - Convert conv; - - for (int i = threadIdx.x; i < vecs.getSize(1); i += blockDim.x) { - outVec[i] = id == -1 ? 0.0f : conv(vec[i]); - } -} - -template -void calcResidual(Tensor& vecs, - Tensor& centroids, - Tensor& vecToCentroid, - Tensor& residuals, - cudaStream_t stream) { - FAISS_ASSERT(vecs.getSize(1) == centroids.getSize(1)); - FAISS_ASSERT(vecs.getSize(1) == residuals.getSize(1)); - FAISS_ASSERT(vecs.getSize(0) == vecToCentroid.getSize(0)); - FAISS_ASSERT(vecs.getSize(0) == residuals.getSize(0)); - - dim3 grid(vecs.getSize(0)); - - int maxThreads = getMaxThreadsCurrentDevice(); - bool largeDim = vecs.getSize(1) > maxThreads; - dim3 block(std::min(vecs.getSize(1), maxThreads)); - - if (largeDim) { - calcResidual<<>>( - vecs, centroids, vecToCentroid, residuals); - } else { - calcResidual<<>>( - vecs, centroids, vecToCentroid, residuals); - } - - CUDA_TEST_ERROR(); -} - -template -void gatherReconstruct(Tensor& listIds, - Tensor& vecs, - Tensor& out, - cudaStream_t stream) { - FAISS_ASSERT(listIds.getSize(0) == out.getSize(0)); - FAISS_ASSERT(vecs.getSize(1) == out.getSize(1)); - - dim3 grid(listIds.getSize(0)); - - int maxThreads = getMaxThreadsCurrentDevice(); - dim3 block(std::min(vecs.getSize(1), maxThreads)); - - gatherReconstruct<<>>(listIds, vecs, out); - - CUDA_TEST_ERROR(); -} - -void runCalcResidual(Tensor& vecs, - Tensor& centroids, - Tensor& vecToCentroid, - Tensor& residuals, - cudaStream_t stream) { - calcResidual(vecs, centroids, vecToCentroid, residuals, stream); -} - -#ifdef FAISS_USE_FLOAT16 -void runCalcResidual(Tensor& vecs, - Tensor& centroids, - Tensor& vecToCentroid, - Tensor& residuals, - cudaStream_t stream) { - calcResidual(vecs, centroids, vecToCentroid, residuals, stream); -} -#endif - -void runReconstruct(Tensor& listIds, - Tensor& vecs, - Tensor& out, - cudaStream_t stream) { - gatherReconstruct(listIds, vecs, out, stream); -} - -#ifdef FAISS_USE_FLOAT16 -void runReconstruct(Tensor& listIds, - Tensor& vecs, - Tensor& out, - cudaStream_t stream) { - gatherReconstruct(listIds, vecs, out, stream); -} -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/impl/VectorResidual.cuh b/core/src/index/thirdparty/faiss/gpu/impl/VectorResidual.cuh deleted file mode 100644 index 8e8cd2e756..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/impl/VectorResidual.cuh +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include - -namespace faiss { namespace gpu { - -// Calculates residual v_i - c_j for all v_i in vecs where j = vecToCentroid[i] -void runCalcResidual(Tensor& vecs, - Tensor& centroids, - Tensor& vecToCentroid, - Tensor& residuals, - cudaStream_t stream); - -void runCalcResidual(Tensor& vecs, - Tensor& centroids, - Tensor& vecToCentroid, - Tensor& residuals, - cudaStream_t stream); - -// Gather vectors -void runReconstruct(Tensor& listIds, - Tensor& vecs, - Tensor& out, - cudaStream_t stream); - -#ifdef FAISS_USE_FLOAT16 -void runReconstruct(Tensor& listIds, - Tensor& vecs, - Tensor& out, - cudaStream_t stream); -# endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/perf/IndexWrapper-inl.h b/core/src/index/thirdparty/faiss/gpu/perf/IndexWrapper-inl.h deleted file mode 100644 index 90eb629509..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/perf/IndexWrapper-inl.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include - -namespace faiss { namespace gpu { - -template -IndexWrapper::IndexWrapper( - int numGpus, - std::function(GpuResources*, int)> init) { - FAISS_ASSERT(numGpus <= faiss::gpu::getNumDevices()); - for (int i = 0; i < numGpus; ++i) { - auto res = std::unique_ptr( - new StandardGpuResources); - - subIndex.emplace_back(init(res.get(), i)); - resources.emplace_back(std::move(res)); - } - - if (numGpus > 1) { - // create proxy - replicaIndex = - std::unique_ptr(new faiss::IndexReplicas); - - for (auto& index : subIndex) { - replicaIndex->addIndex(index.get()); - } - } -} - -template -faiss::Index* -IndexWrapper::getIndex() { - if ((bool) replicaIndex) { - return replicaIndex.get(); - } else { - FAISS_ASSERT(!subIndex.empty()); - return subIndex.front().get(); - } -} - -template -void -IndexWrapper::runOnIndices(std::function f) { - - if ((bool) replicaIndex) { - replicaIndex->runOnIndex( - [f](int, faiss::Index* index) { - f(dynamic_cast(index)); - }); - } else { - FAISS_ASSERT(!subIndex.empty()); - f(subIndex.front().get()); - } -} - -template -void -IndexWrapper::setNumProbes(int nprobe) { - runOnIndices([nprobe](GpuIndex* index) { - index->setNumProbes(nprobe); - }); -} - -} } diff --git a/core/src/index/thirdparty/faiss/gpu/perf/IndexWrapper.h b/core/src/index/thirdparty/faiss/gpu/perf/IndexWrapper.h deleted file mode 100644 index df36255a26..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/perf/IndexWrapper.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -// If we want to run multi-GPU, create a proxy to wrap the indices. -// If we don't want multi-GPU, don't involve the proxy, so it doesn't -// affect the timings. -template -struct IndexWrapper { - std::vector> resources; - std::vector> subIndex; - std::unique_ptr replicaIndex; - - IndexWrapper( - int numGpus, - std::function(GpuResources*, int)> init); - faiss::Index* getIndex(); - - void runOnIndices(std::function f); - void setNumProbes(int nprobe); -}; - -} } - -#include diff --git a/core/src/index/thirdparty/faiss/gpu/perf/PerfBinaryFlat.cu b/core/src/index/thirdparty/faiss/gpu/perf/PerfBinaryFlat.cu deleted file mode 100644 index 3e921c50da..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/perf/PerfBinaryFlat.cu +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -DEFINE_int32(k, 3, "final number of closest results returned"); -DEFINE_int32(num, 128, "# of vecs"); -DEFINE_int32(dim, 128, "# of dimensions"); -DEFINE_int32(num_queries, 3, "number of query vectors"); -DEFINE_int64(seed, -1, "specify random seed"); -DEFINE_int64(pinned_mem, 0, "pinned memory allocation to use"); -DEFINE_bool(cpu, true, "run the CPU code for timing and comparison"); -DEFINE_bool(use_unified_mem, false, "use Pascal unified memory for the index"); - -using namespace faiss::gpu; - -int main(int argc, char** argv) { - gflags::ParseCommandLineFlags(&argc, &argv, true); - - cudaProfilerStop(); - - auto seed = FLAGS_seed != -1L ? FLAGS_seed : time(nullptr); - printf("using seed %ld\n", seed); - - auto numQueries = FLAGS_num_queries; - - auto index = std::unique_ptr( - new faiss::IndexBinaryFlat(FLAGS_dim)); - - HostTensor vecs({FLAGS_num, FLAGS_dim / 8}); - faiss::byte_rand(vecs.data(), vecs.numElements(), seed); - - index->add(FLAGS_num, vecs.data()); - - printf("Database: dim %d num vecs %d\n", FLAGS_dim, FLAGS_num); - printf("Hamming lookup: %d queries, total k %d\n", - numQueries, FLAGS_k); - - // Convert to GPU index - printf("Copying index to GPU...\n"); - - GpuIndexBinaryFlatConfig config; - config.memorySpace = FLAGS_use_unified_mem ? - MemorySpace::Unified : MemorySpace::Device; - - faiss::gpu::StandardGpuResources res; - - faiss::gpu::GpuIndexBinaryFlat gpuIndex(&res, - index.get(), - config); - printf("copy done\n"); - - // Build query vectors - HostTensor cpuQuery({numQueries, FLAGS_dim / 8}); - faiss::byte_rand(cpuQuery.data(), cpuQuery.numElements(), seed); - - // Time faiss CPU - HostTensor - cpuDistances({numQueries, FLAGS_k}); - HostTensor - cpuIndices({numQueries, FLAGS_k}); - - if (FLAGS_cpu) { - float cpuTime = 0.0f; - - CpuTimer timer; - index->search(numQueries, - cpuQuery.data(), - FLAGS_k, - cpuDistances.data(), - cpuIndices.data()); - - cpuTime = timer.elapsedMilliseconds(); - printf("CPU time %.3f ms\n", cpuTime); - } - - HostTensor gpuDistances({numQueries, FLAGS_k}); - HostTensor gpuIndices({numQueries, FLAGS_k}); - - CUDA_VERIFY(cudaProfilerStart()); - faiss::gpu::synchronizeAllDevices(); - - float gpuTime = 0.0f; - - // Time GPU - { - CpuTimer timer; - - gpuIndex.search(cpuQuery.getSize(0), - cpuQuery.data(), - FLAGS_k, - gpuDistances.data(), - gpuIndices.data()); - - // There is a device -> host copy above, so no need to time - // additional synchronization with the GPU - gpuTime = timer.elapsedMilliseconds(); - } - - CUDA_VERIFY(cudaProfilerStop()); - printf("GPU time %.3f ms\n", gpuTime); - - CUDA_VERIFY(cudaDeviceSynchronize()); - - return 0; -} diff --git a/core/src/index/thirdparty/faiss/gpu/perf/PerfClustering.cpp b/core/src/index/thirdparty/faiss/gpu/perf/PerfClustering.cpp deleted file mode 100644 index 6171e77926..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/perf/PerfClustering.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -DEFINE_int32(num, 10000, "# of vecs"); -DEFINE_int32(k, 100, "# of clusters"); -DEFINE_int32(dim, 128, "# of dimensions"); -DEFINE_int32(niter, 10, "# of iterations"); -DEFINE_bool(L2_metric, true, "If true, use L2 metric. If false, use IP metric"); -DEFINE_bool(use_float16, false, "use float16 vectors and math"); -DEFINE_bool(transposed, false, "transposed vector storage"); -DEFINE_bool(verbose, false, "turn on clustering logging"); -DEFINE_int64(seed, -1, "specify random seed"); -DEFINE_int32(num_gpus, 1, "number of gpus to use"); -DEFINE_int64(min_paging_size, -1, "minimum size to use CPU -> GPU paged copies"); -DEFINE_int64(pinned_mem, -1, "pinned memory allocation to use"); -DEFINE_int32(max_points, -1, "max points per centroid"); - -using namespace faiss::gpu; - -int main(int argc, char** argv) { - gflags::ParseCommandLineFlags(&argc, &argv, true); - - cudaProfilerStop(); - - auto seed = FLAGS_seed != -1L ? FLAGS_seed : time(nullptr); - printf("using seed %ld\n", seed); - - std::vector vecs((size_t) FLAGS_num * FLAGS_dim); - faiss::float_rand(vecs.data(), vecs.size(), seed); - - printf("K-means metric %s dim %d centroids %d num train %d niter %d\n", - FLAGS_L2_metric ? "L2" : "IP", - FLAGS_dim, FLAGS_k, FLAGS_num, FLAGS_niter); - printf("float16 math %s\n", FLAGS_use_float16 ? "enabled" : "disabled"); - printf("transposed storage %s\n", FLAGS_transposed ? "enabled" : "disabled"); - printf("verbose %s\n", FLAGS_verbose ? "enabled" : "disabled"); - - auto initFn = [](faiss::gpu::GpuResources* res, int dev) -> - std::unique_ptr { - if (FLAGS_pinned_mem >= 0) { - ((faiss::gpu::StandardGpuResources*) res)->setPinnedMemory( - FLAGS_pinned_mem); - } - - GpuIndexFlatConfig config; - config.device = dev; - config.useFloat16 = FLAGS_use_float16; - config.storeTransposed = FLAGS_transposed; - - auto p = std::unique_ptr( - FLAGS_L2_metric ? - (faiss::gpu::GpuIndexFlat*) - new faiss::gpu::GpuIndexFlatL2(res, FLAGS_dim, config) : - (faiss::gpu::GpuIndexFlat*) - new faiss::gpu::GpuIndexFlatIP(res, FLAGS_dim, config)); - - if (FLAGS_min_paging_size >= 0) { - p->setMinPagingSize(FLAGS_min_paging_size); - } - return p; - }; - - IndexWrapper gpuIndex(FLAGS_num_gpus, initFn); - - CUDA_VERIFY(cudaProfilerStart()); - faiss::gpu::synchronizeAllDevices(); - - float gpuTime = 0.0f; - - faiss::ClusteringParameters cp; - cp.niter = FLAGS_niter; - cp.verbose = FLAGS_verbose; - - if (FLAGS_max_points > 0) { - cp.max_points_per_centroid = FLAGS_max_points; - } - - faiss::Clustering kmeans(FLAGS_dim, FLAGS_k, cp); - - // Time k-means - { - CpuTimer timer; - - kmeans.train(FLAGS_num, vecs.data(), *(gpuIndex.getIndex())); - - // There is a device -> host copy above, so no need to time - // additional synchronization with the GPU - gpuTime = timer.elapsedMilliseconds(); - } - - CUDA_VERIFY(cudaProfilerStop()); - printf("k-means time %.3f ms\n", gpuTime); - - CUDA_VERIFY(cudaDeviceSynchronize()); - - return 0; -} diff --git a/core/src/index/thirdparty/faiss/gpu/perf/PerfFlat.cu b/core/src/index/thirdparty/faiss/gpu/perf/PerfFlat.cu deleted file mode 100644 index 20a16382f1..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/perf/PerfFlat.cu +++ /dev/null @@ -1,149 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -DEFINE_bool(l2, true, "L2 or inner product"); -DEFINE_int32(k, 3, "final number of closest results returned"); -DEFINE_int32(num, 128, "# of vecs"); -DEFINE_int32(dim, 128, "# of dimensions"); -DEFINE_int32(num_queries, 3, "number of query vectors"); -DEFINE_bool(diff, true, "show exact distance + index output discrepancies"); -DEFINE_bool(use_float16, false, "use encodings in float16"); -DEFINE_bool(use_float16_math, false, "perform math in float16"); -DEFINE_bool(transposed, false, "store vectors transposed"); -DEFINE_int64(seed, -1, "specify random seed"); -DEFINE_int32(num_gpus, 1, "number of gpus to use"); -DEFINE_int64(pinned_mem, 0, "pinned memory allocation to use"); -DEFINE_bool(cpu, true, "run the CPU code for timing and comparison"); -DEFINE_bool(use_unified_mem, false, "use Pascal unified memory for the index"); - -using namespace faiss::gpu; - -int main(int argc, char** argv) { - gflags::ParseCommandLineFlags(&argc, &argv, true); - - cudaProfilerStop(); - - auto seed = FLAGS_seed != -1L ? FLAGS_seed : time(nullptr); - printf("using seed %ld\n", seed); - - auto numQueries = FLAGS_num_queries; - - auto index = std::unique_ptr( - new faiss::IndexFlat(FLAGS_dim, FLAGS_l2 ? - faiss::METRIC_L2 : faiss::METRIC_INNER_PRODUCT)); - - HostTensor vecs({FLAGS_num, FLAGS_dim}); - faiss::float_rand(vecs.data(), vecs.numElements(), seed); - - index->add(FLAGS_num, vecs.data()); - - printf("Database: dim %d num vecs %d\n", FLAGS_dim, FLAGS_num); - printf("%s lookup: %d queries, total k %d\n", - FLAGS_l2 ? "L2" : "IP", - numQueries, FLAGS_k); - printf("float16 encoding %s\n", FLAGS_use_float16 ? "enabled" : "disabled"); - printf("transposed storage %s\n", FLAGS_transposed ? "enabled" : "disabled"); - - // Convert to GPU index - printf("Copying index to %d GPU(s)...\n", FLAGS_num_gpus); - - auto initFn = [&index](faiss::gpu::GpuResources* res, int dev) -> - std::unique_ptr { - ((faiss::gpu::StandardGpuResources*) res)->setPinnedMemory( - FLAGS_pinned_mem); - - GpuIndexFlatConfig config; - config.device = dev; - config.useFloat16 = FLAGS_use_float16; - config.storeTransposed = FLAGS_transposed; - config.memorySpace = FLAGS_use_unified_mem ? - MemorySpace::Unified : MemorySpace::Device; - - auto p = std::unique_ptr( - new faiss::gpu::GpuIndexFlat(res, index.get(), config)); - return p; - }; - - IndexWrapper gpuIndex(FLAGS_num_gpus, initFn); - printf("copy done\n"); - - // Build query vectors - HostTensor cpuQuery({numQueries, FLAGS_dim}); - faiss::float_rand(cpuQuery.data(), cpuQuery.numElements(), seed); - - // Time faiss CPU - HostTensor cpuDistances({numQueries, FLAGS_k}); - HostTensor cpuIndices({numQueries, FLAGS_k}); - - if (FLAGS_cpu) { - float cpuTime = 0.0f; - - CpuTimer timer; - index->search(numQueries, - cpuQuery.data(), - FLAGS_k, - cpuDistances.data(), - cpuIndices.data()); - - cpuTime = timer.elapsedMilliseconds(); - printf("CPU time %.3f ms\n", cpuTime); - } - - HostTensor gpuDistances({numQueries, FLAGS_k}); - HostTensor gpuIndices({numQueries, FLAGS_k}); - - CUDA_VERIFY(cudaProfilerStart()); - faiss::gpu::synchronizeAllDevices(); - - float gpuTime = 0.0f; - - // Time GPU - { - CpuTimer timer; - - gpuIndex.getIndex()->search(cpuQuery.getSize(0), - cpuQuery.data(), - FLAGS_k, - gpuDistances.data(), - gpuIndices.data()); - - // There is a device -> host copy above, so no need to time - // additional synchronization with the GPU - gpuTime = timer.elapsedMilliseconds(); - } - - CUDA_VERIFY(cudaProfilerStop()); - printf("GPU time %.3f ms\n", gpuTime); - - if (FLAGS_cpu) { - compareLists(cpuDistances.data(), cpuIndices.data(), - gpuDistances.data(), gpuIndices.data(), - numQueries, FLAGS_k, - "", true, FLAGS_diff, false); - } - - CUDA_VERIFY(cudaDeviceSynchronize()); - - return 0; -} diff --git a/core/src/index/thirdparty/faiss/gpu/perf/PerfIVFFlat.cu b/core/src/index/thirdparty/faiss/gpu/perf/PerfIVFFlat.cu deleted file mode 100644 index 8b51b90ecf..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/perf/PerfIVFFlat.cu +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -DEFINE_int32(nprobe, 5, "number of coarse centroids to probe"); -DEFINE_int32(k, 3, "final number of closest results returned"); -DEFINE_int32(num_queries, 3, "number of query vectors"); -DEFINE_string(in, "/home/jhj/local/index.out", "index file for input"); -DEFINE_bool(diff, true, "show exact distance + index output discrepancies"); -DEFINE_bool(use_float16_coarse, false, "coarse quantizer in float16"); -DEFINE_int64(seed, -1, "specify random seed"); -DEFINE_int32(num_gpus, 1, "number of gpus to use"); -DEFINE_int32(index, 2, "0 = no indices on GPU; 1 = 32 bit, 2 = 64 bit on GPU"); - -using namespace faiss::gpu; - -int main(int argc, char** argv) { - gflags::ParseCommandLineFlags(&argc, &argv, true); - - cudaProfilerStop(); - - auto seed = FLAGS_seed != -1L ? FLAGS_seed : time(nullptr); - printf("using seed %ld\n", seed); - - auto numQueries = FLAGS_num_queries; - - auto index = std::unique_ptr( - dynamic_cast(faiss::read_index(FLAGS_in.c_str()))); - FAISS_ASSERT((bool) index); - index->nprobe = FLAGS_nprobe; - - auto dim = index->d; - - printf("Database: dim %d num vecs %ld\n", dim, index->ntotal); - printf("Coarse centroids: %ld\n", index->quantizer->ntotal); - printf("L2 lookup: %d queries, nprobe %d, total k %d\n", - numQueries, FLAGS_nprobe, FLAGS_k); - printf("float16 coarse quantizer %s\n", - FLAGS_use_float16_coarse ? "enabled" : "disabled"); - - // Convert to GPU index - printf("Copying index to %d GPU(s)...\n", FLAGS_num_gpus); - - auto initFn = [&index](faiss::gpu::GpuResources* res, int dev) -> - std::unique_ptr { - GpuIndexIVFFlatConfig config; - config.device = dev; - config.indicesOptions = (faiss::gpu::IndicesOptions) FLAGS_index; - config.flatConfig.useFloat16 = FLAGS_use_float16_coarse; - - auto p = std::unique_ptr( - new faiss::gpu::GpuIndexIVFFlat(res, - index->d, - index->nlist, - index->metric_type, - config)); - p->copyFrom(index.get()); - return p; - }; - - IndexWrapper gpuIndex(FLAGS_num_gpus, initFn); - gpuIndex.setNumProbes(FLAGS_nprobe); - printf("copy done\n"); - - // Build query vectors - HostTensor cpuQuery({numQueries, dim}); - faiss::float_rand(cpuQuery.data(), cpuQuery.numElements(), seed); - - // Time faiss CPU - HostTensor cpuDistances({numQueries, FLAGS_k}); - HostTensor cpuIndices({numQueries, FLAGS_k}); - - float cpuTime = 0.0f; - - { - CpuTimer timer; - index->search(numQueries, - cpuQuery.data(), - FLAGS_k, - cpuDistances.data(), - cpuIndices.data()); - - cpuTime = timer.elapsedMilliseconds(); - } - - printf("CPU time %.3f ms\n", cpuTime); - - HostTensor gpuDistances({numQueries, FLAGS_k}); - HostTensor gpuIndices({numQueries, FLAGS_k}); - - CUDA_VERIFY(cudaProfilerStart()); - faiss::gpu::synchronizeAllDevices(); - - float gpuTime = 0.0f; - - // Time GPU - { - CpuTimer timer; - - gpuIndex.getIndex()->search(cpuQuery.getSize(0), - cpuQuery.data(), - FLAGS_k, - gpuDistances.data(), - gpuIndices.data()); - - // There is a device -> host copy above, so no need to time - // additional synchronization with the GPU - gpuTime = timer.elapsedMilliseconds(); - } - - CUDA_VERIFY(cudaProfilerStop()); - printf("GPU time %.3f ms\n", gpuTime); - - compareLists(cpuDistances.data(), cpuIndices.data(), - gpuDistances.data(), gpuIndices.data(), - numQueries, FLAGS_k, - "", true, FLAGS_diff, false); - - CUDA_VERIFY(cudaDeviceSynchronize()); - // printf("\ncudaMalloc usage %zd\n", - // resources.getMemoryManager().getHighWaterCudaMalloc()); - - return 0; -} diff --git a/core/src/index/thirdparty/faiss/gpu/perf/PerfIVFPQ.cu b/core/src/index/thirdparty/faiss/gpu/perf/PerfIVFPQ.cu deleted file mode 100644 index 82eb648a1f..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/perf/PerfIVFPQ.cu +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -DEFINE_int32(nprobe, 5, "number of coarse centroids to probe"); -DEFINE_int32(k, 3, "final number of closest results returned"); -DEFINE_int32(num_queries, 3, "number of query vectors"); -DEFINE_string(in, "/home/jhj/local/index.out", "index file for input"); -DEFINE_bool(diff, true, "show exact distance + index output discrepancies"); -DEFINE_bool(use_precomputed, true, "enable or disable precomputed codes"); -DEFINE_bool(float16_lookup, false, "use float16 residual distance tables"); -DEFINE_int64(seed, -1, "specify random seed"); -DEFINE_int32(num_gpus, 1, "number of gpus to use"); -DEFINE_int32(index, 2, "0 = no indices on GPU; 1 = 32 bit, 2 = 64 bit on GPU"); - -using namespace faiss::gpu; - -int main(int argc, char** argv) { - gflags::ParseCommandLineFlags(&argc, &argv, true); - - CUDA_VERIFY(cudaProfilerStop()); - - auto seed = FLAGS_seed != -1L ? FLAGS_seed : time(nullptr); - printf("using seed %ld\n", seed); - - auto numQueries = FLAGS_num_queries; - - auto index = std::unique_ptr( - dynamic_cast(faiss::read_index(FLAGS_in.c_str()))); - FAISS_ASSERT((bool) index); - index->nprobe = FLAGS_nprobe; - - if (!FLAGS_use_precomputed) { - index->use_precomputed_table = 0; - } - - auto dim = index->d; - auto codes = index->pq.M; - auto bitsPerCode = index->pq.nbits; - - printf("Database: dim %d num vecs %ld\n", dim, index->ntotal); - printf("Coarse centroids: %ld\n", index->quantizer->ntotal); - printf("PQ centroids: codes %ld bits per code %ld\n", codes, bitsPerCode); - printf("L2 lookup: %d queries, nprobe %d, total k %d, " - "precomputed codes %d\n\n", - numQueries, FLAGS_nprobe, FLAGS_k, - FLAGS_use_precomputed); - - // Convert to GPU index - printf("Copying index to %d GPU(s)...\n", FLAGS_num_gpus); - - auto precomp = FLAGS_use_precomputed; - auto indicesOpt = (faiss::gpu::IndicesOptions) FLAGS_index; - auto useFloat16Lookup = FLAGS_float16_lookup; - - auto initFn = [precomp, indicesOpt, useFloat16Lookup, &index] - (faiss::gpu::GpuResources* res, int dev) -> - std::unique_ptr { - - faiss::gpu::GpuIndexIVFPQConfig config; - config.device = dev; - config.usePrecomputedTables = precomp; - config.indicesOptions = indicesOpt; - config.useFloat16LookupTables = useFloat16Lookup; - - auto p = std::unique_ptr( - new faiss::gpu::GpuIndexIVFPQ(res, index.get(), config)); - - return p; - }; - - IndexWrapper gpuIndex(FLAGS_num_gpus, initFn); - gpuIndex.setNumProbes(FLAGS_nprobe); - printf("copy done\n"); - - // Build query vectors - HostTensor cpuQuery({numQueries, dim}); - faiss::float_rand(cpuQuery.data(), cpuQuery.numElements(), seed); - - // Time faiss CPU - HostTensor cpuDistances({numQueries, FLAGS_k}); - HostTensor cpuIndices({numQueries, FLAGS_k}); - - float cpuTime = 0.0f; - - { - CpuTimer timer; - index->search(numQueries, - cpuQuery.data(), - FLAGS_k, - cpuDistances.data(), - cpuIndices.data()); - - cpuTime = timer.elapsedMilliseconds(); - } - - printf("CPU time %.3f ms\n", cpuTime); - - HostTensor gpuDistances({numQueries, FLAGS_k}); - HostTensor gpuIndices({numQueries, FLAGS_k}); - - CUDA_VERIFY(cudaProfilerStart()); - faiss::gpu::synchronizeAllDevices(); - - float gpuTime = 0.0f; - - // Time GPU - { - CpuTimer timer; - - gpuIndex.getIndex()->search(cpuQuery.getSize(0), - cpuQuery.data(), - FLAGS_k, - gpuDistances.data(), - gpuIndices.data()); - - // There is a device -> host copy above, so no need to time - // additional synchronization with the GPU - gpuTime = timer.elapsedMilliseconds(); - } - - CUDA_VERIFY(cudaProfilerStop()); - printf("GPU time %.3f ms\n", gpuTime); - - compareLists(cpuDistances.data(), cpuIndices.data(), - gpuDistances.data(), gpuIndices.data(), - numQueries, FLAGS_k, - "", true, FLAGS_diff, false); - - CUDA_VERIFY(cudaDeviceSynchronize()); - // printf("\ncudaMalloc usage %zd\n", - // resources.getMemoryManager().getHighWaterCudaMalloc()); - - return 0; -} diff --git a/core/src/index/thirdparty/faiss/gpu/perf/PerfIVFPQAdd.cpp b/core/src/index/thirdparty/faiss/gpu/perf/PerfIVFPQAdd.cpp deleted file mode 100644 index 1e45d635a5..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/perf/PerfIVFPQAdd.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -DEFINE_int32(batches, 10, "number of batches of vectors to add"); -DEFINE_int32(batch_size, 10000, "number of vectors in each batch"); -DEFINE_int32(dim, 256, "dimension of vectors"); -DEFINE_int32(centroids, 4096, "num coarse centroids to use"); -DEFINE_int32(bytes_per_vec, 32, "bytes per encoded vector"); -DEFINE_int32(bits_per_code, 8, "bits per PQ code"); -DEFINE_int32(index, 2, "0 = no indices on GPU; 1 = 32 bit, 2 = 64 bit on GPU"); -DEFINE_bool(time_gpu, true, "time add to GPU"); -DEFINE_bool(time_cpu, false, "time add to CPU"); -DEFINE_bool(per_batch_time, false, "print per-batch times"); -DEFINE_bool(reserve_memory, false, "whether or not to pre-reserve memory"); - -int main(int argc, char** argv) { - gflags::ParseCommandLineFlags(&argc, &argv, true); - - cudaProfilerStop(); - - int dim = FLAGS_dim; - int numCentroids = FLAGS_centroids; - int bytesPerVec = FLAGS_bytes_per_vec; - int bitsPerCode = FLAGS_bits_per_code; - - faiss::gpu::StandardGpuResources res; - - // IndexIVFPQ will complain, but just give us enough to get through this - int numTrain = 4 * numCentroids; - std::vector trainVecs = faiss::gpu::randVecs(numTrain, dim); - - faiss::IndexFlatL2 coarseQuantizer(dim); - faiss::IndexIVFPQ cpuIndex(&coarseQuantizer, dim, numCentroids, - bytesPerVec, bitsPerCode); - if (FLAGS_time_cpu) { - cpuIndex.train(numTrain, trainVecs.data()); - } - - faiss::gpu::GpuIndexIVFPQConfig config; - config.device = 0; - config.indicesOptions = (faiss::gpu::IndicesOptions) FLAGS_index; - - faiss::gpu::GpuIndexIVFPQ gpuIndex( - &res, dim, numCentroids, bytesPerVec, bitsPerCode, - faiss::METRIC_L2, config); - - if (FLAGS_time_gpu) { - gpuIndex.train(numTrain, trainVecs.data()); - if (FLAGS_reserve_memory) { - size_t numVecs = (size_t) FLAGS_batches * (size_t) FLAGS_batch_size; - gpuIndex.reserveMemory(numVecs); - } - } - - cudaDeviceSynchronize(); - CUDA_VERIFY(cudaProfilerStart()); - - float totalGpuTime = 0.0f; - float totalCpuTime = 0.0f; - - for (int i = 0; i < FLAGS_batches; ++i) { - if (!FLAGS_per_batch_time) { - if (i % 10 == 0) { - printf("Adding batch %d\n", i + 1); - } - } - - auto addVecs = faiss::gpu::randVecs(FLAGS_batch_size, dim); - - if (FLAGS_time_gpu) { - faiss::gpu::CpuTimer timer; - gpuIndex.add(FLAGS_batch_size, addVecs.data()); - CUDA_VERIFY(cudaDeviceSynchronize()); - auto time = timer.elapsedMilliseconds(); - - totalGpuTime += time; - - if (FLAGS_per_batch_time) { - printf("Batch %d | GPU time to add %d vecs: %.3f ms (%.5f ms per)\n", - i + 1, FLAGS_batch_size, time, time / (float) FLAGS_batch_size); - } - } - - if (FLAGS_time_cpu) { - faiss::gpu::CpuTimer timer; - cpuIndex.add(FLAGS_batch_size, addVecs.data()); - auto time = timer.elapsedMilliseconds(); - - totalCpuTime += time; - - if (FLAGS_per_batch_time) { - printf("Batch %d | CPU time to add %d vecs: %.3f ms (%.5f ms per)\n", - i + 1, FLAGS_batch_size, time, time / (float) FLAGS_batch_size); - } - } - } - - CUDA_VERIFY(cudaProfilerStop()); - - int total = FLAGS_batch_size * FLAGS_batches; - - if (FLAGS_time_gpu) { - printf("%d dim, %d centroids, %d x %d encoding\n" - "GPU time to add %d vectors (%d batches, %d per batch): " - "%.3f ms (%.3f us per)\n", - dim, numCentroids, bytesPerVec, bitsPerCode, - total, FLAGS_batches, FLAGS_batch_size, - totalGpuTime, totalGpuTime * 1000.0f / (float) total); - } - - if (FLAGS_time_cpu) { - printf("%d dim, %d centroids, %d x %d encoding\n" - "CPU time to add %d vectors (%d batches, %d per batch): " - "%.3f ms (%.3f us per)\n", - dim, numCentroids, bytesPerVec, bitsPerCode, - total, FLAGS_batches, FLAGS_batch_size, - totalCpuTime, totalCpuTime * 1000.0f / (float) total); - } - - return 0; -} diff --git a/core/src/index/thirdparty/faiss/gpu/perf/PerfSelect.cu b/core/src/index/thirdparty/faiss/gpu/perf/PerfSelect.cu deleted file mode 100644 index 5e2eb49f13..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/perf/PerfSelect.cu +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -DEFINE_int32(rows, 10000, "rows in matrix"); -DEFINE_int32(cols, 40000, "cols in matrix"); -DEFINE_int32(k, 100, "k"); -DEFINE_bool(dir, false, "direction of sort"); -DEFINE_bool(warp, false, "warp select"); -DEFINE_int32(iter, 5, "iterations to run"); -DEFINE_bool(k_powers, false, "test k powers of 2 from 1 -> max k"); - -int main(int argc, char** argv) { - gflags::ParseCommandLineFlags(&argc, &argv, true); - - std::vector v = faiss::gpu::randVecs(FLAGS_rows, FLAGS_cols); - faiss::gpu::HostTensor hostVal({FLAGS_rows, FLAGS_cols}); - - for (int r = 0; r < FLAGS_rows; ++r) { - for (int c = 0; c < FLAGS_cols; ++c) { - hostVal[r][c] = v[r * FLAGS_cols + c]; - } - } - - // Select top-k on GPU - faiss::gpu::DeviceTensor gpuVal(hostVal, 0); - - int startK = FLAGS_k; - int limitK = FLAGS_k; - - if (FLAGS_k_powers) { - startK = 1; - limitK = GPU_MAX_SELECTION_K; - } - - faiss::gpu::DeviceTensor bitset(nullptr, {0}); - for (int k = startK; k <= limitK; k *= 2) { - faiss::gpu::DeviceTensor gpuOutVal({FLAGS_rows, k}); - faiss::gpu::DeviceTensor gpuOutInd({FLAGS_rows, k}); - - for (int i = 0; i < FLAGS_iter; ++i) { - if (FLAGS_warp) { - faiss::gpu::runWarpSelect(gpuVal, gpuOutVal, gpuOutInd, - FLAGS_dir, k, 0); - } else { - faiss::gpu::runBlockSelect(gpuVal, bitset, gpuOutVal, gpuOutInd, - FLAGS_dir, k, 0); - } - } - } - - cudaDeviceSynchronize(); -} diff --git a/core/src/index/thirdparty/faiss/gpu/perf/WriteIndex.cpp b/core/src/index/thirdparty/faiss/gpu/perf/WriteIndex.cpp deleted file mode 100644 index af363787a9..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/perf/WriteIndex.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include - -// For IVFPQ: -DEFINE_bool(ivfpq, false, "use IVFPQ encoding"); -DEFINE_int32(codes, 4, "number of PQ codes per vector"); -DEFINE_int32(bits_per_code, 8, "number of bits per PQ code"); - -// For IVFFlat: -DEFINE_bool(l2, true, "use L2 metric (versus IP metric)"); -DEFINE_bool(ivfflat, false, "use IVF flat encoding"); - -// For both: -DEFINE_string(out, "/home/jhj/local/index.out", "index file for output"); -DEFINE_int32(dim, 128, "vector dimension"); -DEFINE_int32(num_coarse, 100, "number of coarse centroids"); -DEFINE_int32(num, 100000, "total database size"); -DEFINE_int32(num_train, -1, "number of database vecs to train on"); - -template -void fillAndSave(T& index, int numTrain, int num, int dim) { - auto trainVecs = faiss::gpu::randVecs(numTrain, dim); - index.train(numTrain, trainVecs.data()); - - constexpr int kAddChunk = 1000000; - - for (int i = 0; i < num; i += kAddChunk) { - int numRemaining = (num - i) < kAddChunk ? (num - i) : kAddChunk; - auto vecs = faiss::gpu::randVecs(numRemaining, dim); - - printf("adding at %d: %d\n", i, numRemaining); - index.add(numRemaining, vecs.data()); - } - - faiss::write_index(&index, FLAGS_out.c_str()); -} - -int main(int argc, char** argv) { - gflags::ParseCommandLineFlags(&argc, &argv, true); - - // Either ivfpq or ivfflat must be set - if ((FLAGS_ivfpq && FLAGS_ivfflat) || - (!FLAGS_ivfpq && !FLAGS_ivfflat)) { - printf("must specify either ivfpq or ivfflat\n"); - return 1; - } - - auto dim = FLAGS_dim; - auto numCentroids = FLAGS_num_coarse; - auto num = FLAGS_num; - auto numTrain = FLAGS_num_train; - numTrain = numTrain == -1 ? std::max((num / 4), 1) : numTrain; - numTrain = std::min(num, numTrain); - - if (FLAGS_ivfpq) { - faiss::IndexFlatL2 quantizer(dim); - faiss::IndexIVFPQ index(&quantizer, dim, numCentroids, - FLAGS_codes, FLAGS_bits_per_code); - index.verbose = true; - - printf("IVFPQ: codes %d bits per code %d\n", - FLAGS_codes, FLAGS_bits_per_code); - printf("Lists: %d\n", numCentroids); - printf("Database: dim %d num vecs %d trained on %d\n", dim, num, numTrain); - printf("output file: %s\n", FLAGS_out.c_str()); - - fillAndSave(index, numTrain, num, dim); - } else if (FLAGS_ivfflat) { - faiss::IndexFlatL2 quantizerL2(dim); - faiss::IndexFlatIP quantizerIP(dim); - - faiss::IndexFlat* quantizer = FLAGS_l2 ? - (faiss::IndexFlat*) &quantizerL2 : - (faiss::IndexFlat*) &quantizerIP; - - faiss::IndexIVFFlat index(quantizer, dim, numCentroids, - FLAGS_l2 ? faiss::METRIC_L2 : - faiss::METRIC_INNER_PRODUCT); - - printf("IVFFlat: metric %s\n", FLAGS_l2 ? "L2" : "IP"); - printf("Lists: %d\n", numCentroids); - printf("Database: dim %d num vecs %d trained on %d\n", dim, num, numTrain); - printf("output file: %s\n", FLAGS_out.c_str()); - - fillAndSave(index, numTrain, num, dim); - } - - return 0; -} diff --git a/core/src/index/thirdparty/faiss/gpu/perf/slow.py b/core/src/index/thirdparty/faiss/gpu/perf/slow.py deleted file mode 100644 index a096311c4e..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/perf/slow.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#! /usr/bin/env python3 -# this is a slow computation to test whether ctrl-C handling works -import faiss -import numpy as np - -def test_slow(): - d = 256 - index = faiss.index_cpu_to_gpu(faiss.StandardGpuResources(), - 0, faiss.IndexFlatL2(d)) - x = np.random.rand(10 ** 6, d).astype('float32') - print('add') - index.add(x) - print('search') - index.search(x, 10) - print('done') - - -if __name__ == '__main__': - test_slow() diff --git a/core/src/index/thirdparty/faiss/gpu/test/Makefile b/core/src/index/thirdparty/faiss/gpu/test/Makefile deleted file mode 100644 index 6836314810..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/test/Makefile +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - --include ../../makefile.inc - -TESTS_SRC = TestGpuIndexFlat.cpp TestGpuIndexIVFPQ.cpp \ -TestGpuIndexBinaryFlat.cpp TestGpuIndexIVFFlat.cpp TestGpuMemoryException.cpp -CUDA_TESTS_SRC = TestGpuSelect.cu - -TESTS_OBJ = $(TESTS_SRC:.cpp=.o) -CUDA_TESTS_OBJ = $(CUDA_TESTS_SRC:.cu=.o) - -TESTS_BIN = $(TESTS_OBJ:.o=) $(CUDA_TESTS_OBJ:.o=) - - -# test_gpu_index.py test_pytorch_faiss.py - -run: $(TESTS_BIN) $(CUDA_TESTS_BIN) - for t in $(TESTS_BIN) $(CUDA_TESTS_BIN); do ./$$t || exit; done - -$(CUDA_TESTS_OBJ): %.o: %.cu gtest - $(NVCC) $(NVCCFLAGS) -g -O3 -o $@ -c $< -Igtest/include - -$(TESTS_OBJ): %.o: %.cpp gtest - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(CPUFLAGS) -o $@ -c $< -Igtest/include - -$(TESTS_BIN): %: %.o TestUtils.o ../../libfaiss.a gtest/make/gtest.a - $(CXX) -o $@ $^ $(LDFLAGS) $(LIBS) - -demo_ivfpq_indexing_gpu: demo_ivfpq_indexing_gpu.o ../../libfaiss.a - $(CXX) -o $@ $^ $(LDFLAGS) $(LIBS) - -demo_ivfpq_indexing_gpu.o: demo_ivfpq_indexing_gpu.cpp - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(CPUFLAGS) -o $@ -c $^ - -gtest/make/gtest.a: gtest - $(MAKE) -C gtest/make CXX="$(CXX)" CXXFLAGS="$(CXXFLAGS)" gtest.a - -gtest: - curl -L https://github.com/google/googletest/archive/release-1.8.0.tar.gz | tar xz && \ - mv googletest-release-1.8.0/googletest gtest && \ - rm -rf googletest-release-1.8.0 - -clean: - rm -f *.o $(TESTS_BIN) - rm -rf gtest - rm -f demo_ivfpq_indexing_gpu - -.PHONY: clean run diff --git a/core/src/index/thirdparty/faiss/gpu/test/TestGpuDistance.cu b/core/src/index/thirdparty/faiss/gpu/test/TestGpuDistance.cu deleted file mode 100644 index f188a1b7d3..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/test/TestGpuDistance.cu +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void testTransposition(bool colMajorVecs, - bool colMajorQueries, - faiss::MetricType metric, - float metricArg = 0) { - int device = faiss::gpu::randVal(0, faiss::gpu::getNumDevices() - 1); - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - int dim = faiss::gpu::randVal(20, 150); - int numVecs = faiss::gpu::randVal(10, 30000); - int numQuery = faiss::gpu::randVal(1, 1024); - int k = std::min(numVecs, faiss::gpu::randVal(20, 70)); - - // Input data for CPU - std::vector vecs = faiss::gpu::randVecs(numVecs, dim); - std::vector queries = faiss::gpu::randVecs(numQuery, dim); - - if (metric == faiss::MetricType::METRIC_JensenShannon) { - // make values positive - for (auto& v : vecs) { - v = std::abs(v); - if (v == 0) { - v = 1e-6; - } - } - - for (auto& q : queries) { - q = std::abs(q); - if (q == 0) { - q = 1e-6; - } - } - } - - // The CPU index is our reference for the results - faiss::IndexFlat cpuIndex(dim, metric); - cpuIndex.metric_arg = metricArg; - cpuIndex.add(numVecs, vecs.data()); - - std::vector cpuDistance(numQuery * k, 0); - std::vector cpuIndices(numQuery * k, -1); - - cpuIndex.search(numQuery, queries.data(), k, - cpuDistance.data(), cpuIndices.data()); - - // The transpose and distance code assumes the desired device is already set - faiss::gpu::DeviceScope scope(device); - auto stream = res.getDefaultStream(device); - - // Copy input data to GPU, and pre-transpose both vectors and queries for - // passing - auto gpuVecs = faiss::gpu::toDevice( - nullptr, device, vecs.data(), stream, {numVecs, dim}); - auto gpuQueries = faiss::gpu::toDevice( - nullptr, device, queries.data(), stream, {numQuery, dim}); - - faiss::gpu::DeviceTensor vecsT({dim, numVecs}); - faiss::gpu::runTransposeAny(gpuVecs, 0, 1, vecsT, stream); - - faiss::gpu::DeviceTensor queriesT({dim, numQuery}); - faiss::gpu::runTransposeAny(gpuQueries, 0, 1, queriesT, stream); - - std::vector gpuDistance(numQuery * k, 0); - std::vector gpuIndices(numQuery * k, -1); - - faiss::gpu::GpuDistanceParams args; - args.metric = metric; - args.metricArg = metricArg; - args.k = k; - args.dims = dim; - args.vectors = colMajorVecs ? vecsT.data() : gpuVecs.data(); - args.vectorsRowMajor = !colMajorVecs; - args.numVectors = numVecs; - args.queries = colMajorQueries ? queriesT.data() : gpuQueries.data(); - args.queriesRowMajor = !colMajorQueries; - args.numQueries = numQuery; - args.outDistances = gpuDistance.data(); - args.outIndices = gpuIndices.data(); - - faiss::gpu::bfKnn(&res, args); - - std::stringstream str; - str << "metric " << metric - << " colMajorVecs " << colMajorVecs - << " colMajorQueries " << colMajorQueries; - - faiss::gpu::compareLists(cpuDistance.data(), - cpuIndices.data(), - gpuDistance.data(), - gpuIndices.data(), - numQuery, k, - str.str(), - false, false, true, - 6e-3f, 0.1f, 0.015f); -} - -// Test different memory layouts for brute-force k-NN -TEST(TestGpuDistance, Transposition_RR) { - testTransposition(false, false, faiss::MetricType::METRIC_L2); - testTransposition(false, false, faiss::MetricType::METRIC_INNER_PRODUCT); -} - -TEST(TestGpuDistance, Transposition_RC) { - testTransposition(false, true, faiss::MetricType::METRIC_L2); -} - -TEST(TestGpuDistance, Transposition_CR) { - testTransposition(true, false, faiss::MetricType::METRIC_L2); -} - -TEST(TestGpuDistance, Transposition_CC) { - testTransposition(true, true, faiss::MetricType::METRIC_L2); -} - -TEST(TestGpuDistance, L1) { - testTransposition(false, false, faiss::MetricType::METRIC_L1); -} - -// Test other transpositions with the general distance kernel -TEST(TestGpuDistance, L1_RC) { - testTransposition(false, true, faiss::MetricType::METRIC_L1); -} - -TEST(TestGpuDistance, L1_CR) { - testTransposition(true, false, faiss::MetricType::METRIC_L1); -} - -TEST(TestGpuDistance, L1_CC) { - testTransposition(true, true, faiss::MetricType::METRIC_L1); -} - -// Test remainder of metric types -TEST(TestGpuDistance, Linf) { - testTransposition(false, false, faiss::MetricType::METRIC_Linf); -} - -TEST(TestGpuDistance, Lp) { - testTransposition(false, false, faiss::MetricType::METRIC_Lp, 3); -} - -TEST(TestGpuDistance, Canberra) { - testTransposition(false, false, faiss::MetricType::METRIC_Canberra); -} - -TEST(TestGpuDistance, BrayCurtis) { - testTransposition(false, false, faiss::MetricType::METRIC_BrayCurtis); -} - -TEST(TestGpuDistance, JensenShannon) { - testTransposition(false, false, faiss::MetricType::METRIC_JensenShannon); -} - -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - - // just run with a fixed test seed - faiss::gpu::setTestSeed(100); - - return RUN_ALL_TESTS(); -} diff --git a/core/src/index/thirdparty/faiss/gpu/test/TestGpuIndexBinaryFlat.cpp b/core/src/index/thirdparty/faiss/gpu/test/TestGpuIndexBinaryFlat.cpp deleted file mode 100644 index 14c28c155a..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/test/TestGpuIndexBinaryFlat.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void compareBinaryDist(const std::vector& cpuDist, - const std::vector& cpuLabels, - const std::vector& gpuDist, - const std::vector& gpuLabels, - int numQuery, - int k) { - for (int i = 0; i < numQuery; ++i) { - // The index order can be permuted within a group that has the same - // distance, since this is based on the order in which the algorithm - // encounters the values. The last set of equivalent distances seen in the - // min-k might be truncated, so we can't check that set, but all others we - // can check. - std::set cpuLabelSet; - std::set gpuLabelSet; - - int curDist = -1; - - for (int j = 0; j < k; ++j) { - int idx = i * k + j; - - if (curDist == -1) { - curDist = cpuDist[idx]; - } - - if (curDist != cpuDist[idx]) { - // Distances must be monotonically increasing - EXPECT_LT(curDist, cpuDist[idx]); - - // This is a new set of distances - EXPECT_EQ(cpuLabelSet, gpuLabelSet); - curDist = cpuDist[idx]; - cpuLabelSet.clear(); - gpuLabelSet.clear(); - } - - cpuLabelSet.insert(cpuLabels[idx]); - gpuLabelSet.insert(gpuLabels[idx]); - - // Because the distances are reproducible, they must be exactly the same - EXPECT_EQ(cpuDist[idx], gpuDist[idx]); - } - } -} - -template -void testGpuIndexBinaryFlat(int kOverride = -1) { - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexBinaryFlatConfig config; - config.device = faiss::gpu::randVal(0, faiss::gpu::getNumDevices() - 1); - - // multiples of 8 and multiples of 32 use different implementations - int dims = faiss::gpu::randVal(1, 20) * DimMultiple; - faiss::gpu::GpuIndexBinaryFlat gpuIndex(&res, dims, config); - - faiss::IndexBinaryFlat cpuIndex(dims); - - int k = kOverride > 0 ? - kOverride : faiss::gpu::randVal(1, faiss::gpu::getMaxKSelection()); - int numVecs = faiss::gpu::randVal(k + 1, 20000); - int numQuery = faiss::gpu::randVal(1, 1000); - - auto data = faiss::gpu::randBinaryVecs(numVecs, dims); - gpuIndex.add(numVecs, data.data()); - cpuIndex.add(numVecs, data.data()); - - auto query = faiss::gpu::randBinaryVecs(numQuery, dims); - - std::vector cpuDist(numQuery * k); - std::vector cpuLabels(numQuery * k); - - cpuIndex.search(numQuery, - query.data(), - k, - cpuDist.data(), - cpuLabels.data()); - - std::vector gpuDist(numQuery * k); - std::vector gpuLabels(numQuery * k); - - gpuIndex.search(numQuery, - query.data(), - k, - gpuDist.data(), - gpuLabels.data()); - - compareBinaryDist(cpuDist, cpuLabels, - gpuDist, gpuLabels, - numQuery, k); -} - -TEST(TestGpuIndexBinaryFlat, Test8) { - for (int tries = 0; tries < 4; ++tries) { - testGpuIndexBinaryFlat<8>(); - } -} - -TEST(TestGpuIndexBinaryFlat, Test32) { - for (int tries = 0; tries < 4; ++tries) { - testGpuIndexBinaryFlat<32>(); - } -} - -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - - // just run with a fixed test seed - faiss::gpu::setTestSeed(100); - - return RUN_ALL_TESTS(); -} diff --git a/core/src/index/thirdparty/faiss/gpu/test/TestGpuIndexFlat.cpp b/core/src/index/thirdparty/faiss/gpu/test/TestGpuIndexFlat.cpp deleted file mode 100644 index 73cfe20542..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/test/TestGpuIndexFlat.cpp +++ /dev/null @@ -1,393 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - -// FIXME: figure out a better way to test fp16 -constexpr float kF16MaxRelErr = 0.07f; -constexpr float kF32MaxRelErr = 6e-3f; - -struct TestFlatOptions { - TestFlatOptions() - : metric(faiss::MetricType::METRIC_L2), - metricArg(0), - useFloat16(false), - useTransposed(false), - numVecsOverride(-1), - numQueriesOverride(-1), - kOverride(-1), - dimOverride(-1) { - } - - faiss::MetricType metric; - float metricArg; - - bool useFloat16; - bool useTransposed; - int numVecsOverride; - int numQueriesOverride; - int kOverride; - int dimOverride; -}; - -void testFlat(const TestFlatOptions& opt) { - int numVecs = opt.numVecsOverride > 0 ? - opt.numVecsOverride : faiss::gpu::randVal(1000, 5000); - int dim = opt.dimOverride > 0 ? - opt.dimOverride : faiss::gpu::randVal(50, 800); - int numQuery = opt.numQueriesOverride > 0 ? - opt.numQueriesOverride : faiss::gpu::randVal(1, 512); - - // Due to loss of precision in a float16 accumulator, for large k, - // the number of differences is pretty huge. Restrict ourselves to a - // fairly small `k` for float16 - int k = opt.useFloat16 ? - std::min(faiss::gpu::randVal(1, 50), numVecs) : - std::min(faiss::gpu::randVal(1, faiss::gpu::getMaxKSelection()), numVecs); - if (opt.kOverride > 0) { - k = opt.kOverride; - } - - faiss::IndexFlat cpuIndex(dim, opt.metric); - cpuIndex.metric_arg = opt.metricArg; - - // Construct on a random device to test multi-device, if we have - // multiple devices - int device = faiss::gpu::randVal(0, faiss::gpu::getNumDevices() - 1); - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexFlatConfig config; - config.device = device; - config.useFloat16 = opt.useFloat16; - config.storeTransposed = opt.useTransposed; - - faiss::gpu::GpuIndexFlat gpuIndex(&res, dim, opt.metric, config); - gpuIndex.metric_arg = opt.metricArg; - - std::vector vecs = faiss::gpu::randVecs(numVecs, dim); - cpuIndex.add(numVecs, vecs.data()); - gpuIndex.add(numVecs, vecs.data()); - - std::stringstream str; - str << "metric " << opt.metric - << " marg " << opt.metricArg - << " numVecs " << numVecs - << " dim " << dim - << " useFloat16 " << opt.useFloat16 - << " transposed " << opt.useTransposed - << " numQuery " << numQuery - << " k " << k; - - // To some extent, we depend upon the relative error for the test - // for float16 - faiss::gpu::compareIndices(cpuIndex, gpuIndex, numQuery, dim, k, str.str(), - opt.useFloat16 ? kF16MaxRelErr : kF32MaxRelErr, - // FIXME: the fp16 bounds are - // useless when math (the accumulator) is - // in fp16. Figure out another way to test - opt.useFloat16 ? 0.99f : 0.1f, - opt.useFloat16 ? 0.65f : 0.015f); -} - -TEST(TestGpuIndexFlat, IP_Float32) { - for (int tries = 0; tries < 3; ++tries) { - TestFlatOptions opt; - opt.metric = faiss::MetricType::METRIC_INNER_PRODUCT; - opt.useFloat16 = false; - opt.useTransposed = false; - - testFlat(opt); - - opt.useTransposed = true; - testFlat(opt); - } -} - -TEST(TestGpuIndexFlat, L1_Float32) { - TestFlatOptions opt; - opt.metric = faiss::MetricType::METRIC_L1; - opt.useFloat16 = false; - opt.useTransposed = false; - - testFlat(opt); - - opt.useTransposed = true; - testFlat(opt); -} - -TEST(TestGpuIndexFlat, Lp_Float32) { - TestFlatOptions opt; - opt.metric = faiss::MetricType::METRIC_Lp; - opt.metricArg = 5; - opt.useFloat16 = false; - opt.useTransposed = false; - - testFlat(opt); - - // Don't bother testing the transposed version, the L1 test should be good - // enough for that -} - -TEST(TestGpuIndexFlat, L2_Float32) { - for (int tries = 0; tries < 3; ++tries) { - TestFlatOptions opt; - opt.metric = faiss::MetricType::METRIC_L2; - - opt.useFloat16 = false; - opt.useTransposed = false; - - testFlat(opt); - - opt.useTransposed = true; - testFlat(opt); - } -} - -// test specialized k == 1 codepath -TEST(TestGpuIndexFlat, L2_Float32_K1) { - for (int tries = 0; tries < 3; ++tries) { - TestFlatOptions opt; - opt.metric = faiss::MetricType::METRIC_L2; - opt.useFloat16 = false; - opt.useTransposed = false; - opt.kOverride = 1; - - testFlat(opt); - } -} - -TEST(TestGpuIndexFlat, IP_Float16) { - for (int tries = 0; tries < 3; ++tries) { - TestFlatOptions opt; - opt.metric = faiss::MetricType::METRIC_INNER_PRODUCT; - opt.useFloat16 = true; - opt.useTransposed = false; - - testFlat(opt); - - opt.useTransposed = true; - testFlat(opt); - } -} - -TEST(TestGpuIndexFlat, L2_Float16) { - for (int tries = 0; tries < 3; ++tries) { - TestFlatOptions opt; - opt.metric = faiss::MetricType::METRIC_L2; - opt.useFloat16 = true; - opt.useTransposed = false; - - testFlat(opt); - - opt.useTransposed = true; - testFlat(opt); - } -} - -// test specialized k == 1 codepath -TEST(TestGpuIndexFlat, L2_Float16_K1) { - for (int tries = 0; tries < 3; ++tries) { - TestFlatOptions opt; - opt.metric = faiss::MetricType::METRIC_L2; - opt.useFloat16 = true; - opt.useTransposed = false; - opt.kOverride = 1; - - testFlat(opt); - } -} - -// test tiling along a huge vector set -TEST(TestGpuIndexFlat, L2_Tiling) { - for (int tries = 0; tries < 2; ++tries) { - TestFlatOptions opt; - opt.metric = faiss::MetricType::METRIC_L2; - opt.useFloat16 = false; - opt.useTransposed = false; - opt.numVecsOverride = 1000000; - - // keep the rest of the problem reasonably small - opt.numQueriesOverride = 4; - opt.dimOverride = 64; - opt.kOverride = 64; - - testFlat(opt); - } -} - -TEST(TestGpuIndexFlat, QueryEmpty) { - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexFlatConfig config; - config.device = 0; - config.useFloat16 = false; - config.storeTransposed = false; - - int dim = 128; - faiss::gpu::GpuIndexFlatL2 gpuIndex(&res, dim, config); - - // Querying an empty index should not blow up, and just return - // (FLT_MAX, -1) - int numQuery = 10; - int k = 50; - std::vector queries(numQuery * dim, 1.0f); - - std::vector dist(numQuery * k, 0); - std::vector ind(numQuery * k); - - gpuIndex.search(numQuery, queries.data(), k, dist.data(), ind.data()); - - for (auto d : dist) { - EXPECT_EQ(d, std::numeric_limits::max()); - } - - for (auto i : ind) { - EXPECT_EQ(i, -1); - } -} - -TEST(TestGpuIndexFlat, CopyFrom) { - int numVecs = faiss::gpu::randVal(100, 200); - int dim = faiss::gpu::randVal(1, 1000); - - faiss::IndexFlatL2 cpuIndex(dim); - - std::vector vecs = faiss::gpu::randVecs(numVecs, dim); - cpuIndex.add(numVecs, vecs.data()); - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - // Fill with garbage values - int device = faiss::gpu::randVal(0, faiss::gpu::getNumDevices() - 1); - - faiss::gpu::GpuIndexFlatConfig config; - config.device = 0; - config.useFloat16 = false; - config.storeTransposed = false; - - faiss::gpu::GpuIndexFlatL2 gpuIndex(&res, 2000, config); - gpuIndex.copyFrom(&cpuIndex); - - EXPECT_EQ(cpuIndex.ntotal, gpuIndex.ntotal); - EXPECT_EQ(gpuIndex.ntotal, numVecs); - - EXPECT_EQ(cpuIndex.d, gpuIndex.d); - EXPECT_EQ(cpuIndex.d, dim); - - int idx = faiss::gpu::randVal(0, numVecs - 1); - - std::vector gpuVals(dim); - gpuIndex.reconstruct(idx, gpuVals.data()); - - std::vector cpuVals(dim); - cpuIndex.reconstruct(idx, cpuVals.data()); - - EXPECT_EQ(gpuVals, cpuVals); -} - -TEST(TestGpuIndexFlat, CopyTo) { - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - int numVecs = faiss::gpu::randVal(100, 200); - int dim = faiss::gpu::randVal(1, 1000); - - int device = faiss::gpu::randVal(0, faiss::gpu::getNumDevices() - 1); - - faiss::gpu::GpuIndexFlatConfig config; - config.device = device; - config.useFloat16 = false; - config.storeTransposed = false; - - faiss::gpu::GpuIndexFlatL2 gpuIndex(&res, dim, config); - - std::vector vecs = faiss::gpu::randVecs(numVecs, dim); - gpuIndex.add(numVecs, vecs.data()); - - // Fill with garbage values - faiss::IndexFlatL2 cpuIndex(2000); - gpuIndex.copyTo(&cpuIndex); - - EXPECT_EQ(cpuIndex.ntotal, gpuIndex.ntotal); - EXPECT_EQ(gpuIndex.ntotal, numVecs); - - EXPECT_EQ(cpuIndex.d, gpuIndex.d); - EXPECT_EQ(cpuIndex.d, dim); - - int idx = faiss::gpu::randVal(0, numVecs - 1); - - std::vector gpuVals(dim); - gpuIndex.reconstruct(idx, gpuVals.data()); - - std::vector cpuVals(dim); - cpuIndex.reconstruct(idx, cpuVals.data()); - - EXPECT_EQ(gpuVals, cpuVals); -} - -TEST(TestGpuIndexFlat, UnifiedMemory) { - // Construct on a random device to test multi-device, if we have - // multiple devices - int device = faiss::gpu::randVal(0, faiss::gpu::getNumDevices() - 1); - - if (!faiss::gpu::getFullUnifiedMemSupport(device)) { - return; - } - - int dim = 256; - - // FIXME: GpuIndexFlat doesn't support > 2^31 (vecs * dims) due to - // kernel indexing, so we can't test unified memory for memory - // oversubscription. - size_t numVecs = 50000; - int numQuery = 10; - int k = 10; - - faiss::IndexFlatL2 cpuIndexL2(dim); - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexFlatConfig config; - config.device = device; - config.memorySpace = faiss::gpu::MemorySpace::Unified; - - faiss::gpu::GpuIndexFlatL2 gpuIndexL2(&res, dim, config); - - std::vector vecs = faiss::gpu::randVecs(numVecs, dim); - cpuIndexL2.add(numVecs, vecs.data()); - gpuIndexL2.add(numVecs, vecs.data()); - - // To some extent, we depend upon the relative error for the test - // for float16 - faiss::gpu::compareIndices(cpuIndexL2, gpuIndexL2, - numQuery, dim, k, "Unified Memory", - kF32MaxRelErr, - 0.1f, - 0.015f); -} - -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - - // just run with a fixed test seed - faiss::gpu::setTestSeed(100); - - return RUN_ALL_TESTS(); -} diff --git a/core/src/index/thirdparty/faiss/gpu/test/TestGpuIndexIVFFlat.cpp b/core/src/index/thirdparty/faiss/gpu/test/TestGpuIndexIVFFlat.cpp deleted file mode 100644 index 6304252e6b..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/test/TestGpuIndexIVFFlat.cpp +++ /dev/null @@ -1,550 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// FIXME: figure out a better way to test fp16 -constexpr float kF16MaxRelErr = 0.3f; -constexpr float kF32MaxRelErr = 0.03f; - - -struct Options { - Options() { - numAdd = 2 * faiss::gpu::randVal(2000, 5000); - dim = faiss::gpu::randVal(64, 200); - - numCentroids = std::sqrt((float) numAdd / 2); - numTrain = numCentroids * 40; - nprobe = faiss::gpu::randVal(std::min(10, numCentroids), numCentroids); - numQuery = faiss::gpu::randVal(32, 100); - - // Due to the approximate nature of the query and of floating point - // differences between GPU and CPU, to stay within our error bounds, only - // use a small k - k = std::min(faiss::gpu::randVal(10, 30), numAdd / 40); - indicesOpt = faiss::gpu::randSelect({ - faiss::gpu::INDICES_CPU, - faiss::gpu::INDICES_32_BIT, - faiss::gpu::INDICES_64_BIT}); - - device = faiss::gpu::randVal(0, faiss::gpu::getNumDevices() - 1); - } - - std::string toString() const { - std::stringstream str; - str << "IVFFlat device " << device - << " numVecs " << numAdd - << " dim " << dim - << " numCentroids " << numCentroids - << " nprobe " << nprobe - << " numQuery " << numQuery - << " k " << k - << " indicesOpt " << indicesOpt; - - return str.str(); - } - - int numAdd; - int dim; - int numCentroids; - int numTrain; - int nprobe; - int numQuery; - int k; - int device; - faiss::gpu::IndicesOptions indicesOpt; -}; - -void queryTest(faiss::MetricType metricType, - bool useFloat16CoarseQuantizer, - int dimOverride = -1) { - for (int tries = 0; tries < 2; ++tries) { - Options opt; - opt.dim = dimOverride != -1 ? dimOverride : opt.dim; - - std::vector trainVecs = faiss::gpu::randVecs(opt.numTrain, opt.dim); - std::vector addVecs = faiss::gpu::randVecs(opt.numAdd, opt.dim); - - faiss::IndexFlatL2 quantizerL2(opt.dim); - faiss::IndexFlatIP quantizerIP(opt.dim); - faiss::Index* quantizer = - metricType == faiss::METRIC_L2 ? - (faiss::Index*) &quantizerL2 : (faiss::Index*) &quantizerIP; - - faiss::IndexIVFFlat cpuIndex(quantizer, - opt.dim, opt.numCentroids, metricType); - cpuIndex.train(opt.numTrain, trainVecs.data()); - cpuIndex.add(opt.numAdd, addVecs.data()); - cpuIndex.nprobe = opt.nprobe; - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexIVFFlatConfig config; - config.device = opt.device; - config.indicesOptions = opt.indicesOpt; - config.flatConfig.useFloat16 = useFloat16CoarseQuantizer; - - faiss::gpu::GpuIndexIVFFlat gpuIndex(&res, - cpuIndex.d, - cpuIndex.nlist, - cpuIndex.metric_type, - config); - gpuIndex.copyFrom(&cpuIndex); - gpuIndex.setNumProbes(opt.nprobe); - - bool compFloat16 = useFloat16CoarseQuantizer; - faiss::gpu::compareIndices(cpuIndex, gpuIndex, - opt.numQuery, opt.dim, opt.k, opt.toString(), - compFloat16 ? kF16MaxRelErr : kF32MaxRelErr, - // FIXME: the fp16 bounds are - // useless when math (the accumulator) is - // in fp16. Figure out another way to test - compFloat16 ? 0.70f : 0.1f, - compFloat16 ? 0.65f : 0.015f); - } -} - -void addTest(faiss::MetricType metricType, - bool useFloat16CoarseQuantizer) { - for (int tries = 0; tries < 2; ++tries) { - Options opt; - - std::vector trainVecs = faiss::gpu::randVecs(opt.numTrain, opt.dim); - std::vector addVecs = faiss::gpu::randVecs(opt.numAdd, opt.dim); - - faiss::IndexFlatL2 quantizerL2(opt.dim); - faiss::IndexFlatIP quantizerIP(opt.dim); - faiss::Index* quantizer = - metricType == faiss::METRIC_L2 ? - (faiss::Index*) &quantizerL2 : (faiss::Index*) &quantizerIP; - - faiss::IndexIVFFlat cpuIndex(quantizer, - opt.dim, - opt.numCentroids, - metricType); - cpuIndex.train(opt.numTrain, trainVecs.data()); - cpuIndex.nprobe = opt.nprobe; - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexIVFFlatConfig config; - config.device = opt.device; - config.indicesOptions = opt.indicesOpt; - config.flatConfig.useFloat16 = useFloat16CoarseQuantizer; - - faiss::gpu::GpuIndexIVFFlat gpuIndex(&res, - cpuIndex.d, - cpuIndex.nlist, - cpuIndex.metric_type, - config); - gpuIndex.copyFrom(&cpuIndex); - gpuIndex.setNumProbes(opt.nprobe); - - cpuIndex.add(opt.numAdd, addVecs.data()); - gpuIndex.add(opt.numAdd, addVecs.data()); - - bool compFloat16 = useFloat16CoarseQuantizer; - faiss::gpu::compareIndices(cpuIndex, gpuIndex, - opt.numQuery, opt.dim, opt.k, opt.toString(), - compFloat16 ? kF16MaxRelErr : kF32MaxRelErr, - compFloat16 ? 0.70f : 0.1f, - compFloat16 ? 0.30f : 0.015f); - } -} - -void copyToTest(bool useFloat16CoarseQuantizer) { - Options opt; - std::vector trainVecs = faiss::gpu::randVecs(opt.numTrain, opt.dim); - std::vector addVecs = faiss::gpu::randVecs(opt.numAdd, opt.dim); - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexIVFFlatConfig config; - config.device = opt.device; - config.indicesOptions = opt.indicesOpt; - config.flatConfig.useFloat16 = useFloat16CoarseQuantizer; - - faiss::gpu::GpuIndexIVFFlat gpuIndex(&res, - opt.dim, - opt.numCentroids, - faiss::METRIC_L2, - config); - gpuIndex.train(opt.numTrain, trainVecs.data()); - gpuIndex.add(opt.numAdd, addVecs.data()); - gpuIndex.setNumProbes(opt.nprobe); - - // use garbage values to see if we overwrite then - faiss::IndexFlatL2 cpuQuantizer(1); - faiss::IndexIVFFlat cpuIndex(&cpuQuantizer, 1, 1, faiss::METRIC_L2); - cpuIndex.nprobe = 1; - - gpuIndex.copyTo(&cpuIndex); - - EXPECT_EQ(cpuIndex.ntotal, gpuIndex.ntotal); - EXPECT_EQ(gpuIndex.ntotal, opt.numAdd); - - EXPECT_EQ(cpuIndex.d, gpuIndex.d); - EXPECT_EQ(cpuIndex.quantizer->d, gpuIndex.quantizer->d); - EXPECT_EQ(cpuIndex.d, opt.dim); - EXPECT_EQ(cpuIndex.nlist, gpuIndex.getNumLists()); - EXPECT_EQ(cpuIndex.nprobe, gpuIndex.getNumProbes()); - - // Query both objects; results should be equivalent - bool compFloat16 = useFloat16CoarseQuantizer; - faiss::gpu::compareIndices(cpuIndex, gpuIndex, - opt.numQuery, opt.dim, opt.k, opt.toString(), - compFloat16 ? kF16MaxRelErr : kF32MaxRelErr, - compFloat16 ? 0.70f : 0.1f, - compFloat16 ? 0.30f : 0.015f); -} - -void copyFromTest(bool useFloat16CoarseQuantizer) { - Options opt; - std::vector trainVecs = faiss::gpu::randVecs(opt.numTrain, opt.dim); - std::vector addVecs = faiss::gpu::randVecs(opt.numAdd, opt.dim); - - faiss::IndexFlatL2 cpuQuantizer(opt.dim); - faiss::IndexIVFFlat cpuIndex(&cpuQuantizer, - opt.dim, - opt.numCentroids, - faiss::METRIC_L2); - cpuIndex.nprobe = opt.nprobe; - cpuIndex.train(opt.numTrain, trainVecs.data()); - cpuIndex.add(opt.numAdd, addVecs.data()); - - // use garbage values to see if we overwrite then - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexIVFFlatConfig config; - config.device = opt.device; - config.indicesOptions = opt.indicesOpt; - config.flatConfig.useFloat16 = useFloat16CoarseQuantizer; - - faiss::gpu::GpuIndexIVFFlat gpuIndex(&res, - 1, - 1, - faiss::METRIC_L2, - config); - gpuIndex.setNumProbes(1); - - gpuIndex.copyFrom(&cpuIndex); - - EXPECT_EQ(cpuIndex.ntotal, gpuIndex.ntotal); - EXPECT_EQ(gpuIndex.ntotal, opt.numAdd); - - EXPECT_EQ(cpuIndex.d, gpuIndex.d); - EXPECT_EQ(cpuIndex.d, opt.dim); - EXPECT_EQ(cpuIndex.nlist, gpuIndex.getNumLists()); - EXPECT_EQ(cpuIndex.nprobe, gpuIndex.getNumProbes()); - - // Query both objects; results should be equivalent - bool compFloat16 = useFloat16CoarseQuantizer; - faiss::gpu::compareIndices(cpuIndex, gpuIndex, - opt.numQuery, opt.dim, opt.k, opt.toString(), - compFloat16 ? kF16MaxRelErr : kF32MaxRelErr, - compFloat16 ? 0.70f : 0.1f, - compFloat16 ? 0.30f : 0.015f); -} - -TEST(TestGpuIndexIVFFlat, Float32_32_Add_L2) { - addTest(faiss::METRIC_L2, false); -} - -TEST(TestGpuIndexIVFFlat, Float32_32_Add_IP) { - addTest(faiss::METRIC_INNER_PRODUCT, false); -} - -TEST(TestGpuIndexIVFFlat, Float16_32_Add_L2) { - addTest(faiss::METRIC_L2, true); -} - -TEST(TestGpuIndexIVFFlat, Float16_32_Add_IP) { - addTest(faiss::METRIC_INNER_PRODUCT, true); -} - -// -// General query tests -// - -TEST(TestGpuIndexIVFFlat, Float32_Query_L2) { - queryTest(faiss::METRIC_L2, false); -} - -TEST(TestGpuIndexIVFFlat, Float32_Query_IP) { - queryTest(faiss::METRIC_INNER_PRODUCT, false); -} - -// float16 coarse quantizer - -TEST(TestGpuIndexIVFFlat, Float16_32_Query_L2) { - queryTest(faiss::METRIC_L2, true); -} - -TEST(TestGpuIndexIVFFlat, Float16_32_Query_IP) { - queryTest(faiss::METRIC_INNER_PRODUCT, true); -} - -// -// There are IVF list scanning specializations for 64-d and 128-d that we -// make sure we explicitly test here -// - -TEST(TestGpuIndexIVFFlat, Float32_Query_L2_64) { - queryTest(faiss::METRIC_L2, false, 64); -} - -TEST(TestGpuIndexIVFFlat, Float32_Query_IP_64) { - queryTest(faiss::METRIC_INNER_PRODUCT, false, 64); -} - -TEST(TestGpuIndexIVFFlat, Float32_Query_L2_128) { - queryTest(faiss::METRIC_L2, false, 128); -} - -TEST(TestGpuIndexIVFFlat, Float32_Query_IP_128) { - queryTest(faiss::METRIC_INNER_PRODUCT, false, 128); -} - -// -// Copy tests -// - -TEST(TestGpuIndexIVFFlat, Float32_32_CopyTo) { - copyToTest(false); -} - -TEST(TestGpuIndexIVFFlat, Float32_32_CopyFrom) { - copyFromTest(false); -} - -TEST(TestGpuIndexIVFFlat, Float32_negative) { - Options opt; - - auto trainVecs = faiss::gpu::randVecs(opt.numTrain, opt.dim); - auto addVecs = faiss::gpu::randVecs(opt.numAdd, opt.dim); - - // Put all vecs on negative side - for (auto& f : trainVecs) { - f = std::abs(f) * -1.0f; - } - - for (auto& f : addVecs) { - f *= std::abs(f) * -1.0f; - } - - faiss::IndexFlatIP quantizerIP(opt.dim); - faiss::Index* quantizer = (faiss::Index*) &quantizerIP; - - faiss::IndexIVFFlat cpuIndex(quantizer, - opt.dim, opt.numCentroids, - faiss::METRIC_INNER_PRODUCT); - cpuIndex.train(opt.numTrain, trainVecs.data()); - cpuIndex.add(opt.numAdd, addVecs.data()); - cpuIndex.nprobe = opt.nprobe; - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexIVFFlatConfig config; - config.device = opt.device; - config.indicesOptions = opt.indicesOpt; - - faiss::gpu::GpuIndexIVFFlat gpuIndex(&res, - cpuIndex.d, - cpuIndex.nlist, - cpuIndex.metric_type, - config); - gpuIndex.copyFrom(&cpuIndex); - gpuIndex.setNumProbes(opt.nprobe); - - // Construct a positive test set - auto queryVecs = faiss::gpu::randVecs(opt.numQuery, opt.dim); - - // Put all vecs on positive size - for (auto& f : queryVecs) { - f = std::abs(f); - } - - bool compFloat16 = false; - faiss::gpu::compareIndices(queryVecs, - cpuIndex, gpuIndex, - opt.numQuery, opt.dim, opt.k, opt.toString(), - compFloat16 ? kF16MaxRelErr : kF32MaxRelErr, - // FIXME: the fp16 bounds are - // useless when math (the accumulator) is - // in fp16. Figure out another way to test - compFloat16 ? 0.99f : 0.1f, - compFloat16 ? 0.65f : 0.015f); -} - -// -// NaN tests -// - -TEST(TestGpuIndexIVFFlat, QueryNaN) { - Options opt; - - std::vector trainVecs = faiss::gpu::randVecs(opt.numTrain, opt.dim); - std::vector addVecs = faiss::gpu::randVecs(opt.numAdd, opt.dim); - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexIVFFlatConfig config; - config.device = opt.device; - config.indicesOptions = opt.indicesOpt; - config.flatConfig.useFloat16 = faiss::gpu::randBool(); - - faiss::gpu::GpuIndexIVFFlat gpuIndex(&res, - opt.dim, - opt.numCentroids, - faiss::METRIC_L2, - config); - gpuIndex.setNumProbes(opt.nprobe); - - gpuIndex.train(opt.numTrain, trainVecs.data()); - gpuIndex.add(opt.numAdd, addVecs.data()); - - int numQuery = 10; - std::vector nans(numQuery * opt.dim, - std::numeric_limits::quiet_NaN()); - - std::vector distances(numQuery * opt.k, 0); - std::vector indices(numQuery * opt.k, 0); - - gpuIndex.search(numQuery, - nans.data(), - opt.k, - distances.data(), - indices.data()); - - for (int q = 0; q < numQuery; ++q) { - for (int k = 0; k < opt.k; ++k) { - EXPECT_EQ(indices[q * opt.k + k], -1); - EXPECT_EQ(distances[q * opt.k + k], std::numeric_limits::max()); - } - } -} - -TEST(TestGpuIndexIVFFlat, AddNaN) { - Options opt; - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexIVFFlatConfig config; - config.device = opt.device; - config.indicesOptions = opt.indicesOpt; - config.flatConfig.useFloat16 = faiss::gpu::randBool(); - - faiss::gpu::GpuIndexIVFFlat gpuIndex(&res, - opt.dim, - opt.numCentroids, - faiss::METRIC_L2, - config); - gpuIndex.setNumProbes(opt.nprobe); - - int numNans = 10; - std::vector nans(numNans * opt.dim, - std::numeric_limits::quiet_NaN()); - - // Make one vector valid, which should actually add - for (int i = 0; i < opt.dim; ++i) { - nans[i] = 0.0f; - } - - std::vector trainVecs = faiss::gpu::randVecs(opt.numTrain, opt.dim); - gpuIndex.train(opt.numTrain, trainVecs.data()); - - // should not crash - EXPECT_EQ(gpuIndex.ntotal, 0); - gpuIndex.add(numNans, nans.data()); - - std::vector queryVecs = faiss::gpu::randVecs(opt.numQuery, opt.dim); - std::vector distance(opt.numQuery * opt.k, 0); - std::vector indices(opt.numQuery * opt.k, 0); - - // should not crash - gpuIndex.search(opt.numQuery, queryVecs.data(), opt.k, - distance.data(), indices.data()); -} - -TEST(TestGpuIndexIVFFlat, UnifiedMemory) { - // Construct on a random device to test multi-device, if we have - // multiple devices - int device = faiss::gpu::randVal(0, faiss::gpu::getNumDevices() - 1); - - if (!faiss::gpu::getFullUnifiedMemSupport(device)) { - return; - } - - int dim = 128; - - int numCentroids = 256; - // Unfortunately it would take forever to add 24 GB in IVFPQ data, - // so just perform a small test with data allocated in the unified - // memory address space - size_t numAdd = 10000; - size_t numTrain = numCentroids * 40; - int numQuery = 10; - int k = 10; - int nprobe = 8; - - std::vector trainVecs = faiss::gpu::randVecs(numTrain, dim); - std::vector addVecs = faiss::gpu::randVecs(numAdd, dim); - - faiss::IndexFlatL2 quantizer(dim); - faiss::IndexIVFFlat cpuIndex(&quantizer, dim, numCentroids, faiss::METRIC_L2); - - cpuIndex.train(numTrain, trainVecs.data()); - cpuIndex.add(numAdd, addVecs.data()); - cpuIndex.nprobe = nprobe; - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexIVFFlatConfig config; - config.device = device; - config.memorySpace = faiss::gpu::MemorySpace::Unified; - - faiss::gpu::GpuIndexIVFFlat gpuIndex(&res, - dim, - numCentroids, - faiss::METRIC_L2, - config); - gpuIndex.copyFrom(&cpuIndex); - gpuIndex.setNumProbes(nprobe); - - faiss::gpu::compareIndices(cpuIndex, gpuIndex, - numQuery, dim, k, "Unified Memory", - kF32MaxRelErr, - 0.1f, - 0.015f); -} - -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - - // just run with a fixed test seed - faiss::gpu::setTestSeed(100); - - return RUN_ALL_TESTS(); -} diff --git a/core/src/index/thirdparty/faiss/gpu/test/TestGpuIndexIVFPQ.cpp b/core/src/index/thirdparty/faiss/gpu/test/TestGpuIndexIVFPQ.cpp deleted file mode 100644 index 1bee6b4bbf..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/test/TestGpuIndexIVFPQ.cpp +++ /dev/null @@ -1,558 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -void pickEncoding(int& codes, int& dim) { - std::vector codeSizes{ - 3, 4, 8, 12, 16, 20, 24, - 28, 32, 40, 48, 56, 64, 96 - }; - - // Above 32 doesn't work with no precomputed codes - std::vector dimSizes{4, 8, 10, 12, 16, 20, 24, 28, 32}; - - while (true) { - codes = codeSizes[faiss::gpu::randVal(0, codeSizes.size() - 1)]; - dim = codes * dimSizes[faiss::gpu::randVal(0, dimSizes.size() - 1)]; - - // for such a small test, super-low or high dim is more likely to - // generate comparison errors - if (dim < 256 && dim >= 64) { - return; - } - } -} - -struct Options { - Options() { - numAdd = faiss::gpu::randVal(2000, 5000); - numCentroids = std::sqrt((float) numAdd); - numTrain = numCentroids * 40; - - pickEncoding(codes, dim); - - // TODO: Change back to `faiss::gpu::randVal(3, 7)` when we officially - // support non-multiple of 8 subcodes for IVFPQ. - bitsPerCode = 8; - nprobe = std::min(faiss::gpu::randVal(40, 1000), numCentroids); - numQuery = faiss::gpu::randVal(1, 8); - - // Due to the approximate nature of the query and of floating point - // differences between GPU and CPU, to stay within our error bounds, only - // use a small k - k = std::min(faiss::gpu::randVal(5, 20), numAdd / 40); - usePrecomputed = faiss::gpu::randBool(); - indicesOpt = faiss::gpu::randSelect({ - faiss::gpu::INDICES_CPU, - faiss::gpu::INDICES_32_BIT, - faiss::gpu::INDICES_64_BIT}); - if (codes > 48) { - // large codes can only fit using float16 - useFloat16 = true; - } else { - useFloat16 = faiss::gpu::randBool(); - } - - device = faiss::gpu::randVal(0, faiss::gpu::getNumDevices() - 1); - } - - std::string toString() const { - std::stringstream str; - str << "IVFPQ device " << device - << " numVecs " << numAdd - << " dim " << dim - << " numCentroids " << numCentroids - << " codes " << codes - << " bitsPerCode " << bitsPerCode - << " nprobe " << nprobe - << " numQuery " << numQuery - << " k " << k - << " usePrecomputed " << usePrecomputed - << " indicesOpt " << indicesOpt - << " useFloat16 " << useFloat16; - - return str.str(); - } - - float getCompareEpsilon() const { - return 0.03f; - } - - float getPctMaxDiff1() const { - return useFloat16 ? 0.30f : 0.10f; - } - - float getPctMaxDiffN() const { - return useFloat16 ? 0.05f : 0.02f; - } - - int numAdd; - int numCentroids; - int numTrain; - int codes; - int dim; - int bitsPerCode; - int nprobe; - int numQuery; - int k; - bool usePrecomputed; - faiss::gpu::IndicesOptions indicesOpt; - bool useFloat16; - int device; -}; - -TEST(TestGpuIndexIVFPQ, Query_L2) { - for (int tries = 0; tries < 2; ++tries) { - Options opt; - - std::vector trainVecs = faiss::gpu::randVecs(opt.numTrain, opt.dim); - std::vector addVecs = faiss::gpu::randVecs(opt.numAdd, opt.dim); - - faiss::IndexFlatL2 coarseQuantizer(opt.dim); - faiss::IndexIVFPQ cpuIndex(&coarseQuantizer, opt.dim, opt.numCentroids, - opt.codes, opt.bitsPerCode); - cpuIndex.nprobe = opt.nprobe; - cpuIndex.train(opt.numTrain, trainVecs.data()); - cpuIndex.add(opt.numAdd, addVecs.data()); - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexIVFPQConfig config; - config.device = opt.device; - config.usePrecomputedTables = opt.usePrecomputed; - config.indicesOptions = opt.indicesOpt; - config.useFloat16LookupTables = opt.useFloat16; - - faiss::gpu::GpuIndexIVFPQ gpuIndex(&res, &cpuIndex, config); - gpuIndex.setNumProbes(opt.nprobe); - - faiss::gpu::compareIndices(cpuIndex, gpuIndex, - opt.numQuery, opt.dim, opt.k, opt.toString(), - opt.getCompareEpsilon(), - opt.getPctMaxDiff1(), - opt.getPctMaxDiffN()); - } -} - -TEST(TestGpuIndexIVFPQ, Query_IP) { - for (int tries = 0; tries < 2; ++tries) { - Options opt; - - std::vector trainVecs = faiss::gpu::randVecs(opt.numTrain, opt.dim); - std::vector addVecs = faiss::gpu::randVecs(opt.numAdd, opt.dim); - - faiss::IndexFlatIP coarseQuantizer(opt.dim); - faiss::IndexIVFPQ cpuIndex(&coarseQuantizer, opt.dim, opt.numCentroids, - opt.codes, opt.bitsPerCode); - cpuIndex.metric_type = faiss::MetricType::METRIC_INNER_PRODUCT; - - cpuIndex.nprobe = opt.nprobe; - cpuIndex.train(opt.numTrain, trainVecs.data()); - cpuIndex.add(opt.numAdd, addVecs.data()); - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexIVFPQConfig config; - config.device = opt.device; - config.usePrecomputedTables = false; // not supported/required for IP - config.indicesOptions = opt.indicesOpt; - config.useFloat16LookupTables = opt.useFloat16; - - faiss::gpu::GpuIndexIVFPQ gpuIndex(&res, &cpuIndex, config); - gpuIndex.setNumProbes(opt.nprobe); - - faiss::gpu::compareIndices(cpuIndex, gpuIndex, - opt.numQuery, opt.dim, opt.k, opt.toString(), - opt.getCompareEpsilon(), - opt.getPctMaxDiff1(), - opt.getPctMaxDiffN()); - } -} - -TEST(TestGpuIndexIVFPQ, Float16Coarse) { - Options opt; - - std::vector trainVecs = faiss::gpu::randVecs(opt.numTrain, opt.dim); - std::vector addVecs = faiss::gpu::randVecs(opt.numAdd, opt.dim); - - faiss::IndexFlatL2 coarseQuantizer(opt.dim); - faiss::IndexIVFPQ cpuIndex(&coarseQuantizer, opt.dim, opt.numCentroids, - opt.codes, opt.bitsPerCode); - cpuIndex.nprobe = opt.nprobe; - cpuIndex.train(opt.numTrain, trainVecs.data()); - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexIVFPQConfig config; - config.device = opt.device; - config.flatConfig.useFloat16 = true; - config.usePrecomputedTables = opt.usePrecomputed; - config.indicesOptions = opt.indicesOpt; - config.useFloat16LookupTables = opt.useFloat16; - - faiss::gpu::GpuIndexIVFPQ gpuIndex(&res, &cpuIndex, config); - gpuIndex.setNumProbes(opt.nprobe); - - gpuIndex.add(opt.numAdd, addVecs.data()); - cpuIndex.add(opt.numAdd, addVecs.data()); - - faiss::gpu::compareIndices(cpuIndex, gpuIndex, - opt.numQuery, opt.dim, opt.k, opt.toString(), - opt.getCompareEpsilon(), - opt.getPctMaxDiff1(), - opt.getPctMaxDiffN()); -} - -TEST(TestGpuIndexIVFPQ, Add_L2) { - for (int tries = 0; tries < 2; ++tries) { - Options opt; - - std::vector trainVecs = faiss::gpu::randVecs(opt.numTrain, opt.dim); - std::vector addVecs = faiss::gpu::randVecs(opt.numAdd, opt.dim); - - faiss::IndexFlatL2 coarseQuantizer(opt.dim); - faiss::IndexIVFPQ cpuIndex(&coarseQuantizer, opt.dim, opt.numCentroids, - opt.codes, opt.bitsPerCode); - cpuIndex.nprobe = opt.nprobe; - cpuIndex.train(opt.numTrain, trainVecs.data()); - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexIVFPQConfig config; - config.device = opt.device; - config.usePrecomputedTables = opt.usePrecomputed; - config.indicesOptions = opt.indicesOpt; - config.useFloat16LookupTables = opt.useFloat16; - - faiss::gpu::GpuIndexIVFPQ gpuIndex(&res, &cpuIndex, config); - gpuIndex.setNumProbes(opt.nprobe); - - gpuIndex.add(opt.numAdd, addVecs.data()); - cpuIndex.add(opt.numAdd, addVecs.data()); - - faiss::gpu::compareIndices(cpuIndex, gpuIndex, - opt.numQuery, opt.dim, opt.k, opt.toString(), - opt.getCompareEpsilon(), - opt.getPctMaxDiff1(), - opt.getPctMaxDiffN()); - } -} - -TEST(TestGpuIndexIVFPQ, Add_IP) { - for (int tries = 0; tries < 2; ++tries) { - Options opt; - - std::vector trainVecs = faiss::gpu::randVecs(opt.numTrain, opt.dim); - std::vector addVecs = faiss::gpu::randVecs(opt.numAdd, opt.dim); - - faiss::IndexFlatIP coarseQuantizer(opt.dim); - faiss::IndexIVFPQ cpuIndex(&coarseQuantizer, opt.dim, opt.numCentroids, - opt.codes, opt.bitsPerCode); - cpuIndex.metric_type = faiss::MetricType::METRIC_INNER_PRODUCT; - cpuIndex.nprobe = opt.nprobe; - cpuIndex.train(opt.numTrain, trainVecs.data()); - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexIVFPQConfig config; - config.device = opt.device; - config.usePrecomputedTables = opt.usePrecomputed; - config.indicesOptions = opt.indicesOpt; - config.useFloat16LookupTables = opt.useFloat16; - - faiss::gpu::GpuIndexIVFPQ gpuIndex(&res, &cpuIndex, config); - gpuIndex.setNumProbes(opt.nprobe); - - gpuIndex.add(opt.numAdd, addVecs.data()); - cpuIndex.add(opt.numAdd, addVecs.data()); - - faiss::gpu::compareIndices(cpuIndex, gpuIndex, - opt.numQuery, opt.dim, opt.k, opt.toString(), - opt.getCompareEpsilon(), - opt.getPctMaxDiff1(), - opt.getPctMaxDiffN()); - } -} - -TEST(TestGpuIndexIVFPQ, CopyTo) { - Options opt; - std::vector trainVecs = faiss::gpu::randVecs(opt.numTrain, opt.dim); - std::vector addVecs = faiss::gpu::randVecs(opt.numAdd, opt.dim); - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexIVFPQConfig config; - config.device = opt.device; - config.usePrecomputedTables = opt.usePrecomputed; - config.indicesOptions = opt.indicesOpt; - config.useFloat16LookupTables = opt.useFloat16; - - faiss::gpu::GpuIndexIVFPQ gpuIndex(&res, - opt.dim, - opt.numCentroids, - opt.codes, - opt.bitsPerCode, - faiss::METRIC_L2, - config); - gpuIndex.setNumProbes(opt.nprobe); - gpuIndex.train(opt.numTrain, trainVecs.data()); - gpuIndex.add(opt.numAdd, addVecs.data()); - - // Use garbage values to see if we overwrite them - faiss::IndexFlatL2 cpuQuantizer(1); - faiss::IndexIVFPQ cpuIndex(&cpuQuantizer, 1, 1, 1, 1); - - gpuIndex.copyTo(&cpuIndex); - - EXPECT_EQ(cpuIndex.ntotal, gpuIndex.ntotal); - EXPECT_EQ(gpuIndex.ntotal, opt.numAdd); - - EXPECT_EQ(cpuIndex.d, gpuIndex.d); - EXPECT_EQ(cpuIndex.d, opt.dim); - EXPECT_EQ(cpuIndex.nlist, gpuIndex.getNumLists()); - EXPECT_EQ(cpuIndex.nprobe, gpuIndex.getNumProbes()); - EXPECT_EQ(cpuIndex.pq.M, gpuIndex.getNumSubQuantizers()); - EXPECT_EQ(gpuIndex.getNumSubQuantizers(), opt.codes); - EXPECT_EQ(cpuIndex.pq.nbits, gpuIndex.getBitsPerCode()); - EXPECT_EQ(gpuIndex.getBitsPerCode(), opt.bitsPerCode); - - // Query both objects; results should be equivalent - faiss::gpu::compareIndices(cpuIndex, gpuIndex, - opt.numQuery, opt.dim, opt.k, opt.toString(), - opt.getCompareEpsilon(), - opt.getPctMaxDiff1(), - opt.getPctMaxDiffN()); -} - -TEST(TestGpuIndexIVFPQ, CopyFrom) { - Options opt; - std::vector trainVecs = faiss::gpu::randVecs(opt.numTrain, opt.dim); - std::vector addVecs = faiss::gpu::randVecs(opt.numAdd, opt.dim); - - faiss::IndexFlatL2 coarseQuantizer(opt.dim); - faiss::IndexIVFPQ cpuIndex(&coarseQuantizer, opt.dim, opt.numCentroids, - opt.codes, opt.bitsPerCode); - cpuIndex.nprobe = opt.nprobe; - cpuIndex.train(opt.numTrain, trainVecs.data()); - cpuIndex.add(opt.numAdd, addVecs.data()); - - // Use garbage values to see if we overwrite them - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexIVFPQConfig config; - config.device = opt.device; - config.usePrecomputedTables = opt.usePrecomputed; - config.indicesOptions = opt.indicesOpt; - config.useFloat16LookupTables = opt.useFloat16; - - faiss::gpu::GpuIndexIVFPQ - gpuIndex(&res, 1, 1, 1, 1, faiss::METRIC_L2, config); - gpuIndex.setNumProbes(1); - - gpuIndex.copyFrom(&cpuIndex); - - // Make sure we are equivalent - EXPECT_EQ(cpuIndex.ntotal, gpuIndex.ntotal); - EXPECT_EQ(gpuIndex.ntotal, opt.numAdd); - - EXPECT_EQ(cpuIndex.d, gpuIndex.d); - EXPECT_EQ(cpuIndex.d, opt.dim); - EXPECT_EQ(cpuIndex.nlist, gpuIndex.getNumLists()); - EXPECT_EQ(cpuIndex.nprobe, gpuIndex.getNumProbes()); - EXPECT_EQ(cpuIndex.pq.M, gpuIndex.getNumSubQuantizers()); - EXPECT_EQ(gpuIndex.getNumSubQuantizers(), opt.codes); - EXPECT_EQ(cpuIndex.pq.nbits, gpuIndex.getBitsPerCode()); - EXPECT_EQ(gpuIndex.getBitsPerCode(), opt.bitsPerCode); - - // Query both objects; results should be equivalent - faiss::gpu::compareIndices(cpuIndex, gpuIndex, - opt.numQuery, opt.dim, opt.k, opt.toString(), - opt.getCompareEpsilon(), - opt.getPctMaxDiff1(), - opt.getPctMaxDiffN()); -} - -TEST(TestGpuIndexIVFPQ, QueryNaN) { - Options opt; - - std::vector trainVecs = faiss::gpu::randVecs(opt.numTrain, opt.dim); - std::vector addVecs = faiss::gpu::randVecs(opt.numAdd, opt.dim); - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexIVFPQConfig config; - config.device = opt.device; - config.usePrecomputedTables = opt.usePrecomputed; - config.indicesOptions = opt.indicesOpt; - config.useFloat16LookupTables = opt.useFloat16; - - faiss::gpu::GpuIndexIVFPQ gpuIndex(&res, - opt.dim, - opt.numCentroids, - opt.codes, - opt.bitsPerCode, - faiss::METRIC_L2, - config); - - gpuIndex.setNumProbes(opt.nprobe); - - gpuIndex.train(opt.numTrain, trainVecs.data()); - gpuIndex.add(opt.numAdd, addVecs.data()); - - int numQuery = 5; - std::vector nans(numQuery * opt.dim, - std::numeric_limits::quiet_NaN()); - - std::vector distances(numQuery * opt.k, 0); - std::vector indices(numQuery * opt.k, 0); - - gpuIndex.search(numQuery, - nans.data(), - opt.k, - distances.data(), - indices.data()); - - for (int q = 0; q < numQuery; ++q) { - for (int k = 0; k < opt.k; ++k) { - EXPECT_EQ(indices[q * opt.k + k], -1); - EXPECT_EQ(distances[q * opt.k + k], std::numeric_limits::max()); - } - } -} - -TEST(TestGpuIndexIVFPQ, AddNaN) { - Options opt; - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexIVFPQConfig config; - config.device = opt.device; - config.usePrecomputedTables = opt.usePrecomputed; - config.indicesOptions = opt.indicesOpt; - config.useFloat16LookupTables = opt.useFloat16; - - faiss::gpu::GpuIndexIVFPQ gpuIndex(&res, - opt.dim, - opt.numCentroids, - opt.codes, - opt.bitsPerCode, - faiss::METRIC_L2, - config); - - gpuIndex.setNumProbes(opt.nprobe); - - int numNans = 10; - std::vector nans(numNans * opt.dim, - std::numeric_limits::quiet_NaN()); - - // Make one vector valid, which should actually add - for (int i = 0; i < opt.dim; ++i) { - nans[i] = 0.0f; - } - - std::vector trainVecs = faiss::gpu::randVecs(opt.numTrain, opt.dim); - gpuIndex.train(opt.numTrain, trainVecs.data()); - - // should not crash - EXPECT_EQ(gpuIndex.ntotal, 0); - gpuIndex.add(numNans, nans.data()); - - std::vector queryVecs = faiss::gpu::randVecs(opt.numQuery, opt.dim); - std::vector distance(opt.numQuery * opt.k, 0); - std::vector indices(opt.numQuery * opt.k, 0); - - // should not crash - gpuIndex.search(opt.numQuery, queryVecs.data(), opt.k, - distance.data(), indices.data()); -} - -TEST(TestGpuIndexIVFPQ, UnifiedMemory) { - // Construct on a random device to test multi-device, if we have - // multiple devices - int device = faiss::gpu::randVal(0, faiss::gpu::getNumDevices() - 1); - - if (!faiss::gpu::getFullUnifiedMemSupport(device)) { - return; - } - - int dim = 128; - - int numCentroids = 256; - // Unfortunately it would take forever to add 24 GB in IVFPQ data, - // so just perform a small test with data allocated in the unified - // memory address space - size_t numAdd = 10000; - size_t numTrain = numCentroids * 40; - int numQuery = 10; - int k = 10; - int nprobe = 8; - int codes = 8; - int bitsPerCode = 8; - - std::vector trainVecs = faiss::gpu::randVecs(numTrain, dim); - std::vector addVecs = faiss::gpu::randVecs(numAdd, dim); - - faiss::IndexFlatL2 quantizer(dim); - faiss::IndexIVFPQ cpuIndex(&quantizer, dim, numCentroids, codes, bitsPerCode); - - cpuIndex.train(numTrain, trainVecs.data()); - cpuIndex.add(numAdd, addVecs.data()); - cpuIndex.nprobe = nprobe; - - faiss::gpu::StandardGpuResources res; - res.noTempMemory(); - - faiss::gpu::GpuIndexIVFPQConfig config; - config.device = device; - config.memorySpace = faiss::gpu::MemorySpace::Unified; - - faiss::gpu::GpuIndexIVFPQ gpuIndex(&res, - dim, - numCentroids, - codes, - bitsPerCode, - faiss::METRIC_L2, - config); - gpuIndex.copyFrom(&cpuIndex); - gpuIndex.setNumProbes(nprobe); - - faiss::gpu::compareIndices(cpuIndex, gpuIndex, - numQuery, dim, k, "Unified Memory", - 0.015f, - 0.1f, - 0.015f); -} - -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - - // just run with a fixed test seed - faiss::gpu::setTestSeed(100); - - return RUN_ALL_TESTS(); -} diff --git a/core/src/index/thirdparty/faiss/gpu/test/TestGpuMemoryException.cpp b/core/src/index/thirdparty/faiss/gpu/test/TestGpuMemoryException.cpp deleted file mode 100644 index e3bca1d86a..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/test/TestGpuMemoryException.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include - -// Test to see if we can recover after attempting to allocate too much GPU -// memory -TEST(TestGpuMemoryException, AddException) { - size_t numBrokenAdd = std::numeric_limits::max(); - size_t numRealAdd = 10000; - size_t devFree = 0; - size_t devTotal = 0; - - CUDA_VERIFY(cudaMemGetInfo(&devFree, &devTotal)); - - // Figure out the dimensionality needed to get at least greater than devTotal - size_t brokenAddDims = ((devTotal / sizeof(float)) / numBrokenAdd) + 1; - size_t realAddDims = 128; - - faiss::gpu::StandardGpuResources res; - - faiss::gpu::GpuIndexFlatConfig config; - config.device = faiss::gpu::randVal(0, faiss::gpu::getNumDevices() - 1); - - faiss::gpu::GpuIndexFlatL2 - gpuIndexL2Broken(&res, (int) brokenAddDims, config); - faiss::gpu::GpuIndexFlatL2 - gpuIndexL2(&res, (int) realAddDims, config); - faiss::IndexFlatL2 - cpuIndex((int) realAddDims); - - // Should throw on attempting to allocate too much data - { - // allocate memory without initialization - auto vecs = - std::unique_ptr(new float[numBrokenAdd * brokenAddDims]); - EXPECT_THROW(gpuIndexL2Broken.add(numBrokenAdd, vecs.get()), - faiss::FaissException); - } - - // Should be able to add a smaller set of data now - { - auto vecs = faiss::gpu::randVecs(numRealAdd, realAddDims); - EXPECT_NO_THROW(gpuIndexL2.add(numRealAdd, vecs.data())); - cpuIndex.add(numRealAdd, vecs.data()); - } - - // Should throw on attempting to allocate too much data - { - // allocate memory without initialization - auto vecs = - std::unique_ptr(new float[numBrokenAdd * brokenAddDims]); - EXPECT_THROW(gpuIndexL2Broken.add(numBrokenAdd, vecs.get()), - faiss::FaissException); - } - - // Should be able to query results from what we had before - { - size_t numQuery = 10; - auto vecs = faiss::gpu::randVecs(numQuery, realAddDims); - EXPECT_NO_THROW(compareIndices(vecs, cpuIndex, gpuIndexL2, - numQuery, realAddDims, 50, "", - 6e-3f, 0.1f, 0.015f)); - } -} - -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - - // just run with a fixed test seed - faiss::gpu::setTestSeed(100); - - return RUN_ALL_TESTS(); -} diff --git a/core/src/index/thirdparty/faiss/gpu/test/TestGpuSelect.cu b/core/src/index/thirdparty/faiss/gpu/test/TestGpuSelect.cu deleted file mode 100644 index eec621bd5c..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/test/TestGpuSelect.cu +++ /dev/null @@ -1,193 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void testForSize(int rows, int cols, int k, bool dir, bool warp) { - std::vector v = faiss::gpu::randVecs(rows, cols); - faiss::gpu::HostTensor hostVal({rows, cols}); - - for (int r = 0; r < rows; ++r) { - for (int c = 0; c < cols; ++c) { - hostVal[r][c] = v[r * cols + c]; - } - } - - faiss::gpu::DeviceTensor bitset(nullptr, {0}); - - // row -> (val -> idx) - std::unordered_map>> hostOutValAndInd; - for (int r = 0; r < rows; ++r) { - std::vector> closest; - - for (int c = 0; c < cols; ++c) { - closest.emplace_back(c, (float) hostVal[r][c]); - } - - auto dirFalseFn = - [](std::pair& a, std::pair& b) { - return a.second < b.second; - }; - auto dirTrueFn = - [](std::pair& a, std::pair& b) { - return a.second > b.second; - }; - - std::sort(closest.begin(), closest.end(), dir ? dirTrueFn : dirFalseFn); - hostOutValAndInd.emplace(r, closest); - } - - // Select top-k on GPU - faiss::gpu::DeviceTensor gpuVal(hostVal, 0); - faiss::gpu::DeviceTensor gpuOutVal({rows, k}); - faiss::gpu::DeviceTensor gpuOutInd({rows, k}); - - if (warp) { - faiss::gpu::runWarpSelect(gpuVal, gpuOutVal, gpuOutInd, dir, k, 0); - } else { - - faiss::gpu::runBlockSelect(gpuVal, bitset, gpuOutVal, gpuOutInd, dir, k, 0); - } - - // Copy back to CPU - faiss::gpu::HostTensor outVal(gpuOutVal, 0); - faiss::gpu::HostTensor outInd(gpuOutInd, 0); - - for (int r = 0; r < rows; ++r) { - std::unordered_map seenIndices; - - for (int i = 0; i < k; ++i) { - float gpuV = outVal[r][i]; - float cpuV = hostOutValAndInd[r][i].second; - - EXPECT_EQ(gpuV, cpuV) << - "rows " << rows << " cols " << cols << " k " << k << " dir " << dir - << " row " << r << " ind " << i; - - // If there are identical elements in a row that should be - // within the top-k, then it is possible that the index can - // differ, because the order in which the GPU will see the - // equivalent values is different than the CPU (and will remain - // unspecified, since this is affected by the choice of - // k-selection algorithm that we use) - int gpuInd = outInd[r][i]; - int cpuInd = hostOutValAndInd[r][i].first; - - // We should never see duplicate indices, however - auto itSeenIndex = seenIndices.find(gpuInd); - - EXPECT_EQ(itSeenIndex, seenIndices.end()) << - "Row " << r << " user index " << gpuInd << " was seen at both " << - itSeenIndex->second << " and " << i; - - seenIndices[gpuInd] = i; - - if (gpuInd != cpuInd) { - // Gather the values from the original data via index; the - // values should be the same - float gpuGatherV = hostVal[r][gpuInd]; - float cpuGatherV = hostVal[r][cpuInd]; - - EXPECT_EQ(gpuGatherV, cpuGatherV) << - "rows " << rows << " cols " << cols << " k " << k << " dir " << dir - << " row " << r << " ind " << i << " source ind " - << gpuInd << " " << cpuInd; - } - } - } -} - -// General test -TEST(TestGpuSelect, test) { - for (int i = 0; i < 10; ++i) { - int rows = faiss::gpu::randVal(10, 100); - int cols = faiss::gpu::randVal(1, 30000); - int k = std::min(cols, faiss::gpu::randVal(1, GPU_MAX_SELECTION_K)); - bool dir = faiss::gpu::randBool(); - - testForSize(rows, cols, k, dir, false); - } -} - -// Test for k = 1 -TEST(TestGpuSelect, test1) { - for (int i = 0; i < 5; ++i) { - int rows = faiss::gpu::randVal(10, 100); - int cols = faiss::gpu::randVal(1, 30000); - bool dir = faiss::gpu::randBool(); - - testForSize(rows, cols, 1, dir, false); - } -} - -// Test for where k = #cols exactly (we are returning all the values, -// just sorted) -TEST(TestGpuSelect, testExact) { - for (int i = 0; i < 5; ++i) { - int rows = faiss::gpu::randVal(10, 100); - int cols = faiss::gpu::randVal(1, GPU_MAX_SELECTION_K); - bool dir = faiss::gpu::randBool(); - - testForSize(rows, cols, cols, dir, false); - } -} - -// General test -TEST(TestGpuSelect, testWarp) { - for (int i = 0; i < 10; ++i) { - int rows = faiss::gpu::randVal(10, 100); - int cols = faiss::gpu::randVal(1, 30000); - int k = std::min(cols, faiss::gpu::randVal(1, GPU_MAX_SELECTION_K)); - bool dir = faiss::gpu::randBool(); - - testForSize(rows, cols, k, dir, true); - } -} - -// Test for k = 1 -TEST(TestGpuSelect, test1Warp) { - for (int i = 0; i < 5; ++i) { - int rows = faiss::gpu::randVal(10, 100); - int cols = faiss::gpu::randVal(1, 30000); - bool dir = faiss::gpu::randBool(); - - testForSize(rows, cols, 1, dir, true); - } -} - -// Test for where k = #cols exactly (we are returning all the values, -// just sorted) -TEST(TestGpuSelect, testExactWarp) { - for (int i = 0; i < 5; ++i) { - int rows = faiss::gpu::randVal(10, 100); - int cols = faiss::gpu::randVal(1, GPU_MAX_SELECTION_K); - bool dir = faiss::gpu::randBool(); - - testForSize(rows, cols, cols, dir, true); - } -} - -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - - // just run with a fixed test seed - faiss::gpu::setTestSeed(100); - - return RUN_ALL_TESTS(); -} diff --git a/core/src/index/thirdparty/faiss/gpu/test/TestUtils.cpp b/core/src/index/thirdparty/faiss/gpu/test/TestUtils.cpp deleted file mode 100644 index 423d58b87d..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/test/TestUtils.cpp +++ /dev/null @@ -1,315 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -inline float relativeError(float a, float b) { - return std::abs(a - b) / (0.5f * (std::abs(a) + std::abs(b))); -} - -// This seed is also used for the faiss float_rand API; in a test it -// is all within a single thread, so it is ok -long s_seed = 1; - -void newTestSeed() { - struct timespec t; - clock_gettime(CLOCK_REALTIME, &t); - - setTestSeed(t.tv_nsec); -} - -void setTestSeed(long seed) { - printf("testing with random seed %ld\n", seed); - - srand48(seed); - s_seed = seed; -} - -int randVal(int a, int b) { - EXPECT_GE(a, 0); - EXPECT_LE(a, b); - - return a + (lrand48() % (b + 1 - a)); -} - -bool randBool() { - return randSelect({true, false}); -} - -std::vector randVecs(size_t num, size_t dim) { - std::vector v(num * dim); - - faiss::float_rand(v.data(), v.size(), s_seed); - // unfortunately we generate separate sets of vectors, and don't - // want the same values - ++s_seed; - - return v; -} - -std::vector randBinaryVecs(size_t num, size_t dim) { - std::vector v(num * (dim / 8)); - - faiss::byte_rand(v.data(), v.size(), s_seed); - // unfortunately we generate separate sets of vectors, and don't - // want the same values - ++s_seed; - - return v; -} - -void compareIndices( - const std::vector& queryVecs, - faiss::Index& refIndex, - faiss::Index& testIndex, - int numQuery, - int /*dim*/, - int k, - const std::string& configMsg, - float maxRelativeError, - float pctMaxDiff1, - float pctMaxDiffN) { - // Compare - std::vector refDistance(numQuery * k, 0); - std::vector refIndices(numQuery * k, -1); - refIndex.search(numQuery, queryVecs.data(), - k, refDistance.data(), refIndices.data()); - - std::vector testDistance(numQuery * k, 0); - std::vector testIndices(numQuery * k, -1); - testIndex.search(numQuery, queryVecs.data(), - k, testDistance.data(), testIndices.data()); - - faiss::gpu::compareLists(refDistance.data(), - refIndices.data(), - testDistance.data(), - testIndices.data(), - numQuery, k, - configMsg, - true, false, true, - maxRelativeError, pctMaxDiff1, pctMaxDiffN); -} - -void compareIndices(faiss::Index& refIndex, - faiss::Index& testIndex, - int numQuery, int dim, int k, - const std::string& configMsg, - float maxRelativeError, - float pctMaxDiff1, - float pctMaxDiffN) { - auto queryVecs = faiss::gpu::randVecs(numQuery, dim); - - compareIndices(queryVecs, - refIndex, - testIndex, - numQuery, dim, k, - configMsg, - maxRelativeError, - pctMaxDiff1, - pctMaxDiffN); -} - -template -inline T lookup(const T* p, int i, int j, int /*dim1*/, int dim2) { - return p[i * dim2 + j]; -} - -void compareLists(const float* refDist, - const faiss::Index::idx_t* refInd, - const float* testDist, - const faiss::Index::idx_t* testInd, - int dim1, int dim2, - const std::string& configMsg, - bool printBasicStats, bool printDiffs, bool assertOnErr, - float maxRelativeError, - float pctMaxDiff1, - float pctMaxDiffN) { - - float maxAbsErr = 0.0f; - for (int i = 0; i < dim1 * dim2; ++i) { - maxAbsErr = std::max(maxAbsErr, std::abs(refDist[i] - testDist[i])); - } - int numResults = dim1 * dim2; - - // query -> {index -> result position} - std::vector> refIndexMap; - - for (int query = 0; query < dim1; ++query) { - std::unordered_map indices; - - for (int result = 0; result < dim2; ++result) { - indices[lookup(refInd, query, result, dim1, dim2)] = result; - } - - refIndexMap.emplace_back(std::move(indices)); - } - - // See how far off the indices are - // Keep track of the difference for each entry - std::vector> indexDiffs; - - int diff1 = 0; // index differs by 1 - int diffN = 0; // index differs by >1 - int diffInf = 0; // index not found in the other - int nonUniqueIndices = 0; - - double avgDiff = 0.0; - int maxDiff = 0; - float maxRelErr = 0.0f; - - for (int query = 0; query < dim1; ++query) { - std::vector diffs; - std::set uniqueIndices; - - auto& indices = refIndexMap[query]; - - for (int result = 0; result < dim2; ++result) { - auto t = lookup(testInd, query, result, dim1, dim2); - - // All indices reported within a query should be unique; this is - // a serious error if is otherwise the case. - // If -1 is reported (no result due to IVF partitioning or not enough - // entries in the index), then duplicates are allowed, but both the - // reference and test must have -1 in the same position. - if (t == -1) { - EXPECT_EQ(lookup(refInd, query, result, dim1, dim2), t); - } else { - bool uniqueIndex = uniqueIndices.count(t) == 0; - if (assertOnErr) { - EXPECT_TRUE(uniqueIndex) << configMsg - << " " << query - << " " << result - << " " << t; - } - - if (!uniqueIndex) { - ++nonUniqueIndices; - } else { - uniqueIndices.insert(t); - } - - auto it = indices.find(t); - if (it != indices.end()) { - int diff = std::abs(result - it->second); - diffs.push_back(diff); - - if (diff == 1) { - ++diff1; - maxDiff = std::max(diff, maxDiff); - } else if (diff > 1) { - ++diffN; - maxDiff = std::max(diff, maxDiff); - } - - avgDiff += (double) diff; - } else { - ++diffInf; - diffs.push_back(-1); - // don't count this for maxDiff - } - } - - auto refD = lookup(refDist, query, result, dim1, dim2); - auto testD = lookup(testDist, query, result, dim1, dim2); - - float relErr = relativeError(refD, testD); - - if (assertOnErr) { - EXPECT_LE(relErr, maxRelativeError) << configMsg - << " (" << query << ", " << result - << ") refD: " << refD - << " testD: " << testD; - } - - maxRelErr = std::max(maxRelErr, relErr); - } - - indexDiffs.emplace_back(std::move(diffs)); - } - - if (assertOnErr) { - EXPECT_LE((float) (diff1 + diffN + diffInf), - (float) numResults * pctMaxDiff1) << configMsg; - - // Don't count diffInf because that could be diff1 as far as we - // know - EXPECT_LE((float) diffN, (float) numResults * pctMaxDiffN) << configMsg; - } - - avgDiff /= (double) numResults; - - if (printBasicStats) { - if (!configMsg.empty()) { - printf("Config\n" - "----------------------------\n" - "%s\n", - configMsg.c_str()); - } - - printf("Result error and differences\n" - "----------------------------\n" - "max abs diff %.7f rel diff %.7f\n" - "idx diff avg: %.5g max: %d\n" - "idx diff of 1: %d (%.3f%% of queries)\n" - "idx diff of >1: %d (%.3f%% of queries)\n" - "idx diff not found: %d (%.3f%% of queries)" - " [typically a last element inversion]\n" - "non-unique indices: %d (a serious error if >0)\n", - maxAbsErr, maxRelErr, - avgDiff, maxDiff, - diff1, 100.0f * (float) diff1 / (float) numResults, - diffN, 100.0f * (float) diffN / (float) numResults, - diffInf, 100.0f * (float) diffInf / (float) numResults, - nonUniqueIndices); - } - - if (printDiffs) { - printf("differences:\n"); - printf("==================\n"); - for (int query = 0; query < dim1; ++query) { - for (int result = 0; result < dim2; ++result) { - long refI = lookup(refInd, query, result, dim1, dim2); - long testI = lookup(testInd, query, result, dim1, dim2); - - if (refI != testI) { - float refD = lookup(refDist, query, result, dim1, dim2); - float testD = lookup(testDist, query, result, dim1, dim2); - - float maxDist = std::max(refD, testD); - float delta = std::abs(refD - testD); - - float relErr = delta / maxDist; - - if (refD == testD) { - printf("(%d, %d [%d]) (ref %ld tst %ld dist ==)\n", - query, result, - indexDiffs[query][result], - refI, testI); - } else { - printf("(%d, %d [%d]) (ref %ld tst %ld abs %.8f " - "rel %.8f ref %a tst %a)\n", - query, result, - indexDiffs[query][result], - refI, testI, delta, relErr, refD, testD); - } - } - } - } - } -} - -} } diff --git a/core/src/index/thirdparty/faiss/gpu/test/TestUtils.h b/core/src/index/thirdparty/faiss/gpu/test/TestUtils.h deleted file mode 100644 index c59a4ab0ae..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/test/TestUtils.h +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -/// Generates and displays a new seed for the test -void newTestSeed(); - -/// Uses an explicit seed for the test -void setTestSeed(long seed); - -/// Returns the relative error in difference between a and b -/// (|a - b| / (0.5 * (|a| + |b|)) -float relativeError(float a, float b); - -/// Generates a random integer in the range [a, b] -int randVal(int a, int b); - -/// Generates a random bool -bool randBool(); - -/// Select a random value from the given list of values provided as an -/// initializer_list -template -T randSelect(std::initializer_list vals) { - FAISS_ASSERT(vals.size() > 0); - int sel = randVal(0, vals.size()); - - int i = 0; - for (auto v : vals) { - if (i++ == sel) { - return v; - } - } - - // should not get here - return *vals.begin(); -} - -/// Generates a collection of random vectors in the range [0, 1] -std::vector randVecs(size_t num, size_t dim); - -/// Generates a collection of random bit vectors -std::vector randBinaryVecs(size_t num, size_t dim); - -/// Compare two indices via query for similarity, with a user-specified set of -/// query vectors -void compareIndices(const std::vector& queryVecs, - faiss::Index& refIndex, - faiss::Index& testIndex, - int numQuery, int dim, int k, - const std::string& configMsg, - float maxRelativeError = 6e-5f, - float pctMaxDiff1 = 0.1f, - float pctMaxDiffN = 0.005f); - -/// Compare two indices via query for similarity, generating random query -/// vectors -void compareIndices(faiss::Index& refIndex, - faiss::Index& testIndex, - int numQuery, int dim, int k, - const std::string& configMsg, - float maxRelativeError = 6e-5f, - float pctMaxDiff1 = 0.1f, - float pctMaxDiffN = 0.005f); - -/// Display specific differences in the two (distance, index) lists -void compareLists(const float* refDist, - const faiss::Index::idx_t* refInd, - const float* testDist, - const faiss::Index::idx_t* testInd, - int dim1, int dim2, - const std::string& configMsg, - bool printBasicStats, bool printDiffs, bool assertOnErr, - float maxRelativeError = 6e-5f, - float pctMaxDiff1 = 0.1f, - float pctMaxDiffN = 0.005f); - -} } diff --git a/core/src/index/thirdparty/faiss/gpu/test/demo_ivfpq_indexing_gpu.cpp b/core/src/index/thirdparty/faiss/gpu/test/demo_ivfpq_indexing_gpu.cpp deleted file mode 100644 index 852a43cbe9..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/test/demo_ivfpq_indexing_gpu.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// Copyright 2004-present Facebook. All Rights Reserved - - -#include -#include -#include - -#include - - -#include -#include - -#include -#include - -double elapsed () -{ - struct timeval tv; - gettimeofday (&tv, NULL); - return tv.tv_sec + tv.tv_usec * 1e-6; -} - - -int main () -{ - - double t0 = elapsed(); - - // dimension of the vectors to index - int d = 128; - - // size of the database we plan to index - size_t nb = 200 * 1000; - - // make a set of nt training vectors in the unit cube - // (could be the database) - size_t nt = 100 * 1000; - - int dev_no = 0; - /* - printf ("[%.3f s] Begin d=%d nb=%ld nt=%nt dev_no=%d\n", - elapsed() - t0, d, nb, nt, dev_no); - */ - // a reasonable number of centroids to index nb vectors - int ncentroids = int (4 * sqrt (nb)); - - faiss::gpu::StandardGpuResources resources; - - - // the coarse quantizer should not be dealloced before the index - // 4 = nb of bytes per code (d must be a multiple of this) - // 8 = nb of bits per sub-code (almost always 8) - faiss::gpu::GpuIndexIVFPQConfig config; - config.device = dev_no; - - faiss::gpu::GpuIndexIVFPQ index ( - &resources, d, ncentroids, 4, 8, faiss::METRIC_L2, config); - - { // training - printf ("[%.3f s] Generating %ld vectors in %dD for training\n", - elapsed() - t0, nt, d); - - std::vector trainvecs (nt * d); - for (size_t i = 0; i < nt * d; i++) { - trainvecs[i] = drand48(); - } - - printf ("[%.3f s] Training the index\n", - elapsed() - t0); - index.verbose = true; - - index.train (nt, trainvecs.data()); - } - - { // I/O demo - const char *outfilename = "/tmp/index_trained.faissindex"; - printf ("[%.3f s] storing the pre-trained index to %s\n", - elapsed() - t0, outfilename); - - faiss::Index * cpu_index = faiss::gpu::index_gpu_to_cpu (&index); - - write_index (cpu_index, outfilename); - - delete cpu_index; - } - - size_t nq; - std::vector queries; - - { // populating the database - printf ("[%.3f s] Building a dataset of %ld vectors to index\n", - elapsed() - t0, nb); - - std::vector database (nb * d); - for (size_t i = 0; i < nb * d; i++) { - database[i] = drand48(); - } - - printf ("[%.3f s] Adding the vectors to the index\n", - elapsed() - t0); - - index.add (nb, database.data()); - - printf ("[%.3f s] done\n", elapsed() - t0); - - // remember a few elements from the database as queries - int i0 = 1234; - int i1 = 1243; - - nq = i1 - i0; - queries.resize (nq * d); - for (int i = i0; i < i1; i++) { - for (int j = 0; j < d; j++) { - queries [(i - i0) * d + j] = database [i * d + j]; - } - } - - } - - { // searching the database - int k = 5; - printf ("[%.3f s] Searching the %d nearest neighbors " - "of %ld vectors in the index\n", - elapsed() - t0, k, nq); - - std::vector nns (k * nq); - std::vector dis (k * nq); - - index.search (nq, queries.data(), k, dis.data(), nns.data()); - - printf ("[%.3f s] Query results (vector ids, then distances):\n", - elapsed() - t0); - - for (int i = 0; i < nq; i++) { - printf ("query %2d: ", i); - for (int j = 0; j < k; j++) { - printf ("%7ld ", nns[j + i * k]); - } - printf ("\n dis: "); - for (int j = 0; j < k; j++) { - printf ("%7g ", dis[j + i * k]); - } - printf ("\n"); - } - - printf ("note that the nearest neighbor is not at " - "distance 0 due to quantization errors\n"); - } - - return 0; -} diff --git a/core/src/index/thirdparty/faiss/gpu/test/test_gpu_index.py b/core/src/index/thirdparty/faiss/gpu/test/test_gpu_index.py deleted file mode 100644 index 8b17b4801f..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/test/test_gpu_index.py +++ /dev/null @@ -1,314 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -from __future__ import absolute_import, division, print_function, unicode_literals - -import time -import unittest -import numpy as np -import faiss - - -class EvalIVFPQAccuracy(unittest.TestCase): - - def get_dataset(self, small_one=False): - if not small_one: - d = 128 - nb = 100000 - nt = 15000 - nq = 2000 - else: - d = 32 - nb = 1000 - nt = 1000 - nq = 200 - np.random.seed(123) - - # generate points in a low-dim subspace to make the resutls - # look better :-) - d1 = 16 - q, r = np.linalg.qr(np.random.randn(d, d)) - qc = q[:d1, :] - def make_mat(n): - return np.dot( - np.random.random(size=(nb, d1)), qc).astype('float32') - - return (make_mat(nt), make_mat(nb), make_mat(nq)) - - - def test_mm(self): - # trouble with MKL+fbmake that appears only at runtime. Check it here - x = np.random.random(size=(100, 20)).astype('float32') - mat = faiss.PCAMatrix(20, 10) - mat.train(x) - mat.apply_py(x) - - def do_cpu_to_gpu(self, index_key): - ts = [] - ts.append(time.time()) - (xt, xb, xq) = self.get_dataset(small_one=True) - nb, d = xb.shape - - index = faiss.index_factory(d, index_key) - if index.__class__ == faiss.IndexIVFPQ: - # speed up test - index.pq.cp.niter = 2 - index.do_polysemous_training = False - ts.append(time.time()) - - index.train(xt) - ts.append(time.time()) - - # adding some ids because there was a bug in this case - index.add_with_ids(xb, np.arange(nb) * 3 + 12345) - ts.append(time.time()) - - index.nprobe = 4 - D, Iref = index.search(xq, 10) - ts.append(time.time()) - - res = faiss.StandardGpuResources() - gpu_index = faiss.index_cpu_to_gpu(res, 0, index) - ts.append(time.time()) - - gpu_index.setNumProbes(4) - - D, Inew = gpu_index.search(xq, 10) - ts.append(time.time()) - print('times:', [t - ts[0] for t in ts]) - - self.assertGreaterEqual((Iref == Inew).sum(), Iref.size) - - if faiss.get_num_gpus() == 1: - return - - for shard in False, True: - - # test on just 2 GPUs - res = [faiss.StandardGpuResources() for i in range(2)] - co = faiss.GpuMultipleClonerOptions() - co.shard = shard - - gpu_index = faiss.index_cpu_to_gpu_multiple_py(res, index, co) - - faiss.GpuParameterSpace().set_index_parameter( - gpu_index, 'nprobe', 4) - - D, Inew = gpu_index.search(xq, 10) - - # 0.99: allow some tolerance in results otherwise test - # fails occasionally (not reproducible) - self.assertGreaterEqual((Iref == Inew).sum(), Iref.size * 0.99) - - def test_cpu_to_gpu_IVFPQ(self): - self.do_cpu_to_gpu('IVF128,PQ4') - - def test_cpu_to_gpu_IVFFlat(self): - self.do_cpu_to_gpu('IVF128,Flat') - - def test_set_gpu_param(self): - index = faiss.index_factory(12, "PCAR8,IVF10,PQ4") - res = faiss.StandardGpuResources() - gpu_index = faiss.index_cpu_to_gpu(res, 0, index) - faiss.GpuParameterSpace().set_index_parameter(gpu_index, "nprobe", 3) - - -class ReferencedObject(unittest.TestCase): - - d = 16 - xb = np.random.rand(256, d).astype('float32') - nlist = 128 - - d_bin = 256 - xb_bin = np.random.randint(256, size=(10000, d_bin // 8)).astype('uint8') - xq_bin = np.random.randint(256, size=(1000, d_bin // 8)).astype('uint8') - - def test_proxy(self): - index = faiss.IndexReplicas() - for _i in range(3): - sub_index = faiss.IndexFlatL2(self.d) - sub_index.add(self.xb) - index.addIndex(sub_index) - assert index.d == self.d - index.search(self.xb, 10) - - def test_resources(self): - # this used to crash! - index = faiss.index_cpu_to_gpu(faiss.StandardGpuResources(), 0, - faiss.IndexFlatL2(self.d)) - index.add(self.xb) - - def test_flat(self): - index = faiss.GpuIndexFlat(faiss.StandardGpuResources(), - self.d, faiss.METRIC_L2) - index.add(self.xb) - - def test_ivfflat(self): - index = faiss.GpuIndexIVFFlat( - faiss.StandardGpuResources(), - self.d, self.nlist, faiss.METRIC_L2) - index.train(self.xb) - - def test_ivfpq(self): - index_cpu = faiss.IndexIVFPQ( - faiss.IndexFlatL2(self.d), - self.d, self.nlist, 2, 8) - # speed up test - index_cpu.pq.cp.niter = 2 - index_cpu.do_polysemous_training = False - index_cpu.train(self.xb) - - index = faiss.GpuIndexIVFPQ( - faiss.StandardGpuResources(), index_cpu) - index.add(self.xb) - - def test_binary_flat(self): - k = 10 - - index_ref = faiss.IndexBinaryFlat(self.d_bin) - index_ref.add(self.xb_bin) - D_ref, I_ref = index_ref.search(self.xq_bin, k) - - index = faiss.GpuIndexBinaryFlat(faiss.StandardGpuResources(), - self.d_bin) - index.add(self.xb_bin) - D, I = index.search(self.xq_bin, k) - - for d_ref, i_ref, d_new, i_new in zip(D_ref, I_ref, D, I): - # exclude max distance - assert d_ref.max() == d_new.max() - dmax = d_ref.max() - - # sort by (distance, id) pairs to be reproducible - ref = [(d, i) for d, i in zip(d_ref, i_ref) if d < dmax] - ref.sort() - - new = [(d, i) for d, i in zip(d_new, i_new) if d < dmax] - new.sort() - - assert ref == new - - def test_stress(self): - # a mixture of the above, from issue #631 - target = np.random.rand(50, 16).astype('float32') - - index = faiss.IndexReplicas() - size, dim = target.shape - num_gpu = 4 - for _i in range(num_gpu): - config = faiss.GpuIndexFlatConfig() - config.device = 0 # simulate on a single GPU - sub_index = faiss.GpuIndexFlatIP(faiss.StandardGpuResources(), dim, config) - index.addIndex(sub_index) - - index = faiss.IndexIDMap(index) - ids = np.arange(size) - index.add_with_ids(target, ids) - - - -class TestShardedFlat(unittest.TestCase): - - def test_sharded(self): - d = 32 - nb = 1000 - nq = 200 - k = 10 - rs = np.random.RandomState(123) - xb = rs.rand(nb, d).astype('float32') - xq = rs.rand(nq, d).astype('float32') - - index_cpu = faiss.IndexFlatL2(d) - - assert faiss.get_num_gpus() > 1 - - co = faiss.GpuMultipleClonerOptions() - co.shard = True - index = faiss.index_cpu_to_all_gpus(index_cpu, co, ngpu=2) - - index.add(xb) - D, I = index.search(xq, k) - - index_cpu.add(xb) - D_ref, I_ref = index_cpu.search(xq, k) - - assert np.all(I == I_ref) - - del index - index2 = faiss.index_cpu_to_all_gpus(index_cpu, co, ngpu=2) - D2, I2 = index2.search(xq, k) - - assert np.all(I2 == I_ref) - - try: - index2.add(xb) - except RuntimeError: - pass - else: - assert False, "this call should fail!" - - -class TestGPUKmeans(unittest.TestCase): - - def test_kmeans(self): - d = 32 - nb = 1000 - k = 10 - rs = np.random.RandomState(123) - xb = rs.rand(nb, d).astype('float32') - - km1 = faiss.Kmeans(d, k) - obj1 = km1.train(xb) - - km2 = faiss.Kmeans(d, k, gpu=True) - obj2 = km2.train(xb) - - print(obj1, obj2) - assert np.allclose(obj1, obj2) - - -class TestAlternativeDistances(unittest.TestCase): - - def do_test(self, metric, metric_arg=0): - res = faiss.StandardGpuResources() - d = 32 - nb = 1000 - nq = 100 - - rs = np.random.RandomState(123) - xb = rs.rand(nb, d).astype('float32') - xq = rs.rand(nq, d).astype('float32') - - index_ref = faiss.IndexFlat(d, metric) - index_ref.metric_arg = metric_arg - index_ref.add(xb) - Dref, Iref = index_ref.search(xq, 10) - - # build from other index - index = faiss.GpuIndexFlat(res, index_ref) - Dnew, Inew = index.search(xq, 10) - np.testing.assert_array_equal(Inew, Iref) - np.testing.assert_allclose(Dnew, Dref, rtol=1e-6) - - # build from scratch - index = faiss.GpuIndexFlat(res, d, metric) - index.metric_arg = metric_arg - index.add(xb) - - Dnew, Inew = index.search(xq, 10) - np.testing.assert_array_equal(Inew, Iref) - - def test_L1(self): - self.do_test(faiss.METRIC_L1) - - def test_Linf(self): - self.do_test(faiss.METRIC_Linf) - - def test_Lp(self): - self.do_test(faiss.METRIC_Lp, 0.7) - - -if __name__ == '__main__': - unittest.main() diff --git a/core/src/index/thirdparty/faiss/gpu/test/test_gpu_index_ivfsq.py b/core/src/index/thirdparty/faiss/gpu/test/test_gpu_index_ivfsq.py deleted file mode 100644 index 6c312af3e6..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/test/test_gpu_index_ivfsq.py +++ /dev/null @@ -1,229 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#! /usr/bin/env python3 - -from __future__ import print_function -import unittest -import numpy as np -import faiss - -def make_t(num, d, clamp=False): - rs = np.random.RandomState(123) - x = rs.rand(num, d).astype('float32') - if clamp: - x = (x * 255).astype('uint8').astype('float32') - return x - -def make_indices_copy_from_cpu(nlist, d, qtype, by_residual, metric, clamp): - to_train = make_t(10000, d, clamp) - - quantizer_cp = faiss.IndexFlat(d, metric) - idx_cpu = faiss.IndexIVFScalarQuantizer(quantizer_cp, d, nlist, - qtype, metric, by_residual) - - idx_cpu.train(to_train) - idx_cpu.add(to_train) - - res = faiss.StandardGpuResources() - res.noTempMemory() - idx_gpu = faiss.GpuIndexIVFScalarQuantizer(res, idx_cpu) - - return idx_cpu, idx_gpu - - -def make_indices_copy_from_gpu(nlist, d, qtype, by_residual, metric, clamp): - to_train = make_t(10000, d, clamp) - - res = faiss.StandardGpuResources() - res.noTempMemory() - idx_gpu = faiss.GpuIndexIVFScalarQuantizer(res, d, nlist, - qtype, metric, by_residual) - idx_gpu.train(to_train) - idx_gpu.add(to_train) - - quantizer_cp = faiss.IndexFlat(d, metric) - idx_cpu = faiss.IndexIVFScalarQuantizer(quantizer_cp, d, nlist, - qtype, metric, by_residual) - idx_gpu.copyTo(idx_cpu) - - return idx_cpu, idx_gpu - - -def make_indices_train(nlist, d, qtype, by_residual, metric, clamp): - to_train = make_t(10000, d, clamp) - - quantizer_cp = faiss.IndexFlat(d, metric) - idx_cpu = faiss.IndexIVFScalarQuantizer(quantizer_cp, d, nlist, - qtype, metric, by_residual) - assert(by_residual == idx_cpu.by_residual) - - idx_cpu.train(to_train) - idx_cpu.add(to_train) - - res = faiss.StandardGpuResources() - res.noTempMemory() - idx_gpu = faiss.GpuIndexIVFScalarQuantizer(res, d, nlist, - qtype, metric, by_residual) - assert(by_residual == idx_gpu.by_residual) - - idx_gpu.train(to_train) - idx_gpu.add(to_train) - - return idx_cpu, idx_gpu - -# -# Testing functions -# - -def summarize_results(dist, idx): - valid = [] - invalid = [] - for query in range(dist.shape[0]): - valid_sub = {} - invalid_sub = [] - - for order, (d, i) in enumerate(zip(dist[query], idx[query])): - if i == -1: - invalid_sub.append(order) - else: - valid_sub[i] = [order, d] - - valid.append(valid_sub) - invalid.append(invalid_sub) - - return valid, invalid - -def compare_results(d1, i1, d2, i2): - # Count number of index differences - idx_diffs = {} - idx_diffs_inf = 0 - idx_invalid = 0 - - valid1, invalid1 = summarize_results(d1, i1) - valid2, invalid2 = summarize_results(d2, i2) - - # Invalid results should be the same for both - # (except if we happen to hit different centroids) - for inv1, inv2 in zip(invalid1, invalid2): - if (len(inv1) != len(inv2)): - print('mismatch ', len(inv1), len(inv2), inv2[0]) - - assert(len(inv1) == len(inv2)) - idx_invalid += len(inv2) - for x1, x2 in zip(inv1, inv2): - assert(x1 == x2) - - for _, (query1, query2) in enumerate(zip(valid1, valid2)): - for idx1, order_d1 in query1.items(): - order_d2 = query2.get(idx1, None) - if order_d2: - idx_diff = order_d1[0] - order_d2[0] - - if idx_diff not in idx_diffs: - idx_diffs[idx_diff] = 1 - else: - idx_diffs[idx_diff] += 1 - else: - idx_diffs_inf += 1 - - return idx_diffs, idx_diffs_inf, idx_invalid - -def check_diffs(total_num, in_window_thresh, diffs, diff_inf, invalid): - # We require a certain fraction of results to be within +/- diff_window - # index differences - diff_window = 4 - in_window = 0 - - for diff in sorted(diffs): - if abs(diff) <= diff_window: - in_window += diffs[diff] / total_num - - if (in_window < in_window_thresh): - print('error {} {}'.format(in_window, in_window_thresh)) - assert(in_window >= in_window_thresh) - -def do_test_with_index(ci, gi, nprobe, k, clamp, in_window_thresh): - num_query = 11 - to_query = make_t(num_query, ci.d, clamp) - - ci.nprobe = ci.nprobe - gi.nprobe = gi.nprobe - - total_num = num_query * k - check_diffs(total_num, in_window_thresh, - *compare_results(*ci.search(to_query, k), - *gi.search(to_query, k))) - -def do_test(nlist, d, qtype, by_residual, metric, nprobe, k): - clamp = (qtype == faiss.ScalarQuantizer.QT_8bit_direct) - ci, gi = make_indices_copy_from_cpu(nlist, d, qtype, - by_residual, metric, clamp) - # A direct copy should be much more closely in agreement - # (except for fp accumulation order differences) - do_test_with_index(ci, gi, nprobe, k, clamp, 0.99) - - ci, gi = make_indices_copy_from_gpu(nlist, d, qtype, - by_residual, metric, clamp) - # A direct copy should be much more closely in agreement - # (except for fp accumulation order differences) - do_test_with_index(ci, gi, nprobe, k, clamp, 0.99) - - ci, gi = make_indices_train(nlist, d, qtype, - by_residual, metric, clamp) - # Separate training can produce a slightly different coarse quantizer - # and residuals - do_test_with_index(ci, gi, nprobe, k, clamp, 0.8) - -def do_multi_test(qtype): - nlist = 100 - nprobe = 10 - k = 50 - - for d in [11, 64]: - if (qtype != faiss.ScalarQuantizer.QT_8bit_direct): - # residual doesn't make sense here - do_test(nlist, d, qtype, True, - faiss.METRIC_L2, nprobe, k) - do_test(nlist, d, qtype, True, - faiss.METRIC_INNER_PRODUCT, nprobe, k) - do_test(nlist, d, qtype, False, faiss.METRIC_L2, nprobe, k) - do_test(nlist, d, qtype, False, faiss.METRIC_INNER_PRODUCT, nprobe, k) - -# -# Test -# - -class TestSQ(unittest.TestCase): - def test_fp16(self): - do_multi_test(faiss.ScalarQuantizer.QT_fp16) - - def test_8bit(self): - do_multi_test(faiss.ScalarQuantizer.QT_8bit) - - def test_8bit_uniform(self): - do_multi_test(faiss.ScalarQuantizer.QT_8bit_uniform) - - def test_6bit(self): - try: - do_multi_test(faiss.ScalarQuantizer.QT_6bit) - # should not reach here; QT_6bit is unimplemented - except: - print('QT_6bit exception thrown (is expected)') - else: - assert(False) - - def test_4bit(self): - do_multi_test(faiss.ScalarQuantizer.QT_4bit) - - def test_4bit_uniform(self): - do_multi_test(faiss.ScalarQuantizer.QT_4bit_uniform) - - def test_8bit_direct(self): - do_multi_test(faiss.ScalarQuantizer.QT_8bit_direct) - - -if __name__ == '__main__': - unittest.main() diff --git a/core/src/index/thirdparty/faiss/gpu/test/test_pytorch_faiss.py b/core/src/index/thirdparty/faiss/gpu/test/test_pytorch_faiss.py deleted file mode 100644 index f59f711b82..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/test/test_pytorch_faiss.py +++ /dev/null @@ -1,215 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -from __future__ import absolute_import, division, print_function, unicode_literals - -import numpy as np -import unittest -import faiss -import torch - -def swig_ptr_from_FloatTensor(x): - assert x.is_contiguous() - assert x.dtype == torch.float32 - return faiss.cast_integer_to_float_ptr( - x.storage().data_ptr() + x.storage_offset() * 4) - -def swig_ptr_from_LongTensor(x): - assert x.is_contiguous() - assert x.dtype == torch.int64, 'dtype=%s' % x.dtype - return faiss.cast_integer_to_long_ptr( - x.storage().data_ptr() + x.storage_offset() * 8) - - - -def search_index_pytorch(index, x, k, D=None, I=None): - """call the search function of an index with pytorch tensor I/O (CPU - and GPU supported)""" - assert x.is_contiguous() - n, d = x.size() - assert d == index.d - - if D is None: - D = torch.empty((n, k), dtype=torch.float32, device=x.device) - else: - assert D.size() == (n, k) - - if I is None: - I = torch.empty((n, k), dtype=torch.int64, device=x.device) - else: - assert I.size() == (n, k) - torch.cuda.synchronize() - xptr = swig_ptr_from_FloatTensor(x) - Iptr = swig_ptr_from_LongTensor(I) - Dptr = swig_ptr_from_FloatTensor(D) - index.search_c(n, xptr, - k, Dptr, Iptr) - torch.cuda.synchronize() - return D, I - - -def search_raw_array_pytorch(res, xb, xq, k, D=None, I=None, - metric=faiss.METRIC_L2): - assert xb.device == xq.device - - nq, d = xq.size() - if xq.is_contiguous(): - xq_row_major = True - elif xq.t().is_contiguous(): - xq = xq.t() # I initially wrote xq:t(), Lua is still haunting me :-) - xq_row_major = False - else: - raise TypeError('matrix should be row or column-major') - - xq_ptr = swig_ptr_from_FloatTensor(xq) - - nb, d2 = xb.size() - assert d2 == d - if xb.is_contiguous(): - xb_row_major = True - elif xb.t().is_contiguous(): - xb = xb.t() - xb_row_major = False - else: - raise TypeError('matrix should be row or column-major') - xb_ptr = swig_ptr_from_FloatTensor(xb) - - if D is None: - D = torch.empty(nq, k, device=xb.device, dtype=torch.float32) - else: - assert D.shape == (nq, k) - assert D.device == xb.device - - if I is None: - I = torch.empty(nq, k, device=xb.device, dtype=torch.int64) - else: - assert I.shape == (nq, k) - assert I.device == xb.device - - D_ptr = swig_ptr_from_FloatTensor(D) - I_ptr = swig_ptr_from_LongTensor(I) - - faiss.bruteForceKnn(res, metric, - xb_ptr, xb_row_major, nb, - xq_ptr, xq_row_major, nq, - d, k, D_ptr, I_ptr) - - return D, I - -def to_column_major(x): - if hasattr(torch, 'contiguous_format'): - return x.t().clone(memory_format=torch.contiguous_format).t() - else: - # was default setting before memory_format was introduced - return x.t().clone().t() - -class PytorchFaissInterop(unittest.TestCase): - - def test_interop(self): - - d = 16 - nq = 5 - nb = 20 - - xq = faiss.randn(nq * d, 1234).reshape(nq, d) - xb = faiss.randn(nb * d, 1235).reshape(nb, d) - - res = faiss.StandardGpuResources() - index = faiss.GpuIndexFlatIP(res, d) - index.add(xb) - - # reference CPU result - Dref, Iref = index.search(xq, 5) - - # query is pytorch tensor (CPU) - xq_torch = torch.FloatTensor(xq) - - D2, I2 = search_index_pytorch(index, xq_torch, 5) - - assert np.all(Iref == I2.numpy()) - - # query is pytorch tensor (GPU) - xq_torch = xq_torch.cuda() - # no need for a sync here - - D3, I3 = search_index_pytorch(index, xq_torch, 5) - - # D3 and I3 are on torch tensors on GPU as well. - # this does a sync, which is useful because faiss and - # pytorch use different Cuda streams. - res.syncDefaultStreamCurrentDevice() - - assert np.all(Iref == I3.cpu().numpy()) - - def test_raw_array_search(self): - d = 32 - nb = 1024 - nq = 128 - k = 10 - - # make GT on Faiss CPU - - xq = faiss.randn(nq * d, 1234).reshape(nq, d) - xb = faiss.randn(nb * d, 1235).reshape(nb, d) - - index = faiss.IndexFlatL2(d) - index.add(xb) - gt_D, gt_I = index.search(xq, k) - - # resource object, can be re-used over calls - res = faiss.StandardGpuResources() - # put on same stream as pytorch to avoid synchronizing streams - res.setDefaultNullStreamAllDevices() - - for xq_row_major in True, False: - for xb_row_major in True, False: - - # move to pytorch & GPU - xq_t = torch.from_numpy(xq).cuda() - xb_t = torch.from_numpy(xb).cuda() - - if not xq_row_major: - xq_t = to_column_major(xq_t) - assert not xq_t.is_contiguous() - - if not xb_row_major: - xb_t = to_column_major(xb_t) - assert not xb_t.is_contiguous() - - D, I = search_raw_array_pytorch(res, xb_t, xq_t, k) - - # back to CPU for verification - D = D.cpu().numpy() - I = I.cpu().numpy() - - assert np.all(I == gt_I) - assert np.all(np.abs(D - gt_D).max() < 1e-4) - - - - # test on subset - try: - D, I = search_raw_array_pytorch(res, xb_t, xq_t[60:80], k) - except TypeError: - if not xq_row_major: - # then it is expected - continue - # otherwise it is an error - raise - - # back to CPU for verification - D = D.cpu().numpy() - I = I.cpu().numpy() - - assert np.all(I == gt_I[60:80]) - assert np.all(np.abs(D - gt_D[60:80]).max() < 1e-4) - - - - - - -if __name__ == '__main__': - unittest.main() diff --git a/core/src/index/thirdparty/faiss/gpu/utils/BlockSelectFloat.cu b/core/src/index/thirdparty/faiss/gpu/utils/BlockSelectFloat.cu deleted file mode 100644 index 7f1febed3e..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/BlockSelectFloat.cu +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -namespace faiss { namespace gpu { - -// warp Q to thread Q: -// 1, 1 -// 32, 2 -// 64, 3 -// 128, 3 -// 256, 4 -// 512, 8 -// 1024, 8 -// 2048, 8 - -BLOCK_SELECT_DECL(float, true, 1); -BLOCK_SELECT_DECL(float, true, 32); -BLOCK_SELECT_DECL(float, true, 64); -BLOCK_SELECT_DECL(float, true, 128); -BLOCK_SELECT_DECL(float, true, 256); -BLOCK_SELECT_DECL(float, true, 512); -BLOCK_SELECT_DECL(float, true, 1024); -#if GPU_MAX_SELECTION_K >= 2048 -BLOCK_SELECT_DECL(float, true, 2048); -#endif - -BLOCK_SELECT_DECL(float, false, 1); -BLOCK_SELECT_DECL(float, false, 32); -BLOCK_SELECT_DECL(float, false, 64); -BLOCK_SELECT_DECL(float, false, 128); -BLOCK_SELECT_DECL(float, false, 256); -BLOCK_SELECT_DECL(float, false, 512); -BLOCK_SELECT_DECL(float, false, 1024); -#if GPU_MAX_SELECTION_K >= 2048 -BLOCK_SELECT_DECL(float, false, 2048); -#endif - -void runBlockSelect(Tensor& in, - Tensor& bitset, - Tensor& outK, - Tensor& outV, - bool dir, int k, cudaStream_t stream) { - FAISS_ASSERT(k <= GPU_MAX_SELECTION_K); - - if (dir) { - if (k == 1) { - BLOCK_SELECT_CALL(float, true, 1); - } else if (k <= 32) { - BLOCK_SELECT_CALL(float, true, 32); - } else if (k <= 64) { - BLOCK_SELECT_CALL(float, true, 64); - } else if (k <= 128) { - BLOCK_SELECT_CALL(float, true, 128); - } else if (k <= 256) { - BLOCK_SELECT_CALL(float, true, 256); - } else if (k <= 512) { - BLOCK_SELECT_CALL(float, true, 512); - } else if (k <= 1024) { - BLOCK_SELECT_CALL(float, true, 1024); -#if GPU_MAX_SELECTION_K >= 2048 - } else if (k <= 2048) { - BLOCK_SELECT_CALL(float, true, 2048); -#endif - } - } else { - if (k == 1) { - BLOCK_SELECT_CALL(float, false, 1); - } else if (k <= 32) { - BLOCK_SELECT_CALL(float, false, 32); - } else if (k <= 64) { - BLOCK_SELECT_CALL(float, false, 64); - } else if (k <= 128) { - BLOCK_SELECT_CALL(float, false, 128); - } else if (k <= 256) { - BLOCK_SELECT_CALL(float, false, 256); - } else if (k <= 512) { - BLOCK_SELECT_CALL(float, false, 512); - } else if (k <= 1024) { - BLOCK_SELECT_CALL(float, false, 1024); -#if GPU_MAX_SELECTION_K >= 2048 - } else if (k <= 2048) { - BLOCK_SELECT_CALL(float, false, 2048); -#endif - } - } -} - -void runBlockSelectPair(Tensor& inK, - Tensor& inV, - Tensor& bitset, - Tensor& outK, - Tensor& outV, - bool dir, int k, cudaStream_t stream) { - FAISS_ASSERT(k <= GPU_MAX_SELECTION_K); - - if (dir) { - if (k == 1) { - BLOCK_SELECT_PAIR_CALL(float, true, 1); - } else if (k <= 32) { - BLOCK_SELECT_PAIR_CALL(float, true, 32); - } else if (k <= 64) { - BLOCK_SELECT_PAIR_CALL(float, true, 64); - } else if (k <= 128) { - BLOCK_SELECT_PAIR_CALL(float, true, 128); - } else if (k <= 256) { - BLOCK_SELECT_PAIR_CALL(float, true, 256); - } else if (k <= 512) { - BLOCK_SELECT_PAIR_CALL(float, true, 512); - } else if (k <= 1024) { - BLOCK_SELECT_PAIR_CALL(float, true, 1024); -#if GPU_MAX_SELECTION_K >= 2048 - } else if (k <= 2048) { - BLOCK_SELECT_PAIR_CALL(float, true, 2048); -#endif - } - } else { - if (k == 1) { - BLOCK_SELECT_PAIR_CALL(float, false, 1); - } else if (k <= 32) { - BLOCK_SELECT_PAIR_CALL(float, false, 32); - } else if (k <= 64) { - BLOCK_SELECT_PAIR_CALL(float, false, 64); - } else if (k <= 128) { - BLOCK_SELECT_PAIR_CALL(float, false, 128); - } else if (k <= 256) { - BLOCK_SELECT_PAIR_CALL(float, false, 256); - } else if (k <= 512) { - BLOCK_SELECT_PAIR_CALL(float, false, 512); - } else if (k <= 1024) { - BLOCK_SELECT_PAIR_CALL(float, false, 1024); -#if GPU_MAX_SELECTION_K >= 2048 - } else if (k <= 2048) { - BLOCK_SELECT_PAIR_CALL(float, false, 2048); -#endif - } - } -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/BlockSelectHalf.cu b/core/src/index/thirdparty/faiss/gpu/utils/BlockSelectHalf.cu deleted file mode 100644 index f6989fc084..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/BlockSelectHalf.cu +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 - -// warp Q to thread Q: -// 1, 1 -// 32, 2 -// 64, 3 -// 128, 3 -// 256, 4 -// 512, 8 -// 1024, 8 -// 2048, 8 - -BLOCK_SELECT_DECL(half, true, 1); -BLOCK_SELECT_DECL(half, true, 32); -BLOCK_SELECT_DECL(half, true, 64); -BLOCK_SELECT_DECL(half, true, 128); -BLOCK_SELECT_DECL(half, true, 256); -BLOCK_SELECT_DECL(half, true, 512); -BLOCK_SELECT_DECL(half, true, 1024); -#if GPU_MAX_SELECTION_K >= 2048 -BLOCK_SELECT_DECL(half, true, 2048); -#endif - -BLOCK_SELECT_DECL(half, false, 1); -BLOCK_SELECT_DECL(half, false, 32); -BLOCK_SELECT_DECL(half, false, 64); -BLOCK_SELECT_DECL(half, false, 128); -BLOCK_SELECT_DECL(half, false, 256); -BLOCK_SELECT_DECL(half, false, 512); -BLOCK_SELECT_DECL(half, false, 1024); -#if GPU_MAX_SELECTION_K >= 2048 -BLOCK_SELECT_DECL(half, false, 2048); -#endif - -void runBlockSelect(Tensor& in, - Tensor& bitset, - Tensor& outK, - Tensor& outV, - bool dir, int k, cudaStream_t stream) { - FAISS_ASSERT(k <= GPU_MAX_SELECTION_K); - - if (dir) { - if (k == 1) { - BLOCK_SELECT_CALL(half, true, 1); - } else if (k <= 32) { - BLOCK_SELECT_CALL(half, true, 32); - } else if (k <= 64) { - BLOCK_SELECT_CALL(half, true, 64); - } else if (k <= 128) { - BLOCK_SELECT_CALL(half, true, 128); - } else if (k <= 256) { - BLOCK_SELECT_CALL(half, true, 256); - } else if (k <= 512) { - BLOCK_SELECT_CALL(half, true, 512); - } else if (k <= 1024) { - BLOCK_SELECT_CALL(half, true, 1024); -#if GPU_MAX_SELECTION_K >= 2048 - } else if (k <= 2048) { - BLOCK_SELECT_CALL(half, true, 2048); -#endif - } - } else { - if (k == 1) { - BLOCK_SELECT_CALL(half, false, 1); - } else if (k <= 32) { - BLOCK_SELECT_CALL(half, false, 32); - } else if (k <= 64) { - BLOCK_SELECT_CALL(half, false, 64); - } else if (k <= 128) { - BLOCK_SELECT_CALL(half, false, 128); - } else if (k <= 256) { - BLOCK_SELECT_CALL(half, false, 256); - } else if (k <= 512) { - BLOCK_SELECT_CALL(half, false, 512); - } else if (k <= 1024) { - BLOCK_SELECT_CALL(half, false, 1024); -#if GPU_MAX_SELECTION_K >= 2048 - } else if (k <= 2048) { - BLOCK_SELECT_CALL(half, false, 2048); -#endif - } - } -} - -void runBlockSelectPair(Tensor& inK, - Tensor& inV, - Tensor& bitset, - Tensor& outK, - Tensor& outV, - bool dir, int k, cudaStream_t stream) { - FAISS_ASSERT(k <= GPU_MAX_SELECTION_K); - - if (dir) { - if (k == 1) { - BLOCK_SELECT_PAIR_CALL(half, true, 1); - } else if (k <= 32) { - BLOCK_SELECT_PAIR_CALL(half, true, 32); - } else if (k <= 64) { - BLOCK_SELECT_PAIR_CALL(half, true, 64); - } else if (k <= 128) { - BLOCK_SELECT_PAIR_CALL(half, true, 128); - } else if (k <= 256) { - BLOCK_SELECT_PAIR_CALL(half, true, 256); - } else if (k <= 512) { - BLOCK_SELECT_PAIR_CALL(half, true, 512); - } else if (k <= 1024) { - BLOCK_SELECT_PAIR_CALL(half, true, 1024); -#if GPU_MAX_SELECTION_K >= 2048 - } else if (k <= 2048) { - BLOCK_SELECT_PAIR_CALL(half, true, 2048); -#endif - } - } else { - if (k == 1) { - BLOCK_SELECT_PAIR_CALL(half, false, 1); - } else if (k <= 32) { - BLOCK_SELECT_PAIR_CALL(half, false, 32); - } else if (k <= 64) { - BLOCK_SELECT_PAIR_CALL(half, false, 64); - } else if (k <= 128) { - BLOCK_SELECT_PAIR_CALL(half, false, 128); - } else if (k <= 256) { - BLOCK_SELECT_PAIR_CALL(half, false, 256); - } else if (k <= 512) { - BLOCK_SELECT_PAIR_CALL(half, false, 512); - } else if (k <= 1024) { - BLOCK_SELECT_PAIR_CALL(half, false, 1024); -#if GPU_MAX_SELECTION_K >= 2048 - } else if (k <= 2048) { - BLOCK_SELECT_PAIR_CALL(half, false, 2048); -#endif - } - } -} - -#endif // FAISS_USE_FLOAT16 - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/BlockSelectImpl.cuh b/core/src/index/thirdparty/faiss/gpu/utils/BlockSelectImpl.cuh deleted file mode 100644 index 4c32b75194..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/BlockSelectImpl.cuh +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include - -#define BLOCK_SELECT_DECL(TYPE, DIR, WARP_Q) \ - extern void runBlockSelect_ ## TYPE ## _ ## DIR ## _ ## WARP_Q ## _( \ - Tensor& in, \ - Tensor& bitset, \ - Tensor& outK, \ - Tensor& outV, \ - bool dir, \ - int k, \ - cudaStream_t stream); \ - \ - extern void runBlockSelectPair_ ## TYPE ## _ ## DIR ## _ ## WARP_Q ## _( \ - Tensor& inK, \ - Tensor& inV, \ - Tensor& bitset, \ - Tensor& outK, \ - Tensor& outV, \ - bool dir, \ - int k, \ - cudaStream_t stream); - -#define BLOCK_SELECT_IMPL(TYPE, DIR, WARP_Q, THREAD_Q) \ - void runBlockSelect_ ## TYPE ## _ ## DIR ## _ ## WARP_Q ## _( \ - Tensor& in, \ - Tensor& bitset, \ - Tensor& outK, \ - Tensor& outV, \ - bool dir, \ - int k, \ - cudaStream_t stream) { \ - FAISS_ASSERT(in.getSize(0) == outK.getSize(0)); \ - FAISS_ASSERT(in.getSize(0) == outV.getSize(0)); \ - FAISS_ASSERT(outK.getSize(1) == k); \ - FAISS_ASSERT(outV.getSize(1) == k); \ - \ - auto grid = dim3(in.getSize(0)); \ - \ - constexpr int kBlockSelectNumThreads = (WARP_Q <= 1024) ? 128 : 64; \ - auto block = dim3(kBlockSelectNumThreads); \ - \ - FAISS_ASSERT(k <= WARP_Q); \ - FAISS_ASSERT(dir == DIR); \ - \ - auto kInit = dir ? Limits::getMin() : Limits::getMax(); \ - auto vInit = -1; \ - \ - if (bitset.getSize(0) == 0) \ - blockSelect \ - <<>>(in, outK, outV, kInit, vInit, k); \ - else \ - blockSelect \ - <<>>(in, bitset, outK, outV, kInit, vInit, k); \ - CUDA_TEST_ERROR(); \ - } \ - \ - void runBlockSelectPair_ ## TYPE ## _ ## DIR ## _ ## WARP_Q ## _( \ - Tensor& inK, \ - Tensor& inV, \ - Tensor& bitset, \ - Tensor& outK, \ - Tensor& outV, \ - bool dir, \ - int k, \ - cudaStream_t stream) { \ - FAISS_ASSERT(inK.isSameSize(inV)); \ - FAISS_ASSERT(outK.isSameSize(outV)); \ - \ - auto grid = dim3(inK.getSize(0)); \ - \ - constexpr int kBlockSelectNumThreads = (WARP_Q <= 1024) ? 128 : 64; \ - auto block = dim3(kBlockSelectNumThreads); \ - \ - FAISS_ASSERT(k <= WARP_Q); \ - FAISS_ASSERT(dir == DIR); \ - \ - auto kInit = dir ? Limits::getMin() : Limits::getMax(); \ - auto vInit = -1; \ - \ - if (bitset.getSize(0) == 0) \ - blockSelectPair \ - <<>>(inK, inV, outK, outV, kInit, vInit, k); \ - else \ - blockSelectPair \ - <<>>(inK, inV, bitset, outK, outV, kInit, vInit, k); \ - CUDA_TEST_ERROR(); \ - } - - -#define BLOCK_SELECT_CALL(TYPE, DIR, WARP_Q) \ - runBlockSelect_ ## TYPE ## _ ## DIR ## _ ## WARP_Q ## _( \ - in, bitset, outK, outV, dir, k, stream) - -#define BLOCK_SELECT_PAIR_CALL(TYPE, DIR, WARP_Q) \ - runBlockSelectPair_ ## TYPE ## _ ## DIR ## _ ## WARP_Q ## _( \ - inK, inV, bitset, outK, outV, dir, k, stream) diff --git a/core/src/index/thirdparty/faiss/gpu/utils/BlockSelectKernel.cuh b/core/src/index/thirdparty/faiss/gpu/utils/BlockSelectKernel.cuh deleted file mode 100644 index f787335cdf..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/BlockSelectKernel.cuh +++ /dev/null @@ -1,259 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -namespace faiss { namespace gpu { - -template -__global__ void blockSelect(Tensor in, - Tensor outK, - Tensor outV, - K initK, - IndexType initV, - int k) { - constexpr int kNumWarps = ThreadsPerBlock / kWarpSize; - - __shared__ K smemK[kNumWarps * NumWarpQ]; - __shared__ IndexType smemV[kNumWarps * NumWarpQ]; - - BlockSelect, - NumWarpQ, NumThreadQ, ThreadsPerBlock> - heap(initK, initV, smemK, smemV, k); - - // Grid is exactly sized to rows available - int row = blockIdx.x; - - int i = threadIdx.x; - K* inStart = in[row][i].data(); - - // Whole warps must participate in the selection - int limit = utils::roundDown(in.getSize(1), kWarpSize); - - for (; i < limit; i += ThreadsPerBlock) { - heap.add(*inStart, (IndexType) i); - inStart += ThreadsPerBlock; - } - - // Handle last remainder fraction of a warp of elements - if (i < in.getSize(1)) { - heap.addThreadQ(*inStart, (IndexType) i); - } - - heap.reduce(); - - for (int i = threadIdx.x; i < k; i += ThreadsPerBlock) { - outK[row][i] = smemK[i]; - outV[row][i] = smemV[i]; - } -} - -template -__global__ void blockSelectPair(Tensor inK, - Tensor inV, - Tensor outK, - Tensor outV, - K initK, - IndexType initV, - int k) { - constexpr int kNumWarps = ThreadsPerBlock / kWarpSize; - - __shared__ K smemK[kNumWarps * NumWarpQ]; - __shared__ IndexType smemV[kNumWarps * NumWarpQ]; - - BlockSelect, - NumWarpQ, NumThreadQ, ThreadsPerBlock> - heap(initK, initV, smemK, smemV, k); - - // Grid is exactly sized to rows available - int row = blockIdx.x; - - int i = threadIdx.x; - K* inKStart = inK[row][i].data(); - IndexType* inVStart = inV[row][i].data(); - - // Whole warps must participate in the selection - int limit = utils::roundDown(inK.getSize(1), kWarpSize); - - for (; i < limit; i += ThreadsPerBlock) { - heap.add(*inKStart, *inVStart); - inKStart += ThreadsPerBlock; - inVStart += ThreadsPerBlock; - } - - // Handle last remainder fraction of a warp of elements - if (i < inK.getSize(1)) { - heap.addThreadQ(*inKStart, *inVStart); - } - - heap.reduce(); - - for (int i = threadIdx.x; i < k; i += ThreadsPerBlock) { - outK[row][i] = smemK[i]; - outV[row][i] = smemV[i]; - } -} - -// Bitset included -template -__global__ void blockSelect(Tensor in, - Tensor bitset, - Tensor outK, - Tensor outV, - K initK, - IndexType initV, - int k) { - constexpr int kNumWarps = ThreadsPerBlock / kWarpSize; - - __shared__ K smemK[kNumWarps * NumWarpQ]; - __shared__ IndexType smemV[kNumWarps * NumWarpQ]; - - BlockSelect, - NumWarpQ, NumThreadQ, ThreadsPerBlock> - heap(initK, initV, smemK, smemV, k); - - // Grid is exactly sized to rows available - int row = blockIdx.x; - - int i = threadIdx.x; - K* inStart = in[row][i].data(); - - // Whole warps must participate in the selection - int limit = utils::roundDown(in.getSize(1), kWarpSize); - - bool bitsetEmpty = (bitset.getSize(0) == 0); - - for (; i < limit; i += ThreadsPerBlock) { - if (bitsetEmpty || (!(bitset[i >> 3] & (0x1 << (i & 0x7))))) { - heap.addThreadQ(*inStart, (IndexType) i); - } - heap.checkThreadQ(); - - inStart += ThreadsPerBlock; - } - - // Handle last remainder fraction of a warp of elements - if (i < in.getSize(1)) { - if (bitsetEmpty || (!(bitset[i >> 3] & (0x1 << (i & 0x7))))) { - heap.addThreadQ(*inStart, (IndexType) i); - } - } - - heap.reduce(); - - for (int i = threadIdx.x; i < k; i += ThreadsPerBlock) { - outK[row][i] = smemK[i]; - outV[row][i] = smemV[i]; - } -} - -template -__global__ void blockSelectPair(Tensor inK, - Tensor inV, - Tensor bitset, - Tensor outK, - Tensor outV, - K initK, - IndexType initV, - int k) { - constexpr int kNumWarps = ThreadsPerBlock / kWarpSize; - - __shared__ K smemK[kNumWarps * NumWarpQ]; - __shared__ IndexType smemV[kNumWarps * NumWarpQ]; - - BlockSelect, - NumWarpQ, NumThreadQ, ThreadsPerBlock> - heap(initK, initV, smemK, smemV, k); - - // Grid is exactly sized to rows available - int row = blockIdx.x; - - int i = threadIdx.x; - K* inKStart = inK[row][i].data(); - IndexType* inVStart = inV[row][i].data(); - - // Whole warps must participate in the selection - int limit = utils::roundDown(inK.getSize(1), kWarpSize); - - bool bitsetEmpty = (bitset.getSize(0) == 0); - - for (; i < limit; i += ThreadsPerBlock) { - if (bitsetEmpty || (!(bitset[i >> 3] & (0x1 << (i & 0x7))))) { - heap.addThreadQ(*inKStart, *inVStart); - } - heap.checkThreadQ(); - - inKStart += ThreadsPerBlock; - inVStart += ThreadsPerBlock; - } - - // Handle last remainder fraction of a warp of elements - if (i < inK.getSize(1)) { - if (bitsetEmpty || (!(bitset[i >> 3] & (0x1 << (i & 0x7))))) { - heap.addThreadQ(*inKStart, *inVStart); - } - } - - heap.reduce(); - - for (int i = threadIdx.x; i < k; i += ThreadsPerBlock) { - outK[row][i] = smemK[i]; - outV[row][i] = smemV[i]; - } -} - -void runBlockSelect(Tensor& in, - Tensor& bitset, - Tensor& outKeys, - Tensor& outIndices, - bool dir, int k, cudaStream_t stream); - -void runBlockSelectPair(Tensor& inKeys, - Tensor& inIndices, - Tensor& bitset, - Tensor& outKeys, - Tensor& outIndices, - bool dir, int k, cudaStream_t stream); - -#ifdef FAISS_USE_FLOAT16 -void runBlockSelect(Tensor& in, - Tensor& bitset, - Tensor& outKeys, - Tensor& outIndices, - bool dir, int k, cudaStream_t stream); - -void runBlockSelectPair(Tensor& inKeys, - Tensor& inIndices, - Tensor& bitset, - Tensor& outKeys, - Tensor& outIndices, - bool dir, int k, cudaStream_t stream); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/Comparators.cuh b/core/src/index/thirdparty/faiss/gpu/utils/Comparators.cuh deleted file mode 100644 index 5abfab6af5..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/Comparators.cuh +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include - -namespace faiss { namespace gpu { - -template -struct Comparator { - __device__ static inline bool lt(T a, T b) { - return a < b; - } - - __device__ static inline bool gt(T a, T b) { - return a > b; - } -}; - -template <> -struct Comparator { - __device__ static inline bool lt(half a, half b) { -#if FAISS_USE_FULL_FLOAT16 - return __hlt(a, b); -#else - return __half2float(a) < __half2float(b); -#endif // FAISS_USE_FULL_FLOAT16 - } - - __device__ static inline bool gt(half a, half b) { -#if FAISS_USE_FULL_FLOAT16 - return __hgt(a, b); -#else - return __half2float(a) > __half2float(b); -#endif // FAISS_USE_FULL_FLOAT16 - } -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/ConversionOperators.cuh b/core/src/index/thirdparty/faiss/gpu/utils/ConversionOperators.cuh deleted file mode 100644 index cf9b74c971..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/ConversionOperators.cuh +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include - -#include -#include -#include - -namespace faiss { namespace gpu { - -// -// Conversion utilities -// - -template -struct Convert { - inline __device__ To operator()(From v) const { - return (To) v; - } -}; - -#ifdef FAISS_USE_FLOAT16 -template <> -struct Convert { - inline __device__ half operator()(float v) const { - return __float2half(v); - } -}; - -template <> -struct Convert { - inline __device__ float operator()(half v) const { - return __half2float(v); - } -}; -#endif - -template -struct ConvertTo { -}; - -template <> -struct ConvertTo { - static inline __device__ float to(float v) { return v; } -#ifdef FAISS_USE_FLOAT16 - static inline __device__ float to(half v) { return __half2float(v); } -#endif -}; - -template <> -struct ConvertTo { - static inline __device__ float2 to(float2 v) { return v; } -#ifdef FAISS_USE_FLOAT16 - static inline __device__ float2 to(half2 v) { return __half22float2(v); } -#endif -}; - -template <> -struct ConvertTo { - static inline __device__ float4 to(float4 v) { return v; } -#ifdef FAISS_USE_FLOAT16 - static inline __device__ float4 to(Half4 v) { return half4ToFloat4(v); } -#endif -}; - -#ifdef FAISS_USE_FLOAT16 -template <> -struct ConvertTo { - static inline __device__ half to(float v) { return __float2half(v); } - static inline __device__ half to(half v) { return v; } -}; -#endif - -#ifdef FAISS_USE_FLOAT16 -template <> -struct ConvertTo { - static inline __device__ half2 to(float2 v) { return __float22half2_rn(v); } - static inline __device__ half2 to(half2 v) { return v; } -}; -#endif - -#ifdef FAISS_USE_FLOAT16 -template <> -struct ConvertTo { - static inline __device__ Half4 to(float4 v) { return float4ToHalf4(v); } - static inline __device__ Half4 to(Half4 v) { return v; } -}; -#endif - -// Tensor conversion -template -void runConvert(const From* in, - To* out, - size_t num, - cudaStream_t stream) { - thrust::transform(thrust::cuda::par.on(stream), - in, in + num, out, Convert()); -} - -template -void convertTensor(cudaStream_t stream, - Tensor& in, - Tensor& out) { - FAISS_ASSERT(in.numElements() == out.numElements()); - - runConvert(in.data(), out.data(), in.numElements(), stream); -} - -template -DeviceTensor convertTensor(GpuResources* res, - cudaStream_t stream, - Tensor& in) { - DeviceTensor out; - - if (res) { - out = std::move(DeviceTensor( - res->getMemoryManagerCurrentDevice(), - in.sizes(), - stream)); - } else { - out = std::move(DeviceTensor(in.sizes())); - } - - convertTensor(stream, in, out); - return out; -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/CopyUtils.cuh b/core/src/index/thirdparty/faiss/gpu/utils/CopyUtils.cuh deleted file mode 100644 index 922ca4ed0e..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/CopyUtils.cuh +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include - -namespace faiss { namespace gpu { - -/// Ensure the memory at `p` is either on the given device, or copy it -/// to the device in a new allocation. -/// If `resources` is provided, then we will perform a temporary -/// memory allocation if needed. Otherwise, we will call cudaMalloc if -/// needed. -template -DeviceTensor toDevice(GpuResources* resources, - int dstDevice, - T* src, - cudaStream_t stream, - std::initializer_list sizes) { - int dev = getDeviceForAddress(src); - - if (dev == dstDevice) { - // On device we expect - return DeviceTensor(src, sizes); - } else { - // On different device or on host - DeviceScope scope(dstDevice); - - Tensor oldT(src, sizes); - - if (resources) { - DeviceTensor newT(resources->getMemoryManager(dstDevice), - sizes, - stream); - - newT.copyFrom(oldT, stream); - return newT; - } else { - DeviceTensor newT(sizes); - - newT.copyFrom(oldT, stream); - return newT; - } - } -} - -/// Copies data to the CPU, if it is not already on the CPU -template -HostTensor toHost(T* src, - cudaStream_t stream, - std::initializer_list sizes) { - int dev = getDeviceForAddress(src); - - if (dev == -1) { - // Already on the CPU, just wrap in a HostTensor that doesn't own this - // memory - return HostTensor(src, sizes); - } else { - HostTensor out(sizes); - Tensor devData(src, sizes); - out.copyFrom(devData, stream); - - return out; - } -} - -/// Copies a device array's allocation to an address, if necessary -template -inline void fromDevice(T* src, T* dst, size_t num, cudaStream_t stream) { - // It is possible that the array already represents memory at `p`, - // in which case no copy is needed - if (src == dst) { - return; - } - - int dev = getDeviceForAddress(dst); - - if (dev == -1) { - CUDA_VERIFY(cudaMemcpyAsync(dst, - src, - num * sizeof(T), - cudaMemcpyDeviceToHost, - stream)); - } else { - CUDA_VERIFY(cudaMemcpyAsync(dst, - src, - num * sizeof(T), - cudaMemcpyDeviceToDevice, - stream)); - } -} - -/// Copies a device array's allocation to an address, if necessary -template -void fromDevice(Tensor& src, T* dst, cudaStream_t stream) { - FAISS_ASSERT(src.isContiguous()); - fromDevice(src.data(), dst, src.numElements(), stream); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/DeviceDefs.cuh b/core/src/index/thirdparty/faiss/gpu/utils/DeviceDefs.cuh deleted file mode 100644 index 89d3dda289..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/DeviceDefs.cuh +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include - -namespace faiss { namespace gpu { - -#ifdef __CUDA_ARCH__ -#if __CUDA_ARCH__ <= 750 -constexpr int kWarpSize = 32; -#else -#error Unknown __CUDA_ARCH__; please define parameters for compute capability -#endif // __CUDA_ARCH__ types -#endif // __CUDA_ARCH__ - -#ifndef __CUDA_ARCH__ -// dummy value for host compiler -constexpr int kWarpSize = 32; -#endif // !__CUDA_ARCH__ - -// This is a memory barrier for intra-warp writes to shared memory. -__forceinline__ __device__ void warpFence() { - -#if CUDA_VERSION >= 9000 - __syncwarp(); -#else - // For the time being, assume synchronicity. - // __threadfence_block(); -#endif -} - -#if CUDA_VERSION > 9000 -// Based on the CUDA version (we assume what version of nvcc/ptxas we were -// compiled with), the register allocation algorithm is much better, so only -// enable the 2048 selection code if we are above 9.0 (9.2 seems to be ok) -#define GPU_MAX_SELECTION_K 2048 -#else -#define GPU_MAX_SELECTION_K 1024 -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/DeviceMemory.cpp b/core/src/index/thirdparty/faiss/gpu/utils/DeviceMemory.cpp deleted file mode 100644 index 2ce721986a..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/DeviceMemory.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include - -namespace faiss { namespace gpu { - -DeviceMemoryReservation::DeviceMemoryReservation() - : state_(nullptr), - device_(0), - data_(nullptr), - size_(0), - stream_(0) { -} - -DeviceMemoryReservation::DeviceMemoryReservation(DeviceMemory* state, - int device, - void* p, - size_t size, - cudaStream_t stream) - : state_(state), - device_(device), - data_(p), - size_(size), - stream_(stream) { -} - -DeviceMemoryReservation::DeviceMemoryReservation( - DeviceMemoryReservation&& m) noexcept { - - state_ = m.state_; - device_ = m.device_; - data_ = m.data_; - size_ = m.size_; - stream_ = m.stream_; - - m.data_ = nullptr; -} - -DeviceMemoryReservation::~DeviceMemoryReservation() { - if (data_) { - FAISS_ASSERT(state_); - state_->returnAllocation(*this); - } - - data_ = nullptr; -} - -DeviceMemoryReservation& -DeviceMemoryReservation::operator=(DeviceMemoryReservation&& m) { - if (data_) { - FAISS_ASSERT(state_); - state_->returnAllocation(*this); - } - - state_ = m.state_; - device_ = m.device_; - data_ = m.data_; - size_ = m.size_; - stream_ = m.stream_; - - m.data_ = nullptr; - - return *this; -} - -DeviceMemory::~DeviceMemory() { -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/DeviceMemory.h b/core/src/index/thirdparty/faiss/gpu/utils/DeviceMemory.h deleted file mode 100644 index 1bffdc00ac..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/DeviceMemory.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include - -namespace faiss { namespace gpu { - -class DeviceMemory; - -class DeviceMemoryReservation { - public: - DeviceMemoryReservation(); - DeviceMemoryReservation(DeviceMemory* state, - int device, void* p, size_t size, - cudaStream_t stream); - DeviceMemoryReservation(DeviceMemoryReservation&& m) noexcept; - ~DeviceMemoryReservation(); - - DeviceMemoryReservation& operator=(DeviceMemoryReservation&& m); - - int device() { return device_; } - void* get() { return data_; } - size_t size() { return size_; } - cudaStream_t stream() { return stream_; } - - private: - DeviceMemory* state_; - - int device_; - void* data_; - size_t size_; - cudaStream_t stream_; -}; - -/// Manages temporary memory allocations on a GPU device -class DeviceMemory { - public: - virtual ~DeviceMemory(); - - /// Returns the device we are managing memory for - virtual int getDevice() const = 0; - - /// Obtains a temporary memory allocation for our device, - /// whose usage is ordered with respect to the given stream. - virtual DeviceMemoryReservation getMemory(cudaStream_t stream, - size_t size) = 0; - - /// Returns the current size available without calling cudaMalloc - virtual size_t getSizeAvailable() const = 0; - - /// Returns a string containing our current memory manager state - virtual std::string toString() const = 0; - - /// Returns the high-water mark of cudaMalloc allocations for our - /// device - virtual size_t getHighWaterCudaMalloc() const = 0; - - protected: - friend class DeviceMemoryReservation; - virtual void returnAllocation(DeviceMemoryReservation& m) = 0; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/DeviceTensor-inl.cuh b/core/src/index/thirdparty/faiss/gpu/utils/DeviceTensor-inl.cuh deleted file mode 100644 index cff5452989..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/DeviceTensor-inl.cuh +++ /dev/null @@ -1,228 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include // std::move - -namespace faiss { namespace gpu { - -template class PtrTraits> -__host__ -DeviceTensor::DeviceTensor() : - Tensor(), - state_(AllocState::NotOwner), - space_(MemorySpace::Device) { -} - -template class PtrTraits> -__host__ -DeviceTensor::DeviceTensor( - DeviceTensor&& t) : - Tensor(), - state_(AllocState::NotOwner), - space_(MemorySpace::Device) { - this->operator=(std::move(t)); -} - -template class PtrTraits> -__host__ -DeviceTensor& -DeviceTensor::operator=( - DeviceTensor&& t) { - if (this->state_ == AllocState::Owner) { - CUDA_VERIFY(cudaFree(this->data_)); - } - - this->Tensor::operator=( - std::move(t)); - - this->state_ = t.state_; t.state_ = AllocState::NotOwner; - this->space_ = t.space_; - this->reservation_ = std::move(t.reservation_); - - return *this; -} - -template class PtrTraits> -__host__ -DeviceTensor::~DeviceTensor() { - if (state_ == AllocState::Owner) { - FAISS_ASSERT(this->data_ || (this->getSizeInBytes() == 0)); - CUDA_VERIFY(cudaFree(this->data_)); - this->data_ = nullptr; - } - - // Otherwise, if we have a temporary memory reservation, then its - // destructor will return the reservation -} - -template class PtrTraits> -__host__ -DeviceTensor::DeviceTensor( - const IndexT sizes[Dim], - MemorySpace space) : - Tensor(nullptr, sizes), - state_(AllocState::Owner), - space_(space) { - - allocMemorySpace(space, &this->data_, this->getSizeInBytes()); - FAISS_ASSERT(this->data_ || (this->getSizeInBytes() == 0)); -} - -template class PtrTraits> -__host__ -DeviceTensor::DeviceTensor( - std::initializer_list sizes, - MemorySpace space) : - Tensor(nullptr, sizes), - state_(AllocState::Owner), - space_(space) { - - allocMemorySpace(space, &this->data_, this->getSizeInBytes()); - FAISS_ASSERT(this->data_ || (this->getSizeInBytes() == 0)); -} - -// memory reservation constructor -template class PtrTraits> -__host__ -DeviceTensor::DeviceTensor( - DeviceMemory& m, - const IndexT sizes[Dim], - cudaStream_t stream, - MemorySpace space) : - Tensor(nullptr, sizes), - state_(AllocState::Reservation), - space_(space) { - - // FIXME: add MemorySpace to DeviceMemory - auto memory = m.getMemory(stream, this->getSizeInBytes()); - - this->data_ = (T*) memory.get(); - FAISS_ASSERT(this->data_ || (this->getSizeInBytes() == 0)); - reservation_ = std::move(memory); -} - -// memory reservation constructor -template class PtrTraits> -__host__ -DeviceTensor::DeviceTensor( - DeviceMemory& m, - std::initializer_list sizes, - cudaStream_t stream, - MemorySpace space) : - Tensor(nullptr, sizes), - state_(AllocState::Reservation), - space_(space) { - - // FIXME: add MemorySpace to DeviceMemory - auto memory = m.getMemory(stream, this->getSizeInBytes()); - - this->data_ = (T*) memory.get(); - FAISS_ASSERT(this->data_ || (this->getSizeInBytes() == 0)); - reservation_ = std::move(memory); -} - -template class PtrTraits> -__host__ -DeviceTensor::DeviceTensor( - DataPtrType data, - const IndexT sizes[Dim], - MemorySpace space) : - Tensor(data, sizes), - state_(AllocState::NotOwner), - space_(space) { -} - -template class PtrTraits> -__host__ -DeviceTensor::DeviceTensor( - DataPtrType data, - std::initializer_list sizes, - MemorySpace space) : - Tensor(data, sizes), - state_(AllocState::NotOwner), - space_(space) { -} - -template class PtrTraits> -__host__ -DeviceTensor::DeviceTensor( - DataPtrType data, - const IndexT sizes[Dim], - const IndexT strides[Dim], - MemorySpace space) : - Tensor(data, sizes, strides), - state_(AllocState::NotOwner), - space_(space) { -} - -template class PtrTraits> -__host__ -DeviceTensor::DeviceTensor( - Tensor& t, - cudaStream_t stream, - MemorySpace space) : - Tensor(nullptr, t.sizes(), t.strides()), - state_(AllocState::Owner), - space_(space) { - - allocMemorySpace(space_, &this->data_, this->getSizeInBytes()); - FAISS_ASSERT(this->data_ || (this->getSizeInBytes() == 0)); - this->copyFrom(t, stream); -} - -template class PtrTraits> -__host__ -DeviceTensor::DeviceTensor( - DeviceMemory& m, - Tensor& t, - cudaStream_t stream, - MemorySpace space) : - Tensor(nullptr, t.sizes(), t.strides()), - state_(AllocState::Reservation), - space_(space) { - - // FIXME: add MemorySpace to DeviceMemory - auto memory = m.getMemory(stream, this->getSizeInBytes()); - - this->data_ = (T*) memory.get(); - FAISS_ASSERT(this->data_ || (this->getSizeInBytes() == 0)); - reservation_ = std::move(memory); - - this->copyFrom(t, stream); -} - -template class PtrTraits> -__host__ DeviceTensor& -DeviceTensor::zero( - cudaStream_t stream) { - if (this->data_) { - // Region must be contiguous - FAISS_ASSERT(this->isContiguous()); - - CUDA_VERIFY(cudaMemsetAsync( - this->data_, 0, this->getSizeInBytes(), stream)); - } - - return *this; -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/DeviceTensor.cuh b/core/src/index/thirdparty/faiss/gpu/utils/DeviceTensor.cuh deleted file mode 100644 index 78039969c5..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/DeviceTensor.cuh +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include - -namespace faiss { namespace gpu { - -template class PtrTraits = traits::DefaultPtrTraits> -class DeviceTensor : public Tensor { - public: - typedef IndexT IndexType; - typedef typename PtrTraits::PtrType DataPtrType; - - /// Default constructor - __host__ DeviceTensor(); - - /// Destructor - __host__ ~DeviceTensor(); - - /// Move constructor - __host__ DeviceTensor(DeviceTensor&& t); - - /// Move assignment - __host__ DeviceTensor& - operator=(DeviceTensor&& t); - - /// Constructs a tensor of the given size, allocating memory for it - /// locally - __host__ DeviceTensor(const IndexT sizes[Dim], - MemorySpace space = MemorySpace::Device); - __host__ DeviceTensor(std::initializer_list sizes, - MemorySpace space = MemorySpace::Device); - - /// Constructs a tensor of the given size, reserving a temporary - /// memory reservation via a memory manager. - /// The memory reservation should be ordered with respect to the - /// given stream. - __host__ DeviceTensor(DeviceMemory& m, - const IndexT sizes[Dim], - cudaStream_t stream, - MemorySpace space = MemorySpace::Device); - __host__ DeviceTensor(DeviceMemory& m, - std::initializer_list sizes, - cudaStream_t stream, - MemorySpace space = MemorySpace::Device); - - /// Constructs a tensor of the given size and stride, referencing a - /// memory region we do not own - __host__ DeviceTensor(DataPtrType data, - const IndexT sizes[Dim], - MemorySpace space = MemorySpace::Device); - __host__ DeviceTensor(DataPtrType data, - std::initializer_list sizes, - MemorySpace space = MemorySpace::Device); - - /// Constructs a tensor of the given size and stride, referencing a - /// memory region we do not own - __host__ DeviceTensor(DataPtrType data, - const IndexT sizes[Dim], - const IndexT strides[Dim], - MemorySpace space = MemorySpace::Device); - - /// Copies a tensor into ourselves, allocating memory for it locally - __host__ DeviceTensor(Tensor& t, - cudaStream_t stream, - MemorySpace space = MemorySpace::Device); - - /// Copies a tensor into ourselves, reserving a temporary - /// memory reservation via a memory manager. - __host__ DeviceTensor(DeviceMemory& m, - Tensor& t, - cudaStream_t stream, - MemorySpace space = MemorySpace::Device); - - /// Call to zero out memory - __host__ DeviceTensor& - zero(cudaStream_t stream); - - private: - enum AllocState { - /// This tensor itself owns the memory, which must be freed via - /// cudaFree - Owner, - - /// This tensor itself is not an owner of the memory; there is - /// nothing to free - NotOwner, - - /// This tensor has the memory via a temporary memory reservation - Reservation - }; - - AllocState state_; - MemorySpace space_; - DeviceMemoryReservation reservation_; -}; - -} } // namespace - -#include diff --git a/core/src/index/thirdparty/faiss/gpu/utils/DeviceUtils.cu b/core/src/index/thirdparty/faiss/gpu/utils/DeviceUtils.cu deleted file mode 100644 index a8195c9ca6..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/DeviceUtils.cu +++ /dev/null @@ -1,206 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -int getCurrentDevice() { - int dev = -1; - CUDA_VERIFY(cudaGetDevice(&dev)); - FAISS_ASSERT(dev != -1); - - return dev; -} - -void setCurrentDevice(int device) { - CUDA_VERIFY(cudaSetDevice(device)); -} - -int getNumDevices() { - int numDev = -1; - cudaError_t err = cudaGetDeviceCount(&numDev); - if (cudaErrorNoDevice == err) { - numDev = 0; - } else { - CUDA_VERIFY(err); - } - FAISS_ASSERT(numDev != -1); - - return numDev; -} - -void profilerStart() { - CUDA_VERIFY(cudaProfilerStart()); -} - -void profilerStop() { - CUDA_VERIFY(cudaProfilerStop()); -} - -void synchronizeAllDevices() { - for (int i = 0; i < getNumDevices(); ++i) { - DeviceScope scope(i); - - CUDA_VERIFY(cudaDeviceSynchronize()); - } -} - -const cudaDeviceProp& getDeviceProperties(int device) { - static std::mutex mutex; - static std::unordered_map properties; - - std::lock_guard guard(mutex); - - auto it = properties.find(device); - if (it == properties.end()) { - cudaDeviceProp prop; - CUDA_VERIFY(cudaGetDeviceProperties(&prop, device)); - - properties[device] = prop; - it = properties.find(device); - } - - return it->second; -} - -const cudaDeviceProp& getCurrentDeviceProperties() { - return getDeviceProperties(getCurrentDevice()); -} - -int getMaxThreads(int device) { - return getDeviceProperties(device).maxThreadsPerBlock; -} - -int getMaxThreadsCurrentDevice() { - return getMaxThreads(getCurrentDevice()); -} - -size_t getMaxSharedMemPerBlock(int device) { - return getDeviceProperties(device).sharedMemPerBlock; -} - -size_t getMaxSharedMemPerBlockCurrentDevice() { - return getMaxSharedMemPerBlock(getCurrentDevice()); -} - -int getDeviceForAddress(const void* p) { - if (!p) { - return -1; - } - - cudaPointerAttributes att; - cudaError_t err = cudaPointerGetAttributes(&att, p); - FAISS_ASSERT_FMT(err == cudaSuccess || - err == cudaErrorInvalidValue, - "unknown error %d", (int) err); - - if (err == cudaErrorInvalidValue) { - // Make sure the current thread error status has been reset - err = cudaGetLastError(); - FAISS_ASSERT_FMT(err == cudaErrorInvalidValue, - "unknown error %d", (int) err); - return -1; - } else if (att.memoryType == cudaMemoryTypeHost) { - return -1; - } else { - return att.device; - } -} - -bool getFullUnifiedMemSupport(int device) { - const auto& prop = getDeviceProperties(device); - return (prop.major >= 6); -} - -bool getFullUnifiedMemSupportCurrentDevice() { - return getFullUnifiedMemSupport(getCurrentDevice()); -} - -bool getTensorCoreSupport(int device) { - const auto& prop = getDeviceProperties(device); - return (prop.major >= 7); -} - -bool getTensorCoreSupportCurrentDevice() { - return getTensorCoreSupport(getCurrentDevice()); -} - -int getMaxKSelection() { - // Don't use the device at the moment, just base this based on the CUDA SDK - // that we were compiled with - return GPU_MAX_SELECTION_K; -} - -DeviceScope::DeviceScope(int device) { - prevDevice_ = getCurrentDevice(); - - if (prevDevice_ != device) { - setCurrentDevice(device); - } else { - prevDevice_ = -1; - } -} - -DeviceScope::~DeviceScope() { - if (prevDevice_ != -1) { - setCurrentDevice(prevDevice_); - } -} - -CublasHandleScope::CublasHandleScope() { - auto blasStatus = cublasCreate(&blasHandle_); - FAISS_ASSERT(blasStatus == CUBLAS_STATUS_SUCCESS); -} - -CublasHandleScope::~CublasHandleScope() { - auto blasStatus = cublasDestroy(blasHandle_); - FAISS_ASSERT(blasStatus == CUBLAS_STATUS_SUCCESS); -} - -CudaEvent::CudaEvent(cudaStream_t stream) - : event_(0) { - CUDA_VERIFY(cudaEventCreateWithFlags(&event_, cudaEventDisableTiming)); - CUDA_VERIFY(cudaEventRecord(event_, stream)); -} - -CudaEvent::CudaEvent(CudaEvent&& event) noexcept - : event_(std::move(event.event_)) { - event.event_ = 0; -} - -CudaEvent::~CudaEvent() { - if (event_) { - CUDA_VERIFY(cudaEventDestroy(event_)); - } -} - -CudaEvent& -CudaEvent::operator=(CudaEvent&& event) noexcept { - event_ = std::move(event.event_); - event.event_ = 0; - - return *this; -} - -void -CudaEvent::streamWaitOnEvent(cudaStream_t stream) { - CUDA_VERIFY(cudaStreamWaitEvent(stream, event_, 0)); -} - -void -CudaEvent::cpuWaitOnEvent() { - CUDA_VERIFY(cudaEventSynchronize(event_)); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/DeviceUtils.h b/core/src/index/thirdparty/faiss/gpu/utils/DeviceUtils.h deleted file mode 100644 index e9b5426ae4..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/DeviceUtils.h +++ /dev/null @@ -1,191 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -/// Returns the current thread-local GPU device -int getCurrentDevice(); - -/// Sets the current thread-local GPU device -void setCurrentDevice(int device); - -/// Returns the number of available GPU devices -int getNumDevices(); - -/// Starts the CUDA profiler (exposed via SWIG) -void profilerStart(); - -/// Stops the CUDA profiler (exposed via SWIG) -void profilerStop(); - -/// Synchronizes the CPU against all devices (equivalent to -/// cudaDeviceSynchronize for each device) -void synchronizeAllDevices(); - -/// Returns a cached cudaDeviceProp for the given device -const cudaDeviceProp& getDeviceProperties(int device); - -/// Returns the cached cudaDeviceProp for the current device -const cudaDeviceProp& getCurrentDeviceProperties(); - -/// Returns the maximum number of threads available for the given GPU -/// device -int getMaxThreads(int device); - -/// Equivalent to getMaxThreads(getCurrentDevice()) -int getMaxThreadsCurrentDevice(); - -/// Returns the maximum smem available for the given GPU device -size_t getMaxSharedMemPerBlock(int device); - -/// Equivalent to getMaxSharedMemPerBlock(getCurrentDevice()) -size_t getMaxSharedMemPerBlockCurrentDevice(); - -/// For a given pointer, returns whether or not it is located on -/// a device (deviceId >= 0) or the host (-1). -int getDeviceForAddress(const void* p); - -/// Does the given device support full unified memory sharing host -/// memory? -bool getFullUnifiedMemSupport(int device); - -/// Equivalent to getFullUnifiedMemSupport(getCurrentDevice()) -bool getFullUnifiedMemSupportCurrentDevice(); - -/// Does the given device support tensor core operations? -bool getTensorCoreSupport(int device); - -/// Equivalent to getTensorCoreSupport(getCurrentDevice()) -bool getTensorCoreSupportCurrentDevice(); - -/// Returns the maximum k-selection value supported based on the CUDA SDK that -/// we were compiled with. .cu files can use DeviceDefs.cuh, but this is for -/// non-CUDA files -int getMaxKSelection(); - -/// RAII object to set the current device, and restore the previous -/// device upon destruction -class DeviceScope { - public: - explicit DeviceScope(int device); - ~DeviceScope(); - - private: - int prevDevice_; -}; - -/// RAII object to manage a cublasHandle_t -class CublasHandleScope { - public: - CublasHandleScope(); - ~CublasHandleScope(); - - cublasHandle_t get() { return blasHandle_; } - - private: - cublasHandle_t blasHandle_; -}; - -// RAII object to manage a cudaEvent_t -class CudaEvent { - public: - /// Creates an event and records it in this stream - explicit CudaEvent(cudaStream_t stream); - CudaEvent(const CudaEvent& event) = delete; - CudaEvent(CudaEvent&& event) noexcept; - ~CudaEvent(); - - inline cudaEvent_t get() { return event_; } - - /// Wait on this event in this stream - void streamWaitOnEvent(cudaStream_t stream); - - /// Have the CPU wait for the completion of this event - void cpuWaitOnEvent(); - - CudaEvent& operator=(CudaEvent&& event) noexcept; - CudaEvent& operator=(CudaEvent& event) = delete; - - private: - cudaEvent_t event_; -}; - -/// Wrapper to test return status of CUDA functions -#define CUDA_VERIFY(X) \ - do { \ - auto err__ = (X); \ - FAISS_ASSERT_FMT(err__ == cudaSuccess, "CUDA error %d %s", \ - (int) err__, cudaGetErrorString(err__)); \ - } while (0) - -/// Wrapper to synchronously probe for CUDA errors -// #define FAISS_GPU_SYNC_ERROR 1 - -#ifdef FAISS_GPU_SYNC_ERROR -#define CUDA_TEST_ERROR() \ - do { \ - CUDA_VERIFY(cudaDeviceSynchronize()); \ - } while (0) -#else -#define CUDA_TEST_ERROR() \ - do { \ - CUDA_VERIFY(cudaGetLastError()); \ - } while (0) -#endif - -/// Call for a collection of streams to wait on -template -void streamWaitBase(const L1& listWaiting, const L2& listWaitOn) { - // For all the streams we are waiting on, create an event - std::vector events; - for (auto& stream : listWaitOn) { - cudaEvent_t event; - CUDA_VERIFY(cudaEventCreateWithFlags(&event, cudaEventDisableTiming)); - CUDA_VERIFY(cudaEventRecord(event, stream)); - events.push_back(event); - } - - // For all the streams that are waiting, issue a wait - for (auto& stream : listWaiting) { - for (auto& event : events) { - CUDA_VERIFY(cudaStreamWaitEvent(stream, event, 0)); - } - } - - for (auto& event : events) { - CUDA_VERIFY(cudaEventDestroy(event)); - } -} - -/// These versions allow usage of initializer_list as arguments, since -/// otherwise {...} doesn't have a type -template -void streamWait(const L1& a, - const std::initializer_list& b) { - streamWaitBase(a, b); -} - -template -void streamWait(const std::initializer_list& a, - const L2& b) { - streamWaitBase(a, b); -} - -inline void streamWait(const std::initializer_list& a, - const std::initializer_list& b) { - streamWaitBase(a, b); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/DeviceVector.cuh b/core/src/index/thirdparty/faiss/gpu/utils/DeviceVector.cuh deleted file mode 100644 index dac73679fd..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/DeviceVector.cuh +++ /dev/null @@ -1,191 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -/// A simple version of thrust::device_vector, but has more control -/// over whether resize() initializes new space with T() (which we -/// don't want), and control on how much the reserved space grows by -/// upon resize/reserve. It is also meant for POD types only. -template -class DeviceVector { - public: - DeviceVector(MemorySpace space = MemorySpace::Device) - : data_(nullptr), - num_(0), - capacity_(0), - owner(true), - space_(space) { - } - - ~DeviceVector() { - clear(); - } - - void reset(T* data, size_t num, size_t capacity, MemorySpace space = MemorySpace::Device) { - FAISS_ASSERT(data != nullptr); - FAISS_ASSERT(capacity >= num); - clear(); - owner = false; - data_ = data; - num_ = num; - capacity_ = capacity_; - } - - // Clear all allocated memory; reset to zero size - void clear() { - if (owner) { - freeMemorySpace(space_, data_); - } - data_ = nullptr; - num_ = 0; - capacity_ = 0; - owner = true; - } - - size_t size() const { return num_; } - size_t capacity() const { return capacity_; } - T* data() { return data_; } - const T* data() const { return data_; } - - template - std::vector copyToHost(cudaStream_t stream) const { - FAISS_ASSERT(num_ * sizeof(T) % sizeof(OutT) == 0); - - std::vector out((num_ * sizeof(T)) / sizeof(OutT)); - CUDA_VERIFY(cudaMemcpyAsync(out.data(), data_, num_ * sizeof(T), - cudaMemcpyDeviceToHost, stream)); - - return out; - } - - // Returns true if we actually reallocated memory - // If `reserveExact` is true, then we reserve only the memory that - // we need for what we're appending - bool append(const T* d, - size_t n, - cudaStream_t stream, - bool reserveExact = false) { - bool mem = false; - - if (n > 0) { - size_t reserveSize = num_ + n; - if (!reserveExact) { - reserveSize = getNewCapacity_(reserveSize); - } - - mem = reserve(reserveSize, stream); - - int dev = getDeviceForAddress(d); - if (dev == -1) { - CUDA_VERIFY(cudaMemcpyAsync(data_ + num_, d, n * sizeof(T), - cudaMemcpyHostToDevice, stream)); - } else { - CUDA_VERIFY(cudaMemcpyAsync(data_ + num_, d, n * sizeof(T), - cudaMemcpyDeviceToDevice, stream)); - } - num_ += n; - } - - return mem; - } - - // Returns true if we actually reallocated memory - bool resize(size_t newSize, cudaStream_t stream) { - bool mem = false; - - if (num_ < newSize) { - mem = reserve(getNewCapacity_(newSize), stream); - } - - // Don't bother zero initializing the newly accessible memory - // (unlike thrust::device_vector) - num_ = newSize; - - return mem; - } - - // Clean up after oversized allocations, while leaving some space to - // remain for subsequent allocations (if `exact` false) or to - // exactly the space we need (if `exact` true); returns space - // reclaimed in bytes - size_t reclaim(bool exact, cudaStream_t stream) { - size_t free = capacity_ - num_; - - if (exact) { - realloc_(num_, stream); - return free * sizeof(T); - } - - // If more than 1/4th of the space is free, then we want to - // truncate to only having 1/8th of the space free; this still - // preserves some space for new elements, but won't force us to - // double our size right away - if (free > (capacity_ / 4)) { - size_t newFree = capacity_ / 8; - size_t newCapacity = num_ + newFree; - - size_t oldCapacity = capacity_; - FAISS_ASSERT(newCapacity < oldCapacity); - - realloc_(newCapacity, stream); - - return (oldCapacity - newCapacity) * sizeof(T); - } - - return 0; - } - - // Returns true if we actually reallocated memory - bool reserve(size_t newCapacity, cudaStream_t stream) { - if (newCapacity <= capacity_) { - return false; - } - - // Otherwise, we need new space. - realloc_(newCapacity, stream); - return true; - } - - private: - void realloc_(size_t newCapacity, cudaStream_t stream) { - FAISS_ASSERT(num_ <= newCapacity); - FAISS_ASSERT_MSG(owner, "Cannot realloc due to no ownership of mem"); - - T* newData = nullptr; - allocMemorySpace(space_, &newData, newCapacity * sizeof(T)); - CUDA_VERIFY(cudaMemcpyAsync(newData, data_, num_ * sizeof(T), - cudaMemcpyDeviceToDevice, stream)); - freeMemorySpace(space_, data_); - - data_ = newData; - capacity_ = newCapacity; - } - - size_t getNewCapacity_(size_t preferredSize) { - return utils::nextHighestPowerOf2(preferredSize); - } - - T* data_; - size_t num_; - size_t capacity_; - MemorySpace space_; - bool owner = true; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/Float16.cu b/core/src/index/thirdparty/faiss/gpu/utils/Float16.cu deleted file mode 100644 index e1f5c09b9f..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/Float16.cu +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -//#include -#include -#include -#include - -#ifdef FAISS_USE_FLOAT16 - -namespace faiss { namespace gpu { - -bool getDeviceSupportsFloat16Math(int device) { - const auto& prop = getDeviceProperties(device); - - return (prop.major >= 6 || - (prop.major == 5 && prop.minor >= 3)); -} - -__half hostFloat2Half(float a) { -#if CUDA_VERSION >= 9000 - __half_raw raw; - //raw.x = cpu_float2half_rn(a).x; - FAISS_ASSERT_FMT(false, "%s", "cpu_float2half_rn() not support"); - return __half(raw); -#else - __half h; - //h.x = cpu_float2half_rn(a).x; - FAISS_ASSERT_FMT(false, "%s", "cpu_float2half_rn() not support"); - return h; -#endif -} - -} } // namespace - -#endif // FAISS_USE_FLOAT16 diff --git a/core/src/index/thirdparty/faiss/gpu/utils/Float16.cuh b/core/src/index/thirdparty/faiss/gpu/utils/Float16.cuh deleted file mode 100644 index 0af798ba80..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/Float16.cuh +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include - -// We require at least CUDA 8.0 for compilation -#if CUDA_VERSION < 8000 -#error "CUDA >= 8.0 is required" -#endif - -// Some compute capabilities have full float16 ALUs. -#if __CUDA_ARCH__ >= 530 -#define FAISS_USE_FULL_FLOAT16 1 -#endif // __CUDA_ARCH__ types - -#ifdef FAISS_USE_FLOAT16 -#include -#endif - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 - -// 64 bytes containing 4 half (float16) values -struct Half4 { - half2 a; - half2 b; -}; - -inline __device__ float4 half4ToFloat4(Half4 v) { - float2 a = __half22float2(v.a); - float2 b = __half22float2(v.b); - - float4 out; - out.x = a.x; - out.y = a.y; - out.z = b.x; - out.w = b.y; - - return out; -} - -inline __device__ Half4 float4ToHalf4(float4 v) { - float2 a; - a.x = v.x; - a.y = v.y; - - float2 b; - b.x = v.z; - b.y = v.w; - - Half4 out; - out.a = __float22half2_rn(a); - out.b = __float22half2_rn(b); - - return out; -} - -// 128 bytes containing 8 half (float16) values -struct Half8 { - Half4 a; - Half4 b; -}; - -/// Returns true if the given device supports native float16 math -bool getDeviceSupportsFloat16Math(int device); - -__half hostFloat2Half(float v); - -#endif // FAISS_USE_FLOAT16 - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/HostTensor-inl.cuh b/core/src/index/thirdparty/faiss/gpu/utils/HostTensor-inl.cuh deleted file mode 100644 index 37149fc936..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/HostTensor-inl.cuh +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -namespace faiss { namespace gpu { - -template class PtrTraits> -__host__ -HostTensor::HostTensor() : - Tensor(), - state_(AllocState::NotOwner) { -} - -template class PtrTraits> -__host__ -HostTensor::~HostTensor() { - if (state_ == AllocState::Owner) { - FAISS_ASSERT(this->data_ != nullptr); - delete[] this->data_; - this->data_ = nullptr; - } -} - -template class PtrTraits> -__host__ -HostTensor::HostTensor( - HostTensor&& t) : - Tensor(), - state_(AllocState::NotOwner) { - this->operator=(std::move(t)); -} - -template class PtrTraits> -__host__ -HostTensor& -HostTensor::operator=( - HostTensor&& t) { - if (this->state_ == AllocState::Owner) { - FAISS_ASSERT(this->data_ != nullptr); - delete[] this->data_; - this->data_ = nullptr; - } - - this->Tensor::operator=( - std::move(t)); - - this->state_ = t.state_; t.state_ = AllocState::NotOwner; - - return *this; -} - -template class PtrTraits> -__host__ -HostTensor::HostTensor( - const IndexT sizes[Dim]) : - Tensor(nullptr, sizes), - state_(AllocState::Owner) { - - this->data_ = new T[this->numElements()]; - FAISS_ASSERT(this->data_ != nullptr); -} - -template class PtrTraits> -__host__ -HostTensor::HostTensor( - std::initializer_list sizes) : - Tensor(nullptr, sizes), - state_(AllocState::Owner) { - this->data_ = new T[this->numElements()]; - FAISS_ASSERT(this->data_ != nullptr); -} - -template class PtrTraits> -__host__ -HostTensor::HostTensor( - DataPtrType data, - const IndexT sizes[Dim]) : - Tensor(data, sizes), - state_(AllocState::NotOwner) { -} - -template class PtrTraits> -__host__ -HostTensor::HostTensor( - DataPtrType data, - std::initializer_list sizes) : - Tensor(data, sizes), - state_(AllocState::NotOwner) { -} - -template class PtrTraits> -__host__ -HostTensor::HostTensor( - DataPtrType data, - const IndexT sizes[Dim], - const IndexT strides[Dim]) : - Tensor(data, sizes, strides), - state_(AllocState::NotOwner) { -} - -template class PtrTraits> -__host__ -HostTensor::HostTensor( - Tensor& t, - cudaStream_t stream) : - Tensor(nullptr, t.sizes(), t.strides()), - state_(AllocState::Owner) { - // Only contiguous arrays handled for now - FAISS_ASSERT(t.isContiguous()); - - this->data_ = new T[t.numElements()]; - this->copyFrom(t, stream); -} - -/// Call to zero out memory -template class PtrTraits> -__host__ HostTensor& -HostTensor::zero() { - // Region must be contiguous - FAISS_ASSERT(this->isContiguous()); - - if (this->data_ != nullptr) { - memset(this->data_, 0, this->getSizeInBytes()); - } - - return *this; -} - -template class PtrTraits> -__host__ T -HostTensor::maxDiff( - const HostTensor& t) const { - auto size = this->numElements(); - - FAISS_ASSERT(size == t.numElements()); - FAISS_ASSERT(size > 0); - - if (InnerContig) { - auto a = this->data(); - auto b = t.data(); - - T maxDiff = a[0] - b[0]; - // FIXME: type-specific abs() - maxDiff = maxDiff < 0 ? maxDiff * (T) -1 : maxDiff; - - for (IndexT i = 1; i < size; ++i) { - auto diff = a[i] - b[i]; - // FIXME: type-specific abs - diff = diff < 0 ? diff * (T) -1 : diff; - if (diff > maxDiff) { - maxDiff = diff; - } - } - - return maxDiff; - } else { - // non-contiguous - // FIXME - FAISS_ASSERT(false); - return (T) 0; - } -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/HostTensor.cuh b/core/src/index/thirdparty/faiss/gpu/utils/HostTensor.cuh deleted file mode 100644 index 5b8758a8ce..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/HostTensor.cuh +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include - -namespace faiss { namespace gpu { - -template class PtrTraits = traits::DefaultPtrTraits> -class HostTensor : public Tensor { - public: - typedef IndexT IndexType; - typedef typename PtrTraits::PtrType DataPtrType; - - /// Default constructor - __host__ HostTensor(); - - /// Destructor - __host__ ~HostTensor(); - - /// Move constructor - __host__ HostTensor(HostTensor&& t); - - /// Move assignment - __host__ HostTensor& - operator=(HostTensor&& t); - - /// Constructs a tensor of the given size, allocating memory for it - /// locally - __host__ HostTensor(const IndexT sizes[Dim]); - __host__ HostTensor(std::initializer_list sizes); - - /// Constructs a tensor of the given size and stride, referencing a - /// memory region we do not own - __host__ HostTensor(DataPtrType data, - const IndexT sizes[Dim]); - __host__ HostTensor(DataPtrType data, - std::initializer_list sizes); - - /// Constructs a tensor of the given size and stride, referencing a - /// memory region we do not own - __host__ HostTensor(DataPtrType data, - const IndexT sizes[Dim], - const IndexT strides[Dim]); - - /// Copies a tensor into ourselves, allocating memory for it - /// locally. If the tensor is on the GPU, then we will copy it to - /// ourselves wrt the given stream. - __host__ HostTensor(Tensor& t, - cudaStream_t stream); - - /// Call to zero out memory - __host__ HostTensor& zero(); - - /// Returns the maximum difference seen between two tensors - __host__ T - maxDiff(const HostTensor& t) const; - - /// Are the two tensors exactly equal? - __host__ bool - equal(const HostTensor& t) const { - return (maxDiff(t) == (T) 0); - } - - private: - enum AllocState { - /// This tensor itself owns the memory, which must be freed via - /// cudaFree - Owner, - - /// This tensor itself is not an owner of the memory; there is - /// nothing to free - NotOwner, - }; - - AllocState state_; -}; - -} } // namespace - -#include diff --git a/core/src/index/thirdparty/faiss/gpu/utils/Limits.cuh b/core/src/index/thirdparty/faiss/gpu/utils/Limits.cuh deleted file mode 100644 index 7dfaa2e2ce..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/Limits.cuh +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include - -namespace faiss { namespace gpu { - -template -struct Limits { -}; - -// Unfortunately we can't use constexpr because there is no -// constexpr constructor for half -// FIXME: faiss CPU uses +/-FLT_MAX instead of +/-infinity -constexpr float kFloatMax = std::numeric_limits::max(); -constexpr float kFloatMin = std::numeric_limits::lowest(); - -template <> -struct Limits { - static __device__ __host__ inline float getMin() { - return kFloatMin; - } - static __device__ __host__ inline float getMax() { - return kFloatMax; - } -}; - -inline __device__ __host__ half kGetHalf(unsigned short v) { -#if CUDA_VERSION >= 9000 - __half_raw h; - h.x = v; - return __half(h); -#else - half h; - h.x = v; - return h; -#endif -} - -template <> -struct Limits { - static __device__ __host__ inline half getMin() { - return kGetHalf(0xfbffU); - } - static __device__ __host__ inline half getMax() { - return kGetHalf(0x7bffU); - } -}; - -constexpr int kIntMax = std::numeric_limits::max(); -constexpr int kIntMin = std::numeric_limits::lowest(); - -template <> -struct Limits { - static __device__ __host__ inline int getMin() { - return kIntMin; - } - static __device__ __host__ inline int getMax() { - return kIntMax; - } -}; - -template -struct Limits> { - static __device__ __host__ inline Pair getMin() { - return Pair(Limits::getMin(), Limits::getMin()); - } - - static __device__ __host__ inline Pair getMax() { - return Pair(Limits::getMax(), Limits::getMax()); - } -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/LoadStoreOperators.cuh b/core/src/index/thirdparty/faiss/gpu/utils/LoadStoreOperators.cuh deleted file mode 100644 index b49d634461..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/LoadStoreOperators.cuh +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include - -#ifndef __HALF2_TO_UI -// cuda_fp16.hpp doesn't export this -#define __HALF2_TO_UI(var) *(reinterpret_cast(&(var))) -#endif - - -// -// Templated wrappers to express load/store for different scalar and vector -// types, so kernels can have the same written form but can operate -// over half and float, and on vector types transparently -// - -namespace faiss { namespace gpu { - -template -struct LoadStore { - static inline __device__ T load(void* p) { - return *((T*) p); - } - - static inline __device__ void store(void* p, const T& v) { - *((T*) p) = v; - } -}; - -#ifdef FAISS_USE_FLOAT16 - -template <> -struct LoadStore { - static inline __device__ Half4 load(void* p) { - Half4 out; -#if CUDA_VERSION >= 9000 - asm("ld.global.v2.u32 {%0, %1}, [%2];" : - "=r"(__HALF2_TO_UI(out.a)), "=r"(__HALF2_TO_UI(out.b)) : "l"(p)); -#else - asm("ld.global.v2.u32 {%0, %1}, [%2];" : - "=r"(out.a.x), "=r"(out.b.x) : "l"(p)); -#endif - return out; - } - - static inline __device__ void store(void* p, Half4& v) { -#if CUDA_VERSION >= 9000 - asm("st.v2.u32 [%0], {%1, %2};" : : "l"(p), - "r"(__HALF2_TO_UI(v.a)), "r"(__HALF2_TO_UI(v.b))); -#else - asm("st.v2.u32 [%0], {%1, %2};" : : "l"(p), "r"(v.a.x), "r"(v.b.x)); -#endif - } -}; - -template <> -struct LoadStore { - static inline __device__ Half8 load(void* p) { - Half8 out; -#if CUDA_VERSION >= 9000 - asm("ld.global.v4.u32 {%0, %1, %2, %3}, [%4];" : - "=r"(__HALF2_TO_UI(out.a.a)), "=r"(__HALF2_TO_UI(out.a.b)), - "=r"(__HALF2_TO_UI(out.b.a)), "=r"(__HALF2_TO_UI(out.b.b)) : "l"(p)); -#else - asm("ld.global.v4.u32 {%0, %1, %2, %3}, [%4];" : - "=r"(out.a.a.x), "=r"(out.a.b.x), - "=r"(out.b.a.x), "=r"(out.b.b.x) : "l"(p)); -#endif - return out; - } - - static inline __device__ void store(void* p, Half8& v) { -#if CUDA_VERSION >= 9000 - asm("st.v4.u32 [%0], {%1, %2, %3, %4};" - : : "l"(p), "r"(__HALF2_TO_UI(v.a.a)), "r"(__HALF2_TO_UI(v.a.b)), - "r"(__HALF2_TO_UI(v.b.a)), "r"(__HALF2_TO_UI(v.b.b))); -#else - asm("st.v4.u32 [%0], {%1, %2, %3, %4};" - : : "l"(p), "r"(v.a.a.x), "r"(v.a.b.x), "r"(v.b.a.x), "r"(v.b.b.x)); -#endif - } -}; - -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/MathOperators.cuh b/core/src/index/thirdparty/faiss/gpu/utils/MathOperators.cuh deleted file mode 100644 index 7e9f25a2a0..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/MathOperators.cuh +++ /dev/null @@ -1,561 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include - -// -// Templated wrappers to express math for different scalar and vector -// types, so kernels can have the same written form but can operate -// over half and float, and on vector types transparently -// - -namespace faiss { namespace gpu { - -template -struct Math { - typedef T ScalarType; - - static inline __device__ T add(T a, T b) { - return a + b; - } - - static inline __device__ T sub(T a, T b) { - return a - b; - } - - static inline __device__ T mul(T a, T b) { - return a * b; - } - - static inline __device__ T neg(T v) { - return -v; - } - - /// For a vector type, this is a horizontal add, returning sum(v_i) - static inline __device__ float reduceAdd(T v) { - return ConvertTo::to(v); - } - - static inline __device__ bool lt(T a, T b) { - return a < b; - } - - static inline __device__ bool gt(T a, T b) { - return a > b; - } - - static inline __device__ bool eq(T a, T b) { - return a == b; - } - - static inline __device__ T zero() { - return (T) 0; - } -}; - -template <> -struct Math { - typedef float ScalarType; - - static inline __device__ float2 add(float2 a, float2 b) { - float2 v; - v.x = a.x + b.x; - v.y = a.y + b.y; - return v; - } - - static inline __device__ float2 sub(float2 a, float2 b) { - float2 v; - v.x = a.x - b.x; - v.y = a.y - b.y; - return v; - } - - static inline __device__ float2 add(float2 a, float b) { - float2 v; - v.x = a.x + b; - v.y = a.y + b; - return v; - } - - static inline __device__ float2 sub(float2 a, float b) { - float2 v; - v.x = a.x - b; - v.y = a.y - b; - return v; - } - - static inline __device__ float2 mul(float2 a, float2 b) { - float2 v; - v.x = a.x * b.x; - v.y = a.y * b.y; - return v; - } - - static inline __device__ float2 mul(float2 a, float b) { - float2 v; - v.x = a.x * b; - v.y = a.y * b; - return v; - } - - static inline __device__ float2 neg(float2 v) { - v.x = -v.x; - v.y = -v.y; - return v; - } - - /// For a vector type, this is a horizontal add, returning sum(v_i) - static inline __device__ float reduceAdd(float2 v) { - return v.x + v.y; - } - - // not implemented for vector types - // static inline __device__ bool lt(float2 a, float2 b); - // static inline __device__ bool gt(float2 a, float2 b); - // static inline __device__ bool eq(float2 a, float2 b); - - static inline __device__ float2 zero() { - float2 v; - v.x = 0.0f; - v.y = 0.0f; - return v; - } -}; - -template <> -struct Math { - typedef float ScalarType; - - static inline __device__ float4 add(float4 a, float4 b) { - float4 v; - v.x = a.x + b.x; - v.y = a.y + b.y; - v.z = a.z + b.z; - v.w = a.w + b.w; - return v; - } - - static inline __device__ float4 sub(float4 a, float4 b) { - float4 v; - v.x = a.x - b.x; - v.y = a.y - b.y; - v.z = a.z - b.z; - v.w = a.w - b.w; - return v; - } - - static inline __device__ float4 add(float4 a, float b) { - float4 v; - v.x = a.x + b; - v.y = a.y + b; - v.z = a.z + b; - v.w = a.w + b; - return v; - } - - static inline __device__ float4 sub(float4 a, float b) { - float4 v; - v.x = a.x - b; - v.y = a.y - b; - v.z = a.z - b; - v.w = a.w - b; - return v; - } - - static inline __device__ float4 mul(float4 a, float4 b) { - float4 v; - v.x = a.x * b.x; - v.y = a.y * b.y; - v.z = a.z * b.z; - v.w = a.w * b.w; - return v; - } - - static inline __device__ float4 mul(float4 a, float b) { - float4 v; - v.x = a.x * b; - v.y = a.y * b; - v.z = a.z * b; - v.w = a.w * b; - return v; - } - - static inline __device__ float4 neg(float4 v) { - v.x = -v.x; - v.y = -v.y; - v.z = -v.z; - v.w = -v.w; - return v; - } - - /// For a vector type, this is a horizontal add, returning sum(v_i) - static inline __device__ float reduceAdd(float4 v) { - return v.x + v.y + v.z + v.w; - } - - // not implemented for vector types - // static inline __device__ bool lt(float4 a, float4 b); - // static inline __device__ bool gt(float4 a, float4 b); - // static inline __device__ bool eq(float4 a, float4 b); - - static inline __device__ float4 zero() { - float4 v; - v.x = 0.0f; - v.y = 0.0f; - v.z = 0.0f; - v.w = 0.0f; - return v; - } -}; - -#ifdef FAISS_USE_FLOAT16 -template <> -struct Math { - typedef half ScalarType; - - static inline __device__ half add(half a, half b) { -#ifdef FAISS_USE_FULL_FLOAT16 - return __hadd(a, b); -#else - return __float2half(__half2float(a) + __half2float(b)); -#endif - } - - static inline __device__ half sub(half a, half b) { -#ifdef FAISS_USE_FULL_FLOAT16 - return __hsub(a, b); -#else - return __float2half(__half2float(a) - __half2float(b)); -#endif - } - - static inline __device__ half mul(half a, half b) { -#ifdef FAISS_USE_FULL_FLOAT16 - return __hmul(a, b); -#else - return __float2half(__half2float(a) * __half2float(b)); -#endif - } - - static inline __device__ half neg(half v) { -#ifdef FAISS_USE_FULL_FLOAT16 - return __hneg(v); -#else - return __float2half(-__half2float(v)); -#endif - } - - static inline __device__ float reduceAdd(half v) { - return ConvertTo::to(v); - } - - static inline __device__ bool lt(half a, half b) { -#ifdef FAISS_USE_FULL_FLOAT16 - return __hlt(a, b); -#else - return __half2float(a) < __half2float(b); -#endif - } - - static inline __device__ bool gt(half a, half b) { -#ifdef FAISS_USE_FULL_FLOAT16 - return __hgt(a, b); -#else - return __half2float(a) > __half2float(b); -#endif - } - - static inline __device__ bool eq(half a, half b) { -#ifdef FAISS_USE_FULL_FLOAT16 - return __heq(a, b); -#else - return __half2float(a) == __half2float(b); -#endif - } - - static inline __device__ half zero() { -#if CUDA_VERSION >= 9000 - return 0; -#else - half h; - h.x = 0; - return h; -#endif - } -}; - -template <> -struct Math { - typedef half ScalarType; - - static inline __device__ half2 add(half2 a, half2 b) { -#ifdef FAISS_USE_FULL_FLOAT16 - return __hadd2(a, b); -#else - float2 af = __half22float2(a); - float2 bf = __half22float2(b); - - af.x += bf.x; - af.y += bf.y; - - return __float22half2_rn(af); -#endif - } - - static inline __device__ half2 sub(half2 a, half2 b) { -#ifdef FAISS_USE_FULL_FLOAT16 - return __hsub2(a, b); -#else - float2 af = __half22float2(a); - float2 bf = __half22float2(b); - - af.x -= bf.x; - af.y -= bf.y; - - return __float22half2_rn(af); -#endif - } - - static inline __device__ half2 add(half2 a, half b) { -#ifdef FAISS_USE_FULL_FLOAT16 - half2 b2 = __half2half2(b); - return __hadd2(a, b2); -#else - float2 af = __half22float2(a); - float bf = __half2float(b); - - af.x += bf; - af.y += bf; - - return __float22half2_rn(af); -#endif - } - - static inline __device__ half2 sub(half2 a, half b) { -#ifdef FAISS_USE_FULL_FLOAT16 - half2 b2 = __half2half2(b); - return __hsub2(a, b2); -#else - float2 af = __half22float2(a); - float bf = __half2float(b); - - af.x -= bf; - af.y -= bf; - - return __float22half2_rn(af); -#endif - } - - static inline __device__ half2 mul(half2 a, half2 b) { -#ifdef FAISS_USE_FULL_FLOAT16 - return __hmul2(a, b); -#else - float2 af = __half22float2(a); - float2 bf = __half22float2(b); - - af.x *= bf.x; - af.y *= bf.y; - - return __float22half2_rn(af); -#endif - } - - static inline __device__ half2 mul(half2 a, half b) { -#ifdef FAISS_USE_FULL_FLOAT16 - half2 b2 = __half2half2(b); - return __hmul2(a, b2); -#else - float2 af = __half22float2(a); - float bf = __half2float(b); - - af.x *= bf; - af.y *= bf; - - return __float22half2_rn(af); -#endif - } - - static inline __device__ half2 neg(half2 v) { -#ifdef FAISS_USE_FULL_FLOAT16 - return __hneg2(v); -#else - float2 vf = __half22float2(v); - vf.x = -vf.x; - vf.y = -vf.y; - - return __float22half2_rn(vf); -#endif - } - - static inline __device__ float reduceAdd(half2 v) { - float2 vf = __half22float2(v); - vf.x += vf.y; - - return vf.x; - } - - // not implemented for vector types - // static inline __device__ bool lt(half2 a, half2 b); - // static inline __device__ bool gt(half2 a, half2 b); - // static inline __device__ bool eq(half2 a, half2 b); - - static inline __device__ half2 zero() { - return __half2half2(Math::zero()); - } -}; - -template <> -struct Math { - typedef half ScalarType; - - static inline __device__ Half4 add(Half4 a, Half4 b) { - Half4 h; - h.a = Math::add(a.a, b.a); - h.b = Math::add(a.b, b.b); - return h; - } - - static inline __device__ Half4 sub(Half4 a, Half4 b) { - Half4 h; - h.a = Math::sub(a.a, b.a); - h.b = Math::sub(a.b, b.b); - return h; - } - - static inline __device__ Half4 add(Half4 a, half b) { - Half4 h; - h.a = Math::add(a.a, b); - h.b = Math::add(a.b, b); - return h; - } - - static inline __device__ Half4 sub(Half4 a, half b) { - Half4 h; - h.a = Math::sub(a.a, b); - h.b = Math::sub(a.b, b); - return h; - } - - static inline __device__ Half4 mul(Half4 a, Half4 b) { - Half4 h; - h.a = Math::mul(a.a, b.a); - h.b = Math::mul(a.b, b.b); - return h; - } - - static inline __device__ Half4 mul(Half4 a, half b) { - Half4 h; - h.a = Math::mul(a.a, b); - h.b = Math::mul(a.b, b); - return h; - } - - static inline __device__ Half4 neg(Half4 v) { - Half4 h; - h.a = Math::neg(v.a); - h.b = Math::neg(v.b); - return h; - } - - static inline __device__ float reduceAdd(Half4 v) { - float x = Math::reduceAdd(v.a); - float y = Math::reduceAdd(v.b); - return x + y; - } - - // not implemented for vector types - // static inline __device__ bool lt(Half4 a, Half4 b); - // static inline __device__ bool gt(Half4 a, Half4 b); - // static inline __device__ bool eq(Half4 a, Half4 b); - - static inline __device__ Half4 zero() { - Half4 h; - h.a = Math::zero(); - h.b = Math::zero(); - return h; - } -}; - -template <> -struct Math { - typedef half ScalarType; - - static inline __device__ Half8 add(Half8 a, Half8 b) { - Half8 h; - h.a = Math::add(a.a, b.a); - h.b = Math::add(a.b, b.b); - return h; - } - - static inline __device__ Half8 sub(Half8 a, Half8 b) { - Half8 h; - h.a = Math::sub(a.a, b.a); - h.b = Math::sub(a.b, b.b); - return h; - } - - static inline __device__ Half8 add(Half8 a, half b) { - Half8 h; - h.a = Math::add(a.a, b); - h.b = Math::add(a.b, b); - return h; - } - - static inline __device__ Half8 sub(Half8 a, half b) { - Half8 h; - h.a = Math::sub(a.a, b); - h.b = Math::sub(a.b, b); - return h; - } - - static inline __device__ Half8 mul(Half8 a, Half8 b) { - Half8 h; - h.a = Math::mul(a.a, b.a); - h.b = Math::mul(a.b, b.b); - return h; - } - - static inline __device__ Half8 mul(Half8 a, half b) { - Half8 h; - h.a = Math::mul(a.a, b); - h.b = Math::mul(a.b, b); - return h; - } - - static inline __device__ Half8 neg(Half8 v) { - Half8 h; - h.a = Math::neg(v.a); - h.b = Math::neg(v.b); - return h; - } - - static inline __device__ half reduceAdd(Half8 v) { - float x = Math::reduceAdd(v.a); - float y = Math::reduceAdd(v.b); - return x + y; - } - - // not implemented for vector types - // static inline __device__ bool lt(Half8 a, Half8 b); - // static inline __device__ bool gt(Half8 a, Half8 b); - // static inline __device__ bool eq(Half8 a, Half8 b); - - static inline __device__ Half8 zero() { - Half8 h; - h.a = Math::zero(); - h.b = Math::zero(); - return h; - } -}; -#endif // FAISS_USE_FLOAT16 - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/MatrixMult-inl.cuh b/core/src/index/thirdparty/faiss/gpu/utils/MatrixMult-inl.cuh deleted file mode 100644 index ede225e035..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/MatrixMult-inl.cuh +++ /dev/null @@ -1,160 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -class DeviceMemory; - -template -struct GetCudaType; - -template <> -struct GetCudaType { - static constexpr cudaDataType_t Type = CUDA_R_32F; -}; - -template <> -struct GetCudaType { - static constexpr cudaDataType_t Type = CUDA_R_16F; -}; - -template -cublasStatus_t -rawGemm(cublasHandle_t handle, - cublasOperation_t transa, - cublasOperation_t transb, - int m, - int n, - int k, - const float fAlpha, - const AT *A, - int lda, - const BT *B, - int ldb, - const float fBeta, - float *C, - int ldc) { - auto cAT = GetCudaType::Type; - auto cBT = GetCudaType::Type; - - // Always accumulate in f32 - return cublasSgemmEx(handle, transa, transb, m, n, k, - &fAlpha, A, cAT, lda, - B, cBT, ldb, - &fBeta, - C, CUDA_R_32F, ldc); -} - -template -void -runMatrixMult(Tensor& c, bool transC, - Tensor& a, bool transA, - Tensor& b, bool transB, - float alpha, - float beta, - cublasHandle_t handle, - cudaStream_t stream) { - cublasSetStream(handle, stream); - - // Check that we have (m x k) * (k x n) = (m x n) - // using the input row-major layout - int aM = transA ? a.getSize(1) : a.getSize(0); - int aK = transA ? a.getSize(0) : a.getSize(1); - - int bK = transB ? b.getSize(1) : b.getSize(0); - int bN = transB ? b.getSize(0) : b.getSize(1); - - int cM = transC ? c.getSize(1) : c.getSize(0); - int cN = transC ? c.getSize(0) : c.getSize(1); - - FAISS_ASSERT(aM == cM); - FAISS_ASSERT(aK == bK); - FAISS_ASSERT(bN == cN); - - FAISS_ASSERT(a.getStride(1) == 1); - FAISS_ASSERT(b.getStride(1) == 1); - FAISS_ASSERT(c.getStride(1) == 1); - - // Now, we have to represent the matrix multiplication in - // column-major layout - float* pC = c.data(); - - int m = c.getSize(1); // stride 1 size - int n = c.getSize(0); // other size - int k = transA ? a.getSize(0) : a.getSize(1); - - int lda = transC ? a.getStride(0) : b.getStride(0); - int ldb = transC ? b.getStride(0) : a.getStride(0); - int ldc = c.getStride(0); - - auto gemmTrA = transB ? CUBLAS_OP_T : CUBLAS_OP_N; - auto gemmTrB = transA ? CUBLAS_OP_T : CUBLAS_OP_N; - - if (transC) { - gemmTrA = transA ? CUBLAS_OP_N : CUBLAS_OP_T; - gemmTrB = transB ? CUBLAS_OP_N : CUBLAS_OP_T; - } - - cublasStatus_t err; - - if (transC) { - err = rawGemm(handle, - gemmTrA, gemmTrB, - m, n, k, alpha, - a.data(), lda, b.data(), ldb, beta, - pC, ldc); - } else { - err = rawGemm(handle, - gemmTrA, gemmTrB, - m, n, k, alpha, - b.data(), lda, a.data(), ldb, beta, - pC, ldc); - } - - FAISS_ASSERT_FMT(err == CUBLAS_STATUS_SUCCESS, - "cublas failed (%d): " - "(%d, %d)%s x (%d, %d)%s = (%d, %d)%s", - (int) err, - a.getSize(0), a.getSize(1), transA ? "'" : "", - b.getSize(0), b.getSize(1), transB ? "'" : "", - c.getSize(0), c.getSize(1), transC ? "'" : ""); - CUDA_TEST_ERROR(); -} - -template -void runIteratedMatrixMult(Tensor& c, bool transC, - Tensor& a, bool transA, - Tensor& b, bool transB, - float alpha, - float beta, - cublasHandle_t handle, - cudaStream_t stream) { - FAISS_ASSERT(c.getSize(0) == a.getSize(0)); - FAISS_ASSERT(a.getSize(0) == b.getSize(0)); - - for (int i = 0; i < a.getSize(0); ++i) { - auto cView = c[i].view(); - auto aView = a[i].view(); - auto bView = b[i].view(); - - runMatrixMult(cView, transC, - aView, transA, - bView, transB, - alpha, beta, handle, stream); - } -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/MatrixMult.cu b/core/src/index/thirdparty/faiss/gpu/utils/MatrixMult.cu deleted file mode 100644 index 2afb5017b2..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/MatrixMult.cu +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include - -namespace faiss { namespace gpu { - -void -runBatchMatrixMult(Tensor& c, bool transC, - Tensor& a, bool transA, - Tensor& b, bool transB, - float alpha, - float beta, - DeviceMemory& mem, - cublasHandle_t handle, - cudaStream_t stream) { - FAISS_ASSERT(c.getSize(0) == a.getSize(0)); - FAISS_ASSERT(a.getSize(0) == b.getSize(0)); - cublasSetStream(handle, stream); - - // Check that we have (m x k) * (k x n) = (m x n) - // using the input row-major layout - int aM = transA ? a.getSize(2) : a.getSize(1); - int aK = transA ? a.getSize(1) : a.getSize(2); - - int bK = transB ? b.getSize(2) : b.getSize(1); - int bN = transB ? b.getSize(1) : b.getSize(2); - - int cM = transC ? c.getSize(2) : c.getSize(1); - int cN = transC ? c.getSize(1) : c.getSize(2); - - FAISS_ASSERT(aM == cM); - FAISS_ASSERT(aK == bK); - FAISS_ASSERT(bN == cN); - - // Now, we have to represent the matrix multiplication in - // column-major layout - float* pA = transC ? a.data() : b.data(); - float* pB = transC ? b.data() : a.data(); - float* pC = c.data(); - - int m = c.getSize(2); // stride 1 size - int n = c.getSize(1); // other size - int k = transA ? a.getSize(1) : a.getSize(2); - - int lda = transC ? a.getStride(1) : b.getStride(1); - int ldb = transC ? b.getStride(1) : a.getStride(1); - int ldc = c.getStride(1); - - auto gemmTrA = transB ? CUBLAS_OP_T : CUBLAS_OP_N; - auto gemmTrB = transA ? CUBLAS_OP_T : CUBLAS_OP_N; - - if (transC) { - gemmTrA = transA ? CUBLAS_OP_N : CUBLAS_OP_T; - gemmTrB = transB ? CUBLAS_OP_N : CUBLAS_OP_T; - } - - HostTensor hostA({a.getSize(0)}); - HostTensor hostB({b.getSize(0)}); - HostTensor hostC({c.getSize(0)}); - - size_t aOffset = a.getStride(0); - size_t bOffset = b.getStride(0); - size_t cOffset = c.getStride(0); - - for (int i = 0; i < a.getSize(0); ++i) { - hostA[i] = transC ? a.data() + i * aOffset : b.data() + i * bOffset; - hostB[i] = transC ? b.data() + i * bOffset : a.data() + i * aOffset; - hostC[i] = c.data() + i * cOffset; - } - - DeviceTensor deviceA(mem, hostA, stream); - DeviceTensor deviceB(mem, hostB, stream); - DeviceTensor deviceC(mem, hostC, stream); - - auto err = - cublasSgemmBatched(handle, - gemmTrA, gemmTrB, - m, n, k, &alpha, - (const float**) deviceA.data(), lda, - (const float**) deviceB.data(), ldb, &beta, - deviceC.data(), ldc, a.getSize(0)); - FAISS_ASSERT_FMT(err == CUBLAS_STATUS_SUCCESS, - "cublasSgemmBatched failed (%d)", (int) err); - CUDA_TEST_ERROR(); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/MatrixMult.cuh b/core/src/index/thirdparty/faiss/gpu/utils/MatrixMult.cuh deleted file mode 100644 index eeb11ccc5c..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/MatrixMult.cuh +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -class DeviceMemory; - -/// C = alpha * A * B + beta * C -/// Expects row major layout, not fortran/blas column major! -template -void -runMatrixMult(Tensor& c, bool transC, - Tensor& a, bool transA, - Tensor& b, bool transB, - float alpha, - float beta, - cublasHandle_t handle, - cudaStream_t stream); - -/// C_i = alpha * A_i * B_i + beta * C_i -/// where `i` is the outermost dimension, via iterated gemm -/// Expects row major layout, not fortran/blas column major! -template -void runIteratedMatrixMult(Tensor& c, bool transC, - Tensor& a, bool transA, - Tensor& b, bool transB, - float alpha, - float beta, - cublasHandle_t handle, - cudaStream_t stream); - -/// C_i = alpha * A_i * B_i + beta * C_i -/// where `i` is the outermost dimension, via batched gemm -/// Expects row major layout, not fortran/blas column major! -void runBatchMatrixMult(Tensor& c, bool transC, - Tensor& a, bool transA, - Tensor& b, bool transB, - float alpha, - float beta, - DeviceMemory& mem, - cublasHandle_t handle, - cudaStream_t stream); - -} } // namespace - -#include diff --git a/core/src/index/thirdparty/faiss/gpu/utils/MemorySpace.cpp b/core/src/index/thirdparty/faiss/gpu/utils/MemorySpace.cpp deleted file mode 100644 index 282f835784..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/MemorySpace.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include - -namespace faiss { namespace gpu { - -/// Allocates CUDA memory for a given memory space -void allocMemorySpaceV(MemorySpace space, void** p, size_t size) { - switch (space) { - case MemorySpace::Device: - { - auto err = cudaMalloc(p, size); - - // Throw if we fail to allocate - FAISS_THROW_IF_NOT_FMT( - err == cudaSuccess, - "failed to cudaMalloc %zu bytes (error %d %s)", - size, (int) err, cudaGetErrorString(err)); - } - break; - case MemorySpace::Unified: - { -#ifdef FAISS_UNIFIED_MEM - auto err = cudaMallocManaged(p, size); - - // Throw if we fail to allocate - FAISS_THROW_IF_NOT_FMT( - err == cudaSuccess, - "failed to cudaMallocManaged %zu bytes (error %d %s)", - size, (int) err, cudaGetErrorString(err)); -#else - FAISS_THROW_MSG("Attempting to allocate via cudaMallocManaged " - "without CUDA 8+ support"); -#endif - } - break; - case MemorySpace::HostPinned: - { - auto err = cudaHostAlloc(p, size, cudaHostAllocDefault); - - // Throw if we fail to allocate - FAISS_THROW_IF_NOT_FMT( - err == cudaSuccess, - "failed to cudaHostAlloc %zu bytes (error %d %s)", - size, (int) err, cudaGetErrorString(err)); - } - break; - default: - FAISS_ASSERT_FMT(false, "unknown MemorySpace %d", (int) space); - break; - } -} - -// We'll allow allocation to fail, but free should always succeed and be a -// fatal error if it doesn't free -void freeMemorySpace(MemorySpace space, void* p) { - switch (space) { - case MemorySpace::Device: - case MemorySpace::Unified: - { - auto err = cudaFree(p); - FAISS_ASSERT_FMT(err == cudaSuccess, - "Failed to cudaFree pointer %p (error %d %s)", - p, (int) err, cudaGetErrorString(err)); - } - break; - case MemorySpace::HostPinned: - { - auto err = cudaFreeHost(p); - FAISS_ASSERT_FMT(err == cudaSuccess, - "Failed to cudaFreeHost pointer %p (error %d %s)", - p, (int) err, cudaGetErrorString(err)); - } - break; - default: - FAISS_ASSERT_FMT(false, "unknown MemorySpace %d", (int) space); - break; - } -} - -} } diff --git a/core/src/index/thirdparty/faiss/gpu/utils/MemorySpace.h b/core/src/index/thirdparty/faiss/gpu/utils/MemorySpace.h deleted file mode 100644 index f269f06a39..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/MemorySpace.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include - -#if CUDA_VERSION >= 8000 -// Whether or not we enable usage of CUDA Unified Memory -#define FAISS_UNIFIED_MEM 1 -#endif - -namespace faiss { namespace gpu { - -enum MemorySpace { - /// Managed using cudaMalloc/cudaFree - Device = 1, - /// Managed using cudaMallocManaged/cudaFree - Unified = 2, - /// Managed using cudaHostAlloc/cudaFreeHost - HostPinned = 3, -}; - -/// All memory allocations and de-allocations come through these functions - -/// Allocates CUDA memory for a given memory space (void pointer) -/// Throws a FaissException if we are unable to allocate the memory -void allocMemorySpaceV(MemorySpace space, void** p, size_t size); - -template -inline void allocMemorySpace(MemorySpace space, T** p, size_t size) { - allocMemorySpaceV(space, (void**)(void*) p, size); -} - -/// Frees CUDA memory for a given memory space -/// Asserts if we are unable to free the region -void freeMemorySpace(MemorySpace space, void* p); - -} } diff --git a/core/src/index/thirdparty/faiss/gpu/utils/MergeNetworkBlock.cuh b/core/src/index/thirdparty/faiss/gpu/utils/MergeNetworkBlock.cuh deleted file mode 100644 index 2776258b57..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/MergeNetworkBlock.cuh +++ /dev/null @@ -1,289 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -// Merge pairs of lists smaller than blockDim.x (NumThreads) -template -inline __device__ void blockMergeSmall(K* listK, V* listV) { - static_assert(utils::isPowerOf2(L), "L must be a power-of-2"); - static_assert(utils::isPowerOf2(NumThreads), - "NumThreads must be a power-of-2"); - static_assert(L <= NumThreads, "merge list size must be <= NumThreads"); - - // Which pair of lists we are merging - int mergeId = threadIdx.x / L; - - // Which thread we are within the merge - int tid = threadIdx.x % L; - - // listK points to a region of size N * 2 * L - listK += 2 * L * mergeId; - listV += 2 * L * mergeId; - - // It's not a bitonic merge, both lists are in the same direction, - // so handle the first swap assuming the second list is reversed - int pos = L - 1 - tid; - int stride = 2 * tid + 1; - - if (AllThreads || (threadIdx.x < N * L)) { - K ka = listK[pos]; - K kb = listK[pos + stride]; - - bool swap = Dir ? Comp::gt(ka, kb) : Comp::lt(ka, kb); - listK[pos] = swap ? kb : ka; - listK[pos + stride] = swap ? ka : kb; - - V va = listV[pos]; - V vb = listV[pos + stride]; - listV[pos] = swap ? vb : va; - listV[pos + stride] = swap ? va : vb; - - // FIXME: is this a CUDA 9 compiler bug? - // K& ka = listK[pos]; - // K& kb = listK[pos + stride]; - - // bool s = Dir ? Comp::gt(ka, kb) : Comp::lt(ka, kb); - // swap(s, ka, kb); - - // V& va = listV[pos]; - // V& vb = listV[pos + stride]; - // swap(s, va, vb); - } - - __syncthreads(); - -#pragma unroll - for (int stride = L / 2; stride > 0; stride /= 2) { - int pos = 2 * tid - (tid & (stride - 1)); - - if (AllThreads || (threadIdx.x < N * L)) { - K ka = listK[pos]; - K kb = listK[pos + stride]; - - bool swap = Dir ? Comp::gt(ka, kb) : Comp::lt(ka, kb); - listK[pos] = swap ? kb : ka; - listK[pos + stride] = swap ? ka : kb; - - V va = listV[pos]; - V vb = listV[pos + stride]; - listV[pos] = swap ? vb : va; - listV[pos + stride] = swap ? va : vb; - - // FIXME: is this a CUDA 9 compiler bug? - // K& ka = listK[pos]; - // K& kb = listK[pos + stride]; - - // bool s = Dir ? Comp::gt(ka, kb) : Comp::lt(ka, kb); - // swap(s, ka, kb); - - // V& va = listV[pos]; - // V& vb = listV[pos + stride]; - // swap(s, va, vb); - } - - __syncthreads(); - } -} - -// Merge pairs of sorted lists larger than blockDim.x (NumThreads) -template -inline __device__ void blockMergeLarge(K* listK, V* listV) { - static_assert(utils::isPowerOf2(L), "L must be a power-of-2"); - static_assert(L >= kWarpSize, "merge list size must be >= 32"); - static_assert(utils::isPowerOf2(NumThreads), - "NumThreads must be a power-of-2"); - static_assert(L >= NumThreads, "merge list size must be >= NumThreads"); - - // For L > NumThreads, each thread has to perform more work - // per each stride. - constexpr int kLoopPerThread = L / NumThreads; - - // It's not a bitonic merge, both lists are in the same direction, - // so handle the first swap assuming the second list is reversed -#pragma unroll - for (int loop = 0; loop < kLoopPerThread; ++loop) { - int tid = loop * NumThreads + threadIdx.x; - int pos = L - 1 - tid; - int stride = 2 * tid + 1; - - K ka = listK[pos]; - K kb = listK[pos + stride]; - - bool swap = Dir ? Comp::gt(ka, kb) : Comp::lt(ka, kb); - listK[pos] = swap ? kb : ka; - listK[pos + stride] = swap ? ka : kb; - - V va = listV[pos]; - V vb = listV[pos + stride]; - listV[pos] = swap ? vb : va; - listV[pos + stride] = swap ? va : vb; - - // FIXME: is this a CUDA 9 compiler bug? - // K& ka = listK[pos]; - // K& kb = listK[pos + stride]; - - // bool s = Dir ? Comp::gt(ka, kb) : Comp::lt(ka, kb); - // swap(s, ka, kb); - - // V& va = listV[pos]; - // V& vb = listV[pos + stride]; - // swap(s, va, vb); - } - - __syncthreads(); - - constexpr int kSecondLoopPerThread = - FullMerge ? kLoopPerThread : kLoopPerThread / 2; - -#pragma unroll - for (int stride = L / 2; stride > 0; stride /= 2) { -#pragma unroll - for (int loop = 0; loop < kSecondLoopPerThread; ++loop) { - int tid = loop * NumThreads + threadIdx.x; - int pos = 2 * tid - (tid & (stride - 1)); - - K ka = listK[pos]; - K kb = listK[pos + stride]; - - bool swap = Dir ? Comp::gt(ka, kb) : Comp::lt(ka, kb); - listK[pos] = swap ? kb : ka; - listK[pos + stride] = swap ? ka : kb; - - V va = listV[pos]; - V vb = listV[pos + stride]; - listV[pos] = swap ? vb : va; - listV[pos + stride] = swap ? va : vb; - - // FIXME: is this a CUDA 9 compiler bug? - // K& ka = listK[pos]; - // K& kb = listK[pos + stride]; - - // bool s = Dir ? Comp::gt(ka, kb) : Comp::lt(ka, kb); - // swap(s, ka, kb); - - // V& va = listV[pos]; - // V& vb = listV[pos + stride]; - // swap(s, va, vb); - } - - __syncthreads(); - } -} - -/// Class template to prevent static_assert from firing for -/// mixing smaller/larger than block cases -template -struct BlockMerge { -}; - -/// Merging lists smaller than a block -template -struct BlockMerge { - static inline __device__ void merge(K* listK, V* listV) { - constexpr int kNumParallelMerges = NumThreads / L; - constexpr int kNumIterations = N / kNumParallelMerges; - - static_assert(L <= NumThreads, "list must be <= NumThreads"); - static_assert((N < kNumParallelMerges) || - (kNumIterations * kNumParallelMerges == N), - "improper selection of N and L"); - - if (N < kNumParallelMerges) { - // We only need L threads per each list to perform the merge - blockMergeSmall( - listK, listV); - } else { - // All threads participate -#pragma unroll - for (int i = 0; i < kNumIterations; ++i) { - int start = i * kNumParallelMerges * 2 * L; - - blockMergeSmall( - listK + start, listV + start); - } - } - } -}; - -/// Merging lists larger than a block -template -struct BlockMerge { - static inline __device__ void merge(K* listK, V* listV) { - // Each pair of lists is merged sequentially -#pragma unroll - for (int i = 0; i < N; ++i) { - int start = i * 2 * L; - - blockMergeLarge( - listK + start, listV + start); - } - } -}; - -template -inline __device__ void blockMerge(K* listK, V* listV) { - constexpr bool kSmallerThanBlock = (L <= NumThreads); - - BlockMerge:: - merge(listK, listV); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/MergeNetworkUtils.cuh b/core/src/index/thirdparty/faiss/gpu/utils/MergeNetworkUtils.cuh deleted file mode 100644 index 6810345226..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/MergeNetworkUtils.cuh +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -namespace faiss { namespace gpu { - -template -inline __device__ void swap(bool swap, T& x, T& y) { - T tmp = x; - x = swap ? y : x; - y = swap ? tmp : y; -} - -template -inline __device__ void assign(bool assign, T& x, T y) { - x = assign ? y : x; -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/MergeNetworkWarp.cuh b/core/src/index/thirdparty/faiss/gpu/utils/MergeNetworkWarp.cuh deleted file mode 100644 index 4e486b025f..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/MergeNetworkWarp.cuh +++ /dev/null @@ -1,510 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -// -// This file contains functions to: -// -// -perform bitonic merges on pairs of sorted lists, held in -// registers. Each list contains N * kWarpSize (multiple of 32) -// elements for some N. -// The bitonic merge is implemented for arbitrary sizes; -// sorted list A of size N1 * kWarpSize registers -// sorted list B of size N2 * kWarpSize registers => -// sorted list C if size (N1 + N2) * kWarpSize registers. N1 and N2 -// are >= 1 and don't have to be powers of 2. -// -// -perform bitonic sorts on a set of N * kWarpSize key/value pairs -// held in registers, by using the above bitonic merge as a -// primitive. -// N can be an arbitrary N >= 1; i.e., the bitonic sort here supports -// odd sizes and doesn't require the input to be a power of 2. -// -// The sort or merge network is completely statically instantiated via -// template specialization / expansion and constexpr, and it uses warp -// shuffles to exchange values between warp lanes. -// -// A note about comparsions: -// -// For a sorting network of keys only, we only need one -// comparison (a < b). However, what we really need to know is -// if one lane chooses to exchange a value, then the -// corresponding lane should also do the exchange. -// Thus, if one just uses the negation !(x < y) in the higher -// lane, this will also include the case where (x == y). Thus, one -// lane in fact performs an exchange and the other doesn't, but -// because the only value being exchanged is equivalent, nothing has -// changed. -// So, you can get away with just one comparison and its negation. -// -// If we're sorting keys and values, where equivalent keys can -// exist, then this is a problem, since we want to treat (x, v1) -// as not equivalent to (x, v2). -// -// To remedy this, you can either compare with a lexicographic -// ordering (a.k < b.k || (a.k == b.k && a.v < b.v)), which since -// we're predicating all of the choices results in 3 comparisons -// being executed, or we can invert the selection so that there is no -// middle choice of equality; the other lane will likewise -// check that (b.k > a.k) (the higher lane has the values -// swapped). Then, the first lane swaps if and only if the -// second lane swaps; if both lanes have equivalent keys, no -// swap will be performed. This results in only two comparisons -// being executed. -// -// If you don't consider values as well, then this does not produce a -// consistent ordering among (k, v) pairs with equivalent keys but -// different values; for us, we don't really care about ordering or -// stability here. -// -// I have tried both re-arranging the order in the higher lane to get -// away with one comparison or adding the value to the check; both -// result in greater register consumption or lower speed than just -// perfoming both < and > comparisons with the variables, so I just -// stick with this. - -// This function merges kWarpSize / 2L lists in parallel using warp -// shuffles. -// It works on at most size-16 lists, as we need 32 threads for this -// shuffle merge. -// -// If IsBitonic is false, the first stage is reversed, so we don't -// need to sort directionally. It's still technically a bitonic sort. -template -inline __device__ void warpBitonicMergeLE16(K& k, V& v) { - static_assert(utils::isPowerOf2(L), "L must be a power-of-2"); - static_assert(L <= kWarpSize / 2, "merge list size must be <= 16"); - - int laneId = getLaneId(); - - if (!IsBitonic) { - // Reverse the first comparison stage. - // For example, merging a list of size 8 has the exchanges: - // 0 <-> 15, 1 <-> 14, ... - K otherK = shfl_xor(k, 2 * L - 1); - V otherV = shfl_xor(v, 2 * L - 1); - - // Whether we are the lesser thread in the exchange - bool small = !(laneId & L); - - if (Dir) { - // See the comment above how performing both of these - // comparisons in the warp seems to win out over the - // alternatives in practice - bool s = small ? Comp::gt(k, otherK) : Comp::lt(k, otherK); - assign(s, k, otherK); - assign(s, v, otherV); - - } else { - bool s = small ? Comp::lt(k, otherK) : Comp::gt(k, otherK); - assign(s, k, otherK); - assign(s, v, otherV); - } - } - -#pragma unroll - for (int stride = IsBitonic ? L : L / 2; stride > 0; stride /= 2) { - K otherK = shfl_xor(k, stride); - V otherV = shfl_xor(v, stride); - - // Whether we are the lesser thread in the exchange - bool small = !(laneId & stride); - - if (Dir) { - bool s = small ? Comp::gt(k, otherK) : Comp::lt(k, otherK); - assign(s, k, otherK); - assign(s, v, otherV); - - } else { - bool s = small ? Comp::lt(k, otherK) : Comp::gt(k, otherK); - assign(s, k, otherK); - assign(s, v, otherV); - } - } -} - -// Template for performing a bitonic merge of an arbitrary set of -// registers -template -struct BitonicMergeStep { -}; - -// -// Power-of-2 merge specialization -// - -// All merges eventually call this -template -struct BitonicMergeStep { - static inline __device__ void merge(K k[1], V v[1]) { - // Use warp shuffles - warpBitonicMergeLE16(k[0], v[0]); - } -}; - -template -struct BitonicMergeStep { - static inline __device__ void merge(K k[N], V v[N]) { - static_assert(utils::isPowerOf2(N), "must be power of 2"); - static_assert(N > 1, "must be N > 1"); - -#pragma unroll - for (int i = 0; i < N / 2; ++i) { - K& ka = k[i]; - V& va = v[i]; - - K& kb = k[i + N / 2]; - V& vb = v[i + N / 2]; - - bool s = Dir ? Comp::gt(ka, kb) : Comp::lt(ka, kb); - swap(s, ka, kb); - swap(s, va, vb); - } - - { - K newK[N / 2]; - V newV[N / 2]; - -#pragma unroll - for (int i = 0; i < N / 2; ++i) { - newK[i] = k[i]; - newV[i] = v[i]; - } - - BitonicMergeStep::merge(newK, newV); - -#pragma unroll - for (int i = 0; i < N / 2; ++i) { - k[i] = newK[i]; - v[i] = newV[i]; - } - } - - { - K newK[N / 2]; - V newV[N / 2]; - -#pragma unroll - for (int i = 0; i < N / 2; ++i) { - newK[i] = k[i + N / 2]; - newV[i] = v[i + N / 2]; - } - - BitonicMergeStep::merge(newK, newV); - -#pragma unroll - for (int i = 0; i < N / 2; ++i) { - k[i + N / 2] = newK[i]; - v[i + N / 2] = newV[i]; - } - } - } -}; - -// -// Non-power-of-2 merge specialization -// - -// Low recursion -template -struct BitonicMergeStep { - static inline __device__ void merge(K k[N], V v[N]) { - static_assert(!utils::isPowerOf2(N), "must be non-power-of-2"); - static_assert(N >= 3, "must be N >= 3"); - - constexpr int kNextHighestPowerOf2 = utils::nextHighestPowerOf2(N); - -#pragma unroll - for (int i = 0; i < N - kNextHighestPowerOf2 / 2; ++i) { - K& ka = k[i]; - V& va = v[i]; - - K& kb = k[i + kNextHighestPowerOf2 / 2]; - V& vb = v[i + kNextHighestPowerOf2 / 2]; - - bool s = Dir ? Comp::gt(ka, kb) : Comp::lt(ka, kb); - swap(s, ka, kb); - swap(s, va, vb); - } - - constexpr int kLowSize = N - kNextHighestPowerOf2 / 2; - constexpr int kHighSize = kNextHighestPowerOf2 / 2; - { - K newK[kLowSize]; - V newV[kLowSize]; - -#pragma unroll - for (int i = 0; i < kLowSize; ++i) { - newK[i] = k[i]; - newV[i] = v[i]; - } - - constexpr bool kLowIsPowerOf2 = - utils::isPowerOf2(N - kNextHighestPowerOf2 / 2); - // FIXME: compiler doesn't like this expression? compiler bug? -// constexpr bool kLowIsPowerOf2 = utils::isPowerOf2(kLowSize); - BitonicMergeStep::merge(newK, newV); - -#pragma unroll - for (int i = 0; i < kLowSize; ++i) { - k[i] = newK[i]; - v[i] = newV[i]; - } - } - - { - K newK[kHighSize]; - V newV[kHighSize]; - -#pragma unroll - for (int i = 0; i < kHighSize; ++i) { - newK[i] = k[i + kLowSize]; - newV[i] = v[i + kLowSize]; - } - - constexpr bool kHighIsPowerOf2 = - utils::isPowerOf2(kNextHighestPowerOf2 / 2); - // FIXME: compiler doesn't like this expression? compiler bug? -// constexpr bool kHighIsPowerOf2 = utils::isPowerOf2(kHighSize); - BitonicMergeStep::merge(newK, newV); - -#pragma unroll - for (int i = 0; i < kHighSize; ++i) { - k[i + kLowSize] = newK[i]; - v[i + kLowSize] = newV[i]; - } - } - } -}; - -// High recursion -template -struct BitonicMergeStep { - static inline __device__ void merge(K k[N], V v[N]) { - static_assert(!utils::isPowerOf2(N), "must be non-power-of-2"); - static_assert(N >= 3, "must be N >= 3"); - - constexpr int kNextHighestPowerOf2 = utils::nextHighestPowerOf2(N); - -#pragma unroll - for (int i = 0; i < N - kNextHighestPowerOf2 / 2; ++i) { - K& ka = k[i]; - V& va = v[i]; - - K& kb = k[i + kNextHighestPowerOf2 / 2]; - V& vb = v[i + kNextHighestPowerOf2 / 2]; - - bool s = Dir ? Comp::gt(ka, kb) : Comp::lt(ka, kb); - swap(s, ka, kb); - swap(s, va, vb); - } - - constexpr int kLowSize = kNextHighestPowerOf2 / 2; - constexpr int kHighSize = N - kNextHighestPowerOf2 / 2; - { - K newK[kLowSize]; - V newV[kLowSize]; - -#pragma unroll - for (int i = 0; i < kLowSize; ++i) { - newK[i] = k[i]; - newV[i] = v[i]; - } - - constexpr bool kLowIsPowerOf2 = - utils::isPowerOf2(kNextHighestPowerOf2 / 2); - // FIXME: compiler doesn't like this expression? compiler bug? -// constexpr bool kLowIsPowerOf2 = utils::isPowerOf2(kLowSize); - BitonicMergeStep::merge(newK, newV); - -#pragma unroll - for (int i = 0; i < kLowSize; ++i) { - k[i] = newK[i]; - v[i] = newV[i]; - } - } - - { - K newK[kHighSize]; - V newV[kHighSize]; - -#pragma unroll - for (int i = 0; i < kHighSize; ++i) { - newK[i] = k[i + kLowSize]; - newV[i] = v[i + kLowSize]; - } - - constexpr bool kHighIsPowerOf2 = - utils::isPowerOf2(N - kNextHighestPowerOf2 / 2); - // FIXME: compiler doesn't like this expression? compiler bug? -// constexpr bool kHighIsPowerOf2 = utils::isPowerOf2(kHighSize); - BitonicMergeStep::merge(newK, newV); - -#pragma unroll - for (int i = 0; i < kHighSize; ++i) { - k[i + kLowSize] = newK[i]; - v[i + kLowSize] = newV[i]; - } - } - } -}; - -/// Merges two sets of registers across the warp of any size; -/// i.e., merges a sorted k/v list of size kWarpSize * N1 with a -/// sorted k/v list of size kWarpSize * N2, where N1 and N2 are any -/// value >= 1 -template -inline __device__ void warpMergeAnyRegisters(K k1[N1], V v1[N1], - K k2[N2], V v2[N2]) { - constexpr int kSmallestN = N1 < N2 ? N1 : N2; - -#pragma unroll - for (int i = 0; i < kSmallestN; ++i) { - K& ka = k1[N1 - 1 - i]; - V& va = v1[N1 - 1 - i]; - - K& kb = k2[i]; - V& vb = v2[i]; - - K otherKa; - V otherVa; - - if (FullMerge) { - // We need the other values - otherKa = shfl_xor(ka, kWarpSize - 1); - otherVa = shfl_xor(va, kWarpSize - 1); - } - - K otherKb = shfl_xor(kb, kWarpSize - 1); - V otherVb = shfl_xor(vb, kWarpSize - 1); - - // ka is always first in the list, so we needn't use our lane - // in this comparison - bool swapa = Dir ? Comp::gt(ka, otherKb) : Comp::lt(ka, otherKb); - assign(swapa, ka, otherKb); - assign(swapa, va, otherVb); - - // kb is always second in the list, so we needn't use our lane - // in this comparison - if (FullMerge) { - bool swapb = Dir ? Comp::lt(kb, otherKa) : Comp::gt(kb, otherKa); - assign(swapb, kb, otherKa); - assign(swapb, vb, otherVa); - - } else { - // We don't care about updating elements in the second list - } - } - - BitonicMergeStep::merge(k1, v1); - if (FullMerge) { - // Only if we care about N2 do we need to bother merging it fully - BitonicMergeStep::merge(k2, v2); - } -} - -// Recursive template that uses the above bitonic merge to perform a -// bitonic sort -template -struct BitonicSortStep { - static inline __device__ void sort(K k[N], V v[N]) { - static_assert(N > 1, "did not hit specialized case"); - - // Sort recursively - constexpr int kSizeA = N / 2; - constexpr int kSizeB = N - kSizeA; - - K aK[kSizeA]; - V aV[kSizeA]; - -#pragma unroll - for (int i = 0; i < kSizeA; ++i) { - aK[i] = k[i]; - aV[i] = v[i]; - } - - BitonicSortStep::sort(aK, aV); - - K bK[kSizeB]; - V bV[kSizeB]; - -#pragma unroll - for (int i = 0; i < kSizeB; ++i) { - bK[i] = k[i + kSizeA]; - bV[i] = v[i + kSizeA]; - } - - BitonicSortStep::sort(bK, bV); - - // Merge halves - warpMergeAnyRegisters(aK, aV, bK, bV); - -#pragma unroll - for (int i = 0; i < kSizeA; ++i) { - k[i] = aK[i]; - v[i] = aV[i]; - } - -#pragma unroll - for (int i = 0; i < kSizeB; ++i) { - k[i + kSizeA] = bK[i]; - v[i + kSizeA] = bV[i]; - } - } -}; - -// Single warp (N == 1) sorting specialization -template -struct BitonicSortStep { - static inline __device__ void sort(K k[1], V v[1]) { - // Update this code if this changes - // should go from 1 -> kWarpSize in multiples of 2 - static_assert(kWarpSize == 32, "unexpected warp size"); - - warpBitonicMergeLE16(k[0], v[0]); - warpBitonicMergeLE16(k[0], v[0]); - warpBitonicMergeLE16(k[0], v[0]); - warpBitonicMergeLE16(k[0], v[0]); - warpBitonicMergeLE16(k[0], v[0]); - } -}; - -/// Sort a list of kWarpSize * N elements in registers, where N is an -/// arbitrary >= 1 -template -inline __device__ void warpSortAnyRegisters(K k[N], V v[N]) { - BitonicSortStep::sort(k, v); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/NoTypeTensor.cuh b/core/src/index/thirdparty/faiss/gpu/utils/NoTypeTensor.cuh deleted file mode 100644 index fdbc879f35..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/NoTypeTensor.cuh +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include - -namespace faiss { namespace gpu { - -template -class NoTypeTensor { - public: - NoTypeTensor() - : mem_(nullptr), - typeSize_(0) { - } - - template - NoTypeTensor(Tensor& t) - : mem_(t.data()), - typeSize_(sizeof(T)) { - for (int i = 0; i < Dim; ++i) { - size_[i] = t.getSize(i); - stride_[i] = t.getStride(i); - } - } - - NoTypeTensor(void* mem, int typeSize, std::initializer_list sizes) - : mem_(mem), - typeSize_(typeSize) { - - int i = 0; - for (auto s : sizes) { - size_[i++] = s; - } - - stride_[Dim - 1] = (IndexT) 1; - for (int j = Dim - 2; j >= 0; --j) { - stride_[j] = stride_[j + 1] * size_[j + 1]; - } - } - - NoTypeTensor(void* mem, int typeSize, int sizes[Dim]) - : mem_(mem), - typeSize_(typeSize) { - for (int i = 0; i < Dim; ++i) { - size_[i] = sizes[i]; - } - - stride_[Dim - 1] = (IndexT) 1; - for (int i = Dim - 2; i >= 0; --i) { - stride_[i] = stride_[i + 1] * sizes[i + 1]; - } - } - - NoTypeTensor(void* mem, int typeSize, - IndexT sizes[Dim], IndexT strides[Dim]) - : mem_(mem), - typeSize_(typeSize) { - for (int i = 0; i < Dim; ++i) { - size_[i] = sizes[i]; - stride_[i] = strides[i]; - } - } - - int getTypeSize() const { - return typeSize_; - } - - IndexT getSize(int dim) const { - FAISS_ASSERT(dim < Dim); - return size_[dim]; - } - - IndexT getStride(int dim) const { - FAISS_ASSERT(dim < Dim); - return stride_[dim]; - } - - template - Tensor toTensor() { - FAISS_ASSERT(sizeof(T) == typeSize_); - - return Tensor((T*) mem_, size_, stride_); - } - - NoTypeTensor narrowOutermost(IndexT start, - IndexT size) { - char* newPtr = (char*) mem_; - - if (start > 0) { - newPtr += typeSize_ * start * stride_[0]; - } - - IndexT newSize[Dim]; - for (int i = 0; i < Dim; ++i) { - if (i == 0) { - assert(start + size <= size_[0]); - newSize[i] = size; - } else { - newSize[i] = size_[i]; - } - } - - return NoTypeTensor( - newPtr, typeSize_, newSize, stride_); - } - - private: - void* mem_; - int typeSize_; - IndexT size_[Dim]; - IndexT stride_[Dim]; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/Pair.cuh b/core/src/index/thirdparty/faiss/gpu/utils/Pair.cuh deleted file mode 100644 index 0162c91a70..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/Pair.cuh +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include - -namespace faiss { namespace gpu { - -/// A simple pair type for CUDA device usage -template -struct Pair { - constexpr __device__ inline Pair() { - } - - constexpr __device__ inline Pair(K key, V value) - : k(key), v(value) { - } - - __device__ inline bool - operator==(const Pair& rhs) const { - return Math::eq(k, rhs.k) && Math::eq(v, rhs.v); - } - - __device__ inline bool - operator!=(const Pair& rhs) const { - return !operator==(rhs); - } - - __device__ inline bool - operator<(const Pair& rhs) const { - return Math::lt(k, rhs.k) || - (Math::eq(k, rhs.k) && Math::lt(v, rhs.v)); - } - - __device__ inline bool - operator>(const Pair& rhs) const { - return Math::gt(k, rhs.k) || - (Math::eq(k, rhs.k) && Math::gt(v, rhs.v)); - } - - K k; - V v; -}; - -template -inline __device__ Pair shfl_up(const Pair& pair, - unsigned int delta, - int width = kWarpSize) { - return Pair(shfl_up(pair.k, delta, width), - shfl_up(pair.v, delta, width)); -} - -template -inline __device__ Pair shfl_xor(const Pair& pair, - int laneMask, - int width = kWarpSize) { - return Pair(shfl_xor(pair.k, laneMask, width), - shfl_xor(pair.v, laneMask, width)); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/PtxUtils.cuh b/core/src/index/thirdparty/faiss/gpu/utils/PtxUtils.cuh deleted file mode 100644 index d1fad3905f..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/PtxUtils.cuh +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include - -namespace faiss { namespace gpu { - -__device__ __forceinline__ -unsigned int getBitfield(unsigned int val, int pos, int len) { - unsigned int ret; - asm("bfe.u32 %0, %1, %2, %3;" : "=r"(ret) : "r"(val), "r"(pos), "r"(len)); - return ret; -} - -__device__ __forceinline__ -unsigned long getBitfield(unsigned long val, int pos, int len) { - unsigned long ret; - asm("bfe.u64 %0, %1, %2, %3;" : "=l"(ret) : "l"(val), "r"(pos), "r"(len)); - return ret; -} - -__device__ __forceinline__ -unsigned int setBitfield(unsigned int val, - unsigned int toInsert, int pos, int len) { - unsigned int ret; - asm("bfi.b32 %0, %1, %2, %3, %4;" : - "=r"(ret) : "r"(toInsert), "r"(val), "r"(pos), "r"(len)); - return ret; -} - -__device__ __forceinline__ int getLaneId() { - int laneId; - asm("mov.u32 %0, %laneid;" : "=r"(laneId) ); - return laneId; -} - -__device__ __forceinline__ unsigned getLaneMaskLt() { - unsigned mask; - asm("mov.u32 %0, %%lanemask_lt;" : "=r"(mask)); - return mask; -} - -__device__ __forceinline__ unsigned getLaneMaskLe() { - unsigned mask; - asm("mov.u32 %0, %%lanemask_le;" : "=r"(mask)); - return mask; -} - -__device__ __forceinline__ unsigned getLaneMaskGt() { - unsigned mask; - asm("mov.u32 %0, %%lanemask_gt;" : "=r"(mask)); - return mask; -} - -__device__ __forceinline__ unsigned getLaneMaskGe() { - unsigned mask; - asm("mov.u32 %0, %%lanemask_ge;" : "=r"(mask)); - return mask; -} - -__device__ __forceinline__ void namedBarrierWait(int name, int numThreads) { - asm volatile("bar.sync %0, %1;" : : "r"(name), "r"(numThreads) : "memory"); -} - -__device__ __forceinline__ void namedBarrierArrived(int name, int numThreads) { - asm volatile("bar.arrive %0, %1;" : : "r"(name), "r"(numThreads) : "memory"); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/ReductionOperators.cuh b/core/src/index/thirdparty/faiss/gpu/utils/ReductionOperators.cuh deleted file mode 100644 index b810fc66ea..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/ReductionOperators.cuh +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -template -struct Sum { - __device__ inline T operator()(T a, T b) const { - return Math::add(a, b); - } - - inline __device__ T identity() const { - return Math::zero(); - } -}; - -template -struct Min { - __device__ inline T operator()(T a, T b) const { - return Math::lt(a, b) ? a : b; - } - - inline __device__ T identity() const { - return Limits::getMax(); - } -}; - -template -struct Max { - __device__ inline T operator()(T a, T b) const { - return Math::gt(a, b) ? a : b; - } - - inline __device__ T identity() const { - return Limits::getMin(); - } -}; - -/// Used for producing segmented prefix scans; the value of the Pair -/// denotes the start of a new segment for the scan -template -struct SegmentedReduce { - inline __device__ SegmentedReduce(const ReduceOp& o) - : op(o) { - } - - __device__ - inline Pair - operator()(const Pair& a, const Pair& b) const { - return Pair(b.v ? b.k : op(a.k, b.k), - a.v || b.v); - } - - inline __device__ Pair identity() const { - return Pair(op.identity(), false); - } - - ReduceOp op; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/Reductions.cuh b/core/src/index/thirdparty/faiss/gpu/utils/Reductions.cuh deleted file mode 100644 index e99b518630..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/Reductions.cuh +++ /dev/null @@ -1,142 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -template -__device__ inline T warpReduceAll(T val, Op op) { -#pragma unroll - for (int mask = ReduceWidth / 2; mask > 0; mask >>= 1) { - val = op(val, shfl_xor(val, mask)); - } - - return val; -} - -/// Sums a register value across all warp threads -template -__device__ inline T warpReduceAllSum(T val) { - return warpReduceAll, ReduceWidth>(val, Sum()); -} - -/// Performs a block-wide reduction -template -__device__ inline T blockReduceAll(T val, Op op, T* smem) { - int laneId = getLaneId(); - int warpId = threadIdx.x / kWarpSize; - - val = warpReduceAll(val, op); - if (laneId == 0) { - smem[warpId] = val; - } - __syncthreads(); - - if (warpId == 0) { - val = laneId < utils::divUp(blockDim.x, kWarpSize) ? smem[laneId] : - op.identity(); - val = warpReduceAll(val, op); - - if (BroadcastAll) { - __threadfence_block(); - - if (laneId == 0) { - smem[0] = val; - } - } - } - - if (BroadcastAll) { - __syncthreads(); - val = smem[0]; - } - - if (KillWARDependency) { - __syncthreads(); - } - - return val; -} - -/// Performs a block-wide reduction of multiple values simultaneously -template -__device__ inline void blockReduceAll(T val[Num], Op op, T* smem) { - int laneId = getLaneId(); - int warpId = threadIdx.x / kWarpSize; - -#pragma unroll - for (int i = 0; i < Num; ++i) { - val[i] = warpReduceAll(val[i], op); - } - - if (laneId == 0) { -#pragma unroll - for (int i = 0; i < Num; ++i) { - smem[warpId * Num + i] = val[i]; - } - } - - __syncthreads(); - - if (warpId == 0) { -#pragma unroll - for (int i = 0; i < Num; ++i) { - val[i] = - laneId < utils::divUp(blockDim.x, kWarpSize) ? smem[laneId * Num + i] : - op.identity(); - val[i] = warpReduceAll(val[i], op); - } - - if (BroadcastAll) { - __threadfence_block(); - - if (laneId == 0) { -#pragma unroll - for (int i = 0; i < Num; ++i) { - smem[i] = val[i]; - } - } - } - } - - if (BroadcastAll) { - __syncthreads(); -#pragma unroll - for (int i = 0; i < Num; ++i) { - val[i] = smem[i]; - } - } - - if (KillWARDependency) { - __syncthreads(); - } -} - - -/// Sums a register value across the entire block -template -__device__ inline T blockReduceAllSum(T val, T* smem) { - return blockReduceAll, BroadcastAll, KillWARDependency>( - val, Sum(), smem); -} - -template -__device__ inline void blockReduceAllSum(T vals[Num], T* smem) { - return blockReduceAll, BroadcastAll, KillWARDependency>( - vals, Sum(), smem); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/Select.cuh b/core/src/index/thirdparty/faiss/gpu/utils/Select.cuh deleted file mode 100644 index 0dad487140..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/Select.cuh +++ /dev/null @@ -1,563 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -// Specialization for block-wide monotonic merges producing a merge sort -// since what we really want is a constexpr loop expansion -template -struct FinalBlockMerge { -}; - -template -struct FinalBlockMerge<1, NumThreads, K, V, NumWarpQ, Dir, Comp> { - static inline __device__ void merge(K* sharedK, V* sharedV) { - // no merge required; single warp - } -}; - -template -struct FinalBlockMerge<2, NumThreads, K, V, NumWarpQ, Dir, Comp> { - static inline __device__ void merge(K* sharedK, V* sharedV) { - // Final merge doesn't need to fully merge the second list - blockMerge(sharedK, sharedV); - } -}; - -template -struct FinalBlockMerge<4, NumThreads, K, V, NumWarpQ, Dir, Comp> { - static inline __device__ void merge(K* sharedK, V* sharedV) { - blockMerge(sharedK, sharedV); - // Final merge doesn't need to fully merge the second list - blockMerge(sharedK, sharedV); - } -}; - -template -struct FinalBlockMerge<8, NumThreads, K, V, NumWarpQ, Dir, Comp> { - static inline __device__ void merge(K* sharedK, V* sharedV) { - blockMerge(sharedK, sharedV); - blockMerge(sharedK, sharedV); - // Final merge doesn't need to fully merge the second list - blockMerge(sharedK, sharedV); - } -}; - -// `Dir` true, produce largest values. -// `Dir` false, produce smallest values. -template -struct BlockSelect { - static constexpr int kNumWarps = ThreadsPerBlock / kWarpSize; - static constexpr int kTotalWarpSortSize = NumWarpQ; - - __device__ inline BlockSelect(K initKVal, - V initVVal, - K* smemK, - V* smemV, - int k) : - initK(initKVal), - initV(initVVal), - numVals(0), - warpKTop(initKVal), - sharedK(smemK), - sharedV(smemV), - kMinus1(k - 1) { - static_assert(utils::isPowerOf2(ThreadsPerBlock), - "threads must be a power-of-2"); - static_assert(utils::isPowerOf2(NumWarpQ), - "warp queue must be power-of-2"); - - // Fill the per-thread queue keys with the default value -#pragma unroll - for (int i = 0; i < NumThreadQ; ++i) { - threadK[i] = initK; - threadV[i] = initV; - } - - int laneId = getLaneId(); - int warpId = threadIdx.x / kWarpSize; - warpK = sharedK + warpId * kTotalWarpSortSize; - warpV = sharedV + warpId * kTotalWarpSortSize; - - // Fill warp queue (only the actual queue space is fine, not where - // we write the per-thread queues for merging) - for (int i = laneId; i < NumWarpQ; i += kWarpSize) { - warpK[i] = initK; - warpV[i] = initV; - } - - warpFence(); - } - - __device__ inline void addThreadQ(K k, V v) { - if (Dir ? Comp::gt(k, warpKTop) : Comp::lt(k, warpKTop)) { - threadK[numVals] = k; - threadV[numVals ++] = v; - } - } - - __device__ inline void checkThreadQ() { - bool needSort = (numVals == NumThreadQ); - -#if CUDA_VERSION >= 9000 - needSort = __any_sync(0xffffffff, needSort); -#else - needSort = __any(needSort); -#endif - - if (!needSort) { - // no lanes have triggered a sort - return; - } - - // This has a trailing warpFence - mergeWarpQ(); - - // Any top-k elements have been merged into the warp queue; we're - // free to reset the thread queues - numVals = 0; - -#pragma unroll - for (int i = 0; i < NumThreadQ; ++i) { - threadK[i] = initK; - threadV[i] = initV; - } - - // We have to beat at least this element - warpKTop = warpK[kMinus1]; - - warpFence(); - } - - /// This function handles sorting and merging together the - /// per-thread queues with the warp-wide queue, creating a sorted - /// list across both - __device__ inline void mergeWarpQ() { - int laneId = getLaneId(); - - // Sort all of the per-thread queues - warpSortAnyRegisters(threadK, threadV); - - constexpr int kNumWarpQRegisters = NumWarpQ / kWarpSize; - K warpKRegisters[kNumWarpQRegisters]; - V warpVRegisters[kNumWarpQRegisters]; - -#pragma unroll - for (int i = 0; i < kNumWarpQRegisters; ++i) { - warpKRegisters[i] = warpK[i * kWarpSize + laneId]; - warpVRegisters[i] = warpV[i * kWarpSize + laneId]; - } - - warpFence(); - - // The warp queue is already sorted, and now that we've sorted the - // per-thread queue, merge both sorted lists together, producing - // one sorted list - warpMergeAnyRegisters( - warpKRegisters, warpVRegisters, threadK, threadV); - - // Write back out the warp queue -#pragma unroll - for (int i = 0; i < kNumWarpQRegisters; ++i) { - warpK[i * kWarpSize + laneId] = warpKRegisters[i]; - warpV[i * kWarpSize + laneId] = warpVRegisters[i]; - } - - warpFence(); - } - - /// WARNING: all threads in a warp must participate in this. - /// Otherwise, you must call the constituent parts separately. - __device__ inline void add(K k, V v) { - addThreadQ(k, v); - checkThreadQ(); - } - - __device__ inline void reduce() { - // Have all warps dump and merge their queues; this will produce - // the final per-warp results - mergeWarpQ(); - - // block-wide dep; thus far, all warps have been completely - // independent - __syncthreads(); - - // All warp queues are contiguous in smem. - // Now, we have kNumWarps lists of NumWarpQ elements. - // This is a power of 2. - FinalBlockMerge:: - merge(sharedK, sharedV); - - // The block-wide merge has a trailing syncthreads - } - - // Default element key - const K initK; - - // Default element value - const V initV; - - // Number of valid elements in our thread queue - int numVals; - - // The k-th highest (Dir) or lowest (!Dir) element - K warpKTop; - - // Thread queue values - K threadK[NumThreadQ]; - V threadV[NumThreadQ]; - - // Queues for all warps - K* sharedK; - V* sharedV; - - // Our warp's queue (points into sharedK/sharedV) - // warpK[0] is highest (Dir) or lowest (!Dir) - K* warpK; - V* warpV; - - // This is a cached k-1 value - int kMinus1; -}; - -/// Specialization for k == 1 (NumWarpQ == 1) -template -struct BlockSelect { - static constexpr int kNumWarps = ThreadsPerBlock / kWarpSize; - - __device__ inline BlockSelect(K initK, V initV, K* smemK, V* smemV, int k) : - sharedK(smemK), - sharedV(smemV), - threadK(initK), - threadV(initV) { - } - - __device__ inline void addThreadQ(K k, V v) { - bool swap = Dir ? Comp::gt(k, threadK) : Comp::lt(k, threadK); - threadK = swap ? k : threadK; - threadV = swap ? v : threadV; - } - - __device__ inline void checkThreadQ() { - // We don't need to do anything here, since the warp doesn't - // cooperate until the end - } - - __device__ inline void add(K k, V v) { - addThreadQ(k, v); - } - - __device__ inline void reduce() { - // Reduce within the warp - Pair pair(threadK, threadV); - - if (Dir) { - pair = - warpReduceAll, Max>>(pair, Max>()); - } else { - pair = - warpReduceAll, Min>>(pair, Min>()); - } - - // Each warp writes out a single value - int laneId = getLaneId(); - int warpId = threadIdx.x / kWarpSize; - - if (laneId == 0) { - sharedK[warpId] = pair.k; - sharedV[warpId] = pair.v; - } - - __syncthreads(); - - // We typically use this for small blocks (<= 128), just having the first - // thread in the block perform the reduction across warps is - // faster - if (threadIdx.x == 0) { - threadK = sharedK[0]; - threadV = sharedV[0]; - -#pragma unroll - for (int i = 1; i < kNumWarps; ++i) { - K k = sharedK[i]; - V v = sharedV[i]; - - bool swap = Dir ? Comp::gt(k, threadK) : Comp::lt(k, threadK); - threadK = swap ? k : threadK; - threadV = swap ? v : threadV; - } - - // Hopefully a thread's smem reads/writes are ordered wrt - // itself, so no barrier needed :) - sharedK[0] = threadK; - sharedV[0] = threadV; - } - - // In case other threads wish to read this value - __syncthreads(); - } - - // threadK is lowest (Dir) or highest (!Dir) - K threadK; - V threadV; - - // Where we reduce in smem - K* sharedK; - V* sharedV; -}; - -// -// per-warp WarpSelect -// - -// `Dir` true, produce largest values. -// `Dir` false, produce smallest values. -template -struct WarpSelect { - static constexpr int kNumWarpQRegisters = NumWarpQ / kWarpSize; - - __device__ inline WarpSelect(K initKVal, V initVVal, int k) : - initK(initKVal), - initV(initVVal), - numVals(0), - warpKTop(initKVal), - kLane((k - 1) % kWarpSize) { - static_assert(utils::isPowerOf2(ThreadsPerBlock), - "threads must be a power-of-2"); - static_assert(utils::isPowerOf2(NumWarpQ), - "warp queue must be power-of-2"); - - // Fill the per-thread queue keys with the default value -#pragma unroll - for (int i = 0; i < NumThreadQ; ++i) { - threadK[i] = initK; - threadV[i] = initV; - } - - // Fill the warp queue with the default value -#pragma unroll - for (int i = 0; i < kNumWarpQRegisters; ++i) { - warpK[i] = initK; - warpV[i] = initV; - } - } - - __device__ inline void addThreadQ(K k, V v) { - if (Dir ? Comp::gt(k, warpKTop) : Comp::lt(k, warpKTop)) { - // Rotate right -#pragma unroll - for (int i = NumThreadQ - 1; i > 0; --i) { - threadK[i] = threadK[i - 1]; - threadV[i] = threadV[i - 1]; - } - - threadK[0] = k; - threadV[0] = v; - ++numVals; - } - } - - __device__ inline void checkThreadQ() { - bool needSort = (numVals == NumThreadQ); - -#if CUDA_VERSION >= 9000 - needSort = __any_sync(0xffffffff, needSort); -#else - needSort = __any(needSort); -#endif - - if (!needSort) { - // no lanes have triggered a sort - return; - } - - mergeWarpQ(); - - // Any top-k elements have been merged into the warp queue; we're - // free to reset the thread queues - numVals = 0; - -#pragma unroll - for (int i = 0; i < NumThreadQ; ++i) { - threadK[i] = initK; - threadV[i] = initV; - } - - // We have to beat at least this element - warpKTop = shfl(warpK[kNumWarpQRegisters - 1], kLane); - } - - /// This function handles sorting and merging together the - /// per-thread queues with the warp-wide queue, creating a sorted - /// list across both - __device__ inline void mergeWarpQ() { - // Sort all of the per-thread queues - warpSortAnyRegisters(threadK, threadV); - - // The warp queue is already sorted, and now that we've sorted the - // per-thread queue, merge both sorted lists together, producing - // one sorted list - warpMergeAnyRegisters( - warpK, warpV, threadK, threadV); - } - - /// WARNING: all threads in a warp must participate in this. - /// Otherwise, you must call the constituent parts separately. - __device__ inline void add(K k, V v) { - addThreadQ(k, v); - checkThreadQ(); - } - - __device__ inline void reduce() { - // Have all warps dump and merge their queues; this will produce - // the final per-warp results - mergeWarpQ(); - } - - /// Dump final k selected values for this warp out - __device__ inline void writeOut(K* outK, V* outV, int k) { - int laneId = getLaneId(); - -#pragma unroll - for (int i = 0; i < kNumWarpQRegisters; ++i) { - int idx = i * kWarpSize + laneId; - - if (idx < k) { - outK[idx] = warpK[i]; - outV[idx] = warpV[i]; - } - } - } - - // Default element key - const K initK; - - // Default element value - const V initV; - - // Number of valid elements in our thread queue - int numVals; - - // The k-th highest (Dir) or lowest (!Dir) element - K warpKTop; - - // Thread queue values - K threadK[NumThreadQ]; - V threadV[NumThreadQ]; - - // warpK[0] is highest (Dir) or lowest (!Dir) - K warpK[kNumWarpQRegisters]; - V warpV[kNumWarpQRegisters]; - - // This is what lane we should load an approximation (>=k) to the - // kth element from the last register in the warp queue (i.e., - // warpK[kNumWarpQRegisters - 1]). - int kLane; -}; - -/// Specialization for k == 1 (NumWarpQ == 1) -template -struct WarpSelect { - static constexpr int kNumWarps = ThreadsPerBlock / kWarpSize; - - __device__ inline WarpSelect(K initK, V initV, int k) : - threadK(initK), - threadV(initV) { - } - - __device__ inline void addThreadQ(K k, V v) { - bool swap = Dir ? Comp::gt(k, threadK) : Comp::lt(k, threadK); - threadK = swap ? k : threadK; - threadV = swap ? v : threadV; - } - - __device__ inline void checkThreadQ() { - // We don't need to do anything here, since the warp doesn't - // cooperate until the end - } - - __device__ inline void add(K k, V v) { - addThreadQ(k, v); - } - - __device__ inline void reduce() { - // Reduce within the warp - Pair pair(threadK, threadV); - - if (Dir) { - pair = - warpReduceAll, Max>>(pair, Max>()); - } else { - pair = - warpReduceAll, Min>>(pair, Min>()); - } - - threadK = pair.k; - threadV = pair.v; - } - - /// Dump final k selected values for this warp out - __device__ inline void writeOut(K* outK, V* outV, int k) { - if (getLaneId() == 0) { - *outK = threadK; - *outV = threadV; - } - } - - // threadK is lowest (Dir) or highest (!Dir) - K threadK; - V threadV; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/StackDeviceMemory.cpp b/core/src/index/thirdparty/faiss/gpu/utils/StackDeviceMemory.cpp deleted file mode 100644 index 18b8e04cff..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/StackDeviceMemory.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -StackDeviceMemory::Stack::Stack(int d, size_t sz) - : device_(d), - isOwner_(true), - start_(nullptr), - end_(nullptr), - size_(sz), - head_(nullptr), - mallocCurrent_(0), - highWaterMemoryUsed_(0), - highWaterMalloc_(0), - cudaMallocWarning_(true) { - DeviceScope s(device_); - - allocMemorySpace(MemorySpace::Device, &start_, size_); - - head_ = start_; - end_ = start_ + size_; -} - -StackDeviceMemory::Stack::Stack(int d, void* p, size_t sz, bool isOwner) - : device_(d), - isOwner_(isOwner), - start_((char*) p), - end_(((char*) p) + sz), - size_(sz), - head_((char*) p), - mallocCurrent_(0), - highWaterMemoryUsed_(0), - highWaterMalloc_(0), - cudaMallocWarning_(true) { -} - -StackDeviceMemory::Stack::~Stack() { - if (isOwner_) { - DeviceScope s(device_); - - freeMemorySpace(MemorySpace::Device, start_); - } -} - -size_t -StackDeviceMemory::Stack::getSizeAvailable() const { - return (end_ - head_); -} - -char* -StackDeviceMemory::Stack::getAlloc(size_t size, - cudaStream_t stream) { - if (size > (end_ - head_)) { - // Too large for our stack - DeviceScope s(device_); - - if (cudaMallocWarning_) { - // Print our requested size before we attempt the allocation - fprintf(stderr, "WARN: increase temp memory to avoid cudaMalloc, " - "or decrease query/add size (alloc %zu B, highwater %zu B)\n", - size, highWaterMalloc_); - } - - char* p = nullptr; - allocMemorySpace(MemorySpace::Device, &p, size); - - mallocCurrent_ += size; - highWaterMalloc_ = std::max(highWaterMalloc_, mallocCurrent_); - - return p; - } else { - // We can make the allocation out of our stack - // Find all the ranges that we overlap that may have been - // previously allocated; our allocation will be [head, endAlloc) - char* startAlloc = head_; - char* endAlloc = head_ + size; - - while (lastUsers_.size() > 0) { - auto& prevUser = lastUsers_.back(); - - // Because there is a previous user, we must overlap it - FAISS_ASSERT(prevUser.start_ <= endAlloc && prevUser.end_ >= startAlloc); - - if (stream != prevUser.stream_) { - // Synchronization required - // FIXME - FAISS_ASSERT(false); - } - - if (endAlloc < prevUser.end_) { - // Update the previous user info - prevUser.start_ = endAlloc; - - break; - } - - // If we're the exact size of the previous request, then we - // don't need to continue - bool done = (prevUser.end_ == endAlloc); - - lastUsers_.pop_back(); - - if (done) { - break; - } - } - - head_ = endAlloc; - FAISS_ASSERT(head_ <= end_); - - highWaterMemoryUsed_ = std::max(highWaterMemoryUsed_, - (size_t) (head_ - start_)); - return startAlloc; - } -} - -void -StackDeviceMemory::Stack::returnAlloc(char* p, - size_t size, - cudaStream_t stream) { - if (p < start_ || p >= end_) { - // This is not on our stack; it was a one-off allocation - DeviceScope s(device_); - - freeMemorySpace(MemorySpace::Device, p); - - FAISS_ASSERT(mallocCurrent_ >= size); - mallocCurrent_ -= size; - } else { - // This is on our stack - // Allocations should be freed in the reverse order they are made - FAISS_ASSERT(p + size == head_); - - head_ = p; - lastUsers_.push_back(Range(p, p + size, stream)); - } -} - -std::string -StackDeviceMemory::Stack::toString() const { - std::stringstream s; - - s << "SDM device " << device_ << ": Total memory " << size_ << " [" - << (void*) start_ << ", " << (void*) end_ << ")\n"; - s << " Available memory " << (size_t) (end_ - head_) - << " [" << (void*) head_ << ", " << (void*) end_ << ")\n"; - s << " High water temp alloc " << highWaterMemoryUsed_ << "\n"; - s << " High water cudaMalloc " << highWaterMalloc_ << "\n"; - - int i = lastUsers_.size(); - for (auto it = lastUsers_.rbegin(); it != lastUsers_.rend(); ++it) { - s << i-- << ": size " << (size_t) (it->end_ - it->start_) - << " stream " << it->stream_ - << " [" << (void*) it->start_ << ", " << (void*) it->end_ << ")\n"; - } - - return s.str(); -} - -size_t -StackDeviceMemory::Stack::getHighWaterCudaMalloc() const { - return highWaterMalloc_; -} - -StackDeviceMemory::StackDeviceMemory(int device, size_t allocPerDevice) - : device_(device), - stack_(device, allocPerDevice) { -} - -StackDeviceMemory::StackDeviceMemory(int device, - void* p, size_t size, bool isOwner) - : device_(device), - stack_(device, p, size, isOwner) { -} - -StackDeviceMemory::~StackDeviceMemory() { -} - -void -StackDeviceMemory::setCudaMallocWarning(bool b) { - stack_.cudaMallocWarning_ = b; -} - -int -StackDeviceMemory::getDevice() const { - return device_; -} - -DeviceMemoryReservation -StackDeviceMemory::getMemory(cudaStream_t stream, size_t size) { - // We guarantee 16 byte alignment for allocations, so bump up `size` - // to the next highest multiple of 16 - size = utils::roundUp(size, (size_t) 16); - - return DeviceMemoryReservation(this, - device_, - stack_.getAlloc(size, stream), - size, - stream); -} - -size_t -StackDeviceMemory::getSizeAvailable() const { - return stack_.getSizeAvailable(); -} - -std::string -StackDeviceMemory::toString() const { - return stack_.toString(); -} - -size_t -StackDeviceMemory::getHighWaterCudaMalloc() const { - return stack_.getHighWaterCudaMalloc(); -} - -void -StackDeviceMemory::returnAllocation(DeviceMemoryReservation& m) { - FAISS_ASSERT(m.get()); - FAISS_ASSERT(device_ == m.device()); - - stack_.returnAlloc((char*) m.get(), m.size(), m.stream()); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/StackDeviceMemory.h b/core/src/index/thirdparty/faiss/gpu/utils/StackDeviceMemory.h deleted file mode 100644 index f7c3ea14e4..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/StackDeviceMemory.h +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -/// Device memory manager that provides temporary memory allocations -/// out of a region of memory -class StackDeviceMemory : public DeviceMemory { - public: - /// Allocate a new region of memory that we manage - explicit StackDeviceMemory(int device, size_t allocPerDevice); - - /// Manage a region of memory for a particular device, with or - /// without ownership - StackDeviceMemory(int device, void* p, size_t size, bool isOwner); - - ~StackDeviceMemory() override; - - /// Enable or disable the warning about not having enough temporary memory - /// when cudaMalloc gets called - void setCudaMallocWarning(bool b); - - int getDevice() const override; - - DeviceMemoryReservation getMemory(cudaStream_t stream, - size_t size) override; - - size_t getSizeAvailable() const override; - std::string toString() const override; - size_t getHighWaterCudaMalloc() const override; - - protected: - void returnAllocation(DeviceMemoryReservation& m) override; - - protected: - /// Previous allocation ranges and the streams for which - /// synchronization is required - struct Range { - inline Range(char* s, char* e, cudaStream_t str) : - start_(s), end_(e), stream_(str) { - } - - // References a memory range [start, end) - char* start_; - char* end_; - cudaStream_t stream_; - }; - - struct Stack { - /// Constructor that allocates memory via cudaMalloc - Stack(int device, size_t size); - - /// Constructor that references a pre-allocated region of memory - Stack(int device, void* p, size_t size, bool isOwner); - ~Stack(); - - /// Returns how much size is available for an allocation without - /// calling cudaMalloc - size_t getSizeAvailable() const; - - /// Obtains an allocation; all allocations are guaranteed to be 16 - /// byte aligned - char* getAlloc(size_t size, cudaStream_t stream); - - /// Returns an allocation - void returnAlloc(char* p, size_t size, cudaStream_t stream); - - /// Returns the stack state - std::string toString() const; - - /// Returns the high-water mark of cudaMalloc activity - size_t getHighWaterCudaMalloc() const; - - /// Device this allocation is on - int device_; - - /// Do we own our region of memory? - bool isOwner_; - - /// Where our allocation begins and ends - /// [start_, end_) is valid - char* start_; - char* end_; - - /// Total size end_ - start_ - size_t size_; - - /// Stack head within [start, end) - char* head_; - - /// List of previous last users of allocations on our stack, for - /// possible synchronization purposes - std::list lastUsers_; - - /// How much cudaMalloc memory is currently outstanding? - size_t mallocCurrent_; - - /// What's the high water mark in terms of memory used from the - /// temporary buffer? - size_t highWaterMemoryUsed_; - - /// What's the high water mark in terms of memory allocated via - /// cudaMalloc? - size_t highWaterMalloc_; - - /// Whether or not a warning upon cudaMalloc is generated - bool cudaMallocWarning_; - }; - - /// Our device - int device_; - - /// Memory stack - Stack stack_; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/StaticUtils.h b/core/src/index/thirdparty/faiss/gpu/utils/StaticUtils.h deleted file mode 100644 index f6e5505afb..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/StaticUtils.h +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include - -namespace faiss { namespace gpu { namespace utils { - -template -constexpr __host__ __device__ auto divDown(U a, V b) -> decltype(a + b) { - return (a / b); -} - -template -constexpr __host__ __device__ auto divUp(U a, V b) -> decltype(a + b) { - return (a + b - 1) / b; -} - -template -constexpr __host__ __device__ auto roundDown(U a, V b) -> decltype(a + b) { - return divDown(a, b) * b; -} - -template -constexpr __host__ __device__ auto roundUp(U a, V b) -> decltype(a + b) { - return divUp(a, b) * b; -} - -template -constexpr __host__ __device__ T pow(T n, T power) { - return (power > 0 ? n * pow(n, power - 1) : 1); -} - -template -constexpr __host__ __device__ T pow2(T n) { - return pow(2, (T) n); -} - -static_assert(pow2(8) == 256, "pow2"); - -template -constexpr __host__ __device__ int log2(T n, int p = 0) { - return (n <= 1) ? p : log2(n / 2, p + 1); -} - -static_assert(log2(2) == 1, "log2"); -static_assert(log2(3) == 1, "log2"); -static_assert(log2(4) == 2, "log2"); - -template -constexpr __host__ __device__ bool isPowerOf2(T v) { - return (v && !(v & (v - 1))); -} - -static_assert(isPowerOf2(2048), "isPowerOf2"); -static_assert(!isPowerOf2(3333), "isPowerOf2"); - -template -constexpr __host__ __device__ T nextHighestPowerOf2(T v) { - return (isPowerOf2(v) ? (T) 2 * v : ((T) 1 << (log2(v) + 1))); -} - -static_assert(nextHighestPowerOf2(1) == 2, "nextHighestPowerOf2"); -static_assert(nextHighestPowerOf2(2) == 4, "nextHighestPowerOf2"); -static_assert(nextHighestPowerOf2(3) == 4, "nextHighestPowerOf2"); -static_assert(nextHighestPowerOf2(4) == 8, "nextHighestPowerOf2"); - -static_assert(nextHighestPowerOf2(15) == 16, "nextHighestPowerOf2"); -static_assert(nextHighestPowerOf2(16) == 32, "nextHighestPowerOf2"); -static_assert(nextHighestPowerOf2(17) == 32, "nextHighestPowerOf2"); - -static_assert(nextHighestPowerOf2(1536000000u) == 2147483648u, - "nextHighestPowerOf2"); -static_assert(nextHighestPowerOf2((size_t) 2147483648ULL) == - (size_t) 4294967296ULL, "nextHighestPowerOf2"); - -} } } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/Tensor-inl.cuh b/core/src/index/thirdparty/faiss/gpu/utils/Tensor-inl.cuh deleted file mode 100644 index 964fbfb940..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/Tensor-inl.cuh +++ /dev/null @@ -1,746 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include - -namespace faiss { namespace gpu { - -template class PtrTraits> -__host__ __device__ -Tensor::Tensor() - : data_(nullptr) { - static_assert(Dim > 0, "must have > 0 dimensions"); - - for (int i = 0; i < Dim; ++i) { - size_[i] = 0; - stride_[i] = (IndexT) 1; - } -} - -template class PtrTraits> -__host__ __device__ -Tensor::Tensor( - Tensor& t) { - this->operator=(t); -} - -template class PtrTraits> -__host__ __device__ -Tensor::Tensor( - Tensor&& t) { - this->operator=(std::move(t)); -} - -template class PtrTraits> -__host__ __device__ -Tensor& -Tensor::operator=( - Tensor& t) { - data_ = t.data_; - for (int i = 0; i < Dim; ++i) { - size_[i] = t.size_[i]; - stride_[i] = t.stride_[i]; - } - - return *this; -} - -template class PtrTraits> -__host__ __device__ -Tensor& -Tensor::operator=( - Tensor&& t) { - data_ = t.data_; t.data_ = nullptr; - for (int i = 0; i < Dim; ++i) { - stride_[i] = t.stride_[i]; t.stride_[i] = 0; - size_[i] = t.size_[i]; t.size_[i] = 0; - } - - return *this; -} - -template class PtrTraits> -__host__ __device__ -Tensor:: -Tensor(DataPtrType data, const IndexT sizes[Dim]) - : data_(data) { - static_assert(Dim > 0, "must have > 0 dimensions"); - - for (int i = 0; i < Dim; ++i) { - size_[i] = sizes[i]; - } - - stride_[Dim - 1] = (IndexT) 1; - for (int i = Dim - 2; i >= 0; --i) { - stride_[i] = stride_[i + 1] * sizes[i + 1]; - } -} - -template class PtrTraits> -__host__ __device__ -Tensor:: -Tensor(DataPtrType data, std::initializer_list sizes) - : data_(data) { - GPU_FAISS_ASSERT(sizes.size() == Dim); - static_assert(Dim > 0, "must have > 0 dimensions"); - - int i = 0; - for (auto s : sizes) { - size_[i++] = s; - } - - stride_[Dim - 1] = (IndexT) 1; - for (int j = Dim - 2; j >= 0; --j) { - stride_[j] = stride_[j + 1] * size_[j + 1]; - } -} - - -template class PtrTraits> -__host__ __device__ -Tensor::Tensor( - DataPtrType data, const IndexT sizes[Dim], const IndexT strides[Dim]) - : data_(data) { - static_assert(Dim > 0, "must have > 0 dimensions"); - - for (int i = 0; i < Dim; ++i) { - size_[i] = sizes[i]; - stride_[i] = strides[i]; - } -} - -template class PtrTraits> -__host__ void -Tensor::copyFrom( - Tensor& t, - cudaStream_t stream) { - // The tensor must be fully contiguous - GPU_FAISS_ASSERT(this->isContiguous()); - - // Size must be the same (since dimensions are checked and - // continuity is assumed, we need only check total number of - // elements - GPU_FAISS_ASSERT(this->numElements() == t.numElements()); - - if (t.numElements() > 0) { - GPU_FAISS_ASSERT(this->data_); - GPU_FAISS_ASSERT(t.data()); - - int ourDev = getDeviceForAddress(this->data_); - int tDev = getDeviceForAddress(t.data()); - - if (tDev == -1) { - CUDA_VERIFY(cudaMemcpyAsync(this->data_, - t.data(), - this->getSizeInBytes(), - ourDev == -1 ? cudaMemcpyHostToHost : - cudaMemcpyHostToDevice, - stream)); - } else { - CUDA_VERIFY(cudaMemcpyAsync(this->data_, - t.data(), - this->getSizeInBytes(), - ourDev == -1 ? cudaMemcpyDeviceToHost : - cudaMemcpyDeviceToDevice, - stream)); - } - } -} - -template class PtrTraits> -__host__ void -Tensor::copyTo( - Tensor& t, - cudaStream_t stream) { - // The tensor must be fully contiguous - GPU_FAISS_ASSERT(this->isContiguous()); - - // Size must be the same (since dimensions are checked and - // continuity is assumed, we need only check total number of - // elements - GPU_FAISS_ASSERT(this->numElements() == t.numElements()); - - if (t.numElements() > 0) { - GPU_FAISS_ASSERT(this->data_); - GPU_FAISS_ASSERT(t.data()); - - int ourDev = getDeviceForAddress(this->data_); - int tDev = getDeviceForAddress(t.data()); - - if (tDev == -1) { - CUDA_VERIFY(cudaMemcpyAsync(t.data(), - this->data_, - this->getSizeInBytes(), - ourDev == -1 ? cudaMemcpyHostToHost : - cudaMemcpyDeviceToHost, - stream)); - } else { - CUDA_VERIFY(cudaMemcpyAsync(t.data(), - this->data_, - this->getSizeInBytes(), - ourDev == -1 ? cudaMemcpyHostToDevice : - cudaMemcpyDeviceToDevice, - stream)); - } - } -} - -template class PtrTraits> -template -__host__ __device__ bool -Tensor::isSame( - const Tensor& rhs) const { - if (Dim != OtherDim) { - return false; - } - - for (int i = 0; i < Dim; ++i) { - if (this->getSize(i) != rhs.getSize(i)) { - return false; - } - - if (this->getStride(i) != rhs.getStride(i)) { - return false; - } - } - - return true; -} - -template class PtrTraits> -template -__host__ __device__ bool -Tensor::isSameSize( - const Tensor& rhs) const { - if (Dim != OtherDim) { - return false; - } - - for (int i = 0; i < Dim; ++i) { - if (this->getSize(i) != rhs.getSize(i)) { - return false; - } - } - - return true; -} - -template class PtrTraits> -template -__host__ __device__ Tensor -Tensor::cast() { - static_assert(sizeof(U) == sizeof(T), "cast must be to same size object"); - - return Tensor( - reinterpret_cast(data_), size_, stride_); -} - -template class PtrTraits> -template -__host__ __device__ const Tensor -Tensor::cast() const { - static_assert(sizeof(U) == sizeof(T), "cast must be to same size object"); - - return Tensor( - reinterpret_cast(data_), size_, stride_); -} - -template class PtrTraits> -template -__host__ __device__ Tensor -Tensor::castResize() { - static_assert(sizeof(U) >= sizeof(T), "only handles greater sizes"); - constexpr int kMultiple = sizeof(U) / sizeof(T); - - GPU_FAISS_ASSERT(canCastResize()); - - IndexT newSize[Dim]; - IndexT newStride[Dim]; - - for (int i = 0; i < Dim - 1; ++i) { - newSize[i] = size_[i]; - newStride[i] = stride_[i] / kMultiple; - } - - newStride[Dim - 1] = 1; // this is the same as the old stride - newSize[Dim - 1] = size_[Dim - 1] / kMultiple; - - return Tensor( - reinterpret_cast(data_), newSize, newStride); -} - -template class PtrTraits> -template -__host__ __device__ const Tensor -Tensor::castResize() const { - return const_cast*>(this)-> - castResize(); -} - -template class PtrTraits> -template -__host__ __device__ bool -Tensor::canCastResize() const { - static_assert(sizeof(U) >= sizeof(T), "only handles greater sizes"); - constexpr int kMultiple = sizeof(U) / sizeof(T); - - // Ensure that the base pointer is sizeof(U) aligned - if (((uintptr_t) data_) % sizeof(U) != 0) { - return false; - } - - // Check all outer strides - for (int i = 0; i < Dim - 1; ++i) { - if (stride_[i] % kMultiple != 0) { - return false; - } - } - - // Check inner size - if (size_[Dim - 1] % kMultiple != 0) { - return false; - } - - if (stride_[Dim - 1] != 1) { - return false; - } - - return true; -} - -template class PtrTraits> -template -__host__ Tensor -Tensor::castIndexType() const { - if (sizeof(NewIndexT) < sizeof(IndexT)) { - GPU_FAISS_ASSERT(this->canUseIndexType()); - } - - NewIndexT newSize[Dim]; - NewIndexT newStride[Dim]; - for (int i = 0; i < Dim; ++i) { - newSize[i] = (NewIndexT) size_[i]; - newStride[i] = (NewIndexT) stride_[i]; - } - - return Tensor( - data_, newSize, newStride); -} - -template class PtrTraits> -template -__host__ bool -Tensor::canUseIndexType() const { - static_assert(sizeof(size_t) >= sizeof(IndexT), - "index size too large"); - static_assert(sizeof(size_t) >= sizeof(NewIndexT), - "new index size too large"); - - // Find maximum offset that can be calculated - // FIXME: maybe also consider offset in bytes? multiply by sizeof(T)? - size_t maxOffset = 0; - - for (int i = 0; i < Dim; ++i) { - size_t curMaxOffset = (size_t) size_[i] * (size_t) stride_[i]; - if (curMaxOffset > maxOffset) { - maxOffset = curMaxOffset; - } - } - - if (maxOffset > (size_t) std::numeric_limits::max()) { - return false; - } - - return true; -} - -template class PtrTraits> -__host__ __device__ size_t -Tensor::numElements() const { - size_t size = (size_t) getSize(0); - - for (int i = 1; i < Dim; ++i) { - size *= (size_t) getSize(i); - } - - return size; -} - -template class PtrTraits> -__host__ __device__ bool -Tensor::isContiguous() const { - long prevSize = 1; - - for (int i = Dim - 1; i >= 0; --i) { - if (getSize(i) != (IndexT) 1) { - if (getStride(i) == prevSize) { - prevSize *= getSize(i); - } else { - return false; - } - } - } - - return true; -} - -template class PtrTraits> -__host__ __device__ bool -Tensor::isConsistentlySized(int i) const { - if (i == 0 && getStride(i) > 0 && getSize(i) > 0) { - return true; - } else if ((i > 0) && (i < Dim) && (getStride(i) > 0) && - ((getStride(i - 1) / getStride(i)) >= getSize(i))) { - return true; - } - - return false; -} - -template class PtrTraits> -__host__ __device__ bool -Tensor::isConsistentlySized() const { - for (int i = 0; i < Dim; ++i) { - if (!isConsistentlySized(i)) { - return false; - } - } - - return true; -} - -template class PtrTraits> -__host__ __device__ bool -Tensor::isContiguousDim(int i) const { - return (i == Dim - 1) || // just in case - ((i < Dim - 1) && - ((getStride(i) / getStride(i + 1)) == getSize(i + 1))); -} - -template class PtrTraits> -__host__ __device__ Tensor -Tensor::transpose(int dim1, - int dim2) const { - GPU_FAISS_ASSERT(dim1 >= 0 && dim1 < Dim); - GPU_FAISS_ASSERT(dim1 >= 0 && dim2 < Dim); - - // If a tensor is innermost contiguous, one cannot transpose the innermost - // dimension - if (InnerContig) { - GPU_FAISS_ASSERT(dim1 != Dim - 1 && dim2 != Dim - 1); - } - - IndexT newSize[Dim]; - IndexT newStride[Dim]; - - for (int i = 0; i < Dim; ++i) { - newSize[i] = size_[i]; - newStride[i] = stride_[i]; - } - - IndexT tmp = newSize[dim1]; - newSize[dim1] = newSize[dim2]; - newSize[dim2] = tmp; - - tmp = newStride[dim1]; - newStride[dim1] = newStride[dim2]; - newStride[dim2] = tmp; - - return Tensor(data_, newSize, newStride); -} - -template class PtrTraits> -__host__ __device__ Tensor -Tensor::transposeInnermost( - int dim1) const { - GPU_FAISS_ASSERT(dim1 >= 0 && dim1 < Dim); - - // We are exchanging with the innermost dimension - int dim2 = 1; - - IndexT newSize[Dim]; - IndexT newStride[Dim]; - - for (int i = 0; i < Dim; ++i) { - newSize[i] = size_[i]; - newStride[i] = stride_[i]; - } - - IndexT tmp = newSize[dim1]; - newSize[dim1] = newSize[dim2]; - newSize[dim2] = tmp; - - tmp = newStride[dim1]; - newStride[dim1] = newStride[dim2]; - newStride[dim2] = tmp; - - return Tensor(data_, newSize, newStride); -} - -template class PtrTraits> -template -__host__ __device__ Tensor -Tensor::upcastOuter() { - // Can only create tensors of greater dimension - static_assert(NewDim > Dim, "Can only upcast to greater dim"); - - IndexT newSize[NewDim]; - IndexT newStride[NewDim]; - - int shift = NewDim - Dim; - - for (int i = 0; i < NewDim; ++i) { - if (i < shift) { - // These are the extended dimensions - newSize[i] = (IndexT) 1; - newStride[i] = size_[0] * stride_[0]; - } else { - // Shift the remaining dimensions - newSize[i] = size_[i - shift]; - newStride[i] = stride_[i - shift]; - } - } - - return Tensor( - data_, newSize, newStride); -} - -template class PtrTraits> -template -__host__ __device__ Tensor -Tensor::upcastInner() { - // Can only create tensors of greater dimension - static_assert(NewDim > Dim, "Can only upcast to greater dim"); - - IndexT newSize[NewDim]; - IndexT newStride[NewDim]; - - for (int i = 0; i < NewDim; ++i) { - if (i < Dim) { - // Existing dimensions get copied over - newSize[i] = size_[i]; - newStride[i] = stride_[i]; - } else { - // Extended dimensions - newSize[i] = (IndexT) 1; - newStride[i] = (IndexT) 1; - } - } - - return Tensor( - data_, newSize, newStride); -} - -template class PtrTraits> -template -__host__ __device__ Tensor -Tensor::downcastOuter() { - // Can only create tensors of lesser dimension - static_assert(NewDim < Dim, "Can only downcast to lesser dim"); - - // We can't downcast non-contiguous tensors, since it leaves - // garbage data in the tensor. The tensor needs to be contiguous - // in all of the dimensions we are collapsing (no padding in - // them). - for (int i = 0; i < Dim - NewDim; ++i) { - bool cont = isContiguousDim(i); - GPU_FAISS_ASSERT(cont); - } - - IndexT newSize[NewDim]; - IndexT newStride[NewDim]; - - int ignoredDims = Dim - NewDim; - IndexT collapsedSize = 1; - - for (int i = 0; i < Dim; ++i) { - if (i < ignoredDims) { - // Collapse these dimensions - collapsedSize *= getSize(i); - } else { - // Non-collapsed dimensions - if (i == ignoredDims) { - // This is the first non-collapsed dimension - newSize[i - ignoredDims] = collapsedSize * getSize(i); - } else { - // Subsequent non-collapsed dimensions - newSize[i - ignoredDims] = getSize(i); - } - - newStride[i - ignoredDims] = getStride(i); - } - } - - return Tensor( - data_, newSize, newStride); -} - -template class PtrTraits> -template -__host__ __device__ Tensor -Tensor::downcastInner() { - // Can only create tensors of lesser dimension - static_assert(NewDim < Dim, "Can only downcast to lesser dim"); - - // We can't downcast non-contiguous tensors, since it leaves - // garbage data in the tensor. The tensor needs to be contiguous - // in all of the dimensions we are collapsing (no padding in - // them). - for (int i = NewDim; i < Dim; ++i) { - GPU_FAISS_ASSERT(isContiguousDim(i)); - } - - IndexT newSize[NewDim]; - IndexT newStride[NewDim]; - - IndexT collapsedSize = 1; - - for (int i = Dim - 1; i >= 0; --i) { - if (i >= NewDim) { - // Collapse these dimensions - collapsedSize *= getSize(i); - } else { - // Non-collapsed dimensions - if (i == NewDim - 1) { - // This is the first non-collapsed dimension - newSize[i] = collapsedSize * getSize(i); - newStride[i] = getStride(Dim - 1); - } else { - // Subsequent non-collapsed dimensions - newSize[i] = getSize(i); - newStride[i] = getStride(i); - } - } - } - - return Tensor( - data_, newSize, newStride); -} - -template class PtrTraits> -template -__host__ __device__ Tensor -Tensor::view(DataPtrType at) { - static_assert(SubDim >= 1 && SubDim < Dim, - "can only create view of lesser dim"); - - IndexT viewSizes[SubDim]; - IndexT viewStrides[SubDim]; - - for (int i = 0; i < SubDim; ++i) { - viewSizes[i] = size_[Dim - SubDim + i]; - viewStrides[i] = stride_[Dim - SubDim + i]; - } - - return Tensor( - at, viewSizes, viewStrides); -} - -template class PtrTraits> -template -__host__ __device__ Tensor -Tensor::view() { - return view(data_); -} - -template class PtrTraits> -__host__ __device__ Tensor -Tensor::narrowOutermost(IndexT start, - IndexT size) { - return this->narrow(0, start, size); -} - -template class PtrTraits> -__host__ __device__ Tensor -Tensor::narrow(int dim, - IndexT start, - IndexT size) { - DataPtrType newData = data_; - - GPU_FAISS_ASSERT(start >= 0 && - start < size_[dim] && - (start + size) <= size_[dim]); - - if (start > 0) { - newData += (size_t) start * stride_[dim]; - } - - IndexT newSize[Dim]; - for (int i = 0; i < Dim; ++i) { - if (i == dim) { - GPU_FAISS_ASSERT(start + size <= size_[dim]); - newSize[i] = size; - } else { - newSize[i] = size_[i]; - } - } - - // If we were innermost contiguous before, we are still innermost contiguous - return Tensor(newData, newSize, stride_); -} - -template class PtrTraits> -template -__host__ __device__ Tensor -Tensor::view( - std::initializer_list sizes) { - GPU_FAISS_ASSERT(this->isContiguous()); - - GPU_FAISS_ASSERT(sizes.size() == NewDim); - - // The total size of the new view must be the same as the total size - // of the old view - size_t curSize = numElements(); - size_t newSize = 1; - - for (auto s : sizes) { - newSize *= s; - } - - GPU_FAISS_ASSERT(curSize == newSize); - return Tensor(data(), sizes); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/Tensor.cuh b/core/src/index/thirdparty/faiss/gpu/utils/Tensor.cuh deleted file mode 100644 index bb3d956d6b..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/Tensor.cuh +++ /dev/null @@ -1,656 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include - -/// Multi-dimensional array class for CUDA device and host usage. -/// Originally from Facebook's fbcunn, since added to the Torch GPU -/// library cutorch as well. - -namespace faiss { namespace gpu { - -/// Our tensor type -template class PtrTraits> -class Tensor; - -/// Type of a subspace of a tensor -namespace detail { -template class PtrTraits> -class SubTensor; -} - -namespace traits { - -template -struct RestrictPtrTraits { - typedef T* __restrict__ PtrType; -}; - -template -struct DefaultPtrTraits { - typedef T* PtrType; -}; - -} - -/** - Templated multi-dimensional array that supports strided access of - elements. Main access is through `operator[]`; e.g., - `tensor[x][y][z]`. - - - `T` is the contained type (e.g., `float`) - - `Dim` is the tensor rank - - If `InnerContig` is true, then the tensor is assumed to be innermost - - contiguous, and only operations that make sense on contiguous - - arrays are allowed (e.g., no transpose). Strides are still - - calculated, but innermost stride is assumed to be 1. - - `IndexT` is the integer type used for size/stride arrays, and for - - all indexing math. Default is `int`, but for large tensors, `long` - - can be used instead. - - `PtrTraits` are traits applied to our data pointer (T*). By default, - - this is just T*, but RestrictPtrTraits can be used to apply T* - - __restrict__ for alias-free analysis. -*/ -template class PtrTraits = traits::DefaultPtrTraits> -class Tensor { - public: - enum { NumDim = Dim }; - typedef T DataType; - typedef IndexT IndexType; - enum { IsInnerContig = InnerContig }; - typedef typename PtrTraits::PtrType DataPtrType; - typedef Tensor TensorType; - - /// Default constructor - __host__ __device__ Tensor(); - - /// Copy constructor - __host__ __device__ Tensor(Tensor& t); - - /// Move constructor - __host__ __device__ Tensor(Tensor&& t); - - /// Assignment - __host__ __device__ Tensor& - operator=(Tensor& t); - - /// Move assignment - __host__ __device__ Tensor& - operator=(Tensor&& t); - - /// Constructor that calculates strides with no padding - __host__ __device__ Tensor(DataPtrType data, - const IndexT sizes[Dim]); - __host__ __device__ Tensor(DataPtrType data, - std::initializer_list sizes); - - /// Constructor that takes arbitrary size/stride arrays. - /// Errors if you attempt to pass non-contiguous strides to a - /// contiguous tensor. - __host__ __device__ Tensor(DataPtrType data, - const IndexT sizes[Dim], - const IndexT strides[Dim]); - - /// Copies a tensor into ourselves; sizes must match - __host__ void copyFrom(Tensor& t, - cudaStream_t stream); - - /// Copies ourselves into a tensor; sizes must match - __host__ void copyTo(Tensor& t, - cudaStream_t stream); - - /// Returns true if the two tensors are of the same dimensionality, - /// size and stride. - template - __host__ __device__ bool - isSame(const Tensor& rhs) const; - - /// Returns true if the two tensors are of the same dimensionality and size - template - __host__ __device__ bool - isSameSize(const Tensor& rhs) const; - - /// Cast to a tensor of a different type of the same size and - /// stride. U and our type T must be of the same size - template - __host__ __device__ Tensor cast(); - - /// Const version of `cast` - template - __host__ __device__ - const Tensor cast() const; - - /// Cast to a tensor of a different type which is potentially a - /// different size than our type T. Tensor must be aligned and the - /// innermost dimension must be a size that is a multiple of - /// sizeof(U) / sizeof(T), and the stride of the innermost dimension - /// must be contiguous. The stride of all outer dimensions must be a - /// multiple of sizeof(U) / sizeof(T) as well. - template - __host__ __device__ Tensor castResize(); - - /// Const version of `castResize` - template - __host__ __device__ const Tensor - castResize() const; - - /// Returns true if we can castResize() this tensor to the new type - template - __host__ __device__ bool canCastResize() const; - - /// Attempts to cast this tensor to a tensor of a different IndexT. - /// Fails if size or stride entries are not representable in the new - /// IndexT. - template - __host__ Tensor - castIndexType() const; - - /// Returns true if we can use this indexing type to access all elements - /// index type - template - __host__ bool canUseIndexType() const; - - /// Returns a raw pointer to the start of our data. - __host__ __device__ inline DataPtrType data() { - return data_; - } - - /// Returns a raw pointer to the end of our data, assuming - /// continuity - __host__ __device__ inline DataPtrType end() { - return data() + numElements(); - } - - /// Returns a raw pointer to the start of our data (const). - __host__ __device__ inline - const DataPtrType data() const { - return data_; - } - - /// Returns a raw pointer to the end of our data, assuming - /// continuity (const) - __host__ __device__ inline DataPtrType end() const { - return data() + numElements(); - } - - /// Cast to a different datatype - template - __host__ __device__ inline - typename PtrTraits::PtrType dataAs() { - return reinterpret_cast::PtrType>(data_); - } - - /// Cast to a different datatype - template - __host__ __device__ inline - const typename PtrTraits::PtrType dataAs() const { - return reinterpret_cast::PtrType>(data_); - } - - /// Returns a read/write view of a portion of our tensor. - __host__ __device__ inline - detail::SubTensor - operator[](IndexT); - - /// Returns a read/write view of a portion of our tensor (const). - __host__ __device__ inline - const detail::SubTensor - operator[](IndexT) const; - - /// Returns the size of a given dimension, `[0, Dim - 1]`. No bounds - /// checking. - __host__ __device__ inline IndexT getSize(int i) const { - return size_[i]; - } - - /// Returns the stride of a given dimension, `[0, Dim - 1]`. No bounds - /// checking. - __host__ __device__ inline IndexT getStride(int i) const { - return stride_[i]; - } - - /// Returns the total number of elements contained within our data - /// (product of `getSize(i)`) - __host__ __device__ size_t numElements() const; - - /// If we are contiguous, returns the total size in bytes of our - /// data - __host__ __device__ size_t getSizeInBytes() const { - return numElements() * sizeof(T); - } - - /// Returns the size array. - __host__ __device__ inline const IndexT* sizes() const { - return size_; - } - - /// Returns the stride array. - __host__ __device__ inline const IndexT* strides() const { - return stride_; - } - - /// Returns true if there is no padding within the tensor and no - /// re-ordering of the dimensions. - /// ~~~ - /// (stride(i) == size(i + 1) * stride(i + 1)) && stride(dim - 1) == 0 - /// ~~~ - __host__ __device__ bool isContiguous() const; - - /// Returns whether a given dimension has only increasing stride - /// from the previous dimension. A tensor that was permuted by - /// exchanging size and stride only will fail this check. - /// If `i == 0` just check `size > 0`. Returns `false` if `stride` is `<= 0`. - __host__ __device__ bool isConsistentlySized(int i) const; - - // Returns whether at each dimension `stride <= size`. - // If this is not the case then iterating once over the size space will - // touch the same memory locations multiple times. - __host__ __device__ bool isConsistentlySized() const; - - /// Returns true if the given dimension index has no padding - __host__ __device__ bool isContiguousDim(int i) const; - - /// Returns a tensor of the same dimension after transposing the two - /// dimensions given. Does not actually move elements; transposition - /// is made by permuting the size/stride arrays. - /// If the dimensions are not valid, asserts. - __host__ __device__ Tensor - transpose(int dim1, int dim2) const; - - /// Transpose a tensor, exchanging a non-innermost dimension with the - /// innermost dimension, returning a no longer innermost contiguous tensor - __host__ __device__ Tensor - transposeInnermost(int dim1) const; - - /// Upcast a tensor of dimension `D` to some tensor of dimension - /// D' > D by padding the leading dimensions by 1 - /// e.g., upcasting a 2-d tensor `[2][3]` to a 4-d tensor `[1][1][2][3]` - template - __host__ __device__ Tensor - upcastOuter(); - - /// Upcast a tensor of dimension `D` to some tensor of dimension - /// D' > D by padding the lowest/most varying dimensions by 1 - /// e.g., upcasting a 2-d tensor `[2][3]` to a 4-d tensor `[2][3][1][1]` - template - __host__ __device__ Tensor - upcastInner(); - - /// Downcast a tensor of dimension `D` to some tensor of dimension - /// D' < D by collapsing the leading dimensions. asserts if there is - /// padding on the leading dimensions. - template - __host__ __device__ - Tensor downcastOuter(); - - /// Downcast a tensor of dimension `D` to some tensor of dimension - /// D' < D by collapsing the leading dimensions. asserts if there is - /// padding on the leading dimensions. - template - __host__ __device__ - Tensor downcastInner(); - - /// Returns a tensor that is a view of the `SubDim`-dimensional slice - /// of this tensor, starting at `at`. - template - __host__ __device__ Tensor - view(DataPtrType at); - - /// Returns a tensor that is a view of the `SubDim`-dimensional slice - /// of this tensor, starting where our data begins - template - __host__ __device__ Tensor - view(); - - /// Returns a tensor of the same dimension that is a view of the - /// original tensor with the specified dimension restricted to the - /// elements in the range [start, start + size) - __host__ __device__ Tensor - narrowOutermost(IndexT start, IndexT size); - - /// Returns a tensor of the same dimension that is a view of the - /// original tensor with the specified dimension restricted to the - /// elements in the range [start, start + size). - /// Can occur in an arbitrary dimension - __host__ __device__ Tensor - narrow(int dim, IndexT start, IndexT size); - - /// Returns a view of the given tensor expressed as a tensor of a - /// different number of dimensions. - /// Only works if we are contiguous. - template - __host__ __device__ Tensor - view(std::initializer_list sizes); - - protected: - /// Raw pointer to where the tensor data begins - DataPtrType data_; - - /// Array of strides (in sizeof(T) terms) per each dimension - IndexT stride_[Dim]; - - /// Size per each dimension - IndexT size_[Dim]; -}; - -// Utilities for checking a collection of tensors -namespace detail { - -template -bool canUseIndexType() { - return true; -} - -template -bool canUseIndexType(const T& arg, const U&... args) { - return arg.template canUseIndexType() && - canUseIndexType(args...); -} - -} // namespace detail - -template -bool canUseIndexType(const T&... args) { - return detail::canUseIndexType(args...); -} - -namespace detail { - -/// Specialization for a view of a single value (0-dimensional) -template class PtrTraits> -class SubTensor { - public: - __host__ __device__ SubTensor - operator=(typename TensorType::DataType val) { - *data_ = val; - return *this; - } - - // operator T& - __host__ __device__ operator typename TensorType::DataType&() { - return *data_; - } - - // const operator T& returning const T& - __host__ __device__ operator const typename TensorType::DataType&() const { - return *data_; - } - - // operator& returning T* - __host__ __device__ typename TensorType::DataType* operator&() { - return data_; - } - - // const operator& returning const T* - __host__ __device__ const typename TensorType::DataType* operator&() const { - return data_; - } - - /// Returns a raw accessor to our slice. - __host__ __device__ inline typename TensorType::DataPtrType data() { - return data_; - } - - /// Returns a raw accessor to our slice (const). - __host__ __device__ inline - const typename TensorType::DataPtrType data() const { - return data_; - } - - /// Cast to a different datatype. - template - __host__ __device__ T& as() { - return *dataAs(); - } - - /// Cast to a different datatype (const). - template - __host__ __device__ const T& as() const { - return *dataAs(); - } - - /// Cast to a different datatype - template - __host__ __device__ inline - typename PtrTraits::PtrType dataAs() { - return reinterpret_cast::PtrType>(data_); - } - - /// Cast to a different datatype (const) - template - __host__ __device__ inline - typename PtrTraits::PtrType dataAs() const { - return reinterpret_cast::PtrType>(data_); - } - - /// Use the texture cache for reads - __device__ inline typename TensorType::DataType ldg() const { -#if __CUDA_ARCH__ >= 350 - return __ldg(data_); -#else - return *data_; -#endif - } - - /// Use the texture cache for reads; cast as a particular type - template - __device__ inline T ldgAs() const { -#if __CUDA_ARCH__ >= 350 - return __ldg(dataAs()); -#else - return as(); -#endif - } - - protected: - /// One dimension greater can create us - friend class SubTensor; - - /// Our parent tensor can create us - friend class Tensor; - - __host__ __device__ inline SubTensor( - TensorType& t, - typename TensorType::DataPtrType data) - : tensor_(t), - data_(data) { - } - - /// The tensor we're referencing - TensorType& tensor_; - - /// Where our value is located - typename TensorType::DataPtrType const data_; -}; - -/// A `SubDim`-rank slice of a parent Tensor -template class PtrTraits> -class SubTensor { - public: - /// Returns a view of the data located at our offset (the dimension - /// `SubDim` - 1 tensor). - __host__ __device__ inline - SubTensor - operator[](typename TensorType::IndexType index) { - if (TensorType::IsInnerContig && SubDim == 1) { - // Innermost dimension is stride 1 for contiguous arrays - return SubTensor( - tensor_, data_ + index); - } else { - return SubTensor( - tensor_, - data_ + index * tensor_.getStride(TensorType::NumDim - SubDim)); - } - } - - /// Returns a view of the data located at our offset (the dimension - /// `SubDim` - 1 tensor) (const). - __host__ __device__ inline - const SubTensor - operator[](typename TensorType::IndexType index) const { - if (TensorType::IsInnerContig && SubDim == 1) { - // Innermost dimension is stride 1 for contiguous arrays - return SubTensor( - tensor_, data_ + index); - } else { - return SubTensor( - tensor_, - data_ + index * tensor_.getStride(TensorType::NumDim - SubDim)); - } - } - - // operator& returning T* - __host__ __device__ typename TensorType::DataType* operator&() { - return data_; - } - - // const operator& returning const T* - __host__ __device__ const typename TensorType::DataType* operator&() const { - return data_; - } - - /// Returns a raw accessor to our slice. - __host__ __device__ inline typename TensorType::DataPtrType data() { - return data_; - } - - /// Returns a raw accessor to our slice (const). - __host__ __device__ inline - const typename TensorType::DataPtrType data() const { - return data_; - } - - /// Cast to a different datatype. - template - __host__ __device__ T& as() { - return *dataAs(); - } - - /// Cast to a different datatype (const). - template - __host__ __device__ const T& as() const { - return *dataAs(); - } - - /// Cast to a different datatype - template - __host__ __device__ inline - typename PtrTraits::PtrType dataAs() { - return reinterpret_cast::PtrType>(data_); - } - - /// Cast to a different datatype (const) - template - __host__ __device__ inline - typename PtrTraits::PtrType dataAs() const { - return reinterpret_cast::PtrType>(data_); - } - - /// Use the texture cache for reads - __device__ inline typename TensorType::DataType ldg() const { -#if __CUDA_ARCH__ >= 350 - return __ldg(data_); -#else - return *data_; -#endif - } - - /// Use the texture cache for reads; cast as a particular type - template - __device__ inline T ldgAs() const { -#if __CUDA_ARCH__ >= 350 - return __ldg(dataAs()); -#else - return as(); -#endif - } - - /// Returns a tensor that is a view of the SubDim-dimensional slice - /// of this tensor, starting where our data begins - Tensor view() { - return tensor_.template view(data_); - } - - protected: - /// One dimension greater can create us - friend class SubTensor; - - /// Our parent tensor can create us - friend class - Tensor; - - __host__ __device__ inline SubTensor( - TensorType& t, - typename TensorType::DataPtrType data) - : tensor_(t), - data_(data) { - } - - /// The tensor we're referencing - TensorType& tensor_; - - /// The start of our sub-region - typename TensorType::DataPtrType const data_; -}; - -} // namespace detail - -template class PtrTraits> -__host__ __device__ inline -detail::SubTensor, - Dim - 1, PtrTraits> - Tensor::operator[](IndexT index) { - return detail::SubTensor( - detail::SubTensor( - *this, data_)[index]); -} - -template class PtrTraits> -__host__ __device__ inline -const detail::SubTensor, - Dim - 1, PtrTraits> - Tensor::operator[](IndexT index) const { - return detail::SubTensor( - detail::SubTensor( - const_cast(*this), data_)[index]); -} - -} } // namespace - -#include diff --git a/core/src/index/thirdparty/faiss/gpu/utils/ThrustAllocator.cuh b/core/src/index/thirdparty/faiss/gpu/utils/ThrustAllocator.cuh deleted file mode 100644 index 4ca0415bfa..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/ThrustAllocator.cuh +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include - -namespace faiss { namespace gpu { - -/// Allocator for Thrust that comes out of a specified memory space -class GpuResourcesThrustAllocator { - public: - typedef char value_type; - - GpuResourcesThrustAllocator(void* mem, size_t size) - : start_((char*) mem), - cur_((char*) mem), - end_((char*) mem + size) { - } - - ~GpuResourcesThrustAllocator() { - // In the case of an exception being thrown, we may not have called - // deallocate on all of our sub-allocations. Free them here - for (auto p : mallocAllocs_) { - freeMemorySpace(MemorySpace::Device, p); - } - } - - char* allocate(std::ptrdiff_t size) { - if (size <= (end_ - cur_)) { - char* p = cur_; - cur_ += size; - FAISS_ASSERT(cur_ <= end_); - - return p; - } else { - char* p = nullptr; - allocMemorySpace(MemorySpace::Device, &p, size); - mallocAllocs_.insert(p); - return p; - } - } - - void deallocate(char* p, size_t size) { - // Allocations could be returned out-of-order; ignore those we - // didn't cudaMalloc - auto it = mallocAllocs_.find(p); - if (it != mallocAllocs_.end()) { - freeMemorySpace(MemorySpace::Device, p); - mallocAllocs_.erase(it); - } - } - - private: - char* start_; - char* cur_; - char* end_; - std::unordered_set mallocAllocs_; -}; - - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/Timer.cpp b/core/src/index/thirdparty/faiss/gpu/utils/Timer.cpp deleted file mode 100644 index 1764fec10a..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/Timer.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include - -namespace faiss { namespace gpu { - -KernelTimer::KernelTimer(cudaStream_t stream) - : startEvent_(0), - stopEvent_(0), - stream_(stream), - valid_(true) { - CUDA_VERIFY(cudaEventCreate(&startEvent_)); - CUDA_VERIFY(cudaEventCreate(&stopEvent_)); - - CUDA_VERIFY(cudaEventRecord(startEvent_, stream_)); -} - -KernelTimer::~KernelTimer() { - CUDA_VERIFY(cudaEventDestroy(startEvent_)); - CUDA_VERIFY(cudaEventDestroy(stopEvent_)); -} - -float -KernelTimer::elapsedMilliseconds() { - FAISS_ASSERT(valid_); - - CUDA_VERIFY(cudaEventRecord(stopEvent_, stream_)); - CUDA_VERIFY(cudaEventSynchronize(stopEvent_)); - - auto time = 0.0f; - CUDA_VERIFY(cudaEventElapsedTime(&time, startEvent_, stopEvent_)); - valid_ = false; - - return time; -} - -CpuTimer::CpuTimer() { - clock_gettime(CLOCK_REALTIME, &start_); -} - -float -CpuTimer::elapsedMilliseconds() { - struct timespec end; - clock_gettime(CLOCK_REALTIME, &end); - - auto diffS = end.tv_sec - start_.tv_sec; - auto diffNs = end.tv_nsec - start_.tv_nsec; - - return 1000.0f * (float) diffS + ((float) diffNs) / 1000000.0f; -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/Timer.h b/core/src/index/thirdparty/faiss/gpu/utils/Timer.h deleted file mode 100644 index ef2a161a32..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/Timer.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include - -namespace faiss { namespace gpu { - -/// Utility class for timing execution of a kernel -class KernelTimer { - public: - /// Constructor starts the timer and adds an event into the current - /// device stream - KernelTimer(cudaStream_t stream = 0); - - /// Destructor releases event resources - ~KernelTimer(); - - /// Adds a stop event then synchronizes on the stop event to get the - /// actual GPU-side kernel timings for any kernels launched in the - /// current stream. Returns the number of milliseconds elapsed. - /// Can only be called once. - float elapsedMilliseconds(); - - private: - cudaEvent_t startEvent_; - cudaEvent_t stopEvent_; - cudaStream_t stream_; - bool valid_; -}; - -/// CPU wallclock elapsed timer -class CpuTimer { - public: - /// Creates and starts a new timer - CpuTimer(); - - /// Returns elapsed time in milliseconds - float elapsedMilliseconds(); - - private: - struct timespec start_; -}; - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/Transpose.cuh b/core/src/index/thirdparty/faiss/gpu/utils/Transpose.cuh deleted file mode 100644 index c6137d9f0d..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/Transpose.cuh +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include -#include - -namespace faiss { namespace gpu { - -template -struct TensorInfo { - static constexpr int kMaxDims = 8; - - T* data; - IndexT sizes[kMaxDims]; - IndexT strides[kMaxDims]; - int dims; -}; - -template -struct TensorInfoOffset { - __device__ inline static unsigned int get(const TensorInfo& info, - IndexT linearId) { - IndexT offset = 0; - -#pragma unroll - for (int i = Dim - 1; i >= 0; --i) { - IndexT curDimIndex = linearId % info.sizes[i]; - IndexT curDimOffset = curDimIndex * info.strides[i]; - - offset += curDimOffset; - - if (i > 0) { - linearId /= info.sizes[i]; - } - } - - return offset; - } -}; - -template -struct TensorInfoOffset { - __device__ inline static unsigned int get(const TensorInfo& info, - IndexT linearId) { - return linearId; - } -}; - -template -TensorInfo getTensorInfo(const Tensor& t) { - TensorInfo info; - - for (int i = 0; i < Dim; ++i) { - info.sizes[i] = (IndexT) t.getSize(i); - info.strides[i] = (IndexT) t.getStride(i); - } - - info.data = t.data(); - info.dims = Dim; - - return info; -} - -template -__global__ void transposeAny(TensorInfo input, - TensorInfo output, - IndexT totalSize) { - for (IndexT i = blockIdx.x * blockDim.x + threadIdx.x; - i < totalSize; - i += gridDim.x + blockDim.x) { - auto inputOffset = TensorInfoOffset::get(input, i); - auto outputOffset = TensorInfoOffset::get(output, i); - -#if __CUDA_ARCH__ >= 350 - output.data[outputOffset] = __ldg(&input.data[inputOffset]); -#else - output.data[outputOffset] = input.data[inputOffset]; -#endif - } -} - -/// Performs an out-of-place transposition between any two dimensions. -/// Best performance is if the transposed dimensions are not -/// innermost, since the reads and writes will be coalesced. -/// Could include a shared memory transposition if the dimensions -/// being transposed are innermost, but would require support for -/// arbitrary rectangular matrices. -/// This linearized implementation seems to perform well enough, -/// especially for cases that we care about (outer dimension -/// transpositions). -template -void runTransposeAny(Tensor& in, - int dim1, int dim2, - Tensor& out, - cudaStream_t stream) { - static_assert(Dim <= TensorInfo::kMaxDims, - "too many dimensions"); - - FAISS_ASSERT(dim1 != dim2); - FAISS_ASSERT(dim1 < Dim && dim2 < Dim); - - int outSize[Dim]; - - for (int i = 0; i < Dim; ++i) { - outSize[i] = in.getSize(i); - } - - std::swap(outSize[dim1], outSize[dim2]); - - for (int i = 0; i < Dim; ++i) { - FAISS_ASSERT(out.getSize(i) == outSize[i]); - } - - size_t totalSize = in.numElements(); - size_t block = std::min((size_t) getMaxThreadsCurrentDevice(), totalSize); - - if (totalSize <= (size_t) std::numeric_limits::max()) { - // div/mod seems faster with unsigned types - auto inInfo = getTensorInfo(in); - auto outInfo = getTensorInfo(out); - - std::swap(inInfo.sizes[dim1], inInfo.sizes[dim2]); - std::swap(inInfo.strides[dim1], inInfo.strides[dim2]); - - auto grid = std::min(utils::divUp(totalSize, block), (size_t) 4096); - - transposeAny - <<>>(inInfo, outInfo, totalSize); - } else { - auto inInfo = getTensorInfo(in); - auto outInfo = getTensorInfo(out); - - std::swap(inInfo.sizes[dim1], inInfo.sizes[dim2]); - std::swap(inInfo.strides[dim1], inInfo.strides[dim2]); - - auto grid = std::min(utils::divUp(totalSize, block), (size_t) 4096); - - transposeAny - <<>>(inInfo, outInfo, totalSize); - } - CUDA_TEST_ERROR(); -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/WarpSelectFloat.cu b/core/src/index/thirdparty/faiss/gpu/utils/WarpSelectFloat.cu deleted file mode 100644 index 4a03ab1311..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/WarpSelectFloat.cu +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -namespace faiss { namespace gpu { - -// warp Q to thread Q: -// 1, 1 -// 32, 2 -// 64, 3 -// 128, 3 -// 256, 4 -// 512, 8 -// 1024, 8 -// 2048, 8 - -WARP_SELECT_DECL(float, true, 1); -WARP_SELECT_DECL(float, true, 32); -WARP_SELECT_DECL(float, true, 64); -WARP_SELECT_DECL(float, true, 128); -WARP_SELECT_DECL(float, true, 256); -WARP_SELECT_DECL(float, true, 512); -WARP_SELECT_DECL(float, true, 1024); -#if GPU_MAX_SELECTION_K >= 2048 -WARP_SELECT_DECL(float, true, 2048); -#endif - -WARP_SELECT_DECL(float, false, 1); -WARP_SELECT_DECL(float, false, 32); -WARP_SELECT_DECL(float, false, 64); -WARP_SELECT_DECL(float, false, 128); -WARP_SELECT_DECL(float, false, 256); -WARP_SELECT_DECL(float, false, 512); -WARP_SELECT_DECL(float, false, 1024); -#if GPU_MAX_SELECTION_K >= 2048 -WARP_SELECT_DECL(float, false, 2048); -#endif - -void runWarpSelect(Tensor& in, - Tensor& outK, - Tensor& outV, - bool dir, int k, cudaStream_t stream) { - FAISS_ASSERT(k <= 2048); - - if (dir) { - if (k == 1) { - WARP_SELECT_CALL(float, true, 1); - } else if (k <= 32) { - WARP_SELECT_CALL(float, true, 32); - } else if (k <= 64) { - WARP_SELECT_CALL(float, true, 64); - } else if (k <= 128) { - WARP_SELECT_CALL(float, true, 128); - } else if (k <= 256) { - WARP_SELECT_CALL(float, true, 256); - } else if (k <= 512) { - WARP_SELECT_CALL(float, true, 512); - } else if (k <= 1024) { - WARP_SELECT_CALL(float, true, 1024); -#if GPU_MAX_SELECTION_K >= 2048 - } else if (k <= 2048) { - WARP_SELECT_CALL(float, true, 2048); -#endif - } - } else { - if (k == 1) { - WARP_SELECT_CALL(float, false, 1); - } else if (k <= 32) { - WARP_SELECT_CALL(float, false, 32); - } else if (k <= 64) { - WARP_SELECT_CALL(float, false, 64); - } else if (k <= 128) { - WARP_SELECT_CALL(float, false, 128); - } else if (k <= 256) { - WARP_SELECT_CALL(float, false, 256); - } else if (k <= 512) { - WARP_SELECT_CALL(float, false, 512); - } else if (k <= 1024) { - WARP_SELECT_CALL(float, false, 1024); -#if GPU_MAX_SELECTION_K >= 2048 - } else if (k <= 2048) { - WARP_SELECT_CALL(float, false, 2048); -#endif - } - } -} - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/WarpSelectHalf.cu b/core/src/index/thirdparty/faiss/gpu/utils/WarpSelectHalf.cu deleted file mode 100644 index d700ecaee7..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/WarpSelectHalf.cu +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 - -// warp Q to thread Q: -// 1, 1 -// 32, 2 -// 64, 3 -// 128, 3 -// 256, 4 -// 512, 8 -// 1024, 8 -// 2048, 8 - -WARP_SELECT_DECL(half, true, 1); -WARP_SELECT_DECL(half, true, 32); -WARP_SELECT_DECL(half, true, 64); -WARP_SELECT_DECL(half, true, 128); -WARP_SELECT_DECL(half, true, 256); -WARP_SELECT_DECL(half, true, 512); -WARP_SELECT_DECL(half, true, 1024); -#if GPU_MAX_SELECTION_K >= 2048 -WARP_SELECT_DECL(half, true, 2048); -#endif - -WARP_SELECT_DECL(half, false, 1); -WARP_SELECT_DECL(half, false, 32); -WARP_SELECT_DECL(half, false, 64); -WARP_SELECT_DECL(half, false, 128); -WARP_SELECT_DECL(half, false, 256); -WARP_SELECT_DECL(half, false, 512); -WARP_SELECT_DECL(half, false, 1024); -#if GPU_MAX_SELECTION_K >= 2048 -WARP_SELECT_DECL(half, false, 2048); -#endif - -void runWarpSelect(Tensor& in, - Tensor& outK, - Tensor& outV, - bool dir, int k, cudaStream_t stream) { - FAISS_ASSERT(k <= 1024); - - if (dir) { - if (k == 1) { - WARP_SELECT_CALL(half, true, 1); - } else if (k <= 32) { - WARP_SELECT_CALL(half, true, 32); - } else if (k <= 64) { - WARP_SELECT_CALL(half, true, 64); - } else if (k <= 128) { - WARP_SELECT_CALL(half, true, 128); - } else if (k <= 256) { - WARP_SELECT_CALL(half, true, 256); - } else if (k <= 512) { - WARP_SELECT_CALL(half, true, 512); - } else if (k <= 1024) { - WARP_SELECT_CALL(half, true, 1024); -#if GPU_MAX_SELECTION_K >= 2048 - } else if (k <= 2048) { - WARP_SELECT_CALL(half, true, 2048); -#endif - } - } else { - if (k == 1) { - WARP_SELECT_CALL(half, false, 1); - } else if (k <= 32) { - WARP_SELECT_CALL(half, false, 32); - } else if (k <= 64) { - WARP_SELECT_CALL(half, false, 64); - } else if (k <= 128) { - WARP_SELECT_CALL(half, false, 128); - } else if (k <= 256) { - WARP_SELECT_CALL(half, false, 256); - } else if (k <= 512) { - WARP_SELECT_CALL(half, false, 512); - } else if (k <= 1024) { - WARP_SELECT_CALL(half, false, 1024); -#if GPU_MAX_SELECTION_K >= 2048 - } else if (k <= 2048) { - WARP_SELECT_CALL(half, false, 2048); -#endif - } - } -} - -#endif // FAISS_USE_FLOAT16 - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/WarpSelectKernel.cuh b/core/src/index/thirdparty/faiss/gpu/utils/WarpSelectKernel.cuh deleted file mode 100644 index 1b690b0306..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/WarpSelectKernel.cuh +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include - -namespace faiss { namespace gpu { - -template -__global__ void warpSelect(Tensor in, - Tensor outK, - Tensor outV, - K initK, - IndexType initV, - int k) { - constexpr int kNumWarps = ThreadsPerBlock / kWarpSize; - - WarpSelect, - NumWarpQ, NumThreadQ, ThreadsPerBlock> - heap(initK, initV, k); - - int warpId = threadIdx.x / kWarpSize; - int row = blockIdx.x * kNumWarps + warpId; - - if (row >= in.getSize(0)) { - return; - } - - int i = getLaneId(); - K* inStart = in[row][i].data(); - - // Whole warps must participate in the selection - int limit = utils::roundDown(in.getSize(1), kWarpSize); - - for (; i < limit; i += kWarpSize) { - heap.add(*inStart, (IndexType) i); - inStart += kWarpSize; - } - - // Handle non-warp multiple remainder - if (i < in.getSize(1)) { - heap.addThreadQ(*inStart, (IndexType) i); - } - - heap.reduce(); - heap.writeOut(outK[row].data(), - outV[row].data(), k); -} - -void runWarpSelect(Tensor& in, - Tensor& outKeys, - Tensor& outIndices, - bool dir, int k, cudaStream_t stream); - -#ifdef FAISS_USE_FLOAT16 -void runWarpSelect(Tensor& in, - Tensor& outKeys, - Tensor& outIndices, - bool dir, int k, cudaStream_t stream); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/WarpShuffles.cuh b/core/src/index/thirdparty/faiss/gpu/utils/WarpShuffles.cuh deleted file mode 100644 index ec2e5b618c..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/WarpShuffles.cuh +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include - -namespace faiss { namespace gpu { - -template -inline __device__ T shfl(const T val, - int srcLane, int width = kWarpSize) { -#if CUDA_VERSION >= 9000 - return __shfl_sync(0xffffffff, val, srcLane, width); -#else - return __shfl(val, srcLane, width); -#endif -} - -// CUDA SDK does not provide specializations for T* -template -inline __device__ T* shfl(T* const val, - int srcLane, int width = kWarpSize) { - static_assert(sizeof(T*) == sizeof(long long), "pointer size"); - long long v = (long long) val; - - return (T*) shfl(v, srcLane, width); -} - -template -inline __device__ T shfl_up(const T val, - unsigned int delta, int width = kWarpSize) { -#if CUDA_VERSION >= 9000 - return __shfl_up_sync(0xffffffff, val, delta, width); -#else - return __shfl_up(val, delta, width); -#endif -} - -// CUDA SDK does not provide specializations for T* -template -inline __device__ T* shfl_up(T* const val, - unsigned int delta, int width = kWarpSize) { - static_assert(sizeof(T*) == sizeof(long long), "pointer size"); - long long v = (long long) val; - - return (T*) shfl_up(v, delta, width); -} - -template -inline __device__ T shfl_down(const T val, - unsigned int delta, int width = kWarpSize) { -#if CUDA_VERSION >= 9000 - return __shfl_down_sync(0xffffffff, val, delta, width); -#else - return __shfl_down(val, delta, width); -#endif -} - -// CUDA SDK does not provide specializations for T* -template -inline __device__ T* shfl_down(T* const val, - unsigned int delta, int width = kWarpSize) { - static_assert(sizeof(T*) == sizeof(long long), "pointer size"); - long long v = (long long) val; - return (T*) shfl_down(v, delta, width); -} - -template -inline __device__ T shfl_xor(const T val, - int laneMask, int width = kWarpSize) { -#if CUDA_VERSION >= 9000 - return __shfl_xor_sync(0xffffffff, val, laneMask, width); -#else - return __shfl_xor(val, laneMask, width); -#endif -} - -// CUDA SDK does not provide specializations for T* -template -inline __device__ T* shfl_xor(T* const val, - int laneMask, int width = kWarpSize) { - static_assert(sizeof(T*) == sizeof(long long), "pointer size"); - long long v = (long long) val; - return (T*) shfl_xor(v, laneMask, width); -} - -#ifdef FAISS_USE_FLOAT16 -// CUDA 9.0+ has half shuffle -#if CUDA_VERSION < 9000 -inline __device__ half shfl(half v, - int srcLane, int width = kWarpSize) { - unsigned int vu = v.x; - vu = __shfl(vu, srcLane, width); - - half h; - h.x = (unsigned short) vu; - return h; -} - -inline __device__ half shfl_xor(half v, - int laneMask, int width = kWarpSize) { - unsigned int vu = v.x; - vu = __shfl_xor(vu, laneMask, width); - - half h; - h.x = (unsigned short) vu; - return h; -} -#endif // CUDA_VERSION -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat1.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat1.cu deleted file mode 100644 index d53f4dc2aa..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat1.cu +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -BLOCK_SELECT_IMPL(float, true, 1, 1); -BLOCK_SELECT_IMPL(float, false, 1, 1); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat128.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat128.cu deleted file mode 100644 index 2010034a18..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat128.cu +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -BLOCK_SELECT_IMPL(float, true, 128, 3); -BLOCK_SELECT_IMPL(float, false, 128, 3); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat256.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat256.cu deleted file mode 100644 index bcd93f3038..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat256.cu +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -BLOCK_SELECT_IMPL(float, true, 256, 4); -BLOCK_SELECT_IMPL(float, false, 256, 4); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat32.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat32.cu deleted file mode 100644 index 35073dcfcd..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat32.cu +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -BLOCK_SELECT_IMPL(float, true, 32, 2); -BLOCK_SELECT_IMPL(float, false, 32, 2); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat64.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat64.cu deleted file mode 100644 index c2671068ee..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloat64.cu +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -BLOCK_SELECT_IMPL(float, true, 64, 3); -BLOCK_SELECT_IMPL(float, false, 64, 3); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatF1024.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatF1024.cu deleted file mode 100644 index 4c9c5188cb..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatF1024.cu +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -BLOCK_SELECT_IMPL(float, false, 1024, 8); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatF2048.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatF2048.cu deleted file mode 100644 index 7828c2045d..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatF2048.cu +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -namespace faiss { namespace gpu { - -#if GPU_MAX_SELECTION_K >= 2048 -BLOCK_SELECT_IMPL(float, false, 2048, 8); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatF512.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatF512.cu deleted file mode 100644 index f24ee0bfa6..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatF512.cu +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -BLOCK_SELECT_IMPL(float, false, 512, 8); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatT1024.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatT1024.cu deleted file mode 100644 index 1f84b371e3..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatT1024.cu +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -BLOCK_SELECT_IMPL(float, true, 1024, 8); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatT2048.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatT2048.cu deleted file mode 100644 index 48037838a9..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatT2048.cu +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -namespace faiss { namespace gpu { - -#if GPU_MAX_SELECTION_K >= 2048 -BLOCK_SELECT_IMPL(float, true, 2048, 8); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatT512.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatT512.cu deleted file mode 100644 index 3c93edfc09..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectFloatT512.cu +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -BLOCK_SELECT_IMPL(float, true, 512, 8); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf1.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf1.cu deleted file mode 100644 index d2525935c2..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf1.cu +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 -BLOCK_SELECT_IMPL(half, true, 1, 1); -BLOCK_SELECT_IMPL(half, false, 1, 1); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf128.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf128.cu deleted file mode 100644 index 3759af9342..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf128.cu +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 -BLOCK_SELECT_IMPL(half, true, 128, 3); -BLOCK_SELECT_IMPL(half, false, 128, 3); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf256.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf256.cu deleted file mode 100644 index a8a5cf13e9..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf256.cu +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 -BLOCK_SELECT_IMPL(half, true, 256, 4); -BLOCK_SELECT_IMPL(half, false, 256, 4); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf32.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf32.cu deleted file mode 100644 index 18907c5119..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf32.cu +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 -BLOCK_SELECT_IMPL(half, true, 32, 2); -BLOCK_SELECT_IMPL(half, false, 32, 2); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf64.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf64.cu deleted file mode 100644 index 81a9a84a9f..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalf64.cu +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 -BLOCK_SELECT_IMPL(half, true, 64, 3); -BLOCK_SELECT_IMPL(half, false, 64, 3); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfF1024.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfF1024.cu deleted file mode 100644 index e83b615193..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfF1024.cu +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 -BLOCK_SELECT_IMPL(half, false, 1024, 8); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfF2048.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfF2048.cu deleted file mode 100644 index e06c334481..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfF2048.cu +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -namespace faiss { namespace gpu { - -#if GPU_MAX_SELECTION_K >= 2048 -#ifdef FAISS_USE_FLOAT16 -BLOCK_SELECT_IMPL(half, false, 2048, 8); -#endif -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfF512.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfF512.cu deleted file mode 100644 index c1b67bd3de..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfF512.cu +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 -BLOCK_SELECT_IMPL(half, false, 512, 8); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfT1024.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfT1024.cu deleted file mode 100644 index 2fd0dffa37..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfT1024.cu +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 -BLOCK_SELECT_IMPL(half, true, 1024, 8); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfT2048.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfT2048.cu deleted file mode 100644 index f91b6787e2..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfT2048.cu +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -namespace faiss { namespace gpu { - -#if GPU_MAX_SELECTION_K >= 2048 -#ifdef FAISS_USE_FLOAT16 -BLOCK_SELECT_IMPL(half, true, 2048, 8); -#endif -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfT512.cu b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfT512.cu deleted file mode 100644 index a2877db6ed..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectHalfT512.cu +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 -BLOCK_SELECT_IMPL(half, true, 512, 8); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectImpl.cuh b/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectImpl.cuh deleted file mode 100644 index e7a5a03c22..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/blockselect/BlockSelectImpl.cuh +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include - -#define BLOCK_SELECT_DECL(TYPE, DIR, WARP_Q) \ - extern void runBlockSelect_ ## TYPE ## _ ## DIR ## _ ## WARP_Q ## _( \ - Tensor& in, \ - Tensor& bitset, \ - Tensor& outK, \ - Tensor& outV, \ - bool dir, \ - int k, \ - cudaStream_t stream); \ - \ - extern void runBlockSelectPair_ ## TYPE ## _ ## DIR ## _ ## WARP_Q ## _( \ - Tensor& inK, \ - Tensor& inV, \ - Tensor& bitset, \ - Tensor& outK, \ - Tensor& outV, \ - bool dir, \ - int k, \ - cudaStream_t stream); - -#define BLOCK_SELECT_IMPL(TYPE, DIR, WARP_Q, THREAD_Q) \ - void runBlockSelect_ ## TYPE ## _ ## DIR ## _ ## WARP_Q ## _( \ - Tensor& in, \ - Tensor& bitset, \ - Tensor& outK, \ - Tensor& outV, \ - bool dir, \ - int k, \ - cudaStream_t stream) { \ - FAISS_ASSERT(in.getSize(0) == outK.getSize(0)); \ - FAISS_ASSERT(in.getSize(0) == outV.getSize(0)); \ - FAISS_ASSERT(outK.getSize(1) == k); \ - FAISS_ASSERT(outV.getSize(1) == k); \ - \ - auto grid = dim3(in.getSize(0)); \ - \ - constexpr int kBlockSelectNumThreads = (WARP_Q <= 1024) ? 128 : 64; \ - auto block = dim3(kBlockSelectNumThreads); \ - \ - FAISS_ASSERT(k <= WARP_Q); \ - FAISS_ASSERT(dir == DIR); \ - \ - auto kInit = dir ? Limits::getMin() : Limits::getMax(); \ - auto vInit = -1; \ - \ - if (bitset.getSize(0) == 0) \ - blockSelect \ - <<>>(in, outK, outV, kInit, vInit, k); \ - else \ - blockSelect \ - <<>>(in, bitset, outK, outV, kInit, vInit, k); \ - CUDA_TEST_ERROR(); \ - } \ - \ - void runBlockSelectPair_ ## TYPE ## _ ## DIR ## _ ## WARP_Q ## _( \ - Tensor& inK, \ - Tensor& inV, \ - Tensor& bitset, \ - Tensor& outK, \ - Tensor& outV, \ - bool dir, \ - int k, \ - cudaStream_t stream) { \ - FAISS_ASSERT(inK.isSameSize(inV)); \ - FAISS_ASSERT(outK.isSameSize(outV)); \ - \ - auto grid = dim3(inK.getSize(0)); \ - \ - constexpr int kBlockSelectNumThreads = (WARP_Q <= 1024) ? 128 : 64; \ - auto block = dim3(kBlockSelectNumThreads); \ - \ - FAISS_ASSERT(k <= WARP_Q); \ - FAISS_ASSERT(dir == DIR); \ - \ - auto kInit = dir ? Limits::getMin() : Limits::getMax(); \ - auto vInit = -1; \ - \ - if (bitset.getSize(0) == 0) \ - blockSelectPair \ - <<>>(inK, inV, outK, outV, kInit, vInit, k); \ - else \ - blockSelectPair \ - <<>>(inK, inV, bitset, outK, outV, kInit, vInit, k); \ - CUDA_TEST_ERROR(); \ - } - - -#define BLOCK_SELECT_CALL(TYPE, DIR, WARP_Q) \ - runBlockSelect_ ## TYPE ## _ ## DIR ## _ ## WARP_Q ## _( \ - in, bitset, outK, outV, dir, k, stream) - -#define BLOCK_SELECT_PAIR_CALL(TYPE, DIR, WARP_Q) \ - runBlockSelectPair_ ## TYPE ## _ ## DIR ## _ ## WARP_Q ## _( \ - inK, inV, bitset, outK, outV, dir, k, stream) diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat1.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat1.cu deleted file mode 100644 index c641e50fdd..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat1.cu +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -WARP_SELECT_IMPL(float, true, 1, 1); -WARP_SELECT_IMPL(float, false, 1, 1); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat128.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat128.cu deleted file mode 100644 index 76d98d1f20..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat128.cu +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -WARP_SELECT_IMPL(float, true, 128, 3); -WARP_SELECT_IMPL(float, false, 128, 3); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat256.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat256.cu deleted file mode 100644 index a0dd47feb1..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat256.cu +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -WARP_SELECT_IMPL(float, true, 256, 4); -WARP_SELECT_IMPL(float, false, 256, 4); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat32.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat32.cu deleted file mode 100644 index 2461c94857..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat32.cu +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -WARP_SELECT_IMPL(float, true, 32, 2); -WARP_SELECT_IMPL(float, false, 32, 2); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat64.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat64.cu deleted file mode 100644 index a16c3830ca..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloat64.cu +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -WARP_SELECT_IMPL(float, true, 64, 3); -WARP_SELECT_IMPL(float, false, 64, 3); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatF1024.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatF1024.cu deleted file mode 100644 index 9effd9ee75..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatF1024.cu +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -WARP_SELECT_IMPL(float, false, 1024, 8); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatF2048.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatF2048.cu deleted file mode 100644 index 3abc7e61f8..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatF2048.cu +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -namespace faiss { namespace gpu { - -#if GPU_MAX_SELECTION_K >= 2048 -WARP_SELECT_IMPL(float, false, 2048, 8); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatF512.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatF512.cu deleted file mode 100644 index 0d92dc0361..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatF512.cu +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -WARP_SELECT_IMPL(float, false, 512, 8); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatT1024.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatT1024.cu deleted file mode 100644 index caae455f26..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatT1024.cu +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -WARP_SELECT_IMPL(float, true, 1024, 8); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatT2048.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatT2048.cu deleted file mode 100644 index b7cb048461..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatT2048.cu +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -namespace faiss { namespace gpu { - -#if GPU_MAX_SELECTION_K >= 2048 -WARP_SELECT_IMPL(float, true, 2048, 8); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatT512.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatT512.cu deleted file mode 100644 index c8de86a237..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectFloatT512.cu +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -WARP_SELECT_IMPL(float, true, 512, 8); - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf1.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf1.cu deleted file mode 100644 index da3206d454..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf1.cu +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 -WARP_SELECT_IMPL(half, true, 1, 1); -WARP_SELECT_IMPL(half, false, 1, 1); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf128.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf128.cu deleted file mode 100644 index 8705e593c5..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf128.cu +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 -WARP_SELECT_IMPL(half, true, 128, 3); -WARP_SELECT_IMPL(half, false, 128, 3); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf256.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf256.cu deleted file mode 100644 index a7af219582..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf256.cu +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 -WARP_SELECT_IMPL(half, true, 256, 4); -WARP_SELECT_IMPL(half, false, 256, 4); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf32.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf32.cu deleted file mode 100644 index d7ed389aec..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf32.cu +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 -WARP_SELECT_IMPL(half, true, 32, 2); -WARP_SELECT_IMPL(half, false, 32, 2); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf64.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf64.cu deleted file mode 100644 index fea6c40b9c..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalf64.cu +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 -WARP_SELECT_IMPL(half, true, 64, 3); -WARP_SELECT_IMPL(half, false, 64, 3); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfF1024.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfF1024.cu deleted file mode 100644 index d99eea9c7c..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfF1024.cu +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 -WARP_SELECT_IMPL(half, false, 1024, 8); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfF2048.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfF2048.cu deleted file mode 100644 index 030d28e17f..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfF2048.cu +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -namespace faiss { namespace gpu { - -#if GPU_MAX_SELECTION_K >= 2048 -#ifdef FAISS_USE_FLOAT16 -WARP_SELECT_IMPL(half, false, 2048, 8); -#endif -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfF512.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfF512.cu deleted file mode 100644 index 651d727580..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfF512.cu +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 -WARP_SELECT_IMPL(half, false, 512, 8); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfT1024.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfT1024.cu deleted file mode 100644 index 5a576d7c48..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfT1024.cu +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 -WARP_SELECT_IMPL(half, true, 1024, 8); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfT2048.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfT2048.cu deleted file mode 100644 index b5bd1f9e53..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfT2048.cu +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -namespace faiss { namespace gpu { - -#if GPU_MAX_SELECTION_K >= 2048 -#ifdef FAISS_USE_FLOAT16 -WARP_SELECT_IMPL(half, true, 2048, 8); -#endif -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfT512.cu b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfT512.cu deleted file mode 100644 index 21b8660273..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectHalfT512.cu +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -namespace faiss { namespace gpu { - -#ifdef FAISS_USE_FLOAT16 -WARP_SELECT_IMPL(half, true, 512, 8); -#endif - -} } // namespace diff --git a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectImpl.cuh b/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectImpl.cuh deleted file mode 100644 index eee8ef0d5c..0000000000 --- a/core/src/index/thirdparty/faiss/gpu/utils/warpselect/WarpSelectImpl.cuh +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -#define WARP_SELECT_DECL(TYPE, DIR, WARP_Q) \ - extern void runWarpSelect_ ## TYPE ## _ ## DIR ## _ ## WARP_Q ## _( \ - Tensor& in, \ - Tensor& outK, \ - Tensor& outV, \ - bool dir, \ - int k, \ - cudaStream_t stream) - -#define WARP_SELECT_IMPL(TYPE, DIR, WARP_Q, THREAD_Q) \ - void runWarpSelect_ ## TYPE ## _ ## DIR ## _ ## WARP_Q ## _( \ - Tensor& in, \ - Tensor& outK, \ - Tensor& outV, \ - bool dir, \ - int k, \ - cudaStream_t stream) { \ - \ - constexpr int kWarpSelectNumThreads = 128; \ - auto grid = dim3(utils::divUp(in.getSize(0), \ - (kWarpSelectNumThreads / kWarpSize))); \ - auto block = dim3(kWarpSelectNumThreads); \ - \ - FAISS_ASSERT(k <= WARP_Q); \ - FAISS_ASSERT(dir == DIR); \ - \ - auto kInit = dir ? Limits::getMin() : Limits::getMax(); \ - auto vInit = -1; \ - \ - warpSelect \ - <<>>(in, outK, outV, kInit, vInit, k); \ - CUDA_TEST_ERROR(); \ - } - -#define WARP_SELECT_CALL(TYPE, DIR, WARP_Q) \ - runWarpSelect_ ## TYPE ## _ ## DIR ## _ ## WARP_Q ## _( \ - in, outK, outV, dir, k, stream) diff --git a/core/src/index/thirdparty/faiss/impl/AuxIndexStructures.cpp b/core/src/index/thirdparty/faiss/impl/AuxIndexStructures.cpp deleted file mode 100644 index 7482fb7b3b..0000000000 --- a/core/src/index/thirdparty/faiss/impl/AuxIndexStructures.cpp +++ /dev/null @@ -1,322 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include - -#include - - -namespace faiss { - - -/*********************************************************************** - * RangeSearchResult - ***********************************************************************/ - -RangeSearchResult::RangeSearchResult (idx_t nq, bool alloc_lims): nq (nq) { - if (alloc_lims) { - lims = new size_t [nq + 1]; - memset (lims, 0, sizeof(*lims) * (nq + 1)); - } else { - lims = nullptr; - } - labels = nullptr; - distances = nullptr; - buffer_size = 1024 * 256; -} - -/// called when lims contains the nb of elements result entries -/// for each query -void RangeSearchResult::do_allocation () { - size_t ofs = 0; - for (int i = 0; i < nq; i++) { - size_t n = lims[i]; - lims [i] = ofs; - ofs += n; - } - lims [nq] = ofs; - labels = new idx_t [ofs]; - distances = new float [ofs]; -} - -RangeSearchResult::~RangeSearchResult () { - delete [] labels; - delete [] distances; - delete [] lims; -} - - - - - -/*********************************************************************** - * BufferList - ***********************************************************************/ - - -BufferList::BufferList (size_t buffer_size): - buffer_size (buffer_size) -{ - wp = buffer_size; -} - -BufferList::~BufferList () -{ - for (int i = 0; i < buffers.size(); i++) { - delete [] buffers[i].ids; - delete [] buffers[i].dis; - } -} - -void BufferList::add (idx_t id, float dis) { - if (wp == buffer_size) { // need new buffer - append_buffer(); - } - Buffer & buf = buffers.back(); - buf.ids [wp] = id; - buf.dis [wp] = dis; - wp++; -} - - -void BufferList::append_buffer () -{ - Buffer buf = {new idx_t [buffer_size], new float [buffer_size]}; - buffers.push_back (buf); - wp = 0; -} - -/// copy elemnts ofs:ofs+n-1 seen as linear data in the buffers to -/// tables dest_ids, dest_dis -void BufferList::copy_range (size_t ofs, size_t n, - idx_t * dest_ids, float *dest_dis) -{ - size_t bno = ofs / buffer_size; - ofs -= bno * buffer_size; - while (n > 0) { - size_t ncopy = ofs + n < buffer_size ? n : buffer_size - ofs; - Buffer buf = buffers [bno]; - memcpy (dest_ids, buf.ids + ofs, ncopy * sizeof(*dest_ids)); - memcpy (dest_dis, buf.dis + ofs, ncopy * sizeof(*dest_dis)); - dest_ids += ncopy; - dest_dis += ncopy; - ofs = 0; - bno ++; - n -= ncopy; - } -} - - -/*********************************************************************** - * RangeSearchPartialResult - ***********************************************************************/ - -void RangeQueryResult::add (float dis, idx_t id) { - nres++; - pres->add (id, dis); -} - - - -RangeSearchPartialResult::RangeSearchPartialResult (RangeSearchResult * res_in): - BufferList(res_in->buffer_size), - res(res_in) -{} - - -/// begin a new result -RangeQueryResult & - RangeSearchPartialResult::new_result (idx_t qno) -{ - RangeQueryResult qres = {qno, 0, this}; - queries.push_back (qres); - return queries.back(); -} - - -void RangeSearchPartialResult::finalize () -{ - set_lims (); -#pragma omp barrier - -#pragma omp single - res->do_allocation (); - -#pragma omp barrier - copy_result (); -} - - -/// called by range_search before do_allocation -void RangeSearchPartialResult::set_lims () -{ - for (int i = 0; i < queries.size(); i++) { - RangeQueryResult & qres = queries[i]; - res->lims[qres.qno] = qres.nres; - } -} - -/// called by range_search after do_allocation -void RangeSearchPartialResult::copy_result (bool incremental) -{ - size_t ofs = 0; - for (int i = 0; i < queries.size(); i++) { - RangeQueryResult & qres = queries[i]; - - copy_range (ofs, qres.nres, - res->labels + res->lims[qres.qno], - res->distances + res->lims[qres.qno]); - if (incremental) { - res->lims[qres.qno] += qres.nres; - } - ofs += qres.nres; - } -} - -void RangeSearchPartialResult::merge (std::vector & - partial_results, bool do_delete) -{ - - int npres = partial_results.size(); - if (npres == 0) return; - RangeSearchResult *result = partial_results[0]->res; - size_t nx = result->nq; - - // count - for (const RangeSearchPartialResult * pres : partial_results) { - if (!pres) continue; - for (const RangeQueryResult &qres : pres->queries) { - result->lims[qres.qno] += qres.nres; - } - } - result->do_allocation (); - for (int j = 0; j < npres; j++) { - if (!partial_results[j]) continue; - partial_results[j]->copy_result (true); - if (do_delete) { - delete partial_results[j]; - partial_results[j] = nullptr; - } - } - - // reset the limits - for (size_t i = nx; i > 0; i--) { - result->lims [i] = result->lims [i - 1]; - } - result->lims [0] = 0; -} - -/*********************************************************************** - * IDSelectorRange - ***********************************************************************/ - -IDSelectorRange::IDSelectorRange (idx_t imin, idx_t imax): - imin (imin), imax (imax) -{ -} - -bool IDSelectorRange::is_member (idx_t id) const -{ - return id >= imin && id < imax; -} - -/*********************************************************************** - * IDSelectorArray - ***********************************************************************/ - -IDSelectorArray::IDSelectorArray (size_t n, const idx_t *ids): - n (n), ids(ids) -{ -} - -bool IDSelectorArray::is_member (idx_t id) const -{ - for (idx_t i = 0; i < n; i++) { - if (ids[i] == id) return true; - } - return false; -} - - -/*********************************************************************** - * IDSelectorBatch - ***********************************************************************/ - -IDSelectorBatch::IDSelectorBatch (size_t n, const idx_t *indices) -{ - nbits = 0; - while (n > (1L << nbits)) nbits++; - nbits += 5; - // for n = 1M, nbits = 25 is optimal, see P56659518 - - mask = (1L << nbits) - 1; - bloom.resize (1UL << (nbits - 3), 0); - for (long i = 0; i < n; i++) { - Index::idx_t id = indices[i]; - set.insert(id); - id &= mask; - bloom[id >> 3] |= 1 << (id & 7); - } -} - -bool IDSelectorBatch::is_member (idx_t i) const -{ - long im = i & mask; - if(!(bloom[im>>3] & (1 << (im & 7)))) { - return 0; - } - return set.count(i); -} - - -/*********************************************************** - * Interrupt callback - ***********************************************************/ - - -std::unique_ptr InterruptCallback::instance; - -std::mutex InterruptCallback::lock; - -void InterruptCallback::clear_instance () { - delete instance.release (); -} - -void InterruptCallback::check () { - if (!instance.get()) { - return; - } - if (instance->want_interrupt ()) { - FAISS_THROW_MSG ("computation interrupted"); - } -} - -bool InterruptCallback::is_interrupted () { - if (!instance.get()) { - return false; - } - std::lock_guard guard(lock); - return instance->want_interrupt(); -} - - -size_t InterruptCallback::get_period_hint (size_t flops) { - if (!instance.get()) { - return 1L << 30; // never check - } - // for 10M flops, it is reasonable to check once every 10 iterations - return std::max((size_t)10 * 10 * 1000 * 1000 / (flops + 1), (size_t)1); -} - - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/AuxIndexStructures.h b/core/src/index/thirdparty/faiss/impl/AuxIndexStructures.h deleted file mode 100644 index c82b9ed560..0000000000 --- a/core/src/index/thirdparty/faiss/impl/AuxIndexStructures.h +++ /dev/null @@ -1,257 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -// Auxiliary index structures, that are used in indexes but that can -// be forward-declared - -#ifndef FAISS_AUX_INDEX_STRUCTURES_H -#define FAISS_AUX_INDEX_STRUCTURES_H - -#include - -#include -#include -#include -#include - -#include - -namespace faiss { - -/** The objective is to have a simple result structure while - * minimizing the number of mem copies in the result. The method - * do_allocation can be overloaded to allocate the result tables in - * the matrix type of a scripting language like Lua or Python. */ -struct RangeSearchResult { - size_t nq; ///< nb of queries - size_t *lims; ///< size (nq + 1) - - typedef Index::idx_t idx_t; - - idx_t *labels; ///< result for query i is labels[lims[i]:lims[i+1]] - float *distances; ///< corresponding distances (not sorted) - - size_t buffer_size; ///< size of the result buffers used - - /// lims must be allocated on input to range_search. - explicit RangeSearchResult (idx_t nq, bool alloc_lims=true); - - /// called when lims contains the nb of elements result entries - /// for each query - - virtual void do_allocation (); - - virtual ~RangeSearchResult (); -}; - - -/** Encapsulates a set of ids to remove. */ -struct IDSelector { - typedef Index::idx_t idx_t; - virtual bool is_member (idx_t id) const = 0; - virtual ~IDSelector() {} -}; - - - -/** remove ids between [imni, imax) */ -struct IDSelectorRange: IDSelector { - idx_t imin, imax; - - IDSelectorRange (idx_t imin, idx_t imax); - bool is_member(idx_t id) const override; - ~IDSelectorRange() override {} -}; - -/** simple list of elements to remove - * - * this is inefficient in most cases, except for IndexIVF with - * maintain_direct_map - */ -struct IDSelectorArray: IDSelector { - size_t n; - const idx_t *ids; - - IDSelectorArray (size_t n, const idx_t *ids); - bool is_member(idx_t id) const override; - ~IDSelectorArray() override {} -}; - -/** Remove ids from a set. Repetitions of ids in the indices set - * passed to the constructor does not hurt performance. The hash - * function used for the bloom filter and GCC's implementation of - * unordered_set are just the least significant bits of the id. This - * works fine for random ids or ids in sequences but will produce many - * hash collisions if lsb's are always the same */ -struct IDSelectorBatch: IDSelector { - - std::unordered_set set; - - typedef unsigned char uint8_t; - std::vector bloom; // assumes low bits of id are a good hash value - int nbits; - idx_t mask; - - IDSelectorBatch (size_t n, const idx_t *indices); - bool is_member(idx_t id) const override; - ~IDSelectorBatch() override {} -}; - -/**************************************************************** - * Result structures for range search. - * - * The main constraint here is that we want to support parallel - * queries from different threads in various ways: 1 thread per query, - * several threads per query. We store the actual results in blocks of - * fixed size rather than exponentially increasing memory. At the end, - * we copy the block content to a linear result array. - *****************************************************************/ - -/** List of temporary buffers used to store results before they are - * copied to the RangeSearchResult object. */ -struct BufferList { - typedef Index::idx_t idx_t; - - // buffer sizes in # entries - size_t buffer_size; - - struct Buffer { - idx_t *ids; - float *dis; - }; - - std::vector buffers; - size_t wp; ///< write pointer in the last buffer. - - explicit BufferList (size_t buffer_size); - - ~BufferList (); - - /// create a new buffer - void append_buffer (); - - /// add one result, possibly appending a new buffer if needed - void add (idx_t id, float dis); - - /// copy elemnts ofs:ofs+n-1 seen as linear data in the buffers to - /// tables dest_ids, dest_dis - void copy_range (size_t ofs, size_t n, - idx_t * dest_ids, float *dest_dis); - -}; - -struct RangeSearchPartialResult; - -/// result structure for a single query -struct RangeQueryResult { - using idx_t = Index::idx_t; - idx_t qno; //< id of the query - size_t nres; //< nb of results for this query - RangeSearchPartialResult * pres; - - /// called by search function to report a new result - void add (float dis, idx_t id); -}; - -/// the entries in the buffers are split per query -struct RangeSearchPartialResult: BufferList { - RangeSearchResult * res; - - /// eventually the result will be stored in res_in - explicit RangeSearchPartialResult (RangeSearchResult * res_in); - - /// query ids + nb of results per query. - std::vector queries; - - /// begin a new result - RangeQueryResult & new_result (idx_t qno); - - /***************************************** - * functions used at the end of the search to merge the result - * lists */ - void finalize (); - - /// called by range_search before do_allocation - void set_lims (); - - /// called by range_search after do_allocation - void copy_result (bool incremental = false); - - /// merge a set of PartialResult's into one RangeSearchResult - /// on ouptut the partialresults are empty! - static void merge (std::vector & - partial_results, bool do_delete=true); - -}; - - -/*********************************************************** - * The distance computer maintains a current query and computes - * distances to elements in an index that supports random access. - * - * The DistanceComputer is not intended to be thread-safe (eg. because - * it maintains counters) so the distance functions are not const, - * instanciate one from each thread if needed. - ***********************************************************/ -struct DistanceComputer { - using idx_t = Index::idx_t; - - /// called before computing distances - virtual void set_query(const float *x) = 0; - - /// compute distance of vector i to current query - virtual float operator () (idx_t i) = 0; - - /// compute distance between two stored vectors - virtual float symmetric_dis (idx_t i, idx_t j) = 0; - - virtual ~DistanceComputer() {} -}; - -/*********************************************************** - * Interrupt callback - ***********************************************************/ - -struct InterruptCallback { - virtual bool want_interrupt () = 0; - virtual ~InterruptCallback() {} - - // lock that protects concurrent calls to is_interrupted - static std::mutex lock; - - static std::unique_ptr instance; - - static void clear_instance (); - - /** check if: - * - an interrupt callback is set - * - the callback retuns true - * if this is the case, then throw an exception. Should not be called - * from multiple threds. - */ - static void check (); - - /// same as check() but return true if is interrupted instead of - /// throwing. Can be called from multiple threads. - static bool is_interrupted (); - - /** assuming each iteration takes a certain number of flops, what - * is a reasonable interval to check for interrupts? - */ - static size_t get_period_hint (size_t flops); - -}; - - - -}; // namespace faiss - - - -#endif diff --git a/core/src/index/thirdparty/faiss/impl/FaissAssert.h b/core/src/index/thirdparty/faiss/impl/FaissAssert.h deleted file mode 100644 index f906589d46..0000000000 --- a/core/src/index/thirdparty/faiss/impl/FaissAssert.h +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_ASSERT_INCLUDED -#define FAISS_ASSERT_INCLUDED - -#include -#include -#include -#include - -/// -/// Assertions -/// - -#define FAISS_ASSERT(X) \ - do { \ - if (! (X)) { \ - fprintf(stderr, "Faiss assertion '%s' failed in %s " \ - "at %s:%d\n", \ - #X, __PRETTY_FUNCTION__, __FILE__, __LINE__); \ - abort(); \ - } \ - } while (false) - -#define FAISS_ASSERT_MSG(X, MSG) \ - do { \ - if (! (X)) { \ - fprintf(stderr, "Faiss assertion '%s' failed in %s " \ - "at %s:%d; details: " MSG "\n", \ - #X, __PRETTY_FUNCTION__, __FILE__, __LINE__); \ - abort(); \ - } \ - } while (false) - -#define FAISS_ASSERT_FMT(X, FMT, ...) \ - do { \ - if (! (X)) { \ - fprintf(stderr, "Faiss assertion '%s' failed in %s " \ - "at %s:%d; details: " FMT "\n", \ - #X, __PRETTY_FUNCTION__, __FILE__, __LINE__, __VA_ARGS__); \ - abort(); \ - } \ - } while (false) - -/// -/// Exceptions for returning user errors -/// - -#define FAISS_THROW_MSG(MSG) \ - do { \ - throw faiss::FaissException(MSG, __PRETTY_FUNCTION__, __FILE__, __LINE__); \ - } while (false) - -#define FAISS_THROW_FMT(FMT, ...) \ - do { \ - std::string __s; \ - int __size = snprintf(nullptr, 0, FMT, __VA_ARGS__); \ - __s.resize(__size + 1); \ - snprintf(&__s[0], __s.size(), FMT, __VA_ARGS__); \ - throw faiss::FaissException(__s, __PRETTY_FUNCTION__, __FILE__, __LINE__); \ - } while (false) - -/// -/// Exceptions thrown upon a conditional failure -/// - -#define FAISS_THROW_IF_NOT(X) \ - do { \ - if (!(X)) { \ - FAISS_THROW_FMT("Error: '%s' failed", #X); \ - } \ - } while (false) - -#define FAISS_THROW_IF_NOT_MSG(X, MSG) \ - do { \ - if (!(X)) { \ - FAISS_THROW_FMT("Error: '%s' failed: " MSG, #X); \ - } \ - } while (false) - -#define FAISS_THROW_IF_NOT_FMT(X, FMT, ...) \ - do { \ - if (!(X)) { \ - FAISS_THROW_FMT("Error: '%s' failed: " FMT, #X, __VA_ARGS__); \ - } \ - } while (false) - -#endif diff --git a/core/src/index/thirdparty/faiss/impl/FaissException.cpp b/core/src/index/thirdparty/faiss/impl/FaissException.cpp deleted file mode 100644 index c79930e55e..0000000000 --- a/core/src/index/thirdparty/faiss/impl/FaissException.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include -#include - -namespace faiss { - -FaissException::FaissException(const std::string& m) - : msg(m) { -} - -FaissException::FaissException(const std::string& m, - const char* funcName, - const char* file, - int line) { - int size = snprintf(nullptr, 0, "Error in %s at %s:%d: %s", - funcName, file, line, m.c_str()); - msg.resize(size + 1); - snprintf(&msg[0], msg.size(), "Error in %s at %s:%d: %s", - funcName, file, line, m.c_str()); -} - -const char* -FaissException::what() const noexcept { - return msg.c_str(); -} - -void handleExceptions( - std::vector>& exceptions) { - if (exceptions.size() == 1) { - // throw the single received exception directly - std::rethrow_exception(exceptions.front().second); - - } else if (exceptions.size() > 1) { - // multiple exceptions; aggregate them and return a single exception - std::stringstream ss; - - for (auto& p : exceptions) { - try { - std::rethrow_exception(p.second); - } catch (std::exception& ex) { - if (ex.what()) { - // exception message available - ss << "Exception thrown from index " << p.first << ": " - << ex.what() << "\n"; - } else { - // No message available - ss << "Unknown exception thrown from index " << p.first << "\n"; - } - } catch (...) { - ss << "Unknown exception thrown from index " << p.first << "\n"; - } - } - - throw FaissException(ss.str()); - } -} - -} diff --git a/core/src/index/thirdparty/faiss/impl/FaissException.h b/core/src/index/thirdparty/faiss/impl/FaissException.h deleted file mode 100644 index 9d54edbad5..0000000000 --- a/core/src/index/thirdparty/faiss/impl/FaissException.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_EXCEPTION_INCLUDED -#define FAISS_EXCEPTION_INCLUDED - -#include -#include -#include -#include - -namespace faiss { - -/// Base class for Faiss exceptions -class FaissException : public std::exception { - public: - explicit FaissException(const std::string& msg); - - FaissException(const std::string& msg, - const char* funcName, - const char* file, - int line); - - /// from std::exception - const char* what() const noexcept override; - - std::string msg; -}; - -/// Handle multiple exceptions from worker threads, throwing an appropriate -/// exception that aggregates the information -/// The pair int is the thread that generated the exception -void -handleExceptions(std::vector>& exceptions); - -/** bare-bones unique_ptr - * this one deletes with delete [] */ -template -struct ScopeDeleter { - const T * ptr; - explicit ScopeDeleter (const T* ptr = nullptr): ptr (ptr) {} - void release () {ptr = nullptr; } - void set (const T * ptr_in) { ptr = ptr_in; } - void swap (ScopeDeleter &other) {std::swap (ptr, other.ptr); } - ~ScopeDeleter () { - delete [] ptr; - } -}; - -/** same but deletes with the simple delete (least common case) */ -template -struct ScopeDeleter1 { - const T * ptr; - explicit ScopeDeleter1 (const T* ptr = nullptr): ptr (ptr) {} - void release () {ptr = nullptr; } - void set (const T * ptr_in) { ptr = ptr_in; } - void swap (ScopeDeleter1 &other) {std::swap (ptr, other.ptr); } - ~ScopeDeleter1 () { - delete ptr; - } -}; - -} - -#endif diff --git a/core/src/index/thirdparty/faiss/impl/HNSW.cpp b/core/src/index/thirdparty/faiss/impl/HNSW.cpp deleted file mode 100644 index 740ab0d136..0000000000 --- a/core/src/index/thirdparty/faiss/impl/HNSW.cpp +++ /dev/null @@ -1,817 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include - -#include - -namespace faiss { - - -/************************************************************** - * HNSW structure implementation - **************************************************************/ - -int HNSW::nb_neighbors(int layer_no) const -{ - return cum_nneighbor_per_level[layer_no + 1] - - cum_nneighbor_per_level[layer_no]; -} - -void HNSW::set_nb_neighbors(int level_no, int n) -{ - FAISS_THROW_IF_NOT(levels.size() == 0); - int cur_n = nb_neighbors(level_no); - for (int i = level_no + 1; i < cum_nneighbor_per_level.size(); i++) { - cum_nneighbor_per_level[i] += n - cur_n; - } -} - -int HNSW::cum_nb_neighbors(int layer_no) const -{ - return cum_nneighbor_per_level[layer_no]; -} - -void HNSW::neighbor_range(idx_t no, int layer_no, - size_t * begin, size_t * end) const -{ - size_t o = offsets[no]; - *begin = o + cum_nb_neighbors(layer_no); - *end = o + cum_nb_neighbors(layer_no + 1); -} - - - -HNSW::HNSW(int M) : rng(12345) { - set_default_probas(M, 1.0 / log(M)); - max_level = -1; - entry_point = -1; - efSearch = 16; - efConstruction = 40; - upper_beam = 1; - offsets.push_back(0); -} - - -int HNSW::random_level() -{ - double f = rng.rand_float(); - // could be a bit faster with bissection - for (int level = 0; level < assign_probas.size(); level++) { - if (f < assign_probas[level]) { - return level; - } - f -= assign_probas[level]; - } - // happens with exponentially low probability - return assign_probas.size() - 1; -} - -void HNSW::set_default_probas(int M, float levelMult) -{ - int nn = 0; - cum_nneighbor_per_level.push_back (0); - for (int level = 0; ;level++) { - float proba = exp(-level / levelMult) * (1 - exp(-1 / levelMult)); - if (proba < 1e-9) break; - assign_probas.push_back(proba); - nn += level == 0 ? M * 2 : M; - cum_nneighbor_per_level.push_back (nn); - } -} - -void HNSW::clear_neighbor_tables(int level) -{ - for (int i = 0; i < levels.size(); i++) { - size_t begin, end; - neighbor_range(i, level, &begin, &end); - for (size_t j = begin; j < end; j++) { - neighbors[j] = -1; - } - } -} - - -void HNSW::reset() { - max_level = -1; - entry_point = -1; - offsets.clear(); - offsets.push_back(0); - levels.clear(); - neighbors.clear(); -} - - - -void HNSW::print_neighbor_stats(int level) const -{ - FAISS_THROW_IF_NOT (level < cum_nneighbor_per_level.size()); - printf("stats on level %d, max %d neighbors per vertex:\n", - level, nb_neighbors(level)); - size_t tot_neigh = 0, tot_common = 0, tot_reciprocal = 0, n_node = 0; -#pragma omp parallel for reduction(+: tot_neigh) reduction(+: tot_common) \ - reduction(+: tot_reciprocal) reduction(+: n_node) - for (int i = 0; i < levels.size(); i++) { - if (levels[i] > level) { - n_node++; - size_t begin, end; - neighbor_range(i, level, &begin, &end); - std::unordered_set neighset; - for (size_t j = begin; j < end; j++) { - if (neighbors [j] < 0) break; - neighset.insert(neighbors[j]); - } - int n_neigh = neighset.size(); - int n_common = 0; - int n_reciprocal = 0; - for (size_t j = begin; j < end; j++) { - storage_idx_t i2 = neighbors[j]; - if (i2 < 0) break; - FAISS_ASSERT(i2 != i); - size_t begin2, end2; - neighbor_range(i2, level, &begin2, &end2); - for (size_t j2 = begin2; j2 < end2; j2++) { - storage_idx_t i3 = neighbors[j2]; - if (i3 < 0) break; - if (i3 == i) { - n_reciprocal++; - continue; - } - if (neighset.count(i3)) { - neighset.erase(i3); - n_common++; - } - } - } - tot_neigh += n_neigh; - tot_common += n_common; - tot_reciprocal += n_reciprocal; - } - } - float normalizer = n_node; - printf(" nb of nodes at that level %ld\n", n_node); - printf(" neighbors per node: %.2f (%ld)\n", - tot_neigh / normalizer, tot_neigh); - printf(" nb of reciprocal neighbors: %.2f\n", tot_reciprocal / normalizer); - printf(" nb of neighbors that are also neighbor-of-neighbors: %.2f (%ld)\n", - tot_common / normalizer, tot_common); - - - -} - - -void HNSW::fill_with_random_links(size_t n) -{ - int max_level = prepare_level_tab(n); - RandomGenerator rng2(456); - - for (int level = max_level - 1; level >= 0; --level) { - std::vector elts; - for (int i = 0; i < n; i++) { - if (levels[i] > level) { - elts.push_back(i); - } - } - printf ("linking %ld elements in level %d\n", - elts.size(), level); - - if (elts.size() == 1) continue; - - for (int ii = 0; ii < elts.size(); ii++) { - int i = elts[ii]; - size_t begin, end; - neighbor_range(i, 0, &begin, &end); - for (size_t j = begin; j < end; j++) { - int other = 0; - do { - other = elts[rng2.rand_int(elts.size())]; - } while(other == i); - - neighbors[j] = other; - } - } - } -} - - -int HNSW::prepare_level_tab(size_t n, bool preset_levels) -{ - size_t n0 = offsets.size() - 1; - - if (preset_levels) { - FAISS_ASSERT (n0 + n == levels.size()); - } else { - FAISS_ASSERT (n0 == levels.size()); - for (int i = 0; i < n; i++) { - int pt_level = random_level(); - levels.push_back(pt_level + 1); - } - } - - int max_level = 0; - for (int i = 0; i < n; i++) { - int pt_level = levels[i + n0] - 1; - if (pt_level > max_level) max_level = pt_level; - offsets.push_back(offsets.back() + - cum_nb_neighbors(pt_level + 1)); - neighbors.resize(offsets.back(), -1); - } - - return max_level; -} - - -/** Enumerate vertices from farthest to nearest from query, keep a - * neighbor only if there is no previous neighbor that is closer to - * that vertex than the query. - */ -void HNSW::shrink_neighbor_list( - DistanceComputer& qdis, - std::priority_queue& input, - std::vector& output, - int max_size) -{ - while (input.size() > 0) { - NodeDistFarther v1 = input.top(); - input.pop(); - float dist_v1_q = v1.d; - - bool good = true; - for (NodeDistFarther v2 : output) { - float dist_v1_v2 = qdis.symmetric_dis(v2.id, v1.id); - - if (dist_v1_v2 < dist_v1_q) { - good = false; - break; - } - } - - if (good) { - output.push_back(v1); - if (output.size() >= max_size) { - return; - } - } - } -} - - -namespace { - - -using storage_idx_t = HNSW::storage_idx_t; -using NodeDistCloser = HNSW::NodeDistCloser; -using NodeDistFarther = HNSW::NodeDistFarther; - - -/************************************************************** - * Addition subroutines - **************************************************************/ - - -/// remove neighbors from the list to make it smaller than max_size -void shrink_neighbor_list( - DistanceComputer& qdis, - std::priority_queue& resultSet1, - int max_size) -{ - if (resultSet1.size() < max_size) { - return; - } - std::priority_queue resultSet; - std::vector returnlist; - - while (resultSet1.size() > 0) { - resultSet.emplace(resultSet1.top().d, resultSet1.top().id); - resultSet1.pop(); - } - - HNSW::shrink_neighbor_list(qdis, resultSet, returnlist, max_size); - - for (NodeDistFarther curen2 : returnlist) { - resultSet1.emplace(curen2.d, curen2.id); - } - -} - - -/// add a link between two elements, possibly shrinking the list -/// of links to make room for it. -void add_link(HNSW& hnsw, - DistanceComputer& qdis, - storage_idx_t src, storage_idx_t dest, - int level) -{ - size_t begin, end; - hnsw.neighbor_range(src, level, &begin, &end); - if (hnsw.neighbors[end - 1] == -1) { - // there is enough room, find a slot to add it - size_t i = end; - while(i > begin) { - if (hnsw.neighbors[i - 1] != -1) break; - i--; - } - hnsw.neighbors[i] = dest; - return; - } - - // otherwise we let them fight out which to keep - - // copy to resultSet... - std::priority_queue resultSet; - resultSet.emplace(qdis.symmetric_dis(src, dest), dest); - for (size_t i = begin; i < end; i++) { // HERE WAS THE BUG - storage_idx_t neigh = hnsw.neighbors[i]; - resultSet.emplace(qdis.symmetric_dis(src, neigh), neigh); - } - - shrink_neighbor_list(qdis, resultSet, end - begin); - - // ...and back - size_t i = begin; - while (resultSet.size()) { - hnsw.neighbors[i++] = resultSet.top().id; - resultSet.pop(); - } - // they may have shrunk more than just by 1 element - while(i < end) { - hnsw.neighbors[i++] = -1; - } -} - -/// search neighbors on a single level, starting from an entry point -void search_neighbors_to_add( - HNSW& hnsw, - DistanceComputer& qdis, - std::priority_queue& results, - int entry_point, - float d_entry_point, - int level, - VisitedTable &vt) -{ - // top is nearest candidate - std::priority_queue candidates; - - NodeDistFarther ev(d_entry_point, entry_point); - candidates.push(ev); - results.emplace(d_entry_point, entry_point); - vt.set(entry_point); - - while (!candidates.empty()) { - // get nearest - const NodeDistFarther &currEv = candidates.top(); - - if (currEv.d > results.top().d) { - break; - } - int currNode = currEv.id; - candidates.pop(); - - // loop over neighbors - size_t begin, end; - hnsw.neighbor_range(currNode, level, &begin, &end); - for(size_t i = begin; i < end; i++) { - storage_idx_t nodeId = hnsw.neighbors[i]; - if (nodeId < 0) break; - if (vt.get(nodeId)) continue; - vt.set(nodeId); - - float dis = qdis(nodeId); - NodeDistFarther evE1(dis, nodeId); - - if (results.size() < hnsw.efConstruction || - results.top().d > dis) { - - results.emplace(dis, nodeId); - candidates.emplace(dis, nodeId); - if (results.size() > hnsw.efConstruction) { - results.pop(); - } - } - } - } - vt.advance(); -} - - -/************************************************************** - * Searching subroutines - **************************************************************/ - -/// greedily update a nearest vector at a given level -void greedy_update_nearest(const HNSW& hnsw, - DistanceComputer& qdis, - int level, - storage_idx_t& nearest, - float& d_nearest) -{ - for(;;) { - storage_idx_t prev_nearest = nearest; - - size_t begin, end; - hnsw.neighbor_range(nearest, level, &begin, &end); - for(size_t i = begin; i < end; i++) { - storage_idx_t v = hnsw.neighbors[i]; - if (v < 0) break; - float dis = qdis(v); - if (dis < d_nearest) { - nearest = v; - d_nearest = dis; - } - } - if (nearest == prev_nearest) { - return; - } - } -} - - -} // namespace - - -/// Finds neighbors and builds links with them, starting from an entry -/// point. The own neighbor list is assumed to be locked. -void HNSW::add_links_starting_from(DistanceComputer& ptdis, - storage_idx_t pt_id, - storage_idx_t nearest, - float d_nearest, - int level, - omp_lock_t *locks, - VisitedTable &vt) -{ - std::priority_queue link_targets; - - search_neighbors_to_add(*this, ptdis, link_targets, nearest, d_nearest, - level, vt); - - // but we can afford only this many neighbors - int M = nb_neighbors(level); - - ::faiss::shrink_neighbor_list(ptdis, link_targets, M); - - while (!link_targets.empty()) { - int other_id = link_targets.top().id; - - omp_set_lock(&locks[other_id]); - add_link(*this, ptdis, other_id, pt_id, level); - omp_unset_lock(&locks[other_id]); - - add_link(*this, ptdis, pt_id, other_id, level); - - link_targets.pop(); - } -} - - -/************************************************************** - * Building, parallel - **************************************************************/ - -void HNSW::add_with_locks(DistanceComputer& ptdis, int pt_level, int pt_id, - std::vector& locks, - VisitedTable& vt) -{ - // greedy search on upper levels - - storage_idx_t nearest; -#pragma omp critical - { - nearest = entry_point; - - if (nearest == -1) { - max_level = pt_level; - entry_point = pt_id; - } - } - - if (nearest < 0) { - return; - } - - omp_set_lock(&locks[pt_id]); - - int level = max_level; // level at which we start adding neighbors - float d_nearest = ptdis(nearest); - - for(; level > pt_level; level--) { - greedy_update_nearest(*this, ptdis, level, nearest, d_nearest); - } - - for(; level >= 0; level--) { - add_links_starting_from(ptdis, pt_id, nearest, d_nearest, - level, locks.data(), vt); - } - - omp_unset_lock(&locks[pt_id]); - - if (pt_level > max_level) { - max_level = pt_level; - entry_point = pt_id; - } -} - - -/** Do a BFS on the candidates list */ - -int HNSW::search_from_candidates( - DistanceComputer& qdis, int k, - idx_t *I, float *D, - MinimaxHeap& candidates, - VisitedTable& vt, - int level, int nres_in) const -{ - int nres = nres_in; - int ndis = 0; - for (int i = 0; i < candidates.size(); i++) { - idx_t v1 = candidates.ids[i]; - float d = candidates.dis[i]; - FAISS_ASSERT(v1 >= 0); - if (nres < k) { - faiss::maxheap_push(++nres, D, I, d, v1); - } else if (d < D[0]) { - faiss::maxheap_pop(nres--, D, I); - faiss::maxheap_push(++nres, D, I, d, v1); - } - vt.set(v1); - } - - bool do_dis_check = check_relative_distance; - int nstep = 0; - - while (candidates.size() > 0) { - float d0 = 0; - int v0 = candidates.pop_min(&d0); - - if (do_dis_check) { - // tricky stopping condition: there are more that ef - // distances that are processed already that are smaller - // than d0 - - int n_dis_below = candidates.count_below(d0); - if(n_dis_below >= efSearch) { - break; - } - } - - size_t begin, end; - neighbor_range(v0, level, &begin, &end); - - for (size_t j = begin; j < end; j++) { - int v1 = neighbors[j]; - if (v1 < 0) break; - if (vt.get(v1)) { - continue; - } - vt.set(v1); - ndis++; - float d = qdis(v1); - if (nres < k) { - faiss::maxheap_push(++nres, D, I, d, v1); - } else if (d < D[0]) { - faiss::maxheap_pop(nres--, D, I); - faiss::maxheap_push(++nres, D, I, d, v1); - } - candidates.push(v1, d); - } - - nstep++; - if (!do_dis_check && nstep > efSearch) { - break; - } - } - - if (level == 0) { -#pragma omp critical - { - hnsw_stats.n1 ++; - if (candidates.size() == 0) { - hnsw_stats.n2 ++; - } - hnsw_stats.n3 += ndis; - } - } - - return nres; -} - - -/************************************************************** - * Searching - **************************************************************/ - -std::priority_queue HNSW::search_from_candidate_unbounded( - const Node& node, - DistanceComputer& qdis, - int ef, - VisitedTable *vt) const -{ - int ndis = 0; - std::priority_queue top_candidates; - std::priority_queue, std::greater> candidates; - - top_candidates.push(node); - candidates.push(node); - - vt->set(node.second); - - while (!candidates.empty()) { - float d0; - storage_idx_t v0; - std::tie(d0, v0) = candidates.top(); - - if (d0 > top_candidates.top().first) { - break; - } - - candidates.pop(); - - size_t begin, end; - neighbor_range(v0, 0, &begin, &end); - - for (size_t j = begin; j < end; ++j) { - int v1 = neighbors[j]; - - if (v1 < 0) { - break; - } - if (vt->get(v1)) { - continue; - } - - vt->set(v1); - - float d1 = qdis(v1); - ++ndis; - - if (top_candidates.top().first > d1 || top_candidates.size() < ef) { - candidates.emplace(d1, v1); - top_candidates.emplace(d1, v1); - - if (top_candidates.size() > ef) { - top_candidates.pop(); - } - } - } - } - -#pragma omp critical - { - ++hnsw_stats.n1; - if (candidates.size() == 0) { - ++hnsw_stats.n2; - } - hnsw_stats.n3 += ndis; - } - - return top_candidates; -} - -void HNSW::search(DistanceComputer& qdis, int k, - idx_t *I, float *D, - VisitedTable& vt) const -{ - if (upper_beam == 1) { - - // greedy search on upper levels - storage_idx_t nearest = entry_point; - float d_nearest = qdis(nearest); - - for(int level = max_level; level >= 1; level--) { - greedy_update_nearest(*this, qdis, level, nearest, d_nearest); - } - - int ef = std::max(efSearch, k); - if (search_bounded_queue) { - MinimaxHeap candidates(ef); - - candidates.push(nearest, d_nearest); - - search_from_candidates(qdis, k, I, D, candidates, vt, 0); - } else { - std::priority_queue top_candidates = - search_from_candidate_unbounded(Node(d_nearest, nearest), - qdis, ef, &vt); - - while (top_candidates.size() > k) { - top_candidates.pop(); - } - - int nres = 0; - while (!top_candidates.empty()) { - float d; - storage_idx_t label; - std::tie(d, label) = top_candidates.top(); - faiss::maxheap_push(++nres, D, I, d, label); - top_candidates.pop(); - } - } - - vt.advance(); - - } else { - int candidates_size = upper_beam; - MinimaxHeap candidates(candidates_size); - - std::vector I_to_next(candidates_size); - std::vector D_to_next(candidates_size); - - int nres = 1; - I_to_next[0] = entry_point; - D_to_next[0] = qdis(entry_point); - - for(int level = max_level; level >= 0; level--) { - - // copy I, D -> candidates - - candidates.clear(); - - for (int i = 0; i < nres; i++) { - candidates.push(I_to_next[i], D_to_next[i]); - } - - if (level == 0) { - nres = search_from_candidates(qdis, k, I, D, candidates, vt, 0); - } else { - nres = search_from_candidates( - qdis, candidates_size, - I_to_next.data(), D_to_next.data(), - candidates, vt, level - ); - } - vt.advance(); - } - } -} - - -void HNSW::MinimaxHeap::push(storage_idx_t i, float v) { - if (k == n) { - if (v >= dis[0]) return; - faiss::heap_pop (k--, dis.data(), ids.data()); - --nvalid; - } - faiss::heap_push (++k, dis.data(), ids.data(), v, i); - ++nvalid; -} - -float HNSW::MinimaxHeap::max() const { - return dis[0]; -} - -int HNSW::MinimaxHeap::size() const { - return nvalid; -} - -void HNSW::MinimaxHeap::clear() { - nvalid = k = 0; -} - -int HNSW::MinimaxHeap::pop_min(float *vmin_out) { - assert(k > 0); - // returns min. This is an O(n) operation - int i = k - 1; - while (i >= 0) { - if (ids[i] != -1) break; - i--; - } - if (i == -1) return -1; - int imin = i; - float vmin = dis[i]; - i--; - while(i >= 0) { - if (ids[i] != -1 && dis[i] < vmin) { - vmin = dis[i]; - imin = i; - } - i--; - } - if (vmin_out) *vmin_out = vmin; - int ret = ids[imin]; - ids[imin] = -1; - --nvalid; - - return ret; -} - -int HNSW::MinimaxHeap::count_below(float thresh) { - int n_below = 0; - for(int i = 0; i < k; i++) { - if (dis[i] < thresh) { - n_below++; - } - } - - return n_below; -} - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/HNSW.h b/core/src/index/thirdparty/faiss/impl/HNSW.h deleted file mode 100644 index cde99c1c29..0000000000 --- a/core/src/index/thirdparty/faiss/impl/HNSW.h +++ /dev/null @@ -1,275 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#pragma once - -#include -#include -#include - -#include - -#include -#include -#include -#include - - -namespace faiss { - - -/** Implementation of the Hierarchical Navigable Small World - * datastructure. - * - * Efficient and robust approximate nearest neighbor search using - * Hierarchical Navigable Small World graphs - * - * Yu. A. Malkov, D. A. Yashunin, arXiv 2017 - * - * This implmentation is heavily influenced by the NMSlib - * implementation by Yury Malkov and Leonid Boystov - * (https://github.com/searchivarius/nmslib) - * - * The HNSW object stores only the neighbor link structure, see - * IndexHNSW.h for the full index object. - */ - - -struct VisitedTable; -struct DistanceComputer; // from AuxIndexStructures - -struct HNSW { - /// internal storage of vectors (32 bits: this is expensive) - typedef int storage_idx_t; - - /// Faiss results are 64-bit - typedef Index::idx_t idx_t; - - typedef std::pair Node; - - /** Heap structure that allows fast - */ - struct MinimaxHeap { - int n; - int k; - int nvalid; - - std::vector ids; - std::vector dis; - typedef faiss::CMax HC; - - explicit MinimaxHeap(int n): n(n), k(0), nvalid(0), ids(n), dis(n) {} - - void push(storage_idx_t i, float v); - - float max() const; - - int size() const; - - void clear(); - - int pop_min(float *vmin_out = nullptr); - - int count_below(float thresh); - }; - - - /// to sort pairs of (id, distance) from nearest to fathest or the reverse - struct NodeDistCloser { - float d; - int id; - NodeDistCloser(float d, int id): d(d), id(id) {} - bool operator < (const NodeDistCloser &obj1) const { return d < obj1.d; } - }; - - struct NodeDistFarther { - float d; - int id; - NodeDistFarther(float d, int id): d(d), id(id) {} - bool operator < (const NodeDistFarther &obj1) const { return d > obj1.d; } - }; - - - /// assignment probability to each layer (sum=1) - std::vector assign_probas; - - /// number of neighbors stored per layer (cumulative), should not - /// be changed after first add - std::vector cum_nneighbor_per_level; - - /// level of each vector (base level = 1), size = ntotal - std::vector levels; - - /// offsets[i] is the offset in the neighbors array where vector i is stored - /// size ntotal + 1 - std::vector offsets; - - /// neighbors[offsets[i]:offsets[i+1]] is the list of neighbors of vector i - /// for all levels. this is where all storage goes. - std::vector neighbors; - - /// entry point in the search structure (one of the points with maximum level - storage_idx_t entry_point; - - faiss::RandomGenerator rng; - - /// maximum level - int max_level; - - /// expansion factor at construction time - int efConstruction; - - /// expansion factor at search time - int efSearch; - - /// during search: do we check whether the next best distance is good enough? - bool check_relative_distance = true; - - /// number of entry points in levels > 0. - int upper_beam; - - /// use bounded queue during exploration - bool search_bounded_queue = true; - - // methods that initialize the tree sizes - - /// initialize the assign_probas and cum_nneighbor_per_level to - /// have 2*M links on level 0 and M links on levels > 0 - void set_default_probas(int M, float levelMult); - - /// set nb of neighbors for this level (before adding anything) - void set_nb_neighbors(int level_no, int n); - - // methods that access the tree sizes - - /// nb of neighbors for this level - int nb_neighbors(int layer_no) const; - - /// cumumlative nb up to (and excluding) this level - int cum_nb_neighbors(int layer_no) const; - - /// range of entries in the neighbors table of vertex no at layer_no - void neighbor_range(idx_t no, int layer_no, - size_t * begin, size_t * end) const; - - /// only mandatory parameter: nb of neighbors - explicit HNSW(int M = 32); - - /// pick a random level for a new point - int random_level(); - - /// add n random levels to table (for debugging...) - void fill_with_random_links(size_t n); - - void add_links_starting_from(DistanceComputer& ptdis, - storage_idx_t pt_id, - storage_idx_t nearest, - float d_nearest, - int level, - omp_lock_t *locks, - VisitedTable &vt); - - - /** add point pt_id on all levels <= pt_level and build the link - * structure for them. */ - void add_with_locks(DistanceComputer& ptdis, int pt_level, int pt_id, - std::vector& locks, - VisitedTable& vt); - - int search_from_candidates(DistanceComputer& qdis, int k, - idx_t *I, float *D, - MinimaxHeap& candidates, - VisitedTable &vt, - int level, int nres_in = 0) const; - - std::priority_queue search_from_candidate_unbounded( - const Node& node, - DistanceComputer& qdis, - int ef, - VisitedTable *vt - ) const; - - /// search interface - void search(DistanceComputer& qdis, int k, - idx_t *I, float *D, - VisitedTable& vt) const; - - void reset(); - - void clear_neighbor_tables(int level); - void print_neighbor_stats(int level) const; - - int prepare_level_tab(size_t n, bool preset_levels = false); - - static void shrink_neighbor_list( - DistanceComputer& qdis, - std::priority_queue& input, - std::vector& output, - int max_size); - -}; - - -/************************************************************** - * Auxiliary structures - **************************************************************/ - -/// set implementation optimized for fast access. -struct VisitedTable { - std::vector visited; - int visno; - - explicit VisitedTable(int size) - : visited(size), visno(1) {} - - /// set flog #no to true - void set(int no) { - visited[no] = visno; - } - - /// get flag #no - bool get(int no) const { - return visited[no] == visno; - } - - /// reset all flags to false - void advance() { - visno++; - if (visno == 250) { - // 250 rather than 255 because sometimes we use visno and visno+1 - memset(visited.data(), 0, sizeof(visited[0]) * visited.size()); - visno = 1; - } - } -}; - - -struct HNSWStats { - size_t n1, n2, n3; - size_t ndis; - size_t nreorder; - bool view; - - HNSWStats() { - reset(); - } - - void reset() { - n1 = n2 = n3 = 0; - ndis = 0; - nreorder = 0; - view = false; - } -}; - -// global var that collects them all -extern HNSWStats hnsw_stats; - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/PolysemousTraining.cpp b/core/src/index/thirdparty/faiss/impl/PolysemousTraining.cpp deleted file mode 100644 index 32166dce6a..0000000000 --- a/core/src/index/thirdparty/faiss/impl/PolysemousTraining.cpp +++ /dev/null @@ -1,954 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include - -/***************************************** - * Mixed PQ / Hamming - ******************************************/ - -namespace faiss { - - -/**************************************************** - * Optimization code - ****************************************************/ - -SimulatedAnnealingParameters::SimulatedAnnealingParameters () -{ - // set some reasonable defaults for the optimization - init_temperature = 0.7; - temperature_decay = pow (0.9, 1/500.); - // reduce by a factor 0.9 every 500 it - n_iter = 500000; - n_redo = 2; - seed = 123; - verbose = 0; - only_bit_flips = false; - init_random = false; -} - -// what would the cost update be if iw and jw were swapped? -// default implementation just computes both and computes the difference -double PermutationObjective::cost_update ( - const int *perm, int iw, int jw) const -{ - double orig_cost = compute_cost (perm); - - std::vector perm2 (n); - for (int i = 0; i < n; i++) - perm2[i] = perm[i]; - perm2[iw] = perm[jw]; - perm2[jw] = perm[iw]; - - double new_cost = compute_cost (perm2.data()); - return new_cost - orig_cost; -} - - - - -SimulatedAnnealingOptimizer::SimulatedAnnealingOptimizer ( - PermutationObjective *obj, - const SimulatedAnnealingParameters &p): - SimulatedAnnealingParameters (p), - obj (obj), - n(obj->n), - logfile (nullptr) -{ - rnd = new RandomGenerator (p.seed); - FAISS_THROW_IF_NOT (n < 100000 && n >=0 ); -} - -SimulatedAnnealingOptimizer::~SimulatedAnnealingOptimizer () -{ - delete rnd; -} - -// run the optimization and return the best result in best_perm -double SimulatedAnnealingOptimizer::run_optimization (int * best_perm) -{ - double min_cost = 1e30; - - // just do a few runs of the annealing and keep the lowest output cost - for (int it = 0; it < n_redo; it++) { - std::vector perm(n); - for (int i = 0; i < n; i++) - perm[i] = i; - if (init_random) { - for (int i = 0; i < n; i++) { - int j = i + rnd->rand_int (n - i); - std::swap (perm[i], perm[j]); - } - } - float cost = optimize (perm.data()); - if (logfile) fprintf (logfile, "\n"); - if(verbose > 1) { - printf (" optimization run %d: cost=%g %s\n", - it, cost, cost < min_cost ? "keep" : ""); - } - if (cost < min_cost) { - memcpy (best_perm, perm.data(), sizeof(perm[0]) * n); - min_cost = cost; - } - } - return min_cost; -} - -// perform the optimization loop, starting from and modifying -// permutation in-place -double SimulatedAnnealingOptimizer::optimize (int *perm) -{ - double cost = init_cost = obj->compute_cost (perm); - int log2n = 0; - while (!(n <= (1 << log2n))) log2n++; - double temperature = init_temperature; - int n_swap = 0, n_hot = 0; - for (int it = 0; it < n_iter; it++) { - temperature = temperature * temperature_decay; - int iw, jw; - if (only_bit_flips) { - iw = rnd->rand_int (n); - jw = iw ^ (1 << rnd->rand_int (log2n)); - } else { - iw = rnd->rand_int (n); - jw = rnd->rand_int (n - 1); - if (jw == iw) jw++; - } - double delta_cost = obj->cost_update (perm, iw, jw); - if (delta_cost < 0 || rnd->rand_float () < temperature) { - std::swap (perm[iw], perm[jw]); - cost += delta_cost; - n_swap++; - if (delta_cost >= 0) n_hot++; - } - if (verbose > 2 || (verbose > 1 && it % 10000 == 0)) { - printf (" iteration %d cost %g temp %g n_swap %d " - "(%d hot) \r", - it, cost, temperature, n_swap, n_hot); - fflush(stdout); - } - if (logfile) { - fprintf (logfile, "%d %g %g %d %d\n", - it, cost, temperature, n_swap, n_hot); - } - } - if (verbose > 1) printf("\n"); - return cost; -} - - - - - -/**************************************************** - * Cost functions: ReproduceDistanceTable - ****************************************************/ - - - - - - -static inline int hamming_dis (uint64_t a, uint64_t b) -{ - return __builtin_popcountl (a ^ b); -} - -namespace { - -/// optimize permutation to reproduce a distance table with Hamming distances -struct ReproduceWithHammingObjective : PermutationObjective { - int nbits; - double dis_weight_factor; - - static double sqr (double x) { return x * x; } - - - // weihgting of distances: it is more important to reproduce small - // distances well - double dis_weight (double x) const - { - return exp (-dis_weight_factor * x); - } - - std::vector target_dis; // wanted distances (size n^2) - std::vector weights; // weights for each distance (size n^2) - - // cost = quadratic difference between actual distance and Hamming distance - double compute_cost(const int* perm) const override { - double cost = 0; - for (int i = 0; i < n; i++) { - for (int j = 0; j < n; j++) { - double wanted = target_dis[i * n + j]; - double w = weights[i * n + j]; - double actual = hamming_dis(perm[i], perm[j]); - cost += w * sqr(wanted - actual); - } - } - return cost; - } - - - // what would the cost update be if iw and jw were swapped? - // computed in O(n) instead of O(n^2) for the full re-computation - double cost_update(const int* perm, int iw, int jw) const override { - double delta_cost = 0; - - for (int i = 0; i < n; i++) { - if (i == iw) { - for (int j = 0; j < n; j++) { - double wanted = target_dis[i * n + j], w = weights[i * n + j]; - double actual = hamming_dis(perm[i], perm[j]); - delta_cost -= w * sqr(wanted - actual); - double new_actual = - hamming_dis(perm[jw], perm[j == iw ? jw : j == jw ? iw : j]); - delta_cost += w * sqr(wanted - new_actual); - } - } else if (i == jw) { - for (int j = 0; j < n; j++) { - double wanted = target_dis[i * n + j], w = weights[i * n + j]; - double actual = hamming_dis(perm[i], perm[j]); - delta_cost -= w * sqr(wanted - actual); - double new_actual = - hamming_dis(perm[iw], perm[j == iw ? jw : j == jw ? iw : j]); - delta_cost += w * sqr(wanted - new_actual); - } - } else { - int j = iw; - { - double wanted = target_dis[i * n + j], w = weights[i * n + j]; - double actual = hamming_dis(perm[i], perm[j]); - delta_cost -= w * sqr(wanted - actual); - double new_actual = hamming_dis(perm[i], perm[jw]); - delta_cost += w * sqr(wanted - new_actual); - } - j = jw; - { - double wanted = target_dis[i * n + j], w = weights[i * n + j]; - double actual = hamming_dis(perm[i], perm[j]); - delta_cost -= w * sqr(wanted - actual); - double new_actual = hamming_dis(perm[i], perm[iw]); - delta_cost += w * sqr(wanted - new_actual); - } - } - } - - return delta_cost; - } - - - - ReproduceWithHammingObjective ( - int nbits, - const std::vector & dis_table, - double dis_weight_factor): - nbits (nbits), dis_weight_factor (dis_weight_factor) - { - n = 1 << nbits; - FAISS_THROW_IF_NOT (dis_table.size() == n * n); - set_affine_target_dis (dis_table); - } - - void set_affine_target_dis (const std::vector & dis_table) - { - double sum = 0, sum2 = 0; - int n2 = n * n; - for (int i = 0; i < n2; i++) { - sum += dis_table [i]; - sum2 += dis_table [i] * dis_table [i]; - } - double mean = sum / n2; - double stddev = sqrt(sum2 / n2 - (sum / n2) * (sum / n2)); - - target_dis.resize (n2); - - for (int i = 0; i < n2; i++) { - // the mapping function - double td = (dis_table [i] - mean) / stddev * sqrt(nbits / 4) + - nbits / 2; - target_dis[i] = td; - // compute a weight - weights.push_back (dis_weight (td)); - } - - } - - ~ReproduceWithHammingObjective() override {} -}; - -} // anonymous namespace - -// weihgting of distances: it is more important to reproduce small -// distances well -double ReproduceDistancesObjective::dis_weight (double x) const -{ - return exp (-dis_weight_factor * x); -} - - -double ReproduceDistancesObjective::get_source_dis (int i, int j) const -{ - return source_dis [i * n + j]; -} - -// cost = quadratic difference between actual distance and Hamming distance -double ReproduceDistancesObjective::compute_cost (const int *perm) const -{ - double cost = 0; - for (int i = 0; i < n; i++) { - for (int j = 0; j < n; j++) { - double wanted = target_dis [i * n + j]; - double w = weights [i * n + j]; - double actual = get_source_dis (perm[i], perm[j]); - cost += w * sqr (wanted - actual); - } - } - return cost; -} - -// what would the cost update be if iw and jw were swapped? -// computed in O(n) instead of O(n^2) for the full re-computation -double ReproduceDistancesObjective::cost_update( - const int *perm, int iw, int jw) const -{ - double delta_cost = 0; - for (int i = 0; i < n; i++) { - if (i == iw) { - for (int j = 0; j < n; j++) { - double wanted = target_dis [i * n + j], - w = weights [i * n + j]; - double actual = get_source_dis (perm[i], perm[j]); - delta_cost -= w * sqr (wanted - actual); - double new_actual = get_source_dis ( - perm[jw], - perm[j == iw ? jw : j == jw ? iw : j]); - delta_cost += w * sqr (wanted - new_actual); - } - } else if (i == jw) { - for (int j = 0; j < n; j++) { - double wanted = target_dis [i * n + j], - w = weights [i * n + j]; - double actual = get_source_dis (perm[i], perm[j]); - delta_cost -= w * sqr (wanted - actual); - double new_actual = get_source_dis ( - perm[iw], - perm[j == iw ? jw : j == jw ? iw : j]); - delta_cost += w * sqr (wanted - new_actual); - } - } else { - int j = iw; - { - double wanted = target_dis [i * n + j], - w = weights [i * n + j]; - double actual = get_source_dis (perm[i], perm[j]); - delta_cost -= w * sqr (wanted - actual); - double new_actual = get_source_dis (perm[i], perm[jw]); - delta_cost += w * sqr (wanted - new_actual); - } - j = jw; - { - double wanted = target_dis [i * n + j], - w = weights [i * n + j]; - double actual = get_source_dis (perm[i], perm[j]); - delta_cost -= w * sqr (wanted - actual); - double new_actual = get_source_dis (perm[i], perm[iw]); - delta_cost += w * sqr (wanted - new_actual); - } - } - } - return delta_cost; -} - - - -ReproduceDistancesObjective::ReproduceDistancesObjective ( - int n, - const double *source_dis_in, - const double *target_dis_in, - double dis_weight_factor): - dis_weight_factor (dis_weight_factor), - target_dis (target_dis_in) -{ - this->n = n; - set_affine_target_dis (source_dis_in); -} - -void ReproduceDistancesObjective::compute_mean_stdev ( - const double *tab, size_t n2, - double *mean_out, double *stddev_out) -{ - double sum = 0, sum2 = 0; - for (int i = 0; i < n2; i++) { - sum += tab [i]; - sum2 += tab [i] * tab [i]; - } - double mean = sum / n2; - double stddev = sqrt(sum2 / n2 - (sum / n2) * (sum / n2)); - *mean_out = mean; - *stddev_out = stddev; -} - -void ReproduceDistancesObjective::set_affine_target_dis ( - const double *source_dis_in) -{ - int n2 = n * n; - - double mean_src, stddev_src; - compute_mean_stdev (source_dis_in, n2, &mean_src, &stddev_src); - - double mean_target, stddev_target; - compute_mean_stdev (target_dis, n2, &mean_target, &stddev_target); - - printf ("map mean %g std %g -> mean %g std %g\n", - mean_src, stddev_src, mean_target, stddev_target); - - source_dis.resize (n2); - weights.resize (n2); - - for (int i = 0; i < n2; i++) { - // the mapping function - source_dis[i] = (source_dis_in[i] - mean_src) / stddev_src - * stddev_target + mean_target; - - // compute a weight - weights [i] = dis_weight (target_dis[i]); - } - -} - -/**************************************************** - * Cost functions: RankingScore - ****************************************************/ - -/// Maintains a 3D table of elementary costs. -/// Accumulates elements based on Hamming distance comparisons -template -struct Score3Computer: PermutationObjective { - - int nc; - - // cost matrix of size nc * nc *nc - // n_gt (i,j,k) = count of d_gt(x, y-) < d_gt(x, y+) - // where x has PQ code i, y- PQ code j and y+ PQ code k - std::vector n_gt; - - - /// the cost is a triple loop on the nc * nc * nc matrix of entries. - /// - Taccu compute (const int * perm) const - { - Taccu accu = 0; - const Ttab *p = n_gt.data(); - for (int i = 0; i < nc; i++) { - int ip = perm [i]; - for (int j = 0; j < nc; j++) { - int jp = perm [j]; - for (int k = 0; k < nc; k++) { - int kp = perm [k]; - if (hamming_dis (ip, jp) < - hamming_dis (ip, kp)) { - accu += *p; // n_gt [ ( i * nc + j) * nc + k]; - } - p++; - } - } - } - return accu; - } - - - /** cost update if entries iw and jw of the permutation would be - * swapped. - * - * The computation is optimized by avoiding elements in the - * nc*nc*nc cube that are known not to change. For nc=256, this - * reduces the nb of cells to visit to about 6/256 th of the - * cells. Practical speedup is about 8x, and the code is quite - * complex :-/ - */ - Taccu compute_update (const int *perm, int iw, int jw) const - { - assert (iw != jw); - if (iw > jw) std::swap (iw, jw); - - Taccu accu = 0; - const Ttab * n_gt_i = n_gt.data(); - for (int i = 0; i < nc; i++) { - int ip0 = perm [i]; - int ip = perm [i == iw ? jw : i == jw ? iw : i]; - - //accu += update_i (perm, iw, jw, ip0, ip, n_gt_i); - - accu += update_i_cross (perm, iw, jw, - ip0, ip, n_gt_i); - - if (ip != ip0) - accu += update_i_plane (perm, iw, jw, - ip0, ip, n_gt_i); - - n_gt_i += nc * nc; - } - - return accu; - } - - - Taccu update_i (const int *perm, int iw, int jw, - int ip0, int ip, const Ttab * n_gt_i) const - { - Taccu accu = 0; - const Ttab *n_gt_ij = n_gt_i; - for (int j = 0; j < nc; j++) { - int jp0 = perm[j]; - int jp = perm [j == iw ? jw : j == jw ? iw : j]; - for (int k = 0; k < nc; k++) { - int kp0 = perm [k]; - int kp = perm [k == iw ? jw : k == jw ? iw : k]; - int ng = n_gt_ij [k]; - if (hamming_dis (ip, jp) < hamming_dis (ip, kp)) { - accu += ng; - } - if (hamming_dis (ip0, jp0) < hamming_dis (ip0, kp0)) { - accu -= ng; - } - } - n_gt_ij += nc; - } - return accu; - } - - // 2 inner loops for the case ip0 != ip - Taccu update_i_plane (const int *perm, int iw, int jw, - int ip0, int ip, const Ttab * n_gt_i) const - { - Taccu accu = 0; - const Ttab *n_gt_ij = n_gt_i; - - for (int j = 0; j < nc; j++) { - if (j != iw && j != jw) { - int jp = perm[j]; - for (int k = 0; k < nc; k++) { - if (k != iw && k != jw) { - int kp = perm [k]; - Ttab ng = n_gt_ij [k]; - if (hamming_dis (ip, jp) < hamming_dis (ip, kp)) { - accu += ng; - } - if (hamming_dis (ip0, jp) < hamming_dis (ip0, kp)) { - accu -= ng; - } - } - } - } - n_gt_ij += nc; - } - return accu; - } - - /// used for the 8 cells were the 3 indices are swapped - inline Taccu update_k (const int *perm, int iw, int jw, - int ip0, int ip, int jp0, int jp, - int k, - const Ttab * n_gt_ij) const - { - Taccu accu = 0; - int kp0 = perm [k]; - int kp = perm [k == iw ? jw : k == jw ? iw : k]; - Ttab ng = n_gt_ij [k]; - if (hamming_dis (ip, jp) < hamming_dis (ip, kp)) { - accu += ng; - } - if (hamming_dis (ip0, jp0) < hamming_dis (ip0, kp0)) { - accu -= ng; - } - return accu; - } - - /// compute update on a line of k's, where i and j are swapped - Taccu update_j_line (const int *perm, int iw, int jw, - int ip0, int ip, int jp0, int jp, - const Ttab * n_gt_ij) const - { - Taccu accu = 0; - for (int k = 0; k < nc; k++) { - if (k == iw || k == jw) continue; - int kp = perm [k]; - Ttab ng = n_gt_ij [k]; - if (hamming_dis (ip, jp) < hamming_dis (ip, kp)) { - accu += ng; - } - if (hamming_dis (ip0, jp0) < hamming_dis (ip0, kp)) { - accu -= ng; - } - } - return accu; - } - - - /// considers the 2 pairs of crossing lines j=iw or jw and k = iw or kw - Taccu update_i_cross (const int *perm, int iw, int jw, - int ip0, int ip, const Ttab * n_gt_i) const - { - Taccu accu = 0; - const Ttab *n_gt_ij = n_gt_i; - - for (int j = 0; j < nc; j++) { - int jp0 = perm[j]; - int jp = perm [j == iw ? jw : j == jw ? iw : j]; - - accu += update_k (perm, iw, jw, ip0, ip, jp0, jp, iw, n_gt_ij); - accu += update_k (perm, iw, jw, ip0, ip, jp0, jp, jw, n_gt_ij); - - if (jp != jp0) - accu += update_j_line (perm, iw, jw, ip0, ip, jp0, jp, n_gt_ij); - - n_gt_ij += nc; - } - return accu; - } - - - /// PermutationObjective implementeation (just negates the scores - /// for minimization) - - double compute_cost(const int* perm) const override { - return -compute(perm); - } - - double cost_update(const int* perm, int iw, int jw) const override { - double ret = -compute_update(perm, iw, jw); - return ret; - } - - ~Score3Computer() override {} -}; - - - - - -struct IndirectSort { - const float *tab; - bool operator () (int a, int b) {return tab[a] < tab[b]; } -}; - - - -struct RankingScore2: Score3Computer { - int nbits; - int nq, nb; - const uint32_t *qcodes, *bcodes; - const float *gt_distances; - - RankingScore2 (int nbits, int nq, int nb, - const uint32_t *qcodes, const uint32_t *bcodes, - const float *gt_distances): - nbits(nbits), nq(nq), nb(nb), qcodes(qcodes), - bcodes(bcodes), gt_distances(gt_distances) - { - n = nc = 1 << nbits; - n_gt.resize (nc * nc * nc); - init_n_gt (); - } - - - double rank_weight (int r) - { - return 1.0 / (r + 1); - } - - /// count nb of i, j in a x b st. i < j - /// a and b should be sorted on input - /// they are the ranks of j and k respectively. - /// specific version for diff-of-rank weighting, cannot optimized - /// with a cumulative table - double accum_gt_weight_diff (const std::vector & a, - const std::vector & b) - { - int nb = b.size(), na = a.size(); - - double accu = 0; - int j = 0; - for (int i = 0; i < na; i++) { - int ai = a[i]; - while (j < nb && ai >= b[j]) j++; - - double accu_i = 0; - for (int k = j; k < b.size(); k++) - accu_i += rank_weight (b[k] - ai); - - accu += rank_weight (ai) * accu_i; - - } - return accu; - } - - void init_n_gt () - { - for (int q = 0; q < nq; q++) { - const float *gtd = gt_distances + q * nb; - const uint32_t *cb = bcodes;// all same codes - float * n_gt_q = & n_gt [qcodes[q] * nc * nc]; - - printf("init gt for q=%d/%d \r", q, nq); fflush(stdout); - - std::vector rankv (nb); - int * ranks = rankv.data(); - - // elements in each code bin, ordered by rank within each bin - std::vector > tab (nc); - - { // build rank table - IndirectSort s = {gtd}; - for (int j = 0; j < nb; j++) ranks[j] = j; - std::sort (ranks, ranks + nb, s); - } - - for (int rank = 0; rank < nb; rank++) { - int i = ranks [rank]; - tab [cb[i]].push_back (rank); - } - - - // this is very expensive. Any suggestion for improvement - // welcome. - for (int i = 0; i < nc; i++) { - std::vector & di = tab[i]; - for (int j = 0; j < nc; j++) { - std::vector & dj = tab[j]; - n_gt_q [i * nc + j] += accum_gt_weight_diff (di, dj); - - } - } - - } - - } - -}; - - -/***************************************** - * PolysemousTraining - ******************************************/ - - - -PolysemousTraining::PolysemousTraining () -{ - optimization_type = OT_ReproduceDistances_affine; - ntrain_permutation = 0; - dis_weight_factor = log(2); -} - - - -void PolysemousTraining::optimize_reproduce_distances ( - ProductQuantizer &pq) const -{ - - int dsub = pq.dsub; - - int n = pq.ksub; - int nbits = pq.nbits; - -#pragma omp parallel for - for (int m = 0; m < pq.M; m++) { - std::vector dis_table; - - // printf ("Optimizing quantizer %d\n", m); - - float * centroids = pq.get_centroids (m, 0); - - for (int i = 0; i < n; i++) { - for (int j = 0; j < n; j++) { - dis_table.push_back (fvec_L2sqr (centroids + i * dsub, - centroids + j * dsub, - dsub)); - } - } - - std::vector perm (n); - ReproduceWithHammingObjective obj ( - nbits, dis_table, - dis_weight_factor); - - - SimulatedAnnealingOptimizer optim (&obj, *this); - - if (log_pattern.size()) { - char fname[256]; - snprintf (fname, 256, log_pattern.c_str(), m); - printf ("opening log file %s\n", fname); - optim.logfile = fopen (fname, "w"); - FAISS_THROW_IF_NOT_MSG (optim.logfile, "could not open logfile"); - } - double final_cost = optim.run_optimization (perm.data()); - - if (verbose > 0) { - printf ("SimulatedAnnealingOptimizer for m=%d: %g -> %g\n", - m, optim.init_cost, final_cost); - } - - if (log_pattern.size()) fclose (optim.logfile); - - std::vector centroids_copy; - for (int i = 0; i < dsub * n; i++) - centroids_copy.push_back (centroids[i]); - - for (int i = 0; i < n; i++) - memcpy (centroids + perm[i] * dsub, - centroids_copy.data() + i * dsub, - dsub * sizeof(centroids[0])); - - } - -} - - -void PolysemousTraining::optimize_ranking ( - ProductQuantizer &pq, size_t n, const float *x) const -{ - - int dsub = pq.dsub; - - int nbits = pq.nbits; - - std::vector all_codes (pq.code_size * n); - - pq.compute_codes (x, all_codes.data(), n); - - FAISS_THROW_IF_NOT (pq.nbits == 8); - - if (n == 0) - pq.compute_sdc_table (); - -#pragma omp parallel for - for (int m = 0; m < pq.M; m++) { - size_t nq, nb; - std::vector codes; // query codes, then db codes - std::vector gt_distances; // nq * nb matrix of distances - - if (n > 0) { - std::vector xtrain (n * dsub); - for (int i = 0; i < n; i++) - memcpy (xtrain.data() + i * dsub, - x + i * pq.d + m * dsub, - sizeof(float) * dsub); - - codes.resize (n); - for (int i = 0; i < n; i++) - codes [i] = all_codes [i * pq.code_size + m]; - - nq = n / 4; nb = n - nq; - const float *xq = xtrain.data(); - const float *xb = xq + nq * dsub; - - gt_distances.resize (nq * nb); - - pairwise_L2sqr (dsub, - nq, xq, - nb, xb, - gt_distances.data()); - } else { - nq = nb = pq.ksub; - codes.resize (2 * nq); - for (int i = 0; i < nq; i++) - codes[i] = codes [i + nq] = i; - - gt_distances.resize (nq * nb); - - memcpy (gt_distances.data (), - pq.sdc_table.data () + m * nq * nb, - sizeof (float) * nq * nb); - } - - double t0 = getmillisecs (); - - PermutationObjective *obj = new RankingScore2 ( - nbits, nq, nb, - codes.data(), codes.data() + nq, - gt_distances.data ()); - ScopeDeleter1 del (obj); - - if (verbose > 0) { - printf(" m=%d, nq=%ld, nb=%ld, intialize RankingScore " - "in %.3f ms\n", - m, nq, nb, getmillisecs () - t0); - } - - SimulatedAnnealingOptimizer optim (obj, *this); - - if (log_pattern.size()) { - char fname[256]; - snprintf (fname, 256, log_pattern.c_str(), m); - printf ("opening log file %s\n", fname); - optim.logfile = fopen (fname, "w"); - FAISS_THROW_IF_NOT_FMT (optim.logfile, - "could not open logfile %s", fname); - } - - std::vector perm (pq.ksub); - - double final_cost = optim.run_optimization (perm.data()); - printf ("SimulatedAnnealingOptimizer for m=%d: %g -> %g\n", - m, optim.init_cost, final_cost); - - if (log_pattern.size()) fclose (optim.logfile); - - float * centroids = pq.get_centroids (m, 0); - - std::vector centroids_copy; - for (int i = 0; i < dsub * pq.ksub; i++) - centroids_copy.push_back (centroids[i]); - - for (int i = 0; i < pq.ksub; i++) - memcpy (centroids + perm[i] * dsub, - centroids_copy.data() + i * dsub, - dsub * sizeof(centroids[0])); - - } - -} - - - -void PolysemousTraining::optimize_pq_for_hamming (ProductQuantizer &pq, - size_t n, const float *x) const -{ - if (optimization_type == OT_None) { - - } else if (optimization_type == OT_ReproduceDistances_affine) { - optimize_reproduce_distances (pq); - } else { - optimize_ranking (pq, n, x); - } - - pq.compute_sdc_table (); - -} - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/PolysemousTraining.h b/core/src/index/thirdparty/faiss/impl/PolysemousTraining.h deleted file mode 100644 index c27a48c999..0000000000 --- a/core/src/index/thirdparty/faiss/impl/PolysemousTraining.h +++ /dev/null @@ -1,158 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_POLYSEMOUS_TRAINING_INCLUDED -#define FAISS_POLYSEMOUS_TRAINING_INCLUDED - - -#include - - -namespace faiss { - - -/// parameters used for the simulated annealing method -struct SimulatedAnnealingParameters { - - // optimization parameters - double init_temperature; // init probaility of accepting a bad swap - double temperature_decay; // at each iteration the temp is multiplied by this - int n_iter; // nb of iterations - int n_redo; // nb of runs of the simulation - int seed; // random seed - int verbose; - bool only_bit_flips; // restrict permutation changes to bit flips - bool init_random; // intialize with a random permutation (not identity) - - // set reasonable defaults - SimulatedAnnealingParameters (); - -}; - - -/// abstract class for the loss function -struct PermutationObjective { - - int n; - - virtual double compute_cost (const int *perm) const = 0; - - // what would the cost update be if iw and jw were swapped? - // default implementation just computes both and computes the difference - virtual double cost_update (const int *perm, int iw, int jw) const; - - virtual ~PermutationObjective () {} -}; - - -struct ReproduceDistancesObjective : PermutationObjective { - - double dis_weight_factor; - - static double sqr (double x) { return x * x; } - - // weihgting of distances: it is more important to reproduce small - // distances well - double dis_weight (double x) const; - - std::vector source_dis; ///< "real" corrected distances (size n^2) - const double * target_dis; ///< wanted distances (size n^2) - std::vector weights; ///< weights for each distance (size n^2) - - double get_source_dis (int i, int j) const; - - // cost = quadratic difference between actual distance and Hamming distance - double compute_cost(const int* perm) const override; - - // what would the cost update be if iw and jw were swapped? - // computed in O(n) instead of O(n^2) for the full re-computation - double cost_update(const int* perm, int iw, int jw) const override; - - ReproduceDistancesObjective ( - int n, - const double *source_dis_in, - const double *target_dis_in, - double dis_weight_factor); - - static void compute_mean_stdev (const double *tab, size_t n2, - double *mean_out, double *stddev_out); - - void set_affine_target_dis (const double *source_dis_in); - - ~ReproduceDistancesObjective() override {} -}; - -struct RandomGenerator; - -/// Simulated annealing optimization algorithm for permutations. - struct SimulatedAnnealingOptimizer: SimulatedAnnealingParameters { - - PermutationObjective *obj; - int n; ///< size of the permutation - FILE *logfile; /// logs values of the cost function - - SimulatedAnnealingOptimizer (PermutationObjective *obj, - const SimulatedAnnealingParameters &p); - RandomGenerator *rnd; - - /// remember intial cost of optimization - double init_cost; - - // main entry point. Perform the optimization loop, starting from - // and modifying permutation in-place - double optimize (int *perm); - - // run the optimization and return the best result in best_perm - double run_optimization (int * best_perm); - - virtual ~SimulatedAnnealingOptimizer (); -}; - - - - -/// optimizes the order of indices in a ProductQuantizer -struct PolysemousTraining: SimulatedAnnealingParameters { - - enum Optimization_type_t { - OT_None, - OT_ReproduceDistances_affine, ///< default - OT_Ranking_weighted_diff ///< same as _2, but use rank of y+ - rank of y- - }; - Optimization_type_t optimization_type; - - /** use 1/4 of the training points for the optimization, with - * max. ntrain_permutation. If ntrain_permutation == 0: train on - * centroids */ - int ntrain_permutation; - double dis_weight_factor; ///< decay of exp that weights distance loss - - // filename pattern for the logging of iterations - std::string log_pattern; - - // sets default values - PolysemousTraining (); - - /// reorder the centroids so that the Hamming distace becomes a - /// good approximation of the SDC distance (called by train) - void optimize_pq_for_hamming (ProductQuantizer & pq, - size_t n, const float *x) const; - - /// called by optimize_pq_for_hamming - void optimize_ranking (ProductQuantizer &pq, size_t n, const float *x) const; - /// called by optimize_pq_for_hamming - void optimize_reproduce_distances (ProductQuantizer &pq) const; - -}; - - -} // namespace faiss - - -#endif diff --git a/core/src/index/thirdparty/faiss/impl/ProductQuantizer-inl.h b/core/src/index/thirdparty/faiss/impl/ProductQuantizer-inl.h deleted file mode 100644 index 01937dca9f..0000000000 --- a/core/src/index/thirdparty/faiss/impl/ProductQuantizer-inl.h +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -namespace faiss { - -inline -PQEncoderGeneric::PQEncoderGeneric(uint8_t *code, int nbits, - uint8_t offset) - : code(code), offset(offset), nbits(nbits), reg(0) -{ - assert(nbits <= 64); - if (offset > 0) { - reg = (*code & ((1 << offset) - 1)); - } -} - -inline -void PQEncoderGeneric::encode(uint64_t x) -{ - reg |= (uint8_t)(x << offset); - x >>= (8 - offset); - if (offset + nbits >= 8) { - *code++ = reg; - - for (int i = 0; i < (nbits - (8 - offset)) / 8; ++i) { - *code++ = (uint8_t)x; - x >>= 8; - } - - offset += nbits; - offset &= 7; - reg = (uint8_t)x; - } else { - offset += nbits; - } -} - -inline -PQEncoderGeneric::~PQEncoderGeneric() -{ - if (offset > 0) { - *code = reg; - } -} - - -inline -PQEncoder8::PQEncoder8(uint8_t *code, int nbits) - : code(code) { - assert(8 == nbits); -} - -inline -void PQEncoder8::encode(uint64_t x) { - *code++ = (uint8_t)x; -} - -inline -PQEncoder16::PQEncoder16(uint8_t *code, int nbits) - : code((uint16_t *)code) { - assert(16 == nbits); -} - -inline -void PQEncoder16::encode(uint64_t x) { - *code++ = (uint16_t)x; -} - - -inline -PQDecoderGeneric::PQDecoderGeneric(const uint8_t *code, - int nbits) - : code(code), - offset(0), - nbits(nbits), - mask((1ull << nbits) - 1), - reg(0) { - assert(nbits <= 64); -} - -inline -uint64_t PQDecoderGeneric::decode() { - if (offset == 0) { - reg = *code; - } - uint64_t c = (reg >> offset); - - if (offset + nbits >= 8) { - uint64_t e = 8 - offset; - ++code; - for (int i = 0; i < (nbits - (8 - offset)) / 8; ++i) { - c |= ((uint64_t)(*code++) << e); - e += 8; - } - - offset += nbits; - offset &= 7; - if (offset > 0) { - reg = *code; - c |= ((uint64_t)reg << e); - } - } else { - offset += nbits; - } - - return c & mask; -} - - -inline -PQDecoder8::PQDecoder8(const uint8_t *code, int nbits) - : code(code) { - assert(8 == nbits); -} - -inline -uint64_t PQDecoder8::decode() { - return (uint64_t)(*code++); -} - - -inline -PQDecoder16::PQDecoder16(const uint8_t *code, int nbits) - : code((uint16_t *)code) { - assert(16 == nbits); -} - -inline -uint64_t PQDecoder16::decode() { - return (uint64_t)(*code++); -} - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/ProductQuantizer.cpp b/core/src/index/thirdparty/faiss/impl/ProductQuantizer.cpp deleted file mode 100644 index a9658af46a..0000000000 --- a/core/src/index/thirdparty/faiss/impl/ProductQuantizer.cpp +++ /dev/null @@ -1,759 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - - -extern "C" { - -/* declare BLAS functions, see http://www.netlib.org/clapack/cblas/ */ - -int sgemm_ (const char *transa, const char *transb, FINTEGER *m, FINTEGER * - n, FINTEGER *k, const float *alpha, const float *a, - FINTEGER *lda, const float *b, FINTEGER * - ldb, float *beta, float *c, FINTEGER *ldc); - -} - - -namespace faiss { - - -/* compute an estimator using look-up tables for typical values of M */ -template -void pq_estimators_from_tables_Mmul4 (int M, const CT * codes, - size_t ncodes, - const float * __restrict dis_table, - size_t ksub, - size_t k, - float * heap_dis, - int64_t * heap_ids) -{ - - for (size_t j = 0; j < ncodes; j++) { - float dis = 0; - const float *dt = dis_table; - - for (size_t m = 0; m < M; m+=4) { - float dism = 0; - dism = dt[*codes++]; dt += ksub; - dism += dt[*codes++]; dt += ksub; - dism += dt[*codes++]; dt += ksub; - dism += dt[*codes++]; dt += ksub; - dis += dism; - } - - if (C::cmp (heap_dis[0], dis)) { - heap_swap_top (k, heap_dis, heap_ids, dis, j); - } - } -} - - -template -void pq_estimators_from_tables_M4 (const CT * codes, - size_t ncodes, - const float * __restrict dis_table, - size_t ksub, - size_t k, - float * heap_dis, - int64_t * heap_ids) -{ - - for (size_t j = 0; j < ncodes; j++) { - float dis = 0; - const float *dt = dis_table; - dis = dt[*codes++]; dt += ksub; - dis += dt[*codes++]; dt += ksub; - dis += dt[*codes++]; dt += ksub; - dis += dt[*codes++]; - - if (C::cmp (heap_dis[0], dis)) { - heap_swap_top (k, heap_dis, heap_ids, dis, j); - } - } -} - - -template -static inline void pq_estimators_from_tables (const ProductQuantizer& pq, - const CT * codes, - size_t ncodes, - const float * dis_table, - size_t k, - float * heap_dis, - int64_t * heap_ids) -{ - - if (pq.M == 4) { - - pq_estimators_from_tables_M4 (codes, ncodes, - dis_table, pq.ksub, k, - heap_dis, heap_ids); - return; - } - - if (pq.M % 4 == 0) { - pq_estimators_from_tables_Mmul4 (pq.M, codes, ncodes, - dis_table, pq.ksub, k, - heap_dis, heap_ids); - return; - } - - /* Default is relatively slow */ - const size_t M = pq.M; - const size_t ksub = pq.ksub; - for (size_t j = 0; j < ncodes; j++) { - float dis = 0; - const float * __restrict dt = dis_table; - for (int m = 0; m < M; m++) { - dis += dt[*codes++]; - dt += ksub; - } - if (C::cmp (heap_dis[0], dis)) { - heap_swap_top (k, heap_dis, heap_ids, dis, j); - } - } -} - -template -static inline void pq_estimators_from_tables_generic(const ProductQuantizer& pq, - size_t nbits, - const uint8_t *codes, - size_t ncodes, - const float *dis_table, - size_t k, - float *heap_dis, - int64_t *heap_ids) -{ - const size_t M = pq.M; - const size_t ksub = pq.ksub; - for (size_t j = 0; j < ncodes; ++j) { - PQDecoderGeneric decoder( - codes + j * pq.code_size, nbits - ); - float dis = 0; - const float * __restrict dt = dis_table; - for (size_t m = 0; m < M; m++) { - uint64_t c = decoder.decode(); - dis += dt[c]; - dt += ksub; - } - - if (C::cmp(heap_dis[0], dis)) { - heap_swap_top(k, heap_dis, heap_ids, dis, j); - } - } -} - -/********************************************* - * PQ implementation - *********************************************/ - - - -ProductQuantizer::ProductQuantizer (size_t d, size_t M, size_t nbits): - d(d), M(M), nbits(nbits), assign_index(nullptr) -{ - set_derived_values (); -} - -ProductQuantizer::ProductQuantizer () - : ProductQuantizer(0, 1, 0) {} - -void ProductQuantizer::set_derived_values () { - // quite a few derived values - FAISS_THROW_IF_NOT (d % M == 0); - dsub = d / M; - code_size = (nbits * M + 7) / 8; - ksub = 1 << nbits; - centroids.resize (d * ksub); - verbose = false; - train_type = Train_default; -} - -void ProductQuantizer::set_params (const float * centroids_, int m) -{ - memcpy (get_centroids(m, 0), centroids_, - ksub * dsub * sizeof (centroids_[0])); -} - - -static void init_hypercube (int d, int nbits, - int n, const float * x, - float *centroids) -{ - - std::vector mean (d); - for (int i = 0; i < n; i++) - for (int j = 0; j < d; j++) - mean [j] += x[i * d + j]; - - float maxm = 0; - for (int j = 0; j < d; j++) { - mean [j] /= n; - if (fabs(mean[j]) > maxm) maxm = fabs(mean[j]); - } - - for (int i = 0; i < (1 << nbits); i++) { - float * cent = centroids + i * d; - for (int j = 0; j < nbits; j++) - cent[j] = mean [j] + (((i >> j) & 1) ? 1 : -1) * maxm; - for (int j = nbits; j < d; j++) - cent[j] = mean [j]; - } - - -} - -static void init_hypercube_pca (int d, int nbits, - int n, const float * x, - float *centroids) -{ - PCAMatrix pca (d, nbits); - pca.train (n, x); - - - for (int i = 0; i < (1 << nbits); i++) { - float * cent = centroids + i * d; - for (int j = 0; j < d; j++) { - cent[j] = pca.mean[j]; - float f = 1.0; - for (int k = 0; k < nbits; k++) - cent[j] += f * - sqrt (pca.eigenvalues [k]) * - (((i >> k) & 1) ? 1 : -1) * - pca.PCAMat [j + k * d]; - } - } - -} - -void ProductQuantizer::train (int n, const float * x) -{ - if (train_type != Train_shared) { - train_type_t final_train_type; - final_train_type = train_type; - if (train_type == Train_hypercube || - train_type == Train_hypercube_pca) { - if (dsub < nbits) { - final_train_type = Train_default; - printf ("cannot train hypercube: nbits=%ld > log2(d=%ld)\n", - nbits, dsub); - } - } - - float * xslice = new float[n * dsub]; - ScopeDeleter del (xslice); - for (int m = 0; m < M; m++) { - for (int j = 0; j < n; j++) - memcpy (xslice + j * dsub, - x + j * d + m * dsub, - dsub * sizeof(float)); - - Clustering clus (dsub, ksub, cp); - - // we have some initialization for the centroids - if (final_train_type != Train_default) { - clus.centroids.resize (dsub * ksub); - } - - switch (final_train_type) { - case Train_hypercube: - init_hypercube (dsub, nbits, n, xslice, - clus.centroids.data ()); - break; - case Train_hypercube_pca: - init_hypercube_pca (dsub, nbits, n, xslice, - clus.centroids.data ()); - break; - case Train_hot_start: - memcpy (clus.centroids.data(), - get_centroids (m, 0), - dsub * ksub * sizeof (float)); - break; - default: ; - } - - if(verbose) { - clus.verbose = true; - printf ("Training PQ slice %d/%zd\n", m, M); - } - IndexFlatL2 index (dsub); - clus.train (n, xslice, assign_index ? *assign_index : index); - set_params (clus.centroids.data(), m); - } - - - } else { - - Clustering clus (dsub, ksub, cp); - - if(verbose) { - clus.verbose = true; - printf ("Training all PQ slices at once\n"); - } - - IndexFlatL2 index (dsub); - - clus.train (n * M, x, assign_index ? *assign_index : index); - for (int m = 0; m < M; m++) { - set_params (clus.centroids.data(), m); - } - - } -} - -template -void compute_code(const ProductQuantizer& pq, const float *x, uint8_t *code) { - float distances [pq.ksub]; - PQEncoder encoder(code, pq.nbits); - for (size_t m = 0; m < pq.M; m++) { - float mindis = 1e20; - uint64_t idxm = 0; - const float * xsub = x + m * pq.dsub; - - fvec_L2sqr_ny(distances, xsub, pq.get_centroids(m, 0), pq.dsub, pq.ksub); - - /* Find best centroid */ - for (size_t i = 0; i < pq.ksub; i++) { - float dis = distances[i]; - if (dis < mindis) { - mindis = dis; - idxm = i; - } - } - - encoder.encode(idxm); - } -} - -void ProductQuantizer::compute_code(const float * x, uint8_t * code) const { - switch (nbits) { - case 8: - faiss::compute_code(*this, x, code); - break; - - case 16: - faiss::compute_code(*this, x, code); - break; - - default: - faiss::compute_code(*this, x, code); - break; - } -} - -template -void decode(const ProductQuantizer& pq, const uint8_t *code, float *x) -{ - PQDecoder decoder(code, pq.nbits); - for (size_t m = 0; m < pq.M; m++) { - uint64_t c = decoder.decode(); - memcpy(x + m * pq.dsub, pq.get_centroids(m, c), sizeof(float) * pq.dsub); - } -} - -void ProductQuantizer::decode (const uint8_t *code, float *x) const -{ - switch (nbits) { - case 8: - faiss::decode(*this, code, x); - break; - - case 16: - faiss::decode(*this, code, x); - break; - - default: - faiss::decode(*this, code, x); - break; - } -} - - -void ProductQuantizer::decode (const uint8_t *code, float *x, size_t n) const -{ - for (size_t i = 0; i < n; i++) { - this->decode (code + code_size * i, x + d * i); - } -} - - -void ProductQuantizer::compute_code_from_distance_table (const float *tab, - uint8_t *code) const -{ - PQEncoderGeneric encoder(code, nbits); - for (size_t m = 0; m < M; m++) { - float mindis = 1e20; - uint64_t idxm = 0; - - /* Find best centroid */ - for (size_t j = 0; j < ksub; j++) { - float dis = *tab++; - if (dis < mindis) { - mindis = dis; - idxm = j; - } - } - - encoder.encode(idxm); - } -} - -void ProductQuantizer::compute_codes_with_assign_index ( - const float * x, - uint8_t * codes, - size_t n) -{ - FAISS_THROW_IF_NOT (assign_index && assign_index->d == dsub); - - for (size_t m = 0; m < M; m++) { - assign_index->reset (); - assign_index->add (ksub, get_centroids (m, 0)); - size_t bs = 65536; - float * xslice = new float[bs * dsub]; - ScopeDeleter del (xslice); - idx_t *assign = new idx_t[bs]; - ScopeDeleter del2 (assign); - - for (size_t i0 = 0; i0 < n; i0 += bs) { - size_t i1 = std::min(i0 + bs, n); - - for (size_t i = i0; i < i1; i++) { - memcpy (xslice + (i - i0) * dsub, - x + i * d + m * dsub, - dsub * sizeof(float)); - } - - assign_index->assign (i1 - i0, xslice, assign); - - if (nbits == 8) { - uint8_t *c = codes + code_size * i0 + m; - for (size_t i = i0; i < i1; i++) { - *c = assign[i - i0]; - c += M; - } - } else if (nbits == 16) { - uint16_t *c = (uint16_t*)(codes + code_size * i0 + m * 2); - for (size_t i = i0; i < i1; i++) { - *c = assign[i - i0]; - c += M; - } - } else { - for (size_t i = i0; i < i1; ++i) { - uint8_t *c = codes + code_size * i + ((m * nbits) / 8); - uint8_t offset = (m * nbits) % 8; - uint64_t ass = assign[i - i0]; - - PQEncoderGeneric encoder(c, nbits, offset); - encoder.encode(ass); - } - } - - } - } - -} - -void ProductQuantizer::compute_codes (const float * x, - uint8_t * codes, - size_t n) const -{ - // process by blocks to avoid using too much RAM - size_t bs = 256 * 1024; - if (n > bs) { - for (size_t i0 = 0; i0 < n; i0 += bs) { - size_t i1 = std::min(i0 + bs, n); - compute_codes (x + d * i0, codes + code_size * i0, i1 - i0); - } - return; - } - - if (dsub < 16) { // simple direct computation - -#pragma omp parallel for - for (size_t i = 0; i < n; i++) - compute_code (x + i * d, codes + i * code_size); - - } else { // worthwile to use BLAS - float *dis_tables = new float [n * ksub * M]; - ScopeDeleter del (dis_tables); - compute_distance_tables (n, x, dis_tables); - -#pragma omp parallel for - for (size_t i = 0; i < n; i++) { - uint8_t * code = codes + i * code_size; - const float * tab = dis_tables + i * ksub * M; - compute_code_from_distance_table (tab, code); - } - } -} - - -void ProductQuantizer::compute_distance_table (const float * x, - float * dis_table) const -{ - size_t m; - - for (m = 0; m < M; m++) { - fvec_L2sqr_ny (dis_table + m * ksub, - x + m * dsub, - get_centroids(m, 0), - dsub, - ksub); - } -} - -void ProductQuantizer::compute_inner_prod_table (const float * x, - float * dis_table) const -{ - size_t m; - - for (m = 0; m < M; m++) { - fvec_inner_products_ny (dis_table + m * ksub, - x + m * dsub, - get_centroids(m, 0), - dsub, - ksub); - } -} - - -void ProductQuantizer::compute_distance_tables ( - size_t nx, - const float * x, - float * dis_tables) const -{ - - if (dsub < 16) { - -#pragma omp parallel for - for (size_t i = 0; i < nx; i++) { - compute_distance_table (x + i * d, dis_tables + i * ksub * M); - } - - } else { // use BLAS - - for (int m = 0; m < M; m++) { - pairwise_L2sqr (dsub, - nx, x + dsub * m, - ksub, centroids.data() + m * dsub * ksub, - dis_tables + ksub * m, - d, dsub, ksub * M); - } - } -} - -void ProductQuantizer::compute_inner_prod_tables ( - size_t nx, - const float * x, - float * dis_tables) const -{ - - if (dsub < 16) { - -#pragma omp parallel for - for (size_t i = 0; i < nx; i++) { - compute_inner_prod_table (x + i * d, dis_tables + i * ksub * M); - } - - } else { // use BLAS - - // compute distance tables - for (int m = 0; m < M; m++) { - FINTEGER ldc = ksub * M, nxi = nx, ksubi = ksub, - dsubi = dsub, di = d; - float one = 1.0, zero = 0; - - sgemm_ ("Transposed", "Not transposed", - &ksubi, &nxi, &dsubi, - &one, ¢roids [m * dsub * ksub], &dsubi, - x + dsub * m, &di, - &zero, dis_tables + ksub * m, &ldc); - } - - } -} - -template -static void pq_knn_search_with_tables ( - const ProductQuantizer& pq, - size_t nbits, - const float *dis_tables, - const uint8_t * codes, - const size_t ncodes, - HeapArray * res, - bool init_finalize_heap) -{ - size_t k = res->k, nx = res->nh; - size_t ksub = pq.ksub, M = pq.M; - - -#pragma omp parallel for - for (size_t i = 0; i < nx; i++) { - /* query preparation for asymmetric search: compute look-up tables */ - const float* dis_table = dis_tables + i * ksub * M; - - /* Compute distances and keep smallest values */ - int64_t * __restrict heap_ids = res->ids + i * k; - float * __restrict heap_dis = res->val + i * k; - - if (init_finalize_heap) { - heap_heapify (k, heap_dis, heap_ids); - } - - switch (nbits) { - case 8: - pq_estimators_from_tables (pq, - codes, ncodes, - dis_table, - k, heap_dis, heap_ids); - break; - - case 16: - pq_estimators_from_tables (pq, - (uint16_t*)codes, ncodes, - dis_table, - k, heap_dis, heap_ids); - break; - - default: - pq_estimators_from_tables_generic (pq, - nbits, - codes, ncodes, - dis_table, - k, heap_dis, heap_ids); - break; - } - - if (init_finalize_heap) { - heap_reorder (k, heap_dis, heap_ids); - } - } -} - -void ProductQuantizer::search (const float * __restrict x, - size_t nx, - const uint8_t * codes, - const size_t ncodes, - float_maxheap_array_t * res, - bool init_finalize_heap) const -{ - FAISS_THROW_IF_NOT (nx == res->nh); - std::unique_ptr dis_tables(new float [nx * ksub * M]); - compute_distance_tables (nx, x, dis_tables.get()); - - pq_knn_search_with_tables> ( - *this, nbits, dis_tables.get(), codes, ncodes, res, init_finalize_heap); -} - -void ProductQuantizer::search_ip (const float * __restrict x, - size_t nx, - const uint8_t * codes, - const size_t ncodes, - float_minheap_array_t * res, - bool init_finalize_heap) const -{ - FAISS_THROW_IF_NOT (nx == res->nh); - std::unique_ptr dis_tables(new float [nx * ksub * M]); - compute_inner_prod_tables (nx, x, dis_tables.get()); - - pq_knn_search_with_tables > ( - *this, nbits, dis_tables.get(), codes, ncodes, res, init_finalize_heap); -} - - - -static float sqr (float x) { - return x * x; -} - -void ProductQuantizer::compute_sdc_table () -{ - sdc_table.resize (M * ksub * ksub); - - for (int m = 0; m < M; m++) { - - const float *cents = centroids.data() + m * ksub * dsub; - float * dis_tab = sdc_table.data() + m * ksub * ksub; - - // TODO optimize with BLAS - for (int i = 0; i < ksub; i++) { - const float *centi = cents + i * dsub; - for (int j = 0; j < ksub; j++) { - float accu = 0; - const float *centj = cents + j * dsub; - for (int k = 0; k < dsub; k++) - accu += sqr (centi[k] - centj[k]); - dis_tab [i + j * ksub] = accu; - } - } - } -} - -void ProductQuantizer::search_sdc (const uint8_t * qcodes, - size_t nq, - const uint8_t * bcodes, - const size_t nb, - float_maxheap_array_t * res, - bool init_finalize_heap) const -{ - FAISS_THROW_IF_NOT (sdc_table.size() == M * ksub * ksub); - FAISS_THROW_IF_NOT (nbits == 8); - size_t k = res->k; - - -#pragma omp parallel for - for (size_t i = 0; i < nq; i++) { - - /* Compute distances and keep smallest values */ - idx_t * heap_ids = res->ids + i * k; - float * heap_dis = res->val + i * k; - const uint8_t * qcode = qcodes + i * code_size; - - if (init_finalize_heap) - maxheap_heapify (k, heap_dis, heap_ids); - - const uint8_t * bcode = bcodes; - for (size_t j = 0; j < nb; j++) { - float dis = 0; - const float * tab = sdc_table.data(); - for (int m = 0; m < M; m++) { - dis += tab[bcode[m] + qcode[m] * ksub]; - tab += ksub * ksub; - } - if (dis < heap_dis[0]) { - maxheap_swap_top (k, heap_dis, heap_ids, dis, j); - } - bcode += code_size; - } - - if (init_finalize_heap) - maxheap_reorder (k, heap_dis, heap_ids); - } - -} - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/ProductQuantizer.h b/core/src/index/thirdparty/faiss/impl/ProductQuantizer.h deleted file mode 100644 index c900d9c9d4..0000000000 --- a/core/src/index/thirdparty/faiss/impl/ProductQuantizer.h +++ /dev/null @@ -1,237 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_PRODUCT_QUANTIZER_H -#define FAISS_PRODUCT_QUANTIZER_H - -#include - -#include - -#include -#include - -namespace faiss { - -/** Product Quantizer. Implemented only for METRIC_L2 */ -struct ProductQuantizer { - - using idx_t = Index::idx_t; - - size_t d; ///< size of the input vectors - size_t M; ///< number of subquantizers - size_t nbits; ///< number of bits per quantization index - - // values derived from the above - size_t dsub; ///< dimensionality of each subvector - size_t code_size; ///< bytes per indexed vector - size_t ksub; ///< number of centroids for each subquantizer - bool verbose; ///< verbose during training? - - /// initialization - enum train_type_t { - Train_default, - Train_hot_start, ///< the centroids are already initialized - Train_shared, ///< share dictionary accross PQ segments - Train_hypercube, ///< intialize centroids with nbits-D hypercube - Train_hypercube_pca, ///< intialize centroids with nbits-D hypercube - }; - train_type_t train_type; - - ClusteringParameters cp; ///< parameters used during clustering - - /// if non-NULL, use this index for assignment (should be of size - /// d / M) - Index *assign_index; - - /// Centroid table, size M * ksub * dsub - std::vector centroids; - - /// return the centroids associated with subvector m - float * get_centroids (size_t m, size_t i) { - return ¢roids [(m * ksub + i) * dsub]; - } - const float * get_centroids (size_t m, size_t i) const { - return ¢roids [(m * ksub + i) * dsub]; - } - - // Train the product quantizer on a set of points. A clustering - // can be set on input to define non-default clustering parameters - void train (int n, const float *x); - - ProductQuantizer(size_t d, /* dimensionality of the input vectors */ - size_t M, /* number of subquantizers */ - size_t nbits); /* number of bit per subvector index */ - - ProductQuantizer (); - - /// compute derived values when d, M and nbits have been set - void set_derived_values (); - - /// Define the centroids for subquantizer m - void set_params (const float * centroids, int m); - - /// Quantize one vector with the product quantizer - void compute_code (const float * x, uint8_t * code) const ; - - /// same as compute_code for several vectors - void compute_codes (const float * x, - uint8_t * codes, - size_t n) const ; - - /// speed up code assignment using assign_index - /// (non-const because the index is changed) - void compute_codes_with_assign_index ( - const float * x, - uint8_t * codes, - size_t n); - - /// decode a vector from a given code (or n vectors if third argument) - void decode (const uint8_t *code, float *x) const; - void decode (const uint8_t *code, float *x, size_t n) const; - - /// If we happen to have the distance tables precomputed, this is - /// more efficient to compute the codes. - void compute_code_from_distance_table (const float *tab, - uint8_t *code) const; - - - /** Compute distance table for one vector. - * - * The distance table for x = [x_0 x_1 .. x_(M-1)] is a M * ksub - * matrix that contains - * - * dis_table (m, j) = || x_m - c_(m, j)||^2 - * for m = 0..M-1 and j = 0 .. ksub - 1 - * - * where c_(m, j) is the centroid no j of sub-quantizer m. - * - * @param x input vector size d - * @param dis_table output table, size M * ksub - */ - void compute_distance_table (const float * x, - float * dis_table) const; - - void compute_inner_prod_table (const float * x, - float * dis_table) const; - - - /** compute distance table for several vectors - * @param nx nb of input vectors - * @param x input vector size nx * d - * @param dis_table output table, size nx * M * ksub - */ - void compute_distance_tables (size_t nx, - const float * x, - float * dis_tables) const; - - void compute_inner_prod_tables (size_t nx, - const float * x, - float * dis_tables) const; - - - /** perform a search (L2 distance) - * @param x query vectors, size nx * d - * @param nx nb of queries - * @param codes database codes, size ncodes * code_size - * @param ncodes nb of nb vectors - * @param res heap array to store results (nh == nx) - * @param init_finalize_heap initialize heap (input) and sort (output)? - */ - void search (const float * x, - size_t nx, - const uint8_t * codes, - const size_t ncodes, - float_maxheap_array_t *res, - bool init_finalize_heap = true) const; - - /** same as search, but with inner product similarity */ - void search_ip (const float * x, - size_t nx, - const uint8_t * codes, - const size_t ncodes, - float_minheap_array_t *res, - bool init_finalize_heap = true) const; - - - /// Symmetric Distance Table - std::vector sdc_table; - - // intitialize the SDC table from the centroids - void compute_sdc_table (); - - void search_sdc (const uint8_t * qcodes, - size_t nq, - const uint8_t * bcodes, - const size_t ncodes, - float_maxheap_array_t * res, - bool init_finalize_heap = true) const; - -}; - - -/************************************************* - * Objects to encode / decode strings of bits - *************************************************/ - -struct PQEncoderGeneric { - uint8_t *code; ///< code for this vector - uint8_t offset; - const int nbits; ///< number of bits per subquantizer index - - uint8_t reg; - - PQEncoderGeneric(uint8_t *code, int nbits, uint8_t offset = 0); - - void encode(uint64_t x); - - ~PQEncoderGeneric(); -}; - - -struct PQEncoder8 { - uint8_t *code; - PQEncoder8(uint8_t *code, int nbits); - void encode(uint64_t x); -}; - -struct PQEncoder16 { - uint16_t *code; - PQEncoder16(uint8_t *code, int nbits); - void encode(uint64_t x); -}; - - -struct PQDecoderGeneric { - const uint8_t *code; - uint8_t offset; - const int nbits; - const uint64_t mask; - uint8_t reg; - PQDecoderGeneric(const uint8_t *code, int nbits); - uint64_t decode(); -}; - -struct PQDecoder8 { - const uint8_t *code; - PQDecoder8(const uint8_t *code, int nbits); - uint64_t decode(); -}; - -struct PQDecoder16 { - const uint16_t *code; - PQDecoder16(const uint8_t *code, int nbits); - uint64_t decode(); -}; - -} // namespace faiss - -#include - -#endif diff --git a/core/src/index/thirdparty/faiss/impl/ScalarQuantizer.cpp b/core/src/index/thirdparty/faiss/impl/ScalarQuantizer.cpp deleted file mode 100644 index 54f0903c4b..0000000000 --- a/core/src/index/thirdparty/faiss/impl/ScalarQuantizer.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include -#include - -#include -#include -#include - -namespace faiss { - -/******************************************************************* - * ScalarQuantizer implementation - * - * The main source of complexity is to support combinations of 4 - * variants without incurring runtime tests or virtual function calls: - * - * - 4 / 8 bits per code component - * - uniform / non-uniform - * - IP / L2 distance search - * - scalar / AVX distance computation - * - * The appropriate Quantizer object is returned via select_quantizer - * that hides the template mess. - ********************************************************************/ - - - -/******************************************************************* - * ScalarQuantizer implementation - ********************************************************************/ - -ScalarQuantizer::ScalarQuantizer - (size_t d, QuantizerType qtype): - qtype (qtype), rangestat(RangeStat::RS_minmax), rangestat_arg(0), d (d) -{ - switch (qtype) { - case QuantizerType::QT_8bit: - case QuantizerType::QT_8bit_uniform: - case QuantizerType::QT_8bit_direct: - code_size = d; - break; - case QuantizerType::QT_4bit: - case QuantizerType::QT_4bit_uniform: - code_size = (d + 1) / 2; - break; - case QuantizerType::QT_6bit: - code_size = (d * 6 + 7) / 8; - break; - case QuantizerType::QT_fp16: - code_size = d * 2; - break; - } -} - -ScalarQuantizer::ScalarQuantizer (): - qtype(QuantizerType::QT_8bit), - rangestat(RangeStat::RS_minmax), rangestat_arg(0), d (0), code_size(0) -{} - -void ScalarQuantizer::train (size_t n, const float *x) -{ - int bit_per_dim = - qtype == QuantizerType::QT_4bit_uniform ? 4 : - qtype == QuantizerType::QT_4bit ? 4 : - qtype == QuantizerType::QT_6bit ? 6 : - qtype == QuantizerType::QT_8bit_uniform ? 8 : - qtype == QuantizerType::QT_8bit ? 8 : -1; - - switch (qtype) { - case QuantizerType::QT_4bit_uniform: - case QuantizerType::QT_8bit_uniform: - train_Uniform (rangestat, rangestat_arg, - n * d, 1 << bit_per_dim, x, trained); - break; - case QuantizerType::QT_4bit: - case QuantizerType::QT_8bit: - case QuantizerType::QT_6bit: - train_NonUniform (rangestat, rangestat_arg, - n, d, 1 << bit_per_dim, x, trained); - break; - case QuantizerType::QT_fp16: - case QuantizerType::QT_8bit_direct: - // no training necessary - break; - } -} - -void ScalarQuantizer::train_residual(size_t n, - const float *x, - Index *quantizer, - bool by_residual, - bool verbose) -{ - const float * x_in = x; - - // 100k points more than enough - x = fvecs_maybe_subsample ( - d, (size_t*)&n, 100000, - x, verbose, 1234); - - ScopeDeleter del_x (x_in == x ? nullptr : x); - - if (by_residual) { - std::vector idx(n); - quantizer->assign (n, x, idx.data()); - - std::vector residuals(n * d); - quantizer->compute_residual_n (n, x, residuals.data(), idx.data()); - - train (n, residuals.data()); - } else { - train (n, x); - } -} - - -Quantizer *ScalarQuantizer::select_quantizer () const -{ - /* use hook to decide use AVX512 or not */ - sq_sel_quantizer(qtype, d, trained); -} - - -void ScalarQuantizer::compute_codes (const float * x, - uint8_t * codes, - size_t n) const -{ - std::unique_ptr squant(select_quantizer ()); - - memset (codes, 0, code_size * n); -#pragma omp parallel for - for (size_t i = 0; i < n; i++) - squant->encode_vector (x + i * d, codes + i * code_size); -} - -void ScalarQuantizer::decode (const uint8_t *codes, float *x, size_t n) const -{ - std::unique_ptr squant(select_quantizer ()); - -#pragma omp parallel for - for (size_t i = 0; i < n; i++) - squant->decode_vector (codes + i * code_size, x + i * d); -} - - -SQDistanceComputer * -ScalarQuantizer::get_distance_computer (MetricType metric) const -{ - FAISS_THROW_IF_NOT(metric == METRIC_L2 || metric == METRIC_INNER_PRODUCT); - /* use hook to decide use AVX512 or not */ - return sq_get_distance_computer(metric, qtype, d, trained); -} - - -/******************************************************************* - * IndexScalarQuantizer/IndexIVFScalarQuantizer scanner object - * - * It is an InvertedListScanner, but is designed to work with - * IndexScalarQuantizer as well. - ********************************************************************/ - -InvertedListScanner* ScalarQuantizer::select_InvertedListScanner - (MetricType mt, const Index *quantizer, - bool store_pairs, bool by_residual) const -{ - /* use hook to decide use AVX512 or not */ - return sq_sel_inv_list_scanner(mt, this, quantizer, d, store_pairs, by_residual); -} - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/ScalarQuantizer.h b/core/src/index/thirdparty/faiss/impl/ScalarQuantizer.h deleted file mode 100644 index cb447c603b..0000000000 --- a/core/src/index/thirdparty/faiss/impl/ScalarQuantizer.h +++ /dev/null @@ -1,242 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#pragma once - -#include -#include - -namespace faiss { - -/** - * The uniform quantizer has a range [vmin, vmax]. The range can be - * the same for all dimensions (uniform) or specific per dimension - * (default). - */ - -struct ScalarQuantizer { - - QuantizerType qtype; - - /** The uniform encoder can estimate the range of representable - * values of the unform encoder using different statistics. Here - * rs = rangestat_arg */ - - RangeStat rangestat; - float rangestat_arg; - - /// dimension of input vectors - size_t d; - - /// bytes per vector - size_t code_size; - - /// trained values (including the range) - std::vector trained; - - ScalarQuantizer (size_t d, QuantizerType qtype); - ScalarQuantizer (); - - void train (size_t n, const float *x); - - /// Used by an IVF index to train based on the residuals - void train_residual (size_t n, - const float *x, - Index *quantizer, - bool by_residual, - bool verbose); - - /// same as compute_code for several vectors - void compute_codes (const float * x, - uint8_t * codes, - size_t n) const ; - - /// decode a vector from a given code (or n vectors if third argument) - void decode (const uint8_t *code, float *x, size_t n) const; - - - /***************************************************** - * Objects that provide methods for encoding/decoding, distance - * computation and inverted list scanning - *****************************************************/ - - Quantizer * select_quantizer() const; - - SQDistanceComputer *get_distance_computer (MetricType metric = METRIC_L2) - const; - - InvertedListScanner *select_InvertedListScanner - (MetricType mt, const Index *quantizer, bool store_pairs, - bool by_residual=false) const; - -}; - -template -struct IVFSQScannerIP: InvertedListScanner { - DCClass dc; - bool store_pairs, by_residual; - - size_t code_size; - - idx_t list_no; /// current list (set to 0 for Flat index - float accu0; /// added to all distances - - IVFSQScannerIP(int d, const std::vector & trained, - size_t code_size, bool store_pairs, - bool by_residual): - dc(d, trained), store_pairs(store_pairs), - by_residual(by_residual), - code_size(code_size), list_no(0), accu0(0) - {} - - - void set_query (const float *query) override { - dc.set_query (query); - } - - void set_list (idx_t list_no, float coarse_dis) override { - this->list_no = list_no; - accu0 = by_residual ? coarse_dis : 0; - } - - float distance_to_code (const uint8_t *code) const final { - return accu0 + dc.query_to_code (code); - } - - size_t scan_codes (size_t list_size, - const uint8_t *codes, - const idx_t *ids, - float *simi, idx_t *idxi, - size_t k, - ConcurrentBitsetPtr bitset) const override - { - size_t nup = 0; - - for (size_t j = 0; j < list_size; j++) { - if(!bitset || !bitset->test(ids[j])){ - float accu = accu0 + dc.query_to_code (codes); - - if (accu > simi [0]) { - int64_t id = store_pairs ? (list_no << 32 | j) : ids[j]; - minheap_swap_top (k, simi, idxi, accu, id); - nup++; - } - } - codes += code_size; - } - return nup; - } - - void scan_codes_range (size_t list_size, - const uint8_t *codes, - const idx_t *ids, - float radius, - RangeQueryResult & res, - ConcurrentBitsetPtr bitset = nullptr) const override - { - for (size_t j = 0; j < list_size; j++) { - float accu = accu0 + dc.query_to_code (codes); - if (accu > radius) { - int64_t id = store_pairs ? (list_no << 32 | j) : ids[j]; - res.add (accu, id); - } - codes += code_size; - } - } -}; - - -template -struct IVFSQScannerL2: InvertedListScanner { - DCClass dc; - - bool store_pairs, by_residual; - size_t code_size; - const Index *quantizer; - idx_t list_no; /// current inverted list - const float *x; /// current query - - std::vector tmp; - - IVFSQScannerL2(int d, const std::vector & trained, - size_t code_size, const Index *quantizer, - bool store_pairs, bool by_residual): - dc(d, trained), store_pairs(store_pairs), by_residual(by_residual), - code_size(code_size), quantizer(quantizer), - list_no (0), x (nullptr), tmp (d) - { - } - - - void set_query (const float *query) override { - x = query; - if (!quantizer) { - dc.set_query (query); - } - } - - - void set_list (idx_t list_no, float /*coarse_dis*/) override { - if (by_residual) { - this->list_no = list_no; - // shift of x_in wrt centroid - quantizer->Index::compute_residual (x, tmp.data(), list_no); - dc.set_query (tmp.data ()); - } else { - dc.set_query (x); - } - } - - float distance_to_code (const uint8_t *code) const final { - return dc.query_to_code (code); - } - - size_t scan_codes (size_t list_size, - const uint8_t *codes, - const idx_t *ids, - float *simi, idx_t *idxi, - size_t k, - ConcurrentBitsetPtr bitset) const override - { - size_t nup = 0; - for (size_t j = 0; j < list_size; j++) { - if(!bitset || !bitset->test(ids[j])){ - float dis = dc.query_to_code (codes); - - if (dis < simi [0]) { - int64_t id = store_pairs ? (list_no << 32 | j) : ids[j]; - maxheap_swap_top (k, simi, idxi, dis, id); - nup++; - } - } - codes += code_size; - } - return nup; - } - - void scan_codes_range (size_t list_size, - const uint8_t *codes, - const idx_t *ids, - float radius, - RangeQueryResult & res, - ConcurrentBitsetPtr bitset = nullptr) const override - { - for (size_t j = 0; j < list_size; j++) { - float dis = dc.query_to_code (codes); - if (dis < radius) { - int64_t id = store_pairs ? (list_no << 32 | j) : ids[j]; - res.add (dis, id); - } - codes += code_size; - } - } -}; - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerCodec.h b/core/src/index/thirdparty/faiss/impl/ScalarQuantizerCodec.h deleted file mode 100644 index 38abdc7e74..0000000000 --- a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerCodec.h +++ /dev/null @@ -1,603 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#pragma once - -#include -#include -#include - -#include -#include -#include -#include - -namespace faiss { - - -/******************************************************************* - * Codec: converts between values in [0, 1] and an index in a code - * array. The "i" parameter is the vector component index (not byte - * index). - */ - -struct Codec8bit { - static void encode_component (float x, uint8_t *code, int i) { - code[i] = (int)(255 * x); - } - - static float decode_component (const uint8_t *code, int i) { - return (code[i] + 0.5f) / 255.0f; - } -}; - - -struct Codec4bit { - static void encode_component (float x, uint8_t *code, int i) { - code [i / 2] |= (int)(x * 15.0) << ((i & 1) << 2); - } - - static float decode_component (const uint8_t *code, int i) { - return (((code[i / 2] >> ((i & 1) << 2)) & 0xf) + 0.5f) / 15.0f; - } -}; - -struct Codec6bit { - static void encode_component (float x, uint8_t *code, int i) { - int bits = (int)(x * 63.0); - code += (i >> 2) * 3; - switch(i & 3) { - case 0: - code[0] |= bits; - break; - case 1: - code[0] |= bits << 6; - code[1] |= bits >> 2; - break; - case 2: - code[1] |= bits << 4; - code[2] |= bits >> 4; - break; - case 3: - code[2] |= bits << 2; - break; - } - } - - static float decode_component (const uint8_t *code, int i) { - uint8_t bits; - code += (i >> 2) * 3; - switch(i & 3) { - case 0: - bits = code[0] & 0x3f; - break; - case 1: - bits = code[0] >> 6; - bits |= (code[1] & 0xf) << 2; - break; - case 2: - bits = code[1] >> 4; - bits |= (code[2] & 3) << 4; - break; - case 3: - bits = code[2] >> 2; - break; - } - return (bits + 0.5f) / 63.0f; - } -}; - - - -/******************************************************************* - * Quantizer: normalizes scalar vector components, then passes them - * through a codec - *******************************************************************/ - - -template -struct QuantizerTemplate {}; - - -template -struct QuantizerTemplate: Quantizer { - const size_t d; - const float vmin, vdiff; - - QuantizerTemplate(size_t d, const std::vector &trained): - d(d), vmin(trained[0]), vdiff(trained[1]) - { - } - - void encode_vector(const float* x, uint8_t* code) const final { - for (size_t i = 0; i < d; i++) { - float xi = (x[i] - vmin) / vdiff; - if (xi < 0) { - xi = 0; - } - if (xi > 1.0) { - xi = 1.0; - } - Codec::encode_component(xi, code, i); - } - } - - void decode_vector(const uint8_t* code, float* x) const final { - for (size_t i = 0; i < d; i++) { - float xi = Codec::decode_component(code, i); - x[i] = vmin + xi * vdiff; - } - } - - float reconstruct_component (const uint8_t * code, int i) const - { - float xi = Codec::decode_component (code, i); - return vmin + xi * vdiff; - } -}; - - -template -struct QuantizerTemplate: Quantizer { - const size_t d; - const float *vmin, *vdiff; - - QuantizerTemplate (size_t d, const std::vector &trained): - d(d), vmin(trained.data()), vdiff(trained.data() + d) {} - - void encode_vector(const float* x, uint8_t* code) const final { - for (size_t i = 0; i < d; i++) { - float xi = (x[i] - vmin[i]) / vdiff[i]; - if (xi < 0) - xi = 0; - if (xi > 1.0) - xi = 1.0; - Codec::encode_component(xi, code, i); - } - } - - void decode_vector(const uint8_t* code, float* x) const final { - for (size_t i = 0; i < d; i++) { - float xi = Codec::decode_component(code, i); - x[i] = vmin[i] + xi * vdiff[i]; - } - } - - float reconstruct_component (const uint8_t * code, int i) const - { - float xi = Codec::decode_component (code, i); - return vmin[i] + xi * vdiff[i]; - } -}; - - -/******************************************************************* - * FP16 quantizer - *******************************************************************/ - -template -struct QuantizerFP16 {}; - -template<> -struct QuantizerFP16<1>: Quantizer { - const size_t d; - - QuantizerFP16(size_t d, const std::vector & /* unused */): - d(d) {} - - void encode_vector(const float* x, uint8_t* code) const final { - for (size_t i = 0; i < d; i++) { - ((uint16_t*)code)[i] = encode_fp16(x[i]); - } - } - - void decode_vector(const uint8_t* code, float* x) const final { - for (size_t i = 0; i < d; i++) { - x[i] = decode_fp16(((uint16_t*)code)[i]); - } - } - - float reconstruct_component (const uint8_t * code, int i) const - { - return decode_fp16(((uint16_t*)code)[i]); - } -}; - - -/******************************************************************* - * 8bit_direct quantizer - *******************************************************************/ - -template -struct Quantizer8bitDirect {}; - -template<> -struct Quantizer8bitDirect<1>: Quantizer { - const size_t d; - - Quantizer8bitDirect(size_t d, const std::vector & /* unused */): - d(d) {} - - - void encode_vector(const float* x, uint8_t* code) const final { - for (size_t i = 0; i < d; i++) { - code[i] = (uint8_t)x[i]; - } - } - - void decode_vector(const uint8_t* code, float* x) const final { - for (size_t i = 0; i < d; i++) { - x[i] = code[i]; - } - } - - float reconstruct_component (const uint8_t * code, int i) const - { - return code[i]; - } -}; - - -template -Quantizer *select_quantizer_1 ( - QuantizerType qtype, - size_t d, const std::vector & trained) -{ - switch(qtype) { - case QuantizerType::QT_8bit: - return new QuantizerTemplate(d, trained); - case QuantizerType::QT_6bit: - return new QuantizerTemplate(d, trained); - case QuantizerType::QT_4bit: - return new QuantizerTemplate(d, trained); - case QuantizerType::QT_8bit_uniform: - return new QuantizerTemplate(d, trained); - case QuantizerType::QT_4bit_uniform: - return new QuantizerTemplate(d, trained); - case QuantizerType::QT_fp16: - return new QuantizerFP16 (d, trained); - case QuantizerType::QT_8bit_direct: - return new Quantizer8bitDirect (d, trained); - } - FAISS_THROW_MSG ("unknown qtype"); -} - - - -/******************************************************************* - * Similarity: gets vector components and computes a similarity wrt. a - * query vector stored in the object. The data fields just encapsulate - * an accumulator. - */ - -template -struct SimilarityL2 {}; - -template<> -struct SimilarityL2<1> { - static constexpr int simdwidth = 1; - static constexpr MetricType metric_type = METRIC_L2; - - const float *y, *yi; - - explicit SimilarityL2 (const float * y): y(y) {} - - /******* scalar accumulator *******/ - - float accu; - - void begin () { - accu = 0; - yi = y; - } - - void add_component (float x) { - float tmp = *yi++ - x; - accu += tmp * tmp; - } - - void add_component_2 (float x1, float x2) { - float tmp = x1 - x2; - accu += tmp * tmp; - } - - float result () { - return accu; - } -}; - - -template -struct SimilarityIP {}; - -template<> -struct SimilarityIP<1> { - static constexpr int simdwidth = 1; - static constexpr MetricType metric_type = METRIC_INNER_PRODUCT; - const float *y, *yi; - - float accu; - - explicit SimilarityIP (const float * y): - y (y) {} - - void begin () { - accu = 0; - yi = y; - } - - void add_component (float x) { - accu += *yi++ * x; - } - - void add_component_2 (float x1, float x2) { - accu += x1 * x2; - } - - float result () { - return accu; - } -}; - - -/******************************************************************* - * DistanceComputer: combines a similarity and a quantizer to do - * code-to-vector or code-to-code comparisons - *******************************************************************/ - -template -struct DCTemplate : SQDistanceComputer {}; - -template -struct DCTemplate : SQDistanceComputer -{ - using Sim = Similarity; - - Quantizer quant; - - DCTemplate(size_t d, const std::vector &trained): - quant(d, trained) - {} - - float compute_distance(const float* x, const uint8_t* code) const { - Similarity sim(x); - sim.begin(); - for (size_t i = 0; i < quant.d; i++) { - float xi = quant.reconstruct_component(code, i); - sim.add_component(xi); - } - return sim.result(); - } - - float compute_code_distance(const uint8_t* code1, const uint8_t* code2) - const { - Similarity sim(nullptr); - sim.begin(); - for (size_t i = 0; i < quant.d; i++) { - float x1 = quant.reconstruct_component(code1, i); - float x2 = quant.reconstruct_component(code2, i); - sim.add_component_2(x1, x2); - } - return sim.result(); - } - - void set_query (const float *x) final { - q = x; - } - - /// compute distance of vector i to current query - float operator () (idx_t i) final { - return compute_distance (q, codes + i * code_size); - } - - float symmetric_dis (idx_t i, idx_t j) override { - return compute_code_distance (codes + i * code_size, - codes + j * code_size); - } - - float query_to_code (const uint8_t * code) const { - return compute_distance (q, code); - } -}; - - -/******************************************************************* - * DistanceComputerByte: computes distances in the integer domain - *******************************************************************/ - -template -struct DistanceComputerByte : SQDistanceComputer {}; - -template -struct DistanceComputerByte : SQDistanceComputer { - using Sim = Similarity; - - int d; - std::vector tmp; - - DistanceComputerByte(int d, const std::vector &): d(d), tmp(d) { - } - - int compute_code_distance(const uint8_t* code1, const uint8_t* code2) - const { - int accu = 0; - for (int i = 0; i < d; i++) { - if (Sim::metric_type == METRIC_INNER_PRODUCT) { - accu += int(code1[i]) * code2[i]; - } else { - int diff = int(code1[i]) - code2[i]; - accu += diff * diff; - } - } - return accu; - } - - void set_query (const float *x) final { - for (int i = 0; i < d; i++) { - tmp[i] = int(x[i]); - } - } - - int compute_distance(const float* x, const uint8_t* code) { - set_query(x); - return compute_code_distance(tmp.data(), code); - } - - /// compute distance of vector i to current query - float operator () (idx_t i) final { - return compute_distance (q, codes + i * code_size); - } - - float symmetric_dis (idx_t i, idx_t j) override { - return compute_code_distance (codes + i * code_size, - codes + j * code_size); - } - - float query_to_code (const uint8_t * code) const { - return compute_code_distance (tmp.data(), code); - } -}; - - -/******************************************************************* - * select_distance_computer: runtime selection of template - * specialization - *******************************************************************/ - -template -SQDistanceComputer *select_distance_computer ( - QuantizerType qtype, - size_t d, const std::vector & trained) -{ - constexpr int SIMDWIDTH = Sim::simdwidth; - switch(qtype) { - case QuantizerType::QT_8bit_uniform: - return new DCTemplate, - Sim, SIMDWIDTH>(d, trained); - - case QuantizerType::QT_4bit_uniform: - return new DCTemplate, - Sim, SIMDWIDTH>(d, trained); - - case QuantizerType::QT_8bit: - return new DCTemplate, - Sim, SIMDWIDTH>(d, trained); - - case QuantizerType::QT_6bit: - return new DCTemplate, - Sim, SIMDWIDTH>(d, trained); - - case QuantizerType::QT_4bit: - return new DCTemplate, - Sim, SIMDWIDTH>(d, trained); - - case QuantizerType::QT_fp16: - return new DCTemplate - , Sim, SIMDWIDTH>(d, trained); - - case QuantizerType::QT_8bit_direct: - if (d % 16 == 0) { - return new DistanceComputerByte(d, trained); - } else { - return new DCTemplate - , Sim, SIMDWIDTH>(d, trained); - } - } - FAISS_THROW_MSG ("unknown qtype"); - return nullptr; -} - -template -InvertedListScanner* sel2_InvertedListScanner ( - const ScalarQuantizer *sq, - const Index *quantizer, bool store_pairs, bool r) -{ - if (DCClass::Sim::metric_type == METRIC_L2) { - return new IVFSQScannerL2(sq->d, sq->trained, sq->code_size, - quantizer, store_pairs, r); - } else if (DCClass::Sim::metric_type == METRIC_INNER_PRODUCT) { - return new IVFSQScannerIP(sq->d, sq->trained, sq->code_size, - store_pairs, r); - } else { - FAISS_THROW_MSG("unsupported metric type"); - } -} - -template -InvertedListScanner* sel12_InvertedListScanner ( - const ScalarQuantizer *sq, - const Index *quantizer, bool store_pairs, bool r) -{ - constexpr int SIMDWIDTH = Similarity::simdwidth; - using QuantizerClass = QuantizerTemplate; - using DCClass = DCTemplate; - return sel2_InvertedListScanner (sq, quantizer, store_pairs, r); -} - - -template -InvertedListScanner* sel1_InvertedListScanner ( - const ScalarQuantizer *sq, const Index *quantizer, - bool store_pairs, bool r) -{ - constexpr int SIMDWIDTH = Similarity::simdwidth; - switch(sq->qtype) { - case QuantizerType::QT_8bit_uniform: - return sel12_InvertedListScanner - (sq, quantizer, store_pairs, r); - case QuantizerType::QT_4bit_uniform: - return sel12_InvertedListScanner - (sq, quantizer, store_pairs, r); - case QuantizerType::QT_8bit: - return sel12_InvertedListScanner - (sq, quantizer, store_pairs, r); - case QuantizerType::QT_4bit: - return sel12_InvertedListScanner - (sq, quantizer, store_pairs, r); - case QuantizerType::QT_6bit: - return sel12_InvertedListScanner - (sq, quantizer, store_pairs, r); - case QuantizerType::QT_fp16: - return sel2_InvertedListScanner - , Similarity, SIMDWIDTH> > - (sq, quantizer, store_pairs, r); - case QuantizerType::QT_8bit_direct: - if (sq->d % 16 == 0) { - return sel2_InvertedListScanner - > - (sq, quantizer, store_pairs, r); - } else { - return sel2_InvertedListScanner - , - Similarity, SIMDWIDTH> > - (sq, quantizer, store_pairs, r); - } - } - - FAISS_THROW_MSG ("unknown qtype"); - return nullptr; -} - -template -InvertedListScanner* sel0_InvertedListScanner ( - MetricType mt, const ScalarQuantizer *sq, - const Index *quantizer, bool store_pairs, bool by_residual) -{ - if (mt == METRIC_L2) { - return sel1_InvertedListScanner > - (sq, quantizer, store_pairs, by_residual); - } else if (mt == METRIC_INNER_PRODUCT) { - return sel1_InvertedListScanner > - (sq, quantizer, store_pairs, by_residual); - } else { - FAISS_THROW_MSG("unsupported metric type"); - } -} - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerCodec_avx.h b/core/src/index/thirdparty/faiss/impl/ScalarQuantizerCodec_avx.h deleted file mode 100644 index e361376b5f..0000000000 --- a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerCodec_avx.h +++ /dev/null @@ -1,576 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#pragma once - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace faiss { - -/******************************************************************* - * Codec: converts between values in [0, 1] and an index in a code - * array. The "i" parameter is the vector component index (not byte - * index). - */ - -struct Codec8bit_avx : public Codec8bit { - static __m256 decode_8_components (const uint8_t *code, int i) { - uint64_t c8 = *(uint64_t*)(code + i); - __m128i c4lo = _mm_cvtepu8_epi32 (_mm_set1_epi32(c8)); - __m128i c4hi = _mm_cvtepu8_epi32 (_mm_set1_epi32(c8 >> 32)); - // __m256i i8 = _mm256_set_m128i(c4lo, c4hi); - __m256i i8 = _mm256_castsi128_si256 (c4lo); - i8 = _mm256_insertf128_si256 (i8, c4hi, 1); - __m256 f8 = _mm256_cvtepi32_ps (i8); - __m256 half = _mm256_set1_ps (0.5f); - f8 += half; - __m256 one_255 = _mm256_set1_ps (1.f / 255.f); - return f8 * one_255; - } -}; - -struct Codec4bit_avx : public Codec4bit { - static __m256 decode_8_components (const uint8_t *code, int i) { - uint32_t c4 = *(uint32_t*)(code + (i >> 1)); - uint32_t mask = 0x0f0f0f0f; - uint32_t c4ev = c4 & mask; - uint32_t c4od = (c4 >> 4) & mask; - - // the 8 lower bytes of c8 contain the values - __m128i c8 = _mm_unpacklo_epi8 (_mm_set1_epi32(c4ev), - _mm_set1_epi32(c4od)); - __m128i c4lo = _mm_cvtepu8_epi32 (c8); - __m128i c4hi = _mm_cvtepu8_epi32 (_mm_srli_si128(c8, 4)); - __m256i i8 = _mm256_castsi128_si256 (c4lo); - i8 = _mm256_insertf128_si256 (i8, c4hi, 1); - __m256 f8 = _mm256_cvtepi32_ps (i8); - __m256 half = _mm256_set1_ps (0.5f); - f8 += half; - __m256 one_255 = _mm256_set1_ps (1.f / 15.f); - return f8 * one_255; - } -}; - -struct Codec6bit_avx : public Codec6bit { - static __m256 decode_8_components (const uint8_t *code, int i) { - return _mm256_set_ps - (decode_component(code, i + 7), - decode_component(code, i + 6), - decode_component(code, i + 5), - decode_component(code, i + 4), - decode_component(code, i + 3), - decode_component(code, i + 2), - decode_component(code, i + 1), - decode_component(code, i + 0)); - } -}; - - -/******************************************************************* - * Quantizer: normalizes scalar vector components, then passes them - * through a codec - *******************************************************************/ - -template -struct QuantizerTemplate_avx {}; - -template -struct QuantizerTemplate_avx : public QuantizerTemplate { - QuantizerTemplate_avx(size_t d, const std::vector &trained) : - QuantizerTemplate (d, trained) {} -}; - -template -struct QuantizerTemplate_avx : public QuantizerTemplate { - QuantizerTemplate_avx (size_t d, const std::vector &trained) : - QuantizerTemplate (d, trained) {} - - __m256 reconstruct_8_components (const uint8_t * code, int i) const { - __m256 xi = Codec::decode_8_components (code, i); - return _mm256_set1_ps(this->vmin) + xi * _mm256_set1_ps (this->vdiff); - } -}; - -template -struct QuantizerTemplate_avx : public QuantizerTemplate { - QuantizerTemplate_avx (size_t d, const std::vector &trained) : - QuantizerTemplate (d, trained) {} -}; - -template -struct QuantizerTemplate_avx: public QuantizerTemplate { - QuantizerTemplate_avx (size_t d, const std::vector &trained) : - QuantizerTemplate (d, trained) {} - - __m256 reconstruct_8_components (const uint8_t * code, int i) const { - __m256 xi = Codec::decode_8_components (code, i); - return _mm256_loadu_ps (this->vmin + i) + xi * _mm256_loadu_ps (this->vdiff + i); - } -}; - - -/******************************************************************* - * FP16 quantizer - *******************************************************************/ - -template -struct QuantizerFP16_avx {}; - -template<> -struct QuantizerFP16_avx<1> : public QuantizerFP16<1> { - QuantizerFP16_avx (size_t d, const std::vector &unused) : - QuantizerFP16<1> (d, unused) {} -}; - -template<> -struct QuantizerFP16_avx<8>: public QuantizerFP16<1> { - QuantizerFP16_avx (size_t d, const std::vector &trained): - QuantizerFP16<1> (d, trained) {} - - __m256 reconstruct_8_components (const uint8_t * code, int i) const { - __m128i codei = _mm_loadu_si128 ((const __m128i*)(code + 2 * i)); - return _mm256_cvtph_ps (codei); - } -}; - - -/******************************************************************* - * 8bit_direct quantizer - *******************************************************************/ - -template -struct Quantizer8bitDirect_avx {}; - -template<> -struct Quantizer8bitDirect_avx<1> : public Quantizer8bitDirect<1> { - Quantizer8bitDirect_avx (size_t d, const std::vector &unused) : - Quantizer8bitDirect(d, unused) {} -}; - -template<> -struct Quantizer8bitDirect_avx<8>: public Quantizer8bitDirect<1> { - Quantizer8bitDirect_avx (size_t d, const std::vector &trained) : - Quantizer8bitDirect<1> (d, trained) {} - - __m256 reconstruct_8_components (const uint8_t * code, int i) const { - __m128i x8 = _mm_loadl_epi64((__m128i*)(code + i)); // 8 * int8 - __m256i y8 = _mm256_cvtepu8_epi32 (x8); // 8 * int32 - return _mm256_cvtepi32_ps (y8); // 8 * float32 - } -}; - -template -Quantizer *select_quantizer_1_avx (QuantizerType qtype, size_t d, - const std::vector & trained) { - switch(qtype) { - case QuantizerType::QT_8bit: - return new QuantizerTemplate_avx(d, trained); - case QuantizerType::QT_6bit: - return new QuantizerTemplate_avx(d, trained); - case QuantizerType::QT_4bit: - return new QuantizerTemplate_avx(d, trained); - case QuantizerType::QT_8bit_uniform: - return new QuantizerTemplate_avx(d, trained); - case QuantizerType::QT_4bit_uniform: - return new QuantizerTemplate_avx(d, trained); - case QuantizerType::QT_fp16: - return new QuantizerFP16_avx(d, trained); - case QuantizerType::QT_8bit_direct: - return new Quantizer8bitDirect_avx(d, trained); - } - FAISS_THROW_MSG ("unknown qtype"); -} - - -/******************************************************************* - * Similarity: gets vector components and computes a similarity wrt. a - * query vector stored in the object. The data fields just encapsulate - * an accumulator. - */ - -template -struct SimilarityL2_avx {}; - -template<> -struct SimilarityL2_avx<1> : public SimilarityL2<1> { - static constexpr int simdwidth = 1; - static constexpr MetricType metric_type = METRIC_L2; - - explicit SimilarityL2_avx (const float * y) : SimilarityL2<1>(y) {} -}; - -template<> -struct SimilarityL2_avx<8> { - static constexpr int simdwidth = 8; - static constexpr MetricType metric_type = METRIC_L2; - - const float *y, *yi; - - explicit SimilarityL2_avx (const float * y): y(y) {} - __m256 accu8; - - void begin_8 () { - accu8 = _mm256_setzero_ps(); - yi = y; - } - - void add_8_components (__m256 x) { - __m256 yiv = _mm256_loadu_ps (yi); - yi += 8; - __m256 tmp = yiv - x; - accu8 += tmp * tmp; - } - - void add_8_components_2 (__m256 x, __m256 y) { - __m256 tmp = y - x; - accu8 += tmp * tmp; - } - - float result_8 () { - __m256 sum = _mm256_hadd_ps(accu8, accu8); - __m256 sum2 = _mm256_hadd_ps(sum, sum); - // now add the 0th and 4th component - return - _mm_cvtss_f32 (_mm256_castps256_ps128(sum2)) + - _mm_cvtss_f32 (_mm256_extractf128_ps(sum2, 1)); - } -}; - - -template -struct SimilarityIP_avx {}; - -template<> -struct SimilarityIP_avx<1> : public SimilarityIP<1> { - static constexpr int simdwidth = 1; - static constexpr MetricType metric_type = METRIC_INNER_PRODUCT; - - explicit SimilarityIP_avx (const float * y) : SimilarityIP<1>(y) {} -}; - -template<> -struct SimilarityIP_avx<8> { - static constexpr int simdwidth = 8; - static constexpr MetricType metric_type = METRIC_INNER_PRODUCT; - - const float *y, *yi; - - float accu; - - explicit SimilarityIP_avx (const float * y): y (y) {} - - __m256 accu8; - - void begin_8 () { - accu8 = _mm256_setzero_ps(); - yi = y; - } - - void add_8_components (__m256 x) { - __m256 yiv = _mm256_loadu_ps (yi); - yi += 8; - accu8 += yiv * x; - } - - void add_8_components_2 (__m256 x1, __m256 x2) { - accu8 += x1 * x2; - } - - float result_8 () { - __m256 sum = _mm256_hadd_ps(accu8, accu8); - __m256 sum2 = _mm256_hadd_ps(sum, sum); - // now add the 0th and 4th component - return - _mm_cvtss_f32 (_mm256_castps256_ps128(sum2)) + - _mm_cvtss_f32 (_mm256_extractf128_ps(sum2, 1)); - } -}; - - -/******************************************************************* - * DistanceComputer: combines a similarity and a quantizer to do - * code-to-vector or code-to-code comparisons - *******************************************************************/ - -template -struct DCTemplate_avx : SQDistanceComputer {}; - -template -struct DCTemplate_avx : public DCTemplate { - DCTemplate_avx(size_t d, const std::vector &trained) : - DCTemplate(d, trained) {} -}; - -template -struct DCTemplate_avx : SQDistanceComputer { - using Sim = Similarity; - - Quantizer quant; - - DCTemplate_avx(size_t d, const std::vector &trained): - quant(d, trained) {} - - float compute_distance(const float* x, const uint8_t* code) const { - Similarity sim(x); - sim.begin_8(); - for (size_t i = 0; i < quant.d; i += 8) { - __m256 xi = quant.reconstruct_8_components(code, i); - sim.add_8_components(xi); - } - return sim.result_8(); - } - - float compute_code_distance(const uint8_t* code1, const uint8_t* code2) - const { - Similarity sim(nullptr); - sim.begin_8(); - for (size_t i = 0; i < quant.d; i += 8) { - __m256 x1 = quant.reconstruct_8_components(code1, i); - __m256 x2 = quant.reconstruct_8_components(code2, i); - sim.add_8_components_2(x1, x2); - } - return sim.result_8(); - } - - void set_query (const float *x) final { - q = x; - } - - /// compute distance of vector i to current query - float operator () (idx_t i) final { - return compute_distance (q, codes + i * code_size); - } - - float symmetric_dis (idx_t i, idx_t j) override { - return compute_code_distance (codes + i * code_size, - codes + j * code_size); - } - - float query_to_code (const uint8_t * code) const { - return compute_distance (q, code); - } -}; - - -/******************************************************************* - * DistanceComputerByte: computes distances in the integer domain - *******************************************************************/ - -template -struct DistanceComputerByte_avx : SQDistanceComputer {}; - -template -struct DistanceComputerByte_avx : public DistanceComputerByte { - DistanceComputerByte_avx(int d, const std::vector &unused) : - DistanceComputerByte(d, unused) {} -}; - -template -struct DistanceComputerByte_avx : SQDistanceComputer { - using Sim = Similarity; - - int d; - std::vector tmp; - - DistanceComputerByte_avx(int d, const std::vector &): d(d), tmp(d) {} - - int compute_code_distance(const uint8_t* code1, const uint8_t* code2) const { - // __m256i accu = _mm256_setzero_ps (); - __m256i accu = _mm256_setzero_si256 (); - for (int i = 0; i < d; i += 16) { - // load 16 bytes, convert to 16 uint16_t - __m256i c1 = _mm256_cvtepu8_epi16 - (_mm_loadu_si128((__m128i*)(code1 + i))); - __m256i c2 = _mm256_cvtepu8_epi16 - (_mm_loadu_si128((__m128i*)(code2 + i))); - __m256i prod32; - if (Sim::metric_type == METRIC_INNER_PRODUCT) { - prod32 = _mm256_madd_epi16(c1, c2); - } else { - __m256i diff = _mm256_sub_epi16(c1, c2); - prod32 = _mm256_madd_epi16(diff, diff); - } - accu = _mm256_add_epi32 (accu, prod32); - } - __m128i sum = _mm256_extractf128_si256(accu, 0); - sum = _mm_add_epi32 (sum, _mm256_extractf128_si256(accu, 1)); - sum = _mm_hadd_epi32 (sum, sum); - sum = _mm_hadd_epi32 (sum, sum); - return _mm_cvtsi128_si32 (sum); - } - - void set_query (const float *x) final { - /* - for (int i = 0; i < d; i += 8) { - __m256 xi = _mm256_loadu_ps (x + i); - __m256i ci = _mm256_cvtps_epi32(xi); - */ - for (int i = 0; i < d; i++) { - tmp[i] = int(x[i]); - } - } - - int compute_distance(const float* x, const uint8_t* code) { - set_query(x); - return compute_code_distance(tmp.data(), code); - } - - /// compute distance of vector i to current query - float operator () (idx_t i) final { - return compute_distance (q, codes + i * code_size); - } - - float symmetric_dis (idx_t i, idx_t j) override { - return compute_code_distance (codes + i * code_size, - codes + j * code_size); - } - - float query_to_code (const uint8_t * code) const { - return compute_code_distance (tmp.data(), code); - } -}; - - -/******************************************************************* - * select_distance_computer: runtime selection of template - * specialization - *******************************************************************/ - -template -SQDistanceComputer *select_distance_computer_avx ( - QuantizerType qtype, - size_t d, const std::vector & trained) -{ - constexpr int SIMDWIDTH = Sim::simdwidth; - switch(qtype) { - case QuantizerType::QT_8bit_uniform: - return new DCTemplate_avx, - Sim, SIMDWIDTH>(d, trained); - - case QuantizerType::QT_4bit_uniform: - return new DCTemplate_avx, - Sim, SIMDWIDTH>(d, trained); - - case QuantizerType::QT_8bit: - return new DCTemplate_avx, - Sim, SIMDWIDTH>(d, trained); - - case QuantizerType::QT_6bit: - return new DCTemplate_avx, - Sim, SIMDWIDTH>(d, trained); - - case QuantizerType::QT_4bit: - return new DCTemplate_avx, - Sim, SIMDWIDTH>(d, trained); - - case QuantizerType::QT_fp16: - return new DCTemplate_avx - , Sim, SIMDWIDTH>(d, trained); - - case QuantizerType::QT_8bit_direct: - if (d % 16 == 0) { - return new DistanceComputerByte_avx(d, trained); - } else { - return new DCTemplate_avx - , Sim, SIMDWIDTH>(d, trained); - } - } - FAISS_THROW_MSG ("unknown qtype"); - return nullptr; -} - -template -InvertedListScanner* sel2_InvertedListScanner_avx ( - const ScalarQuantizer *sq, - const Index *quantizer, bool store_pairs, bool r) -{ - return sel2_InvertedListScanner (sq, quantizer, store_pairs, r); -} - -template -InvertedListScanner* sel12_InvertedListScanner_avx ( - const ScalarQuantizer *sq, - const Index *quantizer, bool store_pairs, bool r) -{ - constexpr int SIMDWIDTH = Similarity::simdwidth; - using QuantizerClass = QuantizerTemplate_avx; - using DCClass = DCTemplate_avx; - return sel2_InvertedListScanner_avx (sq, quantizer, store_pairs, r); -} - - -template -InvertedListScanner* sel1_InvertedListScanner_avx ( - const ScalarQuantizer *sq, const Index *quantizer, - bool store_pairs, bool r) -{ - constexpr int SIMDWIDTH = Similarity::simdwidth; - switch(sq->qtype) { - case QuantizerType::QT_8bit_uniform: - return sel12_InvertedListScanner_avx - (sq, quantizer, store_pairs, r); - case QuantizerType::QT_4bit_uniform: - return sel12_InvertedListScanner_avx - (sq, quantizer, store_pairs, r); - case QuantizerType::QT_8bit: - return sel12_InvertedListScanner_avx - (sq, quantizer, store_pairs, r); - case QuantizerType::QT_4bit: - return sel12_InvertedListScanner_avx - (sq, quantizer, store_pairs, r); - case QuantizerType::QT_6bit: - return sel12_InvertedListScanner_avx - (sq, quantizer, store_pairs, r); - case QuantizerType::QT_fp16: - return sel2_InvertedListScanner_avx - , Similarity, SIMDWIDTH> > - (sq, quantizer, store_pairs, r); - case QuantizerType::QT_8bit_direct: - if (sq->d % 16 == 0) { - return sel2_InvertedListScanner_avx - > - (sq, quantizer, store_pairs, r); - } else { - return sel2_InvertedListScanner_avx - , - Similarity, SIMDWIDTH> > - (sq, quantizer, store_pairs, r); - } - } - - FAISS_THROW_MSG ("unknown qtype"); - return nullptr; -} - -template -InvertedListScanner* sel0_InvertedListScanner_avx ( - MetricType mt, const ScalarQuantizer *sq, - const Index *quantizer, bool store_pairs, bool by_residual) -{ - if (mt == METRIC_L2) { - return sel1_InvertedListScanner_avx > - (sq, quantizer, store_pairs, by_residual); - } else if (mt == METRIC_INNER_PRODUCT) { - return sel1_InvertedListScanner_avx > - (sq, quantizer, store_pairs, by_residual); - } else { - FAISS_THROW_MSG("unsupported metric type"); - } -} - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerCodec_avx512.h b/core/src/index/thirdparty/faiss/impl/ScalarQuantizerCodec_avx512.h deleted file mode 100644 index 3892f9dfdd..0000000000 --- a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerCodec_avx512.h +++ /dev/null @@ -1,661 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#pragma once - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace faiss { - -/******************************************************************* - * ScalarQuantizer implementation - * - * The main source of complexity is to support combinations of 4 - * variants without incurring runtime tests or virtual function calls: - * - * - 4 / 8 bits per code component - * - uniform / non-uniform - * - IP / L2 distance search - * - scalar / AVX distance computation - * - * The appropriate Quantizer object is returned via select_quantizer - * that hides the template mess. - ********************************************************************/ - - -/******************************************************************* - * Codec: converts between values in [0, 1] and an index in a code - * array. The "i" parameter is the vector component index (not byte - * index). - */ - -struct Codec8bit_avx512 : public Codec8bit_avx { - static __m512 decode_16_components (const uint8_t *code, int i) { - uint64_t c8 = *(uint64_t*)(code + i); - __m256i c8lo = _mm256_cvtepu8_epi32 (_mm_set1_epi64x(c8)); - c8 = *(uint64_t*)(code + i + 8); - __m256i c8hi = _mm256_cvtepu8_epi32 (_mm_set1_epi64x(c8)); - // __m256i i8 = _mm256_set_m128i(c4lo, c4hi); - __m512i i16 = _mm512_castsi256_si512 (c8lo); - i16 = _mm512_inserti32x8 (i16, c8hi, 1); - __m512 f16 = _mm512_cvtepi32_ps (i16); - __m512 half = _mm512_set1_ps (0.5f); - f16 += half; - __m512 one_255 = _mm512_set1_ps (1.f / 255.f); - return f16 * one_255; - } -}; - -struct Codec4bit_avx512 : public Codec4bit_avx { - static __m512 decode_16_components (const uint8_t *code, int i) { - uint64_t c8 = *(uint64_t*)(code + (i >> 1)); - uint64_t mask = 0x0f0f0f0f0f0f0f0f; - uint64_t c8ev = c8 & mask; - uint64_t c8od = (c8 >> 4) & mask; - - // the 8 lower bytes of c8 contain the values - __m128i c16 = _mm_unpacklo_epi8 (_mm_set1_epi64x(c8ev), - _mm_set1_epi64x(c8od)); - __m256i c8lo = _mm256_cvtepu8_epi32 (c16); - __m256i c8hi = _mm256_cvtepu8_epi32 (_mm_srli_si128(c16, 4)); - __m512i i16 = _mm512_castsi256_si512 (c8lo); - i16 = _mm512_inserti32x8 (i16, c8hi, 1); - __m512 f16 = _mm512_cvtepi32_ps (i16); - __m512 half = _mm512_set1_ps (0.5f); - f16 += half; - __m512 one_255 = _mm512_set1_ps (1.f / 15.f); - return f16 * one_255; - } -}; - -struct Codec6bit_avx512 : public Codec6bit_avx { - static __m512 decode_16_components (const uint8_t *code, int i) { - return _mm512_set_ps - (decode_component(code, i + 15), - decode_component(code, i + 14), - decode_component(code, i + 13), - decode_component(code, i + 12), - decode_component(code, i + 11), - decode_component(code, i + 10), - decode_component(code, i + 9), - decode_component(code, i + 8), - decode_component(code, i + 7), - decode_component(code, i + 6), - decode_component(code, i + 5), - decode_component(code, i + 4), - decode_component(code, i + 3), - decode_component(code, i + 2), - decode_component(code, i + 1), - decode_component(code, i + 0)); - } -}; - - -/******************************************************************* - * Quantizer: normalizes scalar vector components, then passes them - * through a codec - *******************************************************************/ - -template -struct QuantizerTemplate_avx512 {}; - -template -struct QuantizerTemplate_avx512 : public QuantizerTemplate_avx { - QuantizerTemplate_avx512(size_t d, const std::vector &trained) : - QuantizerTemplate_avx (d, trained) {} -}; - -template -struct QuantizerTemplate_avx512 : public QuantizerTemplate_avx { - QuantizerTemplate_avx512 (size_t d, const std::vector &trained) : - QuantizerTemplate_avx (d, trained) {} -}; - -template -struct QuantizerTemplate_avx512 : public QuantizerTemplate_avx { - QuantizerTemplate_avx512 (size_t d, const std::vector &trained) : - QuantizerTemplate_avx (d, trained) {} - - __m512 reconstruct_16_components (const uint8_t * code, int i) const { - __m512 xi = Codec::decode_16_components (code, i); - return _mm512_set1_ps(this->vmin) + xi * _mm512_set1_ps (this->vdiff); - } -}; - - -template -struct QuantizerTemplate_avx512 : public QuantizerTemplate_avx { - QuantizerTemplate_avx512 (size_t d, const std::vector &trained) : - QuantizerTemplate_avx (d, trained) {} -}; - -template -struct QuantizerTemplate_avx512 : public QuantizerTemplate_avx { - QuantizerTemplate_avx512 (size_t d, const std::vector &trained): - QuantizerTemplate_avx (d, trained) {} -}; - -template -struct QuantizerTemplate_avx512: public QuantizerTemplate_avx { - QuantizerTemplate_avx512 (size_t d, const std::vector &trained): - QuantizerTemplate_avx (d, trained) {} - - __m512 reconstruct_16_components (const uint8_t * code, int i) const { - __m512 xi = Codec::decode_16_components (code, i); - return _mm512_loadu_ps (this->vmin + i) + xi * _mm512_loadu_ps (this->vdiff + i); - } -}; - -/******************************************************************* - * FP16 quantizer - *******************************************************************/ - -template -struct QuantizerFP16_avx512 {}; - -template<> -struct QuantizerFP16_avx512<1> : public QuantizerFP16_avx<1> { - QuantizerFP16_avx512(size_t d, const std::vector &unused) : - QuantizerFP16_avx<1> (d, unused) {} -}; - -template<> -struct QuantizerFP16_avx512<8> : public QuantizerFP16_avx<8> { - QuantizerFP16_avx512 (size_t d, const std::vector &trained) : - QuantizerFP16_avx<8> (d, trained) {} -}; - -template<> -struct QuantizerFP16_avx512<16>: public QuantizerFP16_avx<8> { - QuantizerFP16_avx512 (size_t d, const std::vector &trained): - QuantizerFP16_avx<8> (d, trained) {} - - __m512 reconstruct_16_components (const uint8_t * code, int i) const { - __m256i codei = _mm256_loadu_si256 ((const __m256i*)(code + 2 * i)); - return _mm512_cvtph_ps (codei); - } -}; - -/******************************************************************* - * 8bit_direct quantizer - *******************************************************************/ - -template -struct Quantizer8bitDirect_avx512 {}; - -template<> -struct Quantizer8bitDirect_avx512<1> : public Quantizer8bitDirect_avx<1> { - Quantizer8bitDirect_avx512(size_t d, const std::vector &unused) : - Quantizer8bitDirect_avx<1> (d, unused) {} -}; - -template<> -struct Quantizer8bitDirect_avx512<8> : public Quantizer8bitDirect_avx<8> { - Quantizer8bitDirect_avx512 (size_t d, const std::vector &trained): - Quantizer8bitDirect_avx<8> (d, trained) {} -}; - -template<> -struct Quantizer8bitDirect_avx512<16> : public Quantizer8bitDirect_avx<8> { - Quantizer8bitDirect_avx512 (size_t d, const std::vector &trained): - Quantizer8bitDirect_avx<8> (d, trained) {} - - __m512 reconstruct_16_components (const uint8_t * code, int i) const { - __m128i x8 = _mm_load_si128((__m128i*)(code + i)); // 16 * int8 - __m512i y8 = _mm512_cvtepu8_epi32 (x8); // 16 * int32 - return _mm512_cvtepi32_ps (y8); // 16 * float32 - } -}; - - -template -Quantizer *select_quantizer_1_avx512 (QuantizerType qtype, size_t d, - const std::vector & trained) -{ - switch(qtype) { - case QuantizerType::QT_8bit: - return new QuantizerTemplate_avx512(d, trained); - case QuantizerType::QT_6bit: - return new QuantizerTemplate_avx512(d, trained); - case QuantizerType::QT_4bit: - return new QuantizerTemplate_avx512(d, trained); - case QuantizerType::QT_8bit_uniform: - return new QuantizerTemplate_avx512(d, trained); - case QuantizerType::QT_4bit_uniform: - return new QuantizerTemplate_avx512(d, trained); - case QuantizerType::QT_fp16: - return new QuantizerFP16_avx512(d, trained); - case QuantizerType::QT_8bit_direct: - return new Quantizer8bitDirect_avx512(d, trained); - } - FAISS_THROW_MSG ("unknown qtype"); -} - - -/******************************************************************* - * Similarity: gets vector components and computes a similarity wrt. a - * query vector stored in the object. The data fields just encapsulate - * an accumulator. - */ - -template -struct SimilarityL2_avx512 {}; - - -template<> -struct SimilarityL2_avx512<1> : public SimilarityL2_avx<1> { - static constexpr int simdwidth = 1; - static constexpr MetricType metric_type = METRIC_L2; - - explicit SimilarityL2_avx512 (const float * y) : SimilarityL2_avx<1> (y) {} -}; - -template<> -struct SimilarityL2_avx512<8> : public SimilarityL2_avx<8> { - static constexpr int simdwidth = 8; - static constexpr MetricType metric_type = METRIC_L2; - - explicit SimilarityL2_avx512 (const float * y) : SimilarityL2_avx<8> (y) {} -}; - -template<> -struct SimilarityL2_avx512<16> { - static constexpr int simdwidth = 16; - static constexpr MetricType metric_type = METRIC_L2; - - const float *y, *yi; - - explicit SimilarityL2_avx512 (const float * y): y(y) {} - __m512 accu16; - - void begin_16 () { - accu16 = _mm512_setzero_ps(); - yi = y; - } - - void add_16_components (__m512 x) { - __m512 yiv = _mm512_loadu_ps (yi); - yi += 16; - __m512 tmp = yiv - x; - accu16 += tmp * tmp; - } - - void add_16_components_2 (__m512 x, __m512 y) { - __m512 tmp = y - x; - accu16 += tmp * tmp; - } - - float result_16 () { - __m256 sum0 = _mm512_extractf32x8_ps(accu16, 1) + _mm512_extractf32x8_ps(accu16, 0); - __m256 sum1 = _mm256_hadd_ps(sum0, sum0); - __m256 sum2 = _mm256_hadd_ps(sum1, sum1); - // now add the 0th and 4th component - return - _mm_cvtss_f32 (_mm256_castps256_ps128(sum2)) + - _mm_cvtss_f32 (_mm256_extractf128_ps(sum2, 1)); - } -}; - - -template -struct SimilarityIP_avx512 {}; - - -template<> -struct SimilarityIP_avx512<1> : public SimilarityIP_avx<1> { - static constexpr int simdwidth = 1; - static constexpr MetricType metric_type = METRIC_INNER_PRODUCT; - - explicit SimilarityIP_avx512 (const float * y) : SimilarityIP_avx<1> (y) {} -}; - -template<> -struct SimilarityIP_avx512<8> : public SimilarityIP_avx<8> { - static constexpr int simdwidth = 8; - static constexpr MetricType metric_type = METRIC_INNER_PRODUCT; - - explicit SimilarityIP_avx512 (const float * y) : SimilarityIP_avx<8> (y) {} -}; - -template<> -struct SimilarityIP_avx512<16> { - static constexpr int simdwidth = 16; - static constexpr MetricType metric_type = METRIC_INNER_PRODUCT; - - const float *y, *yi; - - float accu; - - explicit SimilarityIP_avx512 (const float * y) : y (y) {} - - __m512 accu16; - - void begin_16 () { - accu16 = _mm512_setzero_ps(); - yi = y; - } - - void add_16_components (__m512 x) { - __m512 yiv = _mm512_loadu_ps (yi); - yi += 16; - accu16 += yiv * x; - } - - void add_16_components_2 (__m512 x1, __m512 x2) { - accu16 += x1 * x2; - } - - float result_16 () { - __m256 sum0 = _mm512_extractf32x8_ps(accu16, 1) + _mm512_extractf32x8_ps(accu16, 0); - __m256 sum1 = _mm256_hadd_ps(sum0, sum0); - __m256 sum2 = _mm256_hadd_ps(sum1, sum1); - // now add the 0th and 4th component - return - _mm_cvtss_f32 (_mm256_castps256_ps128(sum2)) + - _mm_cvtss_f32 (_mm256_extractf128_ps(sum2, 1)); - } -}; - - -/******************************************************************* - * DistanceComputer: combines a similarity and a quantizer to do - * code-to-vector or code-to-code comparisons - *******************************************************************/ - -template -struct DCTemplate_avx512 : SQDistanceComputer {}; - -template -struct DCTemplate_avx512 : public DCTemplate_avx { - DCTemplate_avx512(size_t d, const std::vector &trained) : - DCTemplate_avx (d, trained) {} -}; - -template -struct DCTemplate_avx512 : public DCTemplate_avx { - DCTemplate_avx512(size_t d, const std::vector &trained) : - DCTemplate_avx (d, trained) {} -}; - -template -struct DCTemplate_avx512 : SQDistanceComputer { - using Sim = Similarity; - - Quantizer quant; - - DCTemplate_avx512(size_t d, const std::vector &trained): - quant(d, trained) - {} - - float compute_distance(const float* x, const uint8_t* code) const { - Similarity sim(x); - sim.begin_16(); - for (size_t i = 0; i < quant.d; i += 16) { - __m512 xi = quant.reconstruct_16_components(code, i); - sim.add_16_components(xi); - } - return sim.result_16(); - } - - float compute_code_distance(const uint8_t* code1, const uint8_t* code2) const { - Similarity sim(nullptr); - sim.begin_16(); - for (size_t i = 0; i < quant.d; i += 16) { - __m512 x1 = quant.reconstruct_16_components(code1, i); - __m512 x2 = quant.reconstruct_16_components(code2, i); - sim.add_16_components_2(x1, x2); - } - return sim.result_16(); - } - - void set_query (const float *x) final { - q = x; - } - - /// compute distance of vector i to current query - float operator () (idx_t i) final { - return compute_distance (q, codes + i * code_size); - } - - float symmetric_dis (idx_t i, idx_t j) override { - return compute_code_distance (codes + i * code_size, - codes + j * code_size); - } - - float query_to_code (const uint8_t * code) const { - return compute_distance (q, code); - } -}; - - -/******************************************************************* - * DistanceComputerByte: computes distances in the integer domain - *******************************************************************/ - -template -struct DistanceComputerByte_avx512 : SQDistanceComputer {}; - -template -struct DistanceComputerByte_avx512 : public DistanceComputerByte_avx { - DistanceComputerByte_avx512(int d, const std::vector &unused) : - DistanceComputerByte_avx (d, unused) {} -}; - -template -struct DistanceComputerByte_avx512 : public DistanceComputerByte_avx { - DistanceComputerByte_avx512(int d, const std::vector &unused) : - DistanceComputerByte_avx (d, unused) {} -}; - -template -struct DistanceComputerByte_avx512 : SQDistanceComputer { - using Sim = Similarity; - - int d; - std::vector tmp; - - DistanceComputerByte_avx512(int d, const std::vector &): d(d), tmp(d) {} - - int compute_code_distance(const uint8_t* code1, const uint8_t* code2) const { - // __m256i accu = _mm256_setzero_ps (); - __m512i accu = _mm512_setzero_si512 (); - for (int i = 0; i < d; i += 32) { - // load 32 bytes, convert to 16 uint16_t - __m512i c1 = _mm512_cvtepu8_epi16 - (_mm256_loadu_si256((__m256i*)(code1 + i))); - __m512i c2 = _mm512_cvtepu8_epi16 - (_mm256_loadu_si256((__m256i*)(code2 + i))); - __m512i prod32; - if (Sim::metric_type == METRIC_INNER_PRODUCT) { - prod32 = _mm512_madd_epi16(c1, c2); - } else { - __m512i diff = _mm512_sub_epi16(c1, c2); - prod32 = _mm512_madd_epi16(diff, diff); - } - accu = _mm512_add_epi32 (accu, prod32); - } - __m128i sum = _mm512_extracti32x4_epi32(accu, 0); - sum = _mm_add_epi32 (sum, _mm512_extracti32x4_epi32(accu, 1)); - sum = _mm_add_epi32 (sum, _mm512_extracti32x4_epi32(accu, 2)); - sum = _mm_add_epi32 (sum, _mm512_extracti32x4_epi32(accu, 3)); - sum = _mm_hadd_epi32 (sum, sum); - sum = _mm_hadd_epi32 (sum, sum); - return _mm_cvtsi128_si32 (sum); - } - - void set_query (const float *x) final { - /* - for (int i = 0; i < d; i += 8) { - __m256 xi = _mm256_loadu_ps (x + i); - __m256i ci = _mm256_cvtps_epi32(xi); - */ - for (int i = 0; i < d; i++) { - tmp[i] = int(x[i]); - } - } - - int compute_distance(const float* x, const uint8_t* code) { - set_query(x); - return compute_code_distance(tmp.data(), code); - } - - /// compute distance of vector i to current query - float operator () (idx_t i) final { - return compute_distance (q, codes + i * code_size); - } - - float symmetric_dis (idx_t i, idx_t j) override { - return compute_code_distance (codes + i * code_size, - codes + j * code_size); - } - - float query_to_code (const uint8_t * code) const { - return compute_code_distance (tmp.data(), code); - } -}; - - -/******************************************************************* - * select_distance_computer: runtime selection of template - * specialization - *******************************************************************/ - -template -SQDistanceComputer *select_distance_computer_avx512 ( - QuantizerType qtype, - size_t d, const std::vector & trained) -{ - constexpr int SIMDWIDTH = Sim::simdwidth; - switch(qtype) { - case QuantizerType::QT_8bit_uniform: - return new DCTemplate_avx512, - Sim, SIMDWIDTH>(d, trained); - - case QuantizerType::QT_4bit_uniform: - return new DCTemplate_avx512, - Sim, SIMDWIDTH>(d, trained); - - case QuantizerType::QT_8bit: - return new DCTemplate_avx512, - Sim, SIMDWIDTH>(d, trained); - - case QuantizerType::QT_6bit: - return new DCTemplate_avx512, - Sim, SIMDWIDTH>(d, trained); - - case QuantizerType::QT_4bit: - return new DCTemplate_avx512, - Sim, SIMDWIDTH>(d, trained); - - case QuantizerType::QT_fp16: - return new DCTemplate_avx512 - , Sim, SIMDWIDTH>(d, trained); - - case QuantizerType::QT_8bit_direct: - if (d % 16 == 0) { - return new DistanceComputerByte_avx512(d, trained); - } else { - return new DCTemplate_avx512 - , Sim, SIMDWIDTH>(d, trained); - } - } - FAISS_THROW_MSG ("unknown qtype"); - return nullptr; -} - - -template -InvertedListScanner* sel2_InvertedListScanner_avx512 ( - const ScalarQuantizer *sq, - const Index *quantizer, bool store_pairs, bool r) -{ - return sel2_InvertedListScanner (sq, quantizer, store_pairs, r); -} - -template -InvertedListScanner* sel12_InvertedListScanner_avx512 ( - const ScalarQuantizer *sq, - const Index *quantizer, bool store_pairs, bool r) -{ - constexpr int SIMDWIDTH = Similarity::simdwidth; - using QuantizerClass = QuantizerTemplate_avx512; - using DCClass = DCTemplate_avx512; - return sel2_InvertedListScanner_avx512 (sq, quantizer, store_pairs, r); -} - - -template -InvertedListScanner* sel1_InvertedListScanner_avx512 ( - const ScalarQuantizer *sq, const Index *quantizer, - bool store_pairs, bool r) -{ - constexpr int SIMDWIDTH = Similarity::simdwidth; - switch(sq->qtype) { - case QuantizerType::QT_8bit_uniform: - return sel12_InvertedListScanner_avx512 - (sq, quantizer, store_pairs, r); - case QuantizerType::QT_4bit_uniform: - return sel12_InvertedListScanner_avx512 - (sq, quantizer, store_pairs, r); - case QuantizerType::QT_8bit: - return sel12_InvertedListScanner_avx512 - (sq, quantizer, store_pairs, r); - case QuantizerType::QT_4bit: - return sel12_InvertedListScanner_avx512 - (sq, quantizer, store_pairs, r); - case QuantizerType::QT_6bit: - return sel12_InvertedListScanner_avx512 - (sq, quantizer, store_pairs, r); - case QuantizerType::QT_fp16: - return sel2_InvertedListScanner_avx512 - , Similarity, SIMDWIDTH> > - (sq, quantizer, store_pairs, r); - case QuantizerType::QT_8bit_direct: - if (sq->d % 16 == 0) { - return sel2_InvertedListScanner_avx512 - > - (sq, quantizer, store_pairs, r); - } else { - return sel2_InvertedListScanner_avx512 - , - Similarity, SIMDWIDTH> > - (sq, quantizer, store_pairs, r); - } - } - - FAISS_THROW_MSG ("unknown qtype"); - return nullptr; -} - -template -InvertedListScanner* sel0_InvertedListScanner_avx512 ( - MetricType mt, const ScalarQuantizer *sq, - const Index *quantizer, bool store_pairs, bool by_residual) -{ - if (mt == METRIC_L2) { - return sel1_InvertedListScanner_avx512 > - (sq, quantizer, store_pairs, by_residual); - } else if (mt == METRIC_INNER_PRODUCT) { - return sel1_InvertedListScanner_avx512 > - (sq, quantizer, store_pairs, by_residual); - } else { - FAISS_THROW_MSG("unsupported metric type"); - } -} - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC.cpp b/core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC.cpp deleted file mode 100644 index 71fc4807b9..0000000000 --- a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include -#include - -namespace faiss { - -/******************************************************************* - * ScalarQuantizer Distance Computer - ********************************************************************/ - -/* SSE */ -SQDistanceComputer * -sq_get_distance_computer_ref (MetricType metric, QuantizerType qtype, size_t dim, const std::vector& trained) { - if (metric == METRIC_L2) { - return select_distance_computer>(qtype, dim, trained); - } else { - return select_distance_computer>(qtype, dim, trained); - } -} - -Quantizer * -sq_select_quantizer_ref (QuantizerType qtype, size_t dim, const std::vector& trained) { - return select_quantizer_1<1> (qtype, dim, trained); -} - -InvertedListScanner* -sq_select_inverted_list_scanner_ref (MetricType mt, const ScalarQuantizer *sq, const Index *quantizer, size_t dim, bool store_pairs, bool by_residual) { - return sel0_InvertedListScanner<1> (mt, sq, quantizer, store_pairs, by_residual); -} - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC.h b/core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC.h deleted file mode 100644 index d088d54bc9..0000000000 --- a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#pragma once - -#include -#include -#include -#include - -namespace faiss { - -SQDistanceComputer * -sq_get_distance_computer_ref( - MetricType metric, - QuantizerType qtype, - size_t dim, - const std::vector& trained); - -Quantizer * -sq_select_quantizer_ref( - QuantizerType qtype, - size_t dim, - const std::vector& trained); - -InvertedListScanner* -sq_select_inverted_list_scanner_ref( - MetricType mt, - const ScalarQuantizer *sq, - const Index *quantizer, - size_t dim, - bool store_pairs, - bool by_residual); - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC_avx.cpp b/core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC_avx.cpp deleted file mode 100644 index 2da2af6f60..0000000000 --- a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC_avx.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include -#include - -namespace faiss { - -/******************************************************************* - * ScalarQuantizer Distance Computer - ********************************************************************/ - -SQDistanceComputer * -sq_get_distance_computer_avx (MetricType metric, QuantizerType qtype, size_t dim, const std::vector& trained) { - if (metric == METRIC_L2) { - if (dim % 8 == 0) { - return select_distance_computer_avx>(qtype, dim, trained); - } else { - return select_distance_computer_avx>(qtype, dim, trained); - } - } else { - if (dim % 8 == 0) { - return select_distance_computer_avx>(qtype, dim, trained); - } else { - return select_distance_computer_avx>(qtype, dim, trained); - } - } -} - -Quantizer * -sq_select_quantizer_avx (QuantizerType qtype, size_t dim, const std::vector& trained) { - if (dim % 8 == 0) { - return select_quantizer_1_avx<8>(qtype, dim, trained); - } else { - return select_quantizer_1_avx<1> (qtype, dim, trained); - } -} - -InvertedListScanner* -sq_select_inverted_list_scanner_avx (MetricType mt, const ScalarQuantizer *sq, const Index *quantizer, size_t dim, bool store_pairs, bool by_residual) { - if (dim % 8 == 0) { - return sel0_InvertedListScanner_avx<8> (mt, sq, quantizer, store_pairs, by_residual); - } else { - return sel0_InvertedListScanner_avx<1> (mt, sq, quantizer, store_pairs, by_residual); - } -} - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC_avx.h b/core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC_avx.h deleted file mode 100644 index 3b04aa4d2e..0000000000 --- a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC_avx.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#pragma once - -#include -#include -#include -#include - -namespace faiss { - -SQDistanceComputer * -sq_get_distance_computer_avx( - MetricType metric, - QuantizerType qtype, - size_t dim, - const std::vector& trained); - -Quantizer * -sq_select_quantizer_avx( - QuantizerType qtype, - size_t dim, - const std::vector& trained); - -InvertedListScanner* -sq_select_inverted_list_scanner_avx( - MetricType mt, - const ScalarQuantizer *sq, - const Index *quantizer, - size_t dim, - bool store_pairs, - bool by_residual); - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC_avx512.cpp b/core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC_avx512.cpp deleted file mode 100644 index 6a62847c1d..0000000000 --- a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC_avx512.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include -#include - -namespace faiss { - -/******************************************************************* - * ScalarQuantizer Distance Computer - ********************************************************************/ - -SQDistanceComputer * -sq_get_distance_computer_avx512 (MetricType metric, QuantizerType qtype, size_t dim, const std::vector& trained) { - if (metric == METRIC_L2) { - if (dim % 16 == 0) { - return select_distance_computer_avx512>(qtype, dim, trained); - } else if (dim % 8 == 0) { - return select_distance_computer_avx512>(qtype, dim, trained); - } else { - return select_distance_computer_avx512>(qtype, dim, trained); - } - } else { - if (dim % 16 == 0) { - return select_distance_computer_avx512>(qtype, dim, trained); - } else if (dim % 8 == 0) { - return select_distance_computer_avx512>(qtype, dim, trained); - } else { - return select_distance_computer_avx512>(qtype, dim, trained); - } - } -} - -Quantizer * -sq_select_quantizer_avx512 (QuantizerType qtype, size_t dim, const std::vector& trained) { - if (dim % 16 == 0) { - return select_quantizer_1_avx512<16> (qtype, dim, trained); - } else if (dim % 8 == 0) { - return select_quantizer_1_avx512<8> (qtype, dim, trained); - } else { - return select_quantizer_1_avx512<1> (qtype, dim, trained); - } -} - -InvertedListScanner* -sq_select_inverted_list_scanner_avx512 (MetricType mt, const ScalarQuantizer *sq, const Index *quantizer, size_t dim, bool store_pairs, bool by_residual) { - if (dim % 16 == 0) { - return sel0_InvertedListScanner_avx512<16> (mt, sq, quantizer, store_pairs, by_residual); - } else if (dim % 8 == 0) { - return sel0_InvertedListScanner_avx512<8> (mt, sq, quantizer, store_pairs, by_residual); - } else { - return sel0_InvertedListScanner_avx512<1> (mt, sq, quantizer, store_pairs, by_residual); - } -} - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC_avx512.h b/core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC_avx512.h deleted file mode 100644 index f1b03027a9..0000000000 --- a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerDC_avx512.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#pragma once - -#include -#include -#include -#include - -namespace faiss { - -SQDistanceComputer * -sq_get_distance_computer_avx512( - MetricType metric, - QuantizerType qtype, - size_t dim, - const std::vector& trained); - -Quantizer * -sq_select_quantizer_avx512( - QuantizerType qtype, - size_t dim, - const std::vector& trained); - -InvertedListScanner* -sq_select_inverted_list_scanner_avx512( - MetricType mt, - const ScalarQuantizer *sq, - const Index *quantizer, - size_t dim, - bool store_pairs, - bool by_residual); - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerOp.cpp b/core/src/index/thirdparty/faiss/impl/ScalarQuantizerOp.cpp deleted file mode 100644 index 0b0f9aa92f..0000000000 --- a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerOp.cpp +++ /dev/null @@ -1,295 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include -#include - -#include - -#ifdef __SSE__ -#include -#endif - -#include -#include -#include - -namespace faiss { - -#ifdef __AVX__ -#define USE_AVX -#endif - - -#ifdef USE_AVX - -uint16_t encode_fp16 (float x) { - __m128 xf = _mm_set1_ps (x); - __m128i xi = _mm_cvtps_ph ( - xf, _MM_FROUND_TO_NEAREST_INT |_MM_FROUND_NO_EXC); - return _mm_cvtsi128_si32 (xi) & 0xffff; -} - - -float decode_fp16 (uint16_t x) { - __m128i xi = _mm_set1_epi16 (x); - __m128 xf = _mm_cvtph_ps (xi); - return _mm_cvtss_f32 (xf); -} - -#else - -// non-intrinsic FP16 <-> FP32 code adapted from -// https://github.com/ispc/ispc/blob/master/stdlib.ispc - -float floatbits (uint32_t x) { - void *xptr = &x; - return *(float*)xptr; -} - -uint32_t intbits (float f) { - void *fptr = &f; - return *(uint32_t*)fptr; -} - - -uint16_t encode_fp16 (float f) { - // via Fabian "ryg" Giesen. - // https://gist.github.com/2156668 - uint32_t sign_mask = 0x80000000u; - int32_t o; - - uint32_t fint = intbits(f); - uint32_t sign = fint & sign_mask; - fint ^= sign; - - // NOTE all the integer compares in this function can be safely - // compiled into signed compares since all operands are below - // 0x80000000. Important if you want fast straight SSE2 code (since - // there's no unsigned PCMPGTD). - - // Inf or NaN (all exponent bits set) - // NaN->qNaN and Inf->Inf - // unconditional assignment here, will override with right value for - // the regular case below. - uint32_t f32infty = 255u << 23; - o = (fint > f32infty) ? 0x7e00u : 0x7c00u; - - // (De)normalized number or zero - // update fint unconditionally to save the blending; we don't need it - // anymore for the Inf/NaN case anyway. - - const uint32_t round_mask = ~0xfffu; - const uint32_t magic = 15u << 23; - - // Shift exponent down, denormalize if necessary. - // NOTE This represents half-float denormals using single - // precision denormals. The main reason to do this is that - // there's no shift with per-lane variable shifts in SSE*, which - // we'd otherwise need. It has some funky side effects though: - // - This conversion will actually respect the FTZ (Flush To Zero) - // flag in MXCSR - if it's set, no half-float denormals will be - // generated. I'm honestly not sure whether this is good or - // bad. It's definitely interesting. - // - If the underlying HW doesn't support denormals (not an issue - // with Intel CPUs, but might be a problem on GPUs or PS3 SPUs), - // you will always get flush-to-zero behavior. This is bad, - // unless you're on a CPU where you don't care. - // - Denormals tend to be slow. FP32 denormals are rare in - // practice outside of things like recursive filters in DSP - - // not a typical half-float application. Whether FP16 denormals - // are rare in practice, I don't know. Whatever slow path your - // HW may or may not have for denormals, this may well hit it. - float fscale = floatbits(fint & round_mask) * floatbits(magic); - fscale = std::min(fscale, floatbits((31u << 23) - 0x1000u)); - int32_t fint2 = intbits(fscale) - round_mask; - - if (fint < f32infty) - o = fint2 >> 13; // Take the bits! - - return (o | (sign >> 16)); -} - -float decode_fp16 (uint16_t h) { - // https://gist.github.com/2144712 - // Fabian "ryg" Giesen. - - const uint32_t shifted_exp = 0x7c00u << 13; // exponent mask after shift - - int32_t o = ((int32_t)(h & 0x7fffu)) << 13; // exponent/mantissa bits - int32_t exp = shifted_exp & o; // just the exponent - o += (int32_t)(127 - 15) << 23; // exponent adjust - - int32_t infnan_val = o + ((int32_t)(128 - 16) << 23); - int32_t zerodenorm_val = intbits( - floatbits(o + (1u<<23)) - floatbits(113u << 23)); - int32_t reg_val = (exp == 0) ? zerodenorm_val : o; - - int32_t sign_bit = ((int32_t)(h & 0x8000u)) << 16; - return floatbits(((exp == shifted_exp) ? infnan_val : reg_val) | sign_bit); -} - -#endif - - -/******************************************************************* - * Quantizer range training - */ - -static float sqr (float x) { - return x * x; -} - - -void train_Uniform(RangeStat rs, float rs_arg, - idx_t n, int k, const float *x, - std::vector & trained) -{ - trained.resize (2); - float & vmin = trained[0]; - float & vmax = trained[1]; - - if (rs == RangeStat::RS_minmax) { - vmin = HUGE_VAL; vmax = -HUGE_VAL; - for (size_t i = 0; i < n; i++) { - if (x[i] < vmin) vmin = x[i]; - if (x[i] > vmax) vmax = x[i]; - } - float vexp = (vmax - vmin) * rs_arg; - vmin -= vexp; - vmax += vexp; - } else if (rs == RangeStat::RS_meanstd) { - double sum = 0, sum2 = 0; - for (size_t i = 0; i < n; i++) { - sum += x[i]; - sum2 += x[i] * x[i]; - } - float mean = sum / n; - float var = sum2 / n - mean * mean; - float std = var <= 0 ? 1.0 : sqrt(var); - - vmin = mean - std * rs_arg ; - vmax = mean + std * rs_arg ; - } else if (rs == RangeStat::RS_quantiles) { - std::vector x_copy(n); - memcpy(x_copy.data(), x, n * sizeof(*x)); - // TODO just do a qucikselect - std::sort(x_copy.begin(), x_copy.end()); - int o = int(rs_arg * n); - if (o < 0) o = 0; - if (o > n - o) o = n / 2; - vmin = x_copy[o]; - vmax = x_copy[n - 1 - o]; - - } else if (rs == RangeStat::RS_optim) { - float a, b; - float sx = 0; - { - vmin = HUGE_VAL, vmax = -HUGE_VAL; - for (size_t i = 0; i < n; i++) { - if (x[i] < vmin) vmin = x[i]; - if (x[i] > vmax) vmax = x[i]; - sx += x[i]; - } - b = vmin; - a = (vmax - vmin) / (k - 1); - } - int verbose = false; - int niter = 2000; - float last_err = -1; - int iter_last_err = 0; - for (int it = 0; it < niter; it++) { - float sn = 0, sn2 = 0, sxn = 0, err1 = 0; - - for (idx_t i = 0; i < n; i++) { - float xi = x[i]; - float ni = floor ((xi - b) / a + 0.5); - if (ni < 0) ni = 0; - if (ni >= k) ni = k - 1; - err1 += sqr (xi - (ni * a + b)); - sn += ni; - sn2 += ni * ni; - sxn += ni * xi; - } - - if (err1 == last_err) { - iter_last_err ++; - if (iter_last_err == 16) break; - } else { - last_err = err1; - iter_last_err = 0; - } - - float det = sqr (sn) - sn2 * n; - - b = (sn * sxn - sn2 * sx) / det; - a = (sn * sx - n * sxn) / det; - if (verbose) { - printf ("it %d, err1=%g \r", it, err1); - fflush(stdout); - } - } - if (verbose) printf("\n"); - - vmin = b; - vmax = b + a * (k - 1); - - } else { - FAISS_THROW_MSG ("Invalid qtype"); - } - vmax -= vmin; -} - -void train_NonUniform(RangeStat rs, float rs_arg, - idx_t n, int d, int k, const float *x, - std::vector & trained) -{ - trained.resize (2 * d); - float * vmin = trained.data(); - float * vmax = trained.data() + d; - if (rs == RangeStat::RS_minmax) { - memcpy (vmin, x, sizeof(*x) * d); - memcpy (vmax, x, sizeof(*x) * d); - for (size_t i = 1; i < n; i++) { - const float *xi = x + i * d; - for (size_t j = 0; j < d; j++) { - if (xi[j] < vmin[j]) vmin[j] = xi[j]; - if (xi[j] > vmax[j]) vmax[j] = xi[j]; - } - } - float *vdiff = vmax; - for (size_t j = 0; j < d; j++) { - float vexp = (vmax[j] - vmin[j]) * rs_arg; - vmin[j] -= vexp; - vmax[j] += vexp; - vdiff [j] = vmax[j] - vmin[j]; - } - } else { - // transpose - std::vector xt(n * d); - for (size_t i = 1; i < n; i++) { - const float *xi = x + i * d; - for (size_t j = 0; j < d; j++) { - xt[j * n + i] = xi[j]; - } - } - std::vector trained_d(2); -#pragma omp parallel for - for (size_t j = 0; j < d; j++) { - train_Uniform(rs, rs_arg, - n, k, xt.data() + j * n, - trained_d); - vmin[j] = trained_d[0]; - vmax[j] = trained_d[1]; - } - } -} - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerOp.h b/core/src/index/thirdparty/faiss/impl/ScalarQuantizerOp.h deleted file mode 100644 index c272d29bc4..0000000000 --- a/core/src/index/thirdparty/faiss/impl/ScalarQuantizerOp.h +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#pragma once - -#include -#include - -#include - -#include -#include -#include - -namespace faiss { - -typedef Index::idx_t idx_t; - -enum class QuantizerType { - QT_8bit = 0, ///< 8 bits per component - QT_4bit, ///< 4 bits per component - QT_8bit_uniform, ///< same, shared range for all dimensions - QT_4bit_uniform, - QT_fp16, - QT_8bit_direct, /// fast indexing of uint8s - QT_6bit, ///< 6 bits per component -}; - -// rangestat_arg. -enum class RangeStat { - RS_minmax = 0, ///< [min - rs*(max-min), max + rs*(max-min)] - RS_meanstd, ///< [mean - std * rs, mean + std * rs] - RS_quantiles, ///< [Q(rs), Q(1-rs)] - RS_optim, ///< alternate optimization of reconstruction error -}; - -struct Quantizer { - // encodes one vector. Assumes code is filled with 0s on input! - virtual void encode_vector(const float *x, uint8_t *code) const = 0; - virtual void decode_vector(const uint8_t *code, float *x) const = 0; - - virtual ~Quantizer() {} -}; - -struct SQDistanceComputer: DistanceComputer { - const float *q; - const uint8_t *codes; - size_t code_size; - - SQDistanceComputer (): q(nullptr), codes (nullptr), code_size (0) - {} -}; - -extern uint16_t encode_fp16 (float x); -extern float decode_fp16 (uint16_t x); - -extern void train_Uniform(RangeStat rs, float rs_arg, - idx_t n, int k, const float *x, - std::vector & trained); -extern void train_NonUniform(RangeStat rs, float rs_arg, - idx_t n, int d, int k, const float *x, - std::vector & trained); - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/ThreadedIndex-inl.h b/core/src/index/thirdparty/faiss/impl/ThreadedIndex-inl.h deleted file mode 100644 index de549a0288..0000000000 --- a/core/src/index/thirdparty/faiss/impl/ThreadedIndex-inl.h +++ /dev/null @@ -1,192 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include -#include - -namespace faiss { - -template -ThreadedIndex::ThreadedIndex(bool threaded) - // 0 is default dimension - : ThreadedIndex(0, threaded) { -} - -template -ThreadedIndex::ThreadedIndex(int d, bool threaded) - : IndexT(d), - own_fields(false), - isThreaded_(threaded) { - } - -template -ThreadedIndex::~ThreadedIndex() { - for (auto& p : indices_) { - if (isThreaded_) { - // should have worker thread - FAISS_ASSERT((bool) p.second); - - // This will also flush all pending work - p.second->stop(); - p.second->waitForThreadExit(); - } else { - // should not have worker thread - FAISS_ASSERT(!(bool) p.second); - } - - if (own_fields) { - delete p.first; - } - } -} - -template -void ThreadedIndex::addIndex(IndexT* index) { - // We inherit the dimension from the first index added to us if we don't have - // a set dimension - if (indices_.empty() && this->d == 0) { - this->d = index->d; - } - - // The new index must match our set dimension - FAISS_THROW_IF_NOT_FMT(this->d == index->d, - "addIndex: dimension mismatch for " - "newly added index; expecting dim %d, " - "new index has dim %d", - this->d, index->d); - - if (!indices_.empty()) { - auto& existing = indices_.front().first; - - FAISS_THROW_IF_NOT_MSG(index->metric_type == existing->metric_type, - "addIndex: newly added index is " - "of different metric type than old index"); - - // Make sure this index is not duplicated - for (auto& p : indices_) { - FAISS_THROW_IF_NOT_MSG(p.first != index, - "addIndex: attempting to add index " - "that is already in the collection"); - } - } - - indices_.emplace_back( - std::make_pair( - index, - std::unique_ptr(isThreaded_ ? - new WorkerThread : nullptr))); - - onAfterAddIndex(index); -} - -template -void ThreadedIndex::removeIndex(IndexT* index) { - for (auto it = indices_.begin(); it != indices_.end(); ++it) { - if (it->first == index) { - // This is our index; stop the worker thread before removing it, - // to ensure that it has finished before function exit - if (isThreaded_) { - // should have worker thread - FAISS_ASSERT((bool) it->second); - it->second->stop(); - it->second->waitForThreadExit(); - } else { - // should not have worker thread - FAISS_ASSERT(!(bool) it->second); - } - - indices_.erase(it); - onAfterRemoveIndex(index); - - if (own_fields) { - delete index; - } - - return; - } - } - - // could not find our index - FAISS_THROW_MSG("IndexReplicas::removeIndex: index not found"); -} - -template -void ThreadedIndex::runOnIndex(std::function f) { - if (isThreaded_) { - std::vector> v; - - for (int i = 0; i < this->indices_.size(); ++i) { - auto& p = this->indices_[i]; - auto indexPtr = p.first; - v.emplace_back(p.second->add([f, i, indexPtr](){ f(i, indexPtr); })); - } - - waitAndHandleFutures(v); - } else { - // Multiple exceptions may be thrown; gather them as we encounter them, - // while letting everything else run to completion - std::vector> exceptions; - - for (int i = 0; i < this->indices_.size(); ++i) { - auto& p = this->indices_[i]; - try { - f(i, p.first); - } catch (...) { - exceptions.emplace_back(std::make_pair(i, std::current_exception())); - } - } - - handleExceptions(exceptions); - } -} - -template -void ThreadedIndex::runOnIndex( - std::function f) const { - const_cast*>(this)->runOnIndex( - [f](int i, IndexT* idx){ f(i, idx); }); -} - -template -void ThreadedIndex::reset() { - runOnIndex([](int, IndexT* index){ index->reset(); }); - this->ntotal = 0; - this->is_trained = false; -} - -template -void -ThreadedIndex::onAfterAddIndex(IndexT* index) { -} - -template -void -ThreadedIndex::onAfterRemoveIndex(IndexT* index) { -} - -template -void -ThreadedIndex::waitAndHandleFutures(std::vector>& v) { - // Blocking wait for completion for all of the indices, capturing any - // exceptions that are generated - std::vector> exceptions; - - for (int i = 0; i < v.size(); ++i) { - auto& fut = v[i]; - - try { - fut.get(); - } catch (...) { - exceptions.emplace_back(std::make_pair(i, std::current_exception())); - } - } - - handleExceptions(exceptions); -} - -} // namespace diff --git a/core/src/index/thirdparty/faiss/impl/ThreadedIndex.h b/core/src/index/thirdparty/faiss/impl/ThreadedIndex.h deleted file mode 100644 index 89f21486a6..0000000000 --- a/core/src/index/thirdparty/faiss/impl/ThreadedIndex.h +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace faiss { - -/// A holder of indices in a collection of threads -/// The interface to this class itself is not thread safe -template -class ThreadedIndex : public IndexT { - public: - explicit ThreadedIndex(bool threaded); - explicit ThreadedIndex(int d, bool threaded); - - ~ThreadedIndex() override; - - /// override an index that is managed by ourselves. - /// WARNING: once an index is added, it becomes unsafe to touch it from any - /// other thread than that on which is managing it, until we are shut - /// down. Use runOnIndex to perform work on it instead. - void addIndex(IndexT* index); - - /// Remove an index that is managed by ourselves. - /// This will flush all pending work on that index, and then shut - /// down its managing thread, and will remove the index. - void removeIndex(IndexT* index); - - /// Run a function on all indices, in the thread that the index is - /// managed in. - /// Function arguments are (index in collection, index pointer) - void runOnIndex(std::function f); - void runOnIndex(std::function f) const; - - /// faiss::Index API - /// All indices receive the same call - void reset() override; - - /// Returns the number of sub-indices - int count() const { return indices_.size(); } - - /// Returns the i-th sub-index - IndexT* at(int i) { return indices_[i].first; } - - /// Returns the i-th sub-index (const version) - const IndexT* at(int i) const { return indices_[i].first; } - - /// Whether or not we are responsible for deleting our contained indices - bool own_fields; - - protected: - /// Called just after an index is added - virtual void onAfterAddIndex(IndexT* index); - - /// Called just after an index is removed - virtual void onAfterRemoveIndex(IndexT* index); - -protected: - static void waitAndHandleFutures(std::vector>& v); - - /// Collection of Index instances, with their managing worker thread if any - std::vector>> indices_; - - /// Is this index multi-threaded? - bool isThreaded_; -}; - -} // namespace - -#include diff --git a/core/src/index/thirdparty/faiss/impl/index_read.cpp b/core/src/index/thirdparty/faiss/impl/index_read.cpp deleted file mode 100644 index 85cec7d39f..0000000000 --- a/core/src/index/thirdparty/faiss/impl/index_read.cpp +++ /dev/null @@ -1,920 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - - - -namespace faiss { - -/************************************************************* - * I/O macros - * - * we use macros so that we have a line number to report in abort - * (). This makes debugging a lot easier. The IOReader or IOWriter is - * always called f and thus is not passed in as a macro parameter. - **************************************************************/ - - -#define READANDCHECK(ptr, n) { \ - size_t ret = (*f)(ptr, sizeof(*(ptr)), n); \ - FAISS_THROW_IF_NOT_FMT(ret == (n), \ - "read error in %s: %ld != %ld (%s)", \ - f->name.c_str(), ret, size_t(n), strerror(errno)); \ - } - -#define READ1(x) READANDCHECK(&(x), 1) - -// will fail if we write 256G of data at once... -#define READVECTOR(vec) { \ - long size; \ - READANDCHECK (&size, 1); \ - FAISS_THROW_IF_NOT (size >= 0 && size < (1L << 40)); \ - (vec).resize (size); \ - READANDCHECK ((vec).data (), size); \ - } - - - -/************************************************************* - * Read - **************************************************************/ - -static void read_index_header (Index *idx, IOReader *f) { - READ1 (idx->d); - READ1 (idx->ntotal); - Index::idx_t dummy; - READ1 (dummy); - READ1 (dummy); - READ1 (idx->is_trained); - READ1 (idx->metric_type); - if (idx->metric_type > 1) { - READ1 (idx->metric_arg); - } - idx->verbose = false; -} - -VectorTransform* read_VectorTransform (IOReader *f) { - uint32_t h; - READ1 (h); - VectorTransform *vt = nullptr; - - if (h == fourcc ("rrot") || h == fourcc ("PCAm") || - h == fourcc ("LTra") || h == fourcc ("PcAm") || - h == fourcc ("Viqm")) { - LinearTransform *lt = nullptr; - if (h == fourcc ("rrot")) { - lt = new RandomRotationMatrix (); - } else if (h == fourcc ("PCAm") || - h == fourcc ("PcAm")) { - PCAMatrix * pca = new PCAMatrix (); - READ1 (pca->eigen_power); - READ1 (pca->random_rotation); - if (h == fourcc ("PcAm")) - READ1 (pca->balanced_bins); - READVECTOR (pca->mean); - READVECTOR (pca->eigenvalues); - READVECTOR (pca->PCAMat); - lt = pca; - } else if (h == fourcc ("Viqm")) { - ITQMatrix *itqm = new ITQMatrix (); - READ1 (itqm->max_iter); - READ1 (itqm->seed); - lt = itqm; - } else if (h == fourcc ("LTra")) { - lt = new LinearTransform (); - } - READ1 (lt->have_bias); - READVECTOR (lt->A); - READVECTOR (lt->b); - FAISS_THROW_IF_NOT (lt->A.size() >= lt->d_in * lt->d_out); - FAISS_THROW_IF_NOT (!lt->have_bias || lt->b.size() >= lt->d_out); - lt->set_is_orthonormal(); - vt = lt; - } else if (h == fourcc ("RmDT")) { - RemapDimensionsTransform *rdt = new RemapDimensionsTransform (); - READVECTOR (rdt->map); - vt = rdt; - } else if (h == fourcc ("VNrm")) { - NormalizationTransform *nt = new NormalizationTransform (); - READ1 (nt->norm); - vt = nt; - } else if (h == fourcc ("VCnt")) { - CenteringTransform *ct = new CenteringTransform (); - READVECTOR (ct->mean); - vt = ct; - } else if (h == fourcc ("Viqt")) { - ITQTransform *itqt = new ITQTransform (); - - READVECTOR (itqt->mean); - READ1 (itqt->do_pca); - { - ITQMatrix *itqm = dynamic_cast - (read_VectorTransform (f)); - FAISS_THROW_IF_NOT(itqm); - itqt->itq = *itqm; - delete itqm; - } - { - LinearTransform *pi = dynamic_cast - (read_VectorTransform (f)); - FAISS_THROW_IF_NOT (pi); - itqt->pca_then_itq = *pi; - delete pi; - } - vt = itqt; - } else { - FAISS_THROW_MSG("fourcc not recognized"); - } - READ1 (vt->d_in); - READ1 (vt->d_out); - READ1 (vt->is_trained); - return vt; -} - - -static void read_ArrayInvertedLists_sizes ( - IOReader *f, std::vector & sizes) -{ - uint32_t list_type; - READ1(list_type); - if (list_type == fourcc("full")) { - size_t os = sizes.size(); - READVECTOR (sizes); - FAISS_THROW_IF_NOT (os == sizes.size()); - } else if (list_type == fourcc("sprs")) { - std::vector idsizes; - READVECTOR (idsizes); - for (size_t j = 0; j < idsizes.size(); j += 2) { - FAISS_THROW_IF_NOT (idsizes[j] < sizes.size()); - sizes[idsizes[j]] = idsizes[j + 1]; - } - } else { - FAISS_THROW_MSG ("invalid list_type"); - } -} - -InvertedLists *read_InvertedLists (IOReader *f, int io_flags) { - uint32_t h; - READ1 (h); - if (h == fourcc ("il00")) { - fprintf(stderr, "read_InvertedLists:" - " WARN! inverted lists not stored with IVF object\n"); - return nullptr; - } else if (h == fourcc ("iloa") && !(io_flags & IO_FLAG_MMAP)) { - size_t nlist; - size_t code_size; - std::vector list_length; - READ1(nlist); - READ1(code_size); - READVECTOR(list_length); - auto ails = new ReadOnlyArrayInvertedLists(nlist, code_size, list_length); - size_t n; - READ1(n); -#ifdef USE_CPU - ails->readonly_ids.resize(n); - ails->readonly_codes.resize(n*code_size); - READANDCHECK(ails->readonly_ids.data(), n); - READANDCHECK(ails->readonly_codes.data(), n * code_size); -#else - ails->pin_readonly_ids = std::make_shared(n * sizeof(InvertedLists::idx_t)); - ails->pin_readonly_codes = std::make_shared(n * code_size * sizeof(uint8_t)); - READANDCHECK((InvertedLists::idx_t *) ails->pin_readonly_ids->data, n); - READANDCHECK((uint8_t *) ails->pin_readonly_codes->data, n * code_size); -#endif - return ails; - } else if (h == fourcc ("ilar") && !(io_flags & IO_FLAG_MMAP)) { - auto ails = new ArrayInvertedLists (0, 0); - READ1 (ails->nlist); - READ1 (ails->code_size); - ails->ids.resize (ails->nlist); - ails->codes.resize (ails->nlist); - std::vector sizes (ails->nlist); - read_ArrayInvertedLists_sizes (f, sizes); - for (size_t i = 0; i < ails->nlist; i++) { - ails->ids[i].resize (sizes[i]); - ails->codes[i].resize (sizes[i] * ails->code_size); - } - for (size_t i = 0; i < ails->nlist; i++) { - size_t n = ails->ids[i].size(); - if (n > 0) { - READANDCHECK (ails->codes[i].data(), n * ails->code_size); - READANDCHECK (ails->ids[i].data(), n); - } - } - return ails; - } else if (h == fourcc ("ilar") && (io_flags & IO_FLAG_MMAP)) { - // then we load it as an OnDiskInvertedLists - - FileIOReader *reader = dynamic_cast(f); - FAISS_THROW_IF_NOT_MSG(reader, "mmap only supported for File objects"); - FILE *fdesc = reader->f; - - auto ails = new OnDiskInvertedLists (); - READ1 (ails->nlist); - READ1 (ails->code_size); - ails->read_only = true; - ails->lists.resize (ails->nlist); - std::vector sizes (ails->nlist); - read_ArrayInvertedLists_sizes (f, sizes); - size_t o0 = ftell(fdesc), o = o0; - { // do the mmap - struct stat buf; - int ret = fstat (fileno(fdesc), &buf); - FAISS_THROW_IF_NOT_FMT (ret == 0, - "fstat failed: %s", strerror(errno)); - ails->totsize = buf.st_size; - ails->ptr = (uint8_t*)mmap (nullptr, ails->totsize, - PROT_READ, MAP_SHARED, - fileno(fdesc), 0); - FAISS_THROW_IF_NOT_FMT (ails->ptr != MAP_FAILED, - "could not mmap: %s", - strerror(errno)); - } - - for (size_t i = 0; i < ails->nlist; i++) { - OnDiskInvertedLists::List & l = ails->lists[i]; - l.size = l.capacity = sizes[i]; - l.offset = o; - o += l.size * (sizeof(OnDiskInvertedLists::idx_t) + - ails->code_size); - } - FAISS_THROW_IF_NOT(o <= ails->totsize); - // resume normal reading of file - fseek (fdesc, o, SEEK_SET); - return ails; - } else if (h == fourcc ("ilod")) { - OnDiskInvertedLists *od = new OnDiskInvertedLists(); - od->read_only = io_flags & IO_FLAG_READ_ONLY; - READ1 (od->nlist); - READ1 (od->code_size); - // this is a POD object - READVECTOR (od->lists); - { - std::vector v; - READVECTOR(v); - od->slots.assign(v.begin(), v.end()); - } - { - std::vector x; - READVECTOR(x); - od->filename.assign(x.begin(), x.end()); - - if (io_flags & IO_FLAG_ONDISK_SAME_DIR) { - FileIOReader *reader = dynamic_cast(f); - FAISS_THROW_IF_NOT_MSG ( - reader, "IO_FLAG_ONDISK_SAME_DIR only supported " - "when reading from file"); - std::string indexname = reader->name; - std::string dirname = "./"; - size_t slash = indexname.find_last_of('/'); - if (slash != std::string::npos) { - dirname = indexname.substr(0, slash + 1); - } - std::string filename = od->filename; - slash = filename.find_last_of('/'); - if (slash != std::string::npos) { - filename = filename.substr(slash + 1); - } - filename = dirname + filename; - printf("IO_FLAG_ONDISK_SAME_DIR: " - "updating ondisk filename from %s to %s\n", - od->filename.c_str(), filename.c_str()); - od->filename = filename; - } - - } - READ1(od->totsize); - od->do_mmap(); - return od; - } else { - FAISS_THROW_MSG ("read_InvertedLists: unsupported invlist type"); - } -} - -static void read_InvertedLists ( - IndexIVF *ivf, IOReader *f, int io_flags) { - InvertedLists *ils = read_InvertedLists (f, io_flags); - FAISS_THROW_IF_NOT (!ils || (ils->nlist == ivf->nlist && - ils->code_size == ivf->code_size)); - ivf->invlists = ils; - ivf->own_invlists = true; -} - -static void read_ProductQuantizer (ProductQuantizer *pq, IOReader *f) { - READ1 (pq->d); - READ1 (pq->M); - READ1 (pq->nbits); - pq->set_derived_values (); - READVECTOR (pq->centroids); -} - -static void read_ScalarQuantizer (ScalarQuantizer *ivsc, IOReader *f) { - READ1 (ivsc->qtype); - READ1 (ivsc->rangestat); - READ1 (ivsc->rangestat_arg); - READ1 (ivsc->d); - READ1 (ivsc->code_size); - READVECTOR (ivsc->trained); -} - - -static void read_HNSW (HNSW *hnsw, IOReader *f) { - READVECTOR (hnsw->assign_probas); - READVECTOR (hnsw->cum_nneighbor_per_level); - READVECTOR (hnsw->levels); - READVECTOR (hnsw->offsets); - READVECTOR (hnsw->neighbors); - - READ1 (hnsw->entry_point); - READ1 (hnsw->max_level); - READ1 (hnsw->efConstruction); - READ1 (hnsw->efSearch); - READ1 (hnsw->upper_beam); -} - -ProductQuantizer * read_ProductQuantizer (const char*fname) { - FileIOReader reader(fname); - return read_ProductQuantizer(&reader); -} - -ProductQuantizer * read_ProductQuantizer (IOReader *reader) { - ProductQuantizer *pq = new ProductQuantizer(); - ScopeDeleter1 del (pq); - - read_ProductQuantizer(pq, reader); - del.release (); - return pq; -} - -static void read_direct_map (DirectMap *dm, IOReader *f) { - char maintain_direct_map; - READ1 (maintain_direct_map); - dm->type = (DirectMap::Type)maintain_direct_map; - READVECTOR (dm->array); - if (dm->type == DirectMap::Hashtable) { - using idx_t = Index::idx_t; - std::vector> v; - READVECTOR (v); - std::unordered_map & map = dm->hashtable; - map.reserve (v.size()); - for (auto it: v) { - map [it.first] = it.second; - } - } - -} - - -static void read_ivf_header ( - IndexIVF *ivf, IOReader *f, - std::vector > *ids = nullptr) -{ - read_index_header (ivf, f); - READ1 (ivf->nlist); - READ1 (ivf->nprobe); - ivf->quantizer = read_index (f); - ivf->own_fields = true; - if (ids) { // used in legacy "Iv" formats - ids->resize (ivf->nlist); - for (size_t i = 0; i < ivf->nlist; i++) - READVECTOR ((*ids)[i]); - } - read_direct_map (&ivf->direct_map, f); -} - -// used for legacy formats -static ArrayInvertedLists *set_array_invlist( - IndexIVF *ivf, std::vector > &ids) -{ - ArrayInvertedLists *ail = new ArrayInvertedLists ( - ivf->nlist, ivf->code_size); - std::swap (ail->ids, ids); - ivf->invlists = ail; - ivf->own_invlists = true; - return ail; -} - -static IndexIVFPQ *read_ivfpq (IOReader *f, uint32_t h, int io_flags) -{ - bool legacy = h == fourcc ("IvQR") || h == fourcc ("IvPQ"); - - IndexIVFPQR *ivfpqr = - h == fourcc ("IvQR") || h == fourcc ("IwQR") ? - new IndexIVFPQR () : nullptr; - IndexIVFPQ * ivpq = ivfpqr ? ivfpqr : new IndexIVFPQ (); - - std::vector > ids; - read_ivf_header (ivpq, f, legacy ? &ids : nullptr); - READ1 (ivpq->by_residual); - READ1 (ivpq->code_size); - read_ProductQuantizer (&ivpq->pq, f); - - if (legacy) { - ArrayInvertedLists *ail = set_array_invlist (ivpq, ids); - for (size_t i = 0; i < ail->nlist; i++) - READVECTOR (ail->codes[i]); - } else { - read_InvertedLists (ivpq, f, io_flags); - } - - if (ivpq->is_trained) { - // precomputed table not stored. It is cheaper to recompute it - ivpq->use_precomputed_table = 0; - if (ivpq->by_residual) - ivpq->precompute_table (); - if (ivfpqr) { - read_ProductQuantizer (&ivfpqr->refine_pq, f); - READVECTOR (ivfpqr->refine_codes); - READ1 (ivfpqr->k_factor); - } - } - return ivpq; -} - -int read_old_fmt_hack = 0; - -Index *read_index (IOReader *f, int io_flags) { - Index * idx = nullptr; - uint32_t h; - READ1 (h); - if (h == fourcc ("IxFI") || h == fourcc ("IxF2") || h == fourcc("IxFl")) { - IndexFlat *idxf; - if (h == fourcc ("IxFI")) { - idxf = new IndexFlatIP (); - } else if (h == fourcc("IxF2")) { - idxf = new IndexFlatL2 (); - } else { - idxf = new IndexFlat (); - } - read_index_header (idxf, f); - READVECTOR (idxf->xb); - FAISS_THROW_IF_NOT (idxf->xb.size() == idxf->ntotal * idxf->d); - // leak! - idx = idxf; - } else if (h == fourcc("IxHE") || h == fourcc("IxHe")) { - IndexLSH * idxl = new IndexLSH (); - read_index_header (idxl, f); - READ1 (idxl->nbits); - READ1 (idxl->rotate_data); - READ1 (idxl->train_thresholds); - READVECTOR (idxl->thresholds); - READ1 (idxl->bytes_per_vec); - if (h == fourcc("IxHE")) { - FAISS_THROW_IF_NOT_FMT (idxl->nbits % 64 == 0, - "can only read old format IndexLSH with " - "nbits multiple of 64 (got %d)", - (int) idxl->nbits); - // leak - idxl->bytes_per_vec *= 8; - } - { - RandomRotationMatrix *rrot = dynamic_cast - (read_VectorTransform (f)); - FAISS_THROW_IF_NOT_MSG(rrot, "expected a random rotation"); - idxl->rrot = *rrot; - delete rrot; - } - READVECTOR (idxl->codes); - FAISS_THROW_IF_NOT (idxl->rrot.d_in == idxl->d && - idxl->rrot.d_out == idxl->nbits); - FAISS_THROW_IF_NOT ( - idxl->codes.size() == idxl->ntotal * idxl->bytes_per_vec); - idx = idxl; - } else if (h == fourcc ("IxPQ") || h == fourcc ("IxPo") || - h == fourcc ("IxPq")) { - // IxPQ and IxPo were merged into the same IndexPQ object - IndexPQ * idxp =new IndexPQ (); - read_index_header (idxp, f); - read_ProductQuantizer (&idxp->pq, f); - READVECTOR (idxp->codes); - if (h == fourcc ("IxPo") || h == fourcc ("IxPq")) { - READ1 (idxp->search_type); - READ1 (idxp->encode_signs); - READ1 (idxp->polysemous_ht); - } - // Old versoins of PQ all had metric_type set to INNER_PRODUCT - // when they were in fact using L2. Therefore, we force metric type - // to L2 when the old format is detected - if (h == fourcc ("IxPQ") || h == fourcc ("IxPo")) { - idxp->metric_type = METRIC_L2; - } - idx = idxp; - } else if (h == fourcc ("IvFl") || h == fourcc("IvFL")) { // legacy - IndexIVFFlat * ivfl = new IndexIVFFlat (); - std::vector > ids; - read_ivf_header (ivfl, f, &ids); - ivfl->code_size = ivfl->d * sizeof(float); - ArrayInvertedLists *ail = set_array_invlist (ivfl, ids); - - if (h == fourcc ("IvFL")) { - for (size_t i = 0; i < ivfl->nlist; i++) { - READVECTOR (ail->codes[i]); - } - } else { // old format - for (size_t i = 0; i < ivfl->nlist; i++) { - std::vector vec; - READVECTOR (vec); - ail->codes[i].resize(vec.size() * sizeof(float)); - memcpy(ail->codes[i].data(), vec.data(), - ail->codes[i].size()); - } - } - idx = ivfl; - } else if (h == fourcc ("IwFd")) { - IndexIVFFlatDedup * ivfl = new IndexIVFFlatDedup (); - read_ivf_header (ivfl, f); - ivfl->code_size = ivfl->d * sizeof(float); - { - std::vector tab; - READVECTOR (tab); - for (long i = 0; i < tab.size(); i += 2) { - std::pair - pair (tab[i], tab[i + 1]); - ivfl->instances.insert (pair); - } - } - read_InvertedLists (ivfl, f, io_flags); - idx = ivfl; - } else if (h == fourcc ("IwFl")) { - IndexIVFFlat * ivfl = new IndexIVFFlat (); - read_ivf_header (ivfl, f); - ivfl->code_size = ivfl->d * sizeof(float); - read_InvertedLists (ivfl, f, io_flags); - idx = ivfl; - } else if (h == fourcc ("IxSQ")) { - IndexScalarQuantizer * idxs = new IndexScalarQuantizer (); - read_index_header (idxs, f); - read_ScalarQuantizer (&idxs->sq, f); - READVECTOR (idxs->codes); - idxs->code_size = idxs->sq.code_size; - idx = idxs; - } else if (h == fourcc ("IxLa")) { - int d, nsq, scale_nbit, r2; - READ1 (d); - READ1 (nsq); - READ1 (scale_nbit); - READ1 (r2); - IndexLattice *idxl = new IndexLattice (d, nsq, scale_nbit, r2); - read_index_header (idxl, f); - READVECTOR (idxl->trained); - idx = idxl; - } else if(h == fourcc ("IvSQ")) { // legacy - IndexIVFScalarQuantizer * ivsc = new IndexIVFScalarQuantizer(); - std::vector > ids; - read_ivf_header (ivsc, f, &ids); - read_ScalarQuantizer (&ivsc->sq, f); - READ1 (ivsc->code_size); - ArrayInvertedLists *ail = set_array_invlist (ivsc, ids); - for(int i = 0; i < ivsc->nlist; i++) - READVECTOR (ail->codes[i]); - idx = ivsc; - } else if(h == fourcc ("IwSQ") || h == fourcc ("IwSq")) { - IndexIVFScalarQuantizer * ivsc = new IndexIVFScalarQuantizer(); - read_ivf_header (ivsc, f); - read_ScalarQuantizer (&ivsc->sq, f); - READ1 (ivsc->code_size); - if (h == fourcc ("IwSQ")) { - ivsc->by_residual = true; - } else { - READ1 (ivsc->by_residual); - } - read_InvertedLists (ivsc, f, io_flags); - idx = ivsc; - } else if (h == fourcc("ISqH")) { - IndexIVFSQHybrid *ivfsqhbyrid = new IndexIVFSQHybrid(); - read_ivf_header(ivfsqhbyrid, f); - read_ScalarQuantizer(&ivfsqhbyrid->sq, f); - READ1 (ivfsqhbyrid->code_size); - READ1 (ivfsqhbyrid->by_residual); - read_InvertedLists(ivfsqhbyrid, f, io_flags); - idx = ivfsqhbyrid; - } else if(h == fourcc ("IwSh")) { - IndexIVFSpectralHash *ivsp = new IndexIVFSpectralHash (); - read_ivf_header (ivsp, f); - ivsp->vt = read_VectorTransform (f); - ivsp->own_fields = true; - READ1 (ivsp->nbit); - // not stored by write_ivf_header - ivsp->code_size = (ivsp->nbit + 7) / 8; - READ1 (ivsp->period); - READ1 (ivsp->threshold_type); - READVECTOR (ivsp->trained); - read_InvertedLists (ivsp, f, io_flags); - idx = ivsp; - } else if(h == fourcc ("IvPQ") || h == fourcc ("IvQR") || - h == fourcc ("IwPQ") || h == fourcc ("IwQR")) { - - idx = read_ivfpq (f, h, io_flags); - - } else if(h == fourcc ("IxPT")) { - IndexPreTransform * ixpt = new IndexPreTransform(); - ixpt->own_fields = true; - read_index_header (ixpt, f); - int nt; - if (read_old_fmt_hack == 2) { - nt = 1; - } else { - READ1 (nt); - } - for (int i = 0; i < nt; i++) { - ixpt->chain.push_back (read_VectorTransform (f)); - } - ixpt->index = read_index (f, io_flags); - idx = ixpt; - } else if(h == fourcc ("Imiq")) { - MultiIndexQuantizer * imiq = new MultiIndexQuantizer (); - read_index_header (imiq, f); - read_ProductQuantizer (&imiq->pq, f); - idx = imiq; - } else if(h == fourcc ("IxRF")) { - IndexRefineFlat *idxrf = new IndexRefineFlat (); - read_index_header (idxrf, f); - idxrf->base_index = read_index(f, io_flags); - idxrf->own_fields = true; - IndexFlat *rf = dynamic_cast (read_index (f, io_flags)); - std::swap (*rf, idxrf->refine_index); - delete rf; - READ1 (idxrf->k_factor); - idx = idxrf; - } else if(h == fourcc ("IxMp") || h == fourcc ("IxM2")) { - bool is_map2 = h == fourcc ("IxM2"); - IndexIDMap * idxmap = is_map2 ? new IndexIDMap2 () : new IndexIDMap (); - read_index_header (idxmap, f); - idxmap->index = read_index (f, io_flags); - idxmap->own_fields = true; - READVECTOR (idxmap->id_map); - if (is_map2) { - static_cast(idxmap)->construct_rev_map (); - } - idx = idxmap; - } else if (h == fourcc ("Ix2L")) { - Index2Layer * idxp = new Index2Layer (); - read_index_header (idxp, f); - idxp->q1.quantizer = read_index (f, io_flags); - READ1 (idxp->q1.nlist); - READ1 (idxp->q1.quantizer_trains_alone); - read_ProductQuantizer (&idxp->pq, f); - READ1 (idxp->code_size_1); - READ1 (idxp->code_size_2); - READ1 (idxp->code_size); - READVECTOR (idxp->codes); - idx = idxp; - } else if(h == fourcc("IHNf") || h == fourcc("IHNp") || - h == fourcc("IHNs") || h == fourcc("IHN2")) { - IndexHNSW *idxhnsw = nullptr; - if (h == fourcc("IHNf")) idxhnsw = new IndexHNSWFlat (); - if (h == fourcc("IHNp")) idxhnsw = new IndexHNSWPQ (); - if (h == fourcc("IHNs")) idxhnsw = new IndexHNSWSQ (); - if (h == fourcc("IHN2")) idxhnsw = new IndexHNSW2Level (); - read_index_header (idxhnsw, f); - read_HNSW (&idxhnsw->hnsw, f); - idxhnsw->storage = read_index (f, io_flags); - idxhnsw->own_fields = true; - if (h == fourcc("IHNp")) { - dynamic_cast(idxhnsw->storage)->pq.compute_sdc_table (); - } - idx = idxhnsw; - } else { - FAISS_THROW_FMT("Index type 0x%08x not supported\n", h); - idx = nullptr; - } - return idx; -} - - -Index *read_index (FILE * f, int io_flags) { - FileIOReader reader(f); - return read_index(&reader, io_flags); -} - -Index *read_index (const char *fname, int io_flags) { - FileIOReader reader(fname); - Index *idx = read_index (&reader, io_flags); - return idx; -} - -VectorTransform *read_VectorTransform (const char *fname) { - FileIOReader reader(fname); - VectorTransform *vt = read_VectorTransform (&reader); - return vt; -} - - - -/************************************************************* - * Read binary indexes - **************************************************************/ - -static void read_InvertedLists ( - IndexBinaryIVF *ivf, IOReader *f, int io_flags) { - InvertedLists *ils = read_InvertedLists (f, io_flags); - FAISS_THROW_IF_NOT (!ils || (ils->nlist == ivf->nlist && - ils->code_size == ivf->code_size)); - ivf->invlists = ils; - ivf->own_invlists = true; -} - - - -static void read_index_binary_header (IndexBinary *idx, IOReader *f) { - READ1 (idx->d); - READ1 (idx->code_size); - READ1 (idx->ntotal); - READ1 (idx->is_trained); - READ1 (idx->metric_type); - idx->verbose = false; -} - -static void read_binary_ivf_header ( - IndexBinaryIVF *ivf, IOReader *f, - std::vector > *ids = nullptr) -{ - read_index_binary_header (ivf, f); - READ1 (ivf->nlist); - READ1 (ivf->nprobe); - ivf->quantizer = read_index_binary (f); - ivf->own_fields = true; - if (ids) { // used in legacy "Iv" formats - ids->resize (ivf->nlist); - for (size_t i = 0; i < ivf->nlist; i++) - READVECTOR ((*ids)[i]); - } - read_direct_map (&ivf->direct_map, f); -} - -static void read_binary_hash_invlists ( - IndexBinaryHash::InvertedListMap &invlists, - int b, IOReader *f) -{ - size_t sz; - READ1 (sz); - int il_nbit = 0; - READ1 (il_nbit); - // buffer for bitstrings - std::vector buf((b + il_nbit) * sz); - READVECTOR (buf); - BitstringReader rd (buf.data(), buf.size()); - invlists.reserve (sz); - for (size_t i = 0; i < sz; i++) { - uint64_t hash = rd.read(b); - uint64_t ilsz = rd.read(il_nbit); - auto & il = invlists[hash]; - READVECTOR (il.ids); - FAISS_THROW_IF_NOT (il.ids.size() == ilsz); - READVECTOR (il.vecs); - } -} - -static void read_binary_multi_hash_map( - IndexBinaryMultiHash::Map &map, - int b, size_t ntotal, - IOReader *f) -{ - int id_bits; - size_t sz; - READ1 (id_bits); - READ1 (sz); - std::vector buf; - READVECTOR (buf); - size_t nbit = (b + id_bits) * sz + ntotal * id_bits; - FAISS_THROW_IF_NOT (buf.size() == (nbit + 7) / 8); - BitstringReader rd (buf.data(), buf.size()); - map.reserve (sz); - for (size_t i = 0; i < sz; i++) { - uint64_t hash = rd.read(b); - uint64_t ilsz = rd.read(id_bits); - auto & il = map[hash]; - for (size_t j = 0; j < ilsz; j++) { - il.push_back (rd.read (id_bits)); - } - } -} - - - -IndexBinary *read_index_binary (IOReader *f, int io_flags) { - IndexBinary * idx = nullptr; - uint32_t h; - READ1 (h); - if (h == fourcc ("IBxF")) { - IndexBinaryFlat *idxf = new IndexBinaryFlat (); - read_index_binary_header (idxf, f); - READVECTOR (idxf->xb); - FAISS_THROW_IF_NOT (idxf->xb.size() == idxf->ntotal * idxf->code_size); - // leak! - idx = idxf; - } else if (h == fourcc ("IBwF")) { - IndexBinaryIVF *ivf = new IndexBinaryIVF (); - read_binary_ivf_header (ivf, f); - read_InvertedLists (ivf, f, io_flags); - idx = ivf; - } else if (h == fourcc ("IBFf")) { - IndexBinaryFromFloat *idxff = new IndexBinaryFromFloat (); - read_index_binary_header (idxff, f); - idxff->own_fields = true; - idxff->index = read_index (f, io_flags); - idx = idxff; - } else if (h == fourcc ("IBHf")) { - IndexBinaryHNSW *idxhnsw = new IndexBinaryHNSW (); - read_index_binary_header (idxhnsw, f); - read_HNSW (&idxhnsw->hnsw, f); - idxhnsw->storage = read_index_binary (f, io_flags); - idxhnsw->own_fields = true; - idx = idxhnsw; - } else if(h == fourcc ("IBMp") || h == fourcc ("IBM2")) { - bool is_map2 = h == fourcc ("IBM2"); - IndexBinaryIDMap * idxmap = is_map2 ? - new IndexBinaryIDMap2 () : new IndexBinaryIDMap (); - read_index_binary_header (idxmap, f); - idxmap->index = read_index_binary (f, io_flags); - idxmap->own_fields = true; - READVECTOR (idxmap->id_map); - if (is_map2) { - static_cast(idxmap)->construct_rev_map (); - } - idx = idxmap; - } else if(h == fourcc("IBHh")) { - IndexBinaryHash *idxh = new IndexBinaryHash (); - read_index_binary_header (idxh, f); - READ1 (idxh->b); - READ1 (idxh->nflip); - read_binary_hash_invlists(idxh->invlists, idxh->b, f); - idx = idxh; - } else if(h == fourcc("IBHm")) { - IndexBinaryMultiHash* idxmh = new IndexBinaryMultiHash (); - read_index_binary_header (idxmh, f); - idxmh->storage = dynamic_cast (read_index_binary (f)); - FAISS_THROW_IF_NOT(idxmh->storage && idxmh->storage->ntotal == idxmh->ntotal); - idxmh->own_fields = true; - READ1 (idxmh->b); - READ1 (idxmh->nhash); - READ1 (idxmh->nflip); - idxmh->maps.resize (idxmh->nhash); - for (int i = 0; i < idxmh->nhash; i++) { - read_binary_multi_hash_map( - idxmh->maps[i], idxmh->b, idxmh->ntotal, f); - } - idx = idxmh; - } else { - FAISS_THROW_FMT("Index type 0x%08x not supported\n", h); - idx = nullptr; - } - return idx; -} - -IndexBinary *read_index_binary (FILE * f, int io_flags) { - FileIOReader reader(f); - return read_index_binary(&reader, io_flags); -} - -IndexBinary *read_index_binary (const char *fname, int io_flags) { - FileIOReader reader(fname); - IndexBinary *idx = read_index_binary (&reader, io_flags); - return idx; -} - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/index_write.cpp b/core/src/index/thirdparty/faiss/impl/index_write.cpp deleted file mode 100644 index 54fce2fc46..0000000000 --- a/core/src/index/thirdparty/faiss/impl/index_write.cpp +++ /dev/null @@ -1,682 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - - - -/************************************************************* - * The I/O format is the content of the class. For objects that are - * inherited, like Index, a 4-character-code (fourcc) indicates which - * child class this is an instance of. - * - * In this case, the fields of the parent class are written first, - * then the ones for the child classes. Note that this requires - * classes to be serialized to have a constructor without parameters, - * so that the fields can be filled in later. The default constructor - * should set reasonable defaults for all fields. - * - * The fourccs are assigned arbitrarily. When the class changed (added - * or deprecated fields), the fourcc can be replaced. New code should - * be able to read the old fourcc and fill in new classes. - * - * TODO: serialization to strings for use in Python pickle or Torch - * serialization. - * - * TODO: in this file, the read functions that encouter errors may - * leak memory. - **************************************************************/ - - - -namespace faiss { - - -/************************************************************* - * I/O macros - * - * we use macros so that we have a line number to report in abort - * (). This makes debugging a lot easier. The IOReader or IOWriter is - * always called f and thus is not passed in as a macro parameter. - **************************************************************/ - - -#define WRITEANDCHECK(ptr, n) { \ - size_t ret = (*f)(ptr, sizeof(*(ptr)), n); \ - FAISS_THROW_IF_NOT_FMT(ret == (n), \ - "write error in %s: %ld != %ld (%s)", \ - f->name.c_str(), ret, size_t(n), strerror(errno)); \ - } - -#define WRITE1(x) WRITEANDCHECK(&(x), 1) - -#define WRITEVECTOR(vec) { \ - size_t size = (vec).size (); \ - WRITEANDCHECK (&size, 1); \ - WRITEANDCHECK ((vec).data (), size); \ - } - - - -/************************************************************* - * Write - **************************************************************/ -static void write_index_header (const Index *idx, IOWriter *f) { - WRITE1 (idx->d); - WRITE1 (idx->ntotal); - Index::idx_t dummy = 1 << 20; - WRITE1 (dummy); - WRITE1 (dummy); - WRITE1 (idx->is_trained); - WRITE1 (idx->metric_type); - if (idx->metric_type > 1) { - WRITE1 (idx->metric_arg); - } -} - -void write_VectorTransform (const VectorTransform *vt, IOWriter *f) { - if (const LinearTransform * lt = - dynamic_cast < const LinearTransform *> (vt)) { - if (dynamic_cast(lt)) { - uint32_t h = fourcc ("rrot"); - WRITE1 (h); - } else if (const PCAMatrix * pca = - dynamic_cast(lt)) { - uint32_t h = fourcc ("PcAm"); - WRITE1 (h); - WRITE1 (pca->eigen_power); - WRITE1 (pca->random_rotation); - WRITE1 (pca->balanced_bins); - WRITEVECTOR (pca->mean); - WRITEVECTOR (pca->eigenvalues); - WRITEVECTOR (pca->PCAMat); - } else if (const ITQMatrix * itqm = - dynamic_cast(lt)) { - uint32_t h = fourcc ("Viqm"); - WRITE1 (h); - WRITE1 (itqm->max_iter); - WRITE1 (itqm->seed); - } else { - // generic LinearTransform (includes OPQ) - uint32_t h = fourcc ("LTra"); - WRITE1 (h); - } - WRITE1 (lt->have_bias); - WRITEVECTOR (lt->A); - WRITEVECTOR (lt->b); - } else if (const RemapDimensionsTransform *rdt = - dynamic_cast(vt)) { - uint32_t h = fourcc ("RmDT"); - WRITE1 (h); - WRITEVECTOR (rdt->map); - } else if (const NormalizationTransform *nt = - dynamic_cast(vt)) { - uint32_t h = fourcc ("VNrm"); - WRITE1 (h); - WRITE1 (nt->norm); - } else if (const CenteringTransform *ct = - dynamic_cast(vt)) { - uint32_t h = fourcc ("VCnt"); - WRITE1 (h); - WRITEVECTOR (ct->mean); - } else if (const ITQTransform *itqt = - dynamic_cast (vt)) { - uint32_t h = fourcc ("Viqt"); - WRITE1 (h); - WRITEVECTOR (itqt->mean); - WRITE1 (itqt->do_pca); - write_VectorTransform (&itqt->itq, f); - write_VectorTransform (&itqt->pca_then_itq, f); - } else { - FAISS_THROW_MSG ("cannot serialize this"); - } - // common fields - WRITE1 (vt->d_in); - WRITE1 (vt->d_out); - WRITE1 (vt->is_trained); -} - -void write_ProductQuantizer (const ProductQuantizer *pq, IOWriter *f) { - WRITE1 (pq->d); - WRITE1 (pq->M); - WRITE1 (pq->nbits); - WRITEVECTOR (pq->centroids); -} - -static void write_ScalarQuantizer ( - const ScalarQuantizer *ivsc, IOWriter *f) { - WRITE1 (ivsc->qtype); - WRITE1 (ivsc->rangestat); - WRITE1 (ivsc->rangestat_arg); - WRITE1 (ivsc->d); - WRITE1 (ivsc->code_size); - WRITEVECTOR (ivsc->trained); -} - -void write_InvertedLists (const InvertedLists *ils, IOWriter *f) { - if (ils == nullptr) { - uint32_t h = fourcc ("il00"); - WRITE1 (h); - } else if (const auto & ails = - dynamic_cast(ils)) { - uint32_t h = fourcc ("ilar"); - WRITE1 (h); - WRITE1 (ails->nlist); - WRITE1 (ails->code_size); - // here we store either as a full or a sparse data buffer - size_t n_non0 = 0; - for (size_t i = 0; i < ails->nlist; i++) { - if (ails->ids[i].size() > 0) - n_non0++; - } - if (n_non0 > ails->nlist / 2) { - uint32_t list_type = fourcc("full"); - WRITE1 (list_type); - std::vector sizes; - for (size_t i = 0; i < ails->nlist; i++) { - sizes.push_back (ails->ids[i].size()); - } - WRITEVECTOR (sizes); - } else { - int list_type = fourcc("sprs"); // sparse - WRITE1 (list_type); - std::vector sizes; - for (size_t i = 0; i < ails->nlist; i++) { - size_t n = ails->ids[i].size(); - if (n > 0) { - sizes.push_back (i); - sizes.push_back (n); - } - } - WRITEVECTOR (sizes); - } - // make a single contiguous data buffer (useful for mmapping) - for (size_t i = 0; i < ails->nlist; i++) { - size_t n = ails->ids[i].size(); - if (n > 0) { - WRITEANDCHECK (ails->codes[i].data(), n * ails->code_size); - WRITEANDCHECK (ails->ids[i].data(), n); - } - } - } else if (const auto & oa = - dynamic_cast(ils)) { - uint32_t h = fourcc("iloa"); - WRITE1 (h); - WRITE1 (oa->nlist); - WRITE1 (oa->code_size); - WRITEVECTOR(oa->readonly_length); -#ifdef USE_CPU - size_t n = oa->readonly_ids.size(); - WRITE1(n); - WRITEANDCHECK(oa->readonly_ids.data(), n); - WRITEANDCHECK(oa->readonly_codes.data(), n * oa->code_size); -#else - size_t n = oa->pin_readonly_ids->size() / sizeof(InvertedLists::idx_t); - WRITE1(n); - WRITEANDCHECK((InvertedLists::idx_t *) oa->pin_readonly_ids->data, n); - WRITEANDCHECK((uint8_t *) oa->pin_readonly_codes->data, n * oa->code_size); -#endif - } else if (const auto & od = - dynamic_cast(ils)) { - uint32_t h = fourcc ("ilod"); - WRITE1 (h); - WRITE1 (ils->nlist); - WRITE1 (ils->code_size); - // this is a POD object - WRITEVECTOR (od->lists); - - { - std::vector v( - od->slots.begin(), od->slots.end()); - WRITEVECTOR(v); - } - { - std::vector x(od->filename.begin(), od->filename.end()); - WRITEVECTOR(x); - } - WRITE1(od->totsize); - - } else { - fprintf(stderr, "WARN! write_InvertedLists: unsupported invlist type, " - "saving null invlist\n"); - uint32_t h = fourcc ("il00"); - WRITE1 (h); - } -} - - -void write_ProductQuantizer (const ProductQuantizer*pq, const char *fname) { - FileIOWriter writer(fname); - write_ProductQuantizer (pq, &writer); -} - -static void write_HNSW (const HNSW *hnsw, IOWriter *f) { - - WRITEVECTOR (hnsw->assign_probas); - WRITEVECTOR (hnsw->cum_nneighbor_per_level); - WRITEVECTOR (hnsw->levels); - WRITEVECTOR (hnsw->offsets); - WRITEVECTOR (hnsw->neighbors); - - WRITE1 (hnsw->entry_point); - WRITE1 (hnsw->max_level); - WRITE1 (hnsw->efConstruction); - WRITE1 (hnsw->efSearch); - WRITE1 (hnsw->upper_beam); -} - -static void write_direct_map (const DirectMap *dm, IOWriter *f) { - char maintain_direct_map = (char)dm->type; // for backwards compatibility with bool - WRITE1 (maintain_direct_map); - WRITEVECTOR (dm->array); - if (dm->type == DirectMap::Hashtable) { - using idx_t = Index::idx_t; - std::vector> v; - const std::unordered_map & map = dm->hashtable; - v.resize (map.size()); - std::copy(map.begin(), map.end(), v.begin()); - WRITEVECTOR (v); - } -} - -static void write_ivf_header (const IndexIVF *ivf, IOWriter *f) { - write_index_header (ivf, f); - WRITE1 (ivf->nlist); - WRITE1 (ivf->nprobe); - write_index (ivf->quantizer, f); - write_direct_map (&ivf->direct_map, f); -} - -void write_index (const Index *idx, IOWriter *f) { - if (const IndexFlat * idxf = dynamic_cast (idx)) { - uint32_t h = fourcc ( - idxf->metric_type == METRIC_INNER_PRODUCT ? "IxFI" : - idxf->metric_type == METRIC_L2 ? "IxF2" : "IxFl"); - WRITE1 (h); - write_index_header (idx, f); - WRITEVECTOR (idxf->xb); - } else if(const IndexLSH * idxl = dynamic_cast (idx)) { - uint32_t h = fourcc ("IxHe"); - WRITE1 (h); - write_index_header (idx, f); - WRITE1 (idxl->nbits); - WRITE1 (idxl->rotate_data); - WRITE1 (idxl->train_thresholds); - WRITEVECTOR (idxl->thresholds); - WRITE1 (idxl->bytes_per_vec); - write_VectorTransform (&idxl->rrot, f); - WRITEVECTOR (idxl->codes); - } else if(const IndexPQ * idxp = dynamic_cast (idx)) { - uint32_t h = fourcc ("IxPq"); - WRITE1 (h); - write_index_header (idx, f); - write_ProductQuantizer (&idxp->pq, f); - WRITEVECTOR (idxp->codes); - // search params -- maybe not useful to store? - WRITE1 (idxp->search_type); - WRITE1 (idxp->encode_signs); - WRITE1 (idxp->polysemous_ht); - } else if(const Index2Layer * idxp = - dynamic_cast (idx)) { - uint32_t h = fourcc ("Ix2L"); - WRITE1 (h); - write_index_header (idx, f); - write_index (idxp->q1.quantizer, f); - WRITE1 (idxp->q1.nlist); - WRITE1 (idxp->q1.quantizer_trains_alone); - write_ProductQuantizer (&idxp->pq, f); - WRITE1 (idxp->code_size_1); - WRITE1 (idxp->code_size_2); - WRITE1 (idxp->code_size); - WRITEVECTOR (idxp->codes); - } else if(const IndexScalarQuantizer * idxs = - dynamic_cast (idx)) { - uint32_t h = fourcc ("IxSQ"); - WRITE1 (h); - write_index_header (idx, f); - write_ScalarQuantizer (&idxs->sq, f); - WRITEVECTOR (idxs->codes); - } else if(const IndexLattice * idxl = - dynamic_cast (idx)) { - uint32_t h = fourcc ("IxLa"); - WRITE1 (h); - WRITE1 (idxl->d); - WRITE1 (idxl->nsq); - WRITE1 (idxl->scale_nbit); - WRITE1 (idxl->zn_sphere_codec.r2); - write_index_header (idx, f); - WRITEVECTOR (idxl->trained); - } else if(const IndexIVFFlatDedup * ivfl = - dynamic_cast (idx)) { - uint32_t h = fourcc ("IwFd"); - WRITE1 (h); - write_ivf_header (ivfl, f); - { - std::vector tab (2 * ivfl->instances.size()); - long i = 0; - for (auto it = ivfl->instances.begin(); - it != ivfl->instances.end(); ++it) { - tab[i++] = it->first; - tab[i++] = it->second; - } - WRITEVECTOR (tab); - } - write_InvertedLists (ivfl->invlists, f); - } else if(const IndexIVFFlat * ivfl = - dynamic_cast (idx)) { - uint32_t h = fourcc ("IwFl"); - WRITE1 (h); - write_ivf_header (ivfl, f); - write_InvertedLists (ivfl->invlists, f); - } else if(const IndexIVFScalarQuantizer * ivsc = - dynamic_cast (idx)) { - uint32_t h = fourcc ("IwSq"); - WRITE1 (h); - write_ivf_header (ivsc, f); - write_ScalarQuantizer (&ivsc->sq, f); - WRITE1 (ivsc->code_size); - WRITE1 (ivsc->by_residual); - write_InvertedLists (ivsc->invlists, f); - } else if(const IndexIVFSQHybrid *ivfsqhbyrid = - dynamic_cast(idx)) { - uint32_t h = fourcc ("ISqH"); - WRITE1 (h); - write_ivf_header (ivfsqhbyrid, f); - write_ScalarQuantizer (&ivfsqhbyrid->sq, f); - WRITE1 (ivfsqhbyrid->code_size); - WRITE1 (ivfsqhbyrid->by_residual); - write_InvertedLists (ivfsqhbyrid->invlists, f); - } else if(const IndexIVFSpectralHash *ivsp = - dynamic_cast(idx)) { - uint32_t h = fourcc ("IwSh"); - WRITE1 (h); - write_ivf_header (ivsp, f); - write_VectorTransform (ivsp->vt, f); - WRITE1 (ivsp->nbit); - WRITE1 (ivsp->period); - WRITE1 (ivsp->threshold_type); - WRITEVECTOR (ivsp->trained); - write_InvertedLists (ivsp->invlists, f); - } else if(const IndexIVFPQ * ivpq = - dynamic_cast (idx)) { - const IndexIVFPQR * ivfpqr = dynamic_cast (idx); - - uint32_t h = fourcc (ivfpqr ? "IwQR" : "IwPQ"); - WRITE1 (h); - write_ivf_header (ivpq, f); - WRITE1 (ivpq->by_residual); - WRITE1 (ivpq->code_size); - write_ProductQuantizer (&ivpq->pq, f); - write_InvertedLists (ivpq->invlists, f); - if (ivfpqr) { - write_ProductQuantizer (&ivfpqr->refine_pq, f); - WRITEVECTOR (ivfpqr->refine_codes); - WRITE1 (ivfpqr->k_factor); - } - - } else if(const IndexPreTransform * ixpt = - dynamic_cast (idx)) { - uint32_t h = fourcc ("IxPT"); - WRITE1 (h); - write_index_header (ixpt, f); - int nt = ixpt->chain.size(); - WRITE1 (nt); - for (int i = 0; i < nt; i++) - write_VectorTransform (ixpt->chain[i], f); - write_index (ixpt->index, f); - } else if(const MultiIndexQuantizer * imiq = - dynamic_cast (idx)) { - uint32_t h = fourcc ("Imiq"); - WRITE1 (h); - write_index_header (imiq, f); - write_ProductQuantizer (&imiq->pq, f); - } else if(const IndexRefineFlat * idxrf = - dynamic_cast (idx)) { - uint32_t h = fourcc ("IxRF"); - WRITE1 (h); - write_index_header (idxrf, f); - write_index (idxrf->base_index, f); - write_index (&idxrf->refine_index, f); - WRITE1 (idxrf->k_factor); - } else if(const IndexIDMap * idxmap = - dynamic_cast (idx)) { - uint32_t h = - dynamic_cast (idx) ? fourcc ("IxM2") : - fourcc ("IxMp"); - // no need to store additional info for IndexIDMap2 - WRITE1 (h); - write_index_header (idxmap, f); - write_index (idxmap->index, f); - WRITEVECTOR (idxmap->id_map); - } else if(const IndexHNSW * idxhnsw = - dynamic_cast (idx)) { - uint32_t h = - dynamic_cast(idx) ? fourcc("IHNf") : - dynamic_cast(idx) ? fourcc("IHNp") : - dynamic_cast(idx) ? fourcc("IHNs") : - dynamic_cast(idx) ? fourcc("IHN2") : - 0; - FAISS_THROW_IF_NOT (h != 0); - WRITE1 (h); - write_index_header (idxhnsw, f); - write_HNSW (&idxhnsw->hnsw, f); - write_index (idxhnsw->storage, f); - } else { - FAISS_THROW_MSG ("don't know how to serialize this type of index"); - } -} - -void write_index (const Index *idx, FILE *f) { - FileIOWriter writer(f); - write_index (idx, &writer); -} - -void write_index (const Index *idx, const char *fname) { - FileIOWriter writer(fname); - write_index (idx, &writer); -} - -void write_VectorTransform (const VectorTransform *vt, const char *fname) { - FileIOWriter writer(fname); - write_VectorTransform (vt, &writer); -} - - -/************************************************************* - * Write binary indexes - **************************************************************/ - - -static void write_index_binary_header (const IndexBinary *idx, IOWriter *f) { - WRITE1 (idx->d); - WRITE1 (idx->code_size); - WRITE1 (idx->ntotal); - WRITE1 (idx->is_trained); - WRITE1 (idx->metric_type); -} - -static void write_binary_ivf_header (const IndexBinaryIVF *ivf, IOWriter *f) { - write_index_binary_header (ivf, f); - WRITE1 (ivf->nlist); - WRITE1 (ivf->nprobe); - write_index_binary (ivf->quantizer, f); - write_direct_map (&ivf->direct_map, f); -} - -static void write_binary_hash_invlists ( - const IndexBinaryHash::InvertedListMap &invlists, - int b, IOWriter *f) -{ - size_t sz = invlists.size(); - WRITE1 (sz); - size_t maxil = 0; - for (auto it = invlists.begin(); it != invlists.end(); ++it) { - if(it->second.ids.size() > maxil) { - maxil = it->second.ids.size(); - } - } - int il_nbit = 0; - while(maxil >= ((uint64_t)1 << il_nbit)) { - il_nbit++; - } - WRITE1(il_nbit); - - // first write sizes then data, may be useful if we want to - // memmap it at some point - - // buffer for bitstrings - std::vector buf (((b + il_nbit) * sz + 7) / 8); - BitstringWriter wr (buf.data(), buf.size()); - for (auto it = invlists.begin(); it != invlists.end(); ++it) { - wr.write (it->first, b); - wr.write (it->second.ids.size(), il_nbit); - } - WRITEVECTOR (buf); - - for (auto it = invlists.begin(); it != invlists.end(); ++it) { - WRITEVECTOR (it->second.ids); - WRITEVECTOR (it->second.vecs); - } -} - -static void write_binary_multi_hash_map( - const IndexBinaryMultiHash::Map &map, - int b, size_t ntotal, - IOWriter *f) -{ - int id_bits = 0; - while ((ntotal > ((Index::idx_t)1 << id_bits))) { - id_bits++; - } - WRITE1(id_bits); - size_t sz = map.size(); - WRITE1(sz); - size_t nbit = (b + id_bits) * sz + ntotal * id_bits; - std::vector buf((nbit + 7) / 8); - BitstringWriter wr (buf.data(), buf.size()); - for (auto it = map.begin(); it != map.end(); ++it) { - wr.write(it->first, b); - wr.write(it->second.size(), id_bits); - for (auto id : it->second) { - wr.write(id, id_bits); - } - } - WRITEVECTOR (buf); -} - -void write_index_binary (const IndexBinary *idx, IOWriter *f) { - if (const IndexBinaryFlat *idxf = - dynamic_cast (idx)) { - uint32_t h = fourcc ("IBxF"); - WRITE1 (h); - write_index_binary_header (idx, f); - WRITEVECTOR (idxf->xb); - } else if (const IndexBinaryIVF *ivf = - dynamic_cast (idx)) { - uint32_t h = fourcc ("IBwF"); - WRITE1 (h); - write_binary_ivf_header (ivf, f); - write_InvertedLists (ivf->invlists, f); - } else if(const IndexBinaryFromFloat * idxff = - dynamic_cast (idx)) { - uint32_t h = fourcc ("IBFf"); - WRITE1 (h); - write_index_binary_header (idxff, f); - write_index (idxff->index, f); - } else if (const IndexBinaryHNSW *idxhnsw = - dynamic_cast (idx)) { - uint32_t h = fourcc ("IBHf"); - WRITE1 (h); - write_index_binary_header (idxhnsw, f); - write_HNSW (&idxhnsw->hnsw, f); - write_index_binary (idxhnsw->storage, f); - } else if(const IndexBinaryIDMap * idxmap = - dynamic_cast (idx)) { - uint32_t h = - dynamic_cast (idx) ? fourcc ("IBM2") : - fourcc ("IBMp"); - // no need to store additional info for IndexIDMap2 - WRITE1 (h); - write_index_binary_header (idxmap, f); - write_index_binary (idxmap->index, f); - WRITEVECTOR (idxmap->id_map); - } else if (const IndexBinaryHash *idxh = - dynamic_cast (idx)) { - uint32_t h = fourcc ("IBHh"); - WRITE1 (h); - write_index_binary_header (idxh, f); - WRITE1 (idxh->b); - WRITE1 (idxh->nflip); - write_binary_hash_invlists(idxh->invlists, idxh->b, f); - } else if (const IndexBinaryMultiHash *idxmh = - dynamic_cast (idx)) { - uint32_t h = fourcc ("IBHm"); - WRITE1 (h); - write_index_binary_header (idxmh, f); - write_index_binary (idxmh->storage, f); - WRITE1 (idxmh->b); - WRITE1 (idxmh->nhash); - WRITE1 (idxmh->nflip); - for (int i = 0; i < idxmh->nhash; i++) { - write_binary_multi_hash_map( - idxmh->maps[i], idxmh->b, idxmh->ntotal, f); - } - } else { - FAISS_THROW_MSG ("don't know how to serialize this type of index"); - } -} - -void write_index_binary (const IndexBinary *idx, FILE *f) { - FileIOWriter writer(f); - write_index_binary(idx, &writer); -} - -void write_index_binary (const IndexBinary *idx, const char *fname) { - FileIOWriter writer(fname); - write_index_binary (idx, &writer); -} - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/io.cpp b/core/src/index/thirdparty/faiss/impl/io.cpp deleted file mode 100644 index 0954f3c1fc..0000000000 --- a/core/src/index/thirdparty/faiss/impl/io.cpp +++ /dev/null @@ -1,252 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include -#include - -#include -#include - - -namespace faiss { - - -/*********************************************************************** - * IO functions - ***********************************************************************/ - - -int IOReader::fileno () -{ - FAISS_THROW_MSG ("IOReader does not support memory mapping"); -} - -int IOWriter::fileno () -{ - FAISS_THROW_MSG ("IOWriter does not support memory mapping"); -} - -/*********************************************************************** - * IO Vector - ***********************************************************************/ - - -size_t VectorIOWriter::operator()( - const void *ptr, size_t size, size_t nitems) -{ - size_t bytes = size * nitems; - if (bytes > 0) { - size_t o = data.size(); - data.resize(o + bytes); - memcpy (&data[o], ptr, size * nitems); - } - return nitems; -} - -size_t VectorIOReader::operator()( - void *ptr, size_t size, size_t nitems) -{ - if (rp >= data.size()) return 0; - size_t nremain = (data.size() - rp) / size; - if (nremain < nitems) nitems = nremain; - if (size * nitems > 0) { - memcpy (ptr, &data[rp], size * nitems); - rp += size * nitems; - } - return nitems; -} - - - - -/*********************************************************************** - * IO File - ***********************************************************************/ - - - -FileIOReader::FileIOReader(FILE *rf): f(rf) {} - -FileIOReader::FileIOReader(const char * fname) -{ - name = fname; - f = fopen(fname, "rb"); - FAISS_THROW_IF_NOT_FMT (f, "could not open %s for reading: %s", - fname, strerror(errno)); - need_close = true; -} - -FileIOReader::~FileIOReader() { - if (need_close) { - int ret = fclose(f); - if (ret != 0) {// we cannot raise and exception in the destructor - fprintf(stderr, "file %s close error: %s", - name.c_str(), strerror(errno)); - } - } -} - -size_t FileIOReader::operator()(void *ptr, size_t size, size_t nitems) { - return fread(ptr, size, nitems, f); -} - -int FileIOReader::fileno() { - return ::fileno (f); -} - - -FileIOWriter::FileIOWriter(FILE *wf): f(wf) {} - -FileIOWriter::FileIOWriter(const char * fname) -{ - name = fname; - f = fopen(fname, "wb"); - FAISS_THROW_IF_NOT_FMT (f, "could not open %s for writing: %s", - fname, strerror(errno)); - need_close = true; -} - -FileIOWriter::~FileIOWriter() { - if (need_close) { - int ret = fclose(f); - if (ret != 0) { - // we cannot raise and exception in the destructor - fprintf(stderr, "file %s close error: %s", - name.c_str(), strerror(errno)); - } - } -} - -size_t FileIOWriter::operator()(const void *ptr, size_t size, size_t nitems) { - return fwrite(ptr, size, nitems, f); -} - -int FileIOWriter::fileno() { - return ::fileno (f); -} - -/*********************************************************************** - * IO buffer - ***********************************************************************/ - -BufferedIOReader::BufferedIOReader(IOReader *reader, size_t bsz, size_t totsz): - reader(reader), bsz(bsz), totsz(totsz), ofs(0), b0(0), b1(0), buffer(bsz) -{ -} - - -size_t BufferedIOReader::operator()(void *ptr, size_t unitsize, size_t nitems) -{ - size_t size = unitsize * nitems; - if (size == 0) return 0; - char * dst = (char*)ptr; - size_t nb; - - { // first copy available bytes - nb = std::min(b1 - b0, size); - memcpy (dst, buffer.data() + b0, nb); - b0 += nb; - dst += nb; - size -= nb; - } - - if (size > totsz - ofs) { - size = totsz - ofs; - } - // while we would like to have more data - while (size > 0) { - assert (b0 == b1); // buffer empty on input - // try to read from main reader - b0 = 0; - b1 = (*reader)(buffer.data(), 1, std::min(bsz, size)); - - if (b1 == 0) { - // no more bytes available - break; - } - ofs += b1; - - // copy remaining bytes - size_t nb2 = std::min(b1, size); - memcpy (dst, buffer.data(), nb2); - b0 = nb2; - nb += nb2; - dst += nb2; - size -= nb2; - } - return nb / unitsize; -} - - -BufferedIOWriter::BufferedIOWriter(IOWriter *writer, size_t bsz): - writer(writer), bsz(bsz), b0(0), buffer(bsz) -{ -} - -size_t BufferedIOWriter::operator()(const void *ptr, size_t unitsize, size_t nitems) -{ - size_t size = unitsize * nitems; - if (size == 0) return 0; - const char * src = (const char*)ptr; - size_t nb; - - { // copy as many bytes as possible to buffer - nb = std::min(bsz - b0, size); - memcpy (buffer.data() + b0, src, nb); - b0 += nb; - src += nb; - size -= nb; - } - while (size > 0) { - assert(b0 == bsz); - // now we need to flush to add more bytes - size_t ofs = 0; - do { - assert (ofs < 10000000); - size_t written = (*writer)(buffer.data() + ofs, 1, bsz - ofs); - FAISS_THROW_IF_NOT(written > 0); - ofs += written; - } while(ofs != bsz); - - // copy src to buffer - size_t nb1 = std::min(bsz, size); - memcpy (buffer.data(), src, nb1); - b0 = nb1; - nb += nb1; - src += nb1; - size -= nb1; - } - - return nb / unitsize; -} - -BufferedIOWriter::~BufferedIOWriter() -{ - size_t ofs = 0; - while(ofs != b0) { - printf("Destructor write %ld \n", b0 - ofs); - size_t written = (*writer)(buffer.data() + ofs, 1, b0 - ofs); - FAISS_THROW_IF_NOT(written > 0); - ofs += written; - } - -} - - - - - -uint32_t fourcc (const char sx[4]) { - assert(4 == strlen(sx)); - const unsigned char *x = (unsigned char*)sx; - return x[0] | x[1] << 8 | x[2] << 16 | x[3] << 24; -} - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/io.h b/core/src/index/thirdparty/faiss/impl/io.h deleted file mode 100644 index a3a565af26..0000000000 --- a/core/src/index/thirdparty/faiss/impl/io.h +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -/*********************************************************** - * Abstract I/O objects - * - * I/O is always sequential, seek does not need to be supported - * (indexes could be read or written to a pipe). - ***********************************************************/ - -#pragma once - -#include -#include -#include - -#include - -namespace faiss { - - -struct IOReader { - // name that can be used in error messages - std::string name; - - // fread - virtual size_t operator()( - void *ptr, size_t size, size_t nitems) = 0; - - // return a file number that can be memory-mapped - virtual int fileno (); - - virtual ~IOReader() {} -}; - -struct IOWriter { - // name that can be used in error messages - std::string name; - - // fwrite - virtual size_t operator()( - const void *ptr, size_t size, size_t nitems) = 0; - - // return a file number that can be memory-mapped - virtual int fileno (); - - virtual ~IOWriter() {} -}; - - -struct VectorIOReader:IOReader { - std::vector data; - size_t rp = 0; - size_t operator()(void *ptr, size_t size, size_t nitems) override; -}; - -struct VectorIOWriter:IOWriter { - std::vector data; - size_t operator()(const void *ptr, size_t size, size_t nitems) override; -}; - -struct FileIOReader: IOReader { - FILE *f = nullptr; - bool need_close = false; - - FileIOReader(FILE *rf); - - FileIOReader(const char * fname); - - ~FileIOReader() override; - - size_t operator()(void *ptr, size_t size, size_t nitems) override; - - int fileno() override; -}; - -struct FileIOWriter: IOWriter { - FILE *f = nullptr; - bool need_close = false; - - FileIOWriter(FILE *wf); - - FileIOWriter(const char * fname); - - ~FileIOWriter() override; - - size_t operator()(const void *ptr, size_t size, size_t nitems) override; - - int fileno() override; -}; - -/******************************************************* - * Buffered reader + writer - *******************************************************/ - - - -/** wraps an ioreader to make buffered reads to avoid too small reads */ -struct BufferedIOReader: IOReader { - - IOReader *reader; - size_t bsz, totsz, ofs; - size_t b0, b1; ///< range of available bytes in the buffer - std::vector buffer; - - BufferedIOReader(IOReader *reader, size_t bsz, - size_t totsz=(size_t)(-1)); - - size_t operator()(void *ptr, size_t size, size_t nitems) override; -}; - -struct BufferedIOWriter: IOWriter { - - IOWriter *writer; - size_t bsz, ofs; - size_t b0; ///< amount of data in buffer - std::vector buffer; - - BufferedIOWriter(IOWriter *writer, size_t bsz); - - size_t operator()(const void *ptr, size_t size, size_t nitems) override; - - // flushes - ~BufferedIOWriter(); -}; - -/// cast a 4-character string to a uint32_t that can be written and read easily -uint32_t fourcc (const char sx[4]); - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/lattice_Zn.cpp b/core/src/index/thirdparty/faiss/impl/lattice_Zn.cpp deleted file mode 100644 index 3e8458aa94..0000000000 --- a/core/src/index/thirdparty/faiss/impl/lattice_Zn.cpp +++ /dev/null @@ -1,713 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -namespace faiss { - -/******************************************** - * small utility functions - ********************************************/ - -namespace { - -inline float sqr(float x) { - return x * x; -} - - -typedef std::vector point_list_t; - -struct Comb { - std::vector tab; // Pascal's triangle - int nmax; - - explicit Comb(int nmax): nmax(nmax) { - tab.resize(nmax * nmax, 0); - tab[0] = 1; - for(int i = 1; i < nmax; i++) { - tab[i * nmax] = 1; - for(int j = 1; j <= i; j++) { - tab[i * nmax + j] = - tab[(i - 1) * nmax + j] + - tab[(i - 1) * nmax + (j - 1)]; - } - - } - } - - uint64_t operator()(int n, int p) const { - assert (n < nmax && p < nmax); - if (p > n) return 0; - return tab[n * nmax + p]; - } -}; - -Comb comb(100); - - - -// compute combinations of n integer values <= v that sum up to total (squared) -point_list_t sum_of_sq (float total, int v, int n, float add = 0) { - if (total < 0) { - return point_list_t(); - } else if (n == 1) { - while (sqr(v + add) > total) v--; - if (sqr(v + add) == total) { - return point_list_t(1, v + add); - } else { - return point_list_t(); - } - } else { - point_list_t res; - while (v >= 0) { - point_list_t sub_points = - sum_of_sq (total - sqr(v + add), v, n - 1, add); - for (size_t i = 0; i < sub_points.size(); i += n - 1) { - res.push_back (v + add); - for (int j = 0; j < n - 1; j++) { - res.push_back(sub_points[i + j]); - } - } - v--; - } - return res; - } -} - -int decode_comb_1 (uint64_t *n, int k1, int r) { - while (comb(r, k1) > *n) { - r--; - } - *n -= comb(r, k1); - return r; -} - -// optimized version for < 64 bits -long repeats_encode_64 ( - const std::vector & repeats, - int dim, const float *c) -{ - uint64_t coded = 0; - int nfree = dim; - uint64_t code = 0, shift = 1; - for (auto r = repeats.begin(); r != repeats.end(); ++r) { - int rank = 0, occ = 0; - uint64_t code_comb = 0; - uint64_t tosee = ~coded; - for(;;) { - // directly jump to next available slot. - int i = __builtin_ctzl(tosee); - tosee &= ~(1UL << i) ; - if (c[i] == r->val) { - code_comb += comb(rank, occ + 1); - occ++; - coded |= 1UL << i; - if (occ == r->n) break; - } - rank++; - } - uint64_t max_comb = comb(nfree, r->n); - code += shift * code_comb; - shift *= max_comb; - nfree -= r->n; - } - return code; -} - - -void repeats_decode_64( - const std::vector & repeats, - int dim, uint64_t code, float *c) -{ - uint64_t decoded = 0; - int nfree = dim; - for (auto r = repeats.begin(); r != repeats.end(); ++r) { - uint64_t max_comb = comb(nfree, r->n); - uint64_t code_comb = code % max_comb; - code /= max_comb; - - int occ = 0; - int rank = nfree; - int next_rank = decode_comb_1 (&code_comb, r->n, rank); - uint64_t tosee = ((1UL << dim) - 1) ^ decoded; - for(;;) { - int i = 63 - __builtin_clzl(tosee); - tosee &= ~(1UL << i); - rank--; - if (rank == next_rank) { - decoded |= 1UL << i; - c[i] = r->val; - occ++; - if (occ == r->n) break; - next_rank = decode_comb_1 ( - &code_comb, r->n - occ, next_rank); - } - } - nfree -= r->n; - } - -} - - - -} // anonymous namespace - -Repeats::Repeats (int dim, const float *c): dim(dim) -{ - for(int i = 0; i < dim; i++) { - int j = 0; - for(;;) { - if (j == repeats.size()) { - repeats.push_back(Repeat{c[i], 1}); - break; - } - if (repeats[j].val == c[i]) { - repeats[j].n++; - break; - } - j++; - } - } -} - - -long Repeats::count () const -{ - long accu = 1; - int remain = dim; - for (int i = 0; i < repeats.size(); i++) { - accu *= comb(remain, repeats[i].n); - remain -= repeats[i].n; - } - return accu; -} - - - -// version with a bool vector that works for > 64 dim -long Repeats::encode(const float *c) const -{ - if (dim < 64) { - return repeats_encode_64 (repeats, dim, c); - } - std::vector coded(dim, false); - int nfree = dim; - uint64_t code = 0, shift = 1; - for (auto r = repeats.begin(); r != repeats.end(); ++r) { - int rank = 0, occ = 0; - uint64_t code_comb = 0; - for (int i = 0; i < dim; i++) { - if (!coded[i]) { - if (c[i] == r->val) { - code_comb += comb(rank, occ + 1); - occ++; - coded[i] = true; - if (occ == r->n) break; - } - rank++; - } - } - uint64_t max_comb = comb(nfree, r->n); - code += shift * code_comb; - shift *= max_comb; - nfree -= r->n; - } - return code; -} - - - -void Repeats::decode(uint64_t code, float *c) const -{ - if (dim < 64) { - repeats_decode_64 (repeats, dim, code, c); - return; - } - - std::vector decoded(dim, false); - int nfree = dim; - for (auto r = repeats.begin(); r != repeats.end(); ++r) { - uint64_t max_comb = comb(nfree, r->n); - uint64_t code_comb = code % max_comb; - code /= max_comb; - - int occ = 0; - int rank = nfree; - int next_rank = decode_comb_1 (&code_comb, r->n, rank); - for (int i = dim - 1; i >= 0; i--) { - if (!decoded[i]) { - rank--; - if (rank == next_rank) { - decoded[i] = true; - c[i] = r->val; - occ++; - if (occ == r->n) break; - next_rank = decode_comb_1 ( - &code_comb, r->n - occ, next_rank); - } - } - } - nfree -= r->n; - } - -} - - - -/******************************************** - * EnumeratedVectors functions - ********************************************/ - - -void EnumeratedVectors::encode_multi(size_t n, const float *c, - uint64_t * codes) const -{ -#pragma omp parallel if (n > 1000) - { -#pragma omp for - for(int i = 0; i < n; i++) { - codes[i] = encode(c + i * dim); - } - } -} - - -void EnumeratedVectors::decode_multi(size_t n, const uint64_t * codes, - float *c) const -{ -#pragma omp parallel if (n > 1000) - { -#pragma omp for - for(int i = 0; i < n; i++) { - decode(codes[i], c + i * dim); - } - } -} - -void EnumeratedVectors::find_nn ( - size_t nc, const uint64_t * codes, - size_t nq, const float *xq, - long *labels, float *distances) -{ - for (long i = 0; i < nq; i++) { - distances[i] = -1e20; - labels[i] = -1; - } - - float c[dim]; - for(long i = 0; i < nc; i++) { - uint64_t code = codes[nc]; - decode(code, c); - for (long j = 0; j < nq; j++) { - const float *x = xq + j * dim; - float dis = fvec_inner_product(x, c, dim); - if (dis > distances[j]) { - distances[j] = dis; - labels[j] = i; - } - } - } - -} - - -/********************************************************** - * ZnSphereSearch - **********************************************************/ - - -ZnSphereSearch::ZnSphereSearch(int dim, int r2): dimS(dim), r2(r2) { - voc = sum_of_sq(r2, int(ceil(sqrt(r2)) + 1), dim); - natom = voc.size() / dim; -} - -float ZnSphereSearch::search(const float *x, float *c) const { - float tmp[dimS * 2]; - int tmp_int[dimS]; - return search(x, c, tmp, tmp_int); -} - -float ZnSphereSearch::search(const float *x, float *c, - float *tmp, // size 2 *dim - int *tmp_int, // size dim - int *ibest_out - ) const { - int dim = dimS; - assert (natom > 0); - int *o = tmp_int; - float *xabs = tmp; - float *xperm = tmp + dim; - - // argsort - for (int i = 0; i < dim; i++) { - o[i] = i; - xabs[i] = fabsf(x[i]); - } - std::sort(o, o + dim, [xabs](int a, int b) { - return xabs[a] > xabs[b]; - }); - for (int i = 0; i < dim; i++) { - xperm[i] = xabs[o[i]]; - } - // find best - int ibest = -1; - float dpbest = -100; - for (int i = 0; i < natom; i++) { - float dp = fvec_inner_product (voc.data() + i * dim, xperm, dim); - if (dp > dpbest) { - dpbest = dp; - ibest = i; - } - } - // revert sort - const float *cin = voc.data() + ibest * dim; - for (int i = 0; i < dim; i++) { - c[o[i]] = copysignf (cin[i], x[o[i]]); - } - if (ibest_out) { - *ibest_out = ibest; - } - return dpbest; -} - -void ZnSphereSearch::search_multi(int n, const float *x, - float *c_out, - float *dp_out) { -#pragma omp parallel if (n > 1000) - { -#pragma omp for - for(int i = 0; i < n; i++) { - dp_out[i] = search(x + i * dimS, c_out + i * dimS); - } - } -} - - -/********************************************************** - * ZnSphereCodec - **********************************************************/ - -ZnSphereCodec::ZnSphereCodec(int dim, int r2): - ZnSphereSearch(dim, r2), - EnumeratedVectors(dim) -{ - nv = 0; - for (int i = 0; i < natom; i++) { - Repeats repeats(dim, &voc[i * dim]); - CodeSegment cs(repeats); - cs.c0 = nv; - Repeat &br = repeats.repeats.back(); - cs.signbits = br.val == 0 ? dim - br.n : dim; - code_segments.push_back(cs); - nv += repeats.count() << cs.signbits; - } - - uint64_t nvx = nv; - code_size = 0; - while (nvx > 0) { - nvx >>= 8; - code_size++; - } -} - -uint64_t ZnSphereCodec::search_and_encode(const float *x) const { - float tmp[dim * 2]; - int tmp_int[dim]; - int ano; // atom number - float c[dim]; - search(x, c, tmp, tmp_int, &ano); - uint64_t signs = 0; - float cabs[dim]; - int nnz = 0; - for (int i = 0; i < dim; i++) { - cabs[i] = fabs(c[i]); - if (c[i] != 0) { - if (c[i] < 0) { - signs |= 1UL << nnz; - } - nnz ++; - } - } - const CodeSegment &cs = code_segments[ano]; - assert(nnz == cs.signbits); - uint64_t code = cs.c0 + signs; - code += cs.encode(cabs) << cs.signbits; - return code; -} - -uint64_t ZnSphereCodec::encode(const float *x) const -{ - return search_and_encode(x); -} - - -void ZnSphereCodec::decode(uint64_t code, float *c) const { - int i0 = 0, i1 = natom; - while (i0 + 1 < i1) { - int imed = (i0 + i1) / 2; - if (code_segments[imed].c0 <= code) i0 = imed; - else i1 = imed; - } - const CodeSegment &cs = code_segments[i0]; - code -= cs.c0; - uint64_t signs = code; - code >>= cs.signbits; - cs.decode(code, c); - - int nnz = 0; - for (int i = 0; i < dim; i++) { - if (c[i] != 0) { - if (signs & (1UL << nnz)) { - c[i] = -c[i]; - } - nnz ++; - } - } -} - - -/************************************************************** - * ZnSphereCodecRec - **************************************************************/ - -uint64_t ZnSphereCodecRec::get_nv(int ld, int r2a) const -{ - return all_nv[ld * (r2 + 1) + r2a]; -} - - -uint64_t ZnSphereCodecRec::get_nv_cum(int ld, int r2t, int r2a) const -{ - return all_nv_cum[(ld * (r2 + 1) + r2t) * (r2 + 1) + r2a]; -} - -void ZnSphereCodecRec::set_nv_cum(int ld, int r2t, int r2a, uint64_t cum) -{ - all_nv_cum[(ld * (r2 + 1) + r2t) * (r2 + 1) + r2a] = cum; -} - - -ZnSphereCodecRec::ZnSphereCodecRec(int dim, int r2): - EnumeratedVectors(dim), r2(r2) -{ - log2_dim = 0; - while (dim > (1 << log2_dim)) { - log2_dim++; - } - assert(dim == (1 << log2_dim) || - !"dimension must be a power of 2"); - - all_nv.resize((log2_dim + 1) * (r2 + 1)); - all_nv_cum.resize((log2_dim + 1) * (r2 + 1) * (r2 + 1)); - - for (int r2a = 0; r2a <= r2; r2a++) { - int r = int(sqrt(r2a)); - if (r * r == r2a) { - all_nv[r2a] = r == 0 ? 1 : 2; - } else { - all_nv[r2a] = 0; - } - } - - for (int ld = 1; ld <= log2_dim; ld++) { - - for (int r2sub = 0; r2sub <= r2; r2sub++) { - uint64_t nv = 0; - for (int r2a = 0; r2a <= r2sub; r2a++) { - int r2b = r2sub - r2a; - set_nv_cum(ld, r2sub, r2a, nv); - nv += get_nv(ld - 1, r2a) * get_nv(ld - 1, r2b); - } - all_nv[ld * (r2 + 1) + r2sub] = nv; - } - } - nv = get_nv(log2_dim, r2); - - uint64_t nvx = nv; - code_size = 0; - while (nvx > 0) { - nvx >>= 8; - code_size++; - } - - int cache_level = std::min(3, log2_dim - 1); - decode_cache_ld = 0; - assert(cache_level <= log2_dim); - decode_cache.resize((r2 + 1)); - - for (int r2sub = 0; r2sub <= r2; r2sub++) { - int ld = cache_level; - uint64_t nvi = get_nv(ld, r2sub); - std::vector &cache = decode_cache[r2sub]; - int dimsub = (1 << cache_level); - cache.resize (nvi * dimsub); - float c[dim]; - uint64_t code0 = get_nv_cum(cache_level + 1, r2, - r2 - r2sub); - for (int i = 0; i < nvi; i++) { - decode(i + code0, c); - memcpy(&cache[i * dimsub], c + dim - dimsub, - dimsub * sizeof(*c)); - } - } - decode_cache_ld = cache_level; -} - -uint64_t ZnSphereCodecRec::encode(const float *c) const -{ - return encode_centroid(c); -} - - - -uint64_t ZnSphereCodecRec::encode_centroid(const float *c) const -{ - uint64_t codes[dim]; - int norm2s[dim]; - for(int i = 0; i < dim; i++) { - if (c[i] == 0) { - codes[i] = 0; - norm2s[i] = 0; - } else { - int r2i = int(c[i] * c[i]); - norm2s[i] = r2i; - codes[i] = c[i] >= 0 ? 0 : 1; - } - } - int dim2 = dim / 2; - for(int ld = 1; ld <= log2_dim; ld++) { - for (int i = 0; i < dim2; i++) { - int r2a = norm2s[2 * i]; - int r2b = norm2s[2 * i + 1]; - - uint64_t code_a = codes[2 * i]; - uint64_t code_b = codes[2 * i + 1]; - - codes[i] = - get_nv_cum(ld, r2a + r2b, r2a) + - code_a * get_nv(ld - 1, r2b) + - code_b; - norm2s[i] = r2a + r2b; - } - dim2 /= 2; - } - return codes[0]; -} - - - -void ZnSphereCodecRec::decode(uint64_t code, float *c) const -{ - uint64_t codes[dim]; - int norm2s[dim]; - codes[0] = code; - norm2s[0] = r2; - - int dim2 = 1; - for(int ld = log2_dim; ld > decode_cache_ld; ld--) { - for (int i = dim2 - 1; i >= 0; i--) { - int r2sub = norm2s[i]; - int i0 = 0, i1 = r2sub + 1; - uint64_t codei = codes[i]; - const uint64_t *cum = - &all_nv_cum[(ld * (r2 + 1) + r2sub) * (r2 + 1)]; - while (i1 > i0 + 1) { - int imed = (i0 + i1) / 2; - if (cum[imed] <= codei) - i0 = imed; - else - i1 = imed; - } - int r2a = i0, r2b = r2sub - i0; - codei -= cum[r2a]; - norm2s[2 * i] = r2a; - norm2s[2 * i + 1] = r2b; - - uint64_t code_a = codei / get_nv(ld - 1, r2b); - uint64_t code_b = codei % get_nv(ld - 1, r2b); - - codes[2 * i] = code_a; - codes[2 * i + 1] = code_b; - - } - dim2 *= 2; - } - - if (decode_cache_ld == 0) { - for(int i = 0; i < dim; i++) { - if (norm2s[i] == 0) { - c[i] = 0; - } else { - float r = sqrt(norm2s[i]); - assert(r * r == norm2s[i]); - c[i] = codes[i] == 0 ? r : -r; - } - } - } else { - int subdim = 1 << decode_cache_ld; - assert ((dim2 * subdim) == dim); - - for(int i = 0; i < dim2; i++) { - - const std::vector & cache = - decode_cache[norm2s[i]]; - assert(codes[i] < cache.size()); - memcpy(c + i * subdim, - &cache[codes[i] * subdim], - sizeof(*c)* subdim); - } - } -} - -// if not use_rec, instanciate an arbitrary harmless znc_rec -ZnSphereCodecAlt::ZnSphereCodecAlt (int dim, int r2): - ZnSphereCodec (dim, r2), - use_rec ((dim & (dim - 1)) == 0), - znc_rec (use_rec ? dim : 8, - use_rec ? r2 : 14) -{} - -uint64_t ZnSphereCodecAlt::encode(const float *x) const -{ - if (!use_rec) { - // it's ok if the vector is not normalized - return ZnSphereCodec::encode(x); - } else { - // find nearest centroid - std::vector centroid(dim); - search (x, centroid.data()); - return znc_rec.encode(centroid.data()); - } -} - -void ZnSphereCodecAlt::decode(uint64_t code, float *c) const -{ - if (!use_rec) { - ZnSphereCodec::decode (code, c); - } else { - znc_rec.decode (code, c); - } -} - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/impl/lattice_Zn.h b/core/src/index/thirdparty/faiss/impl/lattice_Zn.h deleted file mode 100644 index f346d1e4c5..0000000000 --- a/core/src/index/thirdparty/faiss/impl/lattice_Zn.h +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- -#ifndef FAISS_LATTICE_ZN_H -#define FAISS_LATTICE_ZN_H - -#include -#include -#include - -namespace faiss { - -/** returns the nearest vertex in the sphere to a query. Returns only - * the coordinates, not an id. - * - * Algorithm: all points are derived from a one atom vector up to a - * permutation and sign changes. The search function finds the most - * appropriate atom and transformation. - */ -struct ZnSphereSearch { - int dimS, r2; - int natom; - - /// size dim * ntatom - std::vector voc; - - ZnSphereSearch(int dim, int r2); - - /// find nearest centroid. x does not need to be normalized - float search(const float *x, float *c) const; - - /// full call. Requires externally-allocated temp space - float search(const float *x, float *c, - float *tmp, // size 2 *dim - int *tmp_int, // size dim - int *ibest_out = nullptr - ) const; - - // multi-threaded - void search_multi(int n, const float *x, - float *c_out, - float *dp_out); - -}; - - -/*************************************************************************** - * Support ids as well. - * - * Limitations: ids are limited to 64 bit - ***************************************************************************/ - -struct EnumeratedVectors { - /// size of the collection - uint64_t nv; - int dim; - - explicit EnumeratedVectors(int dim): nv(0), dim(dim) {} - - /// encode a vector from a collection - virtual uint64_t encode(const float *x) const = 0; - - /// decode it - virtual void decode(uint64_t code, float *c) const = 0; - - // call encode on nc vectors - void encode_multi (size_t nc, const float *c, - uint64_t * codes) const; - - // call decode on nc codes - void decode_multi (size_t nc, const uint64_t * codes, - float *c) const; - - // find the nearest neighbor of each xq - // (decodes and computes distances) - void find_nn (size_t n, const uint64_t * codes, - size_t nq, const float *xq, - long *idx, float *dis); - - virtual ~EnumeratedVectors() {} - -}; - -struct Repeat { - float val; - int n; -}; - -/** Repeats: used to encode a vector that has n occurrences of - * val. Encodes the signs and permutation of the vector. Useful for - * atoms. - */ -struct Repeats { - int dim; - std::vector repeats; - - // initialize from a template of the atom. - Repeats(int dim = 0, const float *c = nullptr); - - // count number of possible codes for this atom - long count() const; - - long encode(const float *c) const; - - void decode(uint64_t code, float *c) const; -}; - - -/** codec that can return ids for the encoded vectors - * - * uses the ZnSphereSearch to encode the vector by encoding the - * permutation and signs. Depends on ZnSphereSearch because it uses - * the atom numbers */ -struct ZnSphereCodec: ZnSphereSearch, EnumeratedVectors { - - struct CodeSegment:Repeats { - explicit CodeSegment(const Repeats & r): Repeats(r) {} - uint64_t c0; // first code assigned to segment - int signbits; - }; - - std::vector code_segments; - uint64_t nv; - size_t code_size; - - ZnSphereCodec(int dim, int r2); - - uint64_t search_and_encode(const float *x) const; - - void decode(uint64_t code, float *c) const override; - - /// takes vectors that do not need to be centroids - uint64_t encode(const float *x) const override; - -}; - -/** recursive sphere codec - * - * Uses a recursive decomposition on the dimensions to encode - * centroids found by the ZnSphereSearch. The codes are *not* - * compatible with the ones of ZnSpehreCodec - */ -struct ZnSphereCodecRec: EnumeratedVectors { - - int r2; - - int log2_dim; - int code_size; - - ZnSphereCodecRec(int dim, int r2); - - uint64_t encode_centroid(const float *c) const; - - void decode(uint64_t code, float *c) const override; - - /// vectors need to be centroids (does not work on arbitrary - /// vectors) - uint64_t encode(const float *x) const override; - - std::vector all_nv; - std::vector all_nv_cum; - - int decode_cache_ld; - std::vector > decode_cache; - - // nb of vectors in the sphere in dim 2^ld with r2 radius - uint64_t get_nv(int ld, int r2a) const; - - // cumulative version - uint64_t get_nv_cum(int ld, int r2t, int r2a) const; - void set_nv_cum(int ld, int r2t, int r2a, uint64_t v); - -}; - - -/** Codec that uses the recursive codec if dim is a power of 2 and - * the regular one otherwise */ -struct ZnSphereCodecAlt: ZnSphereCodec { - bool use_rec; - ZnSphereCodecRec znc_rec; - - ZnSphereCodecAlt (int dim, int r2); - - uint64_t encode(const float *x) const override; - - void decode(uint64_t code, float *c) const override; - -}; - - -}; - - -#endif diff --git a/core/src/index/thirdparty/faiss/index_factory.cpp b/core/src/index/thirdparty/faiss/index_factory.cpp deleted file mode 100644 index 456b8e5356..0000000000 --- a/core/src/index/thirdparty/faiss/index_factory.cpp +++ /dev/null @@ -1,425 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -/* - * implementation of Hyper-parameter auto-tuning - */ - -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace faiss { - - -/*************************************************************** - * index_factory - ***************************************************************/ - -namespace { - -struct VTChain { - std::vector chain; - ~VTChain () { - for (int i = 0; i < chain.size(); i++) { - delete chain[i]; - } - } -}; - - -/// what kind of training does this coarse quantizer require? -char get_trains_alone(const Index *coarse_quantizer) { - return - dynamic_cast(coarse_quantizer) ? 1 : - dynamic_cast(coarse_quantizer) ? 2 : - 0; -} - - -} - -Index *index_factory (int d, const char *description_in, MetricType metric) -{ - FAISS_THROW_IF_NOT(metric == METRIC_L2 || - metric == METRIC_INNER_PRODUCT); - VTChain vts; - Index *coarse_quantizer = nullptr; - Index *index = nullptr; - bool add_idmap = false; - bool make_IndexRefineFlat = false; - - ScopeDeleter1 del_coarse_quantizer, del_index; - - char description[strlen(description_in) + 1]; - char *ptr; - memcpy (description, description_in, strlen(description_in) + 1); - - int64_t ncentroids = -1; - bool use_2layer = false; - - for (char *tok = strtok_r (description, " ,", &ptr); - tok; - tok = strtok_r (nullptr, " ,", &ptr)) { - int d_out, opq_M, nbit, M, M2, pq_m, ncent, r2; - std::string stok(tok); - nbit = 8; - - // to avoid mem leaks with exceptions: - // do all tests before any instanciation - - VectorTransform *vt_1 = nullptr; - Index *coarse_quantizer_1 = nullptr; - Index *index_1 = nullptr; - - // VectorTransforms - if (sscanf (tok, "PCA%d", &d_out) == 1) { - vt_1 = new PCAMatrix (d, d_out); - d = d_out; - } else if (sscanf (tok, "PCAR%d", &d_out) == 1) { - vt_1 = new PCAMatrix (d, d_out, 0, true); - d = d_out; - } else if (sscanf (tok, "RR%d", &d_out) == 1) { - vt_1 = new RandomRotationMatrix (d, d_out); - d = d_out; - } else if (sscanf (tok, "PCAW%d", &d_out) == 1) { - vt_1 = new PCAMatrix (d, d_out, -0.5, false); - d = d_out; - } else if (sscanf (tok, "PCAWR%d", &d_out) == 1) { - vt_1 = new PCAMatrix (d, d_out, -0.5, true); - d = d_out; - } else if (sscanf (tok, "OPQ%d_%d", &opq_M, &d_out) == 2) { - vt_1 = new OPQMatrix (d, opq_M, d_out); - d = d_out; - } else if (sscanf (tok, "OPQ%d", &opq_M) == 1) { - vt_1 = new OPQMatrix (d, opq_M); - } else if (sscanf (tok, "ITQ%d", &d_out) == 1) { - vt_1 = new ITQTransform (d, d_out, true); - d = d_out; - } else if (stok == "ITQ") { - vt_1 = new ITQTransform (d, d, false); - } else if (sscanf (tok, "Pad%d", &d_out) == 1) { - if (d_out > d) { - vt_1 = new RemapDimensionsTransform (d, d_out, false); - d = d_out; - } - } else if (stok == "L2norm") { - vt_1 = new NormalizationTransform (d, 2.0); - - // coarse quantizers - } else if (!coarse_quantizer && - sscanf (tok, "IVF%ld_HNSW%d", &ncentroids, &M) == 2) { - FAISS_THROW_IF_NOT (metric == METRIC_L2); - coarse_quantizer_1 = new IndexHNSWFlat (d, M); - - } else if (!coarse_quantizer && - sscanf (tok, "IVF%ld", &ncentroids) == 1) { - if (metric == METRIC_L2) { - coarse_quantizer_1 = new IndexFlatL2 (d); - } else { - coarse_quantizer_1 = new IndexFlatIP (d); - } - } else if (!coarse_quantizer && sscanf (tok, "IMI2x%d", &nbit) == 1) { - FAISS_THROW_IF_NOT_MSG (metric == METRIC_L2, - "MultiIndex not implemented for inner prod search"); - coarse_quantizer_1 = new MultiIndexQuantizer (d, 2, nbit); - ncentroids = 1 << (2 * nbit); - - } else if (!coarse_quantizer && - sscanf (tok, "Residual%dx%d", &M, &nbit) == 2) { - FAISS_THROW_IF_NOT_MSG (metric == METRIC_L2, - "MultiIndex not implemented for inner prod search"); - coarse_quantizer_1 = new MultiIndexQuantizer (d, M, nbit); - ncentroids = int64_t(1) << (M * nbit); - use_2layer = true; - - } else if (!coarse_quantizer && - sscanf (tok, "Residual%ld", &ncentroids) == 1) { - coarse_quantizer_1 = new IndexFlatL2 (d); - use_2layer = true; - - } else if (stok == "IDMap") { - add_idmap = true; - - // IVFs - } else if (!index && (stok == "Flat" || stok == "FlatDedup")) { - if (coarse_quantizer) { - // if there was an IVF in front, then it is an IVFFlat - IndexIVF *index_ivf = stok == "Flat" ? - new IndexIVFFlat ( - coarse_quantizer, d, ncentroids, metric) : - new IndexIVFFlatDedup ( - coarse_quantizer, d, ncentroids, metric); - index_ivf->quantizer_trains_alone = - get_trains_alone (coarse_quantizer); - index_ivf->cp.spherical = metric == METRIC_INNER_PRODUCT; - del_coarse_quantizer.release (); - index_ivf->own_fields = true; - index_1 = index_ivf; - } else { - FAISS_THROW_IF_NOT_MSG (stok != "FlatDedup", - "dedup supported only for IVFFlat"); - index_1 = new IndexFlat (d, metric); - } - } else if (!index && (stok == "SQ8" || stok == "SQ4" || stok == "SQ6" || - stok == "SQfp16")) { - QuantizerType qt = - stok == "SQ8" ? QuantizerType::QT_8bit : - stok == "SQ6" ? QuantizerType::QT_6bit : - stok == "SQ4" ? QuantizerType::QT_4bit : - stok == "SQfp16" ? QuantizerType::QT_fp16 : - QuantizerType::QT_4bit; - if (coarse_quantizer) { - FAISS_THROW_IF_NOT (!use_2layer); - IndexIVFScalarQuantizer *index_ivf = - new IndexIVFScalarQuantizer ( - coarse_quantizer, d, ncentroids, qt, metric); - index_ivf->quantizer_trains_alone = - get_trains_alone (coarse_quantizer); - del_coarse_quantizer.release (); - index_ivf->own_fields = true; - index_1 = index_ivf; - } else { - index_1 = new IndexScalarQuantizer (d, qt, metric); - } - } else if (!index && (stok == "SQ8Hybrid" || stok == "SQ4Hybrid" || stok == "SQ6Hybrid" || - stok == "SQfp16Hybrid")) { - QuantizerType qt = - stok == "SQ8Hybrid" ? QuantizerType::QT_8bit : - stok == "SQ6Hybrid" ? QuantizerType::QT_6bit : - stok == "SQ4Hybrid" ? QuantizerType::QT_4bit : - stok == "SQfp16Hybrid" ? QuantizerType::QT_fp16 : - QuantizerType::QT_4bit; - FAISS_THROW_IF_NOT_MSG(coarse_quantizer, - "SQ Hybrid only with an IVF"); - FAISS_THROW_IF_NOT (!use_2layer); - IndexIVFSQHybrid *index_ivf = - new IndexIVFSQHybrid ( - coarse_quantizer, d, ncentroids, qt, metric); - index_ivf->quantizer_trains_alone = - get_trains_alone (coarse_quantizer); - del_coarse_quantizer.release (); - index_ivf->own_fields = true; - index_1 = index_ivf; - } else if (!index && sscanf (tok, "PQ%d+%d", &M, &M2) == 2) { - FAISS_THROW_IF_NOT_MSG(coarse_quantizer, - "PQ with + works only with an IVF"); - FAISS_THROW_IF_NOT_MSG(metric == METRIC_L2, - "IVFPQR not implemented for inner product search"); - IndexIVFPQR *index_ivf = new IndexIVFPQR ( - coarse_quantizer, d, ncentroids, M, 8, M2, 8); - index_ivf->quantizer_trains_alone = - get_trains_alone (coarse_quantizer); - del_coarse_quantizer.release (); - index_ivf->own_fields = true; - index_1 = index_ivf; - } else if (!index && (sscanf (tok, "PQ%dx%d", &M, &nbit) == 2 || - sscanf (tok, "PQ%d", &M) == 1 || - sscanf (tok, "PQ%dnp", &M) == 1)) { - bool do_polysemous_training = stok.find("np") == std::string::npos; - if (coarse_quantizer) { - if (!use_2layer) { - IndexIVFPQ *index_ivf = new IndexIVFPQ ( - coarse_quantizer, d, ncentroids, M, nbit); - index_ivf->quantizer_trains_alone = - get_trains_alone (coarse_quantizer); - index_ivf->metric_type = metric; - index_ivf->cp.spherical = metric == METRIC_INNER_PRODUCT; - del_coarse_quantizer.release (); - index_ivf->own_fields = true; - index_ivf->do_polysemous_training = do_polysemous_training; - index_1 = index_ivf; - } else { - Index2Layer *index_2l = new Index2Layer - (coarse_quantizer, ncentroids, M, nbit); - index_2l->q1.quantizer_trains_alone = - get_trains_alone (coarse_quantizer); - index_2l->q1.own_fields = true; - index_1 = index_2l; - } - } else { - IndexPQ *index_pq = new IndexPQ (d, M, nbit, metric); - index_pq->do_polysemous_training = do_polysemous_training; - index_1 = index_pq; - } - } else if (!index && - sscanf (tok, "HNSW%d_%d+PQ%d", &M, &ncent, &pq_m) == 3) { - Index * quant = new IndexFlatL2 (d); - IndexHNSW2Level * hidx2l = new IndexHNSW2Level (quant, ncent, pq_m, M); - Index2Layer * idx2l = dynamic_cast(hidx2l->storage); - idx2l->q1.own_fields = true; - index_1 = hidx2l; - } else if (!index && - sscanf (tok, "HNSW%d_2x%d+PQ%d", &M, &nbit, &pq_m) == 3) { - Index * quant = new MultiIndexQuantizer (d, 2, nbit); - IndexHNSW2Level * hidx2l = - new IndexHNSW2Level (quant, 1 << (2 * nbit), pq_m, M); - Index2Layer * idx2l = dynamic_cast(hidx2l->storage); - idx2l->q1.own_fields = true; - idx2l->q1.quantizer_trains_alone = 1; - index_1 = hidx2l; - } else if (!index && - sscanf (tok, "HNSW%d_PQ%d", &M, &pq_m) == 2) { - index_1 = new IndexHNSWPQ (d, pq_m, M); - } else if (!index && - sscanf (tok, "HNSW%d", &M) == 1) { - index_1 = new IndexHNSWFlat (d, M); - } else if (!index && - sscanf (tok, "HNSW%d_SQ%d", &M, &pq_m) == 2 && - pq_m == 8) { - index_1 = new IndexHNSWSQ (d, QuantizerType::QT_8bit, M); - } else if (!index && (stok == "LSH" || stok == "LSHr" || - stok == "LSHrt" || stok == "LSHt")) { - bool rotate_data = strstr(tok, "r") != nullptr; - bool train_thresholds = strstr(tok, "t") != nullptr; - index_1 = new IndexLSH (d, d, rotate_data, train_thresholds); - } else if (!index && - sscanf (tok, "ZnLattice%dx%d_%d", &M, &r2, &nbit) == 3) { - FAISS_THROW_IF_NOT(!coarse_quantizer); - index_1 = new IndexLattice(d, M, nbit, r2); - } else if (stok == "RFlat") { - make_IndexRefineFlat = true; - } else { - FAISS_THROW_FMT( "could not parse token \"%s\" in %s\n", - tok, description_in); - } - - if (index_1 && add_idmap) { - IndexIDMap *idmap = new IndexIDMap(index_1); - del_index.set (idmap); - idmap->own_fields = true; - index_1 = idmap; - add_idmap = false; - } - - if (vt_1) { - vts.chain.push_back (vt_1); - } - - if (coarse_quantizer_1) { - coarse_quantizer = coarse_quantizer_1; - del_coarse_quantizer.set (coarse_quantizer); - } - - if (index_1) { - index = index_1; - del_index.set (index); - } - } - - FAISS_THROW_IF_NOT_FMT(index, "description %s did not generate an index", - description_in); - - // nothing can go wrong now - del_index.release (); - del_coarse_quantizer.release (); - - if (add_idmap) { - fprintf(stderr, "index_factory: WARNING: " - "IDMap option not used\n"); - } - - if (vts.chain.size() > 0) { - IndexPreTransform *index_pt = new IndexPreTransform (index); - index_pt->own_fields = true; - // add from back - while (vts.chain.size() > 0) { - index_pt->prepend_transform (vts.chain.back ()); - vts.chain.pop_back (); - } - index = index_pt; - } - - if (make_IndexRefineFlat) { - IndexRefineFlat *index_rf = new IndexRefineFlat (index); - index_rf->own_fields = true; - index = index_rf; - } - - return index; -} - -IndexBinary *index_binary_factory(int d, const char *description, MetricType metric = METRIC_L2) -{ - IndexBinary *index = nullptr; - - int ncentroids = -1; - int M; - - ScopeDeleter1 del_index; - if (sscanf(description, "BIVF%d_HNSW%d", &ncentroids, &M) == 2) { - IndexBinaryIVF *index_ivf = new IndexBinaryIVF( - new IndexBinaryHNSW(d, M), d, ncentroids - ); - index_ivf->own_fields = true; - index = index_ivf; - - } else if (sscanf(description, "BIVF%d", &ncentroids) == 1) { - IndexBinaryIVF *index_ivf = new IndexBinaryIVF( - new IndexBinaryFlat(d), d, ncentroids - ); - index_ivf->own_fields = true; - index = index_ivf; - - } else if (sscanf(description, "BHNSW%d", &M) == 1) { - IndexBinaryHNSW *index_hnsw = new IndexBinaryHNSW(d, M); - index = index_hnsw; - - } else if (std::string(description) == "BFlat") { - IndexBinary* index_x = new IndexBinaryFlat(d, metric); - - { - IndexBinaryIDMap *idmap = new IndexBinaryIDMap(index_x); - del_index.set (idmap); - idmap->own_fields = true; - index_x = idmap; - } - - if (index_x) { - index = index_x; - del_index.set(index); - } - - } else { - FAISS_THROW_IF_NOT_FMT(index, "description %s did not generate an index", - description); - } - - del_index.release(); - - return index; -} - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/index_factory.h b/core/src/index/thirdparty/faiss/index_factory.h deleted file mode 100644 index ce62734298..0000000000 --- a/core/src/index/thirdparty/faiss/index_factory.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#pragma once - -#include -#include - -namespace faiss { - -/** Build and index with the sequence of processing steps described in - * the string. */ -Index *index_factory (int d, const char *description, - MetricType metric = METRIC_L2); - -IndexBinary *index_binary_factory (int d, const char *description, MetricType metric); - -} diff --git a/core/src/index/thirdparty/faiss/index_io.h b/core/src/index/thirdparty/faiss/index_io.h deleted file mode 100644 index 5aef62c87b..0000000000 --- a/core/src/index/thirdparty/faiss/index_io.h +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -// I/O code for indexes - -#ifndef FAISS_INDEX_IO_H -#define FAISS_INDEX_IO_H - - -#include - -/** I/O functions can read/write to a filename, a file handle or to an - * object that abstracts the medium. - * - * The read functions return objects that should be deallocated with - * delete. All references within these objectes are owned by the - * object. - */ - -namespace faiss { - -struct Index; -struct IndexBinary; -struct VectorTransform; -struct ProductQuantizer; -struct IOReader; -struct IOWriter; -struct InvertedLists; - -void write_index (const Index *idx, const char *fname); -void write_index (const Index *idx, FILE *f); -void write_index (const Index *idx, IOWriter *writer); - -void write_index_binary (const IndexBinary *idx, const char *fname); -void write_index_binary (const IndexBinary *idx, FILE *f); -void write_index_binary (const IndexBinary *idx, IOWriter *writer); - -// The read_index flags are implemented only for a subset of index types. -const int IO_FLAG_MMAP = 1; // try to memmap if possible -const int IO_FLAG_READ_ONLY = 2; -// strip directory component from ondisk filename, and assume it's in -// the same directory as the index file -const int IO_FLAG_ONDISK_SAME_DIR = 4; - -Index *read_index (const char *fname, int io_flags = 0); -Index *read_index (FILE * f, int io_flags = 0); -Index *read_index (IOReader *reader, int io_flags = 0); - -IndexBinary *read_index_binary (const char *fname, int io_flags = 0); -IndexBinary *read_index_binary (FILE * f, int io_flags = 0); -IndexBinary *read_index_binary (IOReader *reader, int io_flags = 0); - -void write_VectorTransform (const VectorTransform *vt, const char *fname); -VectorTransform *read_VectorTransform (const char *fname); - -ProductQuantizer * read_ProductQuantizer (const char*fname); -ProductQuantizer * read_ProductQuantizer (IOReader *reader); - -void write_ProductQuantizer (const ProductQuantizer*pq, const char *fname); -void write_ProductQuantizer (const ProductQuantizer*pq, IOWriter *f); - -void write_InvertedLists (const InvertedLists *ils, IOWriter *f); -InvertedLists *read_InvertedLists (IOReader *reader, int io_flags = 0); - - -} // namespace faiss - - -#endif diff --git a/core/src/index/thirdparty/faiss/makefile.inc.in b/core/src/index/thirdparty/faiss/makefile.inc.in deleted file mode 100644 index 244f94a17c..0000000000 --- a/core/src/index/thirdparty/faiss/makefile.inc.in +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -CXX = @CXX@ -CXXCPP = @CXXCPP@ -CPPFLAGS = -DFINTEGER=int @CPPFLAGS@ @OPENMP_CXXFLAGS@ @NVCC_CPPFLAGS@ -CXXFLAGS = -fPIC @ARCH_CXXFLAGS@ -Wno-sign-compare @CXXFLAGS@ -CPUFLAGS = @ARCH_CPUFLAGS@ -LDFLAGS = @OPENMP_LDFLAGS@ @LDFLAGS@ @NVCC_LDFLAGS@ -LIBS = @BLAS_LIBS@ @LAPACK_LIBS@ @LIBS@ @NVCC_LIBS@ -PYTHONCFLAGS = @PYTHON_CFLAGS@ -I@NUMPY_INCLUDE@ -SWIGFLAGS = -DSWIGWORDSIZE64 - -NVCC = @NVCC@ -CUDA_ROOT = @CUDA_PREFIX@ -CUDA_ARCH = @CUDA_ARCH@ -NVCCFLAGS = -I $(CUDA_ROOT)/targets/x86_64-linux/include/ \ --O3 \ --Xcompiler -fPIC \ --Xcudafe --diag_suppress=unrecognized_attribute \ -$(CUDA_ARCH) \ --lineinfo \ --ccbin $(CXX) - -OS = $(shell uname -s) - -SHAREDEXT = so -SHAREDFLAGS = -shared - -ifeq ($(OS),Darwin) - SHAREDEXT = dylib - SHAREDFLAGS = -dynamiclib -undefined dynamic_lookup - SWIGFLAGS = -endif - -MKDIR_P = @MKDIR_P@ -PYTHON = @PYTHON@ -SWIG = @SWIG@ -AR ?= ar - -prefix ?= @prefix@ -exec_prefix ?= @exec_prefix@ -libdir = @libdir@ -includedir = @includedir@ diff --git a/core/src/index/thirdparty/faiss/misc/test_blas.cpp b/core/src/index/thirdparty/faiss/misc/test_blas.cpp deleted file mode 100644 index be2536497e..0000000000 --- a/core/src/index/thirdparty/faiss/misc/test_blas.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -#undef FINTEGER -#define FINTEGER long - - -extern "C" { - -/* declare BLAS functions, see http://www.netlib.org/clapack/cblas/ */ - -int sgemm_ (const char *transa, const char *transb, FINTEGER *m, FINTEGER * - n, FINTEGER *k, const float *alpha, const float *a, - FINTEGER *lda, const float *b, FINTEGER * - ldb, float *beta, float *c, FINTEGER *ldc); - -/* Lapack functions, see http://www.netlib.org/clapack/old/single/sgeqrf.c */ - -int sgeqrf_ (FINTEGER *m, FINTEGER *n, float *a, FINTEGER *lda, - float *tau, float *work, FINTEGER *lwork, FINTEGER *info); - -} - -float *new_random_vec(int size) -{ - float *x = new float[size]; - for (int i = 0; i < size; i++) - x[i] = drand48(); - return x; -} - - -int main() { - - FINTEGER m = 10, n = 20, k = 30; - float *a = new_random_vec(m * k), *b = new_random_vec(n * k), *c = new float[n * m]; - float one = 1.0, zero = 0.0; - - printf("BLAS test\n"); - - sgemm_("Not transposed", "Not transposed", - &m, &n, &k, &one, a, &m, b, &k, &zero, c, &m); - - printf("errors=\n"); - - for (int i = 0; i < m; i++) { - for (int j = 0; j < n; j++) { - float accu = 0; - for (int l = 0; l < k; l++) - accu += a[i + l * m] * b[l + j * k]; - printf ("%6.3f ", accu - c[i + j * m]); - } - printf("\n"); - } - - long info = 0x64bL << 32; - long mi = 0x64bL << 32 | m; - float *tau = new float[m]; - FINTEGER lwork = -1; - - float work1; - - printf("Intentional Lapack error (appears only for 64-bit INTEGER):\n"); - sgeqrf_ (&mi, &n, c, &m, tau, &work1, &lwork, (FINTEGER*)&info); - - // sgeqrf_ (&m, &n, c, &zeroi, tau, &work1, &lwork, (FINTEGER*)&info); - printf("info=%016lx\n", info); - - if(info >> 32 == 0x64b) { - printf("Lapack uses 32-bit integers\n"); - } else { - printf("Lapack uses 64-bit integers\n"); - } - - - return 0; -} diff --git a/core/src/index/thirdparty/faiss/python/Makefile b/core/src/index/thirdparty/faiss/python/Makefile deleted file mode 100644 index 2836568253..0000000000 --- a/core/src/index/thirdparty/faiss/python/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - --include ../makefile.inc - -ifneq ($(strip $(NVCC)),) - SWIGFLAGS += -DGPU_WRAPPER -endif - -all: build - -# Also silently generates swigfaiss.py. -swigfaiss.cpp: swigfaiss.swig ../libfaiss.a - $(SWIG) -python -c++ -Doverride= -I../ $(SWIGFLAGS) -o $@ $< - -swigfaiss_avx2.cpp: swigfaiss.swig ../libfaiss.a - $(SWIG) -python -c++ -Doverride= -module swigfaiss_avx2 -I../ $(SWIGFLAGS) -o $@ $< - -%.o: %.cpp - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(CPUFLAGS) $(PYTHONCFLAGS) \ - -I../ -c $< -o $@ - -# Extension is .so even on OSX. -_%.so: %.o ../libfaiss.a - $(CXX) $(SHAREDFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) - -build: _swigfaiss.so faiss.py - $(PYTHON) setup.py build - -install: build - $(PYTHON) setup.py install - -clean: - rm -f swigfaiss*.cpp swigfaiss*.o swigfaiss*.py _swigfaiss*.so - rm -rf build/ - -.PHONY: all build clean install diff --git a/core/src/index/thirdparty/faiss/python/faiss.py b/core/src/index/thirdparty/faiss/python/faiss.py deleted file mode 100644 index 2d58b7f708..0000000000 --- a/core/src/index/thirdparty/faiss/python/faiss.py +++ /dev/null @@ -1,812 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#@nolint - -# not linting this file because it imports * form swigfaiss, which -# causes a ton of useless warnings. - -import numpy as np -import sys -import inspect -import pdb -import platform -import subprocess -import logging - - -logger = logging.getLogger(__name__) - - -def instruction_set(): - if platform.system() == "Darwin": - if subprocess.check_output(["/usr/sbin/sysctl", "hw.optional.avx2_0"])[-1] == '1': - return "AVX2" - else: - return "default" - elif platform.system() == "Linux": - import numpy.distutils.cpuinfo - if "avx2" in numpy.distutils.cpuinfo.cpu.info[0].get('flags', ""): - return "AVX2" - else: - return "default" - - -try: - instr_set = instruction_set() - if instr_set == "AVX2": - logger.info("Loading faiss with AVX2 support.") - from .swigfaiss_avx2 import * - else: - logger.info("Loading faiss.") - from .swigfaiss import * - -except ImportError: - # we import * so that the symbol X can be accessed as faiss.X - logger.info("Loading faiss.") - from .swigfaiss import * - - -__version__ = "%d.%d.%d" % (FAISS_VERSION_MAJOR, - FAISS_VERSION_MINOR, - FAISS_VERSION_PATCH) - -################################################################## -# The functions below add or replace some methods for classes -# this is to be able to pass in numpy arrays directly -# The C++ version of the classnames will be suffixed with _c -################################################################## - - -def replace_method(the_class, name, replacement, ignore_missing=False): - try: - orig_method = getattr(the_class, name) - except AttributeError: - if ignore_missing: - return - raise - if orig_method.__name__ == 'replacement_' + name: - # replacement was done in parent class - return - setattr(the_class, name + '_c', orig_method) - setattr(the_class, name, replacement) - - -def handle_Clustering(): - def replacement_train(self, x, index, weights=None): - n, d = x.shape - assert d == self.d - if weights is not None: - assert weights.shape == (n, ) - self.train_c(n, swig_ptr(x), index, swig_ptr(weights)) - else: - self.train_c(n, swig_ptr(x), index) - def replacement_train_encoded(self, x, codec, index, weights=None): - n, d = x.shape - assert d == codec.sa_code_size() - assert codec.d == index.d - if weights is not None: - assert weights.shape == (n, ) - self.train_encoded_c(n, swig_ptr(x), codec, index, swig_ptr(weights)) - else: - self.train_encoded_c(n, swig_ptr(x), codec, index) - replace_method(Clustering, 'train', replacement_train) - replace_method(Clustering, 'train_encoded', replacement_train_encoded) - - -handle_Clustering() - - -def handle_Quantizer(the_class): - - def replacement_train(self, x): - n, d = x.shape - assert d == self.d - self.train_c(n, swig_ptr(x)) - - def replacement_compute_codes(self, x): - n, d = x.shape - assert d == self.d - codes = np.empty((n, self.code_size), dtype='uint8') - self.compute_codes_c(swig_ptr(x), swig_ptr(codes), n) - return codes - - def replacement_decode(self, codes): - n, cs = codes.shape - assert cs == self.code_size - x = np.empty((n, self.d), dtype='float32') - self.decode_c(swig_ptr(codes), swig_ptr(x), n) - return x - - replace_method(the_class, 'train', replacement_train) - replace_method(the_class, 'compute_codes', replacement_compute_codes) - replace_method(the_class, 'decode', replacement_decode) - - -handle_Quantizer(ProductQuantizer) -handle_Quantizer(ScalarQuantizer) - - -def handle_Index(the_class): - - def replacement_add(self, x): - assert x.flags.contiguous - n, d = x.shape - assert d == self.d - self.add_c(n, swig_ptr(x)) - - def replacement_add_with_ids(self, x, ids): - n, d = x.shape - assert d == self.d - assert ids.shape == (n, ), 'not same nb of vectors as ids' - self.add_with_ids_c(n, swig_ptr(x), swig_ptr(ids)) - - def replacement_assign(self, x, k): - n, d = x.shape - assert d == self.d - labels = np.empty((n, k), dtype=np.int64) - self.assign_c(n, swig_ptr(x), swig_ptr(labels), k) - return labels - - def replacement_train(self, x): - assert x.flags.contiguous - n, d = x.shape - assert d == self.d - self.train_c(n, swig_ptr(x)) - - def replacement_search(self, x, k): - n, d = x.shape - assert d == self.d - distances = np.empty((n, k), dtype=np.float32) - labels = np.empty((n, k), dtype=np.int64) - self.search_c(n, swig_ptr(x), - k, swig_ptr(distances), - swig_ptr(labels)) - return distances, labels - - def replacement_search_and_reconstruct(self, x, k): - n, d = x.shape - assert d == self.d - distances = np.empty((n, k), dtype=np.float32) - labels = np.empty((n, k), dtype=np.int64) - recons = np.empty((n, k, d), dtype=np.float32) - self.search_and_reconstruct_c(n, swig_ptr(x), - k, swig_ptr(distances), - swig_ptr(labels), - swig_ptr(recons)) - return distances, labels, recons - - def replacement_remove_ids(self, x): - if isinstance(x, IDSelector): - sel = x - else: - assert x.ndim == 1 - index_ivf = try_extract_index_ivf (self) - if index_ivf and index_ivf.direct_map.type == DirectMap.Hashtable: - sel = IDSelectorArray(x.size, swig_ptr(x)) - else: - sel = IDSelectorBatch(x.size, swig_ptr(x)) - return self.remove_ids_c(sel) - - def replacement_reconstruct(self, key): - x = np.empty(self.d, dtype=np.float32) - self.reconstruct_c(key, swig_ptr(x)) - return x - - def replacement_reconstruct_n(self, n0, ni): - x = np.empty((ni, self.d), dtype=np.float32) - self.reconstruct_n_c(n0, ni, swig_ptr(x)) - return x - - def replacement_update_vectors(self, keys, x): - n = keys.size - assert keys.shape == (n, ) - assert x.shape == (n, self.d) - self.update_vectors_c(n, swig_ptr(keys), swig_ptr(x)) - - def replacement_range_search(self, x, thresh): - n, d = x.shape - assert d == self.d - res = RangeSearchResult(n) - self.range_search_c(n, swig_ptr(x), thresh, res) - # get pointers and copy them - lims = rev_swig_ptr(res.lims, n + 1).copy() - nd = int(lims[-1]) - D = rev_swig_ptr(res.distances, nd).copy() - I = rev_swig_ptr(res.labels, nd).copy() - return lims, D, I - - def replacement_sa_encode(self, x): - n, d = x.shape - assert d == self.d - codes = np.empty((n, self.sa_code_size()), dtype='uint8') - self.sa_encode_c(n, swig_ptr(x), swig_ptr(codes)) - return codes - - def replacement_sa_decode(self, codes): - n, cs = codes.shape - assert cs == self.sa_code_size() - x = np.empty((n, self.d), dtype='float32') - self.sa_decode_c(n, swig_ptr(codes), swig_ptr(x)) - return x - - replace_method(the_class, 'add', replacement_add) - replace_method(the_class, 'add_with_ids', replacement_add_with_ids) - replace_method(the_class, 'assign', replacement_assign) - replace_method(the_class, 'train', replacement_train) - replace_method(the_class, 'search', replacement_search) - replace_method(the_class, 'remove_ids', replacement_remove_ids) - replace_method(the_class, 'reconstruct', replacement_reconstruct) - replace_method(the_class, 'reconstruct_n', replacement_reconstruct_n) - replace_method(the_class, 'range_search', replacement_range_search) - replace_method(the_class, 'update_vectors', replacement_update_vectors, - ignore_missing=True) - replace_method(the_class, 'search_and_reconstruct', - replacement_search_and_reconstruct, ignore_missing=True) - replace_method(the_class, 'sa_encode', replacement_sa_encode) - replace_method(the_class, 'sa_decode', replacement_sa_decode) - -def handle_IndexBinary(the_class): - - def replacement_add(self, x): - assert x.flags.contiguous - n, d = x.shape - assert d * 8 == self.d - self.add_c(n, swig_ptr(x)) - - def replacement_add_with_ids(self, x, ids): - n, d = x.shape - assert d * 8 == self.d - assert ids.shape == (n, ), 'not same nb of vectors as ids' - self.add_with_ids_c(n, swig_ptr(x), swig_ptr(ids)) - - def replacement_train(self, x): - assert x.flags.contiguous - n, d = x.shape - assert d * 8 == self.d - self.train_c(n, swig_ptr(x)) - - def replacement_reconstruct(self, key): - x = np.empty(self.d // 8, dtype=np.uint8) - self.reconstruct_c(key, swig_ptr(x)) - return x - - def replacement_search(self, x, k): - n, d = x.shape - assert d * 8 == self.d - distances = np.empty((n, k), dtype=np.int32) - labels = np.empty((n, k), dtype=np.int64) - self.search_c(n, swig_ptr(x), - k, swig_ptr(distances), - swig_ptr(labels)) - return distances, labels - - def replacement_range_search(self, x, thresh): - n, d = x.shape - assert d * 8 == self.d - res = RangeSearchResult(n) - self.range_search_c(n, swig_ptr(x), thresh, res) - # get pointers and copy them - lims = rev_swig_ptr(res.lims, n + 1).copy() - nd = int(lims[-1]) - D = rev_swig_ptr(res.distances, nd).copy() - I = rev_swig_ptr(res.labels, nd).copy() - return lims, D, I - - def replacement_remove_ids(self, x): - if isinstance(x, IDSelector): - sel = x - else: - assert x.ndim == 1 - sel = IDSelectorBatch(x.size, swig_ptr(x)) - return self.remove_ids_c(sel) - - replace_method(the_class, 'add', replacement_add) - replace_method(the_class, 'add_with_ids', replacement_add_with_ids) - replace_method(the_class, 'train', replacement_train) - replace_method(the_class, 'search', replacement_search) - replace_method(the_class, 'range_search', replacement_range_search) - replace_method(the_class, 'reconstruct', replacement_reconstruct) - replace_method(the_class, 'remove_ids', replacement_remove_ids) - - -def handle_VectorTransform(the_class): - - def apply_method(self, x): - assert x.flags.contiguous - n, d = x.shape - assert d == self.d_in - y = np.empty((n, self.d_out), dtype=np.float32) - self.apply_noalloc(n, swig_ptr(x), swig_ptr(y)) - return y - - def replacement_reverse_transform(self, x): - n, d = x.shape - assert d == self.d_out - y = np.empty((n, self.d_in), dtype=np.float32) - self.reverse_transform_c(n, swig_ptr(x), swig_ptr(y)) - return y - - def replacement_vt_train(self, x): - assert x.flags.contiguous - n, d = x.shape - assert d == self.d_in - self.train_c(n, swig_ptr(x)) - - replace_method(the_class, 'train', replacement_vt_train) - # apply is reserved in Pyton... - the_class.apply_py = apply_method - replace_method(the_class, 'reverse_transform', - replacement_reverse_transform) - - -def handle_AutoTuneCriterion(the_class): - def replacement_set_groundtruth(self, D, I): - if D: - assert I.shape == D.shape - self.nq, self.gt_nnn = I.shape - self.set_groundtruth_c( - self.gt_nnn, swig_ptr(D) if D else None, swig_ptr(I)) - - def replacement_evaluate(self, D, I): - assert I.shape == D.shape - assert I.shape == (self.nq, self.nnn) - return self.evaluate_c(swig_ptr(D), swig_ptr(I)) - - replace_method(the_class, 'set_groundtruth', replacement_set_groundtruth) - replace_method(the_class, 'evaluate', replacement_evaluate) - - -def handle_ParameterSpace(the_class): - def replacement_explore(self, index, xq, crit): - assert xq.shape == (crit.nq, index.d) - ops = OperatingPoints() - self.explore_c(index, crit.nq, swig_ptr(xq), - crit, ops) - return ops - replace_method(the_class, 'explore', replacement_explore) - - -def handle_MatrixStats(the_class): - original_init = the_class.__init__ - - def replacement_init(self, m): - assert len(m.shape) == 2 - original_init(self, m.shape[0], m.shape[1], swig_ptr(m)) - - the_class.__init__ = replacement_init - -handle_MatrixStats(MatrixStats) - - -this_module = sys.modules[__name__] - - -for symbol in dir(this_module): - obj = getattr(this_module, symbol) - # print symbol, isinstance(obj, (type, types.ClassType)) - if inspect.isclass(obj): - the_class = obj - if issubclass(the_class, Index): - handle_Index(the_class) - - if issubclass(the_class, IndexBinary): - handle_IndexBinary(the_class) - - if issubclass(the_class, VectorTransform): - handle_VectorTransform(the_class) - - if issubclass(the_class, AutoTuneCriterion): - handle_AutoTuneCriterion(the_class) - - if issubclass(the_class, ParameterSpace): - handle_ParameterSpace(the_class) - - -########################################### -# Add Python references to objects -# we do this at the Python class wrapper level. -########################################### - -def add_ref_in_constructor(the_class, parameter_no): - # adds a reference to parameter parameter_no in self - # so that that parameter does not get deallocated before self - original_init = the_class.__init__ - - def replacement_init(self, *args): - original_init(self, *args) - self.referenced_objects = [args[parameter_no]] - - def replacement_init_multiple(self, *args): - original_init(self, *args) - pset = parameter_no[len(args)] - self.referenced_objects = [args[no] for no in pset] - - if type(parameter_no) == dict: - # a list of parameters to keep, depending on the number of arguments - the_class.__init__ = replacement_init_multiple - else: - the_class.__init__ = replacement_init - -def add_ref_in_method(the_class, method_name, parameter_no): - original_method = getattr(the_class, method_name) - def replacement_method(self, *args): - ref = args[parameter_no] - if not hasattr(self, 'referenced_objects'): - self.referenced_objects = [ref] - else: - self.referenced_objects.append(ref) - return original_method(self, *args) - setattr(the_class, method_name, replacement_method) - -def add_ref_in_function(function_name, parameter_no): - # assumes the function returns an object - original_function = getattr(this_module, function_name) - def replacement_function(*args): - result = original_function(*args) - ref = args[parameter_no] - result.referenced_objects = [ref] - return result - setattr(this_module, function_name, replacement_function) - -add_ref_in_constructor(IndexIVFFlat, 0) -add_ref_in_constructor(IndexIVFFlatDedup, 0) -add_ref_in_constructor(IndexPreTransform, {2: [0, 1], 1: [0]}) -add_ref_in_method(IndexPreTransform, 'prepend_transform', 0) -add_ref_in_constructor(IndexIVFPQ, 0) -add_ref_in_constructor(IndexIVFPQR, 0) -add_ref_in_constructor(Index2Layer, 0) -add_ref_in_constructor(Level1Quantizer, 0) -add_ref_in_constructor(IndexIVFScalarQuantizer, 0) -add_ref_in_constructor(IndexIDMap, 0) -add_ref_in_constructor(IndexIDMap2, 0) -add_ref_in_constructor(IndexHNSW, 0) -add_ref_in_method(IndexShards, 'add_shard', 0) -add_ref_in_method(IndexBinaryShards, 'add_shard', 0) -add_ref_in_constructor(IndexRefineFlat, 0) -add_ref_in_constructor(IndexBinaryIVF, 0) -add_ref_in_constructor(IndexBinaryFromFloat, 0) -add_ref_in_constructor(IndexBinaryIDMap, 0) -add_ref_in_constructor(IndexBinaryIDMap2, 0) - -add_ref_in_method(IndexReplicas, 'addIndex', 0) -add_ref_in_method(IndexBinaryReplicas, 'addIndex', 0) - -add_ref_in_constructor(BufferedIOWriter, 0) -add_ref_in_constructor(BufferedIOReader, 0) - -# seems really marginal... -# remove_ref_from_method(IndexReplicas, 'removeIndex', 0) - -if hasattr(this_module, 'GpuIndexFlat'): - # handle all the GPUResources refs - add_ref_in_function('index_cpu_to_gpu', 0) - add_ref_in_constructor(GpuIndexFlat, 0) - add_ref_in_constructor(GpuIndexFlatIP, 0) - add_ref_in_constructor(GpuIndexFlatL2, 0) - add_ref_in_constructor(GpuIndexIVFFlat, 0) - add_ref_in_constructor(GpuIndexIVFScalarQuantizer, 0) - add_ref_in_constructor(GpuIndexIVFPQ, 0) - add_ref_in_constructor(GpuIndexBinaryFlat, 0) - - - -########################################### -# GPU functions -########################################### - - -def index_cpu_to_gpu_multiple_py(resources, index, co=None, gpus=None): - """ builds the C++ vectors for the GPU indices and the - resources. Handles the case where the resources are assigned to - the list of GPUs """ - if gpus is None: - gpus = range(len(resources)) - vres = GpuResourcesVector() - vdev = IntVector() - for i, res in zip(gpus, resources): - vdev.push_back(i) - vres.push_back(res) - index = index_cpu_to_gpu_multiple(vres, vdev, index, co) - index.referenced_objects = resources - return index - - -def index_cpu_to_all_gpus(index, co=None, ngpu=-1): - index_gpu = index_cpu_to_gpus_list(index, co=co, gpus=None, ngpu=ngpu) - return index_gpu - - -def index_cpu_to_gpus_list(index, co=None, gpus=None, ngpu=-1): - """ Here we can pass list of GPU ids as a parameter or ngpu to - use first n GPU's. gpus mut be a list or None""" - if (gpus is None) and (ngpu == -1): # All blank - gpus = range(get_num_gpus()) - elif (gpus is None) and (ngpu != -1): # Get number of GPU's only - gpus = range(ngpu) - res = [StandardGpuResources() for _ in gpus] - index_gpu = index_cpu_to_gpu_multiple_py(res, index, co, gpus) - return index_gpu - - -########################################### -# numpy array / std::vector conversions -########################################### - -# mapping from vector names in swigfaiss.swig and the numpy dtype names -vector_name_map = { - 'Float': 'float32', - 'Byte': 'uint8', - 'Char': 'int8', - 'Uint64': 'uint64', - 'Long': 'int64', - 'Int': 'int32', - 'Double': 'float64' - } - -def vector_to_array(v): - """ convert a C++ vector to a numpy array """ - classname = v.__class__.__name__ - assert classname.endswith('Vector') - dtype = np.dtype(vector_name_map[classname[:-6]]) - a = np.empty(v.size(), dtype=dtype) - if v.size() > 0: - memcpy(swig_ptr(a), v.data(), a.nbytes) - return a - - -def vector_float_to_array(v): - return vector_to_array(v) - - -def copy_array_to_vector(a, v): - """ copy a numpy array to a vector """ - n, = a.shape - classname = v.__class__.__name__ - assert classname.endswith('Vector') - dtype = np.dtype(vector_name_map[classname[:-6]]) - assert dtype == a.dtype, ( - 'cannot copy a %s array to a %s (should be %s)' % ( - a.dtype, classname, dtype)) - v.resize(n) - if n > 0: - memcpy(v.data(), swig_ptr(a), a.nbytes) - - -########################################### -# Wrapper for a few functions -########################################### - -def kmin(array, k): - """return k smallest values (and their indices) of the lines of a - float32 array""" - m, n = array.shape - I = np.zeros((m, k), dtype='int64') - D = np.zeros((m, k), dtype='float32') - ha = float_maxheap_array_t() - ha.ids = swig_ptr(I) - ha.val = swig_ptr(D) - ha.nh = m - ha.k = k - ha.heapify() - ha.addn(n, swig_ptr(array)) - ha.reorder() - return D, I - - -def kmax(array, k): - """return k largest values (and their indices) of the lines of a - float32 array""" - m, n = array.shape - I = np.zeros((m, k), dtype='int64') - D = np.zeros((m, k), dtype='float32') - ha = float_minheap_array_t() - ha.ids = swig_ptr(I) - ha.val = swig_ptr(D) - ha.nh = m - ha.k = k - ha.heapify() - ha.addn(n, swig_ptr(array)) - ha.reorder() - return D, I - - -def pairwise_distances(xq, xb, mt=METRIC_L2, metric_arg=0): - """compute the whole pairwise distance matrix between two sets of - vectors""" - nq, d = xq.shape - nb, d2 = xb.shape - assert d == d2 - dis = np.empty((nq, nb), dtype='float32') - if mt == METRIC_L2: - pairwise_L2sqr( - d, nq, swig_ptr(xq), - nb, swig_ptr(xb), - swig_ptr(dis)) - else: - pairwise_extra_distances( - d, nq, swig_ptr(xq), - nb, swig_ptr(xb), - mt, metric_arg, - swig_ptr(dis)) - return dis - - - - -def rand(n, seed=12345): - res = np.empty(n, dtype='float32') - float_rand(swig_ptr(res), res.size, seed) - return res - - -def randint(n, seed=12345, vmax=None): - res = np.empty(n, dtype='int64') - if vmax is None: - int64_rand(swig_ptr(res), res.size, seed) - else: - int64_rand_max(swig_ptr(res), res.size, vmax, seed) - return res - -lrand = randint - -def randn(n, seed=12345): - res = np.empty(n, dtype='float32') - float_randn(swig_ptr(res), res.size, seed) - return res - - -def eval_intersection(I1, I2): - """ size of intersection between each line of two result tables""" - n = I1.shape[0] - assert I2.shape[0] == n - k1, k2 = I1.shape[1], I2.shape[1] - ninter = 0 - for i in range(n): - ninter += ranklist_intersection_size( - k1, swig_ptr(I1[i]), k2, swig_ptr(I2[i])) - return ninter - - -def normalize_L2(x): - fvec_renorm_L2(x.shape[1], x.shape[0], swig_ptr(x)) - -# MapLong2Long interface - -def replacement_map_add(self, keys, vals): - n, = keys.shape - assert (n,) == keys.shape - self.add_c(n, swig_ptr(keys), swig_ptr(vals)) - -def replacement_map_search_multiple(self, keys): - n, = keys.shape - vals = np.empty(n, dtype='int64') - self.search_multiple_c(n, swig_ptr(keys), swig_ptr(vals)) - return vals - -replace_method(MapLong2Long, 'add', replacement_map_add) -replace_method(MapLong2Long, 'search_multiple', replacement_map_search_multiple) - - -########################################### -# Kmeans object -########################################### - - -class Kmeans: - """shallow wrapper around the Clustering object. The important method - is train().""" - - def __init__(self, d, k, **kwargs): - """d: input dimension, k: nb of centroids. Additional - parameters are passed on the ClusteringParameters object, - including niter=25, verbose=False, spherical = False - """ - self.d = d - self.k = k - self.gpu = False - self.cp = ClusteringParameters() - for k, v in kwargs.items(): - if k == 'gpu': - self.gpu = v - else: - # if this raises an exception, it means that it is a non-existent field - getattr(self.cp, k) - setattr(self.cp, k, v) - self.centroids = None - - def train(self, x, weights=None): - n, d = x.shape - assert d == self.d - clus = Clustering(d, self.k, self.cp) - if self.cp.spherical: - self.index = IndexFlatIP(d) - else: - self.index = IndexFlatL2(d) - if self.gpu: - if self.gpu == True: - ngpu = -1 - else: - ngpu = self.gpu - self.index = index_cpu_to_all_gpus(self.index, ngpu=ngpu) - clus.train(x, self.index, weights) - centroids = vector_float_to_array(clus.centroids) - self.centroids = centroids.reshape(self.k, d) - stats = clus.iteration_stats - self.obj = np.array([ - stats.at(i).obj for i in range(stats.size()) - ]) - return self.obj[-1] if self.obj.size > 0 else 0.0 - - def assign(self, x): - assert self.centroids is not None, "should train before assigning" - self.index.reset() - self.index.add(self.centroids) - D, I = self.index.search(x, 1) - return D.ravel(), I.ravel() - -# IndexProxy was renamed to IndexReplicas, remap the old name for any old code -# people may have -IndexProxy = IndexReplicas -ConcatenatedInvertedLists = HStackInvertedLists - -########################################### -# serialization of indexes to byte arrays -########################################### - -def serialize_index(index): - """ convert an index to a numpy uint8 array """ - writer = VectorIOWriter() - write_index(index, writer) - return vector_to_array(writer.data) - -def deserialize_index(data): - reader = VectorIOReader() - copy_array_to_vector(data, reader.data) - return read_index(reader) - -def serialize_index_binary(index): - """ convert an index to a numpy uint8 array """ - writer = VectorIOWriter() - write_index_binary(index, writer) - return vector_to_array(writer.data) - -def deserialize_index_binary(data): - reader = VectorIOReader() - copy_array_to_vector(data, reader.data) - return read_index_binary(reader) - - -########################################### -# ResultHeap -########################################### - -class ResultHeap: - """Accumulate query results from a sliced dataset. The final result will - be in self.D, self.I.""" - - def __init__(self, nq, k): - " nq: number of query vectors, k: number of results per query " - self.I = np.zeros((nq, k), dtype='int64') - self.D = np.zeros((nq, k), dtype='float32') - self.nq, self.k = nq, k - heaps = float_maxheap_array_t() - heaps.k = k - heaps.nh = nq - heaps.val = swig_ptr(self.D) - heaps.ids = swig_ptr(self.I) - heaps.heapify() - self.heaps = heaps - - def add_result(self, D, I): - """D, I do not need to be in a particular order (heap or sorted)""" - assert D.shape == (self.nq, self.k) - assert I.shape == (self.nq, self.k) - self.heaps.addn_with_ids( - self.k, faiss.swig_ptr(D), - faiss.swig_ptr(I), self.k) - - def finalize(self): - self.heaps.reorder() diff --git a/core/src/index/thirdparty/faiss/python/setup.py b/core/src/index/thirdparty/faiss/python/setup.py deleted file mode 100644 index 89b6d398cb..0000000000 --- a/core/src/index/thirdparty/faiss/python/setup.py +++ /dev/null @@ -1,50 +0,0 @@ -from __future__ import print_function -from setuptools import setup, find_packages -import os -import shutil - -here = os.path.abspath(os.path.dirname(__file__)) - -check_fpath = os.path.join("_swigfaiss.so") -if not os.path.exists(check_fpath): - print("Could not find {}".format(check_fpath)) - print("Have you run `make` and `make -C python`?") - -# make the faiss python package dir -shutil.rmtree("faiss", ignore_errors=True) -os.mkdir("faiss") -shutil.copyfile("faiss.py", "faiss/__init__.py") -shutil.copyfile("swigfaiss.py", "faiss/swigfaiss.py") -shutil.copyfile("_swigfaiss.so", "faiss/_swigfaiss.so") -try: - shutil.copyfile("swigfaiss_avx2.py", "faiss/swigfaiss_avx2.py") - shutil.copyfile("_swigfaiss_avx2.so", "faiss/_swigfaiss_avx2.so") -except: - pass - -long_description=""" -Faiss is a library for efficient similarity search and clustering of dense -vectors. It contains algorithms that search in sets of vectors of any size, - up to ones that possibly do not fit in RAM. It also contains supporting -code for evaluation and parameter tuning. Faiss is written in C++ with -complete wrappers for Python/numpy. Some of the most useful algorithms -are implemented on the GPU. It is developed by Facebook AI Research. -""" -setup( - name='faiss', - version='1.6.3', - description='A library for efficient similarity search and clustering of dense vectors', - long_description=long_description, - url='https://github.com/facebookresearch/faiss', - author='Matthijs Douze, Jeff Johnson, Herve Jegou, Lucas Hosseini', - author_email='matthijs@fb.com', - license='MIT', - keywords='search nearest neighbors', - - install_requires=['numpy'], - packages=['faiss'], - package_data={ - 'faiss': ['*.so'], - }, - -) diff --git a/core/src/index/thirdparty/faiss/python/swigfaiss.swig b/core/src/index/thirdparty/faiss/python/swigfaiss.swig deleted file mode 100644 index b0d8b8173d..0000000000 --- a/core/src/index/thirdparty/faiss/python/swigfaiss.swig +++ /dev/null @@ -1,1109 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- C++ -*- - -// This file describes the C++-scripting language bridge for both Lua -// and Python It contains mainly includes and a few macros. There are -// 3 preprocessor macros of interest: -// SWIGLUA: Lua-specific code -// SWIGPYTHON: Python-specific code -// GPU_WRAPPER: also compile interfaces for GPU. - -%module swigfaiss; - -// fbode SWIG fails on warnings, so make them non fatal -#pragma SWIG nowarn=321 -#pragma SWIG nowarn=403 -#pragma SWIG nowarn=325 -#pragma SWIG nowarn=389 -#pragma SWIG nowarn=341 -#pragma SWIG nowarn=512 - -%include - -typedef uint64_t size_t; - -#define __restrict - - -/******************************************************************* - * Copied verbatim to wrapper. Contains the C++-visible includes, and - * the language includes for their respective matrix libraries. - *******************************************************************/ - -%{ - - -#include -#include - - -#ifdef SWIGLUA - -#include - -extern "C" { - -#include -#include -#undef THTensor - -} - -#endif - - -#ifdef SWIGPYTHON - -#undef popcount64 - -#define SWIG_FILE_WITH_INIT -#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION -#include - -#endif - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include - -#include -#include - - -%} - -/******************************************************** - * GIL manipulation and exception handling - ********************************************************/ - -#ifdef SWIGPYTHON -// %catches(faiss::FaissException); - - -// Python-specific: release GIL by default for all functions -%exception { - Py_BEGIN_ALLOW_THREADS - try { - $action - } catch(faiss::FaissException & e) { - PyEval_RestoreThread(_save); - - if (PyErr_Occurred()) { - // some previous code already set the error type. - } else { - PyErr_SetString(PyExc_RuntimeError, e.what()); - } - SWIG_fail; - } catch(std::bad_alloc & ba) { - PyEval_RestoreThread(_save); - PyErr_SetString(PyExc_MemoryError, "std::bad_alloc"); - SWIG_fail; - } - Py_END_ALLOW_THREADS -} - -#endif - -#ifdef SWIGLUA - -%exception { - try { - $action - } catch(faiss::FaissException & e) { - SWIG_Lua_pushferrstring(L, "C++ exception: %s", e.what()); \ - goto fail; - } -} - -#endif - - -/******************************************************************* - * Types of vectors we want to manipulate at the scripting language - * level. - *******************************************************************/ - -// simplified interface for vector -namespace std { - - template - class vector { - public: - vector(); - void push_back(T); - void clear(); - T * data(); - size_t size(); - T at (size_t n) const; - void resize (size_t n); - void swap (vector & other); - }; -}; - - - -%template(FloatVector) std::vector; -%template(DoubleVector) std::vector; -%template(ByteVector) std::vector; -%template(CharVector) std::vector; -// NOTE(hoss): Using unsigned long instead of uint64_t because OSX defines -// uint64_t as unsigned long long, which SWIG is not aware of. -%template(Uint64Vector) std::vector; -%template(LongVector) std::vector; -%template(LongLongVector) std::vector; -%template(IntVector) std::vector; -%template(FloatVectorVector) std::vector >; -%template(ByteVectorVector) std::vector >; -%template(LongVectorVector) std::vector >; -%template(VectorTransformVector) std::vector; -%template(OperatingPointVector) std::vector; -%template(InvertedListsPtrVector) std::vector; -%template(RepeatVector) std::vector; -%template(ClusteringIterationStatsVector) std::vector; - -#ifdef GPU_WRAPPER -%template(GpuResourcesVector) std::vector; -#endif - -%include - -// produces an error on the Mac -%ignore faiss::hamming; - -/******************************************************************* - * Parse headers - *******************************************************************/ - - -%ignore *::cmp; - -%include -%include - -int get_num_gpus(); -void gpu_profiler_start(); -void gpu_profiler_stop(); -void gpu_sync_all_devices(); - -#ifdef GPU_WRAPPER - -%{ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int get_num_gpus() -{ - return faiss::gpu::getNumDevices(); -} - -void gpu_profiler_start() -{ - return faiss::gpu::profilerStart(); -} - -void gpu_profiler_stop() -{ - return faiss::gpu::profilerStop(); -} - -void gpu_sync_all_devices() -{ - return faiss::gpu::synchronizeAllDevices(); -} - -%} - -// causes weird wrapper bug -%ignore *::getMemoryManager; -%ignore *::getMemoryManagerCurrentDevice; - -%include -%include - -#else - -%{ -int get_num_gpus() -{ - return 0; -} - -void gpu_profiler_start() -{ -} - -void gpu_profiler_stop() -{ -} - -void gpu_sync_all_devices() -{ -} -%} - - -#endif - -// order matters because includes are not recursive - -%include -%include -%include - -%include -%include -%include - -%include - -%ignore faiss::ProductQuantizer::get_centroids(size_t,size_t) const; - -%include - -%include -%include -%include -%include -%include -%include -%include -%include -%ignore InvertedListScanner; -%ignore BinaryInvertedListScanner; -%include -// NOTE(hoss): SWIG (wrongly) believes the overloaded const version shadows the -// non-const one. -%warnfilter(509) extract_index_ivf; -%warnfilter(509) try_extract_index_ivf; -%include -%include -%include -%include -%include -%include -%include -%include - -%include -%include - -%ignore faiss::IndexIVFPQ::alloc_type; -%include -%include -%include - -%include -%include -%include -%include -%include -%include - - - - // %ignore faiss::IndexReplicas::at(int) const; - -%include -%template(ThreadedIndexBase) faiss::ThreadedIndex; -%template(ThreadedIndexBaseBinary) faiss::ThreadedIndex; - -%include -%template(IndexShards) faiss::IndexShardsTemplate; -%template(IndexBinaryShards) faiss::IndexShardsTemplate; - -%include -%template(IndexReplicas) faiss::IndexReplicasTemplate; -%template(IndexBinaryReplicas) faiss::IndexReplicasTemplate; - -%include -%template(IndexIDMap) faiss::IndexIDMapTemplate; -%template(IndexBinaryIDMap) faiss::IndexIDMapTemplate; -%template(IndexIDMap2) faiss::IndexIDMap2Template; -%template(IndexBinaryIDMap2) faiss::IndexIDMap2Template; - -#ifdef GPU_WRAPPER - -// quiet SWIG warnings -%ignore faiss::gpu::GpuIndexIVF::GpuIndexIVF; - -%include -%include -%include -%include -%include -%include -%include -%include -%include -%include -%include - -#ifdef SWIGLUA - -/// in Lua, swigfaiss_gpu is known as swigfaiss -%luacode { -local swigfaiss = swigfaiss_gpu -} - -#endif - - -#endif - - - - -/******************************************************************* - * Lua-specific: support async execution of searches in an index - * Python equivalent is just to use Python threads. - *******************************************************************/ - - -#ifdef SWIGLUA - -%{ - - -namespace faiss { - -struct AsyncIndexSearchC { - typedef Index::idx_t idx_t; - const Index * index; - - idx_t n; - const float *x; - idx_t k; - float *distances; - idx_t *labels; - - bool is_finished; - - pthread_t thread; - - - AsyncIndexSearchC (const Index *index, - idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels): - index(index), n(n), x(x), k(k), distances(distances), - labels(labels) - { - is_finished = false; - pthread_create (&thread, NULL, &AsyncIndexSearchC::callback, - this); - } - - static void *callback (void *arg) - { - AsyncIndexSearchC *aidx = (AsyncIndexSearchC *)arg; - aidx->do_search(); - return NULL; - } - - void do_search () - { - index->search (n, x, k, distances, labels); - } - void join () - { - pthread_join (thread, NULL); - } - -}; - -} - -%} - -// re-decrlare only what we need -namespace faiss { - -struct AsyncIndexSearchC { - typedef Index::idx_t idx_t; - bool is_finished; - AsyncIndexSearchC (const Index *index, - idx_t n, const float *x, idx_t k, - float *distances, idx_t *labels); - - - void join (); -}; - -} - - -#endif - - - - -/******************************************************************* - * downcast return of some functions so that the sub-class is used - * instead of the generic upper-class. - *******************************************************************/ - -#ifdef SWIGLUA - -%define DOWNCAST(subclass) - if (dynamic_cast ($1)) { - SWIG_NewPointerObj(L,$1,SWIGTYPE_p_faiss__ ## subclass, $owner); - } else -%enddef - -%define DOWNCAST2(subclass, longname) - if (dynamic_cast ($1)) { - SWIG_NewPointerObj(L,$1,SWIGTYPE_p_faiss__ ## longname, $owner); - } else -%enddef - -%define DOWNCAST_GPU(subclass) - if (dynamic_cast ($1)) { - SWIG_NewPointerObj(L,$1,SWIGTYPE_p_faiss__gpu__ ## subclass, $owner); - } else -%enddef - -#endif - - -#ifdef SWIGPYTHON - -%define DOWNCAST(subclass) - if (dynamic_cast ($1)) { - $result = SWIG_NewPointerObj($1,SWIGTYPE_p_faiss__ ## subclass,$owner); - } else -%enddef - -%define DOWNCAST2(subclass, longname) - if (dynamic_cast ($1)) { - $result = SWIG_NewPointerObj($1,SWIGTYPE_p_faiss__ ## longname,$owner); - } else -%enddef - -%define DOWNCAST_GPU(subclass) - if (dynamic_cast ($1)) { - $result = SWIG_NewPointerObj($1,SWIGTYPE_p_faiss__gpu__ ## subclass,$owner); - } else -%enddef - -#endif - -%newobject read_index; -%newobject read_index_binary; -%newobject read_VectorTransform; -%newobject read_ProductQuantizer; -%newobject clone_index; -%newobject clone_VectorTransform; - -// Subclasses should appear before their parent -%typemap(out) faiss::Index * { - DOWNCAST2 ( IndexIDMap, IndexIDMapTemplateT_faiss__Index_t ) - DOWNCAST2 ( IndexIDMap2, IndexIDMap2TemplateT_faiss__Index_t ) - DOWNCAST2 ( IndexShards, IndexShardsTemplateT_faiss__Index_t ) - DOWNCAST2 ( IndexReplicas, IndexReplicasTemplateT_faiss__Index_t ) - DOWNCAST ( IndexIVFPQR ) - DOWNCAST ( IndexIVFPQ ) - DOWNCAST ( IndexIVFSpectralHash ) - DOWNCAST ( IndexIVFScalarQuantizer ) - DOWNCAST ( IndexIVFFlatDedup ) - DOWNCAST ( IndexIVFFlat ) - DOWNCAST ( IndexIVF ) - DOWNCAST ( IndexFlat ) - DOWNCAST ( IndexPQ ) - DOWNCAST ( IndexScalarQuantizer ) - DOWNCAST ( IndexLSH ) - DOWNCAST ( IndexLattice ) - DOWNCAST ( IndexPreTransform ) - DOWNCAST ( MultiIndexQuantizer ) - DOWNCAST ( IndexHNSWFlat ) - DOWNCAST ( IndexHNSWPQ ) - DOWNCAST ( IndexHNSWSQ ) - DOWNCAST ( IndexHNSW2Level ) - DOWNCAST ( Index2Layer ) -#ifdef GPU_WRAPPER - DOWNCAST_GPU ( GpuIndexIVFPQ ) - DOWNCAST_GPU ( GpuIndexIVFFlat ) - DOWNCAST_GPU ( GpuIndexIVFScalarQuantizer ) - DOWNCAST_GPU ( GpuIndexFlat ) -#endif - // default for non-recognized classes - DOWNCAST ( Index ) - if ($1 == NULL) - { -#ifdef SWIGPYTHON - $result = SWIG_Py_Void(); -#endif - // Lua does not need a push for nil - } else { - assert(false); - } -#ifdef SWIGLUA - SWIG_arg++; -#endif -} - -%typemap(out) faiss::IndexBinary * { - DOWNCAST2 ( IndexBinaryReplicas, IndexReplicasTemplateT_faiss__IndexBinary_t ) - DOWNCAST2 ( IndexBinaryIDMap, IndexIDMapTemplateT_faiss__IndexBinary_t ) - DOWNCAST2 ( IndexBinaryIDMap2, IndexIDMap2TemplateT_faiss__IndexBinary_t ) - DOWNCAST ( IndexBinaryIVF ) - DOWNCAST ( IndexBinaryFlat ) - DOWNCAST ( IndexBinaryFromFloat ) - DOWNCAST ( IndexBinaryHNSW ) -#ifdef GPU_WRAPPER - DOWNCAST_GPU ( GpuIndexBinaryFlat ) -#endif - // default for non-recognized classes - DOWNCAST ( IndexBinary ) - if ($1 == NULL) - { -#ifdef SWIGPYTHON - $result = SWIG_Py_Void(); -#endif - // Lua does not need a push for nil - } else { - assert(false); - } -#ifdef SWIGLUA - SWIG_arg++; -#endif -} - -%typemap(out) faiss::VectorTransform * { - DOWNCAST (RemapDimensionsTransform) - DOWNCAST (OPQMatrix) - DOWNCAST (PCAMatrix) - DOWNCAST (RandomRotationMatrix) - DOWNCAST (LinearTransform) - DOWNCAST (NormalizationTransform) - DOWNCAST (CenteringTransform) - DOWNCAST (VectorTransform) - { - assert(false); - } -#ifdef SWIGLUA - SWIG_arg++; -#endif -} - -%typemap(out) faiss::InvertedLists * { - DOWNCAST (ArrayInvertedLists) - DOWNCAST (OnDiskInvertedLists) - DOWNCAST (VStackInvertedLists) - DOWNCAST (HStackInvertedLists) - DOWNCAST (MaskedInvertedLists) - DOWNCAST (InvertedLists) - { - assert(false); - } -#ifdef SWIGLUA - SWIG_arg++; -#endif -} - -// just to downcast pointers that come from elsewhere (eg. direct -// access to object fields) -%inline %{ -faiss::Index * downcast_index (faiss::Index *index) -{ - return index; -} -faiss::VectorTransform * downcast_VectorTransform (faiss::VectorTransform *vt) -{ - return vt; -} -faiss::IndexBinary * downcast_IndexBinary (faiss::IndexBinary *index) -{ - return index; -} -faiss::InvertedLists * downcast_InvertedLists (faiss::InvertedLists *il) -{ - return il; -} -%} - -%include -%include -%include - -%newobject index_factory; -%newobject index_binary_factory; - -%include -%include -%include - - -#ifdef GPU_WRAPPER - -%include - -%newobject index_gpu_to_cpu; -%newobject index_cpu_to_gpu; -%newobject index_cpu_to_gpu_multiple; - -%include - -#endif - -// Python-specific: do not release GIL any more, as functions below -// use the Python/C API -#ifdef SWIGPYTHON -%exception; -#endif - - - - - -/******************************************************************* - * Python specific: numpy array <-> C++ pointer interface - *******************************************************************/ - -#ifdef SWIGPYTHON - -%{ -PyObject *swig_ptr (PyObject *a) -{ - if(!PyArray_Check(a)) { - PyErr_SetString(PyExc_ValueError, "input not a numpy array"); - return NULL; - } - PyArrayObject *ao = (PyArrayObject *)a; - - if(!PyArray_ISCONTIGUOUS(ao)) { - PyErr_SetString(PyExc_ValueError, "array is not C-contiguous"); - return NULL; - } - void * data = PyArray_DATA(ao); - if(PyArray_TYPE(ao) == NPY_FLOAT32) { - return SWIG_NewPointerObj(data, SWIGTYPE_p_float, 0); - } - if(PyArray_TYPE(ao) == NPY_FLOAT64) { - return SWIG_NewPointerObj(data, SWIGTYPE_p_double, 0); - } - if(PyArray_TYPE(ao) == NPY_INT32) { - return SWIG_NewPointerObj(data, SWIGTYPE_p_int, 0); - } - if(PyArray_TYPE(ao) == NPY_UINT8) { - return SWIG_NewPointerObj(data, SWIGTYPE_p_unsigned_char, 0); - } - if(PyArray_TYPE(ao) == NPY_INT8) { - return SWIG_NewPointerObj(data, SWIGTYPE_p_char, 0); - } - if(PyArray_TYPE(ao) == NPY_UINT64) { -#ifdef SWIGWORDSIZE64 - return SWIG_NewPointerObj(data, SWIGTYPE_p_unsigned_long, 0); -#else - return SWIG_NewPointerObj(data, SWIGTYPE_p_unsigned_long_long, 0); -#endif - } - if(PyArray_TYPE(ao) == NPY_INT64) { -#ifdef SWIGWORDSIZE64 - return SWIG_NewPointerObj(data, SWIGTYPE_p_long, 0); -#else - return SWIG_NewPointerObj(data, SWIGTYPE_p_long_long, 0); -#endif - } - PyErr_SetString(PyExc_ValueError, "did not recognize array type"); - return NULL; -} - - -struct PythonInterruptCallback: faiss::InterruptCallback { - - bool want_interrupt () override { - int err; - { - PyGILState_STATE gstate; - gstate = PyGILState_Ensure(); - err = PyErr_CheckSignals(); - PyGILState_Release(gstate); - } - return err == -1; - } - -}; - - -%} - - -%init %{ - /* needed, else crash at runtime */ - import_array(); - - faiss::InterruptCallback::instance.reset(new PythonInterruptCallback()); - -%} - -// return a pointer usable as input for functions that expect pointers -PyObject *swig_ptr (PyObject *a); - -%define REV_SWIG_PTR(ctype, numpytype) - -%{ -PyObject * rev_swig_ptr(ctype *src, npy_intp size) { - return PyArray_SimpleNewFromData(1, &size, numpytype, src); -} -%} - -PyObject * rev_swig_ptr(ctype *src, size_t size); - -%enddef - -REV_SWIG_PTR(float, NPY_FLOAT32); -REV_SWIG_PTR(int, NPY_INT32); -REV_SWIG_PTR(unsigned char, NPY_UINT8); -REV_SWIG_PTR(int64_t, NPY_INT64); -REV_SWIG_PTR(uint64_t, NPY_UINT64); - -#endif - - - -/******************************************************************* - * Lua specific: Torch tensor <-> C++ pointer interface - *******************************************************************/ - -#ifdef SWIGLUA - - -// provide a XXX_ptr function to convert Lua XXXTensor -> C++ XXX* - -%define TYPE_CONVERSION(ctype, tensortype) - -// typemap for the *_ptr_from_cdata function -%typemap(in) ctype** { - if(lua_type(L, $input) != 10) { - fprintf(stderr, "not cdata input\n"); - SWIG_fail; - } - $1 = (ctype**)lua_topointer(L, $input); -} - - -// SWIG and C declaration for the *_ptr_from_cdata function -%{ -ctype * ctype ## _ptr_from_cdata(ctype **x, long ofs) { - return *x + ofs; -} -%} -ctype * ctype ## _ptr_from_cdata(ctype **x, long ofs); - -// the *_ptr function -%luacode { - -function swigfaiss. ctype ## _ptr(tensor) - assert(tensor:type() == "torch." .. # tensortype, "need a " .. # tensortype) - assert(tensor:isContiguous(), "requires contiguous tensor") - return swigfaiss. ctype ## _ptr_from_cdata( - tensor:storage():data(), - tensor:storageOffset() - 1) -end - -} - -%enddef - -TYPE_CONVERSION (int, IntTensor) -TYPE_CONVERSION (float, FloatTensor) -TYPE_CONVERSION (long, LongTensor) -TYPE_CONVERSION (uint64_t, LongTensor) -TYPE_CONVERSION (uint8_t, ByteTensor) - -#endif - -/******************************************************************* - * How should the template objects apprear in the scripting language? - *******************************************************************/ - -// answer: the same as the C++ typedefs, but we still have to redefine them - -%template() faiss::CMin; -%template() faiss::CMin; -%template() faiss::CMax; -%template() faiss::CMax; - -%template(float_minheap_array_t) faiss::HeapArray >; -%template(int_minheap_array_t) faiss::HeapArray >; - -%template(float_maxheap_array_t) faiss::HeapArray >; -%template(int_maxheap_array_t) faiss::HeapArray >; - - -/******************************************************************* - * Expose a few basic functions - *******************************************************************/ - - -void omp_set_num_threads (int num_threads); -int omp_get_max_threads (); -void *memcpy(void *dest, const void *src, size_t n); - - -/******************************************************************* - * For Faiss/Pytorch interop via pointers encoded as longs - *******************************************************************/ - -%inline %{ -float * cast_integer_to_float_ptr (long x) { - return (float*)x; -} - -long * cast_integer_to_long_ptr (long x) { - return (long*)x; -} - -int * cast_integer_to_int_ptr (long x) { - return (int*)x; -} - -%} - - - -/******************************************************************* - * Range search interface - *******************************************************************/ - -%ignore faiss::BufferList::Buffer; -%ignore faiss::RangeSearchPartialResult::QueryResult; -%ignore faiss::IDSelectorBatch::set; -%ignore faiss::IDSelectorBatch::bloom; - -%ignore faiss::InterruptCallback::instance; -%ignore faiss::InterruptCallback::lock; -%include - -%{ -// may be useful for lua code launched in background from shell - -#include -void ignore_SIGTTIN() { - signal(SIGTTIN, SIG_IGN); -} -%} - -void ignore_SIGTTIN(); - - -%inline %{ - -// numpy misses a hash table implementation, hence this class. It -// represents not found values as -1 like in the Index implementation - -struct MapLong2Long { - std::unordered_map map; - - void add(size_t n, const int64_t *keys, const int64_t *vals) { - map.reserve(map.size() + n); - for (size_t i = 0; i < n; i++) { - map[keys[i]] = vals[i]; - } - } - - long search(int64_t key) { - if (map.count(key) == 0) { - return -1; - } else { - return map[key]; - } - } - - void search_multiple(size_t n, int64_t *keys, int64_t * vals) { - for (size_t i = 0; i < n; i++) { - vals[i] = search(keys[i]); - } - } -}; - -%} - -/******************************************************************* - * Support I/O to arbitrary functions - *******************************************************************/ - - -%inline %{ - -#ifdef SWIGPYTHON - - -struct PyCallbackIOWriter: faiss::IOWriter { - - PyObject * callback; - size_t bs; // maximum write size - - PyCallbackIOWriter(PyObject *callback, - size_t bs = 1024 * 1024): - callback(callback), bs(bs) { - Py_INCREF(callback); - name = "PyCallbackIOWriter"; - } - - size_t operator()(const void *ptrv, size_t size, size_t nitems) override { - size_t ws = size * nitems; - const char *ptr = (const char*)ptrv; - PyGILState_STATE gstate; - gstate = PyGILState_Ensure(); - while(ws > 0) { - size_t wi = ws > bs ? bs : ws; - PyObject* bo = PyBytes_FromStringAndSize(ptr, wi); - PyObject *arglist = Py_BuildValue("(N)", bo); - if(!arglist) { - PyGILState_Release(gstate); - return 0; - } - ptr += wi; - ws -= wi; - PyObject * result = PyObject_CallObject(callback, arglist); - Py_DECREF(arglist); - if (result == NULL) { - PyGILState_Release(gstate); - return 0; - } - Py_DECREF(result); - } - PyGILState_Release(gstate); - return nitems; - } - - ~PyCallbackIOWriter() { - Py_DECREF(callback); - } - -}; - -struct PyCallbackIOReader: faiss::IOReader { - - PyObject * callback; - size_t bs; // maximum buffer size - - PyCallbackIOReader(PyObject *callback, - size_t bs = 1024 * 1024): - callback(callback), bs(bs) { - Py_INCREF(callback); - name = "PyCallbackIOReader"; - } - - size_t operator()(void *ptrv, size_t size, size_t nitems) override { - size_t rs = size * nitems; - char *ptr = (char*)ptrv; - PyGILState_STATE gstate; - gstate = PyGILState_Ensure(); - while(rs > 0) { - size_t ri = rs > bs ? bs : rs; - PyObject *arglist = Py_BuildValue("(n)", ri); - PyObject * result = PyObject_CallObject(callback, arglist); - Py_DECREF(arglist); - if (result == NULL) { - PyGILState_Release(gstate); - return 0; - } - if(!PyBytes_Check(result)) { - Py_DECREF(result); - PyErr_SetString(PyExc_RuntimeError, - "read callback did not return a bytes object"); - PyGILState_Release(gstate); - throw faiss::FaissException("reader error"); - } - size_t sz = PyBytes_Size(result); - if (sz == 0 || sz > rs) { - Py_DECREF(result); - PyErr_Format(PyExc_RuntimeError, - "read callback returned %ld bytes (asked %ld)", - sz, rs); - PyGILState_Release(gstate); - throw faiss::FaissException("reader error"); - } - memcpy(ptr, PyBytes_AsString(result), sz); - Py_DECREF(result); - ptr += sz; - rs -= sz; - } - PyGILState_Release(gstate); - return nitems; - } - - ~PyCallbackIOReader() { - Py_DECREF(callback); - } - -}; - -#endif - -%} - - - -%inline %{ - void wait() { - // in gdb, use return to get out of this function - for(int i = 0; i == 0; i += 0); - } - %} - -// End of file... diff --git a/core/src/index/thirdparty/faiss/tests/Makefile b/core/src/index/thirdparty/faiss/tests/Makefile deleted file mode 100644 index 684100de70..0000000000 --- a/core/src/index/thirdparty/faiss/tests/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - --include ../makefile.inc - -TESTS_SRC = $(wildcard *.cpp) -TESTS_OBJ = $(TESTS_SRC:.cpp=.o) - - -all: run - -run: tests - ./tests - -tests: $(TESTS_OBJ) ../libfaiss.a gtest/make/gtest_main.a - $(CXX) -o $@ $^ $(LDFLAGS) $(LIBS) - -%.o: %.cpp gtest - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(CPUFLAGS) -c -o $@ $< -Igtest/include -I.. - -gtest/make/gtest_main.a: gtest - $(MAKE) -C gtest/make CXX="$(CXX)" CXXFLAGS="$(CXXFLAGS)" gtest_main.a - -gtest: - curl -L https://github.com/google/googletest/archive/release-1.8.0.tar.gz | tar xz && \ - mv googletest-release-1.8.0/googletest gtest && \ - rm -rf googletest-release-1.8.0 - -clean: - rm -f tests - rm -f $(TESTS_OBJ) - rm -rf gtest - - -.PHONY: all clean run diff --git a/core/src/index/thirdparty/faiss/tests/common.py b/core/src/index/thirdparty/faiss/tests/common.py deleted file mode 100644 index 8621dd822a..0000000000 --- a/core/src/index/thirdparty/faiss/tests/common.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -# a few common functions for the tests - -from __future__ import absolute_import, division, print_function, unicode_literals - -import numpy as np -import faiss - -# reduce number of threads to avoid excessive nb of threads in opt -# mode (recuces runtime from 100s to 4s!) -faiss.omp_set_num_threads(4) - - -def random_unitary(n, d, seed): - x = faiss.randn(n * d, seed).reshape(n, d) - faiss.normalize_L2(x) - return x - - -class Randu10k: - - def __init__(self): - self.nb = 10000 - self.nq = 1000 - self.nt = 10000 - self.d = 128 - - self.xb = random_unitary(self.nb, self.d, 1) - self.xt = random_unitary(self.nt, self.d, 2) - self.xq = random_unitary(self.nq, self.d, 3) - - dotprods = np.dot(self.xq, self.xb.T) - self.gt = dotprods.argmax(1) - self.k = 100 - - def launch(self, name, index): - if not index.is_trained: - index.train(self.xt) - index.add(self.xb) - return index.search(self.xq, self.k) - - def evalres(self, DI): - D, I = DI - e = {} - for rank in 1, 10, 100: - e[rank] = ((I[:, :rank] == self.gt.reshape(-1, 1)).sum() / - float(self.nq)) - print("1-recalls: %s" % e) - return e - - -class Randu10kUnbalanced(Randu10k): - - def __init__(self): - Randu10k.__init__(self) - - weights = 0.95 ** np.arange(self.d) - rs = np.random.RandomState(123) - weights = weights[rs.permutation(self.d)] - self.xb *= weights - self.xb /= np.linalg.norm(self.xb, axis=1)[:, np.newaxis] - self.xq *= weights - self.xq /= np.linalg.norm(self.xq, axis=1)[:, np.newaxis] - self.xt *= weights - self.xt /= np.linalg.norm(self.xt, axis=1)[:, np.newaxis] - - dotprods = np.dot(self.xq, self.xb.T) - self.gt = dotprods.argmax(1) - self.k = 100 - - -def get_dataset(d, nb, nt, nq): - rs = np.random.RandomState(123) - xb = rs.rand(nb, d).astype('float32') - xt = rs.rand(nt, d).astype('float32') - xq = rs.rand(nq, d).astype('float32') - - return (xt, xb, xq) - - -def get_dataset_2(d, nt, nb, nq): - """A dataset that is not completely random but still challenging to - index - """ - d1 = 10 # intrinsic dimension (more or less) - n = nb + nt + nq - rs = np.random.RandomState(1338) - x = rs.normal(size=(n, d1)) - x = np.dot(x, rs.rand(d1, d)) - # now we have a d1-dim ellipsoid in d-dimensional space - # higher factor (>4) -> higher frequency -> less linear - x = x * (rs.rand(d) * 4 + 0.1) - x = np.sin(x) - x = x.astype('float32') - return x[:nt], x[nt:nt + nb], x[nt + nb:] - - -def make_binary_dataset(d, nt, nb, nq): - assert d % 8 == 0 - rs = np.random.RandomState(123) - x = rs.randint(256, size=(nb + nq + nt, int(d / 8))).astype('uint8') - return x[:nt], x[nt:-nq], x[-nq:] - - -def compare_binary_result_lists(D1, I1, D2, I2): - """comparing result lists is difficult because there are many - ties. Here we sort by (distance, index) pairs and ignore the largest - distance of each result. Compatible result lists should pass this.""" - assert D1.shape == I1.shape == D2.shape == I2.shape - n, k = D1.shape - ndiff = (D1 != D2).sum() - assert ndiff == 0, '%d differences in distance matrix %s' % ( - ndiff, D1.shape) - - def normalize_DI(D, I): - norm = I.max() + 1.0 - Dr = D.astype('float64') + I / norm - # ignore -1s and elements on last column - Dr[I1 == -1] = 1e20 - Dr[D == D[:, -1:]] = 1e20 - Dr.sort(axis=1) - return Dr - ndiff = (normalize_DI(D1, I1) != normalize_DI(D2, I2)).sum() - assert ndiff == 0, '%d differences in normalized D matrix' % ndiff diff --git a/core/src/index/thirdparty/faiss/tests/test_binary_factory.py b/core/src/index/thirdparty/faiss/tests/test_binary_factory.py deleted file mode 100644 index 70ddbb6e99..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_binary_factory.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -from __future__ import absolute_import, division, print_function - -import unittest -import faiss - - -class TestBinaryFactory(unittest.TestCase): - - def test_factory_IVF(self): - - index = faiss.index_binary_factory(16, "BIVF10") - assert index.invlists is not None - assert index.nlist == 10 - assert index.code_size == 2 - - def test_factory_Flat(self): - - index = faiss.index_binary_factory(16, "BFlat") - assert index.code_size == 2 - - def test_factory_HNSW(self): - - index = faiss.index_binary_factory(256, "BHNSW32") - assert index.code_size == 32 - - def test_factory_IVF_HNSW(self): - - index = faiss.index_binary_factory(256, "BIVF1024_BHNSW32") - assert index.code_size == 32 - assert index.nlist == 1024 diff --git a/core/src/index/thirdparty/faiss/tests/test_binary_flat.cpp b/core/src/index/thirdparty/faiss/tests/test_binary_flat.cpp deleted file mode 100644 index eb20cee87b..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_binary_flat.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -#include - -#include -#include - -TEST(BinaryFlat, accuracy) { - // dimension of the vectors to index - int d = 64; - - // size of the database we plan to index - size_t nb = 1000; - - // make the index object and train it - faiss::IndexBinaryFlat index(d); - - srand(35); - - std::vector database(nb * (d / 8)); - for (size_t i = 0; i < nb * (d / 8); i++) { - database[i] = rand() % 0x100; - } - - { // populating the database - index.add(nb, database.data()); - } - - size_t nq = 200; - - { // searching the database - - std::vector queries(nq * (d / 8)); - for (size_t i = 0; i < nq * (d / 8); i++) { - queries[i] = rand() % 0x100; - } - - int k = 5; - std::vector nns(k * nq); - std::vector dis(k * nq); - - index.search(nq, queries.data(), k, dis.data(), nns.data()); - - for (size_t i = 0; i < nq; ++i) { - faiss::HammingComputer8 hc(queries.data() + i * (d / 8), d / 8); - hamdis_t dist_min = hc.hamming(database.data()); - for (size_t j = 1; j < nb; ++j) { - hamdis_t dist = hc.hamming(database.data() + j * (d / 8)); - if (dist < dist_min) { - dist_min = dist; - } - } - EXPECT_EQ(dist_min, dis[k * i]); - } - } -} diff --git a/core/src/index/thirdparty/faiss/tests/test_binary_hashindex.py b/core/src/index/thirdparty/faiss/tests/test_binary_hashindex.py deleted file mode 100644 index 1ee5a5f7da..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_binary_hashindex.py +++ /dev/null @@ -1,183 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#!/usr/bin/env python3 - -import unittest -import numpy as np -import faiss - -from common import make_binary_dataset - - -def bitvec_shuffle(a, order): - n, d = a.shape - db, = order.shape - b = np.empty((n, db // 8), dtype='uint8') - faiss.bitvec_shuffle( - n, d * 8, db, - faiss.swig_ptr(order), - faiss.swig_ptr(a), faiss.swig_ptr(b)) - return b - - -class TestSmallFuncs(unittest.TestCase): - - def test_shuffle(self): - d = 256 - n = 1000 - rs = np.random.RandomState(123) - o = rs.permutation(d).astype('int32') - - x = rs.randint(256, size=(n, d // 8)).astype('uint8') - - y1 = bitvec_shuffle(x, o[:128]) - y2 = bitvec_shuffle(x, o[128:]) - y = np.hstack((y1, y2)) - - oinv = np.empty(d, dtype='int32') - oinv[o] = np.arange(d) - z = bitvec_shuffle(y, oinv) - - np.testing.assert_array_equal(x, z) - - -class TestRange(unittest.TestCase): - - def test_hash(self): - d = 128 - nq = 100 - nb = 2000 - - (_, xb, xq) = make_binary_dataset(d, 0, nb, nq) - - index_ref = faiss.IndexBinaryFlat(d) - index_ref.add(xb) - - radius = 55 - - Lref, Dref, Iref = index_ref.range_search(xq, radius) - - print("nb res: ", Lref[-1]) - - index = faiss.IndexBinaryHash(d, 10) - index.add(xb) - # index.display() - nfound = [] - ndis = [] - stats = faiss.cvar.indexBinaryHash_stats - for n_bitflips in range(index.b + 1): - index.nflip = n_bitflips - stats.reset() - Lnew, Dnew, Inew = index.range_search(xq, radius) - for i in range(nq): - ref = Iref[Lref[i]:Lref[i + 1]] - new = Inew[Lnew[i]:Lnew[i + 1]] - snew = set(new) - # no duplicates - self.assertTrue(len(new) == len(snew)) - # subset of real results - self.assertTrue(snew <= set(ref)) - nfound.append(Lnew[-1]) - ndis.append(stats.ndis) - print('nfound=', nfound) - print('ndis=', ndis) - nfound = np.array(nfound) - self.assertTrue(nfound[-1] == Lref[-1]) - self.assertTrue(np.all(nfound[1:] >= nfound[:-1])) - - def test_multihash(self): - d = 128 - nq = 100 - nb = 2000 - - (_, xb, xq) = make_binary_dataset(d, 0, nb, nq) - - index_ref = faiss.IndexBinaryFlat(d) - index_ref.add(xb) - - radius = 55 - - Lref, Dref, Iref = index_ref.range_search(xq, radius) - - print("nb res: ", Lref[-1]) - - nfound = [] - ndis = [] - - for nh in 1, 3, 5: - index = faiss.IndexBinaryMultiHash(d, nh, 10) - index.add(xb) - # index.display() - stats = faiss.cvar.indexBinaryHash_stats - index.nflip = 2 - stats.reset() - Lnew, Dnew, Inew = index.range_search(xq, radius) - for i in range(nq): - ref = Iref[Lref[i]:Lref[i + 1]] - new = Inew[Lnew[i]:Lnew[i + 1]] - snew = set(new) - # no duplicates - self.assertTrue(len(new) == len(snew)) - # subset of real results - self.assertTrue(snew <= set(ref)) - nfound.append(Lnew[-1]) - ndis.append(stats.ndis) - print('nfound=', nfound) - print('ndis=', ndis) - nfound = np.array(nfound) - # self.assertTrue(nfound[-1] == Lref[-1]) - self.assertTrue(np.all(nfound[1:] >= nfound[:-1])) - - -class TestKnn(unittest.TestCase): - - def test_hash_and_multihash(self): - d = 128 - nq = 100 - nb = 2000 - - (_, xb, xq) = make_binary_dataset(d, 0, nb, nq) - - index_ref = faiss.IndexBinaryFlat(d) - index_ref.add(xb) - k = 10 - Dref, Iref = index_ref.search(xq, k) - - nfound = {} - for nh in 0, 1, 3, 5: - - for nbit in 4, 7: - if nh == 0: - index = faiss.IndexBinaryHash(d, nbit) - else: - index = faiss.IndexBinaryMultiHash(d, nh, nbit) - index.add(xb) - index.nflip = 2 - Dnew, Inew = index.search(xq, k) - nf = 0 - for i in range(nq): - ref = Iref[i] - new = Inew[i] - snew = set(new) - # no duplicates - self.assertTrue(len(new) == len(snew)) - nf += len(set(ref) & snew) - print('nfound', nh, nbit, nf) - nfound[(nh, nbit)] = nf - self.assertGreater(nfound[(nh, 4)], nfound[(nh, 7)]) - - # test serialization - index2 = faiss.deserialize_index_binary( - faiss.serialize_index_binary(index)) - - D2, I2 = index2.search(xq, k) - np.testing.assert_array_equal(Inew, I2) - np.testing.assert_array_equal(Dnew, D2) - - print('nfound=', nfound) - self.assertGreater(3, abs(nfound[(0, 7)] - nfound[(1, 7)])) - self.assertGreater(nfound[(3, 7)], nfound[(1, 7)]) - self.assertGreater(nfound[(5, 7)], nfound[(3, 7)]) diff --git a/core/src/index/thirdparty/faiss/tests/test_binary_io.py b/core/src/index/thirdparty/faiss/tests/test_binary_io.py deleted file mode 100644 index 4af7dab9ca..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_binary_io.py +++ /dev/null @@ -1,217 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -"""Binary indexes (de)serialization""" - -from __future__ import absolute_import, division, print_function, unicode_literals - -import numpy as np -import unittest -import faiss -import os -import tempfile - -def make_binary_dataset(d, nb, nt, nq): - assert d % 8 == 0 - x = np.random.randint(256, size=(nb + nq + nt, int(d / 8))).astype('uint8') - return x[:nt], x[nt:-nq], x[-nq:] - - -class TestBinaryFlat(unittest.TestCase): - - def __init__(self, *args, **kwargs): - unittest.TestCase.__init__(self, *args, **kwargs) - d = 32 - nt = 0 - nb = 1500 - nq = 500 - - (_, self.xb, self.xq) = make_binary_dataset(d, nb, nt, nq) - - def test_flat(self): - d = self.xq.shape[1] * 8 - - index = faiss.IndexBinaryFlat(d) - index.add(self.xb) - D, I = index.search(self.xq, 3) - - _, tmpnam = tempfile.mkstemp() - try: - faiss.write_index_binary(index, tmpnam) - - index2 = faiss.read_index_binary(tmpnam) - - D2, I2 = index2.search(self.xq, 3) - - assert (I2 == I).all() - assert (D2 == D).all() - - finally: - os.remove(tmpnam) - - -class TestBinaryIVF(unittest.TestCase): - - def __init__(self, *args, **kwargs): - unittest.TestCase.__init__(self, *args, **kwargs) - d = 32 - nt = 200 - nb = 1500 - nq = 500 - - (self.xt, self.xb, self.xq) = make_binary_dataset(d, nb, nt, nq) - - def test_ivf_flat(self): - d = self.xq.shape[1] * 8 - - quantizer = faiss.IndexBinaryFlat(d) - index = faiss.IndexBinaryIVF(quantizer, d, 8) - index.cp.min_points_per_centroid = 5 # quiet warning - index.nprobe = 4 - index.train(self.xt) - index.add(self.xb) - D, I = index.search(self.xq, 3) - - _, tmpnam = tempfile.mkstemp() - - try: - faiss.write_index_binary(index, tmpnam) - - index2 = faiss.read_index_binary(tmpnam) - - D2, I2 = index2.search(self.xq, 3) - - assert (I2 == I).all() - assert (D2 == D).all() - - finally: - os.remove(tmpnam) - - -class TestObjectOwnership(unittest.TestCase): - - def __init__(self, *args, **kwargs): - unittest.TestCase.__init__(self, *args, **kwargs) - d = 32 - nt = 200 - nb = 1500 - nq = 500 - - (self.xt, self.xb, self.xq) = make_binary_dataset(d, nb, nt, nq) - - def test_read_index_ownership(self): - d = self.xq.shape[1] * 8 - - index = faiss.IndexBinaryFlat(d) - index.add(self.xb) - - _, tmpnam = tempfile.mkstemp() - try: - faiss.write_index_binary(index, tmpnam) - - index2 = faiss.read_index_binary(tmpnam) - - assert index2.thisown - finally: - os.remove(tmpnam) - - -class TestBinaryFromFloat(unittest.TestCase): - - def __init__(self, *args, **kwargs): - unittest.TestCase.__init__(self, *args, **kwargs) - d = 32 - nt = 200 - nb = 1500 - nq = 500 - - (self.xt, self.xb, self.xq) = make_binary_dataset(d, nb, nt, nq) - - def test_binary_from_float(self): - d = self.xq.shape[1] * 8 - - float_index = faiss.IndexHNSWFlat(d, 16) - index = faiss.IndexBinaryFromFloat(float_index) - index.add(self.xb) - D, I = index.search(self.xq, 3) - - _, tmpnam = tempfile.mkstemp() - - try: - faiss.write_index_binary(index, tmpnam) - - index2 = faiss.read_index_binary(tmpnam) - - D2, I2 = index2.search(self.xq, 3) - - assert (I2 == I).all() - assert (D2 == D).all() - - finally: - os.remove(tmpnam) - - -class TestBinaryHNSW(unittest.TestCase): - - def __init__(self, *args, **kwargs): - unittest.TestCase.__init__(self, *args, **kwargs) - d = 32 - nt = 200 - nb = 1500 - nq = 500 - - (self.xt, self.xb, self.xq) = make_binary_dataset(d, nb, nt, nq) - - def test_hnsw(self): - d = self.xq.shape[1] * 8 - - index = faiss.IndexBinaryHNSW(d) - index.add(self.xb) - D, I = index.search(self.xq, 3) - - _, tmpnam = tempfile.mkstemp() - - try: - faiss.write_index_binary(index, tmpnam) - - index2 = faiss.read_index_binary(tmpnam) - - D2, I2 = index2.search(self.xq, 3) - - assert (I2 == I).all() - assert (D2 == D).all() - - finally: - os.remove(tmpnam) - - def test_ivf_hnsw(self): - d = self.xq.shape[1] * 8 - - quantizer = faiss.IndexBinaryHNSW(d) - index = faiss.IndexBinaryIVF(quantizer, d, 8) - index.cp.min_points_per_centroid = 5 # quiet warning - index.nprobe = 4 - index.train(self.xt) - index.add(self.xb) - D, I = index.search(self.xq, 3) - - _, tmpnam = tempfile.mkstemp() - - try: - faiss.write_index_binary(index, tmpnam) - - index2 = faiss.read_index_binary(tmpnam) - - D2, I2 = index2.search(self.xq, 3) - - assert (I2 == I).all() - assert (D2 == D).all() - - finally: - os.remove(tmpnam) - - -if __name__ == '__main__': - unittest.main() diff --git a/core/src/index/thirdparty/faiss/tests/test_build_blocks.py b/core/src/index/thirdparty/faiss/tests/test_build_blocks.py deleted file mode 100644 index d1ce73cd1b..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_build_blocks.py +++ /dev/null @@ -1,600 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -from __future__ import absolute_import, division, print_function - -import numpy as np - -import faiss -import unittest - -from common import get_dataset_2 - - - -class TestClustering(unittest.TestCase): - - def test_clustering(self): - d = 64 - n = 1000 - rs = np.random.RandomState(123) - x = rs.uniform(size=(n, d)).astype('float32') - - x *= 10 - - km = faiss.Kmeans(d, 32, niter=10) - err32 = km.train(x) - - # check that objective is decreasing - prev = 1e50 - for o in km.obj: - self.assertGreater(prev, o) - prev = o - - km = faiss.Kmeans(d, 64, niter=10) - err64 = km.train(x) - - # check that 64 centroids give a lower quantization error than 32 - self.assertGreater(err32, err64) - - km = faiss.Kmeans(d, 32, niter=10, int_centroids=True) - err_int = km.train(x) - - # check that integer centoids are not as good as float ones - self.assertGreater(err_int, err32) - self.assertTrue(np.all(km.centroids == np.floor(km.centroids))) - - - def test_nasty_clustering(self): - d = 2 - rs = np.random.RandomState(123) - x = np.zeros((100, d), dtype='float32') - for i in range(5): - x[i * 20:i * 20 + 20] = rs.uniform(size=d) - - # we have 5 distinct points but ask for 10 centroids... - km = faiss.Kmeans(d, 10, niter=10, verbose=True) - km.train(x) - - def test_redo(self): - d = 64 - n = 1000 - - rs = np.random.RandomState(123) - x = rs.uniform(size=(n, d)).astype('float32') - - # make sure that doing 10 redos yields a better objective than just 1 - - clus = faiss.Clustering(d, 20) - clus.nredo = 1 - clus.train(x, faiss.IndexFlatL2(d)) - obj1 = clus.iteration_stats.at(clus.iteration_stats.size() - 1).obj - - clus = faiss.Clustering(d, 20) - clus.nredo = 10 - clus.train(x, faiss.IndexFlatL2(d)) - obj10 = clus.iteration_stats.at(clus.iteration_stats.size() - 1).obj - - self.assertGreater(obj1, obj10) - - def test_1ptpercluster(self): - # https://github.com/facebookresearch/faiss/issues/842 - X = np.random.randint(0, 1, (5, 10)).astype('float32') - k = 5 - niter = 10 - verbose = True - kmeans = faiss.Kmeans(X.shape[1], k, niter=niter, verbose=verbose) - kmeans.train(X) - l2_distances, I = kmeans.index.search(X, 1) - - def test_weighted(self): - d = 32 - sigma = 0.1 - - # Data is naturally clustered in 10 clusters. - # 5 clusters have 100 points - # 5 clusters have 10 points - # run k-means with 5 clusters - - ccent = faiss.randn((10, d), 123) - faiss.normalize_L2(ccent) - x = [ccent[i] + sigma * faiss.randn((100, d), 1234 + i) for i in range(5)] - x += [ccent[i] + sigma * faiss.randn((10, d), 1234 + i) for i in range(5, 10)] - x = np.vstack(x) - - clus = faiss.Clustering(d, 5) - index = faiss.IndexFlatL2(d) - clus.train(x, index) - cdis1, perm1 = index.search(ccent, 1) - - # distance^2 of ground-truth centroids to clusters - cdis1_first = cdis1[:5].sum() - cdis1_last = cdis1[5:].sum() - - # now assign weight 0.1 to the 5 first clusters and weight 10 - # to the 5 last ones and re-run k-means - weights = np.ones(100 * 5 + 10 * 5, dtype='float32') - weights[:100 * 5] = 0.1 - weights[100 * 5:] = 10 - - clus = faiss.Clustering(d, 5) - index = faiss.IndexFlatL2(d) - clus.train(x, index, weights=weights) - cdis2, perm2 = index.search(ccent, 1) - - # distance^2 of ground-truth centroids to clusters - cdis2_first = cdis2[:5].sum() - cdis2_last = cdis2[5:].sum() - - print(cdis1_first, cdis1_last) - print(cdis2_first, cdis2_last) - - # with the new clustering, the last should be much (*2) closer - # to their centroids - self.assertGreater(cdis1_last, cdis1_first * 2) - self.assertGreater(cdis2_first, cdis2_last * 2) - - def test_encoded(self): - d = 32 - k = 5 - xt, xb, xq = get_dataset_2(d, 1000, 0, 0) - - # make sure that training on a compressed then decompressed - # dataset gives the same result as decompressing on-the-fly - - codec = faiss.IndexScalarQuantizer(d, faiss.ScalarQuantizer.QT_4bit) - codec.train(xt) - codes = codec.sa_encode(xt) - - xt2 = codec.sa_decode(codes) - - clus = faiss.Clustering(d, k) - # clus.verbose = True - clus.niter = 0 - index = faiss.IndexFlatL2(d) - clus.train(xt2, index) - ref_centroids = faiss.vector_to_array(clus.centroids).reshape(-1, d) - - _, ref_errs = index.search(xt2, 1) - - clus = faiss.Clustering(d, k) - # clus.verbose = True - clus.niter = 0 - clus.decode_block_size = 120 - index = faiss.IndexFlatL2(d) - clus.train_encoded(codes, codec, index) - new_centroids = faiss.vector_to_array(clus.centroids).reshape(-1, d) - - _, new_errs = index.search(xt2, 1) - - # It's the same operation, so should be bit-exact the same - self.assertTrue(np.all(ref_centroids == new_centroids)) - - -class TestPCA(unittest.TestCase): - - def test_pca(self): - d = 64 - n = 1000 - np.random.seed(123) - x = np.random.random(size=(n, d)).astype('float32') - - pca = faiss.PCAMatrix(d, 10) - pca.train(x) - y = pca.apply_py(x) - - # check that energy per component is decreasing - column_norm2 = (y**2).sum(0) - - prev = 1e50 - for o in column_norm2: - self.assertGreater(prev, o) - prev = o - - -class TestProductQuantizer(unittest.TestCase): - - def test_pq(self): - d = 64 - n = 2000 - cs = 4 - np.random.seed(123) - x = np.random.random(size=(n, d)).astype('float32') - pq = faiss.ProductQuantizer(d, cs, 8) - pq.train(x) - codes = pq.compute_codes(x) - x2 = pq.decode(codes) - diff = ((x - x2)**2).sum() - - # print("diff=", diff) - # diff= 4418.0562 - self.assertGreater(5000, diff) - - pq10 = faiss.ProductQuantizer(d, cs, 10) - assert pq10.code_size == 5 - pq10.verbose = True - pq10.cp.verbose = True - pq10.train(x) - codes = pq10.compute_codes(x) - - x10 = pq10.decode(codes) - diff10 = ((x - x10)**2).sum() - self.assertGreater(diff, diff10) - - def do_test_codec(self, nbit): - pq = faiss.ProductQuantizer(16, 2, nbit) - - # simulate training - rs = np.random.RandomState(123) - centroids = rs.rand(2, 1 << nbit, 8).astype('float32') - faiss.copy_array_to_vector(centroids.ravel(), pq.centroids) - - idx = rs.randint(1 << nbit, size=(100, 2)) - # can be encoded exactly - x = np.hstack(( - centroids[0, idx[:, 0]], - centroids[1, idx[:, 1]] - )) - - # encode / decode - codes = pq.compute_codes(x) - xr = pq.decode(codes) - assert np.all(xr == x) - - # encode w/ external index - assign_index = faiss.IndexFlatL2(8) - pq.assign_index = assign_index - codes2 = np.empty((100, pq.code_size), dtype='uint8') - pq.compute_codes_with_assign_index( - faiss.swig_ptr(x), faiss.swig_ptr(codes2), 100) - assert np.all(codes == codes2) - - def test_codec(self): - for i in range(16): - print("Testing nbits=%d" % (i + 1)) - self.do_test_codec(i + 1) - - -class TestRevSwigPtr(unittest.TestCase): - - def test_rev_swig_ptr(self): - - index = faiss.IndexFlatL2(4) - xb0 = np.vstack([ - i * 10 + np.array([1, 2, 3, 4], dtype='float32') - for i in range(5)]) - index.add(xb0) - xb = faiss.rev_swig_ptr(index.xb.data(), 4 * 5).reshape(5, 4) - self.assertEqual(np.abs(xb0 - xb).sum(), 0) - - -class TestException(unittest.TestCase): - - def test_exception(self): - - index = faiss.IndexFlatL2(10) - - a = np.zeros((5, 10), dtype='float32') - b = np.zeros(5, dtype='int64') - - # an unsupported operation for IndexFlat - self.assertRaises( - RuntimeError, - index.add_with_ids, a, b - ) - # assert 'add_with_ids not implemented' in str(e) - - def test_exception_2(self): - self.assertRaises( - RuntimeError, - faiss.index_factory, 12, 'IVF256,Flat,PQ8' - ) - # assert 'could not parse' in str(e) - - -class TestMapLong2Long(unittest.TestCase): - - def test_maplong2long(self): - keys = np.array([13, 45, 67]) - vals = np.array([3, 8, 2]) - - m = faiss.MapLong2Long() - m.add(keys, vals) - - assert np.all(m.search_multiple(keys) == vals) - - assert m.search(12343) == -1 - - -class TestOrthognalReconstruct(unittest.TestCase): - - def test_recons_orthonormal(self): - lt = faiss.LinearTransform(20, 10, True) - rs = np.random.RandomState(10) - A, _ = np.linalg.qr(rs.randn(20, 20)) - A = A[:10].astype('float32') - faiss.copy_array_to_vector(A.ravel(), lt.A) - faiss.copy_array_to_vector(rs.randn(10).astype('float32'), lt.b) - - lt.set_is_orthonormal() - lt.is_trained = True - assert lt.is_orthonormal - - x = rs.rand(30, 20).astype('float32') - xt = lt.apply_py(x) - xtt = lt.reverse_transform(xt) - xttt = lt.apply_py(xtt) - - err = ((xt - xttt)**2).sum() - - self.assertGreater(1e-5, err) - - def test_recons_orthogona_impossible(self): - lt = faiss.LinearTransform(20, 10, True) - rs = np.random.RandomState(10) - A = rs.randn(10 * 20).astype('float32') - faiss.copy_array_to_vector(A.ravel(), lt.A) - faiss.copy_array_to_vector(rs.randn(10).astype('float32'), lt.b) - lt.is_trained = True - - lt.set_is_orthonormal() - assert not lt.is_orthonormal - - x = rs.rand(30, 20).astype('float32') - xt = lt.apply_py(x) - try: - lt.reverse_transform(xt) - except Exception: - pass - else: - self.assertFalse('should do an exception') - - -class TestMAdd(unittest.TestCase): - - def test_1(self): - # try with dimensions that are multiples of 16 or not - rs = np.random.RandomState(123) - swig_ptr = faiss.swig_ptr - for dim in 16, 32, 20, 25: - for _repeat in 1, 2, 3, 4, 5: - a = rs.rand(dim).astype('float32') - b = rs.rand(dim).astype('float32') - c = np.zeros(dim, dtype='float32') - bf = rs.uniform(5.0) - 2.5 - idx = faiss.fvec_madd_and_argmin( - dim, swig_ptr(a), bf, swig_ptr(b), - swig_ptr(c)) - ref_c = a + b * bf - assert np.abs(c - ref_c).max() < 1e-5 - assert idx == ref_c.argmin() - - -class TestNyFuncs(unittest.TestCase): - - def test_l2(self): - rs = np.random.RandomState(123) - swig_ptr = faiss.swig_ptr - for d in 1, 2, 4, 8, 12, 16: - x = rs.rand(d).astype('float32') - for ny in 128, 129, 130: - print("d=%d ny=%d" % (d, ny)) - y = rs.rand(ny, d).astype('float32') - ref = ((x - y) ** 2).sum(1) - new = np.zeros(ny, dtype='float32') - faiss.fvec_L2sqr_ny(swig_ptr(new), swig_ptr(x), - swig_ptr(y), d, ny) - assert np.abs(ref - new).max() < 1e-4 - - def test_IP(self): - # this one is not optimized with SIMD but just in case - rs = np.random.RandomState(123) - swig_ptr = faiss.swig_ptr - for d in 1, 2, 4, 8, 12, 16: - x = rs.rand(d).astype('float32') - for ny in 128, 129, 130: - print("d=%d ny=%d" % (d, ny)) - y = rs.rand(ny, d).astype('float32') - ref = (x * y).sum(1) - new = np.zeros(ny, dtype='float32') - faiss.fvec_inner_products_ny( - swig_ptr(new), swig_ptr(x), swig_ptr(y), d, ny) - assert np.abs(ref - new).max() < 1e-4 - - -class TestMatrixStats(unittest.TestCase): - - def test_0s(self): - rs = np.random.RandomState(123) - m = rs.rand(40, 20).astype('float32') - m[5:10] = 0 - comments = faiss.MatrixStats(m).comments - print(comments) - assert 'has 5 copies' in comments - assert '5 null vectors' in comments - - def test_copies(self): - rs = np.random.RandomState(123) - m = rs.rand(40, 20).astype('float32') - m[::2] = m[1::2] - comments = faiss.MatrixStats(m).comments - print(comments) - assert '20 vectors are distinct' in comments - - def test_dead_dims(self): - rs = np.random.RandomState(123) - m = rs.rand(40, 20).astype('float32') - m[:, 5:10] = 0 - comments = faiss.MatrixStats(m).comments - print(comments) - assert '5 dimensions are constant' in comments - - def test_rogue_means(self): - rs = np.random.RandomState(123) - m = rs.rand(40, 20).astype('float32') - m[:, 5:10] += 12345 - comments = faiss.MatrixStats(m).comments - print(comments) - assert '5 dimensions are too large wrt. their variance' in comments - - def test_normalized(self): - rs = np.random.RandomState(123) - m = rs.rand(40, 20).astype('float32') - faiss.normalize_L2(m) - comments = faiss.MatrixStats(m).comments - print(comments) - assert 'vectors are normalized' in comments - - -class TestScalarQuantizer(unittest.TestCase): - - def test_8bit_equiv(self): - rs = np.random.RandomState(123) - for _it in range(20): - for d in 13, 16, 24: - x = np.floor(rs.rand(5, d) * 256).astype('float32') - x[0] = 0 - x[1] = 255 - - # make sure to test extreme cases - x[2, 0] = 0 - x[3, 0] = 255 - x[2, 1] = 255 - x[3, 1] = 0 - - ref_index = faiss.IndexScalarQuantizer( - d, faiss.ScalarQuantizer.QT_8bit) - ref_index.train(x[:2]) - ref_index.add(x[2:3]) - - index = faiss.IndexScalarQuantizer( - d, faiss.ScalarQuantizer.QT_8bit_direct) - assert index.is_trained - index.add(x[2:3]) - - assert np.all( - faiss.vector_to_array(ref_index.codes) == - faiss.vector_to_array(index.codes)) - - # Note that distances are not the same because ref_index - # reconstructs x as x + 0.5 - D, I = index.search(x[3:], 1) - - # assert D[0, 0] == Dref[0, 0] - print(D[0, 0], ((x[3] - x[2]) ** 2).sum()) - assert D[0, 0] == ((x[3] - x[2]) ** 2).sum() - - def test_6bit_equiv(self): - rs = np.random.RandomState(123) - for d in 3, 6, 8, 16, 36: - trainset = np.zeros((2, d), dtype='float32') - trainset[0, :] = 0 - trainset[0, :] = 63 - - index = faiss.IndexScalarQuantizer( - d, faiss.ScalarQuantizer.QT_6bit) - index.train(trainset) - - print('cs=', index.code_size) - - x = rs.randint(64, size=(100, d)).astype('float32') - - # verify encoder / decoder - index.add(x) - x2 = index.reconstruct_n(0, x.shape[0]) - assert np.all(x == x2 - 0.5) - - # verify AVX decoder (used only for search) - y = 63 * rs.rand(20, d).astype('float32') - - D, I = index.search(y, 10) - for i in range(20): - for j in range(10): - dis = ((y[i] - x2[I[i, j]]) ** 2).sum() - print(dis, D[i, j]) - assert abs(D[i, j] - dis) / dis < 1e-5 - -class TestRandom(unittest.TestCase): - - def test_rand(self): - x = faiss.rand(2000) - assert np.all(x >= 0) and np.all(x < 1) - h, _ = np.histogram(x, np.arange(0, 1, 0.1)) - assert h.min() > 160 and h.max() < 240 - - def test_randint(self): - x = faiss.randint(20000, vmax=100) - assert np.all(x >= 0) and np.all(x < 100) - c = np.bincount(x, minlength=100) - print(c) - assert c.max() - c.min() < 50 * 2 - - -class TestPairwiseDis(unittest.TestCase): - - def test_L2(self): - swig_ptr = faiss.swig_ptr - x = faiss.rand((100, 10), seed=1) - y = faiss.rand((200, 10), seed=2) - ix = faiss.randint(50, vmax=100) - iy = faiss.randint(50, vmax=200) - dis = np.empty(50, dtype='float32') - faiss.pairwise_indexed_L2sqr( - 10, 50, - swig_ptr(x), swig_ptr(ix), - swig_ptr(y), swig_ptr(iy), - swig_ptr(dis)) - - for i in range(50): - assert np.allclose( - dis[i], ((x[ix[i]] - y[iy[i]]) ** 2).sum()) - - def test_IP(self): - swig_ptr = faiss.swig_ptr - x = faiss.rand((100, 10), seed=1) - y = faiss.rand((200, 10), seed=2) - ix = faiss.randint(50, vmax=100) - iy = faiss.randint(50, vmax=200) - dis = np.empty(50, dtype='float32') - faiss.pairwise_indexed_inner_product( - 10, 50, - swig_ptr(x), swig_ptr(ix), - swig_ptr(y), swig_ptr(iy), - swig_ptr(dis)) - - for i in range(50): - assert np.allclose( - dis[i], np.dot(x[ix[i]], y[iy[i]])) - - -class TestSWIGWrap(unittest.TestCase): - """ various regressions with the SWIG wrapper """ - - def test_size_t_ptr(self): - # issue 1064 - index = faiss.IndexHNSWFlat(10, 32) - - hnsw = index.hnsw - index.add(np.random.rand(100, 10).astype('float32')) - be = np.empty(2, 'uint64') - hnsw.neighbor_range(23, 0, faiss.swig_ptr(be), faiss.swig_ptr(be[1:])) - - def test_id_map_at(self): - # issue 1020 - n_features = 100 - feature_dims = 10 - - features = np.random.random((n_features, feature_dims)).astype(np.float32) - idx = np.arange(n_features).astype(np.int64) - - index = faiss.IndexFlatL2(feature_dims) - index = faiss.IndexIDMap2(index) - index.add_with_ids(features, idx) - - [index.id_map.at(int(i)) for i in range(index.ntotal)] - - -if __name__ == '__main__': - unittest.main() diff --git a/core/src/index/thirdparty/faiss/tests/test_dealloc_invlists.cpp b/core/src/index/thirdparty/faiss/tests/test_dealloc_invlists.cpp deleted file mode 100644 index d77cd242ac..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_dealloc_invlists.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include - -using namespace faiss; - -namespace { - -typedef Index::idx_t idx_t; - - -// dimension of the vectors to index -int d = 32; - -// nb of training vectors -size_t nt = 5000; - -// size of the database points per window step -size_t nb = 1000; - -// nb of queries -size_t nq = 200; - - -std::vector make_data(size_t n) -{ - std::vector database (n * d); - for (size_t i = 0; i < n * d; i++) { - database[i] = drand48(); - } - return database; -} - -std::unique_ptr make_trained_index(const char *index_type) -{ - auto index = std::unique_ptr(index_factory(d, index_type)); - auto xt = make_data(nt * d); - index->train(nt, xt.data()); - ParameterSpace().set_index_parameter (index.get(), "nprobe", 4); - return index; -} - -std::vector search_index(Index *index, const float *xq) { - int k = 10; - std::vector I(k * nq); - std::vector D(k * nq); - index->search (nq, xq, k, D.data(), I.data()); - return I; -} - - - - - -/************************************************************* - * Test functions for a given index type - *************************************************************/ - -struct EncapsulateInvertedLists: InvertedLists { - - const InvertedLists *il; - - EncapsulateInvertedLists(const InvertedLists *il): - InvertedLists(il->nlist, il->code_size), - il(il) - {} - - static void * memdup (const void *m, size_t size) { - if (size == 0) return nullptr; - return memcpy (malloc(size), m, size); - } - - size_t list_size(size_t list_no) const override { - return il->list_size (list_no); - } - - const uint8_t * get_codes (size_t list_no) const override { - return (uint8_t*)memdup (il->get_codes(list_no), - list_size(list_no) * code_size); - } - - const idx_t * get_ids (size_t list_no) const override { - return (idx_t*)memdup (il->get_ids(list_no), - list_size(list_no) * sizeof(idx_t)); - } - - void release_codes (size_t, const uint8_t *codes) const override { - free ((void*)codes); - } - - void release_ids (size_t, const idx_t *ids) const override { - free ((void*)ids); - } - - const uint8_t * get_single_code (size_t list_no, size_t offset) - const override { - return (uint8_t*)memdup (il->get_single_code(list_no, offset), - code_size); - } - - size_t add_entries(size_t, size_t, const idx_t*, const uint8_t*) override { - assert(!"not implemented"); - return 0; - } - - void update_entries(size_t, size_t, size_t, const idx_t*, const uint8_t*) - override { - assert(!"not implemented"); - } - - void resize(size_t, size_t) override { - assert(!"not implemented"); - } - - ~EncapsulateInvertedLists() override {} -}; - - - -int test_dealloc_invlists (const char *index_key) { - - std::unique_ptr index = make_trained_index(index_key); - IndexIVF * index_ivf = ivflib::extract_index_ivf (index.get()); - - auto xb = make_data (nb * d); - index->add(nb, xb.data()); - - auto xq = make_data (nq * d); - - auto ref_res = search_index (index.get(), xq.data()); - - EncapsulateInvertedLists eil(index_ivf->invlists); - - index_ivf->own_invlists = false; - index_ivf->replace_invlists (&eil, false); - - // TEST: this could crash or leak mem - auto new_res = search_index (index.get(), xq.data()); - - // delete explicitly - delete eil.il; - - // just to make sure - EXPECT_EQ (ref_res, new_res); - return 0; -} - -} // anonymous namespace - - - -/************************************************************* - * Test entry points - *************************************************************/ - -TEST(TestIvlistDealloc, IVFFlat) { - test_dealloc_invlists ("IVF32,Flat"); -} - -TEST(TestIvlistDealloc, IVFSQ) { - test_dealloc_invlists ("IVF32,SQ8"); -} - -TEST(TestIvlistDealloc, IVFPQ) { - test_dealloc_invlists ("IVF32,PQ4np"); -} diff --git a/core/src/index/thirdparty/faiss/tests/test_extra_distances.py b/core/src/index/thirdparty/faiss/tests/test_extra_distances.py deleted file mode 100644 index 3977075879..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_extra_distances.py +++ /dev/null @@ -1,143 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#! /usr/bin/env python2 -# noqa E741 - -import numpy as np - -import faiss -import unittest - -from common import get_dataset_2 - -import scipy.spatial.distance - - -class TestExtraDistances(unittest.TestCase): - """ check wrt. the scipy implementation """ - - def make_example(self): - rs = np.random.RandomState(123) - x = rs.rand(5, 32).astype('float32') - y = rs.rand(3, 32).astype('float32') - return x, y - - def run_simple_dis_test(self, ref_func, metric_type): - xq, yb = self.make_example() - ref_dis = np.array([ - [ref_func(x, y) for y in yb] - for x in xq - ]) - new_dis = faiss.pairwise_distances(xq, yb, metric_type) - self.assertTrue(np.allclose(ref_dis, new_dis)) - - def test_L1(self): - self.run_simple_dis_test(scipy.spatial.distance.cityblock, - faiss.METRIC_L1) - - def test_Linf(self): - self.run_simple_dis_test(scipy.spatial.distance.chebyshev, - faiss.METRIC_Linf) - - def test_L2(self): - xq, yb = self.make_example() - ref_dis = np.array([ - [scipy.spatial.distance.sqeuclidean(x, y) for y in yb] - for x in xq - ]) - new_dis = faiss.pairwise_distances(xq, yb, faiss.METRIC_L2) - self.assertTrue(np.allclose(ref_dis, new_dis)) - - ref_dis = np.array([ - [scipy.spatial.distance.euclidean(x, y) for y in yb] - for x in xq - ]) - new_dis = np.sqrt(new_dis) # post processing - self.assertTrue(np.allclose(ref_dis, new_dis)) - - def test_Lp(self): - p = 1.5 - xq, yb = self.make_example() - ref_dis = np.array([ - [scipy.spatial.distance.minkowski(x, y, p) for y in yb] - for x in xq - ]) - new_dis = faiss.pairwise_distances(xq, yb, faiss.METRIC_Lp, p) - new_dis = new_dis ** (1 / p) # post processing - self.assertTrue(np.allclose(ref_dis, new_dis)) - - def test_canberra(self): - self.run_simple_dis_test(scipy.spatial.distance.canberra, - faiss.METRIC_Canberra) - - def test_braycurtis(self): - self.run_simple_dis_test(scipy.spatial.distance.braycurtis, - faiss.METRIC_BrayCurtis) - - def xx_test_jensenshannon(self): - # this distance does not seem to be implemented in scipy - # vectors should probably be L1 normalized - self.run_simple_dis_test(scipy.spatial.distance.jensenshannon, - faiss.METRIC_JensenShannon) - - -class TestKNN(unittest.TestCase): - """ test that the knn search gives the same as distance matrix + argmin """ - - def do_test_knn(self, mt): - d = 10 - nb = 100 - nq = 50 - nt = 0 - xt, xb, xq = get_dataset_2(d, nt, nb, nq) - - index = faiss.IndexFlat(d, mt) - index.add(xb) - - D, I = index.search(xq, 10) - - dis = faiss.pairwise_distances(xq, xb, mt) - o = dis.argsort(axis=1) - assert np.all(I == o[:, :10]) - - for q in range(nq): - assert np.all(D[q] == dis[q, I[q]]) - - index2 = faiss.deserialize_index(faiss.serialize_index(index)) - - D2, I2 = index2.search(xq, 10) - - self.assertTrue(np.all(I == I2)) - - def test_L1(self): - self.do_test_knn(faiss.METRIC_L1) - - def test_Linf(self): - self.do_test_knn(faiss.METRIC_Linf) - - -class TestHNSW(unittest.TestCase): - """ since it has a distance computer, HNSW should work """ - - def test_hnsw(self): - - d = 10 - nb = 1000 - nq = 100 - nt = 0 - xt, xb, xq = get_dataset_2(d, nt, nb, nq) - - mt = faiss.METRIC_L1 - - index = faiss.IndexHNSW(faiss.IndexFlat(d, mt)) - index.add(xb) - - D, I = index.search(xq, 10) - - dis = faiss.pairwise_distances(xq, xb, mt) - - for q in range(nq): - assert np.all(D[q] == dis[q, I[q]]) diff --git a/core/src/index/thirdparty/faiss/tests/test_factory.py b/core/src/index/thirdparty/faiss/tests/test_factory.py deleted file mode 100644 index e08b0ca850..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_factory.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -from __future__ import absolute_import, division, print_function - -import numpy as np -import unittest -import faiss - - -class TestFactory(unittest.TestCase): - - def test_factory_1(self): - - index = faiss.index_factory(12, "IVF10,PQ4") - assert index.do_polysemous_training - - index = faiss.index_factory(12, "IVF10,PQ4np") - assert not index.do_polysemous_training - - index = faiss.index_factory(12, "PQ4") - assert index.do_polysemous_training - - index = faiss.index_factory(12, "PQ4np") - assert not index.do_polysemous_training - - try: - index = faiss.index_factory(10, "PQ4") - except RuntimeError: - pass - else: - assert False, "should do a runtime error" - - def test_factory_2(self): - - index = faiss.index_factory(12, "SQ8") - assert index.code_size == 12 - - def test_factory_3(self): - - index = faiss.index_factory(12, "IVF10,PQ4") - faiss.ParameterSpace().set_index_parameter(index, "nprobe", 3) - assert index.nprobe == 3 - - index = faiss.index_factory(12, "PCAR8,IVF10,PQ4") - faiss.ParameterSpace().set_index_parameter(index, "nprobe", 3) - assert faiss.downcast_index(index.index).nprobe == 3 - - def test_factory_4(self): - index = faiss.index_factory(12, "IVF10,FlatDedup") - assert index.instances is not None - - -class TestCloneSize(unittest.TestCase): - - def test_clone_size(self): - index = faiss.index_factory(20, 'PCA10,Flat') - xb = faiss.rand((100, 20)) - index.train(xb) - index.add(xb) - index2 = faiss.clone_index(index) - assert index2.ntotal == 100 diff --git a/core/src/index/thirdparty/faiss/tests/test_index.py b/core/src/index/thirdparty/faiss/tests/test_index.py deleted file mode 100644 index c41f7f8c0b..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_index.py +++ /dev/null @@ -1,639 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -"""this is a basic test script for simple indices work""" -from __future__ import absolute_import, division, print_function -# no unicode_literals because it messes up in py2 - -import numpy as np -import unittest -import faiss -import tempfile -import os -import re -import warnings - -from common import get_dataset, get_dataset_2 - -class TestModuleInterface(unittest.TestCase): - - def test_version_attribute(self): - assert hasattr(faiss, '__version__') - assert re.match('^\\d+\\.\\d+\\.\\d+$', faiss.__version__) - - -class EvalIVFPQAccuracy(unittest.TestCase): - - def test_IndexIVFPQ(self): - d = 32 - nb = 1000 - nt = 1500 - nq = 200 - - (xt, xb, xq) = get_dataset_2(d, nt, nb, nq) - d = xt.shape[1] - - gt_index = faiss.IndexFlatL2(d) - gt_index.add(xb) - D, gt_nns = gt_index.search(xq, 1) - - coarse_quantizer = faiss.IndexFlatL2(d) - index = faiss.IndexIVFPQ(coarse_quantizer, d, 32, 8, 8) - index.cp.min_points_per_centroid = 5 # quiet warning - index.train(xt) - index.add(xb) - index.nprobe = 4 - D, nns = index.search(xq, 10) - n_ok = (nns == gt_nns).sum() - nq = xq.shape[0] - - self.assertGreater(n_ok, nq * 0.66) - - # check that and Index2Layer gives the same reconstruction - # this is a bit fragile: it assumes 2 runs of training give - # the exact same result. - index2 = faiss.Index2Layer(coarse_quantizer, 32, 8) - if True: - index2.train(xt) - else: - index2.pq = index.pq - index2.is_trained = True - index2.add(xb) - ref_recons = index.reconstruct_n(0, nb) - new_recons = index2.reconstruct_n(0, nb) - self.assertTrue(np.all(ref_recons == new_recons)) - - - def test_IMI(self): - d = 32 - nb = 1000 - nt = 1500 - nq = 200 - - (xt, xb, xq) = get_dataset_2(d, nt, nb, nq) - d = xt.shape[1] - - gt_index = faiss.IndexFlatL2(d) - gt_index.add(xb) - D, gt_nns = gt_index.search(xq, 1) - - nbits = 5 - coarse_quantizer = faiss.MultiIndexQuantizer(d, 2, nbits) - index = faiss.IndexIVFPQ(coarse_quantizer, d, (1 << nbits) ** 2, 8, 8) - index.quantizer_trains_alone = 1 - index.train(xt) - index.add(xb) - index.nprobe = 100 - D, nns = index.search(xq, 10) - n_ok = (nns == gt_nns).sum() - - # Should return 166 on mac, and 170 on linux. - self.assertGreater(n_ok, 165) - - ############# replace with explicit assignment indexes - nbits = 5 - pq = coarse_quantizer.pq - centroids = faiss.vector_to_array(pq.centroids) - centroids = centroids.reshape(pq.M, pq.ksub, pq.dsub) - ai0 = faiss.IndexFlatL2(pq.dsub) - ai0.add(centroids[0]) - ai1 = faiss.IndexFlatL2(pq.dsub) - ai1.add(centroids[1]) - - coarse_quantizer_2 = faiss.MultiIndexQuantizer2(d, nbits, ai0, ai1) - coarse_quantizer_2.pq = pq - coarse_quantizer_2.is_trained = True - - index.quantizer = coarse_quantizer_2 - - index.reset() - index.add(xb) - - D, nns = index.search(xq, 10) - n_ok = (nns == gt_nns).sum() - - # should return the same result - self.assertGreater(n_ok, 165) - - - def test_IMI_2(self): - d = 32 - nb = 1000 - nt = 1500 - nq = 200 - - (xt, xb, xq) = get_dataset_2(d, nt, nb, nq) - d = xt.shape[1] - - gt_index = faiss.IndexFlatL2(d) - gt_index.add(xb) - D, gt_nns = gt_index.search(xq, 1) - - ############# redo including training - nbits = 5 - ai0 = faiss.IndexFlatL2(int(d / 2)) - ai1 = faiss.IndexFlatL2(int(d / 2)) - - coarse_quantizer = faiss.MultiIndexQuantizer2(d, nbits, ai0, ai1) - index = faiss.IndexIVFPQ(coarse_quantizer, d, (1 << nbits) ** 2, 8, 8) - index.quantizer_trains_alone = 1 - index.train(xt) - index.add(xb) - index.nprobe = 100 - D, nns = index.search(xq, 10) - n_ok = (nns == gt_nns).sum() - - # should return the same result - self.assertGreater(n_ok, 165) - - - - - -class TestMultiIndexQuantizer(unittest.TestCase): - - def test_search_k1(self): - - # verify codepath for k = 1 and k > 1 - - d = 64 - nb = 0 - nt = 1500 - nq = 200 - - (xt, xb, xq) = get_dataset(d, nb, nt, nq) - - miq = faiss.MultiIndexQuantizer(d, 2, 6) - - miq.train(xt) - - D1, I1 = miq.search(xq, 1) - - D5, I5 = miq.search(xq, 5) - - self.assertEqual(np.abs(I1[:, :1] - I5[:, :1]).max(), 0) - self.assertEqual(np.abs(D1[:, :1] - D5[:, :1]).max(), 0) - - -class TestScalarQuantizer(unittest.TestCase): - - def test_4variants_ivf(self): - d = 32 - nt = 2500 - nq = 400 - nb = 5000 - - (xt, xb, xq) = get_dataset_2(d, nt, nb, nq) - - # common quantizer - quantizer = faiss.IndexFlatL2(d) - - ncent = 64 - - index_gt = faiss.IndexFlatL2(d) - index_gt.add(xb) - D, I_ref = index_gt.search(xq, 10) - - nok = {} - - index = faiss.IndexIVFFlat(quantizer, d, ncent, - faiss.METRIC_L2) - index.cp.min_points_per_centroid = 5 # quiet warning - index.nprobe = 4 - index.train(xt) - index.add(xb) - D, I = index.search(xq, 10) - nok['flat'] = (I[:, 0] == I_ref[:, 0]).sum() - - for qname in "QT_4bit QT_4bit_uniform QT_8bit QT_8bit_uniform QT_fp16".split(): - qtype = getattr(faiss.ScalarQuantizer, qname) - index = faiss.IndexIVFScalarQuantizer(quantizer, d, ncent, - qtype, faiss.METRIC_L2) - - index.nprobe = 4 - index.train(xt) - index.add(xb) - D, I = index.search(xq, 10) - - nok[qname] = (I[:, 0] == I_ref[:, 0]).sum() - print(nok, nq) - - self.assertGreaterEqual(nok['flat'], nq * 0.6) - # The tests below are a bit fragile, it happens that the - # ordering between uniform and non-uniform are reverted, - # probably because the dataset is small, which introduces - # jitter - self.assertGreaterEqual(nok['flat'], nok['QT_8bit']) - self.assertGreaterEqual(nok['QT_8bit'], nok['QT_4bit']) - self.assertGreaterEqual(nok['QT_8bit'], nok['QT_8bit_uniform']) - self.assertGreaterEqual(nok['QT_4bit'], nok['QT_4bit_uniform']) - self.assertGreaterEqual(nok['QT_fp16'], nok['QT_8bit']) - - def test_4variants(self): - d = 32 - nt = 2500 - nq = 400 - nb = 5000 - - (xt, xb, xq) = get_dataset(d, nb, nt, nq) - - index_gt = faiss.IndexFlatL2(d) - index_gt.add(xb) - D_ref, I_ref = index_gt.search(xq, 10) - - nok = {} - - for qname in "QT_4bit QT_4bit_uniform QT_8bit QT_8bit_uniform QT_fp16".split(): - qtype = getattr(faiss.ScalarQuantizer, qname) - index = faiss.IndexScalarQuantizer(d, qtype, faiss.METRIC_L2) - index.train(xt) - index.add(xb) - D, I = index.search(xq, 10) - nok[qname] = (I[:, 0] == I_ref[:, 0]).sum() - - print(nok, nq) - - self.assertGreaterEqual(nok['QT_8bit'], nq * 0.9) - self.assertGreaterEqual(nok['QT_8bit'], nok['QT_4bit']) - self.assertGreaterEqual(nok['QT_8bit'], nok['QT_8bit_uniform']) - self.assertGreaterEqual(nok['QT_4bit'], nok['QT_4bit_uniform']) - self.assertGreaterEqual(nok['QT_fp16'], nok['QT_8bit']) - - -class TestRangeSearch(unittest.TestCase): - - def test_range_search(self): - d = 4 - nt = 100 - nq = 10 - nb = 50 - - (xt, xb, xq) = get_dataset(d, nb, nt, nq) - - index = faiss.IndexFlatL2(d) - index.add(xb) - - Dref, Iref = index.search(xq, 5) - - thresh = 0.1 # *squared* distance - lims, D, I = index.range_search(xq, thresh) - - for i in range(nq): - Iline = I[lims[i]:lims[i + 1]] - Dline = D[lims[i]:lims[i + 1]] - for j, dis in zip(Iref[i], Dref[i]): - if dis < thresh: - li, = np.where(Iline == j) - self.assertTrue(li.size == 1) - idx = li[0] - self.assertGreaterEqual(1e-4, abs(Dline[idx] - dis)) - - -class TestSearchAndReconstruct(unittest.TestCase): - - def run_search_and_reconstruct(self, index, xb, xq, k=10, eps=None): - n, d = xb.shape - assert xq.shape[1] == d - assert index.d == d - - D_ref, I_ref = index.search(xq, k) - R_ref = index.reconstruct_n(0, n) - D, I, R = index.search_and_reconstruct(xq, k) - - self.assertTrue((D == D_ref).all()) - self.assertTrue((I == I_ref).all()) - self.assertEqual(R.shape[:2], I.shape) - self.assertEqual(R.shape[2], d) - - # (n, k, ..) -> (n * k, ..) - I_flat = I.reshape(-1) - R_flat = R.reshape(-1, d) - # Filter out -1s when not enough results - R_flat = R_flat[I_flat >= 0] - I_flat = I_flat[I_flat >= 0] - - recons_ref_err = np.mean(np.linalg.norm(R_flat - R_ref[I_flat])) - self.assertLessEqual(recons_ref_err, 1e-6) - - def norm1(x): - return np.sqrt((x ** 2).sum(axis=1)) - - recons_err = np.mean(norm1(R_flat - xb[I_flat])) - - print('Reconstruction error = %.3f' % recons_err) - if eps is not None: - self.assertLessEqual(recons_err, eps) - - return D, I, R - - def test_IndexFlat(self): - d = 32 - nb = 1000 - nt = 1500 - nq = 200 - - (xt, xb, xq) = get_dataset(d, nb, nt, nq) - - index = faiss.IndexFlatL2(d) - index.add(xb) - - self.run_search_and_reconstruct(index, xb, xq, eps=0.0) - - def test_IndexIVFFlat(self): - d = 32 - nb = 1000 - nt = 1500 - nq = 200 - - (xt, xb, xq) = get_dataset(d, nb, nt, nq) - - quantizer = faiss.IndexFlatL2(d) - index = faiss.IndexIVFFlat(quantizer, d, 32, faiss.METRIC_L2) - index.cp.min_points_per_centroid = 5 # quiet warning - index.nprobe = 4 - index.train(xt) - index.add(xb) - - self.run_search_and_reconstruct(index, xb, xq, eps=0.0) - - def test_IndexIVFPQ(self): - d = 32 - nb = 1000 - nt = 1500 - nq = 200 - - (xt, xb, xq) = get_dataset(d, nb, nt, nq) - - quantizer = faiss.IndexFlatL2(d) - index = faiss.IndexIVFPQ(quantizer, d, 32, 8, 8) - index.cp.min_points_per_centroid = 5 # quiet warning - index.nprobe = 4 - index.train(xt) - index.add(xb) - - self.run_search_and_reconstruct(index, xb, xq, eps=1.0) - - def test_MultiIndex(self): - d = 32 - nb = 1000 - nt = 1500 - nq = 200 - - (xt, xb, xq) = get_dataset(d, nb, nt, nq) - - index = faiss.index_factory(d, "IMI2x5,PQ8np") - faiss.ParameterSpace().set_index_parameter(index, "nprobe", 4) - index.train(xt) - index.add(xb) - - self.run_search_and_reconstruct(index, xb, xq, eps=1.0) - - def test_IndexTransform(self): - d = 32 - nb = 1000 - nt = 1500 - nq = 200 - - (xt, xb, xq) = get_dataset(d, nb, nt, nq) - - index = faiss.index_factory(d, "L2norm,PCA8,IVF32,PQ8np") - faiss.ParameterSpace().set_index_parameter(index, "nprobe", 4) - index.train(xt) - index.add(xb) - - self.run_search_and_reconstruct(index, xb, xq) - - -class TestHNSW(unittest.TestCase): - - def __init__(self, *args, **kwargs): - unittest.TestCase.__init__(self, *args, **kwargs) - d = 32 - nt = 0 - nb = 1500 - nq = 500 - - (_, self.xb, self.xq) = get_dataset_2(d, nt, nb, nq) - index = faiss.IndexFlatL2(d) - index.add(self.xb) - Dref, Iref = index.search(self.xq, 1) - self.Iref = Iref - - def test_hnsw(self): - d = self.xq.shape[1] - - index = faiss.IndexHNSWFlat(d, 16) - index.add(self.xb) - Dhnsw, Ihnsw = index.search(self.xq, 1) - - self.assertGreaterEqual((self.Iref == Ihnsw).sum(), 460) - - self.io_and_retest(index, Dhnsw, Ihnsw) - - def test_hnsw_unbounded_queue(self): - d = self.xq.shape[1] - - index = faiss.IndexHNSWFlat(d, 16) - index.add(self.xb) - index.search_bounded_queue = False - Dhnsw, Ihnsw = index.search(self.xq, 1) - - self.assertGreaterEqual((self.Iref == Ihnsw).sum(), 460) - - self.io_and_retest(index, Dhnsw, Ihnsw) - - def io_and_retest(self, index, Dhnsw, Ihnsw): - _, tmpfile = tempfile.mkstemp() - try: - faiss.write_index(index, tmpfile) - index2 = faiss.read_index(tmpfile) - finally: - if os.path.exists(tmpfile): - os.unlink(tmpfile) - - Dhnsw2, Ihnsw2 = index2.search(self.xq, 1) - - self.assertTrue(np.all(Dhnsw2 == Dhnsw)) - self.assertTrue(np.all(Ihnsw2 == Ihnsw)) - - # also test clone - index3 = faiss.clone_index(index) - Dhnsw3, Ihnsw3 = index3.search(self.xq, 1) - - self.assertTrue(np.all(Dhnsw3 == Dhnsw)) - self.assertTrue(np.all(Ihnsw3 == Ihnsw)) - - - def test_hnsw_2level(self): - d = self.xq.shape[1] - - quant = faiss.IndexFlatL2(d) - - index = faiss.IndexHNSW2Level(quant, 256, 8, 8) - index.train(self.xb) - index.add(self.xb) - Dhnsw, Ihnsw = index.search(self.xq, 1) - - self.assertGreaterEqual((self.Iref == Ihnsw).sum(), 310) - - self.io_and_retest(index, Dhnsw, Ihnsw) - - def test_add_0_vecs(self): - index = faiss.IndexHNSWFlat(10, 16) - zero_vecs = np.zeros((0, 10), dtype='float32') - # infinite loop - index.add(zero_vecs) - - def test_hnsw_IP(self): - d = self.xq.shape[1] - - index_IP = faiss.IndexFlatIP(d) - index_IP.add(self.xb) - Dref, Iref = index_IP.search(self.xq, 1) - - index = faiss.IndexHNSWFlat(d, 16, faiss.METRIC_INNER_PRODUCT) - index.add(self.xb) - Dhnsw, Ihnsw = index.search(self.xq, 1) - - print('nb equal: ', (Iref == Ihnsw).sum()) - - self.assertGreaterEqual((Iref == Ihnsw).sum(), 480) - - mask = Iref[:, 0] == Ihnsw[:, 0] - assert np.allclose(Dref[mask, 0], Dhnsw[mask, 0]) - - - - -class TestDistancesPositive(unittest.TestCase): - - def test_l2_pos(self): - """ - roundoff errors occur only with the L2 decomposition used - with BLAS, ie. in IndexFlatL2 and with - n > distance_compute_blas_threshold = 20 - """ - - d = 128 - n = 100 - - rs = np.random.RandomState(1234) - x = rs.rand(n, d).astype('float32') - - index = faiss.IndexFlatL2(d) - index.add(x) - - D, I = index.search(x, 10) - - assert np.all(D >= 0) - - -class TestReconsException(unittest.TestCase): - - def test_recons_exception(self): - - d = 64 # dimension - nb = 1000 - rs = np.random.RandomState(1234) - xb = rs.rand(nb, d).astype('float32') - nlist = 10 - quantizer = faiss.IndexFlatL2(d) # the other index - index = faiss.IndexIVFFlat(quantizer, d, nlist) - index.train(xb) - index.add(xb) - index.make_direct_map() - - index.reconstruct(9) - - self.assertRaises( - RuntimeError, - index.reconstruct, 100001 - ) - - def test_reconstuct_after_add(self): - index = faiss.index_factory(10, 'IVF5,SQfp16') - index.train(faiss.randn((100, 10), 123)) - index.add(faiss.randn((100, 10), 345)) - index.make_direct_map() - index.add(faiss.randn((100, 10), 678)) - - # should not raise an exception - index.reconstruct(5) - print(index.ntotal) - index.reconstruct(150) - - -class TestReconsHash(unittest.TestCase): - - def do_test(self, index_key): - d = 32 - index = faiss.index_factory(d, index_key) - index.train(faiss.randn((100, d), 123)) - - # reference reconstruction - index.add(faiss.randn((100, d), 345)) - index.add(faiss.randn((100, d), 678)) - ref_recons = index.reconstruct_n(0, 200) - - # with lookup - index.reset() - rs = np.random.RandomState(123) - ids = rs.choice(10000, size=200, replace=False) - index.add_with_ids(faiss.randn((100, d), 345), ids[:100]) - index.set_direct_map_type(faiss.DirectMap.Hashtable) - index.add_with_ids(faiss.randn((100, d), 678), ids[100:]) - - # compare - for i in range(0, 200, 13): - recons = index.reconstruct(int(ids[i])) - self.assertTrue(np.all(recons == ref_recons[i])) - - # test I/O - buf = faiss.serialize_index(index) - index2 = faiss.deserialize_index(buf) - - # compare - for i in range(0, 200, 13): - recons = index2.reconstruct(int(ids[i])) - self.assertTrue(np.all(recons == ref_recons[i])) - - # remove - toremove = np.ascontiguousarray(ids[0:200:3]) - - sel = faiss.IDSelectorArray(50, faiss.swig_ptr(toremove[:50])) - - # test both ways of removing elements - nremove = index2.remove_ids(sel) - nremove += index2.remove_ids(toremove[50:]) - - self.assertEqual(nremove, len(toremove)) - - for i in range(0, 200, 13): - if i % 3 == 0: - self.assertRaises( - RuntimeError, - index2.reconstruct, int(ids[i]) - ) - else: - recons = index2.reconstruct(int(ids[i])) - self.assertTrue(np.all(recons == ref_recons[i])) - - # index error should raise - self.assertRaises( - RuntimeError, - index.reconstruct, 20000 - ) - - def test_IVFFlat(self): - self.do_test("IVF5,Flat") - - def test_IVFSQ(self): - self.do_test("IVF5,SQfp16") - - def test_IVFPQ(self): - self.do_test("IVF5,PQ4x4np") - -if __name__ == '__main__': - unittest.main() diff --git a/core/src/index/thirdparty/faiss/tests/test_index_accuracy.py b/core/src/index/thirdparty/faiss/tests/test_index_accuracy.py deleted file mode 100644 index d97362f843..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_index_accuracy.py +++ /dev/null @@ -1,673 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -from __future__ import absolute_import, division, print_function, unicode_literals -# noqa E741 -# translation of test_knn.lua - -import numpy as np -import unittest -import faiss - -from common import Randu10k, get_dataset_2, Randu10kUnbalanced - -ev = Randu10k() - -d = ev.d - -# Parameters inverted indexes -ncentroids = int(4 * np.sqrt(ev.nb)) -kprobe = int(np.sqrt(ncentroids)) - -# Parameters for LSH -nbits = d - -# Parameters for indexes involving PQ -M = int(d / 8) # for PQ: #subquantizers -nbits_per_index = 8 # for PQ - - -class IndexAccuracy(unittest.TestCase): - - def test_IndexFlatIP(self): - q = faiss.IndexFlatIP(d) # Ask inner product - res = ev.launch('FLAT / IP', q) - e = ev.evalres(res) - assert e[1] == 1.0 - - def test_IndexFlatL2(self): - q = faiss.IndexFlatL2(d) - res = ev.launch('FLAT / L2', q) - e = ev.evalres(res) - assert e[1] == 1.0 - - def test_ivf_kmeans(self): - ivfk = faiss.IndexIVFFlat(faiss.IndexFlatL2(d), d, ncentroids) - ivfk.nprobe = kprobe - res = ev.launch('IndexIVFFlat', ivfk) - e = ev.evalres(res) - # should give 0.260 0.260 0.260 - assert e[1] > 0.2 - - # test parallel mode - Dref, Iref = ivfk.search(ev.xq, 100) - ivfk.parallel_mode = 1 - Dnew, Inew = ivfk.search(ev.xq, 100) - print((Iref != Inew).sum(), Iref.size) - assert (Iref != Inew).sum() < Iref.size / 5000.0 - assert np.all(Dref == Dnew) - - def test_indexLSH(self): - q = faiss.IndexLSH(d, nbits) - res = ev.launch('FLAT / LSH Cosine', q) - e = ev.evalres(res) - # should give 0.070 0.250 0.580 - assert e[10] > 0.2 - - def test_IndexLSH_32_48(self): - # CHECK: the difference between 32 and 48 does not make much sense - for nbits2 in 32, 48: - q = faiss.IndexLSH(d, nbits2) - res = ev.launch('LSH half size', q) - e = ev.evalres(res) - # should give 0.003 0.019 0.108 - assert e[10] > 0.018 - - def test_IndexPQ(self): - q = faiss.IndexPQ(d, M, nbits_per_index) - res = ev.launch('FLAT / PQ L2', q) - e = ev.evalres(res) - # should give 0.070 0.230 0.260 - assert e[10] > 0.2 - - # Approximate search module: PQ with inner product distance - def test_IndexPQ_ip(self): - q = faiss.IndexPQ(d, M, nbits_per_index, faiss.METRIC_INNER_PRODUCT) - res = ev.launch('FLAT / PQ IP', q) - e = ev.evalres(res) - # should give 0.070 0.230 0.260 - #(same result as regular PQ on normalized distances) - assert e[10] > 0.2 - - def test_IndexIVFPQ(self): - ivfpq = faiss.IndexIVFPQ(faiss.IndexFlatL2(d), d, ncentroids, M, 8) - ivfpq.nprobe = kprobe - res = ev.launch('IVF PQ', ivfpq) - e = ev.evalres(res) - # should give 0.070 0.230 0.260 - assert e[10] > 0.2 - - # TODO: translate evaluation of nested - - # Approximate search: PQ with full vector refinement - def test_IndexPQ_refined(self): - q = faiss.IndexPQ(d, M, nbits_per_index) - res = ev.launch('PQ non-refined', q) - e = ev.evalres(res) - q.reset() - - rq = faiss.IndexRefineFlat(q) - res = ev.launch('PQ refined', rq) - e2 = ev.evalres(res) - assert e2[10] >= e[10] - rq.k_factor = 4 - - res = ev.launch('PQ refined*4', rq) - e3 = ev.evalres(res) - assert e3[10] >= e2[10] - - def test_polysemous(self): - index = faiss.IndexPQ(d, M, nbits_per_index) - index.do_polysemous_training = True - # reduce nb iterations to speed up training for the test - index.polysemous_training.n_iter = 50000 - index.polysemous_training.n_redo = 1 - res = ev.launch('normal PQ', index) - e_baseline = ev.evalres(res) - index.search_type = faiss.IndexPQ.ST_polysemous - - index.polysemous_ht = int(M / 16. * 58) - - stats = faiss.cvar.indexPQ_stats - stats.reset() - - res = ev.launch('Polysemous ht=%d' % index.polysemous_ht, - index) - e_polysemous = ev.evalres(res) - print(e_baseline, e_polysemous, index.polysemous_ht) - print(stats.n_hamming_pass, stats.ncode) - # The randu dataset is difficult, so we are not too picky on - # the results. Here we assert that we have < 10 % loss when - # computing full PQ on fewer than 20% of the data. - assert stats.n_hamming_pass < stats.ncode / 5 - # Test disabled because difference is 0.17 on aarch64 - # TODO check why??? - # assert e_polysemous[10] > e_baseline[10] - 0.1 - - def test_ScalarQuantizer(self): - quantizer = faiss.IndexFlatL2(d) - ivfpq = faiss.IndexIVFScalarQuantizer( - quantizer, d, ncentroids, - faiss.ScalarQuantizer.QT_8bit) - ivfpq.nprobe = kprobe - res = ev.launch('IVF SQ', ivfpq) - e = ev.evalres(res) - # should give 0.234 0.236 0.236 - assert e[10] > 0.235 - - - -class TestSQFlavors(unittest.TestCase): - """ tests IP in addition to L2, non multiple of 8 dimensions - """ - - def add2columns(self, x): - return np.hstack(( - x, np.zeros((x.shape[0], 2), dtype='float32') - )) - - def subtest_add2col(self, xb, xq, index, qname): - """Test with 2 additional dimensions to take also the non-SIMD - codepath. We don't retrain anything but add 2 dims to the - queries, the centroids and the trained ScalarQuantizer. - """ - nb, d = xb.shape - - d2 = d + 2 - xb2 = self.add2columns(xb) - xq2 = self.add2columns(xq) - - nlist = index.nlist - quantizer = faiss.downcast_index(index.quantizer) - quantizer2 = faiss.IndexFlat(d2, index.metric_type) - centroids = faiss.vector_to_array(quantizer.xb).reshape(nlist, d) - centroids2 = self.add2columns(centroids) - quantizer2.add(centroids2) - index2 = faiss.IndexIVFScalarQuantizer( - quantizer2, d2, index.nlist, index.sq.qtype, - index.metric_type) - index2.nprobe = 4 - if qname in ('8bit', '4bit'): - trained = faiss.vector_to_array(index.sq.trained).reshape(2, -1) - nt = trained.shape[1] - # 2 lines: vmins and vdiffs - new_nt = int(nt * d2 / d) - trained2 = np.hstack(( - trained, - np.zeros((2, new_nt - nt), dtype='float32') - )) - trained2[1, nt:] = 1.0 # set vdiff to 1 to avoid div by 0 - faiss.copy_array_to_vector(trained2.ravel(), index2.sq.trained) - else: - index2.sq.trained = index.sq.trained - - index2.is_trained = True - index2.add(xb2) - return index2.search(xq2, 10) - - - # run on Sept 18, 2018 with nprobe=4 + 4 bit bugfix - ref_results = { - (0, '8bit'): 984, - (0, '4bit'): 978, - (0, '8bit_uniform'): 985, - (0, '4bit_uniform'): 979, - (0, 'fp16'): 985, - (1, '8bit'): 979, - (1, '4bit'): 973, - (1, '8bit_uniform'): 979, - (1, '4bit_uniform'): 972, - (1, 'fp16'): 979, - # added 2019-06-26 - (0, '6bit'): 985, - (1, '6bit'): 987, - } - - def subtest(self, mt): - d = 32 - xt, xb, xq = get_dataset_2(d, 2000, 1000, 200) - nlist = 64 - - gt_index = faiss.IndexFlat(d, mt) - gt_index.add(xb) - gt_D, gt_I = gt_index.search(xq, 10) - quantizer = faiss.IndexFlat(d, mt) - for qname in '8bit 4bit 8bit_uniform 4bit_uniform fp16 6bit'.split(): - qtype = getattr(faiss.ScalarQuantizer, 'QT_' + qname) - index = faiss.IndexIVFScalarQuantizer( - quantizer, d, nlist, qtype, mt) - index.train(xt) - index.add(xb) - index.nprobe = 4 # hopefully more robust than 1 - D, I = index.search(xq, 10) - ninter = faiss.eval_intersection(I, gt_I) - print('(%d, %s): %d, ' % (mt, repr(qname), ninter)) - assert abs(ninter - self.ref_results[(mt, qname)]) <= 10 - - if qname == '6bit': - # the test below fails triggers ASAN. TODO check what's wrong - continue - - D2, I2 = self.subtest_add2col(xb, xq, index, qname) - assert np.all(I2 == I) - - # also test range search - - if mt == faiss.METRIC_INNER_PRODUCT: - radius = float(D[:, -1].max()) - else: - radius = float(D[:, -1].min()) - print('radius', radius) - - lims, D3, I3 = index.range_search(xq, radius) - ntot = ndiff = 0 - for i in range(len(xq)): - l0, l1 = lims[i], lims[i + 1] - Inew = set(I3[l0:l1]) - if mt == faiss.METRIC_INNER_PRODUCT: - mask = D2[i] > radius - else: - mask = D2[i] < radius - Iref = set(I2[i, mask]) - ndiff += len(Inew ^ Iref) - ntot += len(Iref) - print('ndiff %d / %d' % (ndiff, ntot)) - assert ndiff < ntot * 0.01 - - for pm in 1, 2: - print('parallel_mode=%d' % pm) - index.parallel_mode = pm - lims4, D4, I4 = index.range_search(xq, radius) - print('sizes', lims4[1:] - lims4[:-1]) - for qno in range(len(lims) - 1): - Iref = I3[lims[qno]: lims[qno+1]] - Inew = I4[lims4[qno]: lims4[qno+1]] - assert set(Iref) == set(Inew), "q %d ref %s new %s" % ( - qno, Iref, Inew) - - def test_SQ_IP(self): - self.subtest(faiss.METRIC_INNER_PRODUCT) - - def test_SQ_L2(self): - self.subtest(faiss.METRIC_L2) - - -class TestSQByte(unittest.TestCase): - - def subtest_8bit_direct(self, metric_type, d): - xt, xb, xq = get_dataset_2(d, 500, 1000, 30) - - # rescale everything to get integer - tmin, tmax = xt.min(), xt.max() - - def rescale(x): - x = np.floor((x - tmin) * 256 / (tmax - tmin)) - x[x < 0] = 0 - x[x > 255] = 255 - return x - - xt = rescale(xt) - xb = rescale(xb) - xq = rescale(xq) - - gt_index = faiss.IndexFlat(d, metric_type) - gt_index.add(xb) - Dref, Iref = gt_index.search(xq, 10) - - index = faiss.IndexScalarQuantizer( - d, faiss.ScalarQuantizer.QT_8bit_direct, metric_type) - index.add(xb) - D, I = index.search(xq, 10) - - assert np.all(I == Iref) - assert np.all(D == Dref) - - # same, with IVF - - nlist = 64 - quantizer = faiss.IndexFlat(d, metric_type) - - gt_index = faiss.IndexIVFFlat(quantizer, d, nlist, metric_type) - gt_index.nprobe = 4 - gt_index.train(xt) - gt_index.add(xb) - Dref, Iref = gt_index.search(xq, 10) - - index = faiss.IndexIVFScalarQuantizer( - quantizer, d, nlist, - faiss.ScalarQuantizer.QT_8bit_direct, metric_type) - index.nprobe = 4 - index.by_residual = False - index.train(xt) - index.add(xb) - D, I = index.search(xq, 10) - - assert np.all(I == Iref) - assert np.all(D == Dref) - - def test_8bit_direct(self): - for d in 13, 16, 24: - for metric_type in faiss.METRIC_L2, faiss.METRIC_INNER_PRODUCT: - self.subtest_8bit_direct(metric_type, d) - - - -class TestPQFlavors(unittest.TestCase): - - # run on Dec 14, 2018 - ref_results = { - (1, True): 800, - (1, True, 20): 794, - (1, False): 769, - (0, True): 831, - (0, True, 20): 828, - (0, False): 829, - } - - def test_IVFPQ_IP(self): - self.subtest(faiss.METRIC_INNER_PRODUCT) - - def test_IVFPQ_L2(self): - self.subtest(faiss.METRIC_L2) - - def subtest(self, mt): - d = 32 - xt, xb, xq = get_dataset_2(d, 2000, 1000, 200) - nlist = 64 - - gt_index = faiss.IndexFlat(d, mt) - gt_index.add(xb) - gt_D, gt_I = gt_index.search(xq, 10) - quantizer = faiss.IndexFlat(d, mt) - for by_residual in True, False: - - index = faiss.IndexIVFPQ( - quantizer, d, nlist, 4, 8) - index.metric_type = mt - index.by_residual = by_residual - if by_residual: - # perform cheap polysemous training - index.do_polysemous_training = True - pt = faiss.PolysemousTraining() - pt.n_iter = 50000 - pt.n_redo = 1 - index.polysemous_training = pt - - index.train(xt) - index.add(xb) - index.nprobe = 4 - D, I = index.search(xq, 10) - - ninter = faiss.eval_intersection(I, gt_I) - print('(%d, %s): %d, ' % (mt, by_residual, ninter)) - - assert abs(ninter - self.ref_results[mt, by_residual]) <= 3 - - index.use_precomputed_table = 0 - D2, I2 = index.search(xq, 10) - assert np.all(I == I2) - - if by_residual: - - index.use_precomputed_table = 1 - index.polysemous_ht = 20 - D, I = index.search(xq, 10) - ninter = faiss.eval_intersection(I, gt_I) - print('(%d, %s, %d): %d, ' % ( - mt, by_residual, index.polysemous_ht, ninter)) - - # polysemous behaves bizarrely on ARM - assert (ninter >= self.ref_results[ - mt, by_residual, index.polysemous_ht] - 4) - - # also test range search - - if mt == faiss.METRIC_INNER_PRODUCT: - radius = float(D[:, -1].max()) - else: - radius = float(D[:, -1].min()) - print('radius', radius) - - lims, D3, I3 = index.range_search(xq, radius) - ntot = ndiff = 0 - for i in range(len(xq)): - l0, l1 = lims[i], lims[i + 1] - Inew = set(I3[l0:l1]) - if mt == faiss.METRIC_INNER_PRODUCT: - mask = D2[i] > radius - else: - mask = D2[i] < radius - Iref = set(I2[i, mask]) - ndiff += len(Inew ^ Iref) - ntot += len(Iref) - print('ndiff %d / %d' % (ndiff, ntot)) - assert ndiff < ntot * 0.02 - - def test_IVFPQ_non8bit(self): - d = 16 - xt, xb, xq = get_dataset_2(d, 10000, 2000, 200) - nlist = 64 - - gt_index = faiss.IndexFlat(d) - gt_index.add(xb) - gt_D, gt_I = gt_index.search(xq, 10) - - quantizer = faiss.IndexFlat(d) - ninter = {} - for v in '2x8', '8x2': - if v == '8x2': - index = faiss.IndexIVFPQ( - quantizer, d, nlist, 2, 8) - else: - index = faiss.IndexIVFPQ( - quantizer, d, nlist, 8, 2) - index.train(xt) - index.add(xb) - index.npobe = 16 - - D, I = index.search(xq, 10) - ninter[v] = faiss.eval_intersection(I, gt_I) - print('ninter=', ninter) - # this should be the case but we don't observe - # that... Probavly too few test points - # assert ninter['2x8'] > ninter['8x2'] - # ref numbers on 2019-11-02 - assert abs(ninter['2x8'] - 458) < 4 - assert abs(ninter['8x2'] - 465) < 4 - - -class TestFlat1D(unittest.TestCase): - - def test_flat_1d(self): - rs = np.random.RandomState(123545) - k = 10 - xb = rs.uniform(size=(100, 1)).astype('float32') - # make sure to test below and above - xq = rs.uniform(size=(1000, 1)).astype('float32') * 1.1 - 0.05 - - ref = faiss.IndexFlatL2(1) - ref.add(xb) - ref_D, ref_I = ref.search(xq, k) - - new = faiss.IndexFlat1D() - new.add(xb) - - new_D, new_I = new.search(xq, 10) - - ndiff = (np.abs(ref_I - new_I) != 0).sum() - - assert(ndiff < 100) - new_D = new_D ** 2 - max_diff_D = np.abs(ref_D - new_D).max() - assert(max_diff_D < 1e-5) - - -class OPQRelativeAccuracy(unittest.TestCase): - # translated from test_opq.lua - - def test_OPQ(self): - - M = 4 - - ev = Randu10kUnbalanced() - d = ev.d - index = faiss.IndexPQ(d, M, 8) - - res = ev.launch('PQ', index) - e_pq = ev.evalres(res) - - index_pq = faiss.IndexPQ(d, M, 8) - opq_matrix = faiss.OPQMatrix(d, M) - # opq_matrix.verbose = true - opq_matrix.niter = 10 - opq_matrix.niter_pq = 4 - index = faiss.IndexPreTransform(opq_matrix, index_pq) - - res = ev.launch('OPQ', index) - e_opq = ev.evalres(res) - - print('e_pq=%s' % e_pq) - print('e_opq=%s' % e_opq) - - # verify that OPQ better than PQ - for r in 1, 10, 100: - assert(e_opq[r] > e_pq[r]) - - def test_OIVFPQ(self): - # Parameters inverted indexes - ncentroids = 50 - M = 4 - - ev = Randu10kUnbalanced() - d = ev.d - quantizer = faiss.IndexFlatL2(d) - index = faiss.IndexIVFPQ(quantizer, d, ncentroids, M, 8) - index.nprobe = 5 - - res = ev.launch('IVFPQ', index) - e_ivfpq = ev.evalres(res) - - quantizer = faiss.IndexFlatL2(d) - index_ivfpq = faiss.IndexIVFPQ(quantizer, d, ncentroids, M, 8) - index_ivfpq.nprobe = 5 - opq_matrix = faiss.OPQMatrix(d, M) - opq_matrix.niter = 10 - index = faiss.IndexPreTransform(opq_matrix, index_ivfpq) - - res = ev.launch('O+IVFPQ', index) - e_oivfpq = ev.evalres(res) - - # verify same on OIVFPQ - for r in 1, 10, 100: - print(e_oivfpq[r], e_ivfpq[r]) - assert(e_oivfpq[r] >= e_ivfpq[r]) - - -class TestRoundoff(unittest.TestCase): - - def test_roundoff(self): - # params that force use of BLAS implementation - nb = 100 - nq = 25 - d = 4 - xb = np.zeros((nb, d), dtype='float32') - - xb[:, 0] = np.arange(nb) + 12345 - xq = xb[:nq] + 0.3 - - index = faiss.IndexFlat(d) - index.add(xb) - - D, I = index.search(xq, 1) - - # this does not work - assert not np.all(I.ravel() == np.arange(nq)) - - index = faiss.IndexPreTransform( - faiss.CenteringTransform(d), - faiss.IndexFlat(d)) - - index.train(xb) - index.add(xb) - - D, I = index.search(xq, 1) - - # this works - assert np.all(I.ravel() == np.arange(nq)) - - -class TestSpectralHash(unittest.TestCase): - - # run on 2019-04-02 - ref_results = { - (32, 'global', 10): 505, - (32, 'centroid', 10): 524, - (32, 'centroid_half', 10): 21, - (32, 'median', 10): 510, - (32, 'global', 1): 8, - (32, 'centroid', 1): 20, - (32, 'centroid_half', 1): 26, - (32, 'median', 1): 14, - (64, 'global', 10): 768, - (64, 'centroid', 10): 767, - (64, 'centroid_half', 10): 21, - (64, 'median', 10): 765, - (64, 'global', 1): 28, - (64, 'centroid', 1): 21, - (64, 'centroid_half', 1): 20, - (64, 'median', 1): 29, - (128, 'global', 10): 968, - (128, 'centroid', 10): 945, - (128, 'centroid_half', 10): 21, - (128, 'median', 10): 958, - (128, 'global', 1): 271, - (128, 'centroid', 1): 279, - (128, 'centroid_half', 1): 171, - (128, 'median', 1): 253, - } - - def test_sh(self): - d = 32 - xt, xb, xq = get_dataset_2(d, 2000, 1000, 200) - nlist, nprobe = 1, 1 - - gt_index = faiss.IndexFlatL2(d) - gt_index.add(xb) - gt_D, gt_I = gt_index.search(xq, 10) - - for nbit in 32, 64, 128: - quantizer = faiss.IndexFlatL2(d) - - index_lsh = faiss.IndexLSH(d, nbit, True) - index_lsh.add(xb) - D, I = index_lsh.search(xq, 10) - ninter = faiss.eval_intersection(I, gt_I) - - print('LSH baseline: %d' % ninter) - - for period in 10.0, 1.0: - - for tt in 'global centroid centroid_half median'.split(): - index = faiss.IndexIVFSpectralHash(quantizer, d, nlist, - nbit, period) - index.nprobe = nprobe - index.threshold_type = getattr( - faiss.IndexIVFSpectralHash, - 'Thresh_' + tt - ) - - index.train(xt) - index.add(xb) - D, I = index.search(xq, 10) - - ninter = faiss.eval_intersection(I, gt_I) - key = (nbit, tt, period) - - print('(%d, %s, %g): %d, ' % (nbit, repr(tt), period, ninter)) - assert abs(ninter - self.ref_results[key]) <= 4 - - -if __name__ == '__main__': - unittest.main() diff --git a/core/src/index/thirdparty/faiss/tests/test_index_binary.py b/core/src/index/thirdparty/faiss/tests/test_index_binary.py deleted file mode 100644 index c61e2fa5df..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_index_binary.py +++ /dev/null @@ -1,375 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -"""this is a basic test script for simple indices work""" -from __future__ import absolute_import, division, print_function, unicode_literals - -import numpy as np -import unittest -import faiss - -from common import compare_binary_result_lists, make_binary_dataset - - - -def binary_to_float(x): - n, d = x.shape - x8 = x.reshape(n * d, -1) - c8 = 2 * ((x8 >> np.arange(8)) & 1).astype('int8') - 1 - return c8.astype('float32').reshape(n, d * 8) - - -def binary_dis(x, y): - return sum(faiss.popcount64(int(xi ^ yi)) for xi, yi in zip(x, y)) - - -class TestBinaryPQ(unittest.TestCase): - """ Use a PQ that mimicks a binary encoder """ - - def test_encode_to_binary(self): - d = 256 - nt = 256 - nb = 1500 - nq = 500 - (xt, xb, xq) = make_binary_dataset(d, nt, nb, nq) - pq = faiss.ProductQuantizer(d, int(d / 8), 8) - - centroids = binary_to_float( - np.tile(np.arange(256), int(d / 8)).astype('uint8').reshape(-1, 1)) - - faiss.copy_array_to_vector(centroids.ravel(), pq.centroids) - pq.is_trained = True - - codes = pq.compute_codes(binary_to_float(xb)) - - assert np.all(codes == xb) - - indexpq = faiss.IndexPQ(d, int(d / 8), 8) - indexpq.pq = pq - indexpq.is_trained = True - - indexpq.add(binary_to_float(xb)) - D, I = indexpq.search(binary_to_float(xq), 3) - - for i in range(nq): - for j, dj in zip(I[i], D[i]): - ref_dis = binary_dis(xq[i], xb[j]) - assert 4 * ref_dis == dj - - nlist = 32 - quantizer = faiss.IndexFlatL2(d) - # pretext class for training - iflat = faiss.IndexIVFFlat(quantizer, d, nlist) - iflat.train(binary_to_float(xt)) - - indexivfpq = faiss.IndexIVFPQ(quantizer, d, nlist, int(d / 8), 8) - - indexivfpq.pq = pq - indexivfpq.is_trained = True - indexivfpq.by_residual = False - - indexivfpq.add(binary_to_float(xb)) - indexivfpq.nprobe = 4 - - D, I = indexivfpq.search(binary_to_float(xq), 3) - - for i in range(nq): - for j, dj in zip(I[i], D[i]): - ref_dis = binary_dis(xq[i], xb[j]) - assert 4 * ref_dis == dj - - -class TestBinaryFlat(unittest.TestCase): - - def __init__(self, *args, **kwargs): - unittest.TestCase.__init__(self, *args, **kwargs) - d = 32 - nt = 0 - nb = 1500 - nq = 500 - - (_, self.xb, self.xq) = make_binary_dataset(d, nt, nb, nq) - - def test_flat(self): - d = self.xq.shape[1] * 8 - nq = self.xq.shape[0] - - index = faiss.IndexBinaryFlat(d) - index.add(self.xb) - D, I = index.search(self.xq, 3) - - for i in range(nq): - for j, dj in zip(I[i], D[i]): - ref_dis = binary_dis(self.xq[i], self.xb[j]) - assert dj == ref_dis - - # test reconstruction - assert np.all(index.reconstruct(12) == self.xb[12]) - - def test_empty_flat(self): - d = self.xq.shape[1] * 8 - - index = faiss.IndexBinaryFlat(d) - - for use_heap in [True, False]: - index.use_heap = use_heap - Dflat, Iflat = index.search(self.xq, 10) - - assert(np.all(Iflat == -1)) - assert(np.all(Dflat == 2147483647)) # NOTE(hoss): int32_t max - - def test_range_search(self): - d = self.xq.shape[1] * 8 - - index = faiss.IndexBinaryFlat(d) - index.add(self.xb) - D, I = index.search(self.xq, 10) - thresh = int(np.median(D[:, -1])) - - lims, D2, I2 = index.range_search(self.xq, thresh) - nt1 = nt2 = 0 - for i in range(len(self.xq)): - range_res = I2[lims[i]:lims[i + 1]] - if thresh > D[i, -1]: - self.assertTrue(set(I[i]) <= set(range_res)) - nt1 += 1 - elif thresh < D[i, -1]: - self.assertTrue(set(range_res) <= set(I[i])) - nt2 += 1 - # in case of equality we have a problem with ties - print('nb tests', nt1, nt2) - # nb tests is actually low... - self.assertTrue(nt1 > 19 and nt2 > 19) - - -class TestBinaryIVF(unittest.TestCase): - - def __init__(self, *args, **kwargs): - unittest.TestCase.__init__(self, *args, **kwargs) - d = 32 - nt = 200 - nb = 1500 - nq = 500 - - (self.xt, self.xb, self.xq) = make_binary_dataset(d, nt, nb, nq) - index = faiss.IndexBinaryFlat(d) - index.add(self.xb) - Dref, Iref = index.search(self.xq, 10) - self.Dref = Dref - - def test_ivf_flat_exhaustive(self): - d = self.xq.shape[1] * 8 - - quantizer = faiss.IndexBinaryFlat(d) - index = faiss.IndexBinaryIVF(quantizer, d, 8) - index.cp.min_points_per_centroid = 5 # quiet warning - index.nprobe = 8 - index.train(self.xt) - index.add(self.xb) - Divfflat, _ = index.search(self.xq, 10) - - np.testing.assert_array_equal(self.Dref, Divfflat) - - def test_ivf_flat2(self): - d = self.xq.shape[1] * 8 - - quantizer = faiss.IndexBinaryFlat(d) - index = faiss.IndexBinaryIVF(quantizer, d, 8) - index.cp.min_points_per_centroid = 5 # quiet warning - index.nprobe = 4 - index.train(self.xt) - index.add(self.xb) - Divfflat, _ = index.search(self.xq, 10) - - self.assertEqual((self.Dref == Divfflat).sum(), 4122) - - def test_ivf_range(self): - d = self.xq.shape[1] * 8 - - quantizer = faiss.IndexBinaryFlat(d) - index = faiss.IndexBinaryIVF(quantizer, d, 8) - index.cp.min_points_per_centroid = 5 # quiet warning - index.nprobe = 4 - index.train(self.xt) - index.add(self.xb) - D, I = index.search(self.xq, 10) - - radius = int(np.median(D[:, -1]) + 1) - Lr, Dr, Ir = index.range_search(self.xq, radius) - - for i in range(len(self.xq)): - res = Ir[Lr[i]:Lr[i + 1]] - if D[i, -1] < radius: - self.assertTrue(set(I[i]) <= set(res)) - else: - subset = I[i, D[i, :] < radius] - self.assertTrue(set(subset) == set(res)) - - - def test_ivf_flat_empty(self): - d = self.xq.shape[1] * 8 - - index = faiss.IndexBinaryIVF(faiss.IndexBinaryFlat(d), d, 8) - index.train(self.xt) - - for use_heap in [True, False]: - index.use_heap = use_heap - Divfflat, Iivfflat = index.search(self.xq, 10) - - assert(np.all(Iivfflat == -1)) - assert(np.all(Divfflat == 2147483647)) # NOTE(hoss): int32_t max - - def test_ivf_reconstruction(self): - d = self.xq.shape[1] * 8 - quantizer = faiss.IndexBinaryFlat(d) - index = faiss.IndexBinaryIVF(quantizer, d, 8) - index.cp.min_points_per_centroid = 5 # quiet warning - index.nprobe = 4 - index.train(self.xt) - - index.add(self.xb) - index.set_direct_map_type(faiss.DirectMap.Array) - - for i in range(0, len(self.xb), 13): - np.testing.assert_array_equal( - index.reconstruct(i), - self.xb[i] - ) - - # try w/ hashtable - index = faiss.IndexBinaryIVF(quantizer, d, 8) - rs = np.random.RandomState(123) - ids = rs.choice(10000, size=len(self.xb), replace=False) - index.add_with_ids(self.xb, ids) - index.set_direct_map_type(faiss.DirectMap.Hashtable) - - for i in range(0, len(self.xb), 13): - np.testing.assert_array_equal( - index.reconstruct(int(ids[i])), - self.xb[i] - ) - - -class TestHNSW(unittest.TestCase): - - def __init__(self, *args, **kwargs): - unittest.TestCase.__init__(self, *args, **kwargs) - d = 32 - nt = 0 - nb = 1500 - nq = 500 - - (_, self.xb, self.xq) = make_binary_dataset(d, nt, nb, nq) - - def test_hnsw_exact_distances(self): - d = self.xq.shape[1] * 8 - nq = self.xq.shape[0] - - index = faiss.IndexBinaryHNSW(d, 16) - index.add(self.xb) - Dists, Ids = index.search(self.xq, 3) - - for i in range(nq): - for j, dj in zip(Ids[i], Dists[i]): - ref_dis = binary_dis(self.xq[i], self.xb[j]) - self.assertEqual(dj, ref_dis) - - def test_hnsw(self): - d = self.xq.shape[1] * 8 - - # NOTE(hoss): Ensure the HNSW construction is deterministic. - nthreads = faiss.omp_get_max_threads() - faiss.omp_set_num_threads(1) - - index_hnsw_float = faiss.IndexHNSWFlat(d, 16) - index_hnsw_ref = faiss.IndexBinaryFromFloat(index_hnsw_float) - - index_hnsw_bin = faiss.IndexBinaryHNSW(d, 16) - - index_hnsw_ref.add(self.xb) - index_hnsw_bin.add(self.xb) - - faiss.omp_set_num_threads(nthreads) - - Dref, Iref = index_hnsw_ref.search(self.xq, 3) - Dbin, Ibin = index_hnsw_bin.search(self.xq, 3) - - self.assertTrue((Dref == Dbin).all()) - - - -class TestReplicasAndShards(unittest.TestCase): - - def test_replicas(self): - d = 32 - nq = 100 - nb = 200 - - (_, xb, xq) = make_binary_dataset(d, 0, nb, nq) - - index_ref = faiss.IndexBinaryFlat(d) - index_ref.add(xb) - - Dref, Iref = index_ref.search(xq, 10) - - nrep = 5 - index = faiss.IndexBinaryReplicas() - for _i in range(nrep): - sub_idx = faiss.IndexBinaryFlat(d) - sub_idx.add(xb) - index.addIndex(sub_idx) - - D, I = index.search(xq, 10) - - self.assertTrue((Dref == D).all()) - self.assertTrue((Iref == I).all()) - - index2 = faiss.IndexBinaryReplicas() - for _i in range(nrep): - sub_idx = faiss.IndexBinaryFlat(d) - index2.addIndex(sub_idx) - - index2.add(xb) - D2, I2 = index2.search(xq, 10) - - self.assertTrue((Dref == D2).all()) - self.assertTrue((Iref == I2).all()) - - def test_shards(self): - d = 32 - nq = 100 - nb = 200 - - (_, xb, xq) = make_binary_dataset(d, 0, nb, nq) - - index_ref = faiss.IndexBinaryFlat(d) - index_ref.add(xb) - - Dref, Iref = index_ref.search(xq, 10) - - nrep = 5 - index = faiss.IndexBinaryShards(d) - for i in range(nrep): - sub_idx = faiss.IndexBinaryFlat(d) - sub_idx.add(xb[i * nb // nrep : (i + 1) * nb // nrep]) - index.add_shard(sub_idx) - - D, I = index.search(xq, 10) - - compare_binary_result_lists(Dref, Iref, D, I) - - index2 = faiss.IndexBinaryShards(d) - for _i in range(nrep): - sub_idx = faiss.IndexBinaryFlat(d) - index2.add_shard(sub_idx) - - index2.add(xb) - D2, I2 = index2.search(xq, 10) - - compare_binary_result_lists(Dref, Iref, D2, I2) - - -if __name__ == '__main__': - unittest.main() diff --git a/core/src/index/thirdparty/faiss/tests/test_index_binary_from_float.py b/core/src/index/thirdparty/faiss/tests/test_index_binary_from_float.py deleted file mode 100644 index 73d6c726d4..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_index_binary_from_float.py +++ /dev/null @@ -1,200 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -from __future__ import absolute_import, division, print_function - -import numpy as np -import unittest -import faiss - - -def make_binary_dataset(d, nb, nt, nq): - assert d % 8 == 0 - rs = np.random.RandomState(123) - x = rs.randint(256, size=(nb + nq + nt, int(d / 8))).astype('uint8') - return x[:nt], x[nt:-nq], x[-nq:] - - -def binary_to_float(x): - n, d = x.shape - x8 = x.reshape(n * d, -1) - c8 = 2 * ((x8 >> np.arange(8)) & 1).astype('int8') - 1 - return c8.astype('float32').reshape(n, d * 8) - - -class TestIndexBinaryFromFloat(unittest.TestCase): - """Use a binary index backed by a float index""" - - def test_index_from_float(self): - d = 256 - nt = 0 - nb = 1500 - nq = 500 - (xt, xb, xq) = make_binary_dataset(d, nb, nt, nq) - - index_ref = faiss.IndexFlatL2(d) - index_ref.add(binary_to_float(xb)) - - index = faiss.IndexFlatL2(d) - index_bin = faiss.IndexBinaryFromFloat(index) - index_bin.add(xb) - - D_ref, I_ref = index_ref.search(binary_to_float(xq), 10) - D, I = index_bin.search(xq, 10) - - np.testing.assert_allclose((D_ref / 4.0).astype('int32'), D) - - def test_wrapped_quantizer(self): - d = 256 - nt = 150 - nb = 1500 - nq = 500 - (xt, xb, xq) = make_binary_dataset(d, nb, nt, nq) - - nlist = 16 - quantizer_ref = faiss.IndexBinaryFlat(d) - index_ref = faiss.IndexBinaryIVF(quantizer_ref, d, nlist) - index_ref.train(xt) - - index_ref.add(xb) - - unwrapped_quantizer = faiss.IndexFlatL2(d) - quantizer = faiss.IndexBinaryFromFloat(unwrapped_quantizer) - index = faiss.IndexBinaryIVF(quantizer, d, nlist) - - index.train(xt) - - index.add(xb) - - D_ref, I_ref = index_ref.search(xq, 10) - D, I = index.search(xq, 10) - - np.testing.assert_array_equal(D_ref, D) - - def test_wrapped_quantizer_IMI(self): - d = 256 - nt = 3500 - nb = 10000 - nq = 500 - (xt, xb, xq) = make_binary_dataset(d, nb, nt, nq) - - index_ref = faiss.IndexBinaryFlat(d) - - index_ref.add(xb) - - nlist_exp = 6 - nlist = 2 ** (2 * nlist_exp) - float_quantizer = faiss.MultiIndexQuantizer(d, 2, nlist_exp) - wrapped_quantizer = faiss.IndexBinaryFromFloat(float_quantizer) - wrapped_quantizer.train(xt) - - assert nlist == float_quantizer.ntotal - - index = faiss.IndexBinaryIVF(wrapped_quantizer, d, - float_quantizer.ntotal) - index.nprobe = 2048 - assert index.is_trained - - index.add(xb) - - D_ref, I_ref = index_ref.search(xq, 10) - D, I = index.search(xq, 10) - - recall = sum(gti[0] in Di[:10] for gti, Di in zip(D_ref, D)) \ - / float(D_ref.shape[0]) - - assert recall > 0.82, "recall = %g" % recall - - def test_wrapped_quantizer_HNSW(self): - faiss.omp_set_num_threads(1) - - def bin2float(v): - def byte2float(byte): - return np.array([-1.0 + 2.0 * (byte & (1 << b) != 0) - for b in range(0, 8)]) - - return np.hstack([byte2float(byte) for byte in v]).astype('float32') - - def floatvec2nparray(v): - return np.array([np.float32(v.at(i)) for i in range(0, v.size())]) \ - .reshape(-1, d) - - d = 256 - nt = 12800 - nb = 10000 - nq = 500 - (xt, xb, xq) = make_binary_dataset(d, nb, nt, nq) - - index_ref = faiss.IndexBinaryFlat(d) - - index_ref.add(xb) - - nlist = 256 - clus = faiss.Clustering(d, nlist) - clus_index = faiss.IndexFlatL2(d) - - xt_f = np.array([bin2float(v) for v in xt]) - clus.train(xt_f, clus_index) - - centroids = floatvec2nparray(clus.centroids) - hnsw_quantizer = faiss.IndexHNSWFlat(d, 32) - hnsw_quantizer.add(centroids) - hnsw_quantizer.is_trained = True - wrapped_quantizer = faiss.IndexBinaryFromFloat(hnsw_quantizer) - - assert nlist == hnsw_quantizer.ntotal - assert nlist == wrapped_quantizer.ntotal - assert wrapped_quantizer.is_trained - - index = faiss.IndexBinaryIVF(wrapped_quantizer, d, - hnsw_quantizer.ntotal) - index.nprobe = 128 - - assert index.is_trained - - index.add(xb) - - D_ref, I_ref = index_ref.search(xq, 10) - D, I = index.search(xq, 10) - - recall = sum(gti[0] in Di[:10] for gti, Di in zip(D_ref, D)) \ - / float(D_ref.shape[0]) - - assert recall > 0.77, "recall = %g" % recall - - -class TestOverrideKmeansQuantizer(unittest.TestCase): - - def test_override(self): - d = 256 - nt = 3500 - nb = 10000 - nq = 500 - (xt, xb, xq) = make_binary_dataset(d, nb, nt, nq) - - def train_and_get_centroids(override_kmeans_index): - index = faiss.index_binary_factory(d, "BIVF10") - index.verbose = True - - if override_kmeans_index is not None: - index.clustering_index = override_kmeans_index - - index.train(xt) - - centroids = faiss.downcast_IndexBinary(index.quantizer).xb - return faiss.vector_to_array(centroids).reshape(-1, d // 8) - - centroids_ref = train_and_get_centroids(None) - - # should do the exact same thing - centroids_new = train_and_get_centroids(faiss.IndexFlatL2(d)) - - assert np.all(centroids_ref == centroids_new) - - # will do less accurate assignment... Sanity check that the - # index is indeed used by kmeans - centroids_new = train_and_get_centroids(faiss.IndexLSH(d, 16)) - - assert not np.all(centroids_ref == centroids_new) diff --git a/core/src/index/thirdparty/faiss/tests/test_index_composite.py b/core/src/index/thirdparty/faiss/tests/test_index_composite.py deleted file mode 100644 index 55230f9d9b..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_index_composite.py +++ /dev/null @@ -1,571 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -""" more elaborate that test_index.py """ -from __future__ import absolute_import, division, print_function - -import numpy as np -import unittest -import faiss -import os -import shutil -import tempfile - -from common import get_dataset_2 - -class TestRemove(unittest.TestCase): - - def do_merge_then_remove(self, ondisk): - d = 10 - nb = 1000 - nq = 200 - nt = 200 - - xt, xb, xq = get_dataset_2(d, nt, nb, nq) - - quantizer = faiss.IndexFlatL2(d) - - index1 = faiss.IndexIVFFlat(quantizer, d, 20) - index1.train(xt) - - filename = None - if ondisk: - filename = tempfile.mkstemp()[1] - invlists = faiss.OnDiskInvertedLists( - index1.nlist, index1.code_size, - filename) - index1.replace_invlists(invlists) - - index1.add(xb[:int(nb / 2)]) - - index2 = faiss.IndexIVFFlat(quantizer, d, 20) - assert index2.is_trained - index2.add(xb[int(nb / 2):]) - - Dref, Iref = index1.search(xq, 10) - index1.merge_from(index2, int(nb / 2)) - - assert index1.ntotal == nb - - index1.remove_ids(faiss.IDSelectorRange(int(nb / 2), nb)) - - assert index1.ntotal == int(nb / 2) - Dnew, Inew = index1.search(xq, 10) - - assert np.all(Dnew == Dref) - assert np.all(Inew == Iref) - - if filename is not None: - os.unlink(filename) - - def test_remove_regular(self): - self.do_merge_then_remove(False) - - def test_remove_ondisk(self): - self.do_merge_then_remove(True) - - def test_remove(self): - # only tests the python interface - - index = faiss.IndexFlat(5) - xb = np.zeros((10, 5), dtype='float32') - xb[:, 0] = np.arange(10) + 1000 - index.add(xb) - index.remove_ids(np.arange(5) * 2) - xb2 = faiss.vector_float_to_array(index.xb).reshape(5, 5) - assert np.all(xb2[:, 0] == xb[np.arange(5) * 2 + 1, 0]) - - def test_remove_id_map(self): - sub_index = faiss.IndexFlat(5) - xb = np.zeros((10, 5), dtype='float32') - xb[:, 0] = np.arange(10) + 1000 - index = faiss.IndexIDMap2(sub_index) - index.add_with_ids(xb, np.arange(10) + 100) - assert index.reconstruct(104)[0] == 1004 - index.remove_ids(np.array([103])) - assert index.reconstruct(104)[0] == 1004 - try: - index.reconstruct(103) - except RuntimeError: - pass - else: - assert False, 'should have raised an exception' - - def test_remove_id_map_2(self): - # from https://github.com/facebookresearch/faiss/issues/255 - rs = np.random.RandomState(1234) - X = rs.randn(10, 10).astype(np.float32) - idx = np.array([0, 10, 20, 30, 40, 5, 15, 25, 35, 45], np.int64) - remove_set = np.array([10, 30], dtype=np.int64) - index = faiss.index_factory(10, 'IDMap,Flat') - index.add_with_ids(X[:5, :], idx[:5]) - index.remove_ids(remove_set) - index.add_with_ids(X[5:, :], idx[5:]) - - print (index.search(X, 1)) - - for i in range(10): - _, searchres = index.search(X[i:i + 1, :], 1) - if idx[i] in remove_set: - assert searchres[0] != idx[i] - else: - assert searchres[0] == idx[i] - - def test_remove_id_map_binary(self): - sub_index = faiss.IndexBinaryFlat(40) - xb = np.zeros((10, 5), dtype='uint8') - xb[:, 0] = np.arange(10) + 100 - index = faiss.IndexBinaryIDMap2(sub_index) - index.add_with_ids(xb, np.arange(10) + 1000) - assert index.reconstruct(1004)[0] == 104 - index.remove_ids(np.array([1003])) - assert index.reconstruct(1004)[0] == 104 - try: - index.reconstruct(1003) - except RuntimeError: - pass - else: - assert False, 'should have raised an exception' - - # while we are there, let's test I/O as well... - _, tmpnam = tempfile.mkstemp() - try: - faiss.write_index_binary(index, tmpnam) - index = faiss.read_index_binary(tmpnam) - finally: - os.remove(tmpnam) - - assert index.reconstruct(1004)[0] == 104 - try: - index.reconstruct(1003) - except RuntimeError: - pass - else: - assert False, 'should have raised an exception' - - - -class TestRangeSearch(unittest.TestCase): - - def test_range_search_id_map(self): - sub_index = faiss.IndexFlat(5, 1) # L2 search instead of inner product - xb = np.zeros((10, 5), dtype='float32') - xb[:, 0] = np.arange(10) + 1000 - index = faiss.IndexIDMap2(sub_index) - index.add_with_ids(xb, np.arange(10) + 100) - dist = float(np.linalg.norm(xb[3] - xb[0])) * 0.99 - res_subindex = sub_index.range_search(xb[[0], :], dist) - res_index = index.range_search(xb[[0], :], dist) - assert len(res_subindex[2]) == 2 - np.testing.assert_array_equal(res_subindex[2] + 100, res_index[2]) - - -class TestUpdate(unittest.TestCase): - - def test_update(self): - d = 64 - nb = 1000 - nt = 1500 - nq = 100 - np.random.seed(123) - xb = np.random.random(size=(nb, d)).astype('float32') - xt = np.random.random(size=(nt, d)).astype('float32') - xq = np.random.random(size=(nq, d)).astype('float32') - - index = faiss.index_factory(d, "IVF64,Flat") - index.train(xt) - index.add(xb) - index.nprobe = 32 - D, I = index.search(xq, 5) - - index.make_direct_map() - recons_before = np.vstack([index.reconstruct(i) for i in range(nb)]) - - # revert order of the 200 first vectors - nu = 200 - index.update_vectors(np.arange(nu), xb[nu - 1::-1].copy()) - - recons_after = np.vstack([index.reconstruct(i) for i in range(nb)]) - - # make sure reconstructions remain the same - diff_recons = recons_before[:nu] - recons_after[nu - 1::-1] - assert np.abs(diff_recons).max() == 0 - - D2, I2 = index.search(xq, 5) - - assert np.all(D == D2) - - gt_map = np.arange(nb) - gt_map[:nu] = np.arange(nu, 0, -1) - 1 - eqs = I.ravel() == gt_map[I2.ravel()] - - assert np.all(eqs) - - -class TestPCAWhite(unittest.TestCase): - - def test_white(self): - - # generate data - d = 4 - nt = 1000 - nb = 200 - nq = 200 - - # normal distribition - x = faiss.randn((nt + nb + nq) * d, 1234).reshape(nt + nb + nq, d) - - index = faiss.index_factory(d, 'Flat') - - xt = x[:nt] - xb = x[nt:-nq] - xq = x[-nq:] - - # NN search on normal distribution - index.add(xb) - Do, Io = index.search(xq, 5) - - # make distribution very skewed - x *= [10, 4, 1, 0.5] - rr, _ = np.linalg.qr(faiss.randn(d * d).reshape(d, d)) - x = np.dot(x, rr).astype('float32') - - xt = x[:nt] - xb = x[nt:-nq] - xq = x[-nq:] - - # L2 search on skewed distribution - index = faiss.index_factory(d, 'Flat') - - index.add(xb) - Dl2, Il2 = index.search(xq, 5) - - # whiten + L2 search on L2 distribution - index = faiss.index_factory(d, 'PCAW%d,Flat' % d) - - index.train(xt) - index.add(xb) - Dw, Iw = index.search(xq, 5) - - # make sure correlation of whitened results with original - # results is much better than simple L2 distances - # should be 961 vs. 264 - assert (faiss.eval_intersection(Io, Iw) > - 2 * faiss.eval_intersection(Io, Il2)) - - -class TestTransformChain(unittest.TestCase): - - def test_chain(self): - - # generate data - d = 4 - nt = 1000 - nb = 200 - nq = 200 - - # normal distribition - x = faiss.randn((nt + nb + nq) * d, 1234).reshape(nt + nb + nq, d) - - # make distribution very skewed - x *= [10, 4, 1, 0.5] - rr, _ = np.linalg.qr(faiss.randn(d * d).reshape(d, d)) - x = np.dot(x, rr).astype('float32') - - xt = x[:nt] - xb = x[nt:-nq] - xq = x[-nq:] - - index = faiss.index_factory(d, "L2norm,PCA2,L2norm,Flat") - - assert index.chain.size() == 3 - l2_1 = faiss.downcast_VectorTransform(index.chain.at(0)) - assert l2_1.norm == 2 - pca = faiss.downcast_VectorTransform(index.chain.at(1)) - assert not pca.is_trained - index.train(xt) - assert pca.is_trained - - index.add(xb) - D, I = index.search(xq, 5) - - # do the computation manually and check if we get the same result - def manual_trans(x): - x = x.copy() - faiss.normalize_L2(x) - x = pca.apply_py(x) - faiss.normalize_L2(x) - return x - - index2 = faiss.IndexFlatL2(2) - index2.add(manual_trans(xb)) - D2, I2 = index2.search(manual_trans(xq), 5) - - assert np.all(I == I2) - -class TestRareIO(unittest.TestCase): - - def compare_results(self, index1, index2, xq): - - Dref, Iref = index1.search(xq, 5) - Dnew, Inew = index2.search(xq, 5) - - assert np.all(Dref == Dnew) - assert np.all(Iref == Inew) - - def do_mmappedIO(self, sparse, in_pretransform=False): - d = 10 - nb = 1000 - nq = 200 - nt = 200 - xt, xb, xq = get_dataset_2(d, nt, nb, nq) - - quantizer = faiss.IndexFlatL2(d) - index1 = faiss.IndexIVFFlat(quantizer, d, 20) - if sparse: - # makes the inverted lists sparse because all elements get - # assigned to the same invlist - xt += (np.ones(10) * 1000).astype('float32') - - if in_pretransform: - # make sure it still works when wrapped in an IndexPreTransform - index1 = faiss.IndexPreTransform(index1) - - index1.train(xt) - index1.add(xb) - - _, fname = tempfile.mkstemp() - try: - - faiss.write_index(index1, fname) - - index2 = faiss.read_index(fname) - self.compare_results(index1, index2, xq) - - index3 = faiss.read_index(fname, faiss.IO_FLAG_MMAP) - self.compare_results(index1, index3, xq) - finally: - if os.path.exists(fname): - os.unlink(fname) - - def test_mmappedIO_sparse(self): - self.do_mmappedIO(True) - - def test_mmappedIO_full(self): - self.do_mmappedIO(False) - - def test_mmappedIO_pretrans(self): - self.do_mmappedIO(False, True) - - -class TestIVFFlatDedup(unittest.TestCase): - - def normalize_res(self, D, I): - dmax = D[-1] - res = [(d, i) for d, i in zip(D, I) if d < dmax] - res.sort() - return res - - def test_dedup(self): - d = 10 - nb = 1000 - nq = 200 - nt = 500 - xt, xb, xq = get_dataset_2(d, nt, nb, nq) - - # introduce duplicates - xb[500:900:2] = xb[501:901:2] - xb[901::4] = xb[900::4] - xb[902::4] = xb[900::4] - xb[903::4] = xb[900::4] - - # also in the train set - xt[201::2] = xt[200::2] - - quantizer = faiss.IndexFlatL2(d) - index_new = faiss.IndexIVFFlatDedup(quantizer, d, 20) - - index_new.verbose = True - # should display - # IndexIVFFlatDedup::train: train on 350 points after dedup (was 500 points) - index_new.train(xt) - - index_ref = faiss.IndexIVFFlat(quantizer, d, 20) - assert index_ref.is_trained - - index_ref.nprobe = 5 - index_ref.add(xb) - index_new.nprobe = 5 - index_new.add(xb) - - Dref, Iref = index_ref.search(xq, 20) - Dnew, Inew = index_new.search(xq, 20) - - for i in range(nq): - ref = self.normalize_res(Dref[i], Iref[i]) - new = self.normalize_res(Dnew[i], Inew[i]) - assert ref == new - - # test I/O - _, tmpfile = tempfile.mkstemp() - try: - faiss.write_index(index_new, tmpfile) - index_st = faiss.read_index(tmpfile) - finally: - if os.path.exists(tmpfile): - os.unlink(tmpfile) - Dst, Ist = index_st.search(xq, 20) - - for i in range(nq): - new = self.normalize_res(Dnew[i], Inew[i]) - st = self.normalize_res(Dst[i], Ist[i]) - assert st == new - - # test remove - toremove = np.hstack((np.arange(3, 1000, 5), np.arange(850, 950))) - index_ref.remove_ids(toremove) - index_new.remove_ids(toremove) - - Dref, Iref = index_ref.search(xq, 20) - Dnew, Inew = index_new.search(xq, 20) - - for i in range(nq): - ref = self.normalize_res(Dref[i], Iref[i]) - new = self.normalize_res(Dnew[i], Inew[i]) - assert ref == new - - -class TestSerialize(unittest.TestCase): - - def test_serialize_to_vector(self): - d = 10 - nb = 1000 - nq = 200 - nt = 500 - xt, xb, xq = get_dataset_2(d, nt, nb, nq) - - index = faiss.IndexFlatL2(d) - index.add(xb) - - Dref, Iref = index.search(xq, 5) - - writer = faiss.VectorIOWriter() - faiss.write_index(index, writer) - - ar_data = faiss.vector_to_array(writer.data) - - # direct transfer of vector - reader = faiss.VectorIOReader() - reader.data.swap(writer.data) - - index2 = faiss.read_index(reader) - - Dnew, Inew = index2.search(xq, 5) - assert np.all(Dnew == Dref) and np.all(Inew == Iref) - - # from intermediate numpy array - reader = faiss.VectorIOReader() - faiss.copy_array_to_vector(ar_data, reader.data) - - index3 = faiss.read_index(reader) - - Dnew, Inew = index3.search(xq, 5) - assert np.all(Dnew == Dref) and np.all(Inew == Iref) - - -class TestRenameOndisk(unittest.TestCase): - - def test_rename(self): - d = 10 - nb = 500 - nq = 100 - nt = 100 - - xt, xb, xq = get_dataset_2(d, nt, nb, nq) - - quantizer = faiss.IndexFlatL2(d) - - index1 = faiss.IndexIVFFlat(quantizer, d, 20) - index1.train(xt) - - dirname = tempfile.mkdtemp() - - try: - - # make an index with ondisk invlists - invlists = faiss.OnDiskInvertedLists( - index1.nlist, index1.code_size, - dirname + '/aa.ondisk') - index1.replace_invlists(invlists) - index1.add(xb) - D1, I1 = index1.search(xq, 10) - faiss.write_index(index1, dirname + '/aa.ivf') - - # move the index elsewhere - os.mkdir(dirname + '/1') - for fname in 'aa.ondisk', 'aa.ivf': - os.rename(dirname + '/' + fname, - dirname + '/1/' + fname) - - # try to read it: fails! - try: - index2 = faiss.read_index(dirname + '/1/aa.ivf') - except RuntimeError: - pass # normal - else: - assert False - - # read it with magic flag - index2 = faiss.read_index(dirname + '/1/aa.ivf', - faiss.IO_FLAG_ONDISK_SAME_DIR) - D2, I2 = index2.search(xq, 10) - assert np.all(I1 == I2) - - finally: - shutil.rmtree(dirname) - - -class TestInvlistMeta(unittest.TestCase): - - def test_slice_vstack(self): - d = 10 - nb = 1000 - nq = 100 - nt = 200 - - xt, xb, xq = get_dataset_2(d, nt, nb, nq) - - quantizer = faiss.IndexFlatL2(d) - index = faiss.IndexIVFFlat(quantizer, d, 30) - - index.train(xt) - index.add(xb) - Dref, Iref = index.search(xq, 10) - - # faiss.wait() - - il0 = index.invlists - ils = [] - ilv = faiss.InvertedListsPtrVector() - for sl in 0, 1, 2: - il = faiss.SliceInvertedLists(il0, sl * 10, sl * 10 + 10) - ils.append(il) - ilv.push_back(il) - - il2 = faiss.VStackInvertedLists(ilv.size(), ilv.data()) - - index2 = faiss.IndexIVFFlat(quantizer, d, 30) - index2.replace_invlists(il2) - index2.ntotal = index.ntotal - - D, I = index2.search(xq, 10) - assert np.all(D == Dref) - assert np.all(I == Iref) - - - - -if __name__ == '__main__': - unittest.main() diff --git a/core/src/index/thirdparty/faiss/tests/test_io.py b/core/src/index/thirdparty/faiss/tests/test_io.py deleted file mode 100644 index 7e3d6edf59..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_io.py +++ /dev/null @@ -1,220 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#!/usr/bin/env python3 - -import numpy as np -import unittest -import faiss -import tempfile -import os -import io -import sys -import warnings -from multiprocessing.dummy import Pool as ThreadPool - -from common import get_dataset, get_dataset_2 - - -class TestIOVariants(unittest.TestCase): - - def test_io_error(self): - d, n = 32, 1000 - x = np.random.uniform(size=(n, d)).astype('float32') - index = faiss.IndexFlatL2(d) - index.add(x) - _, fname = tempfile.mkstemp() - try: - faiss.write_index(index, fname) - - # should be fine - faiss.read_index(fname) - - # now damage file - data = open(fname, 'rb').read() - data = data[:int(len(data) / 2)] - open(fname, 'wb').write(data) - - # should make a nice readable exception that mentions the filename - try: - faiss.read_index(fname) - except RuntimeError as e: - if fname not in str(e): - raise - else: - raise - - finally: - if os.path.exists(fname): - os.unlink(fname) - - -class TestCallbacks(unittest.TestCase): - - def do_write_callback(self, bsz): - d, n = 32, 1000 - x = np.random.uniform(size=(n, d)).astype('float32') - index = faiss.IndexFlatL2(d) - index.add(x) - - f = io.BytesIO() - # test with small block size - writer = faiss.PyCallbackIOWriter(f.write, 1234) - - if bsz > 0: - writer = faiss.BufferedIOWriter(writer, bsz) - - faiss.write_index(index, writer) - del writer # make sure all writes committed - - if sys.version_info[0] < 3: - buf = f.getvalue() - else: - buf = f.getbuffer() - - index2 = faiss.deserialize_index(np.frombuffer(buf, dtype='uint8')) - - self.assertEqual(index.d, index2.d) - self.assertTrue(np.all( - faiss.vector_to_array(index.xb) == faiss.vector_to_array(index2.xb) - )) - - # This is not a callable function: shoudl raise an exception - writer = faiss.PyCallbackIOWriter("blabla") - self.assertRaises( - Exception, - faiss.write_index, index, writer - ) - - def test_buf_read(self): - x = np.random.uniform(size=20) - - _, fname = tempfile.mkstemp() - try: - x.tofile(fname) - - f = open(fname, 'rb') - reader = faiss.PyCallbackIOReader(f.read, 1234) - - bsz = 123 - reader = faiss.BufferedIOReader(reader, bsz) - - y = np.zeros_like(x) - print('nbytes=', y.nbytes) - reader(faiss.swig_ptr(y), y.nbytes, 1) - - np.testing.assert_array_equal(x, y) - finally: - if os.path.exists(fname): - os.unlink(fname) - - def do_read_callback(self, bsz): - d, n = 32, 1000 - x = np.random.uniform(size=(n, d)).astype('float32') - index = faiss.IndexFlatL2(d) - index.add(x) - - _, fname = tempfile.mkstemp() - try: - faiss.write_index(index, fname) - - f = open(fname, 'rb') - - reader = faiss.PyCallbackIOReader(f.read, 1234) - - if bsz > 0: - reader = faiss.BufferedIOReader(reader, bsz) - - index2 = faiss.read_index(reader) - - self.assertEqual(index.d, index2.d) - np.testing.assert_array_equal( - faiss.vector_to_array(index.xb), - faiss.vector_to_array(index2.xb) - ) - - # This is not a callable function: should raise an exception - reader = faiss.PyCallbackIOReader("blabla") - self.assertRaises( - Exception, - faiss.read_index, reader - ) - finally: - if os.path.exists(fname): - os.unlink(fname) - - def test_write_callback(self): - self.do_write_callback(0) - - def test_write_buffer(self): - self.do_write_callback(123) - self.do_write_callback(2345) - - def test_read_callback(self): - self.do_read_callback(0) - - def test_read_callback_buffered(self): - self.do_read_callback(123) - self.do_read_callback(12345) - - def test_read_buffer(self): - d, n = 32, 1000 - x = np.random.uniform(size=(n, d)).astype('float32') - index = faiss.IndexFlatL2(d) - index.add(x) - - _, fname = tempfile.mkstemp() - try: - faiss.write_index(index, fname) - - reader = faiss.BufferedIOReader( - faiss.FileIOReader(fname), 1234) - - index2 = faiss.read_index(reader) - - self.assertEqual(index.d, index2.d) - np.testing.assert_array_equal( - faiss.vector_to_array(index.xb), - faiss.vector_to_array(index2.xb) - ) - - finally: - if os.path.exists(fname): - os.unlink(fname) - - - def test_transfer_pipe(self): - """ transfer an index through a Unix pipe """ - - d, n = 32, 1000 - x = np.random.uniform(size=(n, d)).astype('float32') - index = faiss.IndexFlatL2(d) - index.add(x) - Dref, Iref = index.search(x, 10) - - rf, wf = os.pipe() - - # start thread that will decompress the index - - def index_from_pipe(): - reader = faiss.PyCallbackIOReader(lambda size: os.read(rf, size)) - return faiss.read_index(reader) - - fut = ThreadPool(1).apply_async(index_from_pipe, ()) - - # write to pipe - writer = faiss.PyCallbackIOWriter(lambda b: os.write(wf, b)) - faiss.write_index(index, writer) - - index2 = fut.get() - - # closing is not really useful but it does not hurt - os.close(wf) - os.close(rf) - - Dnew, Inew = index2.search(x, 10) - - np.testing.assert_array_equal(Iref, Inew) - np.testing.assert_array_equal(Dref, Dnew) diff --git a/core/src/index/thirdparty/faiss/tests/test_ivflib.py b/core/src/index/thirdparty/faiss/tests/test_ivflib.py deleted file mode 100644 index 0166013c08..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_ivflib.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -from __future__ import absolute_import, division, print_function - -import unittest -import faiss -import numpy as np - -class TestIVFlib(unittest.TestCase): - - def test_methods_exported(self): - methods = ['check_compatible_for_merge', 'extract_index_ivf', - 'merge_into', 'search_centroid', - 'search_and_return_centroids', 'get_invlist_range', - 'set_invlist_range', 'search_with_parameters'] - - for method in methods: - assert callable(getattr(faiss, method, None)) - - -def search_single_scan(index, xq, k, bs=128): - """performs a search so that the inverted lists are accessed - sequentially by blocks of size bs""" - - # handle pretransform - if isinstance(index, faiss.IndexPreTransform): - xq = index.apply_py(xq) - index = faiss.downcast_index(index.index) - - # coarse assignment - coarse_dis, assign = index.quantizer.search(xq, index.nprobe) - nlist = index.nlist - assign_buckets = assign // bs - nq = len(xq) - - rh = faiss.ResultHeap(nq, k) - index.parallel_mode |= index.PARALLEL_MODE_NO_HEAP_INIT - - for l0 in range(0, nlist, bs): - bucket_no = l0 // bs - skip_rows, skip_cols = np.where(assign_buckets != bucket_no) - sub_assign = assign.copy() - sub_assign[skip_rows, skip_cols] = -1 - - index.search_preassigned( - nq, faiss.swig_ptr(xq), k, - faiss.swig_ptr(sub_assign), faiss.swig_ptr(coarse_dis), - faiss.swig_ptr(rh.D), faiss.swig_ptr(rh.I), - False, None - ) - - rh.finalize() - - return rh.D, rh.I - - -class TestSequentialScan(unittest.TestCase): - - def test_sequential_scan(self): - d = 20 - index = faiss.index_factory(d, 'IVF100,SQ8') - - rs = np.random.RandomState(123) - xt = rs.rand(5000, d).astype('float32') - xb = rs.rand(10000, d).astype('float32') - index.train(xt) - index.add(xb) - k = 15 - xq = rs.rand(200, d).astype('float32') - - ref_D, ref_I = index.search(xq, k) - D, I = search_single_scan(index, xq, k, bs=10) - - assert np.all(D == ref_D) - assert np.all(I == ref_I) diff --git a/core/src/index/thirdparty/faiss/tests/test_ivfpq_codec.cpp b/core/src/index/thirdparty/faiss/tests/test_ivfpq_codec.cpp deleted file mode 100644 index 8d18ac0ad9..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_ivfpq_codec.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -#include - -#include -#include -#include -#include - - -namespace { - -// dimension of the vectors to index -int d = 64; - -// size of the database we plan to index -size_t nb = 8000; - - -double eval_codec_error (long ncentroids, long m, const std::vector &v) -{ - faiss::IndexFlatL2 coarse_quantizer (d); - faiss::IndexIVFPQ index (&coarse_quantizer, d, - ncentroids, m, 8); - index.pq.cp.niter = 10; // speed up train - index.train (nb, v.data()); - - // encode and decode to compute reconstruction error - - std::vector keys (nb); - std::vector codes (nb * m); - index.encode_multiple (nb, keys.data(), v.data(), codes.data(), true); - - std::vector v2 (nb * d); - index.decode_multiple (nb, keys.data(), codes.data(), v2.data()); - - return faiss::fvec_L2sqr (v.data(), v2.data(), nb * d); -} - -} // namespace - - -TEST(IVFPQ, codec) { - - std::vector database (nb * d); - for (size_t i = 0; i < nb * d; i++) { - database[i] = drand48(); - } - - double err0 = eval_codec_error(16, 8, database); - - // should be more accurate as there are more coarse centroids - double err1 = eval_codec_error(128, 8, database); - EXPECT_GT(err0, err1); - - // should be more accurate as there are more PQ codes - double err2 = eval_codec_error(16, 16, database); - EXPECT_GT(err0, err2); -} diff --git a/core/src/index/thirdparty/faiss/tests/test_ivfpq_indexing.cpp b/core/src/index/thirdparty/faiss/tests/test_ivfpq_indexing.cpp deleted file mode 100644 index 9f4bbcd2ca..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_ivfpq_indexing.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include - -#include - -#include -#include -#include - -TEST(IVFPQ, accuracy) { - - // dimension of the vectors to index - int d = 64; - - // size of the database we plan to index - size_t nb = 1000; - - // make a set of nt training vectors in the unit cube - // (could be the database) - size_t nt = 1500; - - // make the index object and train it - faiss::IndexFlatL2 coarse_quantizer (d); - - // a reasonable number of cetroids to index nb vectors - int ncentroids = 25; - - faiss::IndexIVFPQ index (&coarse_quantizer, d, - ncentroids, 16, 8); - - // index that gives the ground-truth - faiss::IndexFlatL2 index_gt (d); - - srand48 (35); - - { // training - - std::vector trainvecs (nt * d); - for (size_t i = 0; i < nt * d; i++) { - trainvecs[i] = drand48(); - } - index.verbose = true; - index.train (nt, trainvecs.data()); - } - - { // populating the database - - std::vector database (nb * d); - for (size_t i = 0; i < nb * d; i++) { - database[i] = drand48(); - } - - index.add (nb, database.data()); - index_gt.add (nb, database.data()); - } - - int nq = 200; - int n_ok; - - { // searching the database - - std::vector queries (nq * d); - for (size_t i = 0; i < nq * d; i++) { - queries[i] = drand48(); - } - - std::vector gt_nns (nq); - std::vector gt_dis (nq); - - index_gt.search (nq, queries.data(), 1, - gt_dis.data(), gt_nns.data()); - - index.nprobe = 5; - int k = 5; - std::vector nns (k * nq); - std::vector dis (k * nq); - - index.search (nq, queries.data(), k, dis.data(), nns.data()); - - n_ok = 0; - for (int q = 0; q < nq; q++) { - - for (int i = 0; i < k; i++) - if (nns[q * k + i] == gt_nns[q]) - n_ok++; - } - EXPECT_GT(n_ok, nq * 0.4); - } - -} diff --git a/core/src/index/thirdparty/faiss/tests/test_lowlevel_ivf.cpp b/core/src/index/thirdparty/faiss/tests/test_lowlevel_ivf.cpp deleted file mode 100644 index 7baf801b7b..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_lowlevel_ivf.cpp +++ /dev/null @@ -1,566 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace faiss; - -namespace { - -typedef Index::idx_t idx_t; - - -// dimension of the vectors to index -int d = 32; - -// nb of training vectors -size_t nt = 5000; - -// size of the database points per window step -size_t nb = 1000; - -// nb of queries -size_t nq = 200; - -int k = 10; - - -std::vector make_data(size_t n) -{ - std::vector database (n * d); - for (size_t i = 0; i < n * d; i++) { - database[i] = drand48(); - } - return database; -} - -std::unique_ptr make_trained_index(const char *index_type, - MetricType metric_type) -{ - auto index = std::unique_ptr(index_factory( - d, index_type, metric_type)); - auto xt = make_data(nt); - index->train(nt, xt.data()); - ParameterSpace().set_index_parameter (index.get(), "nprobe", 4); - return index; -} - -std::vector search_index(Index *index, const float *xq) { - std::vector I(k * nq); - std::vector D(k * nq); - index->search (nq, xq, k, D.data(), I.data()); - return I; -} - - - - -/************************************************************* - * Test functions for a given index type - *************************************************************/ - - - -void test_lowlevel_access (const char *index_key, MetricType metric) { - std::unique_ptr index = make_trained_index(index_key, metric); - - auto xb = make_data (nb); - index->add(nb, xb.data()); - - /** handle the case if we have a preprocessor */ - - const IndexPreTransform *index_pt = - dynamic_cast (index.get()); - - int dt = index->d; - const float * xbt = xb.data(); - std::unique_ptr del_xbt; - - if (index_pt) { - dt = index_pt->index->d; - xbt = index_pt->apply_chain (nb, xb.data()); - if (xbt != xb.data()) { - del_xbt.reset((float*)xbt); - } - } - - IndexIVF * index_ivf = ivflib::extract_index_ivf (index.get()); - - /** Test independent encoding - * - * Makes it possible to do additions on a custom inverted list - * implementation. From a set of vectors, computes the inverted - * list ids + the codes corresponding to each vector. - */ - - std::vector list_nos (nb); - std::vector codes (index_ivf->code_size * nb); - index_ivf->quantizer->assign(nb, xbt, list_nos.data()); - index_ivf->encode_vectors (nb, xbt, list_nos.data(), codes.data()); - - // compare with normal IVF addition - - const InvertedLists *il = index_ivf->invlists; - - for (int list_no = 0; list_no < index_ivf->nlist; list_no++) { - InvertedLists::ScopedCodes ivf_codes (il, list_no); - InvertedLists::ScopedIds ivf_ids (il, list_no); - size_t list_size = il->list_size (list_no); - for (int i = 0; i < list_size; i++) { - const uint8_t *ref_code = ivf_codes.get() + i * il->code_size; - const uint8_t *new_code = - codes.data() + ivf_ids[i] * il->code_size; - EXPECT_EQ (memcmp(ref_code, new_code, il->code_size), 0); - } - } - - /** Test independent search - * - * Manually scans through inverted lists, computing distances and - * ordering results organized in a heap. - */ - - // sample some example queries and get reference search results. - auto xq = make_data (nq); - auto ref_I = search_index (index.get(), xq.data()); - - // handle preprocessing - const float * xqt = xq.data(); - std::unique_ptr del_xqt; - - if (index_pt) { - xqt = index_pt->apply_chain (nq, xq.data()); - if (xqt != xq.data()) { - del_xqt.reset((float*)xqt); - } - } - - // quantize the queries to get the inverted list ids to visit. - int nprobe = index_ivf->nprobe; - - std::vector q_lists (nq * nprobe); - std::vector q_dis (nq * nprobe); - - index_ivf->quantizer->search (nq, xqt, nprobe, - q_dis.data(), q_lists.data()); - - // object that does the scanning and distance computations. - std::unique_ptr scanner ( - index_ivf->get_InvertedListScanner()); - - for (int i = 0; i < nq; i++) { - std::vector I (k, -1); - float default_dis = metric == METRIC_L2 ? HUGE_VAL : -HUGE_VAL; - std::vector D (k, default_dis); - - scanner->set_query (xqt + i * dt); - - for (int j = 0; j < nprobe; j++) { - int list_no = q_lists[i * nprobe + j]; - if (list_no < 0) continue; - scanner->set_list (list_no, q_dis[i * nprobe + j]); - - // here we get the inverted lists from the InvertedLists - // object but they could come from anywhere - - scanner->scan_codes ( - il->list_size (list_no), - InvertedLists::ScopedCodes(il, list_no).get(), - InvertedLists::ScopedIds(il, list_no).get(), - D.data(), I.data(), k); - - if (j == 0) { - // all results so far come from list_no, so let's check if - // the distance function works - for (int jj = 0; jj < k; jj++) { - int vno = I[jj]; - if (vno < 0) break; // heap is not full yet - - // we have the codes from the addition test - float computed_D = scanner->distance_to_code ( - codes.data() + vno * il->code_size); - - EXPECT_EQ (computed_D, D[jj]); - } - } - } - - // re-order heap - if (metric == METRIC_L2) { - maxheap_reorder (k, D.data(), I.data()); - } else { - minheap_reorder (k, D.data(), I.data()); - } - - // check that we have the same results as the reference search - for (int j = 0; j < k; j++) { - EXPECT_EQ (I[j], ref_I[i * k + j]); - } - } - - -} - -} // anonymous namespace - - - -/************************************************************* - * Test entry points - *************************************************************/ - -TEST(TestLowLevelIVF, IVFFlatL2) { - test_lowlevel_access ("IVF32,Flat", METRIC_L2); -} - -TEST(TestLowLevelIVF, PCAIVFFlatL2) { - test_lowlevel_access ("PCAR16,IVF32,Flat", METRIC_L2); -} - -TEST(TestLowLevelIVF, IVFFlatIP) { - test_lowlevel_access ("IVF32,Flat", METRIC_INNER_PRODUCT); -} - -TEST(TestLowLevelIVF, IVFSQL2) { - test_lowlevel_access ("IVF32,SQ8", METRIC_L2); -} - -TEST(TestLowLevelIVF, IVFSQIP) { - test_lowlevel_access ("IVF32,SQ8", METRIC_INNER_PRODUCT); -} - - -TEST(TestLowLevelIVF, IVFPQL2) { - test_lowlevel_access ("IVF32,PQ4np", METRIC_L2); -} - -TEST(TestLowLevelIVF, IVFPQIP) { - test_lowlevel_access ("IVF32,PQ4np", METRIC_INNER_PRODUCT); -} - - -/************************************************************* - * Same for binary (a bit simpler) - *************************************************************/ - -namespace { - -int nbit = 256; - -// here d is used the number of ints -> d=32 means 128 bits - -std::vector make_data_binary(size_t n) -{ - - std::vector database (n * nbit / 8); - for (size_t i = 0; i < n * d; i++) { - database[i] = lrand48(); - } - return database; -} - -std::unique_ptr make_trained_index_binary(const char *index_type) -{ - auto index = std::unique_ptr(index_binary_factory( - nbit, index_type)); - auto xt = make_data_binary (nt); - index->train(nt, xt.data()); - return index; -} - - -void test_lowlevel_access_binary (const char *index_key) { - std::unique_ptr index = - make_trained_index_binary (index_key); - - IndexBinaryIVF * index_ivf = dynamic_cast - (index.get()); - assert (index_ivf); - - index_ivf->nprobe = 4; - - auto xb = make_data_binary (nb); - index->add(nb, xb.data()); - - std::vector list_nos (nb); - index_ivf->quantizer->assign(nb, xb.data(), list_nos.data()); - - /* For binary there is no test for encoding because binary vectors - * are copied verbatim to the inverted lists */ - - const InvertedLists *il = index_ivf->invlists; - - /** Test independent search - * - * Manually scans through inverted lists, computing distances and - * ordering results organized in a heap. - */ - - // sample some example queries and get reference search results. - auto xq = make_data_binary (nq); - - std::vector I_ref(k * nq); - std::vector D_ref(k * nq); - index->search (nq, xq.data(), k, D_ref.data(), I_ref.data()); - - // quantize the queries to get the inverted list ids to visit. - int nprobe = index_ivf->nprobe; - - std::vector q_lists (nq * nprobe); - std::vector q_dis (nq * nprobe); - - // quantize queries - index_ivf->quantizer->search (nq, xq.data(), nprobe, - q_dis.data(), q_lists.data()); - - // object that does the scanning and distance computations. - std::unique_ptr scanner ( - index_ivf->get_InvertedListScanner()); - - for (int i = 0; i < nq; i++) { - std::vector I (k, -1); - uint32_t default_dis = 1 << 30; - std::vector D (k, default_dis); - - scanner->set_query (xq.data() + i * index_ivf->code_size); - - for (int j = 0; j < nprobe; j++) { - int list_no = q_lists[i * nprobe + j]; - if (list_no < 0) continue; - scanner->set_list (list_no, q_dis[i * nprobe + j]); - - // here we get the inverted lists from the InvertedLists - // object but they could come from anywhere - - scanner->scan_codes ( - il->list_size (list_no), - InvertedLists::ScopedCodes(il, list_no).get(), - InvertedLists::ScopedIds(il, list_no).get(), - D.data(), I.data(), k); - - if (j == 0) { - // all results so far come from list_no, so let's check if - // the distance function works - for (int jj = 0; jj < k; jj++) { - int vno = I[jj]; - if (vno < 0) break; // heap is not full yet - - // we have the codes from the addition test - float computed_D = scanner->distance_to_code ( - xb.data() + vno * il->code_size); - - EXPECT_EQ (computed_D, D[jj]); - } - } - } - - printf("new before reroder: ["); - for (int j = 0; j < k; j++) - printf("%ld,%d ", I[j], D[j]); - printf("]\n"); - - // re-order heap - heap_reorder > (k, D.data(), I.data()); - - printf("ref: ["); - for (int j = 0; j < k; j++) - printf("%ld,%d ", I_ref[j], D_ref[j]); - printf("]\nnew: ["); - for (int j = 0; j < k; j++) - printf("%ld,%d ", I[j], D[j]); - printf("]\n"); - - // check that we have the same results as the reference search - for (int j = 0; j < k; j++) { - // here the order is not guaranteed to be the same - // so we scan through ref results - // EXPECT_EQ (I[j], I_ref[i * k + j]); - EXPECT_LE (D[j], D_ref[i * k + k - 1]); - if (D[j] < D_ref[i * k + k - 1]) { - int j2 = 0; - while (j2 < k) { - if (I[j] == I_ref[i * k + j2]) break; - j2++; - } - EXPECT_LT(j2, k); // it was found - if (j2 < k) { - EXPECT_EQ(D[j], D_ref[i * k + j2]); - } - } - - } - - } - - -} - -} // anonymous namespace - - -TEST(TestLowLevelIVF, IVFBinary) { - test_lowlevel_access_binary ("BIVF32"); -} - - -namespace { - -void test_threaded_search (const char *index_key, MetricType metric) { - std::unique_ptr index = make_trained_index(index_key, metric); - - auto xb = make_data (nb); - index->add(nb, xb.data()); - - /** handle the case if we have a preprocessor */ - - const IndexPreTransform *index_pt = - dynamic_cast (index.get()); - - int dt = index->d; - const float * xbt = xb.data(); - std::unique_ptr del_xbt; - - if (index_pt) { - dt = index_pt->index->d; - xbt = index_pt->apply_chain (nb, xb.data()); - if (xbt != xb.data()) { - del_xbt.reset((float*)xbt); - } - } - - IndexIVF * index_ivf = ivflib::extract_index_ivf (index.get()); - - /** Test independent search - * - * Manually scans through inverted lists, computing distances and - * ordering results organized in a heap. - */ - - // sample some example queries and get reference search results. - auto xq = make_data (nq); - auto ref_I = search_index (index.get(), xq.data()); - - // handle preprocessing - const float * xqt = xq.data(); - std::unique_ptr del_xqt; - - if (index_pt) { - xqt = index_pt->apply_chain (nq, xq.data()); - if (xqt != xq.data()) { - del_xqt.reset((float*)xqt); - } - } - - // quantize the queries to get the inverted list ids to visit. - int nprobe = index_ivf->nprobe; - - std::vector q_lists (nq * nprobe); - std::vector q_dis (nq * nprobe); - - index_ivf->quantizer->search (nq, xqt, nprobe, - q_dis.data(), q_lists.data()); - - // now run search in this many threads - int nproc = 3; - - - for (int i = 0; i < nq; i++) { - - // one result table per thread - std::vector I (k * nproc, -1); - float default_dis = metric == METRIC_L2 ? HUGE_VAL : -HUGE_VAL; - std::vector D (k * nproc, default_dis); - - auto search_function = [index_ivf, &I, &D, dt, i, nproc, - xqt, nprobe, &q_dis, &q_lists] - (int rank) { - const InvertedLists *il = index_ivf->invlists; - - // object that does the scanning and distance computations. - std::unique_ptr scanner ( - index_ivf->get_InvertedListScanner()); - - idx_t *local_I = I.data() + rank * k; - float *local_D = D.data() + rank * k; - - scanner->set_query (xqt + i * dt); - - for (int j = rank; j < nprobe; j += nproc) { - int list_no = q_lists[i * nprobe + j]; - if (list_no < 0) continue; - scanner->set_list (list_no, q_dis[i * nprobe + j]); - - scanner->scan_codes ( - il->list_size (list_no), - InvertedLists::ScopedCodes(il, list_no).get(), - InvertedLists::ScopedIds(il, list_no).get(), - local_D, local_I, k); - } - }; - - // start the threads. Threads are numbered rank=0..nproc-1 (a la MPI) - // thread rank takes care of inverted lists - // rank, rank+nproc, rank+2*nproc,... - std::vector threads; - for (int rank = 0; rank < nproc; rank++) { - threads.emplace_back(search_function, rank); - } - - // join threads, merge heaps - for (int rank = 0; rank < nproc; rank++) { - threads[rank].join(); - if (rank == 0) continue; // nothing to merge - // merge into first result - if (metric == METRIC_L2) { - maxheap_addn (k, D.data(), I.data(), - D.data() + rank * k, - I.data() + rank * k, k); - } else { - minheap_addn (k, D.data(), I.data(), - D.data() + rank * k, - I.data() + rank * k, k); - } - } - - // re-order heap - if (metric == METRIC_L2) { - maxheap_reorder (k, D.data(), I.data()); - } else { - minheap_reorder (k, D.data(), I.data()); - } - - // check that we have the same results as the reference search - for (int j = 0; j < k; j++) { - EXPECT_EQ (I[j], ref_I[i * k + j]); - } - } - - -} - -} // anonymous namepace - - -TEST(TestLowLevelIVF, ThreadedSearch) { - test_threaded_search ("IVF32,Flat", METRIC_L2); -} diff --git a/core/src/index/thirdparty/faiss/tests/test_merge.cpp b/core/src/index/thirdparty/faiss/tests/test_merge.cpp deleted file mode 100644 index 47af106149..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_merge.cpp +++ /dev/null @@ -1,257 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - - -namespace { - - -struct Tempfilename { - - static pthread_mutex_t mutex; - - std::string filename; - - Tempfilename (const char *prefix = nullptr) { - pthread_mutex_lock (&mutex); - char *cfname = tempnam (nullptr, prefix); - filename = cfname; - free(cfname); - pthread_mutex_unlock (&mutex); - } - - ~Tempfilename () { - if (access (filename.c_str(), F_OK)) { - unlink (filename.c_str()); - } - } - - const char *c_str() { - return filename.c_str(); - } - -}; - -pthread_mutex_t Tempfilename::mutex = PTHREAD_MUTEX_INITIALIZER; - - -typedef faiss::Index::idx_t idx_t; - -// parameters to use for the test -int d = 64; -size_t nb = 1000; -size_t nq = 100; -int nindex = 4; -int k = 10; -int nlist = 40; - -struct CommonData { - - std::vector database; - std::vector queries; - std::vector ids; - faiss::IndexFlatL2 quantizer; - - CommonData(): database (nb * d), queries (nq * d), ids(nb), quantizer (d) { - - for (size_t i = 0; i < nb * d; i++) { - database[i] = drand48(); - } - for (size_t i = 0; i < nq * d; i++) { - queries[i] = drand48(); - } - for (int i = 0; i < nb; i++) { - ids[i] = 123 + 456 * i; - } - { // just to train the quantizer - faiss::IndexIVFFlat iflat (&quantizer, d, nlist); - iflat.train(nb, database.data()); - } - } -}; - -CommonData cd; - -/// perform a search on shards, then merge and search again and -/// compare results. -int compare_merged (faiss::IndexShards *index_shards, bool shift_ids, - bool standard_merge = true) -{ - - std::vector refI(k * nq); - std::vector refD(k * nq); - - index_shards->search(nq, cd.queries.data(), k, refD.data(), refI.data()); - Tempfilename filename; - - std::vector newI(k * nq); - std::vector newD(k * nq); - - if (standard_merge) { - - for (int i = 1; i < nindex; i++) { - faiss::ivflib::merge_into( - index_shards->at(0), index_shards->at(i), - shift_ids); - } - - index_shards->sync_with_shard_indexes(); - } else { - std::vector lists; - faiss::IndexIVF *index0 = nullptr; - size_t ntotal = 0; - for (int i = 0; i < nindex; i++) { - auto index_ivf = dynamic_cast(index_shards->at(i)); - assert (index_ivf); - if (i == 0) { - index0 = index_ivf; - } - lists.push_back (index_ivf->invlists); - ntotal += index_ivf->ntotal; - } - - auto il = new faiss::OnDiskInvertedLists( - index0->nlist, index0->code_size, - filename.c_str()); - - il->merge_from(lists.data(), lists.size()); - - index0->replace_invlists(il, true); - index0->ntotal = ntotal; - } - // search only on first index - index_shards->at(0)->search(nq, cd.queries.data(), - k, newD.data(), newI.data()); - - size_t ndiff = 0; - for (size_t i = 0; i < k * nq; i++) { - if (refI[i] != newI[i]) { - ndiff ++; - } - } - return ndiff; -} - -} // namespace - - -// test on IVFFlat with implicit numbering -TEST(MERGE, merge_flat_no_ids) { - faiss::IndexShards index_shards(d); - index_shards.own_fields = true; - for (int i = 0; i < nindex; i++) { - index_shards.add_shard ( - new faiss::IndexIVFFlat (&cd.quantizer, d, nlist)); - } - EXPECT_TRUE(index_shards.is_trained); - index_shards.add(nb, cd.database.data()); - size_t prev_ntotal = index_shards.ntotal; - int ndiff = compare_merged(&index_shards, true); - EXPECT_EQ (prev_ntotal, index_shards.ntotal); - EXPECT_EQ(0, ndiff); -} - - -// test on IVFFlat, explicit ids -TEST(MERGE, merge_flat) { - faiss::IndexShards index_shards(d, false, false); - index_shards.own_fields = true; - - for (int i = 0; i < nindex; i++) { - index_shards.add_shard ( - new faiss::IndexIVFFlat (&cd.quantizer, d, nlist)); - } - - EXPECT_TRUE(index_shards.is_trained); - index_shards.add_with_ids(nb, cd.database.data(), cd.ids.data()); - int ndiff = compare_merged(&index_shards, false); - EXPECT_GE(0, ndiff); -} - -// test on IVFFlat and a VectorTransform -TEST(MERGE, merge_flat_vt) { - faiss::IndexShards index_shards(d, false, false); - index_shards.own_fields = true; - - // here we have to retrain because of the vectorTransform - faiss::RandomRotationMatrix rot(d, d); - rot.init(1234); - faiss::IndexFlatL2 quantizer (d); - - { // just to train the quantizer - faiss::IndexIVFFlat iflat (&quantizer, d, nlist); - faiss::IndexPreTransform ipt (&rot, &iflat); - ipt.train(nb, cd.database.data()); - } - - for (int i = 0; i < nindex; i++) { - faiss::IndexPreTransform * ipt = new faiss::IndexPreTransform ( - new faiss::RandomRotationMatrix (rot), - new faiss::IndexIVFFlat (&quantizer, d, nlist) - ); - ipt->own_fields = true; - index_shards.add_shard (ipt); - } - EXPECT_TRUE(index_shards.is_trained); - index_shards.add_with_ids(nb, cd.database.data(), cd.ids.data()); - size_t prev_ntotal = index_shards.ntotal; - int ndiff = compare_merged(&index_shards, false); - EXPECT_EQ (prev_ntotal, index_shards.ntotal); - EXPECT_GE(0, ndiff); -} - - -// put the merged invfile on disk -TEST(MERGE, merge_flat_ondisk) { - faiss::IndexShards index_shards(d, false, false); - index_shards.own_fields = true; - Tempfilename filename; - - for (int i = 0; i < nindex; i++) { - auto ivf = new faiss::IndexIVFFlat (&cd.quantizer, d, nlist); - if (i == 0) { - auto il = new faiss::OnDiskInvertedLists ( - ivf->nlist, ivf->code_size, - filename.c_str()); - ivf->replace_invlists(il, true); - } - index_shards.add_shard (ivf); - } - - EXPECT_TRUE(index_shards.is_trained); - index_shards.add_with_ids(nb, cd.database.data(), cd.ids.data()); - int ndiff = compare_merged(&index_shards, false); - - EXPECT_EQ(ndiff, 0); -} - -// now use ondisk specific merge -TEST(MERGE, merge_flat_ondisk_2) { - faiss::IndexShards index_shards(d, false, false); - index_shards.own_fields = true; - - for (int i = 0; i < nindex; i++) { - index_shards.add_shard ( - new faiss::IndexIVFFlat (&cd.quantizer, d, nlist)); - } - EXPECT_TRUE(index_shards.is_trained); - index_shards.add_with_ids(nb, cd.database.data(), cd.ids.data()); - int ndiff = compare_merged(&index_shards, false, false); - EXPECT_GE(0, ndiff); -} diff --git a/core/src/index/thirdparty/faiss/tests/test_meta_index.py b/core/src/index/thirdparty/faiss/tests/test_meta_index.py deleted file mode 100644 index 137efc2aeb..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_meta_index.py +++ /dev/null @@ -1,264 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -from __future__ import absolute_import, division, print_function, unicode_literals - -# translation of test_meta_index.lua - -import numpy as np -import faiss -import unittest - -from common import Randu10k - -ru = Randu10k() - -xb = ru.xb -xt = ru.xt -xq = ru.xq -nb, d = xb.shape -nq, d = xq.shape - - -class IDRemap(unittest.TestCase): - - def test_id_remap_idmap(self): - # reference: index without remapping - - index = faiss.IndexPQ(d, 8, 8) - k = 10 - index.train(xt) - index.add(xb) - _Dref, Iref = index.search(xq, k) - - # try a remapping - ids = np.arange(nb)[::-1].copy() - - sub_index = faiss.IndexPQ(d, 8, 8) - index2 = faiss.IndexIDMap(sub_index) - - index2.train(xt) - index2.add_with_ids(xb, ids) - - _D, I = index2.search(xq, k) - - assert np.all(I == nb - 1 - Iref) - - def test_id_remap_ivf(self): - # coarse quantizer in common - coarse_quantizer = faiss.IndexFlatIP(d) - ncentroids = 25 - - # reference: index without remapping - - index = faiss.IndexIVFPQ(coarse_quantizer, d, - ncentroids, 8, 8) - index.nprobe = 5 - k = 10 - index.train(xt) - index.add(xb) - _Dref, Iref = index.search(xq, k) - - # try a remapping - ids = np.arange(nb)[::-1].copy() - - index2 = faiss.IndexIVFPQ(coarse_quantizer, d, - ncentroids, 8, 8) - index2.nprobe = 5 - - index2.train(xt) - index2.add_with_ids(xb, ids) - - _D, I = index2.search(xq, k) - assert np.all(I == nb - 1 - Iref) - - -class Shards(unittest.TestCase): - - def test_shards(self): - k = 32 - ref_index = faiss.IndexFlatL2(d) - - print('ref search') - ref_index.add(xb) - _Dref, Iref = ref_index.search(xq, k) - print(Iref[:5, :6]) - - shard_index = faiss.IndexShards(d) - shard_index_2 = faiss.IndexShards(d, True, False) - - ni = 3 - for i in range(ni): - i0 = int(i * nb / ni) - i1 = int((i + 1) * nb / ni) - index = faiss.IndexFlatL2(d) - index.add(xb[i0:i1]) - shard_index.add_shard(index) - - index_2 = faiss.IndexFlatL2(d) - irm = faiss.IndexIDMap(index_2) - shard_index_2.add_shard(irm) - - # test parallel add - shard_index_2.verbose = True - shard_index_2.add(xb) - - for test_no in range(3): - with_threads = test_no == 1 - - print('shard search test_no = %d' % test_no) - if with_threads: - remember_nt = faiss.omp_get_max_threads() - faiss.omp_set_num_threads(1) - shard_index.threaded = True - else: - shard_index.threaded = False - - if test_no != 2: - _D, I = shard_index.search(xq, k) - else: - _D, I = shard_index_2.search(xq, k) - - print(I[:5, :6]) - - if with_threads: - faiss.omp_set_num_threads(remember_nt) - - ndiff = (I != Iref).sum() - - print('%d / %d differences' % (ndiff, nq * k)) - assert(ndiff < nq * k / 1000.) - - -class Merge(unittest.TestCase): - - def make_index_for_merge(self, quant, index_type, master_index): - ncent = 40 - if index_type == 1: - index = faiss.IndexIVFFlat(quant, d, ncent, faiss.METRIC_L2) - if master_index: - index.is_trained = True - elif index_type == 2: - index = faiss.IndexIVFPQ(quant, d, ncent, 4, 8) - if master_index: - index.pq = master_index.pq - index.is_trained = True - elif index_type == 3: - index = faiss.IndexIVFPQR(quant, d, ncent, 4, 8, 8, 8) - if master_index: - index.pq = master_index.pq - index.refine_pq = master_index.refine_pq - index.is_trained = True - elif index_type == 4: - # quant used as the actual index - index = faiss.IndexIDMap(quant) - return index - - def do_test_merge(self, index_type): - k = 16 - quant = faiss.IndexFlatL2(d) - ref_index = self.make_index_for_merge(quant, index_type, False) - - # trains the quantizer - ref_index.train(xt) - - print('ref search') - ref_index.add(xb) - _Dref, Iref = ref_index.search(xq, k) - print(Iref[:5, :6]) - - indexes = [] - ni = 3 - for i in range(ni): - i0 = int(i * nb / ni) - i1 = int((i + 1) * nb / ni) - index = self.make_index_for_merge(quant, index_type, ref_index) - index.is_trained = True - index.add(xb[i0:i1]) - indexes.append(index) - - index = indexes[0] - - for i in range(1, ni): - print('merge ntotal=%d other.ntotal=%d ' % ( - index.ntotal, indexes[i].ntotal)) - index.merge_from(indexes[i], index.ntotal) - - _D, I = index.search(xq, k) - print(I[:5, :6]) - - ndiff = (I != Iref).sum() - print('%d / %d differences' % (ndiff, nq * k)) - assert(ndiff < nq * k / 1000.) - - def test_merge(self): - self.do_test_merge(1) - self.do_test_merge(2) - self.do_test_merge(3) - - def do_test_remove(self, index_type): - k = 16 - quant = faiss.IndexFlatL2(d) - index = self.make_index_for_merge(quant, index_type, None) - - # trains the quantizer - index.train(xt) - - if index_type < 4: - index.add(xb) - else: - gen = np.random.RandomState(1234) - id_list = gen.permutation(nb * 7)[:nb] - index.add_with_ids(xb, id_list) - - - print('ref search ntotal=%d' % index.ntotal) - Dref, Iref = index.search(xq, k) - - toremove = np.zeros(nq * k, dtype=int) - nr = 0 - for i in range(nq): - for j in range(k): - # remove all even results (it's ok if there are duplicates - # in the list of ids) - if Iref[i, j] % 2 == 0: - nr = nr + 1 - toremove[nr] = Iref[i, j] - - print('nr=', nr) - - idsel = faiss.IDSelectorBatch( - nr, faiss.swig_ptr(toremove)) - - for i in range(nr): - assert(idsel.is_member(int(toremove[i]))) - - nremoved = index.remove_ids(idsel) - - print('nremoved=%d ntotal=%d' % (nremoved, index.ntotal)) - - D, I = index.search(xq, k) - - # make sure results are in the same order with even ones removed - for i in range(nq): - j2 = 0 - for j in range(k): - if Iref[i, j] % 2 != 0: - assert I[i, j2] == Iref[i, j] - assert abs(D[i, j2] - Dref[i, j]) < 1e-5 - j2 += 1 - - def test_remove(self): - self.do_test_remove(1) - self.do_test_remove(2) - self.do_test_remove(4) - - - - - - -if __name__ == '__main__': - unittest.main() diff --git a/core/src/index/thirdparty/faiss/tests/test_omp_threads.cpp b/core/src/index/thirdparty/faiss/tests/test_omp_threads.cpp deleted file mode 100644 index 216a89dde1..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_omp_threads.cpp +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -#include - -TEST(Threading, openmp) { - EXPECT_TRUE(faiss::check_openmp()); -} diff --git a/core/src/index/thirdparty/faiss/tests/test_omp_threads_py.py b/core/src/index/thirdparty/faiss/tests/test_omp_threads_py.py deleted file mode 100644 index c96494dc1f..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_omp_threads_py.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -from __future__ import absolute_import, division, print_function, unicode_literals - -import faiss -import unittest - - -class TestOpenMP(unittest.TestCase): - - def test_openmp(self): - assert faiss.check_openmp() diff --git a/core/src/index/thirdparty/faiss/tests/test_ondisk_ivf.cpp b/core/src/index/thirdparty/faiss/tests/test_ondisk_ivf.cpp deleted file mode 100644 index c7f717fafe..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_ondisk_ivf.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include - - -namespace { - -struct Tempfilename { - - static pthread_mutex_t mutex; - - std::string filename; - - Tempfilename (const char *prefix = nullptr) { - pthread_mutex_lock (&mutex); - char *cfname = tempnam (nullptr, prefix); - filename = cfname; - free(cfname); - pthread_mutex_unlock (&mutex); - } - - ~Tempfilename () { - if (access (filename.c_str(), F_OK)) { - unlink (filename.c_str()); - } - } - - const char *c_str() { - return filename.c_str(); - } - -}; - -pthread_mutex_t Tempfilename::mutex = PTHREAD_MUTEX_INITIALIZER; - -} // namespace - - -TEST(ONDISK, make_invlists) { - int nlist = 100; - int code_size = 32; - int nadd = 1000000; - std::unordered_map listnos; - - Tempfilename filename; - - faiss::OnDiskInvertedLists ivf ( - nlist, code_size, - filename.c_str()); - - { - std::vector code(32); - for (int i = 0; i < nadd; i++) { - double d = drand48(); - int list_no = int(nlist * d * d); // skewed distribution - int * ar = (int*)code.data(); - ar[0] = i; - ar[1] = list_no; - ivf.add_entry (list_no, i, code.data()); - listnos[i] = list_no; - } - } - - int ntot = 0; - for (int i = 0; i < nlist; i++) { - int size = ivf.list_size(i); - const faiss::Index::idx_t *ids = ivf.get_ids (i); - const uint8_t *codes = ivf.get_codes (i); - for (int j = 0; j < size; j++) { - faiss::Index::idx_t id = ids[j]; - const int * ar = (const int*)&codes[code_size * j]; - EXPECT_EQ (ar[0], id); - EXPECT_EQ (ar[1], i); - EXPECT_EQ (listnos[id], i); - ntot ++; - } - } - EXPECT_EQ (ntot, nadd); -}; - - -TEST(ONDISK, test_add) { - int d = 8; - int nlist = 30, nq = 200, nb = 1500, k = 10; - faiss::IndexFlatL2 quantizer(d); - { - std::vector x(d * nlist); - faiss::float_rand(x.data(), d * nlist, 12345); - quantizer.add(nlist, x.data()); - } - std::vector xb(d * nb); - faiss::float_rand(xb.data(), d * nb, 23456); - - faiss::IndexIVFFlat index(&quantizer, d, nlist); - index.add(nb, xb.data()); - - std::vector xq(d * nb); - faiss::float_rand(xq.data(), d * nq, 34567); - - std::vector ref_D (nq * k); - std::vector ref_I (nq * k); - - index.search (nq, xq.data(), k, - ref_D.data(), ref_I.data()); - - Tempfilename filename, filename2; - - // test add + search - { - faiss::IndexIVFFlat index2(&quantizer, d, nlist); - - faiss::OnDiskInvertedLists ivf ( - index.nlist, index.code_size, - filename.c_str()); - - index2.replace_invlists(&ivf); - - index2.add(nb, xb.data()); - - std::vector new_D (nq * k); - std::vector new_I (nq * k); - - index2.search (nq, xq.data(), k, - new_D.data(), new_I.data()); - - EXPECT_EQ (ref_D, new_D); - EXPECT_EQ (ref_I, new_I); - - write_index(&index2, filename2.c_str()); - - } - - // test io - { - faiss::Index *index3 = faiss::read_index(filename2.c_str()); - - std::vector new_D (nq * k); - std::vector new_I (nq * k); - - index3->search (nq, xq.data(), k, - new_D.data(), new_I.data()); - - EXPECT_EQ (ref_D, new_D); - EXPECT_EQ (ref_I, new_I); - - delete index3; - } - -}; - - - -// WARN this thest will run multithreaded only in opt mode -TEST(ONDISK, make_invlists_threaded) { - int nlist = 100; - int code_size = 32; - int nadd = 1000000; - - Tempfilename filename; - - faiss::OnDiskInvertedLists ivf ( - nlist, code_size, - filename.c_str()); - - std::vector list_nos (nadd); - - for (int i = 0; i < nadd; i++) { - double d = drand48(); - list_nos[i] = int(nlist * d * d); // skewed distribution - } - -#pragma omp parallel - { - std::vector code(32); -#pragma omp for - for (int i = 0; i < nadd; i++) { - int list_no = list_nos[i]; - int * ar = (int*)code.data(); - ar[0] = i; - ar[1] = list_no; - ivf.add_entry (list_no, i, code.data()); - } - } - - int ntot = 0; - for (int i = 0; i < nlist; i++) { - int size = ivf.list_size(i); - const faiss::Index::idx_t *ids = ivf.get_ids (i); - const uint8_t *codes = ivf.get_codes (i); - for (int j = 0; j < size; j++) { - faiss::Index::idx_t id = ids[j]; - const int * ar = (const int*)&codes[code_size * j]; - EXPECT_EQ (ar[0], id); - EXPECT_EQ (ar[1], i); - EXPECT_EQ (list_nos[id], i); - ntot ++; - } - } - EXPECT_EQ (ntot, nadd); - -}; diff --git a/core/src/index/thirdparty/faiss/tests/test_oom_exception.py b/core/src/index/thirdparty/faiss/tests/test_oom_exception.py deleted file mode 100644 index 72dfdc7e47..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_oom_exception.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#! /usr/bin/env python2 - -import sys -import faiss -import unittest -import resource - -class TestOOMException(unittest.TestCase): - - def test_outrageous_alloc(self): - # Disable test on OSX. - if sys.platform == "darwin": - return - - # https://github.com/facebookresearch/faiss/issues/758 - soft_as, hard_as = resource.getrlimit(resource.RLIMIT_AS) - # make sure that allocing more than 10G will fail - resource.setrlimit(resource.RLIMIT_AS, (10 * 1024 * 1024, hard_as)) - try: - x = faiss.IntVector() - try: - x.resize(10**11) # 400 G of RAM - except MemoryError: - pass # good, that's what we expect - else: - assert False, "should raise exception" - finally: - resource.setrlimit(resource.RLIMIT_AS, (soft_as, hard_as)) - - -if __name__ == '__main__': - unittest.main() diff --git a/core/src/index/thirdparty/faiss/tests/test_pairs_decoding.cpp b/core/src/index/thirdparty/faiss/tests/test_pairs_decoding.cpp deleted file mode 100644 index 7857d0fb50..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_pairs_decoding.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include - - -namespace { - -typedef faiss::Index::idx_t idx_t; - -/************************************************************* - * Test utils - *************************************************************/ - - -// dimension of the vectors to index -int d = 64; - -// size of the database we plan to index -size_t nb = 8000; - -// nb of queries -size_t nq = 200; - -std::vector make_data(size_t n) -{ - std::vector database (n * d); - for (size_t i = 0; i < n * d; i++) { - database[i] = drand48(); - } - return database; -} - -std::unique_ptr make_index(const char *index_type, - const std::vector & x) { - - auto index = std::unique_ptr ( - faiss::index_factory(d, index_type)); - index->train(nb, x.data()); - index->add(nb, x.data()); - return index; -} - -/************************************************************* - * Test functions for a given index type - *************************************************************/ - -bool test_search_centroid(const char *index_key) { - std::vector xb = make_data(nb); // database vectors - auto index = make_index(index_key, xb); - - /* First test: find the centroids associated to the database - vectors and make sure that each vector does indeed appear in - the inverted list corresponding to its centroid */ - - std::vector centroid_ids (nb); - faiss::ivflib::search_centroid( - index.get(), xb.data(), nb, centroid_ids.data()); - - const faiss::IndexIVF * ivf = faiss::ivflib::extract_index_ivf - (index.get()); - - for(int i = 0; i < nb; i++) { - bool found = false; - int list_no = centroid_ids[i]; - int list_size = ivf->invlists->list_size (list_no); - auto * list = ivf->invlists->get_ids (list_no); - - for(int j = 0; j < list_size; j++) { - if (list[j] == i) { - found = true; - break; - } - } - if(!found) return false; - } - return true; -} - -int test_search_and_return_centroids(const char *index_key) { - std::vector xb = make_data(nb); // database vectors - auto index = make_index(index_key, xb); - - std::vector centroid_ids (nb); - faiss::ivflib::search_centroid(index.get(), xb.data(), - nb, centroid_ids.data()); - - faiss::IndexIVF * ivf = - faiss::ivflib::extract_index_ivf (index.get()); - ivf->nprobe = 4; - - std::vector xq = make_data(nq); // database vectors - - int k = 5; - - // compute a reference search result - - std::vector refI (nq * k); - std::vector refD (nq * k); - index->search (nq, xq.data(), k, refD.data(), refI.data()); - - // compute search result - - std::vector newI (nq * k); - std::vector newD (nq * k); - - std::vector query_centroid_ids (nq); - std::vector result_centroid_ids (nq * k); - - faiss::ivflib::search_and_return_centroids(index.get(), - nq, xq.data(), k, - newD.data(), newI.data(), - query_centroid_ids.data(), - result_centroid_ids.data()); - - // first verify that we have the same result as the standard search - - if (newI != refI) { - return 1; - } - - // then check if the result ids are indeed in the inverted list - // they are supposed to be in - - for(int i = 0; i < nq * k; i++) { - int list_no = result_centroid_ids[i]; - int result_no = newI[i]; - - if (result_no < 0) continue; - - bool found = false; - - int list_size = ivf->invlists->list_size (list_no); - auto * list = ivf->invlists->get_ids (list_no); - - for(int j = 0; j < list_size; j++) { - if (list[j] == result_no) { - found = true; - break; - } - } - if(!found) return 2; - } - return 0; -} - -} // namespace - - -/************************************************************* - * Test entry points - *************************************************************/ - -TEST(test_search_centroid, IVFFlat) { - bool ok = test_search_centroid("IVF32,Flat"); - EXPECT_TRUE(ok); -} - -TEST(test_search_centroid, PCAIVFFlat) { - bool ok = test_search_centroid("PCA16,IVF32,Flat"); - EXPECT_TRUE(ok); -} - -TEST(test_search_and_return_centroids, IVFFlat) { - int err = test_search_and_return_centroids("IVF32,Flat"); - EXPECT_NE(err, 1); - EXPECT_NE(err, 2); -} - -TEST(test_search_and_return_centroids, PCAIVFFlat) { - int err = test_search_and_return_centroids("PCA16,IVF32,Flat"); - EXPECT_NE(err, 1); - EXPECT_NE(err, 2); -} diff --git a/core/src/index/thirdparty/faiss/tests/test_params_override.cpp b/core/src/index/thirdparty/faiss/tests/test_params_override.cpp deleted file mode 100644 index d6df2a4efe..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_params_override.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include - -using namespace faiss; - -namespace { - -typedef Index::idx_t idx_t; - - -// dimension of the vectors to index -int d = 32; - -// size of the database we plan to index -size_t nb = 1000; - -// nb of queries -size_t nq = 200; - - - -std::vector make_data(size_t n) -{ - std::vector database (n * d); - for (size_t i = 0; i < n * d; i++) { - database[i] = drand48(); - } - return database; -} - -std::unique_ptr make_index(const char *index_type, - MetricType metric, - const std::vector & x) -{ - std::unique_ptr index(index_factory(d, index_type, metric)); - index->train(nb, x.data()); - index->add(nb, x.data()); - return index; -} - -std::vector search_index(Index *index, const float *xq) { - int k = 10; - std::vector I(k * nq); - std::vector D(k * nq); - index->search (nq, xq, k, D.data(), I.data()); - return I; -} - -std::vector search_index_with_params( - Index *index, const float *xq, IVFSearchParameters *params) { - int k = 10; - std::vector I(k * nq); - std::vector D(k * nq); - ivflib::search_with_parameters (index, nq, xq, k, - D.data(), I.data(), params); - return I; -} - - - - -/************************************************************* - * Test functions for a given index type - *************************************************************/ - -int test_params_override (const char *index_key, MetricType metric) { - std::vector xb = make_data(nb); // database vectors - auto index = make_index(index_key, metric, xb); - //index->train(nb, xb.data()); - // index->add(nb, xb.data()); - std::vector xq = make_data(nq); - ParameterSpace ps; - ps.set_index_parameter(index.get(), "nprobe", 2); - auto res2ref = search_index(index.get(), xq.data()); - ps.set_index_parameter(index.get(), "nprobe", 9); - auto res9ref = search_index(index.get(), xq.data()); - ps.set_index_parameter(index.get(), "nprobe", 1); - - IVFSearchParameters params; - params.max_codes = 0; - params.nprobe = 2; - auto res2new = search_index_with_params(index.get(), xq.data(), ¶ms); - params.nprobe = 9; - auto res9new = search_index_with_params(index.get(), xq.data(), ¶ms); - - if (res2ref != res2new) - return 2; - - if (res9ref != res9new) - return 9; - - return 0; -} - - -} // namespace - - -/************************************************************* - * Test entry points - *************************************************************/ - -TEST(TPO, IVFFlat) { - int err1 = test_params_override ("IVF32,Flat", METRIC_L2); - EXPECT_EQ(err1, 0); - int err2 = test_params_override ("IVF32,Flat", METRIC_INNER_PRODUCT); - EXPECT_EQ(err2, 0); -} - -TEST(TPO, IVFPQ) { - int err1 = test_params_override ("IVF32,PQ8np", METRIC_L2); - EXPECT_EQ(err1, 0); - int err2 = test_params_override ("IVF32,PQ8np", METRIC_INNER_PRODUCT); - EXPECT_EQ(err2, 0); -} - -TEST(TPO, IVFSQ) { - int err1 = test_params_override ("IVF32,SQ8", METRIC_L2); - EXPECT_EQ(err1, 0); - int err2 = test_params_override ("IVF32,SQ8", METRIC_INNER_PRODUCT); - EXPECT_EQ(err2, 0); -} - -TEST(TPO, IVFFlatPP) { - int err1 = test_params_override ("PCA16,IVF32,SQ8", METRIC_L2); - EXPECT_EQ(err1, 0); - int err2 = test_params_override ("PCA16,IVF32,SQ8", METRIC_INNER_PRODUCT); - EXPECT_EQ(err2, 0); -} - - - -/************************************************************* - * Same for binary indexes - *************************************************************/ - - -std::vector make_data_binary(size_t n) { - std::vector database (n * d / 8); - for (size_t i = 0; i < n * d / 8; i++) { - database[i] = lrand48(); - } - return database; -} - -std::unique_ptr make_index(const char *index_type, - const std::vector & x) -{ - - auto index = std::unique_ptr - (dynamic_cast(index_binary_factory (d, index_type))); - index->train(nb, x.data()); - index->add(nb, x.data()); - return index; -} - -std::vector search_index(IndexBinaryIVF *index, const uint8_t *xq) { - int k = 10; - std::vector I(k * nq); - std::vector D(k * nq); - index->search (nq, xq, k, D.data(), I.data()); - return I; -} - -std::vector search_index_with_params( - IndexBinaryIVF *index, const uint8_t *xq, IVFSearchParameters *params) { - int k = 10; - std::vector I(k * nq); - std::vector D(k * nq); - - std::vector Iq(params->nprobe * nq); - std::vector Dq(params->nprobe * nq); - - index->quantizer->search(nq, xq, params->nprobe, - Dq.data(), Iq.data()); - index->search_preassigned(nq, xq, k, Iq.data(), Dq.data(), - D.data(), I.data(), - false, params); - return I; -} - -int test_params_override_binary (const char *index_key) { - std::vector xb = make_data_binary(nb); // database vectors - auto index = make_index (index_key, xb); - index->train(nb, xb.data()); - index->add(nb, xb.data()); - std::vector xq = make_data_binary(nq); - index->nprobe = 2; - auto res2ref = search_index(index.get(), xq.data()); - index->nprobe = 9; - auto res9ref = search_index(index.get(), xq.data()); - index->nprobe = 1; - - IVFSearchParameters params; - params.max_codes = 0; - params.nprobe = 2; - auto res2new = search_index_with_params(index.get(), xq.data(), ¶ms); - params.nprobe = 9; - auto res9new = search_index_with_params(index.get(), xq.data(), ¶ms); - - if (res2ref != res2new) - return 2; - - if (res9ref != res9new) - return 9; - - return 0; -} - -TEST(TPOB, IVF) { - int err1 = test_params_override_binary ("BIVF32"); - EXPECT_EQ(err1, 0); -} diff --git a/core/src/index/thirdparty/faiss/tests/test_pq_encoding.cpp b/core/src/index/thirdparty/faiss/tests/test_pq_encoding.cpp deleted file mode 100644 index 214e925d15..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_pq_encoding.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include - -#include - -#include - - -namespace { - -const std::vector random_vector(size_t s) { - std::vector v(s, 0); - for (size_t i = 0; i < s; ++i) { - v[i] = rand(); - } - - return v; -} - -} // namespace - - -TEST(PQEncoderGeneric, encode) { - const int nsubcodes = 97; - const int minbits = 1; - const int maxbits = 24; - const std::vector values = random_vector(nsubcodes); - - for(int nbits = minbits; nbits <= maxbits; ++nbits) { - std::cerr << "nbits = " << nbits << std::endl; - - const uint64_t mask = (1ull << nbits) - 1; - std::unique_ptr codes( - new uint8_t[(nsubcodes * maxbits + 7) / 8] - ); - - // NOTE(hoss): Necessary scope to ensure trailing bits are flushed to mem. - { - faiss::PQEncoderGeneric encoder(codes.get(), nbits); - for (const auto& v : values) { - encoder.encode(v & mask); - } - } - - faiss::PQDecoderGeneric decoder(codes.get(), nbits); - for (int i = 0; i < nsubcodes; ++i) { - uint64_t v = decoder.decode(); - EXPECT_EQ(values[i] & mask, v); - } - } -} - - -TEST(PQEncoder8, encode) { - const int nsubcodes = 100; - const std::vector values = random_vector(nsubcodes); - const uint64_t mask = 0xFF; - std::unique_ptr codes(new uint8_t[nsubcodes]); - - faiss::PQEncoder8 encoder(codes.get(), 8); - for (const auto& v : values) { - encoder.encode(v & mask); - } - - faiss::PQDecoder8 decoder(codes.get(), 8); - for (int i = 0; i < nsubcodes; ++i) { - uint64_t v = decoder.decode(); - EXPECT_EQ(values[i] & mask, v); - } -} - - -TEST(PQEncoder16, encode) { - const int nsubcodes = 100; - const std::vector values = random_vector(nsubcodes); - const uint64_t mask = 0xFFFF; - std::unique_ptr codes(new uint8_t[2 * nsubcodes]); - - faiss::PQEncoder16 encoder(codes.get(), 16); - for (const auto& v : values) { - encoder.encode(v & mask); - } - - faiss::PQDecoder16 decoder(codes.get(), 16); - for (int i = 0; i < nsubcodes; ++i) { - uint64_t v = decoder.decode(); - EXPECT_EQ(values[i] & mask, v); - } -} diff --git a/core/src/index/thirdparty/faiss/tests/test_referenced_objects.py b/core/src/index/thirdparty/faiss/tests/test_referenced_objects.py deleted file mode 100644 index 35bf0f8eaa..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_referenced_objects.py +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -"""make sure that the referenced objects are kept""" -from __future__ import absolute_import, division, print_function, unicode_literals - -import numpy as np -import unittest -import faiss -import sys -import gc - -d = 10 -xt = np.random.rand(100, d).astype('float32') -xb = np.random.rand(20, d).astype('float32') - - -class TestReferenced(unittest.TestCase): - - def test_IndexIVF(self): - quantizer = faiss.IndexFlatL2(d) - index = faiss.IndexIVFFlat(quantizer, d, 10) - index.train(xt) - index.add(xb) - del quantizer - gc.collect() - index.add(xb) - - def test_count_refs(self): - quantizer = faiss.IndexFlatL2(d) - index = faiss.IndexIVFFlat(quantizer, d, 10) - refc1 = sys.getrefcount(quantizer) - del index - gc.collect() - refc2 = sys.getrefcount(quantizer) - assert refc2 == refc1 - 1 - - def test_IndexIVF_2(self): - index = faiss.IndexIVFFlat(faiss.IndexFlatL2(d), d, 10) - index.train(xt) - index.add(xb) - - def test_IndexPreTransform(self): - ltrans = faiss.NormalizationTransform(d) - sub_index = faiss.IndexFlatL2(d) - index = faiss.IndexPreTransform(ltrans, sub_index) - index.add(xb) - del ltrans - gc.collect() - index.add(xb) - del sub_index - gc.collect() - index.add(xb) - - def test_IndexPreTransform_2(self): - sub_index = faiss.IndexFlatL2(d) - index = faiss.IndexPreTransform(sub_index) - ltrans = faiss.NormalizationTransform(d) - index.prepend_transform(ltrans) - index.add(xb) - del ltrans - gc.collect() - index.add(xb) - del sub_index - gc.collect() - index.add(xb) - - def test_IDMap(self): - sub_index = faiss.IndexFlatL2(d) - index = faiss.IndexIDMap(sub_index) - index.add_with_ids(xb, np.arange(len(xb))) - del sub_index - gc.collect() - index.add_with_ids(xb, np.arange(len(xb))) - - def test_shards(self): - index = faiss.IndexShards(d) - for _i in range(3): - sub_index = faiss.IndexFlatL2(d) - sub_index.add(xb) - index.add_shard(sub_index) - gc.collect() - index.search(xb, 10) - - -dbin = 32 -xtbin = np.random.randint(256, size=(100, int(dbin / 8))).astype('uint8') -xbbin = np.random.randint(256, size=(20, int(dbin / 8))).astype('uint8') - - -class TestReferencedBinary(unittest.TestCase): - - def test_binary_ivf(self): - index = faiss.IndexBinaryIVF(faiss.IndexBinaryFlat(dbin), dbin, 10) - gc.collect() - index.train(xtbin) - - def test_wrap(self): - index = faiss.IndexBinaryFromFloat(faiss.IndexFlatL2(dbin)) - gc.collect() - index.add(xbbin) - -if __name__ == '__main__': - unittest.main() diff --git a/core/src/index/thirdparty/faiss/tests/test_sliding_ivf.cpp b/core/src/index/thirdparty/faiss/tests/test_sliding_ivf.cpp deleted file mode 100644 index 90ab516c83..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_sliding_ivf.cpp +++ /dev/null @@ -1,240 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include - -using namespace faiss; - -typedef Index::idx_t idx_t; - - -// dimension of the vectors to index -int d = 32; - -// nb of training vectors -size_t nt = 5000; - -// size of the database points per window step -size_t nb = 1000; - -// nb of queries -size_t nq = 200; - - -int total_size = 40; -int window_size = 10; - - - - - -std::vector make_data(size_t n) -{ - std::vector database (n * d); - for (size_t i = 0; i < n * d; i++) { - database[i] = drand48(); - } - return database; -} - -std::unique_ptr make_trained_index(const char *index_type) -{ - auto index = std::unique_ptr(index_factory(d, index_type)); - auto xt = make_data(nt * d); - index->train(nt, xt.data()); - ParameterSpace().set_index_parameter (index.get(), "nprobe", 4); - return index; -} - -std::vector search_index(Index *index, const float *xq) { - int k = 10; - std::vector I(k * nq); - std::vector D(k * nq); - index->search (nq, xq, k, D.data(), I.data()); - return I; -} - - - - - -/************************************************************* - * Test functions for a given index type - *************************************************************/ - - -// make a few slices of indexes that can be merged -void make_index_slices (const Index* trained_index, - std::vector > & sub_indexes) { - - for (int i = 0; i < total_size; i++) { - sub_indexes.emplace_back (clone_index (trained_index)); - - printf ("preparing sub-index # %d\n", i); - - Index * index = sub_indexes.back().get(); - - auto xb = make_data(nb * d); - std::vector ids (nb); - for (int j = 0; j < nb; j++) { - ids[j] = lrand48(); - } - index->add_with_ids (nb, xb.data(), ids.data()); - } - -} - -// build merged index explicitly at sliding window position i -Index *make_merged_index( - const Index* trained_index, - const std::vector > & sub_indexes, - int i) { - - Index * merged_index = clone_index (trained_index); - for (int j = i - window_size + 1; j <= i; j++) { - if (j < 0 || j >= total_size) continue; - std::unique_ptr sub_index ( - clone_index (sub_indexes[j].get())); - IndexIVF *ivf0 = ivflib::extract_index_ivf (merged_index); - IndexIVF *ivf1 = ivflib::extract_index_ivf (sub_index.get()); - ivf0->merge_from (*ivf1, 0); - merged_index->ntotal = ivf0->ntotal; - } - return merged_index; -} - -int test_sliding_window (const char *index_key) { - - std::unique_ptr trained_index = make_trained_index(index_key); - - // make the index slices - std::vector > sub_indexes; - - make_index_slices (trained_index.get(), sub_indexes); - - // now slide over the windows - std::unique_ptr index (clone_index (trained_index.get())); - ivflib::SlidingIndexWindow window (index.get()); - - auto xq = make_data (nq * d); - - for (int i = 0; i < total_size + window_size; i++) { - - printf ("doing step %d / %d\n", i, total_size + window_size); - - // update the index - window.step (i < total_size ? sub_indexes[i].get() : nullptr, - i >= window_size); - printf (" current n_slice = %d\n", window.n_slice); - - auto new_res = search_index (index.get(), xq.data()); - - std::unique_ptr merged_index ( - make_merged_index (trained_index.get(), sub_indexes, i)); - - auto ref_res = search_index (merged_index.get(), xq.data ()); - - EXPECT_EQ (ref_res.size(), new_res.size()); - - EXPECT_EQ (ref_res, new_res); - } - return 0; -} - - -int test_sliding_invlists (const char *index_key) { - - std::unique_ptr trained_index = make_trained_index(index_key); - - // make the index slices - std::vector > sub_indexes; - - make_index_slices (trained_index.get(), sub_indexes); - - // now slide over the windows - std::unique_ptr index (clone_index (trained_index.get())); - IndexIVF * index_ivf = ivflib::extract_index_ivf (index.get()); - - auto xq = make_data (nq * d); - - for (int i = 0; i < total_size + window_size; i++) { - - printf ("doing step %d / %d\n", i, total_size + window_size); - - // update the index - std::vector ils; - for (int j = i - window_size + 1; j <= i; j++) { - if (j < 0 || j >= total_size) continue; - ils.push_back (ivflib::extract_index_ivf ( - sub_indexes[j].get())->invlists); - } - if (ils.size() == 0) continue; - - ConcatenatedInvertedLists *ci = - new ConcatenatedInvertedLists (ils.size(), ils.data()); - - // will be deleted by the index - index_ivf->replace_invlists (ci, true); - - printf (" nb invlists = %ld\n", ils.size()); - - auto new_res = search_index (index.get(), xq.data()); - - std::unique_ptr merged_index ( - make_merged_index (trained_index.get(), sub_indexes, i)); - - auto ref_res = search_index (merged_index.get(), xq.data ()); - - EXPECT_EQ (ref_res.size(), new_res.size()); - - size_t ndiff = 0; - for (size_t j = 0; j < ref_res.size(); j++) { - if (ref_res[j] != new_res[j]) - ndiff++; - } - printf(" nb differences: %ld / %ld\n", - ndiff, ref_res.size()); - EXPECT_EQ (ref_res, new_res); - } - return 0; -} - - - - - -/************************************************************* - * Test entry points - *************************************************************/ - -TEST(SlidingWindow, IVFFlat) { - test_sliding_window ("IVF32,Flat"); -} - -TEST(SlidingWindow, PCAIVFFlat) { - test_sliding_window ("PCA24,IVF32,Flat"); -} - -TEST(SlidingInvlists, IVFFlat) { - test_sliding_invlists ("IVF32,Flat"); -} - -TEST(SlidingInvlists, PCAIVFFlat) { - test_sliding_invlists ("PCA24,IVF32,Flat"); -} diff --git a/core/src/index/thirdparty/faiss/tests/test_standalone_codec.py b/core/src/index/thirdparty/faiss/tests/test_standalone_codec.py deleted file mode 100644 index 95dc58c998..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_standalone_codec.py +++ /dev/null @@ -1,314 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -#! /usr/bin/env python2 - -""" test byte codecs """ - -from __future__ import print_function -import numpy as np -import unittest -import faiss -import tempfile -import os - -from common import get_dataset_2 - - -class TestEncodeDecode(unittest.TestCase): - - def do_encode_twice(self, factory_key): - d = 96 - nb = 1000 - nq = 0 - nt = 2000 - - xt, x, _ = get_dataset_2(d, nt, nb, nq) - - assert x.size > 0 - - codec = faiss.index_factory(d, factory_key) - - codec.train(xt) - - codes = codec.sa_encode(x) - x2 = codec.sa_decode(codes) - - codes2 = codec.sa_encode(x2) - - if 'IVF' not in factory_key: - self.assertTrue(np.all(codes == codes2)) - else: - # some rows are not reconstructed exactly because they - # flip into another quantization cell - nrowdiff = (codes != codes2).any(axis=1).sum() - self.assertTrue(nrowdiff < 10) - - x3 = codec.sa_decode(codes2) - if 'IVF' not in factory_key: - self.assertTrue(np.allclose(x2, x3)) - else: - diffs = np.abs(x2 - x3).sum(axis=1) - avg = np.abs(x2).sum(axis=1).mean() - diffs.sort() - assert diffs[-10] < avg * 1e-5 - - def test_SQ8(self): - self.do_encode_twice('SQ8') - - def test_IVFSQ8(self): - self.do_encode_twice('IVF256,SQ8') - - def test_PCAIVFSQ8(self): - self.do_encode_twice('PCAR32,IVF256,SQ8') - - def test_PQ6x8(self): - self.do_encode_twice('PQ6np') - - def test_PQ6x6(self): - self.do_encode_twice('PQ6x6np') - - def test_IVFPQ6x8np(self): - self.do_encode_twice('IVF512,PQ6np') - - def test_LSH(self): - self.do_encode_twice('LSHrt') - - -class TestIndexEquiv(unittest.TestCase): - - def do_test(self, key1, key2): - d = 96 - nb = 1000 - nq = 0 - nt = 2000 - - xt, x, _ = get_dataset_2(d, nt, nb, nq) - - codec_ref = faiss.index_factory(d, key1) - codec_ref.train(xt) - - code_ref = codec_ref.sa_encode(x) - x_recons_ref = codec_ref.sa_decode(code_ref) - - codec_new = faiss.index_factory(d, key2) - codec_new.pq = codec_ref.pq - - # replace quantizer, avoiding mem leak - oldq = codec_new.q1.quantizer - oldq.this.own() - codec_new.q1.own_fields = False - codec_new.q1.quantizer = codec_ref.quantizer - codec_new.is_trained = True - - code_new = codec_new.sa_encode(x) - x_recons_new = codec_new.sa_decode(code_new) - - self.assertTrue(np.all(code_new == code_ref)) - self.assertTrue(np.all(x_recons_new == x_recons_ref)) - - codec_new_2 = faiss.deserialize_index( - faiss.serialize_index(codec_new)) - - code_new = codec_new_2.sa_encode(x) - x_recons_new = codec_new_2.sa_decode(code_new) - - self.assertTrue(np.all(code_new == code_ref)) - self.assertTrue(np.all(x_recons_new == x_recons_ref)) - - def test_IVFPQ(self): - self.do_test("IVF512,PQ6np", "Residual512,PQ6") - - def test_IMI(self): - self.do_test("IMI2x5,PQ6np", "Residual2x5,PQ6") - - -class TestAccuracy(unittest.TestCase): - """ comparative accuracy of a few types of indexes """ - - def compare_accuracy(self, lowac, highac, max_errs=(1e10, 1e10)): - d = 96 - nb = 1000 - nq = 0 - nt = 2000 - - xt, x, _ = get_dataset_2(d, nt, nb, nq) - - errs = [] - - for factory_string in lowac, highac: - - codec = faiss.index_factory(d, factory_string) - print('sa codec: code size %d' % codec.sa_code_size()) - codec.train(xt) - - codes = codec.sa_encode(x) - x2 = codec.sa_decode(codes) - - err = ((x - x2) ** 2).sum() - errs.append(err) - - print(errs) - self.assertGreater(errs[0], errs[1]) - - self.assertGreater(max_errs[0], errs[0]) - self.assertGreater(max_errs[1], errs[1]) - - # just a small IndexLattice I/O test - if 'Lattice' in highac: - codec2 = faiss.deserialize_index( - faiss.serialize_index(codec)) - codes = codec.sa_encode(x) - x3 = codec.sa_decode(codes) - self.assertTrue(np.all(x2 == x3)) - - def test_SQ(self): - self.compare_accuracy('SQ4', 'SQ8') - - def test_SQ2(self): - self.compare_accuracy('SQ6', 'SQ8') - - def test_SQ3(self): - self.compare_accuracy('SQ8', 'SQfp16') - - def test_PQ(self): - self.compare_accuracy('PQ6x8np', 'PQ8x8np') - - def test_PQ2(self): - self.compare_accuracy('PQ8x6np', 'PQ8x8np') - - def test_IVFvsPQ(self): - self.compare_accuracy('PQ8np', 'IVF256,PQ8np') - - def test_Lattice(self): - # measured low/high: 20946.244, 5277.483 - self.compare_accuracy('ZnLattice3x10_4', - 'ZnLattice3x20_4', - (22000, 5400)) - - def test_Lattice2(self): - # here the difference is actually tiny - # measured errs: [16403.072, 15967.735] - self.compare_accuracy('ZnLattice3x12_1', - 'ZnLattice3x12_7', - (18000, 16000)) - - -swig_ptr = faiss.swig_ptr - - -class LatticeTest(unittest.TestCase): - """ Low-level lattice tests """ - - def test_repeats(self): - rs = np.random.RandomState(123) - dim = 32 - for i in range(1000): - vec = np.floor((rs.rand(dim) ** 7) * 3).astype('float32') - vecs = vec.copy() - vecs.sort() - repeats = faiss.Repeats(dim, swig_ptr(vecs)) - rr = [repeats.repeats.at(i) for i in range(repeats.repeats.size())] - # print([(r.val, r.n) for r in rr]) - code = repeats.encode(swig_ptr(vec)) - #print(vec, code) - vec2 = np.zeros(dim, dtype='float32') - repeats.decode(code, swig_ptr(vec2)) - # print(vec2) - assert np.all(vec == vec2) - - def test_ZnSphereCodec_encode_centroid(self): - dim = 8 - r2 = 5 - ref_codec = faiss.ZnSphereCodec(dim, r2) - codec = faiss.ZnSphereCodecRec(dim, r2) - # print(ref_codec.nv, codec.nv) - assert ref_codec.nv == codec.nv - s = set() - for i in range(ref_codec.nv): - c = np.zeros(dim, dtype='float32') - ref_codec.decode(i, swig_ptr(c)) - code = codec.encode_centroid(swig_ptr(c)) - assert 0 <= code < codec.nv - s.add(code) - assert len(s) == codec.nv - - def test_ZnSphereCodecRec(self): - dim = 16 - r2 = 6 - codec = faiss.ZnSphereCodecRec(dim, r2) - # print("nv=", codec.nv) - for i in range(codec.nv): - c = np.zeros(dim, dtype='float32') - codec.decode(i, swig_ptr(c)) - code = codec.encode_centroid(swig_ptr(c)) - assert code == i - - def run_ZnSphereCodecAlt(self, dim, r2): - # dim = 32 - # r2 = 14 - codec = faiss.ZnSphereCodecAlt(dim, r2) - rs = np.random.RandomState(123) - n = 100 - codes = rs.randint(codec.nv, size=n).astype('uint64') - x = np.empty((n, dim), dtype='float32') - codec.decode_multi(n, swig_ptr(codes), swig_ptr(x)) - codes2 = np.empty(n, dtype='uint64') - codec.encode_multi(n, swig_ptr(x), swig_ptr(codes2)) - - assert np.all(codes == codes2) - - def test_ZnSphereCodecAlt32(self): - self.run_ZnSphereCodecAlt(32, 14) - - def test_ZnSphereCodecAlt24(self): - self.run_ZnSphereCodecAlt(24, 14) - - -class TestBitstring(unittest.TestCase): - """ Low-level bit string tests """ - - def test_rw(self): - rs = np.random.RandomState(1234) - nbyte = 1000 - sz = 0 - - bs = np.ones(nbyte, dtype='uint8') - bw = faiss.BitstringWriter(swig_ptr(bs), nbyte) - - if False: - ctrl = [(7, 0x35), (13, 0x1d74)] - for nbit, x in ctrl: - bw.write(x, nbit) - else: - ctrl = [] - while True: - nbit = int(1 + 62 * rs.rand() ** 4) - if sz + nbit > nbyte * 8: - break - x = rs.randint(1 << nbit) - bw.write(x, nbit) - ctrl.append((nbit, x)) - sz += nbit - - bignum = 0 - sz = 0 - for nbit, x in ctrl: - bignum |= x << sz - sz += nbit - - for i in range(nbyte): - self.assertTrue(((bignum >> (i * 8)) & 255) == bs[i]) - - for i in range(nbyte): - print(bin(bs[i] + 256)[3:], end=' ') - print() - - br = faiss.BitstringReader(swig_ptr(bs), nbyte) - - for nbit, xref in ctrl: - xnew = br.read(nbit) - print('nbit %d xref %x xnew %x' % (nbit, xref, xnew)) - self.assertTrue(xnew == xref) diff --git a/core/src/index/thirdparty/faiss/tests/test_threaded_index.cpp b/core/src/index/thirdparty/faiss/tests/test_threaded_index.cpp deleted file mode 100644 index 7cad760c09..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_threaded_index.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace { - -struct TestException : public std::exception { }; - -struct MockIndex : public faiss::Index { - explicit MockIndex(idx_t d) : - faiss::Index(d) { - resetMock(); - } - - void resetMock() { - flag = false; - nCalled = 0; - xCalled = nullptr; - kCalled = 0; - distancesCalled = nullptr; - labelsCalled = nullptr; - } - - void add(idx_t n, const float* x) override { - nCalled = n; - xCalled = x; - } - - void search(idx_t n, - const float* x, - idx_t k, - float* distances, - idx_t* labels) const override { - nCalled = n; - xCalled = x; - kCalled = k; - distancesCalled = distances; - labelsCalled = labels; - } - - void reset() override { } - - bool flag; - - mutable idx_t nCalled; - mutable const float* xCalled; - mutable idx_t kCalled; - mutable float* distancesCalled; - mutable idx_t* labelsCalled; -}; - -template -struct MockThreadedIndex : public faiss::ThreadedIndex { - using idx_t = faiss::Index::idx_t; - - explicit MockThreadedIndex(bool threaded) - : faiss::ThreadedIndex(threaded) { - } - - void add(idx_t, const float*) override { } - void search(idx_t, const float*, idx_t, float*, idx_t*) const override {} - void reset() override {} -}; - -} - -TEST(ThreadedIndex, SingleException) { - std::vector> idxs; - - for (int i = 0; i < 3; ++i) { - idxs.emplace_back(new MockIndex(1)); - } - - auto fn = - [](int i, MockIndex* index) { - if (i == 1) { - throw TestException(); - } else { - std::this_thread::sleep_for(std::chrono::milliseconds(i * 250)); - - index->flag = true; - } - }; - - // Try with threading and without - for (bool threaded : {true, false}) { - // clear flags - for (auto& idx : idxs) { - idx->resetMock(); - } - - MockThreadedIndex ti(threaded); - for (auto& idx : idxs) { - ti.addIndex(idx.get()); - } - - // The second index should throw - EXPECT_THROW(ti.runOnIndex(fn), TestException); - - // Index 0 and 2 should have processed - EXPECT_TRUE(idxs[0]->flag); - EXPECT_TRUE(idxs[2]->flag); - } -} - -TEST(ThreadedIndex, MultipleException) { - std::vector> idxs; - - for (int i = 0; i < 3; ++i) { - idxs.emplace_back(new MockIndex(1)); - } - - auto fn = - [](int i, MockIndex* index) { - if (i < 2) { - throw TestException(); - } else { - std::this_thread::sleep_for(std::chrono::milliseconds(i * 250)); - - index->flag = true; - } - }; - - // Try with threading and without - for (bool threaded : {true, false}) { - // clear flags - for (auto& idx : idxs) { - idx->resetMock(); - } - - MockThreadedIndex ti(threaded); - for (auto& idx : idxs) { - ti.addIndex(idx.get()); - } - - // Multiple indices threw an exception that was aggregated into a - // FaissException - EXPECT_THROW(ti.runOnIndex(fn), faiss::FaissException); - - // Index 2 should have processed - EXPECT_TRUE(idxs[2]->flag); - } -} - -TEST(ThreadedIndex, TestReplica) { - int numReplicas = 5; - int n = 10 * numReplicas; - int d = 3; - int k = 6; - - // Try with threading and without - for (bool threaded : {true, false}) { - std::vector> idxs; - faiss::IndexReplicas replica(d); - - for (int i = 0; i < numReplicas; ++i) { - idxs.emplace_back(new MockIndex(d)); - replica.addIndex(idxs.back().get()); - } - - std::vector x(n * d); - std::vector distances(n * k); - std::vector labels(n * k); - - replica.add(n, x.data()); - - for (int i = 0; i < idxs.size(); ++i) { - EXPECT_EQ(idxs[i]->nCalled, n); - EXPECT_EQ(idxs[i]->xCalled, x.data()); - } - - for (auto& idx : idxs) { - idx->resetMock(); - } - - replica.search(n, x.data(), k, distances.data(), labels.data()); - - for (int i = 0; i < idxs.size(); ++i) { - auto perReplica = n / idxs.size(); - - EXPECT_EQ(idxs[i]->nCalled, perReplica); - EXPECT_EQ(idxs[i]->xCalled, x.data() + i * perReplica * d); - EXPECT_EQ(idxs[i]->kCalled, k); - EXPECT_EQ(idxs[i]->distancesCalled, - distances.data() + (i * perReplica) * k); - EXPECT_EQ(idxs[i]->labelsCalled, - labels.data() + (i * perReplica) * k); - } - } -} - -TEST(ThreadedIndex, TestShards) { - int numShards = 7; - int d = 3; - int n = 10 * numShards; - int k = 6; - - // Try with threading and without - for (bool threaded : {true, false}) { - std::vector> idxs; - faiss::IndexShards shards(d, threaded); - - for (int i = 0; i < numShards; ++i) { - idxs.emplace_back(new MockIndex(d)); - shards.addIndex(idxs.back().get()); - } - - std::vector x(n * d); - std::vector distances(n * k); - std::vector labels(n * k); - - shards.add(n, x.data()); - - for (int i = 0; i < idxs.size(); ++i) { - auto perShard = n / idxs.size(); - - EXPECT_EQ(idxs[i]->nCalled, perShard); - EXPECT_EQ(idxs[i]->xCalled, x.data() + i * perShard * d); - } - - for (auto& idx : idxs) { - idx->resetMock(); - } - - shards.search(n, x.data(), k, distances.data(), labels.data()); - - for (int i = 0; i < idxs.size(); ++i) { - auto perShard = n / idxs.size(); - - EXPECT_EQ(idxs[i]->nCalled, n); - EXPECT_EQ(idxs[i]->xCalled, x.data()); - EXPECT_EQ(idxs[i]->kCalled, k); - // There is a temporary buffer used for shards - EXPECT_EQ(idxs[i]->distancesCalled, - idxs[0]->distancesCalled + i * k * n); - EXPECT_EQ(idxs[i]->labelsCalled, - idxs[0]->labelsCalled + i * k * n); - } - } -} diff --git a/core/src/index/thirdparty/faiss/tests/test_transfer_invlists.cpp b/core/src/index/thirdparty/faiss/tests/test_transfer_invlists.cpp deleted file mode 100644 index 8766d88e6f..0000000000 --- a/core/src/index/thirdparty/faiss/tests/test_transfer_invlists.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace { - -// parameters to use for the test -int d = 64; -size_t nb = 1000; -size_t nq = 100; -size_t nt = 500; -int k = 10; -int nlist = 40; - -using namespace faiss; - -typedef faiss::Index::idx_t idx_t; - - -std::vector get_data (size_t nb, int seed) { - std::vector x (nb * d); - float_randn (x.data(), nb * d, seed); - return x; -} - - -void test_index_type(const char *factory_string) { - - // transfer inverted lists in nslice slices - int nslice = 3; - - /**************************************************************** - * trained reference index - ****************************************************************/ - - std::unique_ptr trained (index_factory (d, factory_string)); - - { - auto xt = get_data (nt, 123); - trained->train (nt, xt.data()); - } - - // sample nq query vectors to check if results are the same - auto xq = get_data (nq, 818); - - - /**************************************************************** - * source index - ***************************************************************/ - std::unique_ptr src_index (clone_index (trained.get())); - - { // add some data to source index - auto xb = get_data (nb, 245); - src_index->add (nb, xb.data()); - } - - ParameterSpace().set_index_parameter (src_index.get(), "nprobe", 4); - - // remember reference search result on source index - std::vector Iref (nq * k); - std::vector Dref (nq * k); - src_index->search (nq, xq.data(), k, Dref.data(), Iref.data()); - - /**************************************************************** - * destination index -- should be replaced by source index - ***************************************************************/ - - std::unique_ptr dst_index (clone_index (trained.get())); - - { // initial state: filled in with some garbage - int nb2 = nb + 10; - auto xb = get_data (nb2, 366); - dst_index->add (nb2, xb.data()); - } - - std::vector Inew (nq * k); - std::vector Dnew (nq * k); - - ParameterSpace().set_index_parameter (dst_index.get(), "nprobe", 4); - - // transfer from source to destination in nslice slices - for (int sl = 0; sl < nslice; sl++) { - - // so far, the indexes are different - dst_index->search (nq, xq.data(), k, Dnew.data(), Inew.data()); - EXPECT_TRUE (Iref != Inew); - EXPECT_TRUE (Dref != Dnew); - - // range of inverted list indices to transfer - long i0 = sl * nlist / nslice; - long i1 = (sl + 1) * nlist / nslice; - - std::vector data_to_transfer; - { - std::unique_ptr il - (ivflib::get_invlist_range (src_index.get(), i0, i1)); - // serialize inverted lists - VectorIOWriter wr; - write_InvertedLists (il.get(), &wr); - data_to_transfer.swap (wr.data); - } - - // transfer data here from source machine to dest machine - - { - VectorIOReader reader; - reader.data.swap (data_to_transfer); - - // deserialize inverted lists - std::unique_ptr il - (dynamic_cast - (read_InvertedLists (&reader))); - - // swap inverted lists. Block searches here! - { - ivflib::set_invlist_range (dst_index.get(), i0, i1, il.get()); - } - } - - } - EXPECT_EQ (dst_index->ntotal, src_index->ntotal); - - // now, the indexes are the same - dst_index->search (nq, xq.data(), k, Dnew.data(), Inew.data()); - EXPECT_TRUE (Iref == Inew); - EXPECT_TRUE (Dref == Dnew); - -} - -} // namespace - - -TEST(TRANS, IVFFlat) { - test_index_type ("IVF40,Flat"); -} - -TEST(TRANS, IVFFlatPreproc) { - test_index_type ("PCAR32,IVF40,Flat"); -} diff --git a/core/src/index/thirdparty/faiss/tutorial/cpp/1-Flat.cpp b/core/src/index/thirdparty/faiss/tutorial/cpp/1-Flat.cpp deleted file mode 100644 index f8632bb6c8..0000000000 --- a/core/src/index/thirdparty/faiss/tutorial/cpp/1-Flat.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -#include - - -int main() { - int d = 64; // dimension - int nb = 100000; // database size - int nq = 10000; // nb of queries - - float *xb = new float[d * nb]; - float *xq = new float[d * nq]; - - for(int i = 0; i < nb; i++) { - for(int j = 0; j < d; j++) - xb[d * i + j] = drand48(); - xb[d * i] += i / 1000.; - } - - for(int i = 0; i < nq; i++) { - for(int j = 0; j < d; j++) - xq[d * i + j] = drand48(); - xq[d * i] += i / 1000.; - } - - faiss::IndexFlatL2 index(d); // call constructor - printf("is_trained = %s\n", index.is_trained ? "true" : "false"); - index.add(nb, xb); // add vectors to the index - printf("ntotal = %ld\n", index.ntotal); - - int k = 4; - - { // sanity check: search 5 first vectors of xb - long *I = new long[k * 5]; - float *D = new float[k * 5]; - - index.search(5, xb, k, D, I); - - // print results - printf("I=\n"); - for(int i = 0; i < 5; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - printf("D=\n"); - for(int i = 0; i < 5; i++) { - for(int j = 0; j < k; j++) - printf("%7g ", D[i * k + j]); - printf("\n"); - } - - delete [] I; - delete [] D; - } - - - { // search xq - long *I = new long[k * nq]; - float *D = new float[k * nq]; - - index.search(nq, xq, k, D, I); - - // print results - printf("I (5 first results)=\n"); - for(int i = 0; i < 5; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - printf("I (5 last results)=\n"); - for(int i = nq - 5; i < nq; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - delete [] I; - delete [] D; - } - - - - delete [] xb; - delete [] xq; - - return 0; -} diff --git a/core/src/index/thirdparty/faiss/tutorial/cpp/2-IVFFlat.cpp b/core/src/index/thirdparty/faiss/tutorial/cpp/2-IVFFlat.cpp deleted file mode 100644 index ce13f1d1ae..0000000000 --- a/core/src/index/thirdparty/faiss/tutorial/cpp/2-IVFFlat.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include -#include - -#include -#include - - -int main() { - int d = 64; // dimension - int nb = 100000; // database size - int nq = 10000; // nb of queries - - float *xb = new float[d * nb]; - float *xq = new float[d * nq]; - - for(int i = 0; i < nb; i++) { - for(int j = 0; j < d; j++) - xb[d * i + j] = drand48(); - xb[d * i] += i / 1000.; - } - - for(int i = 0; i < nq; i++) { - for(int j = 0; j < d; j++) - xq[d * i + j] = drand48(); - xq[d * i] += i / 1000.; - } - - - int nlist = 100; - int k = 4; - - faiss::IndexFlatL2 quantizer(d); // the other index - faiss::IndexIVFFlat index(&quantizer, d, nlist, faiss::METRIC_L2); - // here we specify METRIC_L2, by default it performs inner-product search - assert(!index.is_trained); - index.train(nb, xb); - assert(index.is_trained); - index.add(nb, xb); - - { // search xq - long *I = new long[k * nq]; - float *D = new float[k * nq]; - - index.search(nq, xq, k, D, I); - - printf("I=\n"); - for(int i = nq - 5; i < nq; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - index.nprobe = 10; - index.search(nq, xq, k, D, I); - - printf("I=\n"); - for(int i = nq - 5; i < nq; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - delete [] I; - delete [] D; - } - - - - delete [] xb; - delete [] xq; - - return 0; -} diff --git a/core/src/index/thirdparty/faiss/tutorial/cpp/3-IVFPQ.cpp b/core/src/index/thirdparty/faiss/tutorial/cpp/3-IVFPQ.cpp deleted file mode 100644 index 85a2de0578..0000000000 --- a/core/src/index/thirdparty/faiss/tutorial/cpp/3-IVFPQ.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -#include -#include -#include "../../utils/ConcurrentBitset.h" - - -int main() { - int d = 64; // dimension - int nb = 100000; // database size - int nq = 10;//10000; // nb of queries - faiss::ConcurrentBitsetPtr bitset = std::make_shared(nb); - - float *xb = new float[d * nb]; - float *xq = new float[d * nq]; - - for(int i = 0; i < nb; i++) { - for(int j = 0; j < d; j++) - xb[d * i + j] = drand48(); - xb[d * i] += i / 1000.; - } - - srand((unsigned)time(NULL)); - printf("delete ids: \n"); - for(int i = 0; i < nq; i++) { - auto tmp = rand()%nb; - bitset->set(tmp); - printf("%d ", tmp); - for(int j = 0; j < d; j++) - xq[d * i + j] = xb[d * tmp + j]; -// xq[d * i] += i / 1000.; - } - printf("\n"); - - - int nlist = 100; - int k = 4; - int m = 8; // bytes per vector - faiss::IndexFlatL2 quantizer(d); // the other index - faiss::IndexIVFPQ index(&quantizer, d, nlist, m, 8); - // here we specify METRIC_L2, by default it performs inner-product search - index.train(nb, xb); - index.add(nb, xb); - - printf("------------sanity check----------------\n"); - { // sanity check - long *I = new long[k * 5]; - float *D = new float[k * 5]; - - index.search(5, xb, k, D, I); - - printf("I=\n"); - for(int i = 0; i < 5; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - printf("D=\n"); - for(int i = 0; i < 5; i++) { - for(int j = 0; j < k; j++) - printf("%7g ", D[i * k + j]); - printf("\n"); - } - - delete [] I; - delete [] D; - } - - printf("---------------search xq-------------\n"); - { // search xq - long *I = new long[k * nq]; - float *D = new float[k * nq]; - - index.nprobe = 10; - index.search(nq, xq, k, D, I); - - printf("I=\n"); - for(int i = 0; i < nq; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - delete [] I; - delete [] D; - } - - printf("----------------search xq with delete------------\n"); - { // search xq with delete - long *I = new long[k * nq]; - float *D = new float[k * nq]; - - index.nprobe = 10; - index.search(nq, xq, k, D, I, bitset); - - printf("I=\n"); - for(int i = 0; i < nq; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - delete [] I; - delete [] D; - } - - - - delete [] xb; - delete [] xq; - - return 0; -} diff --git a/core/src/index/thirdparty/faiss/tutorial/cpp/4-GPU.cpp b/core/src/index/thirdparty/faiss/tutorial/cpp/4-GPU.cpp deleted file mode 100644 index 49c5c8a06e..0000000000 --- a/core/src/index/thirdparty/faiss/tutorial/cpp/4-GPU.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include -#include - -#include -#include -#include -#include - - -int main() { - int d = 64; // dimension - int nb = 100000; // database size - int nq = 10000; // nb of queries - - float *xb = new float[d * nb]; - float *xq = new float[d * nq]; - - for(int i = 0; i < nb; i++) { - for(int j = 0; j < d; j++) - xb[d * i + j] = drand48(); - xb[d * i] += i / 1000.; - } - - for(int i = 0; i < nq; i++) { - for(int j = 0; j < d; j++) - xq[d * i + j] = drand48(); - xq[d * i] += i / 1000.; - } - - faiss::gpu::StandardGpuResources res; - - // Using a flat index - - faiss::gpu::GpuIndexFlatL2 index_flat(&res, d); - - printf("is_trained = %s\n", index_flat.is_trained ? "true" : "false"); - index_flat.add(nb, xb); // add vectors to the index - printf("ntotal = %ld\n", index_flat.ntotal); - - int k = 4; - - { // search xq - long *I = new long[k * nq]; - float *D = new float[k * nq]; - - index_flat.search(nq, xq, k, D, I); - - // print results - printf("I (5 first results)=\n"); - for(int i = 0; i < 5; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - printf("I (5 last results)=\n"); - for(int i = nq - 5; i < nq; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - delete [] I; - delete [] D; - } - - // Using an IVF index - - int nlist = 100; - faiss::gpu::GpuIndexIVFFlat index_ivf(&res, d, nlist, faiss::METRIC_L2); - // here we specify METRIC_L2, by default it performs inner-product search - - assert(!index_ivf.is_trained); - index_ivf.train(nb, xb); - assert(index_ivf.is_trained); - index_ivf.add(nb, xb); // add vectors to the index - - printf("is_trained = %s\n", index_ivf.is_trained ? "true" : "false"); - printf("ntotal = %ld\n", index_ivf.ntotal); - - { // search xq - long *I = new long[k * nq]; - float *D = new float[k * nq]; - - index_ivf.search(nq, xq, k, D, I); - - // print results - printf("I (5 first results)=\n"); - for(int i = 0; i < 5; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - printf("I (5 last results)=\n"); - for(int i = nq - 5; i < nq; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - delete [] I; - delete [] D; - } - - - delete [] xb; - delete [] xq; - - return 0; -} diff --git a/core/src/index/thirdparty/faiss/tutorial/cpp/5-GPU.cpp b/core/src/index/thirdparty/faiss/tutorial/cpp/5-GPU.cpp deleted file mode 100644 index 212fb53f1c..0000000000 --- a/core/src/index/thirdparty/faiss/tutorial/cpp/5-GPU.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include -#include -#include - -#include - -#include "faiss/IndexIVF.h" -#include "faiss/IndexFlat.h" -#include "faiss/index_io.h" -#include "faiss/gpu/GpuIndexFlat.h" -#include "faiss/gpu/StandardGpuResources.h" -#include "faiss/gpu/GpuAutoTune.h" -#include "faiss/gpu/GpuCloner.h" -#include "faiss/gpu/GpuClonerOptions.h" -#include "faiss/gpu/GpuIndexIVF.h" - -#include "faiss/impl/FaissAssert.h" -#include "faiss/impl/AuxIndexStructures.h" - -#include "faiss/IndexFlat.h" -#include "faiss/VectorTransform.h" -#include "faiss/IndexLSH.h" -#include "faiss/IndexPQ.h" - -#include "faiss/IndexIVFPQ.h" -#include "faiss/IndexIVFFlat.h" -#include "faiss/IndexIVFSpectralHash.h" -#include "faiss/MetaIndexes.h" -#include "faiss/IndexScalarQuantizer.h" -#include "faiss/IndexHNSW.h" -#include "faiss/OnDiskInvertedLists.h" -#include "faiss/IndexBinaryFlat.h" -#include "faiss/IndexBinaryFromFloat.h" -#include "faiss/IndexBinaryHNSW.h" -#include "faiss/IndexBinaryIVF.h" -#include "faiss/utils/distances.h" -#include "faiss/index_factory.h" - -using namespace faiss; - -#define PRINT_RESULT 0 - -void print_result(const char* unit, long number, long k, long nq, long *I) { - printf("%s: I (2 first results)=\n", unit); - for(int i = 0; i < number; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - printf("%s: I (2 last results)=\n", unit); - for(int i = nq - number; i < nq; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } -} - - -int main() { - const char* filename = "index500k.index"; - -#if PRINT_RESULT - int number = 8; -#endif - - int d = 512; // dimension - int nq = 10; // nb of queries - int nprobe = 1; - float *xq = new float[d * nq]; - for(int i = 0; i < nq; i++) { - for(int j = 0; j < d; j++) { - xq[d * i + j] = drand48(); - } - } - faiss::distance_compute_blas_threshold = 800; - - faiss::gpu::StandardGpuResources res; - - int k = 8; - std::shared_ptr gpu_index_ivf_ptr; - - const char* index_description = "IVF16384,SQ8"; -// const char* index_description = "IVF3276,SQ8"; - - faiss::Index *cpu_index = nullptr; - faiss::IndexIVF* cpu_ivf_index = nullptr; - if((access(filename,F_OK))==-1) { - // create database - long nb = 500000; // database size -// printf("-----------------------\n"); - long size = d * nb; - float *xb = new float[size]; - memset(xb, 0, size * sizeof(float)); - printf("size: %ld\n", (size * sizeof(float)) ); - for(long i = 0; i < nb; i++) { - for(long j = 0; j < d; j++) { - float rand = drand48(); - xb[d * i + j] = rand; - } - } - - faiss::Index *ori_index = faiss::index_factory(d, index_description, faiss::METRIC_L2); - auto device_index = faiss::gpu::index_cpu_to_gpu(&res, 0, ori_index); - - gpu_index_ivf_ptr = std::shared_ptr(device_index); - - assert(!device_index->is_trained); - device_index->train(nb, xb); - assert(device_index->is_trained); - device_index->add(nb, xb); // add vectors to the index - - printf("is_trained = %s\n", device_index->is_trained ? "true" : "false"); - printf("ntotal = %ld\n", device_index->ntotal); - - cpu_index = faiss::gpu::index_gpu_to_cpu ((device_index)); - faiss::write_index(cpu_index, filename); - printf("index.index is stored successfully.\n"); - delete [] xb; - } else { - cpu_index = faiss::read_index(filename); - } - - cpu_ivf_index = dynamic_cast(cpu_index); - if(cpu_ivf_index != nullptr) { - cpu_ivf_index->to_readonly(); - } - - auto init_gpu =[&](int device_id, faiss::gpu::GpuClonerOptions* option) { - option->allInGpu = true; - faiss::Index* tmp_index = faiss::gpu::index_cpu_to_gpu(&res, device_id, cpu_index, option); - delete tmp_index; - }; - - auto gpu_executor = [&](int device_id, faiss::gpu::GpuClonerOptions* option) { - auto tmp_index = faiss::gpu::index_cpu_to_gpu(&res, device_id, cpu_index, option); - delete tmp_index; - double t0 = getmillisecs (); - { - // cpu to gpu - option->allInGpu = true; - - tmp_index = faiss::gpu::index_cpu_to_gpu(&res, device_id, cpu_index, option); - gpu_index_ivf_ptr = std::shared_ptr(tmp_index); - } - double t1 = getmillisecs (); - printf("CPU to GPU loading time: %0.2f\n", t1 - t0); - - { - long *I = new long[k * nq]; - float *D = new float[k * nq]; - if(option->allInGpu) { - faiss::gpu::GpuIndexIVF* gpu_index_ivf = - dynamic_cast(gpu_index_ivf_ptr.get()); - gpu_index_ivf->setNumProbes(nprobe); - for(long i = 0; i < 1; ++ i) { - double t2 = getmillisecs(); - gpu_index_ivf_ptr->search(nq, xq, k, D, I); - double t3 = getmillisecs(); - printf("* GPU: %d, execution time: %0.2f\n", device_id, t3 - t2); - } - } else { - faiss::IndexIVFScalarQuantizer* index_ivf = - dynamic_cast(gpu_index_ivf_ptr.get()); - index_ivf->nprobe = nprobe; - for(long i = 0; i < 1; ++ i) { - double t2 = getmillisecs(); - index_ivf->search(nq, xq, k, D, I); - double t3 = getmillisecs(); - printf("- GPU: %d, execution time: %0.2f\n", device_id, t3 - t2); - } - } - - // print results -#if PRINT_RESULT - print_result("GPU", number, k, nq, I); -#endif - delete [] I; - delete [] D; - } - double t4 = getmillisecs(); - - printf("GPU:%d total time: %0.2f\n", device_id, t4 - t0); - - }; - printf("----------------------------------\n"); - auto cpu_executor = [&]() { // search xq - printf("CPU: \n"); - long *I = new long[k * nq]; - float *D = new float[k * nq]; - - double t4 = getmillisecs(); - faiss::IndexIVF* ivf_index = - dynamic_cast(cpu_index); - ivf_index->nprobe = nprobe; - cpu_index->search(nq, xq, k, D, I); - double t5 = getmillisecs(); - printf("CPU execution time: %0.2f\n", t5 - t4); -#if PRINT_RESULT - print_result("CPU", number, k, nq, I); -#endif - delete [] I; - delete [] D; - }; - - for(long i = 0; i < 1; ++ i) { - cpu_executor(); - } - - faiss::gpu::GpuClonerOptions option0; - faiss::gpu::GpuClonerOptions option1; - -// init_gpu(0, &option0); -// init_gpu(1, &option1); - -// double tx = getmillisecs(); - std::thread t1(gpu_executor, 0, &option0); - std::thread t2(gpu_executor, 1, &option1); - t1.join(); - t2.join(); -// double ty = getmillisecs(); -// printf("Total GPU execution time: %0.2f\n", ty - tx); - - delete [] xq; - return 0; -} diff --git a/core/src/index/thirdparty/faiss/tutorial/cpp/5-Multiple-GPUs.cpp b/core/src/index/thirdparty/faiss/tutorial/cpp/5-Multiple-GPUs.cpp deleted file mode 100644 index 3152b731a1..0000000000 --- a/core/src/index/thirdparty/faiss/tutorial/cpp/5-Multiple-GPUs.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -#include -#include -#include -#include -#include -#include - - -int main() { - int d = 64; // dimension - int nb = 100000; // database size - int nq = 10000; // nb of queries - - float *xb = new float[d * nb]; - float *xq = new float[d * nq]; - - for(int i = 0; i < nb; i++) { - for(int j = 0; j < d; j++) - xb[d * i + j] = drand48(); - xb[d * i] += i / 1000.; - } - - for(int i = 0; i < nq; i++) { - for(int j = 0; j < d; j++) - xq[d * i + j] = drand48(); - xq[d * i] += i / 1000.; - } - - int ngpus = faiss::gpu::getNumDevices(); - - printf("Number of GPUs: %d\n", ngpus); - - std::vector res; - std::vector devs; - for(int i = 0; i < ngpus; i++) { - res.push_back(new faiss::gpu::StandardGpuResources); - devs.push_back(i); - } - - faiss::IndexFlatL2 cpu_index(d); - - faiss::Index *gpu_index = - faiss::gpu::index_cpu_to_gpu_multiple( - res, - devs, - &cpu_index - ); - - printf("is_trained = %s\n", gpu_index->is_trained ? "true" : "false"); - gpu_index->add(nb, xb); // add vectors to the index - printf("ntotal = %ld\n", gpu_index->ntotal); - - int k = 4; - - { // search xq - long *I = new long[k * nq]; - float *D = new float[k * nq]; - - gpu_index->search(nq, xq, k, D, I); - - // print results - printf("I (5 first results)=\n"); - for(int i = 0; i < 5; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - printf("I (5 last results)=\n"); - for(int i = nq - 5; i < nq; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - delete [] I; - delete [] D; - } - - delete gpu_index; - - for(int i = 0; i < ngpus; i++) { - delete res[i]; - } - - delete [] xb; - delete [] xq; - - return 0; -} diff --git a/core/src/index/thirdparty/faiss/tutorial/cpp/6-GPU.cpp b/core/src/index/thirdparty/faiss/tutorial/cpp/6-GPU.cpp deleted file mode 100644 index f992884cba..0000000000 --- a/core/src/index/thirdparty/faiss/tutorial/cpp/6-GPU.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include -#include -#include - -#include - -#include "faiss/IndexIVF.h" -#include "faiss/IndexFlat.h" -#include "faiss/index_io.h" -#include "faiss/gpu/GpuIndexFlat.h" -#include "faiss/gpu/StandardGpuResources.h" -#include "faiss/gpu/GpuAutoTune.h" -#include "faiss/gpu/GpuCloner.h" -#include "faiss/gpu/GpuClonerOptions.h" -#include "faiss/gpu/GpuIndexIVF.h" -#include "faiss/gpu/GpuIndexIVFSQHybrid.h" - -#include "faiss/impl/FaissAssert.h" -#include "faiss/impl/AuxIndexStructures.h" - -#include "faiss/IndexFlat.h" -#include "faiss/VectorTransform.h" -#include "faiss/IndexLSH.h" -#include "faiss/IndexPQ.h" - -#include "faiss/IndexIVFPQ.h" -#include "faiss/IndexIVFFlat.h" -#include "faiss/IndexIVFSpectralHash.h" -#include "faiss/MetaIndexes.h" -#include "faiss/IndexSQHybrid.h" -#include "faiss/IndexHNSW.h" -#include "faiss/OnDiskInvertedLists.h" -#include "faiss/IndexBinaryFlat.h" -#include "faiss/IndexBinaryFromFloat.h" -#include "faiss/IndexBinaryHNSW.h" -#include "faiss/IndexBinaryIVF.h" -#include "faiss/utils/distances.h" -#include "faiss/index_factory.h" - -using namespace faiss; - -#define PRINT_RESULT 0 - -void print_result(const char* unit, long number, long k, long nq, long *I) { - printf("%s: I (2 first results)=\n", unit); - for(int i = 0; i < number; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - printf("%s: I (2 last results)=\n", unit); - for(int i = nq - number; i < nq; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } -} - - -int main() { - const char* filename = "index500k-h.index"; - -#if PRINT_RESULT - int number = 8; -#endif - - int d = 512; // dimension - int nq = 10; // nb of queries - int nprobe = 1; - float *xq = new float[d * nq]; - for(int i = 0; i < nq; i++) { - for(int j = 0; j < d; j++) { - xq[d * i + j] = drand48(); - } - } - faiss::distance_compute_blas_threshold = 800; - - faiss::gpu::StandardGpuResources res; - - int k = 8; - std::shared_ptr gpu_index_ivf_ptr; - - const char* index_description = "IVF16384,SQ8Hybrid"; -// const char* index_description = "IVF3276,SQ8"; - - faiss::Index *cpu_index = nullptr; - faiss::IndexIVF* cpu_ivf_index = nullptr; - if((access(filename,F_OK))==-1) { - // create database - long nb = 500000; // database size -// printf("-----------------------\n"); - long size = d * nb; - float *xb = new float[size]; - memset(xb, 0, size * sizeof(float)); - printf("size: %ld\n", (size * sizeof(float)) ); - for(long i = 0; i < nb; i++) { - for(long j = 0; j < d; j++) { - float rand = drand48(); - xb[d * i + j] = rand; - } - } - - faiss::Index *ori_index = faiss::index_factory(d, index_description, faiss::METRIC_L2); - auto device_index = faiss::gpu::index_cpu_to_gpu(&res, 0, ori_index); - - gpu_index_ivf_ptr = std::shared_ptr(device_index); - - assert(!device_index->is_trained); - device_index->train(nb, xb); - assert(device_index->is_trained); - device_index->add(nb, xb); // add vectors to the index - - printf("is_trained = %s\n", device_index->is_trained ? "true" : "false"); - printf("ntotal = %ld\n", device_index->ntotal); - - cpu_index = faiss::gpu::index_gpu_to_cpu ((device_index)); - faiss::write_index(cpu_index, filename); - printf("index.index is stored successfully.\n"); - delete [] xb; - } else { - cpu_index = faiss::read_index(filename); - } - - cpu_ivf_index = dynamic_cast(cpu_index); - if(cpu_ivf_index != nullptr) { - cpu_ivf_index->to_readonly(); - } - - auto gpu_executor = [&](int device_id, faiss::gpu::GpuClonerOptions* option, faiss::IndexComposition* index_composition) { - auto tmp_index = faiss::gpu::index_cpu_to_gpu(&res, device_id, index_composition, option); - delete tmp_index; - double t0 = getmillisecs (); - { - // cpu to gpu - tmp_index = faiss::gpu::index_cpu_to_gpu(&res, device_id, index_composition, option); - gpu_index_ivf_ptr = std::shared_ptr(tmp_index); - } - double t1 = getmillisecs (); - printf("CPU to GPU loading time: %0.2f\n", t1 - t0); - - { - long *I = new long[k * nq]; - float *D = new float[k * nq]; - - faiss::gpu::GpuIndexIVFSQHybrid* gpu_index_ivf_hybrid = - dynamic_cast(gpu_index_ivf_ptr.get()); - gpu_index_ivf_hybrid->setNumProbes(nprobe); - for(long i = 0; i < 1; ++ i) { - double t2 = getmillisecs(); - gpu_index_ivf_ptr->search(nq, xq, k, D, I); - double t3 = getmillisecs(); - printf("* GPU: %d, execution time: %0.2f\n", device_id, t3 - t2); - } - - // print results -#if PRINT_RESULT - print_result("GPU", number, k, nq, I); -#endif - delete [] I; - delete [] D; - } - double t4 = getmillisecs(); - - printf("GPU:%d total time: %0.2f\n", device_id, t4 - t0); - - }; - printf("----------------------------------\n"); - auto cpu_executor = [&](faiss::IndexComposition* index_composition) { // search xq - printf("CPU: \n"); - long *I = new long[k * nq]; - float *D = new float[k * nq]; - - double t4 = getmillisecs(); - faiss::IndexIVF* ivf_index = - dynamic_cast(cpu_index); - ivf_index->nprobe = nprobe; - - faiss::gpu::GpuIndexFlat* is_gpu_flat_index = dynamic_cast(ivf_index->quantizer); - if(is_gpu_flat_index == nullptr) { - delete ivf_index->quantizer; - ivf_index->quantizer = index_composition->quantizer; - } - - cpu_index->search(nq, xq, k, D, I); - double t5 = getmillisecs(); - printf("CPU execution time: %0.2f\n", t5 - t4); -#if PRINT_RESULT - print_result("CPU", number, k, nq, I); -#endif - delete [] I; - delete [] D; - }; - - - faiss::gpu::GpuClonerOptions option0; - faiss::gpu::GpuClonerOptions option1; - - faiss::IndexComposition index_composition0; - index_composition0.index = cpu_index; - index_composition0.quantizer = nullptr; - index_composition0.mode = 0; // only quantizer - - // Copy quantizer to GPU 0 - auto index1 = faiss::gpu::index_cpu_to_gpu(&res, 0, &index_composition0, &option0); - delete index1; - - faiss::IndexComposition index_composition1; - index_composition1.index = cpu_index; - index_composition1.quantizer = nullptr; - index_composition1.mode = 0; // only quantizer - - // Copy quantizer to GPU 1 - index1 = faiss::gpu::index_cpu_to_gpu(&res, 1, &index_composition1, &option1); - delete index1; - - std::thread t_cpu1(cpu_executor, &index_composition0); - t_cpu1.join(); - std::thread t_cpu2(cpu_executor, &index_composition1); - t_cpu2.join(); - - index_composition0.mode = 2; // only data - index_composition1.mode = 2; // only data - - index1 = faiss::gpu::index_cpu_to_gpu(&res, 0, &index_composition0, &option0); - delete index1; - index1 = faiss::gpu::index_cpu_to_gpu(&res, 1, &index_composition1, &option1); - delete index1; - -// double tx = getmillisecs(); - std::thread t1(gpu_executor, 0, &option0, &index_composition0); - std::thread t2(gpu_executor, 1, &option1, &index_composition1); - t1.join(); - t2.join(); - -// std::thread t3(gpu_executor, 0, &option0, &index_composition0); -// std::thread t4(gpu_executor, 1, &option1, &index_composition1); -// t3.join(); -// t4.join(); -// double ty = getmillisecs(); -// printf("Total GPU execution time: %0.2f\n", ty - tx); - cpu_executor(&index_composition0); - cpu_executor(&index_composition1); - - delete [] xq; - return 0; -} diff --git a/core/src/index/thirdparty/faiss/tutorial/cpp/6-RUN.cpp b/core/src/index/thirdparty/faiss/tutorial/cpp/6-RUN.cpp deleted file mode 100644 index 2c09fef266..0000000000 --- a/core/src/index/thirdparty/faiss/tutorial/cpp/6-RUN.cpp +++ /dev/null @@ -1,247 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include -#include -#include - -#include - -#include "faiss/IndexIVF.h" -#include "faiss/IndexFlat.h" -#include "faiss/index_io.h" -#include "faiss/gpu/GpuIndexFlat.h" -#include "faiss/gpu/StandardGpuResources.h" -#include "faiss/gpu/GpuAutoTune.h" -#include "faiss/gpu/GpuCloner.h" -#include "faiss/gpu/GpuClonerOptions.h" -#include "faiss/gpu/GpuIndexIVF.h" -#include "faiss/gpu/GpuIndexIVFSQHybrid.h" - -#include "faiss/impl/FaissAssert.h" -#include "faiss/impl/AuxIndexStructures.h" - -#include "faiss/IndexFlat.h" -#include "faiss/VectorTransform.h" -#include "faiss/IndexLSH.h" -#include "faiss/IndexPQ.h" - -#include "faiss/IndexIVFPQ.h" -#include "faiss/IndexIVFFlat.h" -#include "faiss/IndexIVFSpectralHash.h" -#include "faiss/MetaIndexes.h" -#include "faiss/IndexSQHybrid.h" -#include "faiss/IndexHNSW.h" -#include "faiss/OnDiskInvertedLists.h" -#include "faiss/IndexBinaryFlat.h" -#include "faiss/IndexBinaryFromFloat.h" -#include "faiss/IndexBinaryHNSW.h" -#include "faiss/IndexBinaryIVF.h" -#include "faiss/utils/distances.h" -#include "faiss/index_factory.h" - -using namespace faiss; - -#define PRINT_RESULT 0 -std::shared_ptr gpu_index_ivf_ptr; -const int d = 512; // dimension -const int nq = 1000; // nb of queries -const int nprobe = 1; -int k = 8; - -void -print_result(const char* unit, long number, long k, long nq, long* I) { - printf("%s: I (2 first results)=\n", unit); - for (int i = 0; i < number; i++) { - for (int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - printf("%s: I (2 last results)=\n", unit); - for (int i = nq - number; i < nq; i++) { - for (int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } -} - -void -cpu_executor(faiss::Index* cpu_index, float*& xq) { // search xq - printf("CPU: \n"); - long* I = new long[k * nq]; - float* D = new float[k * nq]; - - double t4 = getmillisecs(); - faiss::IndexIVF* ivf_index = - dynamic_cast(cpu_index); - ivf_index->nprobe = nprobe; - cpu_index->search(nq, xq, k, D, I); - double t5 = getmillisecs(); - printf("CPU execution time: %0.2f\n", t5 - t4); -#if PRINT_RESULT - print_result("CPU", number, k, nq, I); -#endif - delete[] I; - delete[] D; -}; - -void -hybrid_executor(faiss::Index* cpu_index, - faiss::IndexComposition* index_composition, - float*& xq) { // search xq - printf("HYBRID: \n"); - long* I = new long[k * nq]; - float* D = new float[k * nq]; - - double t4 = getmillisecs(); - faiss::IndexIVF* ivf_index = dynamic_cast(cpu_index); - ivf_index->nprobe = nprobe; - - faiss::gpu::GpuIndexFlat* is_gpu_flat_index = dynamic_cast(ivf_index->quantizer); - if (is_gpu_flat_index == nullptr) { - delete ivf_index->quantizer; - ivf_index->quantizer = index_composition->quantizer; - } - - cpu_index->search(nq, xq, k, D, I); - double t5 = getmillisecs(); - printf("HYBRID execution time: %0.2f\n", t5 - t4); -#if PRINT_RESULT - print_result("HYBRID", number, k, nq, I); -#endif - delete[] I; - delete[] D; -}; - -void -gpu_executor(faiss::gpu::StandardGpuResources& res, - int device_id, - faiss::gpu::GpuClonerOptions* option, - faiss::IndexComposition* index_composition, - float*& xq) { - auto tmp_index = faiss::gpu::index_cpu_to_gpu(&res, device_id, index_composition, option); - delete tmp_index; - double t0 = getmillisecs(); - { - // cpu to gpu - tmp_index = faiss::gpu::index_cpu_to_gpu(&res, device_id, index_composition, option); - gpu_index_ivf_ptr = std::shared_ptr(tmp_index); - } - double t1 = getmillisecs(); - printf("CPU to GPU loading time: %0.2f\n", t1 - t0); - - { - long* I = new long[k * nq]; - float* D = new float[k * nq]; - - faiss::gpu::GpuIndexIVFSQHybrid - * gpu_index_ivf_hybrid = dynamic_cast(gpu_index_ivf_ptr.get()); - gpu_index_ivf_hybrid->setNumProbes(nprobe); - for (long i = 0; i < 1; ++i) { - double t2 = getmillisecs(); - gpu_index_ivf_ptr->search(nq, xq, k, D, I); - double t3 = getmillisecs(); - printf("* GPU: %d, execution time: %0.2f\n", device_id, t3 - t2); - } - - // print results -#if PRINT_RESULT - print_result("GPU", number, k, nq, I); -#endif - delete[] I; - delete[] D; - } - double t4 = getmillisecs(); - - printf("GPU:%d total time: %0.2f\n", device_id, t4 - t0); - -}; - -int -main() { - const char* filename = "index500k-h.index"; - faiss::gpu::StandardGpuResources res; - -#if PRINT_RESULT - int number = 8; -#endif - - float* xq = new float[d * nq]; - for (int i = 0; i < nq; i++) { - for (int j = 0; j < d; j++) { - xq[d * i + j] = drand48(); - } - } - faiss::distance_compute_blas_threshold = 800; - - faiss::Index* cpu_index = nullptr; - faiss::IndexIVF* cpu_ivf_index = nullptr; - if ((access(filename, F_OK)) == -1) { - printf("index file not found."); - exit(-1); - } else { - cpu_index = faiss::read_index(filename); - } - - cpu_ivf_index = dynamic_cast(cpu_index); - if (cpu_ivf_index != nullptr) { - cpu_ivf_index->to_readonly(); - } - - printf("============================\n"); - cpu_executor(cpu_index, xq); - cpu_executor(cpu_index, xq); - printf("============================\n"); - - faiss::gpu::GpuClonerOptions option0; - faiss::gpu::GpuClonerOptions option1; - - faiss::IndexComposition index_composition0; - index_composition0.index = cpu_index; - index_composition0.quantizer = nullptr; - index_composition0.mode = 0; // only quantizer - - // Copy quantizer to GPU 0 - auto index1 = faiss::gpu::index_cpu_to_gpu(&res, 0, &index_composition0, &option0); - delete index1; - - faiss::IndexComposition index_composition1; - index_composition1.index = cpu_index; - index_composition1.quantizer = nullptr; - index_composition1.mode = 0; // only quantizer - - // Copy quantizer to GPU 1 - index1 = faiss::gpu::index_cpu_to_gpu(&res, 1, &index_composition1, &option1); - delete index1; - - hybrid_executor(cpu_index, &index_composition0, xq); - hybrid_executor(cpu_index, &index_composition1, xq); - - printf("============================\n"); - - index_composition0.mode = 2; // only data - index_composition1.mode = 2; // only data - - index1 = faiss::gpu::index_cpu_to_gpu(&res, 0, &index_composition0, &option0); - delete index1; - index1 = faiss::gpu::index_cpu_to_gpu(&res, 1, &index_composition1, &option1); - delete index1; - - gpu_executor(res, 0, &option0, &index_composition0, xq); - gpu_executor(res, 1, &option1, &index_composition1, xq); - - printf("============================\n"); - - hybrid_executor(cpu_index, &index_composition0, xq); - hybrid_executor(cpu_index, &index_composition1, xq); - - delete[] xq; - gpu_index_ivf_ptr = nullptr; - return 0; -} diff --git a/core/src/index/thirdparty/faiss/tutorial/cpp/7-GPU.cpp b/core/src/index/thirdparty/faiss/tutorial/cpp/7-GPU.cpp deleted file mode 100644 index 4ab91f27db..0000000000 --- a/core/src/index/thirdparty/faiss/tutorial/cpp/7-GPU.cpp +++ /dev/null @@ -1,347 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include -#include -#include - -#include - -#include "faiss/IndexIVF.h" -#include "faiss/IndexFlat.h" -#include "faiss/index_io.h" -#include "faiss/gpu/GpuIndexFlat.h" -#include "faiss/gpu/StandardGpuResources.h" -#include "faiss/gpu/GpuAutoTune.h" -#include "faiss/gpu/GpuClonerOptions.h" -#include "faiss/gpu/GpuCloner.h" -#include "faiss/gpu/GpuIndexIVF.h" -#include "faiss/gpu/GpuIndexIVFSQHybrid.h" - - -#include "faiss/IndexFlat.h" -#include "faiss/VectorTransform.h" -#include "faiss/IndexLSH.h" -#include "faiss/IndexPQ.h" - -#include "faiss/IndexIVFPQ.h" -#include "faiss/IndexIVFFlat.h" -#include "faiss/IndexIVFSpectralHash.h" -#include "faiss/MetaIndexes.h" -#include "faiss/IndexSQHybrid.h" -#include "faiss/IndexHNSW.h" -#include "faiss/OnDiskInvertedLists.h" -#include "faiss/IndexBinaryFlat.h" -#include "faiss/IndexBinaryFromFloat.h" -#include "faiss/IndexBinaryHNSW.h" -#include "faiss/IndexBinaryIVF.h" -#include "faiss/utils/distances.h" -#include "faiss/clone_index.h" -#include "faiss/index_factory.h" - -using namespace faiss; - -#define PRINT_RESULT 0 - -void print_result(const char* unit, long number, long k, long nq, long *I) { - printf("%s: I (2 first results)=\n", unit); - for(int i = 0; i < number; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - printf("%s: I (2 last results)=\n", unit); - for(int i = nq - number; i < nq; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } -} - -void -GpuLoad(faiss::gpu::StandardGpuResources* res, - int device_id, - faiss::gpu::GpuClonerOptions* option, - faiss::IndexComposition* index_composition, - std::shared_ptr& gpu_index_ivf_ptr - ) { - - double t0 = getmillisecs (); - - auto tmp_index = faiss::gpu::index_cpu_to_gpu(res, device_id, index_composition, option); - gpu_index_ivf_ptr = std::shared_ptr(tmp_index); - - double t1 = getmillisecs (); - printf("CPU to GPU loading time: %0.2f\n", t1 - t0); -} - -void -GpuExecutor( - std::shared_ptr& gpu_index_ivf_ptr, - faiss::gpu::StandardGpuResources& res, - int device_id, - faiss::gpu::GpuClonerOptions* option, - faiss::IndexComposition* index_composition, - int nq, - int nprobe, - int k, - float* xq) { - double t0 = getmillisecs (); - { - long *I = new long[k * nq]; - float *D = new float[k * nq]; - - faiss::gpu::GpuIndexIVFSQHybrid* gpu_index_ivf_hybrid = - dynamic_cast(gpu_index_ivf_ptr.get()); - gpu_index_ivf_hybrid->setNumProbes(nprobe); - for(long i = 0; i < 4; ++ i) { - double t2 = getmillisecs(); - gpu_index_ivf_ptr->search(nq, xq, k, D, I); - double t3 = getmillisecs(); - printf("* GPU: %d, execution time: %0.2f\n", device_id, t3 - t2); - } - - // print results -#if PRINT_RESULT - print_result("GPU", number, k, nq, I); -#endif - delete [] I; - delete [] D; - gpu_index_ivf_ptr = nullptr; - } - double t4 = getmillisecs(); - - printf("GPU:%d total time: %0.2f\n", device_id, t4 - t0); -} - - -void -GpuExecutor( - faiss::gpu::StandardGpuResources& res, - int device_id, - faiss::gpu::GpuClonerOptions* option, - faiss::IndexComposition* index_composition, - int nq, - int nprobe, - int k, - float* xq) { - auto tmp_index = faiss::gpu::index_cpu_to_gpu(&res, device_id, index_composition, option); - delete tmp_index; - double t0 = getmillisecs (); - // cpu to gpu - tmp_index = faiss::gpu::index_cpu_to_gpu(&res, device_id, index_composition, option); - auto gpu_index_ivf_ptr = std::shared_ptr(tmp_index); - - double t1 = getmillisecs (); - printf("CPU to GPU loading time: %0.2f\n", t1 - t0); - - { - long *I = new long[k * nq]; - float *D = new float[k * nq]; - - faiss::gpu::GpuIndexIVFSQHybrid* gpu_index_ivf_hybrid = - dynamic_cast(gpu_index_ivf_ptr.get()); - gpu_index_ivf_hybrid->setNumProbes(nprobe); - for(long i = 0; i < 4; ++ i) { - double t2 = getmillisecs(); - gpu_index_ivf_ptr->search(nq, xq, k, D, I); - double t3 = getmillisecs(); - printf("* GPU: %d, execution time: %0.2f\n", device_id, t3 - t2); - } - - // print results -#if PRINT_RESULT - print_result("GPU", number, k, nq, I); -#endif - delete [] I; - delete [] D; - gpu_index_ivf_ptr = nullptr; - } - double t4 = getmillisecs(); - - printf("GPU:%d total time: %0.2f\n", device_id, t4 - t0); -} - -void -CpuExecutor( - faiss::IndexComposition* index_composition, - int nq, - int nprobe, - int k, - float* xq, - faiss::Index *cpu_index) { - printf("CPU: \n"); - long *I = new long[k * nq]; - float *D = new float[k * nq]; - - double t4 = getmillisecs(); - faiss::IndexIVF* ivf_index = - dynamic_cast(cpu_index); - ivf_index->nprobe = nprobe; - - faiss::gpu::GpuIndexFlat* is_gpu_flat_index = dynamic_cast(ivf_index->quantizer); - if(is_gpu_flat_index == nullptr) { - delete ivf_index->quantizer; - ivf_index->quantizer = index_composition->quantizer; - } - - cpu_index->search(nq, xq, k, D, I); - double t5 = getmillisecs(); - printf("CPU execution time: %0.2f\n", t5 - t4); -#if PRINT_RESULT - print_result("CPU", number, k, nq, I); -#endif - delete [] I; - delete [] D; -} - -int main() { - const char* filename = "index500k-h.index"; - -#if PRINT_RESULT - int number = 8; -#endif - - int d = 512; // dimension - int nq = 1000; // nb of queries - int nprobe = 8; - float *xq = new float[d * nq]; - for(int i = 0; i < nq; i++) { - for(int j = 0; j < d; j++) { - xq[d * i + j] = drand48(); - } - } - faiss::distance_compute_blas_threshold = 800; - - faiss::gpu::StandardGpuResources res; - - int k = 1000; - std::shared_ptr gpu_index_ivf_ptr; - - const char* index_description = "IVF16384,SQ8Hybrid"; -// const char* index_description = "IVF3276,SQ8"; - - faiss::Index *cpu_index = nullptr; - faiss::IndexIVF* cpu_ivf_index = nullptr; - if((access(filename,F_OK))==-1) { - // create database - long nb = 500000; // database size -// printf("-----------------------\n"); - long size = d * nb; - float *xb = new float[size]; - memset(xb, 0, size * sizeof(float)); - printf("size: %ld\n", (size * sizeof(float)) ); - for(long i = 0; i < nb; i++) { - for(long j = 0; j < d; j++) { - float rand = drand48(); - xb[d * i + j] = rand; - } - } - - faiss::Index *ori_index = faiss::index_factory(d, index_description, faiss::METRIC_L2); - auto device_index = faiss::gpu::index_cpu_to_gpu(&res, 0, ori_index); - - gpu_index_ivf_ptr = std::shared_ptr(device_index); - - assert(!device_index->is_trained); - device_index->train(nb, xb); - assert(device_index->is_trained); - device_index->add(nb, xb); // add vectors to the index - - printf("is_trained = %s\n", device_index->is_trained ? "true" : "false"); - printf("ntotal = %ld\n", device_index->ntotal); - - cpu_index = faiss::gpu::index_gpu_to_cpu ((device_index)); - faiss::write_index(cpu_index, filename); - printf("index.index is stored successfully.\n"); - delete [] xb; - } else { - cpu_index = faiss::read_index(filename); - } - - cpu_ivf_index = dynamic_cast(cpu_index); - if(cpu_ivf_index != nullptr) { - cpu_ivf_index->to_readonly(); - } - - faiss::gpu::GpuClonerOptions option0; - faiss::gpu::GpuClonerOptions option1; - - option0.allInGpu = true; - option1.allInGpu = true; - - faiss::IndexComposition index_composition0; - index_composition0.index = cpu_index; - index_composition0.quantizer = nullptr; - index_composition0.mode = 1; // only quantizer - - // Copy quantizer to GPU 0 - auto index1 = faiss::gpu::index_cpu_to_gpu(&res, 0, &index_composition0, &option0); - delete index1; - - faiss::IndexComposition index_composition1; - index_composition1.index = cpu_index; - index_composition1.quantizer = nullptr; - index_composition1.mode = 1; // only quantizer - - // Copy quantizer to GPU 1 - index1 = faiss::gpu::index_cpu_to_gpu(&res, 1, &index_composition1, &option1); - delete index1; - -// std::thread t_cpu1(cpu_executor, &index_composition0); -// t_cpu1.join(); -// std::thread t_cpu2(cpu_executor, &index_composition1); -// t_cpu2.join(); - -// index_composition0.mode = 2; // only data -// index_composition1.mode = 2; // only data -// -// index1 = faiss::gpu::index_cpu_to_gpu(&res, 0, &index_composition0, &option0); -// delete index1; -// index1 = faiss::gpu::index_cpu_to_gpu(&res, 1, &index_composition1, &option1); -// delete index1; - -// double tx = getmillisecs(); -// std::thread t1(gpu_executor, 0, &option0, &index_composition0); -// std::thread t2(gpu_executor, 1, &option1, &index_composition1); -// t1.join(); -// t2.join(); -// for(long i = 0; i < 10; ++ i) { -// std::shared_ptr gpu_index_ptr00; -// std::shared_ptr gpu_index_ptr01; -// -// std::thread t00(GpuLoad, &res, 0, &option0, &index_composition0, std::ref(gpu_index_ptr00)); -//// std::thread t2(GpuLoad, &res, 1, &option1, &index_composition1, std::ref(gpu_index_ptr1)); -// std::thread t01(GpuLoad, &res, 0, &option0, &index_composition0, std::ref(gpu_index_ptr01)); -// -// t00.join(); -// -// GpuExecutor(gpu_index_ptr00, res, 0, &option0, &index_composition0, nq, nprobe, k, xq); -// -// t01.join(); -//// t2.join(); -// GpuExecutor(gpu_index_ptr01, res, 0, &option0, &index_composition0, nq, nprobe, k, xq); -//// GpuExecutor(gpu_index_ptr1, res, 1, &option1, &index_composition1, nq, nprobe, k, xq); -// } - -// std::thread t3(gpu_executor, 0, &option0, &index_composition0); -// std::thread t4(gpu_executor, 1, &option1, &index_composition1); -// t3.join(); -// t4.join(); -// double ty = getmillisecs(); -// printf("Total GPU execution time: %0.2f\n", ty - tx); - - CpuExecutor(&index_composition0, nq, nprobe, k, xq, cpu_index); - CpuExecutor(&index_composition1, nq, nprobe, k, xq, cpu_index); - - ///// - delete [] xq; - return 0; -} - diff --git a/core/src/index/thirdparty/faiss/tutorial/cpp/8-GPU.cpp b/core/src/index/thirdparty/faiss/tutorial/cpp/8-GPU.cpp deleted file mode 100644 index 11f49a09cc..0000000000 --- a/core/src/index/thirdparty/faiss/tutorial/cpp/8-GPU.cpp +++ /dev/null @@ -1,479 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include -#include -#include - -#include - -#include "faiss/IndexIVF.h" -#include "faiss/IndexFlat.h" -#include "faiss/index_io.h" -#include "faiss/gpu/GpuIndexFlat.h" -#include "faiss/gpu/StandardGpuResources.h" -#include "faiss/gpu/GpuAutoTune.h" -#include "faiss/gpu/GpuClonerOptions.h" -#include "faiss/gpu/GpuCloner.h" -#include "faiss/gpu/GpuIndexIVF.h" -#include "faiss/gpu/GpuIndexIVFSQHybrid.h" - -#include "faiss/IndexFlat.h" -#include "faiss/VectorTransform.h" -#include "faiss/IndexLSH.h" -#include "faiss/IndexPQ.h" - -#include "faiss/IndexIVFPQ.h" -#include "faiss/IndexIVFFlat.h" -#include "faiss/IndexIVFSpectralHash.h" -#include "faiss/MetaIndexes.h" -#include "faiss/IndexSQHybrid.h" -#include "faiss/IndexHNSW.h" -#include "faiss/OnDiskInvertedLists.h" -#include "faiss/IndexBinaryFlat.h" -#include "faiss/IndexBinaryFromFloat.h" -#include "faiss/IndexBinaryHNSW.h" -#include "faiss/IndexBinaryIVF.h" -#include "faiss/utils/distances.h" -#include "faiss/clone_index.h" -#include "faiss/index_factory.h" - -using namespace faiss; - -#define PRINT_RESULT 0 - -void print_result(const char* unit, long number, long k, long nq, long *I) { - printf("%s: I (2 first results)=\n", unit); - for(int i = 0; i < number; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - printf("%s: I (2 last results)=\n", unit); - for(int i = nq - number; i < nq; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } -} - -void -GpuLoad(faiss::gpu::StandardGpuResources* res, - int device_id, - faiss::gpu::GpuClonerOptions* option, - faiss::IndexComposition* index_composition, - std::shared_ptr& gpu_index_ivf_ptr - ) { - - double t0 = getmillisecs (); - - auto tmp_index = faiss::gpu::index_cpu_to_gpu(res, device_id, index_composition, option); - gpu_index_ivf_ptr = std::shared_ptr(tmp_index); - - double t1 = getmillisecs (); - printf("CPU to GPU loading time: %0.2f\n", t1 - t0); -} - -void -GpuExecutor( - std::shared_ptr& gpu_index_ivf_ptr, - faiss::gpu::StandardGpuResources& res, - int device_id, - faiss::gpu::GpuClonerOptions* option, - faiss::IndexComposition* index_composition, - int nq, - int nprobe, - int k, - float* xq) { - double t0 = getmillisecs (); - { - long *I = new long[k * nq]; - float *D = new float[k * nq]; - - faiss::gpu::GpuIndexIVFSQHybrid* gpu_index_ivf_hybrid = - dynamic_cast(gpu_index_ivf_ptr.get()); - gpu_index_ivf_hybrid->setNumProbes(nprobe); - for(long i = 0; i < 4; ++ i) { - double t2 = getmillisecs(); - gpu_index_ivf_ptr->search(nq, xq, k, D, I); - double t3 = getmillisecs(); - printf("* GPU: %d, execution time: %0.2f\n", device_id, t3 - t2); - } - - // print results -#if PRINT_RESULT - print_result("GPU", number, k, nq, I); -#endif - delete [] I; - delete [] D; - gpu_index_ivf_ptr = nullptr; - } - double t4 = getmillisecs(); - - printf("GPU:%d total time: %0.2f\n", device_id, t4 - t0); -} - - -void -GpuExecutor( - faiss::gpu::StandardGpuResources& res, - int device_id, - faiss::gpu::GpuClonerOptions* option, - faiss::IndexComposition* index_composition, - int nq, - int nprobe, - int k, - float* xq) { - auto tmp_index = faiss::gpu::index_cpu_to_gpu(&res, device_id, index_composition, option); - delete tmp_index; - double t0 = getmillisecs (); - // cpu to gpu - tmp_index = faiss::gpu::index_cpu_to_gpu(&res, device_id, index_composition, option); - auto gpu_index_ivf_ptr = std::shared_ptr(tmp_index); - - double t1 = getmillisecs (); - printf("CPU to GPU loading time: %0.2f\n", t1 - t0); - - { - long *I = new long[k * nq]; - float *D = new float[k * nq]; - - faiss::gpu::GpuIndexIVFSQHybrid* gpu_index_ivf_hybrid = - dynamic_cast(gpu_index_ivf_ptr.get()); - gpu_index_ivf_hybrid->setNumProbes(nprobe); - for(long i = 0; i < 4; ++ i) { - double t2 = getmillisecs(); - gpu_index_ivf_ptr->search(nq, xq, k, D, I); - double t3 = getmillisecs(); - printf("* GPU: %d, execution time: %0.2f\n", device_id, t3 - t2); - } - - // print results -#if PRINT_RESULT - print_result("GPU", number, k, nq, I); -#endif - delete [] I; - delete [] D; - gpu_index_ivf_ptr = nullptr; - } - double t4 = getmillisecs(); - - printf("GPU:%d total time: %0.2f\n", device_id, t4 - t0); -} - -void -CpuExecutor( - faiss::IndexComposition* index_composition, - int nq, - int nprobe, - int k, - float* xq, - faiss::Index *cpu_index) { - printf("CPU: \n"); - long *I = new long[k * nq]; - float *D = new float[k * nq]; - - double t4 = getmillisecs(); - faiss::IndexIVF* ivf_index = - dynamic_cast(cpu_index); - ivf_index->nprobe = nprobe; - - faiss::gpu::GpuIndexFlat* is_gpu_flat_index = dynamic_cast(ivf_index->quantizer); - if(is_gpu_flat_index == nullptr) { - delete ivf_index->quantizer; - ivf_index->quantizer = index_composition->quantizer; - } - - cpu_index->search(nq, xq, k, D, I); - double t5 = getmillisecs(); - printf("CPU execution time: %0.2f\n", t5 - t4); -#if PRINT_RESULT - print_result("CPU", number, k, nq, I); -#endif - delete [] I; - delete [] D; -} - -void create_index(const char* filename, const char* index_description, long db_size, long d) { - faiss::gpu::StandardGpuResources res; - if((access(filename,F_OK))==-1) { - // create database - long size = d * db_size; - float *xb = new float[size]; - memset(xb, 0, size * sizeof(float)); - printf("size: %ld\n", (size * sizeof(float)) ); - for(long i = 0; i < db_size; i++) { - for(long j = 0; j < d; j++) { - float rand = drand48(); - xb[d * i + j] = rand; - } - } - - faiss::Index *ori_index = faiss::index_factory(d, index_description, faiss::METRIC_INNER_PRODUCT); - auto device_index = faiss::gpu::index_cpu_to_gpu(&res, 0, ori_index); - - std::shared_ptr gpu_index_ivf_ptr = std::shared_ptr(device_index); - - assert(!device_index->is_trained); - device_index->train(db_size, xb); - assert(device_index->is_trained); - device_index->add(db_size, xb); // add vectors to the index - - printf("is_trained = %s\n", device_index->is_trained ? "true" : "false"); - printf("ntotal = %ld\n", device_index->ntotal); - - faiss::Index *cpu_index = faiss::gpu::index_gpu_to_cpu ((device_index)); - faiss::write_index(cpu_index, filename); - printf("index.index is stored successfully.\n"); - delete [] xb; - } -} - -void execute_index(const char* filename, int d, int nq, int nprobe, int k, float* xq) { - faiss::gpu::StandardGpuResources res; - faiss::Index* cpu_index = faiss::read_index(filename); - faiss::IndexIVF* cpu_ivf_index = dynamic_cast(cpu_index); - - if(cpu_ivf_index != nullptr) { - cpu_ivf_index->to_readonly(); - } - - faiss::gpu::GpuClonerOptions option0; - faiss::gpu::GpuClonerOptions option1; - - option0.allInGpu = true; - option1.allInGpu = true; - - faiss::IndexComposition index_composition0; - index_composition0.index = cpu_index; - index_composition0.quantizer = nullptr; - index_composition0.mode = 1; // only quantizer - - // Copy quantizer to GPU 0 - auto index1 = faiss::gpu::index_cpu_to_gpu(&res, 0, &index_composition0, &option0); - delete index1; - - faiss::IndexComposition index_composition1; - index_composition1.index = cpu_index; - index_composition1.quantizer = nullptr; - index_composition1.mode = 1; // only quantizer - - // Copy quantizer to GPU 1 - index1 = faiss::gpu::index_cpu_to_gpu(&res, 1, &index_composition1, &option1); - delete index1; - - // std::thread t_cpu1(cpu_executor, &index_composition0); - // t_cpu1.join(); - // std::thread t_cpu2(cpu_executor, &index_composition1); - // t_cpu2.join(); - - index_composition0.mode = 2; // only data - index_composition1.mode = 2; // only data - - // index1 = faiss::gpu::index_cpu_to_gpu(&res, 0, &index_composition0, &option0); - // delete index1; - // index1 = faiss::gpu::index_cpu_to_gpu(&res, 1, &index_composition1, &option1); - // delete index1; - - // double tx = getmillisecs(); - // std::thread t1(gpu_executor, 0, &option0, &index_composition0); - // std::thread t2(gpu_executor, 1, &option1, &index_composition1); - // t1.join(); - // t2.join(); - for(long i = 0; i < 1; ++ i) { - std::shared_ptr gpu_index_ptr00; - std::shared_ptr gpu_index_ptr01; - - std::thread t00(GpuLoad, &res, 0, &option0, &index_composition0, std::ref(gpu_index_ptr00)); - // std::thread t2(GpuLoad, &res, 1, &option1, &index_composition1, std::ref(gpu_index_ptr1)); - std::thread t01(GpuLoad, &res, 0, &option0, &index_composition0, std::ref(gpu_index_ptr01)); - - t00.join(); - - GpuExecutor(gpu_index_ptr00, res, 0, &option0, &index_composition0, nq, nprobe, k, xq); - - t01.join(); - // t2.join(); - GpuExecutor(gpu_index_ptr01, res, 0, &option0, &index_composition0, nq, nprobe, k, xq); - // GpuExecutor(gpu_index_ptr1, res, 1, &option1, &index_composition1, nq, nprobe, k, xq); - } - - delete index_composition0.quantizer; - delete index_composition1.quantizer; - delete cpu_index; -} - -int main() { - const char* filename = "index500k-h.index"; - int d = 512; // dimension - int nq = 1000; // nb of queries - int nprobe = 16; - int k = 1000; - float *xq = new float[d * nq]; - for(int i = 0; i < nq; i++) { - for(int j = 0; j < d; j++) { - xq[d * i + j] = drand48(); - } - } - - long db_size = 500000; - const char* index_description = "IVF16384,SQ8Hybrid"; - create_index(filename, index_description, db_size, d); - for(long i = 0; i < 1000; ++ i) { - execute_index(filename, d, nq, nprobe, k, xq); - } - delete[] xq; - xq = nullptr; - return 0; -} - -/* -int main() { - const char* filename = "index500k-h.index"; - -#if PRINT_RESULT - int number = 8; -#endif - - int d = 512; // dimension - int nq = 1000; // nb of queries - int nprobe = 16; - float *xq = new float[d * nq]; - for(int i = 0; i < nq; i++) { - for(int j = 0; j < d; j++) { - xq[d * i + j] = drand48(); - } - } - faiss::distance_compute_blas_threshold = 800; - - faiss::gpu::StandardGpuResources res; - - int k = 1000; - std::shared_ptr gpu_index_ivf_ptr; - - const char* index_description = "IVF16384,SQ8Hybrid"; -// const char* index_description = "IVF3276,SQ8"; - - faiss::Index *cpu_index = nullptr; - faiss::IndexIVF* cpu_ivf_index = nullptr; - if((access(filename,F_OK))==-1) { - // create database - long nb = 500000; // database size -// printf("-----------------------\n"); - long size = d * nb; - float *xb = new float[size]; - memset(xb, 0, size * sizeof(float)); - printf("size: %ld\n", (size * sizeof(float)) ); - for(long i = 0; i < nb; i++) { - for(long j = 0; j < d; j++) { - float rand = drand48(); - xb[d * i + j] = rand; - } - } - - faiss::Index *ori_index = faiss::index_factory(d, index_description, faiss::METRIC_INNER_PRODUCT); - auto device_index = faiss::gpu::index_cpu_to_gpu(&res, 0, ori_index); - - gpu_index_ivf_ptr = std::shared_ptr(device_index); - - assert(!device_index->is_trained); - device_index->train(nb, xb); - assert(device_index->is_trained); - device_index->add(nb, xb); // add vectors to the index - - printf("is_trained = %s\n", device_index->is_trained ? "true" : "false"); - printf("ntotal = %ld\n", device_index->ntotal); - - cpu_index = faiss::gpu::index_gpu_to_cpu ((device_index)); - faiss::write_index(cpu_index, filename); - printf("index.index is stored successfully.\n"); - delete [] xb; - } else { - cpu_index = faiss::read_index(filename); - } - - cpu_ivf_index = dynamic_cast(cpu_index); - if(cpu_ivf_index != nullptr) { - cpu_ivf_index->to_readonly(); - } - - faiss::gpu::GpuClonerOptions option0; - faiss::gpu::GpuClonerOptions option1; - - option0.allInGpu = true; - option1.allInGpu = true; - - faiss::IndexComposition index_composition0; - index_composition0.index = cpu_index; - index_composition0.quantizer = nullptr; - index_composition0.mode = 1; // only quantizer - - // Copy quantizer to GPU 0 - auto index1 = faiss::gpu::index_cpu_to_gpu(&res, 0, &index_composition0, &option0); - delete index1; - - faiss::IndexComposition index_composition1; - index_composition1.index = cpu_index; - index_composition1.quantizer = nullptr; - index_composition1.mode = 1; // only quantizer - - // Copy quantizer to GPU 1 - index1 = faiss::gpu::index_cpu_to_gpu(&res, 1, &index_composition1, &option1); - delete index1; - -// std::thread t_cpu1(cpu_executor, &index_composition0); -// t_cpu1.join(); -// std::thread t_cpu2(cpu_executor, &index_composition1); -// t_cpu2.join(); - - index_composition0.mode = 2; // only data - index_composition1.mode = 2; // only data - - index1 = faiss::gpu::index_cpu_to_gpu(&res, 0, &index_composition0, &option0); - delete index1; - index1 = faiss::gpu::index_cpu_to_gpu(&res, 1, &index_composition1, &option1); - delete index1; - -// double tx = getmillisecs(); -// std::thread t1(gpu_executor, 0, &option0, &index_composition0); -// std::thread t2(gpu_executor, 1, &option1, &index_composition1); -// t1.join(); -// t2.join(); - for(long i = 0; i < 10; ++ i) { - std::shared_ptr gpu_index_ptr00; - std::shared_ptr gpu_index_ptr01; - - std::thread t00(GpuLoad, &res, 0, &option0, &index_composition0, std::ref(gpu_index_ptr00)); -// std::thread t2(GpuLoad, &res, 1, &option1, &index_composition1, std::ref(gpu_index_ptr1)); - std::thread t01(GpuLoad, &res, 0, &option0, &index_composition0, std::ref(gpu_index_ptr01)); - - t00.join(); - - GpuExecutor(gpu_index_ptr00, res, 0, &option0, &index_composition0, nq, nprobe, k, xq); - - t01.join(); -// t2.join(); - GpuExecutor(gpu_index_ptr01, res, 0, &option0, &index_composition0, nq, nprobe, k, xq); -// GpuExecutor(gpu_index_ptr1, res, 1, &option1, &index_composition1, nq, nprobe, k, xq); - } - -// std::thread t3(gpu_executor, 0, &option0, &index_composition0); -// std::thread t4(gpu_executor, 1, &option1, &index_composition1); -// t3.join(); -// t4.join(); -// double ty = getmillisecs(); -// printf("Total GPU execution time: %0.2f\n", ty - tx); -// CpuExecutor(&index_composition0, nq, nprobe, k, xq, cpu_index); -// CpuExecutor(&index_composition1, nq, nprobe, k, xq, cpu_index); - - ///// - delete [] xq; - return 0; -} -*/ diff --git a/core/src/index/thirdparty/faiss/tutorial/cpp/9-BinaryFlat.cpp b/core/src/index/thirdparty/faiss/tutorial/cpp/9-BinaryFlat.cpp deleted file mode 100644 index 547cc6d88d..0000000000 --- a/core/src/index/thirdparty/faiss/tutorial/cpp/9-BinaryFlat.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include -#include - -#include -#include -#include - -// #define TEST_HAMMING - -long int getTime(timeval end, timeval start) { - return 1000*(end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec)/1000; -} - -int main() { - // freopen("0.txt", "w", stdout); - - size_t d = 128; // dimension - size_t nb = 40000000; // database size - size_t nq = 10; // nb of queries - - uint8_t *xb = new uint8_t[d * nb / sizeof(uint8_t)]; - uint8_t *xq = new uint8_t[d * nq / sizeof(uint8_t)]; - - // skip 0 - lrand48(); - - size_t size_to_long = d * nb / sizeof(int32_t); - for(size_t i = 0; i < size_to_long; i++) { - ((int32_t*)xb)[i] = lrand48(); - } - - size_to_long = d * nq / sizeof(long int); - for(size_t i = 0; i < size_to_long; i++) { - ((int32_t*)xq)[i] = lrand48(); - } -#ifdef TEST_HAMMING - printf("test haming\n"); - faiss::IndexBinaryFlat index(d, faiss::MetricType::METRIC_Hamming); -#else - faiss::IndexBinaryFlat index(d, faiss::MetricType::METRIC_Jaccard); -#endif - index.add(nb, xb); - printf("ntotal = %ld d = %d\n", index.ntotal, index.d); - - int k = 10; - -#if 0 - { // sanity check: search 5 first vectors of xb - int64_t *I = new int64_t[k * 5]; - int32_t *D = new int32_t[k * 5]; - float *d_float = reinterpret_cast(D); - - index.search(5, xb, k, D, I); - - // print results - for(int i = 0; i < 5; i++) { - for(int j = 0; j < k; j++) -#ifdef TEST_HAMMING - printf("%8ld %d\n", I[i * k + j], D[i * k + j]); -#else - printf("%8ld %.08f\n", I[i * k + j], d_float[i * k + j]); -#endif - printf("\n"); - } - - delete [] I; - delete [] D; - } -#endif - - { // search xq - int64_t *I = new int64_t[k * nq]; - int32_t *D = new int32_t[k * nq]; - float *d_float = reinterpret_cast(D); - - for (int loop = 1; loop <= nq; loop ++) { - timeval t0; - gettimeofday(&t0, 0); - - index.search(loop, xq, k, D, I); - - timeval t1; - gettimeofday(&t1, 0); - printf("search nq %d time %ldms\n", loop, getTime(t1,t0)); -#if 0 - for (int i = 0; i < loop; i++) { - for(int j = 0; j < k; j++) -#ifdef TEST_HAMMING - printf("%8ld %d\n", I[i * k + j], D[i * k + j]); -#else - printf("%8ld %.08f\n", I[j + i * k], d_float[j + i * k]); -#endif - printf("\n"); - } -#endif - } - - delete [] I; - delete [] D; - } - - delete [] xb; - delete [] xq; - - return 0; -} - - diff --git a/core/src/index/thirdparty/faiss/tutorial/cpp/Makefile b/core/src/index/thirdparty/faiss/tutorial/cpp/Makefile deleted file mode 100644 index 472975f1d9..0000000000 --- a/core/src/index/thirdparty/faiss/tutorial/cpp/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - --include ../../makefile.inc - -CPU_TARGETS = 1-Flat 2-IVFFlat 3-IVFPQ 9-BinaryFlat -GPU_TARGETS = 4-GPU 5-Multiple-GPUs - -default: cpu - -all: cpu gpu - -cpu: $(CPU_TARGETS) - -gpu: $(GPU_TARGETS) - -%: %.cpp ../../libfaiss.a - $(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $@ $^ $(LDFLAGS) -I../../ $(LIBS) - -clean: - rm -f $(CPU_TARGETS) $(GPU_TARGETS) - -.PHONY: all cpu default gpu clean diff --git a/core/src/index/thirdparty/faiss/tutorial/cpp/faiss_test.cpp b/core/src/index/thirdparty/faiss/tutorial/cpp/faiss_test.cpp deleted file mode 100644 index 6377f133c2..0000000000 --- a/core/src/index/thirdparty/faiss/tutorial/cpp/faiss_test.cpp +++ /dev/null @@ -1,378 +0,0 @@ -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - - -#include "faiss/FaissAssert.h" -#include "faiss/AuxIndexStructures.h" - -#include "faiss/IndexFlat.h" -#include "faiss/VectorTransform.h" -#include "faiss/IndexLSH.h" -#include "faiss/IndexPQ.h" -#include "faiss/IndexIVF.h" -#include "faiss/IndexIVFPQ.h" -#include "faiss/IndexIVFFlat.h" -#include "faiss/IndexIVFSpectralHash.h" -#include "faiss/MetaIndexes.h" -#include "faiss/IndexScalarQuantizer.h" -#include "faiss/IndexHNSW.h" -#include "faiss/OnDiskInvertedLists.h" -#include "faiss/IndexBinaryFlat.h" -#include "faiss/IndexBinaryFromFloat.h" -#include "faiss/IndexBinaryHNSW.h" -#include "faiss/IndexBinaryIVF.h" -#include "faiss/gpu/GpuIndexIVFSQ.h" -#include "faiss/utils.h" - - -using namespace faiss; - -void -generate_file(const char *filename, - long nb, - long dimension, - std::string index_desc, - faiss::gpu::StandardGpuResources &res) { - long size = dimension * nb; - float *xb = new float[size]; - printf("size: %lf(GB)\n", (size * sizeof(float)) / (3 * 1024.0 * 1024 * 1024)); - for (long i = 0; i < nb; i++) { - for (long j = 0; j < dimension; j++) { - float rand = drand48(); - xb[dimension * i + j] = rand; - } - } - - faiss::Index *ori_index = faiss::index_factory(dimension, index_desc.c_str(), faiss::METRIC_L2); - auto device_index = faiss::gpu::index_cpu_to_gpu(&res, 0, ori_index); - - assert(!device_index->is_trained); - device_index->train(nb, xb); - assert(device_index->is_trained); - device_index->add(nb, xb); - - faiss::Index *cpu_index = faiss::gpu::index_gpu_to_cpu((device_index)); - faiss::write_index(cpu_index, filename); - printf("index: %s is stored successfully.\n", filename); - delete[] xb; - - return; -} - -faiss::Index * -get_index(const char *filename) { - return faiss::read_index(filename); -} - -void -execute_on_gpu(faiss::Index *index, float *xq, long nq, long k, long nprobe, - faiss::gpu::StandardGpuResources &res, long* I, float* D) { - - double t0 = getmillisecs(); - - faiss::gpu::CpuToGpuClonerOptions option; - option.readonly = true; - faiss::Index *tmp_index = faiss::gpu::cpu_to_gpu(&res, 0, index, &option); - std::shared_ptr gpu_index_ivf_ptr = std::shared_ptr(tmp_index); - - double t1 = getmillisecs(); - printf("CPU to GPU loading time: %0.2f\n", t1 - t0); - - - double t2 = getmillisecs(); - faiss::gpu::GpuIndexIVF *gpu_index_ivf = - dynamic_cast(gpu_index_ivf_ptr.get()); - gpu_index_ivf->setNumProbes(nprobe); - - gpu_index_ivf_ptr->search(nq, xq, k, D, I); - double t3 = getmillisecs(); - printf("GPU execution time: %0.2f\n", t3 - t2); -} - -void execute_on_cpu(faiss::Index *index, float* xq, long nq, long k, long nprobe, long* I, float* D) { - faiss::IndexIVF* ivf_index = - dynamic_cast(index); - ivf_index->nprobe = nprobe; - index->search(nq, xq, k, D, I); -} - -float *construct_queries(long nq, long dimension) { - float *xq = new float[dimension * nq]; - for (int i = 0; i < nq; i++) { - for (int j = 0; j < dimension; j++) { - xq[dimension * i + j] = drand48(); - } - } - return xq; -} - -void print_result(long number, long nq, long k, long *I, float *D) { - printf("I (%ld first results)=\n", number); - for (int i = 0; i < number; i++) { - for (int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - printf("I (%ld last results)=\n", number); - for (int i = nq - number; i < nq; i++) { - for (int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } -} - -void faiss_setting() { - faiss::distance_compute_blas_threshold = 800; -} - -int main() { - const char *filename = "index5.index"; - -#if 0 - long dimension = 512; - long nb = 6000000; - long nq = 1000; - long topk = 16; - long print_number = 8; - long nprobe = 32; - - std::string index_desc = "IVF16384,SQ8"; - faiss::gpu::StandardGpuResources res; - if ((access(filename, F_OK)) == -1) { - printf("file doesn't exist, create one\n"); - generate_file(filename, nb, dimension, index_desc, res); - } - - // Construct queries - float *xq = construct_queries(nq, dimension); - - // Read index - faiss::Index *index = get_index(filename); - - // Execute on GPU - long *I = new long[topk * nq]; - float *D = new float[topk * nq]; - execute_on_gpu(index, xq, nq, topk, nprobe, res, I, D); - - // Print results - print_result(print_number, nq, topk, I, D); - delete[] I; I = nullptr; - delete[] D; D = nullptr; - - // Execute on CPU - I = new long[topk * nq]; - D = new float[topk * nq]; - execute_on_cpu(index, xq, nq, topk, nprobe, I, D); - - // Print results - print_result(print_number, nq, topk, I, D); - delete[] I; - delete[] D; - - return 0; -#else - int number = 8; - int d = 512; // dimension - int nq = 1000; // nb of queries - int nprobe = 16; - float *xq = new float[d * nq]; - for(int i = 0; i < nq; i++) { - for(int j = 0; j < d; j++) { - xq[d * i + j] = drand48(); -// printf("%lf ", xq[d * i + j]); - } -// xq[d * i] += i / 1000.; -// printf("\n"); - } - faiss::distance_compute_blas_threshold = 800; - - faiss::gpu::StandardGpuResources res; - - int k = 16; - std::shared_ptr gpu_index_ivf_ptr; - - const char* index_description = "IVF16384,SQ8"; - // const char* index_description = "IVF3276,Flat"; -// Index *index_factory (int d, const char *description, -// MetricType metric = METRIC_L2); - - faiss::Index *cpu_index = nullptr; - if((access(filename,F_OK))==-1) { - long nb = 6000000; - long dimension = d; - printf("file doesn't exist, create one\n"); - generate_file(filename, nb, dimension, index_description, res); - /* - // create database - // database size -// printf("-----------------------\n"); - long size = d * nb; - float *xb = new float[size]; - memset(xb, 0, size * sizeof(float)); - printf("size: %ld\n", (size * sizeof(float)) ); - for(long i = 0; i < nb; i++) { - for(long j = 0; j < d; j++) { - float rand = drand48(); - xb[d * i + j] = rand; -// printf("%lf ", xb[d * i + j]); - } -// xb[d * i] += i / 1000.; -// printf("\n"); - } - - // Using an IVF index - // here we specify METRIC_L2, by default it performs inner-product search - - faiss::Index *ori_index = faiss::index_factory(d, index_description, faiss::METRIC_L2); - auto device_index = faiss::gpu::index_cpu_to_gpu(&res, 0, ori_index); - - gpu_index_ivf_ptr = std::shared_ptr(device_index); - - assert(!device_index->is_trained); - device_index->train(nb, xb); - assert(device_index->is_trained); - device_index->add(nb, xb); // add vectors to the index - - printf("is_trained = %s\n", device_index->is_trained ? "true" : "false"); - printf("ntotal = %ld\n", device_index->ntotal); - - cpu_index = faiss::gpu::index_gpu_to_cpu ((device_index)); - faiss::write_index(cpu_index, filename); - printf("index.index is stored successfully.\n"); - delete [] xb; - */ - } else { - cpu_index = get_index(filename); - } - - { - // cpu to gpu - double t0 = getmillisecs (); - faiss::gpu::CpuToGpuClonerOptions option; - option.readonly = true; - faiss::Index* tmp_index = faiss::gpu::cpu_to_gpu(&res, 0, cpu_index, &option); - - gpu_index_ivf_ptr = std::shared_ptr(tmp_index); - - // Gpu index dump - - auto gpu_index_ivf_sq_ptr = dynamic_cast(tmp_index); -// gpu_index_ivf_sq_ptr->dump(); - double t1 = getmillisecs (); - printf("CPU to GPU loading time: %0.2f\n", t1 - t0); - // // Cpu index dump - // auto cpu_index_ivf_sq_ptr = dynamic_cast(cpu_index); - // cpu_index_ivf_sq_ptr->dump(); - } - - - { // search xq - long *I = new long[k * nq]; - float *D = new float[k * nq]; - double t2 = getmillisecs(); - faiss::gpu::GpuIndexIVF* gpu_index_ivf = - dynamic_cast(gpu_index_ivf_ptr.get()); - gpu_index_ivf->setNumProbes(nprobe); - - gpu_index_ivf_ptr->search(nq, xq, k, D, I); - double t3 = getmillisecs(); - printf("GPU execution time: %0.2f\n", t3 - t2); - - // print results - printf("GPU: \n"); -#if 0 - printf("GPU: I (2 first results)=\n"); - for(int i = 0; i < number; i++) { - for(int j = 0; j < k; j++) - printf("GPU: %5ld(%f) ", I[i * k + j], D[i * k + j]); - printf("\n"); - } - - printf("GPU: I (2 last results)=\n"); - for(int i = nq - number; i < nq; i++) { - for(int j = 0; j < k; j++) - printf("GPU: %5ld(%f) ", I[i * k + j], D[i * k + j]); - printf("\n"); - } -#else - printf("I (2 first results)=\n"); - for(int i = 0; i < number; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - printf("I (2 last results)=\n"); - for(int i = nq - number; i < nq; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } -#endif - delete [] I; - delete [] D; - } - printf("----------------------------------\n"); - { // search xq - printf("CPU: \n"); - long *I = new long[k * nq]; - float *D = new float[k * nq]; - - double t4 = getmillisecs(); - faiss::IndexIVF* ivf_index = - dynamic_cast(cpu_index); - ivf_index->nprobe = nprobe; - cpu_index->search(nq, xq, k, D, I); - double t5 = getmillisecs(); - printf("CPU execution time: %0.2f\n", t5 - t4); -#if 0 - // print results - printf("CPU: I (2 first results)=\n"); - for(int i = 0; i < number; i++) { - for(int j = 0; j < k; j++) - printf("CPU: %5ld(%f) ", I[i * k + j], D[i * k + j]); - printf("\n"); - } - - printf("CPU: I (2 last results)=\n"); - for(int i = nq - number; i < nq; i++) { - for(int j = 0; j < k; j++) - printf("CPU: %5ld(%f) ", I[i * k + j], D[i * k + j]); - printf("\n"); - } -#else - // print results - printf("I (2 first results)=\n"); - for(int i = 0; i < number; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - printf("I (2 last results)=\n"); - for(int i = nq - number; i < nq; i++) { - for(int j = 0; j < k; j++) - printf("%5ld ", I[i * k + j]); - printf("\n"); - } -#endif - delete [] I; - delete [] D; - } - - - delete [] xq; - return 0; -#endif -} \ No newline at end of file diff --git a/core/src/index/thirdparty/faiss/tutorial/python/1-Flat.py b/core/src/index/thirdparty/faiss/tutorial/python/1-Flat.py deleted file mode 100644 index 584c7bc703..0000000000 --- a/core/src/index/thirdparty/faiss/tutorial/python/1-Flat.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -import numpy as np - -d = 64 # dimension -nb = 100000 # database size -nq = 10000 # nb of queries -np.random.seed(1234) # make reproducible -xb = np.random.random((nb, d)).astype('float32') -xb[:, 0] += np.arange(nb) / 1000. -xq = np.random.random((nq, d)).astype('float32') -xq[:, 0] += np.arange(nq) / 1000. - -import faiss # make faiss available -index = faiss.IndexFlatL2(d) # build the index -print(index.is_trained) -index.add(xb) # add vectors to the index -print(index.ntotal) - -k = 4 # we want to see 4 nearest neighbors -D, I = index.search(xb[:5], k) # sanity check -print(I) -print(D) -D, I = index.search(xq, k) # actual search -print(I[:5]) # neighbors of the 5 first queries -print(I[-5:]) # neighbors of the 5 last queries diff --git a/core/src/index/thirdparty/faiss/tutorial/python/2-IVFFlat.py b/core/src/index/thirdparty/faiss/tutorial/python/2-IVFFlat.py deleted file mode 100644 index a4ac0c4d1f..0000000000 --- a/core/src/index/thirdparty/faiss/tutorial/python/2-IVFFlat.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -import numpy as np - -d = 64 # dimension -nb = 100000 # database size -nq = 10000 # nb of queries -np.random.seed(1234) # make reproducible -xb = np.random.random((nb, d)).astype('float32') -xb[:, 0] += np.arange(nb) / 1000. -xq = np.random.random((nq, d)).astype('float32') -xq[:, 0] += np.arange(nq) / 1000. - -import faiss - -nlist = 100 -k = 4 -quantizer = faiss.IndexFlatL2(d) # the other index -index = faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRIC_L2) -# here we specify METRIC_L2, by default it performs inner-product search - -assert not index.is_trained -index.train(xb) -assert index.is_trained - -index.add(xb) # add may be a bit slower as well -D, I = index.search(xq, k) # actual search -print(I[-5:]) # neighbors of the 5 last queries -index.nprobe = 10 # default nprobe is 1, try a few more -D, I = index.search(xq, k) -print(I[-5:]) # neighbors of the 5 last queries diff --git a/core/src/index/thirdparty/faiss/tutorial/python/3-IVFPQ.py b/core/src/index/thirdparty/faiss/tutorial/python/3-IVFPQ.py deleted file mode 100644 index e502239ca4..0000000000 --- a/core/src/index/thirdparty/faiss/tutorial/python/3-IVFPQ.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -import numpy as np - -d = 64 # dimension -nb = 100000 # database size -nq = 10000 # nb of queries -np.random.seed(1234) # make reproducible -xb = np.random.random((nb, d)).astype('float32') -xb[:, 0] += np.arange(nb) / 1000. -xq = np.random.random((nq, d)).astype('float32') -xq[:, 0] += np.arange(nq) / 1000. - -import faiss - -nlist = 100 -m = 8 -k = 4 -quantizer = faiss.IndexFlatL2(d) # this remains the same -index = faiss.IndexIVFPQ(quantizer, d, nlist, m, 8) - # 8 specifies that each sub-vector is encoded as 8 bits -index.train(xb) -index.add(xb) -D, I = index.search(xb[:5], k) # sanity check -print(I) -print(D) -index.nprobe = 10 # make comparable with experiment above -D, I = index.search(xq, k) # search -print(I[-5:]) diff --git a/core/src/index/thirdparty/faiss/tutorial/python/4-GPU.py b/core/src/index/thirdparty/faiss/tutorial/python/4-GPU.py deleted file mode 100644 index 6f5e37e535..0000000000 --- a/core/src/index/thirdparty/faiss/tutorial/python/4-GPU.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -import numpy as np - -d = 64 # dimension -nb = 100000 # database size -nq = 10000 # nb of queries -np.random.seed(1234) # make reproducible -xb = np.random.random((nb, d)).astype('float32') -xb[:, 0] += np.arange(nb) / 1000. -xq = np.random.random((nq, d)).astype('float32') -xq[:, 0] += np.arange(nq) / 1000. - -import faiss # make faiss available - -res = faiss.StandardGpuResources() # use a single GPU - -## Using a flat index - -index_flat = faiss.IndexFlatL2(d) # build a flat (CPU) index - -# make it a flat GPU index -gpu_index_flat = faiss.index_cpu_to_gpu(res, 0, index_flat) - -gpu_index_flat.add(xb) # add vectors to the index -print(gpu_index_flat.ntotal) - -k = 4 # we want to see 4 nearest neighbors -D, I = gpu_index_flat.search(xq, k) # actual search -print(I[:5]) # neighbors of the 5 first queries -print(I[-5:]) # neighbors of the 5 last queries - - -## Using an IVF index - -nlist = 100 -quantizer = faiss.IndexFlatL2(d) # the other index -index_ivf = faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRIC_L2) -# here we specify METRIC_L2, by default it performs inner-product search - -# make it an IVF GPU index -gpu_index_ivf = faiss.index_cpu_to_gpu(res, 0, index_ivf) - -assert not gpu_index_ivf.is_trained -gpu_index_ivf.train(xb) # add vectors to the index -assert gpu_index_ivf.is_trained - -gpu_index_ivf.add(xb) # add vectors to the index -print(gpu_index_ivf.ntotal) - -k = 4 # we want to see 4 nearest neighbors -D, I = gpu_index_ivf.search(xq, k) # actual search -print(I[:5]) # neighbors of the 5 first queries -print(I[-5:]) # neighbors of the 5 last queries diff --git a/core/src/index/thirdparty/faiss/tutorial/python/5-Multiple-GPUs.py b/core/src/index/thirdparty/faiss/tutorial/python/5-Multiple-GPUs.py deleted file mode 100644 index c458587ce9..0000000000 --- a/core/src/index/thirdparty/faiss/tutorial/python/5-Multiple-GPUs.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -import numpy as np - -d = 64 # dimension -nb = 100000 # database size -nq = 10000 # nb of queries -np.random.seed(1234) # make reproducible -xb = np.random.random((nb, d)).astype('float32') -xb[:, 0] += np.arange(nb) / 1000. -xq = np.random.random((nq, d)).astype('float32') -xq[:, 0] += np.arange(nq) / 1000. - -import faiss # make faiss available - -ngpus = faiss.get_num_gpus() - -print("number of GPUs:", ngpus) - -cpu_index = faiss.IndexFlatL2(d) - -gpu_index = faiss.index_cpu_to_all_gpus( # build the index - cpu_index -) - -gpu_index.add(xb) # add vectors to the index -print(gpu_index.ntotal) - -k = 4 # we want to see 4 nearest neighbors -D, I = gpu_index.search(xq, k) # actual search -print(I[:5]) # neighbors of the 5 first queries -print(I[-5:]) # neighbors of the 5 last queries diff --git a/core/src/index/thirdparty/faiss/utils/BinaryDistance.cpp b/core/src/index/thirdparty/faiss/utils/BinaryDistance.cpp deleted file mode 100644 index d6ebaa44f0..0000000000 --- a/core/src/index/thirdparty/faiss/utils/BinaryDistance.cpp +++ /dev/null @@ -1,337 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace faiss { - -static const size_t size_1M = 1 * 1024 * 1024; -static const size_t batch_size = 65536; - -template -static -void binary_distence_knn_hc( - int bytes_per_code, - float_maxheap_array_t * ha, - const uint8_t * bs1, - const uint8_t * bs2, - size_t n2, - bool order = true, - bool init_heap = true, - ConcurrentBitsetPtr bitset = nullptr) -{ - size_t k = ha->k; - - if ((bytes_per_code + k * (sizeof(float) + sizeof(int64_t))) * ha->nh < size_1M) { - int thread_max_num = omp_get_max_threads(); - // init heap - size_t thread_heap_size = ha->nh * k; - size_t all_heap_size = thread_heap_size * thread_max_num; - float *value = new float[all_heap_size]; - int64_t *labels = new int64_t[all_heap_size]; - for (int i = 0; i < all_heap_size; i++) { - value[i] = 1.0 / 0.0; - labels[i] = -1; - } - - T *hc = new T[ha->nh]; - for (size_t i = 0; i < ha->nh; i++) { - hc[i].set(bs1 + i * bytes_per_code, bytes_per_code); - } - -#pragma omp parallel for - for (size_t j = 0; j < n2; j++) { - if(!bitset || !bitset->test(j)) { - int thread_no = omp_get_thread_num(); - - const uint8_t * bs2_ = bs2 + j * bytes_per_code; - for (size_t i = 0; i < ha->nh; i++) { - tadis_t dis = hc[i].compute (bs2_); - - float * val_ = value + thread_no * thread_heap_size + i * k; - int64_t * ids_ = labels + thread_no * thread_heap_size + i * k; - if (dis < val_[0]) { - faiss::maxheap_swap_top (k, val_, ids_, dis, j); - } - } - } - } - - for (size_t t = 1; t < thread_max_num; t++) { - // merge heap - for (size_t i = 0; i < ha->nh; i++) { - float * __restrict value_x = value + i * k; - int64_t * __restrict labels_x = labels + i * k; - float *value_x_t = value_x + t * thread_heap_size; - int64_t *labels_x_t = labels_x + t * thread_heap_size; - for (size_t j = 0; j < k; j++) { - if (value_x_t[j] < value_x[0]) { - faiss::maxheap_swap_top (k, value_x, labels_x, value_x_t[j], labels_x_t[j]); - } - } - } - } - - // copy result - memcpy(ha->val, value, thread_heap_size * sizeof(float)); - memcpy(ha->ids, labels, thread_heap_size * sizeof(int64_t)); - - delete[] hc; - delete[] value; - delete[] labels; - - } else { - if (init_heap) ha->heapify (); - - const size_t block_size = batch_size; - for (size_t j0 = 0; j0 < n2; j0 += block_size) { - const size_t j1 = std::min(j0 + block_size, n2); -#pragma omp parallel for - for (size_t i = 0; i < ha->nh; i++) { - T hc (bs1 + i * bytes_per_code, bytes_per_code); - - const uint8_t * bs2_ = bs2 + j0 * bytes_per_code; - tadis_t dis; - tadis_t * __restrict bh_val_ = ha->val + i * k; - int64_t * __restrict bh_ids_ = ha->ids + i * k; - size_t j; - for (j = j0; j < j1; j++, bs2_+= bytes_per_code) { - if(!bitset || !bitset->test(j)){ - dis = hc.compute (bs2_); - if (dis < bh_val_[0]) { - faiss::maxheap_swap_top (k, bh_val_, bh_ids_, dis, j); - } - } - } - - } - } - } - - if (order) ha->reorder (); -} - -void binary_distence_knn_hc ( - MetricType metric_type, - float_maxheap_array_t * ha, - const uint8_t * a, - const uint8_t * b, - size_t nb, - size_t ncodes, - int order, - ConcurrentBitsetPtr bitset) -{ - switch (metric_type) { - case METRIC_Jaccard: - case METRIC_Tanimoto: - switch (ncodes) { -#define binary_distence_knn_hc_jaccard(ncodes) \ - case ncodes: \ - binary_distence_knn_hc \ - (ncodes, ha, a, b, nb, order, true, bitset); \ - break; - binary_distence_knn_hc_jaccard(8); - binary_distence_knn_hc_jaccard(16); - binary_distence_knn_hc_jaccard(32); - binary_distence_knn_hc_jaccard(64); - binary_distence_knn_hc_jaccard(128); - binary_distence_knn_hc_jaccard(256); - binary_distence_knn_hc_jaccard(512); -#undef binary_distence_knn_hc_jaccard - default: - binary_distence_knn_hc - (ncodes, ha, a, b, nb, order, true, bitset); - break; - } - break; - - default: - break; - } -} - -template -static -void binary_distence_knn_mc( - int bytes_per_code, - const uint8_t * bs1, - const uint8_t * bs2, - size_t n1, - size_t n2, - size_t k, - float *distances, - int64_t *labels, - ConcurrentBitsetPtr bitset) -{ - if ((bytes_per_code + sizeof(size_t) + k * sizeof(int64_t)) * n1 < size_1M) { - int thread_max_num = omp_get_max_threads(); - - size_t group_num = n1 * thread_max_num; - size_t *match_num = new size_t[group_num]; - int64_t *match_data = new int64_t[group_num * k]; - for (size_t i = 0; i < group_num; i++) { - match_num[i] = 0; - } - - T *hc = new T[n1]; - for (size_t i = 0; i < n1; i++) { - hc[i].set(bs1 + i * bytes_per_code, bytes_per_code); - } - -#pragma omp parallel for - for (size_t j = 0; j < n2; j++) { - if(!bitset || !bitset->test(j)) { - int thread_no = omp_get_thread_num(); - - const uint8_t * bs2_ = bs2 + j * bytes_per_code; - for (size_t i = 0; i < n1; i++) { - if (hc[i].compute(bs2_)) { - size_t match_index = thread_no * n1 + i; - size_t &index = match_num[match_index]; - if (index < k) { - match_data[match_index * k + index] = j; - index++; - } - } - } - } - } - for (size_t i = 0; i < n1; i++) { - size_t n_i = 0; - float *distances_i = distances + i * k; - int64_t *labels_i = labels + i * k; - - for (size_t t = 0; t < thread_max_num && n_i < k; t++) { - size_t match_index = t * n1 + i; - size_t copy_num = std::min(k - n_i, match_num[match_index]); - memcpy(labels_i + n_i, match_data + match_index * k, copy_num * sizeof(int64_t)); - memset(distances_i + n_i, 0, copy_num * sizeof(float)); - n_i += copy_num; - } - for (; n_i < k; n_i++) { - distances_i[n_i] = 1.0 / 0.0; - labels_i[n_i] = -1; - } - } - - delete[] hc; - delete[] match_num; - delete[] match_data; - - } else { - size_t *num = new size_t[n1]; - for (size_t i = 0; i < n1; i++) { - num[i] = 0; - } - - const size_t block_size = batch_size; - for (size_t j0 = 0; j0 < n2; j0 += block_size) { - const size_t j1 = std::min(j0 + block_size, n2); -#pragma omp parallel for - for (size_t i = 0; i < n1; i++) { - size_t num_i = num[i]; - if (num_i == k) continue; - float * dis = distances + i * k; - int64_t * lab = labels + i * k; - - T hc (bs1 + i * bytes_per_code, bytes_per_code); - const uint8_t * bs2_ = bs2 + j0 * bytes_per_code; - for (size_t j = j0; j < j1; j++, bs2_ += bytes_per_code) { - if(!bitset || !bitset->test(j)){ - if (hc.compute (bs2_)) { - dis[num_i] = 0; - lab[num_i] = j; - if (++num_i == k) break; - } - } - } - num[i] = num_i; - } - } - - for (size_t i = 0; i < n1; i++) { - float * dis = distances + i * k; - int64_t * lab = labels + i * k; - for (size_t num_i = num[i]; num_i < k; num_i++) { - dis[num_i] = 1.0 / 0.0; - lab[num_i] = -1; - } - } - - delete[] num; - } -} - -void binary_distence_knn_mc ( - MetricType metric_type, - const uint8_t * a, - const uint8_t * b, - size_t na, - size_t nb, - size_t k, - size_t ncodes, - float *distances, - int64_t *labels, - ConcurrentBitsetPtr bitset) { - - switch (metric_type) { - case METRIC_Substructure: - switch (ncodes) { -#define binary_distence_knn_mc_Substructure(ncodes) \ - case ncodes: \ - binary_distence_knn_mc \ - (ncodes, a, b, na, nb, k, distances, labels, bitset); \ - break; - binary_distence_knn_mc_Substructure(8); - binary_distence_knn_mc_Substructure(16); - binary_distence_knn_mc_Substructure(32); - binary_distence_knn_mc_Substructure(64); - binary_distence_knn_mc_Substructure(128); - binary_distence_knn_mc_Substructure(256); - binary_distence_knn_mc_Substructure(512); -#undef binary_distence_knn_mc_Substructure - default: - binary_distence_knn_mc - (ncodes, a, b, na, nb, k, distances, labels, bitset); - break; - } - break; - - case METRIC_Superstructure: - switch (ncodes) { -#define binary_distence_knn_mc_Superstructure(ncodes) \ - case ncodes: \ - binary_distence_knn_mc \ - (ncodes, a, b, na, nb, k, distances, labels, bitset); \ - break; - binary_distence_knn_mc_Superstructure(8); - binary_distence_knn_mc_Superstructure(16); - binary_distence_knn_mc_Superstructure(32); - binary_distence_knn_mc_Superstructure(64); - binary_distence_knn_mc_Superstructure(128); - binary_distence_knn_mc_Superstructure(256); - binary_distence_knn_mc_Superstructure(512); -#undef binary_distence_knn_mc_Superstructure - default: - binary_distence_knn_mc - (ncodes, a, b, na, nb, k, distances, labels, bitset); - break; - } - break; - - default: - break; - } -} - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/utils/BinaryDistance.h b/core/src/index/thirdparty/faiss/utils/BinaryDistance.h deleted file mode 100644 index fccdfd3674..0000000000 --- a/core/src/index/thirdparty/faiss/utils/BinaryDistance.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef FAISS_BINARY_DISTANCE_H -#define FAISS_BINARY_DISTANCE_H - -#include "faiss/Index.h" - -#include - -#include - -#include - -/* The binary distance type */ -typedef float tadis_t; - -namespace faiss { - -/** Return the k smallest distances for a set of binary query vectors, - * using a max heap. - * @param a queries, size ha->nh * ncodes - * @param b database, size nb * ncodes - * @param nb number of database vectors - * @param ncodes size of the binary codes (bytes) - * @param ordered if != 0: order the results by decreasing distance - * (may be bottleneck for k/n > 0.01) */ - void binary_distence_knn_hc ( - MetricType metric_type, - float_maxheap_array_t * ha, - const uint8_t * a, - const uint8_t * b, - size_t nb, - size_t ncodes, - int ordered, - ConcurrentBitsetPtr bitset = nullptr); - - /** Return the k matched distances for a set of binary query vectors, - * using a max heap. - * @param a queries, size ha->nh * ncodes - * @param b database, size nb * ncodes - * @param na number of queries vectors - * @param nb number of database vectors - * @param k number of the matched vectors to return - * @param ncodes size of the binary codes (bytes) - */ - void binary_distence_knn_mc ( - MetricType metric_type, - const uint8_t * a, - const uint8_t * b, - size_t na, - size_t nb, - size_t k, - size_t ncodes, - float *distances, - int64_t *labels, - ConcurrentBitsetPtr bitset); - -} // namespace faiss - -#include -#include -#include - -#endif // FAISS_BINARY_DISTANCE_H diff --git a/core/src/index/thirdparty/faiss/utils/ConcurrentBitset.cpp b/core/src/index/thirdparty/faiss/utils/ConcurrentBitset.cpp deleted file mode 100644 index f0cec5acce..0000000000 --- a/core/src/index/thirdparty/faiss/utils/ConcurrentBitset.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// 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 limitations -// under the License. - -#include "ConcurrentBitset.h" - -namespace faiss { - -ConcurrentBitset::ConcurrentBitset(id_type_t capacity) : capacity_(capacity), bitset_((capacity + 8 - 1) >> 3) { -} - -bool -ConcurrentBitset::test(id_type_t id) { - return bitset_[id >> 3].load() & (0x1 << (id & 0x7)); -} - -void -ConcurrentBitset::set(id_type_t id) { - bitset_[id >> 3].fetch_or(0x1 << (id & 0x7)); -} - -void -ConcurrentBitset::clear(id_type_t id) { - bitset_[id >> 3].fetch_and(~(0x1 << (id & 0x7))); -} - -size_t -ConcurrentBitset::capacity() { - return capacity_; -} - -size_t -ConcurrentBitset::size() { - return ((capacity_ + 8 - 1) >> 3); -} - -const uint8_t* -ConcurrentBitset::data() { - return reinterpret_cast(bitset_.data()); -} - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/utils/ConcurrentBitset.h b/core/src/index/thirdparty/faiss/utils/ConcurrentBitset.h deleted file mode 100644 index 4ccdc7c893..0000000000 --- a/core/src/index/thirdparty/faiss/utils/ConcurrentBitset.h +++ /dev/null @@ -1,62 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include -#include - -namespace faiss { - -class ConcurrentBitset { - public: - using id_type_t = int64_t; - - explicit ConcurrentBitset(id_type_t size); - - // ConcurrentBitset(const ConcurrentBitset&) = delete; - // ConcurrentBitset& - // operator=(const ConcurrentBitset&) = delete; - - bool - test(id_type_t id); - - void - set(id_type_t id); - - void - clear(id_type_t id); - - size_t - capacity(); - - size_t - size(); - - const uint8_t* - data(); - - private: - size_t capacity_; - std::vector> bitset_; - -}; - -using ConcurrentBitsetPtr = std::shared_ptr; - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/utils/Heap.cpp b/core/src/index/thirdparty/faiss/utils/Heap.cpp deleted file mode 100644 index 0b7cfab547..0000000000 --- a/core/src/index/thirdparty/faiss/utils/Heap.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -/* Function for soft heap */ - -#include - - -namespace faiss { - - -template -void HeapArray::heapify () -{ -#pragma omp parallel for - for (size_t j = 0; j < nh; j++) - heap_heapify (k, val + j * k, ids + j * k); -} - -template -void HeapArray::reorder () -{ -#pragma omp parallel for - for (size_t j = 0; j < nh; j++) - heap_reorder (k, val + j * k, ids + j * k); -} - -template -void HeapArray::addn (size_t nj, const T *vin, TI j0, - size_t i0, int64_t ni) -{ - if (ni == -1) ni = nh; - assert (i0 >= 0 && i0 + ni <= nh); -#pragma omp parallel for - for (size_t i = i0; i < i0 + ni; i++) { - T * __restrict simi = get_val(i); - TI * __restrict idxi = get_ids (i); - const T *ip_line = vin + (i - i0) * nj; - - for (size_t j = 0; j < nj; j++) { - T ip = ip_line [j]; - if (C::cmp(simi[0], ip)) { - heap_swap_top (k, simi, idxi, ip, j + j0); - } - } - } -} - -template -void HeapArray::addn_with_ids ( - size_t nj, const T *vin, const TI *id_in, - int64_t id_stride, size_t i0, int64_t ni) -{ - if (id_in == nullptr) { - addn (nj, vin, 0, i0, ni); - return; - } - if (ni == -1) ni = nh; - assert (i0 >= 0 && i0 + ni <= nh); -#pragma omp parallel for - for (size_t i = i0; i < i0 + ni; i++) { - T * __restrict simi = get_val(i); - TI * __restrict idxi = get_ids (i); - const T *ip_line = vin + (i - i0) * nj; - const TI *id_line = id_in + (i - i0) * id_stride; - - for (size_t j = 0; j < nj; j++) { - T ip = ip_line [j]; - if (C::cmp(simi[0], ip)) { - heap_swap_top (k, simi, idxi, ip, id_line [j]); - } - } - } -} - -template -void HeapArray::per_line_extrema ( - T * out_val, - TI * out_ids) const -{ -#pragma omp parallel for - for (size_t j = 0; j < nh; j++) { - int64_t imin = -1; - typename C::T xval = C::Crev::neutral (); - const typename C::T * x_ = val + j * k; - for (size_t i = 0; i < k; i++) - if (C::cmp (x_[i], xval)) { - xval = x_[i]; - imin = i; - } - if (out_val) - out_val[j] = xval; - - if (out_ids) { - if (ids && imin != -1) - out_ids[j] = ids [j * k + imin]; - else - out_ids[j] = imin; - } - } -} - - - - -// explicit instanciations - -template struct HeapArray >; -template struct HeapArray >; -template struct HeapArray >; -template struct HeapArray >; - - -} // END namespace fasis diff --git a/core/src/index/thirdparty/faiss/utils/Heap.h b/core/src/index/thirdparty/faiss/utils/Heap.h deleted file mode 100644 index 9962cbc112..0000000000 --- a/core/src/index/thirdparty/faiss/utils/Heap.h +++ /dev/null @@ -1,543 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -/* - * C++ support for heaps. The set of functions is tailored for - * efficient similarity search. - * - * There is no specific object for a heap, and the functions that - * operate on a signle heap are inlined, because heaps are often - * small. More complex functions are implemented in Heaps.cpp - * - */ - - -#ifndef FAISS_Heap_h -#define FAISS_Heap_h - -#include -#include -#include - -#include -#include -#include - -#include - - -namespace faiss { - -/******************************************************************* - * C object: uniform handling of min and max heap - *******************************************************************/ - -/** The C object gives the type T of the values in the heap, the type - * of the keys, TI and the comparison that is done: > for the minheap - * and < for the maxheap. The neutral value will always be dropped in - * favor of any other value in the heap. - */ - -template -struct CMax; - -// traits of minheaps = heaps where the minimum value is stored on top -// useful to find the *max* values of an array -template -struct CMin { - typedef T_ T; - typedef TI_ TI; - typedef CMax Crev; - inline static bool cmp (T a, T b) { - return a < b; - } - // value that will be popped first -> must be smaller than all others - // for int types this is not strictly the smallest val (-max - 1) - inline static T neutral () { - return -std::numeric_limits::max(); - } -}; - - -template -struct CMax { - typedef T_ T; - typedef TI_ TI; - typedef CMin Crev; - inline static bool cmp (T a, T b) { - return a > b; - } - inline static T neutral () { - return std::numeric_limits::max(); - } -}; - - -/******************************************************************* - * Basic heap ops: push and pop - *******************************************************************/ - -/** Pops the top element from the heap defined by bh_val[0..k-1] and - * bh_ids[0..k-1]. on output the element at k-1 is undefined. - */ -template inline -void heap_swap_top (size_t k, - typename C::T * bh_val, typename C::TI * bh_ids, - typename C::T val, typename C::TI ids) -{ - bh_val--; /* Use 1-based indexing for easier node->child translation */ - bh_ids--; - size_t i = 1, i1, i2; - while (1) { - i1 = i << 1; - i2 = i1 + 1; - if (i1 > k) - break; - if (i2 == k + 1 || C::cmp(bh_val[i1], bh_val[i2])) { - if (C::cmp(val, bh_val[i1])) - break; - bh_val[i] = bh_val[i1]; - bh_ids[i] = bh_ids[i1]; - i = i1; - } - else { - if (C::cmp(val, bh_val[i2])) - break; - bh_val[i] = bh_val[i2]; - bh_ids[i] = bh_ids[i2]; - i = i2; - } - } - bh_val[i] = val; - bh_ids[i] = ids; -} - - -/** Pops the top element from the heap defined by bh_val[0..k-1] and - * bh_ids[0..k-1]. on output the element at k-1 is undefined. - */ -template inline -void heap_pop (size_t k, typename C::T * bh_val, typename C::TI * bh_ids) -{ - bh_val--; /* Use 1-based indexing for easier node->child translation */ - bh_ids--; - typename C::T val = bh_val[k]; - size_t i = 1, i1, i2; - while (1) { - i1 = i << 1; - i2 = i1 + 1; - if (i1 > k) - break; - if (i2 == k + 1 || C::cmp(bh_val[i1], bh_val[i2])) { - if (C::cmp(val, bh_val[i1])) - break; - bh_val[i] = bh_val[i1]; - bh_ids[i] = bh_ids[i1]; - i = i1; - } - else { - if (C::cmp(val, bh_val[i2])) - break; - bh_val[i] = bh_val[i2]; - bh_ids[i] = bh_ids[i2]; - i = i2; - } - } - bh_val[i] = bh_val[k]; - bh_ids[i] = bh_ids[k]; -} - - - -/** Pushes the element (val, ids) into the heap bh_val[0..k-2] and - * bh_ids[0..k-2]. on output the element at k-1 is defined. - */ -template inline -void heap_push (size_t k, - typename C::T * bh_val, typename C::TI * bh_ids, - typename C::T val, typename C::TI ids) -{ - bh_val--; /* Use 1-based indexing for easier node->child translation */ - bh_ids--; - size_t i = k, i_father; - while (i > 1) { - i_father = i >> 1; - if (!C::cmp (val, bh_val[i_father])) /* the heap structure is ok */ - break; - bh_val[i] = bh_val[i_father]; - bh_ids[i] = bh_ids[i_father]; - i = i_father; - } - bh_val[i] = val; - bh_ids[i] = ids; -} - - - -/* Partial instanciation for heaps with TI = int64_t */ - -template inline -void minheap_swap_top (size_t k, T * bh_val, int64_t * bh_ids, T val, int64_t ids) -{ - heap_swap_top > (k, bh_val, bh_ids, val, ids); -} - - -template inline -void minheap_pop (size_t k, T * bh_val, int64_t * bh_ids) -{ - heap_pop > (k, bh_val, bh_ids); -} - - -template inline -void minheap_push (size_t k, T * bh_val, int64_t * bh_ids, T val, int64_t ids) -{ - heap_push > (k, bh_val, bh_ids, val, ids); -} - - -template inline -void maxheap_swap_top (size_t k, T * bh_val, int64_t * bh_ids, T val, int64_t ids) -{ - heap_swap_top > (k, bh_val, bh_ids, val, ids); -} - - -template inline -void maxheap_pop (size_t k, T * bh_val, int64_t * bh_ids) -{ - heap_pop > (k, bh_val, bh_ids); -} - - -template inline -void maxheap_push (size_t k, T * bh_val, int64_t * bh_ids, T val, int64_t ids) -{ - heap_push > (k, bh_val, bh_ids, val, ids); -} - - - -/******************************************************************* - * Heap initialization - *******************************************************************/ - -/* Initialization phase for the heap (with unconditionnal pushes). - * Store k0 elements in a heap containing up to k values. Note that - * (bh_val, bh_ids) can be the same as (x, ids) */ -template inline -void heap_heapify ( - size_t k, - typename C::T * bh_val, - typename C::TI * bh_ids, - const typename C::T * x = nullptr, - const typename C::TI * ids = nullptr, - size_t k0 = 0) -{ - if (k0 > 0) assert (x); - - if (ids) { - for (size_t i = 0; i < k0; i++) - heap_push (i+1, bh_val, bh_ids, x[i], ids[i]); - } else { - for (size_t i = 0; i < k0; i++) - heap_push (i+1, bh_val, bh_ids, x[i], i); - } - - for (size_t i = k0; i < k; i++) { - bh_val[i] = C::neutral(); - bh_ids[i] = -1; - } - -} - -template inline -void minheap_heapify ( - size_t k, T * bh_val, - int64_t * bh_ids, - const T * x = nullptr, - const int64_t * ids = nullptr, - size_t k0 = 0) -{ - heap_heapify< CMin > (k, bh_val, bh_ids, x, ids, k0); -} - - -template inline -void maxheap_heapify ( - size_t k, - T * bh_val, - int64_t * bh_ids, - const T * x = nullptr, - const int64_t * ids = nullptr, - size_t k0 = 0) -{ - heap_heapify< CMax > (k, bh_val, bh_ids, x, ids, k0); -} - - - -/******************************************************************* - * Add n elements to the heap - *******************************************************************/ - - -/* Add some elements to the heap */ -template inline -void heap_addn (size_t k, - typename C::T * bh_val, typename C::TI * bh_ids, - const typename C::T * x, - const typename C::TI * ids, - size_t n) -{ - size_t i; - if (ids) - for (i = 0; i < n; i++) { - if (C::cmp (bh_val[0], x[i])) { - heap_swap_top (k, bh_val, bh_ids, x[i], ids[i]); - } - } - else - for (i = 0; i < n; i++) { - if (C::cmp (bh_val[0], x[i])) { - heap_swap_top (k, bh_val, bh_ids, x[i], i); - } - } -} - - -/* Partial instanciation for heaps with TI = int64_t */ - -template inline -void minheap_addn (size_t k, T * bh_val, int64_t * bh_ids, - const T * x, const int64_t * ids, size_t n) -{ - heap_addn > (k, bh_val, bh_ids, x, ids, n); -} - -template inline -void maxheap_addn (size_t k, T * bh_val, int64_t * bh_ids, - const T * x, const int64_t * ids, size_t n) -{ - heap_addn > (k, bh_val, bh_ids, x, ids, n); -} - - - - - - -/******************************************************************* - * Heap finalization (reorder elements) - *******************************************************************/ - - -/* This function maps a binary heap into an sorted structure. - It returns the number */ -template inline -size_t heap_reorder (size_t k, typename C::T * bh_val, typename C::TI * bh_ids) -{ - size_t i, ii; - - for (i = 0, ii = 0; i < k; i++) { - /* top element should be put at the end of the list */ - typename C::T val = bh_val[0]; - typename C::TI id = bh_ids[0]; - - /* boundary case: we will over-ride this value if not a true element */ - heap_pop (k-i, bh_val, bh_ids); - bh_val[k-ii-1] = val; - bh_ids[k-ii-1] = id; - if (id != -1) ii++; - } - /* Count the number of elements which are effectively returned */ - size_t nel = ii; - - memmove (bh_val, bh_val+k-ii, ii * sizeof(*bh_val)); - memmove (bh_ids, bh_ids+k-ii, ii * sizeof(*bh_ids)); - - for (; ii < k; ii++) { - bh_val[ii] = C::neutral(); - bh_ids[ii] = -1; - } - return nel; -} - -template inline -size_t minheap_reorder (size_t k, T * bh_val, int64_t * bh_ids) -{ - return heap_reorder< CMin > (k, bh_val, bh_ids); -} - -template inline -size_t maxheap_reorder (size_t k, T * bh_val, int64_t * bh_ids) -{ - return heap_reorder< CMax > (k, bh_val, bh_ids); -} - - - - - -/******************************************************************* - * Operations on heap arrays - *******************************************************************/ - -/** a template structure for a set of [min|max]-heaps it is tailored - * so that the actual data of the heaps can just live in compact - * arrays. - */ -template -struct HeapArray { - typedef typename C::TI TI; - typedef typename C::T T; - - size_t nh; ///< number of heaps - size_t k; ///< allocated size per heap - TI * ids; ///< identifiers (size nh * k) - T * val; ///< values (distances or similarities), size nh * k - - /// Return the list of values for a heap - T * get_val (size_t key) { return val + key * k; } - - /// Correspponding identifiers - TI * get_ids (size_t key) { return ids + key * k; } - - /// prepare all the heaps before adding - void heapify (); - - /** add nj elements to heaps i0:i0+ni, with sequential ids - * - * @param nj nb of elements to add to each heap - * @param vin elements to add, size ni * nj - * @param j0 add this to the ids that are added - * @param i0 first heap to update - * @param ni nb of elements to update (-1 = use nh) - */ - void addn (size_t nj, const T *vin, TI j0 = 0, - size_t i0 = 0, int64_t ni = -1); - - /** same as addn - * - * @param id_in ids of the elements to add, size ni * nj - * @param id_stride stride for id_in - */ - void addn_with_ids ( - size_t nj, const T *vin, const TI *id_in = nullptr, - int64_t id_stride = 0, size_t i0 = 0, int64_t ni = -1); - - /// reorder all the heaps - void reorder (); - - /** this is not really a heap function. It just finds the per-line - * extrema of each line of array D - * @param vals_out extreme value of each line (size nh, or NULL) - * @param idx_out index of extreme value (size nh or NULL) - */ - void per_line_extrema (T *vals_out, TI *idx_out) const; - -}; - - -/* Define useful heaps */ -typedef HeapArray > float_minheap_array_t; -typedef HeapArray > int_minheap_array_t; - -typedef HeapArray > float_maxheap_array_t; -typedef HeapArray > int_maxheap_array_t; - -// The heap templates are instanciated explicitly in Heap.cpp - - - - - - - - - - - - - - - - - - - -/********************************************************************* - * Indirect heaps: instead of having - * - * node i = (bh_ids[i], bh_val[i]), - * - * in indirect heaps, - * - * node i = (bh_ids[i], bh_val[bh_ids[i]]), - * - *********************************************************************/ - - -template -inline -void indirect_heap_pop ( - size_t k, - const typename C::T * bh_val, - typename C::TI * bh_ids) -{ - bh_ids--; /* Use 1-based indexing for easier node->child translation */ - typename C::T val = bh_val[bh_ids[k]]; - size_t i = 1; - while (1) { - size_t i1 = i << 1; - size_t i2 = i1 + 1; - if (i1 > k) - break; - typename C::TI id1 = bh_ids[i1], id2 = bh_ids[i2]; - if (i2 == k + 1 || C::cmp(bh_val[id1], bh_val[id2])) { - if (C::cmp(val, bh_val[id1])) - break; - bh_ids[i] = id1; - i = i1; - } else { - if (C::cmp(val, bh_val[id2])) - break; - bh_ids[i] = id2; - i = i2; - } - } - bh_ids[i] = bh_ids[k]; -} - - - -template -inline -void indirect_heap_push (size_t k, - const typename C::T * bh_val, typename C::TI * bh_ids, - typename C::TI id) -{ - bh_ids--; /* Use 1-based indexing for easier node->child translation */ - typename C::T val = bh_val[id]; - size_t i = k; - while (i > 1) { - size_t i_father = i >> 1; - if (!C::cmp (val, bh_val[bh_ids[i_father]])) - break; - bh_ids[i] = bh_ids[i_father]; - i = i_father; - } - bh_ids[i] = id; -} - - -} // namespace faiss - -#endif /* FAISS_Heap_h */ diff --git a/core/src/index/thirdparty/faiss/utils/WorkerThread.cpp b/core/src/index/thirdparty/faiss/utils/WorkerThread.cpp deleted file mode 100644 index 83b5c97e47..0000000000 --- a/core/src/index/thirdparty/faiss/utils/WorkerThread.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#include -#include -#include - -namespace faiss { - -namespace { - -// Captures any exceptions thrown by the lambda and returns them via the promise -void runCallback(std::function& fn, - std::promise& promise) { - try { - fn(); - promise.set_value(true); - } catch (...) { - promise.set_exception(std::current_exception()); - } -} - -} // namespace - -WorkerThread::WorkerThread() : - wantStop_(false) { - startThread(); - - // Make sure that the thread has started before continuing - add([](){}).get(); -} - -WorkerThread::~WorkerThread() { - stop(); - waitForThreadExit(); -} - -void -WorkerThread::startThread() { - thread_ = std::thread([this](){ threadMain(); }); -} - -void -WorkerThread::stop() { - std::lock_guard guard(mutex_); - - wantStop_ = true; - monitor_.notify_one(); -} - -std::future -WorkerThread::add(std::function f) { - std::lock_guard guard(mutex_); - - if (wantStop_) { - // The timer thread has been stopped, or we want to stop; we can't - // schedule anything else - std::promise p; - auto fut = p.get_future(); - - // did not execute - p.set_value(false); - return fut; - } - - auto pr = std::promise(); - auto fut = pr.get_future(); - - queue_.emplace_back(std::make_pair(std::move(f), std::move(pr))); - - // Wake up our thread - monitor_.notify_one(); - return fut; -} - -void -WorkerThread::threadMain() { - threadLoop(); - - // Call all pending tasks - FAISS_ASSERT(wantStop_); - - // flush all pending operations - for (auto& f : queue_) { - runCallback(f.first, f.second); - } -} - -void -WorkerThread::threadLoop() { - while (true) { - std::pair, std::promise> data; - - { - std::unique_lock lock(mutex_); - - while (!wantStop_ && queue_.empty()) { - monitor_.wait(lock); - } - - if (wantStop_) { - return; - } - - data = std::move(queue_.front()); - queue_.pop_front(); - } - - runCallback(data.first, data.second); - } -} - -void -WorkerThread::waitForThreadExit() { - try { - thread_.join(); - } catch (...) { - } -} - -} // namespace diff --git a/core/src/index/thirdparty/faiss/utils/WorkerThread.h b/core/src/index/thirdparty/faiss/utils/WorkerThread.h deleted file mode 100644 index 7ab21e9f90..0000000000 --- a/core/src/index/thirdparty/faiss/utils/WorkerThread.h +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - -#pragma once - -#include -#include -#include -#include - -namespace faiss { - -class WorkerThread { - public: - WorkerThread(); - - /// Stops and waits for the worker thread to exit, flushing all - /// pending lambdas - ~WorkerThread(); - - /// Request that the worker thread stop itself - void stop(); - - /// Blocking waits in the current thread for the worker thread to - /// stop - void waitForThreadExit(); - - /// Adds a lambda to run on the worker thread; returns a future that - /// can be used to block on its completion. - /// Future status is `true` if the lambda was run in the worker - /// thread; `false` if it was not run, because the worker thread is - /// exiting or has exited. - std::future add(std::function f); - - private: - void startThread(); - void threadMain(); - void threadLoop(); - - /// Thread that all queued lambdas are run on - std::thread thread_; - - /// Mutex for the queue and exit status - std::mutex mutex_; - - /// Monitor for the exit status and the queue - std::condition_variable monitor_; - - /// Whether or not we want the thread to exit - bool wantStop_; - - /// Queue of pending lambdas to call - std::deque, std::promise>> queue_; -}; - -} // namespace diff --git a/core/src/index/thirdparty/faiss/utils/distances.cpp b/core/src/index/thirdparty/faiss/utils/distances.cpp deleted file mode 100644 index e97e873614..0000000000 --- a/core/src/index/thirdparty/faiss/utils/distances.cpp +++ /dev/null @@ -1,1073 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - - -#ifndef FINTEGER -#define FINTEGER long -#endif - - -extern "C" { - -/* declare BLAS functions, see http://www.netlib.org/clapack/cblas/ */ - -int sgemm_ (const char *transa, const char *transb, FINTEGER *m, FINTEGER * - n, FINTEGER *k, const float *alpha, const float *a, - FINTEGER *lda, const float *b, FINTEGER * - ldb, float *beta, float *c, FINTEGER *ldc); - -/* Lapack functions, see http://www.netlib.org/clapack/old/single/sgeqrf.c */ - -int sgeqrf_ (FINTEGER *m, FINTEGER *n, float *a, FINTEGER *lda, - float *tau, float *work, FINTEGER *lwork, FINTEGER *info); - -int sgemv_(const char *trans, FINTEGER *m, FINTEGER *n, float *alpha, - const float *a, FINTEGER *lda, const float *x, FINTEGER *incx, - float *beta, float *y, FINTEGER *incy); - -} - - -namespace faiss { - - -/*************************************************************************** - * Matrix/vector ops - ***************************************************************************/ - - - -/* Compute the inner product between a vector x and - a set of ny vectors y. - These functions are not intended to replace BLAS matrix-matrix, as they - would be significantly less efficient in this case. */ -void fvec_inner_products_ny (float * ip, - const float * x, - const float * y, - size_t d, size_t ny) -{ - // Not sure which one is fastest -#if 0 - { - FINTEGER di = d; - FINTEGER nyi = ny; - float one = 1.0, zero = 0.0; - FINTEGER onei = 1; - sgemv_ ("T", &di, &nyi, &one, y, &di, x, &onei, &zero, ip, &onei); - } -#endif - for (size_t i = 0; i < ny; i++) { - ip[i] = fvec_inner_product (x, y, d); - y += d; - } -} - - - - - -/* Compute the L2 norm of a set of nx vectors */ -void fvec_norms_L2 (float * __restrict nr, - const float * __restrict x, - size_t d, size_t nx) -{ - -#pragma omp parallel for - for (size_t i = 0; i < nx; i++) { - nr[i] = sqrtf (fvec_norm_L2sqr (x + i * d, d)); - } -} - -void fvec_norms_L2sqr (float * __restrict nr, - const float * __restrict x, - size_t d, size_t nx) -{ -#pragma omp parallel for - for (size_t i = 0; i < nx; i++) - nr[i] = fvec_norm_L2sqr (x + i * d, d); -} - - - -void fvec_renorm_L2 (size_t d, size_t nx, float * __restrict x) -{ -#pragma omp parallel for - for (size_t i = 0; i < nx; i++) { - float * __restrict xi = x + i * d; - - float nr = fvec_norm_L2sqr (xi, d); - - if (nr > 0) { - size_t j; - const float inv_nr = 1.0 / sqrtf (nr); - for (j = 0; j < d; j++) - xi[j] *= inv_nr; - } - } -} - - - - - - - -/*************************************************************************** - * KNN functions - ***************************************************************************/ - -int parallel_policy_threshold = 65535; - -/* Find the nearest neighbors for nx queries in a set of ny vectors */ -static void knn_inner_product_sse (const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - float_minheap_array_t * res, - ConcurrentBitsetPtr bitset = nullptr) -{ - size_t k = res->k; - size_t thread_max_num = omp_get_max_threads(); - - if (ny > parallel_policy_threshold || (nx < thread_max_num / 2 && ny >= thread_max_num * 32)) { - size_t block_x = std::min( - get_L3_Size() / (d * sizeof(float) + thread_max_num * k * (sizeof(float) + sizeof(int64_t))), - nx); - - size_t all_heap_size = block_x * k * thread_max_num; - float *value = new float[all_heap_size]; - int64_t *labels = new int64_t[all_heap_size]; - - for (size_t x_from = 0, x_to; x_from < nx; x_from = x_to) { - x_to = std::min(nx, x_from + block_x); - int size = x_to - x_from; - int thread_heap_size = size * k; - - // init heap - for (size_t i = 0; i < all_heap_size; i++) { - value[i] = -1.0 / 0.0; - labels[i] = -1; - } - -#pragma omp parallel for schedule(static) - for (size_t j = 0; j < ny; j++) { - if(!bitset || !bitset->test(j)) { - size_t thread_no = omp_get_thread_num(); - const float *y_j = y + j * d; - const float *x_i = x + x_from * d; - for (size_t i = 0; i < size; i++) { - float disij = fvec_inner_product (x_i, y_j, d); - float * val_ = value + thread_no * thread_heap_size + i * k; - int64_t * ids_ = labels + thread_no * thread_heap_size + i * k; - if (disij > val_[0]) { - minheap_swap_top (k, val_, ids_, disij, j); - } - x_i += d; - } - } - } - - // merge heap - for (size_t t = 1; t < thread_max_num; t++) { - for (size_t i = 0; i < size; i++) { - float * __restrict value_x = value + i * k; - int64_t * __restrict labels_x = labels + i * k; - float *value_x_t = value_x + t * thread_heap_size; - int64_t *labels_x_t = labels_x + t * thread_heap_size; - for (size_t j = 0; j < k; j++) { - if (value_x_t[j] > value_x[0]) { - minheap_swap_top (k, value_x, labels_x, value_x_t[j], labels_x_t[j]); - } - } - } - } - - // sort - for (size_t i = 0; i < size; i++) { - float * value_x = value + i * k; - int64_t * labels_x = labels + i * k; - minheap_reorder (k, value_x, labels_x); - } - - // copy result - memcpy(res->val + x_from * k, value, thread_heap_size * sizeof(float)); - memcpy(res->ids + x_from * k, labels, thread_heap_size * sizeof(int64_t)); - } - delete[] value; - delete[] labels; - - } else { - float * value = res->val; - int64_t * labels = res->ids; - -#pragma omp parallel for - for (size_t i = 0; i < nx; i++) { - const float *x_i = x + i * d; - const float *y_j = y; - - float * __restrict val_ = value + i * k; - int64_t * __restrict ids_ = labels + i * k; - - for (size_t j = 0; j < k; j++) { - val_[j] = -1.0 / 0.0; - ids_[j] = -1; - } - - for (size_t j = 0; j < ny; j++) { - if (!bitset || !bitset->test(j)) { - float disij = fvec_inner_product (x_i, y_j, d); - if (disij > val_[0]) { - minheap_swap_top (k, val_, ids_, disij, j); - } - } - y_j += d; - } - - minheap_reorder (k, val_, ids_); - } - } -} - -static void knn_L2sqr_sse ( - const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - float_maxheap_array_t * res, - ConcurrentBitsetPtr bitset = nullptr) -{ - size_t k = res->k; - size_t thread_max_num = omp_get_max_threads(); - - if (ny > parallel_policy_threshold || (nx < thread_max_num / 2 && ny >= thread_max_num * 32)) { - size_t block_x = std::min( - get_L3_Size() / (d * sizeof(float) + thread_max_num * k * (sizeof(float) + sizeof(int64_t))), - nx); - - size_t all_heap_size = block_x * k * thread_max_num; - float *value = new float[all_heap_size]; - int64_t *labels = new int64_t[all_heap_size]; - - for (size_t x_from = 0, x_to; x_from < nx; x_from = x_to) { - x_to = std::min(nx, x_from + block_x); - int size = x_to - x_from; - int thread_heap_size = size * k; - - // init heap - for (size_t i = 0; i < all_heap_size; i++) { - value[i] = 1.0 / 0.0; - labels[i] = -1; - } - -#pragma omp parallel for schedule(static) - for (size_t j = 0; j < ny; j++) { - if(!bitset || !bitset->test(j)) { - size_t thread_no = omp_get_thread_num(); - const float *y_j = y + j * d; - const float *x_i = x + x_from * d; - for (size_t i = 0; i < size; i++) { - float disij = fvec_L2sqr (x_i, y_j, d); - float * val_ = value + thread_no * thread_heap_size + i * k; - int64_t * ids_ = labels + thread_no * thread_heap_size + i * k; - if (disij < val_[0]) { - maxheap_swap_top (k, val_, ids_, disij, j); - } - x_i += d; - } - } - } - - // merge heap - for (size_t t = 1; t < thread_max_num; t++) { - for (size_t i = 0; i < size; i++) { - float * __restrict value_x = value + i * k; - int64_t * __restrict labels_x = labels + i * k; - float *value_x_t = value_x + t * thread_heap_size; - int64_t *labels_x_t = labels_x + t * thread_heap_size; - for (size_t j = 0; j < k; j++) { - if (value_x_t[j] < value_x[0]) { - maxheap_swap_top (k, value_x, labels_x, value_x_t[j], labels_x_t[j]); - } - } - } - } - - // sort - for (size_t i = 0; i < size; i++) { - float * value_x = value + i * k; - int64_t * labels_x = labels + i * k; - maxheap_reorder (k, value_x, labels_x); - } - - // copy result - memcpy(res->val + x_from * k, value, thread_heap_size * sizeof(float)); - memcpy(res->ids + x_from * k, labels, thread_heap_size * sizeof(int64_t)); - } - delete[] value; - delete[] labels; - - } else { - - float * value = res->val; - int64_t * labels = res->ids; - -#pragma omp parallel for - for (size_t i = 0; i < nx; i++) { - const float *x_i = x + i * d; - const float *y_j = y; - - float * __restrict val_ = value + i * k; - int64_t * __restrict ids_ = labels + i * k; - - for (size_t j = 0; j < k; j++) { - val_[j] = 1.0 / 0.0; - ids_[j] = -1; - } - - for (size_t j = 0; j < ny; j++) { - if (!bitset || !bitset->test(j)) { - float disij = fvec_L2sqr (x_i, y_j, d); - if (disij < val_[0]) { - maxheap_swap_top (k, val_, ids_, disij, j); - } - } - y_j += d; - } - - maxheap_reorder (k, val_, ids_); - } - } -} - -/** Find the nearest neighbors for nx queries in a set of ny vectors */ -static void knn_inner_product_blas ( - const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - float_minheap_array_t * res, - ConcurrentBitsetPtr bitset = nullptr) -{ - res->heapify (); - - // BLAS does not like empty matrices - if (nx == 0 || ny == 0) return; - - size_t k = res->k; - - /* block sizes */ - const size_t bs_x = 4096, bs_y = 1024; - // const size_t bs_x = 16, bs_y = 16; - float *ip_block = new float[bs_x * bs_y]; - ScopeDeleter del1(ip_block);; - - for (size_t i0 = 0; i0 < nx; i0 += bs_x) { - size_t i1 = i0 + bs_x; - if(i1 > nx) i1 = nx; - - for (size_t j0 = 0; j0 < ny; j0 += bs_y) { - size_t j1 = j0 + bs_y; - if (j1 > ny) j1 = ny; - /* compute the actual dot products */ - { - float one = 1, zero = 0; - FINTEGER nyi = j1 - j0, nxi = i1 - i0, di = d; - sgemm_ ("Transpose", "Not transpose", &nyi, &nxi, &di, &one, - y + j0 * d, &di, - x + i0 * d, &di, &zero, - ip_block, &nyi); - } - - /* collect maxima */ -#pragma omp parallel for - for(size_t i = i0; i < i1; i++){ - float * __restrict simi = res->get_val(i); - int64_t * __restrict idxi = res->get_ids (i); - const float *ip_line = ip_block + (i - i0) * (j1 - j0); - - for(size_t j = j0; j < j1; j++){ - if(!bitset || !bitset->test(j)){ - float dis = *ip_line; - - if(dis > simi[0]){ - minheap_swap_top(k, simi, idxi, dis, j); - } - } - ip_line++; - } - } - } - InterruptCallback::check (); - } - res->reorder (); -} - -// distance correction is an operator that can be applied to transform -// the distances -template -static void knn_L2sqr_blas (const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - float_maxheap_array_t * res, - const DistanceCorrection &corr, - ConcurrentBitsetPtr bitset = nullptr) -{ - res->heapify (); - - // BLAS does not like empty matrices - if (nx == 0 || ny == 0) return; - - size_t k = res->k; - - /* block sizes */ - const size_t bs_x = 4096, bs_y = 1024; - // const size_t bs_x = 16, bs_y = 16; - float *ip_block = new float[bs_x * bs_y]; - float *x_norms = new float[nx]; - float *y_norms = new float[ny]; - ScopeDeleter del1(ip_block), del3(x_norms), del2(y_norms); - - fvec_norms_L2sqr (x_norms, x, d, nx); - fvec_norms_L2sqr (y_norms, y, d, ny); - - - for (size_t i0 = 0; i0 < nx; i0 += bs_x) { - size_t i1 = i0 + bs_x; - if(i1 > nx) i1 = nx; - - for (size_t j0 = 0; j0 < ny; j0 += bs_y) { - size_t j1 = j0 + bs_y; - if (j1 > ny) j1 = ny; - /* compute the actual dot products */ - { - float one = 1, zero = 0; - FINTEGER nyi = j1 - j0, nxi = i1 - i0, di = d; - sgemm_ ("Transpose", "Not transpose", &nyi, &nxi, &di, &one, - y + j0 * d, &di, - x + i0 * d, &di, &zero, - ip_block, &nyi); - } - - /* collect minima */ -#pragma omp parallel for - for (size_t i = i0; i < i1; i++) { - float * __restrict simi = res->get_val(i); - int64_t * __restrict idxi = res->get_ids (i); - const float *ip_line = ip_block + (i - i0) * (j1 - j0); - - for (size_t j = j0; j < j1; j++) { - if(!bitset || !bitset->test(j)){ - float ip = *ip_line; - float dis = x_norms[i] + y_norms[j] - 2 * ip; - - // negative values can occur for identical vectors - // due to roundoff errors - if (dis < 0) dis = 0; - - dis = corr (dis, i, j); - - if (dis < simi[0]) { - maxheap_swap_top (k, simi, idxi, dis, j); - } - } - ip_line++; - } - } - } - InterruptCallback::check (); - } - res->reorder (); - -} - -template -static void knn_jaccard_blas (const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - float_maxheap_array_t * res, - const DistanceCorrection &corr, - ConcurrentBitsetPtr bitset = nullptr) -{ - res->heapify (); - - // BLAS does not like empty matrices - if (nx == 0 || ny == 0) return; - - size_t k = res->k; - - /* block sizes */ - const size_t bs_x = 4096, bs_y = 1024; - // const size_t bs_x = 16, bs_y = 16; - float *ip_block = new float[bs_x * bs_y]; - float *x_norms = new float[nx]; - float *y_norms = new float[ny]; - ScopeDeleter del1(ip_block), del3(x_norms), del2(y_norms); - - fvec_norms_L2sqr (x_norms, x, d, nx); - fvec_norms_L2sqr (y_norms, y, d, ny); - - - for (size_t i0 = 0; i0 < nx; i0 += bs_x) { - size_t i1 = i0 + bs_x; - if(i1 > nx) i1 = nx; - - for (size_t j0 = 0; j0 < ny; j0 += bs_y) { - size_t j1 = j0 + bs_y; - if (j1 > ny) j1 = ny; - /* compute the actual dot products */ - { - float one = 1, zero = 0; - FINTEGER nyi = j1 - j0, nxi = i1 - i0, di = d; - sgemm_ ("Transpose", "Not transpose", &nyi, &nxi, &di, &one, - y + j0 * d, &di, - x + i0 * d, &di, &zero, - ip_block, &nyi); - } - - /* collect minima */ -#pragma omp parallel for - for (size_t i = i0; i < i1; i++) { - float * __restrict simi = res->get_val(i); - int64_t * __restrict idxi = res->get_ids (i); - const float *ip_line = ip_block + (i - i0) * (j1 - j0); - - for (size_t j = j0; j < j1; j++) { - if(!bitset || !bitset->test(j)){ - float ip = *ip_line; - float dis = 1.0 - ip / (x_norms[i] + y_norms[j] - ip); - - // negative values can occur for identical vectors - // due to roundoff errors - if (dis < 0) dis = 0; - - dis = corr (dis, i, j); - - if (dis < simi[0]) { - maxheap_swap_top (k, simi, idxi, dis, j); - } - } - ip_line++; - } - } - } - InterruptCallback::check (); - } - res->reorder (); -} - - - - - - - -/******************************************************* - * KNN driver functions - *******************************************************/ - -int distance_compute_blas_threshold = 20; - -void knn_inner_product (const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - float_minheap_array_t * res, - ConcurrentBitsetPtr bitset) -{ - if (nx < distance_compute_blas_threshold) { - knn_inner_product_sse (x, y, d, nx, ny, res, bitset); - } else { - knn_inner_product_blas (x, y, d, nx, ny, res, bitset); - } -} - - - -struct NopDistanceCorrection { - float operator()(float dis, size_t /*qno*/, size_t /*bno*/) const { - return dis; - } -}; - -void knn_L2sqr (const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - float_maxheap_array_t * res, - ConcurrentBitsetPtr bitset) -{ - if (nx < distance_compute_blas_threshold) { - knn_L2sqr_sse (x, y, d, nx, ny, res, bitset); - } else { - NopDistanceCorrection nop; - knn_L2sqr_blas (x, y, d, nx, ny, res, nop, bitset); - } -} - -void knn_jaccard (const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - float_maxheap_array_t * res, - ConcurrentBitsetPtr bitset) -{ - if (d % 4 == 0 && nx < distance_compute_blas_threshold) { -// knn_jaccard_sse (x, y, d, nx, ny, res); - printf("jaccard sse not implemented!\n"); - } else { - NopDistanceCorrection nop; - knn_jaccard_blas (x, y, d, nx, ny, res, nop, bitset); - } -} - -struct BaseShiftDistanceCorrection { - const float *base_shift; - float operator()(float dis, size_t /*qno*/, size_t bno) const { - return dis - base_shift[bno]; - } -}; - -void knn_L2sqr_base_shift ( - const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - float_maxheap_array_t * res, - const float *base_shift) -{ - BaseShiftDistanceCorrection corr = {base_shift}; - knn_L2sqr_blas (x, y, d, nx, ny, res, corr); -} - - - -/*************************************************************************** - * compute a subset of distances - ***************************************************************************/ - -/* compute the inner product between x and a subset y of ny vectors, - whose indices are given by idy. */ -void fvec_inner_products_by_idx (float * __restrict ip, - const float * x, - const float * y, - const int64_t * __restrict ids, /* for y vecs */ - size_t d, size_t nx, size_t ny) -{ -#pragma omp parallel for - for (size_t j = 0; j < nx; j++) { - const int64_t * __restrict idsj = ids + j * ny; - const float * xj = x + j * d; - float * __restrict ipj = ip + j * ny; - for (size_t i = 0; i < ny; i++) { - if (idsj[i] < 0) - continue; - ipj[i] = fvec_inner_product (xj, y + d * idsj[i], d); - } - } -} - - - -/* compute the inner product between x and a subset y of ny vectors, - whose indices are given by idy. */ -void fvec_L2sqr_by_idx (float * __restrict dis, - const float * x, - const float * y, - const int64_t * __restrict ids, /* ids of y vecs */ - size_t d, size_t nx, size_t ny) -{ -#pragma omp parallel for - for (size_t j = 0; j < nx; j++) { - const int64_t * __restrict idsj = ids + j * ny; - const float * xj = x + j * d; - float * __restrict disj = dis + j * ny; - for (size_t i = 0; i < ny; i++) { - if (idsj[i] < 0) - continue; - disj[i] = fvec_L2sqr (xj, y + d * idsj[i], d); - } - } -} - -void pairwise_indexed_L2sqr ( - size_t d, size_t n, - const float * x, const int64_t *ix, - const float * y, const int64_t *iy, - float *dis) -{ -#pragma omp parallel for - for (size_t j = 0; j < n; j++) { - if (ix[j] >= 0 && iy[j] >= 0) { - dis[j] = fvec_L2sqr (x + d * ix[j], y + d * iy[j], d); - } - } -} - -void pairwise_indexed_inner_product ( - size_t d, size_t n, - const float * x, const int64_t *ix, - const float * y, const int64_t *iy, - float *dis) -{ -#pragma omp parallel for - for (size_t j = 0; j < n; j++) { - if (ix[j] >= 0 && iy[j] >= 0) { - dis[j] = fvec_inner_product (x + d * ix[j], y + d * iy[j], d); - } - } -} - - -/* Find the nearest neighbors for nx queries in a set of ny vectors - indexed by ids. May be useful for re-ranking a pre-selected vector list */ -void knn_inner_products_by_idx (const float * x, - const float * y, - const int64_t * ids, - size_t d, size_t nx, size_t ny, - float_minheap_array_t * res) -{ - size_t k = res->k; - -#pragma omp parallel for - for (size_t i = 0; i < nx; i++) { - const float * x_ = x + i * d; - const int64_t * idsi = ids + i * ny; - size_t j; - float * __restrict simi = res->get_val(i); - int64_t * __restrict idxi = res->get_ids (i); - minheap_heapify (k, simi, idxi); - - for (j = 0; j < ny; j++) { - if (idsi[j] < 0) break; - float ip = fvec_inner_product (x_, y + d * idsi[j], d); - - if (ip > simi[0]) { - minheap_swap_top (k, simi, idxi, ip, idsi[j]); - } - } - minheap_reorder (k, simi, idxi); - } - -} - -void knn_L2sqr_by_idx (const float * x, - const float * y, - const int64_t * __restrict ids, - size_t d, size_t nx, size_t ny, - float_maxheap_array_t * res) -{ - size_t k = res->k; - -#pragma omp parallel for - for (size_t i = 0; i < nx; i++) { - const float * x_ = x + i * d; - const int64_t * __restrict idsi = ids + i * ny; - float * __restrict simi = res->get_val(i); - int64_t * __restrict idxi = res->get_ids (i); - maxheap_heapify (res->k, simi, idxi); - for (size_t j = 0; j < ny; j++) { - float disij = fvec_L2sqr (x_, y + d * idsi[j], d); - - if (disij < simi[0]) { - maxheap_swap_top (k, simi, idxi, disij, idsi[j]); - } - } - maxheap_reorder (res->k, simi, idxi); - } - -} - - - - - -/*************************************************************************** - * Range search - ***************************************************************************/ - -/** Find the nearest neighbors for nx queries in a set of ny vectors - * compute_l2 = compute pairwise squared L2 distance rather than inner prod - */ -template -static void range_search_blas ( - const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - float radius, - RangeSearchResult *result) -{ - - // BLAS does not like empty matrices - if (nx == 0 || ny == 0) return; - - /* block sizes */ - const size_t bs_x = 4096, bs_y = 1024; - // const size_t bs_x = 16, bs_y = 16; - float *ip_block = new float[bs_x * bs_y]; - ScopeDeleter del0(ip_block); - - float *x_norms = nullptr, *y_norms = nullptr; - ScopeDeleter del1, del2; - if (compute_l2) { - x_norms = new float[nx]; - del1.set (x_norms); - fvec_norms_L2sqr (x_norms, x, d, nx); - - y_norms = new float[ny]; - del2.set (y_norms); - fvec_norms_L2sqr (y_norms, y, d, ny); - } - - std::vector partial_results; - - for (size_t j0 = 0; j0 < ny; j0 += bs_y) { - size_t j1 = j0 + bs_y; - if (j1 > ny) j1 = ny; - RangeSearchPartialResult * pres = new RangeSearchPartialResult (result); - partial_results.push_back (pres); - - for (size_t i0 = 0; i0 < nx; i0 += bs_x) { - size_t i1 = i0 + bs_x; - if(i1 > nx) i1 = nx; - - /* compute the actual dot products */ - { - float one = 1, zero = 0; - FINTEGER nyi = j1 - j0, nxi = i1 - i0, di = d; - sgemm_ ("Transpose", "Not transpose", &nyi, &nxi, &di, &one, - y + j0 * d, &di, - x + i0 * d, &di, &zero, - ip_block, &nyi); - } - - - for (size_t i = i0; i < i1; i++) { - const float *ip_line = ip_block + (i - i0) * (j1 - j0); - - RangeQueryResult & qres = pres->new_result (i); - - for (size_t j = j0; j < j1; j++) { - float ip = *ip_line++; - if (compute_l2) { - float dis = x_norms[i] + y_norms[j] - 2 * ip; - if (dis < radius) { - qres.add (dis, j); - } - } else { - if (ip > radius) { - qres.add (ip, j); - } - } - } - } - } - InterruptCallback::check (); - } - - RangeSearchPartialResult::merge (partial_results); -} - - -template -static void range_search_sse (const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - float radius, - RangeSearchResult *res) -{ - -#pragma omp parallel - { - RangeSearchPartialResult pres (res); - -#pragma omp for - for (size_t i = 0; i < nx; i++) { - const float * x_ = x + i * d; - const float * y_ = y; - size_t j; - - RangeQueryResult & qres = pres.new_result (i); - - for (j = 0; j < ny; j++) { - if (compute_l2) { - float disij = fvec_L2sqr (x_, y_, d); - if (disij < radius) { - qres.add (disij, j); - } - } else { - float ip = fvec_inner_product (x_, y_, d); - if (ip > radius) { - qres.add (ip, j); - } - } - y_ += d; - } - - } - pres.finalize (); - } - - // check just at the end because the use case is typically just - // when the nb of queries is low. - InterruptCallback::check(); -} - - - - - -void range_search_L2sqr ( - const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - float radius, - RangeSearchResult *res) -{ - - if (nx < distance_compute_blas_threshold) { - range_search_sse (x, y, d, nx, ny, radius, res); - } else { - range_search_blas (x, y, d, nx, ny, radius, res); - } -} - -void range_search_inner_product ( - const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - float radius, - RangeSearchResult *res) -{ - - if (nx < distance_compute_blas_threshold) { - range_search_sse (x, y, d, nx, ny, radius, res); - } else { - range_search_blas (x, y, d, nx, ny, radius, res); - } -} - - -void pairwise_L2sqr (int64_t d, - int64_t nq, const float *xq, - int64_t nb, const float *xb, - float *dis, - int64_t ldq, int64_t ldb, int64_t ldd) -{ - if (nq == 0 || nb == 0) return; - if (ldq == -1) ldq = d; - if (ldb == -1) ldb = d; - if (ldd == -1) ldd = nb; - - // store in beginning of distance matrix to avoid malloc - float *b_norms = dis; - -#pragma omp parallel for - for (int64_t i = 0; i < nb; i++) - b_norms [i] = fvec_norm_L2sqr (xb + i * ldb, d); - -#pragma omp parallel for - for (int64_t i = 1; i < nq; i++) { - float q_norm = fvec_norm_L2sqr (xq + i * ldq, d); - for (int64_t j = 0; j < nb; j++) - dis[i * ldd + j] = q_norm + b_norms [j]; - } - - { - float q_norm = fvec_norm_L2sqr (xq, d); - for (int64_t j = 0; j < nb; j++) - dis[j] += q_norm; - } - - { - FINTEGER nbi = nb, nqi = nq, di = d, ldqi = ldq, ldbi = ldb, lddi = ldd; - float one = 1.0, minus_2 = -2.0; - - sgemm_ ("Transposed", "Not transposed", - &nbi, &nqi, &di, - &minus_2, - xb, &ldbi, - xq, &ldqi, - &one, dis, &lddi); - } - -} - -void elkan_L2_sse ( - const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - int64_t *ids, float *val) { - - if (nx == 0 || ny == 0) { - return; - } - - const size_t bs_y = 1024; - float *data = (float *) malloc((bs_y * (bs_y - 1) / 2) * sizeof (float)); - - for (size_t j0 = 0; j0 < ny; j0 += bs_y) { - BuilderSuspend::check_wait(); - - size_t j1 = j0 + bs_y; - if (j1 > ny) j1 = ny; - - auto Y = [&](size_t i, size_t j) -> float& { - assert(i != j); - i -= j0, j -= j0; - return (i > j) ? data[j + i * (i - 1) / 2] : data[i + j * (j - 1) / 2]; - }; - -#pragma omp parallel for - for (size_t i = j0 + 1; i < j1; i++) { - const float *y_i = y + i * d; - for (size_t j = j0; j < i; j++) { - const float *y_j = y + j * d; - Y(i, j) = sqrt(fvec_L2sqr(y_i, y_j, d)); - } - } - -#pragma omp parallel for - for (size_t i = 0; i < nx; i++) { - const float *x_i = x + i * d; - - int64_t ids_i = j0; - float val_i = sqrt(fvec_L2sqr(x_i, y + j0 * d, d)); - float val_i_2 = val_i * 2; - for (size_t j = j0 + 1; j < j1; j++) { - if (val_i_2 <= Y(ids_i, j)) { - continue; - } - const float *y_j = y + j * d; - float disij = sqrt(fvec_L2sqr(x_i, y_j, d)); - if (disij < val_i) { - ids_i = j; - val_i = disij; - val_i_2 = val_i * 2; - } - } - - if (j0 == 0 || val[i] > val_i) { - val[i] = val_i; - ids[i] = ids_i; - } - } - } - - free(data); -} - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/utils/distances.h b/core/src/index/thirdparty/faiss/utils/distances.h deleted file mode 100644 index b4311d09c6..0000000000 --- a/core/src/index/thirdparty/faiss/utils/distances.h +++ /dev/null @@ -1,271 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -/* All distance functions for L2 and IP distances. - * The actual functions are implemented in distances.cpp and distances_simd.cpp */ - -#pragma once - -#include - -#include -#include - - -namespace faiss { - - /********************************************************* - * Optimized distance/norm/inner prod computations - *********************************************************/ - -#ifdef __SSE__ -float fvec_L2sqr_sse ( - const float * x, - const float * y, - size_t d); - -float fvec_inner_product_sse ( - const float * x, - const float * y, - size_t d); - -float fvec_L1_sse ( - const float * x, - const float * y, - size_t d); - -float fvec_Linf_sse ( - const float * x, - const float * y, - size_t d); -#endif - -float fvec_jaccard ( - const float * x, - const float * y, - size_t d); - -/** Compute pairwise distances between sets of vectors - * - * @param d dimension of the vectors - * @param nq nb of query vectors - * @param nb nb of database vectors - * @param xq query vectors (size nq * d) - * @param xb database vectros (size nb * d) - * @param dis output distances (size nq * nb) - * @param ldq,ldb, ldd strides for the matrices - */ -void pairwise_L2sqr (int64_t d, - int64_t nq, const float *xq, - int64_t nb, const float *xb, - float *dis, - int64_t ldq = -1, int64_t ldb = -1, int64_t ldd = -1); - -/* compute the inner product between nx vectors x and one y */ -void fvec_inner_products_ny ( - float * ip, /* output inner product */ - const float * x, - const float * y, - size_t d, size_t ny); - -/* compute ny square L2 distance bewteen x and a set of contiguous y vectors */ -void fvec_L2sqr_ny ( - float * dis, - const float * x, - const float * y, - size_t d, size_t ny); - - -/** squared norm of a vector */ -float fvec_norm_L2sqr (const float * x, - size_t d); - -/** compute the L2 norms for a set of vectors - * - * @param ip output norms, size nx - * @param x set of vectors, size nx * d - */ -void fvec_norms_L2 (float * ip, const float * x, size_t d, size_t nx); - -/// same as fvec_norms_L2, but computes square norms -void fvec_norms_L2sqr (float * ip, const float * x, size_t d, size_t nx); - -/* L2-renormalize a set of vector. Nothing done if the vector is 0-normed */ -void fvec_renorm_L2 (size_t d, size_t nx, float * x); - - -/* This function exists because the Torch counterpart is extremly slow - (not multi-threaded + unexpected overhead even in single thread). - It is here to implement the usual property |x-y|^2=|x|^2+|y|^2-2 */ -void inner_product_to_L2sqr (float * dis, - const float * nr1, - const float * nr2, - size_t n1, size_t n2); - -/*************************************************************************** - * Compute a subset of distances - ***************************************************************************/ - - /* compute the inner product between x and a subset y of ny vectors, - whose indices are given by idy. */ -void fvec_inner_products_by_idx ( - float * ip, - const float * x, - const float * y, - const int64_t *ids, - size_t d, size_t nx, size_t ny); - -/* same but for a subset in y indexed by idsy (ny vectors in total) */ -void fvec_L2sqr_by_idx ( - float * dis, - const float * x, - const float * y, - const int64_t *ids, /* ids of y vecs */ - size_t d, size_t nx, size_t ny); - - -/** compute dis[j] = L2sqr(x[ix[j]], y[iy[j]]) forall j=0..n-1 - * - * @param x size (max(ix) + 1, d) - * @param y size (max(iy) + 1, d) - * @param ix size n - * @param iy size n - * @param dis size n - */ -void pairwise_indexed_L2sqr ( - size_t d, size_t n, - const float * x, const int64_t *ix, - const float * y, const int64_t *iy, - float *dis); - -/* same for inner product */ -void pairwise_indexed_inner_product ( - size_t d, size_t n, - const float * x, const int64_t *ix, - const float * y, const int64_t *iy, - float *dis); - -/*************************************************************************** - * KNN functions - ***************************************************************************/ - -// threshold on nx above which we switch to BLAS to compute distances -extern int distance_compute_blas_threshold; - -// threshold on nx above which we switch to compute parallel on ny -extern int parallel_policy_threshold; - -/** Return the k nearest neighors of each of the nx vectors x among the ny - * vector y, w.r.t to max inner product - * - * @param x query vectors, size nx * d - * @param y database vectors, size ny * d - * @param res result array, which also provides k. Sorted on output - */ -void knn_inner_product ( - const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - float_minheap_array_t * res, - ConcurrentBitsetPtr bitset = nullptr); - -/** Same as knn_inner_product, for the L2 distance */ -void knn_L2sqr ( - const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - float_maxheap_array_t * res, - ConcurrentBitsetPtr bitset = nullptr); - -void knn_jaccard ( - const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - float_maxheap_array_t * res, - ConcurrentBitsetPtr bitset = nullptr); - -/** same as knn_L2sqr, but base_shift[bno] is subtracted to all - * computed distances. - * - * @param base_shift size ny - */ -void knn_L2sqr_base_shift ( - const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - float_maxheap_array_t * res, - const float *base_shift); - -/* Find the nearest neighbors for nx queries in a set of ny vectors - * indexed by ids. May be useful for re-ranking a pre-selected vector list - */ -void knn_inner_products_by_idx ( - const float * x, - const float * y, - const int64_t * ids, - size_t d, size_t nx, size_t ny, - float_minheap_array_t * res); - -void knn_L2sqr_by_idx (const float * x, - const float * y, - const int64_t * ids, - size_t d, size_t nx, size_t ny, - float_maxheap_array_t * res); - -/*************************************************************************** - * Range search - ***************************************************************************/ - - - -/// Forward declaration, see AuxIndexStructures.h -struct RangeSearchResult; - -/** Return the k nearest neighors of each of the nx vectors x among the ny - * vector y, w.r.t to max inner product - * - * @param x query vectors, size nx * d - * @param y database vectors, size ny * d - * @param radius search radius around the x vectors - * @param result result structure - */ -void range_search_L2sqr ( - const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - float radius, - RangeSearchResult *result); - -/// same as range_search_L2sqr for the inner product similarity -void range_search_inner_product ( - const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - float radius, - RangeSearchResult *result); - - -/*************************************************************************** - * elkan - ***************************************************************************/ - -/** Return the nearest neighors of each of the nx vectors x among the ny - * - * @param x query vectors, size nx * d - * @param y database vectors, size ny * d - * @param ids result array ids - * @param val result array value - */ -void elkan_L2_sse ( - const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - int64_t *ids, float *val); - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/utils/distances_avx.h b/core/src/index/thirdparty/faiss/utils/distances_avx.h deleted file mode 100644 index 734c38ebe7..0000000000 --- a/core/src/index/thirdparty/faiss/utils/distances_avx.h +++ /dev/null @@ -1,32 +0,0 @@ - -// -*- c++ -*- - -/* All distance functions for L2 and IP distances. - * The actual functions are implemented in distances_simd_avx512.cpp */ - -#pragma once - -#include - -namespace faiss { - -/********************************************************* - * Optimized distance/norm/inner prod computations - *********************************************************/ - -/// Squared L2 distance between two vectors -float -fvec_L2sqr_avx(const float* x, const float* y, size_t d); - -/// inner product -float -fvec_inner_product_avx(const float* x, const float* y, size_t d); - -/// L1 distance -float -fvec_L1_avx(const float* x, const float* y, size_t d); - -float -fvec_Linf_avx(const float* x, const float* y, size_t d); - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/utils/distances_avx512.h b/core/src/index/thirdparty/faiss/utils/distances_avx512.h deleted file mode 100644 index d410f3e821..0000000000 --- a/core/src/index/thirdparty/faiss/utils/distances_avx512.h +++ /dev/null @@ -1,32 +0,0 @@ - -// -*- c++ -*- - -/* All distance functions for L2 and IP distances. - * The actual functions are implemented in distances_simd_avx512.cpp */ - -#pragma once - -#include - -namespace faiss { - -/********************************************************* - * Optimized distance/norm/inner prod computations - *********************************************************/ - -/// Squared L2 distance between two vectors -float -fvec_L2sqr_avx512(const float* x, const float* y, size_t d); - -/// inner product -float -fvec_inner_product_avx512(const float * x, const float * y, size_t d); - -/// L1 distance -float -fvec_L1_avx512(const float* x, const float* y, size_t d); - -float -fvec_Linf_avx512(const float* x, const float* y, size_t d); - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/utils/distances_simd.cpp b/core/src/index/thirdparty/faiss/utils/distances_simd.cpp deleted file mode 100644 index e33967d5e6..0000000000 --- a/core/src/index/thirdparty/faiss/utils/distances_simd.cpp +++ /dev/null @@ -1,624 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include -#include - -#include -#include -#include -#include - -#ifdef __SSE__ -#include -#endif - -#ifdef __aarch64__ -#include -#endif - -#include - -namespace faiss { - -/********************************************************* - * Optimized distance computations - *********************************************************/ - - -/* Functions to compute: - - L2 distance between 2 vectors - - inner product between 2 vectors - - L2 norm of a vector - - The functions should probably not be invoked when a large number of - vectors are be processed in batch (in which case Matrix multiply - is faster), but may be useful for comparing vectors isolated in - memory. - - Works with any vectors of any dimension, even unaligned (in which - case they are slower). - -*/ - - -/********************************************************* - * Reference implementations - */ - - -float fvec_L2sqr_ref (const float * x, - const float * y, - size_t d) -{ - size_t i; - float res = 0; - for (i = 0; i < d; i++) { - const float tmp = x[i] - y[i]; - res += tmp * tmp; - } - return res; -} - -float fvec_L1_ref (const float * x, - const float * y, - size_t d) -{ - size_t i; - float res = 0; - for (i = 0; i < d; i++) { - const float tmp = x[i] - y[i]; - res += fabs(tmp); - } - return res; -} - -float fvec_Linf_ref (const float * x, - const float * y, - size_t d) -{ - size_t i; - float res = 0; - for (i = 0; i < d; i++) { - res = fmax(res, fabs(x[i] - y[i])); - } - return res; -} - -float fvec_inner_product_ref (const float * x, - const float * y, - size_t d) -{ - size_t i; - float res = 0; - for (i = 0; i < d; i++) - res += x[i] * y[i]; - return res; -} - -float fvec_norm_L2sqr_ref (const float *x, size_t d) -{ - size_t i; - double res = 0; - for (i = 0; i < d; i++) - res += x[i] * x[i]; - return res; -} - - -void fvec_L2sqr_ny_ref (float * dis, - const float * x, - const float * y, - size_t d, size_t ny) -{ - for (size_t i = 0; i < ny; i++) { - dis[i] = fvec_L2sqr (x, y, d); - y += d; - } -} - - - - -/********************************************************* - * SSE and AVX implementations - */ - -#ifdef __SSE__ - -// reads 0 <= d < 4 floats as __m128 -static inline __m128 masked_read (int d, const float *x) -{ - assert (0 <= d && d < 4); - __attribute__((__aligned__(16))) float buf[4] = {0, 0, 0, 0}; - switch (d) { - case 3: - buf[2] = x[2]; - case 2: - buf[1] = x[1]; - case 1: - buf[0] = x[0]; - } - return _mm_load_ps (buf); - // cannot use AVX2 _mm_mask_set1_epi32 -} - -float fvec_norm_L2sqr (const float * x, - size_t d) -{ - __m128 mx; - __m128 msum1 = _mm_setzero_ps(); - - while (d >= 4) { - mx = _mm_loadu_ps (x); x += 4; - msum1 = _mm_add_ps (msum1, _mm_mul_ps (mx, mx)); - d -= 4; - } - - mx = masked_read (d, x); - msum1 = _mm_add_ps (msum1, _mm_mul_ps (mx, mx)); - - msum1 = _mm_hadd_ps (msum1, msum1); - msum1 = _mm_hadd_ps (msum1, msum1); - return _mm_cvtss_f32 (msum1); -} - -namespace { - -float sqr (float x) { - return x * x; -} - - -void fvec_L2sqr_ny_D1 (float * dis, const float * x, - const float * y, size_t ny) -{ - float x0s = x[0]; - __m128 x0 = _mm_set_ps (x0s, x0s, x0s, x0s); - - size_t i; - for (i = 0; i + 3 < ny; i += 4) { - __m128 tmp, accu; - tmp = x0 - _mm_loadu_ps (y); y += 4; - accu = tmp * tmp; - dis[i] = _mm_cvtss_f32 (accu); - tmp = _mm_shuffle_ps (accu, accu, 1); - dis[i + 1] = _mm_cvtss_f32 (tmp); - tmp = _mm_shuffle_ps (accu, accu, 2); - dis[i + 2] = _mm_cvtss_f32 (tmp); - tmp = _mm_shuffle_ps (accu, accu, 3); - dis[i + 3] = _mm_cvtss_f32 (tmp); - } - while (i < ny) { // handle non-multiple-of-4 case - dis[i++] = sqr(x0s - *y++); - } -} - - -void fvec_L2sqr_ny_D2 (float * dis, const float * x, - const float * y, size_t ny) -{ - __m128 x0 = _mm_set_ps (x[1], x[0], x[1], x[0]); - - size_t i; - for (i = 0; i + 1 < ny; i += 2) { - __m128 tmp, accu; - tmp = x0 - _mm_loadu_ps (y); y += 4; - accu = tmp * tmp; - accu = _mm_hadd_ps (accu, accu); - dis[i] = _mm_cvtss_f32 (accu); - accu = _mm_shuffle_ps (accu, accu, 3); - dis[i + 1] = _mm_cvtss_f32 (accu); - } - if (i < ny) { // handle odd case - dis[i] = sqr(x[0] - y[0]) + sqr(x[1] - y[1]); - } -} - - - -void fvec_L2sqr_ny_D4 (float * dis, const float * x, - const float * y, size_t ny) -{ - __m128 x0 = _mm_loadu_ps(x); - - for (size_t i = 0; i < ny; i++) { - __m128 tmp, accu; - tmp = x0 - _mm_loadu_ps (y); y += 4; - accu = tmp * tmp; - accu = _mm_hadd_ps (accu, accu); - accu = _mm_hadd_ps (accu, accu); - dis[i] = _mm_cvtss_f32 (accu); - } -} - - -void fvec_L2sqr_ny_D8 (float * dis, const float * x, - const float * y, size_t ny) -{ - __m128 x0 = _mm_loadu_ps(x); - __m128 x1 = _mm_loadu_ps(x + 4); - - for (size_t i = 0; i < ny; i++) { - __m128 tmp, accu; - tmp = x0 - _mm_loadu_ps (y); y += 4; - accu = tmp * tmp; - tmp = x1 - _mm_loadu_ps (y); y += 4; - accu += tmp * tmp; - accu = _mm_hadd_ps (accu, accu); - accu = _mm_hadd_ps (accu, accu); - dis[i] = _mm_cvtss_f32 (accu); - } -} - - -void fvec_L2sqr_ny_D12 (float * dis, const float * x, - const float * y, size_t ny) -{ - __m128 x0 = _mm_loadu_ps(x); - __m128 x1 = _mm_loadu_ps(x + 4); - __m128 x2 = _mm_loadu_ps(x + 8); - - for (size_t i = 0; i < ny; i++) { - __m128 tmp, accu; - tmp = x0 - _mm_loadu_ps (y); y += 4; - accu = tmp * tmp; - tmp = x1 - _mm_loadu_ps (y); y += 4; - accu += tmp * tmp; - tmp = x2 - _mm_loadu_ps (y); y += 4; - accu += tmp * tmp; - accu = _mm_hadd_ps (accu, accu); - accu = _mm_hadd_ps (accu, accu); - dis[i] = _mm_cvtss_f32 (accu); - } -} - - -} // anonymous namespace - -void fvec_L2sqr_ny (float * dis, const float * x, - const float * y, size_t d, size_t ny) { - // optimized for a few special cases - switch(d) { - case 1: - fvec_L2sqr_ny_D1 (dis, x, y, ny); - return; - case 2: - fvec_L2sqr_ny_D2 (dis, x, y, ny); - return; - case 4: - fvec_L2sqr_ny_D4 (dis, x, y, ny); - return; - case 8: - fvec_L2sqr_ny_D8 (dis, x, y, ny); - return; - case 12: - fvec_L2sqr_ny_D12 (dis, x, y, ny); - return; - default: - fvec_L2sqr_ny_ref (dis, x, y, d, ny); - return; - } -} - -#endif - -#if defined(__SSE__) // But not AVX - -float fvec_L1_sse (const float * x, const float * y, size_t d) -{ - return fvec_L1_ref (x, y, d); -} - -float fvec_Linf_sse (const float * x, const float * y, size_t d) -{ - return fvec_Linf_ref (x, y, d); -} - - -float fvec_L2sqr_sse (const float * x, - const float * y, - size_t d) -{ - __m128 msum1 = _mm_setzero_ps(); - - while (d >= 4) { - __m128 mx = _mm_loadu_ps (x); x += 4; - __m128 my = _mm_loadu_ps (y); y += 4; - const __m128 a_m_b1 = mx - my; - msum1 += a_m_b1 * a_m_b1; - d -= 4; - } - - if (d > 0) { - // add the last 1, 2 or 3 values - __m128 mx = masked_read (d, x); - __m128 my = masked_read (d, y); - __m128 a_m_b1 = mx - my; - msum1 += a_m_b1 * a_m_b1; - } - - msum1 = _mm_hadd_ps (msum1, msum1); - msum1 = _mm_hadd_ps (msum1, msum1); - return _mm_cvtss_f32 (msum1); -} - - -float fvec_inner_product_sse (const float * x, - const float * y, - size_t d) -{ - __m128 mx, my; - __m128 msum1 = _mm_setzero_ps(); - - while (d >= 4) { - mx = _mm_loadu_ps (x); x += 4; - my = _mm_loadu_ps (y); y += 4; - msum1 = _mm_add_ps (msum1, _mm_mul_ps (mx, my)); - d -= 4; - } - - // add the last 1, 2, or 3 values - mx = masked_read (d, x); - my = masked_read (d, y); - __m128 prod = _mm_mul_ps (mx, my); - - msum1 = _mm_add_ps (msum1, prod); - - msum1 = _mm_hadd_ps (msum1, msum1); - msum1 = _mm_hadd_ps (msum1, msum1); - return _mm_cvtss_f32 (msum1); -} - -#endif /* defined(__SSE__) */ - -//#elif defined(__aarch64__) -// -//float fvec_L2sqr (const float * x, -// const float * y, -// size_t d) -//{ -// if (d & 3) return fvec_L2sqr_ref (x, y, d); -// float32x4_t accu = vdupq_n_f32 (0); -// for (size_t i = 0; i < d; i += 4) { -// float32x4_t xi = vld1q_f32 (x + i); -// float32x4_t yi = vld1q_f32 (y + i); -// float32x4_t sq = vsubq_f32 (xi, yi); -// accu = vfmaq_f32 (accu, sq, sq); -// } -// float32x4_t a2 = vpaddq_f32 (accu, accu); -// return vdups_laneq_f32 (a2, 0) + vdups_laneq_f32 (a2, 1); -//} -// -//float fvec_inner_product (const float * x, -// const float * y, -// size_t d) -//{ -// if (d & 3) return fvec_inner_product_ref (x, y, d); -// float32x4_t accu = vdupq_n_f32 (0); -// for (size_t i = 0; i < d; i += 4) { -// float32x4_t xi = vld1q_f32 (x + i); -// float32x4_t yi = vld1q_f32 (y + i); -// accu = vfmaq_f32 (accu, xi, yi); -// } -// float32x4_t a2 = vpaddq_f32 (accu, accu); -// return vdups_laneq_f32 (a2, 0) + vdups_laneq_f32 (a2, 1); -//} -// -//float fvec_norm_L2sqr (const float *x, size_t d) -//{ -// if (d & 3) return fvec_norm_L2sqr_ref (x, d); -// float32x4_t accu = vdupq_n_f32 (0); -// for (size_t i = 0; i < d; i += 4) { -// float32x4_t xi = vld1q_f32 (x + i); -// accu = vfmaq_f32 (accu, xi, xi); -// } -// float32x4_t a2 = vpaddq_f32 (accu, accu); -// return vdups_laneq_f32 (a2, 0) + vdups_laneq_f32 (a2, 1); -//} -// -//// not optimized for ARM -//void fvec_L2sqr_ny (float * dis, const float * x, -// const float * y, size_t d, size_t ny) { -// fvec_L2sqr_ny_ref (dis, x, y, d, ny); -//} -// -//float fvec_L1 (const float * x, const float * y, size_t d) -//{ -// return fvec_L1_ref (x, y, d); -//} -// -//float fvec_Linf (const float * x, const float * y, size_t d) -//{ -// return fvec_Linf_ref (x, y, d); -//} -// -// -//#else -// scalar implementation - -//float fvec_L2sqr (const float * x, -// const float * y, -// size_t d) -//{ -// return fvec_L2sqr_ref (x, y, d); -//} -// -//float fvec_L1 (const float * x, const float * y, size_t d) -//{ -// return fvec_L1_ref (x, y, d); -//} -// -//float fvec_Linf (const float * x, const float * y, size_t d) -//{ -// return fvec_Linf_ref (x, y, d); -//} -// -//float fvec_inner_product (const float * x, -// const float * y, -// size_t d) -//{ -// return fvec_inner_product_ref (x, y, d); -//} -// -//float fvec_norm_L2sqr (const float *x, size_t d) -//{ -// return fvec_norm_L2sqr_ref (x, d); -//} -// -//void fvec_L2sqr_ny (float * dis, const float * x, -// const float * y, size_t d, size_t ny) { -// fvec_L2sqr_ny_ref (dis, x, y, d, ny); -//} -// -//#endif - - - - -/*************************************************************************** - * heavily optimized table computations - ***************************************************************************/ - - -static inline void fvec_madd_ref (size_t n, const float *a, - float bf, const float *b, float *c) { - for (size_t i = 0; i < n; i++) - c[i] = a[i] + bf * b[i]; -} - -#ifdef __SSE__ - -static inline void fvec_madd_sse (size_t n, const float *a, - float bf, const float *b, float *c) { - n >>= 2; - __m128 bf4 = _mm_set_ps1 (bf); - __m128 * a4 = (__m128*)a; - __m128 * b4 = (__m128*)b; - __m128 * c4 = (__m128*)c; - - while (n--) { - *c4 = _mm_add_ps (*a4, _mm_mul_ps (bf4, *b4)); - b4++; - a4++; - c4++; - } -} - -void fvec_madd (size_t n, const float *a, - float bf, const float *b, float *c) -{ - if ((n & 3) == 0 && - ((((long)a) | ((long)b) | ((long)c)) & 15) == 0) - fvec_madd_sse (n, a, bf, b, c); - else - fvec_madd_ref (n, a, bf, b, c); -} - -#else - -void fvec_madd (size_t n, const float *a, - float bf, const float *b, float *c) -{ - fvec_madd_ref (n, a, bf, b, c); -} - -#endif - -static inline int fvec_madd_and_argmin_ref (size_t n, const float *a, - float bf, const float *b, float *c) { - float vmin = 1e20; - int imin = -1; - - for (size_t i = 0; i < n; i++) { - c[i] = a[i] + bf * b[i]; - if (c[i] < vmin) { - vmin = c[i]; - imin = i; - } - } - return imin; -} - -#ifdef __SSE__ - -static inline int fvec_madd_and_argmin_sse ( - size_t n, const float *a, - float bf, const float *b, float *c) { - n >>= 2; - __m128 bf4 = _mm_set_ps1 (bf); - __m128 vmin4 = _mm_set_ps1 (1e20); - __m128i imin4 = _mm_set1_epi32 (-1); - __m128i idx4 = _mm_set_epi32 (3, 2, 1, 0); - __m128i inc4 = _mm_set1_epi32 (4); - __m128 * a4 = (__m128*)a; - __m128 * b4 = (__m128*)b; - __m128 * c4 = (__m128*)c; - - while (n--) { - __m128 vc4 = _mm_add_ps (*a4, _mm_mul_ps (bf4, *b4)); - *c4 = vc4; - __m128i mask = (__m128i)_mm_cmpgt_ps (vmin4, vc4); - // imin4 = _mm_blendv_epi8 (imin4, idx4, mask); // slower! - - imin4 = _mm_or_si128 (_mm_and_si128 (mask, idx4), - _mm_andnot_si128 (mask, imin4)); - vmin4 = _mm_min_ps (vmin4, vc4); - b4++; - a4++; - c4++; - idx4 = _mm_add_epi32 (idx4, inc4); - } - - // 4 values -> 2 - { - idx4 = _mm_shuffle_epi32 (imin4, 3 << 2 | 2); - __m128 vc4 = _mm_shuffle_ps (vmin4, vmin4, 3 << 2 | 2); - __m128i mask = (__m128i)_mm_cmpgt_ps (vmin4, vc4); - imin4 = _mm_or_si128 (_mm_and_si128 (mask, idx4), - _mm_andnot_si128 (mask, imin4)); - vmin4 = _mm_min_ps (vmin4, vc4); - } - // 2 values -> 1 - { - idx4 = _mm_shuffle_epi32 (imin4, 1); - __m128 vc4 = _mm_shuffle_ps (vmin4, vmin4, 1); - __m128i mask = (__m128i)_mm_cmpgt_ps (vmin4, vc4); - imin4 = _mm_or_si128 (_mm_and_si128 (mask, idx4), - _mm_andnot_si128 (mask, imin4)); - // vmin4 = _mm_min_ps (vmin4, vc4); - } - return _mm_cvtsi128_si32 (imin4); -} - - -int fvec_madd_and_argmin (size_t n, const float *a, - float bf, const float *b, float *c) -{ - if ((n & 3) == 0 && - ((((long)a) | ((long)b) | ((long)c)) & 15) == 0) - return fvec_madd_and_argmin_sse (n, a, bf, b, c); - else - return fvec_madd_and_argmin_ref (n, a, bf, b, c); -} - -#else - -int fvec_madd_and_argmin (size_t n, const float *a, - float bf, const float *b, float *c) -{ - return fvec_madd_and_argmin_ref (n, a, bf, b, c); -} - -#endif - - - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/utils/distances_simd_avx.cpp b/core/src/index/thirdparty/faiss/utils/distances_simd_avx.cpp deleted file mode 100644 index 4a3c83a89e..0000000000 --- a/core/src/index/thirdparty/faiss/utils/distances_simd_avx.cpp +++ /dev/null @@ -1,213 +0,0 @@ - -// -*- c++ -*- - -#include -#include - -#include -#include -#include -#include - -#include - -namespace faiss { - -#ifdef __SSE__ -// reads 0 <= d < 4 floats as __m128 -static inline __m128 masked_read (int d, const float *x) { - assert (0 <= d && d < 4); - __attribute__((__aligned__(16))) float buf[4] = {0, 0, 0, 0}; - switch (d) { - case 3: - buf[2] = x[2]; - case 2: - buf[1] = x[1]; - case 1: - buf[0] = x[0]; - } - return _mm_load_ps(buf); - // cannot use AVX2 _mm_mask_set1_epi32 -} -#endif - -#ifdef __AVX__ - -// reads 0 <= d < 8 floats as __m256 -static inline __m256 masked_read_8 (int d, const float* x) { - assert (0 <= d && d < 8); - if (d < 4) { - __m256 res = _mm256_setzero_ps (); - res = _mm256_insertf128_ps (res, masked_read (d, x), 0); - return res; - } else { - __m256 res = _mm256_setzero_ps (); - res = _mm256_insertf128_ps (res, _mm_loadu_ps (x), 0); - res = _mm256_insertf128_ps (res, masked_read (d - 4, x + 4), 1); - return res; - } -} - -float fvec_inner_product_avx (const float* x, const float* y, size_t d) { - __m256 msum1 = _mm256_setzero_ps(); - - while (d >= 8) { - __m256 mx = _mm256_loadu_ps (x); x += 8; - __m256 my = _mm256_loadu_ps (y); y += 8; - msum1 = _mm256_add_ps (msum1, _mm256_mul_ps (mx, my)); - d -= 8; - } - - __m128 msum2 = _mm256_extractf128_ps(msum1, 1); - msum2 += _mm256_extractf128_ps(msum1, 0); - - if (d >= 4) { - __m128 mx = _mm_loadu_ps (x); x += 4; - __m128 my = _mm_loadu_ps (y); y += 4; - msum2 = _mm_add_ps (msum2, _mm_mul_ps (mx, my)); - d -= 4; - } - - if (d > 0) { - __m128 mx = masked_read (d, x); - __m128 my = masked_read (d, y); - msum2 = _mm_add_ps (msum2, _mm_mul_ps (mx, my)); - } - - msum2 = _mm_hadd_ps (msum2, msum2); - msum2 = _mm_hadd_ps (msum2, msum2); - return _mm_cvtss_f32 (msum2); -} - -float fvec_L2sqr_avx (const float* x, const float* y, size_t d) { - __m256 msum1 = _mm256_setzero_ps(); - - while (d >= 8) { - __m256 mx = _mm256_loadu_ps (x); x += 8; - __m256 my = _mm256_loadu_ps (y); y += 8; - const __m256 a_m_b1 = mx - my; - msum1 += a_m_b1 * a_m_b1; - d -= 8; - } - - __m128 msum2 = _mm256_extractf128_ps(msum1, 1); - msum2 += _mm256_extractf128_ps(msum1, 0); - - if (d >= 4) { - __m128 mx = _mm_loadu_ps (x); x += 4; - __m128 my = _mm_loadu_ps (y); y += 4; - const __m128 a_m_b1 = mx - my; - msum2 += a_m_b1 * a_m_b1; - d -= 4; - } - - if (d > 0) { - __m128 mx = masked_read (d, x); - __m128 my = masked_read (d, y); - __m128 a_m_b1 = mx - my; - msum2 += a_m_b1 * a_m_b1; - } - - msum2 = _mm_hadd_ps (msum2, msum2); - msum2 = _mm_hadd_ps (msum2, msum2); - return _mm_cvtss_f32 (msum2); -} - -float fvec_L1_avx (const float * x, const float * y, size_t d) -{ - __m256 msum1 = _mm256_setzero_ps(); - __m256 signmask = __m256(_mm256_set1_epi32 (0x7fffffffUL)); - - while (d >= 8) { - __m256 mx = _mm256_loadu_ps (x); x += 8; - __m256 my = _mm256_loadu_ps (y); y += 8; - const __m256 a_m_b = mx - my; - msum1 += _mm256_and_ps(signmask, a_m_b); - d -= 8; - } - - __m128 msum2 = _mm256_extractf128_ps(msum1, 1); - msum2 += _mm256_extractf128_ps(msum1, 0); - __m128 signmask2 = __m128(_mm_set1_epi32 (0x7fffffffUL)); - - if (d >= 4) { - __m128 mx = _mm_loadu_ps (x); x += 4; - __m128 my = _mm_loadu_ps (y); y += 4; - const __m128 a_m_b = mx - my; - msum2 += _mm_and_ps(signmask2, a_m_b); - d -= 4; - } - - if (d > 0) { - __m128 mx = masked_read (d, x); - __m128 my = masked_read (d, y); - __m128 a_m_b = mx - my; - msum2 += _mm_and_ps(signmask2, a_m_b); - } - - msum2 = _mm_hadd_ps (msum2, msum2); - msum2 = _mm_hadd_ps (msum2, msum2); - return _mm_cvtss_f32 (msum2); -} - -float fvec_Linf_avx (const float* x, const float* y, size_t d) { - __m256 msum1 = _mm256_setzero_ps(); - __m256 signmask = __m256(_mm256_set1_epi32 (0x7fffffffUL)); - - while (d >= 8) { - __m256 mx = _mm256_loadu_ps (x); x += 8; - __m256 my = _mm256_loadu_ps (y); y += 8; - const __m256 a_m_b = mx - my; - msum1 = _mm256_max_ps(msum1, _mm256_and_ps(signmask, a_m_b)); - d -= 8; - } - - __m128 msum2 = _mm256_extractf128_ps(msum1, 1); - msum2 = _mm_max_ps (msum2, _mm256_extractf128_ps(msum1, 0)); - __m128 signmask2 = __m128(_mm_set1_epi32 (0x7fffffffUL)); - - if (d >= 4) { - __m128 mx = _mm_loadu_ps (x); x += 4; - __m128 my = _mm_loadu_ps (y); y += 4; - const __m128 a_m_b = mx - my; - msum2 = _mm_max_ps(msum2, _mm_and_ps(signmask2, a_m_b)); - d -= 4; - } - - if (d > 0) { - __m128 mx = masked_read (d, x); - __m128 my = masked_read (d, y); - __m128 a_m_b = mx - my; - msum2 = _mm_max_ps(msum2, _mm_and_ps(signmask2, a_m_b)); - } - - msum2 = _mm_max_ps(_mm_movehl_ps(msum2, msum2), msum2); - msum2 = _mm_max_ps(msum2, _mm_shuffle_ps (msum2, msum2, 1)); - return _mm_cvtss_f32 (msum2); -} - -#else - -float fvec_inner_product_avx(const float* x, const float* y, size_t d) { - FAISS_ASSERT(false); - return 0.0; -} - -float fvec_L2sqr_avx(const float* x, const float* y, size_t d) { - FAISS_ASSERT(false); - return 0.0; -} - -float fvec_L1_avx(const float* x, const float* y, size_t d) { - FAISS_ASSERT(false); - return 0.0; -} - -float fvec_Linf_avx (const float* x, const float* y, size_t d) { - FAISS_ASSERT(false); - return 0.0; -} - -#endif - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/utils/distances_simd_avx512.cpp b/core/src/index/thirdparty/faiss/utils/distances_simd_avx512.cpp deleted file mode 100644 index a73d9b7da9..0000000000 --- a/core/src/index/thirdparty/faiss/utils/distances_simd_avx512.cpp +++ /dev/null @@ -1,250 +0,0 @@ - -// -*- c++ -*- - -#include -#include - -#include -#include -#include -#include - -#include - -namespace faiss { - -#ifdef __SSE__ -// reads 0 <= d < 4 floats as __m128 -static inline __m128 masked_read (int d, const float *x) { - assert (0 <= d && d < 4); - __attribute__((__aligned__(16))) float buf[4] = {0, 0, 0, 0}; - switch (d) { - case 3: - buf[2] = x[2]; - case 2: - buf[1] = x[1]; - case 1: - buf[0] = x[0]; - } - return _mm_load_ps(buf); - // cannot use AVX2 _mm_mask_set1_epi32 -} -#endif - -#if (defined(__AVX512F__) && defined(__AVX512DQ__)) - -float -fvec_inner_product_avx512(const float* x, const float* y, size_t d) { - __m512 msum0 = _mm512_setzero_ps(); - - while (d >= 16) { - __m512 mx = _mm512_loadu_ps (x); x += 16; - __m512 my = _mm512_loadu_ps (y); y += 16; - msum0 = _mm512_add_ps (msum0, _mm512_mul_ps (mx, my)); - d -= 16; - } - - __m256 msum1 = _mm512_extractf32x8_ps(msum0, 1); - msum1 += _mm512_extractf32x8_ps(msum0, 0); - - if (d >= 8) { - __m256 mx = _mm256_loadu_ps (x); x += 8; - __m256 my = _mm256_loadu_ps (y); y += 8; - msum1 = _mm256_add_ps (msum1, _mm256_mul_ps (mx, my)); - d -= 8; - } - - __m128 msum2 = _mm256_extractf128_ps(msum1, 1); - msum2 += _mm256_extractf128_ps(msum1, 0); - - if (d >= 4) { - __m128 mx = _mm_loadu_ps (x); x += 4; - __m128 my = _mm_loadu_ps (y); y += 4; - msum2 = _mm_add_ps (msum2, _mm_mul_ps (mx, my)); - d -= 4; - } - - if (d > 0) { - __m128 mx = masked_read (d, x); - __m128 my = masked_read (d, y); - msum2 = _mm_add_ps (msum2, _mm_mul_ps (mx, my)); - } - - msum2 = _mm_hadd_ps (msum2, msum2); - msum2 = _mm_hadd_ps (msum2, msum2); - return _mm_cvtss_f32 (msum2); -} - -float -fvec_L2sqr_avx512(const float* x, const float* y, size_t d) { - __m512 msum0 = _mm512_setzero_ps(); - - while (d >= 16) { - __m512 mx = _mm512_loadu_ps (x); x += 16; - __m512 my = _mm512_loadu_ps (y); y += 16; - const __m512 a_m_b1 = mx - my; - msum0 += a_m_b1 * a_m_b1; - d -= 16; - } - - __m256 msum1 = _mm512_extractf32x8_ps(msum0, 1); - msum1 += _mm512_extractf32x8_ps(msum0, 0); - - if (d >= 8) { - __m256 mx = _mm256_loadu_ps (x); x += 8; - __m256 my = _mm256_loadu_ps (y); y += 8; - const __m256 a_m_b1 = mx - my; - msum1 += a_m_b1 * a_m_b1; - d -= 8; - } - - __m128 msum2 = _mm256_extractf128_ps(msum1, 1); - msum2 += _mm256_extractf128_ps(msum1, 0); - - if (d >= 4) { - __m128 mx = _mm_loadu_ps (x); x += 4; - __m128 my = _mm_loadu_ps (y); y += 4; - const __m128 a_m_b1 = mx - my; - msum2 += a_m_b1 * a_m_b1; - d -= 4; - } - - if (d > 0) { - __m128 mx = masked_read (d, x); - __m128 my = masked_read (d, y); - __m128 a_m_b1 = mx - my; - msum2 += a_m_b1 * a_m_b1; - } - - msum2 = _mm_hadd_ps (msum2, msum2); - msum2 = _mm_hadd_ps (msum2, msum2); - return _mm_cvtss_f32 (msum2); -} - -float -fvec_L1_avx512(const float* x, const float* y, size_t d) { - __m512 msum0 = _mm512_setzero_ps(); - __m512 signmask0 = __m512(_mm512_set1_epi32 (0x7fffffffUL)); - - while (d >= 16) { - __m512 mx = _mm512_loadu_ps (x); x += 16; - __m512 my = _mm512_loadu_ps (y); y += 16; - const __m512 a_m_b = mx - my; - msum0 += _mm512_and_ps(signmask0, a_m_b); - d -= 16; - } - - __m256 msum1 = _mm512_extractf32x8_ps(msum0, 1); - msum1 += _mm512_extractf32x8_ps(msum0, 0); - __m256 signmask1 = __m256(_mm256_set1_epi32 (0x7fffffffUL)); - - if (d >= 8) { - __m256 mx = _mm256_loadu_ps (x); x += 8; - __m256 my = _mm256_loadu_ps (y); y += 8; - const __m256 a_m_b = mx - my; - msum1 += _mm256_and_ps(signmask1, a_m_b); - d -= 8; - } - - __m128 msum2 = _mm256_extractf128_ps(msum1, 1); - msum2 += _mm256_extractf128_ps(msum1, 0); - __m128 signmask2 = __m128(_mm_set1_epi32 (0x7fffffffUL)); - - if (d >= 4) { - __m128 mx = _mm_loadu_ps (x); x += 4; - __m128 my = _mm_loadu_ps (y); y += 4; - const __m128 a_m_b = mx - my; - msum2 += _mm_and_ps(signmask2, a_m_b); - d -= 4; - } - - if (d > 0) { - __m128 mx = masked_read (d, x); - __m128 my = masked_read (d, y); - __m128 a_m_b = mx - my; - msum2 += _mm_and_ps(signmask2, a_m_b); - } - - msum2 = _mm_hadd_ps (msum2, msum2); - msum2 = _mm_hadd_ps (msum2, msum2); - return _mm_cvtss_f32 (msum2); -} - -float -fvec_Linf_avx512(const float* x, const float* y, size_t d) { - __m512 msum0 = _mm512_setzero_ps(); - __m512 signmask0 = __m512(_mm512_set1_epi32 (0x7fffffffUL)); - - while (d >= 16) { - __m512 mx = _mm512_loadu_ps (x); x += 16; - __m512 my = _mm512_loadu_ps (y); y += 16; - const __m512 a_m_b = mx - my; - msum0 = _mm512_max_ps(msum0, _mm512_and_ps(signmask0, a_m_b)); - d -= 16; - } - - __m256 msum1 = _mm512_extractf32x8_ps(msum0, 1); - msum1 = _mm256_max_ps (msum1, _mm512_extractf32x8_ps(msum0, 0)); - __m256 signmask1 = __m256(_mm256_set1_epi32 (0x7fffffffUL)); - - if (d >= 8) { - __m256 mx = _mm256_loadu_ps (x); x += 8; - __m256 my = _mm256_loadu_ps (y); y += 8; - const __m256 a_m_b = mx - my; - msum1 = _mm256_max_ps(msum1, _mm256_and_ps(signmask1, a_m_b)); - d -= 8; - } - - __m128 msum2 = _mm256_extractf128_ps(msum1, 1); - msum2 = _mm_max_ps (msum2, _mm256_extractf128_ps(msum1, 0)); - __m128 signmask2 = __m128(_mm_set1_epi32 (0x7fffffffUL)); - - if (d >= 4) { - __m128 mx = _mm_loadu_ps (x); x += 4; - __m128 my = _mm_loadu_ps (y); y += 4; - const __m128 a_m_b = mx - my; - msum2 = _mm_max_ps(msum2, _mm_and_ps(signmask2, a_m_b)); - d -= 4; - } - - if (d > 0) { - __m128 mx = masked_read (d, x); - __m128 my = masked_read (d, y); - __m128 a_m_b = mx - my; - msum2 = _mm_max_ps(msum2, _mm_and_ps(signmask2, a_m_b)); - } - - msum2 = _mm_max_ps(_mm_movehl_ps(msum2, msum2), msum2); - msum2 = _mm_max_ps(msum2, _mm_shuffle_ps (msum2, msum2, 1)); - return _mm_cvtss_f32 (msum2); -} - -#else - -float -fvec_inner_product_avx512(const float* x, const float* y, size_t d) { - FAISS_ASSERT(false); - return 0.0; -} - -float -fvec_L2sqr_avx512(const float* x, const float* y, size_t d) { - FAISS_ASSERT(false); - return 0.0; -} - -float -fvec_L1_avx512(const float* x, const float* y, size_t d) { - FAISS_ASSERT(false); - return 0.0; -} - -float -fvec_Linf_avx512(const float* x, const float* y, size_t d) { - FAISS_ASSERT(false); - return 0.0; -} - -#endif - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/utils/extra_distances.cpp b/core/src/index/thirdparty/faiss/utils/extra_distances.cpp deleted file mode 100644 index de03b013ac..0000000000 --- a/core/src/index/thirdparty/faiss/utils/extra_distances.cpp +++ /dev/null @@ -1,374 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include - -#include -#include -#include -#include -#include - -namespace faiss { - -/*************************************************************************** - * Distance functions (other than L2 and IP) - ***************************************************************************/ - -struct VectorDistanceL2 { - size_t d; - - float operator () (const float *x, const float *y) const { - return fvec_L2sqr (x, y, d); - } -}; - -struct VectorDistanceL1 { - size_t d; - - float operator () (const float *x, const float *y) const { - return fvec_L1 (x, y, d); - } -}; - -struct VectorDistanceLinf { - size_t d; - - float operator () (const float *x, const float *y) const { - return fvec_Linf (x, y, d); - /* - float vmax = 0; - for (size_t i = 0; i < d; i++) { - float diff = fabs (x[i] - y[i]); - if (diff > vmax) vmax = diff; - } - return vmax;*/ - } -}; - -struct VectorDistanceLp { - size_t d; - const float p; - - float operator () (const float *x, const float *y) const { - float accu = 0; - for (size_t i = 0; i < d; i++) { - float diff = fabs (x[i] - y[i]); - accu += powf (diff, p); - } - return accu; - } -}; - -struct VectorDistanceCanberra { - size_t d; - - float operator () (const float *x, const float *y) const { - float accu = 0; - for (size_t i = 0; i < d; i++) { - float xi = x[i], yi = y[i]; - accu += fabs (xi - yi) / (fabs(xi) + fabs(yi)); - } - return accu; - } -}; - -struct VectorDistanceBrayCurtis { - size_t d; - - float operator () (const float *x, const float *y) const { - float accu_num = 0, accu_den = 0; - for (size_t i = 0; i < d; i++) { - float xi = x[i], yi = y[i]; - accu_num += fabs (xi - yi); - accu_den += fabs (xi + yi); - } - return accu_num / accu_den; - } -}; - -struct VectorDistanceJensenShannon { - size_t d; - - float operator () (const float *x, const float *y) const { - float accu = 0; - - for (size_t i = 0; i < d; i++) { - float xi = x[i], yi = y[i]; - float mi = 0.5 * (xi + yi); - float kl1 = - xi * log(mi / xi); - float kl2 = - yi * log(mi / yi); - accu += kl1 + kl2; - } - return 0.5 * accu; - } -}; - -struct VectorDistanceJaccard { - size_t d; - - float operator () (const float *x, const float *y) const { - float accu_num = 0, accu_den = 0; - const float EPSILON = 0.000001; - for (size_t i = 0; i < d; i++) { - float xi = x[i], yi = y[i]; - if (fabs (xi - yi) < EPSILON) { - accu_num += xi; - accu_den += xi; - } else { - accu_den += xi; - accu_den += yi; - } - } - return 1 - accu_num / accu_den; - } -}; - -struct VectorDistanceTanimoto { - size_t d; - - float operator () (const float *x, const float *y) const { - float accu_num = 0, accu_den = 0; - for (size_t i = 0; i < d; i++) { - float xi = x[i], yi = y[i]; - accu_num += xi * yi; - accu_den += xi * xi + yi * yi - xi * yi; - } - return -log2(accu_num / accu_den) ; - } -}; - - - -namespace { - -template -void pairwise_extra_distances_template ( - VD vd, - int64_t nq, const float *xq, - int64_t nb, const float *xb, - float *dis, - int64_t ldq, int64_t ldb, int64_t ldd) -{ - -#pragma omp parallel for if(nq > 10) - for (int64_t i = 0; i < nq; i++) { - const float *xqi = xq + i * ldq; - const float *xbj = xb; - float *disi = dis + ldd * i; - - for (int64_t j = 0; j < nb; j++) { - disi[j] = vd (xqi, xbj); - xbj += ldb; - } - } -} - - -template -void knn_extra_metrics_template ( - VD vd, - const float * x, - const float * y, - size_t nx, size_t ny, - float_maxheap_array_t * res, - ConcurrentBitsetPtr bitset = nullptr) -{ - size_t k = res->k; - size_t d = vd.d; - size_t check_period = InterruptCallback::get_period_hint (ny * d); - check_period *= omp_get_max_threads(); - - for (size_t i0 = 0; i0 < nx; i0 += check_period) { - size_t i1 = std::min(i0 + check_period, nx); - -#pragma omp parallel for - for (size_t i = i0; i < i1; i++) { - const float * x_i = x + i * d; - const float * y_j = y; - size_t j; - float * simi = res->get_val(i); - int64_t * idxi = res->get_ids (i); - - maxheap_heapify (k, simi, idxi); - for (j = 0; j < ny; j++) { - if (!bitset || !bitset->test(j)) { - float disij = vd (x_i, y_j); - if (disij < simi[0]) { - maxheap_pop (k, simi, idxi); - maxheap_push (k, simi, idxi, disij, j); - } - } - y_j += d; - } - maxheap_reorder (k, simi, idxi); - } - InterruptCallback::check (); - } - -} - - -template -struct ExtraDistanceComputer : DistanceComputer { - VD vd; - Index::idx_t nb; - const float *q; - const float *b; - - float operator () (idx_t i) override { - return vd (q, b + i * vd.d); - } - - float symmetric_dis(idx_t i, idx_t j) override { - return vd (b + j * vd.d, b + i * vd.d); - } - - ExtraDistanceComputer(const VD & vd, const float *xb, - size_t nb, const float *q = nullptr) - : vd(vd), nb(nb), q(q), b(xb) {} - - void set_query(const float *x) override { - q = x; - } -}; - -} // anonymous namespace - -void pairwise_extra_distances ( - int64_t d, - int64_t nq, const float *xq, - int64_t nb, const float *xb, - MetricType mt, float metric_arg, - float *dis, - int64_t ldq, int64_t ldb, int64_t ldd) -{ - if (nq == 0 || nb == 0) return; - if (ldq == -1) ldq = d; - if (ldb == -1) ldb = d; - if (ldd == -1) ldd = nb; - - switch(mt) { -#define HANDLE_VAR(kw) \ - case METRIC_ ## kw: { \ - VectorDistance ## kw vd({(size_t)d}); \ - pairwise_extra_distances_template (vd, nq, xq, nb, xb, \ - dis, ldq, ldb, ldd); \ - break; \ - } - HANDLE_VAR(L2); - HANDLE_VAR(L1); - HANDLE_VAR(Linf); - HANDLE_VAR(Canberra); - HANDLE_VAR(BrayCurtis); - HANDLE_VAR(JensenShannon); -#undef HANDLE_VAR - case METRIC_Lp: { - VectorDistanceLp vd({(size_t)d, metric_arg}); - pairwise_extra_distances_template (vd, nq, xq, nb, xb, - dis, ldq, ldb, ldd); - break; - } - case METRIC_Jaccard: { - VectorDistanceJaccard vd({(size_t) d}); - pairwise_extra_distances_template(vd, nq, xq, nb, xb, - dis, ldq, ldb, ldd); - break; - } - case METRIC_Tanimoto: { - VectorDistanceTanimoto vd({(size_t) d}); - pairwise_extra_distances_template(vd, nq, xq, nb, xb, - dis, ldq, ldb, ldd); - break; - } - default: - FAISS_THROW_MSG ("metric type not implemented"); - } - -} - -void knn_extra_metrics ( - const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - MetricType mt, float metric_arg, - float_maxheap_array_t * res, - ConcurrentBitsetPtr bitset) -{ - - switch(mt) { -#define HANDLE_VAR(kw) \ - case METRIC_ ## kw: { \ - VectorDistance ## kw vd({(size_t)d}); \ - knn_extra_metrics_template (vd, x, y, nx, ny, res, bitset); \ - break; \ - } - HANDLE_VAR(L2); - HANDLE_VAR(L1); - HANDLE_VAR(Linf); - HANDLE_VAR(Canberra); - HANDLE_VAR(BrayCurtis); - HANDLE_VAR(JensenShannon); -#undef HANDLE_VAR - case METRIC_Lp: { - VectorDistanceLp vd({(size_t)d, metric_arg}); - knn_extra_metrics_template (vd, x, y, nx, ny, res, bitset); - break; - } - case METRIC_Jaccard: { - VectorDistanceJaccard vd({(size_t) d}); - knn_extra_metrics_template(vd, x, y, nx, ny, res, bitset); - break; - } - case METRIC_Tanimoto: { - VectorDistanceTanimoto vd({(size_t) d}); - knn_extra_metrics_template(vd, x, y, nx, ny, res, bitset); - break; - } - default: - FAISS_THROW_MSG ("metric type not implemented"); - } - -} - -DistanceComputer *get_extra_distance_computer ( - size_t d, - MetricType mt, float metric_arg, - size_t nb, const float *xb) -{ - - switch(mt) { -#define HANDLE_VAR(kw) \ - case METRIC_ ## kw: { \ - VectorDistance ## kw vd({(size_t)d}); \ - return new ExtraDistanceComputer(vd, xb, nb); \ - } - HANDLE_VAR(L2); - HANDLE_VAR(L1); - HANDLE_VAR(Linf); - HANDLE_VAR(Canberra); - HANDLE_VAR(BrayCurtis); - HANDLE_VAR(JensenShannon); -#undef HANDLE_VAR - case METRIC_Lp: { - VectorDistanceLp vd({(size_t)d, metric_arg}); - return new ExtraDistanceComputer (vd, xb, nb); - break; - } - default: - FAISS_THROW_MSG ("metric type not implemented"); - } - -} - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/utils/extra_distances.h b/core/src/index/thirdparty/faiss/utils/extra_distances.h deleted file mode 100644 index 2ac60aa6ac..0000000000 --- a/core/src/index/thirdparty/faiss/utils/extra_distances.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#ifndef FAISS_distances_h -#define FAISS_distances_h - -/** In this file are the implementations of extra metrics beyond L2 - * and inner product */ - -#include - -#include - -#include - - - -namespace faiss { - - -void pairwise_extra_distances ( - int64_t d, - int64_t nq, const float *xq, - int64_t nb, const float *xb, - MetricType mt, float metric_arg, - float *dis, - int64_t ldq = -1, int64_t ldb = -1, int64_t ldd = -1); - - -void knn_extra_metrics ( - const float * x, - const float * y, - size_t d, size_t nx, size_t ny, - MetricType mt, float metric_arg, - float_maxheap_array_t * res, - ConcurrentBitsetPtr bitset = nullptr); - - -/** get a DistanceComputer that refers to this type of distance and - * indexes a flat array of size nb */ -DistanceComputer *get_extra_distance_computer ( - size_t d, - MetricType mt, float metric_arg, - size_t nb, const float *xb); - -} - - -#endif diff --git a/core/src/index/thirdparty/faiss/utils/hamming-inl.h b/core/src/index/thirdparty/faiss/utils/hamming-inl.h deleted file mode 100644 index 861e1f4308..0000000000 --- a/core/src/index/thirdparty/faiss/utils/hamming-inl.h +++ /dev/null @@ -1,472 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - - -namespace faiss { - - -inline BitstringWriter::BitstringWriter(uint8_t *code, int code_size): - code (code), code_size (code_size), i(0) -{ - bzero (code, code_size); -} - -inline void BitstringWriter::write(uint64_t x, int nbit) { - assert (code_size * 8 >= nbit + i); - // nb of available bits in i / 8 - int na = 8 - (i & 7); - - if (nbit <= na) { - code[i >> 3] |= x << (i & 7); - i += nbit; - return; - } else { - int j = i >> 3; - code[j++] |= x << (i & 7); - i += nbit; - x >>= na; - while (x != 0) { - code[j++] |= x; - x >>= 8; - } - } -} - - -inline BitstringReader::BitstringReader(const uint8_t *code, int code_size): - code (code), code_size (code_size), i(0) -{} - -inline uint64_t BitstringReader::read(int nbit) { - assert (code_size * 8 >= nbit + i); - // nb of available bits in i / 8 - int na = 8 - (i & 7); - // get available bits in current byte - uint64_t res = code[i >> 3] >> (i & 7); - if (nbit <= na) { - res &= (1 << nbit) - 1; - i += nbit; - return res; - } else { - int ofs = na; - int j = (i >> 3) + 1; - i += nbit; - nbit -= na; - while (nbit > 8) { - res |= ((uint64_t)code[j++]) << ofs; - ofs += 8; - nbit -= 8; // TODO remove nbit - } - uint64_t last_byte = code[j]; - last_byte &= (1 << nbit) - 1; - res |= last_byte << ofs; - return res; - } -} - - -/****************************************************************** - * The HammingComputer series of classes compares a single code of - * size 4 to 32 to incoming codes. They are intended for use as a - * template class where it would be inefficient to switch on the code - * size in the inner loop. Hopefully the compiler will inline the - * hamming() functions and put the a0, a1, ... in registers. - ******************************************************************/ - - -struct HammingComputer4 { - uint32_t a0; - - HammingComputer4 () {} - - HammingComputer4 (const uint8_t *a, int code_size) { - set (a, code_size); - } - - void set (const uint8_t *a, int code_size) { - assert (code_size == 4); - a0 = *(uint32_t *)a; - } - - inline int hamming (const uint8_t *b) const { - return popcount64 (*(uint32_t *)b ^ a0); - } - -}; - -struct HammingComputer8 { - uint64_t a0; - - HammingComputer8 () {} - - HammingComputer8 (const uint8_t *a, int code_size) { - set (a, code_size); - } - - void set (const uint8_t *a, int code_size) { - assert (code_size == 8); - a0 = *(uint64_t *)a; - } - - inline int hamming (const uint8_t *b) const { - return popcount64 (*(uint64_t *)b ^ a0); - } - -}; - - -struct HammingComputer16 { - uint64_t a0, a1; - - HammingComputer16 () {} - - HammingComputer16 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - assert (code_size == 16); - const uint64_t *a = (uint64_t *)a8; - a0 = a[0]; a1 = a[1]; - } - - inline int hamming (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - return popcount64 (b[0] ^ a0) + popcount64 (b[1] ^ a1); - } - -}; - -// when applied to an array, 1/2 of the 64-bit accesses are unaligned. -// This incurs a penalty of ~10% wrt. fully aligned accesses. -struct HammingComputer20 { - uint64_t a0, a1; - uint32_t a2; - - HammingComputer20 () {} - - HammingComputer20 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - assert (code_size == 20); - const uint64_t *a = (uint64_t *)a8; - a0 = a[0]; a1 = a[1]; a2 = a[2]; - } - - inline int hamming (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - return popcount64 (b[0] ^ a0) + popcount64 (b[1] ^ a1) + - popcount64 (*(uint32_t*)(b + 2) ^ a2); - } -}; - -struct HammingComputer32 { - uint64_t a0, a1, a2, a3; - - HammingComputer32 () {} - - HammingComputer32 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - assert (code_size == 32); - const uint64_t *a = (uint64_t *)a8; - a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3]; - } - - inline int hamming (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - return popcount64 (b[0] ^ a0) + popcount64 (b[1] ^ a1) + - popcount64 (b[2] ^ a2) + popcount64 (b[3] ^ a3); - } - -}; - -struct HammingComputer64 { - uint64_t a0, a1, a2, a3, a4, a5, a6, a7; - - HammingComputer64 () {} - - HammingComputer64 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - assert (code_size == 64); - const uint64_t *a = (uint64_t *)a8; - a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3]; - a4 = a[4]; a5 = a[5]; a6 = a[6]; a7 = a[7]; - } - - inline int hamming (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - return popcount64 (b[0] ^ a0) + popcount64 (b[1] ^ a1) + - popcount64 (b[2] ^ a2) + popcount64 (b[3] ^ a3) + - popcount64 (b[4] ^ a4) + popcount64 (b[5] ^ a5) + - popcount64 (b[6] ^ a6) + popcount64 (b[7] ^ a7); - } - -}; - -// very inefficient... -struct HammingComputerDefault { - const uint8_t *a; - int n; - - HammingComputerDefault () {} - - HammingComputerDefault (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - a = a8; - n = code_size; - } - - int hamming (const uint8_t *b8) const { - int accu = 0; - for (int i = 0; i < n; i++) - accu += popcount64 (a[i] ^ b8[i]); - return accu; - } - -}; - -struct HammingComputerM8 { - const uint64_t *a; - int n; - - HammingComputerM8 () {} - - HammingComputerM8 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - assert (code_size % 8 == 0); - a = (uint64_t *)a8; - n = code_size / 8; - } - - int hamming (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - int accu = 0; - for (int i = 0; i < n; i++) - accu += popcount64 (a[i] ^ b[i]); - return accu; - } - -}; - -// even more inefficient! -struct HammingComputerM4 { - const uint32_t *a; - int n; - - HammingComputerM4 () {} - - HammingComputerM4 (const uint8_t *a4, int code_size) { - set (a4, code_size); - } - - void set (const uint8_t *a4, int code_size) { - assert (code_size % 4 == 0); - a = (uint32_t *)a4; - n = code_size / 4; - } - - int hamming (const uint8_t *b8) const { - const uint32_t *b = (uint32_t *)b8; - int accu = 0; - for (int i = 0; i < n; i++) - accu += popcount64 (a[i] ^ b[i]); - return accu; - } - -}; - -/*************************************************************************** - * Equivalence with a template class when code size is known at compile time - **************************************************************************/ - -// default template -template -struct HammingComputer: HammingComputerM8 { - HammingComputer (const uint8_t *a, int code_size): - HammingComputerM8(a, code_size) {} -}; - -#define SPECIALIZED_HC(CODE_SIZE) \ - template<> struct HammingComputer: \ - HammingComputer ## CODE_SIZE { \ - HammingComputer (const uint8_t *a): \ - HammingComputer ## CODE_SIZE(a, CODE_SIZE) {} \ - } - -SPECIALIZED_HC(4); -SPECIALIZED_HC(8); -SPECIALIZED_HC(16); -SPECIALIZED_HC(20); -SPECIALIZED_HC(32); -SPECIALIZED_HC(64); - -#undef SPECIALIZED_HC - - -/*************************************************************************** - * generalized Hamming = number of bytes that are different between - * two codes. - ***************************************************************************/ - - -inline int generalized_hamming_64 (uint64_t a) { - a |= a >> 1; - a |= a >> 2; - a |= a >> 4; - a &= 0x0101010101010101UL; - return popcount64 (a); -} - - -struct GenHammingComputer8 { - uint64_t a0; - - GenHammingComputer8 (const uint8_t *a, int code_size) { - assert (code_size == 8); - a0 = *(uint64_t *)a; - } - - inline int hamming (const uint8_t *b) const { - return generalized_hamming_64 (*(uint64_t *)b ^ a0); - } - -}; - - -struct GenHammingComputer16 { - uint64_t a0, a1; - GenHammingComputer16 (const uint8_t *a8, int code_size) { - assert (code_size == 16); - const uint64_t *a = (uint64_t *)a8; - a0 = a[0]; a1 = a[1]; - } - - inline int hamming (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - return generalized_hamming_64 (b[0] ^ a0) + - generalized_hamming_64 (b[1] ^ a1); - } - -}; - -struct GenHammingComputer32 { - uint64_t a0, a1, a2, a3; - - GenHammingComputer32 (const uint8_t *a8, int code_size) { - assert (code_size == 32); - const uint64_t *a = (uint64_t *)a8; - a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3]; - } - - inline int hamming (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - return generalized_hamming_64 (b[0] ^ a0) + - generalized_hamming_64 (b[1] ^ a1) + - generalized_hamming_64 (b[2] ^ a2) + - generalized_hamming_64 (b[3] ^ a3); - } - -}; - -struct GenHammingComputerM8 { - const uint64_t *a; - int n; - - GenHammingComputerM8 (const uint8_t *a8, int code_size) { - assert (code_size % 8 == 0); - a = (uint64_t *)a8; - n = code_size / 8; - } - - int hamming (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - int accu = 0; - for (int i = 0; i < n; i++) - accu += generalized_hamming_64 (a[i] ^ b[i]); - return accu; - } - -}; - - -/** generalized Hamming distances (= count number of code bytes that - are the same) */ -void generalized_hammings_knn_hc ( - int_maxheap_array_t * ha, - const uint8_t * a, - const uint8_t * b, - size_t nb, - size_t code_size, - int ordered = true); - - - -/** This class maintains a list of best distances seen so far. - * - * Since the distances are in a limited range (0 to nbit), the - * object maintains one list per possible distance, and fills - * in only the n-first lists, such that the sum of sizes of the - * n lists is below k. - */ -template -struct HCounterState { - int *counters; - int64_t *ids_per_dis; - - HammingComputer hc; - int thres; - int count_lt; - int count_eq; - int k; - - HCounterState(int *counters, int64_t *ids_per_dis, - const uint8_t *x, int d, int k) - : counters(counters), - ids_per_dis(ids_per_dis), - hc(x, d / 8), - thres(d + 1), - count_lt(0), - count_eq(0), - k(k) {} - - void update_counter(const uint8_t *y, size_t j) { - int32_t dis = hc.hamming(y); - - if (dis <= thres) { - if (dis < thres) { - ids_per_dis[dis * k + counters[dis]++] = j; - ++count_lt; - while (count_lt == k && thres > 0) { - --thres; - count_eq = counters[thres]; - count_lt -= count_eq; - } - } else if (count_eq < k) { - ids_per_dis[dis * k + count_eq++] = j; - counters[dis] = count_eq; - } - } - } -}; - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/utils/hamming.cpp b/core/src/index/thirdparty/faiss/utils/hamming.cpp deleted file mode 100644 index 2e714c34b4..0000000000 --- a/core/src/index/thirdparty/faiss/utils/hamming.cpp +++ /dev/null @@ -1,981 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -/* - * Implementation of Hamming related functions (distances, smallest distance - * selection with regular heap|radix and probabilistic heap|radix. - * - * IMPLEMENTATION NOTES - * Bitvectors are generally assumed to be multiples of 64 bits. - * - * hamdis_t is used for distances because at this time - * it is not clear how we will need to balance - * - flexibility in vector size (unclear more than 2^16 or even 2^8 bitvectors) - * - memory usage - * - cache-misses when dealing with large volumes of data (lower bits is better) - * - * The hamdis_t should optimally be compatibe with one of the Torch Storage - * (Byte,Short,Long) and therefore should be signed for 2-bytes and 4-bytes -*/ - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -static const size_t BLOCKSIZE_QUERY = 8192; -static const size_t size_1M = 1 * 1024 * 1024; - -namespace faiss { - -size_t hamming_batch_size = 65536; - -static const uint8_t hamdis_tab_ham_bytes[256] = { - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 -}; - - -/* Elementary Hamming distance computation: unoptimized */ -template -T hamming (const uint8_t *bs1, - const uint8_t *bs2) -{ - const size_t nbytes = nbits / 8; - size_t i; - T h = 0; - for (i = 0; i < nbytes; i++) - h += (T) hamdis_tab_ham_bytes[bs1[i]^bs2[i]]; - return h; -} - - -/* Hamming distances for multiples of 64 bits */ -template -hamdis_t hamming (const uint64_t * bs1, const uint64_t * bs2) -{ - const size_t nwords = nbits / 64; - size_t i; - hamdis_t h = 0; - for (i = 0; i < nwords; i++) - h += popcount64 (bs1[i] ^ bs2[i]); - return h; -} - - - -/* specialized (optimized) functions */ -template <> -hamdis_t hamming<64> (const uint64_t * pa, const uint64_t * pb) -{ - return popcount64 (pa[0] ^ pb[0]); -} - - -template <> -hamdis_t hamming<128> (const uint64_t *pa, const uint64_t *pb) -{ - return popcount64 (pa[0] ^ pb[0]) + popcount64(pa[1] ^ pb[1]); -} - - -template <> -hamdis_t hamming<256> (const uint64_t * pa, const uint64_t * pb) -{ - return popcount64 (pa[0] ^ pb[0]) - + popcount64 (pa[1] ^ pb[1]) - + popcount64 (pa[2] ^ pb[2]) - + popcount64 (pa[3] ^ pb[3]); -} - - -/* Hamming distances for multiple of 64 bits */ -hamdis_t hamming ( - const uint64_t * bs1, - const uint64_t * bs2, - size_t nwords) -{ - size_t i; - hamdis_t h = 0; - for (i = 0; i < nwords; i++) - h += popcount64 (bs1[i] ^ bs2[i]); - return h; -} - - - -template -void hammings ( - const uint64_t * bs1, - const uint64_t * bs2, - size_t n1, size_t n2, - hamdis_t * dis) - -{ - size_t i, j; - const size_t nwords = nbits / 64; - for (i = 0; i < n1; i++) { - const uint64_t * __restrict bs1_ = bs1 + i * nwords; - hamdis_t * __restrict dis_ = dis + i * n2; - for (j = 0; j < n2; j++) - dis_[j] = hamming(bs1_, bs2 + j * nwords); - } -} - - - -void hammings ( - const uint64_t * bs1, - const uint64_t * bs2, - size_t n1, - size_t n2, - size_t nwords, - hamdis_t * __restrict dis) -{ - size_t i, j; - n1 *= nwords; - n2 *= nwords; - for (i = 0; i < n1; i+=nwords) { - const uint64_t * bs1_ = bs1+i; - for (j = 0; j < n2; j+=nwords) - dis[j] = hamming (bs1_, bs2+j, nwords); - } -} - - - - -/* Count number of matches given a max threshold */ -template -void hamming_count_thres ( - const uint64_t * bs1, - const uint64_t * bs2, - size_t n1, - size_t n2, - hamdis_t ht, - size_t * nptr) -{ - const size_t nwords = nbits / 64; - size_t i, j, posm = 0; - const uint64_t * bs2_ = bs2; - - for (i = 0; i < n1; i++) { - bs2 = bs2_; - for (j = 0; j < n2; j++) { - /* collect the match only if this satisfies the threshold */ - if (hamming (bs1, bs2) <= ht) - posm++; - bs2 += nwords; - } - bs1 += nwords; /* next signature */ - } - *nptr = posm; -} - - -template -void crosshamming_count_thres ( - const uint64_t * dbs, - size_t n, - int ht, - size_t * nptr) -{ - const size_t nwords = nbits / 64; - size_t i, j, posm = 0; - const uint64_t * bs1 = dbs; - for (i = 0; i < n; i++) { - const uint64_t * bs2 = bs1 + 2; - for (j = i + 1; j < n; j++) { - /* collect the match only if this satisfies the threshold */ - if (hamming (bs1, bs2) <= ht) - posm++; - bs2 += nwords; - } - bs1 += nwords; - } - *nptr = posm; -} - - -template -size_t match_hamming_thres ( - const uint64_t * bs1, - const uint64_t * bs2, - size_t n1, - size_t n2, - int ht, - int64_t * idx, - hamdis_t * hams) -{ - const size_t nwords = nbits / 64; - size_t i, j, posm = 0; - hamdis_t h; - const uint64_t * bs2_ = bs2; - for (i = 0; i < n1; i++) { - bs2 = bs2_; - for (j = 0; j < n2; j++) { - /* Here perform the real work of computing the distance */ - h = hamming (bs1, bs2); - - /* collect the match only if this satisfies the threshold */ - if (h <= ht) { - /* Enough space to store another match ? */ - *idx = i; idx++; - *idx = j; idx++; - *hams = h; - hams++; - posm++; - } - bs2+=nwords; /* next signature */ - } - bs1+=nwords; - } - return posm; -} - - -/* Return closest neighbors w.r.t Hamming distance, using a heap. */ -template -static -void hammings_knn_hc ( - int bytes_per_code, - int_maxheap_array_t * ha, - const uint8_t * bs1, - const uint8_t * bs2, - size_t n2, - bool order = true, - bool init_heap = true, - ConcurrentBitsetPtr bitset = nullptr) -{ - size_t k = ha->k; - - if ((bytes_per_code + k * (sizeof(hamdis_t) + sizeof(int64_t))) * ha->nh < size_1M) { - int thread_max_num = omp_get_max_threads(); - // init heap - size_t thread_heap_size = ha->nh * k; - size_t all_heap_size = thread_heap_size * thread_max_num; - hamdis_t *value = new hamdis_t[all_heap_size]; - int64_t *labels = new int64_t[all_heap_size]; - for (int i = 0; i < all_heap_size; i++) { - value[i] = 0x7fffffff; - labels[i] = -1; - } - - HammingComputer *hc = new HammingComputer[ha->nh]; - for (size_t i = 0; i < ha->nh; i++) { - hc[i].set(bs1 + i * bytes_per_code, bytes_per_code); - } - -#pragma omp parallel for - for (size_t j = 0; j < n2; j++) { - if(!bitset || !bitset->test(j)) { - int thread_no = omp_get_thread_num(); - - const uint8_t * bs2_ = bs2 + j * bytes_per_code; - for (size_t i = 0; i < ha->nh; i++) { - hamdis_t dis = hc[i].hamming (bs2_); - - hamdis_t * val_ = value + thread_no * thread_heap_size + i * k; - int64_t * ids_ = labels + thread_no * thread_heap_size + i * k; - if (dis < val_[0]) { - faiss::maxheap_swap_top (k, val_, ids_, dis, j); - } - } - } - } - - for (size_t t = 1; t < thread_max_num; t++) { - // merge heap - for (size_t i = 0; i < ha->nh; i++) { - hamdis_t * __restrict value_x = value + i * k; - int64_t * __restrict labels_x = labels + i * k; - hamdis_t *value_x_t = value_x + t * thread_heap_size; - int64_t *labels_x_t = labels_x + t * thread_heap_size; - for (size_t j = 0; j < k; j++) { - if (value_x_t[j] < value_x[0]) { - faiss::maxheap_swap_top (k, value_x, labels_x, value_x_t[j], labels_x_t[j]); - } - } - } - } - - // copy result - memcpy(ha->val, value, thread_heap_size * sizeof(hamdis_t)); - memcpy(ha->ids, labels, thread_heap_size * sizeof(int64_t)); - - delete[] hc; - delete[] value; - delete[] labels; - - } else { - if (init_heap) ha->heapify (); - const size_t block_size = hamming_batch_size; - for (size_t j0 = 0; j0 < n2; j0 += block_size) { - const size_t j1 = std::min(j0 + block_size, n2); -#pragma omp parallel for - for (size_t i = 0; i < ha->nh; i++) { - HammingComputer hc (bs1 + i * bytes_per_code, bytes_per_code); - - const uint8_t * bs2_ = bs2 + j0 * bytes_per_code; - hamdis_t dis; - hamdis_t * __restrict bh_val_ = ha->val + i * k; - int64_t * __restrict bh_ids_ = ha->ids + i * k; - size_t j; - for (j = j0; j < j1; j++, bs2_+= bytes_per_code) { - if(!bitset || !bitset->test(j)){ - dis = hc.hamming (bs2_); - if (dis < bh_val_[0]) { - faiss::maxheap_swap_top (k, bh_val_, bh_ids_, dis, j); - } - } - } - } - } - } - if (order) ha->reorder (); -} - -/* Return closest neighbors w.r.t Hamming distance, using max count. */ -template -static -void hammings_knn_mc ( - int bytes_per_code, - const uint8_t *a, - const uint8_t *b, - size_t na, - size_t nb, - size_t k, - int32_t *distances, - int64_t *labels, - ConcurrentBitsetPtr bitset = nullptr) -{ - const int nBuckets = bytes_per_code * 8 + 1; - std::vector all_counters(na * nBuckets, 0); - std::unique_ptr all_ids_per_dis(new int64_t[na * nBuckets * k]); - - std::vector> cs; - for (size_t i = 0; i < na; ++i) { - cs.push_back(HCounterState( - all_counters.data() + i * nBuckets, - all_ids_per_dis.get() + i * nBuckets * k, - a + i * bytes_per_code, - 8 * bytes_per_code, - k - )); - } - - const size_t block_size = hamming_batch_size; - for (size_t j0 = 0; j0 < nb; j0 += block_size) { - const size_t j1 = std::min(j0 + block_size, nb); -#pragma omp parallel for - for (size_t i = 0; i < na; ++i) { - for (size_t j = j0; j < j1; ++j) { - if (!bitset || !bitset->test(j)) { - cs[i].update_counter(b + j * bytes_per_code, j); - } - } - } - } - - for (size_t i = 0; i < na; ++i) { - HCounterState& csi = cs[i]; - - int nres = 0; - for (int b = 0; b < nBuckets && nres < k; b++) { - for (int l = 0; l < csi.counters[b] && nres < k; l++) { - labels[i * k + nres] = csi.ids_per_dis[b * k + l]; - distances[i * k + nres] = b; - nres++; - } - } - while (nres < k) { - labels[i * k + nres] = -1; - distances[i * k + nres] = std::numeric_limits::max(); - ++nres; - } - } -} - - - -// works faster than the template version -static -void hammings_knn_hc_1 ( - int_maxheap_array_t * ha, - const uint64_t * bs1, - const uint64_t * bs2, - size_t n2, - bool order = true, - bool init_heap = true, - ConcurrentBitsetPtr bitset = nullptr) -{ - const size_t nwords = 1; - size_t k = ha->k; - - if (init_heap) { - ha->heapify (); - } - - int thread_max_num = omp_get_max_threads(); - if (ha->nh == 1) { - // omp for n2 - int all_heap_size = thread_max_num * k; - hamdis_t *value = new hamdis_t[all_heap_size]; - int64_t *labels = new int64_t[all_heap_size]; - - // init heap - for (int i = 0; i < all_heap_size; i++) { - value[i] = 0x7fffffff; - } - const uint64_t bs1_ = bs1[0]; -#pragma omp parallel for - for (size_t j = 0; j < n2; j++) { - if(!bitset || !bitset->test(j)) { - hamdis_t dis = popcount64 (bs1_ ^ bs2[j]); - - int thread_no = omp_get_thread_num(); - hamdis_t * __restrict val_ = value + thread_no * k; - int64_t * __restrict ids_ = labels + thread_no * k; - if (dis < val_[0]) { - faiss::maxheap_swap_top (k, val_, ids_, dis, j); - } - } - } - // merge heap - hamdis_t * __restrict bh_val_ = ha->val; - int64_t * __restrict bh_ids_ = ha->ids; - for (int i = 0; i < all_heap_size; i++) { - if (value[i] < bh_val_[0]) { - faiss::maxheap_swap_top (k, bh_val_, bh_ids_, value[i], labels[i]); - } - } - - delete[] value; - delete[] labels; - - } else { -#pragma omp parallel for - for (size_t i = 0; i < ha->nh; i++) { - const uint64_t bs1_ = bs1 [i]; - const uint64_t * bs2_ = bs2; - hamdis_t dis; - hamdis_t * bh_val_ = ha->val + i * k; - hamdis_t bh_val_0 = bh_val_[0]; - int64_t * bh_ids_ = ha->ids + i * k; - size_t j; - for (j = 0; j < n2; j++, bs2_+= nwords) { - if(!bitset || !bitset->test(j)){ - dis = popcount64 (bs1_ ^ *bs2_); - if (dis < bh_val_0) { - faiss::maxheap_swap_top (k, bh_val_, bh_ids_, dis, j); - bh_val_0 = bh_val_[0]; - } - } - } - } - } - if (order) { - ha->reorder (); - } -} - - - - -/* Functions to maps vectors to bits. Assume proper allocation done beforehand, - meaning that b should be be able to receive as many bits as x may produce. */ - -/* - * dimension 0 corresponds to the least significant bit of b[0], or - * equivalently to the lsb of the first byte that is stored. - */ -void fvec2bitvec (const float * x, uint8_t * b, size_t d) -{ - for (int i = 0; i < d; i += 8) { - uint8_t w = 0; - uint8_t mask = 1; - int nj = i + 8 <= d ? 8 : d - i; - for (int j = 0; j < nj; j++) { - if (x[i + j] >= 0) - w |= mask; - mask <<= 1; - } - *b = w; - b++; - } -} - - - -/* Same but for n vectors. - Ensure that the ouptut b is byte-aligned (pad with 0s). */ -void fvecs2bitvecs (const float * x, uint8_t * b, size_t d, size_t n) -{ - const int64_t ncodes = ((d + 7) / 8); -#pragma omp parallel for if(n > 100000) - for (size_t i = 0; i < n; i++) - fvec2bitvec (x + i * d, b + i * ncodes, d); -} - - - -void bitvecs2fvecs ( - const uint8_t * b, - float * x, - size_t d, - size_t n) { - - const int64_t ncodes = ((d + 7) / 8); -#pragma omp parallel for if(n > 100000) - for (size_t i = 0; i < n; i++) { - binary_to_real (d, b + i * ncodes, x + i * d); - } -} - - -/* Reverse bit (NOT a optimized function, only used for print purpose) */ -static uint64_t uint64_reverse_bits (uint64_t b) -{ - int i; - uint64_t revb = 0; - for (i = 0; i < 64; i++) { - revb <<= 1; - revb |= b & 1; - b >>= 1; - } - return revb; -} - - -/* print the bit vector */ -void bitvec_print (const uint8_t * b, size_t d) -{ - size_t i, j; - for (i = 0; i < d; ) { - uint64_t brev = uint64_reverse_bits (* (uint64_t *) b); - for (j = 0; j < 64 && i < d; j++, i++) { - printf ("%d", (int) (brev & 1)); - brev >>= 1; - } - b += 8; - printf (" "); - } -} - - -void bitvec_shuffle (size_t n, size_t da, size_t db, - const int *order, - const uint8_t *a, - uint8_t *b) -{ - for(size_t i = 0; i < db; i++) { - FAISS_THROW_IF_NOT (order[i] >= 0 && order[i] < da); - } - size_t lda = (da + 7) / 8; - size_t ldb = (db + 7) / 8; - -#pragma omp parallel for if(n > 10000) - for (size_t i = 0; i < n; i++) { - const uint8_t *ai = a + i * lda; - uint8_t *bi = b + i * ldb; - memset (bi, 0, ldb); - for(size_t i = 0; i < db; i++) { - int o = order[i]; - uint8_t the_bit = (ai[o >> 3] >> (o & 7)) & 1; - bi[i >> 3] |= the_bit << (i & 7); - } - } - -} - - - -/*----------------------------------------*/ -/* Hamming distance computation and k-nn */ - - -#define C64(x) ((uint64_t *)x) - - -/* Compute a set of Hamming distances */ -void hammings ( - const uint8_t * a, - const uint8_t * b, - size_t na, size_t nb, - size_t ncodes, - hamdis_t * __restrict dis) -{ - FAISS_THROW_IF_NOT (ncodes % 8 == 0); - switch (ncodes) { - case 8: - faiss::hammings <64> (C64(a), C64(b), na, nb, dis); return; - case 16: - faiss::hammings <128> (C64(a), C64(b), na, nb, dis); return; - case 32: - faiss::hammings <256> (C64(a), C64(b), na, nb, dis); return; - case 64: - faiss::hammings <512> (C64(a), C64(b), na, nb, dis); return; - default: - faiss::hammings (C64(a), C64(b), na, nb, ncodes * 8, dis); return; - } -} - -void hammings_knn( - int_maxheap_array_t *ha, - const uint8_t *a, - const uint8_t *b, - size_t nb, - size_t ncodes, - int order) -{ - hammings_knn_hc(ha, a, b, nb, ncodes, order); -} - -void hammings_knn_hc ( - int_maxheap_array_t * ha, - const uint8_t * a, - const uint8_t * b, - size_t nb, - size_t ncodes, - int order, - ConcurrentBitsetPtr bitset) -{ - switch (ncodes) { - case 4: - hammings_knn_hc - (4, ha, a, b, nb, order, true, bitset); - break; - case 8: - hammings_knn_hc_1 (ha, C64(a), C64(b), nb, order, true, bitset); - // hammings_knn_hc - // (8, ha, a, b, nb, order, true); - break; - case 16: - hammings_knn_hc - (16, ha, a, b, nb, order, true, bitset); - break; - case 32: - hammings_knn_hc - (32, ha, a, b, nb, order, true, bitset); - break; - default: - if(ncodes % 8 == 0) { - hammings_knn_hc - (ncodes, ha, a, b, nb, order, true, bitset); - } else { - hammings_knn_hc - (ncodes, ha, a, b, nb, order, true, bitset); - - } - } -} - -void hammings_knn_mc( - const uint8_t * a, - const uint8_t * b, - size_t na, - size_t nb, - size_t k, - size_t ncodes, - int32_t *distances, - int64_t *labels, - ConcurrentBitsetPtr bitset) -{ - switch (ncodes) { - case 4: - hammings_knn_mc( - 4, a, b, na, nb, k, distances, labels, bitset - ); - break; - case 8: - // TODO(hoss): Write analog to hammings_knn_hc_1 - // hammings_knn_hc_1 (ha, C64(a), C64(b), nb, order, true); - hammings_knn_mc( - 8, a, b, na, nb, k, distances, labels, bitset - ); - break; - case 16: - hammings_knn_mc( - 16, a, b, na, nb, k, distances, labels, bitset - ); - break; - case 32: - hammings_knn_mc( - 32, a, b, na, nb, k, distances, labels, bitset - ); - break; - default: - if(ncodes % 8 == 0) { - hammings_knn_mc( - ncodes, a, b, na, nb, k, distances, labels, bitset - ); - } else { - hammings_knn_mc( - ncodes, a, b, na, nb, k, distances, labels, bitset - ); - } - } -} -template -static -void hamming_range_search_template ( - const uint8_t * a, - const uint8_t * b, - size_t na, - size_t nb, - int radius, - size_t code_size, - RangeSearchResult *res) -{ - -#pragma omp parallel - { - RangeSearchPartialResult pres (res); - -#pragma omp for - for (size_t i = 0; i < na; i++) { - HammingComputer hc (a + i * code_size, code_size); - const uint8_t * yi = b; - RangeQueryResult & qres = pres.new_result (i); - - for (size_t j = 0; j < nb; j++) { - int dis = hc.hamming (yi); - if (dis < radius) { - qres.add(dis, j); - } - yi += code_size; - } - } - pres.finalize (); - } -} - -void hamming_range_search ( - const uint8_t * a, - const uint8_t * b, - size_t na, - size_t nb, - int radius, - size_t code_size, - RangeSearchResult *result) -{ - -#define HC(name) hamming_range_search_template (a, b, na, nb, radius, code_size, result) - - switch(code_size) { - case 4: HC(HammingComputer4); break; - case 8: HC(HammingComputer8); break; - case 16: HC(HammingComputer16); break; - case 32: HC(HammingComputer32); break; - default: - if (code_size % 8 == 0) { - HC(HammingComputerM8); - } else { - HC(HammingComputerDefault); - } - } -#undef HC -} - - - -/* Count number of matches given a max threshold */ -void hamming_count_thres ( - const uint8_t * bs1, - const uint8_t * bs2, - size_t n1, - size_t n2, - hamdis_t ht, - size_t ncodes, - size_t * nptr) -{ - switch (ncodes) { - case 8: - faiss::hamming_count_thres <64> (C64(bs1), C64(bs2), - n1, n2, ht, nptr); - return; - case 16: - faiss::hamming_count_thres <128> (C64(bs1), C64(bs2), - n1, n2, ht, nptr); - return; - case 32: - faiss::hamming_count_thres <256> (C64(bs1), C64(bs2), - n1, n2, ht, nptr); - return; - case 64: - faiss::hamming_count_thres <512> (C64(bs1), C64(bs2), - n1, n2, ht, nptr); - return; - default: - FAISS_THROW_FMT ("not implemented for %zu bits", ncodes); - } -} - - -/* Count number of cross-matches given a threshold */ -void crosshamming_count_thres ( - const uint8_t * dbs, - size_t n, - hamdis_t ht, - size_t ncodes, - size_t * nptr) -{ - switch (ncodes) { - case 8: - faiss::crosshamming_count_thres <64> (C64(dbs), n, ht, nptr); - return; - case 16: - faiss::crosshamming_count_thres <128> (C64(dbs), n, ht, nptr); - return; - case 32: - faiss::crosshamming_count_thres <256> (C64(dbs), n, ht, nptr); - return; - case 64: - faiss::crosshamming_count_thres <512> (C64(dbs), n, ht, nptr); - return; - default: - FAISS_THROW_FMT ("not implemented for %zu bits", ncodes); - } -} - - -/* Returns all matches given a threshold */ -size_t match_hamming_thres ( - const uint8_t * bs1, - const uint8_t * bs2, - size_t n1, - size_t n2, - hamdis_t ht, - size_t ncodes, - int64_t * idx, - hamdis_t * dis) -{ - switch (ncodes) { - case 8: - return faiss::match_hamming_thres <64> (C64(bs1), C64(bs2), - n1, n2, ht, idx, dis); - case 16: - return faiss::match_hamming_thres <128> (C64(bs1), C64(bs2), - n1, n2, ht, idx, dis); - case 32: - return faiss::match_hamming_thres <256> (C64(bs1), C64(bs2), - n1, n2, ht, idx, dis); - case 64: - return faiss::match_hamming_thres <512> (C64(bs1), C64(bs2), - n1, n2, ht, idx, dis); - default: - FAISS_THROW_FMT ("not implemented for %zu bits", ncodes); - return 0; - } -} - - -#undef C64 - - - -/************************************* - * generalized Hamming distances - ************************************/ - - - -template -static void hamming_dis_inner_loop ( - const uint8_t *ca, - const uint8_t *cb, - size_t nb, - size_t code_size, - int k, - hamdis_t * bh_val_, - int64_t * bh_ids_) -{ - - HammingComputer hc (ca, code_size); - - for (size_t j = 0; j < nb; j++) { - int ndiff = hc.hamming (cb); - cb += code_size; - if (ndiff < bh_val_[0]) { - maxheap_swap_top (k, bh_val_, bh_ids_, ndiff, j); - } - } -} - -void generalized_hammings_knn_hc ( - int_maxheap_array_t * ha, - const uint8_t * a, - const uint8_t * b, - size_t nb, - size_t code_size, - int ordered) -{ - int na = ha->nh; - int k = ha->k; - - if (ordered) - ha->heapify (); - -#pragma omp parallel for - for (int i = 0; i < na; i++) { - const uint8_t *ca = a + i * code_size; - const uint8_t *cb = b; - - hamdis_t * bh_val_ = ha->val + i * k; - int64_t * bh_ids_ = ha->ids + i * k; - - switch (code_size) { - case 8: - hamming_dis_inner_loop - (ca, cb, nb, 8, k, bh_val_, bh_ids_); - break; - case 16: - hamming_dis_inner_loop - (ca, cb, nb, 16, k, bh_val_, bh_ids_); - break; - case 32: - hamming_dis_inner_loop - (ca, cb, nb, 32, k, bh_val_, bh_ids_); - break; - default: - hamming_dis_inner_loop - (ca, cb, nb, code_size, k, bh_val_, bh_ids_); - break; - } - } - - if (ordered) - ha->reorder (); - -} - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/utils/hamming.h b/core/src/index/thirdparty/faiss/utils/hamming.h deleted file mode 100644 index 38bdd651f2..0000000000 --- a/core/src/index/thirdparty/faiss/utils/hamming.h +++ /dev/null @@ -1,243 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -/* - * Hamming distances. The binary vector dimensionality should be a - * multiple of 8, as the elementary operations operate on bytes. If - * you need other sizes, just pad with 0s (this is done by function - * fvecs2bitvecs). - * - * User-defined type hamdis_t is used for distances because at this time - * it is still uncler clear how we will need to balance - * - flexibility in vector size (may need 16- or even 8-bit vectors) - * - memory usage - * - cache-misses when dealing with large volumes of data (fewer bits is better) - * - */ - -#ifndef FAISS_hamming_h -#define FAISS_hamming_h - - -#include - -#include -#include - -/* The Hamming distance type */ -typedef int32_t hamdis_t; - -namespace faiss { - -/************************************************** - * General bit vector functions - **************************************************/ - -struct RangeSearchResult; - -void bitvec_print (const uint8_t * b, size_t d); - - -/* Functions for casting vectors of regular types to compact bits. - They assume proper allocation done beforehand, meaning that b - should be be able to receive as many bits as x may produce. */ - -/* Makes an array of bits from the signs of a float array. The length - of the output array b is rounded up to byte size (allocate - accordingly) */ -void fvecs2bitvecs ( - const float * x, - uint8_t * b, - size_t d, - size_t n); - -void bitvecs2fvecs ( - const uint8_t * b, - float * x, - size_t d, - size_t n); - - -void fvec2bitvec (const float * x, uint8_t * b, size_t d); - -/** Shuffle the bits from b(i, j) := a(i, order[j]) - */ -void bitvec_shuffle (size_t n, size_t da, size_t db, - const int *order, - const uint8_t *a, - uint8_t *b); - - -/*********************************************** - * Generic reader/writer for bit strings - ***********************************************/ - - -struct BitstringWriter { - uint8_t *code; - size_t code_size; - size_t i; // current bit offset - - // code_size in bytes - BitstringWriter(uint8_t *code, int code_size); - - // write the nbit low bits of x - void write(uint64_t x, int nbit); -}; - -struct BitstringReader { - const uint8_t *code; - size_t code_size; - size_t i; - - // code_size in bytes - BitstringReader(const uint8_t *code, int code_size); - - // read nbit bits from the code - uint64_t read(int nbit); -}; - -/************************************************** - * Hamming distance computation functions - **************************************************/ - - - -extern size_t hamming_batch_size; - -inline int popcount64(uint64_t x) { - return __builtin_popcountl(x); -} - - -/** Compute a set of Hamming distances between na and nb binary vectors - * - * @param a size na * nbytespercode - * @param b size nb * nbytespercode - * @param nbytespercode should be multiple of 8 - * @param dis output distances, size na * nb - */ -void hammings ( - const uint8_t * a, - const uint8_t * b, - size_t na, size_t nb, - size_t nbytespercode, - hamdis_t * dis); - - - - -/** Return the k smallest Hamming distances for a set of binary query vectors, - * using a max heap. - * @param a queries, size ha->nh * ncodes - * @param b database, size nb * ncodes - * @param nb number of database vectors - * @param ncodes size of the binary codes (bytes) - * @param ordered if != 0: order the results by decreasing distance - * (may be bottleneck for k/n > 0.01) */ -void hammings_knn_hc ( - int_maxheap_array_t * ha, - const uint8_t * a, - const uint8_t * b, - size_t nb, - size_t ncodes, - int ordered, - ConcurrentBitsetPtr bitset = nullptr); - -/* Legacy alias to hammings_knn_hc. */ -void hammings_knn ( - int_maxheap_array_t * ha, - const uint8_t * a, - const uint8_t * b, - size_t nb, - size_t ncodes, - int ordered, - ConcurrentBitsetPtr bitset = nullptr); - -/** Return the k smallest Hamming distances for a set of binary query vectors, - * using counting max. - * @param a queries, size na * ncodes - * @param b database, size nb * ncodes - * @param na number of query vectors - * @param nb number of database vectors - * @param k number of vectors/distances to return - * @param ncodes size of the binary codes (bytes) - * @param distances output distances from each query vector to its k nearest - * neighbors - * @param labels output ids of the k nearest neighbors to each query vector - */ -void hammings_knn_mc ( - const uint8_t * a, - const uint8_t * b, - size_t na, - size_t nb, - size_t k, - size_t ncodes, - int32_t *distances, - int64_t *labels, - ConcurrentBitsetPtr bitset = nullptr); - -/** same as hammings_knn except we are doing a range search with radius */ -void hamming_range_search ( - const uint8_t * a, - const uint8_t * b, - size_t na, - size_t nb, - int radius, - size_t ncodes, - RangeSearchResult *result); - - -/* Counting the number of matches or of cross-matches (without returning them) - For use with function that assume pre-allocated memory */ -void hamming_count_thres ( - const uint8_t * bs1, - const uint8_t * bs2, - size_t n1, - size_t n2, - hamdis_t ht, - size_t ncodes, - size_t * nptr); - -/* Return all Hamming distances/index passing a thres. Pre-allocation of output - is required. Use hamming_count_thres to determine the proper size. */ -size_t match_hamming_thres ( - const uint8_t * bs1, - const uint8_t * bs2, - size_t n1, - size_t n2, - hamdis_t ht, - size_t ncodes, - int64_t * idx, - hamdis_t * dis); - -/* Cross-matching in a set of vectors */ -void crosshamming_count_thres ( - const uint8_t * dbs, - size_t n, - hamdis_t ht, - size_t ncodes, - size_t * nptr); - - -/* compute the Hamming distances between two codewords of nwords*64 bits */ -hamdis_t hamming ( - const uint64_t * bs1, - const uint64_t * bs2, - size_t nwords); - - - -} // namespace faiss - -// inlined definitions of HammingComputerXX and GenHammingComputerXX - -#include - -#endif /* FAISS_hamming_h */ diff --git a/core/src/index/thirdparty/faiss/utils/instruction_set.h b/core/src/index/thirdparty/faiss/utils/instruction_set.h deleted file mode 100644 index 90ccc2c368..0000000000 --- a/core/src/index/thirdparty/faiss/utils/instruction_set.h +++ /dev/null @@ -1,355 +0,0 @@ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -namespace faiss { - -class InstructionSet { - public: - static InstructionSet& - GetInstance() { - static InstructionSet inst; - return inst; - } - - private: - InstructionSet() - : nIds_{0}, - nExIds_{0}, - isIntel_{false}, - isAMD_{false}, - f_1_ECX_{0}, - f_1_EDX_{0}, - f_7_EBX_{0}, - f_7_ECX_{0}, - f_81_ECX_{0}, - f_81_EDX_{0}, - data_{}, - extdata_{} { - std::array cpui; - - // Calling __cpuid with 0x0 as the function_id argument - // gets the number of the highest valid function ID. - __cpuid(0, cpui[0], cpui[1], cpui[2], cpui[3]); - nIds_ = cpui[0]; - - for (int i = 0; i <= nIds_; ++i) { - __cpuid_count(i, 0, cpui[0], cpui[1], cpui[2], cpui[3]); - data_.push_back(cpui); - } - - // Capture vendor string - char vendor[0x20]; - memset(vendor, 0, sizeof(vendor)); - *reinterpret_cast(vendor) = data_[0][1]; - *reinterpret_cast(vendor + 4) = data_[0][3]; - *reinterpret_cast(vendor + 8) = data_[0][2]; - vendor_ = vendor; - if (vendor_ == "GenuineIntel") { - isIntel_ = true; - } else if (vendor_ == "AuthenticAMD") { - isAMD_ = true; - } - - // load bitset with flags for function 0x00000001 - if (nIds_ >= 1) { - f_1_ECX_ = data_[1][2]; - f_1_EDX_ = data_[1][3]; - } - - // load bitset with flags for function 0x00000007 - if (nIds_ >= 7) { - f_7_EBX_ = data_[7][1]; - f_7_ECX_ = data_[7][2]; - } - - // Calling __cpuid with 0x80000000 as the function_id argument - // gets the number of the highest valid extended ID. - __cpuid(0x80000000, cpui[0], cpui[1], cpui[2], cpui[3]); - nExIds_ = cpui[0]; - - char brand[0x40]; - memset(brand, 0, sizeof(brand)); - - for (int i = 0x80000000; i <= nExIds_; ++i) { - __cpuid_count(i, 0, cpui[0], cpui[1], cpui[2], cpui[3]); - extdata_.push_back(cpui); - } - - // load bitset with flags for function 0x80000001 - if (nExIds_ >= 0x80000001) { - f_81_ECX_ = extdata_[1][2]; - f_81_EDX_ = extdata_[1][3]; - } - - // Interpret CPU brand string if reported - if (nExIds_ >= 0x80000004) { - memcpy(brand, extdata_[2].data(), sizeof(cpui)); - memcpy(brand + 16, extdata_[3].data(), sizeof(cpui)); - memcpy(brand + 32, extdata_[4].data(), sizeof(cpui)); - brand_ = brand; - } - }; - - public: - // getters - std::string - Vendor(void) { - return vendor_; - } - std::string - Brand(void) { - return brand_; - } - - bool - SSE3(void) { - return f_1_ECX_[0]; - } - bool - PCLMULQDQ(void) { - return f_1_ECX_[1]; - } - bool - MONITOR(void) { - return f_1_ECX_[3]; - } - bool - SSSE3(void) { - return f_1_ECX_[9]; - } - bool - FMA(void) { - return f_1_ECX_[12]; - } - bool - CMPXCHG16B(void) { - return f_1_ECX_[13]; - } - bool - SSE41(void) { - return f_1_ECX_[19]; - } - bool - SSE42(void) { - return f_1_ECX_[20]; - } - bool - MOVBE(void) { - return f_1_ECX_[22]; - } - bool - POPCNT(void) { - return f_1_ECX_[23]; - } - bool - AES(void) { - return f_1_ECX_[25]; - } - bool - XSAVE(void) { - return f_1_ECX_[26]; - } - bool - OSXSAVE(void) { - return f_1_ECX_[27]; - } - bool - AVX(void) { - return f_1_ECX_[28]; - } - bool - F16C(void) { - return f_1_ECX_[29]; - } - bool - RDRAND(void) { - return f_1_ECX_[30]; - } - - bool - MSR(void) { - return f_1_EDX_[5]; - } - bool - CX8(void) { - return f_1_EDX_[8]; - } - bool - SEP(void) { - return f_1_EDX_[11]; - } - bool - CMOV(void) { - return f_1_EDX_[15]; - } - bool - CLFSH(void) { - return f_1_EDX_[19]; - } - bool - MMX(void) { - return f_1_EDX_[23]; - } - bool - FXSR(void) { - return f_1_EDX_[24]; - } - bool - SSE(void) { - return f_1_EDX_[25]; - } - bool - SSE2(void) { - return f_1_EDX_[26]; - } - - bool - FSGSBASE(void) { - return f_7_EBX_[0]; - } - bool - BMI1(void) { - return f_7_EBX_[3]; - } - bool - HLE(void) { - return isIntel_ && f_7_EBX_[4]; - } - bool - AVX2(void) { - return f_7_EBX_[5]; - } - bool - BMI2(void) { - return f_7_EBX_[8]; - } - bool - ERMS(void) { - return f_7_EBX_[9]; - } - bool - INVPCID(void) { - return f_7_EBX_[10]; - } - bool - RTM(void) { - return isIntel_ && f_7_EBX_[11]; - } - bool - AVX512F(void) { - return f_7_EBX_[16]; - } - bool - AVX512DQ(void) { - return f_7_EBX_[17]; - } - bool - RDSEED(void) { - return f_7_EBX_[18]; - } - bool - ADX(void) { - return f_7_EBX_[19]; - } - bool - AVX512PF(void) { - return f_7_EBX_[26]; - } - bool - AVX512ER(void) { - return f_7_EBX_[27]; - } - bool - AVX512CD(void) { - return f_7_EBX_[28]; - } - bool - SHA(void) { - return f_7_EBX_[29]; - } - bool - AVX512BW(void) { - return f_7_EBX_[30]; - } - bool - AVX512VL(void) { - return f_7_EBX_[31]; - } - - bool - PREFETCHWT1(void) { - return f_7_ECX_[0]; - } - - bool - LAHF(void) { - return f_81_ECX_[0]; - } - bool - LZCNT(void) { - return isIntel_ && f_81_ECX_[5]; - } - bool - ABM(void) { - return isAMD_ && f_81_ECX_[5]; - } - bool - SSE4a(void) { - return isAMD_ && f_81_ECX_[6]; - } - bool - XOP(void) { - return isAMD_ && f_81_ECX_[11]; - } - bool - TBM(void) { - return isAMD_ && f_81_ECX_[21]; - } - - bool - SYSCALL(void) { - return isIntel_ && f_81_EDX_[11]; - } - bool - MMXEXT(void) { - return isAMD_ && f_81_EDX_[22]; - } - bool - RDTSCP(void) { - return isIntel_ && f_81_EDX_[27]; - } - bool - _3DNOWEXT(void) { - return isAMD_ && f_81_EDX_[30]; - } - bool - _3DNOW(void) { - return isAMD_ && f_81_EDX_[31]; - } - - private: - int nIds_; - int nExIds_; - std::string vendor_; - std::string brand_; - bool isIntel_; - bool isAMD_; - std::bitset<32> f_1_ECX_; - std::bitset<32> f_1_EDX_; - std::bitset<32> f_7_EBX_; - std::bitset<32> f_7_ECX_; - std::bitset<32> f_81_ECX_; - std::bitset<32> f_81_EDX_; - std::vector> data_; - std::vector> extdata_; -}; - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/utils/jaccard-inl.h b/core/src/index/thirdparty/faiss/utils/jaccard-inl.h deleted file mode 100644 index 9aa7fbd924..0000000000 --- a/core/src/index/thirdparty/faiss/utils/jaccard-inl.h +++ /dev/null @@ -1,389 +0,0 @@ -namespace faiss { - - struct JaccardComputer8 { - uint64_t a0; - - JaccardComputer8 () {} - - JaccardComputer8 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - assert (code_size == 8); - const uint64_t *a = (uint64_t *)a8; - a0 = a[0]; - } - - inline float compute (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - int accu_num = popcount64 (b[0] & a0); - int accu_den = popcount64 (b[0] | a0); - if (accu_num == 0) - return 1.0; - return 1.0 - (float)(accu_num) / (float)(accu_den); - } - - }; - - struct JaccardComputer16 { - uint64_t a0, a1; - - JaccardComputer16 () {} - - JaccardComputer16 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - assert (code_size == 16); - const uint64_t *a = (uint64_t *)a8; - a0 = a[0]; a1 = a[1]; - } - - inline float compute (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - int accu_num = popcount64 (b[0] & a0) + popcount64 (b[1] & a1); - int accu_den = popcount64 (b[0] | a0) + popcount64 (b[1] | a1); - if (accu_num == 0) - return 1.0; - return 1.0 - (float)(accu_num) / (float)(accu_den); - } - - }; - - struct JaccardComputer32 { - uint64_t a0, a1, a2, a3; - - JaccardComputer32 () {} - - JaccardComputer32 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - assert (code_size == 32); - const uint64_t *a = (uint64_t *)a8; - a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3]; - } - - inline float compute (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - int accu_num = popcount64 (b[0] & a0) + popcount64 (b[1] & a1) + - popcount64 (b[2] & a2) + popcount64 (b[3] & a3); - int accu_den = popcount64 (b[0] | a0) + popcount64 (b[1] | a1) + - popcount64 (b[2] | a2) + popcount64 (b[3] | a3); - if (accu_num == 0) - return 1.0; - return 1.0 - (float)(accu_num) / (float)(accu_den); - } - - }; - - struct JaccardComputer64 { - uint64_t a0, a1, a2, a3, a4, a5, a6, a7; - - JaccardComputer64 () {} - - JaccardComputer64 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - assert (code_size == 64); - const uint64_t *a = (uint64_t *)a8; - a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3]; - a4 = a[4]; a5 = a[5]; a6 = a[6]; a7 = a[7]; - } - - inline float compute (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - int accu_num = popcount64 (b[0] & a0) + popcount64 (b[1] & a1) + - popcount64 (b[2] & a2) + popcount64 (b[3] & a3) + - popcount64 (b[4] & a4) + popcount64 (b[5] & a5) + - popcount64 (b[6] & a6) + popcount64 (b[7] & a7); - int accu_den = popcount64 (b[0] | a0) + popcount64 (b[1] | a1) + - popcount64 (b[2] | a2) + popcount64 (b[3] | a3) + - popcount64 (b[4] | a4) + popcount64 (b[5] | a5) + - popcount64 (b[6] | a6) + popcount64 (b[7] | a7); - if (accu_num == 0) - return 1.0; - return 1.0 - (float)(accu_num) / (float)(accu_den); - } - - }; - - struct JaccardComputer128 { - uint64_t a0, a1, a2, a3, a4, a5, a6, a7, - a8, a9, a10, a11, a12, a13, a14, a15; - - JaccardComputer128 () {} - - JaccardComputer128 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *au8, int code_size) { - assert (code_size == 128); - const uint64_t *a = (uint64_t *)au8; - a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3]; - a4 = a[4]; a5 = a[5]; a6 = a[6]; a7 = a[7]; - a8 = a[8]; a9 = a[9]; a10 = a[10]; a11 = a[11]; - a12 = a[12]; a13 = a[13]; a14 = a[14]; a15 = a[15]; - } - - inline float compute (const uint8_t *b16) const { - const uint64_t *b = (uint64_t *)b16; - int accu_num = popcount64 (b[0] & a0) + popcount64 (b[1] & a1) + - popcount64 (b[2] & a2) + popcount64 (b[3] & a3) + - popcount64 (b[4] & a4) + popcount64 (b[5] & a5) + - popcount64 (b[6] & a6) + popcount64 (b[7] & a7) + - popcount64 (b[8] & a8) + popcount64 (b[9] & a9) + - popcount64 (b[10] & a10) + popcount64 (b[11] & a11) + - popcount64 (b[12] & a12) + popcount64 (b[13] & a13) + - popcount64 (b[14] & a14) + popcount64 (b[15] & a15); - int accu_den = popcount64 (b[0] | a0) + popcount64 (b[1] | a1) + - popcount64 (b[2] | a2) + popcount64 (b[3] | a3) + - popcount64 (b[4] | a4) + popcount64 (b[5] | a5) + - popcount64 (b[6] | a6) + popcount64 (b[7] | a7) + - popcount64 (b[8] | a8) + popcount64 (b[9] | a9) + - popcount64 (b[10] | a10) + popcount64 (b[11] | a11) + - popcount64 (b[12] | a12) + popcount64 (b[13] | a13) + - popcount64 (b[14] | a14) + popcount64 (b[15] | a15); - if (accu_num == 0) - return 1.0; - return 1.0 - (float)(accu_num) / (float)(accu_den); - } - - }; - -struct JaccardComputer256 { - uint64_t a0,a1,a2,a3,a4,a5,a6,a7, - a8,a9,a10,a11,a12,a13,a14,a15, - a16,a17,a18,a19,a20,a21,a22,a23, - a24,a25,a26,a27,a28,a29,a30,a31; - - JaccardComputer256 () {} - - JaccardComputer256 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *au8, int code_size) { - assert (code_size == 256); - const uint64_t *a = (uint64_t *)au8; - a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3]; - a4 = a[4]; a5 = a[5]; a6 = a[6]; a7 = a[7]; - a8 = a[8]; a9 = a[9]; a10 = a[10]; a11 = a[11]; - a12 = a[12]; a13 = a[13]; a14 = a[14]; a15 = a[15]; - a16 = a[16]; a17 = a[17]; a18 = a[18]; a19 = a[19]; - a20 = a[20]; a21 = a[21]; a22 = a[22]; a23 = a[23]; - a24 = a[24]; a25 = a[25]; a26 = a[26]; a27 = a[27]; - a28 = a[28]; a29 = a[29]; a30 = a[30]; a31 = a[31]; - } - - inline float compute (const uint8_t *b16) const { - const uint64_t *b = (uint64_t *)b16; - int accu_num = popcount64 (b[0] & a0) + popcount64 (b[1] & a1) + - popcount64 (b[2] & a2) + popcount64 (b[3] & a3) + - popcount64 (b[4] & a4) + popcount64 (b[5] & a5) + - popcount64 (b[6] & a6) + popcount64 (b[7] & a7) + - popcount64 (b[8] & a8) + popcount64 (b[9] & a9) + - popcount64 (b[10] & a10) + popcount64 (b[11] & a11) + - popcount64 (b[12] & a12) + popcount64 (b[13] & a13) + - popcount64 (b[14] & a14) + popcount64 (b[15] & a15) + - popcount64 (b[16] & a16) + popcount64 (b[17] & a17) + - popcount64 (b[18] & a18) + popcount64 (b[19] & a19) + - popcount64 (b[20] & a20) + popcount64 (b[21] & a21) + - popcount64 (b[22] & a22) + popcount64 (b[23] & a23) + - popcount64 (b[24] & a24) + popcount64 (b[25] & a25) + - popcount64 (b[26] & a26) + popcount64 (b[27] & a27) + - popcount64 (b[28] & a28) + popcount64 (b[29] & a29) + - popcount64 (b[30] & a30) + popcount64 (b[31] & a31); - int accu_den = popcount64 (b[0] | a0) + popcount64 (b[1] | a1) + - popcount64 (b[2] | a2) + popcount64 (b[3] | a3) + - popcount64 (b[4] | a4) + popcount64 (b[5] | a5) + - popcount64 (b[6] | a6) + popcount64 (b[7] | a7) + - popcount64 (b[8] | a8) + popcount64 (b[9] | a9) + - popcount64 (b[10] | a10) + popcount64 (b[11] | a11) + - popcount64 (b[12] | a12) + popcount64 (b[13] | a13) + - popcount64 (b[14] | a14) + popcount64 (b[15] | a15) + - popcount64 (b[16] | a16) + popcount64 (b[17] | a17) + - popcount64 (b[18] | a18) + popcount64 (b[19] | a19) + - popcount64 (b[20] | a20) + popcount64 (b[21] | a21) + - popcount64 (b[22] | a22) + popcount64 (b[23] | a23) + - popcount64 (b[24] | a24) + popcount64 (b[25] | a25) + - popcount64 (b[26] | a26) + popcount64 (b[27] | a27) + - popcount64 (b[28] | a28) + popcount64 (b[29] | a29) + - popcount64 (b[30] | a30) + popcount64 (b[31] | a31); - if (accu_num == 0) - return 1.0; - return 1.0 - (float)(accu_num) / (float)(accu_den); - } - - }; - - struct JaccardComputer512 { - uint64_t a0,a1,a2,a3,a4,a5,a6,a7, - a8,a9,a10,a11,a12,a13,a14,a15, - a16,a17,a18,a19,a20,a21,a22,a23, - a24,a25,a26,a27,a28,a29,a30,a31, - a32,a33,a34,a35,a36,a37,a38,a39, - a40,a41,a42,a43,a44,a45,a46,a47, - a48,a49,a50,a51,a52,a53,a54,a55, - a56,a57,a58,a59,a60,a61,a62,a63; - - JaccardComputer512 () {} - - JaccardComputer512 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *au8, int code_size) { - assert (code_size == 512); - const uint64_t *a = (uint64_t *)au8; - a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3]; - a4 = a[4]; a5 = a[5]; a6 = a[6]; a7 = a[7]; - a8 = a[8]; a9 = a[9]; a10 = a[10]; a11 = a[11]; - a12 = a[12]; a13 = a[13]; a14 = a[14]; a15 = a[15]; - a16 = a[16]; a17 = a[17]; a18 = a[18]; a19 = a[19]; - a20 = a[20]; a21 = a[21]; a22 = a[22]; a23 = a[23]; - a24 = a[24]; a25 = a[25]; a26 = a[26]; a27 = a[27]; - a28 = a[28]; a29 = a[29]; a30 = a[30]; a31 = a[31]; - a32 = a[32]; a33 = a[33]; a34 = a[34]; a35 = a[35]; - a36 = a[36]; a37 = a[37]; a38 = a[38]; a39 = a[39]; - a40 = a[40]; a41 = a[41]; a42 = a[42]; a43 = a[43]; - a44 = a[44]; a45 = a[45]; a46 = a[46]; a47 = a[47]; - a48 = a[48]; a49 = a[49]; a50 = a[50]; a51 = a[51]; - a52 = a[52]; a53 = a[53]; a54 = a[54]; a55 = a[55]; - a56 = a[56]; a57 = a[57]; a58 = a[58]; a59 = a[59]; - a60 = a[60]; a61 = a[61]; a62 = a[62]; a63 = a[63]; - } - - inline float compute (const uint8_t *b16) const { - const uint64_t *b = (uint64_t *)b16; - int accu_num = popcount64 (b[0] & a0) + popcount64 (b[1] & a1) + - popcount64 (b[2] & a2) + popcount64 (b[3] & a3) + - popcount64 (b[4] & a4) + popcount64 (b[5] & a5) + - popcount64 (b[6] & a6) + popcount64 (b[7] & a7) + - popcount64 (b[8] & a8) + popcount64 (b[9] & a9) + - popcount64 (b[10] & a10) + popcount64 (b[11] & a11) + - popcount64 (b[12] & a12) + popcount64 (b[13] & a13) + - popcount64 (b[14] & a14) + popcount64 (b[15] & a15) + - popcount64 (b[16] & a16) + popcount64 (b[17] & a17) + - popcount64 (b[18] & a18) + popcount64 (b[19] & a19) + - popcount64 (b[20] & a20) + popcount64 (b[21] & a21) + - popcount64 (b[22] & a22) + popcount64 (b[23] & a23) + - popcount64 (b[24] & a24) + popcount64 (b[25] & a25) + - popcount64 (b[26] & a26) + popcount64 (b[27] & a27) + - popcount64 (b[28] & a28) + popcount64 (b[29] & a29) + - popcount64 (b[30] & a30) + popcount64 (b[31] & a31) + - popcount64 (b[32] & a32) + popcount64 (b[33] & a33) + - popcount64 (b[34] & a34) + popcount64 (b[35] & a35) + - popcount64 (b[36] & a36) + popcount64 (b[37] & a37) + - popcount64 (b[38] & a38) + popcount64 (b[39] & a39) + - popcount64 (b[40] & a40) + popcount64 (b[41] & a41) + - popcount64 (b[42] & a42) + popcount64 (b[43] & a43) + - popcount64 (b[44] & a44) + popcount64 (b[45] & a45) + - popcount64 (b[46] & a46) + popcount64 (b[47] & a47) + - popcount64 (b[48] & a48) + popcount64 (b[49] & a49) + - popcount64 (b[50] & a50) + popcount64 (b[51] & a51) + - popcount64 (b[52] & a52) + popcount64 (b[53] & a53) + - popcount64 (b[54] & a54) + popcount64 (b[55] & a55) + - popcount64 (b[56] & a56) + popcount64 (b[57] & a57) + - popcount64 (b[58] & a58) + popcount64 (b[59] & a59) + - popcount64 (b[60] & a60) + popcount64 (b[61] & a61) + - popcount64 (b[62] & a62) + popcount64 (b[63] & a63); - int accu_den = popcount64 (b[0] | a0) + popcount64 (b[1] | a1) + - popcount64 (b[2] | a2) + popcount64 (b[3] | a3) + - popcount64 (b[4] | a4) + popcount64 (b[5] | a5) + - popcount64 (b[6] | a6) + popcount64 (b[7] | a7) + - popcount64 (b[8] | a8) + popcount64 (b[9] | a9) + - popcount64 (b[10] | a10) + popcount64 (b[11] | a11) + - popcount64 (b[12] | a12) + popcount64 (b[13] | a13) + - popcount64 (b[14] | a14) + popcount64 (b[15] | a15) + - popcount64 (b[16] | a16) + popcount64 (b[17] | a17) + - popcount64 (b[18] | a18) + popcount64 (b[19] | a19) + - popcount64 (b[20] | a20) + popcount64 (b[21] | a21) + - popcount64 (b[22] | a22) + popcount64 (b[23] | a23) + - popcount64 (b[24] | a24) + popcount64 (b[25] | a25) + - popcount64 (b[26] | a26) + popcount64 (b[27] | a27) + - popcount64 (b[28] | a28) + popcount64 (b[29] | a29) + - popcount64 (b[30] | a30) + popcount64 (b[31] | a31) + - popcount64 (b[32] | a32) + popcount64 (b[33] | a33) + - popcount64 (b[34] | a34) + popcount64 (b[35] | a35) + - popcount64 (b[36] | a36) + popcount64 (b[37] | a37) + - popcount64 (b[38] | a38) + popcount64 (b[39] | a39) + - popcount64 (b[40] | a40) + popcount64 (b[41] | a41) + - popcount64 (b[42] | a42) + popcount64 (b[43] | a43) + - popcount64 (b[44] | a44) + popcount64 (b[45] | a45) + - popcount64 (b[46] | a46) + popcount64 (b[47] | a47) + - popcount64 (b[48] | a48) + popcount64 (b[49] | a49) + - popcount64 (b[50] | a50) + popcount64 (b[51] | a51) + - popcount64 (b[52] | a52) + popcount64 (b[53] | a53) + - popcount64 (b[54] | a54) + popcount64 (b[55] | a55) + - popcount64 (b[56] | a56) + popcount64 (b[57] | a57) + - popcount64 (b[58] | a58) + popcount64 (b[59] | a59) + - popcount64 (b[60] | a60) + popcount64 (b[61] | a61) + - popcount64 (b[62] | a62) + popcount64 (b[63] | a63); - if (accu_num == 0) - return 1.0; - return 1.0 - (float)(accu_num) / (float)(accu_den); - } - - }; - - struct JaccardComputerDefault { - const uint8_t *a; - int n; - - JaccardComputerDefault () {} - - JaccardComputerDefault (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - a = a8; - n = code_size; - } - - float compute (const uint8_t *b8) const { - int accu_num = 0; - int accu_den = 0; - for (int i = 0; i < n; i++) { - accu_num += popcount64(a[i] & b8[i]); - accu_den += popcount64(a[i] | b8[i]); - } - if (accu_num == 0) - return 1.0; - return 1.0 - (float)(accu_num) / (float)(accu_den); - } - - }; - -// default template - template - struct JaccardComputer: JaccardComputerDefault { - JaccardComputer (const uint8_t *a, int code_size): - JaccardComputerDefault(a, code_size) {} - }; - -#define SPECIALIZED_HC(CODE_SIZE) \ - template<> struct JaccardComputer: \ - JaccardComputer ## CODE_SIZE { \ - JaccardComputer (const uint8_t *a): \ - JaccardComputer ## CODE_SIZE(a, CODE_SIZE) {} \ - } - - SPECIALIZED_HC(8); - SPECIALIZED_HC(16); - SPECIALIZED_HC(32); - SPECIALIZED_HC(64); - SPECIALIZED_HC(128); - SPECIALIZED_HC(256); - SPECIALIZED_HC(512); - -#undef SPECIALIZED_HC - -} diff --git a/core/src/index/thirdparty/faiss/utils/random.cpp b/core/src/index/thirdparty/faiss/utils/random.cpp deleted file mode 100644 index 7f50e0eb1c..0000000000 --- a/core/src/index/thirdparty/faiss/utils/random.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -namespace faiss { - -/************************************************** - * Random data generation functions - **************************************************/ - -RandomGenerator::RandomGenerator (int64_t seed) - : mt((unsigned int)seed) {} - -int RandomGenerator::rand_int () -{ - return mt() & 0x7fffffff; -} - -int64_t RandomGenerator::rand_int64 () -{ - return int64_t(rand_int()) | int64_t(rand_int()) << 31; -} - -int RandomGenerator::rand_int (int max) -{ - return mt() % max; -} - -float RandomGenerator::rand_float () -{ - return mt() / float(mt.max()); -} - -double RandomGenerator::rand_double () -{ - return mt() / double(mt.max()); -} - - -/*********************************************************************** - * Random functions in this C file only exist because Torch - * counterparts are slow and not multi-threaded. Typical use is for - * more than 1-100 billion values. */ - - -/* Generate a set of random floating point values such that x[i] in [0,1] - multi-threading. For this reason, we rely on re-entreant functions. */ -void float_rand (float * x, size_t n, int64_t seed) -{ - // only try to parallelize on large enough arrays - const size_t nblock = n < 1024 ? 1 : 1024; - - RandomGenerator rng0 (seed); - int a0 = rng0.rand_int (), b0 = rng0.rand_int (); - -#pragma omp parallel for - for (size_t j = 0; j < nblock; j++) { - - RandomGenerator rng (a0 + j * b0); - - const size_t istart = j * n / nblock; - const size_t iend = (j + 1) * n / nblock; - - for (size_t i = istart; i < iend; i++) - x[i] = rng.rand_float (); - } -} - - -void float_randn (float * x, size_t n, int64_t seed) -{ - // only try to parallelize on large enough arrays - const size_t nblock = n < 1024 ? 1 : 1024; - - RandomGenerator rng0 (seed); - int a0 = rng0.rand_int (), b0 = rng0.rand_int (); - -#pragma omp parallel for - for (size_t j = 0; j < nblock; j++) { - RandomGenerator rng (a0 + j * b0); - - double a = 0, b = 0, s = 0; - int state = 0; /* generate two number per "do-while" loop */ - - const size_t istart = j * n / nblock; - const size_t iend = (j + 1) * n / nblock; - - for (size_t i = istart; i < iend; i++) { - /* Marsaglia's method (see Knuth) */ - if (state == 0) { - do { - a = 2.0 * rng.rand_double () - 1; - b = 2.0 * rng.rand_double () - 1; - s = a * a + b * b; - } while (s >= 1.0); - x[i] = a * sqrt(-2.0 * log(s) / s); - } - else - x[i] = b * sqrt(-2.0 * log(s) / s); - state = 1 - state; - } - } -} - - -/* Integer versions */ -void int64_rand (int64_t * x, size_t n, int64_t seed) -{ - // only try to parallelize on large enough arrays - const size_t nblock = n < 1024 ? 1 : 1024; - - RandomGenerator rng0 (seed); - int a0 = rng0.rand_int (), b0 = rng0.rand_int (); - -#pragma omp parallel for - for (size_t j = 0; j < nblock; j++) { - - RandomGenerator rng (a0 + j * b0); - - const size_t istart = j * n / nblock; - const size_t iend = (j + 1) * n / nblock; - for (size_t i = istart; i < iend; i++) - x[i] = rng.rand_int64 (); - } -} - -void int64_rand_max (int64_t * x, size_t n, uint64_t max, int64_t seed) -{ - // only try to parallelize on large enough arrays - const size_t nblock = n < 1024 ? 1 : 1024; - - RandomGenerator rng0 (seed); - int a0 = rng0.rand_int (), b0 = rng0.rand_int (); - -#pragma omp parallel for - for (size_t j = 0; j < nblock; j++) { - - RandomGenerator rng (a0 + j * b0); - - const size_t istart = j * n / nblock; - const size_t iend = (j + 1) * n / nblock; - for (size_t i = istart; i < iend; i++) - x[i] = rng.rand_int64 () % max; - } -} - - -void rand_perm (int *perm, size_t n, int64_t seed) -{ - for (size_t i = 0; i < n; i++) perm[i] = i; - - RandomGenerator rng (seed); - - for (size_t i = 0; i + 1 < n; i++) { - int i2 = i + rng.rand_int (n - i); - std::swap(perm[i], perm[i2]); - } -} - - - - -void byte_rand (uint8_t * x, size_t n, int64_t seed) -{ - // only try to parallelize on large enough arrays - const size_t nblock = n < 1024 ? 1 : 1024; - - RandomGenerator rng0 (seed); - int a0 = rng0.rand_int (), b0 = rng0.rand_int (); - -#pragma omp parallel for - for (size_t j = 0; j < nblock; j++) { - - RandomGenerator rng (a0 + j * b0); - - const size_t istart = j * n / nblock; - const size_t iend = (j + 1) * n / nblock; - - size_t i; - for (i = istart; i < iend; i++) - x[i] = rng.rand_int64 (); - } -} - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/utils/random.h b/core/src/index/thirdparty/faiss/utils/random.h deleted file mode 100644 index e94ac068cf..0000000000 --- a/core/src/index/thirdparty/faiss/utils/random.h +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -/* Random generators. Implemented here for speed and to make - * sequences reproducible. - */ - -#pragma once - -#include -#include - - -namespace faiss { - -/************************************************** - * Random data generation functions - **************************************************/ - -/// random generator that can be used in multithreaded contexts -struct RandomGenerator { - - std::mt19937 mt; - - /// random positive integer - int rand_int (); - - /// random int64_t - int64_t rand_int64 (); - - /// generate random integer between 0 and max-1 - int rand_int (int max); - - /// between 0 and 1 - float rand_float (); - - double rand_double (); - - explicit RandomGenerator (int64_t seed = 1234); -}; - -/* Generate an array of uniform random floats / multi-threaded implementation */ -void float_rand (float * x, size_t n, int64_t seed); -void float_randn (float * x, size_t n, int64_t seed); -void int64_rand (int64_t * x, size_t n, int64_t seed); -void byte_rand (uint8_t * x, size_t n, int64_t seed); -// max is actually the maximum value + 1 -void int64_rand_max (int64_t * x, size_t n, uint64_t max, int64_t seed); - -/* random permutation */ -void rand_perm (int * perm, size_t n, int64_t seed); - - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/utils/substructure-inl.h b/core/src/index/thirdparty/faiss/utils/substructure-inl.h deleted file mode 100644 index aa57a5a646..0000000000 --- a/core/src/index/thirdparty/faiss/utils/substructure-inl.h +++ /dev/null @@ -1,302 +0,0 @@ -namespace faiss { - - struct SubstructureComputer8 { - uint64_t a0; - - SubstructureComputer8 () {} - - SubstructureComputer8 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - assert (code_size == 8); - const uint64_t *a = (uint64_t *)a8; - a0 = a[0]; - } - - inline bool compute (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - return (a0 & b[0]) == a0; - } - - }; - - struct SubstructureComputer16 { - uint64_t a0, a1; - - SubstructureComputer16 () {} - - SubstructureComputer16 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - assert (code_size == 16); - const uint64_t *a = (uint64_t *)a8; - a0 = a[0]; a1 = a[1]; - } - - inline bool compute (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - return (a0 & b[0]) == a0 && (a1 & b[1]) == a1; - } - - }; - - struct SubstructureComputer32 { - uint64_t a0, a1, a2, a3; - - SubstructureComputer32 () {} - - SubstructureComputer32 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - assert (code_size == 32); - const uint64_t *a = (uint64_t *)a8; - a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3]; - } - - inline bool compute (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - return (a0 & b[0]) == a0 && (a1 & b[1]) == a1 && - (a2 & b[2]) == a2 && (a3 & b[3]) == a3; - } - - }; - - struct SubstructureComputer64 { - uint64_t a0, a1, a2, a3, a4, a5, a6, a7; - - SubstructureComputer64 () {} - - SubstructureComputer64 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - assert (code_size == 64); - const uint64_t *a = (uint64_t *)a8; - a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3]; - a4 = a[4]; a5 = a[5]; a6 = a[6]; a7 = a[7]; - } - - inline bool compute (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - return (a0 & b[0]) == a0 && (a1 & b[1]) == a1 && - (a2 & b[2]) == a2 && (a3 & b[3]) == a3 && - (a4 & b[4]) == a4 && (a5 & b[5]) == a5 && - (a6 & b[6]) == a6 && (a7 & b[7]) == a7; - } - - }; - - struct SubstructureComputer128 { - uint64_t a0, a1, a2, a3, a4, a5, a6, a7, - a8, a9, a10, a11, a12, a13, a14, a15; - - SubstructureComputer128 () {} - - SubstructureComputer128 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *au8, int code_size) { - assert (code_size == 128); - const uint64_t *a = (uint64_t *)au8; - a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3]; - a4 = a[4]; a5 = a[5]; a6 = a[6]; a7 = a[7]; - a8 = a[8]; a9 = a[9]; a10 = a[10]; a11 = a[11]; - a12 = a[12]; a13 = a[13]; a14 = a[14]; a15 = a[15]; - } - - inline bool compute (const uint8_t *b16) const { - const uint64_t *b = (uint64_t *)b16; - return (a0 & b[0]) == a0 && (a1 & b[1]) == a1 && - (a2 & b[2]) == a2 && (a3 & b[3]) == a3 && - (a4 & b[4]) == a4 && (a5 & b[5]) == a5 && - (a6 & b[6]) == a6 && (a7 & b[7]) == a7 && - (a8 & b[8]) == a8 && (a9 & b[9]) == a9 && - (a10 & b[10]) == a10 && (a11 & b[11]) == a11 && - (a12 & b[12]) == a12 && (a13 & b[13]) == a13 && - (a14 & b[14]) == a14 && (a15 & b[15]) == a15; - } - - }; - - struct SubstructureComputer256 { - uint64_t a0,a1,a2,a3,a4,a5,a6,a7, - a8,a9,a10,a11,a12,a13,a14,a15, - a16,a17,a18,a19,a20,a21,a22,a23, - a24,a25,a26,a27,a28,a29,a30,a31; - - SubstructureComputer256 () {} - - SubstructureComputer256 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *au8, int code_size) { - assert (code_size == 256); - const uint64_t *a = (uint64_t *)au8; - a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3]; - a4 = a[4]; a5 = a[5]; a6 = a[6]; a7 = a[7]; - a8 = a[8]; a9 = a[9]; a10 = a[10]; a11 = a[11]; - a12 = a[12]; a13 = a[13]; a14 = a[14]; a15 = a[15]; - a16 = a[16]; a17 = a[17]; a18 = a[18]; a19 = a[19]; - a20 = a[20]; a21 = a[21]; a22 = a[22]; a23 = a[23]; - a24 = a[24]; a25 = a[25]; a26 = a[26]; a27 = a[27]; - a28 = a[28]; a29 = a[29]; a30 = a[30]; a31 = a[31]; - } - - inline bool compute (const uint8_t *b16) const { - const uint64_t *b = (uint64_t *)b16; - return (a0 & b[0]) == a0 && (a1 & b[1]) == a1 && - (a2 & b[2]) == a2 && (a3 & b[3]) == a3 && - (a4 & b[4]) == a4 && (a5 & b[5]) == a5 && - (a6 & b[6]) == a6 && (a7 & b[7]) == a7 && - (a8 & b[8]) == a8 && (a9 & b[9]) == a9 && - (a10 & b[10]) == a10 && (a11 & b[11]) == a11 && - (a12 & b[12]) == a12 && (a13 & b[13]) == a13 && - (a14 & b[14]) == a14 && (a15 & b[15]) == a15 && - (a16 & b[16]) == a16 && (a17 & b[17]) == a17 && - (a18 & b[18]) == a18 && (a19 & b[19]) == a19 && - (a20 & b[20]) == a20 && (a21 & b[21]) == a21 && - (a22 & b[22]) == a22 && (a23 & b[23]) == a23 && - (a24 & b[24]) == a24 && (a25 & b[25]) == a25 && - (a26 & b[26]) == a26 && (a27 & b[27]) == a27 && - (a28 & b[28]) == a28 && (a29 & b[29]) == a29 && - (a30 & b[30]) == a30 && (a31 & b[31]) == a31; - } - - }; - - struct SubstructureComputer512 { - uint64_t a0,a1,a2,a3,a4,a5,a6,a7, - a8,a9,a10,a11,a12,a13,a14,a15, - a16,a17,a18,a19,a20,a21,a22,a23, - a24,a25,a26,a27,a28,a29,a30,a31, - a32,a33,a34,a35,a36,a37,a38,a39, - a40,a41,a42,a43,a44,a45,a46,a47, - a48,a49,a50,a51,a52,a53,a54,a55, - a56,a57,a58,a59,a60,a61,a62,a63; - - SubstructureComputer512 () {} - - SubstructureComputer512 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *au8, int code_size) { - assert (code_size == 512); - const uint64_t *a = (uint64_t *)au8; - a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3]; - a4 = a[4]; a5 = a[5]; a6 = a[6]; a7 = a[7]; - a8 = a[8]; a9 = a[9]; a10 = a[10]; a11 = a[11]; - a12 = a[12]; a13 = a[13]; a14 = a[14]; a15 = a[15]; - a16 = a[16]; a17 = a[17]; a18 = a[18]; a19 = a[19]; - a20 = a[20]; a21 = a[21]; a22 = a[22]; a23 = a[23]; - a24 = a[24]; a25 = a[25]; a26 = a[26]; a27 = a[27]; - a28 = a[28]; a29 = a[29]; a30 = a[30]; a31 = a[31]; - a32 = a[32]; a33 = a[33]; a34 = a[34]; a35 = a[35]; - a36 = a[36]; a37 = a[37]; a38 = a[38]; a39 = a[39]; - a40 = a[40]; a41 = a[41]; a42 = a[42]; a43 = a[43]; - a44 = a[44]; a45 = a[45]; a46 = a[46]; a47 = a[47]; - a48 = a[48]; a49 = a[49]; a50 = a[50]; a51 = a[51]; - a52 = a[52]; a53 = a[53]; a54 = a[54]; a55 = a[55]; - a56 = a[56]; a57 = a[57]; a58 = a[58]; a59 = a[59]; - a60 = a[60]; a61 = a[61]; a62 = a[62]; a63 = a[63]; - } - - inline bool compute (const uint8_t *b16) const { - const uint64_t *b = (uint64_t *)b16; - return (a0 & b[0]) == a0 && (a1 & b[1]) == a1 && - (a2 & b[2]) == a2 && (a3 & b[3]) == a3 && - (a4 & b[4]) == a4 && (a5 & b[5]) == a5 && - (a6 & b[6]) == a6 && (a7 & b[7]) == a7 && - (a8 & b[8]) == a8 && (a9 & b[9]) == a9 && - (a10 & b[10]) == a10 && (a11 & b[11]) == a11 && - (a12 & b[12]) == a12 && (a13 & b[13]) == a13 && - (a14 & b[14]) == a14 && (a15 & b[15]) == a15 && - (a16 & b[16]) == a16 && (a17 & b[17]) == a17 && - (a18 & b[18]) == a18 && (a19 & b[19]) == a19 && - (a20 & b[20]) == a20 && (a21 & b[21]) == a21 && - (a22 & b[22]) == a22 && (a23 & b[23]) == a23 && - (a24 & b[24]) == a24 && (a25 & b[25]) == a25 && - (a26 & b[26]) == a26 && (a27 & b[27]) == a27 && - (a28 & b[28]) == a28 && (a29 & b[29]) == a29 && - (a30 & b[30]) == a30 && (a31 & b[31]) == a31 && - (a32 & b[32]) == a32 && (a33 & b[33]) == a33 && - (a34 & b[34]) == a34 && (a35 & b[35]) == a35 && - (a36 & b[36]) == a36 && (a37 & b[37]) == a37 && - (a38 & b[38]) == a38 && (a39 & b[39]) == a39 && - (a40 & b[40]) == a40 && (a41 & b[41]) == a41 && - (a42 & b[42]) == a42 && (a43 & b[43]) == a43 && - (a44 & b[44]) == a44 && (a45 & b[45]) == a45 && - (a46 & b[46]) == a46 && (a47 & b[47]) == a47 && - (a48 & b[48]) == a48 && (a49 & b[49]) == a49 && - (a50 & b[50]) == a50 && (a51 & b[51]) == a51 && - (a52 & b[52]) == a52 && (a53 & b[53]) == a53 && - (a54 & b[54]) == a54 && (a55 & b[55]) == a55 && - (a56 & b[56]) == a56 && (a57 & b[57]) == a57 && - (a58 & b[58]) == a58 && (a59 & b[59]) == a59 && - (a60 & b[60]) == a60 && (a61 & b[61]) == a61 && - (a62 & b[62]) == a62 && (a63 & b[63]) == a63; - } - - }; - - struct SubstructureComputerDefault { - const uint8_t *a; - int n; - - SubstructureComputerDefault () {} - - SubstructureComputerDefault (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - a = a8; - n = code_size; - } - - bool compute (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - for (int i = 0; i < n; i++) { - if ((a[i] & b[i]) != a[i]) { - return false; - } - } - return true; - } - - }; - -// default template - template - struct SubstructureComputer: SubstructureComputerDefault { - SubstructureComputer (const uint8_t *a, int code_size): - SubstructureComputerDefault(a, code_size) {} - }; - -#define SPECIALIZED_HC(CODE_SIZE) \ - template<> struct SubstructureComputer: \ - SubstructureComputer ## CODE_SIZE { \ - SubstructureComputer (const uint8_t *a): \ - SubstructureComputer ## CODE_SIZE(a, CODE_SIZE) {} \ - } - - SPECIALIZED_HC(8); - SPECIALIZED_HC(16); - SPECIALIZED_HC(32); - SPECIALIZED_HC(64); - SPECIALIZED_HC(128); - SPECIALIZED_HC(256); - SPECIALIZED_HC(512); - -#undef SPECIALIZED_HC - -} diff --git a/core/src/index/thirdparty/faiss/utils/superstructure-inl.h b/core/src/index/thirdparty/faiss/utils/superstructure-inl.h deleted file mode 100644 index e8b384e75f..0000000000 --- a/core/src/index/thirdparty/faiss/utils/superstructure-inl.h +++ /dev/null @@ -1,302 +0,0 @@ -namespace faiss { - - struct SuperstructureComputer8 { - uint64_t a0; - - SuperstructureComputer8 () {} - - SuperstructureComputer8 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - assert (code_size == 8); - const uint64_t *a = (uint64_t *)a8; - a0 = a[0]; - } - - inline bool compute (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - return (a0 & b[0]) == b[0]; - } - - }; - - struct SuperstructureComputer16 { - uint64_t a0, a1; - - SuperstructureComputer16 () {} - - SuperstructureComputer16 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - assert (code_size == 16); - const uint64_t *a = (uint64_t *)a8; - a0 = a[0]; a1 = a[1]; - } - - inline bool compute (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - return (a0 & b[0]) == b[0] && (a1 & b[1]) == b[1]; - } - - }; - - struct SuperstructureComputer32 { - uint64_t a0, a1, a2, a3; - - SuperstructureComputer32 () {} - - SuperstructureComputer32 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - assert (code_size == 32); - const uint64_t *a = (uint64_t *)a8; - a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3]; - } - - inline bool compute (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - return (a0 & b[0]) == b[0] && (a1 & b[1]) == b[1] && - (a2 & b[2]) == b[2] && (a3 & b[3]) == b[3]; - } - - }; - - struct SuperstructureComputer64 { - uint64_t a0, a1, a2, a3, a4, a5, a6, a7; - - SuperstructureComputer64 () {} - - SuperstructureComputer64 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - assert (code_size == 64); - const uint64_t *a = (uint64_t *)a8; - a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3]; - a4 = a[4]; a5 = a[5]; a6 = a[6]; a7 = a[7]; - } - - inline bool compute (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - return (a0 & b[0]) == b[0] && (a1 & b[1]) == b[1] && - (a2 & b[2]) == b[2] && (a3 & b[3]) == b[3] && - (a4 & b[4]) == b[4] && (a5 & b[5]) == b[5] && - (a6 & b[6]) == b[6] && (a7 & b[7]) == b[7]; - } - - }; - - struct SuperstructureComputer128 { - uint64_t a0, a1, a2, a3, a4, a5, a6, a7, - a8, a9, a10, a11, a12, a13, a14, a15; - - SuperstructureComputer128 () {} - - SuperstructureComputer128 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *au8, int code_size) { - assert (code_size == 128); - const uint64_t *a = (uint64_t *)au8; - a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3]; - a4 = a[4]; a5 = a[5]; a6 = a[6]; a7 = a[7]; - a8 = a[8]; a9 = a[9]; a10 = a[10]; a11 = a[11]; - a12 = a[12]; a13 = a[13]; a14 = a[14]; a15 = a[15]; - } - - inline float compute (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - return (a0 & b[0]) == b[0] && (a1 & b[1]) == b[1] && - (a2 & b[2]) == b[2] && (a3 & b[3]) == b[3] && - (a4 & b[4]) == b[4] && (a5 & b[5]) == b[5] && - (a6 & b[6]) == b[6] && (a7 & b[7]) == b[7] && - (a8 & b[8]) == b[8] && (a9 & b[9]) == b[9] && - (a10 & b[10]) == b[10] && (a11 & b[11]) == b[11] && - (a12 & b[12]) == b[12] && (a13 & b[13]) == b[13] && - (a14 & b[14]) == b[14] && (a15 & b[15]) == b[15]; - } - - }; - - struct SuperstructureComputer256 { - uint64_t a0,a1,a2,a3,a4,a5,a6,a7, - a8,a9,a10,a11,a12,a13,a14,a15, - a16,a17,a18,a19,a20,a21,a22,a23, - a24,a25,a26,a27,a28,a29,a30,a31; - - SuperstructureComputer256 () {} - - SuperstructureComputer256 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *au8, int code_size) { - assert (code_size == 256); - const uint64_t *a = (uint64_t *)au8; - a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3]; - a4 = a[4]; a5 = a[5]; a6 = a[6]; a7 = a[7]; - a8 = a[8]; a9 = a[9]; a10 = a[10]; a11 = a[11]; - a12 = a[12]; a13 = a[13]; a14 = a[14]; a15 = a[15]; - a16 = a[16]; a17 = a[17]; a18 = a[18]; a19 = a[19]; - a20 = a[20]; a21 = a[21]; a22 = a[22]; a23 = a[23]; - a24 = a[24]; a25 = a[25]; a26 = a[26]; a27 = a[27]; - a28 = a[28]; a29 = a[29]; a30 = a[30]; a31 = a[31]; - } - - inline float compute (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - return (a0 & b[0]) == b[0] && (a1 & b[1]) == b[1] && - (a2 & b[2]) == b[2] && (a3 & b[3]) == b[3] && - (a4 & b[4]) == b[4] && (a5 & b[5]) == b[5] && - (a6 & b[6]) == b[6] && (a7 & b[7]) == b[7] && - (a8 & b[8]) == b[8] && (a9 & b[9]) == b[9] && - (a10 & b[10]) == b[10] && (a11 & b[11]) == b[11] && - (a12 & b[12]) == b[12] && (a13 & b[13]) == b[13] && - (a14 & b[14]) == b[14] && (a15 & b[15]) == b[15] && - (a16 & b[16]) == b[16] && (a17 & b[17]) == b[17] && - (a18 & b[18]) == b[18] && (a19 & b[19]) == b[19] && - (a20 & b[20]) == b[20] && (a21 & b[21]) == b[21] && - (a22 & b[22]) == b[22] && (a23 & b[23]) == b[23] && - (a24 & b[24]) == b[24] && (a25 & b[25]) == b[25] && - (a26 & b[26]) == b[26] && (a27 & b[27]) == b[27] && - (a28 & b[28]) == b[28] && (a29 & b[29]) == b[29] && - (a30 & b[30]) == b[30] && (a31 & b[31]) == b[31]; - } - - }; - - struct SuperstructureComputer512 { - uint64_t a0,a1,a2,a3,a4,a5,a6,a7, - a8,a9,a10,a11,a12,a13,a14,a15, - a16,a17,a18,a19,a20,a21,a22,a23, - a24,a25,a26,a27,a28,a29,a30,a31, - a32,a33,a34,a35,a36,a37,a38,a39, - a40,a41,a42,a43,a44,a45,a46,a47, - a48,a49,a50,a51,a52,a53,a54,a55, - a56,a57,a58,a59,a60,a61,a62,a63; - - SuperstructureComputer512 () {} - - SuperstructureComputer512 (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *au8, int code_size) { - assert (code_size == 512); - const uint64_t *a = (uint64_t *)au8; - a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3]; - a4 = a[4]; a5 = a[5]; a6 = a[6]; a7 = a[7]; - a8 = a[8]; a9 = a[9]; a10 = a[10]; a11 = a[11]; - a12 = a[12]; a13 = a[13]; a14 = a[14]; a15 = a[15]; - a16 = a[16]; a17 = a[17]; a18 = a[18]; a19 = a[19]; - a20 = a[20]; a21 = a[21]; a22 = a[22]; a23 = a[23]; - a24 = a[24]; a25 = a[25]; a26 = a[26]; a27 = a[27]; - a28 = a[28]; a29 = a[29]; a30 = a[30]; a31 = a[31]; - a32 = a[32]; a33 = a[33]; a34 = a[34]; a35 = a[35]; - a36 = a[36]; a37 = a[37]; a38 = a[38]; a39 = a[39]; - a40 = a[40]; a41 = a[41]; a42 = a[42]; a43 = a[43]; - a44 = a[44]; a45 = a[45]; a46 = a[46]; a47 = a[47]; - a48 = a[48]; a49 = a[49]; a50 = a[50]; a51 = a[51]; - a52 = a[52]; a53 = a[53]; a54 = a[54]; a55 = a[55]; - a56 = a[56]; a57 = a[57]; a58 = a[58]; a59 = a[59]; - a60 = a[60]; a61 = a[61]; a62 = a[62]; a63 = a[63]; - } - - inline bool compute (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - return (a0 & b[0]) == b[0] && (a1 & b[1]) == b[1] && - (a2 & b[2]) == b[2] && (a3 & b[3]) == b[3] && - (a4 & b[4]) == b[4] && (a5 & b[5]) == b[5] && - (a6 & b[6]) == b[6] && (a7 & b[7]) == b[7] && - (a8 & b[8]) == b[8] && (a9 & b[9]) == b[9] && - (a10 & b[10]) == b[10] && (a11 & b[11]) == b[11] && - (a12 & b[12]) == b[12] && (a13 & b[13]) == b[13] && - (a14 & b[14]) == b[14] && (a15 & b[15]) == b[15] && - (a16 & b[16]) == b[16] && (a17 & b[17]) == b[17] && - (a18 & b[18]) == b[18] && (a19 & b[19]) == b[19] && - (a20 & b[20]) == b[20] && (a21 & b[21]) == b[21] && - (a22 & b[22]) == b[22] && (a23 & b[23]) == b[23] && - (a24 & b[24]) == b[24] && (a25 & b[25]) == b[25] && - (a26 & b[26]) == b[26] && (a27 & b[27]) == b[27] && - (a28 & b[28]) == b[28] && (a29 & b[29]) == b[29] && - (a30 & b[30]) == b[30] && (a31 & b[31]) == b[31] && - (a32 & b[32]) == b[32] && (a33 & b[33]) == b[33] && - (a34 & b[34]) == b[34] && (a35 & b[35]) == b[35] && - (a36 & b[36]) == b[36] && (a37 & b[37]) == b[37] && - (a38 & b[38]) == b[38] && (a39 & b[39]) == b[39] && - (a40 & b[40]) == b[40] && (a41 & b[41]) == b[41] && - (a42 & b[42]) == b[42] && (a43 & b[43]) == b[43] && - (a44 & b[44]) == b[44] && (a45 & b[45]) == b[45] && - (a46 & b[46]) == b[46] && (a47 & b[47]) == b[47] && - (a48 & b[48]) == b[48] && (a49 & b[49]) == b[49] && - (a50 & b[50]) == b[50] && (a51 & b[51]) == b[51] && - (a52 & b[52]) == b[52] && (a53 & b[53]) == b[53] && - (a54 & b[54]) == b[54] && (a55 & b[55]) == b[55] && - (a56 & b[56]) == b[56] && (a57 & b[57]) == b[57] && - (a58 & b[58]) == b[58] && (a59 & b[59]) == b[59] && - (a60 & b[60]) == b[60] && (a61 & b[61]) == b[61] && - (a62 & b[62]) == b[62] && (a63 & b[63]) == b[63]; - } - - }; - - struct SuperstructureComputerDefault { - const uint8_t *a; - int n; - - SuperstructureComputerDefault () {} - - SuperstructureComputerDefault (const uint8_t *a8, int code_size) { - set (a8, code_size); - } - - void set (const uint8_t *a8, int code_size) { - a = a8; - n = code_size; - } - - bool compute (const uint8_t *b8) const { - const uint64_t *b = (uint64_t *)b8; - for (int i = 0; i < n; i++) { - if ((a[i] & b[i]) != b[i]) { - return false; - } - } - return true; - } - - }; - -// default template - template - struct SuperstructureComputer: SuperstructureComputerDefault { - SuperstructureComputer (const uint8_t *a, int code_size): - SuperstructureComputerDefault(a, code_size) {} - }; - -#define SPECIALIZED_HC(CODE_SIZE) \ - template<> struct SuperstructureComputer: \ - SuperstructureComputer ## CODE_SIZE { \ - SuperstructureComputer (const uint8_t *a): \ - SuperstructureComputer ## CODE_SIZE(a, CODE_SIZE) {} \ - } - - SPECIALIZED_HC(8); - SPECIALIZED_HC(16); - SPECIALIZED_HC(32); - SPECIALIZED_HC(64); - SPECIALIZED_HC(128); - SPECIALIZED_HC(256); - SPECIALIZED_HC(512); - -#undef SPECIALIZED_HC - -} diff --git a/core/src/index/thirdparty/faiss/utils/utils.cpp b/core/src/index/thirdparty/faiss/utils/utils.cpp deleted file mode 100644 index 20b2e29553..0000000000 --- a/core/src/index/thirdparty/faiss/utils/utils.cpp +++ /dev/null @@ -1,723 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include - - - -#ifndef FINTEGER -#define FINTEGER long -#endif - - -extern "C" { - -/* declare BLAS functions, see http://www.netlib.org/clapack/cblas/ */ - -int sgemm_ (const char *transa, const char *transb, FINTEGER *m, FINTEGER * - n, FINTEGER *k, const float *alpha, const float *a, - FINTEGER *lda, const float *b, FINTEGER * - ldb, float *beta, float *c, FINTEGER *ldc); - -/* Lapack functions, see http://www.netlib.org/clapack/old/single/sgeqrf.c */ - -int sgeqrf_ (FINTEGER *m, FINTEGER *n, float *a, FINTEGER *lda, - float *tau, float *work, FINTEGER *lwork, FINTEGER *info); - -int sorgqr_(FINTEGER *m, FINTEGER *n, FINTEGER *k, float *a, - FINTEGER *lda, float *tau, float *work, - FINTEGER *lwork, FINTEGER *info); - -int sgemv_(const char *trans, FINTEGER *m, FINTEGER *n, float *alpha, - const float *a, FINTEGER *lda, const float *x, FINTEGER *incx, - float *beta, float *y, FINTEGER *incy); - -} - - -/************************************************** - * Get some stats about the system - **************************************************/ - -namespace faiss { - -double getmillisecs () { - struct timeval tv; - gettimeofday (&tv, nullptr); - return tv.tv_sec * 1e3 + tv.tv_usec * 1e-3; -} - -uint64_t get_cycles () { -#ifdef __x86_64__ - uint32_t high, low; - asm volatile("rdtsc \n\t" - : "=a" (low), - "=d" (high)); - return ((uint64_t)high << 32) | (low); -#else - return 0; -#endif -} - - -#ifdef __linux__ - -size_t get_mem_usage_kb () -{ - int pid = getpid (); - char fname[256]; - snprintf (fname, 256, "/proc/%d/status", pid); - FILE * f = fopen (fname, "r"); - FAISS_THROW_IF_NOT_MSG (f, "cannot open proc status file"); - size_t sz = 0; - for (;;) { - char buf [256]; - if (!fgets (buf, 256, f)) break; - if (sscanf (buf, "VmRSS: %ld kB", &sz) == 1) break; - } - fclose (f); - return sz; -} - -#elif __APPLE__ - -size_t get_mem_usage_kb () -{ - fprintf(stderr, "WARN: get_mem_usage_kb not implemented on the mac\n"); - return 0; -} - -#endif - - - - - -void reflection (const float * __restrict u, - float * __restrict x, - size_t n, size_t d, size_t nu) -{ - size_t i, j, l; - for (i = 0; i < n; i++) { - const float * up = u; - for (l = 0; l < nu; l++) { - float ip1 = 0, ip2 = 0; - - for (j = 0; j < d; j+=2) { - ip1 += up[j] * x[j]; - ip2 += up[j+1] * x[j+1]; - } - float ip = 2 * (ip1 + ip2); - - for (j = 0; j < d; j++) - x[j] -= ip * up[j]; - up += d; - } - x += d; - } -} - - -/* Reference implementation (slower) */ -void reflection_ref (const float * u, float * x, size_t n, size_t d, size_t nu) -{ - size_t i, j, l; - for (i = 0; i < n; i++) { - const float * up = u; - for (l = 0; l < nu; l++) { - double ip = 0; - - for (j = 0; j < d; j++) - ip += up[j] * x[j]; - ip *= 2; - - for (j = 0; j < d; j++) - x[j] -= ip * up[j]; - - up += d; - } - x += d; - } -} - - - - - - -/*************************************************************************** - * Some matrix manipulation functions - ***************************************************************************/ - - -/* This function exists because the Torch counterpart is extremly slow - (not multi-threaded + unexpected overhead even in single thread). - It is here to implement the usual property |x-y|^2=|x|^2+|y|^2-2 */ -void inner_product_to_L2sqr (float * __restrict dis, - const float * nr1, - const float * nr2, - size_t n1, size_t n2) -{ - -#pragma omp parallel for - for (size_t j = 0 ; j < n1 ; j++) { - float * disj = dis + j * n2; - for (size_t i = 0 ; i < n2 ; i++) - disj[i] = nr1[j] + nr2[i] - 2 * disj[i]; - } -} - - -void matrix_qr (int m, int n, float *a) -{ - FAISS_THROW_IF_NOT (m >= n); - FINTEGER mi = m, ni = n, ki = mi < ni ? mi : ni; - std::vector tau (ki); - FINTEGER lwork = -1, info; - float work_size; - - sgeqrf_ (&mi, &ni, a, &mi, tau.data(), - &work_size, &lwork, &info); - lwork = size_t(work_size); - std::vector work (lwork); - - sgeqrf_ (&mi, &ni, a, &mi, - tau.data(), work.data(), &lwork, &info); - - sorgqr_ (&mi, &ni, &ki, a, &mi, tau.data(), - work.data(), &lwork, &info); - -} - - - - -/*************************************************************************** - * Result list routines - ***************************************************************************/ - - -void ranklist_handle_ties (int k, int64_t *idx, const float *dis) -{ - float prev_dis = -1e38; - int prev_i = -1; - for (int i = 0; i < k; i++) { - if (dis[i] != prev_dis) { - if (i > prev_i + 1) { - // sort between prev_i and i - 1 - std::sort (idx + prev_i, idx + i); - } - prev_i = i; - prev_dis = dis[i]; - } - } -} - -size_t merge_result_table_with (size_t n, size_t k, - int64_t *I0, float *D0, - const int64_t *I1, const float *D1, - bool keep_min, - int64_t translation) -{ - size_t n1 = 0; - -#pragma omp parallel reduction(+:n1) - { - std::vector tmpI (k); - std::vector tmpD (k); - -#pragma omp for - for (size_t i = 0; i < n; i++) { - int64_t *lI0 = I0 + i * k; - float *lD0 = D0 + i * k; - const int64_t *lI1 = I1 + i * k; - const float *lD1 = D1 + i * k; - size_t r0 = 0; - size_t r1 = 0; - - if (keep_min) { - for (size_t j = 0; j < k; j++) { - - if (lI0[r0] >= 0 && lD0[r0] < lD1[r1]) { - tmpD[j] = lD0[r0]; - tmpI[j] = lI0[r0]; - r0++; - } else if (lD1[r1] >= 0) { - tmpD[j] = lD1[r1]; - tmpI[j] = lI1[r1] + translation; - r1++; - } else { // both are NaNs - tmpD[j] = NAN; - tmpI[j] = -1; - } - } - } else { - for (size_t j = 0; j < k; j++) { - if (lI0[r0] >= 0 && lD0[r0] > lD1[r1]) { - tmpD[j] = lD0[r0]; - tmpI[j] = lI0[r0]; - r0++; - } else if (lD1[r1] >= 0) { - tmpD[j] = lD1[r1]; - tmpI[j] = lI1[r1] + translation; - r1++; - } else { // both are NaNs - tmpD[j] = NAN; - tmpI[j] = -1; - } - } - } - n1 += r1; - memcpy (lD0, tmpD.data(), sizeof (lD0[0]) * k); - memcpy (lI0, tmpI.data(), sizeof (lI0[0]) * k); - } - } - - return n1; -} - - - -size_t ranklist_intersection_size (size_t k1, const int64_t *v1, - size_t k2, const int64_t *v2_in) -{ - if (k2 > k1) return ranklist_intersection_size (k2, v2_in, k1, v1); - int64_t *v2 = new int64_t [k2]; - memcpy (v2, v2_in, sizeof (int64_t) * k2); - std::sort (v2, v2 + k2); - { // de-dup v2 - int64_t prev = -1; - size_t wp = 0; - for (size_t i = 0; i < k2; i++) { - if (v2 [i] != prev) { - v2[wp++] = prev = v2 [i]; - } - } - k2 = wp; - } - const int64_t seen_flag = 1L << 60; - size_t count = 0; - for (size_t i = 0; i < k1; i++) { - int64_t q = v1 [i]; - size_t i0 = 0, i1 = k2; - while (i0 + 1 < i1) { - size_t imed = (i1 + i0) / 2; - int64_t piv = v2 [imed] & ~seen_flag; - if (piv <= q) i0 = imed; - else i1 = imed; - } - if (v2 [i0] == q) { - count++; - v2 [i0] |= seen_flag; - } - } - delete [] v2; - - return count; -} - -double imbalance_factor (int k, const int *hist) { - double tot = 0, uf = 0; - - for (int i = 0 ; i < k ; i++) { - tot += hist[i]; - uf += hist[i] * (double) hist[i]; - } - uf = uf * k / (tot * tot); - - return uf; -} - - -double imbalance_factor (int n, int k, const int64_t *assign) { - std::vector hist(k, 0); - for (int i = 0; i < n; i++) { - hist[assign[i]]++; - } - - return imbalance_factor (k, hist.data()); -} - - - -int ivec_hist (size_t n, const int * v, int vmax, int *hist) { - memset (hist, 0, sizeof(hist[0]) * vmax); - int nout = 0; - while (n--) { - if (v[n] < 0 || v[n] >= vmax) nout++; - else hist[v[n]]++; - } - return nout; -} - - -void bincode_hist(size_t n, size_t nbits, const uint8_t *codes, int *hist) -{ - FAISS_THROW_IF_NOT (nbits % 8 == 0); - size_t d = nbits / 8; - std::vector accu(d * 256); - const uint8_t *c = codes; - for (size_t i = 0; i < n; i++) - for(int j = 0; j < d; j++) - accu[j * 256 + *c++]++; - memset (hist, 0, sizeof(*hist) * nbits); - for (int i = 0; i < d; i++) { - const int *ai = accu.data() + i * 256; - int * hi = hist + i * 8; - for (int j = 0; j < 256; j++) - for (int k = 0; k < 8; k++) - if ((j >> k) & 1) - hi[k] += ai[j]; - } - -} - - - -size_t ivec_checksum (size_t n, const int *a) -{ - size_t cs = 112909; - while (n--) cs = cs * 65713 + a[n] * 1686049; - return cs; -} - - -namespace { - struct ArgsortComparator { - const float *vals; - bool operator() (const size_t a, const size_t b) const { - return vals[a] < vals[b]; - } - }; - - struct SegmentS { - size_t i0; // begin pointer in the permutation array - size_t i1; // end - size_t len() const { - return i1 - i0; - } - }; - - // see https://en.wikipedia.org/wiki/Merge_algorithm#Parallel_merge - // extended to > 1 merge thread - - // merges 2 ranges that should be consecutive on the source into - // the union of the two on the destination - template - void parallel_merge (const T *src, T *dst, - SegmentS &s1, SegmentS & s2, int nt, - const ArgsortComparator & comp) { - if (s2.len() > s1.len()) { // make sure that s1 larger than s2 - std::swap(s1, s2); - } - - // compute sub-ranges for each thread - SegmentS s1s[nt], s2s[nt], sws[nt]; - s2s[0].i0 = s2.i0; - s2s[nt - 1].i1 = s2.i1; - - // not sure parallel actually helps here -#pragma omp parallel for num_threads(nt) - for (int t = 0; t < nt; t++) { - s1s[t].i0 = s1.i0 + s1.len() * t / nt; - s1s[t].i1 = s1.i0 + s1.len() * (t + 1) / nt; - - if (t + 1 < nt) { - T pivot = src[s1s[t].i1]; - size_t i0 = s2.i0, i1 = s2.i1; - while (i0 + 1 < i1) { - size_t imed = (i1 + i0) / 2; - if (comp (pivot, src[imed])) {i1 = imed; } - else {i0 = imed; } - } - s2s[t].i1 = s2s[t + 1].i0 = i1; - } - } - s1.i0 = std::min(s1.i0, s2.i0); - s1.i1 = std::max(s1.i1, s2.i1); - s2 = s1; - sws[0].i0 = s1.i0; - for (int t = 0; t < nt; t++) { - sws[t].i1 = sws[t].i0 + s1s[t].len() + s2s[t].len(); - if (t + 1 < nt) { - sws[t + 1].i0 = sws[t].i1; - } - } - assert(sws[nt - 1].i1 == s1.i1); - - // do the actual merging -#pragma omp parallel for num_threads(nt) - for (int t = 0; t < nt; t++) { - SegmentS sw = sws[t]; - SegmentS s1t = s1s[t]; - SegmentS s2t = s2s[t]; - if (s1t.i0 < s1t.i1 && s2t.i0 < s2t.i1) { - for (;;) { - // assert (sw.len() == s1t.len() + s2t.len()); - if (comp(src[s1t.i0], src[s2t.i0])) { - dst[sw.i0++] = src[s1t.i0++]; - if (s1t.i0 == s1t.i1) break; - } else { - dst[sw.i0++] = src[s2t.i0++]; - if (s2t.i0 == s2t.i1) break; - } - } - } - if (s1t.len() > 0) { - assert(s1t.len() == sw.len()); - memcpy(dst + sw.i0, src + s1t.i0, s1t.len() * sizeof(dst[0])); - } else if (s2t.len() > 0) { - assert(s2t.len() == sw.len()); - memcpy(dst + sw.i0, src + s2t.i0, s2t.len() * sizeof(dst[0])); - } - } - } - -}; - -void fvec_argsort (size_t n, const float *vals, - size_t *perm) -{ - for (size_t i = 0; i < n; i++) perm[i] = i; - ArgsortComparator comp = {vals}; - std::sort (perm, perm + n, comp); -} - -void fvec_argsort_parallel (size_t n, const float *vals, - size_t *perm) -{ - size_t * perm2 = new size_t[n]; - // 2 result tables, during merging, flip between them - size_t *permB = perm2, *permA = perm; - - int nt = omp_get_max_threads(); - { // prepare correct permutation so that the result ends in perm - // at final iteration - int nseg = nt; - while (nseg > 1) { - nseg = (nseg + 1) / 2; - std::swap (permA, permB); - } - } - -#pragma omp parallel - for (size_t i = 0; i < n; i++) permA[i] = i; - - ArgsortComparator comp = {vals}; - - SegmentS segs[nt]; - - // independent sorts -#pragma omp parallel for - for (int t = 0; t < nt; t++) { - size_t i0 = t * n / nt; - size_t i1 = (t + 1) * n / nt; - SegmentS seg = {i0, i1}; - std::sort (permA + seg.i0, permA + seg.i1, comp); - segs[t] = seg; - } - int prev_nested = omp_get_nested(); - omp_set_nested(1); - - int nseg = nt; - while (nseg > 1) { - int nseg1 = (nseg + 1) / 2; - int sub_nt = nseg % 2 == 0 ? nt : nt - 1; - int sub_nseg1 = nseg / 2; - -#pragma omp parallel for num_threads(nseg1) - for (int s = 0; s < nseg; s += 2) { - if (s + 1 == nseg) { // otherwise isolated segment - memcpy(permB + segs[s].i0, permA + segs[s].i0, - segs[s].len() * sizeof(size_t)); - } else { - int t0 = s * sub_nt / sub_nseg1; - int t1 = (s + 1) * sub_nt / sub_nseg1; - printf("merge %d %d, %d threads\n", s, s + 1, t1 - t0); - parallel_merge(permA, permB, segs[s], segs[s + 1], - t1 - t0, comp); - } - } - for (int s = 0; s < nseg; s += 2) - segs[s / 2] = segs[s]; - nseg = nseg1; - std::swap (permA, permB); - } - assert (permA == perm); - omp_set_nested(prev_nested); - delete [] perm2; -} - - - - - - - - - - - - - - - - - - -const float *fvecs_maybe_subsample ( - size_t d, size_t *n, size_t nmax, const float *x, - bool verbose, int64_t seed) -{ - - if (*n <= nmax) return x; // nothing to do - - size_t n2 = nmax; - if (verbose) { - printf (" Input training set too big (max size is %ld), sampling " - "%ld / %ld vectors\n", nmax, n2, *n); - } - std::vector subset (*n); - rand_perm (subset.data (), *n, seed); - float *x_subset = new float[n2 * d]; - for (int64_t i = 0; i < n2; i++) - memcpy (&x_subset[i * d], - &x[subset[i] * size_t(d)], - sizeof (x[0]) * d); - *n = n2; - return x_subset; -} - - -void binary_to_real(size_t d, const uint8_t *x_in, float *x_out) { - for (size_t i = 0; i < d; ++i) { - x_out[i] = 2 * ((x_in[i >> 3] >> (i & 7)) & 1) - 1; - } -} - -void real_to_binary(size_t d, const float *x_in, uint8_t *x_out) { - for (size_t i = 0; i < d / 8; ++i) { - uint8_t b = 0; - for (int j = 0; j < 8; ++j) { - if (x_in[8 * i + j] > 0) { - b |= (1 << j); - } - } - x_out[i] = b; - } -} - - -// from Python's stringobject.c -uint64_t hash_bytes (const uint8_t *bytes, int64_t n) { - const uint8_t *p = bytes; - uint64_t x = (uint64_t)(*p) << 7; - int64_t len = n; - while (--len >= 0) { - x = (1000003*x) ^ *p++; - } - x ^= n; - return x; -} - - -bool check_openmp() { - omp_set_num_threads(10); - - if (omp_get_max_threads() != 10) { - return false; - } - - std::vector nt_per_thread(10); - size_t sum = 0; - bool in_parallel = true; -#pragma omp parallel reduction(+: sum) - { - if (!omp_in_parallel()) { - in_parallel = false; - } - - int nt = omp_get_num_threads(); - int rank = omp_get_thread_num(); - - nt_per_thread[rank] = nt; -#pragma omp for - for(int i = 0; i < 1000 * 1000 * 10; i++) { - sum += i; - } - } - - if (!in_parallel) { - return false; - } - if (nt_per_thread[0] != 10) { - return false; - } - if (sum == 0) { - return false; - } - - return true; -} - -int64_t get_L3_Size() { - static int64_t l3_size = -1; - constexpr int64_t KB = 1024; - if (l3_size == -1) { - - FILE* file = fopen("/sys/devices/system/cpu/cpu0/cache/index3/size","r"); - int64_t result = 0; - constexpr int64_t line_length = 128; - char line[line_length]; - if (file){ - char* ret = fgets(line, sizeof(line) - 1, file); - - sscanf(line, "%luK", &result); - l3_size = result * KB; - - fclose(file); - } else { - l3_size = 12 * KB * KB; // 12M - } - - } - return l3_size; -} - -void (*LOG_TRACE_)(const std::string&); - -void (*LOG_DEBUG_)(const std::string&); - -void (*LOG_INFO_)(const std::string&); - -void (*LOG_WARNING_)(const std::string&); - -void (*LOG_FATAL_)(const std::string&); - -void (*LOG_ERROR_)(const std::string&); - -} // namespace faiss diff --git a/core/src/index/thirdparty/faiss/utils/utils.h b/core/src/index/thirdparty/faiss/utils/utils.h deleted file mode 100644 index 9be65b10a0..0000000000 --- a/core/src/index/thirdparty/faiss/utils/utils.h +++ /dev/null @@ -1,182 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -// -*- c++ -*- - -/* - * A few utilitary functions for similarity search: - * - optimized exhaustive distance and knn search functions - * - some functions reimplemented from torch for speed - */ - -#ifndef FAISS_utils_h -#define FAISS_utils_h - -#include -#include - -#include - - -namespace faiss { - - -/************************************************** - * Get some stats about the system -**************************************************/ - - -/// ms elapsed since some arbitrary epoch -double getmillisecs (); - -/// get current RSS usage in kB -size_t get_mem_usage_kb (); - - -uint64_t get_cycles (); - -/*************************************************************************** - * Misc matrix and vector manipulation functions - ***************************************************************************/ - - -/** compute c := a + bf * b for a, b and c tables - * - * @param n size of the tables - * @param a size n - * @param b size n - * @param c restult table, size n - */ -void fvec_madd (size_t n, const float *a, - float bf, const float *b, float *c); - - -/** same as fvec_madd, also return index of the min of the result table - * @return index of the min of table c - */ -int fvec_madd_and_argmin (size_t n, const float *a, - float bf, const float *b, float *c); - - -/* perform a reflection (not an efficient implementation, just for test ) */ -void reflection (const float * u, float * x, size_t n, size_t d, size_t nu); - - -/** compute the Q of the QR decomposition for m > n - * @param a size n * m: input matrix and output Q - */ -void matrix_qr (int m, int n, float *a); - -/** distances are supposed to be sorted. Sorts indices with same distance*/ -void ranklist_handle_ties (int k, int64_t *idx, const float *dis); - -/** count the number of comon elements between v1 and v2 - * algorithm = sorting + bissection to avoid double-counting duplicates - */ -size_t ranklist_intersection_size (size_t k1, const int64_t *v1, - size_t k2, const int64_t *v2); - -/** merge a result table into another one - * - * @param I0, D0 first result table, size (n, k) - * @param I1, D1 second result table, size (n, k) - * @param keep_min if true, keep min values, otherwise keep max - * @param translation add this value to all I1's indexes - * @return nb of values that were taken from the second table - */ -size_t merge_result_table_with (size_t n, size_t k, - int64_t *I0, float *D0, - const int64_t *I1, const float *D1, - bool keep_min = true, - int64_t translation = 0); - - -/// a balanced assignment has a IF of 1 -double imbalance_factor (int n, int k, const int64_t *assign); - -/// same, takes a histogram as input -double imbalance_factor (int k, const int *hist); - - -void fvec_argsort (size_t n, const float *vals, - size_t *perm); - -void fvec_argsort_parallel (size_t n, const float *vals, - size_t *perm); - - -/// compute histogram on v -int ivec_hist (size_t n, const int * v, int vmax, int *hist); - -/** Compute histogram of bits on a code array - * - * @param codes size(n, nbits / 8) - * @param hist size(nbits): nb of 1s in the array of codes - */ -void bincode_hist(size_t n, size_t nbits, const uint8_t *codes, int *hist); - - -/// compute a checksum on a table. -size_t ivec_checksum (size_t n, const int *a); - - -/** random subsamples a set of vectors if there are too many of them - * - * @param d dimension of the vectors - * @param n on input: nb of input vectors, output: nb of output vectors - * @param nmax max nb of vectors to keep - * @param x input array, size *n-by-d - * @param seed random seed to use for sampling - * @return x or an array allocated with new [] with *n vectors - */ -const float *fvecs_maybe_subsample ( - size_t d, size_t *n, size_t nmax, const float *x, - bool verbose = false, int64_t seed = 1234); - -/** Convert binary vector to +1/-1 valued float vector. - * - * @param d dimension of the vector (multiple of 8) - * @param x_in input binary vector (uint8_t table of size d / 8) - * @param x_out output float vector (float table of size d) - */ -void binary_to_real(size_t d, const uint8_t *x_in, float *x_out); - -/** Convert float vector to binary vector. Components > 0 are converted to 1, - * others to 0. - * - * @param d dimension of the vector (multiple of 8) - * @param x_in input float vector (float table of size d) - * @param x_out output binary vector (uint8_t table of size d / 8) - */ -void real_to_binary(size_t d, const float *x_in, uint8_t *x_out); - - -/** A reasonable hashing function */ -uint64_t hash_bytes (const uint8_t *bytes, int64_t n); - -/** Whether OpenMP annotations were respected. */ -bool check_openmp(); - -/** get the size of L3 cache */ -int64_t get_L3_Size(); - -extern void (*LOG_TRACE_)(const std::string&); - -extern void (*LOG_DEBUG_)(const std::string&); - -extern void (*LOG_INFO_)(const std::string&); - -extern void (*LOG_WARNING_)(const std::string&); - -extern void (*LOG_FATAL_)(const std::string&); - -extern void (*LOG_ERROR_)(const std::string&); - -} // namspace faiss - - -#endif /* FAISS_utils_h */ diff --git a/core/src/index/thirdparty/hnswlib/bruteforce.h b/core/src/index/thirdparty/hnswlib/bruteforce.h deleted file mode 100644 index ae2fa6a8f6..0000000000 --- a/core/src/index/thirdparty/hnswlib/bruteforce.h +++ /dev/null @@ -1,164 +0,0 @@ -#pragma once -#include -#include -#include -#include - -namespace hnswlib { - -template -class BruteforceSearch : public AlgorithmInterface { - public: - BruteforceSearch(SpaceInterface *s) { - - } - BruteforceSearch(SpaceInterface *s, const std::string &location) { - loadIndex(location, s); - } - - BruteforceSearch(SpaceInterface *s, size_t maxElements) { - maxelements_ = maxElements; - data_size_ = s->get_data_size(); - fstdistfunc_ = s->get_dist_func(); - dist_func_param_ = s->get_dist_func_param(); - size_per_element_ = data_size_ + sizeof(labeltype); - data_ = (char *) malloc(maxElements * size_per_element_); - if (data_ == nullptr) - std::runtime_error("Not enough memory: BruteforceSearch failed to allocate data"); - cur_element_count = 0; - } - - ~BruteforceSearch() { - free(data_); - } - - char *data_; - size_t maxelements_; - size_t cur_element_count; - size_t size_per_element_; - - size_t data_size_; - DISTFUNC fstdistfunc_; - void *dist_func_param_; - std::mutex index_lock; - - std::unordered_map dict_external_to_internal; - - void addPoint(const void *datapoint, labeltype label) { - - size_t idx; - { - std::unique_lock lock(index_lock); - - - - auto search=dict_external_to_internal.find(label); - if (search != dict_external_to_internal.end()) { - idx=search->second; - } - else{ - if (cur_element_count >= maxelements_) { - throw std::runtime_error("The number of elements exceeds the specified limit\n"); - } - idx=cur_element_count; - dict_external_to_internal[label] = idx; - cur_element_count++; - } - } - memcpy(data_ + size_per_element_ * idx + data_size_, &label, sizeof(labeltype)); - memcpy(data_ + size_per_element_ * idx, datapoint, data_size_); - - - - - }; - - void removePoint(labeltype cur_external) { - size_t cur_c=dict_external_to_internal[cur_external]; - - dict_external_to_internal.erase(cur_external); - - labeltype label=*((labeltype*)(data_ + size_per_element_ * (cur_element_count-1) + data_size_)); - dict_external_to_internal[label]=cur_c; - memcpy(data_ + size_per_element_ * cur_c, - data_ + size_per_element_ * (cur_element_count-1), - data_size_+sizeof(labeltype)); - cur_element_count--; - } - - std::priority_queue> - searchKnn(const void *query_data, size_t k) const { - std::priority_queue> topResults; - if (cur_element_count == 0) return topResults; - for (size_t i = 0; i < k; i++) { - dist_t dist = fstdistfunc_(query_data, data_ + size_per_element_ * i, dist_func_param_); - topResults.push(std::pair(dist, *((labeltype *) (data_ + size_per_element_ * i + - data_size_)))); - } - dist_t lastdist = topResults.top().first; - for (size_t i = k; i < cur_element_count; i++) { - dist_t dist = fstdistfunc_(query_data, data_ + size_per_element_ * i, dist_func_param_); - if (dist <= lastdist) { - topResults.push(std::pair(dist, *((labeltype *) (data_ + size_per_element_ * i + - data_size_)))); - if (topResults.size() > k) - topResults.pop(); - lastdist = topResults.top().first; - } - } - return topResults; - }; - - template - std::vector> - searchKnn(const void* query_data, size_t k, Comp comp) { - std::vector> result; - if (cur_element_count == 0) return result; - - auto ret = searchKnn(query_data, k); - - while (!ret.empty()) { - result.push_back(ret.top()); - ret.pop(); - } - - std::sort(result.begin(), result.end(), comp); - - return result; - } - - void saveIndex(const std::string &location) { - std::ofstream output(location, std::ios::binary); - std::streampos position; - - writeBinaryPOD(output, maxelements_); - writeBinaryPOD(output, size_per_element_); - writeBinaryPOD(output, cur_element_count); - - output.write(data_, maxelements_ * size_per_element_); - - output.close(); - } - - void loadIndex(const std::string &location, SpaceInterface *s) { - std::ifstream input(location, std::ios::binary); - std::streampos position; - - readBinaryPOD(input, maxelements_); - readBinaryPOD(input, size_per_element_); - readBinaryPOD(input, cur_element_count); - - data_size_ = s->get_data_size(); - fstdistfunc_ = s->get_dist_func(); - dist_func_param_ = s->get_dist_func_param(); - size_per_element_ = data_size_ + sizeof(labeltype); - data_ = (char *) malloc(maxelements_ * size_per_element_); - if (data_ == nullptr) - std::runtime_error("Not enough memory: loadIndex failed to allocate data"); - - input.read(data_, maxelements_ * size_per_element_); - - input.close(); - } -}; -} diff --git a/core/src/index/thirdparty/hnswlib/hnswalg.h b/core/src/index/thirdparty/hnswlib/hnswalg.h deleted file mode 100644 index d85455389b..0000000000 --- a/core/src/index/thirdparty/hnswlib/hnswalg.h +++ /dev/null @@ -1,1147 +0,0 @@ -#pragma once - -#include "visited_list_pool.h" -#include "hnswlib.h" -#include -#include -#include -#include - -#include "knowhere/index/vector_index/helpers/FaissIO.h" - -namespace hnswlib { - -typedef unsigned int tableint; -typedef unsigned int linklistsizeint; - -template -class HierarchicalNSW : public AlgorithmInterface { - public: - HierarchicalNSW(SpaceInterface *s) { - } - - HierarchicalNSW(SpaceInterface *s, const std::string &location, bool nmslib = false, size_t max_elements=0) { - loadIndex(location, s, max_elements); - } - - HierarchicalNSW(SpaceInterface *s, size_t max_elements, size_t M = 16, size_t ef_construction = 200, size_t random_seed = 100) : - link_list_locks_(max_elements), element_levels_(max_elements) { - // linxj - space = s; - if (auto x = dynamic_cast(s)) { - metric_type_ = 0; - } else if (auto x = dynamic_cast(s)) { - metric_type_ = 1; - } else { - metric_type_ = 100; - } - - max_elements_ = max_elements; - - has_deletions_=false; - data_size_ = s->get_data_size(); - fstdistfunc_ = s->get_dist_func(); - dist_func_param_ = s->get_dist_func_param(); - M_ = M; - maxM_ = M_; - maxM0_ = M_ * 2; - ef_construction_ = std::max(ef_construction,M_); - ef_ = 10; - - level_generator_.seed(random_seed); - - size_links_level0_ = maxM0_ * sizeof(tableint) + sizeof(linklistsizeint); - size_data_per_element_ = size_links_level0_ + data_size_ + sizeof(labeltype); - offsetData_ = size_links_level0_; - label_offset_ = size_links_level0_ + data_size_; - offsetLevel0_ = 0; - - data_level0_memory_ = (char *) malloc(max_elements_ * size_data_per_element_); - if (data_level0_memory_ == nullptr) - throw std::runtime_error("Not enough memory"); - - cur_element_count = 0; - - visited_list_pool_ = new VisitedListPool(1, max_elements); - - - - //initializations for special treatment of the first node - enterpoint_node_ = -1; - maxlevel_ = -1; - - linkLists_ = (char **) malloc(sizeof(void *) * max_elements_); - if (linkLists_ == nullptr) - throw std::runtime_error("Not enough memory: HierarchicalNSW failed to allocate linklists"); - size_links_per_element_ = maxM_ * sizeof(tableint) + sizeof(linklistsizeint); - mult_ = 1 / log(1.0 * M_); - } - - struct CompareByFirst { - constexpr bool operator()(std::pair const &a, - std::pair const &b) const noexcept { - return a.first < b.first; - } - }; - - ~HierarchicalNSW() { - - free(data_level0_memory_); - for (tableint i = 0; i < cur_element_count; i++) { - if (element_levels_[i] > 0) - free(linkLists_[i]); - } - free(linkLists_); - delete visited_list_pool_; - - // linxj: delete - delete space; - } - - // linxj: use for free resource - SpaceInterface *space; - size_t metric_type_; // 0:l2, 1:ip - - size_t max_elements_; - size_t cur_element_count; - size_t size_data_per_element_; - size_t size_links_per_element_; - - size_t M_; - size_t maxM_; - size_t maxM0_; - size_t ef_construction_; - - double mult_; - int maxlevel_; - - - VisitedListPool *visited_list_pool_; - std::mutex cur_element_count_guard_; - - std::vector link_list_locks_; - tableint enterpoint_node_; - - - size_t size_links_level0_; - size_t offsetData_, offsetLevel0_; - - - char *data_level0_memory_; - char **linkLists_; - std::vector element_levels_; - - size_t data_size_; - - bool has_deletions_; - - - size_t label_offset_; - DISTFUNC fstdistfunc_; - void *dist_func_param_; - std::unordered_map label_lookup_; - - std::default_random_engine level_generator_; - - inline labeltype getExternalLabel(tableint internal_id) const { - labeltype return_label; - memcpy(&return_label,(data_level0_memory_ + internal_id * size_data_per_element_ + label_offset_), sizeof(labeltype)); - return return_label; - } - - inline void setExternalLabel(tableint internal_id, labeltype label) const { - memcpy((data_level0_memory_ + internal_id * size_data_per_element_ + label_offset_), &label, sizeof(labeltype)); - } - - inline labeltype *getExternalLabeLp(tableint internal_id) const { - return (labeltype *) (data_level0_memory_ + internal_id * size_data_per_element_ + label_offset_); - } - - inline char *getDataByInternalId(tableint internal_id) const { - return (data_level0_memory_ + internal_id * size_data_per_element_ + offsetData_); - } - - int getRandomLevel(double reverse_size) { - std::uniform_real_distribution distribution(0.0, 1.0); - double r = -log(distribution(level_generator_)) * reverse_size; - return (int) r; - } - - std::priority_queue, std::vector>, CompareByFirst> - searchBaseLayer(tableint ep_id, const void *data_point, int layer) { - VisitedList *vl = visited_list_pool_->getFreeVisitedList(); - vl_type *visited_array = vl->mass; - vl_type visited_array_tag = vl->curV; - - std::priority_queue, std::vector>, CompareByFirst> top_candidates; - std::priority_queue, std::vector>, CompareByFirst> candidateSet; - - dist_t lowerBound; - if (!isMarkedDeleted(ep_id)) { - dist_t dist = fstdistfunc_(data_point, getDataByInternalId(ep_id), dist_func_param_); - top_candidates.emplace(dist, ep_id); - lowerBound = dist; - candidateSet.emplace(-dist, ep_id); - } else { - lowerBound = std::numeric_limits::max(); - candidateSet.emplace(-lowerBound, ep_id); - } - visited_array[ep_id] = visited_array_tag; - - while (!candidateSet.empty()) { - std::pair curr_el_pair = candidateSet.top(); - if ((-curr_el_pair.first) > lowerBound) { - break; - } - candidateSet.pop(); - - tableint curNodeNum = curr_el_pair.second; - - std::unique_lock lock(link_list_locks_[curNodeNum]); - - int *data;// = (int *)(linkList0_ + curNodeNum * size_links_per_element0_); - if (layer == 0) { - data = (int*)get_linklist0(curNodeNum); - } else { - data = (int*)get_linklist(curNodeNum, layer); - // data = (int *) (linkLists_[curNodeNum] + (layer - 1) * size_links_per_element_); - } - size_t size = getListCount((linklistsizeint*)data); - tableint *datal = (tableint *) (data + 1); -#ifdef USE_SSE - _mm_prefetch((char *) (visited_array + *(data + 1)), _MM_HINT_T0); - _mm_prefetch((char *) (visited_array + *(data + 1) + 64), _MM_HINT_T0); - _mm_prefetch(getDataByInternalId(*datal), _MM_HINT_T0); - _mm_prefetch(getDataByInternalId(*(datal + 1)), _MM_HINT_T0); -#endif - - for (size_t j = 0; j < size; j++) { - tableint candidate_id = *(datal + j); - // if (candidate_id == 0) continue; -#ifdef USE_SSE - _mm_prefetch((char *) (visited_array + *(datal + j + 1)), _MM_HINT_T0); - _mm_prefetch(getDataByInternalId(*(datal + j + 1)), _MM_HINT_T0); -#endif - if (visited_array[candidate_id] == visited_array_tag) continue; - visited_array[candidate_id] = visited_array_tag; - char *currObj1 = (getDataByInternalId(candidate_id)); - - dist_t dist1 = fstdistfunc_(data_point, currObj1, dist_func_param_); - if (top_candidates.size() < ef_construction_ || lowerBound > dist1) { - candidateSet.emplace(-dist1, candidate_id); -#ifdef USE_SSE - _mm_prefetch(getDataByInternalId(candidateSet.top().second), _MM_HINT_T0); -#endif - - if (!isMarkedDeleted(candidate_id)) - top_candidates.emplace(dist1, candidate_id); - - if (top_candidates.size() > ef_construction_) - top_candidates.pop(); - - if (!top_candidates.empty()) - lowerBound = top_candidates.top().first; - } - } - } - visited_list_pool_->releaseVisitedList(vl); - - return top_candidates; - } - - template - std::priority_queue, std::vector>, CompareByFirst> - searchBaseLayerST(tableint ep_id, const void *data_point, size_t ef, faiss::ConcurrentBitsetPtr bitset) const { - VisitedList *vl = visited_list_pool_->getFreeVisitedList(); - vl_type *visited_array = vl->mass; - vl_type visited_array_tag = vl->curV; - - std::priority_queue, std::vector>, CompareByFirst> top_candidates; - std::priority_queue, std::vector>, CompareByFirst> candidate_set; - - dist_t lowerBound; -// if (!has_deletions || !isMarkedDeleted(ep_id)) { - if (!has_deletions || !bitset->test((faiss::ConcurrentBitset::id_type_t)getExternalLabel(ep_id))) { - dist_t dist = fstdistfunc_(data_point, getDataByInternalId(ep_id), dist_func_param_); - lowerBound = dist; - top_candidates.emplace(dist, ep_id); - candidate_set.emplace(-dist, ep_id); - } else { - lowerBound = std::numeric_limits::max(); - candidate_set.emplace(-lowerBound, ep_id); - } - - visited_array[ep_id] = visited_array_tag; - - while (!candidate_set.empty()) { - - std::pair current_node_pair = candidate_set.top(); - - if ((-current_node_pair.first) > lowerBound) { - break; - } - candidate_set.pop(); - - tableint current_node_id = current_node_pair.second; - int *data = (int *) get_linklist0(current_node_id); - size_t size = getListCount((linklistsizeint*)data); - // bool cur_node_deleted = isMarkedDeleted(current_node_id); - -#ifdef USE_SSE - _mm_prefetch((char *) (visited_array + *(data + 1)), _MM_HINT_T0); - _mm_prefetch((char *) (visited_array + *(data + 1) + 64), _MM_HINT_T0); - _mm_prefetch(data_level0_memory_ + (*(data + 1)) * size_data_per_element_ + offsetData_, _MM_HINT_T0); - _mm_prefetch((char *) (data + 2), _MM_HINT_T0); -#endif - - for (size_t j = 1; j <= size; j++) { - int candidate_id = *(data + j); - // if (candidate_id == 0) continue; -#ifdef USE_SSE - _mm_prefetch((char *) (visited_array + *(data + j + 1)), _MM_HINT_T0); - _mm_prefetch(data_level0_memory_ + (*(data + j + 1)) * size_data_per_element_ + offsetData_, - _MM_HINT_T0);//////////// -#endif - if (!(visited_array[candidate_id] == visited_array_tag)) { - - visited_array[candidate_id] = visited_array_tag; - - char *currObj1 = (getDataByInternalId(candidate_id)); - dist_t dist = fstdistfunc_(data_point, currObj1, dist_func_param_); - - if (top_candidates.size() < ef || lowerBound > dist) { - candidate_set.emplace(-dist, candidate_id); -#ifdef USE_SSE - _mm_prefetch(data_level0_memory_ + candidate_set.top().second * size_data_per_element_ + - offsetLevel0_,/////////// - _MM_HINT_T0);//////////////////////// -#endif - -// if (!has_deletions || !isMarkedDeleted(candidate_id)) - if (!has_deletions || (!bitset->test((faiss::ConcurrentBitset::id_type_t)getExternalLabel(candidate_id)))) - top_candidates.emplace(dist, candidate_id); - - if (top_candidates.size() > ef) - top_candidates.pop(); - - if (!top_candidates.empty()) - lowerBound = top_candidates.top().first; - } - } - } - } - - visited_list_pool_->releaseVisitedList(vl); - return top_candidates; - } - - void getNeighborsByHeuristic2( - std::priority_queue, std::vector>, CompareByFirst> &top_candidates, - const size_t M) { - if (top_candidates.size() < M) { - return; - } - std::priority_queue> queue_closest; - std::vector> return_list; - while (top_candidates.size() > 0) { - queue_closest.emplace(-top_candidates.top().first, top_candidates.top().second); - top_candidates.pop(); - } - - while (queue_closest.size()) { - if (return_list.size() >= M) - break; - std::pair curent_pair = queue_closest.top(); - dist_t dist_to_query = -curent_pair.first; - queue_closest.pop(); - bool good = true; - for (std::pair second_pair : return_list) { - dist_t curdist = - fstdistfunc_(getDataByInternalId(second_pair.second), - getDataByInternalId(curent_pair.second), - dist_func_param_);; - if (curdist < dist_to_query) { - good = false; - break; - } - } - if (good) { - return_list.push_back(curent_pair); - } - - - } - - for (std::pair curent_pair : return_list) { - - top_candidates.emplace(-curent_pair.first, curent_pair.second); - } - } - - - linklistsizeint *get_linklist0(tableint internal_id) const { - return (linklistsizeint *) (data_level0_memory_ + internal_id * size_data_per_element_ + offsetLevel0_); - }; - - linklistsizeint *get_linklist0(tableint internal_id, char *data_level0_memory_) const { - return (linklistsizeint *) (data_level0_memory_ + internal_id * size_data_per_element_ + offsetLevel0_); - }; - - linklistsizeint *get_linklist(tableint internal_id, int level) const { - return (linklistsizeint *) (linkLists_[internal_id] + (level - 1) * size_links_per_element_); - }; - - void mutuallyConnectNewElement(const void *data_point, tableint cur_c, - std::priority_queue, std::vector>, CompareByFirst> top_candidates, - int level) { - - size_t Mcurmax = level ? maxM_ : maxM0_; - getNeighborsByHeuristic2(top_candidates, M_); - if (top_candidates.size() > M_) - throw std::runtime_error("Should be not be more than M_ candidates returned by the heuristic"); - - std::vector selectedNeighbors; - selectedNeighbors.reserve(M_); - while (top_candidates.size() > 0) { - selectedNeighbors.push_back(top_candidates.top().second); - top_candidates.pop(); - } - - { - linklistsizeint *ll_cur; - if (level == 0) - ll_cur = get_linklist0(cur_c); - else - ll_cur = get_linklist(cur_c, level); - - if (*ll_cur) { - throw std::runtime_error("The newly inserted element should have blank link list"); - } - setListCount(ll_cur,selectedNeighbors.size()); - tableint *data = (tableint *) (ll_cur + 1); - - - for (size_t idx = 0; idx < selectedNeighbors.size(); idx++) { - if (data[idx]) - throw std::runtime_error("Possible memory corruption"); - if (level > element_levels_[selectedNeighbors[idx]]) - throw std::runtime_error("Trying to make a link on a non-existent level"); - - data[idx] = selectedNeighbors[idx]; - - } - } - for (size_t idx = 0; idx < selectedNeighbors.size(); idx++) { - - std::unique_lock lock(link_list_locks_[selectedNeighbors[idx]]); - - - linklistsizeint *ll_other; - if (level == 0) - ll_other = get_linklist0(selectedNeighbors[idx]); - else - ll_other = get_linklist(selectedNeighbors[idx], level); - - size_t sz_link_list_other = getListCount(ll_other); - - if (sz_link_list_other > Mcurmax) - throw std::runtime_error("Bad value of sz_link_list_other"); - if (selectedNeighbors[idx] == cur_c) - throw std::runtime_error("Trying to connect an element to itself"); - if (level > element_levels_[selectedNeighbors[idx]]) - throw std::runtime_error("Trying to make a link on a non-existent level"); - - tableint *data = (tableint *) (ll_other + 1); - if (sz_link_list_other < Mcurmax) { - data[sz_link_list_other] = cur_c; - setListCount(ll_other, sz_link_list_other + 1); - } else { - // finding the "weakest" element to replace it with the new one - dist_t d_max = fstdistfunc_(getDataByInternalId(cur_c), getDataByInternalId(selectedNeighbors[idx]), - dist_func_param_); - // Heuristic: - std::priority_queue, std::vector>, CompareByFirst> candidates; - candidates.emplace(d_max, cur_c); - - for (size_t j = 0; j < sz_link_list_other; j++) { - candidates.emplace( - fstdistfunc_(getDataByInternalId(data[j]), getDataByInternalId(selectedNeighbors[idx]), - dist_func_param_), data[j]); - } - - getNeighborsByHeuristic2(candidates, Mcurmax); - - int indx = 0; - while (candidates.size() > 0) { - data[indx] = candidates.top().second; - candidates.pop(); - indx++; - } - setListCount(ll_other, indx); - // Nearest K: - /*int indx = -1; - for (int j = 0; j < sz_link_list_other; j++) { - dist_t d = fstdistfunc_(getDataByInternalId(data[j]), getDataByInternalId(rez[idx]), dist_func_param_); - if (d > d_max) { - indx = j; - d_max = d; - } - } - if (indx >= 0) { - data[indx] = cur_c; - } */ - } - - } - } - - std::mutex global; - size_t ef_; - - void setEf(size_t ef) { - ef_ = ef; - } - - - std::priority_queue> searchKnnInternal(void *query_data, int k) { - std::priority_queue> top_candidates; - if (cur_element_count == 0) return top_candidates; - tableint currObj = enterpoint_node_; - dist_t curdist = fstdistfunc_(query_data, getDataByInternalId(enterpoint_node_), dist_func_param_); - - for (size_t level = maxlevel_; level > 0; level--) { - bool changed = true; - while (changed) { - changed = false; - int *data; - data = (int *) get_linklist(currObj,level); - int size = getListCount(data); - tableint *datal = (tableint *) (data + 1); - for (int i = 0; i < size; i++) { - tableint cand = datal[i]; - if (cand < 0 || cand > max_elements_) - throw std::runtime_error("cand error"); - dist_t d = fstdistfunc_(query_data, getDataByInternalId(cand), dist_func_param_); - - if (d < curdist) { - curdist = d; - currObj = cand; - changed = true; - } - } - } - } - - if (has_deletions_) { - std::priority_queue> top_candidates1=searchBaseLayerST(currObj, query_data, - ef_); - top_candidates.swap(top_candidates1); - } - else{ - std::priority_queue> top_candidates1=searchBaseLayerST(currObj, query_data, - ef_); - top_candidates.swap(top_candidates1); - } - - while (top_candidates.size() > k) { - top_candidates.pop(); - } - return top_candidates; - }; - - void resizeIndex(size_t new_max_elements){ - if (new_max_elements(new_max_elements).swap(link_list_locks_); - - - // Reallocate base layer - char * data_level0_memory_new = (char *) malloc(new_max_elements * size_data_per_element_); - if (data_level0_memory_new == nullptr) - throw std::runtime_error("Not enough memory: resizeIndex failed to allocate base layer"); - memcpy(data_level0_memory_new, data_level0_memory_,cur_element_count * size_data_per_element_); - free(data_level0_memory_); - data_level0_memory_=data_level0_memory_new; - - // Reallocate all other layers - char ** linkLists_new = (char **) malloc(sizeof(void *) * new_max_elements); - if (linkLists_new == nullptr) - throw std::runtime_error("Not enough memory: resizeIndex failed to allocate other layers"); - memcpy(linkLists_new, linkLists_,cur_element_count * sizeof(void *)); - free(linkLists_); - linkLists_=linkLists_new; - - max_elements_=new_max_elements; - - } - - void saveIndex(milvus::knowhere::MemoryIOWriter& output) { - // write l2/ip calculator - writeBinaryPOD(output, metric_type_); - writeBinaryPOD(output, data_size_); - writeBinaryPOD(output, *((size_t *) dist_func_param_)); - - writeBinaryPOD(output, offsetLevel0_); - writeBinaryPOD(output, max_elements_); - writeBinaryPOD(output, cur_element_count); - writeBinaryPOD(output, size_data_per_element_); - writeBinaryPOD(output, label_offset_); - writeBinaryPOD(output, offsetData_); - writeBinaryPOD(output, maxlevel_); - writeBinaryPOD(output, enterpoint_node_); - writeBinaryPOD(output, maxM_); - - writeBinaryPOD(output, maxM0_); - writeBinaryPOD(output, M_); - writeBinaryPOD(output, mult_); - writeBinaryPOD(output, ef_construction_); - - output.write(data_level0_memory_, cur_element_count * size_data_per_element_); - - for (size_t i = 0; i < cur_element_count; i++) { - unsigned int linkListSize = element_levels_[i] > 0 ? size_links_per_element_ * element_levels_[i] : 0; - writeBinaryPOD(output, linkListSize); - if (linkListSize) - output.write(linkLists_[i], linkListSize); - } - // output.close(); - } - - void loadIndex(milvus::knowhere::MemoryIOReader& input, size_t max_elements_i = 0) { - // linxj: init with metrictype - size_t dim = 100; - readBinaryPOD(input, metric_type_); - readBinaryPOD(input, data_size_); - readBinaryPOD(input, dim); - if (metric_type_ == 0) { - space = new hnswlib::L2Space(dim); - } else if (metric_type_ == 1) { - space = new hnswlib::InnerProductSpace(dim); - } else { - // throw exception - } - fstdistfunc_ = space->get_dist_func(); - dist_func_param_ = space->get_dist_func_param(); - - readBinaryPOD(input, offsetLevel0_); - readBinaryPOD(input, max_elements_); - readBinaryPOD(input, cur_element_count); - - size_t max_elements=max_elements_i; - if(max_elements < cur_element_count) - max_elements = max_elements_; - max_elements_ = max_elements; - readBinaryPOD(input, size_data_per_element_); - readBinaryPOD(input, label_offset_); - readBinaryPOD(input, offsetData_); - readBinaryPOD(input, maxlevel_); - readBinaryPOD(input, enterpoint_node_); - - readBinaryPOD(input, maxM_); - readBinaryPOD(input, maxM0_); - readBinaryPOD(input, M_); - readBinaryPOD(input, mult_); - readBinaryPOD(input, ef_construction_); - - - // data_size_ = s->get_data_size(); - // fstdistfunc_ = s->get_dist_func(); - // dist_func_param_ = s->get_dist_func_param(); - - // auto pos= input.rp; - - - // /// Optional - check if index is ok: - // - // input.seekg(cur_element_count * size_data_per_element_,input.cur); - // for (size_t i = 0; i < cur_element_count; i++) { - // if(input.tellg() < 0 || input.tellg()>=total_filesize){ - // throw std::runtime_error("Index seems to be corrupted or unsupported"); - // } - // - // unsigned int linkListSize; - // readBinaryPOD(input, linkListSize); - // if (linkListSize != 0) { - // input.seekg(linkListSize,input.cur); - // } - // } - // - // // throw exception if it either corrupted or old index - // if(input.tellg()!=total_filesize) - // throw std::runtime_error("Index seems to be corrupted or unsupported"); - // - // input.clear(); - // - // /// Optional check end - // - // input.seekg(pos,input.beg); - - - data_level0_memory_ = (char *) malloc(max_elements * size_data_per_element_); - if (data_level0_memory_ == nullptr) - throw std::runtime_error("Not enough memory: loadIndex failed to allocate level0"); - input.read(data_level0_memory_, cur_element_count * size_data_per_element_); - - - - - size_links_per_element_ = maxM_ * sizeof(tableint) + sizeof(linklistsizeint); - - - size_links_level0_ = maxM0_ * sizeof(tableint) + sizeof(linklistsizeint); - std::vector(max_elements).swap(link_list_locks_); - - - visited_list_pool_ = new VisitedListPool(1, max_elements); - - - linkLists_ = (char **) malloc(sizeof(void *) * max_elements); - if (linkLists_ == nullptr) - throw std::runtime_error("Not enough memory: loadIndex failed to allocate linklists"); - element_levels_ = std::vector(max_elements); - ef_ = 10; - for (size_t i = 0; i < cur_element_count; i++) { - label_lookup_[getExternalLabel(i)]=i; - unsigned int linkListSize; - readBinaryPOD(input, linkListSize); - if (linkListSize == 0) { - element_levels_[i] = 0; - - linkLists_[i] = nullptr; - } else { - element_levels_[i] = linkListSize / size_links_per_element_; - linkLists_[i] = (char *) malloc(linkListSize); - if (linkLists_[i] == nullptr) - throw std::runtime_error("Not enough memory: loadIndex failed to allocate linklist"); - input.read(linkLists_[i], linkListSize); - } - } - - has_deletions_=false; - - for (size_t i = 0; i < cur_element_count; i++) { - if(isMarkedDeleted(i)) - has_deletions_=true; - } - - return; - } - - void saveIndex(const std::string &location) { - std::ofstream output(location, std::ios::binary); - std::streampos position; - - writeBinaryPOD(output, offsetLevel0_); - writeBinaryPOD(output, max_elements_); - writeBinaryPOD(output, cur_element_count); - writeBinaryPOD(output, size_data_per_element_); - writeBinaryPOD(output, label_offset_); - writeBinaryPOD(output, offsetData_); - writeBinaryPOD(output, maxlevel_); - writeBinaryPOD(output, enterpoint_node_); - writeBinaryPOD(output, maxM_); - - writeBinaryPOD(output, maxM0_); - writeBinaryPOD(output, M_); - writeBinaryPOD(output, mult_); - writeBinaryPOD(output, ef_construction_); - - output.write(data_level0_memory_, cur_element_count * size_data_per_element_); - - for (size_t i = 0; i < cur_element_count; i++) { - unsigned int linkListSize = element_levels_[i] > 0 ? size_links_per_element_ * element_levels_[i] : 0; - writeBinaryPOD(output, linkListSize); - if (linkListSize) - output.write(linkLists_[i], linkListSize); - } - output.close(); - } - - void loadIndex(const std::string &location, SpaceInterface *s, size_t max_elements_i=0) { - std::ifstream input(location, std::ios::binary); - - if (!input.is_open()) - throw std::runtime_error("Cannot open file"); - - // get file size: - input.seekg(0,input.end); - std::streampos total_filesize=input.tellg(); - input.seekg(0,input.beg); - - readBinaryPOD(input, offsetLevel0_); - readBinaryPOD(input, max_elements_); - readBinaryPOD(input, cur_element_count); - - size_t max_elements=max_elements_i; - if(max_elements < cur_element_count) - max_elements = max_elements_; - max_elements_ = max_elements; - readBinaryPOD(input, size_data_per_element_); - readBinaryPOD(input, label_offset_); - readBinaryPOD(input, offsetData_); - readBinaryPOD(input, maxlevel_); - readBinaryPOD(input, enterpoint_node_); - - readBinaryPOD(input, maxM_); - readBinaryPOD(input, maxM0_); - readBinaryPOD(input, M_); - readBinaryPOD(input, mult_); - readBinaryPOD(input, ef_construction_); - - data_size_ = s->get_data_size(); - fstdistfunc_ = s->get_dist_func(); - dist_func_param_ = s->get_dist_func_param(); - - auto pos=input.tellg(); - - /// Optional - check if index is ok: - - input.seekg(cur_element_count * size_data_per_element_,input.cur); - for (size_t i = 0; i < cur_element_count; i++) { - if(input.tellg() < 0 || input.tellg()>=total_filesize){ - throw std::runtime_error("Index seems to be corrupted or unsupported"); - } - - unsigned int linkListSize; - readBinaryPOD(input, linkListSize); - if (linkListSize != 0) { - input.seekg(linkListSize,input.cur); - } - } - - // throw exception if it either corrupted or old index - if(input.tellg()!=total_filesize) - throw std::runtime_error("Index seems to be corrupted or unsupported"); - - input.clear(); - - /// Optional check end - - input.seekg(pos,input.beg); - - data_level0_memory_ = (char *) malloc(max_elements * size_data_per_element_); - if (data_level0_memory_ == nullptr) - throw std::runtime_error("Not enough memory: loadIndex failed to allocate level0"); - input.read(data_level0_memory_, cur_element_count * size_data_per_element_); - - size_links_per_element_ = maxM_ * sizeof(tableint) + sizeof(linklistsizeint); - - size_links_level0_ = maxM0_ * sizeof(tableint) + sizeof(linklistsizeint); - std::vector(max_elements).swap(link_list_locks_); - - visited_list_pool_ = new VisitedListPool(1, max_elements); - - linkLists_ = (char **) malloc(sizeof(void *) * max_elements); - if (linkLists_ == nullptr) - throw std::runtime_error("Not enough memory: loadIndex failed to allocate linklists"); - element_levels_ = std::vector(max_elements); - ef_ = 10; - for (size_t i = 0; i < cur_element_count; i++) { - label_lookup_[getExternalLabel(i)]=i; - unsigned int linkListSize; - readBinaryPOD(input, linkListSize); - if (linkListSize == 0) { - element_levels_[i] = 0; - linkLists_[i] = nullptr; - } else { - element_levels_[i] = linkListSize / size_links_per_element_; - linkLists_[i] = (char *) malloc(linkListSize); - if (linkLists_[i] == nullptr) - throw std::runtime_error("Not enough memory: loadIndex failed to allocate linklist"); - input.read(linkLists_[i], linkListSize); - } - } - - has_deletions_=false; - - for (size_t i = 0; i < cur_element_count; i++) { - if(isMarkedDeleted(i)) - has_deletions_=true; - } - - input.close(); - return; - } - - template - std::vector getDataByLabel(labeltype label) { - tableint label_c; - auto search = label_lookup_.find(label); - if (search == label_lookup_.end() || isMarkedDeleted(search->second)) { - throw std::runtime_error("Label not found"); - } - label_c = search->second; - - char* data_ptrv = getDataByInternalId(label_c); - size_t dim = *((size_t *) dist_func_param_); - std::vector data; - data_t* data_ptr = (data_t*) data_ptrv; - for (int i = 0; i < dim; i++) { - data.push_back(*data_ptr); - data_ptr += 1; - } - return data; - } - - static const unsigned char DELETE_MARK = 0x01; - // static const unsigned char REUSE_MARK = 0x10; - /** - * Marks an element with the given label deleted, does NOT really change the current graph. - * @param label - */ - void markDelete(labeltype label) - { - has_deletions_=true; - auto search = label_lookup_.find(label); - if (search == label_lookup_.end()) { - throw std::runtime_error("Label not found"); - } - markDeletedInternal(search->second); - } - - /** - * Uses the first 8 bits of the memory for the linked list to store the mark, - * whereas maxM0_ has to be limited to the lower 24 bits, however, still large enough in almost all cases. - * @param internalId - */ - void markDeletedInternal(tableint internalId) { - unsigned char *ll_cur = ((unsigned char *)get_linklist0(internalId))+2; - *ll_cur |= DELETE_MARK; - } - - /** - * Remove the deleted mark of the node. - * @param internalId - */ - void unmarkDeletedInternal(tableint internalId) { - unsigned char *ll_cur = ((unsigned char *)get_linklist0(internalId))+2; - *ll_cur &= ~DELETE_MARK; - } - - /** - * Checks the first 8 bits of the memory to see if the element is marked deleted. - * @param internalId - * @return - */ - bool isMarkedDeleted(tableint internalId) const { - unsigned char *ll_cur = ((unsigned char*)get_linklist0(internalId))+2; - return *ll_cur & DELETE_MARK; - } - - unsigned short int getListCount(linklistsizeint * ptr) const { - return *((unsigned short int *)ptr); - } - - void setListCount(linklistsizeint * ptr, unsigned short int size) const { - *((unsigned short int*)(ptr))=*((unsigned short int *)&size); - } - - void addPoint(const void *data_point, labeltype label) { - addPoint(data_point, label,-1); - } - - tableint addPoint(const void *data_point, labeltype label, int level) { - tableint cur_c = 0; - { - std::unique_lock lock(cur_element_count_guard_); - if (cur_element_count >= max_elements_) { - throw std::runtime_error("The number of elements exceeds the specified limit"); - }; - - cur_c = cur_element_count; - cur_element_count++; - - auto search = label_lookup_.find(label); - if (search != label_lookup_.end()) { - std::unique_lock lock_el(link_list_locks_[search->second]); - has_deletions_ = true; - markDeletedInternal(search->second); - } - label_lookup_[label] = cur_c; - } - - std::unique_lock lock_el(link_list_locks_[cur_c]); - int curlevel = getRandomLevel(mult_); - if (level > 0) - curlevel = level; - - element_levels_[cur_c] = curlevel; - - std::unique_lock templock(global); - int maxlevelcopy = maxlevel_; - if (curlevel <= maxlevelcopy) - templock.unlock(); - tableint currObj = enterpoint_node_; - tableint enterpoint_copy = enterpoint_node_; - - memset(data_level0_memory_ + cur_c * size_data_per_element_ + offsetLevel0_, 0, size_data_per_element_); - - // Initialisation of the data and label - memcpy(getExternalLabeLp(cur_c), &label, sizeof(labeltype)); - memcpy(getDataByInternalId(cur_c), data_point, data_size_); - - if (curlevel) { - linkLists_[cur_c] = (char *) malloc(size_links_per_element_ * curlevel + 1); - if (linkLists_[cur_c] == nullptr) - throw std::runtime_error("Not enough memory: addPoint failed to allocate linklist"); - memset(linkLists_[cur_c], 0, size_links_per_element_ * curlevel + 1); - } - - if ((signed)currObj != -1) { - - if (curlevel < maxlevelcopy) { - - dist_t curdist = fstdistfunc_(data_point, getDataByInternalId(currObj), dist_func_param_); - for (int level = maxlevelcopy; level > curlevel; level--) { - bool changed = true; - while (changed) { - changed = false; - unsigned int *data; - std::unique_lock lock(link_list_locks_[currObj]); - data = get_linklist(currObj,level); - int size = getListCount(data); - - tableint *datal = (tableint *) (data + 1); - for (int i = 0; i < size; i++) { - tableint cand = datal[i]; - if (cand < 0 || cand > max_elements_) - throw std::runtime_error("cand error"); - dist_t d = fstdistfunc_(data_point, getDataByInternalId(cand), dist_func_param_); - if (d < curdist) { - curdist = d; - currObj = cand; - changed = true; - } - } - } - } - } - - bool epDeleted = isMarkedDeleted(enterpoint_copy); - for (int level = std::min(curlevel, maxlevelcopy); level >= 0; level--) { - if (level > maxlevelcopy || level < 0) // possible? - throw std::runtime_error("Level error"); - - std::priority_queue, std::vector>, CompareByFirst> top_candidates = searchBaseLayer( - currObj, data_point, level); - if (epDeleted) { - top_candidates.emplace(fstdistfunc_(data_point, getDataByInternalId(enterpoint_copy), dist_func_param_), enterpoint_copy); - if (top_candidates.size() > ef_construction_) - top_candidates.pop(); - } - mutuallyConnectNewElement(data_point, cur_c, top_candidates, level); - - currObj = top_candidates.top().second; - } - } else { - // Do nothing for the first element - enterpoint_node_ = 0; - maxlevel_ = curlevel; - } - - //Releasing lock for the maximum level - if (curlevel > maxlevelcopy) { - enterpoint_node_ = cur_c; - maxlevel_ = curlevel; - } - return cur_c; - }; - - std::priority_queue> - searchKnn(const void *query_data, size_t k, faiss::ConcurrentBitsetPtr bitset) const { - std::priority_queue> result; - if (cur_element_count == 0) return result; - - tableint currObj = enterpoint_node_; - dist_t curdist = fstdistfunc_(query_data, getDataByInternalId(enterpoint_node_), dist_func_param_); - - for (int level = maxlevel_; level > 0; level--) { - bool changed = true; - while (changed) { - changed = false; - unsigned int *data; - - data = (unsigned int *) get_linklist(currObj, level); - int size = getListCount(data); - tableint *datal = (tableint *) (data + 1); - for (int i = 0; i < size; i++) { - tableint cand = datal[i]; - if (cand < 0 || cand > max_elements_) - throw std::runtime_error("cand error"); - dist_t d = fstdistfunc_(query_data, getDataByInternalId(cand), dist_func_param_); - - if (d < curdist) { - curdist = d; - currObj = cand; - changed = true; - } - } - } - } - - std::priority_queue, std::vector>, CompareByFirst> top_candidates; - if (bitset != nullptr) { - std::priority_queue, std::vector>, CompareByFirst> - top_candidates1 = searchBaseLayerST(currObj, query_data, std::max(ef_, k), bitset); - top_candidates.swap(top_candidates1); - } - else{ - std::priority_queue, std::vector>, CompareByFirst> - top_candidates1 = searchBaseLayerST(currObj, query_data, std::max(ef_, k), bitset); - top_candidates.swap(top_candidates1); - } - while (top_candidates.size() > k) { - top_candidates.pop(); - } - while (top_candidates.size() > 0) { - std::pair rez = top_candidates.top(); - result.push(std::pair(rez.first, getExternalLabel(rez.second))); - top_candidates.pop(); - } - return result; - }; - - template - std::vector> - searchKnn(const void* query_data, size_t k, Comp comp, faiss::ConcurrentBitsetPtr bitset) { - std::vector> result; - if (cur_element_count == 0) return result; - - auto ret = searchKnn(query_data, k, bitset); - - while (!ret.empty()) { - result.push_back(ret.top()); - ret.pop(); - } - - std::sort(result.begin(), result.end(), comp); - - return result; - } - - int64_t cal_size() { - int64_t ret = 0; - ret += sizeof(*this); - ret += sizeof(*space); - ret += visited_list_pool_->GetSize(); - ret += link_list_locks_.size() * sizeof(std::mutex); - ret += element_levels_.size() * sizeof(int); - ret += max_elements_ * size_data_per_element_; - ret += max_elements_ * sizeof(void*); - for (auto i = 0; i < max_elements_; ++ i) { - ret += linkLists_[i] ? size_links_per_element_ * element_levels_[i] : 0; - } - return ret; - } -}; - -} diff --git a/core/src/index/thirdparty/hnswlib/hnswlib.h b/core/src/index/thirdparty/hnswlib/hnswlib.h deleted file mode 100644 index 89d8d423a7..0000000000 --- a/core/src/index/thirdparty/hnswlib/hnswlib.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once -#ifndef NO_MANUAL_VECTORIZATION -#ifdef __SSE__ -#define USE_SSE -#ifdef __AVX__ -#define USE_AVX -#endif -#endif -#endif - -#if defined(USE_AVX) || defined(USE_SSE) -#ifdef _MSC_VER -#include -#include -#else -#include -#endif - -#if defined(__GNUC__) -#define PORTABLE_ALIGN32 __attribute__((aligned(32))) -#else -#define PORTABLE_ALIGN32 __declspec(align(32)) -#endif -#endif - -#include -#include -#include - -#include -#include - -namespace hnswlib { - typedef int64_t labeltype; - - template - class pairGreater { - public: - bool operator()(const T& p1, const T& p2) { - return p1.first > p2.first; - } - }; - - template - static void writeBinaryPOD(std::ostream &out, const T &podRef) { - out.write((char *) &podRef, sizeof(T)); - } - - template - static void readBinaryPOD(std::istream &in, T &podRef) { - in.read((char *) &podRef, sizeof(T)); - } - - template - static void writeBinaryPOD(W &out, const T &podRef) { - out.write((char *) &podRef, sizeof(T)); - } - - template - static void readBinaryPOD(R &in, T &podRef) { - in.read((char *) &podRef, sizeof(T)); - } - - template - using DISTFUNC = MTYPE(*)(const void *, const void *, const void *); - - - template - class SpaceInterface { - public: - //virtual void search(void *); - virtual size_t get_data_size() = 0; - - virtual DISTFUNC get_dist_func() = 0; - - virtual void *get_dist_func_param() = 0; - - virtual ~SpaceInterface() {} - }; - - template - class AlgorithmInterface { - public: - virtual void addPoint(const void *datapoint, labeltype label)=0; - virtual std::priority_queue> searchKnn(const void *, size_t, faiss::ConcurrentBitsetPtr bitset) const = 0; - template - std::vector> searchKnn(const void*, size_t, Comp, faiss::ConcurrentBitsetPtr bitset) { - } - virtual void saveIndex(const std::string &location)=0; - virtual ~AlgorithmInterface(){ - } - }; -} - -#include "space_l2.h" -#include "space_ip.h" -#include "bruteforce.h" -#include "hnswalg.h" diff --git a/core/src/index/thirdparty/hnswlib/space_ip.h b/core/src/index/thirdparty/hnswlib/space_ip.h deleted file mode 100644 index fc25485e7d..0000000000 --- a/core/src/index/thirdparty/hnswlib/space_ip.h +++ /dev/null @@ -1,253 +0,0 @@ -#pragma once -#include "hnswlib.h" -#include - -namespace hnswlib { - -static float -InnerProduct(const void *pVect1, const void *pVect2, const void *qty_ptr) { -#if 0 /* use FAISS distance calculation algorithm instead */ - size_t qty = *((size_t *) qty_ptr); - float res = 0; - for (unsigned i = 0; i < qty; i++) { - res += ((float *) pVect1)[i] * ((float *) pVect2)[i]; - } - return (1.0f - res); -#else - return (1.0f - faiss::fvec_inner_product((const float*)pVect1, (const float*)pVect2, *((size_t*)qty_ptr))); -#endif -} - -#if 0 /* use FAISS distance calculation algorithm instead */ -#if defined(USE_AVX) - -// Favor using AVX if available. -static float -InnerProductSIMD4Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) { - float PORTABLE_ALIGN32 TmpRes[8]; - float *pVect1 = (float *) pVect1v; - float *pVect2 = (float *) pVect2v; - size_t qty = *((size_t *) qty_ptr); - - size_t qty16 = qty / 16; - size_t qty4 = qty / 4; - - const float *pEnd1 = pVect1 + 16 * qty16; - const float *pEnd2 = pVect1 + 4 * qty4; - - __m256 sum256 = _mm256_set1_ps(0); - - while (pVect1 < pEnd1) { - //_mm_prefetch((char*)(pVect2 + 16), _MM_HINT_T0); - - __m256 v1 = _mm256_loadu_ps(pVect1); - pVect1 += 8; - __m256 v2 = _mm256_loadu_ps(pVect2); - pVect2 += 8; - sum256 = _mm256_add_ps(sum256, _mm256_mul_ps(v1, v2)); - - v1 = _mm256_loadu_ps(pVect1); - pVect1 += 8; - v2 = _mm256_loadu_ps(pVect2); - pVect2 += 8; - sum256 = _mm256_add_ps(sum256, _mm256_mul_ps(v1, v2)); - } - - __m128 v1, v2; - __m128 sum_prod = _mm_add_ps(_mm256_extractf128_ps(sum256, 0), _mm256_extractf128_ps(sum256, 1)); - - while (pVect1 < pEnd2) { - v1 = _mm_loadu_ps(pVect1); - pVect1 += 4; - v2 = _mm_loadu_ps(pVect2); - pVect2 += 4; - sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2)); - } - - _mm_store_ps(TmpRes, sum_prod); - float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3];; - return 1.0f - sum; -} - -#elif defined(USE_SSE) - -static float -InnerProductSIMD4Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) { - float PORTABLE_ALIGN32 TmpRes[8]; - float *pVect1 = (float *) pVect1v; - float *pVect2 = (float *) pVect2v; - size_t qty = *((size_t *) qty_ptr); - - size_t qty16 = qty / 16; - size_t qty4 = qty / 4; - - const float *pEnd1 = pVect1 + 16 * qty16; - const float *pEnd2 = pVect1 + 4 * qty4; - - __m128 v1, v2; - __m128 sum_prod = _mm_set1_ps(0); - - while (pVect1 < pEnd1) { - v1 = _mm_loadu_ps(pVect1); - pVect1 += 4; - v2 = _mm_loadu_ps(pVect2); - pVect2 += 4; - sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2)); - - v1 = _mm_loadu_ps(pVect1); - pVect1 += 4; - v2 = _mm_loadu_ps(pVect2); - pVect2 += 4; - sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2)); - - v1 = _mm_loadu_ps(pVect1); - pVect1 += 4; - v2 = _mm_loadu_ps(pVect2); - pVect2 += 4; - sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2)); - - v1 = _mm_loadu_ps(pVect1); - pVect1 += 4; - v2 = _mm_loadu_ps(pVect2); - pVect2 += 4; - sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2)); - } - - while (pVect1 < pEnd2) { - v1 = _mm_loadu_ps(pVect1); - pVect1 += 4; - v2 = _mm_loadu_ps(pVect2); - pVect2 += 4; - sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2)); - } - - _mm_store_ps(TmpRes, sum_prod); - float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3]; - - return 1.0f - sum; -} - -#endif - -#if defined(USE_AVX) - -static float -InnerProductSIMD16Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) { - float PORTABLE_ALIGN32 TmpRes[8]; - float *pVect1 = (float *) pVect1v; - float *pVect2 = (float *) pVect2v; - size_t qty = *((size_t *) qty_ptr); - - size_t qty16 = qty / 16; - - - const float *pEnd1 = pVect1 + 16 * qty16; - - __m256 sum256 = _mm256_set1_ps(0); - - while (pVect1 < pEnd1) { - //_mm_prefetch((char*)(pVect2 + 16), _MM_HINT_T0); - - __m256 v1 = _mm256_loadu_ps(pVect1); - pVect1 += 8; - __m256 v2 = _mm256_loadu_ps(pVect2); - pVect2 += 8; - sum256 = _mm256_add_ps(sum256, _mm256_mul_ps(v1, v2)); - - v1 = _mm256_loadu_ps(pVect1); - pVect1 += 8; - v2 = _mm256_loadu_ps(pVect2); - pVect2 += 8; - sum256 = _mm256_add_ps(sum256, _mm256_mul_ps(v1, v2)); - } - - _mm256_store_ps(TmpRes, sum256); - float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3] + TmpRes[4] + TmpRes[5] + TmpRes[6] + TmpRes[7]; - - return 1.0f - sum; -} - -#elif defined(USE_SSE) - -static float -InnerProductSIMD16Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) { - float PORTABLE_ALIGN32 TmpRes[8]; - float *pVect1 = (float *) pVect1v; - float *pVect2 = (float *) pVect2v; - size_t qty = *((size_t *) qty_ptr); - - size_t qty16 = qty / 16; - - const float *pEnd1 = pVect1 + 16 * qty16; - - __m128 v1, v2; - __m128 sum_prod = _mm_set1_ps(0); - - while (pVect1 < pEnd1) { - v1 = _mm_loadu_ps(pVect1); - pVect1 += 4; - v2 = _mm_loadu_ps(pVect2); - pVect2 += 4; - sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2)); - - v1 = _mm_loadu_ps(pVect1); - pVect1 += 4; - v2 = _mm_loadu_ps(pVect2); - pVect2 += 4; - sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2)); - - v1 = _mm_loadu_ps(pVect1); - pVect1 += 4; - v2 = _mm_loadu_ps(pVect2); - pVect2 += 4; - sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2)); - - v1 = _mm_loadu_ps(pVect1); - pVect1 += 4; - v2 = _mm_loadu_ps(pVect2); - pVect2 += 4; - sum_prod = _mm_add_ps(sum_prod, _mm_mul_ps(v1, v2)); - } - _mm_store_ps(TmpRes, sum_prod); - float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3]; - - return 1.0f - sum; -} - -#endif -#endif - -class InnerProductSpace : public SpaceInterface { - DISTFUNC fstdistfunc_; - size_t data_size_; - size_t dim_; - public: - InnerProductSpace(size_t dim) { - fstdistfunc_ = InnerProduct; -#if 0 /* use FAISS distance calculation algorithm instead */ -#if defined(USE_AVX) || defined(USE_SSE) - if (dim % 4 == 0) - fstdistfunc_ = InnerProductSIMD4Ext; - if (dim % 16 == 0) - fstdistfunc_ = InnerProductSIMD16Ext; -#endif -#endif - dim_ = dim; - data_size_ = dim * sizeof(float); - } - - size_t get_data_size() { - return data_size_; - } - - DISTFUNC get_dist_func() { - return fstdistfunc_; - } - - void *get_dist_func_param() { - return &dim_; - } - - ~InnerProductSpace() {} -}; -} diff --git a/core/src/index/thirdparty/hnswlib/space_l2.h b/core/src/index/thirdparty/hnswlib/space_l2.h deleted file mode 100644 index 3fd0d2da2c..0000000000 --- a/core/src/index/thirdparty/hnswlib/space_l2.h +++ /dev/null @@ -1,245 +0,0 @@ -#pragma once -#include "hnswlib.h" -#include - -namespace hnswlib { - -static float -L2Sqr(const void *pVect1, const void *pVect2, const void *qty_ptr) { -#if 0 /* use FAISS distance calculation algorithm instead */ - //return *((float *)pVect2); - size_t qty = *((size_t *) qty_ptr); - float res = 0; - for (unsigned i = 0; i < qty; i++) { - float t = ((float *) pVect1)[i] - ((float *) pVect2)[i]; - res += t * t; - } - return (res); -#else - return faiss::fvec_L2sqr((const float*)pVect1, (const float*)pVect2, *((size_t*)qty_ptr)); -#endif -} - -#if 0 /* use FAISS distance calculation algorithm instead */ -#if defined(USE_AVX) - -// Favor using AVX if available. -static float -L2SqrSIMD16Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) { - float *pVect1 = (float *) pVect1v; - float *pVect2 = (float *) pVect2v; - size_t qty = *((size_t *) qty_ptr); - float PORTABLE_ALIGN32 TmpRes[8]; - size_t qty16 = qty >> 4; - - const float *pEnd1 = pVect1 + (qty16 << 4); - - __m256 diff, v1, v2; - __m256 sum = _mm256_set1_ps(0); - - while (pVect1 < pEnd1) { - v1 = _mm256_loadu_ps(pVect1); - pVect1 += 8; - v2 = _mm256_loadu_ps(pVect2); - pVect2 += 8; - diff = _mm256_sub_ps(v1, v2); - sum = _mm256_add_ps(sum, _mm256_mul_ps(diff, diff)); - - v1 = _mm256_loadu_ps(pVect1); - pVect1 += 8; - v2 = _mm256_loadu_ps(pVect2); - pVect2 += 8; - diff = _mm256_sub_ps(v1, v2); - sum = _mm256_add_ps(sum, _mm256_mul_ps(diff, diff)); - } - - _mm256_store_ps(TmpRes, sum); - float res = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3] + TmpRes[4] + TmpRes[5] + TmpRes[6] + TmpRes[7]; - - return (res); -} - -#elif defined(USE_SSE) - -static float -L2SqrSIMD16Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) { - float *pVect1 = (float *) pVect1v; - float *pVect2 = (float *) pVect2v; - size_t qty = *((size_t *) qty_ptr); - float PORTABLE_ALIGN32 TmpRes[8]; - // size_t qty4 = qty >> 2; - size_t qty16 = qty >> 4; - - const float *pEnd1 = pVect1 + (qty16 << 4); - // const float* pEnd2 = pVect1 + (qty4 << 2); - // const float* pEnd3 = pVect1 + qty; - - __m128 diff, v1, v2; - __m128 sum = _mm_set1_ps(0); - - while (pVect1 < pEnd1) { - //_mm_prefetch((char*)(pVect2 + 16), _MM_HINT_T0); - v1 = _mm_loadu_ps(pVect1); - pVect1 += 4; - v2 = _mm_loadu_ps(pVect2); - pVect2 += 4; - diff = _mm_sub_ps(v1, v2); - sum = _mm_add_ps(sum, _mm_mul_ps(diff, diff)); - - v1 = _mm_loadu_ps(pVect1); - pVect1 += 4; - v2 = _mm_loadu_ps(pVect2); - pVect2 += 4; - diff = _mm_sub_ps(v1, v2); - sum = _mm_add_ps(sum, _mm_mul_ps(diff, diff)); - - v1 = _mm_loadu_ps(pVect1); - pVect1 += 4; - v2 = _mm_loadu_ps(pVect2); - pVect2 += 4; - diff = _mm_sub_ps(v1, v2); - sum = _mm_add_ps(sum, _mm_mul_ps(diff, diff)); - - v1 = _mm_loadu_ps(pVect1); - pVect1 += 4; - v2 = _mm_loadu_ps(pVect2); - pVect2 += 4; - diff = _mm_sub_ps(v1, v2); - sum = _mm_add_ps(sum, _mm_mul_ps(diff, diff)); - } - _mm_store_ps(TmpRes, sum); - float res = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3]; - - return (res); -} -#endif - - -#ifdef USE_SSE -static float -L2SqrSIMD4Ext(const void *pVect1v, const void *pVect2v, const void *qty_ptr) { - float PORTABLE_ALIGN32 TmpRes[8]; - float *pVect1 = (float *) pVect1v; - float *pVect2 = (float *) pVect2v; - size_t qty = *((size_t *) qty_ptr); - - - // size_t qty4 = qty >> 2; - size_t qty16 = qty >> 2; - - const float *pEnd1 = pVect1 + (qty16 << 2); - - __m128 diff, v1, v2; - __m128 sum = _mm_set1_ps(0); - - while (pVect1 < pEnd1) { - v1 = _mm_loadu_ps(pVect1); - pVect1 += 4; - v2 = _mm_loadu_ps(pVect2); - pVect2 += 4; - diff = _mm_sub_ps(v1, v2); - sum = _mm_add_ps(sum, _mm_mul_ps(diff, diff)); - } - _mm_store_ps(TmpRes, sum); - float res = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3]; - - return (res); -} -#endif -#endif - -class L2Space : public SpaceInterface { - DISTFUNC fstdistfunc_; - size_t data_size_; - size_t dim_; - public: - L2Space(size_t dim) { - fstdistfunc_ = L2Sqr; -#if 0 /* use FAISS distance calculation algorithm instead */ -#if defined(USE_SSE) || defined(USE_AVX) - if (dim % 4 == 0) - fstdistfunc_ = L2SqrSIMD4Ext; - if (dim % 16 == 0) - fstdistfunc_ = L2SqrSIMD16Ext; - /*else{ - throw runtime_error("Data type not supported!"); - }*/ -#endif -#endif - dim_ = dim; - data_size_ = dim * sizeof(float); - } - - size_t get_data_size() { - return data_size_; - } - - DISTFUNC get_dist_func() { - return fstdistfunc_; - } - - void *get_dist_func_param() { - return &dim_; - } - - ~L2Space() {} -}; - -static int -L2SqrI(const void *__restrict pVect1, const void *__restrict pVect2, const void *__restrict qty_ptr) { - size_t qty = *((size_t *) qty_ptr); - int res = 0; - unsigned char *a = (unsigned char *) pVect1; - unsigned char *b = (unsigned char *) pVect2; - /*for (int i = 0; i < qty; i++) { - int t = int((a)[i]) - int((b)[i]); - res += t*t; - }*/ - - qty = qty >> 2; - for (size_t i = 0; i < qty; i++) { - - res += ((*a) - (*b)) * ((*a) - (*b)); - a++; - b++; - res += ((*a) - (*b)) * ((*a) - (*b)); - a++; - b++; - res += ((*a) - (*b)) * ((*a) - (*b)); - a++; - b++; - res += ((*a) - (*b)) * ((*a) - (*b)); - a++; - b++; - } - - return (res); -} - -class L2SpaceI : public SpaceInterface { - DISTFUNC fstdistfunc_; - size_t data_size_; - size_t dim_; - public: - L2SpaceI(size_t dim) { - fstdistfunc_ = L2SqrI; - dim_ = dim; - data_size_ = dim * sizeof(unsigned char); - } - - size_t get_data_size() { - return data_size_; - } - - DISTFUNC get_dist_func() { - return fstdistfunc_; - } - - void *get_dist_func_param() { - return &dim_; - } - - ~L2SpaceI() {} -}; - -} diff --git a/core/src/index/thirdparty/hnswlib/visited_list_pool.h b/core/src/index/thirdparty/hnswlib/visited_list_pool.h deleted file mode 100644 index 1a86ff6ae8..0000000000 --- a/core/src/index/thirdparty/hnswlib/visited_list_pool.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once - -#include -#include - -namespace hnswlib { -typedef unsigned short int vl_type; - -class VisitedList { - public: - vl_type curV; - vl_type *mass; - unsigned int numelements; - - VisitedList(int numelements1) { - curV = -1; - numelements = numelements1; - mass = new vl_type[numelements]; - } - - void reset() { - curV++; - if (curV == 0) { - memset(mass, 0, sizeof(vl_type) * numelements); - curV++; - } - }; - - - ~VisitedList() { delete[] mass; } -}; - -/////////////////////////////////////////////////////////// -// -// Class for multi-threaded pool-management of VisitedLists -// -///////////////////////////////////////////////////////// - -class VisitedListPool { - std::deque pool; - std::mutex poolguard; - int numelements; - - public: - VisitedListPool(int initmaxpools, int numelements1) { - numelements = numelements1; - for (int i = 0; i < initmaxpools; i++) - pool.push_front(new VisitedList(numelements)); - } - - VisitedList *getFreeVisitedList() { - VisitedList *rez; - { - std::unique_lock lock(poolguard); - if (pool.size() > 0) { - rez = pool.front(); - pool.pop_front(); - } else { - rez = new VisitedList(numelements); - } - } - rez->reset(); - return rez; - }; - - void releaseVisitedList(VisitedList *vl) { - std::unique_lock lock(poolguard); - pool.push_front(vl); - }; - - ~VisitedListPool() { - while (pool.size()) { - VisitedList *rez = pool.front(); - pool.pop_front(); - delete rez; - } - }; - - int64_t GetSize() { - auto visit_list_size = sizeof(VisitedList) + numelements * sizeof(vl_type); - auto pool_size = pool.size() * (sizeof(VisitedList *) + visit_list_size); - return pool_size + sizeof(*this); - } -}; -} - diff --git a/core/src/index/thirdparty/versions.txt b/core/src/index/thirdparty/versions.txt deleted file mode 100644 index c5cea80234..0000000000 --- a/core/src/index/thirdparty/versions.txt +++ /dev/null @@ -1,6 +0,0 @@ -ARROW_VERSION=apache-arrow-0.15.1 -BOOST_VERSION=1.70.0 -GTEST_VERSION=1.8.1 -LAPACK_VERSION=v3.8.0 -OPENBLAS_VERSION=0.3.9 -MKL_VERSION=2019.5.281 diff --git a/core/src/index/unittest/CMakeLists.txt b/core/src/index/unittest/CMakeLists.txt deleted file mode 100644 index 074c39b264..0000000000 --- a/core/src/index/unittest/CMakeLists.txt +++ /dev/null @@ -1,203 +0,0 @@ -include_directories(${INDEX_SOURCE_DIR}/thirdparty) -include_directories(${INDEX_SOURCE_DIR}/thirdparty/SPTAG/AnnService) -include_directories(${INDEX_SOURCE_DIR}/knowhere) -include_directories(${INDEX_SOURCE_DIR}) - -set(depend_libs - gtest gmock gtest_main gmock_main - faiss fiu - ) -if (FAISS_WITH_MKL) - set(depend_libs ${depend_libs} - "-Wl,--start-group \ - ${MKL_LIB_PATH}/libmkl_intel_ilp64.a \ - ${MKL_LIB_PATH}/libmkl_gnu_thread.a \ - ${MKL_LIB_PATH}/libmkl_core.a \ - -Wl,--end-group -lgomp -lpthread -lm -ldl" - ) -else () - set(depend_libs ${depend_libs} - ${OpenBLAS_LIBRARIES} - ${LAPACK_LIBRARIES} - ) -endif () - -set(basic_libs - gomp gfortran pthread - ) - -set(util_srcs - ${MILVUS_THIRDPARTY_SRC}/easyloggingpp/easylogging++.cc - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/adapter/VectorAdapter.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/helpers/FaissIO.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/helpers/IndexParameter.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/IndexType.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/common/Exception.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/common/Log.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/common/Timer.cpp - ${INDEX_SOURCE_DIR}/unittest/utils.cpp - ) - -if (KNOWHERE_GPU_VERSION) - include_directories(${CUDA_INCLUDE_DIRS}) - link_directories("${CUDA_TOOLKIT_ROOT_DIR}/lib64") - set(cuda_lib - cudart - cublas - ) - set(basic_libs ${basic_libs} - ${cuda_lib} - ) - set(util_srcs ${util_srcs} - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/helpers/FaissGpuResourceMgr.cpp - ) -endif () - -set(faiss_srcs - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/FaissBaseIndex.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/FaissBaseBinaryIndex.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/IndexBinaryIDMAP.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/IndexBinaryIVF.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/IndexIDMAP.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/IndexIVF.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/IndexIVFSQ.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/IndexIVFPQ.cpp - ) -if (KNOWHERE_GPU_VERSION) -set(faiss_srcs ${faiss_srcs} - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/helpers/Cloner.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/gpu/IndexGPUIDMAP.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVF.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFSQ.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/gpu/IndexGPUIVFPQ.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/gpu/IndexIVFSQHybrid.cpp - ) -endif () - -################################################################################ -# -if (NOT TARGET test_instructionset) - add_executable(test_instructionset test_instructionset.cpp) -endif () -target_link_libraries(test_instructionset ${depend_libs} ${unittest_libs}) -install(TARGETS test_instructionset DESTINATION unittest) - -################################################################################ -# -if (NOT TARGET test_knowhere_common) - add_executable(test_knowhere_common test_common.cpp ${util_srcs}) -endif () -target_link_libraries(test_knowhere_common ${depend_libs} ${unittest_libs} ${basic_libs}) -install(TARGETS test_knowhere_common DESTINATION unittest) - -if (KNOWHERE_GPU_VERSION) -################################################################################ -# -add_executable(test_gpuresource test_gpuresource.cpp ${util_srcs} ${faiss_srcs}) -target_link_libraries(test_gpuresource ${depend_libs} ${unittest_libs} ${basic_libs}) -install(TARGETS test_gpuresource DESTINATION unittest) - -################################################################################ -# -add_executable(test_customized_index test_customized_index.cpp ${util_srcs} ${faiss_srcs}) -target_link_libraries(test_customized_index ${depend_libs} ${unittest_libs} ${basic_libs}) -install(TARGETS test_customized_index DESTINATION unittest) -endif () - -################################################################################ -# -if (NOT TARGET test_idmap) - add_executable(test_idmap test_idmap.cpp ${faiss_srcs} ${util_srcs}) -endif () -target_link_libraries(test_idmap ${depend_libs} ${unittest_libs} ${basic_libs}) -install(TARGETS test_idmap DESTINATION unittest) - -################################################################################ -# -if (NOT TARGET test_ivf) - add_executable(test_ivf test_ivf.cpp ${faiss_srcs} ${util_srcs}) -endif () -target_link_libraries(test_ivf ${depend_libs} ${unittest_libs} ${basic_libs}) -install(TARGETS test_ivf DESTINATION unittest) - -################################################################################ -# -if (NOT TARGET test_binaryidmap) - add_executable(test_binaryidmap test_binaryidmap.cpp ${faiss_srcs} ${util_srcs}) -endif () -target_link_libraries(test_binaryidmap ${depend_libs} ${unittest_libs} ${basic_libs}) -install(TARGETS test_binaryidmap DESTINATION unittest) - -################################################################################ -# -if (NOT TARGET test_binaryivf) - add_executable(test_binaryivf test_binaryivf.cpp ${faiss_srcs} ${util_srcs}) -endif () -target_link_libraries(test_binaryivf ${depend_libs} ${unittest_libs} ${basic_libs}) -install(TARGETS test_binaryivf DESTINATION unittest) - - -################################################################################ -# -add_definitions(-std=c++11 -O3 -lboost -march=native -Wall -DINFO) - -find_package(OpenMP REQUIRED) -if (OpenMP_FOUND) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") -else () - message(FATAL_ERROR "no OpenMP supprot") -endif () - -include_directories(${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/impl/nsg) -aux_source_directory(${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/impl/nsg nsg_src) -set(interface_src - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/IndexNSG.cpp - ) -if (NOT TARGET test_nsg) - add_executable(test_nsg test_nsg.cpp ${interface_src} ${nsg_src} ${util_srcs} ${faiss_srcs}) -endif () -target_link_libraries(test_nsg ${depend_libs} ${unittest_libs} ${basic_libs}) -install(TARGETS test_nsg DESTINATION unittest) - -################################################################################ -# -set(hnsw_srcs - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/IndexHNSW.cpp - ) -if (NOT TARGET test_hnsw) - add_executable(test_hnsw test_hnsw.cpp ${hnsw_srcs} ${util_srcs}) -endif () -target_link_libraries(test_hnsw ${depend_libs} ${unittest_libs} ${basic_libs}) -install(TARGETS test_hnsw DESTINATION unittest) - -################################################################################ -# -set(sptag_srcs - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/adapter/SptagAdapter.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/helpers/SPTAGParameterMgr.cpp - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/IndexSPTAG.cpp - ) -if (NOT TARGET test_sptag) - add_executable(test_sptag test_sptag.cpp ${sptag_srcs} ${util_srcs}) -endif () -target_link_libraries(test_sptag - SPTAGLibStatic - ${depend_libs} ${unittest_libs} ${basic_libs}) -install(TARGETS test_sptag DESTINATION unittest) - -################################################################################ -# -set(annoy_srcs - ${INDEX_SOURCE_DIR}/knowhere/knowhere/index/vector_index/IndexAnnoy.cpp - ) -if (NOT TARGET test_annoy) - add_executable(test_annoy test_annoy.cpp ${annoy_srcs} ${util_srcs}) -endif () -target_link_libraries(test_annoy ${depend_libs} ${unittest_libs} ${basic_libs}) -install(TARGETS test_annoy DESTINATION unittest) - - -#add_subdirectory(faiss_ori) -#add_subdirectory(faiss_benchmark) -#add_subdirectory(metric_alg_benchmark) diff --git a/core/src/index/unittest/Helper.h b/core/src/index/unittest/Helper.h deleted file mode 100644 index 7ab2810875..0000000000 --- a/core/src/index/unittest/Helper.h +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include - -#include "knowhere/index/vector_index/IndexIVF.h" -#include "knowhere/index/vector_index/IndexIVFPQ.h" -#include "knowhere/index/vector_index/IndexIVFSQ.h" -#include "knowhere/index/vector_index/IndexType.h" -#include "knowhere/index/vector_index/helpers/IndexParameter.h" - -#ifdef MILVUS_GPU_VERSION -#include "knowhere/index/vector_index/gpu/IndexGPUIVF.h" -#include "knowhere/index/vector_index/gpu/IndexGPUIVFPQ.h" -#include "knowhere/index/vector_index/gpu/IndexGPUIVFSQ.h" -#include "knowhere/index/vector_index/gpu/IndexIVFSQHybrid.h" -#endif - -int DEVICEID = 0; -constexpr int64_t DIM = 128; -constexpr int64_t NB = 10000; -constexpr int64_t NQ = 10; -constexpr int64_t K = 10; -constexpr int64_t PINMEM = 1024 * 1024 * 200; -constexpr int64_t TEMPMEM = 1024 * 1024 * 300; -constexpr int64_t RESNUM = 2; - -milvus::knowhere::IVFPtr -IndexFactory(const milvus::knowhere::IndexType& type, const milvus::knowhere::IndexMode mode) { - if (mode == milvus::knowhere::IndexMode::MODE_CPU) { - if (type == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFFLAT) { - return std::make_shared(); - } else if (type == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFPQ) { - return std::make_shared(); - } else if (type == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFSQ8) { - return std::make_shared(); - } else if (type == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFSQ8H) { - std::cout << "IVFSQ8H does not support MODE_CPU" << std::endl; - } else { - std::cout << "Invalid IndexType " << type << std::endl; - } -#ifdef MILVUS_GPU_VERSION - } else { - if (type == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFFLAT) { - return std::make_shared(DEVICEID); - } else if (type == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFPQ) { - return std::make_shared(DEVICEID); - } else if (type == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFSQ8) { - return std::make_shared(DEVICEID); - } else if (type == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFSQ8H) { - return std::make_shared(DEVICEID); - } else { - std::cout << "Invalid IndexType " << type << std::endl; - } -#endif - } - return nullptr; -} - -class ParamGenerator { - public: - static ParamGenerator& - GetInstance() { - static ParamGenerator instance; - return instance; - } - - milvus::knowhere::Config - Gen(const milvus::knowhere::IndexType& type) { - if (type == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFFLAT) { - return milvus::knowhere::Config{ - {milvus::knowhere::meta::DIM, DIM}, - {milvus::knowhere::meta::TOPK, K}, - {milvus::knowhere::IndexParams::nlist, 100}, - {milvus::knowhere::IndexParams::nprobe, 4}, - {milvus::knowhere::Metric::TYPE, milvus::knowhere::Metric::L2}, - {milvus::knowhere::meta::DEVICEID, DEVICEID}, - }; - } else if (type == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFPQ) { - return milvus::knowhere::Config{ - {milvus::knowhere::meta::DIM, DIM}, - {milvus::knowhere::meta::TOPK, K}, - {milvus::knowhere::IndexParams::nlist, 100}, - {milvus::knowhere::IndexParams::nprobe, 4}, - {milvus::knowhere::IndexParams::m, 4}, - {milvus::knowhere::IndexParams::nbits, 8}, - {milvus::knowhere::Metric::TYPE, milvus::knowhere::Metric::L2}, - {milvus::knowhere::meta::DEVICEID, DEVICEID}, - }; - } else if (type == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFSQ8 || - type == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFSQ8H) { - return milvus::knowhere::Config{ - {milvus::knowhere::meta::DIM, DIM}, - {milvus::knowhere::meta::TOPK, K}, - {milvus::knowhere::IndexParams::nlist, 100}, - {milvus::knowhere::IndexParams::nprobe, 4}, - {milvus::knowhere::IndexParams::nbits, 8}, - {milvus::knowhere::Metric::TYPE, milvus::knowhere::Metric::L2}, - {milvus::knowhere::meta::DEVICEID, DEVICEID}, - }; - } else { - std::cout << "Invalid index type " << type << std::endl; - } - return milvus::knowhere::Config(); - } -}; - -#include - -class TestGpuIndexBase : public ::testing::Test { - protected: - void - SetUp() override { -#ifdef MILVUS_GPU_VERSION - milvus::knowhere::FaissGpuResourceMgr::GetInstance().InitDevice(DEVICEID, PINMEM, TEMPMEM, RESNUM); -#endif - } - - void - TearDown() override { -#ifdef MILVUS_GPU_VERSION - milvus::knowhere::FaissGpuResourceMgr::GetInstance().Free(); -#endif - } -}; diff --git a/core/src/index/unittest/SPTAG.cpp b/core/src/index/unittest/SPTAG.cpp deleted file mode 100644 index c12a8060b7..0000000000 --- a/core/src/index/unittest/SPTAG.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -// -//#include -//#include -//#include -//#include -//#include -// -// int -// main(int argc, char* argv[]) { -// using namespace SPTAG; -// const int d = 128; -// const int n = 100; -// -// auto p_data = new float[n * d]; -// -// auto index = VectorIndex::CreateInstance(IndexAlgoType::KDT, VectorValueType::Float); -// -// std::random_device rd; -// std::mt19937 mt(rd()); -// std::uniform_real_distribution dist(1.0, 2.0); -// -// for (auto i = 0; i < n; i++) { -// for (auto j = 0; j < d; j++) { -// p_data[i * d + j] = dist(mt) - 1; -// } -// } -// std::cout << "generate random n * d finished."; -// ByteArray data((uint8_t*)p_data, n * d * sizeof(float), true); -// -// auto vectorset = std::make_shared(data, VectorValueType::Float, d, n); -// index->BuildIndex(vectorset, nullptr); -// -// std::cout << index->GetFeatureDim(); -//} diff --git a/core/src/index/unittest/faiss_benchmark/CMakeLists.txt b/core/src/index/unittest/faiss_benchmark/CMakeLists.txt deleted file mode 100644 index 9c3175506e..0000000000 --- a/core/src/index/unittest/faiss_benchmark/CMakeLists.txt +++ /dev/null @@ -1,53 +0,0 @@ -if (KNOWHERE_GPU_VERSION) - - include_directories(${INDEX_SOURCE_DIR}/thirdparty) - include_directories(${INDEX_SOURCE_DIR}/include) - include_directories(/usr/local/cuda/include) - include_directories(/usr/local/hdf5/include) - - link_directories(/usr/local/cuda/lib64) - link_directories(/usr/local/hdf5/lib) - - set(unittest_libs - gtest gmock gtest_main gmock_main) - - set(depend_libs - faiss hdf5 - ) - if (FAISS_WITH_MKL) - set(depend_libs ${depend_libs} - "-Wl,--start-group \ - ${MKL_LIB_PATH}/libmkl_intel_ilp64.a \ - ${MKL_LIB_PATH}/libmkl_gnu_thread.a \ - ${MKL_LIB_PATH}/libmkl_core.a \ - -Wl,--end-group -lgomp -lpthread -lm -ldl" - ) - else () - set(depend_libs ${depend_libs} - ${OpenBLAS_LIBRARIES} - ${LAPACK_LIBRARIES} - ) - endif () - - set(basic_libs - gomp gfortran pthread - ) - - include_directories(${CUDA_INCLUDE_DIRS}) - link_directories("${CUDA_TOOLKIT_ROOT_DIR}/lib64") - set(cuda_lib - cudart - cublas - ) - set(basic_libs ${basic_libs} - ${cuda_lib} - ) - - add_executable(test_faiss_benchmark faiss_benchmark_test.cpp) - target_link_libraries(test_faiss_benchmark ${depend_libs} ${unittest_libs} ${basic_libs}) - install(TARGETS test_faiss_benchmark DESTINATION unittest) - - add_executable(test_faiss_bitset faiss_bitset_test.cpp) - target_link_libraries(test_faiss_bitset ${depend_libs} ${unittest_libs} ${basic_libs}) - install(TARGETS test_faiss_bitset DESTINATION unittest) -endif () diff --git a/core/src/index/unittest/faiss_benchmark/README.md b/core/src/index/unittest/faiss_benchmark/README.md deleted file mode 100644 index c451ac13b0..0000000000 --- a/core/src/index/unittest/faiss_benchmark/README.md +++ /dev/null @@ -1,25 +0,0 @@ -### To run this FAISS benchmark, please follow these steps: - -#### Step 1: -Download the HDF5 source from: - https://support.hdfgroup.org/ftp/HDF5/releases/ -and build/install to "/usr/local/hdf5". - -#### Step 2: -Download HDF5 data files from: - https://github.com/erikbern/ann-benchmarks - -#### Step 3: -Update 'milvus/core/src/index/unittest/CMakeLists.txt', -uncomment "#add_subdirectory(faiss_benchmark)". - -#### Step 4: -Build Milvus with unittest enabled: "./build.sh -t Release -u", -binary 'test_faiss_benchmark' will be generated. - -#### Step 5: -Put HDF5 data files into the same directory with binary 'test_faiss_benchmark'. - -#### Step 6: -Run test binary 'test_faiss_benchmark'. - diff --git a/core/src/index/unittest/faiss_benchmark/faiss_benchmark_test.cpp b/core/src/index/unittest/faiss_benchmark/faiss_benchmark_test.cpp deleted file mode 100644 index a08b19daed..0000000000 --- a/core/src/index/unittest/faiss_benchmark/faiss_benchmark_test.cpp +++ /dev/null @@ -1,570 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/***************************************************** - * To run this test, please download the HDF5 from - * https://support.hdfgroup.org/ftp/HDF5/releases/ - * and install it to /usr/local/hdf5 . - *****************************************************/ -#define DEBUG_VERBOSE 0 - -const char HDF5_POSTFIX[] = ".hdf5"; -const char HDF5_DATASET_TRAIN[] = "train"; -const char HDF5_DATASET_TEST[] = "test"; -const char HDF5_DATASET_NEIGHBORS[] = "neighbors"; -const char HDF5_DATASET_DISTANCES[] = "distances"; - -const int32_t GPU_DEVICE_IDX = 0; - -enum QueryMode { MODE_CPU = 0, MODE_MIX, MODE_GPU }; - -double -elapsed() { - struct timeval tv; - gettimeofday(&tv, nullptr); - return tv.tv_sec + tv.tv_usec * 1e-6; -} - -void -normalize(float* arr, size_t nq, size_t dim) { - for (size_t i = 0; i < nq; i++) { - double vecLen = 0.0, inv_vecLen = 0.0; - for (size_t j = 0; j < dim; j++) { - double val = arr[i * dim + j]; - vecLen += val * val; - } - inv_vecLen = 1.0 / std::sqrt(vecLen); - for (size_t j = 0; j < dim; j++) { - arr[i * dim + j] = (float)(arr[i * dim + j] * inv_vecLen); - } - } -} - -void* -hdf5_read(const std::string& file_name, const std::string& dataset_name, H5T_class_t dataset_class, size_t& d_out, - size_t& n_out) { - hid_t file, dataset, datatype, dataspace, memspace; - H5T_class_t t_class; /* data type class */ - hsize_t dimsm[3]; /* memory space dimensions */ - hsize_t dims_out[2]; /* dataset dimensions */ - hsize_t count[2]; /* size of the hyperslab in the file */ - hsize_t offset[2]; /* hyperslab offset in the file */ - hsize_t count_out[3]; /* size of the hyperslab in memory */ - hsize_t offset_out[3]; /* hyperslab offset in memory */ - void* data_out; /* output buffer */ - - /* Open the file and the dataset. */ - file = H5Fopen(file_name.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT); - dataset = H5Dopen2(file, dataset_name.c_str(), H5P_DEFAULT); - - /* Get datatype and dataspace handles and then query - * dataset class, order, size, rank and dimensions. */ - datatype = H5Dget_type(dataset); /* datatype handle */ - t_class = H5Tget_class(datatype); - assert(t_class == dataset_class || !"Illegal dataset class type"); - - dataspace = H5Dget_space(dataset); /* dataspace handle */ - H5Sget_simple_extent_dims(dataspace, dims_out, nullptr); - n_out = dims_out[0]; - d_out = dims_out[1]; - - /* Define hyperslab in the dataset. */ - offset[0] = offset[1] = 0; - count[0] = dims_out[0]; - count[1] = dims_out[1]; - H5Sselect_hyperslab(dataspace, H5S_SELECT_SET, offset, nullptr, count, nullptr); - - /* Define the memory dataspace. */ - dimsm[0] = dims_out[0]; - dimsm[1] = dims_out[1]; - dimsm[2] = 1; - memspace = H5Screate_simple(3, dimsm, nullptr); - - /* Define memory hyperslab. */ - offset_out[0] = offset_out[1] = offset_out[2] = 0; - count_out[0] = dims_out[0]; - count_out[1] = dims_out[1]; - count_out[2] = 1; - H5Sselect_hyperslab(memspace, H5S_SELECT_SET, offset_out, nullptr, count_out, nullptr); - - /* Read data from hyperslab in the file into the hyperslab in memory and display. */ - switch (t_class) { - case H5T_INTEGER: - data_out = new int[dims_out[0] * dims_out[1]]; - H5Dread(dataset, H5T_NATIVE_INT, memspace, dataspace, H5P_DEFAULT, data_out); - break; - case H5T_FLOAT: - data_out = new float[dims_out[0] * dims_out[1]]; - H5Dread(dataset, H5T_NATIVE_FLOAT, memspace, dataspace, H5P_DEFAULT, data_out); - break; - default: - printf("Illegal dataset class type\n"); - break; - } - - /* Close/release resources. */ - H5Tclose(datatype); - H5Dclose(dataset); - H5Sclose(dataspace); - H5Sclose(memspace); - H5Fclose(file); - - return data_out; -} - -std::string -get_index_file_name(const std::string& ann_test_name, const std::string& index_key, int32_t data_loops) { - size_t pos = index_key.find_first_of(',', 0); - std::string file_name = ann_test_name; - file_name = file_name + "_" + index_key.substr(0, pos) + "_" + index_key.substr(pos + 1); - file_name = file_name + "_" + std::to_string(data_loops) + ".index"; - return file_name; -} - -bool -parse_ann_test_name(const std::string& ann_test_name, size_t& dim, faiss::MetricType& metric_type) { - size_t pos1, pos2; - - if (ann_test_name.empty()) - return false; - - pos1 = ann_test_name.find_first_of('-', 0); - if (pos1 == std::string::npos) - return false; - pos2 = ann_test_name.find_first_of('-', pos1 + 1); - if (pos2 == std::string::npos) - return false; - - dim = std::stoi(ann_test_name.substr(pos1 + 1, pos2 - pos1 - 1)); - std::string metric_str = ann_test_name.substr(pos2 + 1); - if (metric_str == "angular") { - metric_type = faiss::METRIC_INNER_PRODUCT; - } else if (metric_str == "euclidean") { - metric_type = faiss::METRIC_L2; - } else { - return false; - } - - return true; -} - -int32_t -GetResultHitCount(const faiss::Index::idx_t* ground_index, const faiss::Index::idx_t* index, size_t ground_k, size_t k, - size_t nq, int32_t index_add_loops) { - size_t min_k = std::min(ground_k, k); - int hit = 0; - for (int i = 0; i < nq; i++) { - std::set ground(ground_index + i * ground_k, - ground_index + i * ground_k + min_k / index_add_loops); - for (int j = 0; j < min_k; j++) { - faiss::Index::idx_t id = index[i * k + j]; - if (ground.count(id) > 0) { - hit++; - } - } - } - return hit; -} - -#if DEBUG_VERBOSE -void -print_array(const char* header, bool is_integer, const void* arr, size_t nq, size_t k) { - const int ROW = 10; - const int COL = 10; - assert(ROW <= nq); - assert(COL <= k); - printf("%s\n", header); - printf("==============================================\n"); - for (int i = 0; i < 10; i++) { - for (int j = 0; j < 10; j++) { - if (is_integer) { - printf("%7ld ", ((int64_t*)arr)[i * k + j]); - } else { - printf("%.6f ", ((float*)arr)[i * k + j]); - } - } - printf("\n"); - } - printf("\n"); -} -#endif - -void -load_base_data(faiss::Index*& index, const std::string& ann_test_name, const std::string& index_key, - faiss::gpu::StandardGpuResources& res, const faiss::MetricType metric_type, const size_t dim, - int32_t index_add_loops, QueryMode mode = MODE_CPU) { - double t0 = elapsed(); - - const std::string ann_file_name = ann_test_name + HDF5_POSTFIX; - - faiss::Index *cpu_index = nullptr, *gpu_index = nullptr; - faiss::distance_compute_blas_threshold = 800; - - std::string index_file_name = get_index_file_name(ann_test_name, index_key, index_add_loops); - - try { - printf("[%.3f s] Reading index file: %s\n", elapsed() - t0, index_file_name.c_str()); - cpu_index = faiss::read_index(index_file_name.c_str()); - } catch (...) { - size_t nb, d; - printf("[%.3f s] Loading HDF5 file: %s\n", elapsed() - t0, ann_file_name.c_str()); - float* xb = (float*)hdf5_read(ann_file_name, HDF5_DATASET_TRAIN, H5T_FLOAT, d, nb); - assert(d == dim || !"dataset does not have correct dimension"); - - if (metric_type == faiss::METRIC_INNER_PRODUCT) { - printf("[%.3f s] Normalizing base data set \n", elapsed() - t0); - normalize(xb, nb, d); - } - - printf("[%.3f s] Creating CPU index \"%s\" d=%ld\n", elapsed() - t0, index_key.c_str(), d); - cpu_index = faiss::index_factory(d, index_key.c_str(), metric_type); - - printf("[%.3f s] Cloning CPU index to GPU\n", elapsed() - t0); - gpu_index = faiss::gpu::index_cpu_to_gpu(&res, GPU_DEVICE_IDX, cpu_index); - delete cpu_index; - - printf("[%.3f s] Training on %ld vectors\n", elapsed() - t0, nb); - gpu_index->train(nb, xb); - - // add index multiple times to get ~1G data set - for (int i = 0; i < index_add_loops; i++) { - printf("[%.3f s] No.%d Indexing database, size %ld*%ld\n", elapsed() - t0, i, nb, d); - std::vector xids(nb); - for (int t = 0; t < nb; t++) { - xids[t] = i * nb + t; - } - gpu_index->add_with_ids(nb, xb, xids.data()); - } - - printf("[%.3f s] Coping GPU index to CPU\n", elapsed() - t0); - - cpu_index = faiss::gpu::index_gpu_to_cpu(gpu_index); - delete gpu_index; - - faiss::IndexIVF* cpu_ivf_index = dynamic_cast(cpu_index); - if (cpu_ivf_index != nullptr) { - cpu_ivf_index->to_readonly(); - } - - printf("[%.3f s] Writing index file: %s\n", elapsed() - t0, index_file_name.c_str()); - faiss::write_index(cpu_index, index_file_name.c_str()); - - delete[] xb; - } - - index = cpu_index; -} - -void -load_query_data(faiss::Index::distance_t*& xq, size_t& nq, const std::string& ann_test_name, - const faiss::MetricType metric_type, const size_t dim) { - double t0 = elapsed(); - size_t d; - - const std::string ann_file_name = ann_test_name + HDF5_POSTFIX; - - xq = (float*)hdf5_read(ann_file_name, HDF5_DATASET_TEST, H5T_FLOAT, d, nq); - assert(d == dim || !"query does not have same dimension as train set"); - - if (metric_type == faiss::METRIC_INNER_PRODUCT) { - printf("[%.3f s] Normalizing query data \n", elapsed() - t0); - normalize(xq, nq, d); - } -} - -void -load_ground_truth(faiss::Index::idx_t*& gt, size_t& k, const std::string& ann_test_name, const size_t nq) { - const std::string ann_file_name = ann_test_name + HDF5_POSTFIX; - - // load ground-truth and convert int to long - size_t nq2; - int* gt_int = (int*)hdf5_read(ann_file_name, HDF5_DATASET_NEIGHBORS, H5T_INTEGER, k, nq2); - assert(nq2 == nq || !"incorrect nb of ground truth index"); - - gt = new faiss::Index::idx_t[k * nq]; - for (int i = 0; i < k * nq; i++) { - gt[i] = gt_int[i]; - } - delete[] gt_int; - -#if DEBUG_VERBOSE - faiss::Index::distance_t* gt_dist; // nq * k matrix of ground-truth nearest-neighbors distances - gt_dist = (float*)hdf5_read(ann_file_name, HDF5_DATASET_DISTANCES, H5T_FLOAT, k, nq2); - assert(nq2 == nq || !"incorrect nb of ground truth distance"); - - std::string str; - str = ann_test_name + " ground truth index"; - print_array(str.c_str(), true, gt, nq, k); - str = ann_test_name + " ground truth distance"; - print_array(str.c_str(), false, gt_dist, nq, k); - - delete gt_dist; -#endif -} - -void -test_with_nprobes(const std::string& ann_test_name, const std::string& index_key, faiss::Index* cpu_index, - faiss::gpu::StandardGpuResources& res, const QueryMode query_mode, const faiss::Index::distance_t* xq, - const faiss::Index::idx_t* gt, const std::vector nprobes, const int32_t index_add_loops, - const int32_t search_loops) { - double t0 = elapsed(); - - const std::vector NQ = {10, 100}; - const std::vector K = {10, 100, 1000}; - const size_t GK = 100; // topk of ground truth - - std::unordered_map mode_str_map = { - {MODE_CPU, "MODE_CPU"}, {MODE_MIX, "MODE_MIX"}, {MODE_GPU, "MODE_GPU"}}; - - double copy_time = 0.0; - faiss::Index *gpu_index, *index; - if (query_mode != MODE_CPU) { - faiss::gpu::GpuClonerOptions option; - option.allInGpu = true; - - faiss::IndexComposition index_composition; - index_composition.index = cpu_index; - index_composition.quantizer = nullptr; - switch (query_mode) { - case MODE_MIX: { - index_composition.mode = 1; // 0: all data, 1: copy quantizer, 2: copy data - - // warm up the transmission - gpu_index = faiss::gpu::index_cpu_to_gpu(&res, GPU_DEVICE_IDX, &index_composition, &option); - delete gpu_index; - - copy_time = elapsed(); - gpu_index = faiss::gpu::index_cpu_to_gpu(&res, GPU_DEVICE_IDX, &index_composition, &option); - delete gpu_index; - copy_time = elapsed() - copy_time; - printf("[%.3f s] Copy quantizer completed, cost %f s\n", elapsed() - t0, copy_time); - - auto ivf_index = dynamic_cast(cpu_index); - auto is_gpu_flat_index = dynamic_cast(ivf_index->quantizer); - if (is_gpu_flat_index == nullptr) { - delete ivf_index->quantizer; - ivf_index->quantizer = index_composition.quantizer; - } - index = cpu_index; - break; - } - case MODE_GPU: -#if 1 - index_composition.mode = 0; // 0: all data, 1: copy quantizer, 2: copy data - - // warm up the transmission - gpu_index = faiss::gpu::index_cpu_to_gpu(&res, GPU_DEVICE_IDX, &index_composition, &option); - delete gpu_index; - - copy_time = elapsed(); - gpu_index = faiss::gpu::index_cpu_to_gpu(&res, GPU_DEVICE_IDX, &index_composition, &option); -#else - // warm up the transmission - gpu_index = faiss::gpu::index_cpu_to_gpu(&res, GPU_DEVICE_IDX, cpu_index, &option); - delete gpu_index; - - copy_time = elapsed(); - gpu_index = faiss::gpu::index_cpu_to_gpu(&res, GPU_DEVICE_IDX, cpu_index, &option); -#endif - copy_time = elapsed() - copy_time; - printf("[%.3f s] Copy data completed, cost %f s\n", elapsed() - t0, copy_time); - - delete cpu_index; - index = gpu_index; - break; - } - } else { - index = cpu_index; - } - - for (auto nprobe : nprobes) { - // brute-force need not set nprobe - if (index_key.find("IDMap") == std::string::npos) { - switch (query_mode) { - case MODE_CPU: - case MODE_MIX: { - faiss::ParameterSpace params; - std::string nprobe_str = "nprobe=" + std::to_string(nprobe); - params.set_index_parameters(index, nprobe_str.c_str()); - break; - } - case MODE_GPU: { - faiss::gpu::GpuIndexIVF* gpu_index_ivf = dynamic_cast(index); - gpu_index_ivf->setNumProbes(nprobe); - } - } - } - - // output buffers - faiss::Index::idx_t* I = new faiss::Index::idx_t[NQ.back() * K.back()]; - faiss::Index::distance_t* D = new faiss::Index::distance_t[NQ.back() * K.back()]; - - printf("\n%s | %s - %s | nprobe=%lu\n", ann_test_name.c_str(), index_key.c_str(), - mode_str_map[query_mode].c_str(), nprobe); - printf("======================================================================================\n"); - for (size_t j = 0; j < K.size(); j++) { - size_t t_k = K[j]; - for (size_t i = 0; i < NQ.size(); i++) { - size_t t_nq = NQ[i]; - faiss::indexIVF_stats.quantization_time = 0.0; - faiss::indexIVF_stats.search_time = 0.0; - - double t_start = elapsed(), t_end; - for (int s = 0; s < search_loops; s++) { - index->search(t_nq, xq, t_k, D, I); - } - t_end = elapsed(); - -#if DEBUG_VERBOSE - std::string str; - str = "I (" + index_key + ", nq=" + std::to_string(t_nq) + ", k=" + std::to_string(t_k) + ")"; - print_array(str.c_str(), true, I, t_nq, t_k); - str = "D (" + index_key + ", nq=" + std::to_string(t_nq) + ", k=" + std::to_string(t_k) + ")"; - print_array(str.c_str(), false, D, t_nq, t_k); -#endif - - // k = 100 for ground truth - int32_t hit = GetResultHitCount(gt, I, GK, t_k, t_nq, index_add_loops); - - printf("nq = %4ld, k = %4ld, elapse = %.4fs (quant = %.4fs, search = %.4fs), R@ = %.4f\n", t_nq, t_k, - (t_end - t_start) / search_loops, faiss::indexIVF_stats.quantization_time / 1000 / search_loops, - faiss::indexIVF_stats.search_time / 1000 / search_loops, - (hit / float(t_nq * std::min(GK, t_k) / index_add_loops))); - } - } - printf("======================================================================================\n"); - - delete[] I; - delete[] D; - } - - delete index; -} - -void -test_ann_hdf5(const std::string& ann_test_name, const std::string& cluster_type, const std::string& index_type, - const QueryMode query_mode, int32_t index_add_loops, const std::vector& nprobes, - int32_t search_loops) { - double t0 = elapsed(); - - faiss::gpu::StandardGpuResources res; - - faiss::MetricType metric_type; - size_t dim; - - if (query_mode == MODE_MIX && index_type != "SQ8Hybrid") { - assert(index_type == "SQ8Hybrid" || !"Only SQ8Hybrid support MODE_MIX"); - return; - } - - std::string index_key = cluster_type + "," + index_type; - - if (!parse_ann_test_name(ann_test_name, dim, metric_type)) { - printf("Invalid ann test name: %s\n", ann_test_name.c_str()); - return; - } - - size_t nq, k; - faiss::Index* index; - faiss::Index::distance_t* xq; - faiss::Index::idx_t* gt; // ground-truth index - - printf("[%.3f s] Loading base data\n", elapsed() - t0); - load_base_data(index, ann_test_name, index_key, res, metric_type, dim, index_add_loops, query_mode); - - printf("[%.3f s] Loading queries\n", elapsed() - t0); - load_query_data(xq, nq, ann_test_name, metric_type, dim); - - printf("[%.3f s] Loading ground truth for %ld queries\n", elapsed() - t0, nq); - load_ground_truth(gt, k, ann_test_name, nq); - - test_with_nprobes(ann_test_name, index_key, index, res, query_mode, xq, gt, nprobes, index_add_loops, search_loops); - printf("[%.3f s] Search test done\n\n", elapsed() - t0); - - delete[] xq; - delete[] gt; -} - -/************************************************************************************ - * https://github.com/erikbern/ann-benchmarks - * - * Dataset Dimensions Train_size Test_size Neighbors Distance Download - * Fashion- - MNIST 784 60,000 10,000 100 Euclidean HDF5 (217MB) - * GIST 960 1,000,000 1,000 100 Euclidean HDF5 (3.6GB) - * GloVe 100 1,183,514 10,000 100 Angular HDF5 (463MB) - * GloVe 200 1,183,514 10,000 100 Angular HDF5 (918MB) - * MNIST 784 60,000 10,000 100 Euclidean HDF5 (217MB) - * NYTimes 256 290,000 10,000 100 Angular HDF5 (301MB) - * SIFT 128 1,000,000 10,000 100 Euclidean HDF5 (501MB) - *************************************************************************************/ - -TEST(FAISSTEST, BENCHMARK) { - std::vector param_nprobes = {8, 128}; - const int32_t SEARCH_LOOPS = 5; - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const int32_t SIFT_INSERT_LOOPS = 2; // insert twice to get ~1G data set - - test_ann_hdf5("sift-128-euclidean", "IDMap", "Flat", MODE_CPU, SIFT_INSERT_LOOPS, param_nprobes, SEARCH_LOOPS); - test_ann_hdf5("sift-128-euclidean", "IDMap", "Flat", MODE_GPU, SIFT_INSERT_LOOPS, param_nprobes, SEARCH_LOOPS); - - test_ann_hdf5("sift-128-euclidean", "IVF16384", "Flat", MODE_CPU, SIFT_INSERT_LOOPS, param_nprobes, SEARCH_LOOPS); - test_ann_hdf5("sift-128-euclidean", "IVF16384", "Flat", MODE_GPU, SIFT_INSERT_LOOPS, param_nprobes, SEARCH_LOOPS); - - test_ann_hdf5("sift-128-euclidean", "IVF16384", "SQ8", MODE_CPU, SIFT_INSERT_LOOPS, param_nprobes, SEARCH_LOOPS); - test_ann_hdf5("sift-128-euclidean", "IVF16384", "SQ8", MODE_GPU, SIFT_INSERT_LOOPS, param_nprobes, SEARCH_LOOPS); - - test_ann_hdf5("sift-128-euclidean", "IVF16384", "SQ8Hybrid", MODE_CPU, SIFT_INSERT_LOOPS, param_nprobes, - SEARCH_LOOPS); - test_ann_hdf5("sift-128-euclidean", "IVF16384", "SQ8Hybrid", MODE_MIX, SIFT_INSERT_LOOPS, param_nprobes, - SEARCH_LOOPS); - test_ann_hdf5("sift-128-euclidean", "IVF16384", "SQ8Hybrid", MODE_GPU, SIFT_INSERT_LOOPS, param_nprobes, - SEARCH_LOOPS); - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - const int32_t GLOVE_INSERT_LOOPS = 1; - - test_ann_hdf5("glove-200-angular", "IVF16384", "Flat", MODE_CPU, GLOVE_INSERT_LOOPS, param_nprobes, SEARCH_LOOPS); - test_ann_hdf5("glove-200-angular", "IVF16384", "Flat", MODE_GPU, GLOVE_INSERT_LOOPS, param_nprobes, SEARCH_LOOPS); - - test_ann_hdf5("glove-200-angular", "IVF16384", "SQ8", MODE_CPU, GLOVE_INSERT_LOOPS, param_nprobes, SEARCH_LOOPS); - test_ann_hdf5("glove-200-angular", "IVF16384", "SQ8", MODE_GPU, GLOVE_INSERT_LOOPS, param_nprobes, SEARCH_LOOPS); - - test_ann_hdf5("glove-200-angular", "IVF16384", "SQ8Hybrid", MODE_CPU, GLOVE_INSERT_LOOPS, param_nprobes, - SEARCH_LOOPS); - test_ann_hdf5("glove-200-angular", "IVF16384", "SQ8Hybrid", MODE_MIX, GLOVE_INSERT_LOOPS, param_nprobes, - SEARCH_LOOPS); - test_ann_hdf5("glove-200-angular", "IVF16384", "SQ8Hybrid", MODE_GPU, GLOVE_INSERT_LOOPS, param_nprobes, - SEARCH_LOOPS); -} diff --git a/core/src/index/unittest/faiss_benchmark/faiss_bitset_test.cpp b/core/src/index/unittest/faiss_benchmark/faiss_bitset_test.cpp deleted file mode 100644 index bd8fa1fff8..0000000000 --- a/core/src/index/unittest/faiss_benchmark/faiss_bitset_test.cpp +++ /dev/null @@ -1,573 +0,0 @@ -// 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 limitations -// under the License. - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/***************************************************** - * To run this test, please download the HDF5 from - * https://support.hdfgroup.org/ftp/HDF5/releases/ - * and install it to /usr/local/hdf5 . - *****************************************************/ -#define DEBUG_VERBOSE 0 - -const char HDF5_POSTFIX[] = ".hdf5"; -const char HDF5_DATASET_TRAIN[] = "train"; -const char HDF5_DATASET_TEST[] = "test"; -const char HDF5_DATASET_NEIGHBORS[] = "neighbors"; -const char HDF5_DATASET_DISTANCES[] = "distances"; - -const int32_t GPU_DEVICE_IDX = 0; - -enum QueryMode { MODE_CPU = 0, MODE_MIX, MODE_GPU }; - -double -elapsed() { - struct timeval tv; - gettimeofday(&tv, nullptr); - return tv.tv_sec + tv.tv_usec * 1e-6; -} - -void -normalize(float* arr, size_t nq, size_t dim) { - for (size_t i = 0; i < nq; i++) { - double vecLen = 0.0, inv_vecLen = 0.0; - for (size_t j = 0; j < dim; j++) { - double val = arr[i * dim + j]; - vecLen += val * val; - } - inv_vecLen = 1.0 / std::sqrt(vecLen); - for (size_t j = 0; j < dim; j++) { - arr[i * dim + j] = (float)(arr[i * dim + j] * inv_vecLen); - } - } -} - -void* -hdf5_read(const std::string& file_name, const std::string& dataset_name, H5T_class_t dataset_class, size_t& d_out, - size_t& n_out) { - hid_t file, dataset, datatype, dataspace, memspace; - H5T_class_t t_class; /* data type class */ - hsize_t dimsm[3]; /* memory space dimensions */ - hsize_t dims_out[2]; /* dataset dimensions */ - hsize_t count[2]; /* size of the hyperslab in the file */ - hsize_t offset[2]; /* hyperslab offset in the file */ - hsize_t count_out[3]; /* size of the hyperslab in memory */ - hsize_t offset_out[3]; /* hyperslab offset in memory */ - void* data_out; /* output buffer */ - - /* Open the file and the dataset. */ - file = H5Fopen(file_name.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT); - dataset = H5Dopen2(file, dataset_name.c_str(), H5P_DEFAULT); - - /* Get datatype and dataspace handles and then query - * dataset class, order, size, rank and dimensions. */ - datatype = H5Dget_type(dataset); /* datatype handle */ - t_class = H5Tget_class(datatype); - assert(t_class == dataset_class || !"Illegal dataset class type"); - - dataspace = H5Dget_space(dataset); /* dataspace handle */ - H5Sget_simple_extent_dims(dataspace, dims_out, nullptr); - n_out = dims_out[0]; - d_out = dims_out[1]; - - /* Define hyperslab in the dataset. */ - offset[0] = offset[1] = 0; - count[0] = dims_out[0]; - count[1] = dims_out[1]; - H5Sselect_hyperslab(dataspace, H5S_SELECT_SET, offset, nullptr, count, nullptr); - - /* Define the memory dataspace. */ - dimsm[0] = dims_out[0]; - dimsm[1] = dims_out[1]; - dimsm[2] = 1; - memspace = H5Screate_simple(3, dimsm, nullptr); - - /* Define memory hyperslab. */ - offset_out[0] = offset_out[1] = offset_out[2] = 0; - count_out[0] = dims_out[0]; - count_out[1] = dims_out[1]; - count_out[2] = 1; - H5Sselect_hyperslab(memspace, H5S_SELECT_SET, offset_out, nullptr, count_out, nullptr); - - /* Read data from hyperslab in the file into the hyperslab in memory and display. */ - switch (t_class) { - case H5T_INTEGER: - data_out = new int[dims_out[0] * dims_out[1]]; - H5Dread(dataset, H5T_NATIVE_INT, memspace, dataspace, H5P_DEFAULT, data_out); - break; - case H5T_FLOAT: - data_out = new float[dims_out[0] * dims_out[1]]; - H5Dread(dataset, H5T_NATIVE_FLOAT, memspace, dataspace, H5P_DEFAULT, data_out); - break; - default: - printf("Illegal dataset class type\n"); - break; - } - - /* Close/release resources. */ - H5Tclose(datatype); - H5Dclose(dataset); - H5Sclose(dataspace); - H5Sclose(memspace); - H5Fclose(file); - - return data_out; -} - -std::string -get_index_file_name(const std::string& ann_test_name, const std::string& index_key, int32_t data_loops) { - size_t pos = index_key.find_first_of(',', 0); - std::string file_name = ann_test_name; - file_name = file_name + "_" + index_key.substr(0, pos) + "_" + index_key.substr(pos + 1); - file_name = file_name + "_" + std::to_string(data_loops) + ".index"; - return file_name; -} - -bool -parse_ann_test_name(const std::string& ann_test_name, size_t& dim, faiss::MetricType& metric_type) { - size_t pos1, pos2; - - if (ann_test_name.empty()) - return false; - - pos1 = ann_test_name.find_first_of('-', 0); - if (pos1 == std::string::npos) - return false; - pos2 = ann_test_name.find_first_of('-', pos1 + 1); - if (pos2 == std::string::npos) - return false; - - dim = std::stoi(ann_test_name.substr(pos1 + 1, pos2 - pos1 - 1)); - std::string metric_str = ann_test_name.substr(pos2 + 1); - if (metric_str == "angular") { - metric_type = faiss::METRIC_INNER_PRODUCT; - } else if (metric_str == "euclidean") { - metric_type = faiss::METRIC_L2; - } else { - return false; - } - - return true; -} - -int32_t -GetResultHitCount(const faiss::Index::idx_t* ground_index, const faiss::Index::idx_t* index, size_t ground_k, size_t k, - size_t nq, int32_t index_add_loops) { - size_t min_k = std::min(ground_k, k); - int hit = 0; - for (int i = 0; i < nq; i++) { - std::set ground(ground_index + i * ground_k, - ground_index + i * ground_k + min_k / index_add_loops); - for (int j = 0; j < min_k; j++) { - faiss::Index::idx_t id = index[i * k + j]; - if (ground.count(id) > 0) { - hit++; - } - } - } - return hit; -} - -#if DEBUG_VERBOSE -void -print_array(const char* header, bool is_integer, const void* arr, size_t nq, size_t k) { - const int ROW = 10; - const int COL = 10; - assert(ROW <= nq); - assert(COL <= k); - printf("%s\n", header); - printf("==============================================\n"); - for (int i = 0; i < 10; i++) { - for (int j = 0; j < 10; j++) { - if (is_integer) { - printf("%7ld ", ((int64_t*)arr)[i * k + j]); - } else { - printf("%.6f ", ((float*)arr)[i * k + j]); - } - } - printf("\n"); - } - printf("\n"); -} -#endif - -void -load_base_data(faiss::Index*& index, const std::string& ann_test_name, const std::string& index_key, - faiss::gpu::StandardGpuResources& res, const faiss::MetricType metric_type, const size_t dim, - int32_t index_add_loops, QueryMode mode = MODE_CPU) { - double t0 = elapsed(); - - const std::string ann_file_name = ann_test_name + HDF5_POSTFIX; - - faiss::Index *cpu_index = nullptr, *gpu_index = nullptr; - faiss::distance_compute_blas_threshold = 800; - - std::string index_file_name = get_index_file_name(ann_test_name, index_key, index_add_loops); - - try { - printf("[%.3f s] Reading index file: %s\n", elapsed() - t0, index_file_name.c_str()); - cpu_index = faiss::read_index(index_file_name.c_str()); - } catch (...) { - size_t nb, d; - printf("[%.3f s] Loading HDF5 file: %s\n", elapsed() - t0, ann_file_name.c_str()); - float* xb = (float*)hdf5_read(ann_file_name, HDF5_DATASET_TRAIN, H5T_FLOAT, d, nb); - assert(d == dim || !"dataset does not have correct dimension"); - - if (metric_type == faiss::METRIC_INNER_PRODUCT) { - printf("[%.3f s] Normalizing base data set \n", elapsed() - t0); - normalize(xb, nb, d); - } - - printf("[%.3f s] Creating CPU index \"%s\" d=%ld\n", elapsed() - t0, index_key.c_str(), d); - cpu_index = faiss::index_factory(d, index_key.c_str(), metric_type); - - printf("[%.3f s] Cloning CPU index to GPU\n", elapsed() - t0); - gpu_index = faiss::gpu::index_cpu_to_gpu(&res, GPU_DEVICE_IDX, cpu_index); - delete cpu_index; - - printf("[%.3f s] Training on %ld vectors\n", elapsed() - t0, nb); - gpu_index->train(nb, xb); - - // add index multiple times to get ~1G data set - for (int i = 0; i < index_add_loops; i++) { - printf("[%.3f s] No.%d Indexing database, size %ld*%ld\n", elapsed() - t0, i, nb, d); - std::vector xids(nb); - for (int t = 0; t < nb; t++) { - xids[t] = i * nb + t; - } - gpu_index->add_with_ids(nb, xb, xids.data()); - } - - printf("[%.3f s] Coping GPU index to CPU\n", elapsed() - t0); - - cpu_index = faiss::gpu::index_gpu_to_cpu(gpu_index); - delete gpu_index; - - faiss::IndexIVF* cpu_ivf_index = dynamic_cast(cpu_index); - if (cpu_ivf_index != nullptr) { - cpu_ivf_index->to_readonly(); - } - - printf("[%.3f s] Writing index file: %s\n", elapsed() - t0, index_file_name.c_str()); - faiss::write_index(cpu_index, index_file_name.c_str()); - - delete[] xb; - } - - index = cpu_index; -} - -void -load_query_data(faiss::Index::distance_t*& xq, size_t& nq, const std::string& ann_test_name, - const faiss::MetricType metric_type, const size_t dim) { - double t0 = elapsed(); - size_t d; - - const std::string ann_file_name = ann_test_name + HDF5_POSTFIX; - - xq = (float*)hdf5_read(ann_file_name, HDF5_DATASET_TEST, H5T_FLOAT, d, nq); - assert(d == dim || !"query does not have same dimension as train set"); - - if (metric_type == faiss::METRIC_INNER_PRODUCT) { - printf("[%.3f s] Normalizing query data \n", elapsed() - t0); - normalize(xq, nq, d); - } -} - -void -load_ground_truth(faiss::Index::idx_t*& gt, size_t& k, const std::string& ann_test_name, const size_t nq) { - const std::string ann_file_name = ann_test_name + HDF5_POSTFIX; - - // load ground-truth and convert int to long - size_t nq2; - int* gt_int = (int*)hdf5_read(ann_file_name, HDF5_DATASET_NEIGHBORS, H5T_INTEGER, k, nq2); - assert(nq2 == nq || !"incorrect nb of ground truth index"); - - gt = new faiss::Index::idx_t[k * nq]; - for (int i = 0; i < k * nq; i++) { - gt[i] = gt_int[i]; - } - delete[] gt_int; - -#if DEBUG_VERBOSE - faiss::Index::distance_t* gt_dist; // nq * k matrix of ground-truth nearest-neighbors distances - gt_dist = (float*)hdf5_read(ann_file_name, HDF5_DATASET_DISTANCES, H5T_FLOAT, k, nq2); - assert(nq2 == nq || !"incorrect nb of ground truth distance"); - - std::string str; - str = ann_test_name + " ground truth index"; - print_array(str.c_str(), true, gt, nq, k); - str = ann_test_name + " ground truth distance"; - print_array(str.c_str(), false, gt_dist, nq, k); - - delete gt_dist; -#endif -} - -faiss::ConcurrentBitsetPtr -CreateBitset(size_t size, int32_t percentage) { - if (percentage < 0 || percentage > 100) { - assert(false); - } - - faiss::ConcurrentBitsetPtr bitset_ptr = std::make_shared(size); - if (percentage != 0) { - int32_t step = 100 / percentage; - for (int64_t i = 0; i < size; i += step) { - bitset_ptr->set(i); - } - } - return bitset_ptr; -} - -void -test_with_nprobes(const std::string& ann_test_name, const std::string& index_key, faiss::Index* cpu_index, - faiss::gpu::StandardGpuResources& res, const QueryMode query_mode, const faiss::Index::distance_t* xq, - const faiss::Index::idx_t* gt, const std::vector nprobes, const int32_t index_add_loops, - const int32_t search_loops) { - double t0 = elapsed(); - - const std::vector NQ = {100}; - const std::vector K = {100}; - const size_t GK = 100; // topk of ground truth - - std::unordered_map mode_str_map = { - {MODE_CPU, "MODE_CPU"}, {MODE_MIX, "MODE_MIX"}, {MODE_GPU, "MODE_GPU"}}; - - double copy_time = 0.0; - faiss::Index *gpu_index, *index; - if (query_mode != MODE_CPU) { - faiss::gpu::GpuClonerOptions option; - option.allInGpu = true; - - faiss::IndexComposition index_composition; - index_composition.index = cpu_index; - index_composition.quantizer = nullptr; - switch (query_mode) { - case MODE_MIX: { - index_composition.mode = 1; // 0: all data, 1: copy quantizer, 2: copy data - - // warm up the transmission - gpu_index = faiss::gpu::index_cpu_to_gpu(&res, GPU_DEVICE_IDX, &index_composition, &option); - delete gpu_index; - - copy_time = elapsed(); - gpu_index = faiss::gpu::index_cpu_to_gpu(&res, GPU_DEVICE_IDX, &index_composition, &option); - delete gpu_index; - copy_time = elapsed() - copy_time; - printf("[%.3f s] Copy quantizer completed, cost %f s\n", elapsed() - t0, copy_time); - - auto ivf_index = dynamic_cast(cpu_index); - auto is_gpu_flat_index = dynamic_cast(ivf_index->quantizer); - if (is_gpu_flat_index == nullptr) { - delete ivf_index->quantizer; - ivf_index->quantizer = index_composition.quantizer; - } - index = cpu_index; - break; - } - case MODE_GPU: -#if 1 - index_composition.mode = 0; // 0: all data, 1: copy quantizer, 2: copy data - - // warm up the transmission - gpu_index = faiss::gpu::index_cpu_to_gpu(&res, GPU_DEVICE_IDX, &index_composition, &option); - delete gpu_index; - - copy_time = elapsed(); - gpu_index = faiss::gpu::index_cpu_to_gpu(&res, GPU_DEVICE_IDX, &index_composition, &option); -#else - // warm up the transmission - gpu_index = faiss::gpu::index_cpu_to_gpu(&res, GPU_DEVICE_IDX, cpu_index, &option); - delete gpu_index; - - copy_time = elapsed(); - gpu_index = faiss::gpu::index_cpu_to_gpu(&res, GPU_DEVICE_IDX, cpu_index, &option); -#endif - copy_time = elapsed() - copy_time; - printf("[%.3f s] Copy data completed, cost %f s\n", elapsed() - t0, copy_time); - - delete cpu_index; - index = gpu_index; - break; - } - } else { - index = cpu_index; - } - - std::vector> bitset_array; - bitset_array.push_back(std::make_pair("nil", nullptr)); - bitset_array.push_back(std::make_pair("0", CreateBitset(index->ntotal, 0))); - bitset_array.push_back(std::make_pair("5", CreateBitset(index->ntotal, 5))); - bitset_array.push_back(std::make_pair("50", CreateBitset(index->ntotal, 50))); - bitset_array.push_back(std::make_pair("100", CreateBitset(index->ntotal, 100))); - - for (auto nprobe : nprobes) { - // brute-force need not set nprobe - if (index_key.find("IDMap") == std::string::npos) { - switch (query_mode) { - case MODE_CPU: - case MODE_MIX: { - faiss::ParameterSpace params; - std::string nprobe_str = "nprobe=" + std::to_string(nprobe); - params.set_index_parameters(index, nprobe_str.c_str()); - break; - } - case MODE_GPU: { - faiss::gpu::GpuIndexIVF* gpu_index_ivf = dynamic_cast(index); - gpu_index_ivf->setNumProbes(nprobe); - } - } - } - - // output buffers - faiss::Index::idx_t* I = new faiss::Index::idx_t[NQ.back() * K.back()]; - faiss::Index::distance_t* D = new faiss::Index::distance_t[NQ.back() * K.back()]; - - for (size_t j = 0; j < K.size(); j++) { - size_t t_k = K[j]; - for (size_t i = 0; i < NQ.size(); i++) { - size_t t_nq = NQ[i]; - - printf("\n%s | %s - %s | nq = %4ld, k = %4ld, nprobe=%lu\n", ann_test_name.c_str(), index_key.c_str(), - mode_str_map[query_mode].c_str(), t_nq, t_k, nprobe); - printf("================================================================================\n"); - for (size_t s = 0; s < bitset_array.size(); s++) { - faiss::indexIVF_stats.quantization_time = 0.0; - faiss::indexIVF_stats.search_time = 0.0; - - double t_start = elapsed(), t_end; - for (int loop = 0; loop < search_loops; loop++) { - index->search(t_nq, xq, t_k, D, I, bitset_array[s].second); - } - t_end = elapsed(); - -#if DEBUG_VERBOSE - std::string str; - str = "I (" + index_key + ", nq=" + std::to_string(t_nq) + ", k=" + std::to_string(t_k) + ")"; - print_array(str.c_str(), true, I, t_nq, t_k); - str = "D (" + index_key + ", nq=" + std::to_string(t_nq) + ", k=" + std::to_string(t_k) + ")"; - print_array(str.c_str(), false, D, t_nq, t_k); -#endif - - // k = 100 for ground truth - int32_t hit = GetResultHitCount(gt, I, GK, t_k, t_nq, index_add_loops); - - printf("bitset = %3s%%, elapse = %.4fs (quant = %.4fs, search = %.4fs), R@ = %.4f\n", - bitset_array[s].first.c_str(), (t_end - t_start) / search_loops, - faiss::indexIVF_stats.quantization_time / 1000 / search_loops, - faiss::indexIVF_stats.search_time / 1000 / search_loops, - (hit / float(t_nq * std::min(GK, t_k) / index_add_loops))); - } - printf("================================================================================\n"); - } - } - - delete[] I; - delete[] D; - } - - delete index; -} - -void -test_ann_hdf5(const std::string& ann_test_name, const std::string& cluster_type, const std::string& index_type, - const QueryMode query_mode, int32_t index_add_loops, const std::vector& nprobes, - int32_t search_loops) { - double t0 = elapsed(); - - faiss::gpu::StandardGpuResources res; - - faiss::MetricType metric_type; - size_t dim; - - if (query_mode == MODE_MIX && index_type != "SQ8Hybrid") { - assert(index_type == "SQ8Hybrid" || !"Only SQ8Hybrid support MODE_MIX"); - return; - } - - std::string index_key = cluster_type + "," + index_type; - - if (!parse_ann_test_name(ann_test_name, dim, metric_type)) { - printf("Invalid ann test name: %s\n", ann_test_name.c_str()); - return; - } - - size_t nq, k; - faiss::Index* index; - faiss::Index::distance_t* xq; - faiss::Index::idx_t* gt; // ground-truth index - - printf("[%.3f s] Loading base data\n", elapsed() - t0); - load_base_data(index, ann_test_name, index_key, res, metric_type, dim, index_add_loops, query_mode); - - printf("[%.3f s] Loading queries\n", elapsed() - t0); - load_query_data(xq, nq, ann_test_name, metric_type, dim); - - printf("[%.3f s] Loading ground truth for %ld queries\n", elapsed() - t0, nq); - load_ground_truth(gt, k, ann_test_name, nq); - - test_with_nprobes(ann_test_name, index_key, index, res, query_mode, xq, gt, nprobes, index_add_loops, search_loops); - printf("[%.3f s] Search test done\n\n", elapsed() - t0); - - delete[] xq; - delete[] gt; -} - -/************************************************************************************ - * https://github.com/erikbern/ann-benchmarks - * - * Dataset Dimensions Train_size Test_size Neighbors Distance Download - * Fashion- - MNIST 784 60,000 10,000 100 Euclidean HDF5 (217MB) - * GIST 960 1,000,000 1,000 100 Euclidean HDF5 (3.6GB) - * GloVe 100 1,183,514 10,000 100 Angular HDF5 (463MB) - * GloVe 200 1,183,514 10,000 100 Angular HDF5 (918MB) - * MNIST 784 60,000 10,000 100 Euclidean HDF5 (217MB) - * NYTimes 256 290,000 10,000 100 Angular HDF5 (301MB) - * SIFT 128 1,000,000 10,000 100 Euclidean HDF5 (501MB) - *************************************************************************************/ - -TEST(FAISSTEST, BENCHMARK) { - std::vector param_nprobes = {32, 128}; - const int32_t SEARCH_LOOPS = 1; - const int32_t SIFT_INSERT_LOOPS = 1; - - test_ann_hdf5("sift-128-euclidean", "IVF128", "Flat", MODE_CPU, SIFT_INSERT_LOOPS, param_nprobes, SEARCH_LOOPS); -} diff --git a/core/src/index/unittest/faiss_ori/CMakeLists.txt b/core/src/index/unittest/faiss_ori/CMakeLists.txt deleted file mode 100644 index b612590ad9..0000000000 --- a/core/src/index/unittest/faiss_ori/CMakeLists.txt +++ /dev/null @@ -1,50 +0,0 @@ -if (KNOWHERE_GPU_VERSION) - - include_directories(${INDEX_SOURCE_DIR}/thirdparty) - include_directories(${INDEX_SOURCE_DIR}/include) - - set(unittest_libs - gtest gmock gtest_main gmock_main) - - set(depend_libs - faiss - ) - if (FAISS_WITH_MKL) - set(depend_libs ${depend_libs} - "-Wl,--start-group \ - ${MKL_LIB_PATH}/libmkl_intel_ilp64.a \ - ${MKL_LIB_PATH}/libmkl_gnu_thread.a \ - ${MKL_LIB_PATH}/libmkl_core.a \ - -Wl,--end-group -lgomp -lpthread -lm -ldl" - ) - else () - set(depend_libs ${depend_libs} - ${OpenBLAS_LIBRARIES} - ${LAPACK_LIBRARIES} - ) - - endif () - - set(basic_libs - gomp gfortran pthread - ) - - include_directories(${CUDA_INCLUDE_DIRS}) - link_directories("${CUDA_TOOLKIT_ROOT_DIR}/lib64") - set(cuda_lib - cudart - cublas - ) - set(basic_libs ${basic_libs} - ${cuda_lib} - ) - - # - if (NOT TARGET test_gpu) - add_executable(test_gpu gpuresource_test.cpp) - endif () - target_link_libraries(test_gpu ${depend_libs} ${unittest_libs} ${basic_libs}) - - install(TARGETS test_gpu DESTINATION unittest) - -endif () \ No newline at end of file diff --git a/core/src/index/unittest/faiss_ori/gpuresource_test.cpp b/core/src/index/unittest/faiss_ori/gpuresource_test.cpp deleted file mode 100644 index 85a4ca3b50..0000000000 --- a/core/src/index/unittest/faiss_ori/gpuresource_test.cpp +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -class TestGpuRes { - public: - TestGpuRes() { - res_ = new faiss::gpu::StandardGpuResources; - } - - ~TestGpuRes() { - delete res_; - delete index_; - } - - std::shared_ptr - Do() { - int d = 128; // dimension - int nb = 100000; // database size - int nq = 100; // nb of queries - int nlist = 1638; - - float* xb = new float[d * nb]; - float* xq = new float[d * nq]; - - for (int i = 0; i < nb; i++) { - for (int j = 0; j < d; j++) xb[d * i + j] = drand48(); - xb[d * i] += i / 1000.; - } - - for (int i = 0; i < nq; i++) { - for (int j = 0; j < d; j++) xq[d * i + j] = drand48(); - xq[d * i] += i / 1000.; - } - - index_ = new faiss::gpu::GpuIndexIVFFlat(res_, d, nlist, faiss::METRIC_L2); - index_->train(nb, xb); - index_->add(nb, xb); - - std::shared_ptr host_index = nullptr; - host_index.reset(faiss::gpu::index_gpu_to_cpu(index_)); - return host_index; - } - - private: - faiss::gpu::GpuResources* res_ = nullptr; - faiss::Index* index_ = nullptr; -}; - -TEST(gpuresource, resource) { - TestGpuRes t; - t.Do(); -} - -TEST(test, resource_re) { - int d = 128; // dimension - int nb = 1000000; // database size - int nq = 100; // nb of queries - int nlist = 16384; - int k = 100; - - float* xb = new float[d * nb]; - float* xq = new float[d * nq]; - - for (int i = 0; i < nb; i++) { - for (int j = 0; j < d; j++) xb[d * i + j] = drand48(); - xb[d * i] += i / 1000.; - } - - for (int i = 0; i < nq; i++) { - for (int j = 0; j < d; j++) xq[d * i + j] = drand48(); - xq[d * i] += i / 1000.; - } - - auto elems = nq * k; - auto res_ids = (int64_t*)malloc(sizeof(int64_t) * elems); - auto res_dis = (float*)malloc(sizeof(float) * elems); - - faiss::gpu::StandardGpuResources res; - auto cpu_index = faiss::index_factory(d, "IVF16384, Flat"); - auto device_index = faiss::gpu::index_cpu_to_gpu(&res, 0, cpu_index); - device_index->train(nb, xb); - device_index->add(nb, xb); - auto new_index = faiss::gpu::index_gpu_to_cpu(device_index); - - delete device_index; - - std::cout << "start clone" << std::endl; - auto load = [&] { - std::cout << "start" << std::endl; - faiss::gpu::StandardGpuResources res; - // res.noTempMemory(); - for (int l = 0; l < 100; ++l) { - auto x = faiss::gpu::index_cpu_to_gpu(&res, 1, new_index); - delete x; - } - std::cout << "load finish" << std::endl; - }; - - auto search = [&] { - faiss::gpu::StandardGpuResources res; - auto device_index = faiss::gpu::index_cpu_to_gpu(&res, 1, new_index); - std::cout << "search start" << std::endl; - for (int l = 0; l < 10000; ++l) { - device_index->search(nq, xq, 10, res_dis, res_ids); - } - std::cout << "search finish" << std::endl; - delete device_index; - delete cpu_index; - }; - - load(); - search(); - std::thread t1(search); - std::this_thread::sleep_for(std::chrono::seconds(1)); - std::thread t2(load); - t1.join(); - t2.join(); - std::cout << "finish clone" << std::endl; - - // std::this_thread::sleep_for(5s); - // - // auto device_index_2 = faiss::gpu::index_cpu_to_gpu(&res, 1, cpu_index); - // device_index->train(nb, xb); - // device_index->add(nb, xb); - - // std::cout << "finish clone" << std::endl; - // std::this_thread::sleep_for(5s); - - // std::this_thread::sleep_for(2s); - // std::cout << "start clone" << std::endl; - // auto new_index = faiss::clone_index(device_index); - // std::cout << "start search" << std::endl; - // new_index->search(nq, xq, k, res_dis, res_ids); - - // std::cout << "start clone" << std::endl; - //{ - // faiss::gpu::StandardGpuResources res; - // auto cpu_index = faiss::index_factory(d, "IVF1638, Flat"); - // auto device_index = faiss::gpu::index_cpu_to_gpu(&res, 1, cpu_index); - // device_index->train(nb, xb); - // device_index->add(nb, xb); - // std::cout << "finish clone" << std::endl; - // delete device_index; - // delete cpu_index; - // std::cout << "finish clone" << std::endl; - //} - // - // std::cout << "finish clone" << std::endl; -} diff --git a/core/src/index/unittest/kdtree.cpp b/core/src/index/unittest/kdtree.cpp deleted file mode 100644 index 9da3023309..0000000000 --- a/core/src/index/unittest/kdtree.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -// -//#include -//#include -//#include "knowhere/adapter/sptag.h" -//#include "knowhere/adapter/structure.h" -//#include "knowhere/index/vector_index/cpu_kdt_rng.h" -//#include "knowhere/index/vector_index/definitions.h" -// -// -// knowhere::DatasetPtr -// generate_dataset(int64_t n, int64_t d, int64_t base) { -// auto elems = n * d; -// auto p_data = (float*)malloc(elems * sizeof(float)); -// auto p_id = (int64_t*)malloc(elems * sizeof(int64_t)); -// assert(p_data != nullptr && p_id != nullptr); -// -// for (auto i = 0; i < n; ++i) { -// for (auto j = 0; j < d; ++j) { -// p_data[i * d + j] = float(base + i); -// } -// p_id[i] = i; -// } -// -// std::vector shape{n, d}; -// auto tensor = ConstructFloatTensorSmart((uint8_t*)p_data, elems * sizeof(float), shape); -// std::vector tensors{tensor}; -// std::vector tensor_fields{ConstructFloatField("data")}; -// auto tensor_schema = std::make_shared(tensor_fields); -// -// auto id_array = ConstructInt64ArraySmart((uint8_t*)p_id, n * sizeof(int64_t)); -// std::vector arrays{id_array}; -// std::vector array_fields{ConstructInt64Field("id")}; -// auto array_schema = std::make_shared(tensor_fields); -// -// auto dataset = std::make_shared(std::move(arrays), array_schema, std::move(tensors), tensor_schema); -// -// return dataset; -//} -// -// knowhere::DatasetPtr -// generate_queries(int64_t n, int64_t d, int64_t k, int64_t base) { -// size_t size = sizeof(float) * n * d; -// auto v = (float*)malloc(size); -// // TODO(lxj): check malloc -// for (auto i = 0; i < n; ++i) { -// for (auto j = 0; j < d; ++j) { -// v[i * d + j] = float(base + i); -// } -// } -// -// std::vector data; -// auto buffer = MakeMutableBufferSmart((uint8_t*)v, size); -// std::vector shape{n, d}; -// auto float_type = std::make_shared(); -// auto tensor = std::make_shared(float_type, buffer, shape); -// data.push_back(tensor); -// -// Config meta; -// meta[META_ROWS] = int64_t(n); -// meta[META_DIM] = int64_t(d); -// meta[META_K] = int64_t(k); -// -// auto type = std::make_shared(); -// auto field = std::make_shared("data", type); -// std::vector fields{field}; -// auto schema = std::make_shared(fields); -// -// return std::make_shared(data, schema); -//} -// -// int -// main(int argc, char* argv[]) { -// auto kdt_index = std::make_shared(); -// -// const auto d = 10; -// const auto k = 3; -// const auto nquery = 10; -// -// // ID [0, 99] -// auto train = generate_dataset(100, d, 0); -// // ID [100] -// auto base = generate_dataset(1, d, 0); -// auto queries = generate_queries(nquery, d, k, 0); -// -// // Build Preprocessor -// auto preprocessor = kdt_index->BuildPreprocessor(train, Config()); -// -// // Set Preprocessor -// kdt_index->set_preprocessor(preprocessor); -// -// Config train_config; -// train_config["TPTNumber"] = "64"; -// // Train -// kdt_index->Train(train, train_config); -// -// // Add -// kdt_index->Add(base, Config()); -// -// auto binary = kdt_index->Serialize(); -// auto new_index = std::make_shared(); -// new_index->Load(binary); -// // auto new_index = kdt_index; -// -// Config search_config; -// search_config[META_K] = int64_t(k); -// -// // Search -// auto result = new_index->Search(queries, search_config); -// -// // Print Result -// { -// auto ids = result->array()[0]; -// auto dists = result->array()[1]; -// -// std::stringstream ss_id; -// std::stringstream ss_dist; -// for (auto i = 0; i < nquery; i++) { -// for (auto j = 0; j < k; ++j) { -// ss_id << *ids->data()->GetValues(1, i * k + j) << " "; -// ss_dist << *dists->data()->GetValues(1, i * k + j) << " "; -// } -// ss_id << std::endl; -// ss_dist << std::endl; -// } -// std::cout << "id\n" << ss_id.str() << std::endl; -// std::cout << "dist\n" << ss_dist.str() << std::endl; -// } -//} diff --git a/core/src/index/unittest/metric_alg_benchmark/CMakeLists.txt b/core/src/index/unittest/metric_alg_benchmark/CMakeLists.txt deleted file mode 100644 index 702e72935f..0000000000 --- a/core/src/index/unittest/metric_alg_benchmark/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (C) 2019-2020 Zilliz. All rights reserved. -# -# Licensed 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 limitations under the License. - -set(unittest_libs - gtest gmock gtest_main gmock_main) - -add_executable(test_metric_benchmark metric_benchmark_test.cpp) -target_link_libraries(test_metric_benchmark ${unittest_libs}) -install(TARGETS test_metric_benchmark DESTINATION unittest) diff --git a/core/src/index/unittest/metric_alg_benchmark/metric_benchmark_test.cpp b/core/src/index/unittest/metric_alg_benchmark/metric_benchmark_test.cpp deleted file mode 100644 index 5cdfb6b4a9..0000000000 --- a/core/src/index/unittest/metric_alg_benchmark/metric_benchmark_test.cpp +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include -#include -#include -#include -#include - -typedef float (*metric_func_ptr)(const float*, const float*, size_t); - -constexpr int64_t DIM = 512; -constexpr int64_t NB = 10000; -constexpr int64_t NQ = 5; -constexpr int64_t LOOP = 5; - -void -GenerateData(const int64_t dim, const int64_t n, float* x) { - for (int64_t i = 0; i < n; ++i) { - for (int64_t j = 0; j < dim; ++j) { - x[i * dim + j] = drand48(); - } - } -} - -void -TestMetricAlg(std::unordered_map& func_map, const std::string& key, int64_t loop, - float* distance, const int64_t nb, const float* xb, const int64_t nq, const float* xq, - const int64_t dim) { - int64_t diff = 0; - for (int64_t i = 0; i < loop; i++) { - auto t0 = std::chrono::system_clock::now(); - for (int64_t i = 0; i < nb; i++) { - for (int64_t j = 0; j < nq; j++) { - distance[i * NQ + j] = func_map[key](xb + i * dim, xq + j * dim, dim); - } - } - auto t1 = std::chrono::system_clock::now(); - diff += std::chrono::duration_cast(t1 - t0).count(); - } - std::cout << key << " takes average " << diff / loop << "ms" << std::endl; -} - -void -CheckResult(const float* result1, const float* result2, const size_t size) { - for (size_t i = 0; i < size; i++) { - ASSERT_FLOAT_EQ(result1[i], result2[i]); - } -} - -/////////////////////////////////////////////////////////////////////////////// -/* from faiss/utils/distances_simd.cpp */ -namespace FAISS { -// reads 0 <= d < 4 floats as __m128 -static inline __m128 -masked_read(int d, const float* x) { - assert(0 <= d && d < 4); - __attribute__((__aligned__(16))) float buf[4] = {0, 0, 0, 0}; - switch (d) { - case 3: - buf[2] = x[2]; - case 2: - buf[1] = x[1]; - case 1: - buf[0] = x[0]; - } - return _mm_load_ps(buf); - // cannot use AVX2 _mm_mask_set1_epi32 -} - -static inline __m256 -masked_read_8(int d, const float* x) { - assert(0 <= d && d < 8); - if (d < 4) { - __m256 res = _mm256_setzero_ps(); - res = _mm256_insertf128_ps(res, masked_read(d, x), 0); - return res; - } else { - __m256 res = _mm256_setzero_ps(); - res = _mm256_insertf128_ps(res, _mm_loadu_ps(x), 0); - res = _mm256_insertf128_ps(res, masked_read(d - 4, x + 4), 1); - return res; - } -} - -float -fvec_inner_product_avx(const float* x, const float* y, size_t d) { - __m256 msum1 = _mm256_setzero_ps(); - - while (d >= 8) { - __m256 mx = _mm256_loadu_ps(x); - x += 8; - __m256 my = _mm256_loadu_ps(y); - y += 8; - msum1 = _mm256_add_ps(msum1, _mm256_mul_ps(mx, my)); - d -= 8; - } - - __m128 msum2 = _mm256_extractf128_ps(msum1, 1); - msum2 += _mm256_extractf128_ps(msum1, 0); - - if (d >= 4) { - __m128 mx = _mm_loadu_ps(x); - x += 4; - __m128 my = _mm_loadu_ps(y); - y += 4; - msum2 = _mm_add_ps(msum2, _mm_mul_ps(mx, my)); - d -= 4; - } - - if (d > 0) { - __m128 mx = masked_read(d, x); - __m128 my = masked_read(d, y); - msum2 = _mm_add_ps(msum2, _mm_mul_ps(mx, my)); - } - - msum2 = _mm_hadd_ps(msum2, msum2); - msum2 = _mm_hadd_ps(msum2, msum2); - return _mm_cvtss_f32(msum2); -} - -float -fvec_L2sqr_avx(const float* x, const float* y, size_t d) { - __m256 msum1 = _mm256_setzero_ps(); - - while (d >= 8) { - __m256 mx = _mm256_loadu_ps(x); - x += 8; - __m256 my = _mm256_loadu_ps(y); - y += 8; - const __m256 a_m_b1 = mx - my; - msum1 += a_m_b1 * a_m_b1; - d -= 8; - } - - __m128 msum2 = _mm256_extractf128_ps(msum1, 1); - msum2 += _mm256_extractf128_ps(msum1, 0); - - if (d >= 4) { - __m128 mx = _mm_loadu_ps(x); - x += 4; - __m128 my = _mm_loadu_ps(y); - y += 4; - const __m128 a_m_b1 = mx - my; - msum2 += a_m_b1 * a_m_b1; - d -= 4; - } - - if (d > 0) { - __m128 mx = masked_read(d, x); - __m128 my = masked_read(d, y); - __m128 a_m_b1 = mx - my; - msum2 += a_m_b1 * a_m_b1; - } - - msum2 = _mm_hadd_ps(msum2, msum2); - msum2 = _mm_hadd_ps(msum2, msum2); - return _mm_cvtss_f32(msum2); -} -} // namespace FAISS - -/////////////////////////////////////////////////////////////////////////////// -/* from knowhere/index/vector_index/impl/nsg/Distance.cpp */ -namespace NSG { -float -DistanceL2_Compare(const float* a, const float* b, size_t size) { - float result = 0; - -#define AVX_L2SQR(addr1, addr2, dest, tmp1, tmp2) \ - tmp1 = _mm256_loadu_ps(addr1); \ - tmp2 = _mm256_loadu_ps(addr2); \ - tmp1 = _mm256_sub_ps(tmp1, tmp2); \ - tmp1 = _mm256_mul_ps(tmp1, tmp1); \ - dest = _mm256_add_ps(dest, tmp1); - - __m256 sum; - __m256 l0, l1; - __m256 r0, r1; - unsigned D = (size + 7) & ~7U; - unsigned DR = D % 16; - unsigned DD = D - DR; - const float* l = a; - const float* r = b; - const float* e_l = l + DD; - const float* e_r = r + DD; - float unpack[8] __attribute__((aligned(32))) = {0, 0, 0, 0, 0, 0, 0, 0}; - - sum = _mm256_loadu_ps(unpack); - if (DR) { - AVX_L2SQR(e_l, e_r, sum, l0, r0); - } - - for (unsigned i = 0; i < DD; i += 16, l += 16, r += 16) { - AVX_L2SQR(l, r, sum, l0, r0); - AVX_L2SQR(l + 8, r + 8, sum, l1, r1); - } - _mm256_storeu_ps(unpack, sum); - result = unpack[0] + unpack[1] + unpack[2] + unpack[3] + unpack[4] + unpack[5] + unpack[6] + unpack[7]; - - return result; -} - -float -DistanceIP_Compare(const float* a, const float* b, size_t size) { - float result = 0; - -#define AVX_DOT(addr1, addr2, dest, tmp1, tmp2) \ - tmp1 = _mm256_loadu_ps(addr1); \ - tmp2 = _mm256_loadu_ps(addr2); \ - tmp1 = _mm256_mul_ps(tmp1, tmp2); \ - dest = _mm256_add_ps(dest, tmp1); - - __m256 sum; - __m256 l0, l1; - __m256 r0, r1; - unsigned D = (size + 7) & ~7U; - unsigned DR = D % 16; - unsigned DD = D - DR; - const float* l = a; - const float* r = b; - const float* e_l = l + DD; - const float* e_r = r + DD; - float unpack[8] __attribute__((aligned(32))) = {0, 0, 0, 0, 0, 0, 0, 0}; - - sum = _mm256_loadu_ps(unpack); - if (DR) { - AVX_DOT(e_l, e_r, sum, l0, r0); - } - - for (unsigned i = 0; i < DD; i += 16, l += 16, r += 16) { - AVX_DOT(l, r, sum, l0, r0); - AVX_DOT(l + 8, r + 8, sum, l1, r1); - } - _mm256_storeu_ps(unpack, sum); - result = unpack[0] + unpack[1] + unpack[2] + unpack[3] + unpack[4] + unpack[5] + unpack[6] + unpack[7]; - - return result; -} -} // namespace NSG - -/////////////////////////////////////////////////////////////////////////////// -/* from index/thirdparty/annoy/src/annoylib.h */ -namespace ANNOY { -inline float -hsum256_ps_avx(__m256 v) { - const __m128 x128 = _mm_add_ps(_mm256_extractf128_ps(v, 1), _mm256_castps256_ps128(v)); - const __m128 x64 = _mm_add_ps(x128, _mm_movehl_ps(x128, x128)); - const __m128 x32 = _mm_add_ss(x64, _mm_shuffle_ps(x64, x64, 0x55)); - return _mm_cvtss_f32(x32); -} - -inline float -euclidean_distance(const float* x, const float* y, size_t f) { - float result = 0; - if (f > 7) { - __m256 d = _mm256_setzero_ps(); - for (; f > 7; f -= 8) { - const __m256 diff = _mm256_sub_ps(_mm256_loadu_ps(x), _mm256_loadu_ps(y)); - d = _mm256_add_ps(d, _mm256_mul_ps(diff, diff)); // no support for fmadd in AVX... - x += 8; - y += 8; - } - // Sum all floats in dot register. - result = hsum256_ps_avx(d); - } - // Don't forget the remaining values. - for (; f > 0; f--) { - float tmp = *x - *y; - result += tmp * tmp; - x++; - y++; - } - return result; -} - -inline float -dot(const float* x, const float* y, size_t f) { - float result = 0; - if (f > 7) { - __m256 d = _mm256_setzero_ps(); - for (; f > 7; f -= 8) { - d = _mm256_add_ps(d, _mm256_mul_ps(_mm256_loadu_ps(x), _mm256_loadu_ps(y))); - x += 8; - y += 8; - } - // Sum all floats in dot register. - result += hsum256_ps_avx(d); - } - // Don't forget the remaining values. - for (; f > 0; f--) { - result += *x * *y; - x++; - y++; - } - return result; -} -} // namespace ANNOY - -namespace HNSW { -#define PORTABLE_ALIGN32 __attribute__((aligned(32))) - -static float -L2SqrSIMD16Ext(const float* pVect1v, const float* pVect2v, size_t qty) { - float* pVect1 = (float*)pVect1v; - float* pVect2 = (float*)pVect2v; - // size_t qty = *((size_t *) qty_ptr); - float PORTABLE_ALIGN32 TmpRes[8]; - size_t qty16 = qty >> 4; - - const float* pEnd1 = pVect1 + (qty16 << 4); - - __m256 diff, v1, v2; - __m256 sum = _mm256_set1_ps(0); - - while (pVect1 < pEnd1) { - v1 = _mm256_loadu_ps(pVect1); - pVect1 += 8; - v2 = _mm256_loadu_ps(pVect2); - pVect2 += 8; - diff = _mm256_sub_ps(v1, v2); - sum = _mm256_add_ps(sum, _mm256_mul_ps(diff, diff)); - - v1 = _mm256_loadu_ps(pVect1); - pVect1 += 8; - v2 = _mm256_loadu_ps(pVect2); - pVect2 += 8; - diff = _mm256_sub_ps(v1, v2); - sum = _mm256_add_ps(sum, _mm256_mul_ps(diff, diff)); - } - - _mm256_store_ps(TmpRes, sum); - float res = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3] + TmpRes[4] + TmpRes[5] + TmpRes[6] + TmpRes[7]; - - return (res); -} - -static float -InnerProductSIMD16Ext(const float* pVect1v, const float* pVect2v, size_t qty) { - float PORTABLE_ALIGN32 TmpRes[8]; - float* pVect1 = (float*)pVect1v; - float* pVect2 = (float*)pVect2v; - // size_t qty = *((size_t *) qty_ptr); - - size_t qty16 = qty / 16; - - const float* pEnd1 = pVect1 + 16 * qty16; - - __m256 sum256 = _mm256_set1_ps(0); - - while (pVect1 < pEnd1) { - //_mm_prefetch((char*)(pVect2 + 16), _MM_HINT_T0); - - __m256 v1 = _mm256_loadu_ps(pVect1); - pVect1 += 8; - __m256 v2 = _mm256_loadu_ps(pVect2); - pVect2 += 8; - sum256 = _mm256_add_ps(sum256, _mm256_mul_ps(v1, v2)); - - v1 = _mm256_loadu_ps(pVect1); - pVect1 += 8; - v2 = _mm256_loadu_ps(pVect2); - pVect2 += 8; - sum256 = _mm256_add_ps(sum256, _mm256_mul_ps(v1, v2)); - } - - _mm256_store_ps(TmpRes, sum256); - float sum = TmpRes[0] + TmpRes[1] + TmpRes[2] + TmpRes[3] + TmpRes[4] + TmpRes[5] + TmpRes[6] + TmpRes[7]; - - return sum; -} -} // namespace HNSW - -TEST(METRICTEST, BENCHMARK) { - std::unordered_map func_map; - func_map["FAISS::L2"] = FAISS::fvec_L2sqr_avx; - func_map["NSG::L2"] = NSG::DistanceL2_Compare; - func_map["HNSW::L2"] = HNSW::L2SqrSIMD16Ext; - func_map["ANNOY::L2"] = ANNOY::euclidean_distance; - - func_map["FAISS::IP"] = FAISS::fvec_inner_product_avx; - func_map["NSG::IP"] = NSG::DistanceIP_Compare; - func_map["HNSW::IP"] = HNSW::InnerProductSIMD16Ext; - func_map["ANNOY::IP"] = ANNOY::dot; - - std::vector xb(NB * DIM); - std::vector xq(NQ * DIM); - GenerateData(DIM, NB, xb.data()); - GenerateData(DIM, NQ, xq.data()); - - std::vector distance_faiss(NB * NQ); - std::vector distance_nsg(NB * NQ); - std::vector distance_annoy(NB * NQ); - std::vector distance_hnsw(NB * NQ); - - std::cout << "==========" << std::endl; - TestMetricAlg(func_map, "FAISS::L2", LOOP, distance_faiss.data(), NB, xb.data(), NQ, xq.data(), DIM); - - TestMetricAlg(func_map, "ANNOY::L2", LOOP, distance_annoy.data(), NB, xb.data(), NQ, xq.data(), DIM); - CheckResult(distance_faiss.data(), distance_annoy.data(), NB * NQ); - - std::cout << "==========" << std::endl; - TestMetricAlg(func_map, "FAISS::IP", LOOP, distance_faiss.data(), NB, xb.data(), NQ, xq.data(), DIM); - - TestMetricAlg(func_map, "ANNOY::IP", LOOP, distance_annoy.data(), NB, xb.data(), NQ, xq.data(), DIM); - CheckResult(distance_faiss.data(), distance_annoy.data(), NB * NQ); -} diff --git a/core/src/index/unittest/sift.50NN.graph b/core/src/index/unittest/sift.50NN.graph deleted file mode 100644 index bf7ba7555dbe63bd7997fa84a9aca04de6f964e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2040000 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�S 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 lp^%45_dFp4QLwD|L(bn1zuC2H~ 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�A&!-*KK3-1<5b9AjNZmX<&*Awzuy!#&)U7;b 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 Bfz5xd0#QICCLq7n%BqwM_ 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 z z8|3S2q&%hWQN5k=q5cc%rqq!Ish5oW_`BAoQ?DImmA93T)B|W!P&f83@u#GHNv!@b zCAc*rR(GbZy(nY(Hs$qIRj$$>p)BR~0n$%Fy?}SgDL1RvO2PV72Q`~21YIV_g~UI?~sl75aotY<`VNK4}I$QdO7pR`wU+Ir@s3bZ43bid4xmMF9}{j z+ByJ^?v)9l-Mvje0{2$u)L=qsU^=FrAzegy&qb?WRi}ED`~d{@u2X=aepa2XJ_OqC z)a5SYxf}VNse6qy_tM7!`krU!skS5iH2(xA=XWE@?V?T#p4$`D*}A{03!MY4e)l>- zlXoaYQdWOY{U(AuA0#h5?GGXCE5diAxeokpMDwhj#&3jsxwf#jL~W^aig)#)O^IC- z>WsaE!0$x_F;gJ+yQD7G?~?PkC1up#s`p*Tv-$w_u+C%k8QPP|(09&h^{o0@YByD{ zdm5T&@V=ZnbE&UBR=K}$c-;3WD0-x;i(f=}zw7GL{XVPzSI@65zZIb(;XlS!pSUNi z^(TQiUO*ewDe#vUP-pKqdl73voxb1h5|o`!JNm47hW{CDQ8o5{PyOfA(KiJ@Z4AFs z<^g#*p6A5uZ;X_NO1O1%G(HvEP3jW&Mm z`<^S*#(#ivp1FO=94$$wyGO_R(bJ~(_*%4ACQ;m;d71L==l7`NSzXU1dQRBA)_ben zL;VK1XL}}i1@mVn{h{B0HhcGc&)n$C&hQq5Q%7*Q9$MTYbt&=$HW>p6^-@{+=229H?hV z|D~K~x%!2%ck$j7d=i3(Hazc*@(gKq($u$H0&Y#xH-oG9m?sGUkF>P3txm_YqDe^i z{I=&+%Td;|-+id>c~NOOOIrN>cJfDm0_#)O+Hl}p0G|F@>4=+AZhBV6hdTO@ZnO+| z>F=oxsWM}-k34P6+Fe~s+LOJ%vkmF)58AxlOWZda@$O!u-?VoNrD2{uqn!K7NSv~*8T9Bab%zfN%f_t~$m6_n_x5WM5JzSlx{-u6n z{08_f(FWo7q#`i%d9Om7es7|I?Kejom*1&zJV%kIzEeGg-;Kqj|4BcnM_mk@S-_BI zObxB-sof8*(3an(X~6!BJy&~)-?pm+zlpnP>p#||->PA}ON;u_CcKNA-+8~YeuMqa zN{ip|r^J5i{a*VY0q!mCJATJwzu(}g&gLRP-awqyrK>NkKpQjYo5SFJf%)cn1a<1_ zdDW$lqV6#Ayf;Gn)cZ)QILmvur+QXGK4KDeod@y*_NhEeWPmZGZ-cbci8i|e_b1Xv z6THLiCFS)2e8?D{AWyzg9#NjIA;CN2ai zSz1;Sk0Qul%Rm1|9eHqhbNO)jZTWb4e0lp9ydMLWJiI*r-_%n+@NU0i;1jQpoz1(l zgz|zig#5X3z!Tb@Mx6}M>HUA|T$K%!H+~>4L7H-hcLKgeUGMF?L%MeZ)+9}NLRsVq zWt3O0(T4JXw0umtt%O6Y(Z6{w#yb0fdMU%egV_J83JdTZ1&03{r7v)E;{5C(TX=5Z zx&_`;%4ysAgS0f1*U$M0>s7m0d)7+|%8jE=1>hWIu2f>4Mw36BxuVT$5il<#rr&rj z%^a!<-Py_4ZuTj#-(jy>&RQ%0oMOOvO5SqX(zm)Sb)`igs%XYodxt)U{!gj?iPxFO z>g|<({r>95cmuflFX|WQ{-mE^DCLxMz0>mneex>rtAM+WSRT#0+tz?%UBc_^!QKHX zKdJ7&HF<@B=QmYet1x4>o_^Q|-u@S=yr@24`uVgWRH2@{f!|DhxH3@hAM%?~R$8>b zXmim9lb-Uf-RzXtXVbm&cgi0jXcJPt*6yKg%cRv_FU3sXX^r`!s|BHH@zCFa=keK8B-c?^>59)DC`!XYDPn z`;)XI-{;;TEwiBE3)+7VTs)KP{_6Q8_r25Pk0Ny+hmb_@`+@pJsjS{U7R~X=W^N6qC0ly0wDJMU#?LmHDyS#h6 z`rjBFTnc{ZRDc-Bl>g@Y2H_= ztw`II_glEH%C{6F-!s+Prqq8#61(qt&RhM4c7d$mB3~6`ZnUG$9m;eiZ6$by^Q@jd zKpkz;>a*41pJ2^Q2G7g9ccEX@{r6*@6(DUD>mWfG{ZITi;V$L#5GMzYHtnq7n4WqE znG=avKVN2{eX1NJUH!j$_%Ps2pMm;dwsr)OG6%T$^%{l#2$}Ta1(byKmFx zOVacUUO+o1fO`mhBYDrop03VZAK@;v;A3EWuF?Ib zEi~%`udUa8$$jM|up)uAhc;hOzZ&p5k-viHPQceDbrCw<^NvA(OY-GaA_?*|@*YJ= zb06}qBlm_U;mkwESbo#7VWv}Y*LMu6`(^v^+_^DxIlX{#*o>jhYkwD}g{ZE(Fne;lVy4%#ose1Dtv^-tXe z-9>;A#oRARJD&jO4s~acUR1h>Q}G@Jjv48f7d-EvOlS5BZ91L6=NH1@%*-Y5y9`ag z(tdI3YWtZ1PTFI<%d!~lEu&xlq^<8L^C3Z6hJcrMTMnbl0bs8qU0c;|aO?=|aPrba zM{Z!vBrOO1vXS=cf=e`bG~-(f~GcZojhIi0>!uw9z$O)W5 zz|hWC7r09)-{c#<*wa80Ky0j=a z>CdVhuKcBpuMDqzubi)c-ABag0MrR6&qsl;{&UJ;>I1?F%HPW8>JH9OKL*(P$37$| z*XO0Ia(oQ$4+!cO^mkQPBTu59L0Mj3xC#V)Z{<7G(+uW47iAg}j{sIP;@^4K|4*Hb zx`80=6@~t$#H|RU60t8bj-}}1%b}EGUS=o1AM@%<(hf0C@-qGn>BDN-n0KU&rhif~ z=4%;;RLqBGVe8$uLkZq< z+YdZSlRg-j#6aDuqZ9Jk+a}%C2*K>ehd0?!gUw#1oYT$Y| z?qXm`OGVP_fXhnqCXkkd@_&+cmNKJBn@rl<#3w1c1U$#kwrA%H(B@g%e+60^Q(wFG zyW#W=c)kI?he^vvx#7_B3S~#|K99V%l=~h$a}lNjGkM~;Z)_A}`;h!-@H-1_d1xyK zG+ktUzf0Od%6~%H$>6t_5J9^G$om-BhbSv8iRr^vNq>d>Zz=m0Z5{>xcfeyOI8EgF z9CTeElq7sW{vThfi;KpgoKlfnLcvw0jmP}N5OqQIQ|4p5NGd+BgEN&wyJW7)>cVmOfQ(X%0?fs3$G% z5!&|MOLDx)_n)=sUZibb8@slCZTapw+Ss-2KL@sU_3OMprVq8{cVv$3hmQNuc;5L& z8F`cRp9U$HNo3#78?_Z$&74S$%UOvKJ`b2+#Y?RkupcH+e{eLI1w)+RrH;*z+84nHa zO_?M2!MP*v`AFIY><+A#+thysyrRJN9;rXY7djTwpVIOfFs_2j3(7>&-s&*=oHn$n zPGSB3NxJtGG$XAH<@NWi4xCf87vy;~^^a3VAFt)mevIc$Ja?mfALt)L{wCU74UCNR zQ9<^P^E~HC$h;wMC}qln&r#kpf$Qna@MF-I7kn2(m-b+7w zM!-5ko6=(2bLjhKz!h)tGEbNZY3En^F$Z`(B&`(n_LBA&w66(^!*dmNYe3uB#PCqG zQy)6KyD~Yn4ks=F4h@0TjyV#9j^(s}nDX*guL5^2bi4rP^pvRr4x6F7DS1<&VK%TU zkna5o@_vgM8_z=?BR?zOl+xh834A`LZE4Z|sjlQW< zvg$n4pZI?T>Pgg@D3@rPRgV&Hvn|ZC`kUH}e?nlW!%_dDZM8l4sdv$?tNunkj5-zN z7Ii=BeAEf4V^JTZt|*i~&~}>uTt)#;{ZJEVRcEB$S6b9Psnb%wr7o*DZG=!({gt{X z^-$`+3eldrGj&-pz|=;n&PyGY`ZM)u>bUOFmO86dlvAgs{!Lw(x-IRr+IZE!>6@)= zScg8TO{^YF9i4hN^>pgV)Z1ypRo|vwE(F->;MBK`qI?JXM4g@bHfc#ky8hzNfL99` z*Lc^5T%D(WS^Lk0$A;42Vrd}!$aMh1>B97II5icd^Ta_jd zr)N{=6#$kxG4*Ua>BHpY%^>XsWA-EXujlzbaaZ0w-@lIM6x165UY^@m52i2sWN_P0 z+xm2Ep`EctwMN1L7lGpTy?;SXjA)otUm#L zquxV%koG6fe`@2=-_yCPPl4-WF?)^nBImpM6?H1kdv(9AFa1%S^R5xk`;;fRjWvdDJV1Tpbh!c4fMr|F#3%kueyh_4{0lk_pd4Me;v#R_n*kS z!274P{|)U{2d`eFts+mpRG!oS8+b~4Ni!n{QLZcTVd{^f{6@wmhH~%i+<_8e(Z zHd+PT8|2*!#K}U+R|Bb=0vwgI{vv%b`-ifYGMlp265>a|NJ^eETMXd}cq_}@rEFE| z-y)s^PCruSKEXR)wu4U*>b0lcvy@ftS_ytTsQ&@)%7z7j^AUX%4xZjqBfp~@_c<{4 zgU=P#$T`ZmKK~?M{y$o7Qui}(zX%*<)X~JstM_O(C2+ixvN83OflEE|rU;AYfrP(+ zbDMI?vdX(lc#b5k1JBB{-fcOX_1P16Q3Pf1ZGt?cIQW z$3^}|`S}PiQ&HagYrN0KyKE{_=Pd2LPWS>^9hcqAvEsxth~+5?FxH-V(=Wan@DC8= zRXk7NS+q#fE3>!eVqcJFX%T{~K$?4SM%u0pENyh>z}Y*APSC#pu{xAG`oVj*if5yH zfs^~G_OQ~V>)-w=@dNrmp6MTOc)xDkaknk zNB@Aw@8DIB=dzT`PB_h2DeEfVe$RU%-iHv6dai6WaFwac5cHeaK&<{t-v)hqw4#UoAR8SIw9bBfcMk9 zd&W-R3jHy(IqJ8dUP}9G5P0^vW8+x#U@rB6h8)b5XvVrRWfq1KQ(j*0Yto8CgJb8o z*|(0TW1}yD{k)sDF#JUIit`Z9P&))?wJ#`dw}JApB9+_dw2!L#!~9fkK` zIo8h4qTuCSLM6br8TA}T=Wbu>dFR$B(t`wT_}bRT(C#K;``P}qAMF?WRC|9r>T6rq z|D-AK^kK2j%Rt{Q$`2>jr)CXp@1YO$tFa$*@qQK>?05TD9=-|h`YGzyqOL`q<#GBz zn}28C(@@6w(TCtXNXPp}(6$Ho`V%;33eZM==<$DG)yoGdmj`_B0jCRb1ZjIIqmBPJ zo*hs9Q?#omCT%zG`nzar*O&4Q+R#TcJ#jkPan0wazWz1e0b4&z{WVWfZ#6Vs=lKz_ z|D9bHI)(r{j5=wl|30A+&(8k>JV#UibIKUs1?+|6mP<|0~Y=(xm z;9G?@y7PXKGD~UWBDfWyTps$a0CiUIo}97+=yz=u6?y)UI$zUHCumaFwUv71iCcs7 zM9KwuA3>hFnyi%fzmn9~JOKB`q?HBcdeZOnJ{CADXy+g_j-}iWyw_u#zG2QuOLu4) zK^Oo%MTpg_7bKp{I8I<*d6(!haPJOo&*{fW!1bQe!K_Q~9(_NYHYxiydE=pJGw%nW zF9T^+fH@f$H3{nW+tO}I`ciw3qa#G=2KN*xduN3XV1V-pv*Jg z?*X$fW%a|J!SgQS@&WcO>Pbrv%8Vp*pxuMupd3;Ze1=fY`$9^Bhq6q|FxEfqy$Vj> zlIDGAH);1g_!OfblTdFWbbd=4(bR84`w_HTj=q>k`D)90|owmXpKJtmhj$I%kXdsph~@3(;b z_rU1{?^&3W`T^=ExC|Kj82(3F&j|aOAD(j>3ETm^>&N&9IIRHga?<^;;HALTmvAO^ z*OOO^IijDT|L5-A8o8ls7iIK&gv#XCnD!#|h z5=$y6AfP#M&@iS820w z9TnqG1>W_UHjg{daotx9d0ne%C(*Xyy36&I>pa(N+C^L&YD3XB;<`cLzKmlT z%k$>}z5&Wjhvrwjd&cJp%K4jv?k6b=T#%x*;QbTIiz9`Zr(ao}WBpKJ>BHg7zTF zb%sa%m+l|due%Gr{cZUGbd29HX2tju{l5Lc-;M9)UvNx;FV8G!0uEz?m1P*PY2n?y z*v?y1LNhHgiNhF|S>VhE9m{nGzA9-Ruf?WC0i_B8e39NbtN z=a9y`I6pD=&e$5~-a9DgS|B^{&XMPegL_?Q{{U7I3C@`Ah%MlZ&N7(3#beb-4l&2@*X2fAp$Ju7^l1+VcMYoL=8d__s$0(=!vugjL#`dd1Gw#ar@gk-N@4iJ_}sR@=-SWe)t&xjgIirg}Z0Ke$M6Dt69LYRat?n zfF2TsQ-9EO-V)%7j^w+vj==GPXV>TNl7EH4Vh^zWXHPH41u zv=g|tUm40+MxMS7d-Z-{f4n3yTGAu#PzFjtLJGO+NzD8wch;RbAaDiT4PcAQzovTaUB>zdHa%i z@TTp;JqE_ycI4f&97hF^owSBrm%!tGv1inMko;ok(-}Az{~TW0A^3$9N@UlnU@{yppj*eFJ_8yruoP|8@k3g6+!2n!5KjIVm$u||I49kyppmwpKe1vIQMrx@4UJld^AKx z*8(?@#kIgv+K_$p5O|uvyL;qa12{MIyL8^Y0erhDQw^A6l=F8|TsNGD|2kaG|Lr^M zUyL1eeQ}C9I))BUEAJD=&cQd z2JmkKw-R2PaX-PmCH%F3k2>ggIQ5*D`zv5)1M7LL-=NjA!?s=g7JRDeXY(ZCB;iDrs zJV*H-(lVoqPbpI$Jf7417HR%waku!)*}*#r83LqL6CW@;!MB0(&!KOqK z=!0@ClmOXW?&RyS?>loKfu6Z3BA-u0C09c6KyqW{RXi|Y^96|N;*ySTPV2rsT}T*J5)aqZ%I#kGuU6xTbhA@oD( zM{>R4n#Z+@{wLQt`kVAk6@jMf71u4URrK|`PH_#SZ%QAL{w8CDU7NV(ac#4SJbhKJ zdt4{EUU6;Yy2iDPeko;H&bzibZL%Sh)%K^|P5Yj=Ep8ImQ}Rl|lQueScB?4soY*yocI%>iBiib;5o)(H23$M$aPT@N=BLa$^6l^3 zWA_`EW2m-7_wQ(Xc20W@oZ5V+Q!m;?gOF5i*dx`pZ*7KW4uN`Ip5_nst8%`ufhxfy4TqL7sKz zei40qt~d1~yS~(qZW|f}ylp~R{8n6N+qPZP+kRbpyPo&EF!sPT_YG+QV;i=8`rWwx zwk^BX_;Krf*Y?(vy0$)yVTdBl zwY~Lao!aizo!^Kt55_a7L+ikG{uJP2zCZEv?)PSWxvn?H!8N`0>38BD>d%nR_7Z0k zunyyF0<|gU{%Y&lcAzZAf*BuH1RfHBt1$Nqz(teid}&9B(+6Rr|j7{;Ok(xR?2HjWiG&aCGh&FZ`x*f<5fNvoE%2Z{55BIdVKcxda42RB8 z;Ey5G6=?OLys``-Eg@;o(3f`6jp*tnu&uz=3%Z`6YwU*Sk&Z^U`_RJ`XjMda#-3CL z=0nO=Lnrm2^))ax;AbejE+_2_X~vT1`>X`5KIq~IIO?E_{_yACkzV9^_UY@$t^L>7 zkzh93nzE3#4wMw%wrO3-h`S0OX{cfg?f}}m+Jp($M2EW$VBg(`he-nHrK*RS>g}S#b*j_vf;xl05(S|*@ zq69p8ri63t9kgF%Nd`atRZQrNJ^S6gOQsxjki1DX|P+!2j1AToW z+TS-I*8}jMgAez>!a~e?b07ViKEZv+yd7F|$xxQ!z>S9fH15ffYYy^gYjZsR7a8Pn z2Yg#59=vGxat>e|fOCda;OtL&Wzy<_qYSV;X(w~wNgw?vNxLOxG3oL*}fuOTc&5T=)2WssDJ-SI5fbg?Lxb|u>(zcZisG=K{H7C-YNEuNpk=Q3~&qf9$4?YP=%wYSCtpL^Q1wQA4R_Bw-P_cl)@ zUt7p8;8B)WyeH%RB>ZVN(mv##sY0||N&uGv7|&4a3*L|j+8ugvJ*oZ4`PN|I zv>SQ;hPF@VciN9!L;g;^Yv=q59eQ?0Wpt+<%X8U^iJNlTASaRUzHRM9H^5l~p0s^x z@6yhpokW|6b`tGXN8w>~0KM}b1W$2b)U$Qr9!Krle{pRAhcT64=(j^gvIU;ls7(=K=7d5AE!4kf$u##Y-TMXGm%v*EX&_ zyg58gM;6CUZQ{#-*Opz1GTOl(0-FPU)rEiU-nB{7ejW`k+RA&;zVZRv1)b$Vj^@a& zoxB!l4Us7wd|jk%>;uPkp40Fx7|VMS9L5Gz08ZPg_Vt$FD@+?$g&ch-lNDO|z;_Q> z@}P^}&{7tCuTjWRllszUS_ip`2k48CF&*XH|D6_IcJQv>)p21Q`f`7#c53~t`fl~v z_J?oB3HzbA-+pu~((ihLyl3$2xqD;5@7(n#%7;^4zp8#*_gyWetn$4Ae=*?n(cT5e zzv%ZEJb0#HO?dnvBGym)DRns?cx7=6tqhDdmv5j~g7Wq?+GMoFybXSRcAf#O&7~~) z`u#ksNB?YNV6@XX{=4RKj7kR2j{V!AqtDbir?wUSyZUYQ3;Nr1&JmoqJEol@PhYNf zSjQ=Ss`^;3Dl2Kmn@)g##~FQqF+N>%tDjasgtB}<`YK?ravuUN&+A+Zj!9g-!IPSM zY1&OD;G^Knc#V6|zRvUa$atS`DF-x$ke?Vi-6NS5eD1w;zhx=(=~&p^lVVi z>6}HoaG&NzbwmDAXkX^?jLx>mIvuzd;GRQyfB!QAT7!9ZuVs45eM#C($~6S9ahS?7 zj`F$Ts~_p#15*bb7p5MaBi#gVd)oF6(r&?j3Uu`+={0$;P8sJ`+JV~8*7+ZAF7MO9 z_Y58OgGLSL8uR-ryoJKYAK)4co@taF!ux#SyO4h@l0JaC7)O2r(mm&96MdRzN~b|? z<9LkuJxShKWQTmr+4OZubKcP%I?6I0+IheoP1-GZHx|JCd*hI+E`^3t-hFfXfcXTx zpU}?SYv&pMx8Z9Lx_iPmdL3bMfuj&GXW+@c-~n~?2lyRphQaGyu0!asKfKkVzCBO; z6!`t-{1!c*&3?r1s1MKS!{7lN&nfTx&pmzTkYzo(NdOP_k=Jc6yemt7>Z&4F4)|z{ zj{XMkVD95cbHCYJ;0OorYT(;*eGA+g=v@b9H1d=K-Z;u~yc;`t7g}}TB^_lRLMJD( zcrIjl^fVNHzaifkOwV&{1)OJTz6UM$uDCB{2fPm;&EINwPsmW@DM#KTcsK5{CHxFT zCu>MMhaUHE?+H$2DFB{IT*f&zf}gYSc7wZTIS%1Ip1j_`8v9rs{w|@5NN8LI-Z;uv z;BH8rjHjG2kUQaJ9p$nEyM%HXpl1xGF`WA;HwHYtz+Ke-44t?iC4~Ixybl2W64yEE z?oaX(fMXi+mOzJ7pz#T5#%Q{iWEbtrSWx$|`~y$Qa+$WX6d1qDn7z@+>Aq)UrakAS z9Bp?F*Y{lO;X`{>b@Vq5`ko2%3u%d=@A)t9Q`Yle0=$O-*Mh4h^jy2!kJx8q=Y11! zV<>B%qMd9gbUK3H7;0m(k8{1E?sg(aUh@66uOZjhp|NXy_g`P3d{wSe(6t{>7SB4H z0q!f5$xWJes(DHv%R>J-dP)Mnp6%m)dDrHxz^|Rk_~#nP>iIii zz?^`uz39+$c|%BV&ih1l0q^fY*Ldw6@cc2nl!hU;FV=3WsIkP$#omPjgj9DZrA61sZW1HPus3B^ZrI&R`la}>5t&a z^?N9Jq2PLt^wj9>4BtdL-aQ|~IIsZk4WaEh8Jplmd%<4XzQ3o}pXbc*bQPG3;D4QR z+LpJ{PA8);?aHlro(YYFlrO>i2Hv#|xR;;|dOepY)`p-gpP(aS1sZTY;@S96_h;;i4lEu>v?o(;jX^?_1i(c=Wdg9PSVBJldOFNy&Sk_qp)(4lo}>uRHD9Jpfy1d+wPV z!1I6T;3?_%pz{Mb_QLCzUW~nriMfP-<1{xx>l&~bkoDI@=$m(AHrtb*3ts*Gfw!So271Qi zdya6Ba`oc#Ex=ziyk~*VB+_#6JQW(5km)9Ae^BNrxZT^RzdtqYbQp3di?(6y`rE*7 zpI|?vjahrKHbZUA+H2kKq>b4=PTO*vjrcRlXmid?-Dr2VAGE*G)~F3ryRY_R?e^L> zwM%OAcMq1fYi*#~LL<1eN#=vM5b8)9Wn7=8t@9*M6?83(%I+alOuSdSG9W9|~+1o?TzM=8U2Z7$2CAbYqpv2N-YANjv!lz6Wh7 z4VONN8t|$wq#Zoz$7~9oOk7*Q;aH;`;(gkQ@ySKt!}aP3+U^keS&Uu~KVCh`Rpow& zyRp#5I2+$=EV(`q*RwC_?{9GhfolT2d%%t0S_%Sf<;{3b2ambYM1(H|<}4f=l0%zm0d1ytTX!3DDNKKjUgbx!;giJ9>Mb zt5T*Fu#=E+5AW`is|2lo!RI)gobPiZ@Y>?_f7y<+LH{(m(^jV}O_Ak3ypQBsLYac_ z{%<&KjOU&3QUy5eQPJEzr?@BS`Dn9=pmB-oP53LuyK~wB)LDGeCc{q_`1uJuk;v@% z%e8plOWU~v?IWaD0j?tO)uERb7~@d3QinHz+XznWlM87l)zITi(w#$p%h+3!Jm=`1 zA@mV=UqHJU&p~8a3hZs3*Knny-7ZAA8^JRi9r&As1xPDL8RG;R1Dl@rjNJDCzaO{^ z!1#NGLwPO(TxV!qCT$VC76Rrad5__>DQV%rf$hT{e47Z6B?o0lH{+T>^GEspZ5pm(=LO^@(XzHK(h(=-?*m*b`vy*LDSz9 zD9HP3+zV4C8|jO{dw}~V>I6QokQd42?^k>>AP`dbI7z#JwTBoTcqlhZo1vs_4M6)bTSPbbsObBC!7c_GI!M zkHcw$FG=qRO=WRR{EqUDi;kO)la7y$osNZ$k&d5^k?P9vDjjK#pN@HsX^xlb#9yle}Ntck<_2GkGb0y+n=7``%gyS zW54Y_ueG7fsS+~pAbkaT($@O`zABJ^mdn0<5IkvPX%6gHl-UkG`(k~=*T}2LyZya= zZDQbi@Z1vm?Z7t?-rPs_A269|PujV)X={_e0!)%X?D!BQUt6H-Z)0G!6`n&D*Wudx z{4Jie^c{Q2*A}>s%d>K{y?f?zEy@o9*Ezlq{a?nW{(-*JKtsFyc4TT2g52<9j9@Ox z&xXIP$feKw67{-(`|rT$XVE@3l`_W4YGX7WNV{TnWb`*OoU>~S`-^Ao2HF-q^YK1q zm1PR}pMv8xc-jj;1L;pa^I|l*DbKU#VC3bROnzhFmm!C3bOUKEz_A$q-HZ4cGK4{A z2l%$orY}Tdm!M8(0UHE12{0ofSf8RF*UjsBUdOY)Mc)G68iO|zWjgYF5I)9pe@sHCCGZR)u`IQv4s4P{C|+cQy;lK&aF z&%j$p+Ra4De8L>SJv*JC^NQz*z}}%;65y9xujuSGU<*L&B;^w#Uwv@Qq>N{}D2sZW z#AUsxSL?-kQ18}-^$?>w=vr6mat=KINO|>s2AcaXwLb=_fy$_xLfccg8WVCDR z!8ULYy3^sYHF~$6t+$~GW9#`-li49tk%tDUTed*vAiY0u@If6^w&1 zXe~L{+wk9oD<97_fL#l3ZOGO>JP3UD3xlEIp2{?o>%+UTGoORU*cg2<`i0zQs$E$> zNi&|^b84*h2J(X7)25^yNc)gBVQoIzl(g5x*_pICY1eVC?i^oRkMsP*0ooffY~)$n zllCQTPuh>Z4y9her(LQ#?L(WB_M>p*(1x_x`hl+YAZ<(?Dd#-@ALz%}m7r&=h;}Sv zzFZ3w<=HiZHY)Af+L^LYZwKsOkVRY4Q1TN1t4&F}k?SJuNUpnFTWK?Lo#Z+##%=^Z z#%E~@(ypWp$+_$%@(+SnTaUIX*GSrotS4<$+JllH|0VEib8@ch`Y$1Ajw{-lv}3vU zb1im-vaaD=yY)aHA>_Mm3j=-vmo}$Yw0~_=$}$Z7?B)3#?(Uhm53FO0<4I}qzl3Mw zMfEckq})W(Q;}x;XbRf)R`jBusu<&jzguS;Z38b$L+C4zN#D;k@9=KBoem!3QA5G; zCAeAv7Y%;fp7H3{d0s%;4e%8M&i3eUiRsVk!@EAD(a2%Dt^t4gLX4467RNZpz}(y) zz)Le|_&f4J_`5+_V=(j;I%fU{ol3yyGx9g;^j8gq?=L80yteTl+ekOwqYJdR!23X+ z-Sf8(Jx4%eCjFhhNyom`+36R+w}$i~@HU3~E$GFEwmzlAlr^>?CHHB-{|8fTfPtxn)|UusM6H7DOa zr0yqm{BZy1UGV=04)=%_L+)ja$L>At!n=K%_0j;GiIC$t{9VXS+kvn7;4|LS{h96? zO#%W;vhxX83X6=O}Qm=h?lW?(M7rd~TlITWVZwBVgU* z>F;6Ig{Q5&KMAG1k>`HXR=|HiUMcV#g=SPZ`UB4WrU%Je#`^GToe-CK_e7JYh@#i+>jR~0x z4s8{+!x_V%V?4qkzY}2GQ)WDhdo;COL)p5;?=S+Lrhsqv>N`I-p4B)E{i^!BjoG+|o)_~z z7@lsB?;O2kBygk^gKj#?J|)c<59jf&5u8^}h$1bVIo+}Gys=@kRd3hFB-tsug!Tk&71}zqYdnN!?GOXuZwue!d(@%!2kjfB;75Cg_J^v_UJD-W zATxmf6WZDBC#j%LGoGy+Y1@A z=VrG&W!)pfg=J_yMI`?mXX?XTSuIZ4_nyMqhh)_bj{r zpz#B^28Gfek=6!$uc$}&WL=@W0c^$>^+onI5yw8S~aSfgco|1BV;pK052vW}THS|S!K8N43{w)1n^T_)E ze5XjS3qQS(u_I}&3+yAc?>g76LYm*L{lZPsF9JUbe14mS$=eUS{Yf6~e*4a`KZO6y zz!gF!`-Ifw+27dzl(syV{jF;R{dC{KS2Q^6W9+w`FL{>A8_=}hG7d2aEn~fJa@_`> zvEACxwVNC3tzBL_x-r|vgKOj0_O6Xy`+Qf@^aUK`t}noNohDq`=8IBBzkvRMr<9BF z3-E3XxITf7TzA3Y{tn~R^&w9tKLcs{4+?Of42(Vn=lt`)r~Ti!_AbaX1|E#{G2T2b zZafOuTU>YG!8rOI!0Ox3-?AF~jvFt*qwk~xaK@B}f#YY=N^nm}8GSAG_4-pB4;)MM z-8d$c;L>m7*r2WZ7?-}3DWs1k-Lawy&lxFejQJt*o!7q&y#5>G$-Bb4HgWwp`fc>} zgpjTe=ri&hqk`0nXC--lkukoW0r?nMeT~hjf4`T?T>30?(LVGgI`??MWh~`;yt`jm zzhOae`5hc5EkExE5@JW-Do@+_hxXvPN$#0&PsT*M?lD z1l=9THx8LDQC9oy3G|^X&S&QUXN+)O=-IxUJ6p$5z~n-wwk_L*^X9K8gUB&+Y3;C} zk7*x(#hXoi{{0oMNO zT-exKV~i;pGavr|J;d!D?f@Qb$Hv_{e-8rlDf+OD+m9bdpIebN2e|CR4`z$)>rfw%**Y z^$ls(yY=C`J_Tj9K`jT~wT)+8sqa%f8wX(>YLnJCx*B?mfqTxg@gK%Kde!K+rT@GykcC=&7TBf=j8S`r^)X~dK8yy z1jpkWz{f|ntKjgrrRS077@Ywf?IzFPwQwHa6nJB9jK|&!jA!&e0$(fY)?gqn~=il4s-6zeybP4HeNiWCc+0m6qQr>D+w}}w?QE{)?!kj+>AKc)ofr+iTF}rQ=(y|p(DTx@+a=0DS@35B-$mO0 zR_LvOZ{zqpH^Fsl2r}n|PEl~Umi2t}TF^2+Pg|O2DcqsF>)QZyQ&ZMB*Avu@cD)JE z^gITCH{P=xYJ&eH_xt3>d@q#Ehmgm?eTe4fF6v!JqIp- zTfH6m)3`o{$3@W3!?WLeF?ckN%6ZN^=)vE?@Z0rV_YJ`KEgwN|+BUx+-+9z#@IQsl zW%xG^YYSDoI_lQuLv`~f?ZvdW^3{4#uNv)3lCZT|oM?xXhn zFM#jGrEMZ6rj)dt(A5`E3cYBXe?|Qihd+G++BvlQj|Q)PglW`+J_7v#`Vlf4(-*BB-Pf%;Nh8|s@eR%I%%`c?Fw_}h=3%c#G`HKIP6Cg3$5SpQ9bbge%}-$@qquiwSr zcGQn@k9^mi`eeQi_>`oFHCwH^u8KgzeCfrs~aHeG4L1% zYuj2I$+!qEzn`t}`YX6^q1!Xaunb=Oow!TnE6X>?QI9Kx_Pia~_2lj6y(MrbX^#ux zaXEbS2lqtu6Tv-*%%y-oO`QyYrxD2C1{rp7F9EHzT<4+vfb#L-RojqpE3OUxri}eW zUh=dnU8l?>^!x&N&mr0XTvc$-p^v*r-FQw>w0Wd`!n-jwt9bs0vdCm~PDyqc5<=8C&0;Tb`5j(%ctQAI43W1AZL*ECR;Z=v3&{e$}(i zhrw?`(tiQ3b*U^jz;g^8Rfqlyo{xJlmi!{})561V=>JV%^ebtDst3JK!1)5b?4nFt;O;`Fxpd*Z z9d+S4qbjlvhi`pKV}RcYj;`P<2EN0{qV0bH>B}gmEVeJ()E4qeLc?}ryR&WC?rdwe zG25H%%r<2^jB96WkL`@2T_Sc{Cdk?hd4E!ZY)N4CQ>;0Oi(7M^WyCBQQh-P)Gk zK_=Uz?bJ4DyW2*d?R8jqY+LOFKHK0vWIPX^4ZzxVwSOv$F(k$t7@uHFLR`GUWN;a4 z@EUjH6+S@EEs)Dt1!EM9SExe1@d?H?7`tH1g0TrJNjG-E7>LpFDT91%k+(7Qj6LW~ zzOf27;AIn+F%0*?V~m4w2(6H%Bsh&_Foxk7^->x>KIAXC`42iJ`#wi$^Fp;vxBN(5Mf@fnCj2|$T!MFuu6pS}8mZ1#!#w8e6FqCTubc|In zmcbYVeRxN}-I{h|T*E!+8lPaCfw2smxf|n9jyg9k!FYvF!Kd%eScYWqZ=Ayc;98?^ zV;GDhF^0uhmAF`iZfR%-T=Tfnpbyum11W2~x^|D}JZI&(KUaCKxLAMJ8Uwj@!=r0E zp{!4N=u$yFMrE@FR<It8-xFXbn&`2cO51^X}G4AFV~K)_dGL1 zSypmaHrGY26(4ZdF4PSk>hbJ4D+FB{Lt*Tf^0@9*H?IAS!`}z2>pykpxyPPmV!XfS zlPIsccCFYA97Tbh#Ix)F1i-Z5+4Y>d50dv4?}ND9hwzNFFrHnnx)v8_8(>@`Kjd;P zFTdIVT%)=lp+4{8+z%YhGiJ|%dlX#n%eUtjCIruEu64k72Sz)AwgYwKJ_z;GlV{h~ z+7B`YV*Qn_*8a`rv z2MAs7gw93!M}6MT{rp`Ye_z5mv-4f&zn&{Lh|7InGr;>M>CVBO3l~P0q2O_@?KxtN zz-3In^K8eAZ=tF0JsvQN_?A4&tOnQ5Vh z1eg2TLMZxMwbSiu|6G?v6zj_;AZ26=PP z(FN{vN$U$uZ864^+(4$dGcSxa8Hf(Xt2^|w3cRC%jik)$q)$ig)ZD)Tw`Ug`YxEpG zj8DnLUEgb3bmG3(5ztD+T|b|(C&q#33pBQ65IiME4r85^#TXLfGG>Cu_{Zuzi_;hn z$7#phXTTU+VjPJvAR8&)AALIZ8vmhhwl9~lCS$?B2pq7FNI{G@)_C}|zL4YfG_*bPv=L?C+4Vvx=sUM}{$2_GjEU+n7Jsu5DcF%%z-bGuLRYzqH-CPSclcjFam!*Iem zyFU95-RvOGSl=n&GDc2W0?^RMwH37rDrB6&hmwq(k-(vQ`h>yII&@kpr z|CxR{eQ?Iy>A!Q2js81*Z~Dgcmnlma&-%jjf$8Vcr=>4UKbC$j{Z{(C^gl&`SHGBk zu*Br+yLtqFKk+^l*+aSXmo0^!=bq@((&wf>%f0FaNsEi4aL>E`vP*?kw+hzzBA7rE1wOUDfslw#hi0SntnI;F1goUS&T^=!1X!TYV!Qv{-#}jAAqM2 z<`K~Mj3N7_Bs_=G2l`$5&AtJ?LBMT+j^DO@-(BE7=6kS@=mVX?;2IBZ`;uQtUqb#^ z%KS&0G4{;!7z?3~ci{PLo_{B=0J7L8g(0W?OHp)TT!*n~_8-RG1tVxr;I+T0&9k!D zPuXADPuVBQk8(&;x$Ik}k#7GsmUR0xapZ%B=Lai~Yn)=_*?%dQJld}*=MiA#&v-j| z_?$BGQ3<)u(;wJR$&Y=R{M(Nmg=ckDpUXabN*ZV(m$KS#ssFe>)wmOF>B^FX=Yr_Y zeK_vnp_=2>gtmQ7GwR?v`4ixeCJ`?SvaLa9jlc_Q@xFz(Emi zYXCf;{C=)2;2p}h-xj}`kaY~t?zhSW-Wl-Ik@9Jgu^4z4gYz5G^vl+UhbiFu9-4c| zUrn2ANBUUgGzRAn@Qp>WLwPqY#<($KWj0dIb!tV*UO`{8D7S%f%A&8^Z`<$MZ{P2= z3u%6%erJAze#?Ht_6eSmrcZhb@Ad=s74`x41@;wxk*06`73s#k`hELd>pQofsRSHE1+t?gGs)E! zDN7x2Gy>NUc+5$j{xbcWOKHc)l!rF1FLfn#>KImmx=v4iciKfS+FD=IJ!2>op4!s3 zQ;{A`JMcW#cHIBSM%~jE)4|V1U`K<06?pXnPDdusZq5X)cG=+W9Zzgaabu3{(s<}m z+-*y?7u%g}#FzHXZ?dcTlAOme96K!G;urEowN?s}2!ZY9x(msq? zG%hJAc-p|1F{kICe;Sx5%9f^mHH6ouv*(_- z0RA-a#-~;0IVXID~CCt2#tN*9lwl~TaPXtkw1a5QSj?o?(>0F7Teew!Kx{pM%e~XnVOvwr$AvVw<#W+pfZZ zwOu6w&NgYAv;EnY{cQr<_XcpC1O_xQvP=c<74jcZ#xw9dqiPELRYG^}K}behD~#+j zc~44SDat2B&W~tscPZ-`R;S?G{REzS1r(hz>fhj0gO7T-#L^f??Hbt%&q7~}t*L!;*Z+SoMS zYrtCsI{X+o>%@LQ9jiZ3#^~I7^ZYC8@Glar&(`Q*FuZy8Qe|ZIoGkkdzv(pD&>v|v zft|(OegU>(>g8fM<#@jbe&>bOzjb?>^6lZfXCD1yOzwDkottzwxk(f=lAy7|70mcdSlvDe(2WVo|12LJbL&MedGi#JMT5Q zM{tcGZ6LCAgVtlpxyLstu%2VFgYr`Y%+ZmdC^B}Z+#=u~1G5YrWPyh___j9!mk3$H zf$fbv?(444^ImjNmU4yQ&v@nwlvxIjcIfmvGOi?TA?1DpzBhU)$$K)=bAh)B>DJ3f z@N}Cp)4;ci`~uL5gzsEjjllNY3=AsFM;DGaJ#^%EdK6kb=utP&}qc8d&O^2um0}nuhi*u@XVz?{cY5L zXnPwe=Wn0pO8{N+9)Uj|a&Lf-r<8G@xxcmg1#q6XvKPEP;oaX-eN36NSk%OxK&PGhJVPPr2i?*|_!M18_}(mg`a1ldd&gGrCUI&gOopuDt6P zb4?ilW(qRpf^XM^5nOqJ)1Kiv*fpx_u{!8!BmB9>Ohy^kuiE;wjX177qfNVi%YJ4o zJbeL>}Oy$1$F3MlhE{FH!JiUq6Do^Bd3Vb4>A^#q#JgGq8?7p1r3% zx)f>JkzI5D2TT)qcg~`X=Lx#k|Ld6L+PNWc+Ag$NETPUyQGO=yu9@{2yOw@Vx^^vP zxlEezcu_usuN3cjz+s%8ek=D0#Q1f%8#}LG$2e2>{TN^Bz5`?U+}l$O9IGj-Z>VHs ztc|uhviAXxcH2|Hj7LuWT-uBuh0_+ubDxm5BI6L<)1d9PJo=5X7lH2({2Al-BmgeH z59blvfO!mjL*TT{1flI7fvw<=1kST?V|-QUp%7(r^Zq-y^n)2^sqZTTeCWS&Z$c^f zm1pN*jlip2SDMBz8YBEO&-%^usbv7a=No?sEn~xt7d2+sSj{-U97STzT{ubo%eUwG z_JKcrZEN7&eGBe?s7tzYJbhyN=DP73y6D7yCC3(q3)EDZMp8B6gGdB#oOBHvi* zapZe;hJNjf2&xASz?;iob3)npHt1PuB^Di()Ber@Djuv%t*(k3NUjcsEY8Hsx)%#(4Gu|1)^` z0XTga#)SR?K5d8E^Yy)Y&V#X|za^qS1($P3=V?92cTdT0+})Q_g*^Rx`d^*1&4#8v zRey^uNM3Dd8LL~1D>YXNF8gHrM}2{pDc6|$J?^KG^9ZuprWa6NS@b2n4qm?(*CzU5 z(;$od`7P;x^~~TG@MK#b3!l+kZ-ZYysCHR>kstDWjr4k4`+?JU*_b@PedB@MTOY#n zP_D0mPX@gyz>MKNE~eM^Wc{}!uNC-hbH>~mTW346o{i}<&aWD>Ti><^>pTlM-CGuS zp3N}eZI7OfW7{$w@E73!1J3qw0Df(!o4DJ4jrAMG^OXSgLK)jtVe$vUxBJghQr0$Y z8#Sh9E^W*GY3@5)&Nnw6yqn4McbtOomK&aXAd7p(+{@JpSoe@!gpaG>sSeCg@TMXy zE&Na5xfyBh;qt7Y_sj?1K;DOQ*ALm4=Y_}{4$g1kYXx$-SIjex+zaNuufrkK9b?4~ z`c?OSErc)kfqlxiQv|-ZkoK5o_mC|G-#GH_@P3TEa_G-}ZH=fm_ouBvMrF~yr(Hx_ zpFR?8CE8Q8;b}wD7Nb2yd&@h#Ypcmg=V(vShNDkLdyY21TJW!3 zP8*Ll7i~IE(1Z4r%9PReqP?a8&)NmG)U*bQ|twEdCXJ?G&SA5^=koQm0OVLi7@D1o2 z{0|{!wEvjjPm`tFQGc_>IBTud9z%pRT^up~#}obS-0SdURnt zXI}D*)2t4hHs{^Q;yD(jfYaXm4>~Xg(>Tr((D@NK<1#%%wH}wUG$E}Gcl$s0@3^O9 z09R#bOeXIj&+hd|1}^vXqzA@6*Lguc%DIQqxR2A|)feGDbmMW1E9pX7L0}s2oG}2u z!jb2`o=&9Ow@(D;MBsiR{aa|xB>hLy;)B;dyA1EfxNHNaD$hT2`8${Uxj!U*I5^#V z8RWew@6HvRQz*j<($*oj@i(4f>>0)KRS>$yxFqIYAAY;>e3d+TQ5IvGjE^zCB?ozD z;LUwL&Nqysk*8n5XUt6q?L%I=qn{4E{|>(Sl&ML5_2h26On`d}^3|Dk8Tsx;8GnP` z7$oOf&VP)nasN<|Joi$#x551m?pttAg=Z7Hr^3A#^Z54McVVpbBHGQo0N(@8-*EpM zTz9!wg#TUOXcj@;!A}ctyH~6hxX$pLJ{x-H+qe&GV`vzUT^zVyC|3-eSx6hl^A7sx z_O#V;(ES~nXDQp0G6TR>23)U^w+HwDbe-$8yCd*d>V9sLwOvx8$-09{djJ$3y8T%Mid+{)iA(RR85 zUhdO2(gEW>;j!o}0-5~|^O2Stoax}H9OYI)*L6_*5c&qvjDbtfb8^b0L}uq-+u`#| z@aTUfD_(8VCQ#P-k7tREgD1b~r10tYrz{(gsR8iEp!X9zZ6|Fd&jq+&0%i|+?}29w zmwUz@fhPex8q4=9ydNPweE_;#ZhmM8{_o+*@8m3e&H~0WCG<;u15>?#zs9{KboWrC zCGYMPv_7oQ8RQipP5oEo+4W6v@>hZ1@7nl2*Ava)qX4qG9@QTe1ed?#;dx~zX)ng} zD(f2ZmDMxRR{-;na_%)fPaU|2H3c-Br#2+N7Wuown5#kG{d%6a?%L2gwtkzFuRYRr zV1D4-cXgdhn`SQftV_QA-aA|iz~>%y+e1FmJ&$-A`Y~3@m;&osp0fq$%izg&;auGP zwx(ADM_H~~To0+Q2k5&9bb3&S-@-!=$~@pILS9$$dO^#*feGQ&^GeUcd&}(9Av704 zqc3z@^S%k0Ym#3qlD>yJ(5Gh|`{MEM#V$qaJ}=8vc-|hHH>Q_W2lV9NDqv^ z&H9YI=IGgVQ+(twuIetdDnh#(?@ht;8Z<|NCoObbXW15AH@Q|)mc_v7U+}y3Y{6@! z=_~M@RoA{>@oxNO`2f$9)kY4xG5Z&1at|Y|9+zt+_a}~o&%xn*li;h$eH$>wdpiEO zK62dX&b6O($K`9lICfaaj#Dp4cbs$FNKHKzA%6(hQ_>yF^?f)#o}mov#f;yMM?sz| zaNkV&8Fak}oebofgiJNyZ8zWeVqiK@UmZvf0Z(`EoJNQ7$e)Jp7AHUl=p!@om!X`$ zRdxZLmjk{T?Z@98dkJg<+H6|d!;j$D4Ncb{+Jfs--u+0PjgbL7A1MR(t-vON&P39) za(6${YIyX#^Of8`f~Q3Ak`4Jv{;!RF3f||?JO_K(f!Tswr@1H00bTHHBz+?EFT&ph z_;OA1lKds4cLr}Lbjl;gc-~!$EJlV`@a~$z-*Qa@U#>CsK*#mQGGxj~n(G(WN%JY! z9QsuQ)B$O?c)v*cHlE9a>n-&FolkgvpYrpd^(Hj8A?tj;fw#$v+cT1nwswg8Yh1Z$ z!SydP8uR@#c{kD5Ug$pyM_=e* zFlnEH!#!$|*^mQy6Qg_gZt0KA#XUQ?7Ly-J{si7{!h?IO8o|>!p54b)o^L7|8vaJ7 zd%fJFQ;EF(;MzlZ+gK)^x549|l3v0Y4R455oH`@TAOv zT=2Y@GU9rjdkZdU{Rb|2|A}(zf$?`j`_Z0G+HV2te#&R0m*t+D`x*5F{7&-J-xT2c z0INRpP(L$)n*r{BX){&ewIlVji~QV_b8lNhE@gQFy|vW8{e*o^IqKWK#JxXN5+B~FK>V6?{dz7Mk1c~QmzejwC7ACO&g8#`hC#VE)!>m zDGjW1^;+QD%~cs(4Y|gmXMJz3OFF@`bNJuEp>NG~#eVRFK*Rp<6J&iEj_!GO4)zHA z`T&0c&N;s85B-J4c-sGZR##v2834EIf!w6MpsZ_!2k_z;@Dd#iCcg>q_SMEUJKuM0 z(2_Fx7LAE-O}(~(m!KCo^rxPJ<}k|X2Xze5|Ej;U6J_;38|$DiT>rSS5=Cg=`aboU z>Q@b?J?l5$08Yn%m!#=0cdRl-`V_d5Lc18x^}wx9-QSeXj1G*i&|hARG<~c3#jk>! z&nCvFt}nbeu=>UIqZ@N^47|p190%?f;Df-ruPYt6-KX~)9yarwoP76(O{Jb2z`J|I z+=KTE$iIi*N$ALZf9~hn!Tky4+>7VFv7Y3=5ny}(Pa}93#QSgH3L#%RQ4;XDUvEC? zy}3Rj{UG(=e!WWMA0W?tfA!%#G4kILA3Sab_iftmXn6KFEjpu<*GY5lSV`{6vIw}R z@bZ{6eQf@=f&R54;8?&l8XEq7!8o4#lm7^~$<(WTdv)mbAblS&Ujp+7dG7%8Eou9a zw?6a+@IC>#ekM)-Ujbk}3wj-QeRvzVmxf*i@;ooP7jX8g?mNy3PWKDwM@$8tG4Sh{ zbDL`oL#usgs@18N>U#z-sT-2dtelBYbGj z)NZJK(=!IOX-8Ae{T7}(xScfjENcVLL?5s3_Y!ryf_H72+Dg0e?p{OpJ}=?95@pMf zmzp{-Hl`zW+8lYDCuB)Pe~+BWXlL&4m;)Yt+S>D##n_3sSPOsaYbG$pVd&>FPQtkJ zr(vwUc=k8IjE67=;{`e~9=$NI#%vgekp*6l@qPxn8_3_rv#}b+Nf?jeZ(gkj*4T@) z&^H!iJA7%IG3LS;kJR8OL3$~4qOB!$IPyWSAv`odzK+P;jJ#9GHw&4K!_dbTL>B#B z`nim$u;1H8xr*R$uY=!~-?;lAj5}!nyfF}&ptlRSSzgHIcOC_-zr%hHz1bi5P5O=2 z0LH$-KBWk0o@=+AyZwNDjPWsk=kegv^RQ#$YoNC&GIk6w*21`&=kPcj{x<+>pVS_B zeQ)~M((}CqNzcWz{#(bwQs8uabIcoruKtE@JM#2_Z6nR~m*ZTs5Yl*m5{T_*91lG& zYZvbk@niiA+VuC5=J=#--|rNSe zXYoVuIgYz$rXMsN*FFSB|D@xQz7@x5$8~?JF^DX`Qm!ZRR0OBL_nHa1mErME+VMl4 zhrmm5^8DS`A9&u!l^j_%^RBIYIL~{*F^{r-YvXwD$Q2(tchI#l^W7-p@3Ria!j;6Ech z1v+7$DYhUk;tnO(j0Ng(0%Rzc(^ql~hroj4p zu6~!#;p+@MRHaN;`nL?+wZ~6YMr0`oygs3x(9nN#nEKS8qpwIG(ci%8yV0kl-^u=0 z|IsM$>NC;@q<_i&Rez8DqrbJF|3{yZ{-rBilPIHqNuQFxvG6|6`i|_=-G|!*9QN^* zN!M4kp7d6vU4S2bPy={&z19i6IVR|%axJEB$^Kng^aGuxtUe`uMH_hc3_|@m`ik@~ z9Rc4h%IRnN89wwIWk=^1!LbxL*GNC3n_@htC;vV8ah;_PDF^t{!|OtxJHtmW=;&+G zC*&GyEAI<<_O~GWaxiu6tCH`3RopGiNJ{-+G2>t~t){2SnHh8)V`8Z#l6 zW07lB*P`*EJ&rte?ReP_nGS+)F=^`LMM$jP9m5jkh}EBUr(e)IRe#lh)mLa8TG!Us zT4Xpxx@+cCbltZ{QN{>ZBQWeQk$$b{(v~)vtcL-=SxHSQoB~t>?Y8eb0>?N%}|7 zRR324u{yL4t=G=L*bZ)oGOhq;ovnesYx9ioegGXz|vjGF00$kbQy9L);c-jNsW#RQJo>#$F0qY9bh2T3t|F8x={OyE) z87EsIk7J1aZ^1D7RodtP_%6z~+5?!r)c;oWa+~%UNnRJ^nL_!MJZqOt4ep1eO^2TI z>s9bu9X?8c+uxKeNqyx-w!a*cdCp4y1j;B&5A^Ks;~qh_0+cBP5Al%uHhBHrMr~Q{ z{VNL03f{M(N9|ozfuBX&b*-nb-ZASJXxAd&vkQixk6rLM2l`!+Z#p{o4B4NN?pWCr z-p@nNG1v9$YUJw+?Y^W(!{Zdn=u`LX*=O)m4cx}6B&O_3U>q+~g4;6<99xwoEzd{c z>n2wuy68z+W2!tm{s?k7S24avf4+YAL8SFW$62z)&UN}h&$-EO=-b$x!N9oAa1LZl zP9ONK4y~@}&oQtY_3wD+nENUE_nh%>(a96^bq89WbL_mwxUmi7dEU5Z7BA=77%#_5 z=Txp)oo|gH%{`^cqJ7jo5XX?^E%-YD4)^i6H)1O^jpO{C{Jy*!3wjGU_hi(Drg2;D zwQ#RSDsUbp&Ak(Q;M*86_iwld!@U#R!O{v2N%d+e(^(WdO196#Cu=Q!i}x1HeKe#^6OlXI0K{W~ttvwa)- zjx(Nl>)3OIe8)BWu(QbTSmQYM5Z%~6-XPByg)XE!7C8nv)&!A78{9_drvvYP?)C}x z>GmIsQ9Uu558 z978AW{$`SWp#71*t7QCx<5yPbdZw=Z+eL7Np|ih{D;e*`EObUUjz9K;alfY&M3xt{ zv0Bux_8INg*Wl+N@@tdUeqEJ1+!7jVw||qa)~!6Zr2T3WKgMNDrF)DoLD%_pF3N92M_J$}H|bfS--PyL|2YG> zKSa)h@URp5`Y^P~_os|=J!SE`nU3t*bBql#&c|@ylb&pyHLdNe$1JJN?|A7HG{yU6eX&)TJ& z-(LWiwhZl3#u&{8#&wqSbJtqhxm*J{Uk>sb1y9bQjn~vha|L+qO4@{64;U}rlC&Vt zouJ{|+x-*Te2h!l$Ft*ze!qL*7z2-gQCHfRoa-;9K8!nUfZUEb_FwMZaLsToJl0p} zdPDnEVQ3~K-8uY4=w{`+cYUBAP(9rz&wfn%R1wmndDiw<8$N=_VvL`0g!bL8U5zoc z|94$_5*U46o)NC^>_tv_Hp?yf~7^e^SE_58@ zg3xz=t^0TNE7n4Xh0$pibe@eeRd}Dr-QOP{MA~Y~9i@zGia+5c3S1BQZU#{H0quJ` z&r7(w-gmuWOs&4<0=)M_m&Vun+wJlBp5%8M&+->a`XO+;X37tpSMcUoub!_X<7McK zMPGhj>a-cM>8I?$`$cs5F}Rh*HmIFY8|g^upeSjFz+qe8z~y>U|I-6>YWse`<(>f7 zn8xJ!jTyVA?bda$Yhu4S*Mh~t<#*JY`*>je*8JvNGy6T+&Rd`-*QU|HH|Kd3I5Gnh z32e+ck-&7KylZJ~$aN{ZkN1vTg}H_yYie{DU21pk zjy~+S>_@a^>nG^R^9x|LKi}pW$Yp%VHQMNObedL~;731s#sE43zjZm4OWS)8S&E_m zwp_Qt-36S>kR?F5zR0$Ma*gP}%W`?PNnYMP^Uw4C;!)qp;V}>8n)9BY=SlFJ5x5)K zn4fWdfZh-C&7J1{HFcShyYYlK!Sg!!TX9w9yV3UdR~WVn((CeVCFEPU42c!AwTa-# zM7n!QQUm8YsvR`WLT?Fa6M66Tzd6ho$Wff@4m@>5mgUG2hAdx0r!VsV2!9K~^)CE2 zMy7nE{|%n`jMgzcGjI!2CG`_W$wgO?@buSN#fINfMBHz`{IoKI;ddq~%gqn)P_>3QMx8fkmLI|=>xTkzMZ zhfmS-IG#(x(;ej4fUZ7*&mgkAMY;a+1D#{Y>^_1rJpTj_x#2r$4(bBlR)O~lv9v{MZn&q+!^49Li01=u0nSL`74nB88k1F{vI-QKo>>OmvJ~{|0hcw>P?%NHY?{f z&ULhjX*c_q`qyTrolLux^DymX+Rq+?rw-5Ru{gT*cZQ7Tblx-<{Knrkpv+_Vxg+nu zybHYZy-4b789LF9Y%J*<(z=qSy~}yt9m+2SpYt#GsJ{Tl{eCmx=RQ2lMYgt-Z2)iD z)(UeEQZGA!Swo(CI>(aleoyBv?!k1Q#oOq~y_nAXZgWoojB}jZ=<^J4uh7+Bz{aCa zH_>hvqq7in?!4qS&o#*Zg>>g0(dc#xZO6He^PEoLG5+H=G@RGW<~vM2!0!MG2FMxA5_iX6tAkW(xCgV zS3~fxfVTUn_CkLuaPfih(5$7r-vxh1$~}XZ%D`7e-n;5F0evfJrI5EJw3Q_nbk>qq zm-qJI+z6gMq#XcPQ}_u3S83q$K*N~4#^8yG`vd2vz;xo>^N=qC8%6$B@U)@*6`))# zp2NY_8J=cx9Ve{{crsFtb$B*z@Car5BgaZ;d)D$CbYGMADZo|Yy&v~(=w>OnHc)OT zX_qKFkM>qEJMEA3AHbt5=isR&&vm%}Lc2-8H`o@~dbFuv2>bx!K3whWcPU#9ew|O8 zr0wW8egdxf=%_nwA{ltT0?vLbChmp%Dfn`okqjK#C{7~tJ?=kJb_nI%d*ptQKf!s7 zIF;PQSSTAp*&2OjbxtM;I1^iYF5 z?FP>4v@fiJANPjoyLNw=zhBk|d5l-p|DdhN-#0rznOo=~61dCYTuHjIdH&W~KX7YT z(#|rCOS_C`h4}ku{=Vj1o*R-DfbWUq8)K{Os2BK3!;i6q{$`rrTqnsO8E4s?w8P-MP2D>`b)DD@{d^UMKHdq0OLJ+Jq*6iyho9@gf`&(R4t#CW^vyJeuCja&ug+JTlQ^Hq1HSLT?|eji=tcCp30e!_yEyu( z0Q_M1aUSEj$IeswqnnQ?^C{`hS-zo-uZNc~crHm9=RK>@_a*Yrfz!E(XDn9+wk$Bl zNS!6^IP}|*9|p}r@YxZ4-GJwvls%7LzJ;eQ=%5m1@1ketO`fa#KI|)tb|Y;$7wCu;xk%#JIR@%IVFao7Iy4!my$_a4$fAM=|qbYbDQ~yCWx`cSgq47O%+j(}cehGAHEaiFNYLN$PF=z6f0Pi8_yN^F9=`W$- zccUL^9BGHqgR<Si;2Kl@Cwv? z8i$PfeRoi2|G{@Jm}p74!r*e>RtV)2;wWAAHxj(j$Xw8QZyg z0JQf!Bh9!T{nqXkh~)hMa-QV57qa<{=(9!zF@Edbz&?VnGr(tn-%{|=5;$ckP2OQH z&vctj+e-}1l)&T$z8AV$3@`dsXM^AK*lwbWb(Ht4w{FP$9lX02@GagukX{mA{y^?s zl!*pUp%B^}&&H}`2L2lO5NIl_4z;e1}pl3*~OX>x2Mu0Jj$2^ozct&fTw}U$ilKN#JE7 zuyc@a2lD+1Onu&Gp$psa@6bL4f9^qXpU6zoMncQ|Bc4f_96atv$qCIv@Z1zUN5CHe z?*D*uKZ<)q^u@ZT#P6>p@3(mW0Qf@4qi;5f^4gYGKucNNSMvm3KeN4pBhGJGJ{!+m zOQ4~@QQxD!!TG%30Zt#L{=(0=yH6(}{A?osG%~p_=oWAb$jbxFDcbi&=#1w%igbOE z?%B~#x&zod-2WdyZrjpJD!-rdjiOwdu% z5(DdAsON(`XXIXJb>g!SCohkIT?*cb)QRV%>QHWN_=*GHVCr=Y`E&Cg$ulpsMNq~& z;9f@kTqd7qpO(lM_%cG{S$K&7ehcN*CVn%|nuGx9zky#pMBODD`QC??Sn#NS949U% zG|VB+eeHD6Rg?N356bA-I^<+KI98tG-g-JSKaJT$0J$&2%jdu;B(c?Q&ToSq%c z4Bj-9`yM#mTMLIi_tM8Hp6*dMpb1;Ya|-QAC;BRIgh7u&Nj;48cDk{W-E~XFZuL0 z=>=~0V%!^J3Jv2k-j7;dZV}&u=gZJsA3WN4{4W&kEZRi0y)=Yh?JAz1Nd}xY6J^JT z1Z_l(N!JF`3?8*J{RV6fo(I938~U|TDSK+mdSFMW9b z3S5>oA6oPimlw;`m$V4!mSY`wrn(*F+j@#2Sa0&%h z>sFqX&%ff`y8Mv**1h$y7+CqS?zB$@NqY>gap0{5c1M}X72 ztkOYiP2w-pH(ftmOFU0Em_GCu@1GIhif`Apk@U^-QOGFda}7F5d}`(m*F<@F0uBB* z!$N4w1uvc*{1Y7BC6Rcz$se<$Ffa_2YfuX7e5c{jLYwi1Q5N zOyXUCTxVP_()0c=Wt4-SUgRGMy`Dq#E$e8@YzNmedGVa=Si(4A#0Ty!q^$;5ee!xfH~uUxw0K{d=Wo3` z%{$e05?2;j??dzcG|%zY;oI}Rkcg$cHy}NK0Px$5d8lUSA;liF`gTK zO5AAp3Eh!;7dj^a<6Uf?E&dA_&+QhW9M1}`*=w#mCc3IMO|x+e4T zG|Easx_kY#iTjPTQcSGc*DDj37JS+n{U7$nq^~92yAD0aR1(@Q(H5Rz)x8ff`Z;BP3_la$?tb_< z1y22-mXPlT^_h|NUm2jys|0_)^X@v4ny2!|UgG>8ltql=)s(M%r43M@ z0nha5)7T6g2PwB0L3yqL{Z9EYGw^O$(n~p`t zf@h~43yv4ZnB&cH<9KkK+2^h~YTtVo}Kz`*}eE{_Ze2e&f#2=x|SNPUee4g+Ml?y8v`3Th{>QBi>s^LO*%h zfomV}%Gk=!8OW>L?VkS-(h39LhxAdG3e%_BqVVftkhnH93D~fsM1?he1TQ!((j)P}#1alC$ zJOZZgJjj=4jXd*|47e5Wa+NTg z?_=P4N?a43;_(d8J<>d%B%h6-VLIt0d7gxa&BRO7Xo7V$p7P{bUZl4+G%SZVc^?6c zXPI(Sb_L?I(hmNQ)kN@Z2i7_o#CrwG9mKn5-(KT+oNwz&UbI1J%hFDkpLhM_lS1EJ zzWq-F{p>S?dnfRvb0AaFCf9j)U+Vz*Hs@r#fKR{w4bW~|4y0c#;;DaQfb<=t>jSAR zs}|1!l&e4F3i$KC8itZr-@{9kRRH*5(Abf%o$x(n-67vZaPI)l|7ds*I<5e#&!|3? z72s7~#?aR~QDz6?wgGz={4>cn64+VPQ%dN{4u9|PJ}N7H0l2l4*B6*NGL=^D_V4gExI8-An8Q-v01&htnTo)1$g};e+H%(IAR&2-gnm$_(H(xca({zJWVI)Tcj`M`}F0$ zJl&_%Us7MEanxfH@RlX+0d(r?lpMVJSe^oBL3q$VNgt}A#196yci?5GZuFg!mt4?r zlTZ{KIf&OE_%`^H0bhgkU*IQ}r#`L^NJ|0z`h!h_w$7BHzn6Yp`h@A*#V`Ku8vR@K z9h(cy`g-Yqrazc|W9r=cp6O54n@|t>E>g$KfYGPyF>#UPnL*zfnF@Zv;XQ<>DbIeX zf7@*6FBpJ6Xxc#C$V(~kDvv3PDR1S1c4aMPBxS6=;8o^we?eJGc};n&1$|68$vp>s zI+Uy2|4^380#4<*kWY*9m9m<0SV&G&zViQ~l&O@#>OxC$`mwUxi)@S;`lB*n9p3e) z@D8?faVD7zNrl?KgmPth~Q`bOFp z?MH*@NA1bGjd%O8{mA|p0UrCFeM!Gb`?P-V_UY^d@8&!L&GNF)vY_`8@}GAh3yPe`Q_e8)cN+q`SxV58v(;6(oHJ za9b!-**iT!`(`1Y?#G>mC*|jC^i}OL9iaCd@NvXVAv~q;&7z;qB>pUXwI^>k;>Pn# zL0k~LQz&B*G%VwLHbHxhvbgpm?MT{u%Pf>4C-%yuP-}nB0bsqHeK_7U&ezL&cCnzqgfzTSK;&c=VALh_6aEewzX0Yi<*R>8qzrw1^#j)5m#krHsDpldyZ_`} zo^^P4&&hL<>MQaxgZz`i8AFtz9#DY#Zb@0LbM@iDwbDH^|5qanxTC=6|9KKx+{f`; zWq;^t0=`p`ltJhNEnA_@|K*4v&Hw(WM4#G8T|9MX;1Y z+wrGu!g1&LbR25Ga9oFM7}_edIcS^ER-rvZyM}fTZ4KHcoD)L!49B>32yGZoc<%bEI|#Z5htX+A<<3SKEd2 zx%0R4xbu5}c;{;8ckO@LAe?WtU1;CXuHk&`dNG^+6q?dB;Tt~0JH(k8q#xCT{&PW{R(!?j7DKK;Jr#eFf)S6Vl&Rr;4rC0M5a zDYY6{*DmYG^O*Xn#rvfJukV^Xxt44IpZjgDZPt_iYSy2AY|X*fk1=CiEhqhFa5N^5 zytp1XU)y%BE3Pm01N(+^xozJZUR;A*U+jafNqflW`r|qjzqXL>T4I~qm)vi3&2n$@ z3^;!$pZE59j?#Tm_bcZ@Q(MwPeY`*4+E`p~>`$(D?q_PRaUF8c(mm7nX;a6BdzadQ z{vw~e*ap_U^=|#@-)9|LUnhw3e5vhV9a=xunRTc?pY>!Pv_7qC>(qW~zp_5SNZw^(cSZ zr{pzM&yGpQ{}p)A1{MX4xxlME&9Uh@ZS7~S$BtWN1Z`^8pJ!B^ORQh#3)|7TLc6}U zN$Xiz!nwkBa&B=BagEo`rmSI`Jz~zajh!RhyRhFluek2N1HA3yToR;Rb_276aEQ2Y z%K3ulIbi&MTK~Vc4Y-2f9t3<@#$GvK&hT8rgzEp;<{;kxul4_G{SVvqJpCWrQIvTd zI#U6A!7{<;|Jr)SH9PI-|J82c{U&hky?VYjHT`fO-|nSOrQY1HEkj%caGvAbL0U9$ z?(>%9-92E>OdX`Xw&xn8iF(l>Eua z|A6#qq|yoX8mo}Yfe{z`eDgL_02 zd_ntj()_%A?h(?Ik?qUMs%33+kOaIVmI!Z}FU z)V`_=YJYwgJkDG8X=P-`f@_@Xne(3On0?rJ$?@V?bFQ*ayJjjMJLc@yjz8C7Wozd} z$DHHDIn*)d7;??6L;d^*4(Cb7vvZ|$qVucsuH(?P{7>>LYdd!2#j~28yKGN-p{tOj&sD1nxE9()aEjcqV|OQC9i`O&<3GWk0D*|tCLstgU|6~lwOUU-3eZ%w7xp;bZTHDJ+XqX3l zOW?W&s7vV7Zlb+Jn~UeDJ#$_e+@23CMLF6}w8Lm)(Qb2;{_#6c?J3%OJZG)_PWz4a z6zw`cl203pXR$L-hV~n6H=e0Z%KJv@D?NB?$|7`V$I(XPImVDZNM1trmr0bReMNgm z3ZD9&cwd0NE&8N*{&OX?=+B}(L|+ztU$mv@_oAPQwi9h6`n5Q3#!;?wrt_!1Fxp?V zvt%P)8%}ca#6pAiMeR6az&(ecjYmHj?K95pX`o>~@7ipf)1LsVe~dQ68syhD6y&L& z^bf#yq7Un5t2LBc6hf zyngNay61wom%*r>)9 z?;c^zR0;F{a`J8`PTvmab@%Yq6O{Xv!z01#n&KYgR`R>2?_Q(&!n^bd^#J85{ZBsP zsZWWrnrljcyxZX+-Y<_=&JK^e<+M7b&l_33&OlAT;fqJU8k z`;;&U7=2P)zdoQ}D5oj6DYK1${(5}ZW=?WlD+Jx)(CAv`y0sFRTC`g?%5xnGg4aDu z*SmqpCi3FlIzvgz4*a*g=OOJm@SdF=K%06e&Mo4GfmdJifsEnngk#jleZuF!Z6Tkv|Fw7~F&4Z%jE7czs;Uv!8a&USWrSt$>+cHhN0-ucxz)VaC_ zWjd!jA3IOGPv;!#dhk~uVa|8oMt?}>a_4IAhG|58*9UF&t`V*qt`n{$apVhv_dDJ{ zg%9_?yziwgdE5u^UKrP}n!vnCntK+ZHPSsP*TdW7b=`6ubWL=fbbWL^bq#dAa}8|? z4%bfCJJ;KVj4Rh<_aR&}T|d{;2Cmnx&91$_QKomGxVPq->Yj#o1As1m-F7X%7D!lk zT`z;gA0Ur+jie@zXMigqbNGMXACa#YaeYbiT)t<8^^x>Gktxvb|0+F?W)7kab`YNi z-aT*Pxp2?Iw}ejbDDf;w8s2w9zyF0aoAjOF_J5PSXZa84NdXPs9S{!u6zXpkX%B(* zo|3KLZbbQ>*Y}Gq?>Uhd=ia^WsBG$PjDlRL-j zv!@L+5z7PtS;eN;QFBs;##8K>|C!;vAj6{wjyYg2#0R_{P zZ>keJhpQVnPdi^aCs(0t_i)r5oV%Uhoo}^^eMy>gz4o+P;E&h8#Rp#JQ|IEgCfHij3FJs`EeuP$td6d zT~&LGwLpDhAMK-V@K0od-mndv+IKfWLvzO1Ab7b(K6xoh`Q7Nl z-tBOk_n#x}EudB*v>(s8v||nt?GNKT}nC2aoHLi@}lgfe5Wl}dw}zX^F_QZ7aFyt zDFc=SuQH$eaqiP;zfj&&&eP@=vROFCXlK>Npbhsa;TCb)KxPr=T=g;ZxQD1;skUGD z?40w)f?vC}bDgs5E70Y9sl2K@s=eB|@-Dc#0IU6^72lD81YXqfT<2WBTEd$;oNL`7 z@?N8FyFR*>xrVt=xt_Vsx%R0Kx*jH{pQvA|6W)>s;#@ObFJ0$c_tY8HFC$4)e{~&J zH|+`?>Z3J5?>gFtclFvgfb(82*JRh=imY$0%dX>vfK{(_{nl^5bu&oZ5AanM2q>xX<@W>c0W);n~h^;PI|1efZl;Cpd;AN|2YUK;s^8b)!tr zP#yv12fn@k!+FN}IUTepAHU3Z3gYz_abEBa<6eX>2*(Nj_tkXrJ1^%4);Y`hvz6Ib`Q^|6js+ejV}t zx5qhPR`IRw;r|B==IPlA=Vs4pPeh)cTduT z#La=`+oUgqmfV!3|7mnI{S@5#d8{M-7`W>sVoiny_Z0sjZZ_>&3mAPc-h?jqJ&Upa zr(pe1CsaRl@3S1d)X56Z1bt5iP_Fxx>Y!B#XUJ10z&G)Gpg+ia3ex|yy`VKU^rWSI zFO&BYZLpeht5L2xsCwl@;`MV0!pmIJra)(Q@|}dP285H)Rgw@z*bqT~fyX1%nRm_W z=b0XQ^~YQezKi77-*XsgZFnA~+?>QsfQM>)A1D40Wt9bf74f5q(b=VFO`t*D;Ue{<-r5ITB*phX?^v2Z+)!vr1h4izWiR!PwZyqM--UGhp}Izp zeBL|yDhNEIroN}WPMtCmTwju=9;e>vy`gVF-vZ**1t(LkbErC_`l5C`?`c!7)JMy? zRlQN2(|OwaMb#zsMfFZm=UC@!^-bqsbx3u`df>_c&)NdjVYLGmfKK(>TC|zx=A4JM z@3|g0Kj#3ayu3&Kc=k6f>7G5F3odosaN5cIp3(Cx@^kR3tHuKB`C!i?dyd#M$hnDg z{qVeUBHBIP6Fl$iIwvpP$TI|b)J5Zf??C!qzH38gOVTFroen-rk>>iVeyqN#%~v13 zXV9sxt8ZRN6B9D-6Y=s=+(|rjCk)gQHSx~HT9lhl-nN~wDY{ddk5ZIQ19N=pbizD zE5SB3fU>mlsN=X+YS-#c9_=&Qq_hQ{BRDs=hKK)%Q>T!ZE0lMTvRi_875E*CgP~&v z-y4B(>|LSW`;j(*_l~sbGRjB{OgGB)e}r4|{*XFJ4POs=k0t&fcpp&j{-?6{GW^1~ zc7xP>TURxhU$hnYf5)EV)D}<}73pu=OOU+f2>;`KA?3|x>}};e zJ@hmpi?X=?W2}7dp4A87E(W~%!S}SuJCx^s^yk!94$Aj`(AC?n1E)R2JIO|o*L%*? zg|(}8<=g)k%pJ)*N`1`%t^nn(rd^s5FE8?HonC=n>+vP(_9}3<$-5oiW8o(${CP&J zEcC=OMm;0E5!$N*=lwl#z|M!B1@Ptl7PX&nA>^a044;&8v}wAgxX$R8n}v9N zeU;h&1DAfl&B^OJa~!Xf?FShkbiL6iUvwp2-vs>{^jT1kalUuWaII4Ras5#@Nt6p7fl>c( z?b6pkp9A$D*NX?#*+9Nst6V!=&-8I{jhn^y1=3vKTxWvt>i;IVcI5}|2z}kPN?!@r znbhE}=99AI#r4p^YiecQ3(+UDA4KXh%gue+AICOUTP&t0L_watF*T5CUdOu1ibpLU$tuN_lG zc)Cu8^lQhWGrt?uktokkgs7ss@^d60DN zK-zvT@vZGfd(b%O+)tYJrQ?hlZ9DHmr}~5UjHHJa?MTXwFGVNFl`Y_>EAQ$x%81&9 zv@f~O?|%^uNwh-Ja?vj*}aMN#CfNwI+g3B@}+kX zX;)I$s!Bgrf07s1PWO}CLwcEa*II3j?hzFRkG4hq5VRAzhTR9B_D6MH_j;;;%l)9L z@ajI(1@gHMHJPDZ+WKpzv-P2UGr+OGkpE~yUczLoY( z_levi@=jglo4e4e{<~6sX&d*R+^cfm$-5%urKn{=w|0SKC@e;7AP26~<>j%DBKY zk~Ggi4&d9pb9wohx|u^zUL8*SW!gx&QaRXj5fh+uGO+%Kq<4C#11^A1`<`}e&s1o` zww>)WHE54plojfK?zyTL4kq3IGwDDbYwx!2`5)|!NQ;HejI`k__$d!f>bdrJ?{@YK zhO+2T>frqH!Yr;yerTC3x4kqQ*Krk9uHs2 z$I8vlC(6F=A9yEr6L2Y4+ozPJZ^5I!Ny^^tdDI1GEbXEHW{`Q#dFmi|tgCUr&V=rt z#R<(fsMD74S&B5}b7lAM$Zs9HN8-MnW5c}+?>t)$jJ&u`x-Pl~x)!_kxh}dEx=y-= zx<lIOopgN*$#<@yuBEP( z?#+gDNA*0{GS?~BM|DltH`lO`{^%O%TBvU58tHoHnyLKQm*6_*I_R3J%-RxKx&{*D zOxNRF$mcz>#b|LzErOBLDD~`j3eM3#rr<$DK{ZVoc9a<4Zebu zJA=GcX>;$wdXxD1jM1MW8Iz2^^pty_K9hz#-gV`jSJ%Psy&==+E6zEtQ|biXjTJ`S zE#kW!ulk5(1DE$@4S|L=#Fqu%e)_07Kp%Ka4ZQc#=oja@mW~g z=|}#f;2F*PUT`#rM(>we4vu+b@Lno)roP}E2i$(>tWVH3)B_sQQ1)tYuY;e%#Cs>z zHOkX2S%`YLN1Xqi-h%qLOk51~w1lUo@SzU2n{wxa;|5QLg|0#I_mGsQtaId7KYRf0 z9pt?Lu9Bp?mZjyp2lQ@&&X=e+dHIPr>F-S*=^9I#G)cSVx8mu2EAl4o-htwMCeoFH z`YKJ~(()#RNaGX6!v@lGlXoOIAMwd4<1FwKftQyJe5?Oopgxs_@{_j^-|F7ZU&;#3 ziTYSNhdGzzg^mH#we!~2aLR`k?FY(p&e{GK#{Z}*=U)G7$p8G90L@wG6s5rD8mIk3 z`(AsV${-D)+qv>Nc(M@hdhA^8oT%L4UZygSYpAl#2w-MHi*|w?Jmp1uiFTBn;Lxt3 zO=c-HxUZ^RM)~e@(!BpwdGZB#ltbQtuD40ke&ao`mZ!|K0a}$W4SfJI_tfsy~D0 zlRVS(4xtk5v>EvE1pSed@~sbqz7GLt4Fms2w9!3iC`3CKB;Nfz<(2J}>v<{9QR%}@8VK_C<$C?(sJ?bo{#frZ{AB2--bV;Z4d=)}>g7dt5WZ~Vrx};COyOZxLc#(hU&q>^1(((c$4btVET1BC8B&^Saeg?O^rpU^;2_&pT((Zlt(wv^S{p8cn+k5VJ5nqNp zL4rOx`Y!6T(-mC#=u5Tf2l~tC!?S?C8w1|AXs=TQ_oVmoTn1k6;qy%03Gihm|6y?J z*ROp{f1cln*Z;@!gZl0q;#+?l$H||h52UiN z9f*j4zLzPta12kyp4LZR%4!InPl0_2y5pcB7J43o)V+XC;^TWg&wB9Mk-QH{ z^MBf>!t-@tj?*4}$v=uZ=|Q_xq`VTq%Zq**p)-8?esqH;{Xp(gmVO}mnCKs(pUG+B z?5FyM*pIu=A3Xnci*$WGCebHukyjhBz9{;5=pV8L_{Zec2gI{7j)VH(9!EaMnd8T? zpdU%-9C>7b_95>ac+mGmzZU&hv`eQ4{|CUjwtNPRXUBq|c@-MYnPD>buI0U7)d4fVCMo^-$$N?JDZa+FaCowU;RSX}eLM)@IWWSZyuS89Ul_w7ELv@NScFQlCF1a)_9;qEzWZw}HfNBC|FOl8vkpo~Spxt}_Sa6%Yp%EtRr-e0DT zM$*p3_>Mov!_&J0GV;7h**$?710MHo%fiE2;{9L6JvVEq$c|aOJ^0lzwu6`25i2omE4D z-Ng5w;M)bvCFW3VVESIHpgiy4S`MvuNe`z#{RCd`w9>Dmjs1!5jeOsx{3}u9g+6^c zs*$e(X_MgDJGE92Cog-U@dQEpnRkQbCT$3L!oYo%GNqv|Pf4EE$xG0=hWgn|Ir5?pXnk-h^C=T54}Z#2o4Y<< z%8!G1Dj)U-M%||i`I-`^T&JIs@}@FvJK}Tj-h*dp!Upna|JO%MzffgM^)cn$Cjt61 zF#4j+r_6)Ae?UkI+yiJ;2GGw}`BwjwJ$&m|mI{2OfKwKBe?Xaj0r`W(xrbm``ly&! z8Co9nf3qBAUFp{+S$?EjTd}y6!_^VAJF5d&2So_l_|<#$gR4up!FNf@QpZ((*G{bM zx*2Jc!0(*ozJYaV-O0c8GK6pI#C??N1a1E6D8q=i{^eiWrS%!)Jr(KthPiKGz3V@x z4PKbdz&MXdV%@?J3{# zY^|reCX%s9#xk`YLuO*l%0&y&fLa z)AUJP488g@s^4{I&hUR6=7Lk7MEgDMy3#a8|ZY6#v<(H*i^qndI9{mYpD6a)@{(pi#jqAYs8_&L!E&bZY4gWexLZkye}et1@jKg8SkX6Uj`iNd)~8kgL&BhpE?YzI-GZYp5}cl_$x7o`F~OV4^$N2 z@-mouc>(|4a}&n1Fy&4J-yCo}r2INO#{=uVns=cq0$M)dy*u@~g>>)c^iI#sytjbQ zbbP-IZ*i0xLzqfhd2o&aM+@loZcX*!-q2k!6?H(lb11JP&nUt~;7&ouAMlclveJTY zDReIb_8sDv^Ynkg+E9k~?E61am%%SDi+S45>_20ur*LSr-`L;U@V=1vugKpwQ9?hm zujMD$pX__~H~XaW$r@<#E=Buk1nKstbI`49VjuR7#Af8N@7l+dkH!&iU$IYLqpVKE z*?+3@ot?gEKeTU#!{-Fbv46U6ZvQO+4*OznaY54w^4x%*hV=I`(Ci&xaXe=bpA}rc zQGX*SePJMz%j=$sWHE?Ex&$qI{LpW_u-RQTgUv5Y0r8s$%k+lZ| z$@852d7x80bQpO`5VWs({@i=s|0BP;ymQAP;GIWW@Scuv4mjuBsk~o<9_KCRGVeNj zA6R*80?aO+u0La;Ut5y%zH?Jgp57Jb{cyR6|BP>KXwH3^d3r9$|0bNucR6St1%6)R z*8^>8&W+B4+986(d(J)+0NZG%2|Qbpb_ZG}^Hh`QvoW;IIqF#bQ9I#L@T&X0 zOuaRyOy`Q}1nn)OsTaqjdZxOfHkNYmsPCD35bB!q!KJNUeNlZjn3cK!m-nITv!=eQ z{<)U)9;8+0-T9$BbgOG>EAniKbIojU%ZvL;?pL`V=Dycf%GF22y)yU2+>6q;#CjClZzqtpt5qS6P7E>oJfW5=_VDP)ICNGoVCA2T;zGY~yavS*7 zgF?PMnJL3P%aHGn`T~6u<6Yw1 zn{;nepDXtz)$y{?2JT(z^X1;BdYJo`7kF3q(%;MdOaD{DeM@;c&a(?Jr3fP+yU+yQH8Gxi*ic{wsm&0N?H(bPQ*{CQlFMuBYJY4X$p?!4t@vir{`lFXFTv zIUYL$*9d%{k$*h>*#GGs8cAEhllFow#1$dE32{j%FDdxlpU}s}aXXUw_=L2gr1c{( zJ;slpcGOppcWu$yf|V_lN7_I`PvW)hRpDJ(BL`s-!T)Pew)1}|8uQ&5808V|z|;(i35c4_AtZN`rX+K{{RylYv!YlBwC zNd^OhiFZD79vVd7a1IDUb3A>#8?XGWef&A^%7V^a%5}=wEqGU^6Nht$r!631Mj@ngQsT*gFKy+oD*)4?tHV9e$k0`bk5dZ>%Pbo=5psH=a10Ywjkwd%Q-{3 zwqI>l+Of0`zef4)m$+xDolRTWyF9hGX?LD2?X+5rJ)YwQM$80a|htPbI>|E4}CSk z>0LTKc$b$iDJzx`O*jUg-^gDYo>qioZ{azfGA9r!N5C(6f8^c0-4^t_&neUYMb1n) z`rOQf9&K`yz?GW1iiYMwgc;EIF?nAHeh2SUp-11Ccj2Kf@Y=i8Exp_Lb9lW(JwyWY zBi}zIqA!8BGw(UTt51(-dmixpA7#EtK5g#uqRr5~Xl<6-E~Qg{nppaZHp!CkGo7%E zcWsyY?&#mAUykQxrE@6!jes8YdHI&!ir~;cUS0Yo?WCR3yajw?1L|#(8hR zM&jh9D=@C9HF%FDqydliPJM~>E7I4tH+l6Xa&30qpHKZz1@Zn!`}qEg`qa0`vyb{D zX;;-AtB-PAAVD^8t#%#P|4I2mzw4XCxu(0$yWZ=o?HNzcYAP!zb0`bwo1|>;0{r{= zR))|I-21vci=(f$ynGJM?DVTKrmsz;C5~ ztJ_x8NoL^g1DB7q(bR!<&P(J;2W{TjdWp1i@U)Ed^U%4L`hNo4DDuvr&UXRdmcFr{ zIywp6Yrxk9S`X5${eit0g`5J+3;6S1`I6-E{#ft%PXvDzh?f_A`Abn319_@ntJ^5c zDX%4>-qd-N8I|$WwO=LQCFs+yU-?pbF{G<2Ln=2a11f9g2fupy0lo|IuH3l>o|HLT z@~xbx-@o^D>qDjeK>4yOFv_O-S}Efy?+0MQ;oAULo*WX_secRZ(vGx77KNJL)eZ=K6dU^S9myz8d=Vmx~3)JHA7*qkXJAJdK3*U!hY!IQyz+2!_yi(i7*o z0eNvg^ggpvyz4LKJQ)KYTe2q1iA{+AB|5n)C_v zCw<7Yo$i7*$FOo)UBJ5#77?`PO(QKiwD>=WV}Speczptt)0FX?CzQe360ba07M>!A z)9(5cPxZ&}90~K7GEES;Sm@HOxP$!Od$tuG?m(CJsz<B?`78E@Gl66!)3&pX&MQoq`UmGutrl$UA1k0U5!JmtBS_Vb>olYIN% zVVT5wK^8*LSEpu*ymL5$Y%SmucYBzi<}Mbj0Z^Sc3YHmq*ax|7LUr zxBr(BPW}?mSe&pM9R8QaA!rOxrh784Q05TwT_w%`>)1@ZXC!Cv+(UdmV7}*la1LOZ zo5Cn>KW$StoHl^gM!+^Ftvz|O0M~=KHGIcF=SBL@K(a(Zr+YJZNKXlU3&?v8n0{I5 zv%Ke~j0)u62_K(~wiz-H=h_^q!%)9-_ex_a3ewiUa`Ow>n_l`XOi>5sZ%_vtLO!;2<|07Uo z8&MBg1TF3Pz6_4(&}5%e7tps%n{f$vRG$k1vz8FedtshUpFPCy!KE)pDquG7UV)&z5ytn0KmsqPfm=^ha`L=J zIq`ZPakJ=Go!}!DIQdb}d`LY6sHdL5rvqNTY3{JG@a+843D~-PlN5Fay443Qb0zet zFaFHCcV4*{B7KDcYyhv$lXFR%3O;pQ_aTn(-IhQ##OsmjyW64TKgtRar!J=~_A$>x zdoJxW5A5Ul1}vZw{XS zBmFG&lx9q{BJM-zzR!0h+VcIo=`R-4RjH zS}G^)4Ln)HngU;gvJZl*1bBuJ-xNGMi2n)R{12=jp#L&7cz?uZcpC;!>*!+-;G-`2 z)LFgH;u~;#xBi3w=jAH2XCRz|)+9W05*C2}6Z%Ih^85hK91!tK4E#dVSZIs^W;6H~ z1G9qQInMmRmIUVyyjKQJpE=j2o$we#eb(kX0L`4H^@)3ndME)O-lrum zxxlGC?Jsb;1}K9xWsK-^<2s<6pxiM5dd7f9Sw%f+zx73V+IjSs9u3XPKi-F+UvyRa z!#vTzw#9?cJCU%Spx@os(4@`MdHxz@d_}$Ke|8f3j!>R!)lk~k zeb{cI%8E}a#0uh2)a&XrM$JkR0Gd`z}2~50OOgyDDW$H zx(8W{xV_MqgQx#Tq@SJZjkby>lyf}?^+5W3VCBWRbSijjP>++~y#sW=%`-W$>R|o{ zhkDm*^r_jrKZT});H?YXIOu-{9(A^S^o6F-mV`D)0NgeDCv^6E(|KD=n9ShwNq*rE4B;tEDZ9ENHBBA>!bPXZh|M1X<=Qe#>eJwd< z$%}VqPa`-FCxezEz@Mj#{iII;XB}wvoY*1giHV|o@H;O%KhGfFOT2sbs|sZ|<@pKp zHv^A%Y`3TUalBV1Zx-@7zj|iOJHH!|w*u+W;QWR5cK&ug{|J1`<&Agm=-x{FdHD)Q zwg8Xo(q__~i_7tJ&QD7_$%|)+=EMw%;uHs{oAmsCzg12+G^f)4SHaW4#q{wvBb}{pq&#ui$?P zxLJgx(COXj-jn_hbZm!*c6{3&k>vA^_2ks4ZR0)b(cty&^i|*p1E2S=AB5)R<^xV% zX7gQ{I@8B%5cQLmU|-ai>?Ux}_|`vc4?&-;*XaZLkLgpEAA0mDYn_OG9m$$Woq5mJ zH{{g^Okb}n!0iN&zGC{7#ru3gr?v6}|>_zB5MLlHz|61a`|3g1B z$B8~<`m9Z$|7;@YXQmICybK}V40w5s^fTahj_90<@fV$7?>rNM97LUXztS%7c?MWp zqi5}=0ke>PdysOqUwY1EC@`L_yG9+Y0M9$XXop<_uYdAA0eq8qFAmOq-#;syC@GsmHjFrM}|+)NF!&-)o^SId%Ud{lWc>&CnbMd<*Dz4`?*OyR`Im zm6u|a+m-hPgbcj*p^a|PX3E~jp=A{Cu7~=4>F4Acn2R)R6Z#!_zDN73^7kOpwbA}b zTOXs2ThV^nd~ZP8DcUO^X&d2L+lA*$?~v{~*@n0np89;<;Jq|-C87<7!Ov{)x{kUA zeMBG7uS@+y8?b(8)xn*CI+B-$(4QXILOjEGZi23%v}IX%b8p(UIWOhiaxX3AAZ@vO4?{}AFfQ0`9XR*$GjdOG@%I?q+o)Po|Jmzoimih9&P zvjb^I=sU5*PbdBp%AH9WuA8pA+AuHhy@b5?tsh|3pTglcnmny2t1r0q-&E(&UsGPx zU-Y?j?Nf(QXHaKR|M(dG)l-H*tG<}7OX?Qt8tOJbfa4SDDJ?Lrr|L2KQo07JlejMG zgIR>~Tu*z`kM4m-UF0bAx!$?%#`|GXmTRs4mOn!G8|0Tx?+H`K(f87|bPhrPOYizp zfAJ16buM`c*^}IF(oPfz(3cql+L5#oX-Cp_w4HQi_>kSnb1KT)+N87vU8b(I13iWp zee;#+mF*W$51#kZXJ7l0wxzt}xy1Kp;Lt{;ZlTUlj53tt>jSTyN}H6nD$ks0KiW=T za?eaVlV<|7KSe^XHZXY^&vz@}J>T*rbdQiO%5e|UeZw~i-gCPF-1=eEBi=jT^y~L* zi~EjG2=}2YoP6E`cLcaoz$c-cUhoscQ=k3Xl(CR^eKy>`EW*1!B;I2?9XS8rCI0?g zU^CJF%JMy^FMU21f`1e3?;X3!{N9uMjIz8lcM$OM5&_;<2_vYN!@%wW$4j}GLukX6 zycYvkWoZPLXMI}IFFgNwnYPYP*&B%SOy^-}ssr2u;OA4Hm*^Yr%X(&ZC4Im>R`*k{ zQsy%H;yU1h&@dak*-7h9-WR0Rr`~SxECFm3{qLah;5Y$JScpH*>U~H*z{djWOncXp z90|O93m>1-Kc0ir|2Qc>0e zet<@Od%lJi^%?i3dqvO>;JY|H`=1ulnh8EeP!H#+bK7SM`SZ|jZJ!h58=lowuRBcrm7wgVl%XE@ zh;((fpP;Fjw1P`rP~Ffq*mKL*c~4HfcL%#RzR7nibA$H9bkHB<^(=8i300^Uz~X7w zhxY)q4kJDdW$ClvdeNSAeG{Zl`_v@L_ihdS3+hs~bnXFG+oN@Hj(F{vapdbkkPg?d z#XOpW&vVwU_1e4h(w5eN_lBr<>MN`twd;<45e?vXE^!aYr)|(RCM9tCI_N_Y1n2NX z%#qaR9NJ$U#Q*O3ntbXDLB`BN-kl?VW}a5ZC_{WZ+DUnL8?+Ut&RPLihyHgS9M1I* z;LkZ*-RBBrq#!LN^`rc(yzM;iJg#2j-H8Lp?|ko^tlpGF{NOFlGYAg#sFUDaL7cjZ z^0l(McVUbm->2Y~mvy8qfyYk?BjH0mPW^Ew^`vfhiu}9io9dAzNmrMfLf&_&Q}w*< z#H&O4UykaFzW_gvy1xwWLf{w%AL^I6fb~qr3*ypJKjXpe|353A--fmqh zyrbwA`8|VjnKDlCT^m{pLCYy%!pNH$91py{(cUhi!$=jB8eo9%ps6*>GG4)r1zHo`SbkI4Da%Ta1 zm~t0GKV(wz$PD10Ox^uXUG5`(2you#S)P!bva%6>l{(HtT4C@#jljl2Jt{MNL|9M$ zxs+KKIQz~y%Gd*amwDd-j)kcx8<=qNdLO8~e9id$j`$CG>Q}IoI@E{12{f-E&hv}< z=HGy4W#J^W&oau;zA}?k~DPdgu)e{?~L*%Ftf(bxx{>^xw&=-CUo|)|BNrgzL21+t3pPM;G$B z-yT6&%6Cq_-GeUfU`P_fhpFbdPp8`St&EPf1_FR`965pZYx zey=`-Ip9NogCOO^5%(TxU7_2(>-OYxzxi9rb-#HDF#1=zSM46O``Y?Q&H}Cj!F}`iBy=na^^{sRtJCgkRRk{bR&!+yI?rFQv?H;&(n(m=HKHPWLmvbV0W*&LnUtdAF z`fhqpS2p4XLCb64a(~|a`}n$-IV<*la>+s^~w5; zI(SH2Vam|Q%KsbGXZ$JS=uc>fppSe2jv)QRv(y1#T4!S(=Uu-n|5LmzG`-JvW8n1X zT0+=DT77Wt26iK~cB5?nf6H^-pG8x@JmcuMyNT03%yZY%fY*;)e{y}K99E5IIcV_Q_gdie8U7wTp6ULV zy7Nr+U%-35U{WA~7tduqBRvd9C2xJ(h*Q zcz&!jeI%T?iqM`6J|DnSR^UA2^#Sh}n9qh#-agWAfO9bY_XO$9NzV$-Je2D?(U|@_=(F@ZNn@0bEIL@zv+B0p#EAz zLwnj{1a&xoxJA&C4V+teA3)vZqrBJno=kc&+Os_PuT$PJXy^lfGf6uF9b>@d8NSD) z?SkIlp`kBN=W}^cu2jDC?5(n6Cg?v)P`*@-RE}!{{rBMCJzM2IHIHQ__shUeWjKvP@j*3V2^%rBJxKJohP`QIDbxAaF` z3C%OfyMS$%`O@a36g?~*G^n(MFkzUBOUW$Vw8MteL_ibot%lAcaXJuUU<^2%P!r*U6x@SW?C(^?* zfjL9Edt>Y9$154f{KEE7b{FdA5x6}o5(iE4a+|)t4O$A(9^Ut~lDyY}FGsuo4ctgz zJj2qJw)UJ#8S;6C#WUK!5_gvOEx;e+TV75AU(0-yQ-ZjAz<763JKjGhc#nT`%DhLu zQ}8twx_d(R6zV!V@t&3N(y6n+ULvHS4=p78L;e54cTSk`4yC$`i`nEq4u1bj!~2%h zrEiekyO@50hAjm3xlYuH_sg^bHU)L&Ju&KU%}L8jp8oLvg!1)e9s@m#!7VSf$v+F& zm#NFtq^%<4Bkn%5oB&s9%5BMed+7chT<#BFqO7Ulx=4R^pZH-kwsh#OL*72{b&}`z z)U|uWMR=b?eO3ZzXUeM#d^j*eNp~+g1#R#;edPr>azlrE&*`8q4ZQq8KWag_{eYWB zK7A{D!pjnHRiKXa4QT*PACfLFcc9JxN%gLVB0N(OAI-aWqtxf=9`0(&@P9}A?+@hx z?;rNAU-x;vuVFOl+LjtaLl=T~Ow@)B_lZx##}n!;FW=rZaf>jHpl!yX5meiIU)R?|2%|=g{Q66Qham!qdHF?OdUKS;^>uGR zc$;r+KXoY6`xx|L*QZ^7^;ap^b2|FHYcKM?=zn?60nW2I`mMV!>^%`<3Enl~p0fMC z`mK8}#R*`d>3`a$v>TlU#xpz_z~y}s+MXtY(=$QZv%G7<^E=)*;e8d_sDc5;Ki{@X zDd0Z??h~G?$sa>{KiWM&dL`O-F>U${?RbXvyv+OCv~?lA7t%+LWJA8-sjPaJ^wZF# zT&qm$T?PGF-@>92bld_XSr*igk@`Qu$ zZ}RK+wVd{t%2;g*UHW1vV=JdCKKF^{1Gj*<0nn|jc>p++kCk;(fvcJQJ-~RN4$hD^8Q7}4pUz4fQLg%? zvULIYREF+P8HISBgtnvPtpwa#F~~jeqWoPOIA!mRq*vtKdvSOCf4dZgmaDYuDB$(C zRL{|W(zWX=%J`W)&j_xS+u=>0NcSri5m%V^?mYFG^vsL?k@`Lk=DjR@s${xeKIB!u zOxMLOyyxV7D`n)P{+7Vk9OBXk2;dKgPJKOHSM>vR{T>3mzD%Wn^Gu6(050IY386Fb zuH`}Ai@}TbBKH{@^R2DMy$1InO7g7@NWVwzIg9z!2Bj^@``Nr(;~;Tygmc6#r0+E% z-MtlUgW9RQYxNb%(B4GV#;>jQp|1<|^qg<^GKRvp|7n|o{4`zs9z}beuFLv@y3Rfz z3?)2cF6%*>>$GcZd7jMz3A_}ioO#e04$aC_%2>)vhk#cPtq4uZO3F={i1)tQIO?J? zIOYMPyrn*@Jm=lO`rIkwxhLj+miDZkz^OND6FU$7ATUSZLAkCoZK_Ob1k-p=jBWa%TzDvjf%zmEw6nXw?H8fY}`!IPt&t+fI zf442)&!D#eFs?7&{pB8>|DiJtytSDF^wXc) zX}`kYKgN3*LJGe1MUxl(Uw2Snmne4|w3dLEaCp)WR^L_kzEbk7@997ErH<6oNb2uD z(tD9VGimyw28h>}buIO91)Ni%*ZEaH+J4a22)HWb-wfPoXx&SHNK0KU0&f=DRsYs3 zq@CyK{CkhO=nzF8Bu`P|ufkgao-^o^uM^*l_}#X~22j zW)N`t8ucf?y3rWw#W6P+em%3OAC!B+`w7~+1^}~(@F8&SS1*SK{g1q-d;v7BCXco` zeG43$`oO1z-yzVgPe4`h27z;4llR8FdzMiDF!zF;Q{`7b_{roS4z1eil*gT?)lJ-A zxlA7Sd8<;sI*@vc^1b`Ti%3gvxwM-)Mt1P3%ftca{HyMwzH=y$u$F22bKgh%-!aGBqx>N8o}*IdQ-|zF9(79Vz&5nbY#Z&FP03RV z_)+wA>-;LADDa+Runz4*KSt5FfVVGx2VQk5+gqLWJA(b#cA5$7IB0!^apBpkK|DQk z(4BnRNN*B1nmV(elmS-#%s!irx^Nt5S9OhjpZ-yw_kDo`UTOh<7haY^!>{n8AD?$) zxwiF()@GEgpWbx7)yw_A#2A+j1f0%1biRUEl1VV}W=5^$ykkHcfcd`RIfJ~82ir@#?tH$jd)LN2JjVwT)<)OB9h9d| zpAvZWUfXFtJS-sSTQi(G)Gxy^sqR}ET<$IDkD=d;z8v~i*cY+w|`U zl4mk?vJ_e-5=Qermig*e-nGG2rd(~F_25_g)inCJdlrMD>GPyb2G=?8YCD}oxeZtk z)>AK+pm9FWqmvVX&N)or0ya$LMN4z?Qx`MnYAM2aqJnuT79K1U^ zL6%kSy$;M4zRwX~hWFg$>%#MEcygZG$G7{9rHH#lALvQFIUg#YIzE-VY2x_vJ5fB9 zj~DS&?oC3vb6`j=cZ_>RX%y*sC_8RjOT zzhR#f{{eW~^VAloPmlY%?h`KoMmgI(Tjh55t+iD+2PjAXLz;HNx#U%*cb@TF${OO` z3s%0Bml$YmK^<%3*XHg$FcSmx8DPD`MO*q(^3M*ypfS%{zev9_h z-tS(3_Iqvo-T|Y{KXmTey@6`r%}sf$=tEoROa52K9OArdrWEvC2BtCjwB@_M;GH$@ z1!yZ6PhZ?gS>8wE{WB+^s~Q#O{WC9kmltj6vGAgAx3+if?AqzI<9p|hc69CkPr$9e zw|0N;>v11IoBb~EyD#9KKa-$q3Gv$WweuH+myiwrTjGPHtMhBC*M6TBzSQ?$q0aO} ze*|s%mtO()Jou{eegq!Nknbja%zcKv#8v0J5$Wam?n7RE&gDhl+Ia-`we`*HMw{LM zwg`1Lm;A#ROHbfo5$}CS^ZZn2%Ktusz5q;8`cD+J`Tq>}pd}i(G4w%wanaHQjTjD8Wr)BiPp1Ku8xFE9B#-{t=(tG_9e zs&{&hu{BTcnD~!!zM^f%(H0%SyN$e!X`2J2{SB^NZn;MzZCIg33~{$K^Wf}gU$YtMa5`)DK7 zUg$lt+I%O0N4ZRUp*F<&#Q$X(eBYv6?bgHXBjC~g=bh$*se5gE-cjq_uyd$??TXr| zwFP=_x%NJ7ecBYY4Zg-xUS5D#JFhlg_gnsjHup5N&3T7~wmt8N)z0Vr7TWo=-)RTb zE~u?fyRmk{o8Z*0tesGMp*BEm%-ZL)-DwN_lX&fS%5bZxD{X?>u6x3h_Uwi5At`auc$zWZk0Rhb1ms|osII`&pmMiZX>;N5=Tg#K0z+OCp*i1L<^ z?s&dIANm72Gl730u==;`rf>gBS*vm&S3`&UX0_n45qR{waZEp?&ut*>0A(i4nZS#C zCNERJ?z^NQD8DOfE7vRMDzkf*L-|)-!1EjGLCS{eZR08L3FX|U-zwv)*HtI34{6@L zs9dN#KMFdP*_%U;vafqi>J>wQci-i0+U^_r)BkaG)^S=D-{ao}c4_Gl5FTm?$w!cu z?vQSzLy+zU=>?R=pu0gp>5wiH1!)kFPNnPnelMT>{qcLfcJ|&obLPa%+_`7YIWvzw zAN2+G*Za_S&7tmGgm$k$x%#bZGS>;le-GzbT|P*;{yAf})Z^9b)x*`}8v*kfvbhFu zE#(?u5ODhW-iJ^3))erbdg}G+tp2`KN3IKLJNL)9Z{{U+9J}hr>b6Co=eo_`Vo(P+ zPFtUz>kQWfM}Xf&JG(}l1D>jssqec6*iF7;(|tke>t}=oS0P}6$nqC_dfuDgc)zE9 z*Q?UbeoN!i4u0ePF8l5GJM8y27Wg{SHVMf~0n8lgd4}9H%A(={D2qk%_hILQ>oK&{ z0|r268qfKt<9Tiuks}7}))~Cx$@_^hlNFp9Gclhq7P2#jk26Q>&+iEi&x7+j@A-A} zxmOQShw$7uWqHW+Da!nAKY^bF@a1`Wp5ONdI(}QbP}U!ub1Cz?UJZWyF8>F9bpUmO z$;dGYxff8@j^`OXhf$USzVzeA1YZ3@UBZ|Ezt=tB;RHCkB9q^A^##x8s|0_S$n)E7 zY=FN()Ezi=5cQKVV84vXe8qdSf%4DjCuMP-)3@O~x&u1Sqt3I=cg~B>ot}&BJgEP} zm-#NeP_wej) z=SF=Z&cV*n)u`{B>pbn8?EI_`N1upuc~jt=mz}4bTlJT`2Jb!c^|3go?}Sh1Q|DrR zKz9;yba<9Mp@NF0M>1mAlsf-8pT;p4elU8@$$Jke2o&ev* zb{oHKA34@f0<)00+Zb%m64HleOm;QKUx2IrIb*8t0;gZj7#;h_aTEP*-pzQlU)lf@ z9W%WGnA(hK+b3%#cn3!>>gu1eZ|=iW0>-Uvs~<~W)nC;2+^E&iwB7Z2jeuACb{8_~ zC!3Cp>ccfDn*vVbr5y{Nb2krqp0TH&ExK>@#ktSYIGgIoWdC?>o_(So|BQB3*H%|I z2HbvC7VX#CxwSKEuWkSyZQ|PTUosDBqt?!@ExJAM?js-0y>`9Blxe5dey6_lA-J`F z>$mWGqpe>%x8JA=*X^F8EU()2ZChn1 z!F@ySTZ6kH{gQz^ZNoL7A0)Ykb&WcLdi8-(etku*V_%V1kuuw}9{JyJbxk^f`~Kwr zO#RtB>l?D~KIPdxOJ}L?dRrfsZCje;dRQNiy0Por(OlJY&v1=yZ~N8#N3pp2`+?dK zwgKx}xDRl}z~6_YpQaJd?w4}yZfsk3o#R&g4%cFY3G|aQXYj#VMad z|Ah0M@_Z$A-w|9r8*2(>$!X)synlmumwp6~`nGmSb>L*o1tWpejx`$Chm7|_Tz?0* zwy;g`={_^ha@x$jXQ`<7d`4fc4W$pLTc5hdXI=vCGUfmB4b?t)gZf$FPkmllj>DHR zvBk(Y-ZnRR_vzpKTt|Z2^G-dxrw8w8R^)V_p7Dg|>0@IBVL$3DpVnMGOSMM;zR2H5 zy%eNtw1Kg~skpia&$wLuviez#L%dAcN0b?dXzb!##>_iB9|DKI*?Qz<GHuAakl0U12M$8&Q$ z+ovS??)hv5Y?)ZBm%ud-+$HEke>d0jdfflH4E!nJcM(_5{&7v*J-~MnS&~C@6Mf*B zQmJUa_2e77nwP%b4*opQ_e>yTQ$4q52QZ!sW$fv2`pdJal%+Ax+TwnMzuna94}E>_ zc_{lAUj3a}?QV~tsSQwDUq@utE;xexqulHN{~0>k_vBYTcLCcLd|APvt#B88VcV3X z?N$J%f4&#>lqC`O@~Lgnb5yhgYQMWmU1dsvEXK%^6&17VzFON?-+p>{H&$0!nkWZ( z6=@TH4o>K&v1K_hR?RbT}aQM)NH%3{(F^cQ&^uKGE zkAa=Y^ZGEpJ;3`r2BoRHgR^A_MA@VuKgG4_5B_sTL1UQ(07kl_e*r%`U4uCcqu<*ubn`>!@*$FgHuJFjEZ zF|U2NGBBR8YJ9HmlJCI}@ZOJP9I$6*l_WX-GXoz3n1Vd}E*abFSy#SKo_pmPS-vmY zk3V6Id4{Vo!=A}dtvy}Occs`cz=^v!B$l|SUp;6$ z5458noL{tYJJ)!|m*>FAQ%}mBUw)-soGX3-#+dMTsjK|j&-+qWxs6Hp_dK+xr{!K* zJa4)?FrFRloEi@A&Uwzottr33b9ZELZgfs{5>5!OFM;vQbmz(X;Bs!vPw@bFsRJJ8 zW9RRs)GJ4RIM42}b}n~(xcAzz=-KK`;IkGmo9RpE>->SYW7zLS^!Riv#ek;c$+3I^ zo*mD<;X4uisVup`{S7?bqP?Eerww@i(|&;O-PG4Npie=6!994{%d`H6wp^#dUt}!w z8pf4=kNwcD24DIYZb3(%LN=bY;T7XqU&Ku6T|qv7_oN8VXSfeQrwerTZ)E4Xk$3zJ z_?{xu8gS`H`4@iLg69m^DexW%?`65~OZk!ieJeYI=Ra^R2EIFZ<1sJvGJ*FBe7e4N zzgA-Ec&6bq=BS3uBkrYZ$$L$U(BX6d@b_MMOeaQQsvd83kp5iR-?Uydd z@(4bbC_8ZO!J7&n`nxZ4@9%s53jBBQaUDJu!Jjd!{keKxrS_Yl5zKv*89VE5B6{BB z8S)asgZutE(a%NEQPev|2bgcb>z=_oz^#O4Iq>~T-Rj^{_qjuVr$MHp;Lt`>1U^5f zFNRX4twNwsNnevb;!x=6yKfDRjNI!db6s>X<@KQ9KFyt!Da$5k zxUb6ZwR@Vj@LU`EqiCzVTr1JmwJ0ynSZNC1<7tb3!oWpdRm%J(m#59*BimKROHA-5 zc@0ftWcLB1EoC?PuKnN8Mh$3R?FGiI#teXq{?Q)eI$yhhwwsgi z*a)7r_h^$@Lb*P+r?j26AkQoAP2F+8X)iJkRvW_!#)PuCc6ObA72bltxwf7_9rrl9 z2G_P?jO!Nayn$EO?el?kJ-?swKG4*j;(14>BajQ(^q097j|INV$kZ7Av?B}$XD-Ty z(N`_y1wOUgxOQJn{iWn-3-Fw!sD0MRryosQg7yY&2dB9@N4oBhL?&%E`sI|x{eE{C zSC_!KkUG5?yRJ9l(}#bM)OpmG27qS=FoVF;4gBTd=LWpGUr}95eM$YyeTlz-TfJ=$ zFtwoZAN_KS`s!oqTw#orCycpP-2X$FYojWmZ@+zcf%jYJ+UXel^n6)w03Ygv zt$|UmO9;+PAR_EKk2pK;xw z-e)|4>w+(+qrNj69#&DNP8F^5r-Zig<|lyp3q1P65;HyvFeW^!{sZ_NKwr+KT)$O0 zXzroTK<XVdScUIWI-Na%Hhk3-=6i#g#Q^#+6MJ$RpjTz==q!t+h|_ypX> z9Y&HL1kRYmBhb<}=Q;OFq2u>cf80KvJ^LgH^iM#eKW%NyW+`YTwmxmQ8hHKx(eVm@ z0vATR>Ni*iy}iJ?m%@DxuShqk-G4EL z=iZe2E&dLi`uY6+hA z{1o-(lJA*w&TZPJ^#``)UZ14)Y-Je-o_QpH%l8@Z1IUj9kJ*3;!kcG7WF`MEcnJlk z=Q(%=L?-b32R;33+8HnN+zWoRRn`VBf-?Pi&PP|NuYJ@xDuO)ctBlZiN#DHTz9Rg3 z{)1;f_~FzrO$4z{I2ODrEnha((f~7-OfPV+_8r zp$Fk_8TqlZp^NjphWix2KjM0a@v?~f5bC;SbASDJJiZI=_VC{UUIs(6D)L_?F9i6n z;i)aSQ^1F^MDw2uS(NER@G9TEEN^*I9%ah`?ql$)TwQsmm9-kQ^HMH;^=|o`L%lD! zFGC&qav%Ri=t@&rr0dy`#w%pw9k4x=BM4XIJpJXW=IK&ER7Md8cV__Z@VB zo_iNQqwPl1p1*Vb3Am1w_2*e#umHGMBX@Q3F99D)*+%lzb8PRD@G=$trqh-&;IB4i zaga$F{0*XT%I$-*AmlppwtzDu&7z-ba&3<6W4TrUHU;&bKr0Khnvot-cOZFPse2TjLg8mR zc>V3kE7014Y=;;}8{lak^-jXiZtzqBHaTO!JtN{(zYcZzYg#B+nPd^QyBv{;A<@7E&6vL(vW|fF?f!$l<@Qq zb(-?**vle^!7gE0iH2Q$gGe&nJ z%Wc~56nRM~&z6-ohxc~KR}9*(!0WzB=kVCTM$(>rkt6y$(kvtM3iWmXr!3pJ?*t!Z z;KTJ?LFya-j6239}85_q^s(#LO1z*(-YKV4hypq}T$xCV92=(;m4{0u{u z%A`G{bHEK?EM22)0ng6Uy};+(ZVa-q2Ktx$hWh<>9l473_<;6{1AcYW-@)y??)p}J z)wQnQ?jW*E1>a0yw}Z0?_>)j>8#M**V%nrPy!zW1>nZp9looz$=PJN{#l3wwp5!+! zA8l&Ce#o=m#LDpa6#RaN%h6uD!Ce)+Rlw_B*#YGF{hUHw`^$du`?rnuFxK90xA8B2 zPxr%99r$;gbswpr&)&jv0vz8F}7Hz0K6$!TYxkKI(yU6tJbpZx0^d`{UqLmNCHj?tcQ`5x{KYdJH)( z0Na&(-^sD0X2>^(dN+WnL|Fv#Os8C(d?5HgC*R+Ea}K!5*oz52&aqSME5^($>VE_; zZ)o3b-AlfEq2n^<9Ao`~kCW}~IDH8GRAg{GxfkO!Wy7GaE?1U$>IwbU znmT`jZvgG$o{Mgj&ji<6U^)V?EUu+pv%B8cCa+yT7qV8Q4P1A3MV=KAZ@*K1!?*G5 zSt*Uc?KfLnfx3rwgWb^hle+DJQ%@K{pZHCGA3pU7{6W6{fXu*ZThR9J8eVxUHxio06Ls`+E$QYn-0F*v~?PAYb$EN{Uyf2eE2*_d6dlum=@#>qPUhHl_FC3Vy%HqwsryBhp{t3N>I>hPJ1_Q?zU6nM%2 z4Rr(0`kPEWf7?Vk{bp+?Qx?y<$jLa)1`odLzLUP2=`*orCVw^jHGoDd%Dut}$Sx31(3ghuJ(RQAD^C9p(p{0MrGiesXhiB4wwyi!w{eiCM zBY@T4;J({~ldjoY-B9}e{{b%|ew9lj^zc1z5Wc06%CO;G!g~9Rv ze9_3O?L_-c06z4K{KmB;^z}L9V;<5kp-)18oHii+asCFVer-gJvhV00(Wh~cI#;O^ zppNz-^|c=Gp)C5Fw1sGQ(Z-UCysY$J0RHro>F@cKI;oID`-py^Lcqmci8vlM3F3^ImDoX$23;Qk*>X!??h2=Kbr!bsyI_;OfmdQHRXKRXc8Rcza0x zYh>~ao@Ve@hlcBgyqm!Ffbs<4;6>Kocpu*(g~QKf3`a?w&REnXf2Qmj1}0Pg;MrW7Dzc z*mSHqz8%lsK+|`{cOoXTjewr-kMBx#`bmGf?||=(K5G5w`W1cWeE;;h+m616+Om9i zw5|Cr`fllq*WYgc_&)m1_>Sq5cRx%E?iW+;yWu-+|BU565B%%X_q|n?_0S55fiA*z zBJEp%I-bupoB9WlKUPNOGU_Hn)(O;kf^4_QtIl(3>Uf4pX5hvMj|``v6`BoQ4Zf}* za~0r6(k?&otS?Fb&^GQLL&t+X7KAZR(*7^#rysZ)PdZY$xO(1X3GkkS)?siNKl%g7 zvuS(MAD$mMDZpIlSODExnXHrGA5qEQwV)J7P$xWZ2upiuZP0V5ys9A zikDGG8`?4C(QX|VezdP^BlFy-1@!%3t`ER_k8hi_8W=3v(K*q%b-r!K*ch657WLK8Ik+}f)Ccsk`cianod~uN>JNMJM_NEQ1z<1^V?-*r?s9Ov=@!@Gd_?$l;z*isgwUs^v&q~VH^BjY* z;~2{UfBL|kW2VE0@BMA+tw#pOa~SX|;Ms4$9Qbyub*3MUTXAeRhPF0c$E30pgzjbV z+^6nL@q?=l<(I)X2iQ-z_N8nhWf7FE56*Xg{BZ z%&WLoMP}#Z1N8Y;>OP}xYWOnlpkF9>pphIH=h#=^NJP75fp!C)3xdzN+?a`yz&R(+ zqTKI54xWv7@QgBL$$}j2zjaS--I$C6Xm#O!EA91+`tFVB2cO3Ad3J((?`uH+Ddoi( zdr9fL?c^CZ))#!nyA@?DT!+?|v|&Q>6VivqlN)Om5BkR8O@O}xjPrA}$2rQoQkD|_ zJzGUPaRj*IfycN*V+l{d$06#vzukSi2jICU<1`UGXNmDvmaND!2i*G4E^=RovH_IU zhPJ-9ufU`4ZzwQlX@maARSZ12k-0D9)IHMr>h#AYrMb*WcjNiSP-dUl=Eevb z)3=+vaGsYz!@X@2p);5^*u%Xsdfj<8p3itf_qe%dE;Y$LY)hb*6*>CQP7Ix>_(IQn z&<7riy35GZo}V4r9V7P`llEzld}B}VQD!W0Q?ACg8vkn?vT?^7xEg2Lm9mWB&q|67 zjAy^N2P71H+LZNAdCu@Al5w;j0q=Rh#uV4#UYoGKE$!2Dz+qhTB$D>xUDP$^He&$2 zi26l=H#S#)TO!^!eP`OM-;hdWeru;T-qtgTjf0N1CoiQ=Y+(1p|8it=pXzX~WylMs zeTQ&giSclhxu*_eU_bA(dt5zVKNjB+ZC<@;w+PxTCuuY7wUK)2_xjOmQSN?K&)1(p z8+z{lWzwgV6(qe&QlBpcz1&>agIAkbYVP|(+kLU`F(z67=h>jMsjIH1pFR)roX*Ht zrObYbL-`7>pOeaRZ#>a3?%lK13m&IKe_Z=D zv)sV*m+Isbl}{< znD^|PzrnYP`a$sLqrSSIdVsQQgnn%LIr_Iy_CWIi&k5k|UB=HzaQV&6MqUS=Qz5J0 z+aS-`!Lb5(&wfcu`WJd7kt09XgFO2?DwhI`PhfwhPSs5GEA99R?QovFf%J7euD`<1 z2HNftd`5i-d2f8b`_X3cxIc@mUvPDfc|*S6er;D@@LnCI&IaTNlC&ueK<+N^r7cH0 zQEjf;Z7$O`ooS19@Ee1q9r_7vtX;@{(N8%CemBF1Z8!!#D>9}xQMUkiv=P~U#_nh{ z){ikCIhTT~1oYnHs$EHY({AV-pzIKNw}Er7qPFA5)SC|P{b>{ZBlV!AolCp7Hn6_H z{D-W{qD{$lYeDi{Z@PZfCaf)~3w7thtK&Bx<*tLZFKK(ywxiuw`@D7}ZP8DWQ@fJ3 zq65gCihj^0ypp!l&ZNzH7(D6=7z=Lg&)TXFk&IO|R%9~rXp7SJq+QE+SN+@Cu3bB8 zXLikU5V#6l*C6A4=qQW(^`5}nY3dqRtRK*G8|+K_*K_1zBS#|IrZ#Zy|L+Dro?~~D zy80`P=@?2~<3YP@(kp1Px5PMl%!5QWDMof3dY9oDuS2wm4%4d@5gHTq$-k!PjeQR<{d?jZH`wd%Wc ze~!LV_hYz+CVHQxepBtb+JyCg)&j;oJQHby=iCpYp1#&*j3xc2>bLqvjSumRv&r1+ z+tqKY-&9|%dr;hmqo4L$?gIhvQcv5m_C4+T?kREqNHK7@H{@gBBfyapc>TEU`K&{l zPn+4c%Hp@r?~~s|zeRq>{7&lM^gHJ_((jz#O?|2ppyhW^zpUR%zp>f4_xq~9*6*L+ zF28NHN!Q@RZ{KimeuBKeQ{QiDD7f^aPJzU~i)WJz0=Mx6 z#?Bo8&UbGb@5vl!xn}lUoWr!a@6TfL-18XZ`Vv_@hhzwR{Xx5V9*O${hk|b(sX4fQ zCeQdVar@56S0?!PoxKK(^MQLYJtM?eGGo%*f9TmG?p2%xjPZr83!hNt+R@lV_aMGP zh9&S}EZAXWc2A^nk)QGGzDoB>7Ngufh^|r9OOuf2d0|6=Ye-%b%7Y}&ym|*&9)Uw0 zQJqblQN7K5BI(5O__S5`l08FsgHI5 z&b=NTz^9JrnF(oV4_FKtL*9?TxK8)%)iCgV$Q<_xW2ho+@3~&?pH`MGwBHTx)n(O- zyTEVv@VBzq2DXuVKIekhSd{9}O%AT6;Iuu{LVIf%?;T|sm|LWo1HPIt#?^mqkAH!i zlZkniGG!l!JVn75b*?ORgE z`t`{hN!`o9yaLwu$@eWC_t%iwcP%$?=lkZ+<>NExa4d8q)eHVQ!dG2nbnk9!c=vaUeP_2p>ms;)*8}yl zNKI&K_acqpK0enclsSe1q;1II_;cToF`4cyY5_03!QG#{wj|dq?sv+-eIN2iaGgP( zdy^W0TivSxb?<<`G0*O$a_`kWo*QxflsfKj>P>QA)JU$okh3M%N+jQfATaXkvogqs zG{t|OXJxbAOJI~$e6CsLUpbVw9m)4#DOcCBu9=m+40x2KFDWBQ8RYRkFonS7`=@0Dd+u8kmvXzO?pfe1ZF#osZ6{@Lj+PJG&NgwspK^8}&-NIP z9K*TVKem@^;op#{KC~TE?yY;k)i!qzUjyK6vxAhoXDkRFeXaUci!P?I@zIr13FK6H}tK}0wyN!$3ke$V7#l# z>L>jPTKZ$xLSqDDU4O2=Q{y-E(W+akW540OP#<0k9`)xHytDdi)$6OlkN)4);Luka zoA*yyiXr<O(UYed8LzwZj2$egW=q@XiC5K4^LJ zH?j7@b24ZoWPa2SrmuPiV`2??t`+pvy#~g$M;Pt-41D_Pe&KoxS(U}#s%lP}1bh%2 zSIPU5`$JsMG3WXFQvvAb0q1b|cFc{)^wyUb2YwC$w}^V*APDJK*>&I?H&>`%hUZ1VydZ5suB6cEz}4TvjQS0h%#0)W_jgs&06Q{* zZ#eDe?|pq1i}gM5$|9|%B>9)`;qWQ%(vVMO+)Vi^=$znvl}CB;H@aejOPMc$_j}(z zWU^hhLRY@i0(X`2XY@^S=)FVUqs(tm!Pe}K!jvpu^Z zQ-JGl;8_SCJt_Bhh5e1PV>}n+URmVRzL7_H7yn`87q7pwD4+g@%uDj`(jK;fyeW(F z%F{KT<>3)LDYI=N-#OsVcJVjB8uJ`NTPRB@>e?r^o9#OW{I<_F>Kfmc23k3}-$VXD z+lcEp^6!FYGG(3>6fYa|A#`ou_qZyH6AGN3MAjVz}=(%d1$o($3ggf3GN4ByjsBI^sjrNu#{*p-!o8=y%Gw-}&1)-m~e{ z6Z}T{?eg2`cT{~zJ+}{a_mk(D9DZAJB8%Tl^&G#EoT#!`H#zY3JKL6gzt?^v)zQy#uP)}daUEsaIv0|se$kA2>d^zB@AotajO&YvJo}yX zd**jZ{assjL(0_e)tUS*`ps0w@;ns3k$$g?xspe}x9Z~R>aIVvK={B{Sa|Ae}g zpyL{2EBWqKQRj*Y>?Zh?wr%A4!Tl|v)R&e#E5kUR?WbbkQWkk`!o4)?PutLMx4gOb z84v!XJgeur&&ItjZMoXN9f5J3MW{c8 zI>yc|f{&)u^}JR0M|sYwF>dWcTHtMUFZ-F=4A~$%|@f|Dv_NMx_t7&i3Sg4(p0=kfKFM{3V>~NxwYb&?=VNH!qP-k1>XI{|c^rNl z@oq1MmU>`*@O#FpW6ZUOYX--iI$sc&vEW)tzuPv-I1C)NK>_6X0sgd=-Xza9w(XbE zPqz60c(Pr0aDN0EuGM-&$FY}$ez!k9CBG5);{uzK{0QJZ2hedlgMR7?E`6B3GsgGZ z?~cPTXR-2o)vI<jqmJFerB#sq3d1>>2IbOPpmAb;m^G$FW}LbTjO!v zFX8-I6&WS~>+hBrT!drZ>rrvJ>}s?(pR-m4zAhI;w|_38P$ zbN-HCC7$)+=@*QRT<4*ypRXD8{4KkM@V$g~^!NW#GTuu<*EN>sGUVjGvi$>GJ@D;> zpV`zs#XDk5rsuBfBQ$oiHFEUb@JibVdJ#NFa<444;Kg|L*a5y# zw7~)D8Dp+3T${LdZO7$%z;A$#wrSr3$Gh)P6ZrUsGWRaMfv^0bsW-W}VgpxYE)7lPwQuAZl~6y#ID^$oN?<(dvT9P7QH zJD$7?^oPG=`Zf9PVRHPpfu`fzcdb40`#$!dpNx0UMT&mkd_OXSCxrfVZ<@Yj-&5Z? z?c~18DZ%+Z`7cPmN51d-X@6xgE?e7!@!Q6Pw}h@fx2wpw7WqblyD#wCEwn!z;k|u8 zpJfN`d-Cf6qiw@D^KsDC-ms5xUj=?k!%q$BE#^66mbd=Be}LEC&>bAwC^k|@JIUwd z*9P7g_A=n`_qmJ{Z%Dp25BHYn7pw-Yr_?J6-dB_rgwGtnD~s#;ZQL94xeK_Wz_o%; z*W3R9cbNLdg&JGhk22#!>w`nPfN`Whkf(pp_|OLA8+WeFqAc|fb8qaZap=aN8v8kl zwlQ9H0cER6k>rm8UK@gTftldcw>JV@#;+PjYK*w?t;V8iPuNS@656LU&kw!PNY9@#&x)d(Y+3{z~f$T_mz((&$-um zauNJcElMXV4vg>G2I&77^R3>fPGhXaui({AuKiryXeP4F=6av@dkE|k@T`YN^|y?? z7c^-|4ANte8*9>)dIOO;NM3tjJ)`Xcd7hH(E9822k@quf2qp4IEwbNZd0Wku%% zS18GM!!znm0H@EWH&@TE(~o7GMt7e7B)>a2s{v;`M-W*$aP=HK&+0o*xo7m{qTk*{ zj=a>n!}S69-5c(n?Ni))#-97U-BZ4rHpu{v6O^apS{>f>@p+!!3vgA1ZU}J3x4Exd z-`{l7&(P9;sGo2q`HP|N{&M|zlc`@F9vXm8KcRckqy37;?CBdcR>iGY0ukT6SUw@Q3zP}f-lH?l2HK%KY3XDD19mX*grysS|>qA!8 zp977hBz*?z=nJT0%#rJ&^Rz>8%3K$8>qysZ`pD)2qweq8dl~nC(hmAsRzlxcCH*)0Q}n5I zr`+|qYXg4^`5k!Gm$L$#rAfXc`dAKwuO9cV7hE5Gz!KHyveE$#b%@qTGj2=eT{knz+R&NUu+>WUxo?7j^5YZNDMCUw>2rb0)Z zvKn;VOQLS*-jWZ1cdcdnd_tan6x%pz4+>+(HXKe~5w7m}I7u7355#>Wzwmx1gJxXn zmJgtB!LNH%>_f+1a^z6I{gk$u4Q}_AxTmQ~IPW>_vJLuvdzGabGE9J%&$)g^QjZ*s z9C6{j9J2fhE#q0MgWnhg;|bzHBMnL2(Q_<}1D%F!pHtqPJay8qC|>{{#`2*P z$e>PYjDxzUF}UiJC#YkrLwERULY?IFy*lka$}S?aF|$QtffHI;fb$#u6?In2Bl7Ya%e@_;(^W1^AX0*F0P(Ur0xAS^*yhqB797uZTJb$>iE5iMLQKjCT-}h-CQqS2Cru>_$~CT2G4CU#$_aVt)XpP%VF?%Zi3%f zV{ts2p%-QD4@k^d`U||qi;jh-`rvT=Hl6+(&KOHcnR^P9MV&APV@e%Tol@OS-S9fN z-9HztV_u=YdZjw0`lC9i<8&Q-tM5HPuR0E{*vR7AX)Um>zuceKjXIIYWlY5)`tD!w zPbNPZ`Rb7BpYGq=4lei2ZzpdyV^-btG;N_is_xkWeCn*O8^5H!`erfee8#=9xYs=< zvZ&t;pq_e~y5cp;jiYeyc_;FXv)D<#dY$^Dd(-psJU;xbj<*9mGmyjm>qThuH;nzO z@bnNEV<~QNZ_I@{=R;B^Wb!?F4!=uCE#WgcSK}}IeMG;5#$p;5Vr+(c=TpI(`{I4q zwvnHUHk|}NZ{S~9GIIY5bz8%?Ymqd-)L}f`q)rCLyl3jTesRs>dZi`rhyIwFl$Qrb zICPHk>~BLQ1>Q4z+=t~^J|Ba}IK;-}&t;74roKLyGsu~jvXMc8kvstRBlt%F=RPfCyB31C^Z#T?i0pM}&y z-ca(4TmGE;W8}@CZA;LGJ;C`BT<_90?YQQGr!nLWlPB(<)0f?$bDK8wyk*Zq?!$cN zdCVEYng6&y9Ll^8@8x2!jsT~=`+vY!2lzb5Vf>S^QDwo=iTbDM%ZBi>034g?+jGEn zp?#*nv;E?47W@i-x2TsF951MQ7XF8kW@LT4hH>2y6FN(1?}Nzc9)zOwV`J`jB3}yd zhEv`IT0Dl72OiLnBk&5DkY3QPN4c@jp6#-l@luKcf1hD6c{^h9j)H3faO!Mrz(1dR z&!}ku4&$X$@Z1XCJ)&tgVg(oHdsiXDT_Lb_V6vxk3oJ@X>+fRqE4f3qkf{U zlMA`kBh*_wBUN2Ry`~d+#i_THq|WmT@ajR@)YXN00(%2E?b_-k>M+ZAR$tMss($0Q zRXcDua6Aj}4S^TGm)bbB!>TXor&cd|f=tFXECyEnsU-3RxmRDAMbb{a6*%=7?b%Jp zQlBYJQb$pzQLj+v&>pjla&?trT-7hsceH0m=_1IZZltcEUZQ@ZzT^6#6-j%ydqT9q zYiF-cnYxR5l52-a0p{_H%&Fkj4%&-4+RVE{OWjAEsxY$GCf|4+{VGAqTw}WyH&#s@ zbv`n_2LJb5uaR8ytE)P98vo`zS&Zje;M@VvAA`>rIdx~_guh*ZJb;X z)UF+jWpmA79GmfP&VxmPaUM1%+;xNTa>m3N$7a0T2J$}w##lM$ZsXLPPn|zqx5R=U z*Eq(@8RKRgoUwGSdESs`Y@O>JLKbKW*??hQOP26iGeTniZ|XMDJ^ajvU+QqTE0 zl>05bFB{>_HJ7r);97>dEupc9d(V^f9Lbd6*a^JnA{FI%Ej)YfqrgU`~TpD&X9c=h>FyxE_ICA(DGLGH~qyY;ka&Wo*3$*D}VFx`F2}sSmin z&wV2489y7TqfX#im&TC4507(^@fx`E!v9n5SA$boVp6u4loX!aBeo0L?xQIJE%%GL z@5{Yn?jv&_SVC~5r>=Wx+z;j+G53qP-^RUM?)xeU?LVOF-Z1xlMFNwGtGe2BWGx2H ztjvAx^ZEwdGs4lI$X91mCrgK{?lF4>efOlfr>!v0dEmFN?>%(BMz-tVbw61m%9Ta@ z;*fv&lqY#t4*8VdXnvj{i+mJ=p0X;R{3wHMpzbJb_m)aeebqKnR`+|lx6}5pE#yPJ z(X(ac&wX&}x$XgVPiS}KabKK!>TDm|S-Fq$J^Glr-*!@F&#&@4DP^&59W#z4$B^U6 zF=Ai$0*d&Toc;}rW6LqHkh+dd&*E}ymE>97 zu?M`QKyJsTI-=v%G3dC=N4qZT zx8Q$3okOJmXdCy|mZAS`&ot0gx7IdgTbu{)627ap=?wXz>^d;^S-n_q+sbj19X#sn z7oqJs!9Bx?z*!U8_V-@Cfn}+;I1_D78+hJt3F=G>V@{`z>yZ8ARi!^Gg3mo?{(imv zd4bf5wz1!o#l1_$)g1)iBH-SKk5SOjx4nou`kCvI^tI_*({JpzxE}AN-!Xl;e*cWG z)4!!}@-TQk2itF;{%HMeuaRBfuira;fW{K)8#bPH0TdeS?RckEXwJ3+39u^ywag_rIy%96I_n z^&j|qcwx}d#^85sF7?z8^e27*&ZCqyWsLX@)DJ(Ew)I;&8=2fM@f_Sups(IBn?BK| zk%N4{e~D?gn8>9p>OTG!iEXIfpe;=whcX*q|08vkH3+U#K2Vrs{D3wC<4KJT zeV;nUoz9_-aiiKIv|VVUcnxopxT-s7KiC6|v7*M69-*G!{yelx5IEyOjiJ@{VN7Zz zXnR&^TKGH+Ep-QD{fwPZU+_0Z($ntg;NdfPRrfG{;t4p7E8R)^s+ZWluYe7L?+kdF zll(3CAUHhJtQGIhca+bjjk^Qup3DHW8c`k#e5K)e2X*xcr2+R-`bgWMdkwT*rh@(r z>giWK0}pq&=72|S2<{X0T$zt3KgRo0nX7(reNrtcFHJva6MX?L_es14ZzH_-;7 z%|`o+wioR++E=vS6aiM7iuRie;L(Po{Y9IMb{lOth2dZOjkcXmJTHLu6Y3b(8g0iZ z3*D#G)mD=MxXaW(2(Q{=QgOeZJZ(8aWa$H5Z86$mw7+PJvHi5iXnWDNVw*(UK(xhZ zhq0fu(`bW9!?U&%Z6(@lwAFkHu8ZK(zN`(#cSk#n{y6t8YIk{ud+jjxVO5gvoc0%O zHQINskY}H-q;6Ya?T>-T9vl9~LQA_%ar&$~VNLEWRFzUgnTs3U5d)wZjSst&2mbUOIvlCM6h z4OjhBoz%S^-*T^=bsjk00rwrQ>b2^={?3YfEVK*94Di0fpSrQKs3WQ$s`0M6T|!_=v0aYIRR_LUmF1RJgxTJ=Wi4 zxkp(Y(mddHf!BQ<>aF^Z+*2{1I@-r)0i(_t3h&YOarIGkRqgd(!mIkRvi!`w_KnNP ztsO~=j3qIKL_3Xkl1RoxDUx=WHoTiD z8TZ;}w8I>Q-Z-BEhfyMY~7|+9)q&$+_1)QyM(lVIruj-9vkgdk+TECfcI4)no_H zBH$;{mfz7&+Hl-MI~;iJG1_4g0y87&#vIEMt_ z!}VEj>e~m})Lh3EroMgp6dvp=--{01+n2Vzwl!^EzE9fUT(j9%9jWISs0S@=XpV)) z&~VMC&CGYv^KY*pUmInIkK9~cm-$Y*CUcAgk^3)j=^J&<(C<0|8T7^KYyA}1IN+@h z&eW9qt%w@i;2#Ig&Rl1K+i!|KTy1CiYDT%0r0v$*az%ruI{F86^NV_ucIo^hnjtrT)?2Cgi)J!?rn?^o1!Z)s8L z%mViv(mT}sTv@<1gnFL2*c2L`m$ZSr(#X*Rm`&6xPMb6YerE%oya@&h-*$O#mAs zaL+TrFa0cS{lU_YI=>Kn+6`O4Zz5!q&Y!?lA%9gCzU}a*UwANb8Bgx{fG5GxjAv!B zee^%q%nDENbr-xJQ+^R#3xKVREGvQg2-*KYHus69*a2;89&<4guYAZ_5 zy>=(#7}TMo{9R%2Onr5Ib$fMK&zg6AQ58Hj;AuG5#nj72J@)_&fVOK9*9Ma*^Sl)G zWp#CRdDkLA>UE(`Vcs3}O?7kiU-fo<(n0DO3tEM%^|n#&`4Ee!lNcVf<+-NtdP(@$ z>&|`ZQQAt?q15As!rMM%+76ByB7f1QOX!Y#Q?NQ=XeVb=IZa z`(7DGumfDh;pb!UHGtP@)KM0{@qVBE_WPYzH&y>tA6949zA0|Mhx22(u?YBK9w6vf4-BzzwH}Lo9)V9&O|vWA$ov zV{Ngvq4r{R0QGkDlKa$GS6IVUS?nLjf#cZkygGpWuYRoF9IY>_KRYJWmt(=F`n9%G zb!K(sXxnU%GWBlX2X$}tWo@R8PsgA7wd1uWW$MiCDOP7zpU#R5Et%8%0i*5~t%IwN z*M_!wx9^d9yEfxDyi3}5)$5hz7UfBKJ^}nH?h`OpGJ&raxW*yVI{NW@#*KU9`!SB| z^87R9V?vPuyuZ`NMZjHy`t9L$Hsv2cqda&ffZw(7F2+^vth5dIYRBL^k9^ug3fLFC zr+pYZKV^m=XxwA$&L_Vw`Sp>n3piYJ521W0bX>#dfWB+@-@%n1JOkiQS?WQ1BG>A` zEn}XjOL+my;q4^+s2k~n%uZfScxq}}g8L$JgafPoyPP(<#+dC1o{rk`Wm=` z>P@GfeegADG<7DyR~=}WcL~|1GbRp@{^D63u08kv(eD|! zSMO7&n@?Ut+ECl#VtBlQ%x!3M^{DLR$3v#R0p?F{N&rd(MT z!{ZFnK~g1n_Uz6tNDY9!3hYtx9)e%p%d|3Vgtq#Z z`vo^s?(ac(&SqiqJo7aze2hd6V-`HuvNrM!rN7=o_8r^@ptp#7&kkJ#|LSC(A);^k zI?wJKRM+blfM0O>I}t^BR+b6yVZ3)9`GLQ$kU_s#3!aVV)(`aveg@EXwHOo5nYVb} z!*xFWt#8Y@atAn_Q=NOqlcyid`SSqd*0phQ>KnV@8tfHyU2BC=UJiQ3s=H1z-gZ9u zo(HX8&Nzr6;kK)@ERAs&nIMXc>dx8towb8%sN!a${kAZ}(De?4RfG8?W#I zusylM33AEd7EWK7Ho?tg^FC~)UPW@Bfz!9y7N_mQtM&pY8UF87n5 zy$*WD%NWCOiTcJ|kA;_+@bH>;bf4*u@MUcFhA_Unly#!t_fz)@Z8HM+Ux7P>e8$8W z({KPh#!DQ7&-dWPc%7V-dA^gfEG7R7cyGY93wU!-K9lGC@acJXf5K}4aP9>6O71&_ zQkUy3V2raho+%#vG#MTm(f{94mI8X7PiMSQfB4%C&$;2%vl`nY!yfp^MtjUg?jYAr z(40a_2mSHT)DKaF{5R08Ok1n~&jRp#7sl9N9L%6Sw$p9_;Lkf)yllO%BH@VNpy=M^m%`%2ed$6|5b@1#r&NBLYAUx$!QoAGw?V^V&W>krg(jqiCfo`-maw${Gjntv~Jj0x0sJb`=H`F>Zm zllorVqD)zw3u05(zFW@oQRFSdvtz~>{^8^q=WpCgZg59H(|+B^v*X9JxQy+$pXc$8 zd#;yzVLYEJ7X9UZNMmC9(O$IE>(p38L~T%#x(2V4f8J>SdmUV-qW3@7K{~rD0UV#3T*L1wVv$AxjU3b9uIAA`aJU0DP)O+Z)qfTMkK;Pqj;Iq)S z2S^QpyGdWHgyxWpjCEim$QuLvIPQCczZvy5&}Lo0)rPXk@KFqYjw8bamH*x;OfeKB+0$%B}ncG(hlJ70=UP>^>vxb3_1 zz5pI zQGM2R`#9jL0%zRNW%BeZ#et4?VB-m%Ah&+6?(m^*tG(ED<9c|{2w$Fgs?L1~{E2`w zuG?5`*SE%G8>jM+{#6gxhqat?_4G45E6W<{&*8p!fbSgFLX_!$G;Sw0I0y55oH~C) z!&o1EddtD-`B;OH)ibX=+p0HwYM+=2p4imapLh~EjS12h@R;ZP+|Q;BJnv}`_pZG? zQ%k$Q{yKl7Tl;`#S-k@eV~q3xROep5qU-IJl)0WyL7ws9ci_`Cld=rvz7_nH{U7i0 zuo*tDf?pYWQKoEnfYsJ;j%yRjT?;9_6A{51qQU6a~&uBnDm-}P;GWYtIMI(IPjUEA80A&lK#z}Ys&q$i_KZ9`=l z0*-Pd*X*s~+utG77U0_2b$M%K&q%p84#%DA@uuJ&MAA1AfRk$4qw}W z`(^Y&#iJkoA8$D2wY4*8x3m71x~ONctaYk+*_2QI>y!zYfjql<7~&$FsI}ZT9+Q+|TqS$(UGUX7$%- z$JbAyPbM-P9S1qJ=WF*@AJq1*FGoL(eiY+djkV1NK6OidJ>}rB7;Q8KzV*-CfERr# z>YVy!^vV1MUHzl_bJTy0wKXPI|3E4D(cZ7VniKrW;`}}gUY5aMz5rv0v5}ndo&}ul zyYu_w_aYN*yO+HF)D7i&n6v{Lo~!+a_kSz2a#DW*aDK1S!AD-*52mH|GfVnc=Gsb$7yt`%afbb0N zW_Z65ps%610iOKb+1AwYcW7%+rzZR;i*}!GfC zyLC?fmU4Zr&eJiGNn4b2vFoJ=(3!-uvV1|g-=3a4NBdo)ZAsddv>|E3(GUBEXML~H zeph|2eyg-WIoJ4|)6W{^U!|UYSZzhlU;191_w<|UXVrJAjY?aT{#RoM?@>ql)-|5> z%Q{bKhjmS(&CBoRm+P@b8ll>=spGBHOd^W37}Di`vjDaqs~(9M?k*|^3I}cKKYG9dFR0M z73o9DF2L_r@>3)4O4`PKk7J-^zqqz@UtbdHx)-qyW$yR;hdS}8p9cE-kz*Tl?f~bW z#IDq_E#p(iKC}P2LO+sw`^|Q9PvQvby9Y7q%p&fa!9xS-bKi~Yzwqy##ZSnO1lKM4 z)&6pCWIt#e0;UPFxKD8qIPI?mz>tOqC6)=^cr7W4Llb8OjOW>?11Y-zKlZJ*w2H{!So$28dem1I&vNpd zp>vdZ=8)$Rl?CPlQVHbn9HBN`UF&)dP(GeLkI3_fGQjWTOuQ?M;f|!5mS+%oR#7?l@V9e@lO6!K41Cw&doQ>S!H;XJEATS{yxJBcp}84Y*UFwx zvvP|T_v;lf6)9`N zeM8>$$J}3_PGg<}+TMve0%hNDpE>{x_w7iLJZEPv{SDarq#$&j!0(^5$zfzG$n`FBmFEL@ zpbb{hrpIZYwX~Ztm4A_^uk;;YYtUZCVoK9>*)@24NxLVZ{5AKnX*1iQEALk_@D>LC zIB;2L(~a;@4BAKO7n(QZL;l%@gX!z|+&3X_BJ}Fu|BKs_O^GuY2$TtspPQYg@U_EPP547_j+hO26 z=VU$4G2yc!^!CD!=b}7-=OmPEB<0}VGgcyK!#F(a=ROW!pM&=o+9W0X6ax3};K@kY z5qOIP{wC!ENr$N49D2)vy#OEfO@HXvhXsKRl0F7Uw4YBOoj$#9X>0YQZ;?S;t-6yw zH}xR>d_~BQ8F;Iksi(!r#`_1nI^2KArcS2sO}$GUOucC$@YJ;h-mAI;VtVNEdjIqVQyN6VLMH_5nthf4#wpDeT4}nuZx(n^D z+#iDut;XcvgBNAd7cv1E^u0v+IcP`yEc##cyXcqEA2J{M`cf7E?|M}q zjbq)tdGo2GPexyj{+RKkb3A{}H3n_#{`;Ohw`RQQS1Al1v&lEs&37j+c#Vm3FTVbt zXV7=8T^`uY$Zx#cE3VxcEBmOcFUNOCpOAZ+-0P$)Ex}iV@)po@pOAZh^u6fYSirMB z6MsitKgVPPYYGG4-W$}#S5yt}s1zvDQsMxH*N%kZi_ z!||`pViaxc`3~A5d`F%!-hJPCLECr9_zL$stst)u?cn=l9EUL;zK6<^2A+1(cW;3C z5qS3lmxhnm^!so0MQ(V}9_qPF(}ADL)!!p?4Lg(e^9)Muv)WB-kl&1PqaF7RsU**F z!0|m-ZMWK3hf(J;INqi0{KorRWS-Gq5#FAGU)ye6?lS}HzLA>X)z1DqS7Wb6!=r2G zHNd(~&q7`(w3KB4ZRmUDd+s}?AH;p)A8`FI@b(?c1C0B@AM$*iF{LkLIpz9Rofq8q z-Ii-v>QAF>{oR8i;0w@ByLcabkM*6Nfp_=sxcB@D_wFG-2yb6e?>S@TL-70lrzd?w zy;tB*O<4;1r83V)sOMZX3LNd|lO*urp7lQ|R~GkbyRXJ~)AzjqJo>)5PuzVog~9id zG@Rsqo9XbHg8RGhmYQex;%M7;?(lstN}X`%Y5Sf>ik?5*f0LIql05s%ImiCG0Q@j$ z%>k!t82fe1?PA8}Yv=N}b4r4@19`^i8;h(B%`*_X0&k46wlnQwgJ~;mUdA$i2)uEsODNwD zAF+`q3D+V#?}fH8$=b_~(#F$6d2e~vKBwJncMQfE_x|?hGGN^+FpB=nhb+cJ``z9| zxo0^9=@<7m=yNiD-#Ch;(9plt2pIia#?Eh}zCKU)O=RL)4*3>9Q@_TjbXkd5ap@VF1!#{3%_a*BS@hvs=T&Ee0Ov`Nz9 z-uS=M}V>zgx1OwoFA{6=>H6|L>$^&{e*E zK(7Ox)j4iMIKjaj}zfW$I0TyQjq8P#caMG-1^5r>z9%B+B#w z#D;zdcrH^|5nO%Aw|#Db$2QiN*a7&G;B>D^Vr23R3jK&dsxAR{OlbGwnuqfElwG1P zz6_viLpz8p?oCb%?lQFhPvmE&9lDbD0XSPz-kN@lLH*yL)tTfzqUq4d0p4W5oS{A4 zYg8F}`XRL6dyY#n_#DRb8DO3w-)8V?vv;hnpwE_4&#~`V*FWMpEb5zy;3XIN%gMXJ zH8r-;-&NaqwCS+*?Vl;IR?-LMgvaKZVn0kEr8*uixNzD17vQA7ftn zk!Dlwp0GUNPlPO^=(`W-M}LFMSj{**KY?fCX&%ClF_V{-i8A+@SfRtlV|-EPpE63 z8Hf2PvMoU-&)1wnc_hzcfY&#qkHYxKKH#`Sp0Tg@xjNqMSI3~Sm+nn7*7iJf?|^GE z_r_l)1+FR{^|d7KUqSqRmg7ve0}QryIT75 zi~;?SF<^|Vd(-v-V}B$7_ZiyvEVL5ChjGUGA3FjU35^es7%f zyTGg8Z@)8{xvxv|JLGqKC49C9g=a-hC12mT-w3}mzkyS|;AVh#7`*N?tq0Ge!CMW! z{oZJs_nSA7du`|gxK^dEvgoJQkFXe7+#9Oz!9BSLfk^;f_mBF`buVu*^4x3d9?=MJ z``fO5n=gUG-+%Qtlk|!EJFm&%{d>{`@GXMZSHSAuj-;M`?W$bklNSTLSHR)kU;P^i z$ycA~jy&$+RmZ3T&2`{uPZ|lWPmv)h?dcw4_xQTMS6N>0oCR5@f_Dvd^T2ms>immr zv8eMKyq#kXS`Gf{+~0!6AJlbS@dA9;fH5{?Ci$*W+%FIddb>k;f5^W}8o<~IC;uG% zSCD$zRfhpv7v5(gLrG*GN4fDSu^4ZzfjaVR>`GGF@&vrS5tg=y#XRsAb#5V_cDuUt ze^+=`mYdN102xB4S04OL_%1XBJ_~KHEp9FR#|36G_=?j8tKoeI{5692_mJ@#G%IqQ z1KtMU{D=F-z*HuAhInl1oS|$u*G%B_9O?zMo%Te}-XFs}_Z&FSQH)QXw!&l39Km?p z$MYuce?p#%)Q!dSGT;Z2*Piqeevi>UrT;gN-Ga{o$a0#pP~;hnEZVT#6H$(P&qG;9 z*&xyh?zPLRuf~Em?N}A)ze-$RDhF~6;Od@>MA?{w;p;fbn1T`1{e!wSDJwx;?Q1P* zkGCx2v0$BCr>BxKy7|-bToN#q?*JIi+?U$6u;u$`< zx%YR4=W(B(`}@el5Q_T!V0~BWdWjg1?hkKGzB;1&#j`@oc|#vpM)>x3iG9y|1kek( z76;yUTfOoc^_?4hpFiS$A#L~=xtud8+WHwt?Bz*!VpiNQnlsNb2p&iw`Y`7wONC%I=w|79!6?||3e zkShs{e#Gru^&k3M!s`R*{m3^Ddj1Zu{>SLw4&F<>c(lc0@anJhckc3l$KN(~-bew@ zYf1UI{~Mln(01LyR|NVosaF%)smMFb`!$?Cv|pC-{2%hHR%+X=vXB-wu+xtoy*4LhCgBl$v|@wz_xO7;NLQFNzcV z?tP96kLs+(vK@r3@oC1fsf)TVTHUb+a%~TPtIsEuf69&Vb}#Z0_y|JxHhf1?_Zhsp zhr!<{DhkXR${#}GD6-Th-Gr9|;JyS8Tfut<7~>h9!H+Qui{RUMh2!MsM&|FKn+%%9 zLAb83OZ^buWn&opcHiPrT_7>_Tkt%HdPBmnVUiaIezG82185oBV2r|jXbu5~_Jw-z zn20jt7AC<%ZRjdXL!MnzWdxsVont&-r;afU?sHDXeKYE=MYa>%yDmEnY#iit?Pe^& zV))3D`Ry8Q7Hyk>=Yznf2Bs7;6`@}D#NZ`wJ9S)#xevoNpmD9Py^LKj*0n7145Plf zvgyw9`Lzd0LmubA`)$gMpV$rV{>blIvm-nY0%vA$8fRg9jfa2F zi`FLFigImMp7Hz+ZP<;g>)5*RZG7(-l55Z=qzce-O=>K#vbbI}F4r~BIb>)Ge=kU9 zq3@dWHD#{*+>hitb5GtWS0}y!4)?*hCUxBRqD&omCv`ka+WkxJ-G2!^$GzuA_}=)Q zvwiENzx(Sc z|AapMj%(~J%-58aLf&!Ud=L6{8UN}OGq_fw?n7{2W&GrzEt7J8ojNCZuEcX8>ggL( zANZL29^9*gXxAx2-3sIlBN-E`EbX9G1|F6nQ#0ChEVy#fhV#hJ$g{TAg_KPKcQRA0?sp?wb3S|>;W*B;qe>FLX?p<^*gU0ezp98a}x68B`~PF>yu)-l-p6g(T?rB;S`gmdwW~&X5xqtbiG)Na+Ui7*=p=dfHPnmfeKu1|3;VA&EMBtwdygJ=z zct1r}2I#d0m%f+(XoJzfrl!9C^fl*sJLSIXBY{^ZTm;^$@REyh zQkQb|(jLGxq1`->OTD!WxXvL*EuL=x)0npM9iIe0+LlL>l0mx}__`@GaLb_Ke32Xe zDp4m$iUKAC{1M3Go=*Qi`g?H1Fm8-Lao^|^+NlLFCxQ39Ha1UvPo2{Jq5AF4klzS8 z`cKpWt3snB@Y;ag8>wx&1<(GELp`pgNxs{+xt^rlIcNuM=ltP3umL{Y3x5o}Ur|RL zcQ~|W@tlX}q43lsF_od$lm1ecjPM=cxgz(A;4K%=+u^?#vfbxiebPndLfUaVxQ>8l zK5*TjmxcR(sOviK8F?L$(RD=>a=P9#-d}xj9qqZF{LXXi%OU7d6rc-8AXFUYfr)c4d&qkvPFRDV>@%nU!CSEL?U8XA@0 z;R3YPPt_OIPt`-!G1WuWVXMQRI<043U8b(Ot-9S3%9>Hu2^#9Mo}Z*FVc>cd9(Hqe z&#m?ZZ4M~!Dnw|nnoKGrp)-_ zP28U&F9@7AFKuUgxQ>VRJ@6Pit3Aszl#E3dmvVY8lCik|!kclvp2JiP93O(yeZ$%y zwZ&+=a-Z-g%m?EF%p1t#oX`Qhwm~WC)It7s|B+aR{3F21zxJ7?z`g}=kbG@24M~2Z{l01+@*A)H$Nn*PT-&NPBF~=C9$Fqa zZCoX|+Mgjg_%_hj9Y{yu*BDmc|DOZAE8sESdmpJ9W%^?G)35fMXDm-A+23CQZ$CRm zv@;bY8Q1F?#Mo-j!q;Q*FRendczaiNHPNz5#si zqW&Q6yO2MEI?C`H_m#*S0bgCXCI&7y_glbqg6Dx;^(RaQHXGL%k}@0P;aKv#lT?hI z9pFod5%GTx^?|ga{An~UGgs7YJge5dVmU*Ymw1k(j&TCt@vQIDxNH5P`b&)m z@Jw9&rN$+wGwI`xk0n4Bbp-VXbpmyTIb0Lstkoe7L&tb&^_eWRp)uC#GR9pShi&Y% zI*u{Y>5Nv()yEfPNlLHwOD-SUJ;K{gZbqiy;$AVA4lX2hM!IcnO zT?72aR2zq({`4m0>IM3&^*JS_uk4rikXt{MI!sCUP@ge=)-&TX@oX%|0ItS?s9(t6 zhrlEPzkX?H{6c+s*LQ84NL9+DGn1s=GYZP^Ou7>j%zyta?Nd1X1sHNn=Wo$noRxF%1q@wtxIrl);R z8=&?+ZGhVTw8d%9n}yuk@U#PJo74WM?N1w^c0TQL!zk09R|Awd2+7C$#enN%kbbD z(sf=jc$>gG>v~U{;!D*1n7Xb%<6~{8S01@`L2EGZ&A4~JvOexOo>PMB61=$h9w@8eU4$Zoh-GHee7e>+L282xTdDgSa@Vmw zGp6n^xQ**Gw$2#5!PGZqZwxp)A;S!CeM)Kz{!z5cPV$ZaYXvWn7vMk4`Tz3F`P^vZwBuWo{cr! z01uwkI1)M=V;IBWP?l@RVjN^K@OnOGXVO@3?jq?E9*5lOcj}tPUFs`#jp(}b5Hxeb zhkLd%Qr|VIdgd(XIe)5ax-Qf|?HSc~pmz%%)k*cap5i%(T(8p38R+Z1(9%b(&-Mmo z+rVMpy#_tk)UHv#Be@>F0Ugf+T}_?WsG}^NGmwo`nY4(yoXn10=N8NWujf!bf{yQ>?|Vn;ycbPB;h+&(P{l-de`Rr@(*Dz4M5&>;cCb zc&x(JST*0>&*0G*qQuZI10CPrDezd4y1gi$1MVKcgmCXW`7ZUePtSzsTRb}_Y^APy zbd5o3N7-29{Sv+>l6MSv&liXWMw^R!!JPxDP=5z-`Ysv(w-7wW#RcGPDe(Gp@>9pX z!rIC=(k8~vWd}}Kj`FMzVuF3bc*zl9PKWP()Hwp~cJRBxF~I#I=xQ&y2225D(ch4g zcIk$!`q|1;U;l?bga~l_2;2kUKO$A+eb(=w9Yxh26Z$UH$@NR222>KxOOY}#sQe5wY>o0KVgV5*Eg*MQ?tp7n@ zmioNo;7#!8%h1PcjGF$Ejo>Iq+XdiRKS)Jj^quJU(r2;SBZB)9Zh}qd*+61 z^s9Q2et&i5ziBJ=GWC?p;LA;2^-gskb)HYiS3lA3o)&!S$|osP$6Us}F$5!!Q@_3X zi~js-Auo zXXPlfood5x1K%&|^##V*5N#Cdx!TR;B{{U!X^msCoz#8Rr;Sl@T%>@mHZt2rIn=-1 z|LQlg0lce6pQKznTaf3RjP;L6pN73yYejRPDZu*xF6~9yo8IPm8fhnG6`+3}`q$xI zJJLYp`HsH#JPB=5{@1+gw)5QIqkIiGjAi(S_9{c(N5Gv1_c!E6fiopABPlbcS$kGt z@;zUoF>tQ;jAz(RpBS&;n$Fl|?Od)MT@z*}Jq1QtT!T#q=P%&cLc6+dat-8K>SgkG zz`yIW+|Y5&=6dWtFs{MYg0~p>&(Kb;vyxNCHPmGJ2PPHuUDLU4T18#=uW3KLMg9%= zSt_5j)qmi0edStfG4)*6Er+iEE0>k}*P#C`*OS!k58PSmE+elbvN~t`pZCf#mHgJ= zGJfB<{aE-izJ4V4)1^Tl7)x&){(4~DTWQ?CI*@xYjiv7hjC(7M$1h3!B=Bl%zj~ZH zljn4~XH(ru-B4XleM;Rd0Id3?dY$^7`lkAvdph0UslKMJz|)cElC+`w=#O#V z1iYUAr7XtP8xy~Q`v3o#UB=IcLaRPy?!8P3uGhe!9_}8@x;(o_(s=w5;9CJM_iL_4 z*16QHP967Rs*}5i(|G0}`PvKIbNM;t{@+3w%9?UCoZbU)sZ%=-y8e2^ z{RrOu$>8imedo;9TweqKUf`?oyd50wt$oCpyaT<4z$(jT@EZef9J=xD_rWQw{2FUM zi}LF5qg=0ZZ3)h%jOALiqxJ*i*?#~wF>uDsd-hNixMx8373ll{AM*P*`9qk;dV;eN zax@^%_E^euf68jI*3n1$9{lO|yh^#T_r}&6Z*M$&VcrX4?v2skPrU)qhy|xH_R7)_ z9zBy~JM-jKWc9y3vr~6BE2#m}lc_MtAA}cZPc7q5l~8Gqk_5%mnuWWOxoQ zZNc?``l0X|2b}+5^eOkZ$nOF!|Lf>Fc&E_Mr^#On?Azd60InrmZzFpq@U@55ZQ3n8 zbdr+)J9U%O2KBkVOX^4cbd(JO-%R-a4LpPBh>Xyg39iA&Hi3F^lsy72KREls-+T1! zaju2nHw}4v!806MMd2xkEd9yf1fF_aqrtfr{0k{>$(Wf8Kl)CL5fCE$RRI|CfE#HC}22X&Cnj|NXMtcZ<{+9!`cZw}4lF+I-~R z%)6xxBP;D?EZ0Ep_1CS0PviR=F@9ZB=YdE62PQxL@HhAq{BX)LK=~=xpS1zH`nc2; zijzh|=Pmg4KU9o?@+@TABP;w=gNAKl%%e6SePRCZi*b?uSH^yD9f4*Bp7nbT2W|pw zue~XncW7exi~g|Pr+{C7R}aekpBL98`ofICv>)xGEXY3@dGf)t_9f%M<^h+I@{Gu+ zEUCGVgtliftAB1~3>~Cx7E@-7WgOSa@HB+0=P&D1+eDq}JbRvzy3!rSqr4>e@CpQY zH^A|fdY8F>M)_~xQs+wsfAXn*_86J8nW=ZV|6?4qjWaWLP2IT}G>qju$hAIohLS%C ze2W-||I!xOz^PsD3+4%BNdsR|JTKvXD9Kn`<7_8z?|z;|!0Yp@VL7rSp)abCua2r- zJ093r%B}?9m-j_GnP>1FB2PW3Ao<;+;D==V*k;PqoAtM*L`HQmbvI)M7sKPn$mqTh z{ki&ITOot7#y63vA2`*^jWg3Pn~r<;3>o8~Ecu~*9y)Q1lVadZO*#heb)jd>j&VH3 zGQAHzV|>!Xi*t?fJH|9Qml)S${Ecxy#_HUIm$T3t4Sr*rh68^bxqgk{`$PSd)HB{l zTZ-RkznASuew&Ri+6jOE0@n-vm%^9vOWN#>=cx+3`&fEHPyfC?i8H_{O95o529LW) zo}uRXWPPErj5^IJGd|6D8{^}QIXlPwI_j0;8icm-KAqs#_@6()n-Ei%k^BtsWBlBE z)b-oo8EBrJHdp-Irv%q9-iLnEPOTwE^t@hJ3-nL7vF=m4|DP-c;deKv2l;1 z=?kvLrWq4we4KG_#;`r7j=m$~*e1e5D)3AsZxwB4Y+WSz`i?x0*x0+@xsF1HRMhqC zpC1`_#@CIYz4T#ue(6u)yvMZ5Zg`1=2m8f675Zb0E6Wcb{~(9D=LD|qcW`e=V)5{v zEPKrnE8Uq&y{jIqu6*?szoT#C2^D{Eo@1q$Kd$ z9=KM}<}p!u=;Q%c0p#1r{bTO`p`B~ce`RC%_9D;M$dL;?o2kd}Nc186@k{hFc^zr< z9?)q9d`tiyD2PR-kGaASjwVau3kNjy6WcoV)Sz?g0Dx^ z(?9bQ_u2*Yu{4D*^?7yqN5J|$Q0G_o{*j~}-jnAeT-EW_@73+q+12CK&;8E%{n1`< z9US^|^rz_iG4@!SgT9iydI^u6f&(KnhS8`k4XtSzq-5n zzIwiTy86F*`^(6yuSTDZb^-Nr^?q#t+5@x$sQat)>&MYwqpq(WuMI#yj`rC|a2qeK zk7qhK)%n%u)$8@A=z9rO4$9mEtuIGAuzsIL;7bdC`fEA@qn$v1kFxBBSM7P{;nCQb zxzN&fSDvw=9Z%a%A8=`JGd^Mr_)AjHI2dCEwBRlO{+_4E(1J0gy|5PVf_tBK zaWw`fNS*P>q28(wA)KqaYC;T*J`43-<65-8X;agFXIxB2>T0*rccI;H4Q2Dl)1UD@ z{233E?|=Ng2_FTKyA#>MFab#Co{m#C|KuD3XNHdaDEg|p8JN= zFYsf0=oIkkt8fofU7n5iT*{bpPtX$Z8zbue?B%3Xv z50vqr?r%y3-bCc<>)!}(`t*$(IDovUH$XM_9g>=Ztj&O z9qs)){KO#tKU{l&b1Jg;U>v>yU)pf9lI#cLqCNtzv028XY5UE>_~;VGx*NXSlk5L8 z_|E?V9^)rNa=^Ege4@^$#9z~9uq{8sn6FK~!xE#or3O=Wkug_Iq>oxL^ zGcMjhhU2tTTX4RD94)z)1K(EYRwpfufF^jike`h@&ER7U`4z#fEWXq31s&%O=Ni{Z zLEwFNeb;?|efRW1bRu7$M71!+1lJFFj)s@M;F<$&eND+|qxt0P$0)&dN(}OHzm_uJ zb>CBc7QUDIvYkKL(}sQvTJ!9C-G+L;^KWtWyWzO=KS-P#+JeKm`T?*R1IPgWL1DBD z`L2=u?&u47i|2$nSs#*f^#_z?gicFv>NnDdrSIoucz!@xHR!b>>DMX7RsWCvE$2!7 zM`g%!-J2Xby-CLM8HZ<#U0uqYOJ4RXtsc{b`r6vH zxp$)6m~8Fgo?A1Ed-Wmh>>?{FHlM;}ep82YnU#ChEbbzKuTcqA#H~IP^{Ev(Ufs z2Yh%2igr}}9P6N~AL2dkAHjPKU^;_GUx_{s?e6+S^jk~^?mX|9=NjqLcojJ9?fOXc zbtp?#>J0*}BG0`jKM4J-)E~<{)du|8=-d62UE{eiscjUtSjMntfVE^^iew(H23%#z z(uFfO!RJu&U-DfA=7#|D0C<-GbBOn6JoDI9czTI3HWb)1jMeSTcOP@TN?M;7-4M9h z(0GUX`$$EA_q?$R(8|Vhane!BKZloM;JXaGvRp(Kbslv;^}cPiMMuh~avhJ%{wJle zi0VJ;Wu84@44|=##wn^NC8n)B@4|RQ_d9(9PWK>{pPNa`@1yq&!HXW!1GCXxyN$=+*c{f$9)-iokIQx+~w*m%!uO+?N4IN$%UzHtwUZO5TsqH(ucl%36@`UUttj*B+Ojdu?+U!F323 zJo{X`+@GWal)VaEdA|2`xnBrR{ovhmVD~b16YOKYE18hhaqgH-cu#sj%Xh{1V=A)G z<=%J7cg8X7`*V`~o#8LWvt#}yJo^rupiUWh*g_rO2j8P5^pEeB@6cY__z(DPglvxc z_ej29_u;b>@;rjq0DV-JyyevO96;Y8-!0%KkF!LvRA?e$l{ z-vOMjF)jn70gP?!{I8Jr8@OlFR~LXw8pS(K`|C&WEP!ar^hLPmw?FvjLPvYEw&L9I zQ4M<9g_UJ1v<^^j6>Z`Cr=K_u-kf9gA3OIt=jvy6zIATZS2&M+=W%^hOQ@fnJblm3 z>CW5E*Ut6M$Ik7}q0Paszxf{d&d>U%aa8-u%9fBKdvxyQ+UX z7dZal*>9rXPe-18 z`4MASpTtmT==;&9pnpTZggznt8`}Kiz}<~{p5dVVME`>}`ALjR|8GMdiSf4jJ@k#} z3(@zX&qUvp{u9ToHkyOr(l>G*Jo-4^<6a+>K7u8T)fleIqMbhs8rtvm?dUhshomnj z!B3=bM_-RNe0@vW?X|P(=UGe})Bulurp^K8Hu%zyq@O1onY7`z;hl&DCM$ULKWR_V z&!lh2eO6K68UTJlq z75klZT~vcI-xH0Uu1$PTT=TeoG2T8FT&_`!xzNx2A^EOne2;yvjjNwYil(lzxMs^t zJ;$r#+jXUTCmf&dm++l%|Agb%b)c~kt`S{hI`(~kTywg{bbaaj;d^7uhHF6IzYy@0 z0M@mq-`W4ji$Lx;`1pwWktE;ADm;6BqwyKWW4MO(J^PJ*^1U)1!*|SctYd(i0iEXL zXZ&Bk4BU1D)F}Tjb|3u`vO#UPAJK6B z4fwbNTxIT8!|xdQ(Tb0W2XOGN)m&3rOhR37GyN)($gghO< zp)5au_ibSE(SJQjC7~Apwj)>1qKkuPec$@5r56J15X$s*?}qL`^7MUg3X$)e zsqcLQW%p={uPC2Id0%iV%Uh=t@0b-HzO^)_|5S9V|!14mi^*)#_x^a zh5cOpF6m>pkFx;dx9mK!RfoTMz^8;(TiQ+^yWcgxS^C2}!IM7p!`v&&P3ro+NcetS z0EcHn>A&^+;{2V8XXp5(q$|{~K|S~A8Z+Q`A`TpWH;g&(`{aJ#iPZC(W1NBCoJNUX ze6#XX*Kd?D3Vw(DM(H<>CExFw`-A=7bcRmCx5Rja{@_VW8@$f_JHQ)Dq5s=&qOlM` z>U;j;MxLj@#}o3lAirm`UW32kp^O>YRa>FwF5W~o-xtp^`je~Uy92N}D0jRUHy=L7 zLhmA1$GYb;Wuva|j_=+?-dEqN6r@M+y9Ah1j5Wu7DPVnH9T%=sSJJM&#|xqB`{)@@ zEvV;T>7H$mzg%o zL;L*R|-$wyAgtU<~i*yM(XK9OHkog!mqkvC>Y@VHP8vZhY|7Hj> z*&npSoACM{?X&~=+*fs%=ZWyQntGl!un8R7z*`u4kAW)!j&Z0BLEYWB zg3;i-PJbAiU@Svv>gi8*9yA8QIEM7#bMDmdYpjAX3dSv*;rT1B#v~ZekQkngb1=ps zAzne>v2hGlsb_qHXPWe-Tv?JL%l{Rt(42hZ6^vCdKEXHzV;0s?$2bM!6O3OlCc#*P zgt!Ic6O2bNM!|Rk;}ncvNQg}+Lp|dfj9Zus9%C4caWEdCF|w48L`Kq2w8uzjX={oO z&^E}IhW0Ry!I*}%)LjaHx!}t<24#5(p8u~{1!EDyp<&E|F$u;g7`tH1f-wokFBqqw z4_Le0a_|~^U<|@@lJN${H5g}*5TkI5dZ|p{8soU+-Z+JX*aqz&E#Su(2jdaGgGXZ- zE+ES&cr{MJ_yqs^YfUJ8Lt8sza_B3|GRl94$Aj=S8T`$u|A=;+2EA&?o}a7!@5bEc zqTT?WJCO8!Yts#Ioxzyx!F4kEZNRyPtM*}S$J(U3Ql_80GHtsb9*$977JM;WF9J7( zyp_~zD-H5~A-OMkA9*Edix%8Jq90>{`;a_+n+=g?8~7?Cvo_lx`A3msIrn*}?^zVu zj?%Q$S3L4VO zL0%T8#-NBW4oW5E4Pc@>_uKj)05t$_QD)C=0fXk>OBz?pjpp7{h zW%YpfjQ$|7)sW*gl0G-jV9dxhioSUWzBpiZaKD%9DpCyP3m6CGNcxseQLh;A`gZ)k z@=?$V$-z4g?U9VpYFvM(-Yw+)5jg!oKf|xSq3O`h27FEMXm>Ln;WGI2HLj*k9^eu& z4vNv9+ri`ie(8@imO11lv^*!qb)WIoHFz!qAB*6_wdhRnB*bd3fR-`Rv!GRx z@;Ttm3k_qsl@Oq{ys7_BtJDg9!8EDwAUbT7}qn0{2p#S=S1)kLf$Oe zem;-JN$8vMJdwRT>w`T>UMKF2+0frrpYj;q)k@$nR%kFX>RZ=G=6;?r0p2Be`w!TT z@REu8G<&=sUjOP-?&-?-Ik5wHjLYcCv++Wnp^z1L=dvVR^#hkD1u4JBa|rwzui-gu zV*|W9e+BTI26uO4ZAt!r@aWw7KKIVI`>EfGl$+;#z}^Qg7x(_}_Xf&8 zW{%XqmoR6)PF{7=2HNy2ZRq?Sq<(t(n}GGbs0gj|jIEDJ#w9%h zS1;gx0>8fUmC$a2oXVolPdjviPw^4Y+Nia2Ytz$k#TfjqVTdyH`kicM)K;-D&p=(oQkJuO=@E{AurV-;lOG zZHwB(^?_<5UqYG(PHpqZOJo}#m|4_&OB`6H;XvT#5eUp;! znX=)I75I5VUKH>4Y3O-g-8OjBruZs23R3nT^^M!R51mttLu1d4C-*EdW6Lix&SMk3 zkmW;gtRlJB<0N$48?p&__b$7C#QhlBzqFCLCpy7yrZ33zVYP8hQ66ZX0ml8+v#C29 zncRb-t!xwgXSJiFp+@_K+f75AS5XAEW}^t6qo zg@-J(qw$jO+P&t=+hiM069={;3 z82sG;{|J)j?t8|>Ch8hHd7rlRKZ=db~kp&%mp06u!G_-#o3r_lC4#ni(V(3as`(@^f(rxv)o!+#TG8p^Y4l_9jlK*rP|>i6Ke zH#m%myU7?@3vZs0qAY!tgOr-G=D_r%tv^&$Xa4n6kUbHU!+SllL~`d29YaBhJw*D`f@Ud~u4&i!CyN{zfHkRuJ_?hv@EQl>1%V5wK^q^%;ra~B%M z2diHj(<`4>fU#~V?mvT%g=y$t?i-SZ^1O!U58!D!_v%9GJn5sDhsjqL8c5q4{|o9w zf0Ne+IL~8oU6~5GjnzDcET6$c7xHqGijXJo>Okr|WvHv(6omKhX{Qmi(LH2RxA_QO zgOnQ=xEXq7cs3@nX$W}W+x3xas^;Kn1?}J9wLW>S(ar!j68JZ%XH4P-uEq%#f`{Ye z_W;N60P`bw-sRc&#~^ua!C@TaXyoWXyKSP*7Ty745fjmV>OP)Z^9OZ2e{(o|W}qx5 zb)F$ZS@6Vw*RfVE=0!Uk2c`mJMp^EFYai`a8y?G(&Xf0$divy!&@PK9TLiu*r^*Ni=I9p>81_ypHj#gS(QeEQ#XA=LSs`v~x9 zD{&3wx=&xKzS%s~%K>fIYp&h&%ewZ`H(L`vjZ1P3=eo=Fpm7t%HR&66o#xuj{r4Rh z8_IHpa&>59@{9{BN%_0n>ytLt%X8b*tuv8-n)a;*U8{GbOj-I-Hi-J_TiUnN@;+$`zsC4_jc0A)#&{}^_U!~)xOVA1jDb?* zWdUC@uGts|E2*npTYujOc+yVT4_w8drHwE>V?ukOXM!7N`U&*ikJk>qwV@kZ+7H== z!IOIzoQJeuGzXXV@pl3*+SfMKR)3iG^*rw&`1^y)SZ~kV*5=%wdRL*X{n~G|`}B;R z9Y<txmQVko@2kD?v0CwTTu6#wHUh%B!6%aUBv zyB2qi?|MIe4GxTYz$o}~9q)R-Ecf4&683T$Uy>5~UvO17NX%8gUo5y?r9)ve^i~}Z}MI9>*rG+&{t<{h`vI7cCPL91?pQo%)L6rZt}ke#`V5&CdQy-1%58= zpe(NU-NWq~-!;7JcGvc<#a)-Xc6SZ#THdvH!kS&Zz;(Cld)Ma8z~%bJYB$^}Rl4tm;E`4e#D7|JT92Tl#gYgI6D|x`Y1Qk<@W7SHk{q z{jjsap)8ld`zL7yaP7h0mVE6*naR^utQ|=klQH|RaIf9TxcZIocZWGr{onPHvG>|} zf8wegXfOTmp2!Bk{4Go3(ocu2NqgNC}>`J!!Wxp0WgB+L(;_ zFAq#KW8Af#z7*|95%8feWe;_OzHOy2 zrH0h^JMLa4-{JPub1o=GzTbb}N#_&omRG>%-c093=T5)p?}OiW-MPSTy7R{n=sPDm zr#e?Shd6gQKRGWtN4g)%`NH|26ttYbf?R#?e1ClR-{b20=zHe7>YfeX#bV_7{*)WlNGaj*Tdu#- z{!hWXpX)#1@PFU`VqPf@-u=+ZMt^=o8%7{w9qN~c$27nX0DcK=myP?q(CHq=*aPkZ zaLz;SEZ}d-H52rWso4yi@ho{M|A#e3Q~2pj{iWou=c=zc7q};Jud#6{W9}yS8iS_= z{OCI#2)@7IqYALf@-OYP65M^knH4^7!vAFIL;|y(yaVw68@yelek$-hgVvkmze-*c z;5|R+5M!VOIQFn!X#~D?&@P7T)q$Hv`hhwZX}{d?=DI{dV(p zzX(2UJL8dV!_ObU*$(~4UxX}=;C~x(*jL6BWdf!pV`4Y-ZTl+Fp9!3?MJ18VI4Ik@ zUI^bp=-K9NnQJ{G&@)>d7nPyk6`b!=_ZnrM325BGQ*f8!-DxE}v_7TZqe$(cJ)U~X zG8>wsfc*^q+QX-FzTX$WE5>R(<9QP>6M@Oeb4}8ClHVomQGSn{`?WD00Jb(fZlkUI z*0iGy;*!vxwA(`3;WyxGGsfcJ)o+a74r5H_lAj*A1_AdkaDMM5z^Cyd#-bRjQVM?i z@$A~veX0e(yNW(t0In(Yv$D(q*VpiAEQvONyx^I{v$h276~m!z9Eo-W?Gf`Rze`&z zfMzaaGQK1SDTF*@OP*7&12D#hYy@X=@>js8``oTl$2gUC@TNV)coE}FCV=}b`cfN+ z@ho*{j{w(k;P;F!V^3~?XE^*eBfnl^a8PC+8T(?Kin3^j*ME5*`Rl`j{@c>fQ;*VT zpufdERw1@U*XZcI{GKv8>^pn0Qt#*(T4vSsSYsue{;g?EuOP+udQEOeHT&; zsWtSwl2(B0ei(BAa92p4TRnoLefb8@`l|IAT;<;Lx$DDkC9Y?{modPambyXkm@iLl zxED@*{kUG{s;_x6@C|^GM|r7CnSSmOly#$yIQ75mr`rHn1uWwy_^8|I=r}n`1hxRw*+fM4!wtW+l?Q5F{p>Lbi{a=6B z{`QG|ppVY}k}vzgHn0z$@+^IQfyyX9;+0lQuJ(uRVSiQtzdEw*pp43`|JuH>e{C1r zM_H85wvjLWHTH{q+rGAmenZ>bcCqj5=cXk4SRZ^)S*WMqNS)WRD$sP_KEpR&KZjJJ zg5i9hLugZy?O!Amy5!B_yQMz6lksd@KIOg0O@;R4e;CF43_fYg<9fz>9g4OttoHfQa|hp2amI@+DJ^}46U zm^^LJ+JM8QLDH_=0or|m(H5)?xH)kBs5_4`ZN=K3S8&Zpy(6@dwrSVyp0#Q$c};lH z2Cc2R6!o<=&!9iFZF~M_U1W9ruN`_ku*!0tyyn1#lQ)a>47jnt--K3b+Q4twHOiL* z>zUzO!SDC(CEy}Lc~@wIwDemR@-9)||598{zb>He4q*QdVa_9+hRz$nji8S4cJ68M z9H4tVwHbKjMATnvu6lzqoFKV{>={}sF@>=&Cvzbr)F zDcN~PXuCDEaX$KPIc@WldWV7SMB5Ibd=d52MYG=J`6b$-J20c^n|{bxhifU%A>^%u*HiG&ggpN%s0{oaBY!Pzc>84sb^8NX8vc#7{gbh@1{}uaHYXWdt3TB^-bw++F8n{B z&F=v>8`+J))i=5qocf|K0=EYq^+kumUjVtalm0^g=^xg2tG~H8&#}mEEN^vqcm}Rb zl;xsr4*;*-HJ0&LA0CVwHlDX8G_}bZOK1FCKHy<3-hZnsyTCI5`hSy70b2|lFLOT^ z+^=&z2XH;={0hFilz&D3JZNP=hE$<^e?Pv-jC2aPQ7qojNuM5+jw$)S?_{FpGZ#N)NT91gE8a!K#UC^K)(K}r{o)d zuHWi&>KnhTUi%Gs>f!pb29k_5e@tIAgZ2jUPea$ZSp9SA*!t!4(^RLt8}RyVO45dV zXba=byYoJ&C%Z>R-98OtUfo_@MxzpJ~NE>K>ctz z_0&5Paj#D4IZs2OVT@%;V2leYMSd7^l@F)=DBH^QC^Rm^(+zlz2Ci@77qTozmQ~~# zQ)&F9v6A`0>0XpL(nR<$4${4(?)ChZcF7RNcbsQqG1HRflGag{Glcr!>Hz=lISq$z z_n~T+eZu>r&B6Vn{_jYA;Ebm%4X@gFwZFPwCOhL#TSYnE#aLuaMV*Q8bOE~E;k6%E z{|n^{>iHif{_lwUvh9~R$^-Cw5qM()w5!g6jyBH5(60d;WhBD7{oe@qy!&_x&j3*@le+zK*h1dM#e-3_QAAUimIg}5jUEH_i|B*yf z=ANdXk>wdU>mmCv;Eq8*mh>j|BPd@74|TZuza7R~D2w}lv{Oew@2~&KKMxq=3Fd-V zd$#RnJi&kP`W*Z@xM~yEh8{wh_G#_aq2SQ&ZTo8TZbDl)_mlv~Oytupo|iFd+pdHs z?dIC!jcFal)mR1l%zb*sNEky=7g%lQjuFR-d!Uv?Ov52mf#7?P6UF$QHF?PQ;o zL`M71_>qm2H4#5oZS3ybGzR5E-Yer;YC%U^xHk8D)HROJF>;H#jvr%4rh=~)?~k&~ zq`vK-U!pm%?rSvuLtlYyWm_9(qW{9au)V5-V=no&qx&vx@7g5$M_)!K^6d+K8GaY^ zKSV>%^L6d>>hS7VFrFj`9^+0ba=lNUz6oPeJnPqf*Oze{INvA7gZsrCBfc;8TNz+p z|DP-=dDlwt{=AX7cYZ6kQ<0z%2T*X6xF z319yAi+kOwLOU;T?~>|+|2g@2sB;0_2C=1W}xAd{ON3K5iVaRh2 zek;+Izj4h5?0?W%Nd8@({m*IlQM<-#%JXXa;Sb96ja38ZFUZo3vZ1s=68KpNy|Lg| zmOtrJZOX1gt!ChjpSOW6 zO1XCDPk>d&(eC^ud}w@T+AfVp z{|VXjZ5HERTk-;U4I+y^4gJ8{PW2o2Miy0J{D$ECn!eMo6hb|H$A5xfn|5mWE>3xnq+Lh*jP{=)$T5IA?k&=m^j6@7JxRM! z2=Z$Wa-Uv4@Gk>Taq4P2(!R73d0eykU++XSNCsfv(u*Mx@rRb)0=$Pb&KHP8aRzf zi-3=Q)L8}WefZx(zUx2Z!(4~WrJW*?u_$Hzz@=T#b)4(3@8H?B;OE>=rH!-)7U14B zqIN-LsR$1DP>l!wo0RW^2lrLEziJP3?lSKr1E+Rb_d>ZBOTDl@eEh{VA2{3x<$k?e z)Nvn_Hq(s2Ek*XtiNO!Ov)msC);(A5pSntZKJwjjEmfzB@^OuMXk;g-#eWlx02jf1plR z&7C!v1mmKg^5P4jmwMzueVQY=KM0@wxK9U;y-)L*DhrM{LHzV%J2 zlc_tYUsa-Q)X$s`)TL}wbu#s)>d;k}(;uoX*No?7l)D$zKGHv_j%EL=qq%RjQ{aU@ zW?!j~=||O{>6lCKSNaYoyzA;;zKaPqe08n_TfFVAUZqZ^pLiu@>R#$fvGAhqWPhkz zsXOTpR`1g1tgpE~>BI<(zi&{8M+meiPbGCpbybR%dR&wLG`T-}b>G+vgI z@H+$;zxDcH{hpVi%s(8+$yGwpYdCVfS(Ahk?+Yo`X#Y6Fee;42DwDgfUeJnHU8pz{^*oV- z@a{9O4vce}^OA87?u$28;vZm)mGIl<_tMx4zn{)m&U@}#cV6;ai#YLfmGQV6sOuc) z{P-d5>^H|ajU4c042ApYokyMToEx3%-s4)FXXiG*+wPZlzI47jPaR`Dg3zcza=-hP zz>7I6mS^XtVZgf=-g(0L#JR|M$+@gG^gYMKm=)J9&P_Lw&3VST$$7=tmQ}oC&RzZ| zpz$ltb;i0l4?54e$K7v(bCc(==ZAI)aC&x%@iNBYI1fgF%lR!F8i~0sP|bDx8&>xreveUr2jeENOsQFfH}isS0~ z#Wlum@VQoTjZurTE6^+ltoA$4w|JfWAHk-hsO3H^&=6M_LQ}4jNd*GGjSK2BK&)FHHqiGxU$uD{CL@ElrXNRn(&Mjnq0uNib z??_wjg?1)nDHF}y$5_cq{z-UAp9Fb<%Rt!$^4n3~27Wq{SBm>cuDRjyNBU|H_*e0q z7nwhTehcbvr7zDTn`f7lC-oDS=P!}xLwHqJT}etpUIyw-1E#|N`o);>X7K$s?K6Rs*)NO1SrPbH%8eU0ZhIWK?m<_*F#vvJ$Mb;O_-u8{RMhPap0@OX@#V&uXNFhL z7kx-svPk5n{$JEJ4qaP>apKx2n((ZidWCwIcsAbL!^*Wm7=s?c)%fv-;BU{pG3v&f z8*>>1Ry|jHiTbQD=+ohAHMo=|E)D$wOdjA@BExO^tPSJm2l}rV*AL(;5&7-m%YC?4 zktZYkv?MgT0g z-^X6A%_*-99?$tJ3B3QW;`t|W@a&l;H<9NtJb2d0ea1l|>irGRcW8%p(D1yIVO%|H z*%m+WSkFZ& z1MXbV{*`(U;MX&e&WIbji>b4e@)+=VmO9nq&sg^ib#-&?Q(*|<`A?7O@4vvkn0#eX zAJC7a4x=ujexeScF5)+Q7yYb$qR*)r@ai<`k?K8{fi*T$-Nv}fIb79$jHC2>uKuAe zqps}tdkN3g!QnUE_|4ZDQ_~nzJ%J67w+TMu^&NQ9S7aQfdW!1+bs%FsU4yu0I0Jq4 zmXYL_AYWNtf`_b4_@9jKTPO{!;=pLX+Jwy7uNpEwCz7Ylsv$70S+z^KzTHax@6=zO zn110MD@pyb&GY;KxuuNO?-axnA-e ziGbgd$e@mSi+SWBI9E_VfDA`M`L=NHIxGy>5O_;izom!XJJgNWN2&W8@)}d@8qal< zYcJPsuKx-`Zw$Eq0q0@r^+sOTW3B;XDRV96n$C0fJloFo+HP>I1+VcC%F>*AYiNTv zlYoyt)23+b>oxj#1MpRtE8Fqxc@_Q0^W1&MlK&NaiM%$@(8f9&oZBdC0$;Du#*=_G zX2@9fQQ$SMb^`rlywGjn} zaJ=oH&K$0n>GwL&3Uc297~|2km#zY~C%6hDm$5e!q4zS+#=skAo|<-T3Eq5^&!hd+ zm$a99-gSNQ)sNK6g79ukys@f|{TS%0=Nao`OxSebjg4Q&v-(~a>iXUolVl9CwpVqz zt~@^=ska$hbrt+=xF1ZOHrz^Fjs4Qs8AO)iwD0@SO`PM!xA8ytxJv#}WK<{jySNCR zuaQQQrEad@RlU0*_4LQ)LN@nJdGvqAH zvpW7u@UGA55WG#Jd>wRdG7hIg>s~l8RcH4soNmBsZ>R|#&%Rd2|DL(P_1_G5t3eybFy7RC zZ^55>|5MUd;Pn?P%Y54IANcu-l$7LJ_Yk;Oa9^1FgW$Wzb5VFGZ5cFNANPT;v*2>i zw?1+IYw%5Iv`5a%z??>&<&39{$P|VgMS$%`{!if3H}1Ho4xg@_v(ukf$+x{9Ajdv< z-b=audi!`XGQI=sIdGgu{x-BlF6a)VeH|l3prtJO9rZuzd(h)RHhNk}N z^}zl?p8MYQ59>SCZ+!!J{h4D(Wyx13aBMmrV@Ud>^hIJSsh?5*?{}1K0QXH`UHj{EY(t*= zvEs-Ng7Xe?=&#my?S6Xw!umdM1EYUZAE$oL>(pCHUM<={f4}iF?#u57O=W3M-E2Jf zO8nxxYpkeoqmW26nKYbd;~6~%q7Cm#Yw3W`J%o)I8+)L?lIK;x4yF7eWASHT{H}}N zILYV072@7`XCvjt`CTP18+At_TLJQWhA^&x^}FslAd?x71Ht!%dVRU~+usmg4ww&I zdhVA}&sa_O`$@~V|0}fL7Ow6kc#r<_455Y8{T2LU0=yT{&Ca`-1lrntw1N3wB4-$@ zuaew%;CIA1e>J>xf#2`p$Mr~N+Yj6gpmmpbN`IjKHqTD^79LuV|2_95$bS{MQQ$JJ zF$FO7D7ygf{%1uh+B_3^+Pw5Px;J7UynRAH{71gB?4RboFWO1kZEFE%Lc1Mqg)9>W}5!ZApG|;F>`rFa2B}{A+l=$rz}}b0qw@Z^iw$ z?g9P-K01Os5&XI5#{Dw!{!yOi1LI!g0*r^Qv`uE|fPW;<>XMmBwRygXtnMFHmTTng zg2x5)gM037L3and%!1bJ2P(`YmU0~ry%_lXn`ieA zjwNq6JQ}N#oBI>gFNutI!S7l3#+yoURWJm=oHnF8Qn2AvIDJzL+sNI_)LhPjTU?Nj@xw%2*UR0Y>J z+=tU{aq#*cW&NQsnf$kr*ENcJer}R~k(3Aiy7T-mGX4Zj_xEUXO$sh;rKO;K96Xo7 z89^JRfyWN;UmaQ3aBT>kalmPF?aB23SLd=`;ChX#-xlLHw1pcZW318)aOX$N85)a`Q~&B`;7bOK z=V%@QXB+PQueDo@gOv1(deJrDi!ul3<5WLN!5pD1+CjBz>MvKfQU4kY4)rhX31^_G z4OMy7nbh0V*XDt52)HUxcLqGGJE_m9r*(tA`kb~=ZMN!A>S*e5o@M+BGMol>5P9lw z+I^4G-v5A4eNTNY88Y63#skW(e(z^9ti30$Ro;C`*0#ZVb+nz$e5c zPocac?PQ$s9dLS9r?JMyDjO$b9J6u9#?9;m#&~06myKa=z<652)i~tdjH7hWGnU!- zWn)*2IerHm%)xK=u{z?!32!dd@MKskZ|ja=>>Z z>bPgoxyd@KHO%t<~((g{5d>3 z?|lzn&SU%%U8n3P>K;Q@WqF@_=PlRrz_=H7YJ`OW$4d+>fm|G5rp9KepjeKPn6B1f0a7bMX-LMra)1{&o0` z`QNxRULYO(`2QbYBG)EpdcNyh;5-P-eCm(q`W|iR{)U_2DMNW%?md&XPbA}m>v!;@ zEZ-vMIq>f%8DrslpOd=Uw-Vwlw0r%+vwkjpRNKIRG3JGz%DC&a(7s0<{cBxmr)}`1 zk4+z!|Ak>}hVQC=u^qrZqxKhVYx>)=Am8uMaIVycrVmV;owhK2 zb=t}nQcoM6XGj|xpe$3tpCE@aCGf8-@~<57o4`|oJo2Q>(oin(c7|{1X&clZCl9ub zvL}WoS5|mf71r)TE^O{Q&opA^)7WN^)+>|k9;N;k0T1eDA(W|asgtR*89S`LHVVAzX6jPLgQ^FHQpfd*x}UMZ>Q=@Cs}mX{ ztR84QvGKtfsqeg?US?dd=X12;URl&d)l=1v)pgZfUB9U}8;7lqtR5VIo_eu*bAnE* z&a9rQPOUzy{(PQ#>c#5Vt`A3&jKx-$HU2sYc>V=W-CVue_+R6zM{!m6{SZ9r(=Dmj zm-`yj^-K)sXXk(CdG%ytc}1pW@Dh`TibwqYZuU$r`jAPvKsw{r1i*xU{+HZU`$$kEwh3z>2-}3wiWq$KLSHN$$|HbV2Bz}YaW`9Ci zGI-d?d+EBtZ}h@YzLn(ppIKk>Y`dNzZHG_$;B#bj-Qah-8}*+;&$Ge5AUx3Mf0kw>5F8SdTt ze+oJLR-XXQeIL$at_!k4BmUe0XsEL|5BY6(KGPodjJkf`FOZz~)L4Xdjmg(eAgsLk<)d-3FzJBK8P&Z%oD?3e|T$3-9@yS z_I+a-w8I+{;eQ5rwtoe9(1u@x@{HifK)&%6rGV=U@5WMmK;IVyx3>IjT(#?K(>K;a z|AGH8Fcloeab$(ZmB4GW*Jq$FVJR?K!80E?ZTrSlsQ>6gFot6>xT2srgYx;bp|*eh z6Uvetm?X$D09lM-$p9{6PmEj1O1*?Q7{6;_;5HVfB5h?1i|2(HTc>_vY>eMWeN-91 zH3PbDfzNmu^^hLmEz9)*{q47K3UL09LjYWxNI%jR#=oc=`5(^4@)?s;03MC0@%xyU z`&8r`H?szMov0K4UlPcqEbiT(4ULIhJr7|h`8Bxz40&==FAn}LQnvy9Iv3pMD4$O9 zEU5*Qtq7wH;I9yEJc%)C4Db`ajq9QBSqh85<2g}BloJ@&`J0e08L-#LPl}9($sfdh z`w-d&9&&R(iTayJo&j|Wo~y!xKGR0v^K6BDjQup=s?Qkq%m-!p8hUe(WfE7OzSzVnQR8^BMb>^U%fkEqfI_RD(Bdf6u6^o!xK5W@!Gzvo?Bt7RR8Y zO&Vm(JlfLI@?0AH^=YGtw8Kf--hE*1 zDf^qYyB|*b0;6AnZcem?{3qP!r%#rHr#$W1gF1zvuT8E1@a{JY&CWXyFKv1DzXl_@ z=HzNzr2D?=Lqj`@Ho;ZgCn33iEH6A5+meC0+9GE|+c-*Xllzf(LE;y(ltHes@LUd_ z+@Bgtn}w4)z?*wjx6potC~F7PyBIek;Bh)|QCzR`o+N{gdtTkw{SLUd0zU-Uzu{vA zSNFAEfQNjvp=Sx!2i|jrJ=b_Lu-dX;;yr8!kJ_jGUz*d5u@lhP4-cL*Xe`DU%HHDH zvjAIj-A6tDQ$XA0c3|w6GXeUA{06{I0k>xy_Mz-oa0#O`+PjfuF8w$R9>1aP zWq6xFy)~q=JP)8vJ>$47<=VJwB16XP;G+#6!)rNU9@BO$=<}q&U88+7QFj6S-#{MY zU~C`TQk(9p^ktB-wvzh?&_6+&y$7#l>5D_iv54}Lz_tfQS;~>8t;85M?IE|JYiykH zY{sCKXN<(bleU;T$l;lxzLWaDFVHtt;KjIlS2%J+W z^M9l~L&f*M6u3Tx*IMNHpK@=&r!gu&(gsVoA4MH~=-P^mZ72ooR&aRMOAz`WK%+76 z$`XM8^2lR+#~xB$@Ovg-{5}WzV=DEGE2#tTnZVNlp0&Mn1IHlR*m>3Y+V~IW+vDUJ zC-O10IspF}ZPSSQpOHMfuL*798GP?Rvp;nfP{%plJr?IE%R_!?cs3>_7xl-`2F9pp z-+2tKK~by+sXvsw-1MjC1Zp=_7Gu&%1z7Jfmd0~Ej66xKlt@+*~^H7DBTSb%K<2q%C_x>j7o%v*|*f@oPij={)_bZQNM9s^otQ{ifjc%qDH$ z#!?u^@dmiEQl~%9&&eOa)wtJj@OF&$am}oc>z(0bOr+VCUsqNcwSndH1_;mk)u>`I!z6FQ2 z)6>XO7J9}r81G;#LIe8USco%}|IGVnENLvbUDFtoYOF&y%I<^L^$_C4$E<2+n-5O~ zdCo}tDoaJ$y)4Nwmvf4ir@veGa?=;|Q!jIqDv)u2d&PUX#30xSd5dC$Ca&51}kg*JO_W^r^_HvKY zT=M$Huyz6F9M?CfW6Vl_a9<`FW27BHS@t4_af^S^x3P>X$JTggIhKr}G?~W+BVjH>|9|Sjr+H>D{Bkx3O~t!9SmOW%$K;=hOBM+PsT@oo{hzIpOFy4tTNFe@5D@7S5Rj7oe!u0v=X~e9_uPADo_S(s?#w(f^OhxD8?^Rg$2slI6}VP{ z!??H@+DluscI_#oX}5OVjG?b-&sLUH$T}Wgi&Jj_@LmmJP6Af~@cVxnl_-B0n(uP& zN*VtvL!HR;Czta+pL!e5<$oK0!Mirt{#<+D>o4eg7Uex?2f*nXW*R(H;=MWfTPe3E zls15;kHK&3hWh>mp4GM0xr-v>GV-&5)Bjjq2Mz5A`mQ>2X-jZD6wPA^WS9VsH-Ni_ z9R7DoVPIdQtp9g1m$U`Y4CdYV%>mpiLd)3Ez3>qWPsO136|fgc-^jOAmV0UNkEeaw zQ{FYkW!^hMdpGdLf1X9oAHa8rdfEfrFVuG{+TnN-F~GVG@;_6$Lt9xKgB+X6a5px}vBo`nj#b-vcMNm9>4dDYq&p7HhWWeW1aezx?AVKEB(sxt2gqg$EjnfJ{3f2psjlR37jGb)RFm^=@%EO%oEjX6BavoVCm6?!(Lae||{JQMOL>Bb7G z6CDHI*hKfNshhbcp&2+IgFgzHI>ER4mHM2vNAm#Rnp88!i@ENP9Jyd;6J<&Z0+CJBCX(J5-kFxZlk7Wn1`wCM-Cpq6kB)E={Z#-%O z%2lFX+?!Vyx~^e76MZ{nTw}QJ%d^dQ@LZ5^h^3q zP2d}FJ))1+fR<~c6qH#)UOC`e0Y97T56TXLC)Ypw!RsE$xHv)jRRYSU<0?q``_NUE z-rz_9-ZYf?2Kpr!vyHc(NL~)$5<%w>b$lnB?*y45ks$;88?&$f@mFMyrhixDeIfZf z$d3lMd!xg6J`Ik=@NyWsGjmW59v6e#xco}w?PWY@$o)gw>Kt`E9o%mNr_ZxI-?9J8 z{UVtD0dKp&bCj~i?{B7kRspwy_m{|^EJ?w20(pOi)tIzjXmU}&TD8dzrJfo3qF6j}#dOnc*MAIXmKD^(+H4_?+$iYFAGQ|S>6#9>O|Btd~z_&UF<0kcR4!p|p2XZ6> zt}FOcz~f}-9)gDk;QR?56Xrz!h5jP$?#J!HeFikE2WUIwQ*Yk`{%p`!XIFRs10K}z z+kxW(Wv@`CBhO(G=%wI)Mw_(&*9^*rgz+7buipM0*Id%vx0@9_LrC|m0BsKH|L)r> z0v-1bccV`f2Ipe({{xq@GzYhKXl>Hk$+dTDj}GRpef%nIS&_2Zv9kj^f#(I%qCaRO z*LJSmP+O$-Yi;K0hQ_yP$8HC2+Rcrf(=OQsUYby!>ZjVaJ=0lVz51oPYjw(Lw|ogs zbzAL}o^cXE-DwBcF0TDk9abB974F(TmBsTxv^Q&e_Dm4Z6Y=a2=b#GQwM~2OhxXgH z@TKkh8gv`Ok2Y@Y=i0ZmugBZJJ5o-&r1ouX$?E;aTWF6Az>{`x=R0l5#!nbuk&7!c zY1-PgpK2F(-qf!A1(&fM`cV^rcRl#DJ!spU&GWvnSF*H*mk?kk(N7^ zp&Vs1!owQm83o*%;O+#D!K5AHc{w!31OFEIZ-cKURjNL?4;kx`pOxnnl-&_dol^g> z8I*~>mjYV0%N}IQLOs+%_C&zir!&J-1-_YiT%P|>n|>UIoFBrI`;x<RAni0bkAkNK{N&`aZ;zrqOCU!SJk+J1_oHo7LZ>L@>QXKtbhdDfhOYaUw!!xc z&QtDxxCCwIK;tI#*SSB>xyiXeJEnUD-Mj2u<-R-j2sp3m7j$maPxuzJ zjs1Y#xP64iEa>yA#=CQEF7jO8yH`;As`I$>nscuE>y;%Ba=0#Vubpuw?xEWS9~Yrb z)x_1s|?S zoUhc=T<56c{zjT>m+s(L8({nZZY%P*t}_1IGwwcuhIVAvHw(BM$I}Ygnn7P#wgB(` zhXOp$CO<3mwo^Htn`K=M|6g4;c{74zsk*)#Nb#1qGJjeSHJV(#JhSUdc+#I@yoSD%*5EWo!`MmVI2t2M zEd5m9jxnJ6SmL>xIWK8ee&J}*cH@4D&CFvj0D$?DC(TN`Ly>P=;tJV8}tr>ECsR{52`HL;N5u3 zNaQij@gZeA)B7fuy0`Iv;{w>hxVHh1@rK3=js;e~pmCEi;L-ln47|=?>fhRr+5x9c z>NM~EFN6L<|3|?8a%%-I`T_M1Y7=sPyp`aUKk+Bz&_6gI82yQJ;N?2ywNKTBmi8+D z_h1_|D{{G4NLif6oFiXwIY&Ad7UG+5KJi!XMpvr&QQ{iLT4I$ zUMH7 zG~U?$9|E7oD%;16Gqx|=$7|3Y#x&d4{SN%*{C4cqep|*q`>h!J>^E{3I({>LhpC`z z9J6N=)q|$-%}*$Q7Wk69KSz#H&>2g687u8~XDqbwzb(jjzrV8hJ!t#)JMp{m`|;cG zoA5iZpZkr3g4=%TcVR!azuQmk`+i^7$oISQn=_W#IA-nJ((}9XTPg}}*o^y@{5Jef zj5Rhs+3zzc_jb_p`|w-qMg1F_?0*6rr~Q5guisY&aQUqmC!G%XgUF~X_WQkDeh+>x zek1nv_;0}e?l-YNz*rA`zXju!?c;t+es6t&iT{qq0e2ogW+JCC&wfLGXTvGux8!$d zzqijAz z_@3`0E%zQ={zq06_|s8Nd)`v&s0{b&w8K>J`@e|(kK$(PXeV_2Z^ehy-z4fZ0A6i^ zrI7u7(mw!i2JTnD@eO5+-}V2s+^n#0-?&Wjs7H5Zl*Gsp!wE4K^ znFy`gls9%+|3xa=+4H1aqdE6Z0_RfN#du}onzcz8TRa||#w=@(a!;(W&PjnW-r2R9 z@y3Y*tnGQ%N8uWxIy{ZwS^Lpic+u805gehsCkL;w2d)=gLtdwBUeY>nDU0XH>A%ru zqwc+hI%~|kXW4C|UbfPv+630{ei8UR+&2QJ4zC~Reh7Vm`rbnQzC&7O_|~`VId=J= zc@nujKiIMJAKuS_^ILG-;az=S8~gjD?FOed0{vOW313F$JmklnH^uW{()vPY5pXM^ zd6|29U_DcB33|oe5hq?UEjm4BN zM!(2IUvjNh6`764X-~Nb%4`Fd{-%4-e-|E|*B+4OUPfbT_rkm9)9A-qPZ{^3DNA=` zxrW@1;qL48KYLq(qc6{Mq5S|E@9}(yyZ^;fm2$(#&kLU7@Z)}#`@FlS?hG^^{*RBW zq`3}DMZKLOZwc`6|3&Qo1dW8(PVnJ*enqIiJ=CW%ZQ;EZW!wkdpYQiW_;arM3fh&C zvk82R4bX1zRucK^fu{{+qLC#Z{Ahz(#$6w57Ua_JYX7zGE~E}TSM4r1JSS}y{ZK!x zKHD1>XtkAiMR$c;kP3ltZ4RlnDd&3vl|sBl=v6!FO@`UMA?f@!m7bja7Lan3mlA z-u^{q|GQ*1^7u{e2mUB^I|&~1k@twSvedXKzd!zL@h3h=^aDsB$H;soeCP!OoKKL{?#~7FU)Z20DXfrgl zlREBAqE0)~4#w6PlcNo2IlLHSqb!qX+n&@*ZR%<$cVoL3bI-tgBg)T%jxpa2xEs%X z6day={w;X=QI}PC{t~+7shc$9U4YN-)cIuO>reS-$a4W&j;+R?uSS+h)X&36^h@x0 z9$y&zC#9`VKzj;hjZYs=oh79$KBX?Qa!sP{x*}tB@O=ZV1_9a)xaY{`dR$qwxjF_r zRyv+)L)8}hCbB!$YCF|7s=f1j>c}x!8>==@?XUCT!?Ab(e0%Q9OL$oToOV?0re8qI z7^~gL_!u18X=g#BKk{pzbxdD|9N$y626@_Gwef0q)eby`D?VSIy3IoJ*$hke+Gw@3d)fg^gUEPD~zEopgjYD0+ zJtg>zll>ZaZMpYIGxpX0c%A^hN63?q{EvY%p4B}O#?RKE&fRBgtnEbLjd^u#Xl$&p zt!=rxhFk-_ihcw1qvMo!pX*BQt$=xl_DzXQ5#ZTN`Z4f$R*teHBmFMrUy|1o_@wm3 zuHYI+xlrg2=DY9=mL<^fjFFz5Vk=u4r(0TTodzag*tsLNA-3E(XYc}KuC3L4MJ|CVwYc+Lr) zFX)%a;du#nWl0YFzwn`P_7S|arR*&5z9hXpY02O#4BTVk-}84oqh}J=9C({aUT5&^ zp-eeoDpCh2cwP%1EqQMb{C#+>!r1u&J{pj}AO1UV>1$mKKl)jZ19OIVeZ5)W!Si_D z1BZU#$KWZ%chZ(Rnh3tU^q-v2%uHTA(p&MI3I3I(A98&M{7cFvfo6B`c-F`P;MBuB zd!!5Px{rL%6iLlpJA%5EXFYgcg6oKG;PStEv?H8EM$Zu09r9`ovj<%7lAjSe{x3`e z>NP2Je`KtRp{+b$1 zFtpVe_gyxeag+A!&IL|E-Z~7DPM)Uivjm@^MFydN8oC}WgR^MzcJtbM}_+CL*Cs3uKwJH@*h&R z5btNHAMH*V>7Og1p^kiydhG2s0fn%YR_A>S5w z)&^qicO~$-uD!^m{X~1nBg(pFo~ww4>)J?gZuz zWwe93j&_Z%J@gjuNx8J4egJ>}!HZ{yxMugPP3@GPtr@qM92}mPVQhIcu-eg$MJU5% z+<|cj#vT}pVC+Gx_@Z9fjJ4+*KhQZqe*j;6OoDL)#u;cc)($b&Zk>XW4?Z(Kqq(iTuxp5dpR+nDxf zV2wL44#7AD;}VQT(6?cHf_8Z06O27*2CT6Ovv__U8~$gmeKT)6~IrlztI2s!Q(XO+yy2t>0gqz zh5IGm-vCcb${&R0XUKapJ8MaBs!O_NzD?S0`ga%lnXKy7d|ABuGII>YM>N@HlyC|a`QTp*IXU7Lr#Qoa#NB;Vl$6^(tjKK;O_e z@A~f;@CB$l^?cWcr${r#a{$lk>qp>2y}LixU}zXSk(=l8l=DBLGEmkv>I~?(hSc{S z%;j1!4ZORC_3Xs}^-+O3_5AuV)(dzxaW@8I8g+W%Kl2h;YQ6IgBO>FM{~x#tGH z7<_0`Pe=WphE@dkd%)L$2W{^gd2dcx{lO9RfAuwO=K0D01RMv*Q!hFO56Qu=4tJ6= z+UiRpUmMc3%^yUTe$eqh1Jt1cz-^?Su2RN!nFf3<+Au40^quSHk3T!1CwJDi$ z;mP^QIa8mzbIpD3`t0@H*M#+qn&rIk-ntO#4lI~vQPvQLrc#X%{OT8@!XI)12DIm+IkALpihW4YXG;hI*v?g{d> zGY8;%E$N;)6bxVWz=lWN>v*#gx3Edi`MNp;@ zJU#>V3h66=e;xYs;O{y0rcHY(bhKY@Bwtw?LvISQl!S&c_py{;#5hq1_$lO1&&k-z zy(4^VBmEMzV!3JqKa0B3M*afYiJ|G5`DdXq^Yfk-(_z7H&k^AU}Lx6GY^bA?YV(mq(!JmTn`OqH#zxk*K$I_kP8AsU-z@+2~fw#@1 z8Dn9*h4B=Qace1)m9l9_cRb$&jAy%k%)7R$>A>WVpgrK>ZSdrWw=tCe4}6(P%LN~f zr*lZFKppL)Y3`%C*Uqj`dDzMMD~xkkEeWoV0KX^8~l`{ z{)fU(6!=4V_kTqkzl>*dJaQiVI|t)3a{NtM=hGE|S3LHk?0NE?pWmTzNmilUQfOMA;oxL`Lc{$@j$Mvnd7K>L$J0>i z(fv`MQC?ZJ85pys%|RQ3v1Qr{K4$!M|EzWf_vJR_S(|~j3S-r@O&AaNl5)ngxldPn zhV~Bk^o{{0?kp~F8)IjzoO^uT3*42np}nRI__Z%-x6xjt9Y-6JHXrv)SLHHhTsx9B zCv7|Klh%IZeCD}k+Ihy3X6*MGF5}y@3u#;Wk21#9xo5m1Y3`GD-?#sG97&Ar^( zq^6N(+?{r(5O{VDElHZTCS&X#^6nn;7tm7{?U^Zg{tnp3z)WErabKYJ=T+g%=X_r| zxoT3jF*wTu*Mo1sy@z>$jQ~y`Tn+NvqvU^jYs=op^&fJ$zwifOrt|(L`Gm+wWiGA0xjh&q*kMnXy@)pMFQ}?AwsrxT2!=QFv08sqm8m z9PYpT6d3n+{>;03GW}of)!-UPnVG;p0l&8M*Wtx|rP{rxkfwiU8|m85+j7^|?S9f1 zlyTpwwsZFoX)j-gd>z28ZT%_t$GmHQe*q5vuUi}a>`=Z7>f1Ho68ebgF$)GALx4Cr*?x+c-Kzz1$<~%_z2h()Il_5<6|b40DlZw{C_3SU0Xn&`ulXA zzXx9Z-}4z4!^b1?W^#Q=o@aCRLuUVX)-%10!}t-Iv;$;EK5Yjr;K`T;&l5e#nD!jL zjFr%q@G&^`TY0XiI{h8WYBNYd`_zSJ?Fz;NE-wi`2dSql+*8nR{{gRi zRQ#Win>;51&iygksg8nI+n#nk&k!HZbJiTM*72#KJBRnyTyrVg7ry#(e-|46!M8TD z>dcAG%ZaInoYa?da|ZCYjN}^y-u*T1F?GHj0c=9*%>Mr|Jnw@iWyymav*GnLcupc? z6u7jpT}8J2z&9klKm2`4S|8Hd0iS@n-9X*Uh37tevuTj0xBZ8@JPH59z;D~R#vL2Z zcR-mI@YM^LGRV^c{#?6egNG5o)#g5lGPczz>T)}>G=Y!7JU51Kc}_|B!r<;jdOGNp zqRl+-&;M;P=0sWYLdU&VJHee9{#^6=A7r(;h9E~Qu&$3+P`Af;egS{>s~G4!rfh4< z?xKx1LMu1$enb9u^Y_5or=C&w>8PW(DW}}uQYXJqCvVU$^8OoTM?q8G=2OQ0QVd$| z8S}e%fE=@-{~NUIpYB~V&MY_?bZ2_vuruk-1HiT!(cLC-N%KJYhYrruFo)^O7Ixg2U{-4PV+NB!& zE`i@H$TI3X=Xzxf{20g8jr7LUjpta!P*28$xz8yY zmOL}JD)~pbC*Uk@Uhdn4i6ZsB8+r3(j(b|mN|Na90j4^X>`%Ca@S2t#T5&54$ zdvC<6IawXo_%qJ~_WP>{yz~7w==eXVP2uA&c=DU@+j&fX{tCXF@3YV+{ci7*7XaUB zU>(=p=WZOmYYN9X=YM4>2G4$H#_jvS&9qqc= zHL~mRFTm~kc`&?Z1HL&pEzk4cN zBdYgL1g|y$*Nv_*7r?LUWcB|E@SMiJL%wIkxKE=8@Tu&J)gm2n) zq-#z0VYps(EvbFL^>h@M>+cHCe@Pww0FJWA=lXO3xLr#tOEu(P0>0nrtJ>DO^BrvD zGKQxv>Bi_BqU=WcMk?C;8uel<%sSxxukWwGsm;lFptF>FN|q+YZ? zrK61YPG#vp-TlBd$-aU7`@mBJKF(8i8c8{U3x=O$RcB7h-F*%H$UlNC8+iUeS;0Mkz8?cF?R}n+Hi*3Qz_=%3J@9oXUyuA` zyt^Oa8U1)au-XJ~lh=)VJ7nq)t}@_M7T0L56^l~et0NiPx$^K_2i}%b-nD0M-Y-yJ zuI)N=<)UsX^X}Sn0d?~u`K}9HCq4ka1NR^3Gqb51*Q>dK%?iv6-d$TZqrB@%*N+Ln z*M#dSc~?mP99|M4OC{1K^6Vb?Kf!s0y8I1VMYv}N*KYV52<}MersY{#wEOG_kA7Bt zrS5grSL%M&qTtYv9Rbc?=)d}uCPPnMRb5*Dr*>eWB{h*Fs+D&iYH;r&mRqy^p)$g zE=PI&(zew}w!c&0H{-Y1xzGK_#pI-7} z{d6Bre|X3S4fpp{2d)qAvE2PH#)Gu;9KKKecz0-L_x~gfg-7rj2b!KfZcM6sbe;k0 zK2-PW+@$P!U3>Cbf*;Ro$OpXp zhzfEaDjwQFS=<}teq7^jkC5-)nBH9epN4U>8IaSxQpWjuW`g^g22lo(po`Rp`<~o; z^&|XU07rlB>g7@J=)SFYssEMK%ZzS(S=;_mQPgYjo^)FVVY&LKx%clSJtsl7Czp4b7uYnyL{Hl-rMEWtx z*^eI6M+Wdc+mG78uYJ)y={KO81A6(n+mG_XgZ-xj?PGkzEMVP3Z$Ek${Ei#OZqx$S z^{Hcv{mg!4-?48k=Q0-LI&k)PV>miful7~H2mALs(D56HCS6&wP(Bhq{MILv_B%K$ zL)Y)oalmhX4S4D%1SmJo$ z89j~@mnbuc=Tp!fNkgXwcNNN2;ywph#|p|FcR1@m;FECoKSI>g_1n)RUz>vGs+2b0_NJ_R zBm5r`b#h})^Ke&h_rIF;&3_E6`hRoYUxv_kz@ZHw-X5?3eltMVcvf}u|LE_Hkh=_Y zw1ud%ccff#@MsT-=6(^{DUn6H$#`g}^ZP#~_ra}f+Ge!TR0UQ$jCLF8i(gs~DX*@s zEk%3IT=KP*j0?Qd#~VVwJvfY~v0l{YUm~MA+D7;tiQH68oPM{LdanQ<)`fakE$Xd0 zyd0zKPTDRd&oAKLc#Qh+T!ez|1?XiNa4ZCOMXtT{k8r-*O3?8vv^?NhOul~4!lWC6 zeTBSrJgUeH?wt~=ZZ0RIE&JHRm!Ig4=h2Im0E8%Ld<_lm#;2aty}eY-<=UmM1_ zM_arHFUADwzwJqWRq*TrUk_j!@;;FCcD(;W`Ui|Z?yd8zw$hZ@MthwBPFX5J^CNf+ z1NJiaD!{LU*CphAPnvs@dIK92;M?KepGVKm?M7Qy;9eg&jDK_w;z07&gdqd(C;2W8 zgR2oZ+H!4$rsos5Hz_TA6r^spa$iCHpXJ$D&mNR-13z!ke#3$7Nm>EYjA^(4--~FE zm(WSaeJwcuB!36^Jg-|>HUrm&@A4C18qr z`JQ!CfqH*RdDp=oz|-5{DNlN0$_)V53g8lQ9}SKm`d=H;pCji9a8!WaQR;aFH- z9~>)yGyeS}c?Z{FaP9^6=P<@u@_r<}ENO`;lM!6?d3UX^EGg;BR6)=o;8%ioGJGt7 z=O@AReab|UHk-VwyqAN9WxG(9-@wO3(xS;bMp|ERSWmCPXTBWtbJ9}-p9FfIAJGAr zs-%_W3Ik^?aJaW&Jmr@2yppoYdzL))o;5sM2S)AS0 zmhX|NJ=Z*VYsA$Wd9NYE1j>9)|Efma)FZ81$SWOUD0v%68$}vjGEU#=3NI5G3+yZE zPUYZ%r%scH^u|0-fwp_>M)Cd>zUxsZ z4N14IzJ->$)t5XMg2q#1a(}t9*gmNozqp5iqc`**A2bPEbCK;S5^2q`+uu4 zZm#Fro`$th03C?C`-akhD?RX0z`J+YHO9B3Kc{W70p}XWGo#&)JQ2Cw^W=UcW02g- zY&?_kOYUucopIQG#|z=@H{Oj)axb(oPM(834>;qdT7h#va3#SNiENGyvylBfdB!b8 zBa87{g`sI&mN8()gc+-4?3Zy_o&##!R%dujOgUq>meKbPlV=Rr$IvzA>jPvmj?5S` z&l`1Lv9V^kz-LU^I^bRh?^WK7M>ForShW}2r$Ng&HsiruTN*Rw-YEAzFG9wz$(ul( zlmXsYGS5n#3+?6LH>T}9%10xMxP^)3S=`3M85{Q(bt3Q5Fg7j$&+=tFn|L}Sn>>k2 z`Q^JhIE-gA-c8<>L79~=J@3luer#jk8pH1<`kM7*ow(+ zJe~d1ep3xx#>E-;X1tvJ#rQS*r~Sn~YQMEFId2$8XP044wVe^|LW~?!&W>M+2i>U;Dm$DD}(qro1sW?l~^bdqKV{?TwS6xrH=+Ju!TL z#(%meSRcT6(v0P76^NdHnsA7m2p+%Gv1zKr#852brL{hyQ=c+loQpR)Q4 za=_b3=$E35`!e-GX=^mDv@9^?8GHVsT{c2@I=J+CJ))kKMZe}OVD+JD%hW&WS=ZV( z^}Xsp&Bk57tNvB(r}|d)%|0PdJEOi@&%xFQdy4csTr0SY**7-d^OE(|P6Sq8Z(-=X z;90+`K3e^$`ck#s>i-Q1fQxti!1{jm_iFFdHmvgz|3;r=e~ z`rW6KXPkd1JZr1ezpW2mU%b9_ee~KwOOdydyp+I~Am4ok#z*P5H=f^p1^UzVr7KHT z`0D}8BY3$&-{?Y_!}N{Ol<{oO^UzDddk0|B&=<7#rwpav@UG9u^X4z|{RGkO+U|4E zf5tQat@ite=BMD-j<1bB2)Oa&|H5Mx`rw=J@+W;b5tnxTHozSRPaVFqX~60)$W3}J z()YkeB=gii1pKSze!T-&b#`FyZ|5bP#fgJC{-$D9i$uQO|jH}C_W9*;t zcQN2IhA$YtK0xlfq#uX>+{pa_&n4)~#@h8_PP<6G^@G+Q;L686Auzq6<9y!~JVPko zkUELs>Igs8;Uguy8mE^AK8=6W=KBPCo{@ACe*CZL*1%tf)?ILFgWU_g`$4bTB{{q} z2F1^pjvbw#<=EmJ89zV9&yCKdjzf-r&ZUlBj%Ci1jxWxgjyH}ij*ZTlj(5(R&auv& z&Z~}@j)%^Zslesf>DcC2>D=cW>bU3J>fGyi=$z}E*$mztZ=E}{!K-7TeJlldoF5$5 z9jiN&=iKSI@0{rz6F+Y{4>-p-XE+x*Z#X|W2RRQqpE&0@k2v4N%?r?Wu5r$F9&ny@ z9(8_oUURN=u5ey-&UEf|{)(G3$#+h1ZgXC7&PxeB=VIqI=Pc(c=U8P~M80Px8*5~I zQ8MuA&*2w$F0$t;lNEO^$LBoPqQC0PSr4p!vsK{M2c%DSD{%UMnuGH@cn*X9ePH^6 z-#9@1NcvdyA=&qhVbX_`k;}7@Be=f>tZ{*!C+e99p7UWGlK$GX$PHDLe0;(X1~_S#!GGpYA^v>W|M_NzH8KYE-? zFT>kb?)nKQAX6{$Z*b|?N#{FwmBn)*JSSp5m-_+Y&r$KLl=$;g+%NEgdg#ryj5@jw zPU}zqp=YLeo{GM8^h-r@VSq{Jq^YyJfQ4F-mSxJJbMnxm-L}|)X7uoEhqSl zjj&JXCmac`zrbS~*gjcuQiihH8a#hx3%s5t-@OX<6J_ySh&|BtOqD*+@Z6ODe_qOZ z>eX`}@?-A?O^B^Wrt|hd-22p)*6$bxs;7?Ou!>FKnzm&Efq`)cBR*}S{vFuo=}E@A-nTL!tm=5j6N`o;Y?$KhpMfVC8`u8*pa_k!}q zFz7!qF2nsbuDz6H6Jx+a@`r-2JvfTPPhZl8a{m`Rt&q7V_v!TUO~9n19R^c=AZfoL z`+wjV5By)?uWTI%1D&6MkK~F)t}4ju`H9+cE6}fc!((Iax!~09Xy<;zWkrA-td_S8nsE!#+4L0X@LKb`=`+Hoaj#A zdyO(bla>(}|MTmDb;kQr@a&63w+GKMc<@Y^FMz!T?YyM*h@vil@&CU%khYAnTY_2t zLnHJ5=6(Bs@#n5tJ8{|9{QoWg|H^f-Yi0XfUHX)JnfCMSdRO1{&!o9#c3rHW+Wk>$ zECcP4(9J`dYuh34W=xEI)pdA(;HQGaIQiz(RbSfPb+~=ceP+hvUxe;A&^pcgHJ*#` zZ2zrGyX$A`4SW-D9ilz$^U>6oV}WCaW6u%l-Z`PQ^}@SjQ#$C~gCA|KL&2MZ=hnb? z0>5M4AYhCU&kEmZvV#v<@{kun`z?pJ#N@xr`?v6Jd`2do9ghyf!vXM&B7F-uZL2$0i9huPbPm3S4GPE-%pX0PYLd; z;I2h}d(tk$cUj7Kwx;oi?tL+K(QjcA>9O?NdX(!5TvN&ohL2kCWPIXiWUULHg{1ji z)}X&Ci}8c{bM^P?<1LF!PpJ?0#=j13*OJB%<^rEFiTaKk&_9h;yiJ*-)PE}Y@eiAt z0B@|~3+U*do&+7^3-u?vH(&pBZStN`&KO2x5RdXa5Z*)RXIbeNo*Ceo0~qFsdL*=`pq!#dvy}<2%y}E~69Vr?0I1xDgthuS{xzOkafBHh*x3LM?zowjf zF(Tl_|Dn}qR+xI|Mw$EI+0OHB()Hhd367eS@&A4FpDp3qgRJhi=uCgg&J~=CIUd;~ z;Za|#K4IhCN^rMbI>Ar8{!$Vi)IHQi)KRpDs5hubsDr4>sH>=NG^4EggZhg)ka~x@ zi+YGUiTaJY&~WbRG3qYrFzPevE%V_`-9)=gBs{3ks3WPvs3WO6X@67?(k7&xMmF&QE2;Bn|5A5Rr%@MDpHgp9XHjp`2Br?9Elr(8 zT}2z6x{G#4ZE4!kqM_#*4~`cdq2rk0e!kk=Z^N(u*|U@UB)|{geKPIqnG)%F_AG~_ z;C&7Fk>EZ8Ue8Y&68_3BP&5v{=et;8GUP{aDCz68fTNhj=cC|GzT| z{yZ1NSo37?yq$U&jcoUk$N$vWK{?M^@(klu(8?9U8VHza)UW3&{R%zjhx6n==3a#B z7|*YPZ#`vKhd~oOo#5A)`xu@_ke?NtKf`}p+QI(=dXF^EXBrubZwFcbrY~fHmjCxr zk^1xusNbQfEF0n3*kWUT{hxrrz}7>iO*|*$c`@a8(zj!IHm*1V9ugyy|0hr;0b?w3 zkEibSa}FT?JKDzc!J6`28~bdm@m=5!08<+tjYstiwDa(x&+|KQe*&+ysPDq08LONR zUVHGKjq*b&XKb^v&%4OeM`;{$B5>;49Y?-CP-W=@d~x_X!!;9rlaii?XJf>TMO{qU z>(sYoa%Fglq0B;_9p92crz_8?;KvwsWqBR` zI#WIeS6kXQ0d3iv=S}3frzr<*w3{oKtH1R|dMVm}EA(1Z_xivGT zHtrLk(F3^)LC<|%l_;Z+Y!&I}q4k3F3E)#-XhU8QZQ=f0_xHLsQBODpKc0bfiu!q% z=jDKTE|I!}=M}YwFaLj7S={I5^EK*g2>E@X?f$nE0lt4|O@ZG*@H?A&7|S>CJLNqe zNj@)f*N5tUwbI~muW=|mO@udntnQg}pW7^O?SjTtVAJQorpT2CxNh9Tx%8_}=dv!2 zBg-78P``U+@YLjD}!I|R@A(i`$T7TNUA4X3``S66^K{1e#1!PAm8@ z*0%(87foIzo{cMZUzmIHv@69RhwG$z&~E{a+tkwna9tz+B6N~+Uqjm}OBrOC2A(41 zCq(u$;Hg8slq2m4X`Unhk~HU}eAHob()&Zt7_VXAHy+Hn^8x958Ak^2oDleZ$W?;7 z=MXLh{sMeQP$xeim*;Jd=G~aI1JH7w9SLkJ${2s~B|8wIy-GzOAx>{x$j zhEwiq=zR~3!=xKWmIfYMhB1}^ZmkBKF=^+aQx167ey-(|MO^!7EB~)&FZJU2TgIfl$z?oT zC}oOs^#Y%~8xJR6+sVHTFP=AUjGH!=H^KKD7~|cvXB$7~`Q`F$T-+M?li!)-8P|57 zIt->=E+K1W_*+6B`U#xzyr-vJPh^<@ta2Qr&&S8z<%9nU@JK6Uf0lsN`(%gEENRf@JzH+L=Nnn+uK_N%*;(;hIL>kaT}Tho3} z!FD9?d+@CS_8#Tk&*+}SOyG9!qI(Vd!-sR%4?I@@whVag1Lt|Db<;4;gRd58+9$Gr zuNtyxTX;#oR~F;4jr%tK+cK3Z=CpL%KLv`uFn&LS9`$^T83J)N?i`Dh$of8JduB_=Htm|onk zlIHo0Ey3p+X)NtM9K5cZmcZvs@Touc<-Ia_^|&5!73Feo;B|N!#TfPse4hX4e!mQq z_blTP3DEWUww6HCc)d29vQJ=NWl6QZ?i@2G4bK3Si!W*QfAcjNb~{xH<5yTl?_tUdRpb)s!^X+jl7E zzCYs>M}xZxblsEHl|0wUuB|tbHkmfn$32c`*W&3(HwG}8=l0-e%e(uzT!*_}_q?nG zyt@x`H19FgVKmnWtEORuAjRVcq;>QfxCXFN&#>)2J2U^Lb>>TQe$~{ z57Hxea8L9Y${RzFAQ%0NXMOP3psg(bf^Q^nFS*YmO@Fk0YJJ}=kV${D{_w}hQUjQ# zl+{xkUIMw?d#>+X|GRtJ-Q!-3XMNxL z-}SkFglzi16GGcPMEcTeAZt$e>Po&osVd<6hUeeFr7ZP<)7E{6b}$Y~8+l5uEXdFi zeA=yBkXMDid;>lXfKOYxcJkM`cV-OnY{m*)+S#>P&ktjMq(5&2w`)Xg+w-8gnY2jK zTw7{){|?w7^0cjw|%N7vx{bt zR~uPYP_{`Z;|AY&Z=UCX*Vy{UTHnNVRylW%l?UiLFy!cJ}-JOQtXS{m`?f|Zf@K}uW20Yj2-Y=Yfg*@(i z?kGFZaU941?%BLofqo9^DhhbVi0#1r&6xBYp4?xYo4#bsvo^>b(CR>a&O|QH%4>|w z#yO9s{^tSXd3jsGvk<)BfX6fO(!%py>Qz7ABJzHroN>i#fQ<%DJI-qAOuJDeckMMx zdDf<5{FFAO4%CUZqQ_kN%k@{g*TDIHF7MivjG>AJpT28jzx+RuQ22BYf%_jakfu$l zJ890-{&$8pCv8;5Xt^)Kb%XY)P4Mo2SX2jJN$R$9;FUc|JCu7Uv^BXN(e|W0>>~X^ zS+vP%yEDc~yP7sMW2B4)a1Er5&9$5Mx*Wh5^ArZ2l{`12yn7n7(P?wjRu~^srHxQq zpY}p+cCN8H!BZ3No@R^RSHf&B$TAmNvs`;CKCI41}>1o@=h%&v-A_ zc-ju%0_GSvlw}P#%F}<|;o1l7(zIDc@=nutjKlW7a0-xTd}Yco`T=Doa=9PP|MjXt zzi~g>0MeF{wu?H-$6Z}l9n&*n3Iq2#>0`3fH-SA%|Eo`$`}90p%(JSD1yMIY$9Iqv z9y~K%%X*nidDmAHz?ltsji+|q<~qu=jI)uxlX|WPj?C~_3mEl&*HqfPj9qI< zx@Wr21YbsY@vQfuJf{a&E95B)ulA80z}p9UfOjhG+>$nP-8Kw*6L@#u;}CG)=3QBA z1LM$b1JC>N3^3!-)xr0I!&vlr)Q#t(Str)J|LYdTIFJy&)Zwio|0~XToT|VZ%WiDC zU0@wmJoXBd3%ChZOC z;w)+I&uj~`@go+&SG5r0_tfA;OBrfhPx4R`t2C! zt-Yltcq6%MXZnkDNJxoo9RZJeSvt~;!@WU&({}$4_sYni-lmVBGrSq2tL~<)-#)VdK8(rD1dQMO2zbuJ1s9g1!aq`+m#%1v(+WJ^;seV|pF89nT$;^#`ca8TS+djCTIK@S`l+Vzt-0 zChrTL)8L)MeJ}Svfp`5slJcHu@0vdfo?Q33W}is@@6^#5`uPRQY=`D$aCHN(>+`3; zyQjxBy63j$p+D9Imvf@mM!si)I6r!pi09B2;obSP9C!D- zsG~UV-sA4NKN=ja&t2!c*T%KIYw5Z0^%ecj_4+8_Jnz9XG*W@vb-FfR*W`65Cthvb zb1A2-+O@f~<##i1p0ALa`$b^fpX0e6Nhp_wYcT1a(UFt;H1Mf+YykcMGO9zQhMsjj zA6#|dp(yucz_jC9$d!zI&+n)UOf<4I+fG~CrynotkQoQm(c4|m(swaj#$qj}a=x0y?un>IY-ziKkRqyne*yms7;X>AVf z{(P%t;U^V+MLV4Ftj3>RrXIKRtSv4SKA!QtXzx1(%pc$|_Dg%5>vZjiv+3X34z~l- zif7Nv+7JEZ)Q$GW2ynC}y*_JDoN6P+t42Hqy7@P222g`2UVNx(5HAAG{A(<2|-RN1N$k?gh9!YwQbf zyU$u%uC`unwWZF;Ws5|?hkdXrr%T_u4^g%xUSdq_jW=)*Ika;j=lPO_3P@RZ2^qFU)P6@ z<*w&kk2%IWc57=>mh|wjQ+%Y~l6T6qMcxYJui*VN@Q$QzYw+9|lJ6Gr%cK@5>bAIqUk0^`hkIw*S^Dz1c^c%rzE!x^x&JJALpi_;! zAo|p3?&T?)lJO`4y5BGlp960<%4DN#6u44zzZyVDjBpnv=3=sJmXIg+r@7Wrl!zAGn%AKbE|kwD-rr$B=)ROIgyA zJ`-8qf}Z=YW5Jt~dsF!MlsY&EpN+YCL9=fp^8qrAL57Ju_uyWKHuP-9!oWTN|8eU3 zXY$S>V>{B9f$KbFT2nt$;mtUPr10i>kAo@y6h3ZI=U4R&w;I?DB?&rSj^)&FFfO&zGKcBekIslA6x`l^S(i#n-3Y-4X0(C(j* zr~lk|ojUO2e|03_xjgscz`Dm$zxE*Dj01{0)0K4fUH#y$IgJ&%2)?G!N(_7$@_K$* zfcJa6`yXss;71?2|K+ib_jHue_BR)q^n=fZhB~wHLD~RQ!i(+l0kZhN9A7}EI`yZ$ zRX=lY+Ao^AD$eySdB!(sdtE`fR+KT;suVc=52DxL#j{5COB>!Jfz>}a8<;Ne^%xwD zfNMe75ztVds6&2z=(>N*b*OrS{`Ml|PY1RW_jO$Q&Np(M;%dnIHS)Cg?hQZ}9@RC@ zfL~cWPsTHB5|TF)+~wfY^KAA}HV=6P>4Tot6NUV1p`~p~yoR$2wqOZ z<4bsVPseY-=0Mg4$a9l64}}L~|2_ey=h%3b)dI?S?&NrIcn($t?vCX<=yRTt(;J*Q z;Y~Z6vEaij58jc;6hYaqsfX00IZi9f=it5%d?I9tqO9?Go@H|#+&kgJZ_6_tjg9u4 zvo6qH1s&(klkl_-+|F-jxyFI33$l8iq_Ne`rN(hBp#*T^pkZ2{&hJSf50~$`QHP-_JT^3i;u@i3aoQU zM&#E9pq^M1c+V?%3m)8SrJlJAJRKQlrcyo#JnH@Wy3T;hxykvg8CgG4-Z|g!c)x_4`sDPtE#Unf-d)ev2EXw%O~Da{{I2)U!k7E$ z^aJV}aV@W%#C3Znu25(OMZB`1xKC2O!Pp@8avSrql;>mA;Rwp;XBkL49i%=J0HZVLRo=AiJ{MB+qrD>%go?B=UPYSgLHEDA-s?fW zI)V0a?bfcPwOM@w{C%zz$m<%^HKqIIJtWaD1EA+RR~z~~>OTo}6b+tR&{J0^#^svyZ{SwLdkgSs z^E0k+3OLS@o&!4S3&t$E4(?CdMEd(A?#GZ*ox=6<68H+H9gI0t=P_onAJ6@ul>+|M zg&ITSrSzclC3#8UO})doL-nXXc~>{u1MX+kf$_8lkzIdD4ft_gukMkB^6w%?G*@o& z^o3;s-dGO(P{wa)Ynep4elgF0-wgfsT*kHPU(}aoJZd4{^<5^R59$LmhP5uZ^q;|Y zoZl@wb!?2SHl9Vuu$+8-Y5LXlcfJYSUU+T_oW8V$zk}T1^DGw6Y|$@=Xh9FiZ^HLB4m{!D z38hWzz*_)Z+RgQ&dCtmY^7V%$0FN>4QP5D9{or~IA4QR2Jmp`>q)Q}&~9 z>JNJA0dGOyHNIo|YH&K|rdYeiV!j4jtEXpTe_idSg~gBbR!Ec4zknJp`xontF(`yZ}!M+M^j| zjGa&?Qokuoy73sEo#Yuv3!tSQ&x<(mwG4)5| zFr06igSQj#%Hp@~ z4aW+{5dY8BvEf5#<|h4Zc<_5}Oq%0>c7DehH&*Tt>7Fe*4*JP~F9L4IImZ`c>XuV?+EjW#>p6UB(>WK3eDuxS)K4gR#?!gK z=MnAo2KE0PX~p3snDiac8Vm1>$!|xR=dENTtpu=zz;hp1WApU=DvNP|##b9F?S6>s z@SG98QbW56>8F5m&u;)a3BYfR^i_B@E_(o1Dc<#)zQtv1;g84@1KwQ7RFv}W!_fBO z+R8p}JY;+Nk+IkMZU2St74EIV`Htx?9|1cK{ym@5xhoUp^|O4-<($Svp16VKdN7xL`>wLJTco`ZgW z`k~{9=jp4by`=6tLd!8EKXu|+{+@&S1ererx8JXN-*42hdYI#e=kM2}j?}$=h3Bd~ zdyfAX;C}$Ud(^{D`j!7t+lc!6mvX&%R+g*KRzIrDr7reZ-jS^%d`}@?ooW`m{7yOb zu#ds5Zq$nWlJMkt{OU*!+S|yVo|E#xF5=m<_^$(}ZkL!kZbm(*gQ-uczl|eJz0Py` z9fOJ?(+v197W^qF?;yW%TaFRNdp6;@J?Z&?Qx`0c4C<0m&{r1ubbai+?i%=gX!{My zTSn3sP&W-}lSe)SP*d(cMg9GC^V+L`qwy607Ua}Rje)}akqoAaN%zaH{xZK+Su z^=c#Jj)aeW@Dp#lC=Gl!+Pyh))#ciVjM}x_U#gw96nu<=b{ps!2eF>#zo1`~a`Pfy zt)V=tDG1u?X~rwO;N3HiP6A{6!EDNW#=HCO9E)8aI)3ZxeV3xHE!~f&erMac$KwWg z#)iMLm3gLdA^6B>`yxkw;NxQ$`caqPa7BYhKT39Beg%(qEp^`1z-xD^L|;}<)&{nK zx)~_T!0W3~chxTw2A|7GFGHGkGyRtDflt4UJ`8PH#(f{>QZK24Y`+4p{Y||^zlJ`Y z2GDu~TuaFFJR{F^t4`g8g3EJ}Ar|a-~6r|KL>{ z*nD``KdHZ0yVw9=e+8%ZF5CYo@7mIKL4O6$HzMH;xEz$*#(Q)GV;8cWjGXEWfng=&w~FRhK!wedAzv{z~$h26M*(3w2jRUK8mQN2{%H68V{o$}f- zdxEzam%3^+<@8<7hVMP}*8pi1xQqpSh`bRX*54{``b=9OYj^<;K$xi==cVizthhP7#bznTCzBYB>$pK{KU4L3IZQvUJ zeR&{Vx%8#yqKrDVadC&?%{{8hVw_NC-e*F~_$1?!3h`W=yYWk|nG$k6f&MYxA3-NA z_(t$PjJszn8&hQ5(kbqhpm_tH{Exzp$g>>&jdywpZR3#~GhBo9;r<19J)7BBDdVHm z$K0QGiLy({>j<15*SaY3%5Zi4GH&zGSc0iEvfYRr;xaY^ATJ^98l1@UZ5k}*)m zEwu#3*r=Oa?w>Yx$@nB=n>;JT*eL(E!dNEblZVdq5V72-$O|IoBVd9-2sRHVdei>`QKA7=!Yr6RSy~3*akco z52pQj570jXd}C*zvkM-d06!y~b|XI&T*esu1N}q5`hU9FxH^#j4exC!n+HCP14t2I zTmaVp(^?NcZF_y;sWNppkUlqO;T+F3$gdBs zT+nc?P0u&(dcP)k9)Zt2fv)w{?SKVs!~tt3FpA0Zs9uMUGnr#4F%WoQ07ti za;>57Dgu1}QYY?#b?>6Kh5&fnFRKoso%{fGGZ$Vi($CaWvh(hFDACALk~(h>O?}dS ze}20~sEc6wl;5|0@o&k~XFi9#9h6-H-44L$ul9R(3{Wp`49r+~)K6}lMS1e{u`dO# zE_{yzZWw(=U41WUjw8lp{0LuFs5{3L#{&K8e?!AD!TnB-3GQ2RpOoXR<7q;k9bX)$ z+_R)C#tG|V?m)Spxr{M#jiCKXo0W0J6QN=J@pyR20MAcJ&ki2fE5>g9Lwh(cxF&Ip zGl}|gof8fn*FLUs;$ok{rT@J)w6%e`<}vOl7FrX-s7q-A<2+K7x^fMbD*V-&Xcw}$ zW+_11T%?R^C)ZJ~ag2jfmim-^2v4&qS031x;PV{KAny88R*<#?J{R$Q52GzTXV-Id zD^bofc|Awdvvb=ZgXekPMee_Op1|`+V24oV4{)hJxb8npeYT`uB?c}Myq^182e_v2 ze}{4_NI!y{2Y6l&?%BwcnL6nTy%6|M1CCad_Z-juw7dU-tIa@J8UXVi{KlVI>N%>j zfFA+=N66R~occK2OYsuAuHQXRwH$SlocELP>G`MX3v-~`fqHXI?^&md;q?=6o#FfP zT-Ef@_B>V3Nxe;4W?(i@ZY8*zAxmvwJR{jXpaJ;yOxDcM_x#_C-_FA>^u;`fZY%iF@A7Lf zdBE@FU0;a)5`889r^{mSc^2L5Q2Gos>+!BnXE$)MO z>(|ilqHkytxK?oeLplG~a4qmRp>MlruXa7)y25>>`axZj%!*A}Cx-jNs9&m#xUy%0DjuXkf23Ira{7HH(@Vf`V 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

>OS~)kZ#@ggqI`Kmvg$j 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%0D8J=Q*bKcU%ao*|-oOY1w53UpFFLkcc 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%0D`8edA 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 z∨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 z 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 z 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 n4zRrVZf4qNYXd|7PMVAjUvTD<2Jgx zKk6a*F!G(~S{;7zwGFgA591Q_JgdUz&vPuk$4+lk=iJ=A(9zI50dL1y=V1g zrz{4Vo~7d+t4M5D2U*;6)tS0j;5^sIb68{1({p~j{A_OQq~H+{U&aKhPphCcpXB=q@X9m0vf#dW0Deun1 z$mhH50r$q`9fs~~Y(E-WKcUZc==nY@flR)CjN3~AEF|LgavQVfp6@&qJVKv~;qb)% zOUQlykp7V?bncdYvx z@;J5~0?hj)&stlDJf6YknQNcZA3R%a4Q0*~KLFSHs`JiJU@K4;37xjk?G8*X=s6yC z{_6Me4s~sSzeRmXc#rc%$CP{$H z4efU5?A*(_UKF`=yj7HkLC<--b1cthbMEFG%lTO;^m6}@XSg{JbDrkB*YB?1(Vw_h zmjuvpAEExEH~SOyBk4ENm!!`qIWYQ?^gFpvQQuP#^zHqqZ^bP3~YR|KyKWQ9&x&!(?08jr>D7xsAGQLMYmHXQC zFX@-si|vdt(m$mxbD;Mvu>YdVD{yB*H!uY`FwK!cAM`@X^oLic`~WaZfqzc@Wy-6A zuP;L^aiCvbzP9}{?BKS_DYAo@IX0_YR&72gGqj?i&$ z_yme?BL8@7HVD{0+^;5|4{mqxHep-WOqyZK642D2v4CeOP8a$}R_xu5d-rS^Q)`S- z6JV}!UoRS7*r_qJ#&O@1zWfLpRe>Le490_IqJBT{O?j@3(di4He&NUk-)__=r0ya* z8CyCRezTPuy8EEH3VOq!`(F^_J8ZTFIgQ&bj$J)Z>3!~pBA;iFj|Hwe^o$oC4qkrx z)^hmuhp)OkpllkrD@o_zKN1;@InD&_g4pdo@=D_{_ixib3L-~e=rxAk9%M;~%^y&% zTv>tPHefVK{TtHe(X`l1;Ep<|5mS!nl$#|HY^Oz5oUz6CO!q27IN z*C}fRzw8iBKwD}n?fnWK?$gj-{x42ljzjkd^*y2SF@5G2uG3(%>a>mDpsnz`iAbRb*|_9(fMCw`0MZ6_mnwr%?I2x^lXnT`q=#rIwvhfzx)N9 zP;BR1)9pau>uiw8O$gq@aV@Lfa zriErb;MB#qzdq&2{cLDdLZ1(TPefZ(B%eq50O0e$e;cq*z#l=mv7Lh{UqJm1 z=+;0E*8qJ-xqep#zJE||Y~xDqeFw#m4@EZD0OxW&7GC9$D+#dUp;;Gs288f#MUH0h z@m)5G>sr`8ANB4v{et8=xv`Ko8<>J=1yK1@thU z&h^3s^lj&r2`T%O`x4N(hv?&LN+O ze*a+)$GM(~Bt2tnmESROe{@to?eAFHxW}Bxsf-=LU&VD*^vMTa6J&G@?s;W*u#a-O zKW1qlV>tSib8P>;cmjT&i?kG*9D>Hbqyfm`c-ndTeE2R2=e>kY9JfzGHpk?~EUK%z z)WCkOjTV51>z}TDx+leTP1j4^pU?muXTryISl3itJ9RI#YobBmx*y@qSu)LlFGiXB zQruVM`srcdJX_EI82k(^_wQtb?>=m>oVriIuKV!22Cb zsXV}(1#bZKeEtexrdF2nC8^BHn#`W%p@UuNVyUOR=Z?|I%pCQjf`xJUlGGiCo z%=6M3f`2ajwJx6{XG>uHXMGFa<&HZYe>yJxh4SyA8BH5Fesyf@nAoxINMzpvZa8!t z1N*=E_0a#Gwu=V#3bt{K?bzC}vSV+@y^eW}?Wu^(=7hlmyFEvL_vkwY4u+obL@^}C z(wV^ZzuSqqHqOU?-j4vbFLWFq?*MlNc(I7l4tbnQI6pB4XA(MXK&Dpk%K@D81?MTF zfQdz(81xE6#(vP6i!9D(jLC71^Eb4ee@vvV9H}{F&UvbF{R%qHKfZ7Y8OU{2C&obC6-!qzd>!@IM4U-)AksA53`~>NkPoT;>PL&%?v_+dII$4^C(L zs`HuiyaSB=aK7Syk6uCFdB|_1Wx$m|uP+1XN6>RG-!{swB2Q(~67EL?(-(NIOQPFE zU?0Qt3(8+2lluT;1Nd&DOEF}q4~=KaLmo{1yX3xi(m*o=Jr}`eIoA=$HVInAflWkN zSCV^ezr<#rlKao$#nAhVR2hCXfU8bko%__8cuug-2gsik`U}yiH8gjS4pX+9vzd70^0~4 z=V&1qT`SLeKYR6L{yRjn+}08r%)g*9I-&e->EZzjwIq4}JTC{c;xN zgXnjEL$3wxWdF&9Jgw0=3$ivR-2l$Mz7k%=u$}rxP_Er$xgP%)PVjnRTzMeY7t0 z!_mQSvER=S`v5%8NOIk)G`2C`+JD9vpS%Uw+1TH6egc5=JoT*b z+Jjz;;9;D%=XrXLdtq=qH)#X*@jM03u}FapE+c1r?$t%V7!go zo9JV7oTXoKA=i$b^l455&hd~wM}3%wxj%~Tt;qE`>Yp48&ArfcoR<;YCE@f-WY2)j z24hdhRkoMozk}Gtu~$Lr>qFo1Tx)QbLqq@M9O&v_4L}yhcdqxW!p@#SnT>08=}z5; z$WR)Z`U@NfZw014I`4+|Ozs^k?;xLsZjMcjqZ>uNbhBc&weWa}Trc4}h4)4!c$dM} z`Xk(5>0Yb*;B^kAPeWq~IGy3Yn>;Ui{*7Hd6Zb!8`W@U1{Bzo39OccBtuk`F#1`Yx zb1ijApzC_q*Wf$HZb_ZGXlMIwN3Q*EcyH{fy@w*7ZKLjQ+Cdv=uMeSP+u0trnQdUZ zYS%r;sO>#N*YUJ%lsNMBI9yx48H>L|eM9=qm&lqlP4eF^C5`#phevIBn|x+U$$=--`s{m0L_z6Krr*A0*_A@GBND}jCQB6Bxz8vy5- zGsXpYmdqLS)rXxB`ueGj>pxE0kAp@%=;`a$53VlJ$afq0ii4Mj`m(gM=h<$h{0ONP zbk`u$Htbaro*SVz9X^%09}oNrU8)PZKd%*K>Z&2>pv%}Cg8}tr^GA@IL?}pp( zJPLi+IKJlEvsbnu=Xv;uU!LD&D74x;^xsWJx2n5m4&FkjkS zhs5x%rfvgp$Ix*nIG#JU51gx%p9;t3VZ2wV_k5W0lWBbUB*Ep47u-vJDls3l(&J`d2H}DHd+nu_o(ZO z{Qz`&-5DT zenj1M@&oAH6&?pjBe>6p96uxLU(gyKge)nLmE@lP(%d(|J}+p`ROosT9CbNPs?GHd z`wg)%bDiC@pkk3b zkiN5xGRN`lkl`A<+{5QO_#Svph^B40Z^89iba;%ulc94M`<5bqimqj%Ue6gvQ}3M2 z`Qa$qrF|fFA)f#(bs3KQ?q7GW`V!z?VBc2oYfsq&uHAoL4>HlH(nWsTrtyoUZU<23ZI)kY3|ca5Oe1^J8*F$P3kv}HEnR#F#d z3*akLpO1R`^>FAEL)KTo*nX{%V=Q@MX^{S=57?%*;}USk!&_SxhEE50+I}~nQ4e0j zL+FQGk3#NC=rtO;_Jcm?<8#&r8vU?ce(+rzcmS;bj{KH<8D;;H@5A2p(eo0#T2W`4 z9tKxk{BHWK&5k_&Lo*%lo`vH-H1&Nirr*c;!QoSqdgsOZ%$*zm0KLA*5f8qzD0A-X z99N%pPvrGGUJt&G3ED#6F@S#XzR(&?{e5I`-dqDZ2TA(p9V0mY@T^7iSR_H&-x5__r3UE0Ne>^Oci_RIZ*iD>`Q)YrkblhMgB-&kzynD1Y7 z`v4w}Gxx${GBo}K_dzIfQ|4H+KIsMcegljd8-)Gcd+5H$lE~ti@+j#!{3ale=ck5J zZ(KzNc=(?n{~vr6+1>}H9jG~wWgs|%q3c-J@6&ABc?oq%p`kAKfL{px2yuZK1Fb30 z@;^KGN$w|EK-q4R|5bj6d-sO42Coo0K0|KzkN6LsRpjTu9e~Y^A!&|HD+52Cz9D}O4PzUOA#{(3dq#4F@{L8V?3C?7_99&0LgoVC8l!rR_9!0Bm@}$`IZJya~=09pgT|ix8VBDvAj&k?O-l4n^{9M~Q2)(#HBjEmlot9AFfYh3DpU-jV za~!_4sCR9~n4?SZ@OgG$b7A0$l1_meOD^q?fH_W5u9V;wL^j95$#wOB!hIRiUhdVy@qH0^I5+T|ge1u8UYJMV)PaWnW1kuK)}@v`HlVdtTy*BrbIr^V4FvIc5=^F>q5CPQ32dH20t0s-@&gn&x?B^x6yZvdomWr zv)KZ{RhRbgo{Aj%$oJ50@1yq|?u!Bc3w@<0GPOXLeI)k~U&MaKe;y)r2gm(aS$ICn z06RSxew0t-dMYyhMLI#f`>#ARWDxL0plj@YI5zJI-doUj55+ZLJ!7^@ zoB~{Dr~VK$tAXpeOx=++5*gjQSR5W7g10`J{s4{rwABcBpQnGtqT6HY&r|1mNOEjo zyVxeZXbamVkZWx^54zf~Jn0|$q;{Lm^-lEDHrm9!Ft(xg{hWIDtQ3H@w%rWA@!+IuaxBeBzDv0C$MQ=u5DM_yCr4sk=w>M;WLH0RM=)SyoXa~tmqwZmcjEg zbQlkhM$~!EjJoVc2giWFQ+@}X>!E*I%0?l_Ca!0vpq+zhcj&Ky#!jxkA^()TR}k;D zKxBpPW^8x?*iibIzApV+`mH=GUf-H$x;fsg4W2%<1+>L>==VkTxzrWFKD%j;H0Yck z`A#5{d$9Z`>tJME%Jmn}*2k6@eKK+X2)@g((`Ue{OJVp0!8SHmF0dvx##?Bi1XI5Utp5S>P$^GxJ+f4*}q_q({~-n}cv-8fge0%7lyX`Qk+mSMnW9@`=;2~ zea_{fkqvy;SY5MmeaJI}U8|W3oNGPJ(P=36?%CWG`Z~_ecDV*E<2Z~V*@~{=!0g3V z{(mPua<+udGURsOx%=F^V4qN6x*c>N47rMbB?!Q01$&K;&2D&wXIYYaRM8BNK z>ONP``1U_RLy_Sjaz~-hI^Z)>=e~IV@m!4DG22yelY&>3`fR`(f8sxm+>dKKiT^8d zjFk?1smpJXj8jtZ%pkAt3j4P)bkSTJQ()ipU1Y34Q{eT57%$&B92XMqBudq@X-4GXIAh#s_+icr|FuhSpb<`~0fQQ1G0Sy1v#J-o{=Pq1^Sf zZpdsrmFsZ+=cXcMg{%X|v)(!dV?X#gR{RnB1;FQ5^vw=`$4u^7J&hc$7bXYRb@{!> zp$|fziRTkIhI5Uv3U%MX&-t};^x^Put;Rdkyj#<<4ko) z1phnK&kf<(fRE?T*ONB&2awkor=yt}|8sv7xc%_INV&0s1)y)-)DHUW6>#%&pAI_u z!2d;$J+zPUSwpa&@l)B5wJG`CaQZ&=wdn8q#XTQ?D))aOyU$ZK@PFl*ZAhLJUWFfcOi$d$;KS}F9FYN^NhAT*tb2&_+$SAPyjg`=RHHt%gFZ>8@SKu2IcN;@*7bY z*)qew9CY0eo0#%>*uk^qW+CgR+&gCQ`!pH5%s>afe~ufAAlEK%)MY>V>EqR>QVc%& z)>mQ=&-c>*-kSdS4Q-{5_atc?cGfShf7|#Ged+qV&)Elo*WddHx)YIiJ^kY#xiKlu zd-bd97tYPK^W~n{AR7MP0+S!uwdDHHA3}RRG?KvA@rUs#&TaI~$5I~@#CL-J<^GA2 z;6y@SUBa;0Wps!{_BzPf5dD(@?;3V9bbk!YT+)8v_CfP|u8TrrH~OsM+Hb%H@KTsZKqCdlDHa2r?bpu$(M4hpZLjiTnq+&I^8gXc}LpF8ZdkQlFHz ztU&IWHNnWCuh)Igsld_Cyn}lEzMf0u89@t?xezqAQtz4H`q6Krqi5iZq)opKd+lrY zY@@5l;(phpz;{62WF%v*^{eX>cmL{j;Kp#B135hd=@7h!fTxe${o4BQ2cp+4t~;T7 zBih<=kz=AadwB0aKM}U|9HWuYIg1YI&?O;u35H(@u9Kj@`_?*P8~2$l02z`tRDt zq>RYrIaB?CX^#x8N-c1W*GLP$r10=e;A+VGHTA}b{Kxgv%-A2BHGpPS%Ku0~|3L@OhIxieo51Ny zc_&f<=ou?A3!a`gQwJQ+3?BgP8PNEb@=n~BNB)f1Z#F5K`$_N#f}dyA7>{kNxA7$} zf%m+br`XhUW{h_+KBW`1;&g%k1nYYt;ghLv7f#l<##`uHgG+0Aa&Y6-8}zIS>=C^>lW1MGt+-$9HzcG zWBjy}dSoL1oTTj9O`CY;oHo$T+QIeD?&SLLVv+AbAZ<>cj3qxB$v4dRI?uVWe-nAO zU0cmV-FEurE6R-vh>Jbsx)Aj%q0DN zeGmHHbFSx{t`K$phrx5*5@E}DT>Fl3e)%`@`(ED7bL)S}S_AtLdizfGU7Y~kOQGj8 zXrDxX*Ay4hzkTS`me-(SC*g}L5N{XgKj@A^JEJVl-bz~(_u z-&gw1{MXqGWQfNz?teH|gXjM_egpRh`e+&Wj08_zwj!4?AMTNM9rGA==)?U@Xgh{I zOnnjL^*!%;sBPf**!9$2@bQe17-)xJ!{@a9M#>5S(_Q|QSHX_j`vlkiBhE4OH|Vnr z8t+k79QpjtbcViTXUCuJk9Z9IKcF>;a`#E5M2552+?bPG+~=b1Jh-=k&xcLbr491; zfJO=0$2B0=eRh+#C%JC)5;)i7_5+&`JGgeF-}(-HW;-x1;p=*nXG}Y%>498Npy~PN zSHb%Q*(=dDO{iZ-ooiJ31ZR_$K8!`t-B**Ch8|bQ{Q1D`mbw8t}mU&Ca!h4 z#^k!!OzK<*J4F3hVy z_k+4Vm?#Du?vEm4Y3e#s=K5j_Xv~Ix6tvy%)gKu5ZB{{#C-8QUfd6{i1f30BFU|}+ zvNpi>u2)8)&r$C8BVQ?Koku>;6}lIVEs*PNXnP*fO3E+6;}Ldp&uL!D?Ry!xcR#_F z@YlMyKJ7Yo6wjP%-0n|tJ=`^L&zCj6)j4iM`q2fRjR@q}h|c@KZ;U)=flCG7B+!kb zJ-;D&9{ow=X-{7qMV)6njiJnVSJ%MZzhaEDdqpC+cRhXs`7>CkoP84r)&-|wV@FoKC8L+`{6gr7^_9Jne(b<(4GOk z{akOyW`4)MCOxBmZ3yotQcZAMQ?`IIV~K_af(Ko{U3DT}>+%V9Ysa;L@%o|&+R`%jw# z@0`eX(ckJ>=$nuyl)Mr+`ck8?o4(oIT(>7LLfbXKe!Jk23K=|S$2~6kRU@G5J{F%1 z-_56>y^FT}7T!DHotAzXLYeziBFGQG-}Bh@p)P>`N8IZR))$qCHs}hUzp(3Zu05MQ z6}stzjgL(Fcn<-48Jo;Q_df7$0lvB==yVS zc;5$Z5;%U_v%}lC$>i9{_{3?*uCFH^JUfDu16%oxa^H`-BnGxEG&X>@3VNSlyNBGz zho0vIxkfyda?c^^0Ph6A&4QL^?tTm3x1jI&M1NA|e*G|P&;|VL(EkSAMqmg3A?mrh ze#`Gc!*h+?gI@w!9#Zcbb7HRNQq~_h=Tv=xb z=Mn9sF7aurPSA1AIfYaMn18@;3;Ygp`_U(~cPiS&z4xBo@4I;m`EvS%@hA2j-}5ckLVLk-Z~) z{62KSb{(*fe(Lhbp8{Op*A>x0UA7{V;|}M1#t%5R9E)A1fOnAOSSgJA0odKS=0oVl zjTxZb8(PWGuQIu@3tiE}_=WVq{0I-vFm~U5GU)z_^nipzj1L541~TH^-N4$cKPm92>re+ zf$x9Bic#*_nDc<&0&E8KDo**3OvuG`ZtB;9=Q+m}pgkKIhQn(d$+d+l;JJ=`pK{ld z5B=|X8AN?5^l@$6b?S7;&avC2q0sU8oqc?IHH{kk2b>zHEa^2)hV75WK4fuyg*9p7W>+W%na^$I}LrfUSfaOW>6Po;$%m3e8a9E2p5ZBWEgjH$;zZ*cA2wSKu21 zt&Z5FH+-|fe<`x*=h^@b*ZP(Lr++0SG|B~|E~xQ^%Aq5ZxpeZaX=SNQIR zmj7wDulTR)w`d!6IR#(GH*>L}W2ob#Xs%;OV}W%X<-Y{ZM)IyfhCH;L|1AiBm;VvS zO#T=3pK#rTv?BcV*lr^Df07)#)dM#Oy?@|712E$$E025?NcW&ugYpxkpTKoII0}2! z1;?>s$uPbH7$zxtxo@rw`0k&MrEUW$AJ_j;t}buncO0iq>h}M2k)JXuyE3bjbks-r z)KhxuT#=-1%6uP~_qbPo^;0k9Q=W08uPIjs$F#~@oA!!A2koQ&^Ux^^I%tQl&?61G z_E}Bp#I@S7&u84_+YsXd$^V3&tj-r~VlArD$K{3>+_czLe{9#*`RG;QC%l z`icJK>EIfJ;2BuPI9#F3ID`^`^mU#I*9cwz(@*SLp#SuA4bhkb*8*Krcb(C-!WCSH zBCGTE6wq+p(bxyq3M;|i*oDPhyO!vTeBU4E`XI8Ff@XQ> z_)Ym0``_W-IZ+GjH;I1x3|!YG7J zJ!Km(et+CEeF^=lQs;bdPxR|=R$+L0c2ITN&~J_757%)#d)R&1t`k)ZqEA3eUA}>z z-+AX?&ddDXI}dYy=6uU@K_=Q+W1|V+CWPoZET6kmCpE zbIt`fgln5RqN90cC8qdfD#_&?8f3I#qJ_IjH%fbtyZ zQJ?F@z;%R11$26I{!Mp<*G25ni2imS`iqdYEjsK4E;T$)K<^Lq z%mTkulzjotM*7o!^leJ{W^6ML9Ag!0VXsl}R+qGtMe*Ear7RvY3`>E{shb5{2I%@< z!SA@%7txpNY{>KwT5D;i^4!lrmKP+7vM;3i_)1ece zdYU`V7xE0;QCz$JTN8SF(C-~`{g_2~Rsx`7%%Of1vbgg&s-PceasL;&g&g`)V!5vi z{kKTQD}PLx@x7z5nfNWB+nijP{8vdZ&!ua+o&KkbdX1qS_5r^J9ad3S6q>iOjk2hZ zF;cGg>PJlzPM;)KzC4s|0>;?jvA{P+zW?Luz2mDYp2vLxBuX!Ws36=B5{lFyC{;R$ z0@8a8MT!&)O$ebQh|+r#5R~4VbOjNSCW17TCS8g&so&?xec#vb^ZNa<_nfmkJ3BMG zXV1*+?B3oy>%(lt*jP{gHt=Nub|dA+eY^-Azfip+&krd(1xy%yp?@}({4b$JJ6?aL z=QV7nfAoE3f{#dO)qmTb`#JR0n!~9=Hw?&a?42KY(XF@b{^&FD)JU_N8$- zj-{RO?-=c$i*ZlBW5l%+dGQjXFH2kM27O7*1Htd9-^?n|Hkfk1q0Nzj-#@?Gdzi0MaxG0#=g0tW1CW#7 z$d{7^zrim9>o+|ca?3znzrTyXmm65W<9;*c<$iMX1NcDNz`~C+=6m9*-eJA9)k^Db^PfoqJd0s%uM%@VNl!uND)&tK2%JWmV zHS%sm*_;5nDtNL2udlf@=?m`HL#yk-uC4Y3t|NV{Kle}gS_-`9Ue_hX!!1oY+L9Rg9so^sDipgQ3e9(cz3mV>w-m&=zS(x$&T` zKa8cn-$gdYn&u~eB>2?pn!%f~pvL^VUST}0>l6EszrG9gyT#GWNiV@a`L2Z|P^O;u zj52*^#-AFKs;;MgxF7gK@NJxE47?WP>KdVfKmIe}F_jfLOo(Bj(7A?mE;ek9lB zTwRAL0K9soveE`rA6*14V-$>OGrqC~c-24O244MCyUf_MI^Z@|^efu|=OEJqrIQ$i+9eq-f@M5f@zJvhyjT!%pygMXg7ITwV4;-$+ z8^1gSoW+peMd0;|&451r4C=6tC=W;8jw5|sd*SCaFvbHIFJOFnTk`d_zC<$i(D-xD z`|bwb7PO_$!~eF@Zq3!U;}tW_j!+<@P`)HjyO|K`-ctnW4t@^k!z z#*=Zn;naT%I*gaf&2uO|%Kz=G3Lay6^*tLK<=8jQQ60ZMbsRg!b0zu;fRWa&yn_?q z&3G>Le`BunH*514V>}ZW{nX33>VH<(S04GH-!t|@vDW&`^uy^B)~{?|8yBb_xi@s` z2mS~8^e^jk(+}+bbLy)r%QY{&&Z3Pfv^#@#j=L%r_B^5#@O~0{GDGW0^527J&w_LBV^8vi^XxfopY!a07GLCEU0eN4 zeaW?n$>8+Nlyp}&mJilsa(I;g{r0>8S{1!U%VOX|$VW2m>PJE=FTR~bK1 zihAl{>cZ;0p;!!K8jK@Qmo|PteXJ=U)u3A)+BgPvU}G25ZOc-o9_t#Uu^#HJ#yQ*| z&sYp~aN{}D1J%hqFIc@=eOf(wC|7mNj^K$UZ3rMM_?S(9)r6M)Ji8C|Bl`9NvTTAZ z?oiMET224e0k``eHiCZ>gh3?%p8D(~;C`oGD(*{zZ!ztaWjuWV%s0^H+OF%c=b^PUeK(T)b+qSyHW(SG9p+(27*+BgB9IlwoUvAl@3Xp4cyYw(!> zKRcnz^IUvS&clm+rk&A&dJ*8sM!ogW+?M?D&~gR*Es#MSXg|*Le(Hvk$4{aGww*Xn4cf3&N46vRR>6wndQI;OO#vtDepqIf@QQCW#`Xhkb z3Jv-`+!OE$Z5vZ;Y_YM;DakiZZ~?p+|MwMi8Dsn=a8qg5*umN0Xc~qN4=pQ^DFfx?+VK?Xe@~fl+}(h00<1D(K2V8Zo6TuMu_+Vc~q6 zY3l>(sZ&IQD=EqIfYk+VbN!k6@{%nV{mD4*N!$88-s5=={gw}U^@(@}=}oRRxE6-y zXzG8+xSs{>##8j<**#lpxz?dxcE+dY_&x+rFKF8jOfG1i5AKhtx0ZH1yLSfpm7u>B zdFhe&q)6-z`quM&TS0$&@}Kf-9EWEk90zs|a$HHhI*iAz(BfQ|oqNwxkQa4-{mj}N zg?ZLrou6wNXi_KK!F!<}y9c=RV=pA>BiEpQW80b{N4K5wNJ7F?|vG6;%%V8eKh*ZqiA~?@a}VQKg=BZB@;B1 z0N*9@+)E=bpKgq%;69lJ z(5Q~M1$?eMC!>A`+SSG|CNH;b(oR!k8A1OQrp&!Bp4Z`?mN@XK(;BO-zS$9aqiAy; zNn5HpbVQNoUK!8W{xHD!gV!~*{R~|4;{VM|2iN4J;G`YnfWIg2F*LkEUPI)+lfM0( z@!~r3pXC3;)wSE?z$a(CrUWhlK3pFjOM49|Psej<+MNcB``7DJR+>ED)e7+M8nEli zu5A>A4((>`^*0!c{s&AO`oY+}GtlOm^iR-zGYT7<_EUnt7S{yQ2hjBg&+?Ln`zOFm zfw^p)LxXdQasAF=ElI}e@20Nvi)Xm>guY|oH^%<~xb?qe z0*7nPS;4Ju#(e^V!S7tTk-USz7N@TMqj=<|zvc$*IlsjNyPf)vfOCHJe{Y=c{-R8N z!+Ab}Or2NdWiVw&7+aNqHAb;Cw4UbrDfpas#?Yqwc%J5B4nj_OptZUDavez7B4C{7 z9*{TL_k{N2p)WIK&M&{xFU!C+0k|d5e3W`KfPE2u62NEN<7DbDLME?5S5Ih(M<&@Q zuME8Nr}3ekp}#4#&jRm5^4ve} z@O=>3&xXcEJnK(SfHv)d`@o-|U2PQOpY;E0AE=Ahr)&jz+CA#(vw7AIsD38H= zAGv40YCrUWm%Q+&|J{ei)jTWjSgv`ft9|DBh_>GQYAz-yZji>(!+Kn04KKz7oW8Qm$CkOZ1uG)&)p4zJ~1%iEN%)9+` zpFHW*w$+|?O=1YJ#@-L({%`WLP^Ss-4dLxBJZP_1A;r>$W6Jv03;kccW9AX{tz#P_ zN%p<8I@UsQ_4ZFyQY5Jt$vpwCqez=$sTcT_&v)b-E8m`bW#~GLvXTbJrG5kX$W3yc z#qm3Y`^5jYhj-6~+Yc^f2;>oLIm9>%y%&RFBRk@PdLl^CyGxoRKgVEndb?$`#5 z{|Dl^Oe;ufm|wPWHLmq2@IAoQk@?8~SSrBtGtxJdze4?syw<`m@b{NkLWo!Wpp#rXcYe<6nU#?#gwV4NHF(QnQjvGAg8-H8<1XZH*9 zwdW(RfnII*0kq?M&<7abS#54@_x8X!U$o(x1~~mr`g*kg|KZv5eSLrLMWTb!mhZUl zYHjMfuP;5gnn8=_F?%+%YeY$@mzjL$jS?jP^E#gLcY${va!zp$Xa+9lih0Ov2W>op z$5`@QM_vUzr-65^*frtn;9LtYt;zShppVG4x>L|wp1KLN=l_f^qP#x$#=d)wi{BMv z{~n&T zfmT3|_P_B6`Ui{`H8$;Q=+XzFU8Eo49<*rV%nDEkysdzjmrb;zt*?!*eXTvN{r?|y zYu{_bFF+Q_piN(Zw!e0IcCJ5CU)z5$d=KYYzqkATwcFjpuWf!F7;XEN1y0du(;JCYoH#+~kmZ+ZES zc0>05BkJuT?*d6%TDxB#fVR1IYqn_mUU=j(DTeWxocRk}+Tcf_M>||QT^oN2*Czqq zD`;&E9mY($uUXsJeGXxat86^?2j?>CKj7Yd5#>q7<&*(O67uy4lz{Fq`0`A|IB>d` zqA}x08+t$OxNaveTfo_ba&7uDlvn3@72|p>vh)1M!jwgehyJ|>eti<+TTgicvJEi4 zD?;x<+A9d|CgkadxDL;MBCq+-(}45^GB<{ABeHDCyIzjxK3v;CLsIIM=RMNKS_N+X zA^HR^aX%1x^anfx-#6gVSJ8_;bS=p>hI`;>1&q9CuW#nQDfQmwTAK3J@KA*3523%B zV*vW~t0z$23VeO39|g=n=w1g;o>dSD{hsso4{ffdZf$U1=DHky9Oo^ms}HR({AiEq z|I$ZL9zOI1yajKgNgqQ`9Ch^@c-DbFg-X!y2Y9seTTov+&A4CBBDf1J`q|`VP)_DJ zV2#zx4o&O9ot65YA?7;xP{xPr(PQZArsU@#-!;)GjF}en^%+uj^4B8EmE0HNegf^K zCe486^5mW6dLP_fp}z|E6)1Bpy)v+_6<-VI+X3FoBevNsGvH?caTF)8JQLR+8@;UvAoQUCy;U z*V|lob3M;>IoF_EvmFn;t^vCK=9;kUcCIVC);AG4w2x;4>sqwyg{~F4{`VPpj9GUL z*mXSPyRPT@qHBF00KXr+y`VuG+Vw)$w_U&Oi;P@zb8XwT zZh6^2zp4)`LiWaz#KBWEGObLWah}F@j^W<85MwsW(09q9wG#AK;<*hlyJ@=#xbBhH z54;tT=X=yK22=fGIk?@gvxo94@b;9v7SN)9+`XFWChsHXX7KU@_l4oB8F0m@zmn&d z;6Wd|aU*T$1LJ<`L*qE;ji&A4$WUIcgYSFr)lv@7YYfU0@)|G}%0hQro{dv!0R9EY z@+s*Ivhz&tdg16o)HUwYb%Xlw@gHqhgvUI{&UKAvQM_-o{Q`Z|i1vnh0@nTp{2 z3Hpq+F}}4N<*r2-Gh=M%U~n3D^D3znxT*mEBeeej{Lw&AkMaL8Qbq=OISX7x`f?(0 zZvv-|6aoGT(3~53+Cua9@YVu)8gZ{a=6_dw3ZJXsVHfh7NmB_);LVtl<0vz9w@YiyY2TH!rxaQs*7=ex$AEfS*9#pX6PqA8XLYMCe~Ydqv1^ z3{T(MKeSPTc4x^aZDpZMJ!v5Kpq~NX z4;<>2`ZNCKz7ct4fjQ3eyWG20>e(`z!jTurcS^m}^+DtM^o^*OY6o{f7V6vjM`FQo zHy5f9u(gnbG4}dE^>6w;{Us;gUikF;?Ht+$_#OeqCp7CD)t8!u@#Od0v$v9wuTRu- zg+_x*->rUEeXalTj_5;mFMzs1KJXl-j{aJGy82<&1xf+C30bHss7vS{_CEyG8`KNb z9X_P|2yp5G>I>=?@^Xjgo#5Y0Y8Q_EK)tin@!RfyvHM;1KmJzH{yb=Z3EKSzd&ZDw zWyOKx8g)HO%kzdV!@J-0&&ks-8-ND=ufHPi$Kdr_uHV!%h&;b&G<80NuTj9HKo*{b zq+fRm<8&|Z$#Www%G4KzQ11}e>(uc-fQ!&}1nucBmX|BQHz2tO*jV1L8GCoYV?1vX z=E!33V|=aq6!h<@ zu1k4!@Xv>iYux+4@$G3h8@P=h9u3ZU)K?#W%)RSkErB(*cOCaTAuTiYJg-w7`cL{p zUj71h0`hPlm+@m8xE6s%&-ub#y6o&h*@KlNz$)T$p}g_ilGcYsq@ zUIR~Qk>e@y^rJQjAYoV}_|EX$sxnHgWb=?!LUFQCA_uOeyEdus= zaA=#ArOY#!jzOPxSt?}Y9&qg`_mn?JyOqJe3^^Qz4&&3^4m@mM68MravbszdP;7d_f3w$qu0thEc}#h6xOU^_#fajpUC z_{~~Dduhq{yXSvc7?+7!EbdI^7KFW?Kf7TICTy;QW?;#Z~d>i%0@b!@2t1^xLd%6$MS70-qWjPr#wy!tbTA&FU!f zVl0$;rE3}q)HjaKbExhEKaKnrz^n$By4-Kc8K=Oz&(8Hv_x^RD-qirM59N(0uS6S; zb@f2^Td3z6w`i=D`lhjpoq#jGQGL|C6Y9;b*^DILa}tazJ4Ja7a2(0STtr>p=WfWl zGdR^#zo6f1g4@{K$IvV#xBJS<=e?go#oEO}0>lth}D0koPHE7ySS!hqMWBXHN z|AczZB`KiYSTyIE?BH?l=R?XifX98o?oD+raKEqbzjMK0@?3ZR8-Cn3>^{@dv?nk7 z86TlIr48I0-!zDdmG;KkUg5wzbF*xq~x^o5lKR|a(f;G#%jJRc^%9ar@+V~)b$ z;|2Q7v&D?5KTm#E%027Ovmt0cF~%z=`7LR8AMJfW{=dl8_3PBMTMit?M|I^|p7B!w zJfKVR2|s<|X#jOS$6lRAUMfT}_b?_q(tdBob0%bP7FoThoJgyX(L?IS1Lt`a*?=iU zdovgx{mAQ1UC*vq3S42{^9Hmx23#9SpMvKXVCq0i9CWsXx8C5`5A1nl902AJxEfK< zvn~7&u_f>@nEtBCxTp-S^1w!OpBo(e;3qfzIS3j&kE0*_mqrer@h2}4(DEDo6i0vE z0{4Y5-Z5}B=ea+4JUhhw>xGfk+uRpH2A<2(2;7IMUjTZyLr+2I@%)xL)ZGcZv9x~6 zzM<^|+PD-=|C4TVpPjm20J9sKI&<|KRh7D)2h#?aU&wojG@tvoDLW6m-{c77;Ciy( zA=>^h4=otcChvB65R$`EhjKHI|Zr(X31dA}he&xrd9e0BcEi~T$@ zCpds{&NJSA2X*U_F43Q%1X-PCCti7iUr5X#q3&-3dexvI-n=UyGP7H~Vk zF#;OeQ|}18c<#Jorvd%u`ugqwHUjOx0`3#se@P#?W?qgq)pe7DuM+ZA-_8y`_r6u2 zZfjuYgD+j~pkD3RKMuUQPCV_q|E(1?4&c5mw8)ER9zCF*eje*qAm9lOr>sT zp1ySXu~+4uSj`#t`EHbi@S|>i~;h$ zam$jQpE~-i-S27K(Eb?u0T^R`j3+WCr~%LVvb)fZcBHX%6{)9B+87}JANUTqjWsfU zC?hgnMt%LxQz+j9P4c3TTf0-cRlM4v{->{XMrLF#faWaFqK(@NSbf#T3LT)#_#pk~ z+SU5f^>J$>YtL4p4cjgO{o209Eq#Ri+R&ylaqqjfcPh$_rI+6?sjvO5eO{P$wYl}Z zYjf+H*KdD`eC=cX@SUJZc`H+SG44m-_H6QvRe!)Wigv3i4{-Ruz}l`Q#ShPW;Y0tm zer)YrZC+!Sw2SvsZwz?f045IHnJ7;RtoE;VvT;fN|B1G7Y4ELvt`AB2+>LV!&<~@z z*9QNSdfMT}J!vCfBx$ev-zmRx?G4SJalaTigQjA+Bws+j_SAVc)lQd#%{Ki<<_w3mQ6~OZjZ5QO~K5YHOiSsGAJyXPa zUBB}W)VYkj^+O+m6#cTh7%%>hmA+@^djHGn5IEe|qd&VFV>L5*`+(7ByqWr;vj~=g zrzZUQz4pw8Fxvl=w$=Y`am^eEe&79W`z^mh-4)Q_H`#BnzWdeOPl2aaT-E(NYoY{l z%Zz+F@vQ!EjpUlZV%nQYTNkWDUC$HnTm!%P#z}d`fbnOp5vY@IfNu4SUw}^wE$Y)@ zq+j5pDzKTz`xQ4IorR}teemDNXb^pxJ?;5{r_bm_p(kIk){oS~X81l89vVhn1|2yG~Bj8=v??!$1Od5mj`oF%fl+ctC8r|#A z9+>GQ_rs2$uZ#`oMctpcenWmFN&ndl*_}KOLUyGn&kKz+xPDKb=PF)H&b$)IJ>%nbWNv#k;QzlsP#>(u^&l`; zkn=;5@%ZVHS7TuAai0uUmmp+UI z_6P9Pg2sm6SOI(<`ojPC2tZ>d`tU`{rZEPZlh>HOP6n>Yv~vY{bp`f2>Rbh1ZDjY9 z)EYb)kj1;?r2-~``$%Xp{-YJN4y0~h#)z>Z`N3t}L<*j3(#|~K^PSR(_I97+xvhX{SvV}aq3VZ;Z`u#ln)-mkZ z?E((p1IM^=kB;+#;PBnKPJR7@QP7i?dZ#EKMmj?s{e}AejJ<40yJNUlU(vrfkZ0HI zO7ZOZxyESfYt+AJ{HOjr{gL~TMRw@kPJ8vZjzW%^Xnz^)%F7Az)%$YMzVnT~SbefN zpzSnx^q0QdS@ z&p@-j*>Gsom)j%2JPV9-p5IFM?`0$J0Cg?_<32|HvhF3+N2?F^C20K&xf?61A6Q;C zK$E(|Q2MJ)(xAR_5j=lTXDL_D12YCQ20rwwr2xNri|5U5p&jjW&$^uo&TGhH5Ovfo z{GZHEzmop z^8xhjhYsiG>Cn`YKDgW2p-=8R~T&>Q#l{{I|3I+ zKNx<-j4jA`(e z2Tv^bGZ`08Y2z!(*OEM6*mH;9rHusW>H+>*T)UG$miGPue=hi#PTocGYSYFl?rYPB zNw_~w@{HioycJ&Z+O|d>Cuu`3u!)OMYFu z(f-x-&niJaf`7X|deoC3^GxBnSI<8fHPrm;z;h9|8 z_{V`Yp6fC6o`rVT!;C?99m-hvA9yYaoy};&_%P$zT~l$Nx%;~Nk?%T4Me?-!(@@8F zcVq77f!pvnF|CsIvq7=}GQ2 zlNZ-N{kFQ+nHVDl{n~-rk=lpac5Q&u*7KXL-RD}Twxo8VHr{)bYnN)H){RV(Xq%4X zdW|x_(G@8_NZZ<=>I2%P+O2-~djx{IL|^DoPq@UhcCFuX*IZp&b^nateQjB7V9$b5 zUvQ1sI4o^hd2y{&J5T#Cj%RH}ZAR@&?M-bv z8q=lSIuCl);XL2d{WtQmmNM7#U9We|+&#%Bcy@i?IL~U}8wHH(?)s_qL#u!LO)O1) z<2c{o*%*O-v|pAy*ZJMU{2z3<*EpPV*Y{ob_uJ|^zwrdxA?{~2Cf)tU?m=D;9kZzG zx7_^>+ z-go&YX!hOv173_DO9`Ckclu6vrso36Jd@KHq|J&}_*{$HPF`+keoA>g`lvf}hk?`YSbFGo?fV&Zb|!~5?)6E# z-jberTam$5+VL!Q=bWR|mzT|qpX%Tnf~*G7mz`r6v+(gYatm{*1&l;yt^?|C8xQUJtNK$wctcv_FM+dJAn70;U1a>IUCJM`*vCdzbVZ z^@T?LQXPSdgf8cmDB5*@lk<&whOx@Mz&imMyiDF0l0L7-^zVz5S)|b zWgG8QN}lz_>W9@|+7$k;0ymZXxwLniYid#>$lYxtAAB{=O^kKX!#*I?4YQh(l&+xZgwBL&|nDckO`&_iSwk?`>e- z0>-^z8EB&)Froh|n1~FYkyi`2kz5y&J_a_D=hNIbhK5w&)~0@r=K{#$F=J#Pxcm>5 zBH(gA*?4IC2DlEiU7Kq+aNOm39{jHW>wn;NK|b>GCUhpvO zhF^to>;H`;WvsOYkN;ER|C(qwJP*u!(CGh-{7c*SDBnuiR?4&)G6Gv48k14h0J#{W z_sRcwIR!mm!=rmPK8;|!k@7Hh+d+r_bDf3!ammqLlVDpQyQ0w5j(dG)qiDyyLHfW7 zlE!eK7dqVgF^YL+Hg)SDmmEoX|B!#uWXK!dr_)Ar@;`;%fzaW;k(<;hMmr0+Zl<1o zvbOL&7v9`sbszk{gZn1*7oly>?W#`y-6O9F<@U=B%HD#<{^YsO;Zt}X2%gu-e;>MI zpjR0mqD{|?co2;Zf_${0dH~a#_U*F`T;Cy2Td5%OX$t)#sS`&Z=p)mv)E+y89A|@f z8SPJ@ue7hUxjuu}v6R~=I_9O`Jpx#wgYIG(|C2kP5DSCEms$fsx4 z|HbtXyp*FX9z2t{K7;l+;5&jRKjn>R^FMfTUAz+fxc0pq8NJK10U&Qe!+*dRf|rf( zrj68=I(5N+lV@$R@1R-RZ5Y=Z%vaiLwP~*wxL&0XzM`#>l&wOZDWNfe>oM}&$L$!2 zpx;(G+_ zW3JMnU1)pSi7!(wUEAo>E3~-=+{Rc+XFX`&McE?Yv>!LZhq0dWyae8VV?K`nUjuMt z;TnrfuhIt@XsZxBYLgZR?>OoyV{OiQ;F1^ZmukrDD5)~_2LL~ger*E38(&GrKD1A)_?isL#7y2??um;N_CNqa?G>NU#qLZ3F4>jUyK4Ej$)O9Aj1 zxBQqoduZnb^~1Oikhc^5^tUFEhS9!0RsFV~bJf?Hj=bXFe;GV|p=CLE$HM>5T-{IB zjq<_3duE9K+Ah$Pf#>7!RD?WZweOKuP}Ys>Hm)V8pA23ulb(ut)G!b4@Q zyTR{%GX2N$;yyC>zr8`5&Nthk#W}_KqzCsyq2Kvs0&x29-=R)J@>)ZCFWS4!H9OBa z;p=PaB*}$bz|#|aO{w!8Su?2bIzmh2J_nlouL$R&zQDJGCLR;#sb6z4KB#*ayw05s zpz#^c`k&t3Eac#MFzvWz?Ru8$UaoOf zWKQ^kItQVz2B`&j{eKb9$6ZPtb<-oX>6*Le6Mn<S25$9x<3|=z z_A)q(yY%dEd2vlw-?lzy{nnm=?0Ts-sJ5tn?c(ISmTG))=)4Hyid}nk-P1FV^>ypl z-j*@AhN?a5dB?7~>c7@5cHLNCd$vH(M)rKMY2Y2qbt3biXEbZ?x;{9Vd8-#P)(-AK zc{0lMnQLS3fPU9(+d#j1a^hKf+*gJd*FjxdT|>POX(#dQ2I_^@Ow)qf^-p7hwNFz5 zyM(#WwN=+!pTdu8wd%6ktJ<>ayv81D4;KSYTiLi{ZD`kLT}xG8cHP$X)HrC-uGU6h z4xF*h+TPQ_>p2$Mz9&GY(JR)-!nZf1ixYJs3?NQ(r%pXE2oDelBnq81ub=I}nK;N?AwpN`T*Wg4el@ zhsJWW6(CKb+;s-eYq$YE{c*-DuB82nj89`!^!w{qa=pN~zAoUe0M2*mw}uAwZ7G?{wZbnBt90cF~k+Hkkw({liaa&=uuACoraAKZIRpth#_47bph zwxs87_-?MCo${3b4lTpEUqM?H>Dy$y53WZUJGFTT^A1_^yuk-D0@^^u^G|4>^4Y|%DJ%H{_(De;Gb)la7E0RV+H+8ad zeSo}vv`?sCG=lNO^E9rXETDWo_diE5Ca6<{ zx>o^k&F>%NFpF{53AioPAB_wSaLr5`hZ%Fzkgxw^-iBxU#s3r-5TJkQH~%BxM{wvb zOom+Eq;ESxum4AJooi9r>j=JljDa6{_WX_|wBuateEB6jT?S7)Wq(mt7yPdK7Y4s) zn0QW+vFA-`b3S7yKXnQN-vc@8_gw-X#urpYzWR6LsHdMY4Ki@vP797`uKIQ7(#8<# zCh7se%8P#9kzDVBLw~ORPkpvu0w*5V^K$&JE%Ce0&HZt^;7dQWQ~kmJP~~Q zi1h>8roLulAN2i7lm2GEDXtrK!(QmFFUc0|L?P>dK=j*H22G{;x&V4xd`uY8DwF%toe>Z+_3}yP? zn}f^s0{yZj!QGF1eF&3Do;Rl7zgL{E9uh(lwSgV zCivZttr91bD~~r;m(Z{fc{a@G9`?8OO$_xF`B&?jJ|;twQcE z1LJwh4aqwIp63}O>izD~-cH^Zl$8OlC+)8zc?Pq3M>fhX(6)NJ`#o0EpY9!zr@pkE z4LaQqdxbh5lW#ms1#lhZ-m{>eQtwyr4ggL)D-3+{GB79hEjU_`UPabv!R5a5E|gv5 z*?sKyxJH0u5V)HI_YQn%Lu96nNx-cJuYT9|(TokQi^;3Rz3VFKTb&u>{tuG=*BD?w z;i}Ei7JNq;r=!8w4EO+LwYj>-RG+Q?snm<|hU6~@$1g=+4MSG?Ys&#w4xGk9sSC@? zAb3%ieM@>t_eqYUEtEUXs#50^sSwHUzVk^}#)x*-LtrbD?;2DBxH2K9KY)1~emjuw zyy&{yMxKpxeGVRCkyRV`Q_priD=F=arpz(qd}#d9$CNK28OQ2ab52z!SGRT!{+O%! z!#S?T{JRb(FN1+A7+?+~e-mxSg8NJaa}v+4DbBTar5x~KZ0s)Z zoCfwh$#tT_z&Z9B@O+wkq zSKp`J2Fi>BzePXkM=nNr8}eNPGdA}w`SN1?!CCbI;3q)%!FYm0&}=M(aShFA&wa7(cezJ< zw`s$e2;&p>^Ip17=1cC~KXZ=!QNTQ*jTzu~zf8q&#sWOG;@R^bOVm9}ehTO}_PI3mU8C3rzMi!4JhH9;zHh*BmiFUGmyz#AWbp~KBB~^vp}#A9 zAA;6{;Al<$7VbBZR{*?6$je8YuBo_J#=SA_t5KhK-;U>1%gY9MY0P~X?fO4o{vT;M zl6zI$`{tVbFyP%E=bo?a(3p7s20Xg=agyAE=`bN`KNAcru>-EOlMW_B;74ZO2gl8N4V{zjb4wvlR5r1a1(iBYb^Ie;Lzs zhBD*mwP9WczdoiW^nGFOQ*eKccT!%|d)0;2lhuXQUu%$e86MS>3)1#fQW?sPZ_NY0 zA>CKK({?|QLzzUxKW{Jr31C^+;1sAH?&wnN^oJ@*2}7_{HOwSe-M zN!O)`_A9_sA9z^{jCQhT^yLG8N^q!$E`py$jKkC5mX}V{`-iqGLZ|JHc;EYhh$oim~IKvZm0h{WX&3 ze`wb=uMxDP4d+=YnJF_a(0H@rl>GtiKS1vd@a_*s=J0O(-cIp*-S1xl z+Ae|1xq2IPIqz?$|Ma;VJLbIWyy`jLg(p5$$7dWec>EFg8T1* z>y9igBA0FO;y2tEx(Hh9z-1^}-Q%m4iIe*@KD)%~iFuMROi4EmAb zX6mXdI9I4|s|Toaw+=97;3Eg+`n6j_hyD$9@np0!l;_dpdyZpG>N-#8OLLz|ar#JJ zdcsF$(oB-^-W$l9L4D)7^{I8{-gxp$(Tr8bkNQGs+CEQxl(327vWuhG8p<^=7&Ueb-4erxG`!eY8KPla#dX4;< z%7v@(o`s-q3oy>HC21!)`BxcR>cu;u>m`!&^!r?$kN>6ZM%2j+Tr7C}uRiz2-$oAe z$a|mr(p)DaQ}@(*j?Bl%IyY@RqihXVW22ID{e=4R(mx6~Qg7h<(#8_XmjUaUYv*Ze zH2j^R{b3~kvv@dlyFuUkT$|;hFDU<%`xT@(@*{zBJ#Q6l|Ao-jLQet4ZzEhFyQsO*QCB@U1TOdj&^2&zX(_THTr)_LjPXiUqEK^ z;=W1uK=$LkcTZz|^6oJ<+LQMJ^z?$qy|m@t#t)^1GUu@u;B^Vf_*M5s4xyb9$&d;B z-h%G)GI2j)+3IdAH#>I3`?@O9*x6MB1s>oeM%%d>knTT_1?*P-By zrQEr;8nRe{9E{2DO5WGdWDKsnw2nasBNd~)LtKp)>`Z<>gdr2Ee85QULAXjDFm!wT) zf7~QJ0*~@O${1-)edpJHwB3n&?X>KX-Y|B#INy$em$kGRL$VK*(;V8?p6U(EzdUQp zr-i>|z-X_P=c&kj3Zl2`i$p0 z%%#=2#*&OBGpKJeSS0vP}AtsU^jx*1>Q zw>TEO(reod$deE2H6YLSZKDYH(&)L1?%fcd-+tHGp9hEgJJJBxf>Z{4^0=9#d|Gmq z_xa?@w=_PX{wT)Y1o~C_=J70l+MsQrZzlIYP+rSMf%Lx#s1(lc5yqj9EWc8 zr%AxhgZ@TPgpR zZ_s?oUxQz5@O$v|1e~6^<$2CE!h`+deCwPwhIX7coL5?L@4Vt1(wVFCv2&I3aSm4| z+N`cO`Yt=qIp@vdY9IK1wg+Y!DS;H4!&z!%Mzq*_Asq>iM1nqO@!Xeak zZXL?i`PBbLb57HScAj+1I0tK^+b{P0c&?5!=QR6Odis->p7uI%l?TU*bsdwfd0Y&> z{J@^0%(+>;Gl6`^pX0=FCM~tO%CB?iH0Vt9@o}Fzd2nnh2lcVU@yWfu!adaU|C|PJ zb*@?pPRFuyq3?pW=jQO>_;l>6=gNy?%(3M-d!6KXactS|jyK1VW1~O)oQAwNc|RQ+ zjx)!iHdtZxxhG{ z%TwkUbo@GIog+N2wF_;2156cgw}%eLnq%AXA4`4{VBaN;BOT%$aL>z0U^Bqa=g4s> zSAA9P8FYW(LE0_@+_&NAlC&wECBTyaE%B7akY}u}dlE;1(|4#Q<@(W#`_;a3?)@1$ z+>hwq8Q&%6T7CD=gSR{I?tL@{*tLbp;AscGQ^2@K$2~c|QwQP0xzRO+Sn4?EI$t`6 zdM2)Wj{MFzZ+}d3p7vYtEpqvYa`zH>rmynzo8&jdZ;|t&-x$9gep7Bji{GSr&^M1Z zozpi`=JzKH`Nlr`U2<;sn_x_|-vhrlev9N+{`|f)qYdX8W10Q__|5B2zx)S3{%@)0 zc*FwZxN>|s&Kz&fXO1Dq)+f1wbCPqHbBN=@K6M@n%}tAWAN!N%_^}V1zZ|FT4gG+6 z`RIpB+&fNx;CpnFb{&(BO}`tCO~aknaTL1x@SFv@wa2ut(uN29;e~iF zBB8-Gmu=vjL)+R?*`ehl#*%*V3((siIWMJrIB6310qW}$-@&u78~SPV(d)y}f3tyn zghMv4%a3wPWk81`YX?s;qOIYw$i44^km@ChQCSK|LvDj(ZT+Z*GkAp z`BVazGP=*R{ng!f0NF)QW*;g`WvdMRkI$;~xqac87WV5c`s#PuwSVn9`===7%6c>O z9|ymEy&fF)tN$_KIIzDbP|q=7f7+M!-%k3c0eRY!nRq^jyd5(I!4>M4(%|!4pM8wO z+u-#7W|Xz^v%i#c6nq%tuB??&QtI1R?uFe2eXf}*PtP~W2#+HpL@@Vp)wyu$O()Sb%n0%(+% zrtmQ*5R{wWKflY4_0{D2J*!3i89e(v)Nb&*rwyYn?swF+zjq>o{tDN5@=!-T!tZor z@M%MJ0d79@dRECy%KUEHciJ`olJwWPw&l5Weq(!!m$VRm>yba5`;Tbb*r`dttOKv# z`&ZyeUtnncr5SZ+LQgsJ)dBpzr-7dDq0hCo@noq_OylbJH*pORnr>51{a|+hKN@wV z!EbbbQGj0^Gmd(-$(zGfz144b==+`+ZwQ_DBk-$`T+^EfoOIx5m z$rlct$fOi_;(>ErQQg&eNc*KZb!w7&lF~rCdlwEeRtE80iF)gye>nH8q4@|rALo83 z`4^E{67qhe+?dI{Qc~?3wf^LY9G5ks;?*s^)CQhitmMM z8=3ir+ywu3;PK49-+}l1Jo}&m_59yW$DRKpc8Th)i@09ldPaU^s-M7hSI2`~l8t%uBxe?#WY!)qc_DSxCOTIG(kg zeJ^}p9PiEn>g}Dl_dWMr@g02&UgEj>-Y5(8XZ2jy%$*yY7o0DK@~pk!`|CU)UCt@W zT$|sucjcu_ea~x9?z@+RtMBv}@}yN8BrVBzF#t{KAa+^B972p))|-ql*yNEx8y2c(ywizU81~BMKE?4GtOnw z{ulM^r^n6h?)n*3bcJGb`X+3%`yQa;*1gUNHAlNZ~S4(Cwmmmm2P zhkQxz8(e)K<4N))Z~h;yd?`PDO!8fot8$Phd9i)l^j&|Q>p<%J4(qE{M)H}!ReBDS zhLSP|g7%;Nr7V=0-zt6D(yR@Z4!n&SUycjMkN$Ic3AL@i%X-q3Kz_&{CQZ^Yh`f#5 zFXF0S%Ki=~-#%5Q_Ny@p`q%Y=DF=OB%Es@4@=*rTqRjM<=}Xr?t`E(=(_bc^ttdBE zVhMFT<8%#4dHFrkPqqnIeQ4Tn%3Pl0#rIyHmwAaY22NS}KKp*#7tRlkiGkcZ&b05W z|1HlEz-nvyUfPGoL-{@y%oXe#eGR_T%CQ0WzAv4q_Z{_ocYV(tXT~5brM~acIdC|h z90SHh`2JS`ZU=QUQb)h0vU1+>8@!h!FX~|-Jz1SgeMmi7eQT-sz@-kRE~VZzoIG{Y zBjL!Kr0yHisnwCx(bTuppNuzF56cC<3Di?(Q@>UR+rYE>?ILigQ>phxfUhm^e#h1G z<^+QJoI1LCp8B0Sn>w1havAEZ2QCc!@)8SPePjI@8~3@_9!>Ol@g2}k)!$WweEnSd zyApkAJnNs*PAdft{ayO)^jqmu)3-L8@{7VS&b9rtRmuQc1G?@)(>&VKkEU;}F8K6) z>3`EcxZm#o1`WrZ8mLv?QD6_ z-uFBS?R#x&?S1Wj{Q}?8&OzE3MZKEf(WbRuw0pJvJ;TSo($3X3w_oj_62RJb`o&t( zM~P?k0IM%xGPoWiA8l#-%X7T7UyUWyZr4VZ&(h%NPSW3S3!M6PV|mu6)sgfbsXzeU z$QwXX_fwB+#l7d*sr#wJ_2b#Kg0axDj5@~tH3|=|UpNO2q1<`G`NO%yHNf`d`5pGV z+`ljbc@1lYwgIxC9Ts zgWo+0o(;DFIOl$OsX*K6mg6y(bFA8pTdUd*{(7ijr*ao-yr{^B4UuFhh zU1X|0seY*LnRxbV6muT+wgPK>>}=@zj&@y3Qm0aX?MD5dk(KjoFUo2&&KG0^C(p*( zc=mEPWb!63#^5@azeSmPe|Gr!muK}-d8q*(jx)!N<4Ai;+r{za*c%6&@i2}b$Czux z##d?&IaZ8av9BEu_P=wnYsT6hen-_ewbzurW5_;ryg6R}rCgoW@45P=z+Bkm&O`KF#ckJX#;(|;Xf_u7w)@py-v~|eaf@?&s1<< z;=T^~rAWqp8pk{zK0bo>zoFeY=I1DLe$4?LZ$ZOv&^sXlIO@&?pL&x%E%!D~gpM#| zu$1z&(DFU_j5C|X^C0Se4~-4r^JcD~{<#NQ{{T;ScrHwv%L2?F;50TQk*4E$co7=jV3N;<+_{!ISICXTW8guzT=6laz0k3DgR)lCb#2jgN%f{7T-A~MzN@3C z&(xz0^_()`RRC`W+&t5&EJLT#P(yRaeeV(OjA=kfw zlYiUv+(hZW$#Y}mIuib+!!-f*q9fd^o80C4BRuKLAISZGfuIhe9;WUj&B{}q#Is5C zd#gA70`866e@_4Aq5c8zx=ve~e9tM10XCLi?hr_t4Bg^L| zuIg&Ohrf}Ou{zld%9Ob}QUU6yBdZsx%l4oh^~(B`9VB1tUAH7~u4AbStKYg_rf%+coAySJ!r(OkLXbuM*JXc)h~a zbuibzJR2{Lv84|EBG0aYJ*2(T$ip?R=fR;K9ZQ|JfbpB}cV1tx`o^3W~Y-(^nn8>=4Ucg^pfekW~o^&V~6li=0&?6-Gg&fvGsZ?@mu zb|lZ#S<4)jKb(0QT%NaM{GR^i(Avh|;JQwEZQ#|Tj0e_FEidKB3;C1t0izD1Za9sy z?|4=>)4!{aS>Ky}$ic`|UvWI`G=~=bngz%oPaX9kbu4w6nWVxzs|V;8en>riDE)y` zcT!i=ue+2s^*^iMsUNAwzYibkhx)?YGk+ow)bZ32^f}5)9bh(tqbYUdr6zgm`|AGs zkcX4h_tpE=|Fr}37pvp<1zsDVdU#OJ&kt=G;79#jo&N;o>iJ{1s-vr)>-$yT(4VaB zpiZLRnVo#UH0Posk4(lO`w(3YsoyR;ixv`6$k`@b<|si!Vx zynagX$cuhHc@O#Xl)Zkn%+yuBl_@*S)%8OCd-_21Vd=k97mcUPcSB!_dSh>%eV^)( zE{Fer{yp_l{bA~uuXBGJ{wD*wiF^HQ-;(OXi~ck9P5p?D$0pFaAKdz$T%Svzj%U#8 zFY~=r2Odh6y!dV^gOG1eI;73_(Rb872-ySLw4JD9ecvm`z3-?tv3j(6t-7*pI7c`a zsMpJ*zEf$GC;3$`_kDKG@!d-xPua<rNG=U)FJC9AFRL4?_o}O^FRLT}$-R2* zC7#un)qmBe)qQue*VTXB>+YGj+ZjvlN3M!|)Nj?NJ?Bv! zyDo6*+3Lj0XU`v@JF9!2qs=YISYCWr{0@fR7vB@# z3&*$bLFoMny*s`Wz6ZWH<4L|d?Vv+jwFb#|!1t>)_r6cQSH4TmfuZ*$^bYxM4Itn5 zYaCbSIp3qs!jgO!wZVNqW^(l%(thw=Od#9$#P`N`N?v^T|1U2g9^^^7od@Jio}^!z zrB}M7H^ifS$fNJMbo-7=n{u%J`ywQFeHeb9{a&jlNS|%^ZBQQC z@zQ7?%b)aVtI4-KC@0U(QEqO^n!MPa zbzdjXwxqK%&-RsTAffrz`k}Gqeo)(1UztvRUs3`|9_(l7b*v~4Won!1UAC|6^hHRg z<6B<*7CC;T*>UBZtxUB$?LTGeT&=9_cgKkGn+BXR9l}*!q}B3bhi!#>7vx|Om-AEZ3>nbnHD62uzKRk=8J~YRs{#<$2Cl~Vnc8bEM z2#o$){kgsuj)7(HJsJGDsZ$^j)Q`RZPCv9hLw$r3p>H+#+5@k?x(9gu(E5}0-MOdZ z7&u+)cFZ|;T}#(@>>fP%{)6YL(0LO$V;Eh7ag1Fd$;;colp>WOE#AQ3d%5|x?sN*`2>rj3_^qZs&=iMgX zH7EDC>|SJ+xZ*bjJ;Y%edDtx0&6^u@ju4i3I_-q9(7x<&Wk(4wq0vS-x|Gb{c{ZG(noYx^} zk0IZ=G&5IYi;Ovvmu|qUApJs}4Afb|{XgXU?LGz0tmF+LPd%t7ZMk2sGxbKe8-}h}rF6Kn8U67HyIEN*E3%G{fd*5&0&TaLn{7#Rh<9oCyXG7v zZ}R2*BX7<-%0ju=55^hFUmue5o;*57Y2QeTedG65`knuzS^Ax?l()P(XE|>wTltr7 z$FcKVV!v>8Ty-Himh4CS*Kw4U_ssq_25&q(yu{T$Uj&Tf(Y|ew6FR}^cyp}z-oDMV z=dzB4zJk;l%(!z*r40oAwQFd{{~mBm*$3+Qp4IAj(;mG{eaHP%#-?&n-**f?B@F`J z@$Q+eg`jCa_)}9ZFQ>U*3jE7lw~&0#eg)SA;J&3ibk0c^%1)6#g|<8^Wi55|@4KEq zkvg7ZJApjUJ}D2}Iq0j-wIpdNGWN_C&vTjpeV)%A7S1=2e9woPlo8#Py84^#w?*6! z1XQG0-&~n&jmU_5MJnZJ1m7^RBkZ2`u=3rOk>w zFM+n1 zp}Y`z*`etgJk5g68PM1ioEx~m0nSD6*p0joB8l04m2U&^{-4WCWLOlsu7mppVE$t4 z9)hp&v=>X>U1<6fURu+xbV;YQd`W&K+FuKfS(HilQ_^+XA3*sr%9{Zj4@_Qol=diS zE(i@v;j15X*=8PKVrhR2d~8LQ8KB`7`Cr4+PVhEjT)hU38^E`jv2cpA#^k;HKRyEV zdA(eG7lDr>%>-`=@cm8NOTE)vZ$g*6+yh5faK6Ypcn8=wXuA*P%I^j0iCbUz3h1!E z^hb+#1ajR-d41Yao*Afr4cL?LX+O0_2J-Fyr9V$w#h~Xit`*^X9qGpa<3QO)!5eV$ z=YP9Mr)M0VA-^9y)dKHTp2t9^JSv;^v^Um7+xpvw(}uh}1g15q7WdlACAmJ0q3@vO zYx=hab=^Bzocu>zKY=IrG-~_mzimxhjk$X6=Bt!9Af2aw<^n&G`+dAmsesitI~KUv zT=msn14i5W2XN`9O`z;`Qfg>>hpYPzw6SwQr#7@UyY~CtaPUFj6Y#GiXl4;club{z^5_%k;tqq z^`fDx9euY49*m3d9c)W`@=_BV%}M(3%5!ziULW5s?)CQ#C;i{`ef@y?_M*W(E z(`To>;d;EjK7DukPFjH9HTlW#u8&SzIe}|7WTT(XwSCuECvw%7s6WvEgK>>tTRT6n zo(ohCm|ft~Z&(c;j2APOz<7Xz!KL(D9$XZ_1v(@vz<+>`Iu|wUDz@u)hjz6CK`#k%N-3V=GsN?@-Z{s=ueCqT$ zp+#LwJxqUr`n=z3?R@Qtw@EF5c>p~(88gPFeQ2LR|MSq+ADTM>vxK_(SJkH%(T4r4 zjW841U4tl0-D%t}rR^igNIN1PoW>K%i!!Q4(yk2Ymg-+cc~-A1Oo|6iovkQI9Z319 zm-#)nKmA@02BtH~y}|Z%GidM|t{!23{Y0K;vCTp5-;!UE=fm*iSWpL0&$7?e#S(2& zU>*0`y6T16qUwqEx8v>w%GK4>2h|U0sN9coAPjx&fJL4@ofmeM_TVJ2EzCHC{ zZ4~8d-y8aq4!ra(|v>kOQ{I2V6Ha$a^Gc3yTKc0P5!b#4vK)6S#Lp(CKj zdCZ$SaSzV@o`>Q5Ux2o|B@fQY&h_%*`u|jtbBnT7ZpPr~OaGo{=QQUcWt|lo zjSmPQAN$1^9?zYPgJ##WR>J=;B!3|P#X@k5p^ebo@0wi^;QT)Mty%ys$6^M)pZf7#`lO%zlL$Z|Btixj;^|B9{&@_ z4Tw@iumawM04gXDid2=ZC{jg5ga8S>_fA5U2uPC-0@8aGP?{j32tp{2h~h(&-b7Fk zgx~9x`?=>l=lkdHp0j7)@7>wincaQw&dkp4tqBc&2ikMxr71Ag3GTo2=ja3V0aEv->Z$Pie;2WXkG;o!+se9eEfn5Y` z-%@@O^~Cdj33&IL^{dd|qA$U{@RpR1X0RpwEN8 z6Z$wT4ltLHr#z)EMN@Fa!H55U>DkKyl<`iSGSsh+#3;tdP~P=laQ}Q2unS4|9=Dv7 z_r4qFj%@I-lJq;l(A?o1pr1!);&^bmuQ~!=J|rlAbrugn+04C_>DC9XLD3=owJ-0= zUe2rf^Zf~&y18de+*7?92-*6E0;m1g@qaId7+TyLcOKRz<6f}i*fFBqH64ES-P6BM z+rV7X9LLTz+L_n#ehd8CnUYCYj&vW_c}RPLytv=mgmm{{!~3r8i@G+|Ce8UOLoj3$ ze<46yQ^x(!qTtvL4)rt zbFwai!~c3&4@?!thPE^3lFHy(0!r;2+Do)!EazR@nD=;TPiX|5{*R0{4gZJ6`As|N z0qT&K-q5g!5SA^KBb57;ZNjo;y37b|$`8t&1p^^@vNbTX!LOXD9I1S%ETjyoJgMBN ze5kCcY^fZnOsJfyjH&#pys4a`EU8?n+_Z}}(H^-N80FX%fGB?|Ln>qTq`#FhUk6rM zRvA@Z`tbh0vZeB*@}+&Qj2V_0!*b;|$|yf7Pbgz5gDS^N0A5+sai$zDrYM5x~Kji5aPw} z-S592VKC3m0s2t*t*Vo$7x|s_BqW1V`<1$rbA$Sn^T09E{pKAbg9++tZK%)jl?QtJ zlGlLo)C5@dG<~E!)1r>&m{XT3OFe$)Q-J+BhP4Bjm%-z@s~lX9w$bM9y{b=v>jC+$ zTeHDelQQyS0q}}7aEjT9uBmdf@-=M7P-VE?W217d4CCaKNsXz6hyt-L^aJK=6 z_WCl^uU^)i@C*6&hxV&wJiiC5vSuw{)%7-k=O^-8Q`aEs?gtI_wc|lM`7wA>hwBB6 z%2K<59|)es1a-zSvCMbmuLp;`Gyt#bnm$>@f!D`Qzb$>oTsQUcYDl{4WhC+XxX_w# zguHZrRbtQS>yP#(`L410$Cc&z74Rz$hW+03x6;SWwa9hRHP!XjbwVFmeZlm_bB*$x z|G)67->d7M|5MhNXaCRa7(992-}Txxx+XZ{!KbfdPhx!?Kc$X}JnQr585R8)^=;HQ zPhZBn)TfW#anf9)-UUZ_;Pt=zmHyCg?RUyGvL5j1zZT?K|KFz2p%3E~LLbuf>H8eo z_fcNEkbZ*&p*@jw{XIP=<62yc{GQ~=e<9+d@ZnnT8a*z^oJF75cJaW4{oVA5EJe^4 z&2p~q`dPjQy*0qCA7}>B^q14uQJb0mgZjf=rv7<+TW&9Y zKJ3$}OkrE}f`$)BSFSitd-P*`+c)}iDv#JE$|CxnuC`qncQ*qenP(X3_L==151!4y z?;u|wkef`+_=)yOE3(2$$78Prh$T>h8xjbfJ6-Gx1_Hk9zp+< zC$k&$jN<$D{|pyV=a1B%49rUEDntGL=h;c#{SSxY!1WGCW z_4X9-WT5Q|^8O5UWQk&Khe!Xfc_wXA1X$O_7r^ftSqVG~c=x}Y^}Udn1@N&(ewa5Z zMuzkX^^-y5xnKHT09low{4jvD354Ci>r>K(SpCNT+jEcIJw^9T^)>X2k9to$JotZl z8G+X~!2MbE8Se&f2`+UW_gp9QZF~Q=_l3LH>K%9Pv$|f@0Jpw~?vblk<%Qo(ls!tw z25qx|k(Zx{!}js6JZtwZOrL6xz6#IBfvE?rN2u>haOqF`JmvF3*A4R+!`jm4@*cLI zYqK6idzOKwa?t!3eUKZRSAcgPpbvPoxj#lfRi}NmnP&sO2X%}hO&j=i`1&!(Hw!=B zMWS7QBzc#>tu5X=Fqbe++!t`Zke8#-{}*8z`2XNtd$qRce6;5Xg7=10rY)a=*30DQ zqwlqUSESuHK zJV`liWIgU&wS(axpLpj^L?^4hPI zM|&_1l_QlcmEqkZ*EXY_@*wpp(<`58% zW?~AAx*z|unex&?D;?=~;+R5P5Cgm9#ee<+IrBZ$h zIJGVLA0Nui`VRVk9fRSi1GM^I9S6wse}^JSO9SRQG>v6m@&4G4NS_aH^5S^&+qp!( z-$Mdz<+rL`#&7x+U}^}z*&v{8bclKo1TVVd07n~1K>g1hqf2LwRJq}+wm*0I=p(e_rE(woMeou)9(bX zBJbLToiDT#Yg^t;-@Di4{LlydXBczNCE9a5tKj?*$MZz$Tui=mhBhnrFoyFzWTn0j zfonw_LDK&u*7m8*rv^M|k5bQXO>kb)e?(ijysY85CE-!R144Gf3}{(Ns0zI!z_0B~ zUAP!|HwgOM`c3ho`w1d>ql=ABA%70D3;rH!15$7D|hU)P18{ti0oi_$i?q6`oi};rk!n8ThxF2d+ z=dm<`bBptpbDMLFbB;7hcl!Tl1w!+k^ObXy^PqE|^Vvn(!Z}QR-v5Tvr?@gaICrIj zyDRl+3(!8`+*Jr%&RzazZCyehzBT7Z=S1gA=h-ZULVP%vmIhuPR`F)2PE; z0Jn3Ea*?vs6O8?z;W-O1VI5qVZY{8`>FQ(7quQ3I1FzoxG-+|P={(9M@a~A=+ z-^jo4vjg1oc+L)--jW*$Ao=X8Ul-NBF>2vQ- z-%x!_Ukt)8=^cRa|2efWhwlrriCMqQK~ns*T9A;Vr?b9_4<-1fGk7NBgR} zj{8*!ypJb+1^EMr^+oMUntGPDH2IbnZP0CrPlHo?leE|m!oD8}^;0tW%?KI6+loFA zxAn@8-?u(^-uLr*==|dx^%->gKWCSiFIom@OW@|l!5?_m z(iThM)49@l?lAl)YdM!X2j+riZ4S<_pHP?Qn&1eSk9}f0+xGU0{bS$tfL8m${|guKKD2-AQ~wiEUgq!|?hpINKCzGN z7yHBZw|{JN+sps8w?FJ7Z8Pcr?@7N*VNBQu_M>fYTia*aYfjRhhv+A5HukUm)(d$1 z>lw=0{%xqI9(`)R+wPOVWq)6WKmWhezR~`pTrE_1b6pNpIo(C19POJ-6guxxNMZP5WP(`bgT(_G$WeM(qEe&m=9L@!|fu zXP6x8`eW*+eGoeA1HXYw`Du6XC4$@kx3|CSC+7wG+5DG$6sxmfD8U-|+QC`7wacV^ND(=RPq zYfD3ebC_#%A7ED!e+jI2Vh#dF8YtZp*Z$^M(C5m2s|?P9JSP%f0w$TXtkB>ddv5yq zD&-wV@-haxwU22B8^rtn+Q_tzX*W|ga{Y1ccMWom*FN?UWn2TajqN5sk)VxCTUbkA z`V+K?dHz5fST^9bWoZM`u9n2}&%kJ3)2_BE3fTs_Twh%Gw5@4tyAD2WYTDVfp(Rje zJRu)&+Rn72$%|`9GO>H^Q=-u~;Zt2+A2j!TmXPOui}c%Wu0yrSj|5iUs)0vcKM!^6 z;@vf7H8k0dgL$tGok8kwZ{PLEy_MdiAA!CK&|&+JB`9yWpQB!XnL0iJhwF>>fMLM9 z{&?q@|M`?1e6Nz{9i~@^kC87gy$b+KaKFtxxtY}Aex2Wnvb_6z?&~>+yEmtAm-EOF z^7Q%gZb9dk*TJj)?*85|@@B)E`uh*SzX(6B7y6Pp_T9_71FZXWXP`kFfct>%r|yPM z=Pm!AZ6oQPQ}zzSdz4fE(BABP_%yUS{}m+7{X=;P+ftf;MLUdk5$!C;fz|$E+h`wY&%3sn55cAWrCr3i&NkErqs>KI%LB?3C0*U? zDPnCh`jhE{uFskF7j>)Uz-!lW4x9{ZfOqXO+H#gbn>LsaqC?}xIrlzk`l!o`Yo}|h zx`{fIc9b;I)f3bYT(6z0)Fnz%M!lp7`L5aOC9dtMysPiHHaOS2cDt@G1lD=R|8RDG z3+p=CbAF^Q^%B=}*P?%@*Lh35$a%9lI9!Wdqg?CzK!bWzo*xi902UD&$!M*pxz)q#F zCPfoSbMI6+vJEtP9zolpd$>!@1GWY@oO6^{kLKiif$!_&IluJbS^oij44h+}pH9Q$ zcG8l8*-m-&Z+UUdI1cQ0$CBeH{mcPn9A}GxSx>(Gs9%@k#4+Ufaa=e?9GmL++V4C& z;F*KTl)FZ745kqGA$U%~vE>;B*VMt#mw|SjN?rHB_%-0Rf0hkkND*Ru?sP4HXr zEW)A~@Br5g_!P>y-s&SIFRmM&W%K;nxLA0j-PA2S=jQn~=Xviv`;Kzz6s|$85uR1c z2OXYg^Ss(l@-9-hXXQLMml6EiDAON!|Ho6Eqb23kIqm~(z94d@)Ghf!p~{US4Dz>Q|Np*u2nl0Q~AB%0h$5 zzeWC&q&bhMS3E|#YsV7u{FYLQ9oK$8j&1#}{a*Zb{FWRyVc9uIo^}t0NJbquDs6*L8xyx}qgt^b}$#L5<5RwtR*QYBulo8!uQofcK&vxyDf4_@L;7%jH zN_rB{B?+G2`kJ(fw5Q*Z=f%S3!aV!+F|fIS@m!JqLVkaq?Q-7y5}LGud!}qAZRP*J z`QL7i`yg$fE65n7oOXD95ih`#_Vmn@?G4^B)UQq5^-^DCZQ+i8|0hoUtuxP$Lc>~~ z<;D3#dx~>dF=A~cMZo3%muf3<4s)I>4IR!MJc1!14-BZqOCxi zfqq~g5^F!vuHYGn@EKm$6lM171ivrk_1wrRWvR0$c!w}=j-tHlg==0`Xmm|GPn?nW z>WrmynVvF_5pq(m_PNK&PoP|$KxnZhY)-~3`Kx%}#Jua;l#e)-ytd%2%9vK) zI7OejFMg7->pJB+a)`8tp=%jwjp(EG@VX6rL%?OfxUSSB{6SFHag8Yqjm4mSB>Bo8 z$`$H9&iCr0t|v=uU!Gl$#?cS11Nz~dBTacDFFd#|xOS}~-F0juG(X0>c8VP24JCX4 zP1i|p%Cl>SXEP3g+co4rgu~2tt;p}ib1B;DWALjRy7oQJdo_4}92j|djCz$rZ{}zG zgI9U?4dT|+t=wFbGW!?pYg>LDI$d-6#qfPW*CN^>iTaW# zr>xzX?<5mpIc4Of6L_})(*c~VsOJspt;G9l;O%5SrfuK7#jEl%E5> zcS$cS@4#N84(}9tmb4Yn(4F^bwg+jsD0_`E$KbI!_5Tcxw<*&HIt!C`2VB}o2S9_i zQs=2VwBZ2Cze(P!z?P@aauEA}vf4-s(;iKDZw5|z`3Aas5c(4$3-R3(yT|1@EB8k9 z@z*A3-D@jadJAWrU`@yDmGIIUfxq_J6l~66*)=Jm}iv9H!jr z{1s1mZDHC?<`S+Fw3lfQD?_@rud}>sQ_&uyzM!pZ9Qd`}bv}+R?Q6XakC-{4C-IF|-?fp?^{r`awI*VnPf0Sli3P^q+G^ z3b0vegMQSdemjA3+HmAW+gKE7+Arr2t`W-eevVlCm^P^--nEDQOYAzOZA)3m`P4OT zK55#vw2_U5cI7E$s5Q|c8A{ujb~J5Z`=MQb$>z{l0vKgE?QTy|b_)Dggf{JH+SE>x zev^0YWwGGUW;P2P%7)tMGSRo%=+uqmMc=Vegv$imI^4eck%jHS^1p{*|L8v!ZX4TP z-!lEpii1nrVlU{>&+KS0)W)YMXP;^l&@MTfeD#0(WiT|^rZMoR{nN?Haj@?>35e&mOu)c7>n9@Hdw_y91-&xAwKZ^i?~8ynI7i0YU~saq3Nj zjuc?Gz*leTw9TIcS0CuGP3lwU4rurs+Va4cYn}dmufbF1{EUCfYy_qQ`Oi|WANAO$ z`sBI(dY^;7t4k@D?$-y+_kr&P@89vPf9+c6SOIMph_y-j{pnxr7*O|WK~fLOcZH6H z)YBX~u-FBI`2laMp5n(bh#FU*P0!` zsQ*p|-nB`e`8>eZA$-mptL!zJwD#a}J-bbM1i^L4^DS+H$RIoqpzQPHFDl47Lwy(E z!S&6xY#{l6Lfd`X=^DYcMqZS!m4$yKR*#FPd=}O?_hOZ)m1~t<*Md)3Jq@jSGTab2bF-6K|~cql(}Anme; zvT@Y&5^(yEq(Fmmzq*3*_#^Q8EaTn%SM>~Kc6D5NQTNJCQ0KbJ`zoH*w=%@>Er2tu zcTJ{@`jq-lSig$o+5g&BpY#8_Kc`%N_))LhN1FPadRZ%YEyCDZN1l3{I+g#|t^Rcc ze$?q^(S!=Dk2&`_UpZGfzd4UNpE-}U zpp5gI^OW=1i@ZZd`uyaa=KOR$nso)deyh$|&R@=L&S|cH&ToE;@^Xd3qY2K_&hufv zSM8PVail;;G2q-s`JQ~pN%w8lXEvF4=MvWg_ee$&oNt_`Do2Ipxga#1C*+|0o$snp zz6injuRioSAL{%12>A5BZBCx^J{>` z5k4Xrd&*?WP0Cs);X|27xlB3j4E>1#zg~9{uJubS4#OW{r*()F9N%d_uWChAIiSZ z`^$tl@cUor_2FANFdjUvuTKLvocK0j7wyy&np|&f7Tl<%*Zyb+|Lwu6&X=FGLs5)n z(#r5XC=2{Xyx+DUPhCyjv5emgZIb~y4p5K09H!g}XbJ-JB=tLgcci|ny!V360JwSq z@4lHj(C3Ve{oqnBbB_NO-qpdA;3^ViE}>1F^Y2o}0ltF<;NA|dD&V-sv-TWqOMe0X54b&(Hk$nA&}Uod zyW?ExS+`EK#RhPEL%bRI%!IeVe~wsN#0kp256%Fzsm~t)ulD9IpsgkCroPw;eCx@t z2!3V#yySNz*7iOVx~~w2?a5VmFG27e`OB2?eyYB}UMHM@*WocCd+!jQt5e4YLP6@! z7ocycKOeYf#?c1E&8W|F?CxQ>5224l5ooVNJH+ukkLQCtdlslMbk?8^JiFeDXKmH) zO?(dS1nSa0J%(rfZ#=U+6TE5A*$%pjQsykGpiA_*f087sKcngsJp1ITqUTJ zxZWs>s+YLVINvKryN;;CINz&NxaRy$n!1NFtny4x)~u4x`>u724EqT>G?P_^qZ`2SHuNZ%R9gwh-4MdHIUx|J7gAThw3FW0HuS z!<~QBPt;|c*IiH4VO(?6VboRBZzcsodWHIobDMg~8Oplf?3|}PI2U|+)A zD09ox8q>hxJdmAvLtCLX#F6BkCtcflCW3w;r)f{`>>SR!I-0aQU)UBIqM(8JYILaW z0?;D8+A95a^HR<>SV>q&IpsFn$M)O>FQci~`9d8eFSMjeKe0Ca>GZd}WCOMnp**1h zc$yRRJy^l}OSGSInm*dfUA;-mNW0XeJ?(#eEpGy=Pq{MH8F*1X(yw42!TYToTN&ZQ z@w1h%9Jo}Y* z*V|^&pj@xqFMXBiU*}l$3H1QiCS`f$dS!pxOCFW&>yf@XK>PA;o7pDrgUXA3Htqp+ z1;(*DH!d{3we2_-{btTC(l&Cqg&cn;&EF0dca4`*rLH2PpLeEm#YXh&@Te(xlDk-7%~|0U^biT&Rn zb#BkIYd7snU9r@44ftF6nG?V_7{0WfdY=6a%8UVb1hl8n7F~ev2A!qIze}4Og~xS_ z`G>%%ZTC6qX-K&OjICj$y#~HELE8KO#+d7=J`zpfEf|0{;Kxy}D9@Lf|Hc469A2|Q zn`d$Vf~Mk>Jx2Zh53oKDB}hL6ygnF9dDoXC2%fK@^Br*S;<*;_V$z$#%Y0y_lco=c z>wI;Bz8+108Bdz$hx_xa4@O7f!L6T00m?Pv`7`S42K+sqi-Oa0!uHEp$}HjCJ?AYv zr|)kwrrPuD9(929bbmbF^}%otLVpZpl(hlci@H|PPWnUWTj88}9M~MdC@U-5WT)+v zzpoR2N*VXE8v^$M@xWNd3gz#CXFBv{q7L^+5_n%m{U<4}Z;tyQxvBF*Xx2YNpA7#K zO8d;G;E@;SMdwz(W7lteEByZ5H%j;0wtn*c{`H|)0i6CA?h*TKI^Q`b}9o`v*$ zq~{_%Kj~S^yySa^(*1%yj91TGdUn#Yn6J_fp1u4DK8q9FUpNl#_0ZFdJ`114{4vNl zrL1Q)J-c~?FpIYBL)q)3doFV;d={XdmBg=r{}O4Q$t(rUeP|=^H4LA(JOf?hpz(3w zwe*~&{`Q`&^sHq`+T=ChHbA%ME`Ndc%ar+v=jG&k zM)FtQ_3`)I^?C+#Jp6mFq30OkW5KGu1W3>4y61*>gvUKah)pss{*dc^*q}pRX^m zzM=XB_2xN1zB+vZ`1E^FN0~;tYuj(orhP#j`T=>`I{J~`k)RLh-=q%(_84I;Y3}Q( z|EuFHr>^Qe>!0)}c--e(3Y@$+j>J)lx?2<5pRW_AklvMN_3+<$cif5B`sH2Pz2{C| z^tWk8tPh6u$m>@G$CqyPdamUe=o~`3j--0^IrlTXuTEWX zJazlsEF@?f@&2^|!BD&EgXe!CDcdfm{p3wvw6|(&bJkKDG zpk1{CdyZ=$!Sh(!fjvLAnz|kZ$0L;Y{8&AB^xW2L`XvW#qjP;5==AKCXIni3?%6ZX zaQ#DQ1>HYFtLMWGg3I$}k)(O%>>_2opKJ;BMM8(RcrFhKo^gvO{s%s~5pRIc#?(_a!1$(5Q;C0}{zbrzCO?|I%CUUkw9h%l z$0hjaNBk1+)4Yz*m<;BC-SPn+hXecFZo%)QPegWoTXpM!>VWiKa-)@2P7CdYs{Ug#_fPWqE zJE?yccpp&zFW@W(ukvyix*sQ0Bz(fVy!=XDN$N}h*9_9%16M2Z7Le8%y8i*@Ml^B= z@7oA7p#O8egPXDF&eWZiyc57=V+>>`-b>jh!0GqkT?A$5qqhjH89T?J>w9nxg;(#N zNd)F5Z4e~?0?&?nA>>`rC0e4?b?e$3k#?0Q^^!+sgY~c=-Wdno(aA zc_(@9Ox%%>3z&jI+5-B2gNDDs_atR|0TTh;TWF6v)a8H9wI(Sy-_`5%%OkXFZs2l2 zLmOcCk$#f3jaJm+`yc-I_#^;~CMH(cjj!(8)R?}h=}3fNr1 z(7f%s*B_keYXWdXz#%WQc}^s653x4or+L=?tBqM3uXf}wN!L!S-B{c5Jl=mNUtbCB z%~yH9N33mGTd{kf`b}u(OA0_EdD@t@FS|df-B~+wZql_;YcJMz=-oW}#_XW{6#JJp z(q^qq`7wfiHNEL8?aA7mwNZRJDwO46dQeV~9q)kcvbiTM4vz{%BWGt%7s*f%uUHwu! zm3nPyaH{vNBwd|WJyO4#BfP8g>XQ@JL*>ObwoTmknn7LmyX&rf{Q|-9k(+mQZrfZr z!1nG*+Vbd7KiVf-2-@PckGgg%15_f8hepSfLvDh_Gs2V+nn~FNcbrT z?nv5&*nKX~qRpm$&yBqSyne;i4Zvz6j-_k_b$E8ov7!CnvuE;>3=i%ByXLy4x(Do? z7KNc@6!_ftZApW@0<8CYdLM;nxV^U`eBY?|S$Jny0yHgUEPFqNcSX2PdpAyB=y!d0 ztshTaiSYFhY05zRz_kF}%2DIU`yBjl66+u6-0Xg`GD&SIp;WE9ECRLp%})K zK4zmRt1pGV7-2s$=R5r|vOU(SE-gE{x5Ft+pyhJKpf7Wz+oPo91l`d`3Wy8lJN80z9#-wXG>z9hKcrfqJ+4im{wugP-`VtKu=WpA< zdCR`A&z$pZFWcF5r#twZYo=3odRu|VhO({0Ymh#ZUFd_b--~_h+GW3dOMlvr&ZDkR zEhwHwJ>hxIKB-HZ{pJ3@KG*%={ZroEi?Uzq11~SmTh3q3F|H^2Q#!XzgEr?G*A>?v z=O=w7oy(l}TxVQcTuYqe+(&TjaJ_Jj^Zb|dp=*riyU&5g^(%>bwN<)q=|kyU)Rc0r zRl9(D4!%!Q&OOQbwio5+P|p2K?VIkI>i_XmFtpC?rd?g%GMe&``nUE9Z5rAfKBTPjnRv9dyQck4dNO$ARo&XPRU4Liv~8kJY#V6<(T=Yk zKAe1Y;)9g4yrL)z}Ot80h%zkhl%PiuFd%eUG*mTw%IwBL^d&l333e(!(WOb8OFLmPbh zo&C%WS$Wou{T+GQ=qnKWKSBP_Nhi-)2{vUvL@uy<2;ZyHS(vCyt zHR?M?8EyNy@-s%Exe>f-hfkvpdC|823E?M#wtmaqAc$X^{}rF%!!p|YwbN_o_m0G7 z)Z>2)Y0s}p?Eedmh0a-|Yp-7dFWTi>kk*{?{-01h^xpzDNPjdW_+LZX_n#!)`x7$* zuid)^w0N&#M$#Y48?vWI7GlkWkCEUiOd0nNMgl(`dgP@WJd7n==J_abN9fRQ{|E2d z^tJ#0%Cq)*?^o2$?tdC+r+=T=GY-w*r8cnP|02?suMNom9nzM+E{MF(`1li8|8q#Y zy?Y4y3cgI6{Q=$nZ_q=;-kYfH-v1}MNuA^1w+e0PK7o4={TBrziG|7Y4n*6g3p7Phx9yn?dbIzPW=weh;yd6k zN!keVl|7XeFVQc50qPVugs>D{@bpuGwG>;E}@McRGd?blyo&vTgaX0Y(3~dWY?+K2&^yev_pC)}h z`4x!A!OI-V?x8QQ@cb$?6o%IG@ZTApQsFs)_z~X!09RS^)&n0;`516D0Dd%d)&^z` z@ksjlKIw7LaE0&>&#%(18^K)=nA*T}rp!U!YZ3c@7ze?3pLU3+Uk37Am+%(lMp4IU z;wQngka#ZXuTW+k>0j_X795w!y8&J2pf3@a%cLD7Kb~>W9sUXtw`UA(AUzVeE|i-{ zs1KgkiR;lO-KqN-U{j&}2ilZ?qTIibj@WtT>-sA z?EBR74P)3b;htkd^5;^{`R^{znFud~BNaNoAiWN>d;u*-h_y>*0p}vdlzP!c-nIWY zrb-gjWqOdOj#V1?e!%))SO0>?J43yI^TaOU>*Xm>s>PVT)Xv`d5`*Ci?q+^^C>T`bIZV8n6zoU z7XbHk;*$jTtF{qOA+HuNLwO!cJ+5U#d?v_afV6kWSI*JDz_qkGWinCco6yl0xEiFn z-sS@?fp^zf*H_okAiO9OO@e;gMZU8ILh`^2V4IQW+Uy!$jo7wKm*K!&gy4N1@^Tqm zml)%J^88tTo*7%2=K6xZa+rrmW;_ZyZ)FHG$Ee z$-Za{-1oG>B>MXu@~83q9QpEc1zs`{^zBK7r<1U^f1rb zVom|?eb%!GHK4N--$MiH8$;e0@}l8aoy-3wKg6^DSH6X|keB|z^&sdA?|%#Fr~VQA zRsi>(@S^VhC}sS=Am_mU5Sl^vH^93`^D*?+%t<~l?#0v%gyfK1#Om|KX=CjJ?$y5y z-Z9Y8iE;5KIGpzzLqmITjNy4HxINFPPy06N-UJTirH$~VT(lP$_kMnYN6$Oz8?b|R zdJ$N8nE_4im$fdCvw+7TWG3cyPaF zI{fP^Fo<&6aux#T9rbs4_s)9P=y>Yi0_{72jU#wQ%rji-;p*t>*Xnhi7wb$o0N!NU zz;j)m|N0u51Hh@%_kk|=#23iUbw4>-_pBX&v z_xFc3eT|9(>)Eg#)bD=2XTvs@(`|Ec7&B-RsX!oBmCH3uq}%IqfuuX@fVZuRqT#Dd+#yU7*afr0d_boi@~d;CX?5 z@aowEdC~r`hoF7iZz^oR&`zOELwkZYjJf1%v(Sd2jY7La*oS&HdCnJSiM3xlCusA~ zuA!~LIYIk`c8>^PCWCJmFkyde{kFrlBW)C68yQ`i3EC&LVO%5DMy@Sm4zczl?HT8I)@GDMx^`~u-r9g#2KXkS({ZTHDTzGCk7q!& zV`x**MykC*yQ{v_`bcZHm_%7^5ZWa4yVOpx9vs?nwU6|o{pVBfZ@?}iXyed6;+WMo zqQACwczv_=@sgKh!eD}T%`74Qk}~ZGu2)kTLqGDI8QNUy)ZI(+{v=^aH1tAC3jM8a zu1?;Y^4ik|LA!PY@2t@m#=B}-5L_$U0;9dYEja$*y*i;0{gr;tkK>4R*GunDP^b6a zEd9-%VSIUJW-a*#sB0K_b`hFVKD_R0dq^Q|F<~IVHMnW(QlvHMBtullmQPOKg4 zDbl7!)2_rj2KO|??G_pfuBU4tI?O*I3Cdc+C6L&`^B~HKF_XkuAB3Ceuy%T|1HcJ z^+kHWeoY_;p;cbxcA{Y z=sGtLnq8~MM~Cb;`Z`pHPWLG`QkQh=FX4LqHP2@li>{Mhc`iY^J{0oenAE=K_wsoZ z>pi^H=Q)*j_Ir2S&aopUY^~bItR`|0dTYhv(LO4cH z=lX$k{TuF4MqR2uu9sBA4UH-2R{e~7$URmEWHtJl5X~&)L{80YT zSaF;jrJUy)Jh$Ok$O8ZJ(vf&C<;xMbAby7sKF8x(ozv7KKF`p2{$5@^zvFx`k}{s@ zmv+zdbb(%FFX=cbUU>HGk7t3lFy1RegX^Yqg=>)e&dw*ck>k<1rZWBI8oCSG-=Myg z)MI=bb3Zd%n**id>r=hw`!y*iPh?Cph=2EBx;no-^>`Ir;GUp77Zn_deV+ zcFy)}&sEBKj^DX_D|EY8?3|qk8a$ITF`Bs!c<1!xJUjP$-p8{#y}_aU)Bt)?iIt6J z0ke=c(#AeH3Ow-P+U6S29^C3UW5Mfro>baSULK|F142u}4Z=f|-3G2!JVz9Q9^xE? zoP;doO#{~?>i-TN|0M0dJbSN8A#l4Nv6*@b!-IFY)P`<-DV!flMKa$4KZ15|N!gXu zp0XFu59LJ~rCHjg zO`7CQzOobKQGVr7UUoynYTl=U<00DVt3YTDEDf#F-;jLyEe4)2&s!;Hn?y0jrO|OW z5ST=G>O=KKpmPayuAprC_+);02-xh&6_NL`L6+sire#hjsUF)YJ`=?&8# z?icG0^C)fhjl9UaG|IPqW1rYJ_JwuW7V>0Y*ax<^ePbWlZ~xmT_LXgJf7vJUX}{P{ z_J{pqzlHn6zOY{^5$rp847a8IB8}nk^#7+{q}x6ar+p)T(qtdlN4C4X+XuFz?SGPX zcRjPeY$yA`KCvI{qs8!LKe&$DFV6xi-D^p+y=-Uu)qW}($C}GLCoi9qex7f(7;?fk z!c6EX#JBq=&p#42Gf%E0{}VzFU>~RaA<~Xf$9&!sfIZFoBh*`-`QSyKYmlA?{-!|t z1fI{4mO}n2==m#%E*A}t;9Jjn^b&krfR3ubenfpQ0CN#uHxu`vj^r5Vf$ndq_X%JM zLU$rd?#xu-wzt(Wgc~D|4g7gS^#qcd~3*`M4cDFxq>9fO)hi@L7R9L;7mgw3X1O%~xA2j}bkI>k=jsw9Pi6 z%vQ>1(@o(yGxKLg;@9Yp2Jq287WoJMYS5p%;LY`XeKd0?aPpD@9_>2%Ca6d1-{kzL z-lu-IgLm~f_pa69HuLWMtKR9H+lqAe(bXsQZSYK(`lS2R+Mk@GorCj0lYUsv)#|3| ziR!uPnCge>v+i>zQ_i#Q`aoQzOuAncv^$sU8{vFifpYHIYpaT4T&^NtUhYC)MZ#&y z>T9S!qP~U`!S@OI`XuUq7$jf6z_Qe>Z(&=~^-;9{Z$PW#;S~7wZCpushw?uI_b6?u z&!hg0j+a)zW+m*W9r}`{A7eE6j=>)o`}#cgBV3^F#?W6BKHI?KV|1kB+VS}}{AM6_ zY^M@0r4D&H11+P7-AB;(uql1Kg?D`pf8jYmJEua^Z#?UdSc$TU)J^e-rzu+w-kQVz zD(ci%aWyQgv|@)vS8H z-v!dUQcl0ge$aG)eElQG1fU0c^-t8-HXfLD&~unE{zgIgBB&Ru!(QjPEU@0)7ruwj zvr_8b>Z;zmmkQh=+Dbc+da!!$LS-H5Ey%lf`FX#eIo=^LjyhSna503D?4uwclC(Z>8 z>2@e+^e(?e@K}xZ@qXBm;P{pD6X8ppTR(Tt%W0GH&c0~y-zWZzyxRozbL~;yBbbBo z^5Qq{_wG0CckUc;l)C)h&+_hf?fjrEIBaKSf3{CPWHckNZOtJPepAr z`aC(7icyDSLEDtJr_t2&CEt;DB*&|AuYOJX?db?pLK8D{bJ7sJPU8`@%o>1i{T?nEPcY5%14|0 zLcVrm_if$#{Tu%0lK(pNenFY-(0e?{yhqwr%KQ#)d1*}gTZEgu>jSu#diAYe1ikAi zr%$531tp;QJIXbMx54l_9N13Ku$j6(qwLGz>Oi~~9%I2#3mR9F=DCZ9sIxk4;rWP? zw1>K-``IsKAb{^E`FY{Ny9R%t%xBc|5jbkbpbvmE4tW3P)w2)!68b-R?jP3xR=xHI zaLUUv`0(78_N-bwYm*wvdm`_-iM4NO(@Eg{I%7$D*i6R9ByeO8u-*gHjkIQjcHqHN#qYFUB8)D#M!{1jZS~-&!ZVPJpVz^Cf601YQ)nhr|obn zv2#r@KpPX9Kx+rm8}TeJ9mrcws7)9GUCMiZlCK=?d8QcJN57Z@IntW*6Oah;0q$8q3bhe_tzX{tY;~KCK{JD6a13v8| zuK?$nK;>i4QhAqIb@H3gCkwz|mvnj7HX|>a!SNEo?^ilIk(UP?nF_Jy06&GYkMf=^ zNFS1?owq$pPTK`t<4fm3T7aV?b=Ef4y(K9_`_N?QzOU zr}vC+j$_^gb|ztB06L+)DWM+i_8u@3z-VF-?H2Gp51pP<@H@*+*>_2Q z6rTjk-rx2Rc54 zCV3eFK4suZw0j)9-yvO@wj+G+g1$&_mZ6Sg(AI%Ev(l#3NmFLM6%9N%Gs8n3@OOak zIB0x`axF1l`)cdXxVGA%P$-uCcE9u4k^3u7j>euJ?86yKS_GYiSB_m4I6bFR9?v zK0bq3zgqp(U2~Nmloeb%J;PZX{0+e2TIU+*-M-o{1GGbFV7Bv|8{CV5s{)<~+EiKH zby54d>#4H2>!Eu;uEX9frCj_X;L6k9XQf?KUbcX1C-9NtpuN2p(!D%wMcxmojmW(| z_xXwi`Hu3lrqT}X^HqdC_w2Mcc^9N|RdaA|fM;z+bIH?=q%Eon=}W2G{lj1B3)j8n zq`9Z44XZc&xt6;AxxeRH>pr62Y+Gn_57B+c2jF!bbuYy|#@>WF;E|WDz=>>K}$#Q&0|btpiCk-Mlq&JgHK(fIA!Aa28NRU z0`;rEQ~*a&+PFkcz9(Y;Z~G2ott#|1rcV9NicqE@I5!ZNXUup8z&&vN%T57*nLN)% zzDNEilqo{{Y^092gUsE$7vkCdde2vR=1N|ihbqU0*6JAe^32@N@Zq_+w)D$k;upcw zhqn2f`o5!lf7);t;YI54eBVL%oeMsFz<#E09#CFCe-D9F13nYae(yP{$8(59;CU|e zE~Ag@!FK`hbR{^ijgH|RdXItsW!kF)xJH1tICSjhc@u5&51}&Uos-hQ*BhGU#d+)y zv^byK2FCd+2Kt8enND3ji64eG+vE!6Jj3uLaQjI2+`}(~6vBx7%;mK0uRMDuvp7UtL zvvS`M;9n+I4m(ch#Csdccvkg&>Rv$G$*2Ak@uZIi?*Q8d{@x?+Phi}W(C4cp@fX09 zgC_SZ+@t8odu`}&FXLh~>kZEjgQH0R{PfvQ!lR@qOS;$b1Mu#3D2uk_yQmKAM(RkV z{9N+-()P-;CBe0ZGE1SOJb8XUA#Y;09gMU@eVk?1+FmTC@1O4mfpi5FJr*{ zH=#WAo}|8B^!pfb(hh6Mx6K>yZu`~*cRXP~_;d6AJ-D)i$FnQ%fulZosf0vmeT?z7 zmU5m=c^lsJpF99Q|2N!wxG%;43*Y{Ct7m-ehxcjAQs6EKeZ|4^D)C9m&!gN#`0&iK z^0EFep3iX{On~k=#PPuNC%qrI*E7a`$knaPECsrO-wpCtLu2qKqkV=(#m1$j%l~a{-uZssBl(Cg#)u)wr zm7ym^GYRB%26oHic+bEgXQM}S|wT;GZ$(v+Li zpi{a05O|c~wIhY^?$|<|3*b+mnR4L%l=A*}+;U)Ci?zwPkFE`767?y2DZeRWHKCpz zd|%3K+GQ${UKA3%yU6wV8qcF?17);N;b|o04}(uxZY&`OY1&b=zbqj=GJqb;yYiQJ z8+o2%BI#d{A0WO-Usr`kWsv;Tr~PI&bq}Kr;;CyjFj>IsKCt_WKl1Kgtu}l2f~S!_ z0J_`@zDca_r~AC_GasU@SI33)CifoQ(>=_)d(Q4zsMoy$oP%Q;csIquFLQ!>%PVOU z_buHUc0b@LzJK=urclnkQt$TEp5GYS>rmdk(+0q+^Qo(y21j9VyMOs9xaDOxaV5%l zzvyJjs87B@nZvZ@G4j;7A~S00jRgg*l_jM)88eG~?hu0E>W#dDdCr@r9OFXscwtAE9V&->zb0o#JQ z{C}Sc@VXKhd8rI7-|_q+^?b?mDR6kMih+Kxq{4L zv~6|DxDS74WzP_X@I0O{ zle()@PCG$=-u20y34A_it3`Y-e+c(*_}z~@;+~>sSf1tmC-VN`{X@!m7Hbf(dqTaS zGeeNINF316n0M_6g~{_ARSV`Y?H5Trx1-Dr+WQyqd1lMKPWdcF{-%r}UbKbSk2MI| z9po{Ya{58rPPV`OplziH@3wDqo^3PxsyojC!V;dH3+<1#Jln??0--T6o%g{6Z7q(c zr95jJu#G)?rSFz~sNa|8wEBTt8-U|Bf#)H>>sPI9!})X&X^v-Y%}se8O1}MKKRHHP z6J94cX0!p>M+L$474Y_1ACk4JL=$W)+i5W&JN^HDbg19$t8t`xJ}HIR`(T`lngQ=z zwv05}QhzReq~FUQYCmmK8-UaIsu*=?Te%Gm`{;G})UMSJ*cYhBvEezm@WLi9c6!~tYhab#);qH4fxsyJ+7g-gM3S*X)9X{4*x65`);%u zod&)tu#S;mq4`80G}cx@+aTa3M$?~^F9eNy89VyAIX?e@#})9mNM1?XM1E1|^sXoG z-0}Y@KP4@Kvfih7BZfAlo;BdFD;)jX7JS`!mY1v0uDw8ezV`KuJZqoVrmrnuTfDxa zDWq#ZaBod}zV?YEaH`AdU#gu!yNC7*ZS%Q_-79c!&b>VM1GU@hOR9a|Jpy&?PpQkj zfELiA|EM;6bz|)m>d&5W)pk;se0@*dBZ%N#f6Hku16hT|OKA#l{ z6T#IITJ>9-3r_n}-<3${oKG3|1DXP-KZ^ST+UvDF=*yyyr{`il%Q_$UWXkFfr*B&l z&)O}fkbVI??i2I@|1@yvE2l51{%}LVqpzuFZM}CvA6NZF-D}eS#ytY}6`XI}Pmq_6 z;NA;dR^pKaZ6eY<9RB4+e;X$~m6P z0_V4q2He*K?J~d7M`gfK2zYrpMS3sZmFvAz$GdsFk7p<4XOrI~i2O>dttAq=hB0ou zOQ$Mu-j_oYrrSuC0@sP>-tka@ z`xDaL8=pa%^}6>w0NUQ6&J)z({PV64#4|C+Fbk4FVwk(yyDb1k9J9;?QW2Nk#_TL3HR88;NJ)R z+O|^oZW{3}FHI>s9=cl)-+{KN)crlQ&4=$wzr$U5Nl9-M5n^FC-%@2ElFo`Q$%boN!sRV9B6xPPafWYU$pe*@4GL1HcOkVS&%TX>pTqEW0X*LmK89EK4|2qYY)9IJv?;~|`y}Pm z>u~-fN+$IPXUpBifv_-=u*fJ89aJw9jep(-x)eZaC?mF;1Eh{|>*} zfwcF10&e;G8kno#c%L$zqeJbujXJay$xBaQo`Y8D){f*DzX+`M0?*ob@5nw-YM0VR za0{4)z$Y>G;z=(K{BC$x*HGtB$52n%L|%~hfxxN5tb?ARq*Y`bDpR;`rR*|+G|### z_bC6x16z}F>Otxsl^Giez__Qjn4oO=8|9KO@8CAJ@Gw{}X2|P!ry9&H~L;2Cr?f)b=@hOXTgQxEq@BXhsTawej z<@b1qa^F*K3wZp$hK8gs0yYReo|F8N_zL;M!Bv*n^Z&}A@}mAyqF_j_R%e+;T0Z#p z9N-C_)k~CV^=VYMQIApYP>xo9R_BPLUS(wU74;c)3)^ZQaXVlJ0h0hO@0EB2++Ps9 zQ&~ObZR*}ip8L6Di8E2=b9nW>!oIvO2B$jFKUptN)MwLrvj*E;vs>UlIB{j=tDQJU=HqflvRQSEiZlkG@r&rS@#LzEeAZ*C%fQ@AJX4l4pGY zf2K|K*LwxN(|!KPcMZx&yXm{9|CMXobjqi~?@P4TSzzW6%gYex)gF9|P>A&N#QLav zW?8?vg4Ct|`cu5OgHHY8&+%M~^d#zC1s$HJ)@Sb_(pS*dr-?nA?b+#1;PGSXjfbB4 z^hM8&F2y_b6~Jp#E%> z_nh@}d=K(60ovyiY-8Ih30(SH>epyH+IF_pMf%qE|CTn}%z9uuPlP7@H0`s_;Lu;P zC3&{7zMK2OqmQM1cOSa!(}C2lZ|29qM*wI0R$`oZ&fUJSA8mWj&$omA&q%jVexUq% z%AALW+6M4HQYh4?`fA35ZX)>$2=b?&qitFgINRE@=C-f?n4W2`0}lI3KTFT0+g|!d zdS>18@Ai#nab@NOS`O8gr1|4iAV@KTSmO=xHREj{yY-+89} z3+VE!`vcPT<+LyL{q(&2IcT~|Is5#8^udcdsXhfy65RL90}a*1OFyV9=nJRMS})p2 z+p0FwHhiyHphdqn?W3PC4z!7?Z|Fa#ZOb!m-V5e^W%?VnA}=R(>0_iG@B{OKI)MLQ z>pkS!SoO8jw@!QPtGw&?RsdLa8TWIw-Rd)Uhi~08c>3e%GpDbdy!-(V>#0LsE-!p& zA64IL3mw{5-GA0j>VG1gqrCQ6?X6A0>7I0JV(qa@$@9*s-tep*>i^GXCoK+I)D>Dm z$7e4UEapb)h2Z_r;|3F~$v(g7^6wiynsV|pzW{jjA zkAv%9aJgsL3-}p~gDyPZfY#68Bc70#_r`>g;Lz_%A1UQbeM$7yC~G^>?#i0_gXnLf zFG&n#I`ck&zPScozr}~3zdB(j<@FWmO}gi=GePen-n;Rf#Cg6OMN z4SMxMnFkHZnNQOv`l2Yeege*%)UBRT7ur6gocm9n=gtCLJiI7VyaQd{5ul%x>#y>N z_Q`f+YnxO?(1xk~up4lR#BIsz%JY11HzU?2r(EWmy@U6m@DcXcy3Mn4&hNlvqkXim zYfI7I<~lwET(41g*iNSmF_1LvW!h}i8~o3EBgd8M`)Et;<{f~YB@Tk^)DReb+m_L1M=0Nvez`$jJwh$Q1Yok$#@gYvgUO3L zy-S&A3EDg}(hk}UpMqy?>b9jaufAp0XFb|KHw54l7-j0aq$}qZ1+Q%z5sj=%Z?~8FRb3Inp$p;?STV{(5^pPVDq zrQK7%Ds4;JP_;j$-+Mut<6S$FHcWk(wI8(r2c)Olv~mS$d-|^eb;!$n%4$!J_Y75mS zcOM+yW10rK;*?c3@cW9QY)kT#D>8#aoz?qGvjU@R;kT#FQyE3uUtj2P56!(f&j!lN zNN8?IC`*`6zP=CoLc9(Q=lSlm*|=U5C*ONGGmzGww$Rr>eX9z5w_Z;aF^jtd)0fq_a{3O`4_=E0({=FmrMHKO9Dy$HM0k&O(w5x}D))Hg0j{nS$!zTSr}{d|&nze>Gl<)7GlaTCBd z6g>Kn6z4m6ld@gGsct_F{`Bqf?zO(a-hxkgvD{YL&im=c5x)S8ch>EQWvs+tkD&cZ zgKs=}DbV&c>8GK0I&D#l^3lMb1h;qGbq2=v7)PG6V0m!IP>*)(%!J95{{Wot)1Hxh z!}=roO{W2S1G>wAZyq7RZ<=;H2JP~Ci~iDgO1~TF|NpwW5BT55`tkpcGfJ{TDF>f( z9HbB@vLc%(GeWZWJ}C2WtjL~`y|PDEiR_WcjQX0{WR#4o@PFLT>GQjF``>Qw>vp|g z*LYsf=XG7L>w2#1dM#6#b%HUxwhwCzcC63QvUlyG>vP5K7yEXwPcOioK5*^={r3A~ zDpMPDhxFpp1fSme-^CN@9%#I(w`eeptv;A6e62+vl(#}OtXk57{B za>)+CC%5oB=lf31;QQ|U$2Qe<4vtLYYzbM0j54k=M^uLY`X}Mxyc{`*-~1lM>qUqD z<~#4496$Rv%G)`*M%6C=_rs#w*^@PE$2g3w&J^J@MP+A*-tGENX8KBb&Xtg_wy4f3 zwf&=Tb@py-wCoqWIeP~ms~_A?uuAi3VRNp2SbIb~O|ZVu{P<1l)cKX!xOzXIymh*^ zw=UnE2=0%Xs=lTtJgv0N!ui+H_G#s;E$*D6&wIMI=<~>rKdBA(O?m&`Xzst$^Unr) zN5c7KdDY*qdQTV3?RRT;&&FkR@j%b+zBKRMEr~v~{A`ZO6fP>vQGNF1XG^y)-dy~b z()KRTzI1$H+u;1G*k1ab^4PHLm)|YTxe#Y5?3>&6Y|phnF5dqUl^Go_{4y@C?neE; zEck=GlXchryLn}oDBz{uk4EQHPVYbaH{R;(>e0C#U%RIi^Vx7F3oT1lpB;%kiOq*S z=&GLgtQ@=2r~O~0Jhr5p>wEg^ElZoG@NRuLT``*zyAJ!#SAwxhf$e9V(%FdEqnssY zck&J1xx?|`QH^h~<{jSqA6|!4mM!b5*_#K!WNTu>Vvl;G`YTuW;^xh?<*igXHmy|} z7w5@;6<%n;6Yv!wmp<5k{vYHia@R@)vd_MiZhd2^4_PA%{y*|V5@wcdzH<805yw`@|pMd{o1hzDAs|3ga0Bk@DQyRe5$C?EgB zXN0W#&Cy!FRr%;Yjuc6D{aOBmB`pPPS*mX`jMz1^ocFHp%bDmIg0$>Vv%t z+JZAVnoa4H;|IyM>>cRT7i<*z==k!<*nFGtSNdnF#$(IU$^Oqbm+?>f5-s|W zuaLTAcVojxi5_6Zg1((lIkaiZyEDFL?RbaugZBqsXf{6RJ*8ORJQbbBhMc~9!QGGW zMmJerf58)f;+|-9FW9qjzq+7L@HoZ=k7LZ(w)E>)n}7O^jS4LrRnNQOUGeVB(tq>+ zGv#{+e$#sBz40!rTsmI*(CX>CYg@nQ6wKWj>m$7D%#|^JU-Ir$=KabK8JIN3zu)}7 zwLUf``-a;uD({`!sWk5qd&>N!f1%iW>Fnqg;k#ro*`02xT|RW~9{OvrzSG!@Ze6^* zbhNw_U1t@@d*>A2Qy}N@S@=i)$#UdN^4rj2GUpb<(|ZPw40>K=_^Di9%y;JZ#e8XI ztn8WvJ~sSi=rYgfdHL2O{wsI(|HT5|8@kSGiuv2{ad4)UOv-2G^`7^yoU=xJXUMiI zMz3$FzEU2U(>YtTyxjBI1wJ2qN63+6%5w{RWVRgMzFkay{8ef97JM&+@5bxlNUytA z`FuUdpU$uHAvv?QXD*HGxnyICV9d}{85sWnp?m%(3hx=HTtY-4e0!3 zFs|$wTzC!MtMaYj&0k&jb6gb8?&mI|A>b5dq$`A z19Jbuc?Gtpoy&8l=Fq}YrLp_mTyVd_Q9ZjCf$hroHU8ay_innA!5Xk>G_foFHkzE5 zAJ>?%FD=m+x})LHV7Oa>4a>a^?)Ds8o%^G~*#m74@Bgs+>7c?+g=?$pEdHha-@f4N z09wJ^zI1)CYr%bq+g6vq<*3G7|7_p<(!MkK7uU{oBbuA#JIkQYW~$8Y{r{x;aQRyp%_H;lk)C%i-CYj8-?C5tFOyaE0-Rs|{%G)CvoQ2q@|NMW=Mf2X=90>2b!g+M*PgdsH-k)XbBXjr9J^!VC zK+9~^y*b#I75}*O#e(5%xAWm|m;PRO(!p$A-PI~X&%&01N2jaj7rRaPom2i6&A0EA zKXOFtifvEt2&?Uj5k?U&C7!#-&b9rT0b1L-X6&r7EZ z<$K9rlHa9$mG7lJ%vo4xXRiv5J#@dqy9K_M%k&P>;jUOc_oUI${&3W!{dn!lKO5}p z>#J`CZ-(%-kJGc-gXwX9R=iIz?B(`vdRVkf-Sd4tFJ8Q9F@Hz8X?~T?zS5cVpWGt2 zE7$g#mHSTf=LeM?;!0+IL@L|{aRcU+< z_(QO#@OOB>m~DnX1Un>q4ciPK2R;hR1%nO9nRh-C>@UxicU}F$H-e3b@59)dJ0kc~ zMGO1NIfdtg$L29C9NAC!jI2>Q8w?u?8wsBRHj;fyAGE!&hfD~j`xH;;{|EIW`|&IN ze=QvNKd`f~*|4qfYuF^1{2th0_#>=RKYv*MH_%kw-&ObV=;8a|PXD=r!B>KB1>c9I zOXn-Ge`#nTM-0l8tM$X{g4eJe+<+_A7yft<2!X=EPj!(N3=s&`Cjdlh!= zKe>r)LH;2lk$uQnWS&ziLpCEr?%Ch%3VRgDbDNh>o+RI`H)4{!M|L5V2~qEpUMUd_BW;P_88Fpiy0P!GD*^q3?fX$w65WZO0dmE5J|N`xNUdG#%T2 z_#ab5mefD+9$&0a$TWNRe^9<8Pg=vsjpR=9ntsq9)<*KvmX+6c$(&@(6_hWWQhl_OJ<-PIO!g;Z>MOLITid6!Zh6PnDvU4OH9UT#bpB?mMYFrx zywBcU@8~R*KQ9>GYj)K63;T{}pDKPee5?&e4oq4zP7em(V(+iD!@6R9xvBSr4-`K+ z@33`d+v@OB;3xHIZGP0d>b*X?HdhGOf0sW^H1cb*F1%d&!{N1ku*Q~uUhS?Lycvr5 zaZIS~&BE2Y=YBXoO5S1b+us}4Ye)26*X~Ebd?8p5gom|&f5>aWqnmxJw3owUiq;h8 z!OsY0uloPHW6nFiZ~ZlGFs={Rp_SXJv<<`29dGom-)?O78&!UA+<)=u=+=SO`(a~x zXT!x>^5@FU8BWfZj}885jnBcYYu_$^tIF|ncOU)p!P+If`KezJeyfJhPL=;r^=p2j z-}>_5GkI(1sipC6pDlbQ3yyEI9uyvY)cM#?h>#5jnx~aJt0ET^-(CJ5!GF27eD8+- z{gcY`uU{d!{PFp_^RwT-@>d4?)L_%))Bk@dTIlEL^!e?N4X!=U9?2i|pQTS(U%e9j zQ&;Zk()nW8%j~CYIOgjcjhnsI*{*9V=Ul@?AJbsid+lYux3FMy=)LOvyYg?8eot`u zggQ?(TYW=5bO*J2p6kxZeq9?UjA(xd&i>)Su60~xFAIk)t9N^Kh79jLoV|W7@6VN6 zqIByTTE+zD_1f`Gi;sgvPx8&+|Dm#T2lt8EU#t62?K~19 zFEr1b7rnjl_C1W{f=>rYf3ny>V_7+Rf_Z{@Mle^Y&;L|l?>e(`KQGX&+*x^kz~3ug zv3Ye%<8ednz8pL@x+(hq`QU9(8ygPo{f<80+&~My%Ds^oe6jy)MALlX`%LkX)%itr^ohRsUGX!e{kVE) zc`W=7DI6R=hxDG4Ll*A8b6U$)X7BJIC;huLGLi3tl3{iTx35MsS;k%OBkK<`%~{na z3z2Q+sQgLMeOLWWUOFgzH!N+f>XGLbZmh|D7l+5I&8cw%&5P(K11?ctx@+u-+MTC- zHc+zM_@3SS@^1CXhGd~Rf`^vJqk~LxXtD1k_=dqQwdY$0%SVInCyXj4yO4AK5MJaJ zGBY`u+;e7S4i3*>X2N~t+>hT%; zcKEN_8gf>3$iieM^7iD_Av=>}$kP1%PA$GATu!PU867R}N5krs<8rvftaEfkH+?cgK% z`tbAFzV`X|oKqRTJ$!fgw}*8yz=GY>-xL;#NDIMM-M+AzCphA#FvNP5T0_Lp7E{r7d$OrA$;ly<)P)rrC(5B z`(hv4xc7_=Ylq6MJ)*I!J$5N}Gj>$As&|?j6MaX@d%d>Vzu3Ciu%8YV+wa%IjV+B0 zi#`3p{-0MH>}AtO<8Nw%z05f!euiwW?Cg9Roj-cDI(%2wDgHyWvx%{dv7!AqSjz;1 z4UJ8Vp9@;ZZ*-b>g##JVxj_0&I#hQW%~1c4HUC&xvvTyH?j-z0^w5>QQCoDa&Kc6n zIyZQGY0e)yd*=?rBWi!k+T5}_Y*_qx?vD<7U4B8%LDI$eF3YryBR%f(1$vqTgTq(& zqSBA9-y(P7dh?soe^on^mwt8aqh-cox@vOYB8At>Kco200vT`K=yA4lt#EX9lVNb+OMOP4PJd2jr2iflUi9L0-&+iirw9hUIX~dpE6Z1yF8!GB!eris19db53Abc#tvqv(MSH^<%yQnbSJr%);H}&D4MV z>buqRzK;q|Jn8k#qa)M_&hb6}WJGIsbtW`+_{q7V-CDp`e#7dKZ#N4bT2`q1dmrI|r`pywJgWy~P z8Il~xhkfk8q<7f6>s*7g2hJOi4V^#uV=#wA<094Jm+w9IzVn;^L&3QT-w2p5T=>3| zd$($ipyhzZ-Ck+W{Bhx<;gfVo7exQnBPPi-&hG5j|KltFc)`9kzL?&ALNPwXcR!b| zK0agT@-M9YQ_CZh&03$c2TWVQ!;o!OFYpukdUb!=|D~!&?)rRd#wOu9cIYIz@t|Pv zEyA0zfBi1l`}U5#+#2$J^sI03aQ0n{%^L5ig7=N^9#*|~gW($&OBeG2^9_uBqQUnu&@xwbM>pQf zjBd_Gi*tF8G|qFE&av^a@VYZtqk4AtmV3A6sLb-!agNX3TF%%pgzp~Jb^n*Ur|PQgulDI++{drFvw<8)Yo?wK3_gL%6)?F-dei; z__%k-ybgc6P$6XHO zI_Kc+iF}dW9r2~$|EE6vGF*oHUi`HCyFC^V+`XOb6Z0PwSJzg1K?!?QwKB^t#st3qNh2#+SbuZuoD!G=0y+ z{kC*|@pQ8GNqhDh!NMQgf9ZAX;X?-|(SoP_UmYx+%kN6tq_XxQI#Ie;yfod6J(B*F zUX}i3Lb%bp(vRBz=yHYyo9@&ei+{%F;>Y(51|19???ioUw7nE+bgoO+Z}#Xb${QCP zdKPPheb!oXS?T8&@acDk*K5UiZhPtI|BlPoz^|QS3kw$(EUZ^>XBj`bJNkb~@q^8y z&!M~U)+y!-=$<^hIepHC)f*f9_oC(N z1-_ZTEA5iX&RgBF!P~sL`xHNG?WsLJ;LgBrSJ<#XpX0lZc>KNle`jfYF8NQMTb%{M zW#8yoqIOO%!11dE`YC(LBBi}Myt!37&a;2NXFlLRtPFnNJqN!k?I%57)L8tdaag22 z<9m5V>CQ7bdynq1JaMvT^?%&Z@F;Ko+IXsY^tt{&P@C@9o3XUBnjd%6#wI{B`M%gg3kBI-@4(#(dN3y!w}~l{?u!sf}p{>i7EM%X2no zYwJJNxw@DRV&nd!<?O$YJ$>e44zU#(?mA^%v@)Tc`-{EBxn%O4{fREeo@hT>EqL}Zw4mwy0=b7y!~UT^$z+4)k)3tY ze`Ga%HKelk5@RxW9+|FY@cc1-jQ$)vhkR#r8%w$yvK+a}8D!_LoI|FMaW2_@VeeWh z8qEP?GziVOf=F$Gmi!byZ zo>V#;{0_BobMNYG#WUCLX{{N@hWks^eW5;Gs`=)-euq~6#OiH2BL1%M<(~gkx^M0+ zR{h(XH|N*xH^%gy1>=!uUUx`5V>mq&EgzKjjoLmmyna^RYQ>Y)#sTJJaKBR862aX# z9E{5`;qYGY$&(X`>GE$32HDO%8egg01(pAHG&rNUN&oo;y;xaiLEZ`GX{8@hS+d`g z;rT$gt=TiZ?qlI~O)!=%{lL;sXe`OG|E#@V2kW)c-wDnSd%mUee-ED*!|R|qn^VDf zzcQbccX`iWsgJIx>>fQIS2})?&VQxgqh-pEo z2*+vbpL@#Zw|#wZtnK6KyTN;tf7=-TIvDGhzi{|{T7Te&{#ZM6g_HY(&oBRt@-`Z1 zUkui?J+lX`5DxoP-WlYIO?FxIY+oDC*9N|6fpA}^^pCtB;d5r`4_E(%`o%Z=7AtRT zp&|Z%cP5=wofpF8-pVc2eEj)9??yPX5xv_uI8)-jYWAibD#QNtS^#`o=it)ZrF3rP z{vJ%<_HlR8;o9zDIPU<>lz4Lv)5?w{Um2&hCxFHsN-B^;WEmvnC4{^W(p% z`Mh>@k1gyTu79b{Nx}FqJbzVx&Jmo$>s(`TSmC+oT(&jH`Go0f`=W4mUS7BH^arbxhd<9 z+j@4d^Mk=~=5D9rEqdOzuv+EG#@iIT>-me}`2FC7{zQIspfP*1^ocxW?QRr)->M!y z4d3-2|R{$;!d! zFV#0rqk{dL(hllB-8Oo^YYf75#`^k=(l@Fs8r_ZLZt%4$?|t}@eJ`H(Yqj;q!avJD zy87;T(qFq5X081#i@#kReRNbf>f4vY&Ad1~`t&ay`nvU}{ux`@7w2e>7TDtW29T{M z`aU$5-Iw{L%5Pga{$iJeue)cRr(eGQb8dug?()Xidi-4T)VT;}?A<+lTYd3l@NVgy zm@)j=ajzQQdmFsvOFw03ZI*X%^THzZT6H!r{f5d|@6V_ockKDL@~5so|I;5==8}p# zm+7nuod`c#w9t+3U%0G#^y7T6>B{NJ>BMge?mvQeVX)}R`HRzM(~WrdUWda-t^qJG*+9JMjt>=P7hB5d;G_<*1-=pP&+F&Og^mS!dWzXc#%72y5Dt}fs zS+KfCh)-_8cQPU-BbH}*_VM4y8eyyz8E_l3v z_r){Qs|>3hHca{yvIn_h-S8og;DPB@=tIn%JRaTGFQVsx@_g6w)7C3;i7}${CUcxopO8h#HrLihXippq zd?vZ*g>WHze6@1Mhd!Pjp1ea(Za$E2$+_gIeMV1`k?7ga4R3x-{FRo9PV?igaCogY z(4rspn|>lwn)75#b5p+?m%;HOQx3jEWIpeK{v=--7vpKX^uM`p{Jaas*ZW9z9F#G= zm&T6_N@nyY7C4KS<`$WOOi3&p>s>Iu^Ic{KGQ$?z_{qUL3xpU zsNc;^V_|&Ed-K;^CU24%$$tE}jEz3k_vZOt_4lH+WsJRt`rf-pcJvM!AN^}?OdQkT zkmrr-VZBG*we5o8T`~6FUGMdf@bSJG!;4$v_NXt>a%<1~73k0wtnA48ip?JHJfZ!a zFBn_?5v8qNe@_-38x;A@3|-v3_1h}ZbAD~|8RHMIdHJgjG+vdNuXgzgY!VziK3lvy zBiQ>_4yH5g{01HfhI>Y*sVskjO`BWvecMFWz0vZ`%F!uq+}yl%M0-|sd?yG0#Ww&g z?#MX5e0OcI1@iy?O3&_*f4O{j*{~=K}Q^pQXvd#og6>g76^h6(g!QZ|$;MTvj=<#=ZK!*f4lY{ zuj&lpvt;ESE&b8r3o3Jb@X&&1ns~QGX$MC8_0=0vjMurf{D%vBmp60ImsXZdf{o#e z(e!EQTSxna!9Tq^6M4JJ@##JsxnX%vL&E#fhc6!^>S2U#;#n%@^l^ z-|zp3;)QF+cdWi0e13S&0&i5k{R;T7{hB9F)K~YFH>P;O|GqEmnnTNH3w@Uo`IKoe$PK(KI$Xw`{DwSeZw|ZNKuIkKdql zGRKf&cK}Q`x-~JHK8dEOgL_#xlk?7~oy{ueEdKk^G`=}9HdkL?dEcmimlLn{CvYVt#~c1jlcwS zSAN6#(|dhL_4lnDyXJ$n>pgZZ&%Hq2aqG|r_5WMLTd#}1RarL93oGjkj&F9b=X&4y z9I%6um(W7T#J=b~JE45`$CK+{eeBHC&W-WNf_HF*@R?uz>MR~?ez4XFc18VZ4RC&+ zAFTI%#)00SXx4Y^o5o2$e-u66udH|6Jn-(HQOpn4{oOOxE%@X{!9OnjqS~508mi7S21Aju!W8KU1Gw5}fPGKcVMG>X-k7(>uMx z->mQPCoA{=)cWa(;jOdfjSSANrGKk%|ETz9ZC1uNrf;jQU29{S;BHntSMk52f4}e` zPdwe&24bRK$z9<5qWDLx-xx36n9-H3AMTIL-W(`xWVHOeax>J%Ps)F(^utP9IoMl8 z^Bv)gmS0xKI%|Esruf;)j)^X3N7=!bt&f&1UZOO%8@?^p;kV~3J%qL@KE1MEDE?3V zJy1D!B9P0imDcNp%A0dU^S!Y$Zu*j6(5>a0^Tv1D%6zkSE@(}2M)tb!V+;SpJSjf7 z{Pm*IIpX)Kb7!qQiE!`76zH-}AY$^urrte2R0pzN4{y?W4svm}UwNe?w<5oy|P6IqVGM zrvsCGgoiaZ_$@l;I8Svxt~}ZI;{NXt>?OjDzwpuFadY$O%l)6fJhm|Yh?n;+k!kTg z?kDwKg}(-ST;b{Ft@}3)^xiZ#cd9%Yn1ACJqS3dL-8JgmC0U%Td_?eOD36?t58~5| z7XE_liN4?DtgLT4`Q9eqM1IC>jK0U|o0?y)>@DFsvb+VVdszMAJD9$Yc~Q?ZmCkQ* z-u@4dem;f1mC4U|vSQ!k#GBCD_}&y>M(5WK2nO8`+vSMbFmLEh_%WK}I~QiG&W^(y z|Jvm1Y;B1b;+xbcXt13I7Z351hNXHCbsRi=Axn(@YYn8=2+aE_%?ylgj7H&U{Mr)t%xi~-jrRLB9wZBH|)q~Yv zs(H5Li0G_c{J3?9t?Jj+d$zXFVqWcB8|Ilk!P;-HxuZ6(EzRCyZfxGOxwU9z$ieoN ze^u5THCG;PtjVuWl=hGCre~N?y%lPMT>MG@cMT_V(_C4(=TnEpZ->Vo;j?OeYwxgs znUA;C7QKc0yX_(G)UNqQj{aVKO>f?bzBmE?mqd^T=Fdw>G}!%VzyIhs+zc za`VmnIW-tBRhKS^?VFu@w-N0R_1#;ww{HE+p3d)wJ^iWj$=B~zh8>Jw5IZ>gIa@kC z#m}otzeR82d=(wa=fi={gbYlM{-FP4c60IKXe4v9!?V+m4IgLXhF1>%x<}8};;}vB zX?G})Rc;y5dsuAUc8C9h<=@tP@GUYt)3(*azgm+&Tf5_n*B=o+!zc06$CUq2btdZE zD~IQGc4ft4^Tj-!P#JUB+%r$fhx?XhKfJj3;DWh15S(vU_p60nhfb2wZY~ck^y}c+ zoB5f+$-1~_Sv&TB>w-1X9)4>yZC5&7{&vB_2UwHMbNm3=2!DWYK;KH$9a$bZ3SN9$ z_b+BAuy=be_0_SZZBf~wrQK9n>+nU<_jq&RzHqsy{Bfn(6TV-VGaBygnauW?`i_kP zE$E!DpwIF5yEixNOTLkKY4A_(d8gw4>-mJ?lg8-D%KHZ5$PwXE+GpmPwBJrWPTVH^zpCABnuFIA zyTfz3+Vj0dc4vF{y3tFHaF-`q@SFO5&_9R24L%oN%2#LF>f?3s!u)KGDdtP_i)h<8 zy!4fQk8C~o4aE~GkLQ{u+E(h_#1}iO#;*s@d|tsGY@GS^3>n_O6AjMBxdVK7`SxzS zIUgcpIA!rS!q<4M+c=mfl0WYSUQ{ zIw`g-`l;8#m5ptUXxXCr?zN^{`gY})3MSpr6Xmh3{VRCxQ9Pr(g~IEk+D8i+-M37( zsh&IGeo}jM09zIFcb+V|_pIKlm0z!LbZf|j;?2V0rTSpX>RGGys-Fh$)jPKQ?Q8$B z)>giEr`7g!Jr9i@K6&h=+muFkut;@(QTdx1gEMQ+w;tGG-9zqsD2tVMN9C3*@7>Ct zU+n%mw!yyzXZCO(w7tSl+Y1-yDxRplYnvbZ72HF}ZtE^W`bl^!7ObU%!LB^2KG`Cg z_*3)6rmtMBzwDETR@WMQMP=xT?`e)NRo}ByFBnYp^S$PW4PR^c>a{Z>9O+2WqmRE{ zJL7}nZbWwzPF0%z{9|RT=V#S6y2sY;1#^Z={r*PtXPw%i6FjkY=`gns?oG8ZRcW)< zCjI1F{WmY^4JYsa;gzA^r02XmIJ@^uzsY7#_jp}&JykpA&~FF&6AtF6dG%~Koe>_> z7t@u#QMNhGFJa~2n~(QZ=b>O66h3sI-h+iIzj|%bv6`oR2GBz1L5{XA)1%oFtnK(& zI+~SggFcSlguaB%Wtz%Zw@)wZ9}IdsYq|B3j?W!jmzR$3rYpI;w!T)|bcGLHls2R= z|3GU(?fb6$%#}H>_y4NWMvv;f558~6eWT@#_%2dE}?T=;FZQ3^oQ-^e z+9AJ@offGsnT+lC?&u(oZB{-S<}G#x%$YE999uD2kqw#5<$e-(vfUG2WVKm_*B`+m z|Dk2(%Dz#avv(8v?<}74d-hebud#K`Z-!u*5A#=NAo{Ma{+Rx|qiCLD=ljU6uNREJ zbA9a2<`p@Y?CnhM$lAX?JpWKRb7^p1I>(3iaAy&DdedM!19(7X@Q80Wc6bc)(78bN zY~TO5y0*p*Orqtt{r`XROJ&FrOGg)5!86q*tB@VYE7vuy(+ATTVR8q*j0bz3Y(#BG z6aH%B;>m)2Ms1Q;zE_zW>rdw=KCHhtDUW~0kotp6LmqNw;koeMDVSuQ8N*@K%KR%B zWEnElEd6&U(22Ey2c=JWq_U@1)_0(c3thvX>o0g-&^y6q{{8x8WNp$p&?C?x&|A=L z&=<@S41Gb@zz_J8`i{Qh{&1(Sc(pe91Gs}~(O~cwFvj#9-e2Ryj?b5Xjh`Q}@m;2O zhE0FJ`o=w0bA&Iy1GW}A4!R5L$QjYY2iW-0z3>}GOQ=j7FLTB?8{0jL`4{j}upXIX zpH|SEu(lmoU2|@p%39;*FSfRsXZ#ziY37#sV;$mmV7~EPu+F{GnDBvEy?T5it|-h_ zuzr3}A6OU7QTGm6@2rQ;pxzq(&X}$ie&*X*mF2&{Uji+BIjl>OKJgv2uJG+xpyzoD z)-S#v-fL@^HHZI*bJ>hr4SH$|~o#$s_4FuFW=FB+%5bK-!_+JQ)HS1h;Dre1l zKfLgjYiP5vvG!SOtVvf7Oj@6;v3yRf8`d1_h406VX`Z0viC{caxUurSRrp}}v-W?= z%JQGWKRTbhXl3!2%S8)Y<3qvxTYDuKNkM=z3R_U znd8d)XYjvXSv;dVo)0gL?d68xK41F0!QdOWLNwzeHm==Qnp;zaH{UC^8~o=q^)sFn zEzU5p7d+H|{^{%!Y>i|G-wt5UJU;r?ieC2$uuNKO3%<}v4CRFzC<&%B* z?QUKlxpVZy#-E*rjRP%Y2(km2l1xV?AkVP9?H`O&gMD4&bz|vd&ppa}zq;EM$dV@o zW2Nen`^Y)t3uGEH;zz;$LE*#TydDgC6fz-xXNBNQH9S5dI;W^S`IF4FO0?srHZ5&TKm4P1$WCj8|M!~?jjEk1-dfvHlJ#JL}9sXzaZ0)da{j_nmzF51w zlhz_WP0Lo#{e9LU>)h-Eliuk|`oC6itc8b_Z!NQ5j4OZs>UfVoU!FC=n&yr_?>K#f zwT{oxUiG>4YH+RN?_?dZmRdJA4Gx{L@wM()C+%x!*}impva=Uw_TM|WTyvx(77N~y|E1;l@^=X@ z<2AnjyA?lQJDV0y=$Va;UVlh)^@Y;uX6R_%>pwf)*8QKqux(|H_wLOdI**MD=F^9j zJEgom3da=oDzF94Q69a?Tm5I-e5m%=4xL?ox^QFf=^CXw=gc?KJ79nOed+sE)js@w z<d7Bipg|_D^2X{1TYv0mOEzm)s1-=Uvz@sD52Y7LMBkiJND%lABAVb9WzEx4=epyCIEM;368m%i0E`gHx$^sBzmN9?W(7SQre z@e5EtQz0!9sKBCz7cYI&x+UVxX?cNUhbKmfB&&LztJ~X&=@xH&~?v7(h zrwgZZ|4V&!R{#0LOIg%)7=3_2L~;G4X3kv=guk|UHbp^?0~?_y<00+ zW|Pw9==r6dA1&{wp69IWk7iHiX`G)dUZcKRc0_$v`eD5uB^_~&8<6oH})*;hph|8 z1pn3oA2YPT9UbU_FWLraK^t5j`7aG<(;gbo2d^R$pkYum>N|^Rms0Iim^v zXmLl_N6|X8c*9}s7q!QK@6vGCr+U+d!wH3Jsz*<~LGb?G|BK3dpmF|3b#|%jHudpw z<;_&PV@h+r^{HUpQlH&h{V~mtE5qTg>b~0Zu-gCW@a9SQJMX?jb(}eQrJ%2GZvO08 zx&8Y8X?Qw6vTCp%=)XJ5W@%1N6TRz3%TkR2TJ-tg95Y77!dx1hU&h6F4bCZZ%=|IV z=8OK*&5xnwS) z=U=6n8-sHVo%#Td<`LY`Gev99;CwqRTFkkZs`sbpG{^oF-E&pOIU9Zkb2TpS6{CeM zl8w?mChU=C_v{Sz0mbf1`5;*AO#BJ1Ep`rk=VJE5cl*D5|JSGtI}aPA^W=|(gZo$5 zsZOsQKAL=u`AD*HvQfIPg{_hO^Tx`YU0+(i**1?4rgiw7+8?xI@?{tr-M%%udvlD9 z)cJJwq`@_ro${OlTO>Q>;C(6VItLbpRi7Pm(ARi{(yWtgjMhUoNcKmzM83k-VrT8x zDc!fizG(flZn9CbHH`}fyXX4h-F zM;}h6;kW!mf!snq`AT`@nHz(1W$VVO!P>bnQAVjQUravGzNf-Z^UP=^A1yqjcf4_b za`H*?6yM6rDtAG3|5W6x9z z{m^VD1Ih%0?qtLIVwvHsWyNeNe4I|HO*%z)P2j_y@Bhgo;VX}x;dKNUtwI>TF4z_juR`-Z{dXK zxu`sH44(l0M2nUF&(g>-Y&O?c2XD>)Xz9YmJ@Zw_#{5u-D~`lmIyzx z(l2X=t%a@Upwg}=|CnInr`d8&X>5K|yi9nq!?5-6UpyiDPYoY>xqT|XfB4|>$USIz zzO;Ys)&YYUybCS#&*HShoB>6>k$F->F|7uI(d=eT(ja+TEk_z8`mW zbLDRX;a1si7A~z{x2k=6*>U0M`*-U%UawZie!E$CPFJ~uBILs2d+H-+ET`=Mxbi+| zoc5~CYf86Q-cy}exC&8Yn`0nBb!|mPR`v%^>YX6hjC(+{F-#T1Q z3FbqUq069ypgY(x{EY#91zpC-`u(}`d^dcc{`pccj0t_k;CeuBabq~lQ=OlNkF#O7 zh2uk&xukX%t^a-#%+15=ndq^;==b}A>$~LV*LVAszDf1AD$G>b+pELILbLa@#)S@Q(eS6+8mP>${=Zuqec9{L=zi>f)=!U@wq$)WU+LeiO?nP`Gd@Ri zRCnJ|!LIHWrTww^$m$HKtph60@9C=Ao>2OottAJB`~AVVqB8FPrfa)38a4{X{=wQa z*zD`{C*#7OK5fh4ljLG&{THY{U5dMDhL`95H?+`e(}6!xeY&VcDns`@W8w9l_b;ZW zqK~4ReyaAI<2s{svaCC}4+{30Jr90+vGuRAMGKTenKoY-=eqbJ|2n5>PKrz_(f@_u{& z_v&5p{VnT&d!U^AJ*qanW7n0oNa3UEStA|{H{Z_Mq4{YIxT7}Ss;>9ny73eBYx{u8 zdEd>~-xtVnn^$(;>biSx_TCpV;LXwgNwC%kwl#(i%B$gP{qg)GNx{yQ#oA0r3CyMFj}edC^Y`-}NPUu$gb2Xr9xwsg+)#1nNb#m`osZl6Ae4%i%6 zp|)Zqhx|*RUCkt$*z+#?5@VCfuK^9dpclFm~*8^dR&j_7ro_Snm>S^M-!r!tfp% zO!Ls3r7NMoVQ(=%=}F8d_7?Mk?;8CJ9SVJ`J&hig?S(GqN3~54!Z zy;k+*@nk8uV0cT^$T|;NMhcmT>*Q;4(iw)@XCFMm6GYh8Ii?>$D#W6_N_#w+84@xb`v zPoe{Vi>Jja}#%?~Y#;z8a5<|Hk*?iSg2SXnuHj@rk}w zjS)QspFX@Z{RAGJzXSf4f8ZcbjGx9!^8v%3^AU6(>Zt{^@TbEkMO@ue_5BM_2bRmBnKYDSf%%tXlc&OZVN-uatgg zw7wHQ*Ec8Tsm|f`<9*FT_jA#St<)U*Y&1>Ye0i~YGgN=|;LwkKRQdOVZ!KRiTnB=A zM$cDA!ye^13yqdzYiFJC8`<+?(KTzdUOq?og#YT*o2^N@P0xE)XIM12t8Lrp-Lf|R z5iag3bT{0rwR2>6?p{0Y6P&TOo*YwpW!(F|N_ig#44STeLE3_q=Sdeo+2K;XFPZe_j0d`T>u(U}b()|F081=LhP?!uZz)e%%o9gZLM4lZ2&AEWt?wSksTqUEFJiT%Z%a>ne@ z5S-g9_eSmfGTaV})?+50G+#HZ{Oy(BDLm{MFIM&krOg|DQ-%Bd(RWGt-oeGHw`vu~P@V4t`>F7~F&gna(~jtW zY51&Dt7p$c%jf1wH&fkrYlAQTJJGpl{d7xxFmHYG^X4i4`we@(xw)a=_|=bV4Aw8W zSN($M`cv&}9^Nz8hu2oty7AZY`06iEz59y~5AV;1+myzswtf~3zJvV7@H(TuvYt2} zFur+c?f6&EPgU;C@}CL5^@;s#+u{$R+ubt1s;#-}Q?wi!u3wFo!)j;B+PS56?+C}+ zD!)*$uBa{V;jQ8Iqte|UYK(TSpRTRF7Yd)NkI(-??`7$}$^2ODTT7O&56=zmxXMf!-KI8@?9JmE^JY3TFw&ibztXmXV&_anKJxNYHZgJCVSRa z#=ZVvPy8rcU+F)dioXONiyeI9$~afRKXRM!_*${E{zsJVoYu(7yN`>^kDv1mB7PO@ zZ)}$DRu>P*X2+i9%)=JJrhC}F`e<1>8t_^Cre=u-X9CUV`NIP*hBsoXUN(9*s-3M1 z|E%4)!-ef^vtW$qc}%qN$-;}VdEFKs+Xl<|f<5X3wrso}j70;Q27*uwFH&ySi!%XF0=JomC?|DBcjeKdVN z8n)_r;&=Bobl5qYYze9~{I-Yda({^gXAc&zyD`h<_ldX;g% z?(f2X-|Djq`OcH?$gzFhT)pd}>+yl!|H?Xd_fGk~#kWo6+1tJzeBaV@wuoN}o82D^ zTa|BI2603K+}|p|30&vvoKJGk$ET&kfvx$<{$E`gHadMV*X)z}!~MCx47NVmuIwd8 zG-r#o&!^_Vo);>QKeO{naCNrH9j5vWU29fmmEINm;A+8QtJb&r!&#vJD}T_A%oo;s z=gbdU9u78}7+ctC1?SIKFYSkoI~y1N5s$})byM?u=s@&0u54CpTKGctFt)9&i^){% zWNcTPRgW#qd3v_DwW1Yo$ksJ>%p|_>yy#+EBeT69?jN=;vBR;Sv9+O-WrP@k7Pe%=lXkN&sN5M#umnw#-D$p zZLNO#UH|dD_*HzTvjg~4wCo<8?pi!L{4TG}`}^;1s_Pn8cM5%28TSDlR6l&&81F6g zZo9kbhs}{+^?YPy?@;3_}nU`!!Mz#)Dupoy4ZW`ZS#EWAq#o@5`y4eUpBRo|`VhK1dhAr_BCKcS3Jr-?gXmJE6Cr zH=*006S=u^^cD72J|}yYZZD^Ew|}}XXKd|18=XfM@6|i@O8Eb?{9SuqvAO0B@RutW z*ok*w%o9#`Hl8CJ-}wr3pU&3oU74GjOUsqlZ?eOi6Uzka!h&<`?!o*<`F!Tru8uP} z?!wr(HfF30A9Z))-B>zb@vlY`S$>A-+N1hUl;_@yA>nvU|0_1x2GQWXbblsYCR)B& z`RC_o%`AQ^9QbrQSNmjR!p6py=Dxat%J3!MyTHfXS>LHD&wqUS(%4U?uMu{>=lg$j zu-UTM$4WmW80;?R_dI$3w<@sbZBYHg`u}llUmrdbMm1*P z_Kn7cU;DhPw?jE zy!lP9L~mq%(+}Y}yl3xM#(PHZIq1i?cR0~6d8haT(L0eJ)(9^713!;}!Q*?My<6U6 za>-iZb7HXRG+(a_-#)Y~S{?dgy1BhdTcY@d(&*>tf$93_ltf3FR?W8d$fTXZg#9m_j=Z*8FENTe z_KVHK|8wC$p0(!B7Obs{UkLVRif6AM$hEJ9%lp;+OW~Kzo9)BFx;iLxzg4;A!+o{d z_)g)f!ZPB@{-*fK+B+&Z2L+S;c=SN~N%L{q`s26FJNA)dX7Bwh4J~*lyd}O0FE&N* zBOVf;hTp>*;?wYJR~6$K@lAM4d=?%PzvLV?9&YdqHXaRsiuc2dE;OXMSG{GchZn_v z&fkAL*_)NwsQk(Nj=l%|&C>8`&Wz(j@tJti)r$8j9#;EH7uM_%HRZ3hwe; zw=(qkEBCxk^YPXCh(7d~){%*4Yl_G8JWXSKadVxGV2kDsUH>7)<4fDGcm9pW-rXk8 z_55n-kCf)jlRHh^O>%N=9a|gcmVZd)mM;DJ;jRC{p<^B2++|O52g%&Qa<=K?=A?T} zt}J}3dA?%dy#Lbj*|{3CaAo($CoexxztskPD_yIzr|!o3Pj!DD%$rBIuat+krXO9p z{{3wD;Dzxdc;yZIzjg3#uil5@LobWR9^ZK3y&o?9%Z=qU)%{-m=Dh5p^%))R6w&Cr zg81bNE4y&G?$H=OUAw22c4qCKSwF5-KmMe;3-x?g^p9;^d{>Zv4_} z@I5#!SR=#zqvqGR@-Gf9Kc0`n|DwYArM)|%J}-VLxXXppD??k?nxh+5*Zzwye>ohT zn;lyjywl*lf_Nfw@-E?lFS93D)A2iO5%w!~4EqLp17#R(0L~JwoBYT2%o?ZbTgpXqLp^v~Pu`gNM?IritXY?QVDE1^e2R0^p5&97O z3BPT7k8=#q^)Ad>I=;;Q@j=hy>OXwf=C$W+1U`=4$sS_QVSlnG%~Y^2@mH|NY}*`O zJY3N7PUE$C;rM8wqdL1l_eE~st$uwl*bgv7`Q72hj!WR{XdZz#|~-Cp_e(X@In2X#e!P<}tAJ*PH|_cYbxD^E8> z2W5VkCun>nc*aD3(sLaY+(*k_GdlGl-G#MXKhIxTv`jwNr1ix*W_`IQJgh6$ts8>7 z$?!>Q**{CSp0Nj6vu>%ctbf+7&4X|KvEDelKYjDr8T+@xflcV?^8e6ykhcyixN~r` z=&;W1UmK@J%L|qLV)d<|)-G$5HS+lS?eK6|K78rbzg_>#T-mv6Z`J0jHS@d0W242| zWo_c4Xw4eDW6&C8y|8B88fIcJ(;Gy+$=mNFSbVUv#^F)+ZG8|vb^=` z!@_E{F>zh0Ppws(1($wd-f(qh)_Hwv)~lskGwB2-uCdWErur|{u65Mf$`A6;;QzAv z{0Cns9WDQ;9(^(0F}*O|G9By$%frc2)OFUN@}tV7MEDpCVhWZ$IKK+^^yL!gvwyHCwNHFzDv!@7QF~ zVo$d3^7H#;^?%d<=c~gWyGb!StUZ1I;$gLI|Mu+}?*u!nZ@00@{;u-uuI!*}ukN(i zKfKAqmsO4(_79DvcaBXLfAL}Eyq{}Smkx|?qI)dp-}p|l!P?uGicU6G-+XlE#R>gq z``r!P^6Bx$M$3i8dlWus+*WFi9Nd5W`L3-ube@lf)6P9#U;IMlx179vs(w2?oPJ#V zmCF4vTHP10YyFL{KD{#k2lvX;yIx<}`@-|SaB?pa`@^W}?q9jDm48m{;-N1u?T+Gm zgR@U%->c5f!9J<9LB1XTyk&U@SMRxS`dYBqK+y8rXvAC7PdmrxUY2KuHz$K{zohea zj?#WdXMIjE2CDBYrEgT-R+&u(nwzDY_xSf2dUl4=oiSvWzs^2M&Y?$kHWGjTa&6<= zKO8+t_wAbqw}rEB=#p*NE}Xf1vA*3ooc0K}yK3|KaM-cAb!O@G?aqTbU-|z5lXsNI diff --git a/core/src/index/unittest/siftsmall_base.fvecs b/core/src/index/unittest/siftsmall_base.fvecs deleted file mode 100644 index e3b90ae1ee8baaf4b18d82a314ba8e01e7582654..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5160000 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�ym{WC6l2y7|B616WBl3Lb($2o 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%0AOYxe?>cq3dwhu;>mI>xfY3LgOpIr;4~%;#pHLU$ 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

Fpf$(RWUDfcdTQe`{-cmiFk$ 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%0AF(-U%Bqvtj!wgPg0#82dPpy2Po40OP~ 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%0DYx|D2(1;BKpdv`)AlR8zO+D&_Wx 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 &7r}AgVeN5}^(5<)!&gFer1LIl=#;(;r(|inX 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

CkR#T; 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≦)U?_wIrn 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%0DK~^Ou`rCqX;UUa6ro_#f!N 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

e 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

    Uyt+LHOxSlV;V&N*Pz%|@b+k~4h;Ava&H2= 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 z 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
      AT& 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

      %R10PpWMRSJZ_5M^S=2RZk+SD-kvyd55H>& z>>1kcPAzs3eR5>`WBA^;#dm2ECugkvJI9yshoFdarzoTG`Kj+^B4-cmfQC3@>-a_H z|9xkir7|8IhaKwGiqocQ-7#=W;wBd6S!rLH)n{$ zr`T`lc>yQynJI74LEd>{U!KWXe=+vpe)M3TYYe%W^L_1EmFGatzC;k?x;=+c(xALc zKhJ(^mtH0N7(#~pFNpaMNN4$Q@Wx-PxU+n?9@vZf<=vfCxyA+jUHmKj+M*GN=j41X za39`)YoOF$lk=Q$#K;?a1$b`uSmLbJ6XSicPoIw#US9)F87FO|i~PBWZBM>yt=nM- zoMK(iaX0hBnsU81#%I%V`>TVt-FWcH73TjESwv3_)oH~`3Ape!P0#0KrIP>58PkhqvhRDVvIk8dkW_o z*Vyk#ysNo@b3OGOwcQXqpcG&n`LiJ8DlyOde;f4FbnR0yM!);p#QSVwJb)PE(xk6C zTK)+&?7v9F`0wg^FJFWI1{WUZ;psJqF3M)VBcv+QFUHSWFNVgCxI>oD4!j#^u=1T9#D2ETjSqHF0GJKu(w zvgg*fMj6-f_u=Gy_cr9#5{$pY@0>;S*^6tw2yTK}61??(4;DG^2kce0>mH2t9{vN& zt2wMEXB|=J3>@qBY~_4@r{d?aobMi`i$Q(xW5%3^AkO3Ql^RpNhx6_o+N0$jU zaPFtN%kP1*2DSB^cNfe+0xRNM{DUy|Ztlt9yNqowYsQK-3Id;88v`}|$g##a#|y>@ ze#k*PX9qUG{3Y-?);9-1%nI%N9rgn49QOEuWbQ4T@7gBDxjMAs{C|SG04{+Ia6L`P zj?%?AhxyLq`>_`LKE@~Kd%v@s&k zYhs-9EBx}q7=I(O^f7+M1pJ4R2G^{bS4WC*uZMHVeU!DSjWSZL0=q@m;yQ-8 z@yi9MaiPvZ1H(^`fC3`@}q~p4(~b3Cs4}) z{J-H2(GA-7P807Ue9!&@bnxC6WAznj@qfYCJMuMpQE|o`+b9k#S z#`we>eYW*@H~qY`_ZIyW)cxsC#9QCU^@_3%6E%R@%cDe*;2YNf+as zK8G9UW?V~R?45c6u7jV09$zi9s%K-*TSJhuBrf0^?5X@8d~d%8H?L}ym<2xh9$ogU zaB9!6i_uf+jHB`M`Xl%jK60HWjEmgF_I{ke{T^+;YbdhDU-JC@$X=#$(fAm>k@Ywl z-+#_g&cQX*HJtA_=da*q*xunHe`jor&)^Q|qcEd3=a2dC5$(ZGWee}UdjL{Q*?0e+ z5=-JzHLsB^@%HY2XL29iGp^+|z9V36Cnmh(CH(imHMJAoT6f{Be=X~t7~=xC7w%OH z{Qf)#O~spcPao38px*njLtYO$Ik1mCmEqaI@3UpST3q{a&hh?u{+rnQ44fa%g?tcq+f)*>#*Kyb<@N4ip@M}>?kLTh`dXf9V)#3-89T*LnS}Ybx$ZBGx$;Q(iyw z_h@5|of}=`Q{vx%z_)@kww$ps7vL87cfd>R;jWfsw7vB)^5*&+Ohx~=pLwlSKu6u5 zz_)1UndZB;F2?*lY6tdt;<}v2*r~X`8*=5(%U{EvIFFd-=idz*IR9G_#x}$lcl*w} zdQY*3|CR*)B7x8IZeq+^np=ryf6)R%sbCb>{^DJ=IFOi?$UYk zTlf^?I;XsT^VZavS!W`0;uhR+_iH}JeOr)gZ!P2Zgt7Pj^ZXw8f9Y{$zggV(vpvQ3 zJnJ>MC;!Cf`Smfbq2!)vxP9P%@4=Y!B=GMN{8GHYVSfdteIG@j_`{%feF{0S!cn6JXvTglZ zcHr#6xUYfxFasOV#u(Y5{m$5<<+h*$_W2yYv2p=gU@c+)*U>9~t(K0qR(6v}&3-&-g z{}2=7XNLPIH{~DWy9Jik1&R1t?D-_;c?O?j&(O8p$LATX@mr^sKB$RL?fD!`_$}jr z>wtI1`5YHX*cb1N&sBqVKLcMa&NSWOy|-V1p}*?+ zu_vG9F2=L*UTW83jxlSXUBq5ai5C$gr(KKdJHqF>vNe+GQ_bj0Yd<$L_r>EZr{cE4&lrp7~j!C+4wxJG0_`4L2lipk=jcb^E-=S;z!q(fr)Xu44#s7E>vb{qU`_%{unUaU zeoegZSbZ(ADaYI2pYVGgCvxB=w(p|h-Z54?e~_c380SBRTY+OZ>*!x&`{!t-H}#yc z`)gn7eaF~AyyGqQ0yLn-XU!vU3vL4(TfayUWBvwQgnyjB6aIwnKWls~{8W1U?&S&E zer|&%-bMA;b2+dD0qQj@tFbW)P$ZGNX6*b|sq1~Y0%}Q_KR|E5DZJ-d<}iN-niy++ z34iD~OKWV51Lt2>oH@bxBXYcB_8~g`ImgUD!ny8_SkG=Be8t%Ft?N10r}*~J_t5@X zhP8{V@Ow5*jGh*q@D-7__aJ7+xJq1ait&s*d;2VBJF^Cy{Gf^bni#)}7`K5R^6}g6 z5JzZF{6F&@Xt7Joz%woq*f;OI%aiYOO=AMP!)N{l;(iMLFXnZh^aY%v{tV8(OV1rX zG4w^OVvgfBMtgz1OP=)tpIBojd0;Qzi6h{=-`yGCL4D@iDo*~8IHmL!!65teE6zUy z3(x}do>8|*7jTSSdyjn$+q>ad&f4a@#qW8Y0O#$2oRYBb;{O)(F}Tit7^BY^*L8!K zwu(vE*7vz_UyP}x@b=}L#!jV=aedo-2mViDt`q-bBJb~NEqUij%RE=$-M6W@o)qKW zm_H}RaoG#w{~zk>FA`C#@z1K|hjmu|2HQB_ohx!Tpq~GL@d~ap2Mm4COXRURFX0>X zKDx{Plw)BAc>(AA`fKss_~1u4xKAzju#NZkreW+I@XjrbWepdw zUjd(|7EHyp`Ca3KJ*xv_eDAj9&xkVD<9APfROI4Aw!A0f9L{LpN#3FPkJN6->ww>h z-ShVZ{~&13)X(0jc!tA1QNJbbJz?x~co&p=(Bmt2&@*}t7w8jk3Tp8=Hb(v!+gc^p zxHiV;-Dgrx)Ur#SzAu0=o?DPpyOQ z)djqtO@4o=<#~P&xF%~jM_J#N*zz2;K~}{D;|ttj;TqL}E;7X9GyfWJovxvXygBao zs`3|J?e{8U=I}?L^i^VB!~4%lmT=SgjkyKyXP9?t1}=bF4j5ko!@61j2SVKg&_(_X zp9I*CoO|p1_4*9$;pCmSC8p+wT>M-74M@cOm;{_PeoV~$Zo*rCL){k6XQM&a;+}qr zU%vbq(JA9KSmZq86TD*FFX489d%91|5^c_-Ja(^o;)+=3FFD4zp0U0n5&7@E!kyqZ z#=CCs11@&em?>HXS;e-4bDF|Ngae1CljyaziVksCnn zK7QBQh$1o0XKm3jR-AjzcnQ?SKeFTerO)Ckx+h+-hnLK`i!N(X7qRZQ@P77bPbKh~ z>sd_a-{R}z!#%J@DxU?!G$O`5_?US*#))wUT-!(ZyeIkt-_V~L$az1WfJW33(;b-!T>~W9MnG1N}dY#cOZ@)G}l2{@p{DxQ21rzo3S^c|EpujayYQ zml=ECHu{M1Zg|GlSm*dTx`zv3%#yM9#9AHW1{h-><`&NH$cnXuG5h$;JHb|*BgJ42 zKU!>i@;vQxj$O+F-$(eI~tk-U;*Gf?Dk1hs=G8 zcD@H_=TeT)JB)j@_4NA;C~XXCxU-S%XK=+YYcJM6L0>`JcjQ?4278ImJMH(d74{x* zE_3CYjGox?@1A$~=EU!Tfk)NicM13LIoOyV?^tWQu2bN?c;3A{Xw{699B{lwyZ^(U zqZi;1SYttKZ%!6__kJ7$&-FSea~aabcn4nLcOKW>g1Tc5ha|TIL6FOLPOjL9f9naNmqGPwp7I$O3zj zdF$_hp+0&E!q^yf!uC$Me;e>;V6VJ(X9^gC5c@27%W31(O@=wUS1x|tQtVG@*XnCLA zme}50Z0ot-3+!Y4#5EwW=Vgp`PTzB%gE>fyU6U}*XUqC8(R+-`?+4{QTfliP zuYvp1G4A0$!oC4o?8{ki;Jj1z`4Bi)f>+dz-AC`yThQVQpg8{)ehKWai@`l;bG`-* z{uckxBSY)Ce^0>zJ3s;}(1PpujSq7`hwpQI4}d+GV=du+dcWK^*Rw%4=q=hkcaK`@ z5PK%@o>^1^Io)IM}OTX zsU10UavHeaIL5WO7v6o>djeLVmV0pS(Hj3Fw6XOZ_N&jn+>B^-l18!PtHB9OOTd$DZYJLf2w_@9$fXjIHDv zW6##Tzm+-b9D^QMe>Tb4;Meyl_#23LSJ3#fpB;Yd8h;i4ZEy-|@l1|D2ZnvD$r$gt zeFXMY%(Z5j4{Hc9{>Nkqw?obW+I4;)F}?#6oOQk-)_+H{mNoth_#cB`fL{T75DU2S z`%|6cye;q;jPb&UNU!&z^IF0vut{hb5vrfcvXm^(uoZ-0(^>NFsM z7JPeW>;dnYSjQeB^7Ee=$NI04W6UV27(;zyoJZasihNUY;2VLIwe`gLci3+bvoZ$^ zT#WCj|IU8bvJdVt?%*0=KP@^y!ZuefW#Ad)>Yec!YT@U`RkCL6{0%Xsr$Sp(?k${r z7b84l&u|Z%fEK^c#_;aI{y^~g{0{SAPeuP;=A+_U>^XkV^a|+ZfPK0a=e~vfFZf8O;7-N4mipQo9h zqkl5lyPo(HV$AdCUjlpJ2~dXK(Q|Tr*Lly1Z_qK018u(d#64;lw?MyI z?pVNW(LPg(HH7!k{ucO4AAO9z4z0*1V?Z0@zTG0e^kZI=;XEBNgP`a5<=6Nd#wEvI z{2u5TTfddda2=oF{~lZh=Jaq&{A!<%FTgzKxrUT^@9dFda#m<#_t3A<_SQNFob&pO zsXf0H`Vwd|ug^7?+8b;U@CCzK@}CuJ>!0626L%)~0OO5S9LxLcIQMfkXhL=rYaD_${~3{Y z)z|>mNs;dxwsF6Qw_n%s1hnvtKqAKZJRAM$-`pAN$=AKx=RUmsT?0Y9GW;_j{Lk^N z(J2P^us>^jK2y%^-rWF69@y*0j6K(jz+M-?`VT=*%pT(+E#o%xFW_CT;+##4^{kPw zmCuQ>)({ut`T{NA13A%TpE`j1S-3~o`iC>Z_Rg+?&%k$ZQhPWaf_E3XbpMi4L`UXF| z^^E%j{yI1le6CZ@Sm!XdVXU}*bL{sKuwM}|(8Fi?0Wl}QJ^cq*f<_)hMvGqMnBh#Y zoo|MgGqwjlZ}Qe`8N1d5^m&IG>>}p3j03-X+3PL76=ONqwhN4n7~y)h;1vIt=ouI! z^EVm40?zjYEQvcnyBGER<|J~Q&-p_haZbO7MU3#h(_j}#Io`E7xA)Py`d7euo^u=H z^HTnQ&3tzbz2bL0#;6CuUc&nfF2Fq?-^7TyvDlvB95{!41-ivA6rao6puz7M?0_y9 zgCFqs+KBUBV=S+@SMI@cknp)SxfElL{uSdtqupEQc?nv0(Ew{&|Io>(G1^SAd&4=$ZIBOKK=l=qIjL*6Kh$moM#SQVq zG++VeJh#At80!Uo=W##(fd4S(Cj1B3J+R+7FuuXB+#~h?1imPLhXnr~<6(_q)Q4h^ zehzi;5_Uufco5_&M-?Cg2?I#nO7L-E|EdHD2NO z&RqqUf$M8>T)-#LX6;_BLC;vu^KH}nJLF}5|V z7r=GcbGZX^#zEXvdVFiRU7(#nEuO<&IQ^mp)}9Tu-|e|^0q6X#-}QAlN5Azy0S)$4 z33?!BynD3;U&D)9=J>@7c+Z^Q z@d9+rm$Fa4z5x1s9`!xUT6-UaIrQTF)OQsu$@w+fJ!{N|FS26n9ZTql;vHiS@jZrn z272tNxF=;Ef6q9X#8}(4?}L;4eZb!BsqVqLj*XweT>=N#ThPVG$@kc&_!_ixyZ`e% z9(cz0KnvdheL`FD{Qm|z;9TaW7^vY#VE+Z~JFo{_e}k`xwU*`p>j>*~#3ayR+mqu9 z=r!2r1Fq{8Fvod&Vp?;uPUr`BM&4((XWSHy@hWp|_SwfFXtMtRt*?Psx?moB-pK@Z z)c9PNJrBaT!H%5gSs7Er_3q*8f%okb&{n*0J>y&WU6=VSn1O_Ujb4C5puhY*BJYf` z4Ke4*65f4$2)tKQiNHhtV>tJ$7JKd@e}`~h>{{gg&QQMV)LgNxDKA{}d9p+85)-ba zl8Es;>LI!A!Bot91ngxhE&eaSYhWKI6a{Veg9 zf9I4cgG7uyP5U_@wydQkrk2Fm=gIk>qg_ue&SSk6-2>;JihRU4$J%)8S}u^|I!=J~ z-V?|Cd3{1(B+k9B<=>3m2lvak0ad^udFJ+k6YZY5-Xam>XUrbHkKq!i#plPGuIm}_ zS*c~7dGox#uA>&$=K96`99z$C>?t|Jv&TI4TZ?0Pd-yN%XA?ff*wm9E$a7h-uJtpp2Iaczn{12x#NlWEXcKy@4$p_$VqSwaTko8$OhhdZo_>8 z>iJ!>&xE}@$FNUTzWp2n-+|`!0cq?xVyyYb*Ag-IzQF&vbwH5>cYuEeI{oypSLHir z5L3Pr?pIu?VeV(-{s%ZmEpqz(TniC3JKEl9W4;8wQ70E z*qoNQ6YzS%@8Elm?;iL$@GjNovw`>7@!ngf2etGuzALS757w*2-o6L!soZdvs(>!W zd*2Wv=Q^FQNDpt`0(iaw>i&#N#B^Zb&{MHS3!0qYXip^K-5v0rPQ^LPdq8g5gZ;fQ z53P*xqqer(YtUi0Krv=U{2aLd?v?u;G0?-04!Z}3InEq?Lkzlzb^Kk=y-N7yd*E2k z+RuUg{RzFue%~oW&x}{Vn1oK`&e2~ncJ79;2yEx^o@|VxpCXa@PvAUD<4%Bcl>KOl z(f2E0-4^XLRrnd>6UKXB3EnUs-Wk|Ri#Fdg>ab_oKeF@};~l*L{}>p%u3{c!-*fa% zwl@`T%@Z)sFyD8h`>1sJ&j$kE8rXNlxOeKZj$^n3(Bo@CL%wV6(Eq~XTUu5i_xn6C*Lv-@wIp-YPcx~5vGsl*Gt~0j&A>0aR>yuahf4bfn)UM;|``$bV zKEbE&<-8yMz=exbs8AtzPoctviiJ?2!o?|6xNva_6)seq!i5VLgAn2pL^uQ!L@*B< zA&dwnTpmmi!Gw)5;Sx-^1QSFsVPlNB1QRYH1Q9~`e!tl>S97=W4BgfzZ%qFDgnNX~b6^hF)wkAbPU{?rxeaeEo`*eCy@PT9F^Id4K%u>=-NB+uR-8UqBx-d-k64dVg1} zq0b~Ks}$2`hx_QjG0;zm@CTnoH@M5d@c>*qiax|w-e>GwO}H!aH{ph00@lpkYn7uC zf5CY83j4W{EBtTZTRFz=d6@4p`eR_OoEx15y#kktpT7CNGw%Zr;90qLD;ws%UzWhV zdCon)N{Z>szQiWigTOWb`{z5wduJVf7Fp8*q@v%)b_YCVz6BpJ9*k{diTwaYs`Vr_0*P%P(uo?FZxc3432;Bdh%=L*pPfVsgWgGSs(;nROlDX&L-8a^n zd-!MQ1-1eGHu@MnU>||?1$u#YZ;ycE4x9E1%wd@8K%F}%K?fCMMZ_aS%#7~@*WSoASke|uyd6|ng{53qj$jP2o9 z#>Es<*Rkd*`FVhUhX1V+qu&WW^;zlRZ(|E=r{IwBuH+cujB}kp{}CAP8uc0M@Ogmk zKCm|Lvs}d%-*>RpdOf$i-){1$zZ*WmerR6u^Yue%5-xd;vQA0`2;5V0UlkcnT7**Pj5nLEi#<_giobeBWLGQ8=G7*YGUzUdc1S zSYlGMP5}Yb^5*pWgJ)yULcYk-wPNare!||mSJ%pWhPQ8(+h9K~{0Qe= zb)BAZkCs0G1M>lGoa?R`3(rMu%|BpI_ywpo{d;g97&jKa!{5A@uz8OTMVX(X?aKl^ z;J-s#(?|G>Wi0=QaRTNr_W`IP-xGTin>8dkd_!Q*f?hL!2W}x>{%-kzeZc;iHgpwz z55W=~1HaFH4)l$f_Lk5+=&=6{_^fo!TfzS)*uAUfv{n%@;XUkq8CUzjTz_*u0sjL6 zydd5)R?KA&wrJlw-+(*7cgSVnUaWmp^fB6J=`pw}56s~j_WMj?s>{5!2G6~fk-0rQ z0f})>Ox_n-yEzl+;p&}ntp%`#FTfIX@@UU?pIF!1@Iy?jYb>#OhUU2rE`pE;>H+=} zY{n(VBiMiw(8HUfE4uz(V2$=xze8Z{J9PCo#ytMF7A_TzEU(BG&b{USzQRuR*@pnF zd`wKjmZH?h_!;;R`vc&;T7#d1x4^cEKPqEubZm`cE&TvANW?C;>r zXU(o*o$jHEH4gX&7=gb3!8R9r?ECH;(AKZth%I6sG0hK%Gv}kSrhfShi@S9V#b(}z z+6&k96Ld-2`i zGb5Lw;AJJPl`2#rjq`Zlif1T97S8UBYaKSiqE7lUhdf4s5D9>Dbg=ZMRwe3TdgrEKk zps#*D-y`-O95TPf`~>|7+-7`?R#MPQ=10u^KKWnl!#~?|@3p2LpDny|wJJWc_O3u&N{=Gn(^Bu-1CZAJm<{p^8hh5zZ9+pKEXk zR@jsjQ|vWtkH8XK0V8(D=5%}}j9p^^Tz^~gIM=s>x1T=}>$+7gGS}aJ+H0TD%ix&# z1@s1Ou0*`~-zoo|(beKJY6sS)pJG3(`D>uv^>eQx)6>1f=04oFHM|9$Y6Gsh03Eis z!ARVW`HHb?tiTcDwLWml31fYYwa$MtegNFFG5T7UaDNNt&(J>ep2-H-18a!LDf4^d zTkQJYMgJC}qTCZpxYS04#xdMtro3w}Adp;?&mW zds@E6_1If6rsn-AeqnATVp@B}+}iA8UKhJD_TT+!@4yb95iSebIBOgjuhB2cXMs8T zf-Sm(H=j@vwgoVkYh<6yOwaNg*scRP^A7k`IW!+~0q$qOx?L+pHs>Gp`F|hY@2&%! zd=D1P-vmL-4%j!pZzVX_vY*<%0ZYOB1+b(Ly?{&TP84~>_!@Tav~_KnkKi`r1^Sn0Yw^w)YYp!2eQ+Px z`!7I>$p_-q^Zc z&%D}4#*g7P_=#YirD*pRz;&O19=mz;8?oD)TK5IU=CB7F;5+ezx&GFhg}4OlZC6n~ z3*;Bo=42-3@qP3z=we1XCH5g)0@g7XX&j{St#aUTc$CpiEAP|Tm}Z&MI6 z5`P75D1J-kRa{$%nBKeAIWlf#!2d_=X=cwp*j(HEoxEt|0d{}avwu5~>ulD)52ski z7!&V$tqjy>U|oqk2k2a1=}FF2{@L*e*WpKvhmKFdR# z=UDIZnVmt3>HE+cpI|(fOT-<)`TSl5bFroi^*sQeV&=iTFYxYohG!Dfowly$*f+EM z{#k-C-aF4>EY z;?=X$f5dKW?|@^_iu=Bct*+DI+e#vD<@y*p7kyvdh0%4fmZ+(Arx8%xZ{yh_b!KG*bfdc#0Z^I9hTfZcHaly1#^1;AAo!wXlqyl*VL!h@LloA zb&w;0u3(AZ3*eczVjlB`&=>igKHwP6J;*;|taz^0Vt=)Zge}F(JNqRz^XW5S51^=* z*jhOx_8V-~N6wt?X^81=`Po^YxdnFlQ?$>d^Hvfu9WXxkA3bV2laxn(x3+g#VtVgB zV;+p{>uq>*?9iWrD8BNCj)C|56g&jUIDuH}5L5r`!AHP5R7KuBT>w?=?-mS!1#MpI z*@4i8sI6%YcN2_^eYcu>11>SP)(zc4)j3(0ef5GeiflM%Oh)(Pmr%*RZ_@t^(`P zSFxYJ!uBPwFUEVmZO^yZ#=sd2<uXFa$JnY^r_Yr>t)K54^Xv*&{nhs88*l;G+df)$THE!X$nA;u47FREe!hcR>0_$p zQQ_~+cBlEt$htuzby#Ed7`X8X*)~^x!nu4^zJV#(Iay#tu?qx2Q zuzw8f?W>X|Q@qO98^5>axIO&maQ^?BxQAg6?_T7+gYILWBw`=IeYA&PWBV=eTwly; zKVoj~2XIT^bKHvO;+?SX`iY3vV;bvcZ??1ivhMpD+%xoI#;2gya94nPF#kMHA70-NQ-dS#AS{Sp$Ct+9Q%-x}dWAl%*JlaS2!2acp<~>iC@&UH{;8A%Ge8$*k z(7WD8cF8}?ld$~&@B8IP5TJ_u4&LX~vvR*{=A9^#qSS)U`G{#n-n{m6WV|pIUB$DI zPv9yr|FZZD_+7@PHf{rY;QL4)5pW4qaXtcTQTmwj-@H-2bD)n)+)B;-OE5B4#_;_) z{VwxY?7s6;;j#l`@4R=|x`j0-;*>sKJ~JcZOYoh1smFD0W80t)!2o_*=7Il?`9tPC z+J1inE`Sbhi*3QU2Z?d*oiSPPITOa_eOi7utp8oRguRMuukd*f_}tYR<@@OJe+$pJ z6FB!7*jn+~6u$?j_?+YMU0~C0d{^`V+WX{vuu#|JcniDF!WZC>c}I-A>tDrY4Ffjq>K<65 zHPrgm!m~fd?tK~YbB!fq-#rV)X9B0_<2s^e9H5HN?i<8?3p}&D27J{$V{wR|zKOAW z?a-4>0`Qht6aPH-I=rwTfeenz8JE6qH{|)|o@GkKA@qSrfl1F_(Sa9!G8i;8Q=$O&%jT>x%F`z%N{VaDT zv3W=3KQFoJ{xcu>XWomwa!qUPL?aPXpAO-lgC(e9?$peyUF{jl-$t+Ti4to*>!|DW z%ukqqiQi-3Gt;htXE)%Z|9xQ3YG2IX$<1WI=WBeu!#*D=CI{n=nH%>FT*r8T6ClUt|3NsS^r9jbKaHr zz;Vg3#P1^Gp7?XZ{1EN|+{1mm&hXZmz?yMK%v^eGJ{Q&`pKD;~GwpMnu&v;#J(zo6 zw!l076ub>wE5QZG{bMgZHgoKN`Rq%AH<#zvF&=^Y>wx~QWz5H*t_gc419o%iy95yv z6^!p;_n*zl57?7jfi`i9-vRJWv|_y61|unAg7AgPysz088g! z1xDtc?Siqqwtv+*CUZIWE{GA>*T7e~4A;f9)+zQQ=K8O+Bi7*exCEEyG2P=OwqV?Y zD~wz5x&IOj#Q1!fua!0S+t_yK%%NQ;?>8o!cKHCVZ_QP#`6iGX@$U-PGY+7(zJy&L zd#`Bk3Idz^{S!K23sC#L!0!6?eFe;+Pvtwt##&STPN=W_T>|;JxSloN13t%R64S^# zGZ5?Ed^XM`rn9wQZ-NV8zD6R>J20Yq{5R+|=ol+QOrN*k;A8xIpcUhNM+Dk)|1aLZ zz41l7+aJoulV2f?U=O>JUpN^67sM^d+ahuH!ze{VllffOU<;9HO5A<$dgT3fG}sqYIfy z%p1V-@~(JhYsL}Ne!gQaE`SbOE3R+-2SA^^$X zr@cMPkE3yi*tghS%RcA5QbRD7^SQJSS3zFC)R%}|!;b~^vw*Z`d$(q5cmF;9Rm|=G zm%;Jh9vCBU%J<4m>Jj8p?Yd+0O_pC2m59EfTvF3gRbI}2L z^Z(;UdA5v~L-Jb8{k-Tv3N)~<&W(E)-Ad~te|^SxG35>H@;$kX@hp8`dKY{q@*Zkp z_@TkK_!)Z(9oXlRuz7#=IY#f34fekgzb?NU4#ac?dEc`jb4Ee-oALFo)f_WAKf^uo z^7H(G?d==WJzrDTs9z)UkFeX5Q}77i4qc_5Q^MBARA=R?_FcXKBm6%3YvJVQ^$q0p z@A~-uxrd+Q!)E_S10y}wQNE5F;|>-vIl`FBwY74;f4()}kNI5VBC$TN z!MGLoyT+#7&-^Q(l`*Eg8+gY7TJheyZV&HSEYPjEuAg^m@7?nm|30={Gxm=e=UnkJ zgZtCAh3kMm`HYIovhe2LC!WtexVM1)i#YE!z(2%(g1(9V7PR@@muo!5o@+zT#oXRG zYkgJHtEOVH7gzS=s{!`1KBKO`Ph8t~B9sSIc?|BWz*LTeyfLi_&bSs+@ zcZ}Uyf|xeH;{ls;7oSU@72~bJIG^pS4PW=x!yf_Hc!936U~WzB&pYTIssv-7-M+jB z&g{WR%;YKlx$c+|OnYqp{mIwK$U3({74x(`ur@`T^^cLK=YJG?-XG(+xc)Krh&eO$ zO}<^TdTH~X55#?l)^A@9{U4EE-gnVl{#jx_L+jUy`Me_=kQjHOkv^v1d40YggV)Kf ztp9M2Pmsg9tnc1y_&2c+`B;!?&Z-|q(=Og3Itk!{;e-|J9-eo+OgwOlf+kICb_kX!?-mebd zbHY4n2j+K=b1|>Im-jiHOT_dns`u4U{SS%NXP+2nFZM|vhh7;mxv0G2?mm0)hZ5$$*=SYX?NTQ{cKP8j>X8sS|#ivL|~ zk3sE$e3hQ@B{=(ZYz#VKQ^uI`nzJjP1&8?TfO}toWf}MQ+S?aEpMkmjwv3%WW9+-o zI*l`C1PRzPIrn0mpHCZf6?0mLpG$R(HMWu1Ko4lwzX;!>^-tge^CNT*Z%)_n^Vl`U z60>A{4CHJc>_3KPv0UFBsSJ?~2%) z4(=wfCyG8B#@4?B?#Vtmm#aCPM@)S>Wo*Bkw?2-y*nMu?_gv%$?C#UPotKoSeimK^ z0o>27V68tGuv_~nuvUGaGah5g?Q`%xxCjz9Wr!)}0k$m6&Fiz1j0K*1VD~-bI{F^S zVe<|r;5ycou=!p!#};fD=XmsE;NBJ>_l|S}{?;Wr#v8cWKmC4+{TNtx?q~7$tUKTu zzJqE^f_Hugd<+iZt+~VQ8s@S`!Cad;U2_L|xSs?4R>mMhO#3vZXB?o)65ASlg#QAJ zaMm#x3+##OZ=J*WJ=NR`=QBw$eMj)K(D7WNya!gqj=*|c%W>=f5StR%J*NTG-gJz= z1G$go{zgpa<8$>G?2|+;`)Pkp%6d5mW6$;p_P>J5V9C6SW9?!ME&_WIfk(lb`9bl^ zfA+w*tj?eGF{s>C?5^!|GvdF6^DKR4wY$D~-Gg@H&3TP+j}Br2tqd{s z(fMca7eVIBpAp#~#m^4^-oqHwQEbbSL*6xA&$TMA-MvLjwgKHW{F?DE;qBW#d8R!k zX26W!$3&IbhDA#Ho$z(EH>7U*mm`TWggurhL5`xYnBmo}slG zSNCk(fSo5@m+IT)q=}2Ww1bnZHk*ck(&7#Ms=Ym)EK_SU}k*ay&o9-HT2?tcO6RdP&B zc~0Q(VZRT|Jz}4WHuK1T4|br6b=5w&57!YrG1eLAO|jo8oX?;&B)ATEhaUs?&@-3I zx{MR{oA8PGSZtjd9GBeQb!+jSxF+l(XK1;bWe#%%|{qL5) zN33g93C2hA#s3-OU|eNj{+yV1fHinWx~S}Q?pLw-{67Qc7Ck;Ac+U7E?8oR}e$3dM zRm}MWbix0MPvzAAfD>!r9SAU&i`ewBPW1yY#8iX6hvc>Q#(F>Hs@M->+@F4PN!Y&w zwKnffzI)X%rhLYFCg$p9c>*7MkhvO%?y=i@_x8vb=B|_PmCpo{V(O>d0N%J!&eyvK z0$UZw*X2RSxRu2GVD@K3@*CExYX&|&SXl$w`L@*67193p8`J0E8g3!qNY8u#$G|;( zjIVdFit$xk+y1(zDtS-XS73~(zP8`SyT13d1HrtN0q*yp$DV-qtrJB&2jw!_JmSDy zjYLe}xvX8@I$L>M>~fxYEAGA89WU{*&MH@!_c1*e_T_!%`OFw^ft+*Sw?0>Xx7>h) zz2XA%Ux7mqpo6Q@Gv1Ns6fEF+bnYMaNm5orl=l$YIv1ddoN?M#+L>1|m%X=!4LU&I z@cJ6-=a~9kbUv$^K#k8>uq{4514=}x1-t9Jj?Z@$dDr|B+XlS^9kwcov1|StJOXz? zhpiR+zQulMEI0z@QU>%*xDk8^%p+Pc{ukK(2mE6XpJIyfd{W8V#=9@yW$N~B>*L(@ zUDNuWgZBB*=NRq;`=a>G>s!J-0^Xg=z;(_`%2R6_1EAe&Fc!wU?iQHGKkxbr{5io- z`3Sqb=d@4cyyG2tBF=l{{$NYM^$+mr8E?>Q=El0u-!Z-gT3Iq5@ik7rD>GhS^PFHe zmwVZPD)#w4_7qrvIs6Q$68LHx;pA7$d&fo0X^-63S{;Qb%KVq`N!arlqeoE1eD=1+ z{TkmLIFo4B$M_82w*Eurr@-7b|5+>pZT9nZQvW`od;~vAF6VO@<9w|x@437ncKw>Q zzJ}j`qdk0(x5w_jeuB^O%x8dCR@m*2{hW*6JB&4teslR7+!uS|vR_Ppf85%?-^)&2*TXaMo;s62l3k{4@%C6={cA`W9If=xbGhs{}EJi zZF}+-wh?_3y+OB9zZ2&B(-MqV`2Wn$CXk)4_Qs&82^pk+JV3_vzig3|gtRZ{U|8!0SY=-COI|$Mo!cJj>r<{{Wk{*@Jn1 zUGoX{ymtBXfsU91>?3-Eb}T=D9q@hk6}Sb~%oiZxtLS$DyU$S1_z~K*j~GY1JUi=f z?lX1v{xkvd8*C4OYi)q*rg=5XAKc4NFZ?!o%p zR~OS6%0Fc;=JF}ro8YG)?|F|;Po4q0@3BW)pb@g)}eN6qVy~y#^R__2t>$pj-`=EnY>bZHRti%0AWa{jP^B=IwTYoFYZ?%CT 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

      g0Oz=JIvn-**=B&QpGzt-;TVb&1*k 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�tKysm7^Yn|rgG z{hrYI5;f-X41eIa*1VoMKjCMV9)6d|c_zEWTAr2rd_HNU z?D>(n8uK3ZN1%#p8e^($ef#?(&`$U-7Q4nT7_Z1t|7mWSh0TzLm@P z2H<|UFKNcteOpn(JK_GcGRE{CbWJ}6NAOqCIUY{2Z|9cxS>A%Bb-+9Lz5|!Q5lH%5 ziD_UTJ)CFU<8vI?7s*kN`aoT$_uskfWm9r`_yzVAe6GdZyeH_qr|{;yNtSTl0qYs> zT3>)5FAKKy^5+#C`aLi2N{>B&1Oh15@N>ei!8Cu$e((By5Rr%Hz8Lq4dG5mB1U+^) zk7Mlq`^^3kbjB31-%$j60e9SRf$u54@4!P~jRf>p*)W#3wr4iNO}$SM2k@eU`xbl( z2F7optrx@(K{e>jOl**dF*r0GyWPqpp_JSA!lhGwzVn;lYMnh{ml73n9CSv_k5tv8U#Ki#q_Ru zhR;2#|HkBP>_36Leq!t)OYjxj|4w(RK3uNpn6W(^0{3JtTm0tv%-h3j64QI&Yxsco z?y5)p*8LaQE579{-x%+?_3iy&#ygjO`QI1ak@K36m)ADq>_h)`ay#t#``Pu|llypq zm-g|59+9TseV){5cqnfL41lIK)b=Y&MeH`o)w}TIS&g=YJYwVCHe`e$w zdoTt;VkWNl>i+aRtg)>vs*Kp~#b>~{j&T+HOpHhHoLryfcE7Ye&m*{jn3l7KbN_@j z{E8UwX(FZ-&%wLvUVaB}>{E2dxXNwD#_038xe4k#?olxAW18Rj5_W*>V{FZr=ql#A z54ZLw?iky6f-d)4o|u${-49^U8RXTZH28P^`=JI2rP zAAy8D0DY|lCkp69IZ0`^Uyu5XJPzRup#ay+8(mNuSch6HydFBq-D|}11J>a~KJN#8PjLl8>df+?N?}Z(9fX}hzx5TYW>{pB* zV_WN8?5n`<4I}Z!Bw(+u>puP)|j_?_G4)^|4qQs%? zCD4lV_tEs2y=VOfwYnXAPo8IJAFXT{`#EI|>SsddsN&p1l=?VFY|pTj)3d_42e&pq zrXB;`%7Gj)32k1RYupgiKG>)C)w`SURk63b#W&)AS?W~&2ApeI;`c7)UMh)R!ud|V z4mx<_&Pi~8;5?&u(Ap!uDn8rJDgQ&^eLkw#_va<%JM;pyvSK^}b1wnoTj^t(v-+H; z_UK;OkN17V@3ZqMxCuJ)dTYpw7R<7Un3< z;lBL_%*FHg3YfcV?cWsc@*DUrrr(X4KVXlb2V3Soz~_DLf#;|HoG^YsjJf%4pChM% z&%f^*pQ-u1tKmWIf0yqCJvAMtGT#Z>z12Se&tZcv?=@Q6z4ICSIWR8~*P-owEP3z4 z<$Ed5es=h;;M)NE3dT$0fot7>17P1mzc4ouYmW1N2KNi_6!@I|6*zAqrp6vJeo{Dp zz8>JjeQfu5Pfg4PwDXEu>ljwF7xZ}9qV zGTwrV!0`wYcna^`bzX7ISijJ3F9{g81&UmU&-lDf{3E^%+J1TvaIPugcMSu258qMY zUB?=Dj#q$Zcz}L|PT(BtO!2Ywu)awD` z>-c-da*oY)J?_cP%H^b~mS=SbxOaJv$n_kO8rWCf3w^|R9^Zi;t`kLunEJ9e{f>=S zF5r6sPCz~v{1H=K^$W%~;NAgs?u7q2eziRWS`6UI{`C9jqf);s;(oeb{a;|`Uf|4k zea@o}YOK-na-w5AAz@7KpdaAJD17#weOVFjIeE_3bk0O=xI?u5Dg(AT;yd8j-dnM*zAI?omDUX6I#ER5D^Q<}Lu~Iz zCy*8Th+O#;Q{O%IMSRH^^i6Fz^ZF9gp#uz#Gg7BK*`MaigZhO$W5qw=pMg`s=b1e# zwLifQe64(m&%0@^&&4^3X^%$o-!3t(v5Na&`_^xbrw#wGDyVZ;ZEax<_q7$r_HpcbL4<+`UCdNI<{lEp#u6xoU_U(Jk z_u*sEa`G$Uzrz0z*q^$UAviO9FNkw}?*s3}sg%DzknmYcan7NPBc`*xLEZ!4XRHIb zHCTcKzlU#&?R^gHC*TUmy~2Cu&%u3sz5|c3okJ+r@mciz%t^rd3($oT(>wJ$ct2~L z;BRZJ@OAj?!x-WFxsPcMYdv7x0r$dZ);X-d0zT7!0OoHPe~5OUl!&Q!`CDLQJOJk# zfamUf*7E+I6n~(f<9`aC0BgGsNnl?FuGw?hfRUO<=%3^ByuGX;rF6hbE4&pvL@(;j4;jGnwRQ!wOcSFzb3VGJ7 zXG7+xICl^H_b6YGlj4-hy}D=iVU6n`M*R2qzXL1yZWON-d`_*~!B4>E{QJb@UDqE= z?i#y_y-k5Bk=N@-4W`}!}h%Ml5kkfAP z4ZdsY(Mxj1>(e&>CGiJ0hx_DSZi;nRz_rf6If*mRUK>#7$o0#!mb1vMoPOZfe@CRf zDf`TMA2gufoR@GP!!zV+lRCF zKE~`Y?_1)30N%ZE?V4#_*KdtYxkKFgQ?z+)oX;@+J#Y`qyNbOjYq-m!zcX^Z5t~_q zZyytDZ4=`D|5|Bd8#%1;1IAn1btd#NV|w#}eJC@upS4|N_uP*Tt_Aa-;hjI9#g?(| z)f%*L&M$&m`E1edm7KnH^8eqMQJs7?_?O9(dxU;R$~gbqUDnBCNUHPs4q27BG56Z% zejlA;*oz*0jQ<7kb;%84OKinw#CK+Z##n$Nc^+cE3*Cnf)N$u>Tz?F6KO=YOv&|{m zGse9Ewf;VK0=d3p!KB3I=m|AmfS_gr)F;Fr66>*B;QrftgKb@(jf>!%#Ng$5ueM|~_rx4qrN#e6sqrznc`fu{-dI-lw?(cq#(pEVe@0rzwYWa#ckS;8 zIey0f1oC@kOU?}L=cHx4d*1vnIo`Rv4_k8Fk4waNfuEzfmXlG}aWjz0pA$ZXpMoAv zajbijeYEe&IT-KpkV!0teK7au_y#U2uda48jIqA=e+Rgy`=Hj^EAzML0ItLStJwEa zKME>=Z|)4#T9wid_yt^#cK!~nq+krb0{{0bGSyHdo&h#KYKjS19CjKme{Fa2T;_m$vre@AN}a&@OSiA(5u2Z zzq!`?gm?oI{u1Aq+Fq?=zv>R&xi660qXWN1TeF^(+Wqy<*AWe;{49% z{Kl5p4@&%Vw4YgR9ghxjT!;I)2OQs5o-(QYZg`FOSgzvRm;3Mp?f9`Qh`%QGw%{Xl z7i1K)@Rf62+U}#{+&Ala_wCR1Owp%8jbr=?aq}<9gNC@0&sq+w;r*^+{R3<{>+j$% z(at$9{0v)}pw0g)aDC6wDF$@t#yk+(KyB?g=zwRc?h9v*XKdWug*uH)Gy@Z0 z?y7titgz*mz%!eo8^-HS-erYb^{C_BX<2Yc?E&3k1kKt_klJ6MD z+u!ejcjA4}lc!kEd7Rfdo%sO5PDzfd9h3Teu0@b+$3|>=7qF3mK8d=cRZP5ws8Cq9yY>T}^OgqK- z{Rn(N@6GAJD{SX_gY8^Zo)NnNyzlPa3EaQXd%(OXxgGVM6SJPUjr{~n3vUnhp2*9B zUKXxI^8UYOjD1uwf1kX=GWKfWy*ut_hjxt@KvQyN=ruSdmglF2^UsL|Xz;^W`9478 zOk%iy*Q{6Mz%gZ98)YBSSFwQk6f#1eiUp`lI>k_wCgFPv*mSR1hU3;H_DQNNC|0<4s zKx_h>`xA7oi|rVB=jy=|pnV7U-pKP(C}Ow|kBK$dW3jH|tZlut!JBVQ``U-sHdZCD zjd@3`k#k+|hIHZIFSHxc!IkqaJ_IM!*pz_(2b`F|2k40zwtnqyxybq?}M|H{XV|c$!h}{D2(+vC)tjOu%7w8rpV2N#y)_e)(Ac2OMbD3j5 z3w-C$cf4q@^{>KtSKUwNs%s`Ai*~#(PkC#|3$t;=Ct}nZS!pF(&Vw z@@`!L5F606vQ!!hZm~Q#~V8n~}{48uCc{!<<}{yorHugDF2 zpF{V_ysv=gW!;EjPwdI_b-X!Ckl+Hi?hfepz+PH>#TrX&#~5$0+mJbv%f#jL7=xhb|>0`AKdpkL(^eD}p39PjUI zV{tFs?-}^8{uxo(uZVHx$S!l{9!BBXr{6svY|N9>VwmSS+z|-sY|4a~brQLmM{my4 z5i`%8H@ODx-_(}hChoa>N8YJmCtxi(wfm~Au42u;h7XR*mQ#O00eppVI^Vr-1JCAW*3amMdkyYN1j_M|vo+c`u8 zL10`_*LxxF`>aZea%N~n-go%ixnlTpB7N;OG1uyQ=CLv0_k;T&;2rxO$bz4M@w~qB zdqIb<?JGpLfs1s?ETi59!A1Ljor;eG;t0xzkxP24YQydyEZgPr^FHl6`5 z>R4(&$KHJYI^rk5`@2Hh;~i|{6QJD1e*@l;IJ=_>+x&WNr@5n?ybsh_mby6?UUBS4 zaM!_oa0QsJHZOuCu}y*?$o*<>h~ra~Jw#VA)&a$LgZE)9-U-iE>B}AP{^faw@VTG& z;4Xt>YAu2Dc0meuLib>b?cVNzT!(!3dt9?4&zx7pF9F9?`2jz{Z%Rk(5bgZg<_p`Sa*vH@SFMzT1F|2owxcqX1x97*e zdA^Y&e=Oe;Gsm&c*Tle@@9(M3zXT7lp8@x7EUu{`Zq6Zat! zMsxfHaQz>GD(0AL?J0OR`rWW{?u&O~+>`ZsVm?}8UtI{n{#+B0q6U|`PbO)*)iI6*yAnymN7kO@XZsgHZk*7#^jNR;Z8JY zd$ZTC;LSf3IPX?Z%=-2*EquUvKCa1m5^zr%;?^_oOJZLDd7n4;^Gz9Vj{6kkD|2jX zY>NGu=lN=n<+sF~<2G2qIZukS4F5dOd*#{=(VORamTA9jcDpi18^fQ0I)}Yhd5iDf zTk`>WQS#-d`1ZPkwvM@ZPwHT_cmE2oU!R@jXuN4N{D^_~z10H!1|(q33ZJ2nQ{wDi z-Yq%a9qmb}naG*qe+2B?GqsyLlPJ+#lEc7TCXedus4U zVt!9{&%Oel_kd<^$zjXwb`eirmCc%Oai+yU-M z<2b|`3A-<2`f>-12eDiW{hTqbr^*uhEiupb2rRGu+N6Z?Z;b$8+?%Z=2Nrol(TVCiGY2`yQw<89&qP z)3f#rE`cf+v2TEDU>E)|+Po?eSj4Ua*K!ZN9`WWGSGpMH`IsDSCB-nmJ%}fuAwQo7 zoVBfIT>b%AmRJivhw~1-0PdH)Uja*OWu`86#PEC9NAOjQ?coPGJ+?aMV;^EW=L&6} zwWeTA%(&nxHnZ=^2}AV8!?ie@veWH$NJ}lukiP*1yXDz6Z3nG`M*MYU&b;c9x=q_ti4Toa(Xy(#`|gR0kOMa zNv(#wv3M68vyc9uTT(?af|p zVUPRwvtS82>WBo!V&CFawByvv&#Lv0iMPbv=NR*(x^HvhQ?Os`T471-KC#+U)$-ai z_(uGUwB`GNbzEN?L!xs{KEP0hCW2_1sGW#Q~qKY?!<>wQeXejkIpM{r%q zIVArB;=aRr?4EedX|S&n_k5>t*4_m{Tqx#Fh*=}|hCde9|Ifm`R&Pij!*^NlNB#E$ zuFLaXgO^|i8uFB`*jLE!fX|}&n{tV`|3BQ0(E-MiV%VProt7OB9%WUw1#k;@v-dT2V?iq-l-=bVcSCk{0wp$u_2Dz z7B?+L)BNA`*rB&c%UF5ywuyaLnPN{yevO+`*O2)bn7jwr*6&MfV9tJeM)I-2dln10 zU7+maud%zx?)e|Ye2%Q+oeuomPs#E-v+=j+6*_@E^MK>L8#P}YjQ9Js^&X&=dGQC2 zpK)qs!)M-n`}CgWciA|F{9D8i;6Ez44Y6&Kh})<8-EQ20asCY6In5u7W9{3!EFXp6 zwl>$^6y6#M`)qC_KQC>Wzl-fT7pU$0J^c6B)*bKtgxGW7+0?!H4ZdTVam}#}RV4*! zjLo^&lWG%kt@1Ns+Sc55Hympp^84g^c0Yo#1b&d$h@TP9D$oBj{GX`N#dgwp^O`HY zk$1QJeSvp!Q(T+a*{n}Z_r~|D?>2Mv?ZH0p5Z?#hTh~7)_9eQ556}WdoxuF&urK<# z7TP+sw(qQ6{3__1&Wet=$91_BV&$7jK~5cXJk?HIJU{%e*7F1bX6%oacF) znA-P57n#aGtYbZ~#qDc>z+1x9FPhGxK|}AT}0r99O08=K=9!U=Lm8(Sh&% zzX+DtGf;c6hT~iFfTAydwd6a#awQ`xzrS1|XDqI%1MWo&rr1>`*pBh6?*PwYQ$8T( z@9f_L$3~C=T43M%)_}8?bNl<@F1B;3oqt_?&#_8kyzgG;_a2(BeNM=6jt1DH^-d+Q zh*{q|;9YHrS<5lMMZ4xI?%^@f6a0N+g4hC`(C)o+b=byxwH$E% zYw&Y)Tl_Jw|Mz*2GZyzyam{bw>~~WfHz6KDE%Ma~G~n{hrjfso z-Zs{?xyR1yenzQZ=bhvC)S1BfEIQAoSl_)l73@~fi)Sl;XH?rfd-1M(UE)*pKCvc- zaZBt2Vt0YHk{oE^1IWLCo1@)(>zxyF6!+h~Pm$fzA07UW#NE@n)>Bf0arT|yw~2Q} zeqR2J=mLJ&Lk#tj8s9v7b^kl?DQM*i8btdN@u%Pf99U;0GyFHeXMP`T-=0|&`|pW4 zpAu2(6VGcSZe7o>g*zws_MOij-g8>u=bCWN*P|2KyJ{~zct)P<>98C4C)kb)Y<;yg zTJ-gs!~9coi^#d2V`BE!qI0i>?_>Bg0=YN9{u*Kd)cSJPo(gLd@4y7xKC|H4*Szoz zI)FJB8Rxlu3*HgNc~<`Zn(zHaPQWjT9a3k%+<{u7g=<8Sybr9`xi`7foEUyrwXbb* zMEw8ZzFA|N$XnaFd{$l8SOTsGj!!~+EHTs%#+kb*#`65|+a##@AzX|8588MASiDzl z40~@K{ju1$?@2jfkDGD?{|dA*F!jEu6EV-KM=up9k#W9*^wIfg$6jx%@f=3rAHuSKqBoM-Ai z^_i(+Ujb+RDR=}|Yt<|R-?@wl6pSbUw7Fa{h{r>{kOCM)z zvPOr0f)<{wbrP`#xMsh1Ou?;!AJOJm{{q??&S%WuIW7X%1WB7jI+)MJR$l2e^ z0^>)e&LO&snmK6m<$LP5FZO1v%2i_5fIVFX4ZPCF=>3)>=YOoptAB4O6H(TWZT&0M zdI7pp&$^R>j$GF*c8T3a%TGaGEBp+5P2By@`+{HlRtGu4^(cM11tmEBr+zW6S>;k{LRmnZzKL8Jb zYyALh%A8uRLwx|wOMB`@&=Y)%o}%}Q?L3ZE=Q+&--aF@$v)4`WOuhy0gNvIy;}d8Z z>)LW%3WPD{aZmmZ68<)EJ@UT$7R7EyxgEB@hxu%}Z~1&GkKQ4cz*XS+PN>lkPhb!K zE8xBx7eQQoiP$G#0lc4%bq)6N6nJlx6vI89;d`cEmpsqbJ-ZD~usiZxOUPrJ|6ka~ zl|ITl6s_$%!Zl|h)`2N8YrEIG;62a~Z-MqBVC^2iGtM3>5#FCx^W483PBHZDnzfJ6 z2cSw%%;({s!1XkxcIM$4kidPozkmeq?}(dXA1~p&*DvIq19+d@-#&)@b^euH5p$i} z`g_Euz`eKMIaq)Mmc{Pk?C*Z-+H0)_?h19RVK3HO7X+L&|>yx z6SFVR!y02Tp3e@wDfZ;N2XL{;$Ju@O5Y9UG@Xk6YdbjXCZ}0Hbzee2qm8f}M+U(}D zih(hwCAX{VK>K^?yJLsBydMkDI?nrrJ}3Bc?q#6g;R#vab2P65uEYBk*el}lb9w5Y zt`DvO?)S2cC2}e$hB~vK;Z4ka!@p&&>%cJq60yuN-ywEg|LOe1ekA_{{Sv=v^h+Tt!RNezUzI(nxyRLmudrmQY);xswZaD6$HgRP^uJ`K+m;w7pF}!>1;RR@j z&4Ies?a28a&bhtYc~0z0*seL?TVPG|*5Etf=R{ZwvDO+p#qh4OKXZL1oX=Y78EDCI z@8l=N|CKhl4LV|1&`aR=^r<=CxiV&fZm1FHU9bd>vtA=J_ogIx$NBl|8XNrDSJrT@yCXhF2ily9_U15;{fZv0lgIuF z&d-Zo;O}75(fF2}4~Q*^S$~f0=Z=_y7Cu12_B<5(yoK$U1YFMvXk++2Bag)od~t`| z8guL$b3q4wU$_HfEqn)-*v@Hv$KC?Y9dL?k@wuFoxOH9v#oPe7&maI6aMy`V(e9bOJJ&-{*BdePVjrH_0z8)|Zcd<`>wwrru*9}jU5mWq>`|`6_l}Om_rq=A zzWMCxx8wx5bMy+o$F}~rz*@fs9o!6<`xxK8u7g{^``t1|Si>=&fZo{Xj2k3~*>ggB zk6!}EH2BA8$6f{jT5z{uVh*;SXFa**i6GwLH$sWw{6B=d1Umfl(q`9vN?(I`Eo|0q!W#V4}`*Y5K zTcSV5zX7fSd)hDg4ZODfF3^+FpB1|gJ^Wk_XgiPb|KNMy_VDdFud_Lm4~UsNmU*u4 z9IS7HGcVUJoHsaRy&Nr4<_{Qz~X|czAF!p`o_Pn{5=E)nk_lWart99mZ z-lMO;bugA0oa?#|t^#`*?~isH!-#(Tov;)MIQQF}+#~hg;NJr?;CtQPYR#5B_rrOo za2J?wid{wCH8nunGc~_T7{hn`p>cTk&bZRXP}4ng-S*~l)DTzRV0#aoBLV0C0be@{ zI%2p#&hsOjdy|M&F?S7mbK&eI;9KCF*8Cgs7TerJd=G7&?}8)H5L1@eo{M7^*xrwb zOywV!@c#^ae@x-+KR`3e(e8ouZy?4ud>Rm~Z!>2#I8_qB19cZjyqz%Rs z>xfOjs(c@q5_2Et#GLos80K@&e}eZM?0FBpDTl<4K?~o-(C0X3O58abu*9x+$eiy> zJVFnNf$z}n;ctMo1K8sl)E=EL$eYQ51^#uk`8{@m3v`cOp*!Gut?ON!0&^SqDuL~N zbT0Q<-Z|~L?*B_-OJaXVd#*EVMc#a|Piz6MVYiX#Vco=>`vUPRU;@TsUHfWcsQpA9 zyUHG%^Pixd$2HwH2XTJa_X13a>(9|MP^FC_E?<8)OvLn~=ry*_n_>;`z(@G|=oWM_ zJO>H8j`3dme(i}Rct5NC40C*o@3;U<(BQkrO|fr~H^olo0rfHd3Ut^%-YkFTjD7;H zf@ChWt>e4tYf$rCf1Zz=yVxE2C3)5~=l5VAI}s0HJ@584w0S+TrXbLcv3Knby}-Br zE?Q2%$_a6Mc@3U{S71V{iQz1IZ1?5?m|?fX)s>r!k#aePJV2wF=N8t8> z^FNZ0^BD^`@1Sd~cfgvCaX-6_e8;yS!KItS?`h{N4*l%GyO;UyU@LtLcg48^dn|Ke z-v2fFQ}jg;*v6;8HBZpa`xdS(_yRoxeW^Fab}#lFhdwG;;CJL03v>@!kigE(Chv7m z&J6q!-yY=}QQ~X#cX0L>*a7zn`y1f>u5(?(?&0j&UX1?*$#Fe%pxqPqduQ&u8TRFf_#Uwfpo71Kc8qf!gBJe{dJ3G|d9wsRu~#5r*YAW+ z$ngxfNlV_p;C&Xa zwU-8XryZ9--7~oaPJo;+=ABrhqtsE0DKX>Dl{xm_vv#fa>pRZx7A?71&_CiI!yN(Z zYmX(7(||j0pMX@p6aEJ~O0Ijp0##b__!xMP3($s5K*IN+A(6z_y*LzYDS8I{|7lsc-L#L=etvEa~$iPX~6_c!9H-$4$T8W z-ilaLZ1?Od>|cQ;_CjtX=Ff@k!rv+13EeBl$#ozB=XpquT%(VD0({12=7OGhm0QI9 z-#vwTn*>4lZ@2NpXT-ytvBdEEPaZ?g^DSfV86)p~^xr_j9?R+86aN#uYkEhhUGLl` zSJ!pyK5UVmdY(~J_GB|9*KKCe_`S1Tseec|lpo>v{HHmq5=5?QI9Nohm83)?i zq=kD4exk-T5ZKnL=N63HBkuFGDR+teVBE7~jhpgi;Vy0&Yp!E*UB*Y0^GC0k-+Q`7 zcLnleaqI`yW$ab;7P(URKzFevfzU4XoByxfP7+Nq{N9u2+LCpf+WuR&ed?&k(#CM- zjsJ$c7L3K`+RrM*ecvXY!y|Isli8LWpFN*zxy|_<3OTmU=4CwOzL)uF3&aL;a*+UPm{SXRW=AjSD{us-edl$JiDFwbk<*Yfx6Vk8@Q-c| zckLM4+*kNjlKC;*X}^y?0q)sY?8kA|*(P$EJ=Z+fFYn&C58C+|FA4QC%((mb9WZZG z$|VHZ}OE1v0dO;=j($YaJ*;cdJlk{pD}AimBch&=!c z?2a*GN%-#LA)Mmah-{wz2)OIydH;DJ>)A1xl4D(%qdjt1Z#}adX;re{vAHca^ zo^$SvG-Z#NbK09ZE9^EhHT)pvd&_6hymOox@H@o+QuwdX#FM)L@ zAnytM3AX#}S|5Ox*g4S$?&DMVm1FTm{c}PxPGtta4=$7IJ|t}CtFj>W9oPZK#FwCx zk6|xv5zpiBU5oemkHqAyBeyE~MDw3>spUIhy@WQe13i9}{2983bB>3=&mHsqerylo z3b8plk=FqC>ou{2_Iz@#T#+}#+Ze`t1#iuSZ!FZ_OLU#*(ap2k@{M5rd&IATC1|Kw zWrA&dfezrEc26S603Eh_dJTAvt??pt?k>u^iFv$}=AM(X{sU^gf%|mCJO4#}bRWam z9y{M3!u8JyXT4;D9kHPdN{ecF%?vc7D=LzwX0&6RBT~M32XSxP& zK}W2Pn~|IFtH_OI4ewn(LhpkGXky3+HAuiY+(%&_iM-k)Sy{Xj^e6MsO2qIkb4}(ih#RXi z#rG`4A3%Z&(15&e#ctrt*(cw;&iGh}UnTBybb?*^C3Yg#qn*qBuVSq_xBWLUFpdqz z_*rAkro^+xc7FTm7~^@2#d+E|i*(o`uLW)f-e>F%=z!1Fywn)yJR@Fv>#QdbbNU!Q z4{nk3E0D@~`TF+-|0aF~RB`QV5J3cO?Kh)wD}2YeZpYQ09PgY#?v(i7fO%t)cU`xM zd-l6E2Q4S;vA!>lv0b0>EHV5pZe7>(G5DbH0kMw8L*$;IIu6zqAR>->Kx<#9MhVQ_}x9vK}<33zBM3W?-Rd_J#~z0Cnl;~ zfm=8hnB!XgOsvw!P_IQhuj@+23aoudEZ1lTvF8<-WZmXqe#em8M0U^rAa($ELcVk4 zKG6v%U1XKx2POUq`9FZMv@z6h9UZV<73a5h6J?LkRo;V>xAx02PHq26U$lLh_j_;y z_^#?Ar{o`jEq@<4{vx_5V}Ff(0T{E7AXl;G8~l!VAB8J??aS{;Ra&?uXkwVl`C9BM zft`T!%2`+G@I5Eb>$r?J?)=8pO$_JgxEa_d{uu3fj>Ueb#^62y5o7ku4stHSH}&6= zk=GMD03CLV_Pf(9a}<2%yoFHUVxkz!`ijyAn>)#^BDHEp8Yg0|L%Mi%Acj~v-jf> zx~|U@?T?80on{aI1VoGmWA^wH;tBk<{Jo*RGDF{GY^}ME{{{XM?YJrSSD?XGyo27A zBeeI&`CMZa$N4Vo?*lWeD~7zevf^9#0O*6 z&IelT8G1awu^w1odz)B0^FE73B8K-y-WTf^>7vv@e?!gFbrs%m$sB5>QrA5+Z!)S= z(zCMXAK*L2Rq>v???J2wo8t3h&4=LUWKLbbr}X7LQ1c?rdaN<^nq2$Nd(K$%s`U6j z$rF?R5ZilH#b^IA2zVu7FF+OVPL>$f>6)7w8<_k~%kz`7hU?H(Ce(8NCWiT+V?P0* z59GcE6UJG?JLSD>O8y;fq>Z6Z`c0~E z1lWhQ=jaO?-hIO0tfgE%17bdAA2|z?D~wh zInbLEJgU@7`L&{kcNT8Co&F2UXm++wg6Pxu)DNzI&2N-283x z`~Gwftp71%H|0Jtdw!1YqI`ysTO|=Q=Mi|X=$=>utlbbx*a7DF*3z!hV*9T1*^|q? zR}y`z#J#8XxS3=AlK3Go$2rE*#xQ?EPvCm=SnBu(jBVk&ExfgN-~-P6^}M#pto->v z<;-uvru^0#Rj4)2v41~%j0vB+JD`fa=m%T^p8XY2#pg9k4CmjY8!?g>U% zU`C#MVvgf{XL%Rx(KERWoI^eso3N{xa}R8jx=!Cio3cj@$BOBwUd;<)Kf+nto|>qP zy3J0C;qO#WM&o@4Uj<4Z!)K^9^7`@J%ML6UF`0h-O z=OgzKdi+lDIr{j3(x){c}56-{b;A`%x)J^1X+avSrt%u(##O>9bZR733 z`R6|~|LK2|ioDj`kyy`lyZ6=^*AM(h#61JCKbl`IfyafD_bzqByf5~Wu&rm`zk|!Q ztpTiM&QhP8mRu#uj`y%H0pG{& zkK))A!&vv~8e9+7*b{T$gn8GACu1Ot7fE4$n?2*gu|4|!&0#Nmj_j`?=6L_R8Y}cZ z=)h0#6QK0?t~Et=^AG=bZh7r^YS%KB6~52n-@ugd&e6nKFW#MxfOYN^y)b7a&hI*p ziJQ0H;A?6k@7!lvCD8T1GcpRDnzgQTh_UEDqi!&#_RP6su=N8wuL-4OKhAWm_lg>M zpL5p65At2t1#-5D`ypzrb2)LIGpEA5!EQC*IU7Isym!h6=+A#v$}!Zl=!$8qJzZQJ4yckpuW$W?}`q* zWp3AB>o|W)d@SBQ<36vObLcM%-!RuUv1i9y$6hDEd+z(hJzwFsG4#-*n*!}CH;3;g z-=nAGZId^|t)Ix>D#TwiK5cv_G}r!x`*E3^m!mma#<>^vYv1dFC0*F){B{6T_Xbo^}<_$+M~R?c=||KLE~Suj-aD=J;$K!o33a^%VUd zaAHoGdyd_cAK-mBzeiN5HPrHs&EGF-0=mZEP{;S+9E{~EzT@1>jvQ_0sqzH74LsnU z*iY^ae}(NGnSy=n&(LYao9}zH0e)s%JKrfnRU(G>wa>9VHWejdQSQK3@V^Hg`Bm&A z;Tv;pVl4V!0{7D%gS@fSF$wM(*pxPgapt*i-Y3`B5O2W@Ti-qL&h*3-`*NL*`xG3& zYoC)Cey?^9_u=l3_yp%Z7;{hd(9Yo*?W5(iTk{b0l31Wc0)M?ZdN1MUH8{42dHEdi z?R5$AyPkNE*8%4=&pD6447B(?n3FG`1>cyQz3sp~1=>}<#kXb?L(LjjTi5z6+^OJu zZi!g^HzYn=_V}{IPjfLQ#H=G;leg_TsQ1?MkZ-9ymX4Tn1n~|uVkGWSV(cw+-MdQr zUOWP8_+7O3c`RS!w{Z6N1p5S7tJX;|#6BiIA^re;i0+6hZ4AG!+TTAx!mnbDDKS4A zAA-F0!dqhw8shGMPdw26?sga0hx@z$KKJ(5ljAt=nCI-c+i2%@u48=bCGtAZ;OFNY zE#DJUyN;j8`vfepqr@4JrHf&H*V<#-Lo2Yep!e~OpZ<4BiM8b0gL5Q&KbLNSz&3Y+ z?Xz=)?Hccagx$ku!TuL9_iB#afQh<@dG3Yh-4S;lp*VMx*bo`sF?+-p#I5@Wa1C@p z2C&y9u$J@8fq5@M4}Sq|uL(Ghu>)d%2iENHTQJ3bOKcZC7RLmZBIg*_@eFj>uF+aenDMRzIlq)2$&%Q0Vi&;zv@s$vt^XV(@;pQH ztE9lfmz#sf%KK5tC?9K1m?)9b|5_jAqU{6&dhPcnd zd%#|Q1nz4CXK%m3uYLE%@ts$(k6rwafOFpmKA%CnjhvFt?hdgJfoEhNRZ-rP^1lu#zwg>q?6Kl~Ou?8&h8~nET39aoK_JHdNIph%kD*hX^V;p;o z&U~0L?7@ndx$esZyhq*zpg48``p9m!&0PcY<*MX;V_eN`VzBjRIk(NJUy$GoFTt^6h|S>U*kjo*`Q{#i06n=4Xu)QFQ*x|7!FJC#MNTBRqw<|_$9Xp7BVvz? zljpl|F{%|Y)V6=`WWsNNb=9|Tj^@T!`Z&v*F(&Y<_?^mr$Ktc@y5c?HKpx+p4u49CE z%UpY@{b{@B9r`i)=g#Q`+%3>kZ&SRh*7uqC3&`soC4EcmOW?cEJl_vh`WWU_Un-vk zE&P9zcRb27-@5LD<9+{)f8T5w|A)ex+u+N)|BAdlq*BwnBJW+U@&tZ?-=iJ(5g3a~2SH^Ojae401c<0>g zCu&~iT7C}y8@3eL%T|B89N3!Czon0L@K*e0$!1q`qUk^L>nzhI56xrlSV zym{uI;J?B*zA0Ul&sB6IkhxYHOtoC>_}nJK*US9ycq5qHck_8R*)z`Y*J z6Jn0@Ic=f!OaI`PLfCq>Oj}mSYb4H8JON5B(f-Y>LrnZ1=tg_SWHhPHXa< zzs2{?>;lF2P+lW^m5AZqMfo=*J$}fe>l*OR1kN-69Jptj;u!bhN3hz$H!*xC_zZqq z{inoF_kr95s`S{_{SJ6H+_MRDB;xk+6~6bx?}g^IV2>rV1+H}I_$@D zuYJw1&94&J)~)$D9^?bhMo##gbi@LvZ{R-z_T1u&9vm{pvpoRM;a-3sZY}3n6f`4o zZDI-6klPj{w7uSfbA0B!bJ+H@16u535ah~dL9dC~!(RC;ctY+Rf14Qh&(j@aow`>w z_W@&Pz;#_izeGE)_02V|?ip*J2|Yu*e)AR2*B-Wsxy~;RvGwOgcj!Be^IjP1VpzZX z?%n?u_3dzNq72hr3kn zK=!G5gWNZa335Hl_wgHS&(8kc*FE6gnfnmr9^hK?8gPWYAl{+PU4o11lGBpYg9OZZ z2{#tw;%nmnl*6uax(|$dT6y%xz_ltZdV+6{e+53D*7+q^!0n*>AQ2>Bev1yUhb@dv zL8HyS$f@O~H_P2c>&sc+{ZaIHvE_R38cyD4qJitdsbKq^%DED@XXU;+m*|MOcMV$K zby+Kc>mbj`SU(T$0L61VwjO9>xWDd?XZ4Q2e_HPV`B%+B2l1!aEzoX8(C>iv11?}FrN6Hdy9dqm2_wkPzV&T&%HQ8?*erb$UWebsSr5+nRf*SsOXmKz#Ooa^rE=dpaQ_3;OYj$94SNad zzQXseH{c;K*E?}4G3>Yd?m9dCv0NbE`|Z9sw%y4-qQ`e{s;saRP`ALG--9al zXn-Z_Q_3?|!#A7V46* zU_AOeV4VZ79MuZ=IlgzneAjvi67k$0^{*Sp-;|Yg;FCJeSjRc$Z5fx~THqQw;B)l@ zaJ;!4T!lF^xLcqh-s7)9kDdD=tfUy~S*ybyi@9GAe-5^ZJ=k~de@ns){uN_)%J+eP zU{5z|#n4YeKZCmg=8W?`1~H|Lp~oJbfV^j%_p{u;j`&zy|4VW%f-3$Sj?FyxB4YUL z{x`Xv?LJt7(|v+}3fH11#NEpi@Rr!hJmLwA#kli%2dd~tRBx~yXRr3@_ma~&OPcfK ze|rMgkXyw(&vp&+x<>Pu(+6|I`5nm`9lrSqJ7GJ1hP?($umC;rb3*KM##rBdaonZ^ zad~TZ@Zm2ppl9^|%vDlMm>0a5ZXS-K(sNuWYJ{oWrbZ|TRXy-HkHFyH7(J^L@ z9iYW-1Y)m_=|b;J?vZyJ?88mX*^nii?+@q9?_cH|%K>BT|6jn*FMCQcti40G1uHr1 zH{`qT=G9!|S>)y%@bEApp-=oQCO3o$h1MmR64>s3)K%RH$&%pW(^{mmN$71jD zzAJMc{-!wp_wc*u+*1te>WEE%z1z=N0^fRad99H8&UY$NEcA-F{1j|T5AWHG#XEL` zn9q%KrEy&n-zRpf#M>C&5f|_qY}XKIp`;j|-_07CqxC5$K z`#XH^$yKy@qKRSt1@U7L#Oz_M4LbN`wb730(A&iOAwRy0Gh)_uFXUS4xgK>xJfXe! zYdHB+A>M!ho|~NYzeG30wn>6t!ncL*(5~YIwBP~UT$^|T_GC@_^B(&-b}H~U@LjvS z&qw{e;4Zd%E7y>xn~E{0VT*79?UVupjrw zeueq+0Vd|MZfhrSAMQQy(s;2O>`$;A=iE#D1(<@`=QZNu8SowloOR7L#~LlY9HBk4 zO=*es!20fieKk?C(Drp*4y@o>VnW+h z9f2v(KPTh`u%2~&M^tfrzJD))AZI~N1K#5A0e$yZEw8jO^e`p<5pcdl%v#Dm{&V1G zc8*65=O5PTtpz%83?7iTkIv)K=A4dWtZVkWg?%rvU8jFW;~ehyb<+FeJXW6H?}CqkwLJHLAB(<-L=MoWG-6 zxCY&+i(JJ24lIE^nV-)MyNjWQ<7c4NN9$iAuS0tu=Jnr}!*9xxoWGKD6>Lh1p|5|) zVS6uJ&jPFqyhrZm{lX=*&$E4WjG3Ynx&aU19Phd(Xls~fudX|pS7evCXJftJ7S2A} z7{;5{VO!$_c=tU|>*hNLZ=a5F|6gLecYWcl@7|tD&|-o8dzbRMPZI_|m2orl0cgt@ z_vQi!e8uPBM|{u9xmLg)W+i5yUF6hrKN2zL^>f8>V{vZxDffl8hbpdlif>FE@cHwh zB-H<{SsTN?H0W)z$JirK_i&tJ?EmEV&<1{+xM!b$h!Kn3Sr2VpduxI71lSaF>%MMs z`p$1o6MBO~&L*!VM@B#3-jXM0j}87T@@T^XW2k?wRfMu_4~qE*Y}HSIdm@2z|GK0;5#k% zNleM-V$K%%lwALeqmPnTass~t?uoqnZBJLg3VQ;aPo0SQuJD|N-;r;Cj`0m}J(uve z3cPFRL11r_HJtO?hi8-5GfH%vYp#8|r{<4k>HIN{oY^h$1^F)>voX%BAhrbmDsvdy zls<;<(f>uxHfhV>86CpceVcB~WzT*l?4sT09{BuJab1o*1aFCZzs((s=lMDCj<}Y( zSN3nsUm6Er0l)i<#oE?gfv4aAf865^u|9_Pw|TFC{na_F)iK6-SD=M^0v6cb2hYI! zWRBw>0CTg%FyGo3cmd2?1M7G2&hNS=jsg1KwPWlBaDUvJS@E62`2#-~ufC#>-GT%h zr@nA=$ZhZy=al=1xc8z1-eF^>>&13H;}b9!_)S=0$kn%YkFNiH!i)G{fD}3Hogc32 zKft@NEhiYaM?G`ifYW|PmFzR0FTD9<`H`4+$MbDs%navk-ABMRck-hg*P8c(*nRAM z;QE|TX(Oww?+_R6%{7n;_W--ao;U{d*sejW@ST4wmAgn>t}Sz!lVa%gOXAN!fK9nf z%yDbRSt}SbmLA`l9a`vbifi^A<9atGV)%Ww?%O8sI_n*&|68+TVhy!i@21$}T{!pD z+IjtIV)kKt7rP^tKqCkKaC3Mz9s42ZC%%t=3)}q){0_{B zsT=h7#9sk@?J2R4L+tT3u{ANrzXmBFfZTO_$2i9u;CD>V(=#)ELM)MM%{d5c*Xc7L ze*>%vN7cN(KNGW#d5Qc6gc{%2uizY;jN>1HDUf&n>?_R2o`N0VxEXp@a(ndW_`d_L zt0g}H>stRFXvyh_Sx?FPOzbM$E1=}LO3h~D_kxJw^Tj!RPW%j6VprM6enYH7ufX51 z)nB4}#@e&bqGvn>30%TAulAJ4n-Oo&=D7DQwov?D6~Oov?UFmNLw(opd&jzJ_hf;N z7|@|7_z#K8eFl1B_K^F)cARVZ2&55b?GDb~Zee>zT42vTdCn=ct(DMz*5x&#eJ-3^ zX=CWi8qVW>dcSfH=(RTJL6ED|9>W+g$2;*9=r2IN7sWp)zVkbWe8kY#$M_9s@mD49 z9{xVI<6JAOKe9h3=6(DSbTO=>CDvjuiMbBzIM%o^&uN0)kkbLClGQ)Qk?yn z!;JVo2x5wPZDVP#wO?>$2`~4pgVA({C&Z1Z;}gKUzLE* zLi{(NgX@U3z`lOLci{^lyj#Uj#CqU(#aerC=6Lt5 zA=kywll2e5Yq%xaUIMNM&ZC`cX#;aUCifvQ$9j{32EX2oDKYQS8vO?NSzxaB&-r=~ zk`u9v9e0N3ML2{%?Kqzeaxx z60tpFC8sIUlH*-;pFW0nj%~6}-0?o!*MRw5kO>mFORO;lKEih09r07a_Fel1K7f2f ztjdyD?wPpt-;o%`?2^}GZ_1jua6Jk5-ghk>xp~jf_UJk$Xy=!AUdLGXfLP55^2bvD z`-G=4a_!T+7Bq~{Ksn!+a36xF-~_HW2XX#G;Ae{SCSX548}@P??YTY#^{gE?fm;D{ zrl5^s-{;tWBlq{<1~?Vs9XV_CzljUwM|70&5krkX5_=80l50an{{$T8T{!^C?@F%k9rs^39fxntE^_#tAJ5^c^fBz!$HZo(PUY10>if~T zPwNw3;0JL3kGG6-AIzC=;kx>J!FZxl)8BJe+s4Fj-rj@HL4$8!(~=wb3EQ#4yYdF! z{Tlbp$n5te-Z8EP_sIPkcyD|BvFPuSKZA4ce15I@PxLrVqNc*bBk)X3Q9v*{M4(Lzlm9_92(=iUv6eM*0cS!8pHRe2WjkAwK zU~cuR{JAzTUWpj$ zT6>E>F@BDn*J^-*{{g-hJO!_SwWcz|w}JZxIG;7_=Lzt+JB|+Z*nPTmnbt3&fbZ$M)*H_L{J}7~=IC*E6oYEzM_K zMPBTaI|o(TFfZld|5kqAshsg+@qXJ|#Bdg;&Wo)W>sk&Auiw~Q13Sl|F9pu|#`$@S z|59RDQ;&9yp69ry9-nL3pw}Sp+hy`F&O9+bf7YmSfbD#9^a6M{97oiMqRI1jvUfo@ z;abKY;WzIHy#lpAwR<7=0>9600!^uL16$5<0t?2M{g)WW_I-vv0oEIfeV+rr^L!0F zd)F}oL5&19*ycH(pKs>ao9q51dqed1ci4M@Pc-ZRHP68!Q$51#@*OPUxE=nLvue~I6<$s3O?7neYEeeHu7-z8vtW#IFJ4AGd{2ezMi-w{#VKgo{4?xQ;ZqQ0o*&l zbFj{s?_!uE&}+Cepe>A3*K_KyH>HOLaqM@`4}f{rpURr}&kOH)xP~TP_u$z5$#qMd z`|%K@5;ucSK>ZYd3y!e^evS5dmrsnx(&4kO75W4?mpMH!UQQ@^Ph#M!e;+Q0`xHF~ z3EnvO((_Z=cztf!G5X=N?cVK@h~aa=J!y<#T*W>+@G;y9IM;K~5##&|xP;w+`n%!( z!Mmx|SHv34-VoPfPkD3o`OMj4UVlw4`fqT?e1gu;?uZZa@8JI}_yYL;P*M!FUNXK0 zT*rO%Q!tiPdh#77@0ywzM-1z>&SPL7V{v^;;2a-5(m zeaN4nJ+IKmxCK?rbN;$V>eG^EjCICR>zP;O!rG&;{&79uBj$C$xNWJQu*F~Znk!H%D@h|H}Lyx9e@X5hOaGg4SIv`1YiBVV9wb3OK=7p zZ_pjF3-mjTt>b?e$3FErUlwe|Sa~rOKO^#YX7qW9AIBWyzXfi9FM<2%{LZ6oP1kL% z`{w$qbd2r2jp1)3hl~TeihXvB-4i+YaLQZ1#eWU{_rPANcsBBP@x2MQ;0Rw8_shQW ze#B6FHR3ybEoi{_jPPWM80yPuFUeI`*)X2z16$zSX~gF|d{rFJjVb5T5!-?*GUAE6 zLN!tPEcwnQ=6l0=bcJt5vZkJ&r#;#pjTwu%-p8rG#`t;G#(;D3UF%f58%+#*;dzgx zWBd40xA)pIn2w${f!3&$~dKBD*ht)bE7W zs5K?EUH5^0FqReLb80o(1RW&OPDNw!ZU;yI@iL z_Uryu&U$lvo|)@%-e2PLyN`C%y3JtU!>?}z*MPbY9X_GxGv?Obus(fjV$Vvw2ERS~ z`C?y+Irbc24rXwkjdL^skUUrU-Ei#F-^Gw~TJm!OKlEGAvsv%4H=xexT=tTPX+Qwy zJ=c%)@j5p1`1Ltog?{&B3tUg`8J}x$PnX~jY~aod0(}SAS4-@==pMVqcvt1>+T`4e z4R{Er$Td-V#y2Z|Iq#c&>GO_Q*Llp@M|)=vi~nPEPn}l4c}`n=&f|XS&wGVd627sl z%pum?D$dt1ju`gvTgKXo@!v7Fw>3zS-IqTu$HZCFc=t80H%F!6=lC2hvtJwTp*`#p z=kVWocCF6gI>+K3xnH5C&h+2iuDz&xV&-5fi9Bm8L5&~B)PLtX;>Y}!+%Lh3T=&WI zv`#~;@EOj6&pJ2jxVTjZa9!bj*G|R1`JCf_AEda-dOy7*C*-?^fS;jTv}^q%oU+0G z4BP#huCb5d8L+>X#xovEF!t`rTYDch|vKHjJGk-&x|OA}4w{V;l5X?6(6o zE=_pj?Nffb=hpE%EV@Kd#67I zKfq6|0XE;=W9InL+>*$P^6wFy(BGOk`@C`wgWS@)`}G@Q{smlH7We=SSmTrPzSQ~D zJ=}pl;NOE5=qi=>v#cY|aiAN41b%CN3A`(QuHFSztmXY_Vt9ubYus^}FYsqUx8O}U z@5VJ?zxtfhyjkfh^c7p)yD8t}lT*|Wi_bIc7?0r|nDgHt!HvcF>`V@1$e|PY?ulLcH5<_1%@Of6#=gPQ_m<@Sn-~=#>KZf^V9_L~HQDcws zcfcIqC$2e+jUl!zI?(pyy1h?haV_q-bDn~lQ)A5OqK<}rb8Ox_aPpqLxgtuedWk=w zJ79fd6KJsKpf?8n7QQ#YA=qc!;co!4`2T$PlE;~Z=G zy~+8sTc8~=JXiLz0_ODi8e)F{YkV!^8r#SL<&B;5+yq0N3d~*^FWq*uK-ftHOE4^WR|XyC?;<3)iEoSaaWc=vUzD z5tn1ay-e9WAx=CbCgC4T;7`D{`S-FV_E@?YMr-`?z8jQ?tgig{Z@6vY$Ha4d9q}Qk z6FTc92A*1Qhq+T`@L!1LMw^E%^{ zczrLeZ`_iYV_?6ZgL78jerU-f725i#?S#hFWjHIj@+N_y*3ie-l`v1p$uawc%asEqHTW>smj^>tbzL zh&w6ofi*G4t%wq&0T@OZowSe`odh-Iu+kX@;{%Y0OwU_yaiK zH}=yA60BF5*~evEsJy}_>1Qme`w&iEsszutj_ z?f6st-peXA#=71Exgf5J`Oa&-HToDVL9J_C{p{5Kj<^?~3rAOFMqcgzDl`1P<34`{ zW}uJP5efTdHMUTZk^S;CN6V5n&)_=0+=*Mymzx>7j-~4di&m6a_P7I%w@-KJr-<3K3yn{>B z^}XU}SxcR%cu!ql2j~0m7O3LdeP$GMo!>f_f=|EV`$Ik=5AzT6l^g@_8h}nA5ZEcf@q~eK&bO-G`k*o@ca6KErSQ^-tvbOv?B0-|VR2-T$J* z7+3ehxyH8dyYaa8;&bQzr?|SWmG@aX20gs>PqC*Ga1W?2e;ZvT;qQTBom~fH=lBdHCRHl_jEVpQdR?2h5j#$a`T=#P}X?ADut< zh4x)z&pulFDC{sh1Md1qE&Ebi|L&fj~TC(j>4zvkS>{|T_J zpPTAlp0!u8k0xHvpU0IP>0gllzmsq5RIJ0S-*{4#S10Mow+y{3-B0k_}Xy+DB!3ASE{i}iv+TWLqaSv~T6MV+C)Of_$ z{rv!hHokmc;5+aVpE+~m!3wyRck#UitZS|{qoAY4EqvdC$H1O{2Asz}ZvcJo7kwWs z@A)Niv!IvMOJD)E;F`SeIk*<|z&--q0q;(WHqX5{gtNZ}TaiBlbA0cCEqGP<6y-kU z$l=HB(eDKT-+@|dh`9RGUT<1&B7WZq=Pel1?vwBO6l3pe`=4;0(VE;-;NFj=jp4l2 zYvPW;Sllny>^*%7+DU#xo@ea+HP>?)%lC}!!QMTG*(Cp3;qC8(V&^j|MLcWw+8Wov z@4=Wi%rn&g1#qv%5;6Q;M*m3+={-Vtjp zM>YBw-Ve9ogzFsN3)gFo=W6VFltO2!e$JRC1IG^{icvi;RhjA|^ys>Qz-_5_DaCMIVz(03>;(mked#j4~&$_e1 ztH<)3*fqY0*Po{i_EhA1&=sAE-5@>r`Wx`k9tzan0-y8ug|B-?be0FiZtRUc5XL3Y zs2O+KBat&+evp*a4|~L0Cx-LCV0=vxvNbCY;|n+Alz)N5sUxFq6AXiw^yKbN2iogf4mmK4I|5MI7&gJ>|9l^Nq+zsQ7`~)ul z+jN-99PVRsehWN{CiE6z?C(&XW5TxXSbSgo3$BZ?cd)G`?>l9eEb&JS_u?isj2Hes z?>_oF%wu4U+vtYa1k5wO#r_^$-zXa9|V#jqG z#vS+oens8P)flwi`8Q%b`>B{C?;e}$Jh`W0Z^=0y<#qV?i9N*UIrqSGuDMOg zYl=9h^*6*MxGK&gyyw>Qy`$*!^XUu5K5HT(Qqu06El@3_`d^wW-Z`-HX=$7(|G)O{*m8U_iqI@z+S4z`7ZQc-v=Gz z%e4{IGj{#vCb{xi@H>3gv{vBnL6uw|-m|(ud+)|#{S?FBtG|Ww%%*aI@7TN@oU#8e z`_%`z)LIZ@?^W(#%llsY0o1duyyq7&^lzN|KNWj&PrRq|9X*)W#E{ja-zF}~*gGVj z99Ljg`HWc4P|iDjjd6=zMLVAXdIi4)fjt)Q>sU6#q!>KrIR7-K#=l3d&v536HGX{N ziE)(_^yV1-T2eOc+up8nLsT2XXSn&eOrk=UliGIQQcXZ08m|yx+SIz*-+; zeN|TY{sNbumyy5n)|U5q@4-t@C1Q97cuw9iNG=;{Ou71+rgmheQnZJ=a}w zjNiNDy{O`PedfF;>yf|m`dWC`c3kd&_47VNje4zJ*9Gx@zi)Q%&T|CkdfngN7<57l z&!;W+5q|fh1CQ`KZv*!o_6Oi{4{=x75aayfIk1Ln=wtXSyo2v9@XTi*0Y6Ke_kC~% z#-iU^FM(@riT9j3>P=+<_aR&W?Uoqz8o2khuW?>ajNCJFt(ugU|)k3oyizKvm}9OvO$@cvwZ|=-`GHT#^Tq4++=EZ>_U&DF{uAsM=sK76wVlg373&0X`h#&6@_qPu_r=IM7nZ?aCtxRjiqC3 zjCI`y$4cIZ^53EvJp6?3!3G3atE22KbdPQR0>3@afN}O0_{Xxv@8``DoMVqA#jt+& zz`Hv1GTcL-b8}qN61ydKEXI2Ne+T}qFRz`@mxAqneFyB}BhcWha!g%+CwL0nv)muN zwLSkW_zf^lEP#7+2AoSg2OZ;{JjeF5WxN4%(1JBM0l$JX*Zq1@;2QVwx!(7%tv>^& zz`botKhEC^#`f!6hl~|{=CAOnYwy80;1tisIS=6NZ3UW;DN^18uGckON(}2*l)Q&< z=2uDZ$M`P59IWA5d<|G)yAJ#QEu1k4e-zF&UB~B|MUTH_oc95jVm&1%A2bAi+1F2dx}1#(fR6a|T?!2bc5X^Q=B5?tP#b<2#|juWhX3 zBap&~*K!}9d0h8p0Vri&@DK}zXDY{Z0GQttJv=b^ij?R-Wqd!_9RZQ z&w#b+9cYP<0&CUZ4ZWW|aXnl@Ut@eJjK_8N;l2hzYy$%RZ;XxU8Jl0@jd=;T#Ww?M zV644W>4*{c@VnQCzOY9C?NpSxOti1x?V2{`L zi17k{3icW8v*5mMK!>jf0koY*SgQq9{Lc7g;Tp7J-%1xlj?aVV{sstqSQ>?ZL2>U?AA$e-am1J0QkH=>C7hvXR-paoSN?~`-BH>~et zv}+uTW7jr^GtOsDNiodZp_N+aP51?H+=R^VjJJ`^%e@~)^hKO?D_{NUC|m;{@%p~V zxzDfg{~WkRrH`SfPw-juy!3FYF1B$sUhN##pGu11J=wyY88Z?&_uMmao`&&QI(#32 zgug3)BO3GCp6RFH9mwjkEp+ZOs*WjdqRsUY7}s>8Q~I@4_b_h@DFPEO4HjKZ^0${{SrUCF%rdfnt51 z`R_pky3l)7&dHPayT)Qvw??HH-pRfL8f>NZEq8$5yLCuz&$tm)V)(tt`mXCS+WE|H z;X2@Y+%wnUK3xi24;p-~(HdD|sNuMS3&t(5rtc2#xgaXukut z*irCH#vOJ8T#wJ!9K8nKr7D5#zItXYu~ocVj<>{L!#)A_qof$-&*!HDzkBW)w69~o z4XU&;{4Hf!^5^BVKz@Z!C|wL|%>#Zc@cX>JnACEAlsUZn>3yueh;sJm4m^je^-0P1 zxy7%%f^%PHAm_)he{!z%XZZc!SBO@w^eJy|%Dlul&&QzpefKP?Sl5^= zYEakl4zcn*@k`KxPECwH&)(Q@48CD(ylXKw;cGw#wDtM7`D>tx_omKQYc<4*1>956 zke4Nf*be=B{LZz4n>!bM^!Q!hL*Sm?0WCgJCE>dPlqjDC$F+WT4)-&{fZ=_*VzKbF6eSCpkrPl^QO`ie(`QZ`zIk1PZtQq&j z`pg^W+84&)yCV3Oa0yuRgmFhq5ByFSXvJ|Olo&n_t$RxR4AeNelfB{lk^Uoj#`bLN ze_)&U7@dXq0LFe?IL~Uscnk8Lz?<8k6@5P6`n1ijduJW%Cu(FVT;;u2^46~+SK(fW z`rjw?{N~^ZzT2RR@oW69OTYhYcs}Bdix{HigwI!7d;>H5Ji+fC{WtL4I2J#X?bX~9 z>@Eg2(C(9U?}0x60;P-^>uh_{Q#=8_{kQjq+WM>5kLyvUHTAhh*J6GE*J&SB z9RD2NIP?EY8(fO91#jVZePcOc+=DiT{5AHt4?hpB*%kh8*zR4GmU!p4t}(`1uZp#t zL;oExl@*-*%6|#o0OR@f3;Q}MbyB>p;u(Gl-{QN8?nYdVGWV}=_R&mv#`f;#j`P&L z4g3fAm-rrm8{iN$0dSY;jnCQM+?%gKf@_IwKnI+|9M`phTLb-b zw0FY!UDG?jGt;i;NR>-*K6|v+h{3II#W<1U^D!%E;X9Cka~|No2h44Ob9bN-#I(R# z_4fkzbp`^zdSM(`0sFM?`)Hr9hrrr%e4-=Abp*JL|9zl#oc-_{d2|ER!kVZ0!C&@< zcWm9`1TR|Px&J5d^ZrwC4LGk@gXfIR>xg}UZ7t8hJ#`&nE%+t2>pX$GK+8L~@+W+r zR~rKwbbto_I(~cbj_fnGmUFuwPr>-R1R43W-5mQ6uDwc%p`LxTaPNV@XHV``gPlMJ zTHv_FZ=a$szUO;G?GtlEl^F7G!MkV9KO6HU&NbAts{byT|6RL>#E&InSg*br++tE| ziO(GC?UH=P@HapcyCQ~~^3Ly`j>Yd(`VN`bbxh|mNAywlZ=8&6&o##PQ$yTT=F~N2 zSC77DeU|dsOcNPA89Rcs>3SWvr_virjdJ5i!Z$Jw?fBS3jD-r$} zbV3{J@9gF}4$uI{e&#r*y*k#vMElMC zNca@v@9YhKi+9`y@$fz9jD>pzI{YoDV%`SczEOV3!Y5!miQ73bJ6zO^@e zcXAE?Hra#R2Qltb2Y)!qw@w@7_a`{#=@?)34)1*9{+<&zFExzouNC zo`W;IpIcm2*W z7I}Rw{GWk6PQ|re!~X;Lb@^waryS!J^?duJjpkz#rF}lJ^$(a&+#7t&;94x zlYHYvigMjO#ONrsk z>e&73@r`9!a?ST$ayS}$Z?=q;dGQaU;is|d!qY)^=Jk{y_p}FH4E>#8x7Yy`pDCY* zCL9eh$$H`^WF{9sLE`qVCHNL}#A;WupEIU+ulFa!s4O57F-ee}DFKWGvQujL&y?OYBsN-G&l-9QXPiFQr|o)bjCmbg1L}Gi<@dAP!dUjIhj&in+_R6sS0EVA@V97f z#e3rK8~Pp;J+5t?fK&e+UOncmVeb9Hxqs~_#&N*8?Dwmnn{Y}Z=`dqL|^q8 zu6tmO)>t?Zfu4xIcd7YT&|&vrEY@{v2H; z;m>1yEB)~G?}T#Bd#SOl>$n9`dHhw}#{TTi zq`n92>F(aJ4+(oJo~w5v)#lhc<@y)e+;smRsUKK_39$Wj@2c!2}wE`Pp&N13^{1ODY zJ@8rU@y~&uA=dRATev7O_MqQ0eiIxr?$Gi|AFuDc^IX6y&Yxo=5AzwH6|O~F*JsuH z*BHaty*fqD&^Q}p?yD}V|(uJ zV)tl!vj2v9E$D%@)cTg#-fL@Aac^dfui>|@_n;;AQW!hGdvXta46G5vnA@R`iAi8t z{!Z9p_l0jparWk1Z|x1ggFQgs0)35bhI;<);(Fa@@2R!#pgZC|L|dc97WTUa&+$Ji z=+J@hiV*Ak4Y0nyN4dB5-oVY#D_|}6%z6I+eh(glyI>8@;Vy+dYvp}M+q3=n{Vw39 zlJ6m$IkQqnpK~4n{bDR_4EMlZa-KXs#oV4e@89R(mmt>-$O_CV#KU~2 zxh`ydsbpVpsiPV$5q@% z&rr@h_uwA(0b1x6?%^@;%ssmeI0ehX_0&AY4t$q_ulC|R*0DzQ;Jd@5O%u`%A>kv0s9YIvXIT zL=4ZP@7)t1*WpvLj@K~_Tot)9#yvXw)gy7g|Fk!(ZC>-x`Y(Xs_>2Y&aU!=B15IOi3vWk#I!>@&}SzK;KSiK*ImuY6GRp&W6y z(JOS1PrtQX^T+rb##Q8M?%QzA-Q#NtT670ydjn@3$L}$=w?p8Xd^g+$TgKk?HRy<2 zqUZQe&^=>w?Ck)Z1>dFMb1km1uY4ho86V)!&k?qDs@TH^aL)Zc`WlF+EbGH|&+B(W z?_!6~byw*b*IxJG?Qa1BzgT0tKlXQ2IDO9D0(0GO?Vk85Nn1bKzMg`AfezmcRO#WZ z@x1WsqJ6I5q<8x14zr6F<<5-M$U)_T$bq$W?z9D~(jwlgmV}x`5 z1te>rFJo#bvF^p)^1GpN?pKb3Ye54N=s~+Tyleap(l@h7mr_Z!B(pL%T9Tg8}` z@hRMgz`b{le1`aY_zqaZx^jK$5otTA6Q47fS(=KnPZP*b50C%$-e|A=8WFi-ZP&OwRdlc z-H_9Rb+PeWN=v*>}_1nHP-o!bI+~s9@^hj?8o=SE3ndq-`I~2NAauCd_DZH_?p83!4{gawAbKs2i z-c@PwDf(t$+TXwqez~=Es5_PppPWd+{EY2UG4>91YmWIXh!{RwS8!X#-of|4FTh(M z;2U%g*I`@xUB(S|3j$mU_J%m?$eXthTJ0wJW2Zz6`#Ho7J?azotAD#U?CU?!`_$Jj zuV`=Z+q=)|5`JwA_IUl~MU>|oJr(N+^QX^LjdgyX#Wt$vwRVr>JGcNH<3=FXeE`?S zut)D;H`u$xb^O#_{vPfN;5^nGOP#-E&J9RWShV}`J~>LlKF6M+t2|+Bp7JMb>yO3z z;JHVzr)c>eJILvPcUHa921lR)J`a9x%kQu9Szot{OF zcR%HqKT*Rtd+`jWqR%_%y-IO)j#a7qJ$x(Rdr-wY=-hL3?hkz%m^Ux=YU~!j_j;`# zgqnSR58tfDqPL(aT#L5urug(*-}s0o|Fh-Cj2Dc1#!IyEzs6_o5lHwu;CKs+djNkO z1ok05F$aztptiRj?X!^Jgk$IMxmtmb;P!#JRou&OfOEZp-hgXhUJ%p}4M^qpg8Vx! z{4TK%=MZaZ_CS5*F2K1~d-F_MbWqhIwkhTlme(xyrNXZ#e- zIYS@P20uUo#$SMW`7AiZmJ6`K7HhBq_HTSWb7Su9mA?bv--4Tr^(Qc6?0OfVXY5^9 zSNjQLV-mhvv+BTSo;gS8D9AONL0s3b!Ca1bWB$Up!)}1Ftu|s^?}EHFAAmmteJkQ} z4Eh|LfffE4$P%S~(Fy(;T%HTNigW0*-w)BANg_wTSmT!ue9D6HBgXoCMyqWP4daKP zN{V5fE!;l-?}2%>wmr?{z&rT;e3AbhSmI0McOXC&`%LhMz`FJC13mU%@tuM>wsF_6 zm55>gJi9Hp1^h1Z6xfSvGS|=Dj@T9i*y8tD^PO}XBz(p?f1o$u4z};DFMxNtBgUHE zDQi9i55S7prf~9~;FGsk*YDaJV~QAK&buYf*eZPt`?kX8U68Za83;IYdu;D{i@t_- zKh6tpjCDP`D*kTmc{VZRx99+lt$PfVF8BcwaE)`|8e7Kpkbt&+^Qz3To%0yD-VW%i zVsEbH0NuxM{w-Q@JrToxyN)F|1PPzq_zj(bZ;C&~)qOX13unv%-NHN9SX#zi41EmyIcCX)tNSLuOXRK5gXh?RuA-el2YTSY zKV185kQQIi!$*6=n!iW8H)B~c_J4b>e?H-i+t%L;O46=%NM(Nvx`bX)_FzA|#QUDl zY8Q#myIsXT&v*10F|0M80rpfrpvE(>{)t@Y_dV0^$i0QX!|xh>XH9#Zm6{1HcZZm% zd|dMS9h|Y|!C1Or1g=l~;~4FF{`vk-$--Ozd{QR@ zvf$TneqN2`1!LdM~Pj2O?;}G`{5cqNo{_wD^Z#E-^jKU^8^c_FH}N}6 z!`K=#xCP^mvHN8m*H9(lZ}ESL&-KZROM$QRJ5L8r!C2lQH;8k-+&6Y3kU6oB;8*5= z;mk6Xe^|@+MjylH=ws|VpvpeH>pm0MM>0&pBMdvtLN&TKLO4^7ITDsxGxbS z7T~9XH=k8qQ%?sT7sm$;lg0F%5FqJz01vRvr7$V$9dtU?hAoqZ-5>fuWL~3@} z2`<3Q7{-@^y}~E&oo|FOMN*XehIanL3D@ISz9rB7&*!TnhTjS6n%sv2;5zJir;y`a z@f^&*tW|=K@OxKXgIprUIH7L?0=or^GS)AD`Fla(>}4%i#Cm-Ua6g{yUH!ZcxV$eV z?;(6$OYVjIhL}7brjlZ)CvW}{xwpylygG0S

      flag = bitwise::Or(MyEnum::val1, flag);
      -namespace bitwise { -template -static inline base::type::EnumType -And(Enum e, base::type::EnumType flag) { - return static_cast(flag) & static_cast(e); -} -template -static inline base::type::EnumType -Not(Enum e, base::type::EnumType flag) { - return static_cast(flag) & ~(static_cast(e)); -} -template -static inline base::type::EnumType -Or(Enum e, base::type::EnumType flag) { - return static_cast(flag) | static_cast(e); -} -} // namespace bitwise -template -static inline void -addFlag(Enum e, base::type::EnumType* flag) { - *flag = base::utils::bitwise::Or(e, *flag); -} -template -static inline void -removeFlag(Enum e, base::type::EnumType* flag) { - *flag = base::utils::bitwise::Not(e, *flag); -} -template -static inline bool -hasFlag(Enum e, base::type::EnumType flag) { - return base::utils::bitwise::And(e, flag) > 0x0; -} -} // namespace utils -namespace threading { -#if ELPP_THREADING_ENABLED -#if !ELPP_USE_STD_THREADING -namespace internal { -/// @brief A mutex wrapper for compiler that dont yet support std::recursive_mutex -class Mutex : base::NoCopy { - public: - Mutex(void) { -#if ELPP_OS_UNIX - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&m_underlyingMutex, &attr); - pthread_mutexattr_destroy(&attr); -#elif ELPP_OS_WINDOWS - InitializeCriticalSection(&m_underlyingMutex); -#endif // ELPP_OS_UNIX - } - - virtual ~Mutex(void) { -#if ELPP_OS_UNIX - pthread_mutex_destroy(&m_underlyingMutex); -#elif ELPP_OS_WINDOWS - DeleteCriticalSection(&m_underlyingMutex); -#endif // ELPP_OS_UNIX - } - - inline void - lock(void) { -#if ELPP_OS_UNIX - pthread_mutex_lock(&m_underlyingMutex); -#elif ELPP_OS_WINDOWS - EnterCriticalSection(&m_underlyingMutex); -#endif // ELPP_OS_UNIX - } - - inline bool - try_lock(void) { -#if ELPP_OS_UNIX - return (pthread_mutex_trylock(&m_underlyingMutex) == 0); -#elif ELPP_OS_WINDOWS - return TryEnterCriticalSection(&m_underlyingMutex); -#endif // ELPP_OS_UNIX - } - - inline void - unlock(void) { -#if ELPP_OS_UNIX - pthread_mutex_unlock(&m_underlyingMutex); -#elif ELPP_OS_WINDOWS - LeaveCriticalSection(&m_underlyingMutex); -#endif // ELPP_OS_UNIX - } - - private: -#if ELPP_OS_UNIX - pthread_mutex_t m_underlyingMutex; -#elif ELPP_OS_WINDOWS - CRITICAL_SECTION m_underlyingMutex; -#endif // ELPP_OS_UNIX -}; -/// @brief Scoped lock for compiler that dont yet support std::lock_guard -template -class ScopedLock : base::NoCopy { - public: - explicit ScopedLock(M& mutex) { - m_mutex = &mutex; - m_mutex->lock(); - } - - virtual ~ScopedLock(void) { - m_mutex->unlock(); - } - - private: - M* m_mutex; - ScopedLock(void); -}; -} // namespace internal -typedef base::threading::internal::Mutex Mutex; -typedef base::threading::internal::ScopedLock ScopedLock; -#else -typedef std::recursive_mutex Mutex; -typedef std::lock_guard ScopedLock; -#endif // !ELPP_USE_STD_THREADING -#else -namespace internal { -/// @brief Mutex wrapper used when multi-threading is disabled. -class NoMutex : base::NoCopy { - public: - NoMutex(void) { - } - inline void - lock(void) { - } - inline bool - try_lock(void) { - return true; - } - inline void - unlock(void) { - } -}; -/// @brief Lock guard wrapper used when multi-threading is disabled. -template -class NoScopedLock : base::NoCopy { - public: - explicit NoScopedLock(Mutex&) { - } - virtual ~NoScopedLock(void) { - } - - private: - NoScopedLock(void); -}; -} // namespace internal -typedef base::threading::internal::NoMutex Mutex; -typedef base::threading::internal::NoScopedLock ScopedLock; -#endif // ELPP_THREADING_ENABLED -/// @brief Base of thread safe class, this class is inheritable-only -class ThreadSafe { - public: - virtual inline void - acquireLock(void) ELPP_FINAL { - m_mutex.lock(); - } - virtual inline void - releaseLock(void) ELPP_FINAL { - m_mutex.unlock(); - } - virtual inline base::threading::Mutex& - lock(void) ELPP_FINAL { - return m_mutex; - } - - protected: - ThreadSafe(void) { - } - virtual ~ThreadSafe(void) { - } - - private: - base::threading::Mutex m_mutex; -}; - -#if ELPP_THREADING_ENABLED -#if !ELPP_USE_STD_THREADING -/// @brief Gets ID of currently running threading in windows systems. On unix, nothing is returned. -static std::string -getCurrentThreadId(void) { - std::stringstream ss; -#if (ELPP_OS_WINDOWS) - ss << GetCurrentThreadId(); -#endif // (ELPP_OS_WINDOWS) - return ss.str(); -} -#else -/// @brief Gets ID of currently running threading using std::this_thread::get_id() -static std::string -getCurrentThreadId(void) { - std::stringstream ss; - ss << std::this_thread::get_id(); - return ss.str(); -} -#endif // !ELPP_USE_STD_THREADING -#else -static inline std::string -getCurrentThreadId(void) { - return std::string(); -} -#endif // ELPP_THREADING_ENABLED -} // namespace threading -namespace utils { -class File : base::StaticClass { - public: - /// @brief Creates new out file stream for specified filename. - /// @return Pointer to newly created fstream or nullptr - static base::type::fstream_t* - newFileStream(const std::string& filename); - - /// @brief Gets size of file provided in stream - static std::size_t - getSizeOfFile(base::type::fstream_t* fs); - - /// @brief Determines whether or not provided path exist in current file system - static bool - pathExists(const char* path, bool considerFile = false); - - /// @brief Creates specified path on file system - /// @param path Path to create. - static bool - createPath(const std::string& path); - /// @brief Extracts path of filename with leading slash - static std::string - extractPathFromFilename(const std::string& fullPath, const char* seperator = base::consts::kFilePathSeperator); - /// @brief builds stripped filename and puts it in buff - static void - buildStrippedFilename(const char* filename, char buff[], - std::size_t limit = base::consts::kSourceFilenameMaxLength); - /// @brief builds base filename and puts it in buff - static void - buildBaseFilename(const std::string& fullPath, char buff[], - std::size_t limit = base::consts::kSourceFilenameMaxLength, - const char* seperator = base::consts::kFilePathSeperator); -}; -/// @brief String utilities helper class used internally. You should not use it. -class Str : base::StaticClass { - public: - /// @brief Checks if character is digit. Dont use libc implementation of it to prevent locale issues. - static inline bool - isDigit(char c) { - return c >= '0' && c <= '9'; - } - - /// @brief Matches wildcards, '*' and '?' only supported. - static bool - wildCardMatch(const char* str, const char* pattern); - - static std::string& - ltrim(std::string& str); - static std::string& - rtrim(std::string& str); - static std::string& - trim(std::string& str); - - /// @brief Determines whether or not str starts with specified string - /// @param str String to check - /// @param start String to check against - /// @return Returns true if starts with specified string, false otherwise - static bool - startsWith(const std::string& str, const std::string& start); - - /// @brief Determines whether or not str ends with specified string - /// @param str String to check - /// @param end String to check against - /// @return Returns true if ends with specified string, false otherwise - static bool - endsWith(const std::string& str, const std::string& end); - - /// @brief Replaces all instances of replaceWhat with 'replaceWith'. Original variable is changed for performance. - /// @param [in,out] str String to replace from - /// @param replaceWhat Character to replace - /// @param replaceWith Character to replace with - /// @return Modified version of str - static std::string& - replaceAll(std::string& str, char replaceWhat, char replaceWith); - - /// @brief Replaces all instances of 'replaceWhat' with 'replaceWith'. (String version) Replaces in place - /// @param str String to replace from - /// @param replaceWhat Character to replace - /// @param replaceWith Character to replace with - /// @return Modified (original) str - static std::string& - replaceAll(std::string& str, const std::string& replaceWhat, const std::string& replaceWith); - - static void - replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, - const base::type::string_t& replaceWith); -#if defined(ELPP_UNICODE) - static void - replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, - const std::string& replaceWith); -#endif // defined(ELPP_UNICODE) - /// @brief Converts string to uppercase - /// @param str String to convert - /// @return Uppercase string - static std::string& - toUpper(std::string& str); - - /// @brief Compares cstring equality - uses strcmp - static bool - cStringEq(const char* s1, const char* s2); - - /// @brief Compares cstring equality (case-insensitive) - uses toupper(char) - /// Dont use strcasecmp because of CRT (VC++) - static bool - cStringCaseEq(const char* s1, const char* s2); - - /// @brief Returns true if c exist in str - static bool - contains(const char* str, char c); - - static char* - convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded = true); - static char* - addToBuff(const char* str, char* buf, const char* bufLim); - static char* - clearBuff(char buff[], std::size_t lim); - - /// @brief Converst wchar* to char* - /// NOTE: Need to free return value after use! - static char* - wcharPtrToCharPtr(const wchar_t* line); -}; -/// @brief Operating System helper static class used internally. You should not use it. -class OS : base::StaticClass { - public: -#if ELPP_OS_WINDOWS - /// @brief Gets environment variables for Windows based OS. - /// We are not using getenv(const char*) because of CRT deprecation - /// @param varname Variable name to get environment variable value for - /// @return If variable exist the value of it otherwise nullptr - static const char* - getWindowsEnvironmentVariable(const char* varname); -#endif // ELPP_OS_WINDOWS -#if ELPP_OS_ANDROID - /// @brief Reads android property value - static std::string - getProperty(const char* prop); - - /// @brief Reads android device name - static std::string - getDeviceName(void); -#endif // ELPP_OS_ANDROID - - /// @brief Runs command on terminal and returns the output. - /// - /// @detail This is applicable only on unix based systems, for all other OS, an empty string is returned. - /// @param command Bash command - /// @return Result of bash output or empty string if no result found. - static const std::string - getBashOutput(const char* command); - - /// @brief Gets environment variable. This is cross-platform and CRT safe (for VC++) - /// @param variableName Environment variable name - /// @param defaultVal If no environment variable or value found the value to return by default - /// @param alternativeBashCommand If environment variable not found what would be alternative bash command - /// in order to look for value user is looking for. E.g, for 'user' alternative command will 'whoami' - static std::string - getEnvironmentVariable(const char* variableName, const char* defaultVal, - const char* alternativeBashCommand = nullptr); - /// @brief Gets current username. - static std::string - currentUser(void); - - /// @brief Gets current host name or computer name. - /// - /// @detail For android systems this is device name with its manufacturer and model seperated by hyphen - static std::string - currentHost(void); - /// @brief Whether or not terminal supports colors - static bool - termSupportsColor(void); -}; -/// @brief Contains utilities for cross-platform date/time. This class make use of el::base::utils::Str -class DateTime : base::StaticClass { - public: - /// @brief Cross platform gettimeofday for Windows and unix platform. This can be used to determine current - /// microsecond. - /// - /// @detail For unix system it uses gettimeofday(timeval*, timezone*) and for Windows, a seperate implementation is - /// provided - /// @param [in,out] tv Pointer that gets updated - static void - gettimeofday(struct timeval* tv); - - /// @brief Gets current date and time with a subsecond part. - /// @param format User provided date/time format - /// @param ssPrec A pointer to base::SubsecondPrecision from configuration (non-null) - /// @returns string based date time in specified format. - static std::string - getDateTime(const char* format, const base::SubsecondPrecision* ssPrec); - - /// @brief Converts timeval (struct from ctime) to string using specified format and subsecond precision - static std::string - timevalToString(struct timeval tval, const char* format, const el::base::SubsecondPrecision* ssPrec); - - /// @brief Formats time to get unit accordingly, units like second if > 1000 or minutes if > 60000 etc - static base::type::string_t - formatTime(unsigned long long time, base::TimestampUnit timestampUnit); - - /// @brief Gets time difference in milli/micro second depending on timestampUnit - static unsigned long long - getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, - base::TimestampUnit timestampUnit); - - static struct ::tm* - buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo); - - private: - static char* - parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, std::size_t msec, - const base::SubsecondPrecision* ssPrec); -}; -/// @brief Command line arguments for application if specified using el::Helpers::setArgs(..) or START_EASYLOGGINGPP(..) -class CommandLineArgs { - public: - CommandLineArgs(void) { - setArgs(0, static_cast(nullptr)); - } - CommandLineArgs(int argc, const char** argv) { - setArgs(argc, argv); - } - CommandLineArgs(int argc, char** argv) { - setArgs(argc, argv); - } - virtual ~CommandLineArgs(void) { - } - /// @brief Sets arguments and parses them - inline void - setArgs(int argc, const char** argv) { - setArgs(argc, const_cast(argv)); - } - /// @brief Sets arguments and parses them - void - setArgs(int argc, char** argv); - /// @brief Returns true if arguments contain paramKey with a value (seperated by '=') - bool - hasParamWithValue(const char* paramKey) const; - /// @brief Returns value of arguments - /// @see hasParamWithValue(const char*) - const char* - getParamValue(const char* paramKey) const; - /// @brief Return true if arguments has a param (not having a value) i,e without '=' - bool - hasParam(const char* paramKey) const; - /// @brief Returns true if no params available. This exclude argv[0] - bool - empty(void) const; - /// @brief Returns total number of arguments. This exclude argv[0] - std::size_t - size(void) const; - friend base::type::ostream_t& - operator<<(base::type::ostream_t& os, const CommandLineArgs& c); - - private: - int m_argc; - char** m_argv; - std::unordered_map m_paramsWithValue; - std::vector m_params; -}; -/// @brief Abstract registry (aka repository) that provides basic interface for pointer repository specified by T_Ptr -/// type. -/// -/// @detail Most of the functions are virtual final methods but anything implementing this abstract class should -/// implement unregisterAll() and deepCopy(const AbstractRegistry&) and write registerNew() method -/// according to container and few more methods; get() to find element, unregister() to unregister single entry. Please -/// note that this is thread-unsafe and should also implement thread-safety mechanisms in implementation. -template -class AbstractRegistry : public base::threading::ThreadSafe { - public: - typedef typename Container::iterator iterator; - typedef typename Container::const_iterator const_iterator; - - /// @brief Default constructor - AbstractRegistry(void) { - } - - /// @brief Move constructor that is useful for base classes - AbstractRegistry(AbstractRegistry&& sr) { - if (this == &sr) { - return; - } - unregisterAll(); - m_list = std::move(sr.m_list); - } - - bool - operator==(const AbstractRegistry& other) { - if (size() != other.size()) { - return false; - } - for (std::size_t i = 0; i < m_list.size(); ++i) { - if (m_list.at(i) != other.m_list.at(i)) { - return false; - } - } - return true; - } - - bool - operator!=(const AbstractRegistry& other) { - if (size() != other.size()) { - return true; - } - for (std::size_t i = 0; i < m_list.size(); ++i) { - if (m_list.at(i) != other.m_list.at(i)) { - return true; - } - } - return false; - } - - /// @brief Assignment move operator - AbstractRegistry& - operator=(AbstractRegistry&& sr) { - if (this == &sr) { - return *this; - } - unregisterAll(); - m_list = std::move(sr.m_list); - return *this; - } - - virtual ~AbstractRegistry(void) { - } - - /// @return Iterator pointer from start of repository - virtual inline iterator - begin(void) ELPP_FINAL { - return m_list.begin(); - } - - /// @return Iterator pointer from end of repository - virtual inline iterator - end(void) ELPP_FINAL { - return m_list.end(); - } - - /// @return Constant iterator pointer from start of repository - virtual inline const_iterator - cbegin(void) const ELPP_FINAL { - return m_list.cbegin(); - } - - /// @return End of repository - virtual inline const_iterator - cend(void) const ELPP_FINAL { - return m_list.cend(); - } - - /// @return Whether or not repository is empty - virtual inline bool - empty(void) const ELPP_FINAL { - return m_list.empty(); - } - - /// @return Size of repository - virtual inline std::size_t - size(void) const ELPP_FINAL { - return m_list.size(); - } - - /// @brief Returns underlying container by reference - virtual inline Container& - list(void) ELPP_FINAL { - return m_list; - } - - /// @brief Returns underlying container by constant reference. - virtual inline const Container& - list(void) const ELPP_FINAL { - return m_list; - } - - /// @brief Unregisters all the pointers from current repository. - virtual void - unregisterAll(void) = 0; - - protected: - virtual void - deepCopy(const AbstractRegistry&) = 0; - void - reinitDeepCopy(const AbstractRegistry& sr) { - unregisterAll(); - deepCopy(sr); - } - - private: - Container m_list; -}; - -/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (non-predicate version) -/// -/// @detail NOTE: This is thread-unsafe implementation (although it contains lock function, it does not use these -/// functions) -/// of AbstractRegistry. Any implementation of this class should be -/// explicitly (by using lock functions) -template -class Registry : public AbstractRegistry> { - public: - typedef typename Registry::iterator iterator; - typedef typename Registry::const_iterator const_iterator; - - Registry(void) { - } - - /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. - Registry(const Registry& sr) : AbstractRegistry>() { - if (this == &sr) { - return; - } - this->reinitDeepCopy(sr); - } - - /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element - /// @see unregisterAll() - /// @see deepCopy(const AbstractRegistry&) - Registry& - operator=(const Registry& sr) { - if (this == &sr) { - return *this; - } - this->reinitDeepCopy(sr); - return *this; - } - - virtual ~Registry(void) { - unregisterAll(); - } - - protected: - virtual void - unregisterAll(void) ELPP_FINAL { - if (!this->empty()) { - for (auto&& curr : this->list()) { - base::utils::safeDelete(curr.second); - } - this->list().clear(); - } - } - - /// @brief Registers new registry to repository. - virtual void - registerNew(const T_Key& uniqKey, T_Ptr* ptr) ELPP_FINAL { - unregister(uniqKey); - this->list().insert(std::make_pair(uniqKey, ptr)); - } - - /// @brief Unregisters single entry mapped to specified unique key - void - unregister(const T_Key& uniqKey) { - T_Ptr* existing = get(uniqKey); - if (existing != nullptr) { - this->list().erase(uniqKey); - base::utils::safeDelete(existing); - } - } - - /// @brief Gets pointer from repository. If none found, nullptr is returned. - T_Ptr* - get(const T_Key& uniqKey) { - iterator it = this->list().find(uniqKey); - return it == this->list().end() ? nullptr : it->second; - } - - private: - virtual void - deepCopy(const AbstractRegistry>& sr) ELPP_FINAL { - for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) { - registerNew(it->first, new T_Ptr(*it->second)); - } - } -}; - -/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (predicate version) -/// -/// @detail NOTE: This is thread-unsafe implementation of AbstractRegistry. Any implementation of this -/// class should be made thread-safe explicitly -template -class RegistryWithPred : public AbstractRegistry> { - public: - typedef typename RegistryWithPred::iterator iterator; - typedef typename RegistryWithPred::const_iterator const_iterator; - - RegistryWithPred(void) { - } - - virtual ~RegistryWithPred(void) { - unregisterAll(); - } - - /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. - RegistryWithPred(const RegistryWithPred& sr) : AbstractRegistry>() { - if (this == &sr) { - return; - } - this->reinitDeepCopy(sr); - } - - /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element - /// @see unregisterAll() - /// @see deepCopy(const AbstractRegistry&) - RegistryWithPred& - operator=(const RegistryWithPred& sr) { - if (this == &sr) { - return *this; - } - this->reinitDeepCopy(sr); - return *this; - } - - friend base::type::ostream_t& - operator<<(base::type::ostream_t& os, const RegistryWithPred& sr) { - for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { - os << ELPP_LITERAL(" ") << **it << ELPP_LITERAL("\n"); - } - return os; - } - - protected: - virtual void - unregisterAll(void) ELPP_FINAL { - if (!this->empty()) { - for (auto&& curr : this->list()) { - base::utils::safeDelete(curr); - } - this->list().clear(); - } - } - - virtual void - unregister(T_Ptr*& ptr) ELPP_FINAL { - if (ptr) { - iterator iter = this->begin(); - for (; iter != this->end(); ++iter) { - if (ptr == *iter) { - break; - } - } - if (iter != this->end() && *iter != nullptr) { - this->list().erase(iter); - base::utils::safeDelete(*iter); - } - } - } - - virtual inline void - registerNew(T_Ptr* ptr) ELPP_FINAL { - this->list().push_back(ptr); - } - - /// @brief Gets pointer from repository with speicifed arguments. Arguments are passed to predicate - /// in order to validate pointer. - template - T_Ptr* - get(const T& arg1, const T2 arg2) { - iterator iter = std::find_if(this->list().begin(), this->list().end(), Pred(arg1, arg2)); - if (iter != this->list().end() && *iter != nullptr) { - return *iter; - } - return nullptr; - } - - private: - virtual void - deepCopy(const AbstractRegistry>& sr) { - for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { - registerNew(new T_Ptr(**it)); - } - } -}; -class Utils { - public: - template - static bool - installCallback(const std::string& id, std::unordered_map* mapT) { - if (mapT->find(id) == mapT->end()) { - mapT->insert(std::make_pair(id, TPtr(new T()))); - return true; - } - return false; - } - - template - static void - uninstallCallback(const std::string& id, std::unordered_map* mapT) { - if (mapT->find(id) != mapT->end()) { - mapT->erase(id); - } - } - - template - static T* - callback(const std::string& id, std::unordered_map* mapT) { - typename std::unordered_map::iterator iter = mapT->find(id); - if (iter != mapT->end()) { - return static_cast(iter->second.get()); - } - return nullptr; - } -}; -} // namespace utils -} // namespace base -/// @brief Base of Easylogging++ friendly class -/// -/// @detail After inheriting this class publicly, implement pure-virtual function `void log(std::ostream&) const` -class Loggable { - public: - virtual ~Loggable(void) { - } - virtual void - log(el::base::type::ostream_t&) const = 0; - - private: - friend inline el::base::type::ostream_t& - operator<<(el::base::type::ostream_t& os, const Loggable& loggable) { - loggable.log(os); - return os; - } -}; -namespace base { -/// @brief Represents log format containing flags and date format. This is used internally to start initial log -class LogFormat : public Loggable { - public: - LogFormat(void); - LogFormat(Level level, const base::type::string_t& format); - LogFormat(const LogFormat& logFormat); - LogFormat(LogFormat&& logFormat); - LogFormat& - operator=(const LogFormat& logFormat); - virtual ~LogFormat(void) { - } - bool - operator==(const LogFormat& other); - - /// @brief Updates format to be used while logging. - /// @param userFormat User provided format - void - parseFromFormat(const base::type::string_t& userFormat); - - inline Level - level(void) const { - return m_level; - } - - inline const base::type::string_t& - userFormat(void) const { - return m_userFormat; - } - - inline const base::type::string_t& - format(void) const { - return m_format; - } - - inline const std::string& - dateTimeFormat(void) const { - return m_dateTimeFormat; - } - - inline base::type::EnumType - flags(void) const { - return m_flags; - } - - inline bool - hasFlag(base::FormatFlags flag) const { - return base::utils::hasFlag(flag, m_flags); - } - - virtual void - log(el::base::type::ostream_t& os) const { - os << m_format; - } - - protected: - /// @brief Updates date time format if available in currFormat. - /// @param index Index where %datetime, %date or %time was found - /// @param [in,out] currFormat current format that is being used to format - virtual void - updateDateFormat(std::size_t index, base::type::string_t& currFormat) ELPP_FINAL; - - /// @brief Updates %level from format. This is so that we dont have to do it at log-writing-time. It uses m_format - /// and m_level - virtual void - updateFormatSpec(void) ELPP_FINAL; - - inline void - addFlag(base::FormatFlags flag) { - base::utils::addFlag(flag, &m_flags); - } - - private: - Level m_level; - base::type::string_t m_userFormat; - base::type::string_t m_format; - std::string m_dateTimeFormat; - base::type::EnumType m_flags; - std::string m_currentUser; - std::string m_currentHost; - friend class el::Logger; // To resolve loggerId format specifier easily -}; -} // namespace base -/// @brief Resolving function for format specifier -typedef std::function FormatSpecifierValueResolver; -/// @brief User-provided custom format specifier -/// @see el::Helpers::installCustomFormatSpecifier -/// @see FormatSpecifierValueResolver -class CustomFormatSpecifier { - public: - CustomFormatSpecifier(const char* formatSpecifier, const FormatSpecifierValueResolver& resolver) - : m_formatSpecifier(formatSpecifier), m_resolver(resolver) { - } - inline const char* - formatSpecifier(void) const { - return m_formatSpecifier; - } - inline const FormatSpecifierValueResolver& - resolver(void) const { - return m_resolver; - } - inline bool - operator==(const char* formatSpecifier) { - return strcmp(m_formatSpecifier, formatSpecifier) == 0; - } - - private: - const char* m_formatSpecifier; - FormatSpecifierValueResolver m_resolver; -}; -/// @brief Represents single configuration that has representing level, configuration type and a string based value. -/// -/// @detail String based value means any value either its boolean, integer or string itself, it will be embedded inside -/// quotes and will be parsed later. -/// -/// Consider some examples below: -/// * el::Configuration confEnabledInfo(el::Level::Info, el::ConfigurationType::Enabled, "true"); -/// * el::Configuration confMaxLogFileSizeInfo(el::Level::Info, el::ConfigurationType::MaxLogFileSize, "2048"); -/// * el::Configuration confFilenameInfo(el::Level::Info, el::ConfigurationType::Filename, "/var/log/my.log"); -class Configuration : public Loggable { - public: - Configuration(const Configuration& c); - Configuration& - operator=(const Configuration& c); - - virtual ~Configuration(void) { - } - - /// @brief Full constructor used to sets value of configuration - Configuration(Level level, ConfigurationType configurationType, const std::string& value); - - /// @brief Gets level of current configuration - inline Level - level(void) const { - return m_level; - } - - /// @brief Gets configuration type of current configuration - inline ConfigurationType - configurationType(void) const { - return m_configurationType; - } - - /// @brief Gets string based configuration value - inline const std::string& - value(void) const { - return m_value; - } - - /// @brief Set string based configuration value - /// @param value Value to set. Values have to be std::string; For boolean values use "true", "false", for any - /// integral values - /// use them in quotes. They will be parsed when configuring - inline void - setValue(const std::string& value) { - m_value = value; - } - - virtual void - log(el::base::type::ostream_t& os) const; - - /// @brief Used to find configuration from configuration (pointers) repository. Avoid using it. - class Predicate { - public: - Predicate(Level level, ConfigurationType configurationType); - - bool - operator()(const Configuration* conf) const; - - private: - Level m_level; - ConfigurationType m_configurationType; - }; - - private: - Level m_level; - ConfigurationType m_configurationType; - std::string m_value; -}; - -/// @brief Thread-safe Configuration repository -/// -/// @detail This repository represents configurations for all the levels and configuration type mapped to a value. -class Configurations : public base::utils::RegistryWithPred { - public: - /// @brief Default constructor with empty repository - Configurations(void); - - /// @brief Constructor used to set configurations using configuration file. - /// @param configurationFile Full path to configuration file - /// @param useDefaultsForRemaining Lets you set the remaining configurations to default. - /// @param base If provided, this configuration will be based off existing repository that this argument is pointing - /// to. - /// @see parseFromFile(const std::string&, Configurations* base) - /// @see setRemainingToDefault() - Configurations(const std::string& configurationFile, bool useDefaultsForRemaining = true, - Configurations* base = nullptr); - - virtual ~Configurations(void) { - } - - /// @brief Parses configuration from file. - /// @param configurationFile Full path to configuration file - /// @param base Configurations to base new configuration repository off. This value is used when you want to use - /// existing Configurations to base all the values and then set rest of configuration via configuration file. - /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure - /// you - /// do not proceed without successful parse. - bool - parseFromFile(const std::string& configurationFile, Configurations* base = nullptr); - - /// @brief Parse configurations from configuration string. - /// - /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary - /// new line characters are provided. - /// @param base Configurations to base new configuration repository off. This value is used when you want to use - /// existing Configurations to base all the values and then set rest of configuration via configuration text. - /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure - /// you - /// do not proceed without successful parse. - bool - parseFromText(const std::string& configurationsString, Configurations* base = nullptr); - - /// @brief Sets configuration based-off an existing configurations. - /// @param base Pointer to existing configurations. - void - setFromBase(Configurations* base); - - /// @brief Determines whether or not specified configuration type exists in the repository. - /// - /// @detail Returns as soon as first level is found. - /// @param configurationType Type of configuration to check existence for. - bool - hasConfiguration(ConfigurationType configurationType); - - /// @brief Determines whether or not specified configuration type exists for specified level - /// @param level Level to check - /// @param configurationType Type of configuration to check existence for. - bool - hasConfiguration(Level level, ConfigurationType configurationType); - - /// @brief Sets value of configuration for specified level. - /// - /// @detail Any existing configuration for specified level will be replaced. Also note that configuration types - /// ConfigurationType::SubsecondPrecision and ConfigurationType::PerformanceTracking will be ignored if not set for - /// Level::Global because these configurations are not dependant on level. - /// @param level Level to set configuration for (el::Level). - /// @param configurationType Type of configuration (el::ConfigurationType) - /// @param value A string based value. Regardless of what the data type of configuration is, it will always be - /// string from users' point of view. This is then parsed later to be used internally. - /// @see Configuration::setValue(const std::string& value) - /// @see el::Level - /// @see el::ConfigurationType - void - set(Level level, ConfigurationType configurationType, const std::string& value); - - /// @brief Sets single configuration based on other single configuration. - /// @see set(Level level, ConfigurationType configurationType, const std::string& value) - void - set(Configuration* conf); - - inline Configuration* - get(Level level, ConfigurationType configurationType) { - base::threading::ScopedLock scopedLock(lock()); - return RegistryWithPred::get(level, configurationType); - } - - /// @brief Sets configuration for all levels. - /// @param configurationType Type of configuration - /// @param value String based value - /// @see Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) - inline void - setGlobally(ConfigurationType configurationType, const std::string& value) { - setGlobally(configurationType, value, false); - } - - /// @brief Clears repository so that all the configurations are unset - inline void - clear(void) { - base::threading::ScopedLock scopedLock(lock()); - unregisterAll(); - } - - /// @brief Gets configuration file used in parsing this configurations. - /// - /// @detail If this repository was set manually or by text this returns empty string. - inline const std::string& - configurationFile(void) const { - return m_configurationFile; - } - - /// @brief Sets configurations to "factory based" configurations. - void - setToDefault(void); - - /// @brief Lets you set the remaining configurations to default. - /// - /// @detail By remaining, it means that the level/type a configuration does not exist for. - /// This function is useful when you want to minimize chances of failures, e.g, if you have a configuration file - /// that sets configuration for all the configurations except for Enabled or not, we use this so that ENABLED is set - /// to default i.e, true. If you dont do this explicitly (either by calling this function or by using second param - /// in Constructor and try to access a value, an error is thrown - void - setRemainingToDefault(void); - - /// @brief Parser used internally to parse configurations from file or text. - /// - /// @detail This class makes use of base::utils::Str. - /// You should not need this unless you are working on some tool for Easylogging++ - class Parser : base::StaticClass { - public: - /// @brief Parses configuration from file. - /// @param configurationFile Full path to configuration file - /// @param sender Sender configurations pointer. Usually 'this' is used from calling class - /// @param base Configurations to base new configuration repository off. This value is used when you want to use - /// existing Configurations to base all the values and then set rest of configuration via configuration - /// file. - /// @return True if successfully parsed, false otherwise. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make - /// sure you - /// do not proceed without successful parse. - static bool - parseFromFile(const std::string& configurationFile, Configurations* sender, Configurations* base = nullptr); - - /// @brief Parse configurations from configuration string. - /// - /// @detail This configuration string has same syntax as configuration file contents. Make sure all the - /// necessary new line characters are provided. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you - /// do not proceed without successful parse (This is recommended) - /// @param configurationsString the configuration in plain text format - /// @param sender Sender configurations pointer. Usually 'this' is used from calling class - /// @param base Configurations to base new configuration repository off. This value is used when you want to use - /// existing Configurations to base all the values and then set rest of configuration via configuration - /// text. - /// @return True if successfully parsed, false otherwise. - static bool - parseFromText(const std::string& configurationsString, Configurations* sender, Configurations* base = nullptr); - - private: - friend class el::Loggers; - static void - ignoreComments(std::string* line); - static bool - isLevel(const std::string& line); - static bool - isComment(const std::string& line); - static inline bool - isConfig(const std::string& line); - static bool - parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, Level* currLevel, - Configurations* conf); - }; - - private: - std::string m_configurationFile; - bool m_isFromFile; - friend class el::Loggers; - - /// @brief Unsafely sets configuration if does not already exist - void - unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value); - - /// @brief Thread unsafe set - void - unsafeSet(Level level, ConfigurationType configurationType, const std::string& value); - - /// @brief Sets configurations for all levels including Level::Global if includeGlobalLevel is true - /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) - void - setGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel); - - /// @brief Sets configurations (Unsafely) for all levels including Level::Global if includeGlobalLevel is true - /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) - void - unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel); -}; - -namespace base { -typedef std::shared_ptr FileStreamPtr; -typedef std::unordered_map LogStreamsReferenceMap; -/// @brief Configurations with data types. -/// -/// @detail el::Configurations have string based values. This is whats used internally in order to read correct -/// configurations. This is to perform faster while writing logs using correct configurations. -/// -/// This is thread safe and final class containing non-virtual destructor (means nothing should inherit this class) -class TypedConfigurations : public base::threading::ThreadSafe { - public: - /// @brief Constructor to initialize (construct) the object off el::Configurations - /// @param configurations Configurations pointer/reference to base this typed configurations off. - /// @param logStreamsReference Use ELPP->registeredLoggers()->logStreamsReference() - TypedConfigurations(Configurations* configurations, base::LogStreamsReferenceMap* logStreamsReference); - - TypedConfigurations(const TypedConfigurations& other); - - virtual ~TypedConfigurations(void) { - } - - const Configurations* - configurations(void) const { - return m_configurations; - } - - bool - enabled(Level level); - bool - toFile(Level level); - const std::string& - filename(Level level); - bool - toStandardOutput(Level level); - const base::LogFormat& - logFormat(Level level); - const base::SubsecondPrecision& - subsecondPrecision(Level level = Level::Global); - const base::MillisecondsWidth& - millisecondsWidth(Level level = Level::Global); - bool - performanceTracking(Level level = Level::Global); - base::type::fstream_t* - fileStream(Level level); - std::size_t - maxLogFileSize(Level level); - std::size_t - logFlushThreshold(Level level); - - private: - Configurations* m_configurations; - std::unordered_map m_enabledMap; - std::unordered_map m_toFileMap; - std::unordered_map m_filenameMap; - std::unordered_map m_toStandardOutputMap; - std::unordered_map m_logFormatMap; - std::unordered_map m_subsecondPrecisionMap; - std::unordered_map m_performanceTrackingMap; - std::unordered_map m_fileStreamMap; - std::unordered_map m_maxLogFileSizeMap; - std::unordered_map m_logFlushThresholdMap; - base::LogStreamsReferenceMap* m_logStreamsReference; - - friend class el::Helpers; - friend class el::base::MessageBuilder; - friend class el::base::Writer; - friend class el::base::DefaultLogDispatchCallback; - friend class el::base::LogDispatcher; - - template - inline Conf_T - getConfigByVal(Level level, const std::unordered_map* confMap, const char* confName) { - base::threading::ScopedLock scopedLock(lock()); - return unsafeGetConfigByVal(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope - } - - template - inline Conf_T& - getConfigByRef(Level level, std::unordered_map* confMap, const char* confName) { - base::threading::ScopedLock scopedLock(lock()); - return unsafeGetConfigByRef(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope - } - - template - Conf_T - unsafeGetConfigByVal(Level level, const std::unordered_map* confMap, const char* confName) { - ELPP_UNUSED(confName); - typename std::unordered_map::const_iterator it = confMap->find(level); - if (it == confMap->end()) { - try { - return confMap->at(Level::Global); - } catch (...) { - ELPP_INTERNAL_ERROR("Unable to get configuration [" - << confName << "] for level [" << LevelHelper::convertToString(level) << "]" - << std::endl - << "Please ensure you have properly configured logger.", - false); - return Conf_T(); - } - } - return it->second; - } - - template - Conf_T& - unsafeGetConfigByRef(Level level, std::unordered_map* confMap, const char* confName) { - ELPP_UNUSED(confName); - typename std::unordered_map::iterator it = confMap->find(level); - if (it == confMap->end()) { - try { - return confMap->at(Level::Global); - } catch (...) { - ELPP_INTERNAL_ERROR("Unable to get configuration [" - << confName << "] for level [" << LevelHelper::convertToString(level) << "]" - << std::endl - << "Please ensure you have properly configured logger.", - false); - } - } - return it->second; - } - - template - void - setValue(Level level, const Conf_T& value, std::unordered_map* confMap, - bool includeGlobalLevel = true) { - // If map is empty and we are allowed to add into generic level (Level::Global), do it! - if (confMap->empty() && includeGlobalLevel) { - confMap->insert(std::make_pair(Level::Global, value)); - return; - } - // If same value exist in generic level already, dont add it to explicit level - typename std::unordered_map::iterator it = confMap->find(Level::Global); - if (it != confMap->end() && it->second == value) { - return; - } - // Now make sure we dont double up values if we really need to add it to explicit level - it = confMap->find(level); - if (it == confMap->end()) { - // Value not found for level, add new - confMap->insert(std::make_pair(level, value)); - } else { - // Value found, just update value - confMap->at(level) = value; - } - } - - void - build(Configurations* configurations); - unsigned long - getULong(std::string confVal); - std::string - resolveFilename(const std::string& filename); - void - insertFile(Level level, const std::string& fullFilename); - bool - unsafeValidateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback); - - inline bool - validateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback) { - base::threading::ScopedLock scopedLock(lock()); - return unsafeValidateFileRolling(level, preRollOutCallback); - } -}; -/// @brief Class that keeps record of current line hit for occasional logging -class HitCounter { - public: - HitCounter(void) : m_filename(""), m_lineNumber(0), m_hitCounts(0) { - } - - HitCounter(const char* filename, base::type::LineNumber lineNumber) - : m_filename(filename), m_lineNumber(lineNumber), m_hitCounts(0) { - } - - HitCounter(const HitCounter& hitCounter) - : m_filename(hitCounter.m_filename), - m_lineNumber(hitCounter.m_lineNumber), - m_hitCounts(hitCounter.m_hitCounts) { - } - - HitCounter& - operator=(const HitCounter& hitCounter) { - if (&hitCounter != this) { - m_filename = hitCounter.m_filename; - m_lineNumber = hitCounter.m_lineNumber; - m_hitCounts = hitCounter.m_hitCounts; - } - return *this; - } - - virtual ~HitCounter(void) { - } - - /// @brief Resets location of current hit counter - inline void - resetLocation(const char* filename, base::type::LineNumber lineNumber) { - m_filename = filename; - m_lineNumber = lineNumber; - } - - /// @brief Validates hit counts and resets it if necessary - inline void - validateHitCounts(std::size_t n) { - if (m_hitCounts >= base::consts::kMaxLogPerCounter) { - m_hitCounts = (n >= 1 ? base::consts::kMaxLogPerCounter % n : 0); - } - ++m_hitCounts; - } - - inline const char* - filename(void) const { - return m_filename; - } - - inline base::type::LineNumber - lineNumber(void) const { - return m_lineNumber; - } - - inline std::size_t - hitCounts(void) const { - return m_hitCounts; - } - - inline void - increment(void) { - ++m_hitCounts; - } - - class Predicate { - public: - Predicate(const char* filename, base::type::LineNumber lineNumber) - : m_filename(filename), m_lineNumber(lineNumber) { - } - inline bool - operator()(const HitCounter* counter) { - return ((counter != nullptr) && (strcmp(counter->m_filename, m_filename) == 0) && - (counter->m_lineNumber == m_lineNumber)); - } - - private: - const char* m_filename; - base::type::LineNumber m_lineNumber; - }; - - private: - const char* m_filename; - base::type::LineNumber m_lineNumber; - std::size_t m_hitCounts; -}; -/// @brief Repository for hit counters used across the application -class RegisteredHitCounters : public base::utils::RegistryWithPred { - public: - /// @brief Validates counter for every N, i.e, registers new if does not exist otherwise updates original one - /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned - bool - validateEveryN(const char* filename, base::type::LineNumber lineNumber, std::size_t n); - - /// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one - /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned - bool - validateAfterN(const char* filename, base::type::LineNumber lineNumber, std::size_t n); - - /// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one - /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned - bool - validateNTimes(const char* filename, base::type::LineNumber lineNumber, std::size_t n); - - /// @brief Gets hit counter registered at specified position - inline const base::HitCounter* - getCounter(const char* filename, base::type::LineNumber lineNumber) { - base::threading::ScopedLock scopedLock(lock()); - return get(filename, lineNumber); - } -}; -/// @brief Action to be taken for dispatching -enum class DispatchAction : base::type::EnumType { None = 1, NormalLog = 2, SysLog = 4 }; -} // namespace base -template -class Callback : protected base::threading::ThreadSafe { - public: - Callback(void) : m_enabled(true) { - } - inline bool - enabled(void) const { - return m_enabled; - } - inline void - setEnabled(bool enabled) { - base::threading::ScopedLock scopedLock(lock()); - m_enabled = enabled; - } - - protected: - virtual void - handle(const T* handlePtr) = 0; - - private: - bool m_enabled; -}; -class LogDispatchData { - public: - LogDispatchData() : m_logMessage(nullptr), m_dispatchAction(base::DispatchAction::None) { - } - inline const LogMessage* - logMessage(void) const { - return m_logMessage; - } - inline base::DispatchAction - dispatchAction(void) const { - return m_dispatchAction; - } - inline void - setLogMessage(LogMessage* logMessage) { - m_logMessage = logMessage; - } - inline void - setDispatchAction(base::DispatchAction dispatchAction) { - m_dispatchAction = dispatchAction; - } - - private: - LogMessage* m_logMessage; - base::DispatchAction m_dispatchAction; - friend class base::LogDispatcher; -}; -class LogDispatchCallback : public Callback { - protected: - virtual void - handle(const LogDispatchData* data); - base::threading::Mutex& - fileHandle(const LogDispatchData* data); - - private: - friend class base::LogDispatcher; - std::unordered_map> m_fileLocks; - base::threading::Mutex m_fileLocksMapLock; -}; -class PerformanceTrackingCallback : public Callback { - private: - friend class base::PerformanceTracker; -}; -class LoggerRegistrationCallback : public Callback { - private: - friend class base::RegisteredLoggers; -}; -class LogBuilder : base::NoCopy { - public: - LogBuilder() : m_termSupportsColor(base::utils::OS::termSupportsColor()) { - } - virtual ~LogBuilder(void) { - ELPP_INTERNAL_INFO(3, "Destroying log builder...") - } - virtual base::type::string_t - build(const LogMessage* logMessage, bool appendNewLine) const = 0; - void - convertToColoredOutput(base::type::string_t* logLine, Level level); - - private: - bool m_termSupportsColor; - friend class el::base::DefaultLogDispatchCallback; -}; -typedef std::shared_ptr LogBuilderPtr; -/// @brief Represents a logger holding ID and configurations we need to write logs -/// -/// @detail This class does not write logs itself instead its used by writer to read configuations from. -class Logger : public base::threading::ThreadSafe, public Loggable { - public: - Logger(const std::string& id, base::LogStreamsReferenceMap* logStreamsReference); - Logger(const std::string& id, const Configurations& configurations, - base::LogStreamsReferenceMap* logStreamsReference); - Logger(const Logger& logger); - Logger& - operator=(const Logger& logger); - - virtual ~Logger(void) { - base::utils::safeDelete(m_typedConfigurations); - } - - virtual inline void - log(el::base::type::ostream_t& os) const { - os << m_id.c_str(); - } - - /// @brief Configures the logger using specified configurations. - void - configure(const Configurations& configurations); - - /// @brief Reconfigures logger using existing configurations - void - reconfigure(void); - - inline const std::string& - id(void) const { - return m_id; - } - - inline const std::string& - parentApplicationName(void) const { - return m_parentApplicationName; - } - - inline void - setParentApplicationName(const std::string& parentApplicationName) { - m_parentApplicationName = parentApplicationName; - } - - inline Configurations* - configurations(void) { - return &m_configurations; - } - - inline base::TypedConfigurations* - typedConfigurations(void) { - return m_typedConfigurations; - } - - static bool - isValidId(const std::string& id); - - /// @brief Flushes logger to sync all log files for all levels - void - flush(void); - - void - flush(Level level, base::type::fstream_t* fs); - - inline bool - isFlushNeeded(Level level) { - return ++m_unflushedCount.find(level)->second >= m_typedConfigurations->logFlushThreshold(level); - } - - inline LogBuilder* - logBuilder(void) const { - return m_logBuilder.get(); - } - - inline void - setLogBuilder(const LogBuilderPtr& logBuilder) { - m_logBuilder = logBuilder; - } - - inline bool - enabled(Level level) const { - return m_typedConfigurations->enabled(level); - } - -#if ELPP_VARIADIC_TEMPLATES_SUPPORTED -#define LOGGER_LEVEL_WRITERS_SIGNATURES(FUNCTION_NAME) \ - template \ - inline void FUNCTION_NAME(const char*, const T&, const Args&...); \ - template \ - inline void FUNCTION_NAME(const T&); - - template - inline void - verbose(int, const char*, const T&, const Args&...); - - template - inline void - verbose(int, const T&); - - LOGGER_LEVEL_WRITERS_SIGNATURES(info) - LOGGER_LEVEL_WRITERS_SIGNATURES(debug) - LOGGER_LEVEL_WRITERS_SIGNATURES(warn) - LOGGER_LEVEL_WRITERS_SIGNATURES(error) - LOGGER_LEVEL_WRITERS_SIGNATURES(fatal) - LOGGER_LEVEL_WRITERS_SIGNATURES(trace) -#undef LOGGER_LEVEL_WRITERS_SIGNATURES -#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED - private: - std::string m_id; - base::TypedConfigurations* m_typedConfigurations; - base::type::stringstream_t m_stream; - std::string m_parentApplicationName; - bool m_isConfigured; - Configurations m_configurations; - std::unordered_map m_unflushedCount; - base::LogStreamsReferenceMap* m_logStreamsReference; - LogBuilderPtr m_logBuilder; - - friend class el::LogMessage; - friend class el::Loggers; - friend class el::Helpers; - friend class el::base::RegisteredLoggers; - friend class el::base::DefaultLogDispatchCallback; - friend class el::base::MessageBuilder; - friend class el::base::Writer; - friend class el::base::PErrorWriter; - friend class el::base::Storage; - friend class el::base::PerformanceTracker; - friend class el::base::LogDispatcher; - - Logger(void); - -#if ELPP_VARIADIC_TEMPLATES_SUPPORTED - template - void - log_(Level, int, const char*, const T&, const Args&...); - - template - inline void - log_(Level, int, const T&); - - template - void - log(Level, const char*, const T&, const Args&...); - - template - inline void - log(Level, const T&); -#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED - - void - initUnflushedCount(void); - - inline base::type::stringstream_t& - stream(void) { - return m_stream; - } - - void - resolveLoggerFormatSpec(void) const; -}; -namespace base { -/// @brief Loggers repository -class RegisteredLoggers : public base::utils::Registry { - public: - explicit RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder); - - virtual ~RegisteredLoggers(void) { - unsafeFlushAll(); - } - - inline void - setDefaultConfigurations(const Configurations& configurations) { - base::threading::ScopedLock scopedLock(lock()); - m_defaultConfigurations.setFromBase(const_cast(&configurations)); - } - - inline Configurations* - defaultConfigurations(void) { - return &m_defaultConfigurations; - } - - Logger* - get(const std::string& id, bool forceCreation = true); - - template - inline bool - installLoggerRegistrationCallback(const std::string& id) { - return base::utils::Utils::installCallback( - id, &m_loggerRegistrationCallbacks); - } - - template - inline void - uninstallLoggerRegistrationCallback(const std::string& id) { - base::utils::Utils::uninstallCallback( - id, &m_loggerRegistrationCallbacks); - } - - template - inline T* - loggerRegistrationCallback(const std::string& id) { - return base::utils::Utils::callback( - id, &m_loggerRegistrationCallbacks); - } - - bool - remove(const std::string& id); - - inline bool - has(const std::string& id) { - return get(id, false) != nullptr; - } - - inline void - unregister(Logger*& logger) { - base::threading::ScopedLock scopedLock(lock()); - base::utils::Registry::unregister(logger->id()); - } - - inline base::LogStreamsReferenceMap* - logStreamsReference(void) { - return &m_logStreamsReference; - } - - inline void - flushAll(void) { - base::threading::ScopedLock scopedLock(lock()); - unsafeFlushAll(); - } - - inline void - setDefaultLogBuilder(LogBuilderPtr& logBuilderPtr) { - base::threading::ScopedLock scopedLock(lock()); - m_defaultLogBuilder = logBuilderPtr; - } - - private: - LogBuilderPtr m_defaultLogBuilder; - Configurations m_defaultConfigurations; - base::LogStreamsReferenceMap m_logStreamsReference; - std::unordered_map m_loggerRegistrationCallbacks; - friend class el::base::Storage; - - void - unsafeFlushAll(void); -}; -/// @brief Represents registries for verbose logging -class VRegistry : base::NoCopy, public base::threading::ThreadSafe { - public: - explicit VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags); - - /// @brief Sets verbose level. Accepted range is 0-9 - void - setLevel(base::type::VerboseLevel level); - - inline base::type::VerboseLevel - level(void) const { - return m_level; - } - - inline void - clearModules(void) { - base::threading::ScopedLock scopedLock(lock()); - m_modules.clear(); - } - - void - setModules(const char* modules); - - bool - allowed(base::type::VerboseLevel vlevel, const char* file); - - inline const std::unordered_map& - modules(void) const { - return m_modules; - } - - void - setFromArgs(const base::utils::CommandLineArgs* commandLineArgs); - - /// @brief Whether or not vModules enabled - inline bool - vModulesEnabled(void) { - return !base::utils::hasFlag(LoggingFlag::DisableVModules, *m_pFlags); - } - - private: - base::type::VerboseLevel m_level; - base::type::EnumType* m_pFlags; - std::unordered_map m_modules; -}; -} // namespace base -class LogMessage { - public: - LogMessage(Level level, const std::string& file, base::type::LineNumber line, const std::string& func, - base::type::VerboseLevel verboseLevel, Logger* logger) - : m_level(level), - m_file(file), - m_line(line), - m_func(func), - m_verboseLevel(verboseLevel), - m_logger(logger), - m_message(logger->stream().str()) { - } - inline Level - level(void) const { - return m_level; - } - inline const std::string& - file(void) const { - return m_file; - } - inline base::type::LineNumber - line(void) const { - return m_line; - } - inline const std::string& - func(void) const { - return m_func; - } - inline base::type::VerboseLevel - verboseLevel(void) const { - return m_verboseLevel; - } - inline Logger* - logger(void) const { - return m_logger; - } - inline const base::type::string_t& - message(void) const { - return m_message; - } - - private: - Level m_level; - std::string m_file; - base::type::LineNumber m_line; - std::string m_func; - base::type::VerboseLevel m_verboseLevel; - Logger* m_logger; - base::type::string_t m_message; -}; -namespace base { -#if ELPP_ASYNC_LOGGING -class AsyncLogItem { - public: - explicit AsyncLogItem(const LogMessage& logMessage, const LogDispatchData& data, - const base::type::string_t& logLine) - : m_logMessage(logMessage), m_dispatchData(data), m_logLine(logLine) { - } - virtual ~AsyncLogItem() { - } - inline LogMessage* - logMessage(void) { - return &m_logMessage; - } - inline LogDispatchData* - data(void) { - return &m_dispatchData; - } - inline base::type::string_t - logLine(void) { - return m_logLine; - } - - private: - LogMessage m_logMessage; - LogDispatchData m_dispatchData; - base::type::string_t m_logLine; -}; -class AsyncLogQueue : public base::threading::ThreadSafe { - public: - virtual ~AsyncLogQueue() { - ELPP_INTERNAL_INFO(6, "~AsyncLogQueue"); - } - - inline AsyncLogItem - next(void) { - base::threading::ScopedLock scopedLock(lock()); - AsyncLogItem result = m_queue.front(); - m_queue.pop(); - return result; - } - - inline void - push(const AsyncLogItem& item) { - base::threading::ScopedLock scopedLock(lock()); - m_queue.push(item); - } - inline void - pop(void) { - base::threading::ScopedLock scopedLock(lock()); - m_queue.pop(); - } - inline AsyncLogItem - front(void) { - base::threading::ScopedLock scopedLock(lock()); - return m_queue.front(); - } - inline bool - empty(void) { - base::threading::ScopedLock scopedLock(lock()); - return m_queue.empty(); - } - - private: - std::queue m_queue; -}; -class IWorker { - public: - virtual ~IWorker() { - } - virtual void - start() = 0; -}; -#endif // ELPP_ASYNC_LOGGING -/// @brief Easylogging++ management storage -class Storage : base::NoCopy, public base::threading::ThreadSafe { - public: -#if ELPP_ASYNC_LOGGING - Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker); -#else - explicit Storage(const LogBuilderPtr& defaultLogBuilder); -#endif // ELPP_ASYNC_LOGGING - - virtual ~Storage(void); - - inline bool - validateEveryNCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t occasion) { - return hitCounters()->validateEveryN(filename, lineNumber, occasion); - } - - inline bool - validateAfterNCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { - return hitCounters()->validateAfterN(filename, lineNumber, n); - } - - inline bool - validateNTimesCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { - return hitCounters()->validateNTimes(filename, lineNumber, n); - } - - inline base::RegisteredHitCounters* - hitCounters(void) const { - return m_registeredHitCounters; - } - - inline base::RegisteredLoggers* - registeredLoggers(void) const { - return m_registeredLoggers; - } - - inline base::VRegistry* - vRegistry(void) const { - return m_vRegistry; - } - -#if ELPP_ASYNC_LOGGING - inline base::AsyncLogQueue* - asyncLogQueue(void) const { - return m_asyncLogQueue; - } -#endif // ELPP_ASYNC_LOGGING - - inline const base::utils::CommandLineArgs* - commandLineArgs(void) const { - return &m_commandLineArgs; - } - - inline void - addFlag(LoggingFlag flag) { - base::utils::addFlag(flag, &m_flags); - } - - inline void - removeFlag(LoggingFlag flag) { - base::utils::removeFlag(flag, &m_flags); - } - - inline bool - hasFlag(LoggingFlag flag) const { - return base::utils::hasFlag(flag, m_flags); - } - - inline base::type::EnumType - flags(void) const { - return m_flags; - } - - inline void - setFlags(base::type::EnumType flags) { - m_flags = flags; - } - - inline void - setPreRollOutCallback(const PreRollOutCallback& callback) { - m_preRollOutCallback = callback; - } - - inline void - unsetPreRollOutCallback(void) { - m_preRollOutCallback = base::defaultPreRollOutCallback; - } - - inline PreRollOutCallback& - preRollOutCallback(void) { - return m_preRollOutCallback; - } - - bool - hasCustomFormatSpecifier(const char* formatSpecifier); - void - installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier); - bool - uninstallCustomFormatSpecifier(const char* formatSpecifier); - - const std::vector* - customFormatSpecifiers(void) const { - return &m_customFormatSpecifiers; - } - - base::threading::Mutex& - customFormatSpecifiersLock() { - return m_customFormatSpecifiersLock; - } - - inline void - setLoggingLevel(Level level) { - m_loggingLevel = level; - } - - template - inline bool - installLogDispatchCallback(const std::string& id) { - return base::utils::Utils::installCallback(id, &m_logDispatchCallbacks); - } - - template - inline void - uninstallLogDispatchCallback(const std::string& id) { - base::utils::Utils::uninstallCallback(id, &m_logDispatchCallbacks); - } - template - inline T* - logDispatchCallback(const std::string& id) { - return base::utils::Utils::callback(id, &m_logDispatchCallbacks); - } - -#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) - template - inline bool - installPerformanceTrackingCallback(const std::string& id) { - return base::utils::Utils::installCallback( - id, &m_performanceTrackingCallbacks); - } - - template - inline void - uninstallPerformanceTrackingCallback(const std::string& id) { - base::utils::Utils::uninstallCallback( - id, &m_performanceTrackingCallbacks); - } - - template - inline T* - performanceTrackingCallback(const std::string& id) { - return base::utils::Utils::callback( - id, &m_performanceTrackingCallbacks); - } -#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) - - /// @brief Sets thread name for current thread. Requires std::thread - inline void - setThreadName(const std::string& name) { - if (name.empty()) - return; - base::threading::ScopedLock scopedLock(m_threadNamesLock); - m_threadNames[base::threading::getCurrentThreadId()] = name; - } - - inline std::string - getThreadName(const std::string& threadId) { - base::threading::ScopedLock scopedLock(m_threadNamesLock); - std::unordered_map::const_iterator it = m_threadNames.find(threadId); - if (it == m_threadNames.end()) { - return threadId; - } - return it->second; - } - - private: - base::RegisteredHitCounters* m_registeredHitCounters; - base::RegisteredLoggers* m_registeredLoggers; - base::type::EnumType m_flags; - base::VRegistry* m_vRegistry; -#if ELPP_ASYNC_LOGGING - base::AsyncLogQueue* m_asyncLogQueue; - base::IWorker* m_asyncDispatchWorker; -#endif // ELPP_ASYNC_LOGGING - base::utils::CommandLineArgs m_commandLineArgs; - PreRollOutCallback m_preRollOutCallback; - std::unordered_map m_logDispatchCallbacks; - std::unordered_map m_performanceTrackingCallbacks; - std::unordered_map m_threadNames; - std::vector m_customFormatSpecifiers; - base::threading::Mutex m_customFormatSpecifiersLock; - base::threading::Mutex m_threadNamesLock; - Level m_loggingLevel; - - friend class el::Helpers; - friend class el::base::DefaultLogDispatchCallback; - friend class el::LogBuilder; - friend class el::base::MessageBuilder; - friend class el::base::Writer; - friend class el::base::PerformanceTracker; - friend class el::base::LogDispatcher; - - void - setApplicationArguments(int argc, char** argv); - - inline void - setApplicationArguments(int argc, const char** argv) { - setApplicationArguments(argc, const_cast(argv)); - } -}; -extern ELPP_EXPORT base::type::StoragePointer elStorage; -#define ELPP el::base::elStorage -class DefaultLogDispatchCallback : public LogDispatchCallback { - protected: - void - handle(const LogDispatchData* data); - - private: - const LogDispatchData* m_data; - void - dispatch(base::type::string_t&& logLine); -}; -#if ELPP_ASYNC_LOGGING -class AsyncLogDispatchCallback : public LogDispatchCallback { - protected: - void - handle(const LogDispatchData* data); -}; -class AsyncDispatchWorker : public base::IWorker, public base::threading::ThreadSafe { - public: - AsyncDispatchWorker(); - virtual ~AsyncDispatchWorker(); - - bool - clean(void); - void - emptyQueue(void); - virtual void - start(void); - void - handle(AsyncLogItem* logItem); - void - run(void); - - void - setContinueRunning(bool value) { - base::threading::ScopedLock scopedLock(m_continueRunningLock); - m_continueRunning = value; - } - - bool - continueRunning(void) const { - return m_continueRunning; - } - - private: - std::condition_variable cv; - bool m_continueRunning; - base::threading::Mutex m_continueRunningLock; -}; -#endif // ELPP_ASYNC_LOGGING -} // namespace base -namespace base { -class DefaultLogBuilder : public LogBuilder { - public: - base::type::string_t - build(const LogMessage* logMessage, bool appendNewLine) const; -}; -/// @brief Dispatches log messages -class LogDispatcher : base::NoCopy { - public: - LogDispatcher(bool proceed, LogMessage* logMessage, base::DispatchAction dispatchAction) - : m_proceed(proceed), m_logMessage(logMessage), m_dispatchAction(std::move(dispatchAction)) { - } - - void - dispatch(void); - - private: - bool m_proceed; - LogMessage* m_logMessage; - base::DispatchAction m_dispatchAction; -}; -#if defined(ELPP_STL_LOGGING) -/// @brief Workarounds to write some STL logs -/// -/// @detail There is workaround needed to loop through some stl containers. In order to do that, we need iterable -/// containers of same type and provide iterator interface and pass it on to writeIterator(). Remember, this is passed -/// by value in constructor so that we dont change original containers. This operation is as expensive as -/// Big-O(std::min(class_.size(), base::consts::kMaxLogPerContainer)) -namespace workarounds { -/// @brief Abstract IterableContainer template that provides interface for iterable classes of type T -template -class IterableContainer { - public: - typedef typename Container::iterator iterator; - typedef typename Container::const_iterator const_iterator; - IterableContainer(void) { - } - virtual ~IterableContainer(void) { - } - iterator - begin(void) { - return getContainer().begin(); - } - iterator - end(void) { - return getContainer().end(); - } - - private: - virtual Container& - getContainer(void) = 0; -}; -/// @brief Implements IterableContainer and provides iterable std::priority_queue class -template , - typename Comparator = std::less> -class IterablePriorityQueue : public IterableContainer, - public std::priority_queue { - public: - IterablePriorityQueue(std::priority_queue queue_) { - std::size_t count_ = 0; - while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { - this->push(queue_.top()); - queue_.pop(); - } - } - - private: - inline Container& - getContainer(void) { - return this->c; - } -}; -/// @brief Implements IterableContainer and provides iterable std::queue class -template > -class IterableQueue : public IterableContainer, public std::queue { - public: - IterableQueue(std::queue queue_) { - std::size_t count_ = 0; - while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { - this->push(queue_.front()); - queue_.pop(); - } - } - - private: - inline Container& - getContainer(void) { - return this->c; - } -}; -/// @brief Implements IterableContainer and provides iterable std::stack class -template > -class IterableStack : public IterableContainer, public std::stack { - public: - IterableStack(std::stack stack_) { - std::size_t count_ = 0; - while (++count_ < base::consts::kMaxLogPerContainer && !stack_.empty()) { - this->push(stack_.top()); - stack_.pop(); - } - } - - private: - inline Container& - getContainer(void) { - return this->c; - } -}; -} // namespace workarounds -#endif // defined(ELPP_STL_LOGGING) -// Log message builder -class MessageBuilder { - public: - MessageBuilder(void) : m_logger(nullptr), m_containerLogSeperator(ELPP_LITERAL("")) { - } - void - initialize(Logger* logger); - -#define ELPP_SIMPLE_LOG(LOG_TYPE) \ - MessageBuilder& operator<<(LOG_TYPE msg) { \ - m_logger->stream() << msg; \ - if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { \ - m_logger->stream() << " "; \ - } \ - return *this; \ - } - - inline MessageBuilder& - operator<<(const std::string& msg) { - return operator<<(msg.c_str()); - } - ELPP_SIMPLE_LOG(char) - ELPP_SIMPLE_LOG(bool) - ELPP_SIMPLE_LOG(signed short) - ELPP_SIMPLE_LOG(unsigned short) - ELPP_SIMPLE_LOG(signed int) - ELPP_SIMPLE_LOG(unsigned int) - ELPP_SIMPLE_LOG(signed long) - ELPP_SIMPLE_LOG(unsigned long) - ELPP_SIMPLE_LOG(float) - ELPP_SIMPLE_LOG(double) - ELPP_SIMPLE_LOG(char*) - ELPP_SIMPLE_LOG(const char*) - ELPP_SIMPLE_LOG(const void*) - ELPP_SIMPLE_LOG(long double) - inline MessageBuilder& - operator<<(const std::wstring& msg) { - return operator<<(msg.c_str()); - } - MessageBuilder& - operator<<(const wchar_t* msg); - // ostream manipulators - inline MessageBuilder& - operator<<(std::ostream& (*OStreamMani)(std::ostream&)) { - m_logger->stream() << OStreamMani; - return *this; - } -#define ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(temp) \ - template \ - inline MessageBuilder& operator<<(const temp& template_inst) { \ - return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ - } -#define ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(temp) \ - template \ - inline MessageBuilder& operator<<(const temp& template_inst) { \ - return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ - } -#define ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(temp) \ - template \ - inline MessageBuilder& operator<<(const temp& template_inst) { \ - return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ - } -#define ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(temp) \ - template \ - inline MessageBuilder& operator<<(const temp& template_inst) { \ - return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ - } -#define ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(temp) \ - template \ - inline MessageBuilder& operator<<(const temp& template_inst) { \ - return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ - } - -#if defined(ELPP_STL_LOGGING) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::vector) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::list) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::deque) - ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::set) - ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::multiset) - ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::map) - ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::multimap) - template - inline MessageBuilder& - operator<<(const std::queue& queue_) { - base::workarounds::IterableQueue iterableQueue_ = - static_cast>(queue_); - return writeIterator(iterableQueue_.begin(), iterableQueue_.end(), iterableQueue_.size()); - } - template - inline MessageBuilder& - operator<<(const std::stack& stack_) { - base::workarounds::IterableStack iterableStack_ = - static_cast>(stack_); - return writeIterator(iterableStack_.begin(), iterableStack_.end(), iterableStack_.size()); - } - template - inline MessageBuilder& - operator<<(const std::priority_queue& priorityQueue_) { - base::workarounds::IterablePriorityQueue iterablePriorityQueue_ = - static_cast>(priorityQueue_); - return writeIterator(iterablePriorityQueue_.begin(), iterablePriorityQueue_.end(), - iterablePriorityQueue_.size()); - } - template - MessageBuilder& - operator<<(const std::pair& pair_) { - m_logger->stream() << ELPP_LITERAL("("); - operator<<(static_cast(pair_.first)); - m_logger->stream() << ELPP_LITERAL(", "); - operator<<(static_cast(pair_.second)); - m_logger->stream() << ELPP_LITERAL(")"); - return *this; - } - template - MessageBuilder& - operator<<(const std::bitset& bitset_) { - m_logger->stream() << ELPP_LITERAL("["); - operator<<(bitset_.to_string()); - m_logger->stream() << ELPP_LITERAL("]"); - return *this; - } -#if defined(ELPP_LOG_STD_ARRAY) - template - inline MessageBuilder& - operator<<(const std::array& array) { - return writeIterator(array.begin(), array.end(), array.size()); - } -#endif // defined(ELPP_LOG_STD_ARRAY) -#if defined(ELPP_LOG_UNORDERED_MAP) - ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_map) - ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_multimap) -#endif // defined(ELPP_LOG_UNORDERED_MAP) -#if defined(ELPP_LOG_UNORDERED_SET) - ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_set) - ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_multiset) -#endif // defined(ELPP_LOG_UNORDERED_SET) -#endif // defined(ELPP_STL_LOGGING) -#if defined(ELPP_QT_LOGGING) - inline MessageBuilder& - operator<<(const QString& msg) { -#if defined(ELPP_UNICODE) - m_logger->stream() << msg.toStdWString(); -#else - m_logger->stream() << msg.toStdString(); -#endif // defined(ELPP_UNICODE) - return *this; - } - inline MessageBuilder& - operator<<(const QByteArray& msg) { - return operator<<(QString(msg)); - } - inline MessageBuilder& - operator<<(const QStringRef& msg) { - return operator<<(msg.toString()); - } - inline MessageBuilder& - operator<<(qint64 msg) { -#if defined(ELPP_UNICODE) - m_logger->stream() << QString::number(msg).toStdWString(); -#else - m_logger->stream() << QString::number(msg).toStdString(); -#endif // defined(ELPP_UNICODE) - return *this; - } - inline MessageBuilder& - operator<<(quint64 msg) { -#if defined(ELPP_UNICODE) - m_logger->stream() << QString::number(msg).toStdWString(); -#else - m_logger->stream() << QString::number(msg).toStdString(); -#endif // defined(ELPP_UNICODE) - return *this; - } - inline MessageBuilder& - operator<<(QChar msg) { - m_logger->stream() << msg.toLatin1(); - return *this; - } - inline MessageBuilder& - operator<<(const QLatin1String& msg) { - m_logger->stream() << msg.latin1(); - return *this; - } - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QList) - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QVector) - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QQueue) - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QSet) - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QLinkedList) - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QStack) - template - MessageBuilder& - operator<<(const QPair& pair_) { - m_logger->stream() << ELPP_LITERAL("("); - operator<<(static_cast(pair_.first)); - m_logger->stream() << ELPP_LITERAL(", "); - operator<<(static_cast(pair_.second)); - m_logger->stream() << ELPP_LITERAL(")"); - return *this; - } - template - MessageBuilder& - operator<<(const QMap& map_) { - m_logger->stream() << ELPP_LITERAL("["); - QList keys = map_.keys(); - typename QList::const_iterator begin = keys.begin(); - typename QList::const_iterator end = keys.end(); - int max_ = static_cast(base::consts::kMaxLogPerContainer); // to prevent warning - for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { - m_logger->stream() << ELPP_LITERAL("("); - operator<<(static_cast(*begin)); - m_logger->stream() << ELPP_LITERAL(", "); - operator<<(static_cast(map_.value(*begin))); - m_logger->stream() << ELPP_LITERAL(")"); - m_logger->stream() << ((index_ < keys.size() - 1) ? m_containerLogSeperator : ELPP_LITERAL("")); - } - if (begin != end) { - m_logger->stream() << ELPP_LITERAL("..."); - } - m_logger->stream() << ELPP_LITERAL("]"); - return *this; - } - template - inline MessageBuilder& - operator<<(const QMultiMap& map_) { - operator<<(static_cast>(map_)); - return *this; - } - template - MessageBuilder& - operator<<(const QHash& hash_) { - m_logger->stream() << ELPP_LITERAL("["); - QList keys = hash_.keys(); - typename QList::const_iterator begin = keys.begin(); - typename QList::const_iterator end = keys.end(); - int max_ = static_cast(base::consts::kMaxLogPerContainer); // prevent type warning - for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { - m_logger->stream() << ELPP_LITERAL("("); - operator<<(static_cast(*begin)); - m_logger->stream() << ELPP_LITERAL(", "); - operator<<(static_cast(hash_.value(*begin))); - m_logger->stream() << ELPP_LITERAL(")"); - m_logger->stream() << ((index_ < keys.size() - 1) ? m_containerLogSeperator : ELPP_LITERAL("")); - } - if (begin != end) { - m_logger->stream() << ELPP_LITERAL("..."); - } - m_logger->stream() << ELPP_LITERAL("]"); - return *this; - } - template - inline MessageBuilder& - operator<<(const QMultiHash& multiHash_) { - operator<<(static_cast>(multiHash_)); - return *this; - } -#endif // defined(ELPP_QT_LOGGING) -#if defined(ELPP_BOOST_LOGGING) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::vector) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::stable_vector) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::list) - ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::deque) - ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::map) - ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::flat_map) - ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::set) - ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::flat_set) -#endif // defined(ELPP_BOOST_LOGGING) - - /// @brief Macro used internally that can be used externally to make containers easylogging++ friendly - /// - /// @detail This macro expands to write an ostream& operator<< for container. This container is expected to - /// have begin() and end() methods that return respective iterators - /// @param ContainerType Type of container e.g, MyList from WX_DECLARE_LIST(int, MyList); in wxwidgets - /// @param SizeMethod Method used to get size of container. - /// @param ElementInstance Instance of element to be fed out. Insance name is "elem". See WXELPP_ENABLED macro - /// for an example usage -#define MAKE_CONTAINERELPP_FRIENDLY(ContainerType, SizeMethod, ElementInstance) \ - el::base::type::ostream_t& operator<<(el::base::type::ostream_t& ss, const ContainerType& container) { \ - const el::base::type::char_t* sep = \ - ELPP->hasFlag(el::LoggingFlag::NewLineForContainer) ? ELPP_LITERAL("\n ") : ELPP_LITERAL(", "); \ - ContainerType::const_iterator elem = container.begin(); \ - ContainerType::const_iterator endElem = container.end(); \ - std::size_t size_ = container.SizeMethod; \ - ss << ELPP_LITERAL("["); \ - for (std::size_t i = 0; elem != endElem && i < el::base::consts::kMaxLogPerContainer; ++i, ++elem) { \ - ss << ElementInstance; \ - ss << ((i < size_ - 1) ? sep : ELPP_LITERAL("")); \ - } \ - if (elem != endElem) { \ - ss << ELPP_LITERAL("..."); \ - } \ - ss << ELPP_LITERAL("]"); \ - return ss; \ - } -#if defined(ELPP_WXWIDGETS_LOGGING) - ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(wxVector) -#define ELPP_WX_PTR_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), *(*elem)) -#define ELPP_WX_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), (*elem)) -#define ELPP_WX_HASH_MAP_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), \ -ELPP_LITERAL("(") << elem->first << ELPP_LITERAL(", ") << elem->second << ELPP_LITERAL(")") -#else -#define ELPP_WX_PTR_ENABLED(ContainerType) -#define ELPP_WX_ENABLED(ContainerType) -#define ELPP_WX_HASH_MAP_ENABLED(ContainerType) -#endif // defined(ELPP_WXWIDGETS_LOGGING) - // Other classes - template - ELPP_SIMPLE_LOG(const Class&) -#undef ELPP_SIMPLE_LOG -#undef ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG -#undef ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG -#undef ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG -#undef ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG -#undef ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG - private : Logger* m_logger; - const base::type::char_t* m_containerLogSeperator; - - template - MessageBuilder& - writeIterator(Iterator begin_, Iterator end_, std::size_t size_) { - m_logger->stream() << ELPP_LITERAL("["); - for (std::size_t i = 0; begin_ != end_ && i < base::consts::kMaxLogPerContainer; ++i, ++begin_) { - operator<<(*begin_); - m_logger->stream() << ((i < size_ - 1) ? m_containerLogSeperator : ELPP_LITERAL("")); - } - if (begin_ != end_) { - m_logger->stream() << ELPP_LITERAL("..."); - } - m_logger->stream() << ELPP_LITERAL("]"); - if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { - m_logger->stream() << " "; - } - return *this; - } -}; -/// @brief Writes nothing - Used when certain log is disabled -class NullWriter : base::NoCopy { - public: - NullWriter(void) { - } - - // Null manipulator - inline NullWriter& - operator<<(std::ostream& (*)(std::ostream&)) { - return *this; - } - - template - inline NullWriter& - operator<<(const T&) { - return *this; - } - - inline operator bool() { - return true; - } -}; -/// @brief Main entry point of each logging -class Writer : base::NoCopy { - public: - Writer(Level level, const char* file, base::type::LineNumber line, const char* func, - base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, - base::type::VerboseLevel verboseLevel = 0) - : m_msg(nullptr), - m_level(level), - m_file(file), - m_line(line), - m_func(func), - m_verboseLevel(verboseLevel), - m_logger(nullptr), - m_proceed(false), - m_dispatchAction(dispatchAction) { - } - - Writer(LogMessage* msg, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog) - : m_msg(msg), - m_level(msg != nullptr ? msg->level() : Level::Unknown), - m_line(0), - m_logger(nullptr), - m_proceed(false), - m_dispatchAction(dispatchAction) { - } - - virtual ~Writer(void) { - processDispatch(); - } - - template - inline Writer& - operator<<(const T& log) { -#if ELPP_LOGGING_ENABLED - if (m_proceed) { - m_messageBuilder << log; - } -#endif // ELPP_LOGGING_ENABLED - return *this; - } - - inline Writer& - operator<<(std::ostream& (*log)(std::ostream&)) { -#if ELPP_LOGGING_ENABLED - if (m_proceed) { - m_messageBuilder << log; - } -#endif // ELPP_LOGGING_ENABLED - return *this; - } - - inline operator bool() { - return true; - } - - Writer& - construct(Logger* logger, bool needLock = true); - Writer& - construct(int count, const char* loggerIds, ...); - - protected: - LogMessage* m_msg; - Level m_level; - const char* m_file; - const base::type::LineNumber m_line; - const char* m_func; - base::type::VerboseLevel m_verboseLevel; - Logger* m_logger; - bool m_proceed; - base::MessageBuilder m_messageBuilder; - base::DispatchAction m_dispatchAction; - std::vector m_loggerIds; - friend class el::Helpers; - - void - initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true); - void - processDispatch(); - void - triggerDispatch(void); -}; -class PErrorWriter : public base::Writer { - public: - PErrorWriter(Level level, const char* file, base::type::LineNumber line, const char* func, - base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, - base::type::VerboseLevel verboseLevel = 0) - : base::Writer(level, file, line, func, dispatchAction, verboseLevel) { - } - - virtual ~PErrorWriter(void); -}; -} // namespace base -// Logging from Logger class. Why this is here? Because we have Storage and Writer class available -#if ELPP_VARIADIC_TEMPLATES_SUPPORTED -template -void -Logger::log_(Level level, int vlevel, const char* s, const T& value, const Args&... args) { - base::MessageBuilder b; - b.initialize(this); - while (*s) { - if (*s == base::consts::kFormatSpecifierChar) { - if (*(s + 1) == base::consts::kFormatSpecifierChar) { - ++s; - } else { - if (*(s + 1) == base::consts::kFormatSpecifierCharValue) { - ++s; - b << value; - log_(level, vlevel, ++s, args...); - return; - } - } - } - b << *s++; - } - ELPP_INTERNAL_ERROR("Too many arguments provided. Unable to handle. Please provide more format specifiers", false); -} -template -void -Logger::log_(Level level, int vlevel, const T& log) { - if (level == Level::Verbose) { - if (ELPP->vRegistry()->allowed(vlevel, __FILE__)) { - base::Writer(Level::Verbose, "FILE", 0, "FUNCTION", base::DispatchAction::NormalLog, vlevel) - .construct(this, false) - << log; - } else { - stream().str(ELPP_LITERAL("")); - releaseLock(); - } - } else { - base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log; - } -} -template -inline void -Logger::log(Level level, const char* s, const T& value, const Args&... args) { - acquireLock(); // released in Writer! - log_(level, 0, s, value, args...); -} -template -inline void -Logger::log(Level level, const T& log) { - acquireLock(); // released in Writer! - log_(level, 0, log); -} -#if ELPP_VERBOSE_LOG -template -inline void -Logger::verbose(int vlevel, const char* s, const T& value, const Args&... args) { - acquireLock(); // released in Writer! - log_(el::Level::Verbose, vlevel, s, value, args...); -} -template -inline void -Logger::verbose(int vlevel, const T& log) { - acquireLock(); // released in Writer! - log_(el::Level::Verbose, vlevel, log); -} -#else -template -inline void -Logger::verbose(int, const char*, const T&, const Args&...) { - return; -} -template -inline void -Logger::verbose(int, const T&) { - return; -} -#endif // ELPP_VERBOSE_LOG -#define LOGGER_LEVEL_WRITERS(FUNCTION_NAME, LOG_LEVEL) \ - template \ - inline void Logger::FUNCTION_NAME(const char* s, const T& value, const Args&... args) { \ - log(LOG_LEVEL, s, value, args...); \ - } \ - template \ - inline void Logger::FUNCTION_NAME(const T& value) { \ - log(LOG_LEVEL, value); \ - } -#define LOGGER_LEVEL_WRITERS_DISABLED(FUNCTION_NAME, LOG_LEVEL) \ - template \ - inline void Logger::FUNCTION_NAME(const char*, const T&, const Args&...) { \ - return; \ - } \ - template \ - inline void Logger::FUNCTION_NAME(const T&) { \ - return; \ - } - -#if ELPP_INFO_LOG -LOGGER_LEVEL_WRITERS(info, Level::Info) -#else -LOGGER_LEVEL_WRITERS_DISABLED(info, Level::Info) -#endif // ELPP_INFO_LOG -#if ELPP_DEBUG_LOG -LOGGER_LEVEL_WRITERS(debug, Level::Debug) -#else -LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug) -#endif // ELPP_DEBUG_LOG -#if ELPP_WARNING_LOG -LOGGER_LEVEL_WRITERS(warn, Level::Warning) -#else -LOGGER_LEVEL_WRITERS_DISABLED(warn, Level::Warning) -#endif // ELPP_WARNING_LOG -#if ELPP_ERROR_LOG -LOGGER_LEVEL_WRITERS(error, Level::Error) -#else -LOGGER_LEVEL_WRITERS_DISABLED(error, Level::Error) -#endif // ELPP_ERROR_LOG -#if ELPP_FATAL_LOG -LOGGER_LEVEL_WRITERS(fatal, Level::Fatal) -#else -LOGGER_LEVEL_WRITERS_DISABLED(fatal, Level::Fatal) -#endif // ELPP_FATAL_LOG -#if ELPP_TRACE_LOG -LOGGER_LEVEL_WRITERS(trace, Level::Trace) -#else -LOGGER_LEVEL_WRITERS_DISABLED(trace, Level::Trace) -#endif // ELPP_TRACE_LOG -#undef LOGGER_LEVEL_WRITERS -#undef LOGGER_LEVEL_WRITERS_DISABLED -#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED -#if ELPP_COMPILER_MSVC -#define ELPP_VARIADIC_FUNC_MSVC(variadicFunction, variadicArgs) variadicFunction variadicArgs -#define ELPP_VARIADIC_FUNC_MSVC_RUN(variadicFunction, ...) ELPP_VARIADIC_FUNC_MSVC(variadicFunction, (__VA_ARGS__)) -#define el_getVALength(...) \ - ELPP_VARIADIC_FUNC_MSVC_RUN(el_resolveVALength, 0, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) -#else -#if ELPP_COMPILER_CLANG -#define el_getVALength(...) el_resolveVALength(0, __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) -#else -#define el_getVALength(...) el_resolveVALength(0, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) -#endif // ELPP_COMPILER_CLANG -#endif // ELPP_COMPILER_MSVC -#define el_resolveVALength(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N -#define ELPP_WRITE_LOG(writer, level, dispatchAction, ...) \ - writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) -#define ELPP_WRITE_LOG_IF(writer, condition, level, dispatchAction, ...) \ - if (condition) \ - writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) -#define ELPP_WRITE_LOG_EVERY_N(writer, occasion, level, dispatchAction, ...) \ - ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion) && \ - writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction) \ - .construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) -#define ELPP_WRITE_LOG_AFTER_N(writer, n, level, dispatchAction, ...) \ - ELPP->validateAfterNCounter(__FILE__, __LINE__, n) && writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction) \ - .construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) -#define ELPP_WRITE_LOG_N_TIMES(writer, n, level, dispatchAction, ...) \ - ELPP->validateNTimesCounter(__FILE__, __LINE__, n) && writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction) \ - .construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) -#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) -class PerformanceTrackingData { - public: - enum class DataType : base::type::EnumType { Checkpoint = 1, Complete = 2 }; - // Do not use constructor, will run into multiple definition error, use init(PerformanceTracker*) - explicit PerformanceTrackingData(DataType dataType) - : m_performanceTracker(nullptr), - m_dataType(dataType), - m_firstCheckpoint(false), - m_file(""), - m_line(0), - m_func("") { - } - inline const std::string* - blockName(void) const; - inline const struct timeval* - startTime(void) const; - inline const struct timeval* - endTime(void) const; - inline const struct timeval* - lastCheckpointTime(void) const; - inline const base::PerformanceTracker* - performanceTracker(void) const { - return m_performanceTracker; - } - inline PerformanceTrackingData::DataType - dataType(void) const { - return m_dataType; - } - inline bool - firstCheckpoint(void) const { - return m_firstCheckpoint; - } - inline std::string - checkpointId(void) const { - return m_checkpointId; - } - inline const char* - file(void) const { - return m_file; - } - inline base::type::LineNumber - line(void) const { - return m_line; - } - inline const char* - func(void) const { - return m_func; - } - inline const base::type::string_t* - formattedTimeTaken() const { - return &m_formattedTimeTaken; - } - inline const std::string& - loggerId(void) const; - - private: - base::PerformanceTracker* m_performanceTracker; - base::type::string_t m_formattedTimeTaken; - PerformanceTrackingData::DataType m_dataType; - bool m_firstCheckpoint; - std::string m_checkpointId; - const char* m_file; - base::type::LineNumber m_line; - const char* m_func; - inline void - init(base::PerformanceTracker* performanceTracker, bool firstCheckpoint = false) { - m_performanceTracker = performanceTracker; - m_firstCheckpoint = firstCheckpoint; - } - - friend class el::base::PerformanceTracker; -}; -namespace base { -/// @brief Represents performanceTracker block of code that conditionally adds performance status to log -/// either when goes outside the scope of when checkpoint() is called -class PerformanceTracker : public base::threading::ThreadSafe, public Loggable { - public: - PerformanceTracker(const std::string& blockName, - base::TimestampUnit timestampUnit = base::TimestampUnit::Millisecond, - const std::string& loggerId = std::string(el::base::consts::kPerformanceLoggerId), - bool scopedLog = true, Level level = base::consts::kPerformanceTrackerDefaultLevel); - /// @brief Copy constructor - PerformanceTracker(const PerformanceTracker& t) - : m_blockName(t.m_blockName), - m_timestampUnit(t.m_timestampUnit), - m_loggerId(t.m_loggerId), - m_scopedLog(t.m_scopedLog), - m_level(t.m_level), - m_hasChecked(t.m_hasChecked), - m_lastCheckpointId(t.m_lastCheckpointId), - m_enabled(t.m_enabled), - m_startTime(t.m_startTime), - m_endTime(t.m_endTime), - m_lastCheckpointTime(t.m_lastCheckpointTime) { - } - virtual ~PerformanceTracker(void); - /// @brief A checkpoint for current performanceTracker block. - void - checkpoint(const std::string& id = std::string(), const char* file = __FILE__, - base::type::LineNumber line = __LINE__, const char* func = ""); - inline Level - level(void) const { - return m_level; - } - - private: - std::string m_blockName; - base::TimestampUnit m_timestampUnit; - std::string m_loggerId; - bool m_scopedLog; - Level m_level; - bool m_hasChecked; - std::string m_lastCheckpointId; - bool m_enabled; - struct timeval m_startTime, m_endTime, m_lastCheckpointTime; - - PerformanceTracker(void); - - friend class el::PerformanceTrackingData; - friend class base::DefaultPerformanceTrackingCallback; - - const inline base::type::string_t - getFormattedTimeTaken() const { - return getFormattedTimeTaken(m_startTime); - } - - const base::type::string_t - getFormattedTimeTaken(struct timeval startTime) const; - - virtual inline void - log(el::base::type::ostream_t& os) const { - os << getFormattedTimeTaken(); - } -}; -class DefaultPerformanceTrackingCallback : public PerformanceTrackingCallback { - protected: - void - handle(const PerformanceTrackingData* data) { - m_data = data; - base::type::stringstream_t ss; - if (m_data->dataType() == PerformanceTrackingData::DataType::Complete) { - ss << ELPP_LITERAL("Executed [") << m_data->blockName()->c_str() << ELPP_LITERAL("] in [") - << *m_data->formattedTimeTaken() << ELPP_LITERAL("]"); - } else { - ss << ELPP_LITERAL("Performance checkpoint"); - if (!m_data->checkpointId().empty()) { - ss << ELPP_LITERAL(" [") << m_data->checkpointId().c_str() << ELPP_LITERAL("]"); - } - ss << ELPP_LITERAL(" for block [") << m_data->blockName()->c_str() << ELPP_LITERAL("] : [") - << *m_data->performanceTracker(); - if (!ELPP->hasFlag(LoggingFlag::DisablePerformanceTrackingCheckpointComparison) && - m_data->performanceTracker()->m_hasChecked) { - ss << ELPP_LITERAL(" ([") << *m_data->formattedTimeTaken() << ELPP_LITERAL("] from "); - if (m_data->performanceTracker()->m_lastCheckpointId.empty()) { - ss << ELPP_LITERAL("last checkpoint"); - } else { - ss << ELPP_LITERAL("checkpoint '") << m_data->performanceTracker()->m_lastCheckpointId.c_str() - << ELPP_LITERAL("'"); - } - ss << ELPP_LITERAL(")]"); - } else { - ss << ELPP_LITERAL("]"); - } - } - el::base::Writer(m_data->performanceTracker()->level(), m_data->file(), m_data->line(), m_data->func()) - .construct(1, m_data->loggerId().c_str()) - << ss.str(); - } - - private: - const PerformanceTrackingData* m_data; -}; -} // namespace base -inline const std::string* -PerformanceTrackingData::blockName() const { - return const_cast(&m_performanceTracker->m_blockName); -} -inline const struct timeval* -PerformanceTrackingData::startTime() const { - return const_cast(&m_performanceTracker->m_startTime); -} -inline const struct timeval* -PerformanceTrackingData::endTime() const { - return const_cast(&m_performanceTracker->m_endTime); -} -inline const struct timeval* -PerformanceTrackingData::lastCheckpointTime() const { - return const_cast(&m_performanceTracker->m_lastCheckpointTime); -} -inline const std::string& -PerformanceTrackingData::loggerId(void) const { - return m_performanceTracker->m_loggerId; -} -#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) -namespace base { -/// @brief Contains some internal debugging tools like crash handler and stack tracer -namespace debug { -#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) -class StackTrace : base::NoCopy { - public: - static const unsigned int kMaxStack = 64; - static const unsigned int kStackStart = 2; // We want to skip c'tor and StackTrace::generateNew() - class StackTraceEntry { - public: - StackTraceEntry(std::size_t index, const std::string& loc, const std::string& demang, const std::string& hex, - const std::string& addr); - StackTraceEntry(std::size_t index, const std::string& loc) : m_index(index), m_location(loc) { - } - std::size_t m_index; - std::string m_location; - std::string m_demangled; - std::string m_hex; - std::string m_addr; - friend std::ostream& - operator<<(std::ostream& ss, const StackTraceEntry& si); - - private: - StackTraceEntry(void); - }; - - StackTrace(void) { - generateNew(); - } - - virtual ~StackTrace(void) { - } - - inline std::vector& - getLatestStack(void) { - return m_stack; - } - - friend std::ostream& - operator<<(std::ostream& os, const StackTrace& st); - - private: - std::vector m_stack; - - void - generateNew(void); -}; -/// @brief Handles unexpected crashes -class CrashHandler : base::NoCopy { - public: - typedef void (*Handler)(int); - - explicit CrashHandler(bool useDefault); - explicit CrashHandler(const Handler& cHandler) { - setHandler(cHandler); - } - void - setHandler(const Handler& cHandler); - - private: - Handler m_handler; -}; -#else -class CrashHandler { - public: - explicit CrashHandler(bool) { - } -}; -#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) -} // namespace debug -} // namespace base -extern base::debug::CrashHandler elCrashHandler; -#define MAKE_LOGGABLE(ClassType, ClassInstance, OutputStreamInstance) \ - el::base::type::ostream_t& operator<<(el::base::type::ostream_t& OutputStreamInstance, \ - const ClassType& ClassInstance) -/// @brief Initializes syslog with process ID, options and facility. calls closelog() on d'tor -class SysLogInitializer { - public: - SysLogInitializer(const char* processIdent, int options = 0, int facility = 0) { -#if defined(ELPP_SYSLOG) - openlog(processIdent, options, facility); -#else - ELPP_UNUSED(processIdent); - ELPP_UNUSED(options); - ELPP_UNUSED(facility); -#endif // defined(ELPP_SYSLOG) - } - virtual ~SysLogInitializer(void) { -#if defined(ELPP_SYSLOG) - closelog(); -#endif // defined(ELPP_SYSLOG) - } -}; -#define ELPP_INITIALIZE_SYSLOG(id, opt, fac) el::SysLogInitializer elSyslogInit(id, opt, fac) -/// @brief Static helpers for developers -class Helpers : base::StaticClass { - public: - /// @brief Shares logging repository (base::Storage) - static inline void - setStorage(base::type::StoragePointer storage) { - ELPP = storage; - } - /// @return Main storage repository - static inline base::type::StoragePointer - storage() { - return ELPP; - } - /// @brief Sets application arguments and figures out whats active for logging and whats not. - static inline void - setArgs(int argc, char** argv) { - ELPP->setApplicationArguments(argc, argv); - } - /// @copydoc setArgs(int argc, char** argv) - static inline void - setArgs(int argc, const char** argv) { - ELPP->setApplicationArguments(argc, const_cast(argv)); - } - /// @brief Sets thread name for current thread. Requires std::thread - static inline void - setThreadName(const std::string& name) { - ELPP->setThreadName(name); - } - static inline std::string - getThreadName() { - return ELPP->getThreadName(base::threading::getCurrentThreadId()); - } -#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) - /// @brief Overrides default crash handler and installs custom handler. - /// @param crashHandler A functor with no return type that takes single int argument. - /// Handler is a typedef with specification: void (*Handler)(int) - static inline void - setCrashHandler(const el::base::debug::CrashHandler::Handler& crashHandler) { - el::elCrashHandler.setHandler(crashHandler); - } - /// @brief Abort due to crash with signal in parameter - /// @param sig Crash signal - static void - crashAbort(int sig, const char* sourceFile = "", unsigned int long line = 0); - /// @brief Logs reason of crash as per sig - /// @param sig Crash signal - /// @param stackTraceIfAvailable Includes stack trace if available - /// @param level Logging level - /// @param logger Logger to use for logging - static void - logCrashReason(int sig, bool stackTraceIfAvailable = false, Level level = Level::Fatal, - const char* logger = base::consts::kDefaultLoggerId); -#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) - /// @brief Installs pre rollout callback, this callback is triggered when log file is about to be rolled out - /// (can be useful for backing up) - static inline void - installPreRollOutCallback(const PreRollOutCallback& callback) { - ELPP->setPreRollOutCallback(callback); - } - /// @brief Uninstalls pre rollout callback - static inline void - uninstallPreRollOutCallback(void) { - ELPP->unsetPreRollOutCallback(); - } - /// @brief Installs post log dispatch callback, this callback is triggered when log is dispatched - template - static inline bool - installLogDispatchCallback(const std::string& id) { - return ELPP->installLogDispatchCallback(id); - } - /// @brief Uninstalls log dispatch callback - template - static inline void - uninstallLogDispatchCallback(const std::string& id) { - ELPP->uninstallLogDispatchCallback(id); - } - template - static inline T* - logDispatchCallback(const std::string& id) { - return ELPP->logDispatchCallback(id); - } -#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) - /// @brief Installs post performance tracking callback, this callback is triggered when performance tracking is - /// finished - template - static inline bool - installPerformanceTrackingCallback(const std::string& id) { - return ELPP->installPerformanceTrackingCallback(id); - } - /// @brief Uninstalls post performance tracking handler - template - static inline void - uninstallPerformanceTrackingCallback(const std::string& id) { - ELPP->uninstallPerformanceTrackingCallback(id); - } - template - static inline T* - performanceTrackingCallback(const std::string& id) { - return ELPP->performanceTrackingCallback(id); - } -#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) - /// @brief Converts template to std::string - useful for loggable classes to log containers within - /// log(std::ostream&) const - template - static std::string - convertTemplateToStdString(const T& templ) { - el::Logger* logger = ELPP->registeredLoggers()->get(el::base::consts::kDefaultLoggerId); - if (logger == nullptr) { - return std::string(); - } - base::MessageBuilder b; - b.initialize(logger); - logger->acquireLock(); - b << templ; -#if defined(ELPP_UNICODE) - std::string s = std::string(logger->stream().str().begin(), logger->stream().str().end()); -#else - std::string s = logger->stream().str(); -#endif // defined(ELPP_UNICODE) - logger->stream().str(ELPP_LITERAL("")); - logger->releaseLock(); - return s; - } - /// @brief Returns command line arguments (pointer) provided to easylogging++ - static inline const el::base::utils::CommandLineArgs* - commandLineArgs(void) { - return ELPP->commandLineArgs(); - } - /// @brief Reserve space for custom format specifiers for performance - /// @see std::vector::reserve - static inline void - reserveCustomFormatSpecifiers(std::size_t size) { - ELPP->m_customFormatSpecifiers.reserve(size); - } - /// @brief Installs user defined format specifier and handler - static inline void - installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { - ELPP->installCustomFormatSpecifier(customFormatSpecifier); - } - /// @brief Uninstalls user defined format specifier and handler - static inline bool - uninstallCustomFormatSpecifier(const char* formatSpecifier) { - return ELPP->uninstallCustomFormatSpecifier(formatSpecifier); - } - /// @brief Returns true if custom format specifier is installed - static inline bool - hasCustomFormatSpecifier(const char* formatSpecifier) { - return ELPP->hasCustomFormatSpecifier(formatSpecifier); - } - static inline void - validateFileRolling(Logger* logger, Level level) { - if (ELPP == nullptr || logger == nullptr) - return; - logger->m_typedConfigurations->validateFileRolling(level, ELPP->preRollOutCallback()); - } -}; -/// @brief Static helpers to deal with loggers and their configurations -class Loggers : base::StaticClass { - public: - /// @brief Gets existing or registers new logger - static Logger* - getLogger(const std::string& identity, bool registerIfNotAvailable = true); - /// @brief Changes default log builder for future loggers - static void - setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr); - /// @brief Installs logger registration callback, this callback is triggered when new logger is registered - template - static inline bool - installLoggerRegistrationCallback(const std::string& id) { - return ELPP->registeredLoggers()->installLoggerRegistrationCallback(id); - } - /// @brief Uninstalls log dispatch callback - template - static inline void - uninstallLoggerRegistrationCallback(const std::string& id) { - ELPP->registeredLoggers()->uninstallLoggerRegistrationCallback(id); - } - template - static inline T* - loggerRegistrationCallback(const std::string& id) { - return ELPP->registeredLoggers()->loggerRegistrationCallback(id); - } - /// @brief Unregisters logger - use it only when you know what you are doing, you may unregister - /// loggers initialized / used by third-party libs. - static bool - unregisterLogger(const std::string& identity); - /// @brief Whether or not logger with id is registered - static bool - hasLogger(const std::string& identity); - /// @brief Reconfigures specified logger with new configurations - static Logger* - reconfigureLogger(Logger* logger, const Configurations& configurations); - /// @brief Reconfigures logger with new configurations after looking it up using identity - static Logger* - reconfigureLogger(const std::string& identity, const Configurations& configurations); - /// @brief Reconfigures logger's single configuration - static Logger* - reconfigureLogger(const std::string& identity, ConfigurationType configurationType, const std::string& value); - /// @brief Reconfigures all the existing loggers with new configurations - static void - reconfigureAllLoggers(const Configurations& configurations); - /// @brief Reconfigures single configuration for all the loggers - static inline void - reconfigureAllLoggers(ConfigurationType configurationType, const std::string& value) { - reconfigureAllLoggers(Level::Global, configurationType, value); - } - /// @brief Reconfigures single configuration for all the loggers for specified level - static void - reconfigureAllLoggers(Level level, ConfigurationType configurationType, const std::string& value); - /// @brief Sets default configurations. This configuration is used for future (and conditionally for existing) - /// loggers - static void - setDefaultConfigurations(const Configurations& configurations, bool reconfigureExistingLoggers = false); - /// @brief Returns current default - static const Configurations* - defaultConfigurations(void); - /// @brief Returns log stream reference pointer if needed by user - static const base::LogStreamsReferenceMap* - logStreamsReference(void); - /// @brief Default typed configuration based on existing defaultConf - static base::TypedConfigurations - defaultTypedConfigurations(void); - /// @brief Populates all logger IDs in current repository. - /// @param [out] targetList List of fill up. - static std::vector* - populateAllLoggerIds(std::vector* targetList); - /// @brief Sets configurations from global configuration file. - static void - configureFromGlobal(const char* globalConfigurationFilePath); - /// @brief Configures loggers using command line arg. Ensure you have already set command line args, - /// @return False if invalid argument or argument with no value provided, true if attempted to configure logger. - /// If true is returned that does not mean it has been configured successfully, it only means that it - /// has attempeted to configure logger using configuration file provided in argument - static bool - configureFromArg(const char* argKey); - /// @brief Flushes all loggers for all levels - Be careful if you dont know how many loggers are registered - static void - flushAll(void); - /// @brief Adds logging flag used internally. - static inline void - addFlag(LoggingFlag flag) { - ELPP->addFlag(flag); - } - /// @brief Removes logging flag used internally. - static inline void - removeFlag(LoggingFlag flag) { - ELPP->removeFlag(flag); - } - /// @brief Determines whether or not certain flag is active - static inline bool - hasFlag(LoggingFlag flag) { - return ELPP->hasFlag(flag); - } - /// @brief Adds flag and removes it when scope goes out - class ScopedAddFlag { - public: - ScopedAddFlag(LoggingFlag flag) : m_flag(flag) { - Loggers::addFlag(m_flag); - } - ~ScopedAddFlag(void) { - Loggers::removeFlag(m_flag); - } - - private: - LoggingFlag m_flag; - }; - /// @brief Removes flag and add it when scope goes out - class ScopedRemoveFlag { - public: - ScopedRemoveFlag(LoggingFlag flag) : m_flag(flag) { - Loggers::removeFlag(m_flag); - } - ~ScopedRemoveFlag(void) { - Loggers::addFlag(m_flag); - } - - private: - LoggingFlag m_flag; - }; - /// @brief Sets hierarchy for logging. Needs to enable logging flag (HierarchicalLogging) - static void - setLoggingLevel(Level level) { - ELPP->setLoggingLevel(level); - } - /// @brief Sets verbose level on the fly - static void - setVerboseLevel(base::type::VerboseLevel level); - /// @brief Gets current verbose level - static base::type::VerboseLevel - verboseLevel(void); - /// @brief Sets vmodules as specified (on the fly) - static void - setVModules(const char* modules); - /// @brief Clears vmodules - static void - clearVModules(void); -}; -class VersionInfo : base::StaticClass { - public: - /// @brief Current version number - static const std::string - version(void); - - /// @brief Release date of current version - static const std::string - releaseDate(void); -}; -} // namespace el -#undef VLOG_IS_ON -/// @brief Determines whether verbose logging is on for specified level current file. -#define VLOG_IS_ON(verboseLevel) (ELPP->vRegistry()->allowed(verboseLevel, __FILE__)) -#undef TIMED_BLOCK -#undef TIMED_SCOPE -#undef TIMED_SCOPE_IF -#undef TIMED_FUNC -#undef TIMED_FUNC_IF -#undef ELPP_MIN_UNIT -#if defined(ELPP_PERFORMANCE_MICROSECONDS) -#define ELPP_MIN_UNIT el::base::TimestampUnit::Microsecond -#else -#define ELPP_MIN_UNIT el::base::TimestampUnit::Millisecond -#endif // (defined(ELPP_PERFORMANCE_MICROSECONDS)) -/// @brief Performance tracked scope. Performance gets written when goes out of scope using -/// 'performance' logger. -/// -/// @detail Please note in order to check the performance at a certain time you can use obj->checkpoint(); -/// @see el::base::PerformanceTracker -/// @see el::base::PerformanceTracker::checkpoint -// Note: Do not surround this definition with null macro because of obj instance -#define TIMED_SCOPE_IF(obj, blockname, condition) \ - el::base::type::PerformanceTrackerPtr obj(condition ? new el::base::PerformanceTracker(blockname, ELPP_MIN_UNIT) \ - : nullptr) -#define TIMED_SCOPE(obj, blockname) TIMED_SCOPE_IF(obj, blockname, true) -#define TIMED_BLOCK(obj, blockName) \ - for (struct { \ - int i; \ - el::base::type::PerformanceTrackerPtr timer; \ - } obj = {0, \ - el::base::type::PerformanceTrackerPtr(new el::base::PerformanceTracker(blockName, ELPP_MIN_UNIT))}; \ - obj.i < 1; ++obj.i) -/// @brief Performance tracked function. Performance gets written when goes out of scope using -/// 'performance' logger. -/// -/// @detail Please note in order to check the performance at a certain time you can use obj->checkpoint(); -/// @see el::base::PerformanceTracker -/// @see el::base::PerformanceTracker::checkpoint -#define TIMED_FUNC_IF(obj, condition) TIMED_SCOPE_IF(obj, ELPP_FUNC, condition) -#define TIMED_FUNC(obj) TIMED_SCOPE(obj, ELPP_FUNC) -#undef PERFORMANCE_CHECKPOINT -#undef PERFORMANCE_CHECKPOINT_WITH_ID -#define PERFORMANCE_CHECKPOINT(obj) obj->checkpoint(std::string(), __FILE__, __LINE__, ELPP_FUNC) -#define PERFORMANCE_CHECKPOINT_WITH_ID(obj, id) obj->checkpoint(id, __FILE__, __LINE__, ELPP_FUNC) -#undef ELPP_COUNTER -#undef ELPP_COUNTER_POS -/// @brief Gets hit counter for file/line -#define ELPP_COUNTER (ELPP->hitCounters()->getCounter(__FILE__, __LINE__)) -/// @brief Gets hit counter position for file/line, -1 if not registered yet -#define ELPP_COUNTER_POS (ELPP_COUNTER == nullptr ? -1 : ELPP_COUNTER->hitCounts()) -// Undef levels to support LOG(LEVEL) -#undef INFO -#undef WARNING -#undef DEBUG -#undef ERROR -#undef FATAL -#undef TRACE -#undef VERBOSE -// Undef existing -#undef CINFO -#undef CWARNING -#undef CDEBUG -#undef CFATAL -#undef CERROR -#undef CTRACE -#undef CVERBOSE -#undef CINFO_IF -#undef CWARNING_IF -#undef CDEBUG_IF -#undef CERROR_IF -#undef CFATAL_IF -#undef CTRACE_IF -#undef CVERBOSE_IF -#undef CINFO_EVERY_N -#undef CWARNING_EVERY_N -#undef CDEBUG_EVERY_N -#undef CERROR_EVERY_N -#undef CFATAL_EVERY_N -#undef CTRACE_EVERY_N -#undef CVERBOSE_EVERY_N -#undef CINFO_AFTER_N -#undef CWARNING_AFTER_N -#undef CDEBUG_AFTER_N -#undef CERROR_AFTER_N -#undef CFATAL_AFTER_N -#undef CTRACE_AFTER_N -#undef CVERBOSE_AFTER_N -#undef CINFO_N_TIMES -#undef CWARNING_N_TIMES -#undef CDEBUG_N_TIMES -#undef CERROR_N_TIMES -#undef CFATAL_N_TIMES -#undef CTRACE_N_TIMES -#undef CVERBOSE_N_TIMES -// Normal logs -#if ELPP_INFO_LOG -#define CINFO(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Info, dispatchAction, __VA_ARGS__) -#else -#define CINFO(writer, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_INFO_LOG -#if ELPP_WARNING_LOG -#define CWARNING(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Warning, dispatchAction, __VA_ARGS__) -#else -#define CWARNING(writer, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_WARNING_LOG -#if ELPP_DEBUG_LOG -#define CDEBUG(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Debug, dispatchAction, __VA_ARGS__) -#else -#define CDEBUG(writer, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_DEBUG_LOG -#if ELPP_ERROR_LOG -#define CERROR(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Error, dispatchAction, __VA_ARGS__) -#else -#define CERROR(writer, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_ERROR_LOG -#if ELPP_FATAL_LOG -#define CFATAL(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Fatal, dispatchAction, __VA_ARGS__) -#else -#define CFATAL(writer, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_FATAL_LOG -#if ELPP_TRACE_LOG -#define CTRACE(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Trace, dispatchAction, __VA_ARGS__) -#else -#define CTRACE(writer, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_TRACE_LOG -#if ELPP_VERBOSE_LOG -#define CVERBOSE(writer, vlevel, dispatchAction, ...) \ - if (VLOG_IS_ON(vlevel)) \ - writer(el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel) \ - .construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) -#else -#define CVERBOSE(writer, vlevel, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_VERBOSE_LOG -// Conditional logs -#if ELPP_INFO_LOG -#define CINFO_IF(writer, condition_, dispatchAction, ...) \ - ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Info, dispatchAction, __VA_ARGS__) -#else -#define CINFO_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_INFO_LOG -#if ELPP_WARNING_LOG -#define CWARNING_IF(writer, condition_, dispatchAction, ...) \ - ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Warning, dispatchAction, __VA_ARGS__) -#else -#define CWARNING_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_WARNING_LOG -#if ELPP_DEBUG_LOG -#define CDEBUG_IF(writer, condition_, dispatchAction, ...) \ - ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Debug, dispatchAction, __VA_ARGS__) -#else -#define CDEBUG_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_DEBUG_LOG -#if ELPP_ERROR_LOG -#define CERROR_IF(writer, condition_, dispatchAction, ...) \ - ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Error, dispatchAction, __VA_ARGS__) -#else -#define CERROR_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_ERROR_LOG -#if ELPP_FATAL_LOG -#define CFATAL_IF(writer, condition_, dispatchAction, ...) \ - ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Fatal, dispatchAction, __VA_ARGS__) -#else -#define CFATAL_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_FATAL_LOG -#if ELPP_TRACE_LOG -#define CTRACE_IF(writer, condition_, dispatchAction, ...) \ - ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Trace, dispatchAction, __VA_ARGS__) -#else -#define CTRACE_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_TRACE_LOG -#if ELPP_VERBOSE_LOG -#define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) \ - if (VLOG_IS_ON(vlevel) && (condition_)) \ - writer(el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel) \ - .construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) -#else -#define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_VERBOSE_LOG -// Occasional logs -#if ELPP_INFO_LOG -#define CINFO_EVERY_N(writer, occasion, dispatchAction, ...) \ - ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Info, dispatchAction, __VA_ARGS__) -#else -#define CINFO_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_INFO_LOG -#if ELPP_WARNING_LOG -#define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...) \ - ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Warning, dispatchAction, __VA_ARGS__) -#else -#define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_WARNING_LOG -#if ELPP_DEBUG_LOG -#define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...) \ - ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Debug, dispatchAction, __VA_ARGS__) -#else -#define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_DEBUG_LOG -#if ELPP_ERROR_LOG -#define CERROR_EVERY_N(writer, occasion, dispatchAction, ...) \ - ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Error, dispatchAction, __VA_ARGS__) -#else -#define CERROR_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_ERROR_LOG -#if ELPP_FATAL_LOG -#define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...) \ - ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Fatal, dispatchAction, __VA_ARGS__) -#else -#define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_FATAL_LOG -#if ELPP_TRACE_LOG -#define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...) \ - ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Trace, dispatchAction, __VA_ARGS__) -#else -#define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_TRACE_LOG -#if ELPP_VERBOSE_LOG -#define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...) \ - CVERBOSE_IF(writer, ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion), vlevel, dispatchAction, __VA_ARGS__) -#else -#define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_VERBOSE_LOG -// After N logs -#if ELPP_INFO_LOG -#define CINFO_AFTER_N(writer, n, dispatchAction, ...) \ - ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) -#else -#define CINFO_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_INFO_LOG -#if ELPP_WARNING_LOG -#define CWARNING_AFTER_N(writer, n, dispatchAction, ...) \ - ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) -#else -#define CWARNING_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_WARNING_LOG -#if ELPP_DEBUG_LOG -#define CDEBUG_AFTER_N(writer, n, dispatchAction, ...) \ - ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) -#else -#define CDEBUG_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_DEBUG_LOG -#if ELPP_ERROR_LOG -#define CERROR_AFTER_N(writer, n, dispatchAction, ...) \ - ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) -#else -#define CERROR_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_ERROR_LOG -#if ELPP_FATAL_LOG -#define CFATAL_AFTER_N(writer, n, dispatchAction, ...) \ - ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) -#else -#define CFATAL_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_FATAL_LOG -#if ELPP_TRACE_LOG -#define CTRACE_AFTER_N(writer, n, dispatchAction, ...) \ - ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) -#else -#define CTRACE_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_TRACE_LOG -#if ELPP_VERBOSE_LOG -#define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...) \ - CVERBOSE_IF(writer, ELPP->validateAfterNCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) -#else -#define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_VERBOSE_LOG -// N Times logs -#if ELPP_INFO_LOG -#define CINFO_N_TIMES(writer, n, dispatchAction, ...) \ - ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) -#else -#define CINFO_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_INFO_LOG -#if ELPP_WARNING_LOG -#define CWARNING_N_TIMES(writer, n, dispatchAction, ...) \ - ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) -#else -#define CWARNING_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_WARNING_LOG -#if ELPP_DEBUG_LOG -#define CDEBUG_N_TIMES(writer, n, dispatchAction, ...) \ - ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) -#else -#define CDEBUG_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_DEBUG_LOG -#if ELPP_ERROR_LOG -#define CERROR_N_TIMES(writer, n, dispatchAction, ...) \ - ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) -#else -#define CERROR_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_ERROR_LOG -#if ELPP_FATAL_LOG -#define CFATAL_N_TIMES(writer, n, dispatchAction, ...) \ - ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) -#else -#define CFATAL_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_FATAL_LOG -#if ELPP_TRACE_LOG -#define CTRACE_N_TIMES(writer, n, dispatchAction, ...) \ - ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) -#else -#define CTRACE_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_TRACE_LOG -#if ELPP_VERBOSE_LOG -#define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...) \ - CVERBOSE_IF(writer, ELPP->validateNTimesCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) -#else -#define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() -#endif // ELPP_VERBOSE_LOG -// -// Custom Loggers - Requires (level, dispatchAction, loggerId/s) -// -// undef existing -#undef CLOG -#undef CLOG_VERBOSE -#undef CVLOG -#undef CLOG_IF -#undef CLOG_VERBOSE_IF -#undef CVLOG_IF -#undef CLOG_EVERY_N -#undef CVLOG_EVERY_N -#undef CLOG_AFTER_N -#undef CVLOG_AFTER_N -#undef CLOG_N_TIMES -#undef CVLOG_N_TIMES -// Normal logs -#define CLOG(LEVEL, ...) C##LEVEL(el::base::Writer, el::base::DispatchAction::NormalLog, __VA_ARGS__) -#define CVLOG(vlevel, ...) CVERBOSE(el::base::Writer, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) -// Conditional logs -#define CLOG_IF(condition, LEVEL, ...) \ - C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__) -#define CVLOG_IF(condition, vlevel, ...) \ - CVERBOSE_IF(el::base::Writer, condition, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) -// Hit counts based logs -#define CLOG_EVERY_N(n, LEVEL, ...) \ - C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) -#define CVLOG_EVERY_N(n, vlevel, ...) \ - CVERBOSE_EVERY_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) -#define CLOG_AFTER_N(n, LEVEL, ...) \ - C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) -#define CVLOG_AFTER_N(n, vlevel, ...) \ - CVERBOSE_AFTER_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) -#define CLOG_N_TIMES(n, LEVEL, ...) \ - C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) -#define CVLOG_N_TIMES(n, vlevel, ...) \ - CVERBOSE_N_TIMES(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) -// -// Default Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros -// -// undef existing -#undef LOG -#undef VLOG -#undef LOG_IF -#undef VLOG_IF -#undef LOG_EVERY_N -#undef VLOG_EVERY_N -#undef LOG_AFTER_N -#undef VLOG_AFTER_N -#undef LOG_N_TIMES -#undef VLOG_N_TIMES -#undef ELPP_CURR_FILE_LOGGER_ID -#if defined(ELPP_DEFAULT_LOGGER) -#define ELPP_CURR_FILE_LOGGER_ID ELPP_DEFAULT_LOGGER -#else -#define ELPP_CURR_FILE_LOGGER_ID el::base::consts::kDefaultLoggerId -#endif -#undef ELPP_TRACE -#define ELPP_TRACE CLOG(TRACE, ELPP_CURR_FILE_LOGGER_ID) -// Normal logs -#define LOG(LEVEL) CLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) -#define VLOG(vlevel) CVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID) -// Conditional logs -#define LOG_IF(condition, LEVEL) CLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) -#define VLOG_IF(condition, vlevel) CVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID) -// Hit counts based logs -#define LOG_EVERY_N(n, LEVEL) CLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) -#define VLOG_EVERY_N(n, vlevel) CVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) -#define LOG_AFTER_N(n, LEVEL) CLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) -#define VLOG_AFTER_N(n, vlevel) CVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) -#define LOG_N_TIMES(n, LEVEL) CLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) -#define VLOG_N_TIMES(n, vlevel) CVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) -// Generic PLOG() -#undef CPLOG -#undef CPLOG_IF -#undef PLOG -#undef PLOG_IF -#undef DCPLOG -#undef DCPLOG_IF -#undef DPLOG -#undef DPLOG_IF -#define CPLOG(LEVEL, ...) C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__) -#define CPLOG_IF(condition, LEVEL, ...) \ - C##LEVEL##_IF(el::base::PErrorWriter, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__) -#define DCPLOG(LEVEL, ...) \ - if (ELPP_DEBUG_LOG) \ - C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__) -#define DCPLOG_IF(condition, LEVEL, ...) \ - C##LEVEL##_IF(el::base::PErrorWriter, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::NormalLog, \ - __VA_ARGS__) -#define PLOG(LEVEL) CPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) -#define PLOG_IF(condition, LEVEL) CPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) -#define DPLOG(LEVEL) DCPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) -#define DPLOG_IF(condition, LEVEL) DCPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) -// Generic SYSLOG() -#undef CSYSLOG -#undef CSYSLOG_IF -#undef CSYSLOG_EVERY_N -#undef CSYSLOG_AFTER_N -#undef CSYSLOG_N_TIMES -#undef SYSLOG -#undef SYSLOG_IF -#undef SYSLOG_EVERY_N -#undef SYSLOG_AFTER_N -#undef SYSLOG_N_TIMES -#undef DCSYSLOG -#undef DCSYSLOG_IF -#undef DCSYSLOG_EVERY_N -#undef DCSYSLOG_AFTER_N -#undef DCSYSLOG_N_TIMES -#undef DSYSLOG -#undef DSYSLOG_IF -#undef DSYSLOG_EVERY_N -#undef DSYSLOG_AFTER_N -#undef DSYSLOG_N_TIMES -#if defined(ELPP_SYSLOG) -#define CSYSLOG(LEVEL, ...) C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) -#define CSYSLOG_IF(condition, LEVEL, ...) \ - C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::SysLog, __VA_ARGS__) -#define CSYSLOG_EVERY_N(n, LEVEL, ...) \ - C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) -#define CSYSLOG_AFTER_N(n, LEVEL, ...) \ - C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) -#define CSYSLOG_N_TIMES(n, LEVEL, ...) \ - C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) -#define SYSLOG(LEVEL) CSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) -#define SYSLOG_IF(condition, LEVEL) CSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) -#define SYSLOG_EVERY_N(n, LEVEL) CSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) -#define SYSLOG_AFTER_N(n, LEVEL) CSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) -#define SYSLOG_N_TIMES(n, LEVEL) CSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) -#define DCSYSLOG(LEVEL, ...) \ - if (ELPP_DEBUG_LOG) \ - C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) -#define DCSYSLOG_IF(condition, LEVEL, ...) \ - C##LEVEL##_IF(el::base::Writer, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::SysLog, __VA_ARGS__) -#define DCSYSLOG_EVERY_N(n, LEVEL, ...) \ - if (ELPP_DEBUG_LOG) \ - C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) -#define DCSYSLOG_AFTER_N(n, LEVEL, ...) \ - if (ELPP_DEBUG_LOG) \ - C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) -#define DCSYSLOG_N_TIMES(n, LEVEL, ...) \ - if (ELPP_DEBUG_LOG) \ - C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) -#define DSYSLOG(LEVEL) DCSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) -#define DSYSLOG_IF(condition, LEVEL) DCSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) -#define DSYSLOG_EVERY_N(n, LEVEL) DCSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) -#define DSYSLOG_AFTER_N(n, LEVEL) DCSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) -#define DSYSLOG_N_TIMES(n, LEVEL) DCSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) -#else -#define CSYSLOG(LEVEL, ...) el::base::NullWriter() -#define CSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() -#define CSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() -#define CSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() -#define CSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() -#define SYSLOG(LEVEL) el::base::NullWriter() -#define SYSLOG_IF(condition, LEVEL) el::base::NullWriter() -#define SYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() -#define SYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() -#define SYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() -#define DCSYSLOG(LEVEL, ...) el::base::NullWriter() -#define DCSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() -#define DCSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() -#define DCSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() -#define DCSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() -#define DSYSLOG(LEVEL) el::base::NullWriter() -#define DSYSLOG_IF(condition, LEVEL) el::base::NullWriter() -#define DSYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() -#define DSYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() -#define DSYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() -#endif // defined(ELPP_SYSLOG) -// -// Custom Debug Only Loggers - Requires (level, loggerId/s) -// -// undef existing -#undef DCLOG -#undef DCVLOG -#undef DCLOG_IF -#undef DCVLOG_IF -#undef DCLOG_EVERY_N -#undef DCVLOG_EVERY_N -#undef DCLOG_AFTER_N -#undef DCVLOG_AFTER_N -#undef DCLOG_N_TIMES -#undef DCVLOG_N_TIMES -// Normal logs -#define DCLOG(LEVEL, ...) \ - if (ELPP_DEBUG_LOG) \ - CLOG(LEVEL, __VA_ARGS__) -#define DCLOG_VERBOSE(vlevel, ...) \ - if (ELPP_DEBUG_LOG) \ - CLOG_VERBOSE(vlevel, __VA_ARGS__) -#define DCVLOG(vlevel, ...) \ - if (ELPP_DEBUG_LOG) \ - CVLOG(vlevel, __VA_ARGS__) -// Conditional logs -#define DCLOG_IF(condition, LEVEL, ...) \ - if (ELPP_DEBUG_LOG) \ - CLOG_IF(condition, LEVEL, __VA_ARGS__) -#define DCVLOG_IF(condition, vlevel, ...) \ - if (ELPP_DEBUG_LOG) \ - CVLOG_IF(condition, vlevel, __VA_ARGS__) -// Hit counts based logs -#define DCLOG_EVERY_N(n, LEVEL, ...) \ - if (ELPP_DEBUG_LOG) \ - CLOG_EVERY_N(n, LEVEL, __VA_ARGS__) -#define DCVLOG_EVERY_N(n, vlevel, ...) \ - if (ELPP_DEBUG_LOG) \ - CVLOG_EVERY_N(n, vlevel, __VA_ARGS__) -#define DCLOG_AFTER_N(n, LEVEL, ...) \ - if (ELPP_DEBUG_LOG) \ - CLOG_AFTER_N(n, LEVEL, __VA_ARGS__) -#define DCVLOG_AFTER_N(n, vlevel, ...) \ - if (ELPP_DEBUG_LOG) \ - CVLOG_AFTER_N(n, vlevel, __VA_ARGS__) -#define DCLOG_N_TIMES(n, LEVEL, ...) \ - if (ELPP_DEBUG_LOG) \ - CLOG_N_TIMES(n, LEVEL, __VA_ARGS__) -#define DCVLOG_N_TIMES(n, vlevel, ...) \ - if (ELPP_DEBUG_LOG) \ - CVLOG_N_TIMES(n, vlevel, __VA_ARGS__) -// -// Default Debug Only Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros -// -#if !defined(ELPP_NO_DEBUG_MACROS) -// undef existing -#undef DLOG -#undef DVLOG -#undef DLOG_IF -#undef DVLOG_IF -#undef DLOG_EVERY_N -#undef DVLOG_EVERY_N -#undef DLOG_AFTER_N -#undef DVLOG_AFTER_N -#undef DLOG_N_TIMES -#undef DVLOG_N_TIMES -// Normal logs -#define DLOG(LEVEL) DCLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) -#define DVLOG(vlevel) DCVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID) -// Conditional logs -#define DLOG_IF(condition, LEVEL) DCLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) -#define DVLOG_IF(condition, vlevel) DCVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID) -// Hit counts based logs -#define DLOG_EVERY_N(n, LEVEL) DCLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) -#define DVLOG_EVERY_N(n, vlevel) DCVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) -#define DLOG_AFTER_N(n, LEVEL) DCLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) -#define DVLOG_AFTER_N(n, vlevel) DCVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) -#define DLOG_N_TIMES(n, LEVEL) DCLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) -#define DVLOG_N_TIMES(n, vlevel) DCVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) -#endif // defined(ELPP_NO_DEBUG_MACROS) -#if !defined(ELPP_NO_CHECK_MACROS) -// Check macros -#undef CCHECK -#undef CPCHECK -#undef CCHECK_EQ -#undef CCHECK_NE -#undef CCHECK_LT -#undef CCHECK_GT -#undef CCHECK_LE -#undef CCHECK_GE -#undef CCHECK_BOUNDS -#undef CCHECK_NOTNULL -#undef CCHECK_STRCASEEQ -#undef CCHECK_STRCASENE -#undef CHECK -#undef PCHECK -#undef CHECK_EQ -#undef CHECK_NE -#undef CHECK_LT -#undef CHECK_GT -#undef CHECK_LE -#undef CHECK_GE -#undef CHECK_BOUNDS -#undef CHECK_NOTNULL -#undef CHECK_STRCASEEQ -#undef CHECK_STRCASENE -#define CCHECK(condition, ...) CLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] " -#define CPCHECK(condition, ...) CPLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] " -#define CHECK(condition) CCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) -#define PCHECK(condition) CPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) -#define CCHECK_EQ(a, b, ...) CCHECK(a == b, __VA_ARGS__) -#define CCHECK_NE(a, b, ...) CCHECK(a != b, __VA_ARGS__) -#define CCHECK_LT(a, b, ...) CCHECK(a < b, __VA_ARGS__) -#define CCHECK_GT(a, b, ...) CCHECK(a > b, __VA_ARGS__) -#define CCHECK_LE(a, b, ...) CCHECK(a <= b, __VA_ARGS__) -#define CCHECK_GE(a, b, ...) CCHECK(a >= b, __VA_ARGS__) -#define CCHECK_BOUNDS(val, min, max, ...) CCHECK(val >= min && val <= max, __VA_ARGS__) -#define CHECK_EQ(a, b) CCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID) -#define CHECK_NE(a, b) CCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID) -#define CHECK_LT(a, b) CCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID) -#define CHECK_GT(a, b) CCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID) -#define CHECK_LE(a, b) CCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID) -#define CHECK_GE(a, b) CCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID) -#define CHECK_BOUNDS(val, min, max) CCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID) -#define CCHECK_NOTNULL(ptr, ...) CCHECK((ptr) != nullptr, __VA_ARGS__) -#define CCHECK_STREQ(str1, str2, ...) \ - CLOG_IF(!el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \ - << "Check failed: [" << #str1 << " == " << #str2 << "] " -#define CCHECK_STRNE(str1, str2, ...) \ - CLOG_IF(el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \ - << "Check failed: [" << #str1 << " != " << #str2 << "] " -#define CCHECK_STRCASEEQ(str1, str2, ...) \ - CLOG_IF(!el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \ - << "Check failed: [" << #str1 << " == " << #str2 << "] " -#define CCHECK_STRCASENE(str1, str2, ...) \ - CLOG_IF(el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \ - << "Check failed: [" << #str1 << " != " << #str2 << "] " -#define CHECK_NOTNULL(ptr) CCHECK_NOTNULL((ptr), ELPP_CURR_FILE_LOGGER_ID) -#define CHECK_STREQ(str1, str2) CCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) -#define CHECK_STRNE(str1, str2) CCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) -#define CHECK_STRCASEEQ(str1, str2) CCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) -#define CHECK_STRCASENE(str1, str2) CCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) -#undef DCCHECK -#undef DCCHECK_EQ -#undef DCCHECK_NE -#undef DCCHECK_LT -#undef DCCHECK_GT -#undef DCCHECK_LE -#undef DCCHECK_GE -#undef DCCHECK_BOUNDS -#undef DCCHECK_NOTNULL -#undef DCCHECK_STRCASEEQ -#undef DCCHECK_STRCASENE -#undef DCPCHECK -#undef DCHECK -#undef DCHECK_EQ -#undef DCHECK_NE -#undef DCHECK_LT -#undef DCHECK_GT -#undef DCHECK_LE -#undef DCHECK_GE -#undef DCHECK_BOUNDS_ -#undef DCHECK_NOTNULL -#undef DCHECK_STRCASEEQ -#undef DCHECK_STRCASENE -#undef DPCHECK -#define DCCHECK(condition, ...) \ - if (ELPP_DEBUG_LOG) \ - CCHECK(condition, __VA_ARGS__) -#define DCCHECK_EQ(a, b, ...) \ - if (ELPP_DEBUG_LOG) \ - CCHECK_EQ(a, b, __VA_ARGS__) -#define DCCHECK_NE(a, b, ...) \ - if (ELPP_DEBUG_LOG) \ - CCHECK_NE(a, b, __VA_ARGS__) -#define DCCHECK_LT(a, b, ...) \ - if (ELPP_DEBUG_LOG) \ - CCHECK_LT(a, b, __VA_ARGS__) -#define DCCHECK_GT(a, b, ...) \ - if (ELPP_DEBUG_LOG) \ - CCHECK_GT(a, b, __VA_ARGS__) -#define DCCHECK_LE(a, b, ...) \ - if (ELPP_DEBUG_LOG) \ - CCHECK_LE(a, b, __VA_ARGS__) -#define DCCHECK_GE(a, b, ...) \ - if (ELPP_DEBUG_LOG) \ - CCHECK_GE(a, b, __VA_ARGS__) -#define DCCHECK_BOUNDS(val, min, max, ...) \ - if (ELPP_DEBUG_LOG) \ - CCHECK_BOUNDS(val, min, max, __VA_ARGS__) -#define DCCHECK_NOTNULL(ptr, ...) \ - if (ELPP_DEBUG_LOG) \ - CCHECK_NOTNULL((ptr), __VA_ARGS__) -#define DCCHECK_STREQ(str1, str2, ...) \ - if (ELPP_DEBUG_LOG) \ - CCHECK_STREQ(str1, str2, __VA_ARGS__) -#define DCCHECK_STRNE(str1, str2, ...) \ - if (ELPP_DEBUG_LOG) \ - CCHECK_STRNE(str1, str2, __VA_ARGS__) -#define DCCHECK_STRCASEEQ(str1, str2, ...) \ - if (ELPP_DEBUG_LOG) \ - CCHECK_STRCASEEQ(str1, str2, __VA_ARGS__) -#define DCCHECK_STRCASENE(str1, str2, ...) \ - if (ELPP_DEBUG_LOG) \ - CCHECK_STRCASENE(str1, str2, __VA_ARGS__) -#define DCPCHECK(condition, ...) \ - if (ELPP_DEBUG_LOG) \ - CPCHECK(condition, __VA_ARGS__) -#define DCHECK(condition) DCCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) -#define DCHECK_EQ(a, b) DCCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID) -#define DCHECK_NE(a, b) DCCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID) -#define DCHECK_LT(a, b) DCCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID) -#define DCHECK_GT(a, b) DCCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID) -#define DCHECK_LE(a, b) DCCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID) -#define DCHECK_GE(a, b) DCCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID) -#define DCHECK_BOUNDS(val, min, max) DCCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID) -#define DCHECK_NOTNULL(ptr) DCCHECK_NOTNULL((ptr), ELPP_CURR_FILE_LOGGER_ID) -#define DCHECK_STREQ(str1, str2) DCCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) -#define DCHECK_STRNE(str1, str2) DCCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) -#define DCHECK_STRCASEEQ(str1, str2) DCCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) -#define DCHECK_STRCASENE(str1, str2) DCCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) -#define DPCHECK(condition) DCPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) -#endif // defined(ELPP_NO_CHECK_MACROS) -#if defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) -#define ELPP_USE_DEF_CRASH_HANDLER false -#else -#define ELPP_USE_DEF_CRASH_HANDLER true -#endif // defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) -#define ELPP_CRASH_HANDLER_INIT -#define ELPP_INIT_EASYLOGGINGPP(val) \ - namespace el { \ - namespace base { \ - el::base::type::StoragePointer elStorage(val); \ - } \ - el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER); \ - } - -#if ELPP_ASYNC_LOGGING -#define INITIALIZE_EASYLOGGINGPP \ - ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()), \ - new el::base::AsyncDispatchWorker())) -#else -#define INITIALIZE_EASYLOGGINGPP \ - ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()))) -#endif // ELPP_ASYNC_LOGGING -#define INITIALIZE_NULL_EASYLOGGINGPP \ - namespace el { \ - namespace base { \ - el::base::type::StoragePointer elStorage; \ - } \ - el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER); \ - } -#define SHARE_EASYLOGGINGPP(initializedStorage) \ - namespace el { \ - namespace base { \ - el::base::type::StoragePointer elStorage(initializedStorage); \ - } \ - el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER); \ - } - -#if defined(ELPP_UNICODE) -#define START_EASYLOGGINGPP(argc, argv) \ - el::Helpers::setArgs(argc, argv); \ - std::locale::global(std::locale("")) -#else -#define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv) -#endif // defined(ELPP_UNICODE) -#endif // EASYLOGGINGPP_H diff --git a/core/thirdparty/nlohmann/json.hpp b/core/thirdparty/nlohmann/json.hpp deleted file mode 100644 index 4acdcd3aea..0000000000 --- a/core/thirdparty/nlohmann/json.hpp +++ /dev/null @@ -1,21006 +0,0 @@ -/* - __ _____ _____ _____ - __| | __| | | | JSON for Modern C++ -| | |__ | | | | | | version 3.7.0 -|_____|_____|_____|_|___| https://github.com/nlohmann/json - -Licensed under the MIT License . -SPDX-License-Identifier: MIT -Copyright (c) 2013-2019 Niels Lohmann . - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -#ifndef INCLUDE_NLOHMANN_JSON_HPP_ -#define INCLUDE_NLOHMANN_JSON_HPP_ - -#define NLOHMANN_JSON_VERSION_MAJOR 3 -#define NLOHMANN_JSON_VERSION_MINOR 7 -#define NLOHMANN_JSON_VERSION_PATCH 0 - -#include // all_of, find, for_each -#include // assert -#include // and, not, or -#include // nullptr_t, ptrdiff_t, size_t -#include // hash, less -#include // initializer_list -#include // istream, ostream -#include // random_access_iterator_tag -#include // unique_ptr -#include // accumulate -#include // string, stoi, to_string -#include // declval, forward, move, pair, swap -#include // vector - -// #include - -#include - -// #include - -#include // transform -#include // array -#include // and, not -#include // forward_list -#include // inserter, front_inserter, end -#include // map -#include // string -#include // tuple, make_tuple -#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible -#include // unordered_map -#include // pair, declval -#include // valarray - -// #include - -#include // exception -#include // runtime_error -#include // to_string - -// #include - -#include // size_t - -namespace nlohmann { -namespace detail { -/// struct to capture the start position of the current token -struct position_t { - /// the total number of characters read - std::size_t chars_read_total = 0; - /// the number of characters read in the current line - std::size_t chars_read_current_line = 0; - /// the number of lines read - std::size_t lines_read = 0; - - /// conversion to size_t to preserve SAX interface - constexpr operator size_t() const { - return chars_read_total; - } -}; - -} // namespace detail -} // namespace nlohmann - -// #include - -#include // pair -// #include -/* Hedley - https://nemequ.github.io/hedley - * Created by Evan Nemerson - * - * To the extent possible under law, the author(s) have dedicated all - * copyright and related and neighboring rights to this software to - * the public domain worldwide. This software is distributed without - * any warranty. - * - * For details, see . - * SPDX-License-Identifier: CC0-1.0 - */ - -#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 9) -#if defined(JSON_HEDLEY_VERSION) -#undef JSON_HEDLEY_VERSION -#endif -#define JSON_HEDLEY_VERSION 9 - -#if defined(JSON_HEDLEY_STRINGIFY_EX) -#undef JSON_HEDLEY_STRINGIFY_EX -#endif -#define JSON_HEDLEY_STRINGIFY_EX(x) #x - -#if defined(JSON_HEDLEY_STRINGIFY) -#undef JSON_HEDLEY_STRINGIFY -#endif -#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) - -#if defined(JSON_HEDLEY_CONCAT_EX) -#undef JSON_HEDLEY_CONCAT_EX -#endif -#define JSON_HEDLEY_CONCAT_EX(a, b) a##b - -#if defined(JSON_HEDLEY_CONCAT) -#undef JSON_HEDLEY_CONCAT -#endif -#define JSON_HEDLEY_CONCAT(a, b) JSON_HEDLEY_CONCAT_EX(a, b) - -#if defined(JSON_HEDLEY_VERSION_ENCODE) -#undef JSON_HEDLEY_VERSION_ENCODE -#endif -#define JSON_HEDLEY_VERSION_ENCODE(major, minor, revision) (((major)*1000000) + ((minor)*1000) + (revision)) - -#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) -#undef JSON_HEDLEY_VERSION_DECODE_MAJOR -#endif -#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) - -#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) -#undef JSON_HEDLEY_VERSION_DECODE_MINOR -#endif -#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) - -#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) -#undef JSON_HEDLEY_VERSION_DECODE_REVISION -#endif -#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) - -#if defined(JSON_HEDLEY_GNUC_VERSION) -#undef JSON_HEDLEY_GNUC_VERSION -#endif -#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) -#define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) -#elif defined(__GNUC__) -#define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) -#endif - -#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) -#undef JSON_HEDLEY_GNUC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_GNUC_VERSION) -#define JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) \ - (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(JSON_HEDLEY_MSVC_VERSION) -#undef JSON_HEDLEY_MSVC_VERSION -#endif -#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) -#define JSON_HEDLEY_MSVC_VERSION \ - JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, \ - (_MSC_FULL_VER % 100000) / 100) -#elif defined(_MSC_FULL_VER) -#define JSON_HEDLEY_MSVC_VERSION \ - JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) -#elif defined(_MSC_VER) -#define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) -#endif - -#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) -#undef JSON_HEDLEY_MSVC_VERSION_CHECK -#endif -#if !defined(_MSC_VER) -#define JSON_HEDLEY_MSVC_VERSION_CHECK(major, minor, patch) (0) -#elif defined(_MSC_VER) && (_MSC_VER >= 1400) -#define JSON_HEDLEY_MSVC_VERSION_CHECK(major, minor, patch) \ - (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) -#elif defined(_MSC_VER) && (_MSC_VER >= 1200) -#define JSON_HEDLEY_MSVC_VERSION_CHECK(major, minor, patch) \ - (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) -#else -#define JSON_HEDLEY_MSVC_VERSION_CHECK(major, minor, patch) (_MSC_VER >= ((major * 100) + (minor))) -#endif - -#if defined(JSON_HEDLEY_INTEL_VERSION) -#undef JSON_HEDLEY_INTEL_VERSION -#endif -#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) -#define JSON_HEDLEY_INTEL_VERSION \ - JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) -#elif defined(__INTEL_COMPILER) -#define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) -#endif - -#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) -#undef JSON_HEDLEY_INTEL_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_INTEL_VERSION) -#define JSON_HEDLEY_INTEL_VERSION_CHECK(major, minor, patch) \ - (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_INTEL_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(JSON_HEDLEY_PGI_VERSION) -#undef JSON_HEDLEY_PGI_VERSION -#endif -#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) -#define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) -#endif - -#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) -#undef JSON_HEDLEY_PGI_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_PGI_VERSION) -#define JSON_HEDLEY_PGI_VERSION_CHECK(major, minor, patch) \ - (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_PGI_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(JSON_HEDLEY_SUNPRO_VERSION) -#undef JSON_HEDLEY_SUNPRO_VERSION -#endif -#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) -#define JSON_HEDLEY_SUNPRO_VERSION \ - JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), \ - (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) -#elif defined(__SUNPRO_C) -#define JSON_HEDLEY_SUNPRO_VERSION \ - JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C)&0xf) -#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) -#define JSON_HEDLEY_SUNPRO_VERSION \ - JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), \ - (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), \ - (__SUNPRO_CC & 0xf) * 10) -#elif defined(__SUNPRO_CC) -#define JSON_HEDLEY_SUNPRO_VERSION \ - JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC)&0xf) -#endif - -#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) -#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_SUNPRO_VERSION) -#define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major, minor, patch) \ - (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) -#undef JSON_HEDLEY_EMSCRIPTEN_VERSION -#endif -#if defined(__EMSCRIPTEN__) -#define JSON_HEDLEY_EMSCRIPTEN_VERSION \ - JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) -#endif - -#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) -#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) -#define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major, minor, patch) \ - (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(JSON_HEDLEY_ARM_VERSION) -#undef JSON_HEDLEY_ARM_VERSION -#endif -#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) -#define JSON_HEDLEY_ARM_VERSION \ - JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, \ - (__ARMCOMPILER_VERSION % 10000) / 100) -#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) -#define JSON_HEDLEY_ARM_VERSION \ - JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, \ - (__ARMCC_VERSION % 10000) / 100) -#endif - -#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) -#undef JSON_HEDLEY_ARM_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_ARM_VERSION) -#define JSON_HEDLEY_ARM_VERSION_CHECK(major, minor, patch) \ - (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_ARM_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(JSON_HEDLEY_IBM_VERSION) -#undef JSON_HEDLEY_IBM_VERSION -#endif -#if defined(__ibmxl__) -#define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) -#elif defined(__xlC__) && defined(__xlC_ver__) -#define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) -#elif defined(__xlC__) -#define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) -#endif - -#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) -#undef JSON_HEDLEY_IBM_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_IBM_VERSION) -#define JSON_HEDLEY_IBM_VERSION_CHECK(major, minor, patch) \ - (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_IBM_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_VERSION) -#undef JSON_HEDLEY_TI_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) -#define JSON_HEDLEY_TI_VERSION \ - JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, \ - (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_VERSION_CHECK) -#undef JSON_HEDLEY_TI_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_VERSION) -#define JSON_HEDLEY_TI_VERSION_CHECK(major, minor, patch) \ - (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_TI_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(JSON_HEDLEY_CRAY_VERSION) -#undef JSON_HEDLEY_CRAY_VERSION -#endif -#if defined(_CRAYC) -#if defined(_RELEASE_PATCHLEVEL) -#define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) -#else -#define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) -#endif -#endif - -#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) -#undef JSON_HEDLEY_CRAY_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_CRAY_VERSION) -#define JSON_HEDLEY_CRAY_VERSION_CHECK(major, minor, patch) \ - (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_CRAY_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(JSON_HEDLEY_IAR_VERSION) -#undef JSON_HEDLEY_IAR_VERSION -#endif -#if defined(__IAR_SYSTEMS_ICC__) -#if __VER__ > 1000 -#define JSON_HEDLEY_IAR_VERSION \ - JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) -#else -#define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(VER / 100, __VER__ % 100, 0) -#endif -#endif - -#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) -#undef JSON_HEDLEY_IAR_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_IAR_VERSION) -#define JSON_HEDLEY_IAR_VERSION_CHECK(major, minor, patch) \ - (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_IAR_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(JSON_HEDLEY_TINYC_VERSION) -#undef JSON_HEDLEY_TINYC_VERSION -#endif -#if defined(__TINYC__) -#define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) -#endif - -#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) -#undef JSON_HEDLEY_TINYC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TINYC_VERSION) -#define JSON_HEDLEY_TINYC_VERSION_CHECK(major, minor, patch) \ - (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_TINYC_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(JSON_HEDLEY_DMC_VERSION) -#undef JSON_HEDLEY_DMC_VERSION -#endif -#if defined(__DMC__) -#define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) -#endif - -#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) -#undef JSON_HEDLEY_DMC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_DMC_VERSION) -#define JSON_HEDLEY_DMC_VERSION_CHECK(major, minor, patch) \ - (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_DMC_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(JSON_HEDLEY_COMPCERT_VERSION) -#undef JSON_HEDLEY_COMPCERT_VERSION -#endif -#if defined(__COMPCERT_VERSION__) -#define JSON_HEDLEY_COMPCERT_VERSION \ - JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, \ - __COMPCERT_VERSION__ % 100) -#endif - -#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) -#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_COMPCERT_VERSION) -#define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major, minor, patch) \ - (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(JSON_HEDLEY_PELLES_VERSION) -#undef JSON_HEDLEY_PELLES_VERSION -#endif -#if defined(__POCC__) -#define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) -#endif - -#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) -#undef JSON_HEDLEY_PELLES_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_PELLES_VERSION) -#define JSON_HEDLEY_PELLES_VERSION_CHECK(major, minor, patch) \ - (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_PELLES_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(JSON_HEDLEY_GCC_VERSION) -#undef JSON_HEDLEY_GCC_VERSION -#endif -#if defined(JSON_HEDLEY_GNUC_VERSION) && !defined(__clang__) && !defined(JSON_HEDLEY_INTEL_VERSION) && \ - !defined(JSON_HEDLEY_PGI_VERSION) && !defined(JSON_HEDLEY_ARM_VERSION) && !defined(JSON_HEDLEY_TI_VERSION) && \ - !defined(__COMPCERT__) -#define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION -#endif - -#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) -#undef JSON_HEDLEY_GCC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_GCC_VERSION) -#define JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) \ - (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else -#define JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) (0) -#endif - -#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) -#undef JSON_HEDLEY_HAS_ATTRIBUTE -#endif -#if defined(__has_attribute) -#define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) -#else -#define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) -#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE -#endif -#if defined(__has_attribute) -#define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute, major, minor, patch) __has_attribute(attribute) -#else -#define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute, major, minor, patch) \ - JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) -#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE -#endif -#if defined(__has_attribute) -#define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute, major, minor, patch) __has_attribute(attribute) -#else -#define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute, major, minor, patch) JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) -#endif - -#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) -#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE -#endif -#if defined(__has_cpp_attribute) && defined(__cplusplus) -#define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) -#else -#define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) -#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE -#endif -#if defined(__has_cpp_attribute) && defined(__cplusplus) -#define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute, major, minor, patch) __has_cpp_attribute(attribute) -#else -#define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute, major, minor, patch) \ - JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) -#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE -#endif -#if defined(__has_cpp_attribute) && defined(__cplusplus) -#define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute, major, minor, patch) __has_cpp_attribute(attribute) -#else -#define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute, major, minor, patch) \ - JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) -#endif - -#if defined(JSON_HEDLEY_HAS_BUILTIN) -#undef JSON_HEDLEY_HAS_BUILTIN -#endif -#if defined(__has_builtin) -#define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) -#else -#define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) -#undef JSON_HEDLEY_GNUC_HAS_BUILTIN -#endif -#if defined(__has_builtin) -#define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin, major, minor, patch) __has_builtin(builtin) -#else -#define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin, major, minor, patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) -#undef JSON_HEDLEY_GCC_HAS_BUILTIN -#endif -#if defined(__has_builtin) -#define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin, major, minor, patch) __has_builtin(builtin) -#else -#define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin, major, minor, patch) JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) -#endif - -#if defined(JSON_HEDLEY_HAS_FEATURE) -#undef JSON_HEDLEY_HAS_FEATURE -#endif -#if defined(__has_feature) -#define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) -#else -#define JSON_HEDLEY_HAS_FEATURE(feature) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) -#undef JSON_HEDLEY_GNUC_HAS_FEATURE -#endif -#if defined(__has_feature) -#define JSON_HEDLEY_GNUC_HAS_FEATURE(feature, major, minor, patch) __has_feature(feature) -#else -#define JSON_HEDLEY_GNUC_HAS_FEATURE(feature, major, minor, patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) -#undef JSON_HEDLEY_GCC_HAS_FEATURE -#endif -#if defined(__has_feature) -#define JSON_HEDLEY_GCC_HAS_FEATURE(feature, major, minor, patch) __has_feature(feature) -#else -#define JSON_HEDLEY_GCC_HAS_FEATURE(feature, major, minor, patch) JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) -#endif - -#if defined(JSON_HEDLEY_HAS_EXTENSION) -#undef JSON_HEDLEY_HAS_EXTENSION -#endif -#if defined(__has_extension) -#define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) -#else -#define JSON_HEDLEY_HAS_EXTENSION(extension) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) -#undef JSON_HEDLEY_GNUC_HAS_EXTENSION -#endif -#if defined(__has_extension) -#define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension, major, minor, patch) __has_extension(extension) -#else -#define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension, major, minor, patch) \ - JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) -#undef JSON_HEDLEY_GCC_HAS_EXTENSION -#endif -#if defined(__has_extension) -#define JSON_HEDLEY_GCC_HAS_EXTENSION(extension, major, minor, patch) __has_extension(extension) -#else -#define JSON_HEDLEY_GCC_HAS_EXTENSION(extension, major, minor, patch) JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) -#endif - -#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) -#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE -#endif -#if defined(__has_declspec_attribute) -#define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) -#else -#define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) -#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE -#endif -#if defined(__has_declspec_attribute) -#define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute, major, minor, patch) __has_declspec_attribute(attribute) -#else -#define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute, major, minor, patch) \ - JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) -#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE -#endif -#if defined(__has_declspec_attribute) -#define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute, major, minor, patch) __has_declspec_attribute(attribute) -#else -#define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute, major, minor, patch) \ - JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) -#endif - -#if defined(JSON_HEDLEY_HAS_WARNING) -#undef JSON_HEDLEY_HAS_WARNING -#endif -#if defined(__has_warning) -#define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) -#else -#define JSON_HEDLEY_HAS_WARNING(warning) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) -#undef JSON_HEDLEY_GNUC_HAS_WARNING -#endif -#if defined(__has_warning) -#define JSON_HEDLEY_GNUC_HAS_WARNING(warning, major, minor, patch) __has_warning(warning) -#else -#define JSON_HEDLEY_GNUC_HAS_WARNING(warning, major, minor, patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major, minor, patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_WARNING) -#undef JSON_HEDLEY_GCC_HAS_WARNING -#endif -#if defined(__has_warning) -#define JSON_HEDLEY_GCC_HAS_WARNING(warning, major, minor, patch) __has_warning(warning) -#else -#define JSON_HEDLEY_GCC_HAS_WARNING(warning, major, minor, patch) JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) -#endif - -#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || defined(__clang__) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3, 0, 0) || JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) || JSON_HEDLEY_PGI_VERSION_CHECK(18, 4, 0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || JSON_HEDLEY_TI_VERSION_CHECK(6, 0, 0) || \ - JSON_HEDLEY_CRAY_VERSION_CHECK(5, 0, 0) || JSON_HEDLEY_TINYC_VERSION_CHECK(0, 9, 17) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(8, 0, 0) || \ - (JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) && defined(__C99_PRAGMA_OPERATOR)) -#define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15, 0, 0) -#define JSON_HEDLEY_PRAGMA(value) __pragma(value) -#else -#define JSON_HEDLEY_PRAGMA(value) -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) -#undef JSON_HEDLEY_DIAGNOSTIC_PUSH -#endif -#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) -#undef JSON_HEDLEY_DIAGNOSTIC_POP -#endif -#if defined(__clang__) -#define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") -#define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) -#define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") -#define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4, 6, 0) -#define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") -#define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15, 0, 0) -#define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) -#define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) -#elif JSON_HEDLEY_ARM_VERSION_CHECK(5, 6, 0) -#define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") -#define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") -#elif JSON_HEDLEY_TI_VERSION_CHECK(8, 1, 0) -#define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") -#define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2, 90, 0) -#define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") -#define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") -#else -#define JSON_HEDLEY_DIAGNOSTIC_PUSH -#define JSON_HEDLEY_DIAGNOSTIC_POP -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) -#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") -#elif JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4, 3, 0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15, 0, 0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable : 4996)) -#elif JSON_HEDLEY_TI_VERSION_CHECK(8, 0, 0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 13, 0) && !defined(__cplusplus) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 13, 0) && defined(__cplusplus) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2, 90, 0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") -#else -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) -#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") -#elif JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4, 3, 0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15, 0, 0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable : 4068)) -#elif JSON_HEDLEY_TI_VERSION_CHECK(8, 0, 0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") -#else -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) -#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(3, 0, 0) -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") -#else -#define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL -#endif - -#if defined(JSON_HEDLEY_DEPRECATED) -#undef JSON_HEDLEY_DEPRECATED -#endif -#if defined(JSON_HEDLEY_DEPRECATED_FOR) -#undef JSON_HEDLEY_DEPRECATED_FOR -#endif -#if defined(__cplusplus) && (__cplusplus >= 201402L) -#define JSON_HEDLEY_DEPRECATED(since) [[deprecated("Since " #since)]] -#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) [[deprecated("Since " #since "; use " #replacement)]] -#elif JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) || JSON_HEDLEY_GCC_VERSION_CHECK(4, 5, 0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_ARM_VERSION_CHECK(5, 6, 0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 13, 0) || JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8, 3, 0) -#define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) -#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) \ - __attribute__((__deprecated__("Since " #since "; use " #replacement))) -#elif JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 1, 0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || JSON_HEDLEY_TI_VERSION_CHECK(8, 0, 0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7, 3, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) -#define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) -#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(14, 0, 0) -#define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " #since)) -#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13, 10, 0) || JSON_HEDLEY_PELLES_VERSION_CHECK(6, 50, 0) -#define JSON_HEDLEY_DEPRECATED(since) _declspec(deprecated) -#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) -#define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") -#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") -#else -#define JSON_HEDLEY_DEPRECATED(since) -#define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) -#endif - -#if defined(JSON_HEDLEY_UNAVAILABLE) -#undef JSON_HEDLEY_UNAVAILABLE -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(warning) || JSON_HEDLEY_GCC_VERSION_CHECK(4, 3, 0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) -#define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) -#else -#define JSON_HEDLEY_UNAVAILABLE(available_since) -#endif - -#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) -#undef JSON_HEDLEY_WARN_UNUSED_RESULT -#endif -#if defined(__cplusplus) && (__cplusplus >= 201703L) -#define JSON_HEDLEY_WARN_UNUSED_RESULT [[nodiscard]] -#elif JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 4, 0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_TI_VERSION_CHECK(8, 0, 0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7, 3, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 15, 0) && defined(__cplusplus)) || JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) -#define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) -#elif defined(_Check_return_) /* SAL */ -#define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ -#else -#define JSON_HEDLEY_WARN_UNUSED_RESULT -#endif - -#if defined(JSON_HEDLEY_SENTINEL) -#undef JSON_HEDLEY_SENTINEL -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || JSON_HEDLEY_GCC_VERSION_CHECK(4, 0, 0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_ARM_VERSION_CHECK(5, 4, 0) -#define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) -#else -#define JSON_HEDLEY_SENTINEL(position) -#endif - -#if defined(JSON_HEDLEY_NO_RETURN) -#undef JSON_HEDLEY_NO_RETURN -#endif -#if JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) -#define JSON_HEDLEY_NO_RETURN __noreturn -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) -#define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) -#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L -#define JSON_HEDLEY_NO_RETURN _Noreturn -#elif defined(__cplusplus) && (__cplusplus >= 201103L) -#define JSON_HEDLEY_NO_RETURN [[noreturn]] -#elif JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 2, 0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) || JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) || JSON_HEDLEY_TI_VERSION_CHECK(18, 0, 0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(17, 3, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) -#define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13, 10, 0) -#define JSON_HEDLEY_NO_RETURN __declspec(noreturn) -#elif JSON_HEDLEY_TI_VERSION_CHECK(6, 0, 0) && defined(__cplusplus) -#define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") -#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3, 2, 0) -#define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9, 0, 0) -#define JSON_HEDLEY_NO_RETURN __declspec(noreturn) -#else -#define JSON_HEDLEY_NO_RETURN -#endif - -#if defined(JSON_HEDLEY_UNREACHABLE) -#undef JSON_HEDLEY_UNREACHABLE -#endif -#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) -#undef JSON_HEDLEY_UNREACHABLE_RETURN -#endif -#if (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4, 5, 0) || JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(13, 1, 5) -#define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13, 10, 0) -#define JSON_HEDLEY_UNREACHABLE() __assume(0) -#elif JSON_HEDLEY_TI_VERSION_CHECK(6, 0, 0) -#if defined(__cplusplus) -#define JSON_HEDLEY_UNREACHABLE() std::_nassert(0) -#else -#define JSON_HEDLEY_UNREACHABLE() _nassert(0) -#endif -#define JSON_HEDLEY_UNREACHABLE_RETURN(value) return value -#elif defined(EXIT_FAILURE) -#define JSON_HEDLEY_UNREACHABLE() abort() -#else -#define JSON_HEDLEY_UNREACHABLE() -#define JSON_HEDLEY_UNREACHABLE_RETURN(value) return value -#endif -#if !defined(JSON_HEDLEY_UNREACHABLE_RETURN) -#define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() -#endif - -#if defined(JSON_HEDLEY_ASSUME) -#undef JSON_HEDLEY_ASSUME -#endif -#if JSON_HEDLEY_MSVC_VERSION_CHECK(13, 10, 0) || JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) -#define JSON_HEDLEY_ASSUME(expr) __assume(expr) -#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) -#define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) -#elif JSON_HEDLEY_TI_VERSION_CHECK(6, 0, 0) -#if defined(__cplusplus) -#define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) -#else -#define JSON_HEDLEY_ASSUME(expr) _nassert(expr) -#endif -#elif (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && !defined(JSON_HEDLEY_ARM_VERSION)) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4, 5, 0) || JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(13, 1, 5) -#define JSON_HEDLEY_ASSUME(expr) ((void)((expr) ? 1 : (__builtin_unreachable(), 1))) -#else -#define JSON_HEDLEY_ASSUME(expr) ((void)(expr)) -#endif - -JSON_HEDLEY_DIAGNOSTIC_PUSH -#if JSON_HEDLEY_HAS_WARNING("-Wvariadic-macros") || JSON_HEDLEY_GCC_VERSION_CHECK(4, 0, 0) -#if defined(__clang__) -#pragma clang diagnostic ignored "-Wvariadic-macros" -#elif defined(JSON_HEDLEY_GCC_VERSION) -#pragma GCC diagnostic ignored "-Wvariadic-macros" -#endif -#endif -#if defined(JSON_HEDLEY_NON_NULL) -#undef JSON_HEDLEY_NON_NULL -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 3, 0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) -#define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) -#else -#define JSON_HEDLEY_NON_NULL(...) -#endif -JSON_HEDLEY_DIAGNOSTIC_POP - -#if defined(JSON_HEDLEY_PRINTF_FORMAT) -#undef JSON_HEDLEY_PRINTF_FORMAT -#endif -#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format, 4, 4, 0) && !defined(__USE_MINGW_ANSI_STDIO) -#define JSON_HEDLEY_PRINTF_FORMAT(string_idx, first_to_check) \ - __attribute__((__format__(ms_printf, string_idx, first_to_check))) -#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format, 4, 4, 0) && defined(__USE_MINGW_ANSI_STDIO) -#define JSON_HEDLEY_PRINTF_FORMAT(string_idx, first_to_check) \ - __attribute__((__format__(gnu_printf, string_idx, first_to_check))) -#elif JSON_HEDLEY_HAS_ATTRIBUTE(format) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 1, 0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_ARM_VERSION_CHECK(5, 6, 0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) || JSON_HEDLEY_TI_VERSION_CHECK(8, 0, 0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7, 3, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) -#define JSON_HEDLEY_PRINTF_FORMAT(string_idx, first_to_check) \ - __attribute__((__format__(__printf__, string_idx, first_to_check))) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6, 0, 0) -#define JSON_HEDLEY_PRINTF_FORMAT(string_idx, first_to_check) __declspec(vaformat(printf, string_idx, first_to_check)) -#else -#define JSON_HEDLEY_PRINTF_FORMAT(string_idx, first_to_check) -#endif - -#if defined(JSON_HEDLEY_CONSTEXPR) -#undef JSON_HEDLEY_CONSTEXPR -#endif -#if defined(__cplusplus) -#if __cplusplus >= 201103L -#define JSON_HEDLEY_CONSTEXPR constexpr -#endif -#endif -#if !defined(JSON_HEDLEY_CONSTEXPR) -#define JSON_HEDLEY_CONSTEXPR -#endif - -#if defined(JSON_HEDLEY_PREDICT) -#undef JSON_HEDLEY_PREDICT -#endif -#if defined(JSON_HEDLEY_LIKELY) -#undef JSON_HEDLEY_LIKELY -#endif -#if defined(JSON_HEDLEY_UNLIKELY) -#undef JSON_HEDLEY_UNLIKELY -#endif -#if defined(JSON_HEDLEY_UNPREDICTABLE) -#undef JSON_HEDLEY_UNPREDICTABLE -#endif -#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) -#define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable(!!(expr)) -#endif -#if JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) || JSON_HEDLEY_GCC_VERSION_CHECK(9, 0, 0) -#define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability(expr, value, probability) -#define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1, probability) -#define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0, probability) -#define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) -#define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) -#if !defined(JSON_HEDLEY_BUILTIN_UNPREDICTABLE) -#define JSON_HEDLEY_BUILTIN_UNPREDICTABLE(expr) __builtin_expect_with_probability(!!(expr), 1, 0.5) -#endif -#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 0, 0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || \ - (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 15, 0) && defined(__cplusplus)) || JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) || JSON_HEDLEY_TI_VERSION_CHECK(6, 1, 0) || \ - JSON_HEDLEY_TINYC_VERSION_CHECK(0, 9, 27) -#define JSON_HEDLEY_PREDICT(expr, expected, probability) \ - (((probability) >= 0.9) ? __builtin_expect(!!(expr), (expected)) : (((void)(expected)), !!(expr))) -#define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ - (__extension__({ \ - JSON_HEDLEY_CONSTEXPR double hedley_probability_ = (probability); \ - ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) \ - : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ - })) -#define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ - (__extension__({ \ - JSON_HEDLEY_CONSTEXPR double hedley_probability_ = (probability); \ - ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) \ - : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ - })) -#define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) -#define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) -#else -#define JSON_HEDLEY_PREDICT(expr, expected, probability) (((void)(expected)), !!(expr)) -#define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) -#define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) -#define JSON_HEDLEY_LIKELY(expr) (!!(expr)) -#define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) -#endif -#if !defined(JSON_HEDLEY_UNPREDICTABLE) -#define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) -#endif - -#if defined(JSON_HEDLEY_MALLOC) -#undef JSON_HEDLEY_MALLOC -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 1, 0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || JSON_HEDLEY_IBM_VERSION_CHECK(12, 1, 0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8, 0, 0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7, 3, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) -#define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(14, 0, 0) -#define JSON_HEDLEY_MALLOC __declspec(restrict) -#else -#define JSON_HEDLEY_MALLOC -#endif - -#if defined(JSON_HEDLEY_PURE) -#undef JSON_HEDLEY_PURE -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(pure) || JSON_HEDLEY_GCC_VERSION_CHECK(2, 96, 0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8, 0, 0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7, 3, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) -#define JSON_HEDLEY_PURE __attribute__((__pure__)) -#elif JSON_HEDLEY_TI_VERSION_CHECK(6, 0, 0) && defined(__cplusplus) -#define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") -#else -#define JSON_HEDLEY_PURE -#endif - -#if defined(JSON_HEDLEY_CONST) -#undef JSON_HEDLEY_CONST -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(const) || JSON_HEDLEY_GCC_VERSION_CHECK(2, 5, 0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8, 0, 0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7, 3, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) -#define JSON_HEDLEY_CONST __attribute__((__const__)) -#else -#define JSON_HEDLEY_CONST JSON_HEDLEY_PURE -#endif - -#if defined(JSON_HEDLEY_RESTRICT) -#undef JSON_HEDLEY_RESTRICT -#endif -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) -#define JSON_HEDLEY_RESTRICT restrict -#elif JSON_HEDLEY_GCC_VERSION_CHECK(3, 1, 0) || JSON_HEDLEY_MSVC_VERSION_CHECK(14, 0, 0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) || JSON_HEDLEY_PGI_VERSION_CHECK(17, 10, 0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8, 0, 0) || (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 14, 0) && defined(__cplusplus)) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) || defined(__clang__) -#define JSON_HEDLEY_RESTRICT __restrict -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 3, 0) && !defined(__cplusplus) -#define JSON_HEDLEY_RESTRICT _Restrict -#else -#define JSON_HEDLEY_RESTRICT -#endif - -#if defined(JSON_HEDLEY_INLINE) -#undef JSON_HEDLEY_INLINE -#endif -#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || (defined(__cplusplus) && (__cplusplus >= 199711L)) -#define JSON_HEDLEY_INLINE inline -#elif defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_ARM_VERSION_CHECK(6, 2, 0) -#define JSON_HEDLEY_INLINE __inline__ -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(12, 0, 0) || JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8, 0, 0) -#define JSON_HEDLEY_INLINE __inline -#else -#define JSON_HEDLEY_INLINE -#endif - -#if defined(JSON_HEDLEY_ALWAYS_INLINE) -#undef JSON_HEDLEY_ALWAYS_INLINE -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || JSON_HEDLEY_GCC_VERSION_CHECK(4, 0, 0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8, 0, 0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7, 3, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) -#define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(12, 0, 0) -#define JSON_HEDLEY_ALWAYS_INLINE __forceinline -#elif JSON_HEDLEY_TI_VERSION_CHECK(7, 0, 0) && defined(__cplusplus) -#define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) -#define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") -#else -#define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE -#endif - -#if defined(JSON_HEDLEY_NEVER_INLINE) -#undef JSON_HEDLEY_NEVER_INLINE -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || JSON_HEDLEY_GCC_VERSION_CHECK(4, 0, 0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || JSON_HEDLEY_IBM_VERSION_CHECK(10, 1, 0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8, 0, 0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7, 3, 0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) -#define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13, 10, 0) -#define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) -#elif JSON_HEDLEY_PGI_VERSION_CHECK(10, 2, 0) -#define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") -#elif JSON_HEDLEY_TI_VERSION_CHECK(6, 0, 0) && defined(__cplusplus) -#define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) -#define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") -#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3, 2, 0) -#define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9, 0, 0) -#define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) -#else -#define JSON_HEDLEY_NEVER_INLINE -#endif - -#if defined(JSON_HEDLEY_PRIVATE) -#undef JSON_HEDLEY_PRIVATE -#endif -#if defined(JSON_HEDLEY_PUBLIC) -#undef JSON_HEDLEY_PUBLIC -#endif -#if defined(JSON_HEDLEY_IMPORT) -#undef JSON_HEDLEY_IMPORT -#endif -#if defined(_WIN32) || defined(__CYGWIN__) -#define JSON_HEDLEY_PRIVATE -#define JSON_HEDLEY_PUBLIC __declspec(dllexport) -#define JSON_HEDLEY_IMPORT __declspec(dllimport) -#else -#if JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 3, 0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 11, 0) || JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || JSON_HEDLEY_IBM_VERSION_CHECK(13, 1, 0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(8, 0, 0) || \ - (JSON_HEDLEY_TI_VERSION_CHECK(7, 3, 0) && defined(__TI_EABI__) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) -#define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) -#define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) -#else -#define JSON_HEDLEY_PRIVATE -#define JSON_HEDLEY_PUBLIC -#endif -#define JSON_HEDLEY_IMPORT extern -#endif - -#if defined(JSON_HEDLEY_NO_THROW) -#undef JSON_HEDLEY_NO_THROW -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 3, 0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) -#define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(13, 1, 0) || JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) -#define JSON_HEDLEY_NO_THROW __declspec(nothrow) -#else -#define JSON_HEDLEY_NO_THROW -#endif - -#if defined(JSON_HEDLEY_FALL_THROUGH) -#undef JSON_HEDLEY_FALL_THROUGH -#endif -#if defined(__cplusplus) && (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 15, 0)) && \ - !defined(JSON_HEDLEY_PGI_VERSION) -#if (__cplusplus >= 201703L) || ((__cplusplus >= 201103L) && JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough)) -#define JSON_HEDLEY_FALL_THROUGH [[fallthrough]] -#elif (__cplusplus >= 201103L) && JSON_HEDLEY_HAS_CPP_ATTRIBUTE(clang::fallthrough) -#define JSON_HEDLEY_FALL_THROUGH [[clang::fallthrough]] -#elif (__cplusplus >= 201103L) && JSON_HEDLEY_GCC_VERSION_CHECK(7, 0, 0) -#define JSON_HEDLEY_FALL_THROUGH [[gnu::fallthrough]] -#endif -#endif -#if !defined(JSON_HEDLEY_FALL_THROUGH) -#if JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(fallthrough, 7, 0, 0) && !defined(JSON_HEDLEY_PGI_VERSION) -#define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) -#elif defined(__fallthrough) /* SAL */ -#define JSON_HEDLEY_FALL_THROUGH __fallthrough -#else -#define JSON_HEDLEY_FALL_THROUGH -#endif -#endif - -#if defined(JSON_HEDLEY_RETURNS_NON_NULL) -#undef JSON_HEDLEY_RETURNS_NON_NULL -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || JSON_HEDLEY_GCC_VERSION_CHECK(4, 9, 0) -#define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) -#elif defined(_Ret_notnull_) /* SAL */ -#define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ -#else -#define JSON_HEDLEY_RETURNS_NON_NULL -#endif - -#if defined(JSON_HEDLEY_ARRAY_PARAM) -#undef JSON_HEDLEY_ARRAY_PARAM -#endif -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__STDC_NO_VLA__) && \ - !defined(__cplusplus) && !defined(JSON_HEDLEY_PGI_VERSION) && !defined(JSON_HEDLEY_TINYC_VERSION) -#define JSON_HEDLEY_ARRAY_PARAM(name) (name) -#else -#define JSON_HEDLEY_ARRAY_PARAM(name) -#endif - -#if defined(JSON_HEDLEY_IS_CONSTANT) -#undef JSON_HEDLEY_IS_CONSTANT -#endif -#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) -#undef JSON_HEDLEY_REQUIRE_CONSTEXPR -#endif -/* Note the double-underscore. For internal use only; no API - * guarantees! */ -#if defined(JSON_HEDLEY__IS_CONSTEXPR) -#undef JSON_HEDLEY__IS_CONSTEXPR -#endif - -#if JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 4, 0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_TINYC_VERSION_CHECK(0, 9, 19) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4, 1, 0) || JSON_HEDLEY_IBM_VERSION_CHECK(13, 1, 0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(6, 1, 0) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5, 10, 0) || \ - JSON_HEDLEY_CRAY_VERSION_CHECK(8, 1, 0) -#define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) -#endif -#if !defined(__cplusplus) -#if JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || JSON_HEDLEY_GCC_VERSION_CHECK(3, 4, 0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || JSON_HEDLEY_IBM_VERSION_CHECK(13, 1, 0) || \ - JSON_HEDLEY_CRAY_VERSION_CHECK(8, 1, 0) || JSON_HEDLEY_ARM_VERSION_CHECK(5, 4, 0) || \ - JSON_HEDLEY_TINYC_VERSION_CHECK(0, 9, 24) -#if defined(__INTPTR_TYPE__) -#define JSON_HEDLEY__IS_CONSTEXPR(expr) \ - __builtin_types_compatible_p(__typeof__((1 ? (void*)((__INTPTR_TYPE__)((expr)*0)) : (int*)0)), int*) -#else -#include -#define JSON_HEDLEY__IS_CONSTEXPR(expr) \ - __builtin_types_compatible_p(__typeof__((1 ? (void*)((intptr_t)((expr)*0)) : (int*)0)), int*) -#endif -#elif (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ - !defined(JSON_HEDLEY_PGI_VERSION)) || \ - JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) || JSON_HEDLEY_GCC_VERSION_CHECK(4, 9, 0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(17, 0, 0) || JSON_HEDLEY_IBM_VERSION_CHECK(12, 1, 0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(5, 3, 0) -#if defined(__INTPTR_TYPE__) -#define JSON_HEDLEY__IS_CONSTEXPR(expr) \ - _Generic((1 ? (void*)((__INTPTR_TYPE__)((expr)*0)) : (int*)0), int* : 1, void* : 0) -#else -#include -#define JSON_HEDLEY__IS_CONSTEXPR(expr) _Generic((1 ? (void*)((intptr_t)*0) : (int*)0), int* : 1, void* : 0) -#endif -#elif defined(JSON_HEDLEY_GCC_VERSION) || defined(JSON_HEDLEY_INTEL_VERSION) || defined(JSON_HEDLEY_TINYC_VERSION) || \ - defined(JSON_HEDLEY_TI_VERSION) || defined(__clang__) -#define JSON_HEDLEY__IS_CONSTEXPR(expr) \ - (sizeof(void) != sizeof(*(1 ? ((void*)((expr)*0L)) : ((struct { char v[sizeof(void) * 2]; }*)1)))) -#endif -#endif -#if defined(JSON_HEDLEY__IS_CONSTEXPR) -#if !defined(JSON_HEDLEY_IS_CONSTANT) -#define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY__IS_CONSTEXPR(expr) -#endif -#define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY__IS_CONSTEXPR(expr) ? (expr) : (-1)) -#else -#if !defined(JSON_HEDLEY_IS_CONSTANT) -#define JSON_HEDLEY_IS_CONSTANT(expr) (0) -#endif -#define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) -#endif - -#if defined(JSON_HEDLEY_BEGIN_C_DECLS) -#undef JSON_HEDLEY_BEGIN_C_DECLS -#endif -#if defined(JSON_HEDLEY_END_C_DECLS) -#undef JSON_HEDLEY_END_C_DECLS -#endif -#if defined(JSON_HEDLEY_C_DECL) -#undef JSON_HEDLEY_C_DECL -#endif -#if defined(__cplusplus) -#define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { -#define JSON_HEDLEY_END_C_DECLS } -#define JSON_HEDLEY_C_DECL extern "C" -#else -#define JSON_HEDLEY_BEGIN_C_DECLS -#define JSON_HEDLEY_END_C_DECLS -#define JSON_HEDLEY_C_DECL -#endif - -#if defined(JSON_HEDLEY_STATIC_ASSERT) -#undef JSON_HEDLEY_STATIC_ASSERT -#endif -#if !defined(__cplusplus) && \ - ((defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || JSON_HEDLEY_HAS_FEATURE(c_static_assert) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(6, 0, 0) || JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) || defined(_Static_assert)) -#define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) -#elif (defined(__cplusplus) && (__cplusplus >= 201703L)) || JSON_HEDLEY_MSVC_VERSION_CHECK(16, 0, 0) || \ - (defined(__cplusplus) && JSON_HEDLEY_TI_VERSION_CHECK(8, 3, 0)) -#define JSON_HEDLEY_STATIC_ASSERT(expr, message) static_assert(expr, message) -#elif defined(__cplusplus) && (__cplusplus >= 201103L) -#define JSON_HEDLEY_STATIC_ASSERT(expr, message) static_assert(expr) -#else -#define JSON_HEDLEY_STATIC_ASSERT(expr, message) -#endif - -#if defined(JSON_HEDLEY_CONST_CAST) -#undef JSON_HEDLEY_CONST_CAST -#endif -#if defined(__cplusplus) -#define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) -#elif JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || JSON_HEDLEY_GCC_VERSION_CHECK(4, 6, 0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) -#define JSON_HEDLEY_CONST_CAST(T, expr) \ - (__extension__({ \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL((T)(expr)); \ - JSON_HEDLEY_DIAGNOSTIC_POP \ - })) -#else -#define JSON_HEDLEY_CONST_CAST(T, expr) ((T)(expr)) -#endif - -#if defined(JSON_HEDLEY_REINTERPRET_CAST) -#undef JSON_HEDLEY_REINTERPRET_CAST -#endif -#if defined(__cplusplus) -#define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) -#else -#define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (*((T*)&(expr))) -#endif - -#if defined(JSON_HEDLEY_STATIC_CAST) -#undef JSON_HEDLEY_STATIC_CAST -#endif -#if defined(__cplusplus) -#define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) -#else -#define JSON_HEDLEY_STATIC_CAST(T, expr) ((T)(expr)) -#endif - -#if defined(JSON_HEDLEY_CPP_CAST) -#undef JSON_HEDLEY_CPP_CAST -#endif -#if defined(__cplusplus) -#define JSON_HEDLEY_CPP_CAST(T, expr) static_cast(expr) -#else -#define JSON_HEDLEY_CPP_CAST(T, expr) (expr) -#endif - -#if defined(JSON_HEDLEY_MESSAGE) -#undef JSON_HEDLEY_MESSAGE -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") -#define JSON_HEDLEY_MESSAGE(msg) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ - JSON_HEDLEY_PRAGMA(message msg) \ - JSON_HEDLEY_DIAGNOSTIC_POP -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4, 4, 0) || JSON_HEDLEY_INTEL_VERSION_CHECK(13, 0, 0) -#define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) -#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5, 0, 0) -#define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8, 0, 0) -#define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2, 0, 0) -#define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) -#else -#define JSON_HEDLEY_MESSAGE(msg) -#endif - -#if defined(JSON_HEDLEY_WARNING) -#undef JSON_HEDLEY_WARNING -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") -#define JSON_HEDLEY_WARNING(msg) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ - JSON_HEDLEY_PRAGMA(clang warning msg) \ - JSON_HEDLEY_DIAGNOSTIC_POP -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4, 8, 0) || JSON_HEDLEY_PGI_VERSION_CHECK(18, 4, 0) -#define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15, 0, 0) -#define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) -#else -#define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) -#endif - -#if defined(JSON_HEDLEY_REQUIRE_MSG) -#undef JSON_HEDLEY_REQUIRE_MSG -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) -#if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") -#define JSON_HEDLEY_REQUIRE_MSG(expr, msg) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") __attribute__((__diagnose_if__(!(expr), msg, "error"))) \ - JSON_HEDLEY_DIAGNOSTIC_POP -#else -#define JSON_HEDLEY_REQUIRE_MSG(expr, msg) __attribute__((__diagnose_if__(!(expr), msg, "error"))) -#endif -#else -#define JSON_HEDLEY_REQUIRE_MSG(expr, msg) -#endif - -#if defined(JSON_HEDLEY_REQUIRE) -#undef JSON_HEDLEY_REQUIRE -#endif -#define JSON_HEDLEY_REQUIRE(expr) JSON_HEDLEY_REQUIRE_MSG(expr, #expr) - -#if defined(JSON_HEDLEY_FLAGS) -#undef JSON_HEDLEY_FLAGS -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) -#define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) -#endif - -#if defined(JSON_HEDLEY_FLAGS_CAST) -#undef JSON_HEDLEY_FLAGS_CAST -#endif -#if JSON_HEDLEY_INTEL_VERSION_CHECK(19, 0, 0) -#define JSON_HEDLEY_FLAGS_CAST(T, expr) \ - (__extension__({ \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("warning(disable:188)")((T)(expr)); \ - JSON_HEDLEY_DIAGNOSTIC_POP \ - })) -#else -#define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) -#endif - -/* Remaining macros are deprecated. */ - -#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) -#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK -#endif -#if defined(__clang__) -#define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major, minor, patch) (0) -#else -#define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major, minor, patch) JSON_HEDLEY_GCC_VERSION_CHECK(major, minor, patch) -#endif - -#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) -#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE -#endif -#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) - -#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) -#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE -#endif -#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) - -#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) -#undef JSON_HEDLEY_CLANG_HAS_BUILTIN -#endif -#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) - -#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) -#undef JSON_HEDLEY_CLANG_HAS_FEATURE -#endif -#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) - -#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) -#undef JSON_HEDLEY_CLANG_HAS_EXTENSION -#endif -#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) - -#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) -#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE -#endif -#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) - -#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) -#undef JSON_HEDLEY_CLANG_HAS_WARNING -#endif -#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) - -#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ - -// This file contains all internal macro definitions -// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them - -// exclude unsupported compilers -#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) -#if defined(__clang__) -#if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 -#error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" -#endif -#elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) -#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 -#error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" -#endif -#endif -#endif - -// C++ language standard detection -#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 -#define JSON_HAS_CPP_17 -#define JSON_HAS_CPP_14 -#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) -#define JSON_HAS_CPP_14 -#endif - -// disable float-equal warnings on GCC/clang -#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" -#endif - -// disable documentation warnings on clang -#if defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdocumentation" -#endif - -// allow to disable exceptions -#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) -#define JSON_THROW(exception) throw exception -#define JSON_TRY try -#define JSON_CATCH(exception) catch (exception) -#define JSON_INTERNAL_CATCH(exception) catch (exception) -#else -#include -#define JSON_THROW(exception) std::abort() -#define JSON_TRY if (true) -#define JSON_CATCH(exception) if (false) -#define JSON_INTERNAL_CATCH(exception) if (false) -#endif - -// override exception macros -#if defined(JSON_THROW_USER) -#undef JSON_THROW -#define JSON_THROW JSON_THROW_USER -#endif -#if defined(JSON_TRY_USER) -#undef JSON_TRY -#define JSON_TRY JSON_TRY_USER -#endif -#if defined(JSON_CATCH_USER) -#undef JSON_CATCH -#define JSON_CATCH JSON_CATCH_USER -#undef JSON_INTERNAL_CATCH -#define JSON_INTERNAL_CATCH JSON_CATCH_USER -#endif -#if defined(JSON_INTERNAL_CATCH_USER) -#undef JSON_INTERNAL_CATCH -#define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER -#endif - -/*! -@brief macro to briefly define a mapping between an enum and JSON -@def NLOHMANN_JSON_SERIALIZE_ENUM -@since version 3.4.0 -*/ -#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ - template \ - inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) { \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if( \ - std::begin(m), std::end(m), \ - [e](const std::pair& ej_pair) -> bool { return ej_pair.first == e; }); \ - j = ((it != std::end(m)) ? it : std::begin(m))->second; \ - } \ - template \ - inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) { \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if( \ - std::begin(m), std::end(m), \ - [j](const std::pair& ej_pair) -> bool { return ej_pair.second == j; }); \ - e = ((it != std::end(m)) ? it : std::begin(m))->first; \ - } - -// Ugly macros to avoid uglier copy-paste when specializing basic_json. They -// may be removed in the future once the class is split. - -#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ - template

      AC!5stNXMX|4R58xJ-otNiKQA%H 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 nm_%c*w^Sc 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 Vhbi0y|B$%P%$7RGoS)Sc{|8Fn@vr~@ diff --git a/core/src/index/unittest/test_annoy.cpp b/core/src/index/unittest/test_annoy.cpp deleted file mode 100644 index 7787c44082..0000000000 --- a/core/src/index/unittest/test_annoy.cpp +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include -#include -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/IndexAnnoy.h" - -#include "unittest/utils.h" - -using ::testing::Combine; -using ::testing::TestWithParam; -using ::testing::Values; - -class AnnoyTest : public DataGen, public TestWithParam { - protected: - void - SetUp() override { - IndexType = GetParam(); - Generate(128, 10000, 10); - index_ = std::make_shared(); - conf = milvus::knowhere::Config{ - {milvus::knowhere::meta::DIM, dim}, - {milvus::knowhere::meta::TOPK, 10}, - {milvus::knowhere::IndexParams::n_trees, 4}, - {milvus::knowhere::IndexParams::search_k, 100}, - {milvus::knowhere::Metric::TYPE, milvus::knowhere::Metric::L2}, - }; - } - - protected: - milvus::knowhere::Config conf; - std::shared_ptr index_ = nullptr; - std::string IndexType; -}; - -INSTANTIATE_TEST_CASE_P(AnnoyParameters, AnnoyTest, Values("Annoy")); - -TEST_P(AnnoyTest, annoy_basic) { - assert(!xb.empty()); - - // null faiss index - { - ASSERT_ANY_THROW(index_->Train(base_dataset, conf)); - ASSERT_ANY_THROW(index_->Query(query_dataset, conf)); - ASSERT_ANY_THROW(index_->Serialize(conf)); - ASSERT_ANY_THROW(index_->Add(base_dataset, conf)); - ASSERT_ANY_THROW(index_->AddWithoutIds(base_dataset, conf)); - ASSERT_ANY_THROW(index_->Count()); - ASSERT_ANY_THROW(index_->Dim()); - } - - index_->BuildAll(base_dataset, conf); // Train + Add - ASSERT_EQ(index_->Count(), nb); - ASSERT_EQ(index_->Dim(), dim); - - auto result = index_->Query(query_dataset, conf); - AssertAnns(result, nq, k); - - /* - * output result to check by eyes - { - auto ids = result->Get(milvus::knowhere::meta::IDS); - auto dist = result->Get(milvus::knowhere::meta::DISTANCE); - - std::stringstream ss_id; - std::stringstream ss_dist; - for (auto i = 0; i < nq; i++) { - for (auto j = 0; j < k; ++j) { - // ss_id << *ids->data()->GetValues(1, i * k + j) << " "; - // ss_dist << *dists->data()->GetValues(1, i * k + j) << " "; - ss_id << *((int64_t*)(ids) + i * k + j) << " "; - ss_dist << *((float*)(dist) + i * k + j) << " "; - } - ss_id << std::endl; - ss_dist << std::endl; - } - std::cout << "id\n" << ss_id.str() << std::endl; - std::cout << "dist\n" << ss_dist.str() << std::endl; - } - */ -} - -TEST_P(AnnoyTest, annoy_delete) { - assert(!xb.empty()); - - index_->BuildAll(base_dataset, conf); // Train + Add - ASSERT_EQ(index_->Count(), nb); - ASSERT_EQ(index_->Dim(), dim); - - faiss::ConcurrentBitsetPtr bitset = std::make_shared(nb); - for (auto i = 0; i < nq; ++i) { - bitset->set(i); - } - - auto result1 = index_->Query(query_dataset, conf); - AssertAnns(result1, nq, k); - - index_->SetBlacklist(bitset); - auto result2 = index_->Query(query_dataset, conf); - AssertAnns(result2, nq, k, CheckMode::CHECK_NOT_EQUAL); - - /* - * delete result checked by eyes - auto ids1 = result1->Get(milvus::knowhere::meta::IDS); - auto ids2 = result2->Get(milvus::knowhere::meta::IDS); - std::cout << std::endl; - for (int i = 0; i < nq; ++ i) { - std::cout << "ids1: "; - for (int j = 0; j < k; ++ j) { - std::cout << *(ids1 + i * k + j) << " "; - } - std::cout << " ids2: "; - for (int j = 0; j < k; ++ j) { - std::cout << *(ids2 + i * k + j) << " "; - } - std::cout << std::endl; - for (int j = 0; j < std::min(5, k>>1); ++ j) { - ASSERT_EQ(*(ids1 + i * k + j + 1), *(ids2 + i * k + j)); - } - } - */ - /* - * output result to check by eyes - { - auto ids = result->Get(milvus::knowhere::meta::IDS); - auto dist = result->Get(milvus::knowhere::meta::DISTANCE); - - std::stringstream ss_id; - std::stringstream ss_dist; - for (auto i = 0; i < nq; i++) { - for (auto j = 0; j < k; ++j) { - // ss_id << *ids->data()->GetValues(1, i * k + j) << " "; - // ss_dist << *dists->data()->GetValues(1, i * k + j) << " "; - ss_id << *((int64_t*)(ids) + i * k + j) << " "; - ss_dist << *((float*)(dist) + i * k + j) << " "; - } - ss_id << std::endl; - ss_dist << std::endl; - } - std::cout << "id\n" << ss_id.str() << std::endl; - std::cout << "dist\n" << ss_dist.str() << std::endl; - } - */ -} - -TEST_P(AnnoyTest, annoy_serialize) { - auto serialize = [](const std::string& filename, milvus::knowhere::BinaryPtr& bin, uint8_t* ret) { - { - // write and flush - FileIOWriter writer(filename); - writer(static_cast(bin->data.get()), bin->size); - } - - FileIOReader reader(filename); - reader(ret, bin->size); - }; - - { - // serialize index - index_->BuildAll(base_dataset, conf); - auto binaryset = index_->Serialize(); - - auto bin_data = binaryset.GetByName("annoy_index_data"); - std::string filename1 = "/tmp/annoy_test_data_serialize.bin"; - auto load_data1 = new uint8_t[bin_data->size]; - serialize(filename1, bin_data, load_data1); - - auto bin_metric_type = binaryset.GetByName("annoy_metric_type"); - std::string filename2 = "/tmp/annoy_test_metric_type_serialize.bin"; - auto load_data2 = new uint8_t[bin_metric_type->size]; - serialize(filename2, bin_metric_type, load_data2); - - auto bin_dim = binaryset.GetByName("annoy_dim"); - std::string filename3 = "/tmp/annoy_test_dim_serialize.bin"; - auto load_data3 = new uint8_t[bin_dim->size]; - serialize(filename3, bin_dim, load_data3); - - binaryset.clear(); - std::shared_ptr index_data(load_data1); - binaryset.Append("annoy_index_data", index_data, bin_data->size); - - std::shared_ptr metric_data(load_data2); - binaryset.Append("annoy_metric_type", metric_data, bin_metric_type->size); - - std::shared_ptr dim_data(load_data3); - binaryset.Append("annoy_dim", dim_data, bin_dim->size); - - index_->Load(binaryset); - ASSERT_EQ(index_->Count(), nb); - ASSERT_EQ(index_->Dim(), dim); - auto result = index_->Query(query_dataset, conf); - AssertAnns(result, nq, conf[milvus::knowhere::meta::TOPK]); - } -} - -/* - * faiss style test - * keep it -int -main() { - int64_t d = 64; // dimension - int64_t nb = 10000; // database size - int64_t nq = 10; // 10000; // nb of queries - faiss::ConcurrentBitsetPtr bitset = std::make_shared(nb); - - int64_t* ids = new int64_t[nb]; - float* xb = new float[d * nb]; - float* xq = new float[d * nq]; - - for (int i = 0; i < nb; i++) { - for (int j = 0; j < d; j++) xb[d * i + j] = (float)drand48(); - xb[d * i] += i / 1000.; - ids[i] = i; - } - printf("gen xb and ids done! \n"); - - // srand((unsigned)time(nullptr)); - auto random_seed = (unsigned)time(nullptr); - printf("delete ids: \n"); - for (int i = 0; i < nq; i++) { - auto tmp = rand_r(&random_seed) % nb; - printf("%d\n", tmp); - // std::cout << "before delete, test result: " << bitset->test(tmp) << std::endl; - bitset->set(tmp); - // std::cout << "after delete, test result: " << bitset->test(tmp) << std::endl; - for (int j = 0; j < d; j++) xq[d * i + j] = xb[d * tmp + j]; - // xq[d * i] += i / 1000.; - } - printf("\n"); - - int k = 4; - int n_trees = 5; - int search_k = 100; - milvus::knowhere::IndexAnnoy index; - milvus::knowhere::DatasetPtr base_dataset = generate_dataset(nb, d, (const void*)xb, ids); - - milvus::knowhere::Config base_conf{ - {milvus::knowhere::meta::DIM, d}, - {milvus::knowhere::meta::TOPK, k}, - {milvus::knowhere::IndexParams::n_trees, n_trees}, - {milvus::knowhere::Metric::TYPE, milvus::knowhere::Metric::L2}, - }; - milvus::knowhere::DatasetPtr query_dataset = generate_query_dataset(nq, d, (const void*)xq); - milvus::knowhere::Config query_conf{ - {milvus::knowhere::meta::DIM, d}, - {milvus::knowhere::meta::TOPK, k}, - {milvus::knowhere::IndexParams::search_k, search_k}, - }; - - index.BuildAll(base_dataset, base_conf); - - printf("------------sanity check----------------\n"); - { // sanity check - auto res = index.Query(query_dataset, query_conf); - printf("Query done!\n"); - const int64_t* I = res->Get(milvus::knowhere::meta::IDS); - float* D = res->Get(milvus::knowhere::meta::DISTANCE); - - printf("I=\n"); - for (int i = 0; i < 5; i++) { - for (int j = 0; j < k; j++) printf("%5ld ", I[i * k + j]); - printf("\n"); - } - - printf("D=\n"); - for (int i = 0; i < 5; i++) { - for (int j = 0; j < k; j++) printf("%7g ", D[i * k + j]); - printf("\n"); - } - } - - printf("---------------search xq-------------\n"); - { // search xq - auto res = index.Query(query_dataset, query_conf); - const int64_t* I = res->Get(milvus::knowhere::meta::IDS); - - printf("I=\n"); - for (int i = 0; i < nq; i++) { - for (int j = 0; j < k; j++) printf("%5ld ", I[i * k + j]); - printf("\n"); - } - } - - printf("----------------search xq with delete------------\n"); - { // search xq with delete - index.SetBlacklist(bitset); - auto res = index.Query(query_dataset, query_conf); - auto I = res->Get(milvus::knowhere::meta::IDS); - - printf("I=\n"); - for (int i = 0; i < nq; i++) { - for (int j = 0; j < k; j++) printf("%5ld ", I[i * k + j]); - printf("\n"); - } - } - - delete[] xb; - delete[] xq; - delete[] ids; - - return 0; -} -*/ diff --git a/core/src/index/unittest/test_binaryidmap.cpp b/core/src/index/unittest/test_binaryidmap.cpp deleted file mode 100644 index cfbdc85a8f..0000000000 --- a/core/src/index/unittest/test_binaryidmap.cpp +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/IndexBinaryIDMAP.h" - -#include "Helper.h" -#include "unittest/utils.h" - -using ::testing::Combine; -using ::testing::TestWithParam; -using ::testing::Values; - -class BinaryIDMAPTest : public DataGen, public TestWithParam { - protected: - void - SetUp() override { - Init_with_default(true); - index_ = std::make_shared(); - } - - void - TearDown() override{}; - - protected: - milvus::knowhere::BinaryIDMAPPtr index_ = nullptr; -}; - -INSTANTIATE_TEST_CASE_P(METRICParameters, BinaryIDMAPTest, - Values(std::string("JACCARD"), std::string("TANIMOTO"), std::string("HAMMING"))); - -TEST_P(BinaryIDMAPTest, binaryidmap_basic) { - ASSERT_TRUE(!xb_bin.empty()); - - std::string MetricType = GetParam(); - milvus::knowhere::Config conf{ - {milvus::knowhere::meta::DIM, dim}, - {milvus::knowhere::meta::TOPK, k}, - {milvus::knowhere::Metric::TYPE, MetricType}, - }; - - // null faiss index - { - ASSERT_ANY_THROW(index_->Serialize()); - ASSERT_ANY_THROW(index_->Query(query_dataset, conf)); - ASSERT_ANY_THROW(index_->Add(nullptr, conf)); - ASSERT_ANY_THROW(index_->AddWithoutIds(nullptr, conf)); - } - - index_->Train(base_dataset, conf); - index_->Add(base_dataset, conf); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->Dim(), dim); - ASSERT_TRUE(index_->GetRawVectors() != nullptr); - ASSERT_TRUE(index_->GetRawIds() != nullptr); - auto result = index_->Query(query_dataset, conf); - AssertAnns(result, nq, k); - // PrintResult(result, nq, k); - - auto binaryset = index_->Serialize(); - auto new_index = std::make_shared(); - new_index->Load(binaryset); - auto result2 = new_index->Query(query_dataset, conf); - AssertAnns(result2, nq, k); - // PrintResult(re_result, nq, k); - - faiss::ConcurrentBitsetPtr concurrent_bitset_ptr = std::make_shared(nb); - for (int64_t i = 0; i < nq; ++i) { - concurrent_bitset_ptr->set(i); - } - index_->SetBlacklist(concurrent_bitset_ptr); - - auto result_bs_1 = index_->Query(query_dataset, conf); - AssertAnns(result_bs_1, nq, k, CheckMode::CHECK_NOT_EQUAL); - - // auto result4 = index_->SearchById(id_dataset, conf); - // AssertAneq(result4, nq, k); -} - -TEST_P(BinaryIDMAPTest, binaryidmap_serialize) { - auto serialize = [](const std::string& filename, milvus::knowhere::BinaryPtr& bin, uint8_t* ret) { - FileIOWriter writer(filename); - writer(static_cast(bin->data.get()), bin->size); - - FileIOReader reader(filename); - reader(ret, bin->size); - }; - - std::string MetricType = GetParam(); - milvus::knowhere::Config conf{ - {milvus::knowhere::meta::DIM, dim}, - {milvus::knowhere::meta::TOPK, k}, - {milvus::knowhere::Metric::TYPE, MetricType}, - }; - - { - // serialize index - index_->Train(base_dataset, conf); - index_->AddWithoutIds(base_dataset, milvus::knowhere::Config()); - auto re_result = index_->Query(query_dataset, conf); - AssertAnns(re_result, nq, k); - // PrintResult(re_result, nq, k); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->Dim(), dim); - auto binaryset = index_->Serialize(); - auto bin = binaryset.GetByName("BinaryIVF"); - - std::string filename = "/tmp/bianryidmap_test_serialize.bin"; - auto load_data = new uint8_t[bin->size]; - serialize(filename, bin, load_data); - - binaryset.clear(); - std::shared_ptr data(load_data); - binaryset.Append("BinaryIVF", data, bin->size); - - index_->Load(binaryset); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->Dim(), dim); - auto result = index_->Query(query_dataset, conf); - AssertAnns(result, nq, k); - // PrintResult(result, nq, k); - } -} diff --git a/core/src/index/unittest/test_binaryivf.cpp b/core/src/index/unittest/test_binaryivf.cpp deleted file mode 100644 index 76e099db8e..0000000000 --- a/core/src/index/unittest/test_binaryivf.cpp +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/common/Timer.h" -#include "knowhere/index/vector_index/IndexBinaryIVF.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" -#include "unittest/Helper.h" -#include "unittest/utils.h" - -using ::testing::Combine; -using ::testing::TestWithParam; -using ::testing::Values; - -class BinaryIVFTest : public DataGen, public TestWithParam { - protected: - void - SetUp() override { - std::string MetricType = GetParam(); - Init_with_default(true); - // nb = 1000000; - // nq = 1000; - // k = 1000; - // Generate(DIM, NB, NQ); - index_ = std::make_shared(); - - milvus::knowhere::Config temp_conf{ - {milvus::knowhere::meta::DIM, dim}, {milvus::knowhere::meta::TOPK, k}, - {milvus::knowhere::IndexParams::nlist, 100}, {milvus::knowhere::IndexParams::nprobe, 10}, - {milvus::knowhere::Metric::TYPE, MetricType}, - }; - conf = temp_conf; - } - - void - TearDown() override { - } - - protected: - std::string index_type; - milvus::knowhere::Config conf; - milvus::knowhere::BinaryIVFIndexPtr index_ = nullptr; -}; - -INSTANTIATE_TEST_CASE_P(METRICParameters, BinaryIVFTest, - Values(std::string("JACCARD"), std::string("TANIMOTO"), std::string("HAMMING"))); - -TEST_P(BinaryIVFTest, binaryivf_basic) { - assert(!xb_bin.empty()); - - // null faiss index - { - ASSERT_ANY_THROW(index_->Serialize()); - ASSERT_ANY_THROW(index_->Query(query_dataset, conf)); - ASSERT_ANY_THROW(index_->Add(nullptr, conf)); - ASSERT_ANY_THROW(index_->AddWithoutIds(nullptr, conf)); - } - - index_->BuildAll(base_dataset, conf); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->Dim(), dim); - - auto result = index_->Query(query_dataset, conf); - AssertAnns(result, nq, conf[milvus::knowhere::meta::TOPK]); - // PrintResult(result, nq, k); - - faiss::ConcurrentBitsetPtr concurrent_bitset_ptr = std::make_shared(nb); - for (int64_t i = 0; i < nq; ++i) { - concurrent_bitset_ptr->set(i); - } - index_->SetBlacklist(concurrent_bitset_ptr); - - auto result2 = index_->Query(query_dataset, conf); - AssertAnns(result2, nq, k, CheckMode::CHECK_NOT_EQUAL); - -#if 0 - auto result3 = index_->QueryById(id_dataset, conf); - AssertAnns(result3, nq, k, CheckMode::CHECK_NOT_EQUAL); - - auto result4 = index_->GetVectorById(xid_dataset, conf); - AssertBinVeceq(result4, base_dataset, xid_dataset, nq, dim/8); -#endif -} - -TEST_P(BinaryIVFTest, binaryivf_serialize) { - auto serialize = [](const std::string& filename, milvus::knowhere::BinaryPtr& bin, uint8_t* ret) { - FileIOWriter writer(filename); - writer(static_cast(bin->data.get()), bin->size); - - FileIOReader reader(filename); - reader(ret, bin->size); - }; - - // { - // // serialize index-model - // auto model = index_->Train(base_dataset, conf); - // auto binaryset = model->Serialize(); - // auto bin = binaryset.GetByName("BinaryIVF"); - // - // std::string filename = "/tmp/binaryivf_test_model_serialize.bin"; - // auto load_data = new uint8_t[bin->size]; - // serialize(filename, bin, load_data); - // - // binaryset.clear(); - // auto data = std::make_shared(); - // data.reset(load_data); - // binaryset.Append("BinaryIVF", data, bin->size); - // - // model->Load(binaryset); - // - // index_->set_index_model(model); - // index_->Add(base_dataset, conf); - // auto result = index_->Query(query_dataset, conf); - // AssertAnns(result, nq, conf[milvus::knowhere::meta::TOPK]); - // } - - { - // serialize index - index_->BuildAll(base_dataset, conf); - // index_->set_index_model(model); - // index_->Add(base_dataset, conf); - auto binaryset = index_->Serialize(); - auto bin = binaryset.GetByName("BinaryIVF"); - - std::string filename = "/tmp/binaryivf_test_serialize.bin"; - auto load_data = new uint8_t[bin->size]; - serialize(filename, bin, load_data); - - binaryset.clear(); - std::shared_ptr data(load_data); - binaryset.Append("BinaryIVF", data, bin->size); - - index_->Load(binaryset); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->Dim(), dim); - auto result = index_->Query(query_dataset, conf); - AssertAnns(result, nq, conf[milvus::knowhere::meta::TOPK]); - // PrintResult(result, nq, k); - } -} diff --git a/core/src/index/unittest/test_common.cpp b/core/src/index/unittest/test_common.cpp deleted file mode 100644 index bdebac1eb2..0000000000 --- a/core/src/index/unittest/test_common.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include "knowhere/common/Dataset.h" -#include "knowhere/common/Timer.h" -#include "knowhere/knowhere/common/Exception.h" -#include "unittest/utils.h" - -/*Some unittest for knowhere/common, mainly for improve code coverage.*/ - -TEST(COMMON_TEST, dataset_test) { - milvus::knowhere::Dataset set; - int64_t v1 = 111; - - set.Set("key1", v1); - auto get_v1 = set.Get("key1"); - ASSERT_EQ(get_v1, v1); - - ASSERT_ANY_THROW(set.Get("key1")); - ASSERT_ANY_THROW(set.Get("dummy")); -} - -TEST(COMMON_TEST, knowhere_exception) { - const std::string msg = "test"; - milvus::knowhere::KnowhereException ex(msg); - ASSERT_EQ(ex.what(), msg); -} - -TEST(COMMON_TEST, time_recoder) { - InitLog(); - - milvus::knowhere::TimeRecorder recoder("COMMTEST", 0); - sleep(1); - double span = recoder.ElapseFromBegin("get time"); - ASSERT_GE(span, 1.0); -} diff --git a/core/src/index/unittest/test_customized_index.cpp b/core/src/index/unittest/test_customized_index.cpp deleted file mode 100644 index 03c55d4509..0000000000 --- a/core/src/index/unittest/test_customized_index.cpp +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include -#include -#include - -#include "knowhere/common/Timer.h" -#include "knowhere/index/vector_index/IndexType.h" -#include "unittest/Helper.h" -#include "unittest/utils.h" - -class SingleIndexTest : public DataGen, public TestGpuIndexBase { - protected: - void - SetUp() override { - TestGpuIndexBase::SetUp(); - nb = 100000; - nq = 1000; - dim = DIM; - Generate(dim, nb, nq); - k = 1000; - } - - void - TearDown() override { - TestGpuIndexBase::TearDown(); - } - - protected: - milvus::knowhere::IndexType index_type_; - milvus::knowhere::IndexMode index_mode_; - milvus::knowhere::IVFPtr index_ = nullptr; -}; - -#ifdef MILVUS_GPU_VERSION -TEST_F(SingleIndexTest, IVFSQHybrid) { - assert(!xb.empty()); - - index_type_ = milvus::knowhere::IndexEnum::INDEX_FAISS_IVFSQ8H; - index_mode_ = milvus::knowhere::IndexMode::MODE_GPU; - index_ = IndexFactory(index_type_, index_mode_); - - auto conf = ParamGenerator::GetInstance().Gen(index_type_); - - fiu_init(0); - index_->Train(base_dataset, conf); - index_->Add(base_dataset, conf); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->Dim(), dim); - - auto binaryset = index_->Serialize(); - { - // copy cpu to gpu - auto cpu_idx = std::make_shared(DEVICEID); - cpu_idx->Load(binaryset); - - { - for (int i = 0; i < 3; ++i) { - auto gpu_idx = cpu_idx->CopyCpuToGpu(DEVICEID, conf); - auto result = gpu_idx->Query(query_dataset, conf); - AssertAnns(result, nq, conf[milvus::knowhere::meta::TOPK]); - // PrintResult(result, nq, k); - } - } - } - - { - // quantization already in gpu, only copy data - auto cpu_idx = std::make_shared(DEVICEID); - cpu_idx->Load(binaryset); - - ASSERT_ANY_THROW(cpu_idx->CopyCpuToGpuWithQuantizer(-1, conf)); - auto pair = cpu_idx->CopyCpuToGpuWithQuantizer(DEVICEID, conf); - auto gpu_idx = pair.first; - - auto result = gpu_idx->Query(query_dataset, conf); - AssertAnns(result, nq, conf[milvus::knowhere::meta::TOPK]); - // PrintResult(result, nq, k); - - milvus::json quantizer_conf{{milvus::knowhere::meta::DEVICEID, DEVICEID}, {"mode", 2}}; - for (int i = 0; i < 2; ++i) { - auto hybrid_idx = std::make_shared(DEVICEID); - hybrid_idx->Load(binaryset); - auto quantization = hybrid_idx->LoadQuantizer(quantizer_conf); - auto new_idx = hybrid_idx->LoadData(quantization, quantizer_conf); - auto result = new_idx->Query(query_dataset, conf); - AssertAnns(result, nq, conf[milvus::knowhere::meta::TOPK]); - // PrintResult(result, nq, k); - } - } - - { - // quantization already in gpu, only set quantization - auto cpu_idx = std::make_shared(DEVICEID); - cpu_idx->Load(binaryset); - - auto pair = cpu_idx->CopyCpuToGpuWithQuantizer(DEVICEID, conf); - auto quantization = pair.second; - - for (int i = 0; i < 2; ++i) { - auto hybrid_idx = std::make_shared(DEVICEID); - hybrid_idx->Load(binaryset); - - hybrid_idx->SetQuantizer(quantization); - auto result = hybrid_idx->Query(query_dataset, conf); - AssertAnns(result, nq, conf[milvus::knowhere::meta::TOPK]); - // PrintResult(result, nq, k); - hybrid_idx->UnsetQuantizer(); - } - } -} - -// TEST_F(SingleIndexTest, thread_safe) { -// assert(!xb.empty()); -// -// index_type = "IVFSQHybrid"; -// index_ = IndexFactory(index_type); -// auto base = ParamGenerator::GetInstance().Gen(ParameterType::ivfsq); -// auto conf = std::dynamic_pointer_cast(base); -// conf->nlist = 16384; -// conf->k = k; -// conf->nprobe = 10; -// conf->d = dim; -// auto preprocessor = index_->BuildPreprocessor(base_dataset, conf); -// index_->set_preprocessor(preprocessor); -// -// auto model = index_->Train(base_dataset, conf); -// index_->set_index_model(model); -// index_->Add(base_dataset, conf); -// EXPECT_EQ(index_->Count(), nb); -// EXPECT_EQ(index_->Dimension(), dim); -// -// auto binaryset = index_->Serialize(); -// -// -// -// auto cpu_idx = std::make_shared(DEVICEID); -// cpu_idx->Load(binaryset); -// auto pair = cpu_idx->CopyCpuToGpuWithQuantizer(DEVICEID, conf); -// auto quantizer = pair.second; -// -// auto quantizer_conf = std::make_shared(); -// quantizer_conf->mode = 2; // only copy data -// quantizer_conf->gpu_id = DEVICEID; -// -// auto CopyAllToGpu = [&](int64_t search_count, bool do_search = false) { -// for (int i = 0; i < search_count; ++i) { -// auto gpu_idx = cpu_idx->CopyCpuToGpu(DEVICEID, conf); -// if (do_search) { -// auto result = gpu_idx->Search(query_dataset, conf); -// AssertAnns(result, nq, conf->k); -// } -// } -// }; -// -// auto hybrid_qt_idx = std::make_shared(DEVICEID); -// hybrid_qt_idx->Load(binaryset); -// auto SetQuantizerDoSearch = [&](int64_t search_count) { -// for (int i = 0; i < search_count; ++i) { -// hybrid_qt_idx->SetQuantizer(quantizer); -// auto result = hybrid_qt_idx->Search(query_dataset, conf); -// AssertAnns(result, nq, conf->k); -// // PrintResult(result, nq, k); -// hybrid_qt_idx->UnsetQuantizer(); -// } -// }; -// -// auto hybrid_data_idx = std::make_shared(DEVICEID); -// hybrid_data_idx->Load(binaryset); -// auto LoadDataDoSearch = [&](int64_t search_count, bool do_search = false) { -// for (int i = 0; i < search_count; ++i) { -// auto hybrid_idx = hybrid_data_idx->LoadData(quantizer, quantizer_conf); -// if (do_search) { -// auto result = hybrid_idx->Search(query_dataset, conf); -//// AssertAnns(result, nq, conf->k); -// } -// } -// }; -// -// milvus::knowhere::TimeRecorder tc(""); -// CopyAllToGpu(2000/2, false); -// tc.RecordSection("CopyAllToGpu witout search"); -// CopyAllToGpu(400/2, true); -// tc.RecordSection("CopyAllToGpu with search"); -// SetQuantizerDoSearch(6); -// tc.RecordSection("SetQuantizer with search"); -// LoadDataDoSearch(2000/2, false); -// tc.RecordSection("LoadData without search"); -// LoadDataDoSearch(400/2, true); -// tc.RecordSection("LoadData with search"); -// -// { -// std::thread t1(CopyAllToGpu, 2000, false); -// std::thread t2(CopyAllToGpu, 400, true); -// t1.join(); -// t2.join(); -// } -// -// { -// std::thread t1(SetQuantizerDoSearch, 12); -// std::thread t2(CopyAllToGpu, 400, true); -// t1.join(); -// t2.join(); -// } -// -// { -// std::thread t1(SetQuantizerDoSearch, 12); -// std::thread t2(LoadDataDoSearch, 400, true); -// t1.join(); -// t2.join(); -// } -// -// { -// std::thread t1(LoadDataDoSearch, 2000, false); -// std::thread t2(LoadDataDoSearch, 400, true); -// t1.join(); -// t2.join(); -// } -// -//} - -#endif diff --git a/core/src/index/unittest/test_gpuresource.cpp b/core/src/index/unittest/test_gpuresource.cpp deleted file mode 100644 index e70404e176..0000000000 --- a/core/src/index/unittest/test_gpuresource.cpp +++ /dev/null @@ -1,302 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include - -#include -#include - -#include -#include -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/common/Timer.h" -#include "knowhere/index/vector_index/IndexIVF.h" -#include "knowhere/index/vector_index/IndexIVFPQ.h" -#include "knowhere/index/vector_index/IndexIVFSQ.h" -#include "knowhere/index/vector_index/IndexType.h" -#include "knowhere/index/vector_index/gpu/IndexGPUIVF.h" -#include "knowhere/index/vector_index/gpu/IndexGPUIVFPQ.h" -#include "knowhere/index/vector_index/gpu/IndexGPUIVFSQ.h" -#include "knowhere/index/vector_index/gpu/IndexIVFSQHybrid.h" -#include "knowhere/index/vector_index/helpers/Cloner.h" - -#include "unittest/Helper.h" -#include "unittest/utils.h" - -class GPURESTEST : public DataGen, public TestGpuIndexBase { - protected: - void - SetUp() override { - TestGpuIndexBase::SetUp(); - Generate(DIM, NB, NQ); - - k = K; - elems = nq * k; - ids = (int64_t*)malloc(sizeof(int64_t) * elems); - dis = (float*)malloc(sizeof(float) * elems); - } - - void - TearDown() override { - delete ids; - delete dis; - TestGpuIndexBase::TearDown(); - } - - protected: - milvus::knowhere::IndexType index_type_; - milvus::knowhere::IndexMode index_mode_; - milvus::knowhere::IVFPtr index_ = nullptr; - - int64_t* ids = nullptr; - float* dis = nullptr; - int64_t elems = 0; -}; - -TEST_F(GPURESTEST, copyandsearch) { - // search and copy at the same time - printf("==================\n"); - - index_type_ = milvus::knowhere::IndexEnum::INDEX_FAISS_IVFFLAT; - index_mode_ = milvus::knowhere::IndexMode::MODE_GPU; - index_ = IndexFactory(index_type_, index_mode_); - - auto conf = ParamGenerator::GetInstance().Gen(index_type_); - index_->Train(base_dataset, conf); - index_->Add(base_dataset, conf); - auto result = index_->Query(query_dataset, conf); - AssertAnns(result, nq, k); - - index_->SetIndexSize(nb * dim * sizeof(float)); - auto cpu_idx = milvus::knowhere::cloner::CopyGpuToCpu(index_, milvus::knowhere::Config()); - milvus::knowhere::IVFPtr ivf_idx = std::dynamic_pointer_cast(cpu_idx); - ivf_idx->Seal(); - auto search_idx = milvus::knowhere::cloner::CopyCpuToGpu(cpu_idx, DEVICEID, milvus::knowhere::Config()); - - constexpr int64_t search_count = 50; - constexpr int64_t load_count = 15; - auto search_func = [&] { - // TimeRecorder tc("search&load"); - for (int i = 0; i < search_count; ++i) { - search_idx->Query(query_dataset, conf); - // if (i > search_count - 6 || i == 0) - // tc.RecordSection("search once"); - } - // tc.ElapseFromBegin("search finish"); - }; - auto load_func = [&] { - // TimeRecorder tc("search&load"); - for (int i = 0; i < load_count; ++i) { - milvus::knowhere::cloner::CopyCpuToGpu(cpu_idx, DEVICEID, milvus::knowhere::Config()); - // if (i > load_count -5 || i < 5) - // tc.RecordSection("Copy to gpu"); - } - // tc.ElapseFromBegin("load finish"); - }; - - milvus::knowhere::TimeRecorder tc("Basic"); - milvus::knowhere::cloner::CopyCpuToGpu(cpu_idx, DEVICEID, milvus::knowhere::Config()); - tc.RecordSection("Copy to gpu once"); - search_idx->Query(query_dataset, conf); - tc.RecordSection("Search once"); - search_func(); - tc.RecordSection("Search total cost"); - load_func(); - tc.RecordSection("Copy total cost"); - - std::thread search_thread(search_func); - std::thread load_thread(load_func); - search_thread.join(); - load_thread.join(); - tc.RecordSection("Copy&Search total"); -} - -TEST_F(GPURESTEST, trainandsearch) { - index_type_ = milvus::knowhere::IndexEnum::INDEX_FAISS_IVFFLAT; - index_mode_ = milvus::knowhere::IndexMode::MODE_GPU; - index_ = IndexFactory(index_type_, index_mode_); - - auto conf = ParamGenerator::GetInstance().Gen(index_type_); - index_->Train(base_dataset, conf); - index_->Add(base_dataset, conf); - index_->SetIndexSize(nb * dim * sizeof(float)); - auto cpu_idx = milvus::knowhere::cloner::CopyGpuToCpu(index_, milvus::knowhere::Config()); - milvus::knowhere::IVFPtr ivf_idx = std::dynamic_pointer_cast(cpu_idx); - ivf_idx->Seal(); - auto search_idx = milvus::knowhere::cloner::CopyCpuToGpu(cpu_idx, DEVICEID, milvus::knowhere::Config()); - - constexpr int train_count = 5; - constexpr int search_count = 200; - auto train_stage = [&] { - for (int i = 0; i < train_count; ++i) { - index_->Train(base_dataset, conf); - index_->Add(base_dataset, conf); - } - }; - auto search_stage = [&](milvus::knowhere::VecIndexPtr& search_idx) { - for (int i = 0; i < search_count; ++i) { - auto result = search_idx->Query(query_dataset, conf); - AssertAnns(result, nq, k); - } - }; - - // TimeRecorder tc("record"); - // train_stage(); - // tc.RecordSection("train cost"); - // search_stage(search_idx); - // tc.RecordSection("search cost"); - - { - // search and build parallel - std::thread search_thread(search_stage, std::ref(search_idx)); - std::thread train_thread(train_stage); - train_thread.join(); - search_thread.join(); - } - { - // build parallel - std::thread train_1(train_stage); - std::thread train_2(train_stage); - train_1.join(); - train_2.join(); - } - { - // search parallel - auto search_idx_2 = milvus::knowhere::cloner::CopyCpuToGpu(cpu_idx, DEVICEID, milvus::knowhere::Config()); - std::thread search_1(search_stage, std::ref(search_idx)); - std::thread search_2(search_stage, std::ref(search_idx_2)); - search_1.join(); - search_2.join(); - } -} - -#ifdef CompareToOriFaiss -TEST_F(GPURESTEST, gpu_ivf_resource_test) { - assert(!xb.empty()); - - { - index_ = std::make_shared(-1); - ASSERT_EQ(std::dynamic_pointer_cast(index_)->GetGpuDevice(), -1); - std::dynamic_pointer_cast(index_)->SetGpuDevice(DEVICEID); - ASSERT_EQ(std::dynamic_pointer_cast(index_)->GetGpuDevice(), DEVICEID); - - auto conf = ParamGenerator::GetInstance().Gen(ParameterType::ivfsq); - auto preprocessor = index_->BuildPreprocessor(base_dataset, conf); - index_->set_preprocessor(preprocessor); - auto model = index_->Train(base_dataset, conf); - index_->set_index_model(model); - index_->Add(base_dataset, conf); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->Dimension(), dim); - - // milvus::knowhere::TimeRecorder tc("knowere GPUIVF"); - for (int i = 0; i < search_count; ++i) { - index_->Search(query_dataset, conf); - if (i > search_count - 6 || i < 5) - // tc.RecordSection("search once"); - } - // tc.ElapseFromBegin("search all"); - } - milvus::knowhere::FaissGpuResourceMgr::GetInstance().Dump(); - - // { - // // ori faiss IVF-Search - // faiss::gpu::StandardGpuResources res; - // faiss::gpu::GpuIndexIVFFlatConfig idx_config; - // idx_config.device = DEVICEID; - // faiss::gpu::GpuIndexIVFFlat device_index(&res, dim, 1638, faiss::METRIC_L2, idx_config); - // device_index.train(nb, xb.data()); - // device_index.add(nb, xb.data()); - // - // milvus::knowhere::TimeRecorder tc("ori IVF"); - // for (int i = 0; i < search_count; ++i) { - // device_index.search(nq, xq.data(), k, dis, ids); - // if (i > search_count - 6 || i < 5) - // tc.RecordSection("search once"); - // } - // tc.ElapseFromBegin("search all"); - // } -} - -TEST_F(GPURESTEST, gpuivfsq) { - { - // knowhere gpu ivfsq - index_type = "GPUIVFSQ"; - index_ = IndexFactory(index_type); - - auto conf = std::make_shared(); - conf->nlist = 1638; - conf->d = dim; - conf->gpu_id = DEVICEID; - conf->metric_type = milvus::knowhere::METRICTYPE::L2; - conf->k = k; - conf->nbits = 8; - conf->nprobe = 1; - - auto preprocessor = index_->BuildPreprocessor(base_dataset, conf); - index_->set_preprocessor(preprocessor); - auto model = index_->Train(base_dataset, conf); - index_->set_index_model(model); - index_->Add(base_dataset, conf); - // auto result = index_->Search(query_dataset, conf); - // AssertAnns(result, nq, k); - - auto cpu_idx = milvus::knowhere::cloner::CopyGpuToCpu(index_, milvus::knowhere::Config()); - cpu_idx->Seal(); - - milvus::knowhere::TimeRecorder tc("knowhere GPUSQ8"); - auto search_idx = milvus::knowhere::cloner::CopyCpuToGpu(cpu_idx, DEVICEID, milvus::knowhere::Config()); - tc.RecordSection("Copy to gpu"); - for (int i = 0; i < search_count; ++i) { - search_idx->Search(query_dataset, conf); - if (i > search_count - 6 || i < 5) - tc.RecordSection("search once"); - } - tc.ElapseFromBegin("search all"); - } - - { - // Ori gpuivfsq Test - const char* index_description = "IVF1638,SQ8"; - faiss::Index* ori_index = faiss::index_factory(dim, index_description, faiss::METRIC_L2); - - faiss::gpu::StandardGpuResources res; - auto device_index = faiss::gpu::index_cpu_to_gpu(&res, DEVICEID, ori_index); - device_index->train(nb, xb.data()); - device_index->add(nb, xb.data()); - - auto cpu_index = faiss::gpu::index_gpu_to_cpu(device_index); - auto idx = dynamic_cast(cpu_index); - if (idx != nullptr) { - idx->to_readonly(); - } - delete device_index; - delete ori_index; - - faiss::gpu::GpuClonerOptions option; - option.allInGpu = true; - - milvus::knowhere::TimeRecorder tc("ori GPUSQ8"); - faiss::Index* search_idx = faiss::gpu::index_cpu_to_gpu(&res, DEVICEID, cpu_index, &option); - tc.RecordSection("Copy to gpu"); - for (int i = 0; i < search_count; ++i) { - search_idx->search(nq, xq.data(), k, dis, ids); - if (i > search_count - 6 || i < 5) - tc.RecordSection("search once"); - } - tc.ElapseFromBegin("search all"); - delete cpu_index; - delete search_idx; - } -} -#endif diff --git a/core/src/index/unittest/test_hnsw.cpp b/core/src/index/unittest/test_hnsw.cpp deleted file mode 100644 index 48f458fca9..0000000000 --- a/core/src/index/unittest/test_hnsw.cpp +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include -#include -#include -#include -#include "knowhere/common/Exception.h" -#include "unittest/utils.h" - -using ::testing::Combine; -using ::testing::TestWithParam; -using ::testing::Values; - -class HNSWTest : public DataGen, public TestWithParam { - protected: - void - SetUp() override { - IndexType = GetParam(); - std::cout << "IndexType from GetParam() is: " << IndexType << std::endl; - Generate(64, 10000, 10); // dim = 64, nb = 10000, nq = 10 - index_ = std::make_shared(); - conf = milvus::knowhere::Config{ - {milvus::knowhere::meta::DIM, 64}, {milvus::knowhere::meta::TOPK, 10}, - {milvus::knowhere::IndexParams::M, 16}, {milvus::knowhere::IndexParams::efConstruction, 200}, - {milvus::knowhere::IndexParams::ef, 200}, {milvus::knowhere::Metric::TYPE, milvus::knowhere::Metric::L2}, - }; - } - - protected: - milvus::knowhere::Config conf; - std::shared_ptr index_ = nullptr; - std::string IndexType; -}; - -INSTANTIATE_TEST_CASE_P(HNSWParameters, HNSWTest, Values("HNSW")); - -TEST_P(HNSWTest, HNSW_basic) { - assert(!xb.empty()); - - // null faiss index - { - ASSERT_ANY_THROW(index_->Serialize()); - ASSERT_ANY_THROW(index_->Query(query_dataset, conf)); - ASSERT_ANY_THROW(index_->Add(nullptr, conf)); - ASSERT_ANY_THROW(index_->AddWithoutIds(nullptr, conf)); - ASSERT_ANY_THROW(index_->Count()); - ASSERT_ANY_THROW(index_->Dim()); - } - - index_->Train(base_dataset, conf); - index_->Add(base_dataset, conf); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->Dim(), dim); - - auto result = index_->Query(query_dataset, conf); - AssertAnns(result, nq, k); -} - -TEST_P(HNSWTest, HNSW_delete) { - assert(!xb.empty()); - - index_->Train(base_dataset, conf); - index_->Add(base_dataset, conf); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->Dim(), dim); - - faiss::ConcurrentBitsetPtr bitset = std::make_shared(nb); - for (auto i = 0; i < nq; ++i) { - bitset->set(i); - } - auto result1 = index_->Query(query_dataset, conf); - AssertAnns(result1, nq, k); - - index_->SetBlacklist(bitset); - auto result2 = index_->Query(query_dataset, conf); - AssertAnns(result2, nq, k, CheckMode::CHECK_NOT_EQUAL); - - /* - * delete result checked by eyes - auto ids1 = result1->Get(milvus::knowhere::meta::IDS); - auto ids2 = result2->Get(milvus::knowhere::meta::IDS); - std::cout << std::endl; - for (int i = 0; i < nq; ++ i) { - std::cout << "ids1: "; - for (int j = 0; j < k; ++ j) { - std::cout << *(ids1 + i * k + j) << " "; - } - std::cout << "ids2: "; - for (int j = 0; j < k; ++ j) { - std::cout << *(ids2 + i * k + j) << " "; - } - std::cout << std::endl; - for (int j = 0; j < std::min(5, k>>1); ++ j) { - ASSERT_EQ(*(ids1 + i * k + j + 1), *(ids2 + i * k + j)); - } - } - */ -} - -TEST_P(HNSWTest, HNSW_serialize) { - auto serialize = [](const std::string& filename, milvus::knowhere::BinaryPtr& bin, uint8_t* ret) { - { - FileIOWriter writer(filename); - writer(static_cast(bin->data.get()), bin->size); - } - - FileIOReader reader(filename); - reader(ret, bin->size); - }; - - { - index_->Train(base_dataset, conf); - index_->Add(base_dataset, conf); - auto binaryset = index_->Serialize(); - auto bin = binaryset.GetByName("HNSW"); - - std::string filename = "/tmp/HNSW_test_serialize.bin"; - auto load_data = new uint8_t[bin->size]; - serialize(filename, bin, load_data); - - binaryset.clear(); - std::shared_ptr data(load_data); - binaryset.Append("HNSW", data, bin->size); - - index_->Load(binaryset); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->Dim(), dim); - auto result = index_->Query(query_dataset, conf); - AssertAnns(result, nq, conf[milvus::knowhere::meta::TOPK]); - } -} - -/* - * faiss style test - * keep it -int -main() { - int64_t d = 64; // dimension - int64_t nb = 10000; // database size - int64_t nq = 10; // 10000; // nb of queries - faiss::ConcurrentBitsetPtr bitset = std::make_shared(nb); - - int64_t* ids = new int64_t[nb]; - float* xb = new float[d * nb]; - float* xq = new float[d * nq]; - // int64_t *ids = (int64_t*)malloc(nb * sizeof(int64_t)); - // float* xb = (float*)malloc(d * nb * sizeof(float)); - // float* xq = (float*)malloc(d * nq * sizeof(float)); - - for (int i = 0; i < nb; i++) { - for (int j = 0; j < d; j++) xb[d * i + j] = drand48(); - xb[d * i] += i / 1000.; - ids[i] = i; - } -// printf("gen xb and ids done! \n"); - - // srand((unsigned)time(nullptr)); - auto random_seed = (unsigned)time(nullptr); -// printf("delete ids: \n"); - for (int i = 0; i < nq; i++) { - auto tmp = rand_r(&random_seed) % nb; -// printf("%ld\n", tmp); - // std::cout << "before delete, test result: " << bitset->test(tmp) << std::endl; - bitset->set(tmp); - // std::cout << "after delete, test result: " << bitset->test(tmp) << std::endl; - for (int j = 0; j < d; j++) xq[d * i + j] = xb[d * tmp + j]; - // xq[d * i] += i / 1000.; - } -// printf("\n"); - - int k = 4; - int m = 16; - int ef = 200; - milvus::knowhere::IndexHNSW index; - milvus::knowhere::DatasetPtr base_dataset = generate_dataset(nb, d, (const void*)xb, ids); -// base_dataset->Set(milvus::knowhere::meta::ROWS, nb); -// base_dataset->Set(milvus::knowhere::meta::DIM, d); -// base_dataset->Set(milvus::knowhere::meta::TENSOR, (const void*)xb); -// base_dataset->Set(milvus::knowhere::meta::IDS, (const int64_t*)ids); - - milvus::knowhere::Config base_conf{ - {milvus::knowhere::meta::DIM, d}, - {milvus::knowhere::meta::TOPK, k}, - {milvus::knowhere::IndexParams::M, m}, - {milvus::knowhere::IndexParams::efConstruction, ef}, - {milvus::knowhere::Metric::TYPE, milvus::knowhere::Metric::L2}, - }; - milvus::knowhere::DatasetPtr query_dataset = generate_query_dataset(nq, d, (const void*)xq); - milvus::knowhere::Config query_conf{ - {milvus::knowhere::meta::DIM, d}, - {milvus::knowhere::meta::TOPK, k}, - {milvus::knowhere::IndexParams::M, m}, - {milvus::knowhere::IndexParams::ef, ef}, - {milvus::knowhere::Metric::TYPE, milvus::knowhere::Metric::L2}, - }; - - index.Train(base_dataset, base_conf); - index.Add(base_dataset, base_conf); - -// printf("------------sanity check----------------\n"); - { // sanity check - auto res = index.Query(query_dataset, query_conf); -// printf("Query done!\n"); - const int64_t* I = res->Get(milvus::knowhere::meta::IDS); -// float* D = res->Get(milvus::knowhere::meta::DISTANCE); - -// printf("I=\n"); -// for (int i = 0; i < 5; i++) { -// for (int j = 0; j < k; j++) printf("%5ld ", I[i * k + j]); -// printf("\n"); -// } - -// printf("D=\n"); -// for (int i = 0; i < 5; i++) { -// for (int j = 0; j < k; j++) printf("%7g ", D[i * k + j]); -// printf("\n"); -// } - } - -// printf("---------------search xq-------------\n"); - { // search xq - auto res = index.Query(query_dataset, query_conf); - const int64_t* I = res->Get(milvus::knowhere::meta::IDS); - - printf("I=\n"); - for (int i = 0; i < nq; i++) { - for (int j = 0; j < k; j++) printf("%5ld ", I[i * k + j]); - printf("\n"); - } - } - - printf("----------------search xq with delete------------\n"); - { // search xq with delete - index.SetBlacklist(bitset); - auto res = index.Query(query_dataset, query_conf); - auto I = res->Get(milvus::knowhere::meta::IDS); - - printf("I=\n"); - for (int i = 0; i < nq; i++) { - for (int j = 0; j < k; j++) printf("%5ld ", I[i * k + j]); - printf("\n"); - } - } - - delete[] xb; - delete[] xq; - delete[] ids; - - return 0; -} -*/ diff --git a/core/src/index/unittest/test_idmap.cpp b/core/src/index/unittest/test_idmap.cpp deleted file mode 100644 index 2abbd8e270..0000000000 --- a/core/src/index/unittest/test_idmap.cpp +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include - -#include -#include -#include -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/IndexIDMAP.h" -#include "knowhere/index/vector_index/IndexType.h" -#ifdef MILVUS_GPU_VERSION -#include -#include "knowhere/index/vector_index/gpu/IndexGPUIDMAP.h" -#include "knowhere/index/vector_index/helpers/Cloner.h" -#include "knowhere/index/vector_index/helpers/FaissGpuResourceMgr.h" -#endif -#include "Helper.h" -#include "unittest/utils.h" - -using ::testing::Combine; -using ::testing::TestWithParam; -using ::testing::Values; - -class IDMAPTest : public DataGen, public TestWithParam { - protected: - void - SetUp() override { -#ifdef MILVUS_GPU_VERSION - milvus::knowhere::FaissGpuResourceMgr::GetInstance().InitDevice(DEVICEID, PINMEM, TEMPMEM, RESNUM); -#endif - index_mode_ = GetParam(); - Init_with_default(); - index_ = std::make_shared(); - } - - void - TearDown() override { -#ifdef MILVUS_GPU_VERSION - milvus::knowhere::FaissGpuResourceMgr::GetInstance().Free(); -#endif - } - - protected: - milvus::knowhere::IDMAPPtr index_ = nullptr; - milvus::knowhere::IndexMode index_mode_; -}; - -INSTANTIATE_TEST_CASE_P(IDMAPParameters, IDMAPTest, - Values( -#ifdef MILVUS_GPU_VERSION - milvus::knowhere::IndexMode::MODE_GPU, -#endif - milvus::knowhere::IndexMode::MODE_CPU)); - -TEST_P(IDMAPTest, idmap_basic) { - ASSERT_TRUE(!xb.empty()); - - milvus::knowhere::Config conf{{milvus::knowhere::meta::DIM, dim}, - {milvus::knowhere::meta::TOPK, k}, - {milvus::knowhere::Metric::TYPE, milvus::knowhere::Metric::L2}}; - - // null faiss index - { - ASSERT_ANY_THROW(index_->Serialize()); - ASSERT_ANY_THROW(index_->Query(query_dataset, conf)); - ASSERT_ANY_THROW(index_->Add(nullptr, conf)); - ASSERT_ANY_THROW(index_->AddWithoutIds(nullptr, conf)); - } - - index_->Train(base_dataset, conf); - index_->Add(base_dataset, conf); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->Dim(), dim); - ASSERT_TRUE(index_->GetRawVectors() != nullptr); - ASSERT_TRUE(index_->GetRawIds() != nullptr); - auto result = index_->Query(query_dataset, conf); - AssertAnns(result, nq, k); - // PrintResult(result, nq, k); - - if (index_mode_ == milvus::knowhere::IndexMode::MODE_GPU) { -#ifdef MILVUS_GPU_VERSION - // cpu to gpu - index_ = std::dynamic_pointer_cast(index_->CopyCpuToGpu(DEVICEID, conf)); -#endif - } - - auto binaryset = index_->Serialize(); - auto new_index = std::make_shared(); - new_index->Load(binaryset); - auto result2 = new_index->Query(query_dataset, conf); - AssertAnns(result2, nq, k); - // PrintResult(re_result, nq, k); - -#if 0 - auto result3 = new_index->QueryById(id_dataset, conf); - AssertAnns(result3, nq, k); - - auto result4 = new_index->GetVectorById(xid_dataset, conf); - AssertVec(result4, base_dataset, xid_dataset, 1, dim); -#endif - - faiss::ConcurrentBitsetPtr concurrent_bitset_ptr = std::make_shared(nb); - for (int64_t i = 0; i < nq; ++i) { - concurrent_bitset_ptr->set(i); - } - index_->SetBlacklist(concurrent_bitset_ptr); - - auto result_bs_1 = index_->Query(query_dataset, conf); - AssertAnns(result_bs_1, nq, k, CheckMode::CHECK_NOT_EQUAL); - -#if 0 - auto result_bs_2 = index_->QueryById(id_dataset, conf); - AssertAnns(result_bs_2, nq, k, CheckMode::CHECK_NOT_EQUAL); - - auto result_bs_3 = index_->GetVectorById(xid_dataset, conf); - AssertVec(result_bs_3, base_dataset, xid_dataset, 1, dim, CheckMode::CHECK_NOT_EQUAL); -#endif -} - -TEST_P(IDMAPTest, idmap_serialize) { - auto serialize = [](const std::string& filename, milvus::knowhere::BinaryPtr& bin, uint8_t* ret) { - FileIOWriter writer(filename); - writer(static_cast(bin->data.get()), bin->size); - - FileIOReader reader(filename); - reader(ret, bin->size); - }; - - milvus::knowhere::Config conf{{milvus::knowhere::meta::DIM, dim}, - {milvus::knowhere::meta::TOPK, k}, - {milvus::knowhere::Metric::TYPE, milvus::knowhere::Metric::L2}}; - - { - // serialize index - index_->Train(base_dataset, conf); - index_->Add(base_dataset, milvus::knowhere::Config()); - - if (index_mode_ == milvus::knowhere::IndexMode::MODE_GPU) { -#ifdef MILVUS_GPU_VERSION - // cpu to gpu - index_ = std::dynamic_pointer_cast(index_->CopyCpuToGpu(DEVICEID, conf)); -#endif - } - - auto re_result = index_->Query(query_dataset, conf); - AssertAnns(re_result, nq, k); - // PrintResult(re_result, nq, k); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->Dim(), dim); - auto binaryset = index_->Serialize(); - auto bin = binaryset.GetByName("IVF"); - - std::string filename = "/tmp/idmap_test_serialize.bin"; - auto load_data = new uint8_t[bin->size]; - serialize(filename, bin, load_data); - - binaryset.clear(); - std::shared_ptr data(load_data); - binaryset.Append("IVF", data, bin->size); - - index_->Load(binaryset); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->Dim(), dim); - auto result = index_->Query(query_dataset, conf); - AssertAnns(result, nq, k); - // PrintResult(result, nq, k); - } -} - -#ifdef MILVUS_GPU_VERSION -TEST_P(IDMAPTest, idmap_copy) { - ASSERT_TRUE(!xb.empty()); - - milvus::knowhere::Config conf{{milvus::knowhere::meta::DIM, dim}, - {milvus::knowhere::meta::TOPK, k}, - {milvus::knowhere::Metric::TYPE, milvus::knowhere::Metric::L2}}; - - index_->Train(base_dataset, conf); - index_->Add(base_dataset, conf); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->Dim(), dim); - ASSERT_TRUE(index_->GetRawVectors() != nullptr); - ASSERT_TRUE(index_->GetRawIds() != nullptr); - auto result = index_->Query(query_dataset, conf); - AssertAnns(result, nq, k); - // PrintResult(result, nq, k); - - { - // clone - // auto clone_index = index_->Clone(); - // auto clone_result = clone_index->Search(query_dataset, conf); - // AssertAnns(clone_result, nq, k); - } - - { - // cpu to gpu - ASSERT_ANY_THROW(milvus::knowhere::cloner::CopyCpuToGpu(index_, -1, conf)); - auto clone_index = milvus::knowhere::cloner::CopyCpuToGpu(index_, DEVICEID, conf); - auto clone_result = clone_index->Query(query_dataset, conf); - AssertAnns(clone_result, nq, k); - ASSERT_THROW({ std::static_pointer_cast(clone_index)->GetRawVectors(); }, - milvus::knowhere::KnowhereException); - ASSERT_THROW({ std::static_pointer_cast(clone_index)->GetRawIds(); }, - milvus::knowhere::KnowhereException); - - fiu_init(0); - fiu_enable("GPUIDMP.SerializeImpl.throw_exception", 1, nullptr, 0); - ASSERT_ANY_THROW(clone_index->Serialize()); - fiu_disable("GPUIDMP.SerializeImpl.throw_exception"); - - auto binary = clone_index->Serialize(); - clone_index->Load(binary); - auto new_result = clone_index->Query(query_dataset, conf); - AssertAnns(new_result, nq, k); - - // auto clone_gpu_idx = clone_index->Clone(); - // auto clone_gpu_res = clone_gpu_idx->Search(query_dataset, conf); - // AssertAnns(clone_gpu_res, nq, k); - - // gpu to cpu - auto host_index = milvus::knowhere::cloner::CopyGpuToCpu(clone_index, conf); - auto host_result = host_index->Query(query_dataset, conf); - AssertAnns(host_result, nq, k); - ASSERT_TRUE(std::static_pointer_cast(host_index)->GetRawVectors() != nullptr); - ASSERT_TRUE(std::static_pointer_cast(host_index)->GetRawIds() != nullptr); - - // gpu to gpu - auto device_index = milvus::knowhere::cloner::CopyCpuToGpu(index_, DEVICEID, conf); - auto new_device_index = - std::static_pointer_cast(device_index)->CopyGpuToGpu(DEVICEID, conf); - auto device_result = new_device_index->Query(query_dataset, conf); - AssertAnns(device_result, nq, k); - } -} -#endif diff --git a/core/src/index/unittest/test_instructionset.cpp b/core/src/index/unittest/test_instructionset.cpp deleted file mode 100644 index f1eb5746ae..0000000000 --- a/core/src/index/unittest/test_instructionset.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// 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 limitations -// under the License. - -#include "faiss/utils/instruction_set.h" - -#include -#include - -void -ShowInstructionSet() { - auto& outstream = std::cout; - - auto support_message = [&outstream](const std::string& isa_feature, bool is_supported) { - outstream << isa_feature << (is_supported ? " supported" : " not supported") << std::endl; - }; - - faiss::InstructionSet& instruction_set_inst = faiss::InstructionSet::GetInstance(); - - std::cout << instruction_set_inst.Vendor() << std::endl; - std::cout << instruction_set_inst.Brand() << std::endl; - - support_message("3DNOW", instruction_set_inst._3DNOW()); - support_message("3DNOWEXT", instruction_set_inst._3DNOWEXT()); - support_message("ABM", instruction_set_inst.ABM()); - support_message("ADX", instruction_set_inst.ADX()); - support_message("AES", instruction_set_inst.AES()); - support_message("AVX", instruction_set_inst.AVX()); - support_message("AVX2", instruction_set_inst.AVX2()); - support_message("AVX512BW", instruction_set_inst.AVX512BW()); - support_message("AVX512CD", instruction_set_inst.AVX512CD()); - support_message("AVX512DQ", instruction_set_inst.AVX512DQ()); - support_message("AVX512ER", instruction_set_inst.AVX512ER()); - support_message("AVX512F", instruction_set_inst.AVX512F()); - support_message("AVX512PF", instruction_set_inst.AVX512PF()); - support_message("AVX512VL", instruction_set_inst.AVX512VL()); - support_message("BMI1", instruction_set_inst.BMI1()); - support_message("BMI2", instruction_set_inst.BMI2()); - support_message("CLFSH", instruction_set_inst.CLFSH()); - support_message("CMOV", instruction_set_inst.CMOV()); - support_message("CMPXCHG16B", instruction_set_inst.CMPXCHG16B()); - support_message("CX8", instruction_set_inst.CX8()); - support_message("ERMS", instruction_set_inst.ERMS()); - support_message("F16C", instruction_set_inst.F16C()); - support_message("FMA", instruction_set_inst.FMA()); - support_message("FSGSBASE", instruction_set_inst.FSGSBASE()); - support_message("FXSR", instruction_set_inst.FXSR()); - support_message("HLE", instruction_set_inst.HLE()); - support_message("INVPCID", instruction_set_inst.INVPCID()); - support_message("LAHF", instruction_set_inst.LAHF()); - support_message("LZCNT", instruction_set_inst.LZCNT()); - support_message("MMX", instruction_set_inst.MMX()); - support_message("MMXEXT", instruction_set_inst.MMXEXT()); - support_message("MONITOR", instruction_set_inst.MONITOR()); - support_message("MOVBE", instruction_set_inst.MOVBE()); - support_message("MSR", instruction_set_inst.MSR()); - support_message("OSXSAVE", instruction_set_inst.OSXSAVE()); - support_message("PCLMULQDQ", instruction_set_inst.PCLMULQDQ()); - support_message("POPCNT", instruction_set_inst.POPCNT()); - support_message("PREFETCHWT1", instruction_set_inst.PREFETCHWT1()); - support_message("RDRAND", instruction_set_inst.RDRAND()); - support_message("RDSEED", instruction_set_inst.RDSEED()); - support_message("RDTSCP", instruction_set_inst.RDTSCP()); - support_message("RTM", instruction_set_inst.RTM()); - support_message("SEP", instruction_set_inst.SEP()); - support_message("SHA", instruction_set_inst.SHA()); - support_message("SSE", instruction_set_inst.SSE()); - support_message("SSE2", instruction_set_inst.SSE2()); - support_message("SSE3", instruction_set_inst.SSE3()); - support_message("SSE4.1", instruction_set_inst.SSE41()); - support_message("SSE4.2", instruction_set_inst.SSE42()); - support_message("SSE4a", instruction_set_inst.SSE4a()); - support_message("SSSE3", instruction_set_inst.SSSE3()); - support_message("SYSCALL", instruction_set_inst.SYSCALL()); - support_message("TBM", instruction_set_inst.TBM()); - support_message("XOP", instruction_set_inst.XOP()); - support_message("XSAVE", instruction_set_inst.XSAVE()); -} - -TEST(InstructionSetTest, INSTRUCTION_SET_TEST) { - ASSERT_NO_FATAL_FAILURE(ShowInstructionSet()); -} diff --git a/core/src/index/unittest/test_ivf.cpp b/core/src/index/unittest/test_ivf.cpp deleted file mode 100644 index 1827d0b4d1..0000000000 --- a/core/src/index/unittest/test_ivf.cpp +++ /dev/null @@ -1,405 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include - -#include -#include -#include -#include - -#ifdef MILVUS_GPU_VERSION -#include -#endif - -#include "knowhere/common/Exception.h" -#include "knowhere/common/Timer.h" -#include "knowhere/index/vector_index/IndexIVF.h" -#include "knowhere/index/vector_index/IndexIVFPQ.h" -#include "knowhere/index/vector_index/IndexIVFSQ.h" -#include "knowhere/index/vector_index/IndexType.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" - -#ifdef MILVUS_GPU_VERSION -#include "knowhere/index/vector_index/gpu/IndexGPUIVF.h" -#include "knowhere/index/vector_index/gpu/IndexGPUIVFPQ.h" -#include "knowhere/index/vector_index/gpu/IndexGPUIVFSQ.h" -#include "knowhere/index/vector_index/gpu/IndexIVFSQHybrid.h" -#include "knowhere/index/vector_index/helpers/Cloner.h" -#include "knowhere/index/vector_index/helpers/FaissGpuResourceMgr.h" -#endif - -#include "unittest/Helper.h" -#include "unittest/utils.h" - -using ::testing::Combine; -using ::testing::TestWithParam; -using ::testing::Values; - -class IVFTest : public DataGen, - public TestWithParam<::std::tuple> { - protected: - void - SetUp() override { -#ifdef MILVUS_GPU_VERSION - milvus::knowhere::FaissGpuResourceMgr::GetInstance().InitDevice(DEVICEID, PINMEM, TEMPMEM, RESNUM); -#endif - std::tie(index_type_, index_mode_) = GetParam(); - // Init_with_default(); - // nb = 1000000; - // nq = 1000; - // k = 1000; - Generate(DIM, NB, NQ); - index_ = IndexFactory(index_type_, index_mode_); - conf_ = ParamGenerator::GetInstance().Gen(index_type_); - // conf_->Dump(); - } - - void - TearDown() override { -#ifdef MILVUS_GPU_VERSION - milvus::knowhere::FaissGpuResourceMgr::GetInstance().Free(); -#endif - } - - protected: - milvus::knowhere::IndexType index_type_; - milvus::knowhere::IndexMode index_mode_; - milvus::knowhere::Config conf_; - milvus::knowhere::IVFPtr index_ = nullptr; -}; - -INSTANTIATE_TEST_CASE_P( - IVFParameters, IVFTest, - Values( -#ifdef MILVUS_GPU_VERSION - std::make_tuple(milvus::knowhere::IndexEnum::INDEX_FAISS_IVFFLAT, milvus::knowhere::IndexMode::MODE_GPU), - std::make_tuple(milvus::knowhere::IndexEnum::INDEX_FAISS_IVFPQ, milvus::knowhere::IndexMode::MODE_GPU), - std::make_tuple(milvus::knowhere::IndexEnum::INDEX_FAISS_IVFSQ8, milvus::knowhere::IndexMode::MODE_GPU), - std::make_tuple(milvus::knowhere::IndexEnum::INDEX_FAISS_IVFSQ8H, milvus::knowhere::IndexMode::MODE_GPU), -#endif - std::make_tuple(milvus::knowhere::IndexEnum::INDEX_FAISS_IVFFLAT, milvus::knowhere::IndexMode::MODE_CPU), - std::make_tuple(milvus::knowhere::IndexEnum::INDEX_FAISS_IVFPQ, milvus::knowhere::IndexMode::MODE_CPU), - std::make_tuple(milvus::knowhere::IndexEnum::INDEX_FAISS_IVFSQ8, milvus::knowhere::IndexMode::MODE_CPU))); - -TEST_P(IVFTest, ivf_basic_cpu) { - assert(!xb.empty()); - - if (index_mode_ != milvus::knowhere::IndexMode::MODE_CPU) { - return; - } - - // null faiss index - ASSERT_ANY_THROW(index_->Add(base_dataset, conf_)); - ASSERT_ANY_THROW(index_->AddWithoutIds(base_dataset, conf_)); - - index_->Train(base_dataset, conf_); - index_->AddWithoutIds(base_dataset, conf_); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->Dim(), dim); - - auto result = index_->Query(query_dataset, conf_); - AssertAnns(result, nq, k); - // PrintResult(result, nq, k); - - if (index_type_ != milvus::knowhere::IndexEnum::INDEX_FAISS_IVFPQ) { -#if 0 - auto result2 = index_->QueryById(id_dataset, conf_); - AssertAnns(result2, nq, k); - - if (index_type_ != milvus::knowhere::IndexEnum::INDEX_FAISS_IVFSQ8) { - auto result3 = index_->GetVectorById(xid_dataset, conf_); - AssertVec(result3, base_dataset, xid_dataset, 1, dim); - } else { - auto result3 = index_->GetVectorById(xid_dataset, conf_); - /* for SQ8, sometimes the mean diff can bigger than 20% */ - // AssertVec(result3, base_dataset, xid_dataset, 1, dim, CheckMode::CHECK_APPROXIMATE_EQUAL); - } -#endif - - faiss::ConcurrentBitsetPtr concurrent_bitset_ptr = std::make_shared(nb); - for (int64_t i = 0; i < nq; ++i) { - concurrent_bitset_ptr->set(i); - } - index_->SetBlacklist(concurrent_bitset_ptr); - - auto result_bs_1 = index_->Query(query_dataset, conf_); - AssertAnns(result_bs_1, nq, k, CheckMode::CHECK_NOT_EQUAL); - // PrintResult(result, nq, k); - -#if 0 - auto result_bs_2 = index_->QueryById(id_dataset, conf_); - AssertAnns(result_bs_2, nq, k, CheckMode::CHECK_NOT_EQUAL); - // PrintResult(result, nq, k); - - auto result_bs_3 = index_->GetVectorById(xid_dataset, conf_); - AssertVec(result_bs_3, base_dataset, xid_dataset, 1, dim, CheckMode::CHECK_NOT_EQUAL); -#endif - } - -#ifdef MILVUS_GPU_VERSION - milvus::knowhere::FaissGpuResourceMgr::GetInstance().Dump(); -#endif -} - -TEST_P(IVFTest, ivf_basic_gpu) { - assert(!xb.empty()); - - if (index_mode_ != milvus::knowhere::IndexMode::MODE_GPU) { - return; - } - - // null faiss index - ASSERT_ANY_THROW(index_->Add(base_dataset, conf_)); - ASSERT_ANY_THROW(index_->AddWithoutIds(base_dataset, conf_)); - - index_->BuildAll(base_dataset, conf_); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->Dim(), dim); - - auto result = index_->Query(query_dataset, conf_); - AssertAnns(result, nq, k); - // PrintResult(result, nq, k); - - faiss::ConcurrentBitsetPtr concurrent_bitset_ptr = std::make_shared(nb); - for (int64_t i = 0; i < nq; ++i) { - concurrent_bitset_ptr->set(i); - } - index_->SetBlacklist(concurrent_bitset_ptr); - - auto result_bs_1 = index_->Query(query_dataset, conf_); - AssertAnns(result_bs_1, nq, k, CheckMode::CHECK_NOT_EQUAL); - // PrintResult(result, nq, k); - -#ifdef MILVUS_GPU_VERSION - milvus::knowhere::FaissGpuResourceMgr::GetInstance().Dump(); -#endif -} - -TEST_P(IVFTest, ivf_serialize) { - fiu_init(0); - auto serialize = [](const std::string& filename, milvus::knowhere::BinaryPtr& bin, uint8_t* ret) { - FileIOWriter writer(filename); - writer(static_cast(bin->data.get()), bin->size); - - FileIOReader reader(filename); - reader(ret, bin->size); - }; - - { - // serialize index - index_->Train(base_dataset, conf_); - index_->Add(base_dataset, conf_); - auto binaryset = index_->Serialize(); - auto bin = binaryset.GetByName("IVF"); - - std::string filename = "/tmp/ivf_test_serialize.bin"; - auto load_data = new uint8_t[bin->size]; - serialize(filename, bin, load_data); - - binaryset.clear(); - std::shared_ptr data(load_data); - binaryset.Append("IVF", data, bin->size); - - index_->Load(binaryset); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->Dim(), dim); - auto result = index_->Query(query_dataset, conf_); - AssertAnns(result, nq, conf_[milvus::knowhere::meta::TOPK]); - } -} - -// TODO(linxj): deprecated -#ifdef MILVUS_GPU_VERSION -TEST_P(IVFTest, clone_test) { - assert(!xb.empty()); - - index_->Train(base_dataset, conf_); - index_->Add(base_dataset, conf_); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->Dim(), dim); - - /* set peseodo index size, avoid throw exception */ - index_->SetIndexSize(nq * dim * sizeof(float)); - - auto result = index_->Query(query_dataset, conf_); - AssertAnns(result, nq, conf_[milvus::knowhere::meta::TOPK]); - // PrintResult(result, nq, k); - - auto AssertEqual = [&](milvus::knowhere::DatasetPtr p1, milvus::knowhere::DatasetPtr p2) { - auto ids_p1 = p1->Get(milvus::knowhere::meta::IDS); - auto ids_p2 = p2->Get(milvus::knowhere::meta::IDS); - - for (int i = 0; i < nq * k; ++i) { - EXPECT_EQ(*((int64_t*)(ids_p2) + i), *((int64_t*)(ids_p1) + i)); - // EXPECT_EQ(*(ids_p2->data()->GetValues(1, i)), *(ids_p1->data()->GetValues(1, - // i))); - } - }; - - // { - // // clone in place - // std::vector support_idx_vec{"IVF", "GPUIVF", "IVFPQ", "IVFSQ", "GPUIVFSQ"}; - // auto finder = std::find(support_idx_vec.cbegin(), support_idx_vec.cend(), index_type); - // if (finder != support_idx_vec.cend()) { - // EXPECT_NO_THROW({ - // auto clone_index = index_->Clone(); - // auto clone_result = clone_index->Search(query_dataset, conf); - // //AssertAnns(result, nq, conf[milvus::knowhere::meta::TOPK]); - // AssertEqual(result, clone_result); - // std::cout << "inplace clone [" << index_type << "] success" << std::endl; - // }); - // } else { - // EXPECT_THROW({ - // std::cout << "inplace clone [" << index_type << "] failed" << std::endl; - // auto clone_index = index_->Clone(); - // }, KnowhereException); - // } - // } - - { - // copy from gpu to cpu - if (index_mode_ == milvus::knowhere::IndexMode::MODE_GPU) { - EXPECT_NO_THROW({ - auto clone_index = milvus::knowhere::cloner::CopyGpuToCpu(index_, milvus::knowhere::Config()); - auto clone_result = clone_index->Query(query_dataset, conf_); - AssertEqual(result, clone_result); - std::cout << "clone G <=> C [" << index_type_ << "] success" << std::endl; - }); - } else { - EXPECT_THROW( - { - std::cout << "clone G <=> C [" << index_type_ << "] failed" << std::endl; - auto clone_index = milvus::knowhere::cloner::CopyGpuToCpu(index_, milvus::knowhere::Config()); - }, - milvus::knowhere::KnowhereException); - } - } - - { - // copy to gpu - if (index_type_ != milvus::knowhere::IndexEnum::INDEX_FAISS_IVFSQ8H) { - EXPECT_NO_THROW({ - auto clone_index = milvus::knowhere::cloner::CopyCpuToGpu(index_, DEVICEID, milvus::knowhere::Config()); - auto clone_result = clone_index->Query(query_dataset, conf_); - AssertEqual(result, clone_result); - std::cout << "clone C <=> G [" << index_type_ << "] success" << std::endl; - }); - EXPECT_ANY_THROW(milvus::knowhere::cloner::CopyCpuToGpu(index_, -1, milvus::knowhere::Config())); - } - } -} -#endif - -#ifdef MILVUS_GPU_VERSION -TEST_P(IVFTest, gpu_seal_test) { - if (index_mode_ != milvus::knowhere::IndexMode::MODE_GPU) { - return; - } - assert(!xb.empty()); - - ASSERT_ANY_THROW(index_->Query(query_dataset, conf_)); - ASSERT_ANY_THROW(index_->Seal()); - - index_->Train(base_dataset, conf_); - index_->Add(base_dataset, conf_); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->Dim(), dim); - - /* set peseodo index size, avoid throw exception */ - index_->SetIndexSize(nq * dim * sizeof(float)); - - auto result = index_->Query(query_dataset, conf_); - AssertAnns(result, nq, conf_[milvus::knowhere::meta::TOPK]); - - fiu_init(0); - fiu_enable("IVF.Search.throw_std_exception", 1, nullptr, 0); - ASSERT_ANY_THROW(index_->Query(query_dataset, conf_)); - fiu_disable("IVF.Search.throw_std_exception"); - fiu_enable("IVF.Search.throw_faiss_exception", 1, nullptr, 0); - ASSERT_ANY_THROW(index_->Query(query_dataset, conf_)); - fiu_disable("IVF.Search.throw_faiss_exception"); - - auto cpu_idx = milvus::knowhere::cloner::CopyGpuToCpu(index_, milvus::knowhere::Config()); - milvus::knowhere::IVFPtr ivf_idx = std::dynamic_pointer_cast(cpu_idx); - - milvus::knowhere::TimeRecorder tc("CopyToGpu"); - milvus::knowhere::cloner::CopyCpuToGpu(cpu_idx, DEVICEID, milvus::knowhere::Config()); - auto without_seal = tc.RecordSection("Without seal"); - ivf_idx->Seal(); - tc.RecordSection("seal cost"); - milvus::knowhere::cloner::CopyCpuToGpu(cpu_idx, DEVICEID, milvus::knowhere::Config()); - auto with_seal = tc.RecordSection("With seal"); - ASSERT_GE(without_seal, with_seal); - - // copy to GPU with invalid device id - ASSERT_ANY_THROW(milvus::knowhere::cloner::CopyCpuToGpu(cpu_idx, -1, milvus::knowhere::Config())); -} - -TEST_P(IVFTest, invalid_gpu_source) { - if (index_mode_ != milvus::knowhere::IndexMode::MODE_GPU) { - return; - } - - auto invalid_conf = ParamGenerator::GetInstance().Gen(index_type_); - invalid_conf[milvus::knowhere::meta::DEVICEID] = -1; - - if (index_type_ == milvus::knowhere::IndexEnum::INDEX_FAISS_IVFFLAT) { - // null faiss index - index_->SetIndexSize(0); - milvus::knowhere::cloner::CopyGpuToCpu(index_, milvus::knowhere::Config()); - } - - index_->Train(base_dataset, conf_); - - fiu_init(0); - fiu_enable("GPUIVF.SerializeImpl.throw_exception", 1, nullptr, 0); - ASSERT_ANY_THROW(index_->Serialize()); - fiu_disable("GPUIVF.SerializeImpl.throw_exception"); - - fiu_enable("GPUIVF.search_impl.invald_index", 1, nullptr, 0); - ASSERT_ANY_THROW(index_->Query(base_dataset, invalid_conf)); - fiu_disable("GPUIVF.search_impl.invald_index"); - - auto ivf_index = std::dynamic_pointer_cast(index_); - if (ivf_index) { - auto gpu_index = std::dynamic_pointer_cast(ivf_index); - gpu_index->SetGpuDevice(-1); - ASSERT_EQ(gpu_index->GetGpuDevice(), -1); - } - - // ASSERT_ANY_THROW(index_->Load(binaryset)); - ASSERT_ANY_THROW(index_->Train(base_dataset, invalid_conf)); -} - -TEST_P(IVFTest, IVFSQHybrid_test) { - if (index_type_ != milvus::knowhere::IndexEnum::INDEX_FAISS_IVFSQ8H) { - return; - } - fiu_init(0); - - index_->SetIndexSize(0); - milvus::knowhere::cloner::CopyGpuToCpu(index_, conf_); - ASSERT_ANY_THROW(milvus::knowhere::cloner::CopyCpuToGpu(index_, -1, conf_)); - - fiu_enable("FaissGpuResourceMgr.GetRes.ret_null", 1, nullptr, 0); - ASSERT_ANY_THROW(index_->Train(base_dataset, conf_)); - ASSERT_ANY_THROW(index_->CopyCpuToGpu(DEVICEID, conf_)); - fiu_disable("FaissGpuResourceMgr.GetRes.ret_null"); - - index_->Train(base_dataset, conf_); - auto index = std::dynamic_pointer_cast(index_); - ASSERT_TRUE(index != nullptr); - ASSERT_ANY_THROW(index->UnsetQuantizer()); - - ASSERT_ANY_THROW(index->SetQuantizer(nullptr)); -} -#endif diff --git a/core/src/index/unittest/test_knowhere.cpp b/core/src/index/unittest/test_knowhere.cpp deleted file mode 100644 index 1e2fccf5c3..0000000000 --- a/core/src/index/unittest/test_knowhere.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "config/Config.h" -#include "wrapper/KnowhereResource.h" -#include "wrapper/utils.h" - -#include -#include -#include - -TEST_F(KnowhereTest, KNOWHERE_RESOURCE_TEST) { - std::string config_path(CONFIG_PATH); - config_path += CONFIG_FILE; - milvus::server::Config& config = milvus::server::Config::GetInstance(); - milvus::Status s = config.LoadConfigFile(config_path); - ASSERT_TRUE(s.ok()); - - milvus::engine::KnowhereResource::Initialize(); - milvus::engine::KnowhereResource::Finalize(); - -#ifdef MILVUS_GPU_VERSION - fiu_init(0); - fiu_enable("check_config_gpu_resource_enable_fail", 1, nullptr, 0); - s = milvus::engine::KnowhereResource::Initialize(); - ASSERT_FALSE(s.ok()); - fiu_disable("check_config_gpu_resource_enable_fail"); - - fiu_enable("KnowhereResource.Initialize.disable_gpu", 1, nullptr, 0); - s = milvus::engine::KnowhereResource::Initialize(); - ASSERT_TRUE(s.ok()); - fiu_disable("KnowhereResource.Initialize.disable_gpu"); - - fiu_enable("check_gpu_resource_config_build_index_fail", 1, nullptr, 0); - s = milvus::engine::KnowhereResource::Initialize(); - ASSERT_FALSE(s.ok()); - fiu_disable("check_gpu_resource_config_build_index_fail"); - - fiu_enable("check_gpu_resource_config_search_fail", 1, nullptr, 0); - s = milvus::engine::KnowhereResource::Initialize(); - ASSERT_FALSE(s.ok()); - fiu_disable("check_gpu_resource_config_search_fail"); -#endif -} diff --git a/core/src/index/unittest/test_nsg.cpp b/core/src/index/unittest/test_nsg.cpp deleted file mode 100644 index c4dc46a869..0000000000 --- a/core/src/index/unittest/test_nsg.cpp +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include -#include -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/FaissBaseIndex.h" -#include "knowhere/index/vector_index/IndexNSG.h" -#include "knowhere/index/vector_index/helpers/IndexParameter.h" -#ifdef MILVUS_GPU_VERSION -#include "knowhere/index/vector_index/gpu/IndexGPUIDMAP.h" -#include "knowhere/index/vector_index/helpers/Cloner.h" -#include "knowhere/index/vector_index/helpers/FaissGpuResourceMgr.h" -#endif - -#include "knowhere/common/Timer.h" -#include "knowhere/index/vector_index/impl/nsg/NSGIO.h" - -#include "unittest/utils.h" - -using ::testing::Combine; -using ::testing::TestWithParam; -using ::testing::Values; - -constexpr int64_t DEVICE_GPU0 = 0; - -class NSGInterfaceTest : public DataGen, public ::testing::Test { - protected: - void - SetUp() override { -#ifdef MILVUS_GPU_VERSION - int64_t MB = 1024 * 1024; - milvus::knowhere::FaissGpuResourceMgr::GetInstance().InitDevice(DEVICE_GPU0, MB * 200, MB * 600, 1); -#endif - int nsg_dim = 256; - Generate(nsg_dim, 20000, nq); - index_ = std::make_shared(); - - train_conf = milvus::knowhere::Config{{milvus::knowhere::meta::DIM, 256}, - {milvus::knowhere::IndexParams::nlist, 163}, - {milvus::knowhere::IndexParams::nprobe, 8}, - {milvus::knowhere::IndexParams::knng, 20}, - {milvus::knowhere::IndexParams::search_length, 40}, - {milvus::knowhere::IndexParams::out_degree, 30}, - {milvus::knowhere::IndexParams::candidate, 100}, - {milvus::knowhere::Metric::TYPE, milvus::knowhere::Metric::L2}}; - - search_conf = milvus::knowhere::Config{ - {milvus::knowhere::meta::TOPK, k}, - {milvus::knowhere::IndexParams::search_length, 30}, - }; - } - - void - TearDown() override { -#ifdef MILVUS_GPU_VERSION - milvus::knowhere::FaissGpuResourceMgr::GetInstance().Free(); -#endif - } - - protected: - std::shared_ptr index_; - milvus::knowhere::Config train_conf; - milvus::knowhere::Config search_conf; -}; - -TEST_F(NSGInterfaceTest, basic_test) { - assert(!xb.empty()); - fiu_init(0); - // untrained index - { - ASSERT_ANY_THROW(index_->Serialize()); - ASSERT_ANY_THROW(index_->Query(query_dataset, search_conf)); - ASSERT_ANY_THROW(index_->Add(base_dataset, search_conf)); - ASSERT_ANY_THROW(index_->AddWithoutIds(base_dataset, search_conf)); - } - - train_conf[milvus::knowhere::meta::DEVICEID] = -1; - index_->BuildAll(base_dataset, train_conf); - auto result = index_->Query(query_dataset, search_conf); - AssertAnns(result, nq, k); - - auto binaryset = index_->Serialize(); - { - fiu_enable("NSG.Serialize.throw_exception", 1, nullptr, 0); - ASSERT_ANY_THROW(index_->Serialize()); - fiu_disable("NSG.Serialize.throw_exception"); - } - - /* test NSG GPU train */ - auto new_index_1 = std::make_shared(DEVICE_GPU0); - train_conf[milvus::knowhere::meta::DEVICEID] = DEVICE_GPU0; - new_index_1->BuildAll(base_dataset, train_conf); - auto new_result_1 = new_index_1->Query(query_dataset, search_conf); - AssertAnns(new_result_1, nq, k); - - /* test NSG index load */ - auto new_index_2 = std::make_shared(); - new_index_2->Load(binaryset); - { - fiu_enable("NSG.Load.throw_exception", 1, nullptr, 0); - ASSERT_ANY_THROW(new_index_2->Load(binaryset)); - fiu_disable("NSG.Load.throw_exception"); - } - - auto new_result_2 = new_index_2->Query(query_dataset, search_conf); - AssertAnns(new_result_2, nq, k); - - ASSERT_EQ(index_->Count(), nb); - ASSERT_EQ(index_->Dim(), dim); -} - -TEST_F(NSGInterfaceTest, compare_test) { - milvus::knowhere::impl::DistanceL2 distanceL2; - milvus::knowhere::impl::DistanceIP distanceIP; - - milvus::knowhere::TimeRecorder tc("Compare"); - for (int i = 0; i < 1000; ++i) { - distanceL2.Compare(xb.data(), xq.data(), 256); - } - tc.RecordSection("L2"); - for (int i = 0; i < 1000; ++i) { - distanceIP.Compare(xb.data(), xq.data(), 256); - } - tc.RecordSection("IP"); -} - -TEST_F(NSGInterfaceTest, delete_test) { - assert(!xb.empty()); - - train_conf[milvus::knowhere::meta::DEVICEID] = DEVICE_GPU0; - index_->Train(base_dataset, train_conf); - - auto result = index_->Query(query_dataset, search_conf); - AssertAnns(result, nq, k); - - ASSERT_EQ(index_->Count(), nb); - ASSERT_EQ(index_->Dim(), dim); - - faiss::ConcurrentBitsetPtr bitset = std::make_shared(nb); - for (int i = 0; i < nq; i++) { - bitset->set(i); - } - - auto I_before = result->Get(milvus::knowhere::meta::IDS); - - // search xq with delete - index_->SetBlacklist(bitset); - auto result_after = index_->Query(query_dataset, search_conf); - AssertAnns(result_after, nq, k, CheckMode::CHECK_NOT_EQUAL); - auto I_after = result_after->Get(milvus::knowhere::meta::IDS); - - // First vector deleted - for (int i = 0; i < nq; i++) { - ASSERT_NE(I_before[i * k], I_after[i * k]); - } -} diff --git a/core/src/index/unittest/test_sptag.cpp b/core/src/index/unittest/test_sptag.cpp deleted file mode 100644 index 65349e5e8e..0000000000 --- a/core/src/index/unittest/test_sptag.cpp +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include -#include - -#include "knowhere/common/Exception.h" -#include "knowhere/index/vector_index/IndexSPTAG.h" -#include "knowhere/index/vector_index/adapter/SptagAdapter.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" - -#include "unittest/utils.h" - -using ::testing::Combine; -using ::testing::TestWithParam; -using ::testing::Values; - -class SPTAGTest : public DataGen, public TestWithParam { - protected: - void - SetUp() override { - IndexType = GetParam(); - Generate(128, 100, 5); - index_ = std::make_shared(IndexType); - if (IndexType == "KDT") { - conf = milvus::knowhere::Config{ - {milvus::knowhere::meta::DIM, dim}, - {milvus::knowhere::meta::TOPK, 10}, - {milvus::knowhere::Metric::TYPE, milvus::knowhere::Metric::L2}, - }; - } else { - conf = milvus::knowhere::Config{ - {milvus::knowhere::meta::DIM, dim}, - {milvus::knowhere::meta::TOPK, 10}, - {milvus::knowhere::Metric::TYPE, milvus::knowhere::Metric::L2}, - }; - } - - Init_with_default(); - } - - protected: - milvus::knowhere::Config conf; - std::shared_ptr index_ = nullptr; - std::string IndexType; -}; - -INSTANTIATE_TEST_CASE_P(SPTAGParameters, SPTAGTest, Values("KDT", "BKT")); - -// TODO(lxj): add test about count() and dimension() -TEST_P(SPTAGTest, sptag_basic) { - assert(!xb.empty()); - - // null faiss index - { - ASSERT_ANY_THROW(index_->Add(nullptr, conf)); - ASSERT_ANY_THROW(index_->AddWithoutIds(nullptr, conf)); - } - - index_->BuildAll(base_dataset, conf); - // index_->Add(base_dataset, conf); - auto result = index_->Query(query_dataset, conf); - AssertAnns(result, nq, k); - - { - auto ids = result->Get(milvus::knowhere::meta::IDS); - auto dist = result->Get(milvus::knowhere::meta::DISTANCE); - - std::stringstream ss_id; - std::stringstream ss_dist; - for (auto i = 0; i < nq; i++) { - for (auto j = 0; j < k; ++j) { - // ss_id << *ids->data()->GetValues(1, i * k + j) << " "; - // ss_dist << *dists->data()->GetValues(1, i * k + j) << " "; - ss_id << *((int64_t*)(ids) + i * k + j) << " "; - ss_dist << *((float*)(dist) + i * k + j) << " "; - } - ss_id << std::endl; - ss_dist << std::endl; - } - std::cout << "id\n" << ss_id.str() << std::endl; - std::cout << "dist\n" << ss_dist.str() << std::endl; - } -} - -TEST_P(SPTAGTest, sptag_serialize) { - assert(!xb.empty()); - - index_->Train(base_dataset, conf); - // index_->Add(base_dataset, conf); - auto binaryset = index_->Serialize(); - auto new_index = std::make_shared(IndexType); - new_index->Load(binaryset); - auto result = new_index->Query(query_dataset, conf); - AssertAnns(result, nq, k); - PrintResult(result, nq, k); - ASSERT_EQ(new_index->Count(), nb); - ASSERT_EQ(new_index->Dim(), dim); - // ASSERT_THROW({ new_index->Clone(); }, milvus::knowhere::KnowhereException); - // ASSERT_NO_THROW({ new_index->Seal(); }); - - { - int fileno = 0; - const std::string& base_name = "/tmp/sptag_serialize_test_bin_"; - std::vector filename_list; - std::vector> meta_list; - for (auto& iter : binaryset.binary_map_) { - const std::string& filename = base_name + std::to_string(fileno); - FileIOWriter writer(filename); - writer(iter.second->data.get(), iter.second->size); - - meta_list.emplace_back(std::make_pair(iter.first, iter.second->size)); - filename_list.push_back(filename); - ++fileno; - } - - milvus::knowhere::BinarySet load_data_list; - for (int i = 0; i < filename_list.size() && i < meta_list.size(); ++i) { - auto bin_size = meta_list[i].second; - FileIOReader reader(filename_list[i]); - - auto load_data = new uint8_t[bin_size]; - reader(load_data, bin_size); - std::shared_ptr data(load_data); - load_data_list.Append(meta_list[i].first, data, bin_size); - } - - auto new_index = std::make_shared(IndexType); - new_index->Load(load_data_list); - auto result = new_index->Query(query_dataset, conf); - AssertAnns(result, nq, k); - PrintResult(result, nq, k); - } -} diff --git a/core/src/index/unittest/test_vecindex.cpp b/core/src/index/unittest/test_vecindex.cpp deleted file mode 100644 index dc231f6403..0000000000 --- a/core/src/index/unittest/test_vecindex.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include - -#include "knowhere/index/vector_index/IndexType.h" -#include "knowhere/index/vector_index/VecIndex.h" -#include "knowhere/index/vector_index/VecIndexFactory.h" -#include "knowhere/index/vector_index/helpers/FaissGpuResourceMgr.h" - -#ifdef MILVUS_GPU_VERSION -#include "knowhere/index/vector_index/helpers/Cloner.h" -#endif - -#include "unittest/Helper.h" -#include "unittest/utils.h" - -using ::testing::Combine; -using ::testing::TestWithParam; -using ::testing::Values; - -class VecIndexTest : public DataGen, public Tuple> { - protected: - void - SetUp() override { -#ifdef MILVUS_GPU_VERSION - milvus::knowhere::FaissGpuResourceMgr::GetInstance().InitDevice(DEVICEID, PINMEM, TEMPMEM, RESNUM); -#endif - std::tie(index_type_, index_mode_, parameter_type_) = GetParam(); - Generate(DIM, NB, NQ); - index_ = milvus::knowhere::VecIndexFactory::GetInstance().CreateVecIndex(index_type_, index_mode_); - conf = ParamGenerator::GetInstance().Gen(parameter_type_); - } - - void - TearDown() override { -#ifdef MILVUS_GPU_VERSION - milvus::knowhere::FaissGpuResourceMgr::GetInstance().Free(); -#endif - } - - protected: - milvus::knowhere::IndexType index_type_; - milvus::knowhere::IndexMode index_mode_; - ParameterType parameter_type_; - milvus::knowhere::Config conf; - milvus::knowhere::VecIndexPtr index_ = nullptr; -}; - -INSTANTIATE_TEST_CASE_P( - IVFParameters, IVFTest, - Values( -#ifdef MILVUS_GPU_VERSION - std::make_tuple(milvus::knowhere::IndexType::INDEX_FAISS_IVFFLAT, milvus::knowhere::IndexMode::MODE_GPU), - std::make_tuple(milvus::knowhere::IndexType::INDEX_FAISS_IVFPQ, milvus::knowhere::IndexMode::MODE_GPU), - std::make_tuple(milvus::knowhere::IndexType::INDEX_FAISS_IVFSQ8, milvus::knowhere::IndexMode::MODE_GPU), - std::make_tuple(milvus::knowhere::IndexType::INDEX_FAISS_IVFSQ8H, milvus::knowhere::IndexMode::MODE_GPU), -#endif - std::make_tuple(milvus::knowhere::IndexType::INDEX_FAISS_IVFFLAT, milvus::knowhere::IndexMode::MODE_CPU), - std::make_tuple(milvus::knowhere::IndexType::INDEX_FAISS_IVFPQ, milvus::knowhere::IndexMode::MODE_CPU), - std::make_tuple(milvus::knowhere::IndexType::INDEX_FAISS_IVFSQ8, milvus::knowhere::IndexMode::MODE_CPU), - std::make_tuple(milvus::knowhere::IndexType::INDEX_NSG, milvus::knowhere::IndexMode::MODE_CPU), - std::make_tuple(milvus::knowhere::IndexType::INDEX_HNSW, milvus::knowhere::IndexMode::MODE_CPU), - std::make_tuple(milvus::knowhere::IndexType::INDEX_SPTAG_KDT_RNT, milvus::knowhere::IndexMode::MODE_CPU), - std::make_tuple(milvus::knowhere::IndexType::INDEX_SPTAG_BKT_RNT, milvus::knowhere::IndexMode::MODE_CPU))); - -TEST_P(VecIndexTest, basic) { - assert(!xb.empty()); - KNOWHERE_LOG_DEBUG << "conf: " << conf->dump(); - - index_->BuildAll(base_dataset, conf); - EXPECT_EQ(index_->Dim(), dim); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->index_type(), index_type_); - EXPECT_EQ(index_->index_mode(), index_mode_); - - auto result = index_->Query(query_dataset, conf); - AssertAnns(result, nq, conf[milvus::knowhere::meta::TOPK]); - PrintResult(result, nq, k); -} - -TEST_P(VecIndexTest, serialize) { - index_->BuildAll(base_dataset, conf); - EXPECT_EQ(index_->Dim(), dim); - EXPECT_EQ(index_->Count(), nb); - EXPECT_EQ(index_->index_type(), index_type_); - EXPECT_EQ(index_->index_mode(), index_mode_); - auto result = index_->Query(query_dataset, conf); - AssertAnns(result, nq, conf[milvus::knowhere::meta::TOPK]); - - auto binaryset = index_->Serialize(); - auto new_index = milvus::knowhere::VecIndexFactory::GetInstance().CreateVecIndex(index_type_, index_mode_); - new_index->Load(binaryset); - EXPECT_EQ(index_->Dim(), new_index->Dim()); - EXPECT_EQ(index_->Count(), new_index->Count()); - EXPECT_EQ(index_->index_type(), new_index->index_type()); - EXPECT_EQ(index_->index_mode(), new_index->index_mode()); - auto new_result = new_index_->Query(query_dataset, conf); - AssertAnns(new_result, nq, conf[milvus::knowhere::meta::TOPK]); -} - -// todo -#ifdef MILVUS_GPU_VERSION -TEST_P(VecIndexTest, copytogpu) { - // todo -} - -TEST_P(VecIndexTest, copytocpu) { - // todo -} -#endif diff --git a/core/src/index/unittest/test_wrapper.cpp b/core/src/index/unittest/test_wrapper.cpp deleted file mode 100644 index 7798701838..0000000000 --- a/core/src/index/unittest/test_wrapper.cpp +++ /dev/null @@ -1,447 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "easyloggingpp/easylogging++.h" - -#ifdef MILVUS_GPU_VERSION - -#include "knowhere/index/vector_index/helpers/FaissGpuResourceMgr.h" -#include "wrapper/WrapperException.h" - -#endif - -#include -#include -#include - -#include "knowhere/index/vector_index/helpers/IndexParameter.h" -#include "wrapper/VecIndex.h" -#include "wrapper/utils.h" - -INITIALIZE_EASYLOGGINGPP - -using ::testing::Combine; -using ::testing::TestWithParam; -using ::testing::Values; - -class KnowhereWrapperTest - : public DataGenBase, - public TestWithParam<::std::tuple> { - protected: - void - SetUp() override { -#ifdef MILVUS_GPU_VERSION - knowhere::FaissGpuResourceMgr::GetInstance().InitDevice(DEVICEID, PINMEM, TEMPMEM, RESNUM); -#endif - std::string generator_type; - std::tie(index_type, generator_type, dim, nb, nq, k) = GetParam(); - GenData(dim, nb, nq, xb, xq, ids, k, gt_ids, gt_dis); - - knowhere::Config tempconf{{knowhere::Metric::TYPE, knowhere::Metric::L2}, - {knowhere::meta::ROWS, nb}, - {knowhere::meta::DIM, dim}, - {knowhere::meta::TOPK, k}, - {knowhere::meta::DEVICEID, DEVICEID}}; - - index_ = GetVecIndexFactory(index_type); - conf = ParamGenerator::GetInstance().GenBuild(index_type, tempconf); - searchconf = ParamGenerator::GetInstance().GenSearchConf(index_type, tempconf); - } - - void - TearDown() override { -#ifdef MILVUS_GPU_VERSION - knowhere::FaissGpuResourceMgr::GetInstance().Free(); -#endif - } - - protected: - milvus::engine::IndexType index_type; - milvus::engine::VecIndexPtr index_ = nullptr; - knowhere::Config conf; - knowhere::Config searchconf; -}; - -INSTANTIATE_TEST_CASE_P( - WrapperParam, KnowhereWrapperTest, - Values( -//["Index type", "Generator type", "dim", "nb", "nq", "k", "build config", "search config"] -#ifdef MILVUS_GPU_VERSION - std::make_tuple(milvus::engine::IndexType::FAISS_IVFFLAT_GPU, "Default", DIM, NB, 10, 10), - std::make_tuple(milvus::engine::IndexType::FAISS_IVFFLAT_MIX, "Default", 64, 1000, 10, 10), - std::make_tuple(milvus::engine::IndexType::FAISS_IVFSQ8_GPU, "Default", DIM, NB, 10, 10), - std::make_tuple(milvus::engine::IndexType::FAISS_IVFSQ8_MIX, "Default", DIM, NB, 10, 10), - std::make_tuple(milvus::engine::IndexType::FAISS_IVFPQ_MIX, "Default", 64, 1000, 10, 10), -// std::make_tuple(milvus::engine::IndexType::NSG_MIX, "Default", 128, 250000, 10, 10), -#endif - // std::make_tuple(milvus::engine::IndexType::SPTAG_KDT_RNT_CPU, "Default", 128, 100, 10, 10), - // std::make_tuple(milvus::engine::IndexType::SPTAG_BKT_RNT_CPU, "Default", 126, 100, 10, 10), - std::make_tuple(milvus::engine::IndexType::HNSW, "Default", 64, 10000, 5, 10), - std::make_tuple(milvus::engine::IndexType::FAISS_IDMAP, "Default", 64, 1000, 10, 10), - std::make_tuple(milvus::engine::IndexType::FAISS_IVFFLAT_CPU, "Default", 64, 1000, 10, 10), - std::make_tuple(milvus::engine::IndexType::FAISS_IVFSQ8_CPU, "Default", DIM, NB, 10, 10))); - -#ifdef MILVUS_GPU_VERSION -TEST_P(KnowhereWrapperTest, WRAPPER_EXCEPTION_TEST) { - std::string err_msg = "failed"; - milvus::engine::WrapperException ex(err_msg); - - std::string msg = ex.what(); - EXPECT_EQ(msg, err_msg); -} - -#endif - -TEST_P(KnowhereWrapperTest, BASE_TEST) { - EXPECT_EQ(index_->GetType(), index_type); - - auto elems = nq * k; - std::vector res_ids(elems); - std::vector res_dis(elems); - - index_->BuildAll(nb, xb.data(), ids.data(), conf); - index_->Search(nq, xq.data(), res_dis.data(), res_ids.data(), searchconf); - AssertResult(res_ids, res_dis); - - { - index_->GetDeviceId(); - - fiu_init(0); - fiu_enable("VecIndexImpl.BuildAll.throw_knowhere_exception", 1, nullptr, 0); - fiu_enable("BFIndex.BuildAll.throw_knowhere_exception", 1, nullptr, 0); - fiu_enable("IVFMixIndex.BuildAll.throw_knowhere_exception", 1, nullptr, 0); - auto s = index_->BuildAll(nb, xb.data(), ids.data(), conf); - fiu_disable("IVFMixIndex.BuildAll.throw_knowhere_exception"); - fiu_disable("BFIndex.BuildAll.throw_knowhere_exception"); - fiu_disable("VecIndexImpl.BuildAll.throw_knowhere_exception"); - - fiu_enable("VecIndexImpl.BuildAll.throw_std_exception", 1, nullptr, 0); - fiu_enable("BFIndex.BuildAll.throw_std_exception", 1, nullptr, 0); - fiu_enable("IVFMixIndex.BuildAll.throw_std_exception", 1, nullptr, 0); - s = index_->BuildAll(nb, xb.data(), ids.data(), conf); - fiu_disable("IVFMixIndex.BuildAll.throw_std_exception"); - fiu_disable("BFIndex.BuildAll.throw_std_exception"); - fiu_disable("VecIndexImpl.BuildAll.throw_std_exception"); - - fiu_enable("VecIndexImpl.Add.throw_knowhere_exception", 1, nullptr, 0); - s = index_->Add(nb, xb.data(), ids.data()); - fiu_disable("VecIndexImpl.Add.throw_knowhere_exception"); - - fiu_enable("VecIndexImpl.Add.throw_std_exception", 1, nullptr, 0); - s = index_->Add(nb, xb.data(), ids.data()); - fiu_disable("VecIndexImpl.Add.throw_std_exception"); - - fiu_enable("VecIndexImpl.Search.throw_knowhere_exception", 1, nullptr, 0); - s = index_->Search(nq, xq.data(), res_dis.data(), res_ids.data(), searchconf); - fiu_disable("VecIndexImpl.Search.throw_knowhere_exception"); - - fiu_enable("VecIndexImpl.Search.throw_std_exception", 1, nullptr, 0); - s = index_->Search(nq, xq.data(), res_dis.data(), res_ids.data(), searchconf); - fiu_disable("VecIndexImpl.Search.throw_std_exception"); - } -} - -#ifdef MILVUS_GPU_VERSION -TEST_P(KnowhereWrapperTest, TO_GPU_TEST) { - if (index_type == milvus::engine::IndexType::HNSW) { - return; - } - EXPECT_EQ(index_->GetType(), index_type); - - auto elems = nq * k; - std::vector res_ids(elems); - std::vector res_dis(elems); - - index_->BuildAll(nb, xb.data(), ids.data(), conf); - index_->Search(nq, xq.data(), res_dis.data(), res_ids.data(), searchconf); - AssertResult(res_ids, res_dis); - - { - auto dev_idx = index_->CopyToGpu(DEVICEID); - for (int i = 0; i < 10; ++i) { - dev_idx->Search(nq, xq.data(), res_dis.data(), res_ids.data(), searchconf); - } - AssertResult(res_ids, res_dis); - } - - { - std::string file_location = "/tmp/knowhere_gpu_file"; - write_index(index_, file_location); - auto new_index = milvus::engine::read_index(file_location); - - auto dev_idx = new_index->CopyToGpu(DEVICEID); - for (int i = 0; i < 10; ++i) { - dev_idx->Search(nq, xq.data(), res_dis.data(), res_ids.data(), searchconf); - } - AssertResult(res_ids, res_dis); - } -} - -#endif - -TEST_P(KnowhereWrapperTest, SERIALIZE_TEST) { - std::cout << "type: " << static_cast(index_type) << std::endl; - EXPECT_EQ(index_->GetType(), index_type); - - auto elems = nq * k; - std::vector res_ids(elems); - std::vector res_dis(elems); - index_->BuildAll(nb, xb.data(), ids.data(), conf); - index_->Search(nq, xq.data(), res_dis.data(), res_ids.data(), searchconf); - AssertResult(res_ids, res_dis); - - { - auto binary = index_->Serialize(); - auto type = index_->GetType(); - auto new_index = GetVecIndexFactory(type); - new_index->Load(binary); - EXPECT_EQ(new_index->Dimension(), index_->Dimension()); - EXPECT_EQ(new_index->Count(), index_->Count()); - - std::vector res_ids(elems); - std::vector res_dis(elems); - new_index->Search(nq, xq.data(), res_dis.data(), res_ids.data(), searchconf); - AssertResult(res_ids, res_dis); - } - - { - std::string file_location = "/tmp/knowhere"; - write_index(index_, file_location); - auto new_index = milvus::engine::read_index(file_location); - EXPECT_EQ(new_index->GetType(), ConvertToCpuIndexType(index_type)); - EXPECT_EQ(new_index->Dimension(), index_->Dimension()); - EXPECT_EQ(new_index->Count(), index_->Count()); - - std::vector res_ids(elems); - std::vector res_dis(elems); - new_index->Search(nq, xq.data(), res_dis.data(), res_ids.data(), searchconf); - AssertResult(res_ids, res_dis); - } - - { - std::string file_location = "/tmp/knowhere_gpu_file"; - fiu_init(0); - fiu_enable("VecIndex.write_index.throw_knowhere_exception", 1, nullptr, 0); - auto s = write_index(index_, file_location); - ASSERT_FALSE(s.ok()); - fiu_disable("VecIndex.write_index.throw_knowhere_exception"); - - fiu_enable("VecIndex.write_index.throw_std_exception", 1, nullptr, 0); - s = write_index(index_, file_location); - ASSERT_FALSE(s.ok()); - fiu_disable("VecIndex.write_index.throw_std_exception"); - - fiu_enable("VecIndex.write_index.throw_no_space_exception", 1, nullptr, 0); - s = write_index(index_, file_location); - ASSERT_FALSE(s.ok()); - fiu_disable("VecIndex.write_index.throw_no_space_exception"); - } -} - -// #include "wrapper/ConfAdapter.h" - -// TEST(whatever, test_config) { -// milvus::engine::TempMetaConf conf; -// conf.nprobe = 16; -// conf.dim = 128; -// auto nsg_conf = std::make_shared(); -// nsg_conf->Match(conf); -// nsg_conf->MatchSearch(conf, milvus::engine::IndexType::NSG_MIX); - -// auto pq_conf = std::make_shared(); -// pq_conf->Match(conf); -// pq_conf->MatchSearch(conf, milvus::engine::IndexType::FAISS_IVFPQ_MIX); - -// auto kdt_conf = std::make_shared(); -// kdt_conf->Match(conf); -// kdt_conf->MatchSearch(conf, milvus::engine::IndexType::SPTAG_KDT_RNT_CPU); - -// auto bkt_conf = std::make_shared(); -// bkt_conf->Match(conf); -// bkt_conf->MatchSearch(conf, milvus::engine::IndexType::SPTAG_BKT_RNT_CPU); - -// auto config_mgr = milvus::engine::AdapterMgr::GetInstance(); -// try { -// config_mgr.GetAdapter(milvus::engine::IndexType::INVALID); -// } catch (std::exception& e) { -// std::cout << "catch an expected exception" << std::endl; -// } - -// conf.size = 1000000.0; -// conf.nlist = 10; -// auto ivf_conf = std::make_shared(); -// ivf_conf->Match(conf); -// conf.nprobe = -1; -// ivf_conf->MatchSearch(conf, milvus::engine::IndexType::FAISS_IVFFLAT_GPU); -// conf.nprobe = 4096; -// ivf_conf->MatchSearch(conf, milvus::engine::IndexType::FAISS_IVFPQ_GPU); - -// auto ivf_pq_conf = std::make_shared(); -// conf.metric_type = knowhere::METRICTYPE::IP; -// try { -// ivf_pq_conf->Match(conf); -// } catch (std::exception& e) { -// std::cout << "catch an expected exception" << std::endl; -// } - -// conf.metric_type = knowhere::METRICTYPE::L2; -// fiu_init(0); -// fiu_enable("IVFPQConfAdapter.Match.empty_resset", 1, nullptr, 0); -// try { -// ivf_pq_conf->Match(conf); -// } catch (std::exception& e) { -// std::cout << "catch an expected exception" << std::endl; -// } -// fiu_disable("IVFPQConfAdapter.Match.empty_resset"); - -// conf.nprobe = -1; -// try { -// ivf_pq_conf->MatchSearch(conf, milvus::engine::IndexType::FAISS_IVFPQ_GPU); -// } catch (std::exception& e) { -// std::cout << "catch an expected exception" << std::endl; -// } -// } - -#include "wrapper/VecImpl.h" - -TEST(BFIndex, test_bf_index_fail) { - auto bf_ptr = std::make_shared(nullptr); - auto float_vec = bf_ptr->GetRawVectors(); - ASSERT_EQ(float_vec, nullptr); - milvus::engine::Config config; - - fiu_init(0); - fiu_enable("BFIndex.Build.throw_knowhere_exception", 1, nullptr, 0); - auto err_code = bf_ptr->Build(config); - ASSERT_EQ(err_code, milvus::KNOWHERE_UNEXPECTED_ERROR); - fiu_disable("BFIndex.Build.throw_knowhere_exception"); - - fiu_enable("BFIndex.Build.throw_std_exception", 1, nullptr, 0); - err_code = bf_ptr->Build(config); - ASSERT_EQ(err_code, milvus::KNOWHERE_ERROR); - fiu_disable("BFIndex.Build.throw_std_exception"); -} - -// #include "knowhere/index/vector_index/IndexIDMAP.h" -// #include "src/wrapper/VecImpl.h" -// #include "src/index/unittest/utils.h" -// The two case below prove NSG is concern with data distribution -// Further work: 1. Use right basedata and pass it by milvus -// a. batch size is 100000 [Pass] -// b. transfer all at once [Pass] -// 2. Use SIFT1M in test and check time cost [] -// TEST_P(KnowhereWrapperTest, nsgwithidmap) { -// auto idmap = GetVecIndexFactory(milvus::engine::IndexType::FAISS_IDMAP); -// auto ori_xb = xb; -// auto ori_ids = ids; -// std::vector temp_xb; -// std::vector temp_ids; -// nb = 50000; -// for (int i = 0; i < 20; ++i) { -// GenData(dim, nb, nq, xb, xq, ids, k, gt_ids, gt_dis); -// assert(xb.size() == nb*dim); -// //#define IDMAP -// #ifdef IDMAP -// temp_xb.insert(temp_xb.end(), xb.data(), xb.data() + nb*dim); -// temp_ids.insert(temp_ids.end(), ori_ids.data()+nb*i, ori_ids.data() + nb*(i+1)); -// if (i == 0) { -// idmap->BuildAll(nb, temp_xb.data(), temp_ids.data(), conf); -// } else { -// idmap->Add(nb, temp_xb.data(), temp_ids.data()); -// } -// temp_xb.clear(); -// temp_ids.clear(); -// #else -// temp_xb.insert(temp_xb.end(), xb.data(), xb.data() + nb*dim); -// temp_ids.insert(temp_ids.end(), ori_ids.data()+nb*i, ori_ids.data() + nb*(i+1)); -// #endif -// } - -// #ifdef IDMAP -// auto idmap_idx = std::dynamic_pointer_cast(idmap); -// auto x = idmap_idx->Count(); -// index_->BuildAll(idmap_idx->Count(), idmap_idx->GetRawVectors(), idmap_idx->GetRawIds(), conf); -// #else -// assert(temp_xb.size() == 1000000*128); -// index_->BuildAll(1000000, temp_xb.data(), ori_ids.data(), conf); -// #endif -// } - -// TEST_P(KnowhereWrapperTest, nsgwithsidmap) { -// auto idmap = GetVecIndexFactory(milvus::engine::IndexType::FAISS_IDMAP); -// auto ori_xb = xb; -// std::vector temp_xb; -// std::vector temp_ids; -// nb = 50000; -// for (int i = 0; i < 20; ++i) { -// #define IDMAP -// #ifdef IDMAP -// temp_xb.insert(temp_xb.end(), ori_xb.data()+nb*dim*i, ori_xb.data() + nb*dim*(i+1)); -// temp_ids.insert(temp_ids.end(), ids.data()+nb*i, ids.data() + nb*(i+1)); -// if (i == 0) { -// idmap->BuildAll(nb, temp_xb.data(), temp_ids.data(), conf); -// } else { -// idmap->Add(nb, temp_xb.data(), temp_ids.data()); -// } -// temp_xb.clear(); -// temp_ids.clear(); -// #else -// temp_xb.insert(temp_xb.end(), ori_xb.data()+nb*dim*i, ori_xb.data() + nb*dim*(i+1)); -// temp_ids.insert(temp_ids.end(), ids.data()+nb*i, ids.data() + nb*(i+1)); -// #endif -// } - -// #ifdef IDMAP -// auto idmap_idx = std::dynamic_pointer_cast(idmap); -// auto x = idmap_idx->Count(); -// index_->BuildAll(idmap_idx->Count(), idmap_idx->GetRawVectors(), idmap_idx->GetRawIds(), conf); -// #else -// index_->BuildAll(1000000, temp_xb.data(), temp_ids.data(), conf); -// #endif - -// // The code use to store raw base data -// FileIOWriter writer("/tmp/newraw"); -// ori_xb.shrink_to_fit(); -// std::cout << "size" << ori_xb.size(); -// writer(static_cast(ori_xb.data()), ori_xb.size()* sizeof(float)); -// std::cout << "Finish!" << std::endl; -// } - -// void load_data(char* filename, float*& data, unsigned& num, -// unsigned& dim) { // load data with sift10K pattern -// std::ifstream in(filename, std::ios::binary); -// if (!in.is_open()) { -// std::cout << "open file error" << std::endl; -// exit(-1); -// } -// in.read((char*)&dim, 4); -// in.seekg(0, std::ios::end); -// std::ios::pos_type ss = in.tellg(); -// size_t fsize = (size_t)ss; -// num = (unsigned)(fsize / (dim + 1) / 4); -// data = new float[(size_t)num * (size_t)dim]; - -// in.seekg(0, std::ios::beg); -// for (size_t i = 0; i < num; i++) { -// in.seekg(4, std::ios::cur); -// in.read((char*)(data + i * dim), dim * 4); -// } -// in.close(); -// } - -// TEST_P(KnowhereWrapperTest, Sift1M) { -// float* data = nullptr; -// unsigned points_num, dim; -// load_data("/mnt/112d53a6-5592-4360-a33b-7fd789456fce/workspace/Data/sift/sift_base.fvecs", data, points_num, -// dim); std::cout << points_num << " " << dim << std::endl; - -// index_->BuildAll(points_num, data, ids.data(), conf); -// } diff --git a/core/src/index/unittest/utils.cpp b/core/src/index/unittest/utils.cpp deleted file mode 100644 index df9c2a0821..0000000000 --- a/core/src/index/unittest/utils.cpp +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "unittest/utils.h" -#include "knowhere/index/vector_index/adapter/VectorAdapter.h" - -#include -#include -#include -#include -#include - -INITIALIZE_EASYLOGGINGPP - -void -InitLog() { - el::Configurations defaultConf; - defaultConf.setToDefault(); - defaultConf.set(el::Level::Debug, el::ConfigurationType::Format, "[%thread-%datetime-%level]: %msg (%fbase:%line)"); - el::Loggers::reconfigureLogger("default", defaultConf); -} - -void -DataGen::Init_with_default(const bool is_binary) { - Generate(dim, nb, nq, is_binary); -} - -void -DataGen::Generate(const int dim, const int nb, const int nq, const bool is_binary) { - this->dim = dim; - this->nb = nb; - this->nq = nq; - - if (!is_binary) { - GenAll(dim, nb, xb, ids, xids, nq, xq); - assert(xb.size() == (size_t)dim * nb); - assert(xq.size() == (size_t)dim * nq); - - base_dataset = milvus::knowhere::GenDatasetWithIds(nb, dim, xb.data(), ids.data()); - query_dataset = milvus::knowhere::GenDataset(nq, dim, xq.data()); - } else { - int64_t dim_x = dim / 8; - GenAll(dim_x, nb, xb_bin, ids, xids, nq, xq_bin); - assert(xb_bin.size() == (size_t)dim_x * nb); - assert(xq_bin.size() == (size_t)dim_x * nq); - - base_dataset = milvus::knowhere::GenDatasetWithIds(nb, dim, xb_bin.data(), ids.data()); - query_dataset = milvus::knowhere::GenDataset(nq, dim, xq_bin.data()); - } - - id_dataset = milvus::knowhere::GenDatasetWithIds(nq, dim, nullptr, ids.data()); - xid_dataset = milvus::knowhere::GenDatasetWithIds(nq, dim, nullptr, xids.data()); -} - -void -GenAll(const int64_t dim, const int64_t nb, std::vector& xb, std::vector& ids, - std::vector& xids, const int64_t nq, std::vector& xq) { - xb.resize(nb * dim); - xq.resize(nq * dim); - ids.resize(nb); - xids.resize(1); - GenBase(dim, nb, xb.data(), ids.data(), nq, xq.data(), xids.data(), false); -} - -void -GenAll(const int64_t dim, const int64_t nb, std::vector& xb, std::vector& ids, - std::vector& xids, const int64_t nq, std::vector& xq) { - xb.resize(nb * dim); - xq.resize(nq * dim); - ids.resize(nb); - xids.resize(1); - GenBase(dim, nb, xb.data(), ids.data(), nq, xq.data(), xids.data(), true); -} - -void -GenBase(const int64_t dim, const int64_t nb, const void* xb, int64_t* ids, const int64_t nq, const void* xq, - int64_t* xids, bool is_binary) { - if (!is_binary) { - float* xb_f = (float*)xb; - float* xq_f = (float*)xq; - for (auto i = 0; i < nb; ++i) { - for (auto j = 0; j < dim; ++j) { - xb_f[i * dim + j] = drand48(); - } - xb_f[dim * i] += i / 1000.; - ids[i] = i; - } - for (int64_t i = 0; i < nq * dim; ++i) { - xq_f[i] = xb_f[i]; - } - } else { - uint8_t* xb_u = (uint8_t*)xb; - uint8_t* xq_u = (uint8_t*)xq; - for (auto i = 0; i < nb; ++i) { - for (auto j = 0; j < dim; ++j) { - xb_u[i * dim + j] = (uint8_t)lrand48(); - } - xb_u[dim * i] += i / 1000.; - ids[i] = i; - } - for (int64_t i = 0; i < nq * dim; ++i) { - xq_u[i] = xb_u[i]; - } - } - xids[0] = 3; // pseudo random -} - -FileIOReader::FileIOReader(const std::string& fname) { - name = fname; - fs = std::fstream(name, std::ios::in | std::ios::binary); -} - -FileIOReader::~FileIOReader() { - fs.close(); -} - -size_t -FileIOReader::operator()(void* ptr, size_t size) { - fs.read(reinterpret_cast(ptr), size); - return size; -} - -FileIOWriter::FileIOWriter(const std::string& fname) { - name = fname; - fs = std::fstream(name, std::ios::out | std::ios::binary); -} - -FileIOWriter::~FileIOWriter() { - fs.close(); -} - -size_t -FileIOWriter::operator()(void* ptr, size_t size) { - fs.write(reinterpret_cast(ptr), size); - return size; -} - -void -AssertAnns(const milvus::knowhere::DatasetPtr& result, const int nq, const int k, const CheckMode check_mode) { - auto ids = result->Get(milvus::knowhere::meta::IDS); - for (auto i = 0; i < nq; i++) { - switch (check_mode) { - case CheckMode::CHECK_EQUAL: - ASSERT_EQ(i, *((int64_t*)(ids) + i * k)); - break; - case CheckMode::CHECK_NOT_EQUAL: - ASSERT_NE(i, *((int64_t*)(ids) + i * k)); - break; - default: - ASSERT_TRUE(false); - break; - } - } -} - -#if 0 -void -AssertVec(const milvus::knowhere::DatasetPtr& result, const milvus::knowhere::DatasetPtr& base_dataset, - const milvus::knowhere::DatasetPtr& id_dataset, const int n, const int dim, const CheckMode check_mode) { - float* base = (float*)base_dataset->Get(milvus::knowhere::meta::TENSOR); - auto ids = id_dataset->Get(milvus::knowhere::meta::IDS); - auto x = result->Get(milvus::knowhere::meta::TENSOR); - for (auto i = 0; i < n; i++) { - auto id = ids[i]; - for (auto j = 0; j < dim; j++) { - switch (check_mode) { - case CheckMode::CHECK_EQUAL: { - ASSERT_EQ(*(base + id * dim + j), *(x + i * dim + j)); - break; - } - case CheckMode::CHECK_NOT_EQUAL: { - ASSERT_NE(*(base + id * dim + j), *(x + i * dim + j)); - break; - } - case CheckMode::CHECK_APPROXIMATE_EQUAL: { - float a = *(base + id * dim + j); - float b = *(x + i * dim + j); - ASSERT_TRUE((std::fabs(a - b) / std::fabs(a)) < 0.1); - break; - } - default: - ASSERT_TRUE(false); - break; - } - } - } -} - -void -AssertBinVec(const milvus::knowhere::DatasetPtr& result, const milvus::knowhere::DatasetPtr& base_dataset, - const milvus::knowhere::DatasetPtr& id_dataset, const int n, const int dim, const CheckMode check_mode) { - auto base = (uint8_t*)base_dataset->Get(milvus::knowhere::meta::TENSOR); - auto ids = id_dataset->Get(milvus::knowhere::meta::IDS); - auto x = result->Get(milvus::knowhere::meta::TENSOR); - for (auto i = 0; i < 1; i++) { - auto id = ids[i]; - for (auto j = 0; j < dim; j++) { - ASSERT_EQ(*(base + id * dim + j), *(x + i * dim + j)); - } - } -} -#endif - -void -PrintResult(const milvus::knowhere::DatasetPtr& result, const int& nq, const int& k) { - auto ids = result->Get(milvus::knowhere::meta::IDS); - auto dist = result->Get(milvus::knowhere::meta::DISTANCE); - - std::stringstream ss_id; - std::stringstream ss_dist; - for (auto i = 0; i < nq; i++) { - for (auto j = 0; j < k; ++j) { - // ss_id << *(ids->data()->GetValues(1, i * k + j)) << " "; - // ss_dist << *(dists->data()->GetValues(1, i * k + j)) << " "; - ss_id << *((int64_t*)(ids) + i * k + j) << " "; - ss_dist << *((float*)(dist) + i * k + j) << " "; - } - ss_id << std::endl; - ss_dist << std::endl; - } - std::cout << "id\n" << ss_id.str() << std::endl; - std::cout << "dist\n" << ss_dist.str() << std::endl; -} - -// not used -#if 0 -void -Load_nns_graph(std::vector>& final_graph, const char* filename) { - std::vector> knng; - - std::ifstream in(filename, std::ios::binary); - unsigned k; - in.read((char*)&k, sizeof(unsigned)); - in.seekg(0, std::ios::end); - std::ios::pos_type ss = in.tellg(); - size_t fsize = (size_t)ss; - size_t num = (size_t)(fsize / (k + 1) / 4); - in.seekg(0, std::ios::beg); - - knng.resize(num); - knng.reserve(num); - int64_t kk = (k + 3) / 4 * 4; - for (size_t i = 0; i < num; i++) { - in.seekg(4, std::ios::cur); - knng[i].resize(k); - knng[i].reserve(kk); - in.read((char*)knng[i].data(), k * sizeof(unsigned)); - } - in.close(); - - final_graph.resize(knng.size()); - for (int i = 0; i < knng.size(); ++i) { - final_graph[i].resize(knng[i].size()); - for (int j = 0; j < knng[i].size(); ++j) { - final_graph[i][j] = knng[i][j]; - } - } -} - -float* -fvecs_read(const char* fname, size_t* d_out, size_t* n_out) { - FILE* f = fopen(fname, "r"); - if (!f) { - fprintf(stderr, "could not open %s\n", fname); - perror(""); - abort(); - } - int d; - fread(&d, 1, sizeof(int), f); - assert((d > 0 && d < 1000000) || !"unreasonable dimension"); - fseek(f, 0, SEEK_SET); - struct stat st; - fstat(fileno(f), &st); - size_t sz = st.st_size; - assert(sz % ((d + 1) * 4) == 0 || !"weird file size"); - size_t n = sz / ((d + 1) * 4); - - *d_out = d; - *n_out = n; - float* x = new float[n * (d + 1)]; - size_t nr = fread(x, sizeof(float), n * (d + 1), f); - assert(nr == n * (d + 1) || !"could not read whole file"); - - // shift array to remove row headers - for (size_t i = 0; i < n; i++) memmove(x + i * d, x + 1 + i * (d + 1), d * sizeof(*x)); - - fclose(f); - return x; -} - -int* // not very clean, but works as long as sizeof(int) == sizeof(float) -ivecs_read(const char* fname, size_t* d_out, size_t* n_out) { - return (int*)fvecs_read(fname, d_out, n_out); -} -#endif diff --git a/core/src/index/unittest/utils.h b/core/src/index/unittest/utils.h deleted file mode 100644 index 2d10762550..0000000000 --- a/core/src/index/unittest/utils.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include - -#include "knowhere/common/Dataset.h" -#include "knowhere/common/Log.h" - -class DataGen { - protected: - void - Init_with_default(const bool is_binary = false); - - void - Generate(const int dim, const int nb, const int nq, const bool is_binary = false); - - protected: - int nb = 10000; - int nq = 10; - int dim = 64; - int k = 10; - std::vector xb; - std::vector xq; - std::vector xb_bin; - std::vector xq_bin; - std::vector ids; - std::vector xids; - milvus::knowhere::DatasetPtr base_dataset = nullptr; - milvus::knowhere::DatasetPtr query_dataset = nullptr; - milvus::knowhere::DatasetPtr id_dataset = nullptr; - milvus::knowhere::DatasetPtr xid_dataset = nullptr; -}; - -extern void -GenAll(const int64_t dim, const int64_t nb, std::vector& xb, std::vector& ids, - std::vector& xids, const int64_t nq, std::vector& xq); - -extern void -GenAll(const int64_t dim, const int64_t nb, std::vector& xb, std::vector& ids, - std::vector& xids, const int64_t nq, std::vector& xq); - -extern void -GenBase(const int64_t dim, const int64_t nb, const void* xb, int64_t* ids, const int64_t nq, const void* xq, - int64_t* xids, const bool is_binary); - -extern void -InitLog(); - -enum class CheckMode { - CHECK_EQUAL = 0, - CHECK_NOT_EQUAL = 1, - CHECK_APPROXIMATE_EQUAL = 2, -}; - -void -AssertAnns(const milvus::knowhere::DatasetPtr& result, const int nq, const int k, - const CheckMode check_mode = CheckMode::CHECK_EQUAL); - -void -AssertVec(const milvus::knowhere::DatasetPtr& result, const milvus::knowhere::DatasetPtr& base_dataset, - const milvus::knowhere::DatasetPtr& id_dataset, const int n, const int dim, - const CheckMode check_mode = CheckMode::CHECK_EQUAL); - -void -AssertBinVec(const milvus::knowhere::DatasetPtr& result, const milvus::knowhere::DatasetPtr& base_dataset, - const milvus::knowhere::DatasetPtr& id_dataset, const int n, const int dim, - const CheckMode check_mode = CheckMode::CHECK_EQUAL); - -void -PrintResult(const milvus::knowhere::DatasetPtr& result, const int& nq, const int& k); - -struct FileIOWriter { - std::fstream fs; - std::string name; - - explicit FileIOWriter(const std::string& fname); - ~FileIOWriter(); - size_t - operator()(void* ptr, size_t size); -}; - -struct FileIOReader { - std::fstream fs; - std::string name; - - explicit FileIOReader(const std::string& fname); - ~FileIOReader(); - size_t - operator()(void* ptr, size_t size); -}; - -void -Load_nns_graph(std::vector>& final_graph_, const char* filename); - -float* -fvecs_read(const char* fname, size_t* d_out, size_t* n_out); - -int* -ivecs_read(const char* fname, size_t* d_out, size_t* n_out); diff --git a/core/src/main.cpp b/core/src/main.cpp deleted file mode 100644 index e35010f374..0000000000 --- a/core/src/main.cpp +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include -#include -#include -#include - -#include "easyloggingpp/easylogging++.h" -#include "server/Server.h" -#include "src/version.h" -#include "utils/SignalUtil.h" -#include "utils/Status.h" - -INITIALIZE_EASYLOGGINGPP - -void -print_help(const std::string& app_name) { - std::cout << std::endl << "Usage: " << app_name << " [OPTIONS]" << std::endl << std::endl; - std::cout << " Options:" << std::endl; - std::cout << " -h --help Print this help." << std::endl; - std::cout << " -c --conf_file filename Read configuration from the file." << std::endl; - std::cout << " -d --daemon Daemonize this application." << std::endl; - std::cout << " -p --pid_file filename PID file used by daemonized app." << std::endl; - std::cout << std::endl; -} - -void -print_banner() { - std::cout << std::endl; - std::cout << " __ _________ _ ____ ______ " << std::endl; - std::cout << " / |/ / _/ /| | / / / / / __/ " << std::endl; - std::cout << " / /|_/ // // /_| |/ / /_/ /\\ \\ " << std::endl; - std::cout << " /_/ /_/___/____/___/\\____/___/ " << std::endl; - std::cout << std::endl; - std::cout << "Welcome to use Milvus!" << std::endl; - std::cout << "Milvus " << BUILD_TYPE << " version: v" << MILVUS_VERSION << ", built at " << BUILD_TIME << ", with " -#ifdef WITH_MKL - << "MKL" -#else - << "OpenBLAS" -#endif - << " library." << std::endl; -#ifdef MILVUS_GPU_VERSION - std::cout << "You are using Milvus GPU edition" << std::endl; -#else - std::cout << "You are using Milvus CPU edition" << std::endl; -#endif - std::cout << "Last commit id: " << LAST_COMMIT_ID << std::endl; - std::cout << std::endl; -} - -int -main(int argc, char* argv[]) { - print_banner(); - - static struct option long_options[] = {{"conf_file", required_argument, nullptr, 'c'}, - {"help", no_argument, nullptr, 'h'}, - {"daemon", no_argument, nullptr, 'd'}, - {"pid_file", required_argument, nullptr, 'p'}, - {nullptr, 0, nullptr, 0}}; - - int option_index = 0; - int64_t start_daemonized = 0; - - std::string config_filename; - std::string pid_filename; - std::string app_name = argv[0]; - milvus::Status s; - - milvus::server::Server& server = milvus::server::Server::GetInstance(); - - if (argc < 2) { - print_help(app_name); - goto FAIL; - } - - int value; - while ((value = getopt_long(argc, argv, "c:p:dh", long_options, &option_index)) != -1) { - switch (value) { - case 'c': { - char* config_filename_ptr = strdup(optarg); - config_filename = config_filename_ptr; - free(config_filename_ptr); - std::cout << "Loading configuration from: " << config_filename << std::endl; - break; - } - case 'p': { - char* pid_filename_ptr = strdup(optarg); - pid_filename = pid_filename_ptr; - free(pid_filename_ptr); - std::cout << pid_filename << std::endl; - break; - } - case 'd': - start_daemonized = 1; - break; - case 'h': - print_help(app_name); - return EXIT_SUCCESS; - case '?': - print_help(app_name); - return EXIT_FAILURE; - default: - print_help(app_name); - break; - } - } - - /* Handle Signal */ - signal(SIGHUP, milvus::server::SignalUtil::HandleSignal); - signal(SIGINT, milvus::server::SignalUtil::HandleSignal); - signal(SIGUSR1, milvus::server::SignalUtil::HandleSignal); - signal(SIGSEGV, milvus::server::SignalUtil::HandleSignal); - signal(SIGUSR2, milvus::server::SignalUtil::HandleSignal); - signal(SIGTERM, milvus::server::SignalUtil::HandleSignal); - - server.Init(start_daemonized, pid_filename, config_filename); - - s = server.Start(); - if (s.ok()) { - std::cout << "Milvus server started successfully!" << std::endl; - } else { - std::cout << s.message() << std::endl; - goto FAIL; - } - - /* wait signal */ - pause(); - - return EXIT_SUCCESS; - -FAIL: - std::cout << "Milvus server exit..." << std::endl; - return EXIT_FAILURE; -} diff --git a/core/src/metrics/MetricBase.h b/core/src/metrics/MetricBase.h deleted file mode 100644 index c3fad68349..0000000000 --- a/core/src/metrics/MetricBase.h +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "SystemInfo.h" -#include "utils/Status.h" - -#include - -namespace milvus { -namespace server { -class MetricsBase { - public: - static MetricsBase& - GetInstance() { - static MetricsBase instance; - return instance; - } - - virtual Status - Init() { - return Status::OK(); - } - - virtual void - AddVectorsSuccessTotalIncrement(double value = 1) { - } - - virtual void - AddVectorsFailTotalIncrement(double value = 1) { - } - - virtual void - AddVectorsDurationHistogramOberve(double value) { - } - - virtual void - RawFileSizeHistogramObserve(double value) { - } - - virtual void - IndexFileSizeHistogramObserve(double value) { - } - - virtual void - BuildIndexDurationSecondsHistogramObserve(double value) { - } - - virtual void - CpuCacheUsageGaugeSet(double value) { - } - - virtual void - GpuCacheUsageGaugeSet() { - } - - virtual void - MetaAccessTotalIncrement(double value = 1) { - } - - virtual void - MetaAccessDurationSecondsHistogramObserve(double value) { - } - - virtual void - FaissDiskLoadDurationSecondsHistogramObserve(double value) { - } - - virtual void - FaissDiskLoadSizeBytesHistogramObserve(double value) { - } - - virtual void - CacheAccessTotalIncrement(double value = 1) { - } - - virtual void - MemTableMergeDurationSecondsHistogramObserve(double value) { - } - - virtual void - SearchIndexDataDurationSecondsHistogramObserve(double value) { - } - - virtual void - SearchRawDataDurationSecondsHistogramObserve(double value) { - } - - virtual void - IndexFileSizeTotalIncrement(double value = 1) { - } - - virtual void - RawFileSizeTotalIncrement(double value = 1) { - } - - virtual void - IndexFileSizeGaugeSet(double value) { - } - - virtual void - RawFileSizeGaugeSet(double value) { - } - - virtual void - FaissDiskLoadIOSpeedGaugeSet(double value) { - } - - virtual void - QueryResponseSummaryObserve(double value) { - } - - virtual void - DiskStoreIOSpeedGaugeSet(double value) { - } - - virtual void - DataFileSizeGaugeSet(double value) { - } - - virtual void - AddVectorsSuccessGaugeSet(double value) { - } - - virtual void - AddVectorsFailGaugeSet(double value) { - } - - virtual void - QueryVectorResponseSummaryObserve(double value, int count = 1) { - } - - virtual void - QueryVectorResponsePerSecondGaugeSet(double value) { - } - - virtual void - CPUUsagePercentSet() { - } - - virtual void - RAMUsagePercentSet() { - } - - virtual void - QueryResponsePerSecondGaugeSet(double value) { - } - - virtual void - GPUPercentGaugeSet() { - } - - virtual void - GPUMemoryUsageGaugeSet() { - } - - virtual void - AddVectorsPerSecondGaugeSet(int num_vector, int dim, double time) { - } - - virtual void - QueryIndexTypePerSecondSet(std::string type, double value) { - } - - virtual void - ConnectionGaugeIncrement() { - } - - virtual void - ConnectionGaugeDecrement() { - } - - virtual void - KeepingAliveCounterIncrement(double value = 1) { - } - - virtual void - OctetsSet() { - } - - virtual void - CPUCoreUsagePercentSet() { - } - - virtual void - GPUTemperature() { - } - - virtual void - CPUTemperature() { - } - - virtual void - PushToGateway() { - } -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/metrics/Metrics.cpp b/core/src/metrics/Metrics.cpp deleted file mode 100644 index df40b76b66..0000000000 --- a/core/src/metrics/Metrics.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "metrics/Metrics.h" -#include "config/Config.h" -#ifdef MILVUS_WITH_PROMETHEUS -#include "metrics/prometheus/PrometheusMetrics.h" -#endif - -#include - -namespace milvus { -namespace server { - -MetricsBase& -Metrics::GetInstance() { - static MetricsBase& instance = CreateMetricsCollector(); - return instance; -} - -MetricsBase& -Metrics::CreateMetricsCollector() { -#ifdef MILVUS_WITH_PROMETHEUS - return PrometheusMetrics::GetInstance(); -#else - return MetricsBase::GetInstance(); -#endif -} - -} // namespace server -} // namespace milvus diff --git a/core/src/metrics/Metrics.h b/core/src/metrics/Metrics.h deleted file mode 100644 index 09df67596b..0000000000 --- a/core/src/metrics/Metrics.h +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "MetricBase.h" -#include "db/meta/MetaTypes.h" - -namespace milvus { -namespace server { - -#define METRICS_NOW_TIME std::chrono::system_clock::now() -#define METRICS_MICROSECONDS(a, b) (std::chrono::duration_cast(b - a)).count(); - -enum class MetricCollectorType { INVALID, PROMETHEUS, ZABBIX }; - -class Metrics { - public: - static MetricsBase& - GetInstance(); - - private: - static MetricsBase& - CreateMetricsCollector(); -}; -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -class CollectMetricsBase { - protected: - CollectMetricsBase() { - start_time_ = METRICS_NOW_TIME; - } - - virtual ~CollectMetricsBase() = default; - - double - TimeFromBegine() { - auto end_time = METRICS_NOW_TIME; - return METRICS_MICROSECONDS(start_time_, end_time); - } - - protected: - using TIME_POINT = std::chrono::system_clock::time_point; - TIME_POINT start_time_; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -class CollectInsertMetrics : CollectMetricsBase { - public: - CollectInsertMetrics(size_t n, Status& status) : n_(n), status_(status) { - } - - ~CollectInsertMetrics() { - if (n_ > 0) { - auto total_time = TimeFromBegine(); - double avg_time = total_time / n_; - for (size_t i = 0; i < n_; ++i) { - Metrics::GetInstance().AddVectorsDurationHistogramOberve(avg_time); - } - - // server::Metrics::GetInstance().add_vector_duration_seconds_quantiles().Observe((average_time)); - if (status_.ok()) { - server::Metrics::GetInstance().AddVectorsSuccessTotalIncrement(n_); - server::Metrics::GetInstance().AddVectorsSuccessGaugeSet(n_); - } else { - server::Metrics::GetInstance().AddVectorsFailTotalIncrement(n_); - server::Metrics::GetInstance().AddVectorsFailGaugeSet(n_); - } - } - } - - private: - size_t n_; - Status& status_; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -class CollectQueryMetrics : CollectMetricsBase { - public: - explicit CollectQueryMetrics(size_t nq) : nq_(nq) { - } - - ~CollectQueryMetrics() { - if (nq_ > 0) { - auto total_time = TimeFromBegine(); - for (size_t i = 0; i < nq_; ++i) { - server::Metrics::GetInstance().QueryResponseSummaryObserve(total_time); - } - auto average_time = total_time / nq_; - server::Metrics::GetInstance().QueryVectorResponseSummaryObserve(average_time, nq_); - server::Metrics::GetInstance().QueryVectorResponsePerSecondGaugeSet(double(nq_) / total_time); - } - } - - private: - size_t nq_; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -class CollectMergeFilesMetrics : CollectMetricsBase { - public: - CollectMergeFilesMetrics() { - } - - ~CollectMergeFilesMetrics() { - auto total_time = TimeFromBegine(); - server::Metrics::GetInstance().MemTableMergeDurationSecondsHistogramObserve(total_time); - } -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -class CollectBuildIndexMetrics : CollectMetricsBase { - public: - CollectBuildIndexMetrics() { - } - - ~CollectBuildIndexMetrics() { - auto total_time = TimeFromBegine(); - server::Metrics::GetInstance().BuildIndexDurationSecondsHistogramObserve(total_time); - } -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -class CollectExecutionEngineMetrics : CollectMetricsBase { - public: - explicit CollectExecutionEngineMetrics(double physical_size) : physical_size_(physical_size) { - } - - ~CollectExecutionEngineMetrics() { - auto total_time = TimeFromBegine(); - - server::Metrics::GetInstance().FaissDiskLoadDurationSecondsHistogramObserve(total_time); - server::Metrics::GetInstance().FaissDiskLoadSizeBytesHistogramObserve(physical_size_); - server::Metrics::GetInstance().FaissDiskLoadIOSpeedGaugeSet(physical_size_ / double(total_time)); - } - - private: - double physical_size_; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -class CollectSerializeMetrics : CollectMetricsBase { - public: - explicit CollectSerializeMetrics(size_t size) : size_(size) { - } - - ~CollectSerializeMetrics() { - auto total_time = TimeFromBegine(); - server::Metrics::GetInstance().DiskStoreIOSpeedGaugeSet((double)size_ / total_time); - } - - private: - size_t size_; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -class CollectAddMetrics : CollectMetricsBase { - public: - CollectAddMetrics(size_t n, uint16_t dimension) : n_(n), dimension_(dimension) { - } - - ~CollectAddMetrics() { - auto total_time = TimeFromBegine(); - server::Metrics::GetInstance().AddVectorsPerSecondGaugeSet(static_cast(n_), static_cast(dimension_), - total_time); - } - - private: - size_t n_; - uint16_t dimension_; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -class CollectDurationMetrics : CollectMetricsBase { - public: - explicit CollectDurationMetrics(int index_type) : index_type_(index_type) { - } - - ~CollectDurationMetrics() { - auto total_time = TimeFromBegine(); - switch (index_type_) { - case engine::meta::SegmentSchema::RAW: { - server::Metrics::GetInstance().SearchRawDataDurationSecondsHistogramObserve(total_time); - break; - } - case engine::meta::SegmentSchema::TO_INDEX: { - server::Metrics::GetInstance().SearchRawDataDurationSecondsHistogramObserve(total_time); - break; - } - default: { - server::Metrics::GetInstance().SearchIndexDataDurationSecondsHistogramObserve(total_time); - break; - } - } - } - - private: - int index_type_; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -class CollectSearchTaskMetrics : CollectMetricsBase { - public: - explicit CollectSearchTaskMetrics(int index_type) : index_type_(index_type) { - } - - ~CollectSearchTaskMetrics() { - auto total_time = TimeFromBegine(); - switch (index_type_) { - case engine::meta::SegmentSchema::RAW: { - server::Metrics::GetInstance().SearchRawDataDurationSecondsHistogramObserve(total_time); - break; - } - case engine::meta::SegmentSchema::TO_INDEX: { - server::Metrics::GetInstance().SearchRawDataDurationSecondsHistogramObserve(total_time); - break; - } - default: { - server::Metrics::GetInstance().SearchIndexDataDurationSecondsHistogramObserve(total_time); - break; - } - } - } - - private: - int index_type_; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -class MetricCollector : CollectMetricsBase { - public: - MetricCollector() { - server::Metrics::GetInstance().MetaAccessTotalIncrement(); - } - - ~MetricCollector() { - auto total_time = TimeFromBegine(); - server::Metrics::GetInstance().MetaAccessDurationSecondsHistogramObserve(total_time); - } -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/metrics/SystemInfo.cpp b/core/src/metrics/SystemInfo.cpp deleted file mode 100644 index a96bdec6c3..0000000000 --- a/core/src/metrics/SystemInfo.cpp +++ /dev/null @@ -1,410 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "metrics/SystemInfo.h" -#include "thirdparty/nlohmann/json.hpp" -#include "utils/Exception.h" -#include "utils/Log.h" - -#include -#include -#include -#include -#include -#include - -#ifdef MILVUS_GPU_VERSION - -#include - -#endif - -namespace milvus { -namespace server { - -void -SystemInfo::Init() { - if (initialized_) { - return; - } - - initialized_ = true; - - // initialize CPU information - try { - struct tms time_sample; - char line[128]; - last_cpu_ = times(&time_sample); - last_sys_cpu_ = time_sample.tms_stime; - last_user_cpu_ = time_sample.tms_utime; - num_processors_ = 0; - FILE* file = fopen("/proc/cpuinfo", "r"); - if (file) { - while (fgets(line, 128, file) != nullptr) { - if (strncmp(line, "processor", 9) == 0) { - num_processors_++; - } - if (strncmp(line, "physical", 8) == 0) { - num_physical_processors_ = ParseLine(line); - } - } - fclose(file); - } else { - LOG_SERVER_ERROR_ << "Failed to read /proc/cpuinfo"; - } - total_ram_ = GetPhysicalMemory(); - } catch (std::exception& ex) { - std::string msg = "Failed to read /proc/cpuinfo, reason: " + std::string(ex.what()); - LOG_SERVER_ERROR_ << msg; - } - -#ifdef MILVUS_GPU_VERSION - // initialize GPU information - nvmlReturn_t nvmlresult; - nvmlresult = nvmlInit(); - fiu_do_on("SystemInfo.Init.nvmInit_fail", nvmlresult = NVML_ERROR_NOT_FOUND); - if (NVML_SUCCESS != nvmlresult) { - LOG_SERVER_ERROR_ << "System information initilization failed"; - return; - } - nvmlresult = nvmlDeviceGetCount(&num_device_); - fiu_do_on("SystemInfo.Init.nvm_getDevice_fail", nvmlresult = NVML_ERROR_NOT_FOUND); - if (NVML_SUCCESS != nvmlresult) { - LOG_SERVER_ERROR_ << "Unable to get devidce number"; - return; - } -#endif - - // initialize network traffic information - try { - std::pair in_and_out_octets = Octets(); - in_octets_ = in_and_out_octets.first; - out_octets_ = in_and_out_octets.second; - net_time_ = std::chrono::system_clock::now(); - } catch (std::exception& ex) { - std::string msg = "Failed to initialize network traffic information, reason: " + std::string(ex.what()); - LOG_SERVER_ERROR_ << msg; - } -} - -int64_t -SystemInfo::ParseLine(char* line) { - // This assumes that a digit will be found and the line ends in " Kb". - int i = strlen(line); - const char* p = line; - while (*p < '0' || *p > '9') { - p++; - } - line[i - 3] = '\0'; - i = atoi(p); - return i; -} - -int64_t -SystemInfo::GetPhysicalMemory() { - struct sysinfo memInfo; - sysinfo(&memInfo); - int64_t totalPhysMem = memInfo.totalram; - // Multiply in next statement to avoid int overflow on right hand side... - totalPhysMem *= memInfo.mem_unit; - - return totalPhysMem; -} - -int64_t -SystemInfo::GetProcessUsedMemory() { - try { - // Note: this value is in KB! - FILE* file = fopen("/proc/self/status", "r"); - int64_t result = 0; - constexpr int64_t KB = 1024; - if (file) { - constexpr int64_t line_length = 128; - char line[line_length]; - - while (fgets(line, line_length, file) != nullptr) { - if (strncmp(line, "VmRSS:", 6) == 0) { - result = ParseLine(line); - break; - } - } - fclose(file); - } else { - LOG_SERVER_ERROR_ << "Failed to read /proc/self/status"; - } - - // return value in Byte - return (result * KB); - } catch (std::exception& ex) { - std::string msg = "Failed to read /proc/self/status, reason: " + std::string(ex.what()); - LOG_SERVER_ERROR_ << msg; - return 0; - } -} - -double -SystemInfo::MemoryPercent() { - fiu_do_on("SystemInfo.MemoryPercent.mock", initialized_ = false); - if (!initialized_) { - Init(); - } - - double mem_used = static_cast(GetProcessUsedMemory() * 100); - return mem_used / static_cast(total_ram_); -} - -std::vector -SystemInfo::CPUCorePercent() { - std::vector prev_work_time_array; - std::vector prev_total_time_array = getTotalCpuTime(prev_work_time_array); - usleep(100000); - std::vector cur_work_time_array; - std::vector cur_total_time_array = getTotalCpuTime(cur_work_time_array); - - std::vector cpu_core_percent; - for (size_t i = 0; i < cur_total_time_array.size(); i++) { - double total_cpu_time = cur_total_time_array[i] - prev_total_time_array[i]; - double cpu_work_time = cur_work_time_array[i] - prev_work_time_array[i]; - cpu_core_percent.push_back((cpu_work_time / total_cpu_time) * 100); - } - return cpu_core_percent; -} - -std::vector -SystemInfo::getTotalCpuTime(std::vector& work_time_array) { - std::vector total_time_array; - try { - FILE* file = fopen("/proc/stat", "r"); - fiu_do_on("SystemInfo.getTotalCpuTime.open_proc", file = NULL); - if (file == NULL) { - LOG_SERVER_ERROR_ << "Failed to read /proc/stat"; - return total_time_array; - } - - int64_t user = 0, nice = 0, system = 0, idle = 0; - int64_t iowait = 0, irq = 0, softirq = 0, steal = 0, guest = 0, guestnice = 0; - - for (int i = 0; i < num_processors_; i++) { - char buffer[1024]; - char* ret = fgets(buffer, sizeof(buffer) - 1, file); - fiu_do_on("SystemInfo.getTotalCpuTime.read_proc", ret = NULL); - if (ret == NULL) { - LOG_SERVER_ERROR_ << "Could not read stat file"; - fclose(file); - return total_time_array; - } - - sscanf(buffer, "cpu %16lu %16lu %16lu %16lu %16lu %16lu %16lu %16lu %16lu %16lu", &user, &nice, &system, - &idle, &iowait, &irq, &softirq, &steal, &guest, &guestnice); - - work_time_array.push_back(user + nice + system); - total_time_array.push_back(user + nice + system + idle + iowait + irq + softirq + steal); - } - - fclose(file); - } catch (std::exception& ex) { - std::string msg = "Failed to read /proc/stat, reason: " + std::string(ex.what()); - LOG_SERVER_ERROR_ << msg; - } - - return total_time_array; -} - -double -SystemInfo::CPUPercent() { - fiu_do_on("SystemInfo.CPUPercent.mock", initialized_ = false); - if (!initialized_) { - Init(); - } - struct tms time_sample; - clock_t now; - double percent; - - now = times(&time_sample); - if (now <= last_cpu_ || time_sample.tms_stime < last_sys_cpu_ || time_sample.tms_utime < last_user_cpu_) { - // Overflow detection. Just skip this value. - percent = -1.0; - } else { - percent = (time_sample.tms_stime - last_sys_cpu_) + (time_sample.tms_utime - last_user_cpu_); - percent /= (now - last_cpu_); - percent *= 100; - } - last_cpu_ = now; - last_sys_cpu_ = time_sample.tms_stime; - last_user_cpu_ = time_sample.tms_utime; - - return percent; -} - -std::vector -SystemInfo::GPUMemoryTotal() { - // get GPU usage percent - fiu_do_on("SystemInfo.GPUMemoryTotal.mock", initialized_ = false); - if (!initialized_) - Init(); - std::vector result; - -#ifdef MILVUS_GPU_VERSION - nvmlMemory_t nvmlMemory; - for (uint32_t i = 0; i < num_device_; ++i) { - nvmlDevice_t device; - nvmlDeviceGetHandleByIndex(i, &device); - nvmlDeviceGetMemoryInfo(device, &nvmlMemory); - result.push_back(nvmlMemory.total); - } -#endif - - return result; -} - -std::vector -SystemInfo::GPUTemperature() { - fiu_do_on("SystemInfo.GPUTemperature.mock", initialized_ = false); - if (!initialized_) - Init(); - std::vector result; - -#ifdef MILVUS_GPU_VERSION - for (uint32_t i = 0; i < num_device_; i++) { - nvmlDevice_t device; - nvmlDeviceGetHandleByIndex(i, &device); - unsigned int temp; - nvmlDeviceGetTemperature(device, NVML_TEMPERATURE_GPU, &temp); - result.push_back(temp); - } -#endif - - return result; -} - -std::vector -SystemInfo::CPUTemperature() { - std::vector result; - std::string path = "/sys/class/hwmon/"; - try { - DIR* dir = opendir(path.c_str()); - fiu_do_on("SystemInfo.CPUTemperature.opendir", dir = NULL); - if (!dir) { - LOG_SERVER_ERROR_ << "Could not open hwmon directory"; - return result; - } - - struct dirent* ptr = NULL; - while ((ptr = readdir(dir)) != NULL) { - std::string filename(path); - filename.append(ptr->d_name); - - char buf[100]; - if (readlink(filename.c_str(), buf, 100) != -1) { - std::string m(buf); - if (m.find("coretemp") != std::string::npos) { - std::string object = filename; - object += "/temp1_input"; - FILE* file = fopen(object.c_str(), "r"); - fiu_do_on("SystemInfo.CPUTemperature.openfile", file = NULL); - if (file == nullptr) { - LOG_SERVER_ERROR_ << "Could not open temperature file"; - return result; - } - float temp; - if (fscanf(file, "%f", &temp) != -1) { - result.push_back(temp / 1000); - } - fclose(file); - } - } - } - closedir(dir); - } catch (std::exception& ex) { - std::string msg = "Failed to get cpu temperature, reason: " + std::string(ex.what()); - LOG_SERVER_ERROR_ << msg; - } - - return result; -} - -std::vector -SystemInfo::GPUMemoryUsed() { - // get GPU memory used - fiu_do_on("SystemInfo.GPUMemoryUsed.mock", initialized_ = false); - if (!initialized_) - Init(); - - std::vector result; - -#ifdef MILVUS_GPU_VERSION - nvmlMemory_t nvmlMemory; - for (uint32_t i = 0; i < num_device_; ++i) { - nvmlDevice_t device; - nvmlDeviceGetHandleByIndex(i, &device); - nvmlDeviceGetMemoryInfo(device, &nvmlMemory); - result.push_back(nvmlMemory.used); - } -#endif - - return result; -} - -std::pair -SystemInfo::Octets() { - const std::string filename = "/proc/net/netstat"; - std::ifstream file(filename); - std::string lastline = ""; - std::string line = ""; - while (true) { - getline(file, line); - if (file.fail()) { - break; - } - lastline = line; - } - std::vector space_position; - size_t space_pos = lastline.find(" "); - while (space_pos != std::string::npos) { - space_position.push_back(space_pos); - space_pos = lastline.find(" ", space_pos + 1); - } - // InOctets is between 6th and 7th " " and OutOctets is between 7th and 8th " " - size_t inoctets_begin = space_position[6] + 1; - size_t inoctets_length = space_position[7] - inoctets_begin; - size_t outoctets_begin = space_position[7] + 1; - size_t outoctets_length = space_position[8] - outoctets_begin; - std::string inoctets = lastline.substr(inoctets_begin, inoctets_length); - std::string outoctets = lastline.substr(outoctets_begin, outoctets_length); - - int64_t inoctets_bytes = std::stoull(inoctets); - int64_t outoctets_bytes = std::stoull(outoctets); - std::pair res(inoctets_bytes, outoctets_bytes); - return res; -} - -void -SystemInfo::GetSysInfoJsonStr(std::string& result) { - std::map sys_info_map; - - sys_info_map["memory_total"] = std::to_string(GetPhysicalMemory()); - sys_info_map["memory_used"] = std::to_string(GetProcessUsedMemory()); - - std::vector gpu_mem_total = GPUMemoryTotal(); - std::vector gpu_mem_used = GPUMemoryUsed(); - for (size_t i = 0; i < gpu_mem_total.size(); i++) { - std::string key_total = "gpu" + std::to_string(i) + "_memory_total"; - std::string key_used = "gpu" + std::to_string(i) + "_memory_used"; - sys_info_map[key_total] = std::to_string(gpu_mem_total[i]); - sys_info_map[key_used] = std::to_string(gpu_mem_used[i]); - } - - nlohmann::json sys_info_json(sys_info_map); - result = sys_info_json.dump(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/metrics/SystemInfo.h b/core/src/metrics/SystemInfo.h deleted file mode 100644 index 6413ab9149..0000000000 --- a/core/src/metrics/SystemInfo.h +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include - -namespace milvus { -namespace server { - -class SystemInfo { - private: - int64_t total_ram_ = 0; - clock_t last_cpu_ = clock_t(); - clock_t last_sys_cpu_ = clock_t(); - clock_t last_user_cpu_ = clock_t(); - std::chrono::system_clock::time_point net_time_ = std::chrono::system_clock::now(); - int32_t num_processors_ = 0; - int32_t num_physical_processors_ = 0; - // number of GPU - uint32_t num_device_ = 0; - int64_t in_octets_ = 0; - int64_t out_octets_ = 0; - bool initialized_ = false; - - public: - static SystemInfo& - GetInstance() { - static SystemInfo instance; - return instance; - } - - void - Init(); - - int32_t - num_processor() const { - return num_processors_; - } - - int32_t - num_physical_processors() const { - return num_physical_processors_; - } - - int32_t - num_device() const { - return num_device_; - } - - int64_t - get_inoctets() { - return in_octets_; - } - - int64_t - get_octets() { - return out_octets_; - } - - std::chrono::system_clock::time_point - get_nettime() { - return net_time_; - } - - void - set_inoctets(int64_t value) { - in_octets_ = value; - } - - void - set_outoctets(int64_t value) { - out_octets_ = value; - } - - void - set_nettime() { - net_time_ = std::chrono::system_clock::now(); - } - - int64_t - ParseLine(char* line); - int64_t - GetPhysicalMemory(); - int64_t - GetProcessUsedMemory(); - double - MemoryPercent(); - double - CPUPercent(); - std::pair - Octets(); - std::vector - GPUMemoryTotal(); - std::vector - GPUMemoryUsed(); - - std::vector - CPUCorePercent(); - std::vector - getTotalCpuTime(std::vector& workTime); - std::vector - GPUTemperature(); - std::vector - CPUTemperature(); - - void - GetSysInfoJsonStr(std::string& result); -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/metrics/prometheus/PrometheusMetrics.cpp b/core/src/metrics/prometheus/PrometheusMetrics.cpp deleted file mode 100644 index 22452e864d..0000000000 --- a/core/src/metrics/prometheus/PrometheusMetrics.cpp +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "metrics/prometheus/PrometheusMetrics.h" -#include "cache/GpuCacheMgr.h" -#include "config/Config.h" -#include "metrics/SystemInfo.h" -#include "utils/Log.h" - -#include -#include -#include - -namespace milvus { -namespace server { - -Status -PrometheusMetrics::Init() { - try { - Config& config = Config::GetInstance(); - STATUS_CHECK(config.GetMetricConfigEnableMonitor(startup_)); - if (!startup_) { - return Status::OK(); - } - - // Following should be read from config file. - std::string server_port, push_port, push_address; - STATUS_CHECK(config.GetNetworkConfigBindPort(server_port)); - STATUS_CHECK(config.GetMetricConfigPort(push_port)); - STATUS_CHECK(config.GetMetricConfigAddress(push_address)); - - const std::string uri = std::string("/metrics"); - // const std::size_t num_threads = 2; - - std::string hostportstr; - char hostname[1024]; - if (gethostname(hostname, sizeof(hostname)) == 0) { - hostportstr = std::string(hostname) + ":" + server_port; - } else { - hostportstr = "pushgateway"; - } - - auto labels = prometheus::Gateway::GetInstanceLabel(hostportstr); - - // Init pushgateway - gateway_ = std::make_shared(push_address, push_port, "milvus_metrics", labels); - - // Init Exposer - // exposer_ptr_ = std::make_shared(bind_address, uri, num_threads); - - // Pushgateway Registry - gateway_->RegisterCollectable(registry_); - } catch (std::exception& ex) { - LOG_SERVER_ERROR_ << "Failed to connect prometheus server: " << std::string(ex.what()); - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -void -PrometheusMetrics::CPUUsagePercentSet() { - if (!startup_) { - return; - } - - double usage_percent = server::SystemInfo::GetInstance().CPUPercent(); - CPU_usage_percent_.Set(usage_percent); -} - -void -PrometheusMetrics::RAMUsagePercentSet() { - if (!startup_) { - return; - } - - double usage_percent = server::SystemInfo::GetInstance().MemoryPercent(); - RAM_usage_percent_.Set(usage_percent); -} - -void -PrometheusMetrics::GPUPercentGaugeSet() { - if (!startup_) { - return; - } - - int numDevice = server::SystemInfo::GetInstance().num_device(); - std::vector used_total = server::SystemInfo::GetInstance().GPUMemoryTotal(); - std::vector used_memory = server::SystemInfo::GetInstance().GPUMemoryUsed(); - - for (int i = 0; i < numDevice; ++i) { - prometheus::Gauge& GPU_percent = GPU_percent_.Add({{"DeviceNum", std::to_string(i)}}); - double percent = (double)used_memory[i] / (double)used_total[i]; - GPU_percent.Set(percent * 100); - } -} - -void -PrometheusMetrics::GPUMemoryUsageGaugeSet() { - if (!startup_) { - return; - } - - std::vector values = server::SystemInfo::GetInstance().GPUMemoryUsed(); - constexpr int64_t MB = 1024 * 1024; - int numDevice = server::SystemInfo::GetInstance().num_device(); - - for (int i = 0; i < numDevice; ++i) { - prometheus::Gauge& GPU_memory = GPU_memory_usage_.Add({{"DeviceNum", std::to_string(i)}}); - GPU_memory.Set(values[i] / MB); - } -} - -void -PrometheusMetrics::AddVectorsPerSecondGaugeSet(int num_vector, int dim, double time) { - // MB/s - if (!startup_) { - return; - } - - int64_t MtoB = 1024 * 1024; - int64_t size = num_vector * dim * 4; - add_vectors_per_second_gauge_.Set(size / time / MtoB); -} - -void -PrometheusMetrics::QueryIndexTypePerSecondSet(std::string type, double value) { - if (!startup_) { - return; - } - - if (type == "IVF") { - query_index_IVF_type_per_second_gauge_.Set(value); - } else if (type == "IDMap") { - query_index_IDMAP_type_per_second_gauge_.Set(value); - } -} - -void -PrometheusMetrics::ConnectionGaugeIncrement() { - if (!startup_) { - return; - } - - connection_gauge_.Increment(); -} - -void -PrometheusMetrics::ConnectionGaugeDecrement() { - if (!startup_) { - return; - } - - connection_gauge_.Decrement(); -} - -void -PrometheusMetrics::OctetsSet() { - if (!startup_) { - return; - } - - try { - // get old stats and reset them - uint64_t old_inoctets = SystemInfo::GetInstance().get_inoctets(); - uint64_t old_outoctets = SystemInfo::GetInstance().get_octets(); - auto old_time = SystemInfo::GetInstance().get_nettime(); - - std::pair in_and_out_octets = SystemInfo::GetInstance().Octets(); - SystemInfo::GetInstance().set_inoctets(in_and_out_octets.first); - SystemInfo::GetInstance().set_outoctets(in_and_out_octets.second); - SystemInfo::GetInstance().set_nettime(); - - // - constexpr double micro_to_second = 1e-6; - auto now_time = std::chrono::system_clock::now(); - auto total_microsecond = METRICS_MICROSECONDS(old_time, now_time); - auto total_second = total_microsecond * micro_to_second; - if (total_second == 0) { - return; - } - - inoctets_gauge_.Set((in_and_out_octets.first - old_inoctets) / total_second); - outoctets_gauge_.Set((in_and_out_octets.second - old_outoctets) / total_second); - } catch (std::exception& ex) { - std::string msg = "Failed to set in/out octets, reason: " + std::string(ex.what()); - LOG_SERVER_ERROR_ << msg; - } -} - -void -PrometheusMetrics::CPUCoreUsagePercentSet() { - if (!startup_) { - return; - } - - std::vector cpu_core_percent = server::SystemInfo::GetInstance().CPUCorePercent(); - - for (size_t i = 0; i < cpu_core_percent.size(); ++i) { - prometheus::Gauge& core_percent = CPU_.Add({{"CPU", std::to_string(i)}}); - core_percent.Set(cpu_core_percent[i]); - } -} - -void -PrometheusMetrics::GPUTemperature() { - if (!startup_) { - return; - } - - std::vector GPU_temperatures = server::SystemInfo::GetInstance().GPUTemperature(); - - for (size_t i = 0; i < GPU_temperatures.size(); ++i) { - prometheus::Gauge& gpu_temp = GPU_temperature_.Add({{"GPU", std::to_string(i)}}); - gpu_temp.Set(GPU_temperatures[i]); - } -} - -void -PrometheusMetrics::CPUTemperature() { - if (!startup_) { - return; - } - - std::vector CPU_temperatures = server::SystemInfo::GetInstance().CPUTemperature(); - - float avg_cpu_temp = 0; - for (size_t i = 0; i < CPU_temperatures.size(); ++i) { - avg_cpu_temp += CPU_temperatures[i]; - } - avg_cpu_temp /= CPU_temperatures.size(); - - prometheus::Gauge& cpu_temp = CPU_temperature_.Add({{"CPU", std::to_string(0)}}); - cpu_temp.Set(avg_cpu_temp); - - // for (int i = 0; i < CPU_temperatures.size(); ++i) { - // prometheus::Gauge& cpu_temp = CPU_temperature_.Add({{"CPU", std::to_string(i)}}); - // cpu_temp.Set(CPU_temperatures[i]); - // } -} - -void -PrometheusMetrics::GpuCacheUsageGaugeSet() { - // std::vector gpu_ids = {0}; - // for(auto i = 0; i < gpu_ids.size(); ++i) { - // uint64_t cache_usage = cache::GpuCacheMgr::GetInstance(gpu_ids[i])->CacheUsage(); - // uint64_t cache_capacity = cache::GpuCacheMgr::GetInstance(gpu_ids[i])->CacheCapacity(); - // prometheus::Gauge &gpu_cache = gpu_cache_usage_.Add({{"GPU_Cache", std::to_string(i)}}); - // gpu_cache.Set(cache_usage * 100 / cache_capacity); - // } -} - -} // namespace server -} // namespace milvus diff --git a/core/src/metrics/prometheus/PrometheusMetrics.h b/core/src/metrics/prometheus/PrometheusMetrics.h deleted file mode 100644 index b452eb2224..0000000000 --- a/core/src/metrics/prometheus/PrometheusMetrics.h +++ /dev/null @@ -1,660 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "metrics/MetricBase.h" -#include "utils/Log.h" -#include "utils/Status.h" - -#define METRICS_NOW_TIME std::chrono::system_clock::now() -//#define server::Metrics::GetInstance() server::GetInstance() -#define METRICS_MICROSECONDS(a, b) (std::chrono::duration_cast(b - a)).count(); - -namespace milvus { -namespace server { - -class PrometheusMetrics : public MetricsBase { - public: - static PrometheusMetrics& - GetInstance() { - static PrometheusMetrics instance; - return instance; - } - - Status - Init() override; - - private: - std::shared_ptr gateway_; - std::shared_ptr registry_ = std::make_shared(); - bool startup_ = false; - - public: - void - SetStartup(bool startup) { - startup_ = startup; - } - - void - AddVectorsSuccessTotalIncrement(double value = 1.0) override { - if (startup_) { - add_vectors_success_total_.Increment(value); - } - } - - void - AddVectorsFailTotalIncrement(double value = 1.0) override { - if (startup_) { - add_vectors_fail_total_.Increment(value); - } - } - - void - AddVectorsDurationHistogramOberve(double value) override { - if (startup_) { - add_vectors_duration_histogram_.Observe(value); - } - } - - void - RawFileSizeHistogramObserve(double value) override { - if (startup_) { - raw_files_size_histogram_.Observe(value); - } - } - - void - IndexFileSizeHistogramObserve(double value) override { - if (startup_) { - index_files_size_histogram_.Observe(value); - } - } - - void - BuildIndexDurationSecondsHistogramObserve(double value) override { - if (startup_) { - build_index_duration_seconds_histogram_.Observe(value); - } - } - - void - CpuCacheUsageGaugeSet(double value) override { - if (startup_) { - cpu_cache_usage_gauge_.Set(value); - } - } - - void - GpuCacheUsageGaugeSet() override; - - void - MetaAccessTotalIncrement(double value = 1) override { - if (startup_) { - meta_access_total_.Increment(value); - } - } - - void - MetaAccessDurationSecondsHistogramObserve(double value) override { - if (startup_) { - meta_access_duration_seconds_histogram_.Observe(value); - } - } - - void - FaissDiskLoadDurationSecondsHistogramObserve(double value) override { - if (startup_) { - faiss_disk_load_duration_seconds_histogram_.Observe(value); - } - } - - void - FaissDiskLoadSizeBytesHistogramObserve(double value) override { - if (startup_) { - faiss_disk_load_size_bytes_histogram_.Observe(value); - } - } - - void - FaissDiskLoadIOSpeedGaugeSet(double value) override { - if (startup_) { - faiss_disk_load_IO_speed_gauge_.Set(value); - } - } - - void - CacheAccessTotalIncrement(double value = 1) override { - if (startup_) { - cache_access_total_.Increment(value); - } - } - - void - MemTableMergeDurationSecondsHistogramObserve(double value) override { - if (startup_) { - mem_table_merge_duration_seconds_histogram_.Observe(value); - } - } - - void - SearchIndexDataDurationSecondsHistogramObserve(double value) override { - if (startup_) { - search_index_data_duration_seconds_histogram_.Observe(value); - } - } - - void - SearchRawDataDurationSecondsHistogramObserve(double value) override { - if (startup_) { - search_raw_data_duration_seconds_histogram_.Observe(value); - } - } - - void - IndexFileSizeTotalIncrement(double value = 1) override { - if (startup_) { - index_file_size_total_.Increment(value); - } - } - - void - RawFileSizeTotalIncrement(double value = 1) override { - if (startup_) { - raw_file_size_total_.Increment(value); - } - } - - void - IndexFileSizeGaugeSet(double value) override { - if (startup_) { - index_file_size_gauge_.Set(value); - } - } - - void - RawFileSizeGaugeSet(double value) override { - if (startup_) { - raw_file_size_gauge_.Set(value); - } - } - - void - QueryResponseSummaryObserve(double value) override { - if (startup_) { - query_response_summary_.Observe(value); - } - } - - void - DiskStoreIOSpeedGaugeSet(double value) override { - if (startup_) { - disk_store_IO_speed_gauge_.Set(value); - } - } - - void - DataFileSizeGaugeSet(double value) override { - if (startup_) { - data_file_size_gauge_.Set(value); - } - } - - void - AddVectorsSuccessGaugeSet(double value) override { - if (startup_) { - add_vectors_success_gauge_.Set(value); - } - } - - void - AddVectorsFailGaugeSet(double value) override { - if (startup_) { - add_vectors_fail_gauge_.Set(value); - } - } - - void - QueryVectorResponseSummaryObserve(double value, int count = 1) override { - if (startup_) { - for (int i = 0; i < count; ++i) { - query_vector_response_summary_.Observe(value); - } - } - } - - void - QueryVectorResponsePerSecondGaugeSet(double value) override { - if (startup_) { - query_vector_response_per_second_gauge_.Set(value); - } - } - - void - CPUUsagePercentSet() override; - void - CPUCoreUsagePercentSet() override; - - void - RAMUsagePercentSet() override; - - void - QueryResponsePerSecondGaugeSet(double value) override { - if (startup_) { - query_response_per_second_gauge.Set(value); - } - } - - void - GPUPercentGaugeSet() override; - void - GPUMemoryUsageGaugeSet() override; - void - AddVectorsPerSecondGaugeSet(int num_vector, int dim, double time) override; - void - QueryIndexTypePerSecondSet(std::string type, double value) override; - void - ConnectionGaugeIncrement() override; - void - ConnectionGaugeDecrement() override; - - void - KeepingAliveCounterIncrement(double value = 1) override { - if (startup_) { - keeping_alive_counter_.Increment(value); - } - } - - void - OctetsSet() override; - - void - GPUTemperature() override; - void - CPUTemperature() override; - - void - PushToGateway() override { - if (startup_) { - if (gateway_->Push() != 200) { - LOG_ENGINE_WARNING_ << "Metrics pushgateway failed"; - } - } - } - - std::shared_ptr& - gateway() { - return gateway_; - } - - // prometheus::Exposer& exposer() { return exposer_;} - std::shared_ptr& - registry_ptr() { - return registry_; - } - - // ..... - private: - ////all from db_connection.cpp - // prometheus::Family &connect_request_ = prometheus::BuildCounter() - // .Name("connection_total") - // .Help("total number of connection has been made") - // .Register(*registry_); - // prometheus::Counter &connection_total_ = connect_request_.Add({}); - - ////all from DBImpl.cpp - using BucketBoundaries = std::vector; - // record add_group request - prometheus::Family& add_group_request_ = prometheus::BuildCounter() - .Name("add_group_request_total") - .Help("the number of add_group request") - .Register(*registry_); - - prometheus::Counter& add_group_success_total_ = add_group_request_.Add({{"outcome", "success"}}); - prometheus::Counter& add_group_fail_total_ = add_group_request_.Add({{"outcome", "fail"}}); - - // record get_group request - prometheus::Family& get_group_request_ = prometheus::BuildCounter() - .Name("get_group_request_total") - .Help("the number of get_group request") - .Register(*registry_); - - prometheus::Counter& get_group_success_total_ = get_group_request_.Add({{"outcome", "success"}}); - prometheus::Counter& get_group_fail_total_ = get_group_request_.Add({{"outcome", "fail"}}); - - // record has_group request - prometheus::Family& has_group_request_ = prometheus::BuildCounter() - .Name("has_group_request_total") - .Help("the number of has_group request") - .Register(*registry_); - - prometheus::Counter& has_group_success_total_ = has_group_request_.Add({{"outcome", "success"}}); - prometheus::Counter& has_group_fail_total_ = has_group_request_.Add({{"outcome", "fail"}}); - - // record get_group_files - prometheus::Family& get_group_files_request_ = - prometheus::BuildCounter() - .Name("get_group_files_request_total") - .Help("the number of get_group_files request") - .Register(*registry_); - - prometheus::Counter& get_group_files_success_total_ = get_group_files_request_.Add({{"outcome", "success"}}); - prometheus::Counter& get_group_files_fail_total_ = get_group_files_request_.Add({{"outcome", "fail"}}); - - // record add_vectors count and average time - // need to be considered - prometheus::Family& add_vectors_request_ = prometheus::BuildCounter() - .Name("add_vectors_request_total") - .Help("the number of vectors added") - .Register(*registry_); - prometheus::Counter& add_vectors_success_total_ = add_vectors_request_.Add({{"outcome", "success"}}); - prometheus::Counter& add_vectors_fail_total_ = add_vectors_request_.Add({{"outcome", "fail"}}); - - prometheus::Family& add_vectors_duration_seconds_ = - prometheus::BuildHistogram() - .Name("add_vector_duration_microseconds") - .Help("average time of adding every vector") - .Register(*registry_); - prometheus::Histogram& add_vectors_duration_histogram_ = - add_vectors_duration_seconds_.Add({}, BucketBoundaries{0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.08, 0.1, 0.5, 1}); - - // record search count and average time - prometheus::Family& search_request_ = prometheus::BuildCounter() - .Name("search_request_total") - .Help("the number of search request") - .Register(*registry_); - prometheus::Counter& search_success_total_ = search_request_.Add({{"outcome", "success"}}); - prometheus::Counter& search_fail_total_ = search_request_.Add({{"outcome", "fail"}}); - - prometheus::Family& search_request_duration_seconds_ = - prometheus::BuildHistogram() - .Name("search_request_duration_microsecond") - .Help("histogram of processing time for each search") - .Register(*registry_); - prometheus::Histogram& search_duration_histogram_ = - search_request_duration_seconds_.Add({}, BucketBoundaries{0.1, 1.0, 10.0}); - - // record raw_files size histogram - prometheus::Family& raw_files_size_ = prometheus::BuildHistogram() - .Name("search_raw_files_bytes") - .Help("histogram of raw files size by bytes") - .Register(*registry_); - prometheus::Histogram& raw_files_size_histogram_ = - raw_files_size_.Add({}, BucketBoundaries{1e9, 2e9, 4e9, 6e9, 8e9, 1e10}); - - // record index_files size histogram - prometheus::Family& index_files_size_ = prometheus::BuildHistogram() - .Name("search_index_files_bytes") - .Help("histogram of index files size by bytes") - .Register(*registry_); - prometheus::Histogram& index_files_size_histogram_ = - index_files_size_.Add({}, BucketBoundaries{1e9, 2e9, 4e9, 6e9, 8e9, 1e10}); - - // record index and raw files size counter - prometheus::Family& file_size_total_ = prometheus::BuildCounter() - .Name("search_file_size_total") - .Help("searched index and raw file size") - .Register(*registry_); - prometheus::Counter& index_file_size_total_ = file_size_total_.Add({{"type", "index"}}); - prometheus::Counter& raw_file_size_total_ = file_size_total_.Add({{"type", "raw"}}); - - // record index and raw files size counter - prometheus::Family& file_size_gauge_ = prometheus::BuildGauge() - .Name("search_file_size_gauge") - .Help("searched current index and raw file size") - .Register(*registry_); - prometheus::Gauge& index_file_size_gauge_ = file_size_gauge_.Add({{"type", "index"}}); - prometheus::Gauge& raw_file_size_gauge_ = file_size_gauge_.Add({{"type", "raw"}}); - - // record processing time for building index - prometheus::Family& build_index_duration_seconds_ = - prometheus::BuildHistogram() - .Name("build_index_duration_microseconds") - .Help("histogram of processing time for building index") - .Register(*registry_); - prometheus::Histogram& build_index_duration_seconds_histogram_ = - build_index_duration_seconds_.Add({}, BucketBoundaries{5e5, 2e6, 4e6, 6e6, 8e6, 1e7}); - - // record processing time for all building index - prometheus::Family& all_build_index_duration_seconds_ = - prometheus::BuildHistogram() - .Name("all_build_index_duration_microseconds") - .Help("histogram of processing time for building index") - .Register(*registry_); - prometheus::Histogram& all_build_index_duration_seconds_histogram_ = - all_build_index_duration_seconds_.Add({}, BucketBoundaries{2e6, 4e6, 6e6, 8e6, 1e7}); - - // record duration of merging mem collection - prometheus::Family& mem_table_merge_duration_seconds_ = - prometheus::BuildHistogram() - .Name("mem_table_merge_duration_microseconds") - .Help("histogram of processing time for merging mem tables") - .Register(*registry_); - prometheus::Histogram& mem_table_merge_duration_seconds_histogram_ = - mem_table_merge_duration_seconds_.Add({}, BucketBoundaries{5e4, 1e5, 2e5, 4e5, 6e5, 8e5, 1e6}); - - // record search index and raw data duration - prometheus::Family& search_data_duration_seconds_ = - prometheus::BuildHistogram() - .Name("search_data_duration_microseconds") - .Help("histograms of processing time for search index and raw data") - .Register(*registry_); - prometheus::Histogram& search_index_data_duration_seconds_histogram_ = - search_data_duration_seconds_.Add({{"type", "index"}}, BucketBoundaries{1e5, 2e5, 4e5, 6e5, 8e5}); - prometheus::Histogram& search_raw_data_duration_seconds_histogram_ = - search_data_duration_seconds_.Add({{"type", "raw"}}, BucketBoundaries{1e5, 2e5, 4e5, 6e5, 8e5}); - - ////all form Cache.cpp - // record cache usage, when insert/erase/clear/free - - ////all from Meta.cpp - // record meta visit count and time - // prometheus::Family &meta_visit_ = prometheus::BuildCounter() - // .Name("meta_visit_total") - // .Help("the number of accessing Meta") - // .Register(*registry_); - // prometheus::Counter &meta_visit_total_ = meta_visit_.Add({{}}); - // - // prometheus::Family &meta_visit_duration_seconds_ = prometheus::BuildHistogram() - // .Name("meta_visit_duration_seconds") - // .Help("histogram of processing time to get data from mata") - // .Register(*registry_); - // prometheus::Histogram &meta_visit_duration_seconds_histogram_ = - // meta_visit_duration_seconds_.Add({{}}, BucketBoundaries{0.1, 1.0, 10.0}); - - ////all from MemManager.cpp - // record memory usage percent - prometheus::Family& mem_usage_percent_ = - prometheus::BuildGauge().Name("memory_usage_percent").Help("memory usage percent").Register(*registry_); - prometheus::Gauge& mem_usage_percent_gauge_ = mem_usage_percent_.Add({}); - - // record memory usage toal - prometheus::Family& mem_usage_total_ = - prometheus::BuildGauge().Name("memory_usage_total").Help("memory usage total").Register(*registry_); - prometheus::Gauge& mem_usage_total_gauge_ = mem_usage_total_.Add({}); - - ////all from DBMetaImpl.cpp - // record meta access count - prometheus::Family& meta_access_ = - prometheus::BuildCounter().Name("meta_access_total").Help("the number of meta accessing").Register(*registry_); - prometheus::Counter& meta_access_total_ = meta_access_.Add({}); - - // record meta access duration - prometheus::Family& meta_access_duration_seconds_ = - prometheus::BuildHistogram() - .Name("meta_access_duration_microseconds") - .Help("histogram of processing time for accessing mata") - .Register(*registry_); - prometheus::Histogram& meta_access_duration_seconds_histogram_ = - meta_access_duration_seconds_.Add({}, BucketBoundaries{100, 300, 500, 700, 900, 2000, 4000, 6000, 8000, 20000}); - - ////all from FaissExecutionEngine.cpp - // record data loading from disk count, size, duration, IO speed - prometheus::Family& disk_load_duration_second_ = - prometheus::BuildHistogram() - .Name("disk_load_duration_microseconds") - .Help("Histogram of processing time for loading data from disk") - .Register(*registry_); - prometheus::Histogram& faiss_disk_load_duration_seconds_histogram_ = - disk_load_duration_second_.Add({{"DB", "Faiss"}}, BucketBoundaries{2e5, 4e5, 6e5, 8e5}); - - prometheus::Family& disk_load_size_bytes_ = - prometheus::BuildHistogram() - .Name("disk_load_size_bytes") - .Help("Histogram of data size by bytes for loading data from disk") - .Register(*registry_); - prometheus::Histogram& faiss_disk_load_size_bytes_histogram_ = - disk_load_size_bytes_.Add({{"DB", "Faiss"}}, BucketBoundaries{1e9, 2e9, 4e9, 6e9, 8e9}); - - // prometheus::Family &disk_load_IO_speed_ = prometheus::BuildHistogram() - // .Name("disk_load_IO_speed_byte_per_sec") - // .Help("Histogram of IO speed for loading data from disk") - // .Register(*registry_); - // prometheus::Histogram &faiss_disk_load_IO_speed_histogram_ = - // disk_load_IO_speed_.Add({{"DB","Faiss"}},BucketBoundaries{1000, 2000, 3000, 4000, 6000, 8000}); - - prometheus::Family& faiss_disk_load_IO_speed_ = prometheus::BuildGauge() - .Name("disk_load_IO_speed_byte_per_microsec") - .Help("disk IO speed ") - .Register(*registry_); - prometheus::Gauge& faiss_disk_load_IO_speed_gauge_ = faiss_disk_load_IO_speed_.Add({{"DB", "Faiss"}}); - - ////all from CacheMgr.cpp - // record cache access count - prometheus::Family& cache_access_ = prometheus::BuildCounter() - .Name("cache_access_total") - .Help("the count of accessing cache ") - .Register(*registry_); - prometheus::Counter& cache_access_total_ = cache_access_.Add({}); - - // record CPU cache usage and % - prometheus::Family& cpu_cache_usage_ = - prometheus::BuildGauge().Name("cache_usage_bytes").Help("current cache usage by bytes").Register(*registry_); - prometheus::Gauge& cpu_cache_usage_gauge_ = cpu_cache_usage_.Add({}); - - // record GPU cache usage and % - prometheus::Family& gpu_cache_usage_ = prometheus::BuildGauge() - .Name("gpu_cache_usage_bytes") - .Help("current gpu cache usage by bytes") - .Register(*registry_); - - // record query response - using Quantiles = std::vector; - prometheus::Family& query_response_ = - prometheus::BuildSummary().Name("query_response_summary").Help("query response summary").Register(*registry_); - prometheus::Summary& query_response_summary_ = - query_response_.Add({}, Quantiles{{0.95, 0.00}, {0.9, 0.05}, {0.8, 0.1}}); - - prometheus::Family& query_vector_response_ = prometheus::BuildSummary() - .Name("query_vector_response_summary") - .Help("query each vector response summary") - .Register(*registry_); - prometheus::Summary& query_vector_response_summary_ = - query_vector_response_.Add({}, Quantiles{{0.95, 0.00}, {0.9, 0.05}, {0.8, 0.1}}); - - prometheus::Family& query_vector_response_per_second_ = - prometheus::BuildGauge() - .Name("query_vector_response_per_microsecond") - .Help("the number of vectors can be queried every second ") - .Register(*registry_); - prometheus::Gauge& query_vector_response_per_second_gauge_ = query_vector_response_per_second_.Add({}); - - prometheus::Family& query_response_per_second_ = - prometheus::BuildGauge() - .Name("query_response_per_microsecond") - .Help("the number of queries can be processed every microsecond") - .Register(*registry_); - prometheus::Gauge& query_response_per_second_gauge = query_response_per_second_.Add({}); - - prometheus::Family& disk_store_IO_speed_ = - prometheus::BuildGauge() - .Name("disk_store_IO_speed_bytes_per_microseconds") - .Help("disk_store_IO_speed") - .Register(*registry_); - prometheus::Gauge& disk_store_IO_speed_gauge_ = disk_store_IO_speed_.Add({}); - - prometheus::Family& data_file_size_ = - prometheus::BuildGauge().Name("data_file_size_bytes").Help("data file size by bytes").Register(*registry_); - prometheus::Gauge& data_file_size_gauge_ = data_file_size_.Add({}); - - prometheus::Family& add_vectors_ = - prometheus::BuildGauge().Name("add_vectors").Help("current added vectors").Register(*registry_); - prometheus::Gauge& add_vectors_success_gauge_ = add_vectors_.Add({{"outcome", "success"}}); - prometheus::Gauge& add_vectors_fail_gauge_ = add_vectors_.Add({{"outcome", "fail"}}); - - prometheus::Family& add_vectors_per_second_ = prometheus::BuildGauge() - .Name("add_vectors_throughput_per_microsecond") - .Help("add vectors throughput per microsecond") - .Register(*registry_); - prometheus::Gauge& add_vectors_per_second_gauge_ = add_vectors_per_second_.Add({}); - - prometheus::Family& CPU_ = prometheus::BuildGauge() - .Name("CPU_usage_percent") - .Help("CPU usage percent by this this process") - .Register(*registry_); - prometheus::Gauge& CPU_usage_percent_ = CPU_.Add({{"CPU", "avg"}}); - - prometheus::Family& RAM_ = prometheus::BuildGauge() - .Name("RAM_usage_percent") - .Help("RAM usage percent by this process") - .Register(*registry_); - prometheus::Gauge& RAM_usage_percent_ = RAM_.Add({}); - - // GPU Usage Percent - prometheus::Family& GPU_percent_ = - prometheus::BuildGauge().Name("Gpu_usage_percent").Help("GPU_usage_percent ").Register(*registry_); - - // GPU Mempry used - prometheus::Family& GPU_memory_usage_ = - prometheus::BuildGauge().Name("GPU_memory_usage_total").Help("GPU memory usage total ").Register(*registry_); - - prometheus::Family& query_index_type_per_second_ = - prometheus::BuildGauge() - .Name("query_index_throughtout_per_microsecond") - .Help("query index throughtout per microsecond") - .Register(*registry_); - prometheus::Gauge& query_index_IVF_type_per_second_gauge_ = - query_index_type_per_second_.Add({{"IndexType", "IVF"}}); - prometheus::Gauge& query_index_IDMAP_type_per_second_gauge_ = - query_index_type_per_second_.Add({{"IndexType", "IDMAP"}}); - - prometheus::Family& connection_ = - prometheus::BuildGauge().Name("connection_number").Help("the number of connections").Register(*registry_); - prometheus::Gauge& connection_gauge_ = connection_.Add({}); - - prometheus::Family& keeping_alive_ = prometheus::BuildCounter() - .Name("keeping_alive_seconds_total") - .Help("total seconds of the serve alive") - .Register(*registry_); - prometheus::Counter& keeping_alive_counter_ = keeping_alive_.Add({}); - - prometheus::Family& octets_ = - prometheus::BuildGauge().Name("octets_bytes_per_second").Help("octets bytes per second").Register(*registry_); - prometheus::Gauge& inoctets_gauge_ = octets_.Add({{"type", "inoctets"}}); - prometheus::Gauge& outoctets_gauge_ = octets_.Add({{"type", "outoctets"}}); - - prometheus::Family& GPU_temperature_ = - prometheus::BuildGauge().Name("GPU_temperature").Help("GPU temperature").Register(*registry_); - - prometheus::Family& CPU_temperature_ = - prometheus::BuildGauge().Name("CPU_temperature").Help("CPU temperature").Register(*registry_); -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/query/BinaryQuery.cpp b/core/src/query/BinaryQuery.cpp deleted file mode 100644 index 4882e0140e..0000000000 --- a/core/src/query/BinaryQuery.cpp +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include -#include -#include - -#include "query/BinaryQuery.h" - -namespace milvus { -namespace query { - -BinaryQueryPtr -ConstructBinTree(std::vector queries, QueryRelation relation, uint64_t idx) { - if (idx == queries.size()) { - return nullptr; - } else if (idx == queries.size() - 1) { - return queries[idx]->getBinaryQuery(); - } else { - BinaryQueryPtr bquery = std::make_shared(); - bquery->relation = relation; - bquery->left_query = std::make_shared(); - bquery->right_query = std::make_shared(); - bquery->left_query->bin = queries[idx]->getBinaryQuery(); - ++idx; - bquery->right_query->bin = ConstructBinTree(queries, relation, idx); - return bquery; - } -} - -Status -ConstructLeafBinTree(std::vector leaf_queries, BinaryQueryPtr binary_query, uint64_t idx) { - if (idx == leaf_queries.size()) { - return Status::OK(); - } - binary_query->left_query = std::make_shared(); - binary_query->right_query = std::make_shared(); - if (leaf_queries.size() == leaf_queries.size() - 1) { - binary_query->left_query->leaf = leaf_queries[idx]; - return Status::OK(); - } else if (idx == leaf_queries.size() - 2) { - binary_query->left_query->leaf = leaf_queries[idx]; - ++idx; - binary_query->right_query->leaf = leaf_queries[idx]; - return Status::OK(); - } else { - binary_query->left_query->bin->relation = binary_query->relation; - binary_query->right_query->leaf = leaf_queries[idx]; - ++idx; - return ConstructLeafBinTree(leaf_queries, binary_query->left_query->bin, idx); - } -} - -Status -GenBinaryQuery(BooleanQueryPtr query, BinaryQueryPtr& binary_query) { - if (query->getBooleanQuerys().size() == 0) { - if (binary_query->relation == QueryRelation::AND || binary_query->relation == QueryRelation::OR) { - // Put VectorQuery to the end of leafqueries - auto query_size = query->getLeafQueries().size(); - for (uint64_t i = 0; i < query_size; ++i) { - if (query->getLeafQueries()[i]->vector_query != nullptr) { - std::swap(query->getLeafQueries()[i], query->getLeafQueries()[0]); - break; - } - } - return ConstructLeafBinTree(query->getLeafQueries(), binary_query, 0); - } else { - switch (query->getOccur()) { - case Occur::MUST: { - binary_query->relation = QueryRelation::AND; - return GenBinaryQuery(query, binary_query); - } - case Occur::MUST_NOT: - case Occur::SHOULD: { - binary_query->relation = QueryRelation::OR; - return GenBinaryQuery(query, binary_query); - } - default: - return Status::OK(); - } - } - } - - if (query->getBooleanQuerys().size() == 1) { - auto bc = query->getBooleanQuerys()[0]; - binary_query->left_query = std::make_shared(); - switch (bc->getOccur()) { - case Occur::MUST: { - binary_query->relation = QueryRelation::AND; - return GenBinaryQuery(bc, binary_query); - } - case Occur::MUST_NOT: - case Occur::SHOULD: { - binary_query->relation = QueryRelation::OR; - return GenBinaryQuery(bc, binary_query); - } - default: - return Status::OK(); - } - } - - // Construct binary query for every single boolean query - std::vector must_queries; - std::vector must_not_queries; - std::vector should_queries; - Status status; - for (auto& _query : query->getBooleanQuerys()) { - status = GenBinaryQuery(_query, _query->getBinaryQuery()); - if (!status.ok()) { - return status; - } - if (_query->getOccur() == Occur::MUST) { - must_queries.emplace_back(_query); - } else if (_query->getOccur() == Occur::MUST_NOT) { - must_not_queries.emplace_back(_query); - } else { - should_queries.emplace_back(_query); - } - } - - // Construct binary query for combine boolean queries - BinaryQueryPtr must_bquery, should_bquery, must_not_bquery; - uint64_t bquery_num = 0; - if (must_queries.size() > 1) { - // Construct a must binary tree - must_bquery = ConstructBinTree(must_queries, QueryRelation::R1, 0); - ++bquery_num; - } else if (must_queries.size() == 1) { - must_bquery = must_queries[0]->getBinaryQuery(); - ++bquery_num; - } - - if (should_queries.size() > 1) { - // Construct a should binary tree - should_bquery = ConstructBinTree(should_queries, QueryRelation::R2, 0); - ++bquery_num; - } else if (should_queries.size() == 1) { - should_bquery = should_queries[0]->getBinaryQuery(); - ++bquery_num; - } - - if (must_not_queries.size() > 1) { - // Construct a must_not binary tree - must_not_bquery = ConstructBinTree(must_not_queries, QueryRelation::R1, 0); - ++bquery_num; - } else if (must_not_queries.size() == 1) { - must_not_bquery = must_not_queries[0]->getBinaryQuery(); - ++bquery_num; - } - - binary_query->left_query = std::make_shared(); - binary_query->right_query = std::make_shared(); - BinaryQueryPtr must_should_query = std::make_shared(); - must_should_query->left_query = std::make_shared(); - must_should_query->right_query = std::make_shared(); - if (bquery_num == 3) { - must_should_query->relation = QueryRelation::R3; - must_should_query->left_query->bin = must_bquery; - must_should_query->right_query->bin = should_bquery; - binary_query->relation = QueryRelation::R1; - binary_query->left_query->bin = must_should_query; - binary_query->right_query->bin = must_not_bquery; - } else if (bquery_num == 2) { - if (must_bquery == nullptr) { - binary_query->relation = QueryRelation::R3; - binary_query->left_query->bin = must_not_bquery; - binary_query->right_query->bin = should_bquery; - } else if (should_bquery == nullptr) { - binary_query->relation = QueryRelation::R4; - binary_query->left_query->bin = must_bquery; - binary_query->right_query->bin = must_not_bquery; - } else { - binary_query->relation = QueryRelation::R3; - binary_query->left_query->bin = must_bquery; - binary_query->right_query->bin = should_bquery; - } - } else { - if (must_bquery != nullptr) { - binary_query = must_bquery; - } else if (should_bquery != nullptr) { - binary_query = should_bquery; - } else { - binary_query = must_not_bquery; - } - } - - return Status::OK(); -} - -uint64_t -BinaryQueryHeight(BinaryQueryPtr& binary_query) { - if (binary_query == nullptr) { - return 1; - } - uint64_t left_height = 0, right_height = 0; - if (binary_query->left_query != nullptr) { - left_height = BinaryQueryHeight(binary_query->left_query->bin); - } - if (binary_query->right_query != nullptr) { - right_height = BinaryQueryHeight(binary_query->right_query->bin); - } - return left_height > right_height ? left_height + 1 : right_height + 1; -} - -bool -ValidateBinaryQuery(BinaryQueryPtr& binary_query) { - // Only for one layer BooleanQuery - uint64_t height = BinaryQueryHeight(binary_query); - return height > 1; -} - -} // namespace query -} // namespace milvus diff --git a/core/src/query/BinaryQuery.h b/core/src/query/BinaryQuery.h deleted file mode 100644 index 5c296338ce..0000000000 --- a/core/src/query/BinaryQuery.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -#include "BooleanQuery.h" - -namespace milvus { -namespace query { - -BinaryQueryPtr -ConstructBinTree(std::vector clauses, QueryRelation relation, uint64_t idx); - -Status -ConstructLeafBinTree(std::vector leaf_clauses, BinaryQueryPtr binary_query, uint64_t idx); - -Status -GenBinaryQuery(BooleanQueryPtr clause, BinaryQueryPtr& binary_query); - -uint64_t -BinaryQueryHeight(BinaryQueryPtr& binary_query); - -bool -ValidateBinaryQuery(BinaryQueryPtr& binary_query); - -} // namespace query -} // namespace milvus diff --git a/core/src/query/BooleanQuery.h b/core/src/query/BooleanQuery.h deleted file mode 100644 index e5304aad11..0000000000 --- a/core/src/query/BooleanQuery.h +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -#include "GeneralQuery.h" -#include "utils/Status.h" - -namespace milvus { -namespace query { - -enum class Occur { - INVALID = 0, - MUST, - MUST_NOT, - SHOULD, -}; - -class BooleanQuery { - public: - BooleanQuery() { - } - - explicit BooleanQuery(Occur occur) : occur_(occur) { - } - - Occur - getOccur() { - return occur_; - } - - void - SetOccur(Occur occur) { - occur_ = occur; - } - - void - AddBooleanQuery(std::shared_ptr boolean_clause) { - boolean_clauses_.emplace_back(boolean_clause); - } - - void - AddLeafQuery(LeafQueryPtr leaf_query) { - leaf_queries_.emplace_back(leaf_query); - } - - void - SetLeafQuery(std::vector leaf_queries) { - leaf_queries_ = leaf_queries; - } - - std::vector> - getBooleanQuerys() { - return boolean_clauses_; - } - - BinaryQueryPtr& - getBinaryQuery() { - return binary_query_; - } - - std::vector& - getLeafQueries() { - return leaf_queries_; - } - - private: - Occur occur_ = Occur::INVALID; - std::vector> boolean_clauses_; - std::vector leaf_queries_; - BinaryQueryPtr binary_query_ = std::make_shared(); -}; -using BooleanQueryPtr = std::shared_ptr; - -} // namespace query -} // namespace milvus diff --git a/core/src/query/GeneralQuery.h b/core/src/query/GeneralQuery.h deleted file mode 100644 index 41b9d5c574..0000000000 --- a/core/src/query/GeneralQuery.h +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include "utils/Json.h" - -namespace milvus { -namespace query { - -enum class CompareOperator { - LT = 0, - LTE, - EQ, - GT, - GTE, - NE, -}; - -enum class QueryRelation { - INVALID = 0, - R1, - R2, - R3, - R4, - AND, - OR, -}; - -struct QueryColumn { - std::string name; - std::string column_value; -}; - -struct TermQuery { - std::string field_name; - std::vector field_value; - float boost; -}; -using TermQueryPtr = std::shared_ptr; - -struct CompareExpr { - CompareOperator compare_operator; - std::string operand; -}; - -struct RangeQuery { - std::string field_name; - std::vector compare_expr; - float boost; -}; -using RangeQueryPtr = std::shared_ptr; - -struct VectorRecord { - std::vector float_data; - std::vector binary_data; -}; - -struct VectorQuery { - std::string field_name; - milvus::json extra_params; - int64_t topk; - float boost; - VectorRecord query_vector; -}; -using VectorQueryPtr = std::shared_ptr; - -struct LeafQuery; -using LeafQueryPtr = std::shared_ptr; - -struct BinaryQuery; -using BinaryQueryPtr = std::shared_ptr; - -struct GeneralQuery { - LeafQueryPtr leaf; - BinaryQueryPtr bin = std::make_shared(); -}; -using GeneralQueryPtr = std::shared_ptr; - -struct LeafQuery { - TermQueryPtr term_query; - RangeQueryPtr range_query; - VectorQueryPtr vector_query; - float query_boost; -}; - -struct BinaryQuery { - GeneralQueryPtr left_query; - GeneralQueryPtr right_query; - QueryRelation relation; - float query_boost; -}; - -} // namespace query -} // namespace milvus diff --git a/core/src/scheduler/Algorithm.cpp b/core/src/scheduler/Algorithm.cpp deleted file mode 100644 index a8b9748290..0000000000 --- a/core/src/scheduler/Algorithm.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/Algorithm.h" - -#include -#include -#include - -namespace milvus { -namespace scheduler { - -constexpr uint64_t MAXINT = std::numeric_limits::max(); - -uint64_t -ShortestPath(const ResourcePtr& src, const ResourcePtr& dest, const ResourceMgrPtr& res_mgr, - std::vector& path) { - uint64_t num_of_resources = res_mgr->GetAllResources().size(); - std::unordered_map id_name_map; - std::unordered_map name_id_map; - for (uint64_t i = 0; i < num_of_resources; ++i) { - id_name_map.insert(std::make_pair(i, res_mgr->GetAllResources().at(i)->name())); - name_id_map.insert(std::make_pair(res_mgr->GetAllResources().at(i)->name(), i)); - } - - std::vector> dis_matrix; - dis_matrix.resize(num_of_resources); - for (uint64_t i = 0; i < num_of_resources; ++i) { - dis_matrix[i].resize(num_of_resources); - for (uint64_t j = 0; j < num_of_resources; ++j) { - dis_matrix[i][j] = MAXINT; - } - dis_matrix[i][i] = 0; - } - - std::vector vis(num_of_resources, false); - std::vector dis(num_of_resources, MAXINT); - for (auto& res : res_mgr->GetAllResources()) { - auto cur_node = std::static_pointer_cast(res); - auto cur_neighbours = cur_node->GetNeighbours(); - - for (auto& neighbour : cur_neighbours) { - auto neighbour_res = std::static_pointer_cast(neighbour.neighbour_node); - dis_matrix[name_id_map.at(res->name())][name_id_map.at(neighbour_res->name())] = - neighbour.connection.transport_cost(); - } - } - - for (uint64_t i = 0; i < num_of_resources; ++i) { - dis[i] = dis_matrix[name_id_map.at(src->name())][i]; - } - - vis[name_id_map.at(src->name())] = true; - std::vector parent(num_of_resources, -1); - - for (uint64_t i = 0; i < num_of_resources; ++i) { - uint64_t minn = MAXINT; - uint64_t temp = 0; - for (uint64_t j = 0; j < num_of_resources; ++j) { - if (!vis[j] && dis[j] < minn) { - minn = dis[j]; - temp = j; - } - } - vis[temp] = true; - - if (i == 0) { - parent[temp] = name_id_map.at(src->name()); - } - - for (uint64_t j = 0; j < num_of_resources; ++j) { - if (!vis[j] && dis_matrix[temp][j] != MAXINT && dis_matrix[temp][j] + dis[temp] < dis[j]) { - dis[j] = dis_matrix[temp][j] + dis[temp]; - parent[j] = temp; - } - } - } - - int64_t parent_idx = parent[name_id_map.at(dest->name())]; - if (parent_idx != -1) { - path.push_back(dest->name()); - } - while (parent_idx != -1) { - path.push_back(id_name_map.at(parent_idx)); - parent_idx = parent[parent_idx]; - } - return dis[name_id_map.at(dest->name())]; -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/Algorithm.h b/core/src/scheduler/Algorithm.h deleted file mode 100644 index c508728aa7..0000000000 --- a/core/src/scheduler/Algorithm.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "ResourceMgr.h" -#include "resource/Resource.h" - -#include -#include - -namespace milvus { -namespace scheduler { - -uint64_t -ShortestPath(const ResourcePtr& src, const ResourcePtr& dest, const ResourceMgrPtr& res_mgr, - std::vector& path); - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/BuildMgr.cpp b/core/src/scheduler/BuildMgr.cpp deleted file mode 100644 index 222e0dc987..0000000000 --- a/core/src/scheduler/BuildMgr.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/BuildMgr.h" - -namespace milvus { -namespace scheduler {} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/BuildMgr.h b/core/src/scheduler/BuildMgr.h deleted file mode 100644 index 33ff700722..0000000000 --- a/core/src/scheduler/BuildMgr.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace milvus { -namespace scheduler { - -class BuildMgr { - public: - explicit BuildMgr(int64_t concurrent_limit) : available_(concurrent_limit) { - } - - public: - void - Put() { - std::lock_guard lock(mutex_); - ++available_; - } - - bool - Take() { - std::lock_guard lock(mutex_); - if (available_ < 1) { - return false; - } else { - --available_; - return true; - } - } - - int64_t - NumOfAvailable() { - return available_; - } - - private: - std::int64_t available_; - std::mutex mutex_; -}; - -using BuildMgrPtr = std::shared_ptr; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/CPUBuilder.cpp b/core/src/scheduler/CPUBuilder.cpp deleted file mode 100644 index 78794b4cc0..0000000000 --- a/core/src/scheduler/CPUBuilder.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/CPUBuilder.h" -#include "utils/Log.h" - -namespace milvus { -namespace scheduler { - -void -CPUBuilder::Start() { - std::lock_guard lock(mutex_); - if (not running_) { - running_ = true; - thread_ = std::thread(&CPUBuilder::worker_function, this); - } -} - -void -CPUBuilder::Stop() { - std::lock_guard lock(mutex_); - if (running_) { - this->Put(nullptr); - thread_.join(); - running_ = false; - } -} - -void -CPUBuilder::Put(const TaskPtr& task) { - { - std::lock_guard lock(queue_mutex_); - queue_.push(task); - } - queue_cv_.notify_one(); -} - -void -CPUBuilder::worker_function() { - SetThreadName("cpubuilder_thread"); - while (running_) { - std::unique_lock lock(queue_mutex_); - queue_cv_.wait(lock, [&] { return not queue_.empty(); }); - auto task = queue_.front(); - queue_.pop(); - lock.unlock(); - - if (task == nullptr) { - // thread exit - break; - } - task->Load(LoadType::DISK2CPU, 0); - task->Execute(); - } -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/CPUBuilder.h b/core/src/scheduler/CPUBuilder.h deleted file mode 100644 index 1c4a4f12f2..0000000000 --- a/core/src/scheduler/CPUBuilder.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include - -#include "task/Task.h" - -namespace milvus { -namespace scheduler { - -class CPUBuilder { - public: - CPUBuilder() = default; - - void - Start(); - - void - Stop(); - - void - Put(const TaskPtr& task); - - private: - void - worker_function(); - - private: - bool running_ = false; - std::mutex mutex_; - std::thread thread_; - - std::queue queue_; - std::condition_variable queue_cv_; - std::mutex queue_mutex_; -}; - -using CPUBuilderPtr = std::shared_ptr; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/CircleQueue.h b/core/src/scheduler/CircleQueue.h deleted file mode 100644 index 174ca0880e..0000000000 --- a/core/src/scheduler/CircleQueue.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace milvus { -namespace scheduler { - -template -class CircleQueue { - using value_type = T; - using atomic_size_type = std::atomic_ullong; - using size_type = uint64_t; - using const_reference = const value_type&; -#define MEMORY_ORDER (std::memory_order::memory_order_seq_cst) - - public: - explicit CircleQueue(size_type cap) : data_(cap, nullptr), capacity_(cap), front_() { - front_.store(cap - 1, MEMORY_ORDER); - } - - CircleQueue() = delete; - CircleQueue(const CircleQueue& q) = delete; - CircleQueue(CircleQueue&& q) = delete; - - public: - const_reference operator[](size_type n) { - return data_[n % capacity_]; - } - - size_type - front() { - return front_.load(MEMORY_ORDER); - } - - size_type - rear() { - return rear_; - } - - size_type - size() { - return size_; - } - - size_type - capacity() { - return capacity_; - } - - void - set_front(uint64_t last_finish) { - if (last_finish == rear_) { - throw; - } - front_.store(last_finish % capacity_, MEMORY_ORDER); - } - - void - put(const value_type& x) { - if ((rear_) % capacity_ == front_.load(MEMORY_ORDER)) { - throw; - } - data_[rear_] = x; - rear_ = ++rear_ % capacity_; - if (size_ < capacity_) { - ++size_; - } - } - - void - put(value_type&& x) { - if ((rear_) % capacity_ == front_.load(MEMORY_ORDER)) { - throw; - } - data_[rear_] = std::move(x); - rear_ = ++rear_ % capacity_; - if (size_ < capacity_) { - ++size_; - } - } - - private: - std::vector data_; - size_type capacity_; - atomic_size_type front_; - size_type rear_ = 0; - size_type size_ = 0; -#undef MEMORY_ORDER -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/Definition.h b/core/src/scheduler/Definition.h deleted file mode 100644 index 10526804c9..0000000000 --- a/core/src/scheduler/Definition.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "db/engine/EngineFactory.h" -#include "db/engine/ExecutionEngine.h" -#include "db/meta/MetaTypes.h" - -namespace milvus { -namespace scheduler { - -using SegmentSchemaPtr = engine::meta::SegmentSchemaPtr; -using SegmentSchema = engine::meta::SegmentSchema; - -using ExecutionEnginePtr = engine::ExecutionEnginePtr; -using EngineFactory = engine::EngineFactory; -using EngineType = engine::EngineType; -using MetricType = engine::MetricType; - -constexpr uint64_t TASK_TABLE_MAX_COUNT = 1ULL << 16ULL; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/JobMgr.cpp b/core/src/scheduler/JobMgr.cpp deleted file mode 100644 index 0856c749be..0000000000 --- a/core/src/scheduler/JobMgr.cpp +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/JobMgr.h" - -#include "src/db/Utils.h" -#include "src/segment/SegmentReader.h" - -#include -#include - -#include "SchedInst.h" -#include "TaskCreator.h" -#include "scheduler/Algorithm.h" -#include "scheduler/CPUBuilder.h" -#include "scheduler/tasklabel/SpecResLabel.h" -#include "selector/Optimizer.h" -#include "task/Task.h" - -namespace milvus { -namespace scheduler { - -JobMgr::JobMgr(ResourceMgrPtr res_mgr) : res_mgr_(std::move(res_mgr)) { -} - -void -JobMgr::Start() { - if (not running_) { - running_ = true; - worker_thread_ = std::thread(&JobMgr::worker_function, this); - } -} - -void -JobMgr::Stop() { - if (running_) { - this->Put(nullptr); - worker_thread_.join(); - running_ = false; - } -} - -json -JobMgr::Dump() const { - json ret{ - {"running", running_}, - {"event_queue_length", queue_.size()}, - }; - return ret; -} - -void -JobMgr::Put(const JobPtr& job) { - { - std::lock_guard lock(mutex_); - queue_.push(job); - } - cv_.notify_one(); -} - -void -JobMgr::worker_function() { - SetThreadName("jobmgr_thread"); - while (running_) { - std::unique_lock lock(mutex_); - cv_.wait(lock, [this] { return !queue_.empty(); }); - auto job = queue_.front(); - queue_.pop(); - lock.unlock(); - if (job == nullptr) { - break; - } - - auto tasks = build_task(job); - - // TODO(zhiru): if the job is search by ids, pass any task where the ids don't exist - auto search_job = std::dynamic_pointer_cast(job); - if (search_job != nullptr) { - search_job->GetResultIds().resize(search_job->nq(), -1); - search_job->GetResultDistances().resize(search_job->nq(), std::numeric_limits::max()); - - if (search_job->vectors().float_data_.empty() && search_job->vectors().binary_data_.empty() && - !search_job->vectors().id_array_.empty()) { - for (auto task = tasks.begin(); task != tasks.end();) { - auto search_task = std::static_pointer_cast(*task); - auto location = search_task->GetLocation(); - - // Load bloom filter - std::string segment_dir; - engine::utils::GetParentPath(location, segment_dir); - segment::SegmentReader segment_reader(segment_dir); - segment::IdBloomFilterPtr id_bloom_filter_ptr; - segment_reader.LoadBloomFilter(id_bloom_filter_ptr); - - // Check if the id is present. - bool pass = true; - for (auto& id : search_job->vectors().id_array_) { - if (id_bloom_filter_ptr->Check(id)) { - pass = false; - break; - } - } - - if (pass) { - // std::cout << search_task->GetIndexId() << std::endl; - search_job->SearchDone(search_task->GetIndexId()); - task = tasks.erase(task); - } else { - task++; - } - } - } - } - - // for (auto &task : tasks) { - // if ... - // search_job->SearchDone(task->id); - // tasks.erase(task); - // } - - for (auto& task : tasks) { - OptimizerInst::GetInstance()->Run(task); - } - - for (auto& task : tasks) { - calculate_path(res_mgr_, task); - } - - // disk resources NEVER be empty. - if (auto disk = res_mgr_->GetDiskResources()[0].lock()) { - // if (auto disk = res_mgr_->GetCpuResources()[0].lock()) { - for (auto& task : tasks) { - if (task->Type() == TaskType::BuildIndexTask && task->path().Last() == "cpu") { - CPUBuilderInst::GetInstance()->Put(task); - } else { - disk->task_table().Put(task, nullptr); - } - } - } - } -} - -std::vector -JobMgr::build_task(const JobPtr& job) { - return TaskCreator::Create(job); -} - -void -JobMgr::calculate_path(const ResourceMgrPtr& res_mgr, const TaskPtr& task) { - if (task->type_ != TaskType::SearchTask && task->type_ != TaskType::BuildIndexTask) { - return; - } - - if (task->label()->Type() != TaskLabelType::SPECIFIED_RESOURCE) { - return; - } - - std::vector path; - auto spec_label = std::static_pointer_cast(task->label()); - auto src = res_mgr->GetDiskResources()[0]; - auto dest = spec_label->resource(); - ShortestPath(src.lock(), dest.lock(), res_mgr, path); - task->path() = Path(path, path.size() - 1); -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/JobMgr.h b/core/src/scheduler/JobMgr.h deleted file mode 100644 index 0e9a21f8eb..0000000000 --- a/core/src/scheduler/JobMgr.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ResourceMgr.h" -#include "interface/interfaces.h" -#include "job/Job.h" -#include "task/Task.h" - -namespace milvus { -namespace scheduler { - -class JobMgr : public interface::dumpable { - public: - explicit JobMgr(ResourceMgrPtr res_mgr); - - void - Start(); - - void - Stop(); - - json - Dump() const override; - - public: - void - Put(const JobPtr& job); - - private: - void - worker_function(); - - static std::vector - build_task(const JobPtr& job); - - public: - static void - calculate_path(const ResourceMgrPtr& res_mgr, const TaskPtr& task); - - private: - bool running_ = false; - std::queue queue_; - - std::thread worker_thread_; - - std::mutex mutex_; - std::condition_variable cv_; - - ResourceMgrPtr res_mgr_ = nullptr; -}; - -using JobMgrPtr = std::shared_ptr; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/ResourceFactory.cpp b/core/src/scheduler/ResourceFactory.cpp deleted file mode 100644 index 789ecdb9b3..0000000000 --- a/core/src/scheduler/ResourceFactory.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/ResourceFactory.h" - -namespace milvus { -namespace scheduler { - -std::shared_ptr -ResourceFactory::Create(const std::string& name, const std::string& type, uint64_t device_id, bool enable_executor) { - if (type == "DISK") { - return std::make_shared(name, device_id, enable_executor); - } else if (type == "CPU") { - return std::make_shared(name, device_id, enable_executor); - } else if (type == "GPU") { - return std::make_shared(name, device_id, enable_executor); - } else { - return nullptr; - } -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/ResourceFactory.h b/core/src/scheduler/ResourceFactory.h deleted file mode 100644 index 7a3819a040..0000000000 --- a/core/src/scheduler/ResourceFactory.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -#include "resource/CpuResource.h" -#include "resource/DiskResource.h" -#include "resource/GpuResource.h" -#include "resource/Resource.h" - -namespace milvus { -namespace scheduler { - -class ResourceFactory { - public: - static std::shared_ptr - Create(const std::string& name, const std::string& type, uint64_t device_id, bool enable_executor = true); -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/ResourceMgr.cpp b/core/src/scheduler/ResourceMgr.cpp deleted file mode 100644 index ea5e3171d2..0000000000 --- a/core/src/scheduler/ResourceMgr.cpp +++ /dev/null @@ -1,259 +0,0 @@ - -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/ResourceMgr.h" -#include "utils/Log.h" - -namespace milvus { -namespace scheduler { - -void -ResourceMgr::Start() { - if (not check_resource_valid()) { - LOG_ENGINE_ERROR_ << "Resources invalid, cannot start ResourceMgr."; - LOG_ENGINE_ERROR_ << Dump(); - return; - } - - std::lock_guard lck(resources_mutex_); - for (auto& resource : resources_) { - resource->Start(); - } - running_ = true; - worker_thread_ = std::thread(&ResourceMgr::event_process, this); -} - -void -ResourceMgr::Stop() { - { - std::lock_guard lock(event_mutex_); - running_ = false; - queue_.push(nullptr); - event_cv_.notify_one(); - } - worker_thread_.join(); - - std::lock_guard lck(resources_mutex_); - for (auto& resource : resources_) { - resource->Stop(); - } -} - -ResourceWPtr -ResourceMgr::Add(ResourcePtr&& resource) { - ResourceWPtr ret(resource); - - std::lock_guard lck(resources_mutex_); - if (running_) { - LOG_ENGINE_ERROR_ << "ResourceMgr is running, not allow to add resource"; - return ret; - } - - resource->RegisterSubscriber(std::bind(&ResourceMgr::post_event, this, std::placeholders::_1)); - - switch (resource->type()) { - case ResourceType::DISK: { - disk_resources_.emplace_back(ResourceWPtr(resource)); - break; - } - case ResourceType::CPU: { - cpu_resources_.emplace_back(ResourceWPtr(resource)); - break; - } - case ResourceType::GPU: { - gpu_resources_.emplace_back(ResourceWPtr(resource)); - break; - } - default: { break; } - } - resources_.emplace_back(resource); - - return ret; -} - -bool -ResourceMgr::Connect(const std::string& name1, const std::string& name2, Connection& connection) { - auto res1 = GetResource(name1); - auto res2 = GetResource(name2); - if (res1 && res2) { - res1->AddNeighbour(std::static_pointer_cast(res2), connection); - // TODO(wxyu): enable when task balance supported - // res2->AddNeighbour(std::static_pointer_cast(res1), connection); - return true; - } - return false; -} - -void -ResourceMgr::Clear() { - std::lock_guard lck(resources_mutex_); - if (running_) { - LOG_ENGINE_ERROR_ << "ResourceMgr is running, cannot clear."; - return; - } - disk_resources_.clear(); - cpu_resources_.clear(); - gpu_resources_.clear(); - resources_.clear(); -} - -std::vector -ResourceMgr::GetComputeResources() { - std::vector result; - for (auto& resource : resources_) { - if (resource->HasExecutor()) { - result.emplace_back(resource); - } - } - return result; -} - -ResourcePtr -ResourceMgr::GetResource(ResourceType type, uint64_t device_id) { - for (auto& resource : resources_) { - if (resource->type() == type && resource->device_id() == device_id) { - return resource; - } - } - return nullptr; -} - -ResourcePtr -ResourceMgr::GetResource(const std::string& name) { - for (auto& resource : resources_) { - if (resource->name() == name) { - return resource; - } - } - return nullptr; -} - -uint64_t -ResourceMgr::GetNumOfResource() const { - return resources_.size(); -} - -uint64_t -ResourceMgr::GetNumOfComputeResource() const { - uint64_t count = 0; - for (auto& res : resources_) { - if (res->HasExecutor()) { - ++count; - } - } - return count; -} - -uint64_t -ResourceMgr::GetNumGpuResource() const { - uint64_t num = 0; - for (auto& res : resources_) { - if (res->type() == ResourceType::GPU) { - num++; - } - } - return num; -} - -json -ResourceMgr::Dump() const { - json resources{}; - for (auto& res : resources_) { - resources.push_back(res->Dump()); - } - json ret{ - {"number_of_resource", resources_.size()}, - {"number_of_disk_resource", disk_resources_.size()}, - {"number_of_cpu_resource", cpu_resources_.size()}, - {"number_of_gpu_resource", gpu_resources_.size()}, - {"resources", resources}, - }; - return ret; -} - -std::string -ResourceMgr::DumpTaskTables() { - std::stringstream ss; - ss << ">>>>>>>>>>>>>>>ResourceMgr::DumpTaskTable<<<<<<<<<<<<<<<" << std::endl; - for (auto& resource : resources_) { - ss << resource->name() << std::endl; - ss << resource->task_table().Dump().dump(); - ss << resource->name() << std::endl << std::endl; - } - return ss.str(); -} - -bool -ResourceMgr::check_resource_valid() { - { - // TODO: check one disk-resource, one cpu-resource, zero or more gpu-resource; - if (GetDiskResources().size() != 1) { - return false; - } - if (GetCpuResources().size() != 1) { - return false; - } - } - - { - // TODO: one compute-resource at least; - if (GetNumOfComputeResource() < 1) { - return false; - } - } - - { - // TODO: check disk only connect with cpu - } - - { - // TODO: check gpu only connect with cpu - } - - { - // TODO: check if exists isolated node - } - - return true; -} - -void -ResourceMgr::post_event(const EventPtr& event) { - { - std::lock_guard lock(event_mutex_); - queue_.emplace(event); - } - event_cv_.notify_one(); -} - -void -ResourceMgr::event_process() { - SetThreadName("resevt_thread"); - while (running_) { - std::unique_lock lock(event_mutex_); - event_cv_.wait(lock, [this] { return !queue_.empty(); }); - - auto event = queue_.front(); - queue_.pop(); - lock.unlock(); - if (event == nullptr) { - break; - } - - if (subscriber_) { - subscriber_(event); - } - } -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/ResourceMgr.h b/core/src/scheduler/ResourceMgr.h deleted file mode 100644 index 4055525ded..0000000000 --- a/core/src/scheduler/ResourceMgr.h +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "interface/interfaces.h" -#include "resource/Resource.h" -#include "utils/Log.h" - -namespace milvus { -namespace scheduler { - -class ResourceMgr : public interface::dumpable { - public: - ResourceMgr() = default; - - public: - /******** Management Interface ********/ - void - Start(); - - void - Stop(); - - ResourceWPtr - Add(ResourcePtr&& resource); - - bool - Connect(const std::string& name1, const std::string& name2, Connection& connection); - - void - Clear(); - - inline void - RegisterSubscriber(std::function subscriber) { - subscriber_ = std::move(subscriber); - } - - public: - /******** Management Interface ********/ - inline std::vector& - GetDiskResources() { - return disk_resources_; - } - - inline std::vector& - GetCpuResources() { - return cpu_resources_; - } - - inline std::vector& - GetGpuResources() { - return gpu_resources_; - } - - inline std::vector - GetAllResources() { - return resources_; - } - - std::vector - GetComputeResources(); - - ResourcePtr - GetResource(ResourceType type, uint64_t device_id); - - ResourcePtr - GetResource(const std::string& name); - - uint64_t - GetNumOfResource() const; - - uint64_t - GetNumOfComputeResource() const; - - uint64_t - GetNumGpuResource() const; - - public: - // TODO(wxyu): add stats interface(low) - - public: - /******** Utility Functions ********/ - json - Dump() const override; - - std::string - DumpTaskTables(); - - private: - bool - check_resource_valid(); - - void - post_event(const EventPtr& event); - - void - event_process(); - - private: - bool running_ = false; - - std::vector disk_resources_; - std::vector cpu_resources_; - std::vector gpu_resources_; - std::vector resources_; - mutable std::mutex resources_mutex_; - - std::queue queue_; - std::function subscriber_ = nullptr; - std::mutex event_mutex_; - std::condition_variable event_cv_; - - std::thread worker_thread_; -}; - -using ResourceMgrPtr = std::shared_ptr; -using ResourceMgrWPtr = std::weak_ptr; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/SchedInst.cpp b/core/src/scheduler/SchedInst.cpp deleted file mode 100644 index 7ccff67696..0000000000 --- a/core/src/scheduler/SchedInst.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/SchedInst.h" -#include "ResourceFactory.h" -#include "Utils.h" -#include "config/Config.h" - -#include -#include -#include -#include -#include - -namespace milvus { -namespace scheduler { - -ResourceMgrPtr ResMgrInst::instance = nullptr; -std::mutex ResMgrInst::mutex_; - -SchedulerPtr SchedInst::instance = nullptr; -std::mutex SchedInst::mutex_; - -scheduler::JobMgrPtr JobMgrInst::instance = nullptr; -std::mutex JobMgrInst::mutex_; - -OptimizerPtr OptimizerInst::instance = nullptr; -std::mutex OptimizerInst::mutex_; - -BuildMgrPtr BuildMgrInst::instance = nullptr; -std::mutex BuildMgrInst::mutex_; - -CPUBuilderPtr CPUBuilderInst::instance = nullptr; -std::mutex CPUBuilderInst::mutex_; - -void -load_simple_config() { - // create and connect - ResMgrInst::GetInstance()->Add(ResourceFactory::Create("disk", "DISK", 0, false)); - - auto io = Connection("io", 500); - ResMgrInst::GetInstance()->Add(ResourceFactory::Create("cpu", "CPU", 0)); - ResMgrInst::GetInstance()->Connect("disk", "cpu", io); - -// get resources -#ifdef MILVUS_GPU_VERSION - bool enable_gpu = false; - server::Config& config = server::Config::GetInstance(); - config.GetGpuResourceConfigEnable(enable_gpu); - if (enable_gpu) { - std::vector gpu_ids; - config.GetGpuResourceConfigSearchResources(gpu_ids); - std::vector build_gpu_ids; - config.GetGpuResourceConfigBuildIndexResources(build_gpu_ids); - auto pcie = Connection("pcie", 12000); - fiu_do_on("load_simple_config_mock", build_gpu_ids.push_back(1)); - - std::vector not_find_build_ids; - for (auto& build_id : build_gpu_ids) { - bool find_gpu_id = false; - for (auto& gpu_id : gpu_ids) { - if (gpu_id == build_id) { - find_gpu_id = true; - break; - } - } - if (not find_gpu_id) { - not_find_build_ids.emplace_back(build_id); - } - } - - for (auto& gpu_id : gpu_ids) { - ResMgrInst::GetInstance()->Add(ResourceFactory::Create(std::to_string(gpu_id), "GPU", gpu_id)); - ResMgrInst::GetInstance()->Connect("cpu", std::to_string(gpu_id), pcie); - } - - for (auto& not_find_id : not_find_build_ids) { - ResMgrInst::GetInstance()->Add(ResourceFactory::Create(std::to_string(not_find_id), "GPU", not_find_id)); - ResMgrInst::GetInstance()->Connect("cpu", std::to_string(not_find_id), pcie); - } - } -#endif -} - -void -StartSchedulerService() { - load_simple_config(); - OptimizerInst::GetInstance()->Init(); - ResMgrInst::GetInstance()->Start(); - SchedInst::GetInstance()->Start(); - JobMgrInst::GetInstance()->Start(); - CPUBuilderInst::GetInstance()->Start(); -} - -void -StopSchedulerService() { - CPUBuilderInst::GetInstance()->Stop(); - JobMgrInst::GetInstance()->Stop(); - SchedInst::GetInstance()->Stop(); - ResMgrInst::GetInstance()->Stop(); -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/SchedInst.h b/core/src/scheduler/SchedInst.h deleted file mode 100644 index eaf180e574..0000000000 --- a/core/src/scheduler/SchedInst.h +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "BuildMgr.h" -#include "CPUBuilder.h" -#include "JobMgr.h" -#include "ResourceMgr.h" -#include "Scheduler.h" -#include "Utils.h" -#include "selector/BuildIndexPass.h" -#include "selector/FaissFlatPass.h" -#include "selector/FaissIVFFlatPass.h" -#include "selector/FaissIVFPQPass.h" -#include "selector/FaissIVFSQ8HPass.h" -#include "selector/FaissIVFSQ8Pass.h" -#include "selector/FallbackPass.h" -#include "selector/Optimizer.h" - -#include -#include -#include -#include - -namespace milvus { -namespace scheduler { - -class ResMgrInst { - public: - static ResourceMgrPtr - GetInstance() { - if (instance == nullptr) { - std::lock_guard lock(mutex_); - if (instance == nullptr) { - instance = std::make_shared(); - } - } - return instance; - } - - private: - static ResourceMgrPtr instance; - static std::mutex mutex_; -}; - -class SchedInst { - public: - static SchedulerPtr - GetInstance() { - if (instance == nullptr) { - std::lock_guard lock(mutex_); - if (instance == nullptr) { - instance = std::make_shared(ResMgrInst::GetInstance()); - } - } - return instance; - } - - private: - static SchedulerPtr instance; - static std::mutex mutex_; -}; - -class JobMgrInst { - public: - static scheduler::JobMgrPtr - GetInstance() { - if (instance == nullptr) { - std::lock_guard lock(mutex_); - if (instance == nullptr) { - instance = std::make_shared(ResMgrInst::GetInstance()); - } - } - return instance; - } - - private: - static scheduler::JobMgrPtr instance; - static std::mutex mutex_; -}; - -class OptimizerInst { - public: - static OptimizerPtr - GetInstance() { - if (instance == nullptr) { - std::lock_guard lock(mutex_); - if (instance == nullptr) { - std::vector pass_list; -#ifdef MILVUS_GPU_VERSION - bool enable_gpu = false; - server::Config& config = server::Config::GetInstance(); - config.GetGpuResourceConfigEnable(enable_gpu); - if (enable_gpu) { - std::vector build_gpus; - std::vector search_gpus; - int64_t gpu_search_threshold; - config.GetGpuResourceConfigBuildIndexResources(build_gpus); - config.GetGpuResourceConfigSearchResources(search_gpus); - config.GetGpuResourceConfigGpuSearchThreshold(gpu_search_threshold); - std::string build_msg = "Build index gpu:"; - for (auto build_id : build_gpus) { - build_msg.append(" gpu" + std::to_string(build_id)); - } - LOG_SERVER_DEBUG_ << LogOut("[%s][%d] %s", "search", 0, build_msg.c_str()); - - std::string search_msg = "Search gpu:"; - for (auto search_id : search_gpus) { - search_msg.append(" gpu" + std::to_string(search_id)); - } - search_msg.append(". gpu_search_threshold:" + std::to_string(gpu_search_threshold)); - LOG_SERVER_DEBUG_ << LogOut("[%s][%d] %s", "search", 0, build_msg.c_str()); - - pass_list.push_back(std::make_shared()); - pass_list.push_back(std::make_shared()); - pass_list.push_back(std::make_shared()); - pass_list.push_back(std::make_shared()); - pass_list.push_back(std::make_shared()); - pass_list.push_back(std::make_shared()); - } -#endif - pass_list.push_back(std::make_shared()); - instance = std::make_shared(pass_list); - } - } - return instance; - } - - private: - static scheduler::OptimizerPtr instance; - static std::mutex mutex_; -}; - -class BuildMgrInst { - public: - static BuildMgrPtr - GetInstance() { - if (instance == nullptr) { - std::lock_guard lock(mutex_); - if (instance == nullptr) { - instance = std::make_shared(4); - } - } - return instance; - } - - private: - static BuildMgrPtr instance; - static std::mutex mutex_; -}; - -class CPUBuilderInst { - public: - static CPUBuilderPtr - GetInstance() { - if (instance == nullptr) { - std::lock_guard lock(mutex_); - if (instance == nullptr) { - instance = std::make_shared(); - } - } - return instance; - } - - private: - static CPUBuilderPtr instance; - static std::mutex mutex_; -}; - -void -StartSchedulerService(); - -void -StopSchedulerService(); - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/Scheduler.cpp b/core/src/scheduler/Scheduler.cpp deleted file mode 100644 index bc7c788490..0000000000 --- a/core/src/scheduler/Scheduler.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/Scheduler.h" -#include "Algorithm.h" -#include "action/Action.h" -#include "cache/GpuCacheMgr.h" -#include "event/LoadCompletedEvent.h" - -#include - -namespace milvus { -namespace scheduler { - -Scheduler::Scheduler(ResourceMgrPtr res_mgr) : running_(false), res_mgr_(std::move(res_mgr)) { - res_mgr_->RegisterSubscriber(std::bind(&Scheduler::PostEvent, this, std::placeholders::_1)); - event_register_.insert(std::make_pair(static_cast(EventType::START_UP), - std::bind(&Scheduler::OnStartUp, this, std::placeholders::_1))); - event_register_.insert(std::make_pair(static_cast(EventType::LOAD_COMPLETED), - std::bind(&Scheduler::OnLoadCompleted, this, std::placeholders::_1))); - event_register_.insert(std::make_pair(static_cast(EventType::TASK_TABLE_UPDATED), - std::bind(&Scheduler::OnTaskTableUpdated, this, std::placeholders::_1))); - event_register_.insert(std::make_pair(static_cast(EventType::FINISH_TASK), - std::bind(&Scheduler::OnFinishTask, this, std::placeholders::_1))); -} - -Scheduler::~Scheduler() { - res_mgr_ = nullptr; -} - -void -Scheduler::Start() { - running_ = true; - worker_thread_ = std::thread(&Scheduler::worker_function, this); -} - -void -Scheduler::Stop() { - { - std::lock_guard lock(event_mutex_); - running_ = false; - event_queue_.push(nullptr); - event_cv_.notify_one(); - } - worker_thread_.join(); -} - -void -Scheduler::PostEvent(const EventPtr& event) { - { - std::lock_guard lock(event_mutex_); - event_queue_.push(event); - } - event_cv_.notify_one(); -} - -json -Scheduler::Dump() const { - json ret{ - {"running", running_}, - {"event_queue_length", event_queue_.size()}, - }; - return ret; -} - -void -Scheduler::process(const EventPtr& event) { - auto process_event = event_register_.at(static_cast(event->Type())); - process_event(event); -} - -void -Scheduler::worker_function() { - SetThreadName("schedevt_thread"); - while (running_) { - std::unique_lock lock(event_mutex_); - event_cv_.wait(lock, [this] { return !event_queue_.empty(); }); - auto event = event_queue_.front(); - event_queue_.pop(); - if (event == nullptr) { - break; - } - - process(event); - } -} - -// TODO(wxyu): refactor the function -void -Scheduler::OnLoadCompleted(const EventPtr& event) { - auto load_completed_event = std::static_pointer_cast(event); - - auto resource = event->resource_; - resource->WakeupExecutor(); - - auto task_table_type = load_completed_event->task_table_item_->task->label()->Type(); - switch (task_table_type) { - case TaskLabelType::SPECIFIED_RESOURCE: { - Action::SpecifiedResourceLabelTaskScheduler(res_mgr_, resource, load_completed_event); - break; - } - case TaskLabelType::BROADCAST: { - if (resource->HasExecutor() == false) { - load_completed_event->task_table_item_->Move(); - } - Action::PushTaskToAllNeighbour(load_completed_event->task_table_item_, resource); - break; - } - default: { break; } - } - resource->WakeupLoader(); -} - -void -Scheduler::OnStartUp(const EventPtr& event) { - event->resource_->WakeupLoader(); -} - -void -Scheduler::OnFinishTask(const EventPtr& event) { - event->resource_->WakeupLoader(); -} - -void -Scheduler::OnTaskTableUpdated(const EventPtr& event) { - event->resource_->WakeupLoader(); -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/Scheduler.h b/core/src/scheduler/Scheduler.h deleted file mode 100644 index c68aaf1b36..0000000000 --- a/core/src/scheduler/Scheduler.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "ResourceMgr.h" -#include "interface/interfaces.h" -#include "resource/Resource.h" -#include "utils/Log.h" - -namespace milvus { -namespace scheduler { - -class Scheduler : public interface::dumpable { - public: - explicit Scheduler(ResourceMgrPtr res_mgr); - - ~Scheduler(); - - Scheduler(const Scheduler&) = delete; - Scheduler(Scheduler&&) = delete; - - void - Start(); - - void - Stop(); - - void - PostEvent(const EventPtr& event); - - json - Dump() const override; - - private: - void - OnStartUp(const EventPtr& event); - - void - OnFinishTask(const EventPtr& event); - - void - OnLoadCompleted(const EventPtr& event); - - void - OnTaskTableUpdated(const EventPtr& event); - - private: - void - process(const EventPtr& event); - - void - worker_function(); - - private: - bool running_; - - std::unordered_map> event_register_; - - ResourceMgrPtr res_mgr_; - std::queue event_queue_; - std::thread worker_thread_; - std::mutex event_mutex_; - std::condition_variable event_cv_; -}; - -using SchedulerPtr = std::shared_ptr; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/TaskCreator.cpp b/core/src/scheduler/TaskCreator.cpp deleted file mode 100644 index 7196af63ce..0000000000 --- a/core/src/scheduler/TaskCreator.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/TaskCreator.h" -#include "SchedInst.h" -#include "tasklabel/BroadcastLabel.h" -#include "tasklabel/SpecResLabel.h" - -namespace milvus { -namespace scheduler { - -std::vector -TaskCreator::Create(const JobPtr& job) { - switch (job->type()) { - case JobType::SEARCH: { - return Create(std::static_pointer_cast(job)); - } - case JobType::DELETE: { - return Create(std::static_pointer_cast(job)); - } - case JobType::BUILD: { - return Create(std::static_pointer_cast(job)); - } - default: { - // TODO(wxyu): error - return std::vector(); - } - } -} - -std::vector -TaskCreator::Create(const SearchJobPtr& job) { - std::vector tasks; - for (auto& index_file : job->index_files()) { - auto task = std::make_shared(job->GetContext(), index_file.second, nullptr); - task->job_ = job; - tasks.emplace_back(task); - } - - return tasks; -} - -std::vector -TaskCreator::Create(const DeleteJobPtr& job) { - std::vector tasks; - auto label = std::make_shared(); - auto task = std::make_shared(job, label); - task->job_ = job; - tasks.emplace_back(task); - - return tasks; -} - -std::vector -TaskCreator::Create(const BuildIndexJobPtr& job) { - std::vector tasks; - for (auto& to_index_file : job->to_index_files()) { - auto task = std::make_shared(to_index_file.second, nullptr); - task->job_ = job; - tasks.emplace_back(task); - } - return tasks; -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/TaskCreator.h b/core/src/scheduler/TaskCreator.h deleted file mode 100644 index a1dd8bb8a4..0000000000 --- a/core/src/scheduler/TaskCreator.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "job/DeleteJob.h" -#include "job/Job.h" -#include "job/SearchJob.h" -#include "task/BuildIndexTask.h" -#include "task/DeleteTask.h" -#include "task/SearchTask.h" -#include "task/Task.h" - -namespace milvus { -namespace scheduler { - -class TaskCreator { - public: - static std::vector - Create(const JobPtr& job); - - public: - static std::vector - Create(const SearchJobPtr& job); - - static std::vector - Create(const DeleteJobPtr& job); - - static std::vector - Create(const BuildIndexJobPtr& job); -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/TaskTable.cpp b/core/src/scheduler/TaskTable.cpp deleted file mode 100644 index 90996dc549..0000000000 --- a/core/src/scheduler/TaskTable.cpp +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/TaskTable.h" -#include "Utils.h" -#include "event/TaskTableUpdatedEvent.h" -#include "scheduler/SchedInst.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" - -#include -#include -#include - -namespace milvus { -namespace scheduler { - -std::string -ToString(TaskTableItemState state) { - switch (state) { - case TaskTableItemState::INVALID: - return "INVALID"; - case TaskTableItemState::START: - return "START"; - case TaskTableItemState::LOADING: - return "LOADING"; - case TaskTableItemState::LOADED: - return "LOADED"; - case TaskTableItemState::EXECUTING: - return "EXECUTING"; - case TaskTableItemState::EXECUTED: - return "EXECUTED"; - case TaskTableItemState::MOVING: - return "MOVING"; - case TaskTableItemState::MOVED: - return "MOVED"; - default: - return ""; - } -} - -json -TaskTimestamp::Dump() const { - json ret{ - {"start", start}, {"load", load}, {"loaded", loaded}, {"execute", execute}, - {"executed", executed}, {"move", move}, {"moved", moved}, {"finish", finish}, - }; - return ret; -} - -bool -TaskTableItem::IsFinish() { - return state == TaskTableItemState::MOVED || state == TaskTableItemState::EXECUTED; -} - -bool -TaskTableItem::Load() { - std::unique_lock lock(mutex); - if (state == TaskTableItemState::START) { - state = TaskTableItemState::LOADING; - lock.unlock(); - timestamp.load = get_current_timestamp(); - return true; - } - return false; -} - -bool -TaskTableItem::Loaded() { - std::unique_lock lock(mutex); - if (state == TaskTableItemState::LOADING) { - state = TaskTableItemState::LOADED; - lock.unlock(); - timestamp.loaded = get_current_timestamp(); - return true; - } - return false; -} - -bool -TaskTableItem::Execute() { - std::unique_lock lock(mutex); - if (state == TaskTableItemState::LOADED) { - state = TaskTableItemState::EXECUTING; - lock.unlock(); - timestamp.execute = get_current_timestamp(); - return true; - } - return false; -} - -bool -TaskTableItem::Executed() { - std::unique_lock lock(mutex); - if (state == TaskTableItemState::EXECUTING) { - state = TaskTableItemState::EXECUTED; - lock.unlock(); - timestamp.executed = get_current_timestamp(); - timestamp.finish = get_current_timestamp(); - return true; - } - return false; -} - -bool -TaskTableItem::Move() { - std::unique_lock lock(mutex); - if (state == TaskTableItemState::LOADED) { - state = TaskTableItemState::MOVING; - lock.unlock(); - timestamp.move = get_current_timestamp(); - return true; - } - return false; -} - -bool -TaskTableItem::Moved() { - std::unique_lock lock(mutex); - if (state == TaskTableItemState::MOVING) { - state = TaskTableItemState::MOVED; - lock.unlock(); - timestamp.moved = get_current_timestamp(); - timestamp.finish = get_current_timestamp(); - return true; - } - return false; -} - -json -TaskTableItem::Dump() const { - json ret{ - {"id", id}, - {"task", (int64_t)task.get()}, - {"state", ToString(state)}, - {"timestamp", timestamp.Dump()}, - }; - return ret; -} - -std::vector -TaskTable::PickToLoad(uint64_t limit) { -#if 1 - // TimeRecorder rc(""); - std::vector indexes; - bool cross = false; - - uint64_t available_begin = table_.front() + 1; - for (uint64_t i = 0, loaded_count = 0, pick_count = 0; i < table_.size() && pick_count < limit; ++i) { - auto index = available_begin + i; - if (not table_[index]) - break; - if (index % table_.capacity() == table_.rear()) - break; - if (not cross && table_[index]->IsFinish()) { - table_.set_front(index); - } else if (table_[index]->state == TaskTableItemState::LOADED) { - cross = true; - ++loaded_count; - if (loaded_count > 2) - return std::vector(); - } else if (table_[index]->state == TaskTableItemState::START) { - auto task = table_[index]->task; - - // if task is a build index task, limit it - if (task->Type() == TaskType::BuildIndexTask && task->path().Current() == "cpu") { - if (BuildMgrInst::GetInstance()->NumOfAvailable() < 1) { - LOG_SERVER_WARNING_ << "BuildMgr doesnot have available place for building index"; - continue; - } - } - cross = true; - indexes.push_back(index); - ++pick_count; - } - } - // rc.ElapseFromBegin("PickToLoad "); - return indexes; -#else - size_t count = 0; - for (uint64_t j = last_finish_ + 1; j < table_.size(); ++j) { - if (not table_[j]) { - LOG_SERVER_WARNING_ << "collection[" << j << "] is nullptr"; - } - - if (table_[j]->task->path().Current() == "cpu") { - if (table_[j]->task->Type() == TaskType::BuildIndexTask && BuildMgrInst::GetInstance()->numoftasks() < 1) { - return std::vector(); - } - } - - if (table_[j]->state == TaskTableItemState::LOADED) { - ++count; - if (count > 2) - return std::vector(); - } - } - - std::vector indexes; - bool cross = false; - for (uint64_t i = last_finish_ + 1, count = 0; i < table_.size() && count < limit; ++i) { - if (not cross && table_[i]->IsFinish()) { - last_finish_ = i; - } else if (table_[i]->state == TaskTableItemState::START) { - auto task = table_[i]->task; - if (task->Type() == TaskType::BuildIndexTask && task->path().Current() == "cpu") { - if (BuildMgrInst::GetInstance()->numoftasks() == 0) { - break; - } else { - cross = true; - indexes.push_back(i); - ++count; - BuildMgrInst::GetInstance()->take(); - } - } else { - cross = true; - indexes.push_back(i); - ++count; - } - } - } - return indexes; -#endif -} - -std::vector -TaskTable::PickToExecute(uint64_t limit) { - // TimeRecorder rc(""); - std::vector indexes; - bool cross = false; - uint64_t available_begin = table_.front() + 1; - for (uint64_t i = 0, pick_count = 0; i < table_.size() && pick_count < limit; ++i) { - uint64_t index = available_begin + i; - if (not table_[index]) { - break; - } - if (index % table_.capacity() == table_.rear()) { - break; - } - - if (not cross && table_[index]->IsFinish()) { - table_.set_front(index); - } else if (table_[index]->state == TaskTableItemState::LOADED) { - cross = true; - indexes.push_back(index); - ++pick_count; - } - } - // rc.ElapseFromBegin("PickToExecute "); - return indexes; -} - -void -TaskTable::Put(TaskPtr task, TaskTableItemPtr from) { - auto item = std::make_shared(std::move(from)); - item->id = id_++; - item->task = std::move(task); - item->state = TaskTableItemState::START; - item->timestamp.start = get_current_timestamp(); - table_.put(std::move(item)); - if (subscriber_) { - subscriber_(); - } -} - -size_t -TaskTable::TaskToExecute() { - size_t count = 0; - auto begin = table_.front() + 1; - for (size_t i = 0; i < table_.size(); ++i) { - auto index = begin + i; - if (table_[index] && table_[index]->state == TaskTableItemState::LOADED) { - ++count; - } - } - return count; -} - -json -TaskTable::Dump() const { - json ret{{"error.message", "not support yet."}}; - return ret; -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/TaskTable.h b/core/src/scheduler/TaskTable.h deleted file mode 100644 index 43f2b55af3..0000000000 --- a/core/src/scheduler/TaskTable.h +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "CircleQueue.h" -#include "event/Event.h" -#include "interface/interfaces.h" -#include "task/SearchTask.h" - -namespace milvus { -namespace scheduler { - -enum class TaskTableItemState { - INVALID, - START, // idle - LOADING, // loading data from other resource - LOADED, // ready to exec or move - EXECUTING, // executing, locking util executed or failed - EXECUTED, // executed, termination state - MOVING, // moving to another resource, locking util executed or failed - MOVED, // moved, termination state -}; - -struct TaskTimestamp : public interface::dumpable { - uint64_t start = 0; - uint64_t move = 0; - uint64_t moved = 0; - uint64_t load = 0; - uint64_t loaded = 0; - uint64_t execute = 0; - uint64_t executed = 0; - uint64_t finish = 0; - - json - Dump() const override; -}; - -struct TaskTableItem; -using TaskTableItemPtr = std::shared_ptr; - -struct TaskTableItem : public interface::dumpable { - explicit TaskTableItem(TaskTableItemPtr f = nullptr) - : id(0), task(nullptr), state(TaskTableItemState::INVALID), mutex(), from(std::move(f)) { - } - - TaskTableItem(const TaskTableItem& src) = delete; - TaskTableItem(TaskTableItem&&) = delete; - - uint64_t id; // auto increment from 0; - TaskPtr task; // the task; - TaskTableItemState state; // the state; - std::mutex mutex; - TaskTimestamp timestamp; - TaskTableItemPtr from; - - bool - IsFinish(); - - bool - Load(); - - bool - Loaded(); - - bool - Execute(); - - bool - Executed(); - - bool - Move(); - - bool - Moved(); - - json - Dump() const override; -}; - -class TaskTable : public interface::dumpable { - public: - TaskTable() : table_(TASK_TABLE_MAX_COUNT) { - } - - TaskTable(const TaskTable&) = delete; - TaskTable(TaskTable&&) = delete; - - public: - json - Dump() const override; - - public: - inline void - RegisterSubscriber(std::function subscriber) { - subscriber_ = std::move(subscriber); - } - - void - Put(TaskPtr task, TaskTableItemPtr from = nullptr); - - size_t - TaskToExecute(); - - std::vector - PickToLoad(uint64_t limit); - - std::vector - PickToExecute(uint64_t limit); - - public: - inline const TaskTableItemPtr& operator[](uint64_t index) { - return table_[index]; - } - - inline const TaskTableItemPtr& - at(uint64_t index) { - return table_[index]; - } - - inline size_t - capacity() { - return table_.capacity(); - } - - inline size_t - size() { - return table_.size(); - } - - public: - /******** Action ********/ - - // TODO(wxyu): bool to Status - /* - * Load a task; - * Set state loading; - * Called by loader; - */ - inline bool - Load(uint64_t index) { - return table_[index]->Load(); - } - - /* - * Load task finished; - * Set state loaded; - * Called by loader; - */ - inline bool - Loaded(uint64_t index) { - return table_[index]->Loaded(); - } - - /* - * Execute a task; - * Set state executing; - * Called by executor; - */ - inline bool - Execute(uint64_t index) { - return table_[index]->Execute(); - } - - /* - * Execute task finished; - * Set state executed; - * Called by executor; - */ - inline bool - Executed(uint64_t index) { - return table_[index]->Executed(); - } - - /* - * Move a task; - * Set state moving; - * Called by scheduler; - */ - - inline bool - Move(uint64_t index) { - return table_[index]->Move(); - } - - /* - * Move task finished; - * Set state moved; - * Called by scheduler; - */ - inline bool - Moved(uint64_t index) { - return table_[index]->Moved(); - } - - private: - std::uint64_t id_ = 0; - CircleQueue table_; - std::function subscriber_ = nullptr; - - // cache last finish avoid Pick task from begin always - // pick from (last_finish_ + 1) - // init with -1, pick from (last_finish_ + 1) = 0 - uint64_t last_finish_ = -1; -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/Utils.cpp b/core/src/scheduler/Utils.cpp deleted file mode 100644 index c145164d12..0000000000 --- a/core/src/scheduler/Utils.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/Utils.h" - -#ifdef MILVUS_GPU_VERSION -#include -#endif -#include -#include -#include - -namespace milvus { -namespace scheduler { - -uint64_t -get_current_timestamp() { - std::chrono::time_point now = std::chrono::system_clock::now(); - auto duration = now.time_since_epoch(); - auto millis = std::chrono::duration_cast(duration).count(); - return millis; -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/Utils.h b/core/src/scheduler/Utils.h deleted file mode 100644 index 876267047f..0000000000 --- a/core/src/scheduler/Utils.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include - -namespace milvus { -namespace scheduler { - -uint64_t -get_current_timestamp(); - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/action/Action.h b/core/src/scheduler/action/Action.h deleted file mode 100644 index d3f6270996..0000000000 --- a/core/src/scheduler/action/Action.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "scheduler/ResourceMgr.h" -#include "scheduler/resource/Resource.h" - -#include - -namespace milvus { -namespace scheduler { - -class Action { - public: - static void - PushTaskToNeighbourRandomly(TaskTableItemPtr task_item, const ResourcePtr& self); - - static void - PushTaskToAllNeighbour(TaskTableItemPtr task_item, const ResourcePtr& self); - - static void - PushTaskToResource(TaskTableItemPtr task_item, const ResourcePtr& dest); - - static void - SpecifiedResourceLabelTaskScheduler(const ResourceMgrPtr& res_mgr, ResourcePtr resource, - std::shared_ptr event); -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/action/PushTaskToNeighbour.cpp b/core/src/scheduler/action/PushTaskToNeighbour.cpp deleted file mode 100644 index 86a8b5872a..0000000000 --- a/core/src/scheduler/action/PushTaskToNeighbour.cpp +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include -#include "cache/GpuCacheMgr.h" -#include "scheduler/Algorithm.h" -#include "scheduler/action/Action.h" -#include "scheduler/tasklabel/SpecResLabel.h" - -namespace milvus { -namespace scheduler { - -std::vector -get_neighbours(const ResourcePtr& self) { - std::vector neighbours; - for (auto& neighbour_node : self->GetNeighbours()) { - auto node = neighbour_node.neighbour_node; - if (not node) - continue; - - auto resource = std::static_pointer_cast(node); - // if (not resource->HasExecutor()) continue; - - neighbours.emplace_back(resource); - } - return neighbours; -} - -// This function has not been invoked, comment it for code coverage -#if 0 -std::vector> -get_neighbours_with_connetion(const ResourcePtr& self) { - std::vector> neighbours; - for (auto& neighbour_node : self->GetNeighbours()) { - auto node = neighbour_node.neighbour_node; - if (not node) - continue; - - auto resource = std::static_pointer_cast(node); - // if (not resource->HasExecutor()) continue; - Connection conn = neighbour_node.connection; - neighbours.emplace_back(std::make_pair(resource, conn)); - } - return neighbours; -} - -void -Action::PushTaskToNeighbourRandomly(TaskTableItemPtr task_item, const ResourcePtr& self) { - auto neighbours = get_neighbours_with_connetion(self); - if (not neighbours.empty()) { - std::vector speeds; - uint64_t total_speed = 0; - for (auto& neighbour : neighbours) { - uint64_t speed = neighbour.second.speed(); - speeds.emplace_back(speed); - total_speed += speed; - } - - unsigned seed1 = std::chrono::system_clock::now().time_since_epoch().count(); - std::mt19937 mt(seed1); - std::uniform_int_distribution dist(0, total_speed); - uint64_t index = 0; - int64_t rd_speed = dist(mt); - for (uint64_t i = 0; i < speeds.size(); ++i) { - rd_speed -= speeds[i]; - if (rd_speed <= 0) { - neighbours[i].first->task_table().Put(task_item->task, task_item); - return; - } - } - - } else { - // TODO(wxyu): process - } -} -#endif - -void -Action::PushTaskToAllNeighbour(TaskTableItemPtr task_item, const ResourcePtr& self) { - auto neighbours = get_neighbours(self); - for (auto& neighbour : neighbours) { - neighbour->task_table().Put(task_item->task, task_item); - } -} - -#if 0 -void -Action::PushTaskToResource(TaskTableItemPtr task_item, const ResourcePtr& dest) { - dest->task_table().Put(task_item->task, task_item); -} -#endif - -void -Action::SpecifiedResourceLabelTaskScheduler(const ResourceMgrPtr& res_mgr, ResourcePtr resource, - std::shared_ptr event) { - auto task_item = event->task_table_item_; - auto task = event->task_table_item_->task; - - if (resource->name() == task->path().Last()) { - resource->WakeupExecutor(); - } else { - auto next_res_name = task->path().Next(); - auto next_res = res_mgr->GetResource(next_res_name); - // if (event->task_table_item_->Move()) { - // next_res->task_table().Put(task); - // } - event->task_table_item_->Move(); - next_res->task_table().Put(task, task_item); - } -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/event/Event.h b/core/src/scheduler/event/Event.h deleted file mode 100644 index c7231f6d83..0000000000 --- a/core/src/scheduler/event/Event.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include - -namespace milvus { -namespace scheduler { - -enum class EventType { START_UP, LOAD_COMPLETED, FINISH_TASK, TASK_TABLE_UPDATED }; - -class Resource; - -class Event { - public: - explicit Event(EventType type, std::shared_ptr resource) : type_(type), resource_(std::move(resource)) { - } - - inline EventType - Type() const { - return type_; - } - - virtual std::string - Dump() const = 0; - - friend std::ostream& - operator<<(std::ostream& out, const Event& event); - - public: - EventType type_; - std::shared_ptr resource_; -}; - -using EventPtr = std::shared_ptr; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/event/EventDump.cpp b/core/src/scheduler/event/EventDump.cpp deleted file mode 100644 index 21391b04ea..0000000000 --- a/core/src/scheduler/event/EventDump.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "Event.h" -#include "FinishTaskEvent.h" -#include "LoadCompletedEvent.h" -#include "StartUpEvent.h" -#include "TaskTableUpdatedEvent.h" - -namespace milvus { -namespace scheduler { - -std::ostream& -operator<<(std::ostream& out, const Event& event) { - out << event.Dump(); - return out; -} - -std::ostream& -operator<<(std::ostream& out, const StartUpEvent& event) { - out << event.Dump(); - return out; -} - -std::ostream& -operator<<(std::ostream& out, const LoadCompletedEvent& event) { - out << event.Dump(); - return out; -} - -std::ostream& -operator<<(std::ostream& out, const FinishTaskEvent& event) { - out << event.Dump(); - return out; -} - -std::ostream& -operator<<(std::ostream& out, const TaskTableUpdatedEvent& event) { - out << event.Dump(); - return out; -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/event/FinishTaskEvent.h b/core/src/scheduler/event/FinishTaskEvent.h deleted file mode 100644 index d9d78874b8..0000000000 --- a/core/src/scheduler/event/FinishTaskEvent.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "scheduler/TaskTable.h" -#include "scheduler/event/Event.h" - -#include -#include -#include - -namespace milvus { -namespace scheduler { - -class FinishTaskEvent : public Event { - public: - FinishTaskEvent(std::shared_ptr resource, TaskTableItemPtr task_table_item) - : Event(EventType::FINISH_TASK, std::move(resource)), task_table_item_(std::move(task_table_item)) { - } - - inline std::string - Dump() const override { - return ""; - } - - friend std::ostream& - operator<<(std::ostream& out, const FinishTaskEvent& event); - - public: - TaskTableItemPtr task_table_item_; -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/event/LoadCompletedEvent.h b/core/src/scheduler/event/LoadCompletedEvent.h deleted file mode 100644 index 3e488bd449..0000000000 --- a/core/src/scheduler/event/LoadCompletedEvent.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "scheduler/TaskTable.h" -#include "scheduler/event/Event.h" - -#include -#include -#include - -namespace milvus { -namespace scheduler { - -class LoadCompletedEvent : public Event { - public: - LoadCompletedEvent(std::shared_ptr resource, TaskTableItemPtr task_table_item) - : Event(EventType::LOAD_COMPLETED, std::move(resource)), task_table_item_(std::move(task_table_item)) { - } - - inline std::string - Dump() const override { - return ""; - } - - friend std::ostream& - operator<<(std::ostream& out, const LoadCompletedEvent& event); - - public: - TaskTableItemPtr task_table_item_; -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/event/StartUpEvent.h b/core/src/scheduler/event/StartUpEvent.h deleted file mode 100644 index b156f72bb4..0000000000 --- a/core/src/scheduler/event/StartUpEvent.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "scheduler/event/Event.h" - -#include -#include -#include - -namespace milvus { -namespace scheduler { - -class StartUpEvent : public Event { - public: - explicit StartUpEvent(std::shared_ptr resource) : Event(EventType::START_UP, std::move(resource)) { - } - - inline std::string - Dump() const override { - return ""; - } - - friend std::ostream& - operator<<(std::ostream& out, const StartUpEvent& event); -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/event/TaskTableUpdatedEvent.h b/core/src/scheduler/event/TaskTableUpdatedEvent.h deleted file mode 100644 index afa411ce35..0000000000 --- a/core/src/scheduler/event/TaskTableUpdatedEvent.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "Event.h" - -#include -#include -#include - -namespace milvus { -namespace scheduler { - -class TaskTableUpdatedEvent : public Event { - public: - explicit TaskTableUpdatedEvent(std::shared_ptr resource) - : Event(EventType::TASK_TABLE_UPDATED, std::move(resource)) { - } - - inline std::string - Dump() const override { - return ""; - } - - friend std::ostream& - operator<<(std::ostream& out, const TaskTableUpdatedEvent& event); -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/interface/interfaces.h b/core/src/scheduler/interface/interfaces.h deleted file mode 100644 index b65d6461d3..0000000000 --- a/core/src/scheduler/interface/interfaces.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "utils/Json.h" - -namespace milvus { -namespace interface { - -struct dumpable { - virtual ~dumpable() = default; - - virtual json - Dump() const = 0; -}; - -} // namespace interface -} // namespace milvus diff --git a/core/src/scheduler/job/BuildIndexJob.cpp b/core/src/scheduler/job/BuildIndexJob.cpp deleted file mode 100644 index 80a7ff8d16..0000000000 --- a/core/src/scheduler/job/BuildIndexJob.cpp +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/job/BuildIndexJob.h" - -#include - -#include "utils/Log.h" - -namespace milvus { -namespace scheduler { - -BuildIndexJob::BuildIndexJob(engine::meta::MetaPtr meta_ptr, engine::DBOptions options) - : Job(JobType::BUILD), meta_ptr_(std::move(meta_ptr)), options_(std::move(options)) { - SetIdentity("BuildIndexJob"); - AddCacheInsertDataListener(); -} - -bool -BuildIndexJob::AddToIndexFiles(const engine::meta::SegmentSchemaPtr& to_index_file) { - std::unique_lock lock(mutex_); - if (to_index_file == nullptr || to_index_files_.find(to_index_file->id_) != to_index_files_.end()) { - return false; - } - - LOG_SERVER_DEBUG_ << "BuildIndexJob " << id() << " add to_index file: " << to_index_file->id_ - << ", location: " << to_index_file->location_; - - to_index_files_[to_index_file->id_] = to_index_file; - return true; -} - -void -BuildIndexJob::WaitBuildIndexFinish() { - std::unique_lock lock(mutex_); - cv_.wait(lock, [this] { return to_index_files_.empty(); }); - LOG_SERVER_DEBUG_ << "BuildIndexJob " << id() << " all done"; -} - -void -BuildIndexJob::BuildIndexDone(size_t to_index_id) { - std::unique_lock lock(mutex_); - to_index_files_.erase(to_index_id); - cv_.notify_all(); - LOG_SERVER_DEBUG_ << "BuildIndexJob " << id() << " finish index file: " << to_index_id; -} - -json -BuildIndexJob::Dump() const { - json ret{ - {"number_of_to_index_file", to_index_files_.size()}, - }; - auto base = Job::Dump(); - ret.insert(base.begin(), base.end()); - return ret; -} - -void -BuildIndexJob::OnCacheInsertDataChanged(bool value) { - options_.insert_cache_immediately_ = value; -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/job/BuildIndexJob.h b/core/src/scheduler/job/BuildIndexJob.h deleted file mode 100644 index cd8b802dea..0000000000 --- a/core/src/scheduler/job/BuildIndexJob.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config/handler/CacheConfigHandler.h" -#include "db/meta/Meta.h" -#include "scheduler/Definition.h" -#include "scheduler/job/Job.h" - -namespace milvus { -namespace scheduler { - -using engine::meta::SegmentSchemaPtr; - -using Id2ToIndexMap = std::unordered_map; -using Id2ToTableFileMap = std::unordered_map; - -class BuildIndexJob : public Job, public server::CacheConfigHandler { - public: - explicit BuildIndexJob(engine::meta::MetaPtr meta_ptr, engine::DBOptions options); - - ~BuildIndexJob() = default; - - public: - bool - AddToIndexFiles(const SegmentSchemaPtr& to_index_file); - - void - WaitBuildIndexFinish(); - - void - BuildIndexDone(size_t to_index_id); - - json - Dump() const override; - - public: - Status& - GetStatus() { - return status_; - } - - Id2ToIndexMap& - to_index_files() { - return to_index_files_; - } - - engine::meta::MetaPtr - meta() const { - return meta_ptr_; - } - - engine::DBOptions - options() const { - return options_; - } - - protected: - void - OnCacheInsertDataChanged(bool value) override; - - private: - Id2ToIndexMap to_index_files_; - engine::meta::MetaPtr meta_ptr_; - engine::DBOptions options_; - - Status status_; - std::mutex mutex_; - std::condition_variable cv_; -}; - -using BuildIndexJobPtr = std::shared_ptr; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/job/DeleteJob.cpp b/core/src/scheduler/job/DeleteJob.cpp deleted file mode 100644 index 2a462cbf79..0000000000 --- a/core/src/scheduler/job/DeleteJob.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/job/DeleteJob.h" - -#include - -namespace milvus { -namespace scheduler { - -DeleteJob::DeleteJob(std::string collection_id, engine::meta::MetaPtr meta_ptr, uint64_t num_resource) - : Job(JobType::DELETE), - collection_id_(std::move(collection_id)), - meta_ptr_(std::move(meta_ptr)), - num_resource_(num_resource) { -} - -void -DeleteJob::WaitAndDelete() { - std::unique_lock lock(mutex_); - cv_.wait(lock, [&] { return done_resource == num_resource_; }); - meta_ptr_->DeleteCollectionFiles({collection_id_}); -} - -void -DeleteJob::ResourceDone() { - { - std::lock_guard lock(mutex_); - ++done_resource; - } - cv_.notify_one(); -} - -json -DeleteJob::Dump() const { - json ret{ - {"collection_id", collection_id_}, - {"number_of_resource", num_resource_}, - {"number_of_done", done_resource}, - }; - auto base = Job::Dump(); - ret.insert(base.begin(), base.end()); - return ret; -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/job/DeleteJob.h b/core/src/scheduler/job/DeleteJob.h deleted file mode 100644 index c1e96a221e..0000000000 --- a/core/src/scheduler/job/DeleteJob.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Job.h" -#include "db/meta/Meta.h" - -namespace milvus { -namespace scheduler { - -class DeleteJob : public Job { - public: - DeleteJob(std::string collection_id, engine::meta::MetaPtr meta_ptr, uint64_t num_resource); - - public: - void - WaitAndDelete(); - - void - ResourceDone(); - - json - Dump() const override; - - public: - std::string - collection_id() const { - return collection_id_; - } - - engine::meta::MetaPtr - meta() const { - return meta_ptr_; - } - - private: - std::string collection_id_; - engine::meta::MetaPtr meta_ptr_; - - uint64_t num_resource_ = 0; - uint64_t done_resource = 0; - std::mutex mutex_; - std::condition_variable cv_; -}; - -using DeleteJobPtr = std::shared_ptr; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/job/Job.cpp b/core/src/scheduler/job/Job.cpp deleted file mode 100644 index 43aa1b93b8..0000000000 --- a/core/src/scheduler/job/Job.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/job/Job.h" - -namespace milvus { -namespace scheduler { - -namespace { -std::mutex unique_job_mutex; -uint64_t unique_job_id = 0; -} // namespace - -Job::Job(JobType type) : type_(type) { - std::lock_guard lock(unique_job_mutex); - id_ = unique_job_id++; -} - -json -Job::Dump() const { - json ret{ - {"id", id_}, - {"type", type_}, - }; - return ret; -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/job/Job.h b/core/src/scheduler/job/Job.h deleted file mode 100644 index d350928bf3..0000000000 --- a/core/src/scheduler/job/Job.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "scheduler/interface/interfaces.h" - -#include "server/context/Context.h" - -namespace milvus { -namespace scheduler { - -enum class JobType { - INVALID, - SEARCH, - DELETE, - BUILD, -}; - -using JobId = std::uint64_t; - -class Job : public interface::dumpable { - public: - inline JobId - id() const { - return id_; - } - - inline JobType - type() const { - return type_; - } - - json - Dump() const override; - - protected: - explicit Job(JobType type); - - private: - JobId id_ = 0; - JobType type_; -}; - -using JobPtr = std::shared_ptr; -using JobWPtr = std::weak_ptr; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/job/SearchJob.cpp b/core/src/scheduler/job/SearchJob.cpp deleted file mode 100644 index 78d2280674..0000000000 --- a/core/src/scheduler/job/SearchJob.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/job/SearchJob.h" - -#include "utils/Log.h" - -namespace milvus { -namespace scheduler { - -SearchJob::SearchJob(const std::shared_ptr& context, uint64_t topk, const milvus::json& extra_params, - const engine::VectorsData& vectors) - : Job(JobType::SEARCH), context_(context), topk_(topk), extra_params_(extra_params), vectors_(vectors) { -} - -SearchJob::SearchJob(const std::shared_ptr& context, milvus::query::GeneralQueryPtr general_query, - std::unordered_map& attr_type, - const engine::VectorsData& vectors) - : Job(JobType::SEARCH), context_(context), vectors_(vectors), general_query_(general_query), attr_type_(attr_type) { -} - -bool -SearchJob::AddIndexFile(const SegmentSchemaPtr& index_file) { - std::unique_lock lock(mutex_); - if (index_file == nullptr || index_files_.find(index_file->id_) != index_files_.end()) { - return false; - } - - LOG_SERVER_DEBUG_ << LogOut("[%s][%ld] SearchJob %ld add index file: %ld", "search", 0, id(), index_file->id_); - - index_files_[index_file->id_] = index_file; - return true; -} - -void -SearchJob::WaitResult() { - std::unique_lock lock(mutex_); - cv_.wait(lock, [this] { return index_files_.empty(); }); - LOG_SERVER_DEBUG_ << LogOut("[%s][%ld] SearchJob %ld all done", "search", 0, id()); -} - -void -SearchJob::SearchDone(size_t index_id) { - std::unique_lock lock(mutex_); - index_files_.erase(index_id); - if (index_files_.empty()) { - cv_.notify_all(); - } - - LOG_SERVER_DEBUG_ << LogOut("[%s][%ld] SearchJob %ld finish index file: %ld", "search", 0, id(), index_id); -} - -ResultIds& -SearchJob::GetResultIds() { - return result_ids_; -} - -ResultDistances& -SearchJob::GetResultDistances() { - return result_distances_; -} - -Status& -SearchJob::GetStatus() { - return status_; -} - -json -SearchJob::Dump() const { - json ret{ - {"topk", topk_}, - {"nq", vectors_.vector_count_}, - {"extra_params", extra_params_.dump()}, - }; - auto base = Job::Dump(); - ret.insert(base.begin(), base.end()); - return ret; -} - -const std::shared_ptr& -SearchJob::GetContext() const { - return context_; -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/job/SearchJob.h b/core/src/scheduler/job/SearchJob.h deleted file mode 100644 index 429acd1840..0000000000 --- a/core/src/scheduler/job/SearchJob.h +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Job.h" -#include "db/Types.h" -#include "db/meta/MetaTypes.h" - -#include "query/GeneralQuery.h" - -#include "server/context/Context.h" - -namespace milvus { -namespace scheduler { - -using engine::meta::SegmentSchemaPtr; - -using Id2IndexMap = std::unordered_map; - -using ResultIds = engine::ResultIds; -using ResultDistances = engine::ResultDistances; - -class SearchJob : public Job { - public: - SearchJob(const std::shared_ptr& context, uint64_t topk, const milvus::json& extra_params, - const engine::VectorsData& vectors); - - SearchJob(const std::shared_ptr& context, query::GeneralQueryPtr general_query, - std::unordered_map& attr_type, - const engine::VectorsData& vectorsData); - - public: - bool - AddIndexFile(const SegmentSchemaPtr& index_file); - - void - WaitResult(); - - void - SearchDone(size_t index_id); - - ResultIds& - GetResultIds(); - - ResultDistances& - GetResultDistances(); - - Status& - GetStatus(); - - json - Dump() const override; - - public: - const std::shared_ptr& - GetContext() const; - - uint64_t - topk() const { - return topk_; - } - - uint64_t - nq() const { - return vectors_.vector_count_; - } - - const milvus::json& - extra_params() const { - return extra_params_; - } - - const engine::VectorsData& - vectors() const { - return vectors_; - } - - Id2IndexMap& - index_files() { - return index_files_; - } - - std::mutex& - mutex() { - return mutex_; - } - - query::GeneralQueryPtr - general_query() { - return general_query_; - } - - std::unordered_map& - attr_type() { - return attr_type_; - } - - uint64_t& - vector_count() { - return vector_count_; - } - - private: - const std::shared_ptr context_; - - uint64_t topk_ = 0; - milvus::json extra_params_; - // TODO: smart pointer - const engine::VectorsData& vectors_; - - Id2IndexMap index_files_; - // TODO: column-base better ? - ResultIds result_ids_; - ResultDistances result_distances_; - Status status_; - - query::GeneralQueryPtr general_query_; - std::unordered_map attr_type_; - uint64_t vector_count_; - - std::mutex mutex_; - std::condition_variable cv_; -}; - -using SearchJobPtr = std::shared_ptr; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/resource/Connection.h b/core/src/scheduler/resource/Connection.h deleted file mode 100644 index 145d101cbf..0000000000 --- a/core/src/scheduler/resource/Connection.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include - -namespace milvus { -namespace scheduler { - -class Connection { - public: - // TODO: update construct function, speed: double->uint64_t - Connection(std::string name, double speed) : name_(std::move(name)), speed_(speed) { - } - - const std::string& - name() const { - return name_; - } - - uint64_t - speed() const { - return speed_; - } - - uint64_t - transport_cost() { - return 1024 / speed_; - } - - public: - std::string - Dump() const { - std::stringstream ss; - ss << ""; - return ss.str(); - } - - private: - std::string name_; - uint64_t speed_; -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/resource/CpuResource.cpp b/core/src/scheduler/resource/CpuResource.cpp deleted file mode 100644 index 23a6ababb9..0000000000 --- a/core/src/scheduler/resource/CpuResource.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/resource/CpuResource.h" - -#include - -namespace milvus { -namespace scheduler { - -std::ostream& -operator<<(std::ostream& out, const CpuResource& resource) { - out << resource.Dump().dump(); - return out; -} - -CpuResource::CpuResource(std::string name, uint64_t device_id, bool enable_executor) - : Resource(std::move(name), ResourceType::CPU, device_id, enable_executor) { -} - -void -CpuResource::LoadFile(TaskPtr task) { - task->Load(LoadType::DISK2CPU, 0); -} - -void -CpuResource::Process(TaskPtr task) { - task->Execute(); -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/resource/CpuResource.h b/core/src/scheduler/resource/CpuResource.h deleted file mode 100644 index 5ce19c7342..0000000000 --- a/core/src/scheduler/resource/CpuResource.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -#include "Resource.h" - -namespace milvus { -namespace scheduler { - -class CpuResource : public Resource { - public: - explicit CpuResource(std::string name, uint64_t device_id, bool enable_executor); - - friend std::ostream& - operator<<(std::ostream& out, const CpuResource& resource); - - protected: - void - LoadFile(TaskPtr task) override; - - void - Process(TaskPtr task) override; -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/resource/DiskResource.cpp b/core/src/scheduler/resource/DiskResource.cpp deleted file mode 100644 index d0b5ed7abc..0000000000 --- a/core/src/scheduler/resource/DiskResource.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/resource/DiskResource.h" - -#include -#include - -namespace milvus { -namespace scheduler { - -std::ostream& -operator<<(std::ostream& out, const DiskResource& resource) { - out << resource.Dump(); - return out; -} - -DiskResource::DiskResource(std::string name, uint64_t device_id, bool enable_executor) - : Resource(std::move(name), ResourceType::DISK, device_id, enable_executor) { -} - -void -DiskResource::LoadFile(TaskPtr task) { -} - -void -DiskResource::Process(TaskPtr task) { -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/resource/DiskResource.h b/core/src/scheduler/resource/DiskResource.h deleted file mode 100644 index 136f2a8fcb..0000000000 --- a/core/src/scheduler/resource/DiskResource.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "Resource.h" - -#include - -namespace milvus { -namespace scheduler { - -class DiskResource : public Resource { - public: - explicit DiskResource(std::string name, uint64_t device_id, bool enable_executor); - - friend std::ostream& - operator<<(std::ostream& out, const DiskResource& resource); - - protected: - void - LoadFile(TaskPtr task) override; - - void - Process(TaskPtr task) override; -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/resource/GpuResource.cpp b/core/src/scheduler/resource/GpuResource.cpp deleted file mode 100644 index 0671e0c812..0000000000 --- a/core/src/scheduler/resource/GpuResource.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/resource/GpuResource.h" - -namespace milvus { -namespace scheduler { - -std::ostream& -operator<<(std::ostream& out, const GpuResource& resource) { - out << resource.Dump().dump(); - return out; -} - -GpuResource::GpuResource(std::string name, uint64_t device_id, bool enable_executor) - : Resource(std::move(name), ResourceType::GPU, device_id, enable_executor) { -} - -void -GpuResource::LoadFile(TaskPtr task) { - task->Load(LoadType::CPU2GPU, device_id_); -} - -void -GpuResource::Process(TaskPtr task) { - task->Execute(); -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/resource/GpuResource.h b/core/src/scheduler/resource/GpuResource.h deleted file mode 100644 index 5727ae602d..0000000000 --- a/core/src/scheduler/resource/GpuResource.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "Resource.h" - -#include -#include - -namespace milvus { -namespace scheduler { - -class GpuResource : public Resource { - public: - explicit GpuResource(std::string name, uint64_t device_id, bool enable_executor); - - friend std::ostream& - operator<<(std::ostream& out, const GpuResource& resource); - - protected: - void - LoadFile(TaskPtr task) override; - - void - Process(TaskPtr task) override; -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/resource/Node.cpp b/core/src/scheduler/resource/Node.cpp deleted file mode 100644 index a5a70af6e5..0000000000 --- a/core/src/scheduler/resource/Node.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/resource/Node.h" - -#include -#include - -namespace milvus { -namespace scheduler { - -Node::Node() { - static std::atomic_uint_fast8_t counter(0); - id_ = counter++; -} - -std::vector -Node::GetNeighbours() { - std::lock_guard lk(mutex_); - std::vector ret; - for (auto& e : neighbours_) { - ret.push_back(e.second); - } - return ret; -} - -json -Node::Dump() const { - json neighbours; - for (auto& neighbour : neighbours_) { - json n; - n["id"] = neighbour.first; - n["connection"] = neighbour.second.connection.Dump(); - neighbours.push_back(n); - } - - json ret{ - {"id", id_}, - {"neighbours", neighbours}, - }; - return ret; -} - -void -Node::AddNeighbour(const NeighbourNodePtr& neighbour_node, Connection& connection) { - std::lock_guard lk(mutex_); - neighbours_.emplace(std::make_pair(neighbour_node->id_, Neighbour(neighbour_node, connection))); - // else do nothing, consider it.. -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/resource/Node.h b/core/src/scheduler/resource/Node.h deleted file mode 100644 index d5b90c7012..0000000000 --- a/core/src/scheduler/resource/Node.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include - -#include "Connection.h" -#include "scheduler/TaskTable.h" -#include "scheduler/interface/interfaces.h" - -namespace milvus { -namespace scheduler { - -class Node; - -using NeighbourNodePtr = std::shared_ptr; - -struct Neighbour { - Neighbour(NeighbourNodePtr nei, Connection conn) : neighbour_node(std::move(nei)), connection(std::move(conn)) { - } - - ~Neighbour() { - neighbour_node = nullptr; - } - - NeighbourNodePtr neighbour_node; - Connection connection; -}; - -// TODO(lxj): return type void -> Status -class Node : public interface::dumpable { - public: - Node(); - - void - AddNeighbour(const NeighbourNodePtr& neighbour_node, Connection& connection); - - std::vector - GetNeighbours(); - - public: - json - Dump() const override; - - private: - std::mutex mutex_; - uint8_t id_; - std::map neighbours_; -}; - -using NodePtr = std::shared_ptr; -using NodeWPtr = std::weak_ptr; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/resource/Resource.cpp b/core/src/scheduler/resource/Resource.cpp deleted file mode 100644 index d1bb34c0c5..0000000000 --- a/core/src/scheduler/resource/Resource.cpp +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/resource/Resource.h" -#include "scheduler/SchedInst.h" -#include "scheduler/Utils.h" - -#include -#include -#include - -namespace milvus { -namespace scheduler { - -std::ostream& -operator<<(std::ostream& out, const Resource& resource) { - out << resource.Dump(); - return out; -} - -std::string -ToString(ResourceType type) { - switch (type) { - case ResourceType::DISK: { - return "DISK"; - } - case ResourceType::CPU: { - return "CPU"; - } - case ResourceType::GPU: { - return "GPU"; - } - default: { return "UNKNOWN"; } - } -} - -Resource::Resource(std::string name, ResourceType type, uint64_t device_id, bool enable_executor) - : device_id_(device_id), name_(std::move(name)), type_(type), enable_executor_(enable_executor) { - // register subscriber in tasktable - task_table_.RegisterSubscriber([&] { - if (subscriber_) { - auto event = std::make_shared(shared_from_this()); - subscriber_(std::static_pointer_cast(event)); - } - }); -} - -void -Resource::Start() { - running_ = true; - loader_thread_ = std::thread(&Resource::loader_function, this); - if (enable_executor_) { - executor_thread_ = std::thread(&Resource::executor_function, this); - } -} - -void -Resource::Stop() { - running_ = false; - WakeupLoader(); - loader_thread_.join(); - if (enable_executor_) { - WakeupExecutor(); - executor_thread_.join(); - } -} - -void -Resource::WakeupLoader() { - { - std::lock_guard lock(load_mutex_); - load_flag_ = true; - } - load_cv_.notify_one(); -} - -void -Resource::WakeupExecutor() { - { - std::lock_guard lock(exec_mutex_); - exec_flag_ = true; - } - exec_cv_.notify_one(); -} - -json -Resource::Dump() const { - json ret{ - {"device_id", device_id_}, - {"name", name_}, - {"type", ToString(type_)}, - {"task_average_cost", TaskAvgCost()}, - {"task_total_cost", total_cost_}, - {"total_tasks", total_task_}, - {"running", running_}, - {"enable_executor", enable_executor_}, - }; - return ret; -} - -uint64_t -Resource::NumOfTaskToExec() { - return task_table_.TaskToExecute(); -} - -TaskTableItemPtr -Resource::pick_task_load() { - auto indexes = task_table_.PickToLoad(10); - for (auto index : indexes) { - // try to set one task loading, then return - if (task_table_.Load(index)) - return task_table_.at(index); - // else try next - } - return nullptr; -} - -TaskTableItemPtr -Resource::pick_task_execute() { - auto indexes = task_table_.PickToExecute(std::numeric_limits::max()); - for (auto index : indexes) { - // try to set one task executing, then return - if (task_table_[index]->task->label()->Type() == TaskLabelType::SPECIFIED_RESOURCE) { - if (task_table_[index]->task->path().Last() != name()) { - continue; - } - } - - if (task_table_.Execute(index)) { - return task_table_.at(index); - } - // if (task_table_[index]->task->label()->Type() == TaskLabelType::SPECIFIED_RESOURCE) { - // if (task_table_.Get(index)->task->path().Current() == task_table_.Get(index)->task->path().Last() - // && - // task_table_.Get(index)->task->path().Last() == name()) { - // if (task_table_.Execute(index)) { - // return task_table_.Get(index); - // } - // } - // } - // else try next - } - return nullptr; -} - -void -Resource::loader_function() { - SetThreadName("taskloader_th"); - while (running_) { - std::unique_lock lock(load_mutex_); - load_cv_.wait(lock, [&] { return load_flag_; }); - load_flag_ = false; - lock.unlock(); - while (true) { - auto task_item = pick_task_load(); - if (task_item == nullptr) { - break; - } - if (task_item->task->Type() == TaskType::BuildIndexTask && name() == "cpu") { - BuildMgrInst::GetInstance()->Take(); - LOG_SERVER_DEBUG_ << name() << " load BuildIndexTask"; - } - LoadFile(task_item->task); - task_item->Loaded(); - if (task_item->from) { - task_item->from->Moved(); - task_item->from = nullptr; - } - if (subscriber_) { - auto event = std::make_shared(shared_from_this(), task_item); - subscriber_(std::static_pointer_cast(event)); - } - } - } -} - -void -Resource::executor_function() { - SetThreadName("taskexecutor_th"); - if (subscriber_) { - auto event = std::make_shared(shared_from_this()); - subscriber_(std::static_pointer_cast(event)); - } - while (running_) { - std::unique_lock lock(exec_mutex_); - exec_cv_.wait(lock, [&] { return exec_flag_; }); - exec_flag_ = false; - lock.unlock(); - while (true) { - auto task_item = pick_task_execute(); - if (task_item == nullptr) { - break; - } - auto start = get_current_timestamp(); - Process(task_item->task); - auto finish = get_current_timestamp(); - ++total_task_; - total_cost_ += finish - start; - - task_item->Executed(); - - if (task_item->task->Type() == TaskType::BuildIndexTask) { - BuildMgrInst::GetInstance()->Put(); - ResMgrInst::GetInstance()->GetResource("cpu")->WakeupLoader(); - ResMgrInst::GetInstance()->GetResource("disk")->WakeupLoader(); - } - - if (subscriber_) { - auto event = std::make_shared(shared_from_this(), task_item); - subscriber_(std::static_pointer_cast(event)); - } - } - } -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/resource/Resource.h b/core/src/scheduler/resource/Resource.h deleted file mode 100644 index d2a5cfa433..0000000000 --- a/core/src/scheduler/resource/Resource.h +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "../TaskTable.h" -#include "../event/Event.h" -#include "../event/FinishTaskEvent.h" -#include "../event/LoadCompletedEvent.h" -#include "../event/StartUpEvent.h" -#include "../event/TaskTableUpdatedEvent.h" -#include "../task/Task.h" -#include "Connection.h" -#include "Node.h" - -namespace milvus { -namespace scheduler { - -// TODO(wxyu): Storage, Route, Executor -enum class ResourceType { - DISK = 0, - CPU = 1, - GPU = 2, - TEST = 3, -}; - -class Resource : public Node, public std::enable_shared_from_this { - public: - /* - * Start loader and executor if enable; - */ - void - Start(); - - /* - * Stop loader and executor, join it, blocking util thread exited; - */ - void - Stop(); - - /* - * wake up loader; - */ - void - WakeupLoader(); - - /* - * wake up executor; - */ - void - WakeupExecutor(); - - inline void - RegisterSubscriber(std::function subscriber) { - subscriber_ = std::move(subscriber); - } - - json - Dump() const override; - - public: - inline std::string - name() const { - return name_; - } - - inline ResourceType - type() const { - return type_; - } - - inline uint64_t - device_id() const { - return device_id_; - } - - TaskTable& - task_table() { - return task_table_; - } - - public: - inline bool - HasExecutor() const { - return enable_executor_; - } - - // TODO(wxyu): const - uint64_t - NumOfTaskToExec(); - - // TODO(wxyu): need double ? - inline uint64_t - TaskAvgCost() const { - if (total_task_ == 0) { - return 0; - } - return total_cost_ / total_task_; - } - - inline uint64_t - TotalTasks() const { - return total_task_; - } - - friend std::ostream& - operator<<(std::ostream& out, const Resource& resource); - - protected: - Resource(std::string name, ResourceType type, uint64_t device_id, bool enable_executor); - - /* - * Implementation by inherit class; - * Blocking function; - */ - virtual void - LoadFile(TaskPtr task) = 0; - - /* - * Implementation by inherit class; - * Blocking function; - */ - virtual void - Process(TaskPtr task) = 0; - - private: - /* - * Pick one task to load; - * Order by start time; - */ - TaskTableItemPtr - pick_task_load(); - - /* - * Pick one task to execute; - * Pick by start time and priority; - */ - TaskTableItemPtr - pick_task_execute(); - - private: - /* - * Only called by load thread; - */ - void - loader_function(); - - /* - * Only called by worker thread; - */ - void - executor_function(); - - protected: - uint64_t device_id_; - std::string name_; - - private: - ResourceType type_; - - TaskTable task_table_; - - uint64_t total_cost_ = 0; - uint64_t total_task_ = 0; - - std::function subscriber_ = nullptr; - - bool running_ = false; - bool enable_executor_ = true; - std::thread loader_thread_; - std::thread executor_thread_; - - bool load_flag_ = false; - bool exec_flag_ = false; - std::mutex load_mutex_; - std::mutex exec_mutex_; - std::condition_variable load_cv_; - std::condition_variable exec_cv_; -}; - -using ResourcePtr = std::shared_ptr; -using ResourceWPtr = std::weak_ptr; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/resource/TestResource.cpp b/core/src/scheduler/resource/TestResource.cpp deleted file mode 100644 index e3d4485525..0000000000 --- a/core/src/scheduler/resource/TestResource.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/resource/TestResource.h" - -#include - -namespace milvus { -namespace scheduler { - -std::ostream& -operator<<(std::ostream& out, const TestResource& resource) { - out << resource.Dump(); - return out; -} - -TestResource::TestResource(std::string name, uint64_t device_id, bool enable_executor) - : Resource(std::move(name), ResourceType::TEST, device_id, enable_executor) { -} - -void -TestResource::LoadFile(TaskPtr task) { - task->Load(LoadType::TEST, 0); -} - -void -TestResource::Process(TaskPtr task) { - task->Execute(); -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/resource/TestResource.h b/core/src/scheduler/resource/TestResource.h deleted file mode 100644 index 969031e024..0000000000 --- a/core/src/scheduler/resource/TestResource.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "Resource.h" - -#include -#include - -namespace milvus { -namespace scheduler { - -class TestResource : public Resource { - public: - explicit TestResource(std::string name, uint64_t device_id, bool enable_executor); - - friend std::ostream& - operator<<(std::ostream& out, const TestResource& resource); - - protected: - void - LoadFile(TaskPtr task) override; - - void - Process(TaskPtr task) override; -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/selector/BuildIndexPass.cpp b/core/src/scheduler/selector/BuildIndexPass.cpp deleted file mode 100644 index 4047f2b999..0000000000 --- a/core/src/scheduler/selector/BuildIndexPass.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#include - -#include "scheduler/SchedInst.h" -#include "scheduler/Utils.h" -#include "scheduler/selector/BuildIndexPass.h" -#include "scheduler/tasklabel/SpecResLabel.h" -#ifdef MILVUS_GPU_VERSION -namespace milvus { -namespace scheduler { - -void -BuildIndexPass::Init() { - server::Config& config = server::Config::GetInstance(); - Status s = config.GetGpuResourceConfigBuildIndexResources(build_gpus_); - fiu_do_on("BuildIndexPass.Init.get_config_fail", s = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!s.ok()) { - throw std::exception(); - } - - SetIdentity("BuildIndexPass"); - AddGpuEnableListener(); - AddGpuBuildResourcesListener(); -} - -bool -BuildIndexPass::Run(const TaskPtr& task) { - if (task->Type() != TaskType::BuildIndexTask) - return false; - - ResourcePtr res_ptr; - if (!gpu_enable_) { - LOG_SERVER_DEBUG_ << "Gpu disabled, specify cpu to build index!"; - res_ptr = ResMgrInst::GetInstance()->GetResource("cpu"); - } else { - fiu_do_on("BuildIndexPass.Run.empty_gpu_ids", build_gpus_.clear()); - if (build_gpus_.empty()) { - LOG_SERVER_WARNING_ << "BuildIndexPass cannot get build index gpu!"; - return false; - } - LOG_SERVER_DEBUG_ << "Specify gpu" << build_gpus_[idx_] << " to build index!"; - res_ptr = ResMgrInst::GetInstance()->GetResource(ResourceType::GPU, build_gpus_[idx_]); - idx_ = (idx_ + 1) % build_gpus_.size(); - } - - auto label = std::make_shared(std::weak_ptr(res_ptr)); - task->label() = label; - - return true; -} - -} // namespace scheduler -} // namespace milvus -#endif diff --git a/core/src/scheduler/selector/BuildIndexPass.h b/core/src/scheduler/selector/BuildIndexPass.h deleted file mode 100644 index 220ac69049..0000000000 --- a/core/src/scheduler/selector/BuildIndexPass.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#ifdef MILVUS_GPU_VERSION -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config/handler/GpuResourceConfigHandler.h" -#include "scheduler/selector/Pass.h" - -namespace milvus { -namespace scheduler { - -class BuildIndexPass : public Pass, public server::GpuResourceConfigHandler { - public: - BuildIndexPass() = default; - - public: - void - Init() override; - - bool - Run(const TaskPtr& task) override; - - private: - uint64_t idx_ = 0; -}; - -using BuildIndexPassPtr = std::shared_ptr; - -} // namespace scheduler -} // namespace milvus -#endif diff --git a/core/src/scheduler/selector/FaissFlatPass.cpp b/core/src/scheduler/selector/FaissFlatPass.cpp deleted file mode 100644 index 2252388076..0000000000 --- a/core/src/scheduler/selector/FaissFlatPass.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#ifdef MILVUS_GPU_VERSION -#include "scheduler/selector/FaissFlatPass.h" -#include "cache/GpuCacheMgr.h" -#include "config/Config.h" -#include "scheduler/SchedInst.h" -#include "scheduler/Utils.h" -#include "scheduler/task/SearchTask.h" -#include "scheduler/tasklabel/SpecResLabel.h" -#include "utils/Log.h" - -#include -namespace milvus { -namespace scheduler { - -void -FaissFlatPass::Init() { - server::Config& config = server::Config::GetInstance(); - Status s = config.GetGpuResourceConfigGpuSearchThreshold(threshold_); - if (!s.ok()) { - threshold_ = std::numeric_limits::max(); - } - s = config.GetGpuResourceConfigSearchResources(search_gpus_); - if (!s.ok()) { - throw std::exception(); - } - - SetIdentity("FaissFlatPass"); - AddGpuEnableListener(); - AddGpuSearchThresholdListener(); - AddGpuSearchResourcesListener(); -} - -bool -FaissFlatPass::Run(const TaskPtr& task) { - if (task->Type() != TaskType::SearchTask) { - return false; - } - - auto search_task = std::static_pointer_cast(task); - if (search_task->file_->engine_type_ != (int)engine::EngineType::FAISS_IDMAP) { - return false; - } - - auto search_job = std::static_pointer_cast(search_task->job_.lock()); - ResourcePtr res_ptr; - if (!gpu_enable_) { - LOG_SERVER_DEBUG_ << LogOut("[%s][%d] FaissFlatPass: gpu disable, specify cpu to search!", "search", 0); - res_ptr = ResMgrInst::GetInstance()->GetResource("cpu"); - } else if (search_job->nq() < (uint64_t)threshold_) { - LOG_SERVER_DEBUG_ << LogOut("[%s][%d] FaissFlatPass: nq < gpu_search_threshold, specify cpu to search!", - "search", 0); - res_ptr = ResMgrInst::GetInstance()->GetResource("cpu"); - } else { - LOG_SERVER_DEBUG_ << LogOut("[%s][%d] FaissFlatPass: nq >= gpu_search_threshold, specify gpu %d to search!", - "search", 0, search_gpus_[idx_]); - res_ptr = ResMgrInst::GetInstance()->GetResource(ResourceType::GPU, search_gpus_[idx_]); - idx_ = (idx_ + 1) % search_gpus_.size(); - } - auto label = std::make_shared(res_ptr); - task->label() = label; - return true; -} - -} // namespace scheduler -} // namespace milvus -#endif diff --git a/core/src/scheduler/selector/FaissFlatPass.h b/core/src/scheduler/selector/FaissFlatPass.h deleted file mode 100644 index 54231c4b16..0000000000 --- a/core/src/scheduler/selector/FaissFlatPass.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#ifdef MILVUS_GPU_VERSION -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config/handler/GpuResourceConfigHandler.h" -#include "scheduler/selector/Pass.h" - -namespace milvus { -namespace scheduler { - -class FaissFlatPass : public Pass, public server::GpuResourceConfigHandler { - public: - FaissFlatPass() = default; - - public: - void - Init() override; - - bool - Run(const TaskPtr& task) override; - - private: - int64_t idx_ = 0; -}; - -using FaissFlatPassPtr = std::shared_ptr; - -} // namespace scheduler -} // namespace milvus -#endif diff --git a/core/src/scheduler/selector/FaissIVFFlatPass.cpp b/core/src/scheduler/selector/FaissIVFFlatPass.cpp deleted file mode 100644 index 33670da907..0000000000 --- a/core/src/scheduler/selector/FaissIVFFlatPass.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#ifdef MILVUS_GPU_VERSION -#include "scheduler/selector/FaissIVFFlatPass.h" -#include "cache/GpuCacheMgr.h" -#include "config/Config.h" -#include "scheduler/SchedInst.h" -#include "scheduler/Utils.h" -#include "scheduler/task/SearchTask.h" -#include "scheduler/tasklabel/SpecResLabel.h" -#include "utils/Log.h" - -namespace milvus { -namespace scheduler { - -void -FaissIVFFlatPass::Init() { -#ifdef MILVUS_GPU_VERSION - server::Config& config = server::Config::GetInstance(); - Status s = config.GetGpuResourceConfigGpuSearchThreshold(threshold_); - if (!s.ok()) { - threshold_ = std::numeric_limits::max(); - } - s = config.GetGpuResourceConfigSearchResources(search_gpus_); - if (!s.ok()) { - throw std::exception(); - } - - SetIdentity("FaissIVFFlatPass"); - AddGpuEnableListener(); - AddGpuSearchThresholdListener(); - AddGpuSearchResourcesListener(); -#endif -} - -bool -FaissIVFFlatPass::Run(const TaskPtr& task) { - if (task->Type() != TaskType::SearchTask) { - return false; - } - - auto search_task = std::static_pointer_cast(task); - if (search_task->file_->engine_type_ != (int)engine::EngineType::FAISS_IVFFLAT) { - return false; - } - - auto search_job = std::static_pointer_cast(search_task->job_.lock()); - ResourcePtr res_ptr; - if (!gpu_enable_) { - LOG_SERVER_DEBUG_ << LogOut("[%s][%d] FaissIVFFlatPass: gpu disable, specify cpu to search!", "search", 0); - res_ptr = ResMgrInst::GetInstance()->GetResource("cpu"); - } else if (search_job->nq() < (uint64_t)threshold_) { - LOG_SERVER_DEBUG_ << LogOut("[%s][%d] FaissIVFFlatPass: nq < gpu_search_threshold, specify cpu to search!", - "search", 0); - res_ptr = ResMgrInst::GetInstance()->GetResource("cpu"); - } else { - LOG_SERVER_DEBUG_ << LogOut("[%s][%d] FaissIVFFlatPass: nq >= gpu_search_threshold, specify gpu %d to search!", - "search", 0, search_gpus_[idx_]); - res_ptr = ResMgrInst::GetInstance()->GetResource(ResourceType::GPU, search_gpus_[idx_]); - idx_ = (idx_ + 1) % search_gpus_.size(); - } - auto label = std::make_shared(res_ptr); - task->label() = label; - return true; -} - -} // namespace scheduler -} // namespace milvus -#endif diff --git a/core/src/scheduler/selector/FaissIVFFlatPass.h b/core/src/scheduler/selector/FaissIVFFlatPass.h deleted file mode 100644 index 2c1b4e7ebb..0000000000 --- a/core/src/scheduler/selector/FaissIVFFlatPass.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#ifdef MILVUS_GPU_VERSION -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config/handler/GpuResourceConfigHandler.h" -#include "scheduler/selector/Pass.h" - -namespace milvus { -namespace scheduler { - -class FaissIVFFlatPass : public Pass, public server::GpuResourceConfigHandler { - public: - FaissIVFFlatPass() = default; - - public: - void - Init() override; - - bool - Run(const TaskPtr& task) override; - - private: - int64_t idx_ = 0; -}; - -using FaissIVFFlatPassPtr = std::shared_ptr; - -} // namespace scheduler -} // namespace milvus -#endif diff --git a/core/src/scheduler/selector/FaissIVFPQPass.cpp b/core/src/scheduler/selector/FaissIVFPQPass.cpp deleted file mode 100644 index c91484ce90..0000000000 --- a/core/src/scheduler/selector/FaissIVFPQPass.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#ifdef MILVUS_GPU_VERSION -#include "scheduler/selector/FaissIVFPQPass.h" -#include "cache/GpuCacheMgr.h" -#include "config/Config.h" -#include "scheduler/SchedInst.h" -#include "scheduler/Utils.h" -#include "scheduler/task/SearchTask.h" -#include "scheduler/tasklabel/SpecResLabel.h" -#include "utils/Log.h" - -#include - -namespace milvus { -namespace scheduler { - -void -FaissIVFPQPass::Init() { -#ifdef MILVUS_GPU_VERSION - server::Config& config = server::Config::GetInstance(); - Status s = config.GetGpuResourceConfigGpuSearchThreshold(threshold_); - if (!s.ok()) { - threshold_ = std::numeric_limits::max(); - } - s = config.GetGpuResourceConfigSearchResources(search_gpus_); - if (!s.ok()) { - throw std::exception(); - } - - SetIdentity("FaissIVFPQPass"); - AddGpuEnableListener(); - AddGpuSearchThresholdListener(); - AddGpuSearchResourcesListener(); -#endif -} - -bool -FaissIVFPQPass::Run(const TaskPtr& task) { - if (task->Type() != TaskType::SearchTask) { - return false; - } - - auto search_task = std::static_pointer_cast(task); - if (search_task->file_->engine_type_ != (int)engine::EngineType::FAISS_PQ) { - return false; - } - - auto search_job = std::static_pointer_cast(search_task->job_.lock()); - ResourcePtr res_ptr; - if (!gpu_enable_) { - LOG_SERVER_DEBUG_ << LogOut("[%s][%d] FaissIVFPQPass: gpu disable, specify cpu to search!", "search", 0); - res_ptr = ResMgrInst::GetInstance()->GetResource("cpu"); - } else if (search_job->nq() < (uint64_t)threshold_) { - LOG_SERVER_DEBUG_ << LogOut("[%s][%d] FaissIVFPQPass: nq < gpu_search_threshold, specify cpu to search!", - "search", 0); - res_ptr = ResMgrInst::GetInstance()->GetResource("cpu"); - } else { - LOG_SERVER_DEBUG_ << LogOut("[%s][%d] FaissIVFPQPass: nq >= gpu_search_threshold, specify gpu %d to search!", - "search", 0, search_gpus_[idx_]); - res_ptr = ResMgrInst::GetInstance()->GetResource(ResourceType::GPU, search_gpus_[idx_]); - idx_ = (idx_ + 1) % search_gpus_.size(); - } - auto label = std::make_shared(res_ptr); - task->label() = label; - return true; -} - -} // namespace scheduler -} // namespace milvus -#endif diff --git a/core/src/scheduler/selector/FaissIVFPQPass.h b/core/src/scheduler/selector/FaissIVFPQPass.h deleted file mode 100644 index b4fa296537..0000000000 --- a/core/src/scheduler/selector/FaissIVFPQPass.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#ifdef MILVUS_GPU_VERSION -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config/handler/GpuResourceConfigHandler.h" -#include "scheduler/selector/Pass.h" - -namespace milvus { -namespace scheduler { - -class FaissIVFPQPass : public Pass, public server::GpuResourceConfigHandler { - public: - FaissIVFPQPass() = default; - - public: - void - Init() override; - - bool - Run(const TaskPtr& task) override; - - private: - int64_t idx_ = 0; -}; - -using FaissIVFPQPassPtr = std::shared_ptr; - -} // namespace scheduler -} // namespace milvus -#endif diff --git a/core/src/scheduler/selector/FaissIVFSQ8HPass.cpp b/core/src/scheduler/selector/FaissIVFSQ8HPass.cpp deleted file mode 100644 index 66a45cffa8..0000000000 --- a/core/src/scheduler/selector/FaissIVFSQ8HPass.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#ifdef MILVUS_GPU_VERSION -#include "scheduler/selector/FaissIVFSQ8HPass.h" -#include "cache/GpuCacheMgr.h" -#include "config/Config.h" -#include "scheduler/SchedInst.h" -#include "scheduler/Utils.h" -#include "scheduler/task/SearchTask.h" -#include "scheduler/tasklabel/SpecResLabel.h" -#include "utils/Log.h" - -namespace milvus { -namespace scheduler { - -void -FaissIVFSQ8HPass::Init() { - server::Config& config = server::Config::GetInstance(); - Status s = config.GetGpuResourceConfigGpuSearchThreshold(threshold_); - if (!s.ok()) { - threshold_ = std::numeric_limits::max(); - } - s = config.GetGpuResourceConfigSearchResources(search_gpus_); - if (!s.ok()) { - throw std::exception(); - } - - SetIdentity("FaissIVFSQ8HPass"); - AddGpuEnableListener(); - AddGpuSearchThresholdListener(); - AddGpuSearchResourcesListener(); -} - -bool -FaissIVFSQ8HPass::Run(const TaskPtr& task) { - if (task->Type() != TaskType::SearchTask) { - return false; - } - - auto search_task = std::static_pointer_cast(task); - if (search_task->file_->engine_type_ != (int)engine::EngineType::FAISS_IVFSQ8H) { - return false; - } - - auto search_job = std::static_pointer_cast(search_task->job_.lock()); - ResourcePtr res_ptr; - if (!gpu_enable_) { - LOG_SERVER_DEBUG_ << LogOut("[%s][%d] FaissIVFSQ8HPass: gpu disable, specify cpu to search!", "search", 0); - res_ptr = ResMgrInst::GetInstance()->GetResource("cpu"); - } - if (search_job->nq() < (uint64_t)threshold_) { - LOG_SERVER_DEBUG_ << LogOut("[%s][%d] FaissIVFSQ8HPass: nq < gpu_search_threshold, specify cpu to search!", - "search", 0); - res_ptr = ResMgrInst::GetInstance()->GetResource("cpu"); - } else { - LOG_SERVER_DEBUG_ << LogOut("[%s][%d] FaissIVFSQ8HPass: nq >= gpu_search_threshold, specify gpu %d to search!", - "search", 0, search_gpus_[idx_]); - res_ptr = ResMgrInst::GetInstance()->GetResource(ResourceType::GPU, search_gpus_[idx_]); - idx_ = (idx_ + 1) % search_gpus_.size(); - } - auto label = std::make_shared(res_ptr); - task->label() = label; - return true; -} - -} // namespace scheduler -} // namespace milvus -#endif diff --git a/core/src/scheduler/selector/FaissIVFSQ8HPass.h b/core/src/scheduler/selector/FaissIVFSQ8HPass.h deleted file mode 100644 index 1050352c01..0000000000 --- a/core/src/scheduler/selector/FaissIVFSQ8HPass.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#ifdef MILVUS_GPU_VERSION -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config/handler/GpuResourceConfigHandler.h" -#include "scheduler/selector/Pass.h" - -namespace milvus { -namespace scheduler { - -class FaissIVFSQ8HPass : public Pass, public server::GpuResourceConfigHandler { - public: - FaissIVFSQ8HPass() = default; - - public: - void - Init() override; - - bool - Run(const TaskPtr& task) override; - - private: - int64_t idx_ = 0; -}; - -using FaissIVFSQ8HPassPtr = std::shared_ptr; - -} // namespace scheduler -} // namespace milvus -#endif diff --git a/core/src/scheduler/selector/FaissIVFSQ8Pass.cpp b/core/src/scheduler/selector/FaissIVFSQ8Pass.cpp deleted file mode 100644 index 056ae01204..0000000000 --- a/core/src/scheduler/selector/FaissIVFSQ8Pass.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#ifdef MILVUS_GPU_VERSION -#include "scheduler/selector/FaissIVFSQ8Pass.h" -#include "cache/GpuCacheMgr.h" -#include "config/Config.h" -#include "scheduler/SchedInst.h" -#include "scheduler/Utils.h" -#include "scheduler/task/SearchTask.h" -#include "scheduler/tasklabel/SpecResLabel.h" -#include "utils/Log.h" - -namespace milvus { -namespace scheduler { - -void -FaissIVFSQ8Pass::Init() { -#ifdef MILVUS_GPU_VERSION - server::Config& config = server::Config::GetInstance(); - Status s = config.GetGpuResourceConfigGpuSearchThreshold(threshold_); - if (!s.ok()) { - threshold_ = std::numeric_limits::max(); - } - s = config.GetGpuResourceConfigSearchResources(search_gpus_); - if (!s.ok()) { - throw std::exception(); - } - - SetIdentity("FaissIVFSQ8Pass"); - AddGpuEnableListener(); - AddGpuSearchThresholdListener(); - AddGpuSearchResourcesListener(); -#endif -} - -bool -FaissIVFSQ8Pass::Run(const TaskPtr& task) { - if (task->Type() != TaskType::SearchTask) { - return false; - } - - auto search_task = std::static_pointer_cast(task); - if (search_task->file_->engine_type_ != (int)engine::EngineType::FAISS_IVFSQ8) { - return false; - } - - auto search_job = std::static_pointer_cast(search_task->job_.lock()); - ResourcePtr res_ptr; - if (!gpu_enable_) { - LOG_SERVER_DEBUG_ << LogOut("[%s][%d] FaissIVFSQ8Pass: gpu disable, specify cpu to search!", "search", 0); - res_ptr = ResMgrInst::GetInstance()->GetResource("cpu"); - } else if (search_job->nq() < (uint64_t)threshold_) { - LOG_SERVER_DEBUG_ << LogOut("[%s][%d] FaissIVFSQ8Pass: nq < gpu_search_threshold, specify cpu to search!", - "search", 0); - res_ptr = ResMgrInst::GetInstance()->GetResource("cpu"); - } else { - LOG_SERVER_DEBUG_ << LogOut("[%s][%d] FaissIVFSQ8Pass: nq >= gpu_search_threshold, specify gpu %d to search!", - "search", 0, search_gpus_[idx_]); - res_ptr = ResMgrInst::GetInstance()->GetResource(ResourceType::GPU, search_gpus_[idx_]); - idx_ = (idx_ + 1) % search_gpus_.size(); - } - auto label = std::make_shared(res_ptr); - task->label() = label; - return true; -} - -} // namespace scheduler -} // namespace milvus -#endif diff --git a/core/src/scheduler/selector/FaissIVFSQ8Pass.h b/core/src/scheduler/selector/FaissIVFSQ8Pass.h deleted file mode 100644 index 99530c7635..0000000000 --- a/core/src/scheduler/selector/FaissIVFSQ8Pass.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#ifdef MILVUS_GPU_VERSION -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config/handler/GpuResourceConfigHandler.h" -#include "scheduler/selector/Pass.h" - -namespace milvus { -namespace scheduler { - -class FaissIVFSQ8Pass : public Pass, public server::GpuResourceConfigHandler { - public: - FaissIVFSQ8Pass() = default; - - public: - void - Init() override; - - bool - Run(const TaskPtr& task) override; - - private: - int64_t idx_ = 0; -}; - -using FaissIVFSQ8PassPtr = std::shared_ptr; - -} // namespace scheduler -} // namespace milvus -#endif diff --git a/core/src/scheduler/selector/FallbackPass.cpp b/core/src/scheduler/selector/FallbackPass.cpp deleted file mode 100644 index e4bb51f8d0..0000000000 --- a/core/src/scheduler/selector/FallbackPass.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/selector/FallbackPass.h" -#include "scheduler/SchedInst.h" -#include "scheduler/tasklabel/SpecResLabel.h" - -namespace milvus { -namespace scheduler { - -void -FallbackPass::Init() { -} - -bool -FallbackPass::Run(const TaskPtr& task) { - auto task_type = task->Type(); - if (task_type != TaskType::SearchTask && task_type != TaskType::BuildIndexTask) { - return false; - } - // NEVER be empty - LOG_SERVER_DEBUG_ << "FallbackPass!"; - auto cpu = ResMgrInst::GetInstance()->GetCpuResources()[0]; - auto label = std::make_shared(cpu); - task->label() = label; - return true; -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/selector/FallbackPass.h b/core/src/scheduler/selector/FallbackPass.h deleted file mode 100644 index 4486b5d55c..0000000000 --- a/core/src/scheduler/selector/FallbackPass.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#pragma once - -#include -#include - -#include "Pass.h" - -namespace milvus { -namespace scheduler { - -class FallbackPass : public Pass { - public: - FallbackPass() = default; - - public: - void - Init() override; - - bool - Run(const TaskPtr& task) override; -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/selector/Optimizer.cpp b/core/src/scheduler/selector/Optimizer.cpp deleted file mode 100644 index 497287fa05..0000000000 --- a/core/src/scheduler/selector/Optimizer.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/selector/Optimizer.h" - -namespace milvus { -namespace scheduler { - -void -Optimizer::Init() { - for (auto& pass : pass_list_) { - pass->Init(); - } -} - -bool -Optimizer::Run(const TaskPtr& task) { - for (auto& pass : pass_list_) { - if (pass->Run(task)) { - return true; - } - } - - return false; -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/selector/Optimizer.h b/core/src/scheduler/selector/Optimizer.h deleted file mode 100644 index 5bf6ddf07f..0000000000 --- a/core/src/scheduler/selector/Optimizer.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Pass.h" - -namespace milvus { -namespace scheduler { - -class Optimizer { - public: - explicit Optimizer(std::vector pass_list) : pass_list_(std::move(pass_list)) { - } - - void - Init(); - - bool - Run(const TaskPtr& task); - - private: - std::vector pass_list_; -}; - -using OptimizerPtr = std::shared_ptr; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/selector/Pass.h b/core/src/scheduler/selector/Pass.h deleted file mode 100644 index e28c8fdbe7..0000000000 --- a/core/src/scheduler/selector/Pass.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "scheduler/task/Task.h" - -namespace milvus { -namespace scheduler { - -class Pass { - public: - virtual void - Init() = 0; - - virtual bool - Run(const TaskPtr& task) = 0; -}; -using PassPtr = std::shared_ptr; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/task/BuildIndexTask.cpp b/core/src/scheduler/task/BuildIndexTask.cpp deleted file mode 100644 index 0b49f5e8bc..0000000000 --- a/core/src/scheduler/task/BuildIndexTask.cpp +++ /dev/null @@ -1,247 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/task/BuildIndexTask.h" - -#include - -#include -#include -#include -#include - -#include "db/Utils.h" -#include "db/engine/EngineFactory.h" -#include "metrics/Metrics.h" -#include "scheduler/job/BuildIndexJob.h" -#include "utils/CommonUtil.h" -#include "utils/Exception.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -namespace milvus { -namespace scheduler { - -XBuildIndexTask::XBuildIndexTask(SegmentSchemaPtr file, TaskLabelPtr label) - : Task(TaskType::BuildIndexTask, std::move(label)), file_(file) { - if (file_) { - EngineType engine_type; - if (file->file_type_ == SegmentSchema::FILE_TYPE::RAW || - file->file_type_ == SegmentSchema::FILE_TYPE::TO_INDEX || - file->file_type_ == SegmentSchema::FILE_TYPE::BACKUP) { - engine_type = engine::utils::IsBinaryMetricType(file->metric_type_) ? EngineType::FAISS_BIN_IDMAP - : EngineType::FAISS_IDMAP; - } else { - engine_type = (EngineType)file->engine_type_; - } - - auto json = milvus::json::parse(file_->index_params_); - to_index_engine_ = EngineFactory::Build(file_->dimension_, file_->location_, engine_type, - (MetricType)file_->metric_type_, json); - } -} - -void -XBuildIndexTask::Load(milvus::scheduler::LoadType type, uint8_t device_id) { - TimeRecorder rc("XBuildIndexTask::Load"); - Status stat = Status::OK(); - std::string error_msg; - std::string type_str; - - if (auto job = job_.lock()) { - auto build_index_job = std::static_pointer_cast(job); - auto options = build_index_job->options(); - try { - if (type == LoadType::DISK2CPU) { - stat = to_index_engine_->Load(options.insert_cache_immediately_); - type_str = "DISK2CPU"; - } else if (type == LoadType::CPU2GPU) { - stat = to_index_engine_->CopyToIndexFileToGpu(device_id); - type_str = "CPU2GPU:" + std::to_string(device_id); - } else { - error_msg = "Wrong load type"; - stat = Status(SERVER_UNEXPECTED_ERROR, error_msg); - } - fiu_do_on("XBuildIndexTask.Load.throw_std_exception", throw std::exception()); - } catch (std::exception& ex) { - // typical error: out of disk space or permition denied - error_msg = "Failed to load to_index file: " + std::string(ex.what()); - LOG_ENGINE_ERROR_ << error_msg; - stat = Status(SERVER_UNEXPECTED_ERROR, error_msg); - } - fiu_do_on("XBuildIndexTask.Load.out_of_memory", stat = Status(SERVER_UNEXPECTED_ERROR, "out of memory")); - if (!stat.ok()) { - Status s; - if (stat.ToString().find("out of memory") != std::string::npos) { - error_msg = "out of memory: " + type_str; - s = Status(SERVER_UNEXPECTED_ERROR, error_msg); - } else { - error_msg = "Failed to load to_index file: " + type_str; - s = Status(SERVER_UNEXPECTED_ERROR, error_msg); - } - - LOG_ENGINE_ERROR_ << s.message(); - - if (auto job = job_.lock()) { - auto build_index_job = std::static_pointer_cast(job); - build_index_job->GetStatus() = s; - build_index_job->BuildIndexDone(file_->id_); - } - - return; - } - - size_t file_size = to_index_engine_->Size(); - - std::string info = "Build index task load file id:" + std::to_string(file_->id_) + " " + type_str + - " file type:" + std::to_string(file_->file_type_) + " size:" + std::to_string(file_size) + - " bytes from location: " + file_->location_ + " totally cost"; - rc.ElapseFromBegin(info); - - to_index_id_ = file_->id_; - to_index_type_ = file_->file_type_; - } -} - -void -XBuildIndexTask::Execute() { - TimeRecorderAuto rc("XBuildIndexTask::Execute " + std::to_string(to_index_id_)); - - if (auto job = job_.lock()) { - auto build_index_job = std::static_pointer_cast(job); - if (to_index_engine_ == nullptr) { - build_index_job->BuildIndexDone(to_index_id_); - build_index_job->GetStatus() = Status(DB_ERROR, "source index is null"); - return; - } - - std::string location = file_->location_; - std::shared_ptr index; - - // step 1: create collection file - engine::meta::SegmentSchema table_file; - table_file.collection_id_ = file_->collection_id_; - table_file.segment_id_ = file_->file_id_; - table_file.date_ = file_->date_; - table_file.file_type_ = engine::meta::SegmentSchema::NEW_INDEX; - - engine::meta::MetaPtr meta_ptr = build_index_job->meta(); - Status status = meta_ptr->CreateCollectionFile(table_file); - - fiu_do_on("XBuildIndexTask.Execute.create_table_success", status = Status::OK()); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << "Failed to create collection file: " << status.ToString(); - build_index_job->BuildIndexDone(to_index_id_); - build_index_job->GetStatus() = status; - to_index_engine_ = nullptr; - return; - } - - auto failed_build_index = [&](std::string log_msg, std::string err_msg) { - table_file.file_type_ = engine::meta::SegmentSchema::TO_DELETE; - status = meta_ptr->UpdateCollectionFile(table_file); - LOG_ENGINE_ERROR_ << log_msg; - - build_index_job->BuildIndexDone(to_index_id_); - build_index_job->GetStatus() = Status(DB_ERROR, err_msg); - to_index_engine_ = nullptr; - }; - - // step 2: build index - try { - LOG_ENGINE_DEBUG_ << "Begin build index for file:" + table_file.location_; - index = to_index_engine_->BuildIndex(table_file.location_, (EngineType)table_file.engine_type_); - fiu_do_on("XBuildIndexTask.Execute.build_index_fail", index = nullptr); - if (index == nullptr) { - std::string log_msg = "Failed to build index " + table_file.file_id_ + ", reason: source index is null"; - failed_build_index(log_msg, "source index is null"); - return; - } - } catch (std::exception& ex) { - std::string msg = "Failed to build index " + table_file.file_id_ + ", reason: " + std::string(ex.what()); - failed_build_index(msg, ex.what()); - return; - } - - // step 3: if collection has been deleted, dont save index file - bool has_collection = false; - meta_ptr->HasCollection(file_->collection_id_, has_collection); - fiu_do_on("XBuildIndexTask.Execute.has_collection", has_collection = true); - - if (!has_collection) { - std::string msg = "Failed to build index " + table_file.file_id_ + ", reason: collection has been deleted"; - failed_build_index(msg, "Collection has been deleted"); - return; - } - - // step 4: save index file - try { - fiu_do_on("XBuildIndexTask.Execute.throw_std_exception", throw std::exception()); - status = index->Serialize(); - if (!status.ok()) { - std::string msg = - "Failed to persist index file: " + table_file.location_ + ", reason: " + status.message(); - failed_build_index(msg, status.message()); - return; - } - } catch (std::exception& ex) { - // if failed to serialize index file to disk - // typical error: out of disk space, out of memory or permition denied - std::string msg = - "Failed to persist index file:" + table_file.location_ + ", exception:" + std::string(ex.what()); - failed_build_index(msg, ex.what()); - return; - } - - // step 5: update meta - table_file.file_type_ = engine::meta::SegmentSchema::INDEX; - table_file.file_size_ = server::CommonUtil::GetFileSize(table_file.location_); - table_file.row_count_ = file_->row_count_; // index->Count(); - - auto origin_file = *file_; - origin_file.file_type_ = engine::meta::SegmentSchema::BACKUP; - - engine::meta::SegmentsSchema update_files = {table_file, origin_file}; - - if (status.ok()) { // makesure index file is sucessfully serialized to disk - status = meta_ptr->UpdateCollectionFiles(update_files); - } - - fiu_do_on("XBuildIndexTask.Execute.update_table_file_fail", status = Status(SERVER_UNEXPECTED_ERROR, "")); - if (status.ok()) { - LOG_ENGINE_DEBUG_ << "New index file " << table_file.file_id_ << " of size " << table_file.file_size_ - << " bytes" - << " from file " << origin_file.file_id_; - if (build_index_job->options().insert_cache_immediately_) { - index->Cache(); - } - } else { - // failed to update meta, mark the new file as to_delete, don't delete old file - origin_file.file_type_ = engine::meta::SegmentSchema::TO_INDEX; - status = meta_ptr->UpdateCollectionFile(origin_file); - LOG_ENGINE_DEBUG_ << "Failed to update file to index, mark file: " << origin_file.file_id_ - << " to to_index"; - - table_file.file_type_ = engine::meta::SegmentSchema::TO_DELETE; - status = meta_ptr->UpdateCollectionFile(table_file); - LOG_ENGINE_DEBUG_ << "Failed to up date file to index, mark file: " << table_file.file_id_ - << " to to_delete"; - } - - build_index_job->BuildIndexDone(to_index_id_); - } - - to_index_engine_ = nullptr; -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/task/BuildIndexTask.h b/core/src/scheduler/task/BuildIndexTask.h deleted file mode 100644 index 1f9d704949..0000000000 --- a/core/src/scheduler/task/BuildIndexTask.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "Task.h" -#include "scheduler/Definition.h" -#include "scheduler/job/BuildIndexJob.h" - -namespace milvus { -namespace scheduler { - -class XBuildIndexTask : public Task { - public: - explicit XBuildIndexTask(SegmentSchemaPtr file, TaskLabelPtr label); - - void - Load(LoadType type, uint8_t device_id) override; - - void - Execute() override; - - public: - SegmentSchemaPtr file_; - SegmentSchema table_file_; - size_t to_index_id_ = 0; - int to_index_type_ = 0; - ExecutionEnginePtr to_index_engine_ = nullptr; -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/task/DeleteTask.cpp b/core/src/scheduler/task/DeleteTask.cpp deleted file mode 100644 index a85f6078c7..0000000000 --- a/core/src/scheduler/task/DeleteTask.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/task/DeleteTask.h" - -#include - -namespace milvus { -namespace scheduler { - -XDeleteTask::XDeleteTask(const scheduler::DeleteJobPtr& delete_job, TaskLabelPtr label) - : Task(TaskType::DeleteTask, std::move(label)), delete_job_(delete_job) { -} - -void -XDeleteTask::Load(LoadType type, uint8_t device_id) { -} - -void -XDeleteTask::Execute() { - delete_job_->ResourceDone(); -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/task/DeleteTask.h b/core/src/scheduler/task/DeleteTask.h deleted file mode 100644 index 84dbb5b520..0000000000 --- a/core/src/scheduler/task/DeleteTask.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "Task.h" -#include "scheduler/job/DeleteJob.h" - -namespace milvus { -namespace scheduler { - -class XDeleteTask : public Task { - public: - explicit XDeleteTask(const scheduler::DeleteJobPtr& delete_job, TaskLabelPtr label); - - void - Load(LoadType type, uint8_t device_id) override; - - void - Execute() override; - - public: - scheduler::DeleteJobPtr delete_job_; -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/task/Path.h b/core/src/scheduler/task/Path.h deleted file mode 100644 index 7916038eb1..0000000000 --- a/core/src/scheduler/task/Path.h +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -namespace milvus { -namespace scheduler { - -class Path { - public: - Path() = default; - - Path(std::vector& path, uint64_t index) : path_(path), index_(index) { - } - - void - push_back(const std::string& str) { - path_.push_back(str); - } - - std::vector - Dump() { - return path_; - } - - std::string - Current() { - if (!path_.empty() && path_.size() > index_) { - return path_[index_]; - } else { - return ""; - } - } - - std::string - Next() { - if (index_ > 0 && !path_.empty()) { - --index_; - return path_[index_]; - } else { - return ""; - } - } - - std::string - Last() { - if (!path_.empty()) { - return path_[0]; - } else { - return ""; - } - } - - std::string - ToString() { - std::string str = path_[index_]; - for (int64_t i = index_; i > 0; i--) { - str += "->" + path_[i - 1]; - } - return str; - } - - public: - std::string& operator[](uint64_t index) { - return path_[index]; - } - - std::vector::iterator - begin() { - return path_.begin(); - } - - std::vector::iterator - end() { - return path_.end(); - } - - public: - std::vector path_; - uint64_t index_ = 0; -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/task/SearchTask.cpp b/core/src/scheduler/task/SearchTask.cpp deleted file mode 100644 index 521c81f179..0000000000 --- a/core/src/scheduler/task/SearchTask.cpp +++ /dev/null @@ -1,498 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/task/SearchTask.h" - -#include - -#include -#include -#include -#include -#include -#include - -#include "db/Utils.h" -#include "db/engine/EngineFactory.h" -#include "index/knowhere/knowhere/index/vector_index/helpers/IndexParameter.h" -#include "metrics/Metrics.h" -#include "scheduler/SchedInst.h" -#include "scheduler/job/SearchJob.h" -#include "segment/SegmentReader.h" -#include "utils/CommonUtil.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -namespace milvus { -namespace scheduler { - -static constexpr size_t PARALLEL_REDUCE_THRESHOLD = 10000; -static constexpr size_t PARALLEL_REDUCE_BATCH = 1000; - -// TODO(wxyu): remove unused code -// bool -// NeedParallelReduce(uint64_t nq, uint64_t topk) { -// server::ServerConfig &config = server::ServerConfig::GetInstance(); -// server::ConfigNode &db_config = config.GetConfig(server::CONFIG_DB); -// bool need_parallel = db_config.GetBoolValue(server::CONFIG_DB_PARALLEL_REDUCE, false); -// if (!need_parallel) { -// return false; -// } -// -// return nq * topk >= PARALLEL_REDUCE_THRESHOLD; -//} -// -// void -// ParallelReduce(std::function &reduce_function, size_t max_index) { -// size_t reduce_batch = PARALLEL_REDUCE_BATCH; -// -// auto thread_count = std::thread::hardware_concurrency() - 1; //not all core do this work -// if (thread_count > 0) { -// reduce_batch = max_index / thread_count + 1; -// } -// LOG_ENGINE_DEBUG_ << "use " << thread_count << -// " thread parallelly do reduce, each thread process " << reduce_batch << " vectors"; -// -// std::vector > thread_array; -// size_t from_index = 0; -// while (from_index < max_index) { -// size_t to_index = from_index + reduce_batch; -// if (to_index > max_index) { -// to_index = max_index; -// } -// -// auto reduce_thread = std::make_shared(reduce_function, from_index, to_index); -// thread_array.push_back(reduce_thread); -// -// from_index = to_index; -// } -// -// for (auto &thread_ptr : thread_array) { -// thread_ptr->join(); -// } -//} - -void -CollectFileMetrics(int file_type, size_t file_size) { - server::MetricsBase& inst = server::Metrics::GetInstance(); - switch (file_type) { - case SegmentSchema::RAW: - case SegmentSchema::TO_INDEX: { - inst.RawFileSizeHistogramObserve(file_size); - inst.RawFileSizeTotalIncrement(file_size); - inst.RawFileSizeGaugeSet(file_size); - break; - } - default: { - inst.IndexFileSizeHistogramObserve(file_size); - inst.IndexFileSizeTotalIncrement(file_size); - inst.IndexFileSizeGaugeSet(file_size); - break; - } - } -} - -XSearchTask::XSearchTask(const std::shared_ptr& context, SegmentSchemaPtr file, TaskLabelPtr label) - : Task(TaskType::SearchTask, std::move(label)), context_(context), file_(file) { - if (file_) { - // distance -- value 0 means two vectors equal, ascending reduce, L2/HAMMING/JACCARD/TONIMOTO ... - // similarity -- value 1 means two vectors equal, descending reduce, IP - if (file_->metric_type_ == static_cast(MetricType::IP)) { - ascending_reduce = false; - } - - EngineType engine_type; - if (file->file_type_ == SegmentSchema::FILE_TYPE::RAW || - file->file_type_ == SegmentSchema::FILE_TYPE::TO_INDEX || - file->file_type_ == SegmentSchema::FILE_TYPE::BACKUP) { - engine_type = engine::utils::IsBinaryMetricType(file->metric_type_) ? EngineType::FAISS_BIN_IDMAP - : EngineType::FAISS_IDMAP; - } else { - engine_type = (EngineType)file->engine_type_; - } - - milvus::json json_params; - if (!file_->index_params_.empty()) { - json_params = milvus::json::parse(file_->index_params_); - if (json_params.contains(knowhere::Metric::TYPE) && - (engine_type == EngineType::FAISS_BIN_IDMAP || engine_type == EngineType::FAISS_IDMAP)) - ascending_reduce = json_params[knowhere::Metric::TYPE] != static_cast(MetricType::IP); - } - // if (auto job = job_.lock()) { - // auto search_job = std::static_pointer_cast(job); - // query::GeneralQueryPtr general_query = search_job->general_query(); - // if (general_query != nullptr) { - // std::unordered_map types; - // auto attr_type = search_job->attr_type(); - // auto type_it = attr_type.begin(); - // for (; type_it != attr_type.end(); type_it++) { - // types.insert(std::make_pair(type_it->first, (engine::DataType)(type_it->second))); - // } - // index_engine_ = - // EngineFactory::Build(file_->dimension_, file_->location_, engine_type, - // (MetricType)file_->metric_type_, types, json_params); - // } - // } - index_engine_ = EngineFactory::Build(file_->dimension_, file_->location_, engine_type, - (MetricType)file_->metric_type_, json_params); - } -} - -void -XSearchTask::Load(LoadType type, uint8_t device_id) { - milvus::server::ContextFollower tracer(context_, "XSearchTask::Load " + std::to_string(file_->id_)); - - TimeRecorder rc(LogOut("[%s][%ld]", "search", 0)); - Status stat = Status::OK(); - std::string error_msg; - std::string type_str; - - try { - fiu_do_on("XSearchTask.Load.throw_std_exception", throw std::exception()); - if (type == LoadType::DISK2CPU) { - stat = index_engine_->Load(); - type_str = "DISK2CPU"; - } else if (type == LoadType::CPU2GPU) { - bool hybrid = false; - if (index_engine_->IndexEngineType() == engine::EngineType::FAISS_IVFSQ8H) { - hybrid = true; - } - stat = index_engine_->CopyToGpu(device_id, hybrid); - type_str = "CPU2GPU" + std::to_string(device_id); - } else if (type == LoadType::GPU2CPU) { - stat = index_engine_->CopyToCpu(); - type_str = "GPU2CPU"; - } else { - error_msg = "Wrong load type"; - stat = Status(SERVER_UNEXPECTED_ERROR, error_msg); - } - } catch (std::exception& ex) { - // typical error: out of disk space or permition denied - error_msg = "Failed to load index file: " + std::string(ex.what()); - LOG_ENGINE_ERROR_ << LogOut("[%s][%ld] Encounter execption: %s", "search", 0, error_msg.c_str()); - stat = Status(SERVER_UNEXPECTED_ERROR, error_msg); - } - fiu_do_on("XSearchTask.Load.out_of_memory", stat = Status(SERVER_UNEXPECTED_ERROR, "out of memory")); - - if (!stat.ok()) { - Status s; - if (stat.ToString().find("out of memory") != std::string::npos) { - error_msg = "out of memory: " + type_str + " : " + stat.message(); - s = Status(SERVER_OUT_OF_MEMORY, error_msg); - } else { - error_msg = "Failed to load index file: " + type_str + " : " + stat.message(); - s = Status(SERVER_UNEXPECTED_ERROR, error_msg); - } - - if (auto job = job_.lock()) { - auto search_job = std::static_pointer_cast(job); - search_job->SearchDone(file_->id_); - search_job->GetStatus() = s; - } - - return; - } - - size_t file_size = index_engine_->Size(); - - std::string info = "Search task load file id:" + std::to_string(file_->id_) + " " + type_str + - " file type:" + std::to_string(file_->file_type_) + " size:" + std::to_string(file_size) + - " bytes from location: " + file_->location_ + " totally cost"; - rc.ElapseFromBegin(info); - - CollectFileMetrics(file_->file_type_, file_size); - - // step 2: return search task for later execution - index_id_ = file_->id_; - index_type_ = file_->file_type_; - // search_contexts_.swap(search_contexts_); -} - -void -XSearchTask::Execute() { - milvus::server::ContextFollower tracer(context_, "XSearchTask::Execute " + std::to_string(index_id_)); - - // LOG_ENGINE_DEBUG_ << "Searching in file id:" << index_id_ << " with " - // << search_contexts_.size() << " tasks"; - - // TimeRecorder rc("DoSearch file id:" + std::to_string(index_id_)); - TimeRecorder rc(LogOut("[%s][%ld] DoSearch file id:%ld", "search", 0, index_id_)); - - server::CollectDurationMetrics metrics(index_type_); - - std::vector output_ids; - std::vector output_distance; - - if (auto job = job_.lock()) { - auto search_job = std::static_pointer_cast(job); - - if (index_engine_ == nullptr) { - search_job->SearchDone(index_id_); - return; - } - - // step 1: allocate memory - query::GeneralQueryPtr general_query = search_job->general_query(); - - uint64_t nq = search_job->nq(); - uint64_t topk = search_job->topk(); - - const milvus::json& extra_params = search_job->extra_params(); - const engine::VectorsData& vectors = search_job->vectors(); - - output_ids.resize(topk * nq); - output_distance.resize(topk * nq); - std::string hdr = - "job " + std::to_string(search_job->id()) + " nq " + std::to_string(nq) + " topk " + std::to_string(topk); - - try { - fiu_do_on("XSearchTask.Execute.throw_std_exception", throw std::exception()); - // step 2: search - bool hybrid = false; - if (index_engine_->IndexEngineType() == engine::EngineType::FAISS_IVFSQ8H && - ResMgrInst::GetInstance()->GetResource(path().Last())->type() == ResourceType::CPU) { - hybrid = true; - } - Status s; - if (general_query != nullptr) { - std::unordered_map types; - auto attr_type = search_job->attr_type(); - auto type_it = attr_type.begin(); - for (; type_it != attr_type.end(); type_it++) { - types.insert(std::make_pair(type_it->first, (engine::DataType)(type_it->second))); - } - faiss::ConcurrentBitsetPtr bitset; - s = index_engine_->ExecBinaryQuery(general_query, bitset, types, nq, topk, output_distance, output_ids); - - if (!s.ok()) { - search_job->GetStatus() = s; - search_job->SearchDone(index_id_); - return; - } - - auto spec_k = file_->row_count_ < topk ? file_->row_count_ : topk; - if (spec_k == 0) { - LOG_ENGINE_WARNING_ << "Searching in an empty file. file location = " << file_->location_; - } else { - std::unique_lock lock(search_job->mutex()); - search_job->vector_count() = nq; - XSearchTask::MergeTopkToResultSet(output_ids, output_distance, spec_k, nq, topk, ascending_reduce, - search_job->GetResultIds(), search_job->GetResultDistances()); - - if (search_job->GetResultIds().empty()) { - LOG_ENGINE_ERROR_ << "Result reduce error: result id array is empty"; - } - } - search_job->SearchDone(index_id_); - index_engine_ = nullptr; - return; - } - if (!vectors.float_data_.empty()) { - s = index_engine_->Search(nq, vectors.float_data_.data(), topk, extra_params, output_distance.data(), - output_ids.data(), hybrid); - } else if (!vectors.binary_data_.empty()) { - s = index_engine_->Search(nq, vectors.binary_data_.data(), topk, extra_params, output_distance.data(), - output_ids.data(), hybrid); - } - - fiu_do_on("XSearchTask.Execute.search_fail", s = Status(SERVER_UNEXPECTED_ERROR, "")); - - if (!s.ok()) { - search_job->GetStatus() = s; - search_job->SearchDone(index_id_); - return; - } - - // double span = rc.RecordSection(hdr + ", do search"); - // search_job->AccumSearchCost(span); - - // step 3: pick up topk result - auto spec_k = file_->row_count_ < topk ? file_->row_count_ : topk; - if (spec_k == 0) { - LOG_ENGINE_WARNING_ << LogOut("[%s][%ld] Searching in an empty file. file location = %s", "search", 0, - file_->location_.c_str()); - } else { - std::unique_lock lock(search_job->mutex()); - XSearchTask::MergeTopkToResultSet(output_ids, output_distance, spec_k, nq, topk, ascending_reduce, - search_job->GetResultIds(), search_job->GetResultDistances()); - LOG_ENGINE_DEBUG_ << "Merged result: " - << "nq = " << nq << ", topk = " << topk << ", len of ids = " << output_ids.size() - << ", len of distance = " << output_distance.size(); - - if (search_job->GetResultIds().empty()) { - LOG_ENGINE_ERROR_ << "Result reduce error: result id array is empty!"; - } - } - - // span = rc.RecordSection(hdr + ", reduce topk"); - // search_job->AccumReduceCost(span); - } catch (std::exception& ex) { - LOG_ENGINE_ERROR_ << LogOut("[%s][%ld] SearchTask encounter exception: %s", "search", 0, ex.what()); - search_job->GetStatus() = Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - // step 4: notify to send result to client - search_job->SearchDone(index_id_); - } - - rc.ElapseFromBegin("totally cost"); - - // release index in resource - index_engine_ = nullptr; -} - -void -XSearchTask::MergeTopkToResultSet(const scheduler::ResultIds& src_ids, const scheduler::ResultDistances& src_distances, - size_t src_k, size_t nq, size_t topk, bool ascending, scheduler::ResultIds& tar_ids, - scheduler::ResultDistances& tar_distances) { - if (src_ids.empty()) { - LOG_ENGINE_DEBUG_ << LogOut("[%s][%d] Search result is empty.", "search", 0); - return; - } - - size_t tar_k = tar_ids.size() / nq; - size_t buf_k = std::min(topk, src_k + tar_k); - - scheduler::ResultIds buf_ids(nq * buf_k, -1); - scheduler::ResultDistances buf_distances(nq * buf_k, 0.0); - - for (uint64_t i = 0; i < nq; i++) { - size_t buf_k_j = 0, src_k_j = 0, tar_k_j = 0; - size_t buf_idx, src_idx, tar_idx; - - size_t buf_k_multi_i = buf_k * i; - size_t src_k_multi_i = topk * i; - size_t tar_k_multi_i = tar_k * i; - - while (buf_k_j < buf_k && src_k_j < src_k && tar_k_j < tar_k) { - src_idx = src_k_multi_i + src_k_j; - tar_idx = tar_k_multi_i + tar_k_j; - buf_idx = buf_k_multi_i + buf_k_j; - - if ((tar_ids[tar_idx] == -1) || // initialized value - (ascending && src_distances[src_idx] < tar_distances[tar_idx]) || - (!ascending && src_distances[src_idx] > tar_distances[tar_idx])) { - buf_ids[buf_idx] = src_ids[src_idx]; - buf_distances[buf_idx] = src_distances[src_idx]; - src_k_j++; - } else { - buf_ids[buf_idx] = tar_ids[tar_idx]; - buf_distances[buf_idx] = tar_distances[tar_idx]; - tar_k_j++; - } - buf_k_j++; - } - - if (buf_k_j < buf_k) { - if (src_k_j < src_k) { - while (buf_k_j < buf_k && src_k_j < src_k) { - buf_idx = buf_k_multi_i + buf_k_j; - src_idx = src_k_multi_i + src_k_j; - buf_ids[buf_idx] = src_ids[src_idx]; - buf_distances[buf_idx] = src_distances[src_idx]; - src_k_j++; - buf_k_j++; - } - } else { - while (buf_k_j < buf_k && tar_k_j < tar_k) { - buf_idx = buf_k_multi_i + buf_k_j; - tar_idx = tar_k_multi_i + tar_k_j; - buf_ids[buf_idx] = tar_ids[tar_idx]; - buf_distances[buf_idx] = tar_distances[tar_idx]; - tar_k_j++; - buf_k_j++; - } - } - } - } - tar_ids.swap(buf_ids); - tar_distances.swap(buf_distances); -} - -const std::string& -XSearchTask::GetLocation() const { - return file_->location_; -} - -size_t -XSearchTask::GetIndexId() const { - return file_->id_; -} - -// void -// XSearchTask::MergeTopkArray(std::vector& tar_ids, std::vector& tar_distance, uint64_t& tar_input_k, -// const std::vector& src_ids, const std::vector& src_distance, -// uint64_t src_input_k, uint64_t nq, uint64_t topk, bool ascending) { -// if (src_ids.empty() || src_distance.empty()) { -// return; -// } -// -// uint64_t output_k = std::min(topk, tar_input_k + src_input_k); -// std::vector id_buf(nq * output_k, -1); -// std::vector dist_buf(nq * output_k, 0.0); -// -// uint64_t buf_k, src_k, tar_k; -// uint64_t src_idx, tar_idx, buf_idx; -// uint64_t src_input_k_multi_i, tar_input_k_multi_i, buf_k_multi_i; -// -// for (uint64_t i = 0; i < nq; i++) { -// src_input_k_multi_i = src_input_k * i; -// tar_input_k_multi_i = tar_input_k * i; -// buf_k_multi_i = output_k * i; -// buf_k = src_k = tar_k = 0; -// while (buf_k < output_k && src_k < src_input_k && tar_k < tar_input_k) { -// src_idx = src_input_k_multi_i + src_k; -// tar_idx = tar_input_k_multi_i + tar_k; -// buf_idx = buf_k_multi_i + buf_k; -// if ((ascending && src_distance[src_idx] < tar_distance[tar_idx]) || -// (!ascending && src_distance[src_idx] > tar_distance[tar_idx])) { -// id_buf[buf_idx] = src_ids[src_idx]; -// dist_buf[buf_idx] = src_distance[src_idx]; -// src_k++; -// } else { -// id_buf[buf_idx] = tar_ids[tar_idx]; -// dist_buf[buf_idx] = tar_distance[tar_idx]; -// tar_k++; -// } -// buf_k++; -// } -// -// if (buf_k < output_k) { -// if (src_k < src_input_k) { -// while (buf_k < output_k && src_k < src_input_k) { -// src_idx = src_input_k_multi_i + src_k; -// buf_idx = buf_k_multi_i + buf_k; -// id_buf[buf_idx] = src_ids[src_idx]; -// dist_buf[buf_idx] = src_distance[src_idx]; -// src_k++; -// buf_k++; -// } -// } else { -// while (buf_k < output_k && tar_k < tar_input_k) { -// tar_idx = tar_input_k_multi_i + tar_k; -// buf_idx = buf_k_multi_i + buf_k; -// id_buf[buf_idx] = tar_ids[tar_idx]; -// dist_buf[buf_idx] = tar_distance[tar_idx]; -// tar_k++; -// buf_k++; -// } -// } -// } -// } -// -// tar_ids.swap(id_buf); -// tar_distance.swap(dist_buf); -// tar_input_k = output_k; -//} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/task/SearchTask.h b/core/src/scheduler/task/SearchTask.h deleted file mode 100644 index 267ab926d8..0000000000 --- a/core/src/scheduler/task/SearchTask.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include - -#include "Task.h" -#include "scheduler/Definition.h" -#include "scheduler/job/SearchJob.h" - -namespace milvus { -namespace scheduler { - -// TODO(wxyu): rewrite -class XSearchTask : public Task { - public: - explicit XSearchTask(const std::shared_ptr& context, SegmentSchemaPtr file, TaskLabelPtr label); - - void - Load(LoadType type, uint8_t device_id) override; - - void - Execute() override; - - public: - static void - MergeTopkToResultSet(const scheduler::ResultIds& src_ids, const scheduler::ResultDistances& src_distances, - size_t src_k, size_t nq, size_t topk, bool ascending, scheduler::ResultIds& tar_ids, - scheduler::ResultDistances& tar_distances); - - // static void - // MergeTopkArray(std::vector& tar_ids, std::vector& tar_distance, uint64_t& tar_input_k, - // const std::vector& src_ids, const std::vector& src_distance, uint64_t - // src_input_k, uint64_t nq, uint64_t topk, bool ascending); - - const std::string& - GetLocation() const; - - size_t - GetIndexId() const; - - public: - const std::shared_ptr context_; - - SegmentSchemaPtr file_; - - size_t index_id_ = 0; - int index_type_ = 0; - ExecutionEnginePtr index_engine_ = nullptr; - - // distance -- value 0 means two vectors equal, ascending reduce, L2/HAMMING/JACCARD/TONIMOTO ... - // similarity -- infinity value means two vectors equal, descending reduce, IP - bool ascending_reduce = true; -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/task/Task.h b/core/src/scheduler/task/Task.h deleted file mode 100644 index b8cc1ef45e..0000000000 --- a/core/src/scheduler/task/Task.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "Path.h" -#include "scheduler/job/Job.h" -#include "scheduler/tasklabel/TaskLabel.h" -#include "utils/Status.h" - -#include -#include -#include - -namespace milvus { -namespace scheduler { - -enum class LoadType { - DISK2CPU, - CPU2GPU, - GPU2CPU, - TEST, -}; - -enum class TaskType { - SearchTask, - DeleteTask, - BuildIndexTask, - TestTask, -}; - -class Task; - -using TaskPtr = std::shared_ptr; - -// TODO: re-design -class Task { - public: - explicit Task(TaskType type, TaskLabelPtr label) : type_(type), label_(std::move(label)) { - } - - /* - * Just Getter; - */ - inline TaskType - Type() const { - return type_; - } - - /* - * Transport path; - */ - inline Path& - path() { - return task_path_; - } - - /* - * Getter and Setter; - */ - inline TaskLabelPtr& - label() { - return label_; - } - - public: - virtual void - Load(LoadType type, uint8_t device_id) = 0; - - virtual void - Execute() = 0; - - public: - Path task_path_; - scheduler::JobWPtr job_; - TaskType type_; - TaskLabelPtr label_ = nullptr; -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/task/TestTask.cpp b/core/src/scheduler/task/TestTask.cpp deleted file mode 100644 index 7a382c6c2e..0000000000 --- a/core/src/scheduler/task/TestTask.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "scheduler/task/TestTask.h" - -#include - -#include "cache/GpuCacheMgr.h" - -namespace milvus { -namespace scheduler { - -TestTask::TestTask(const std::shared_ptr& context, SegmentSchemaPtr& file, TaskLabelPtr label) - : XSearchTask(context, file, std::move(label)) { -} - -void -TestTask::Load(LoadType type, uint8_t device_id) { - load_count_++; -} - -void -TestTask::Execute() { - { - std::lock_guard lock(mutex_); - exec_count_++; - done_ = true; - } - cv_.notify_one(); -} - -void -TestTask::Wait() { - std::unique_lock lock(mutex_); - cv_.wait(lock, [&] { return done_; }); -} - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/task/TestTask.h b/core/src/scheduler/task/TestTask.h deleted file mode 100644 index b70fea645c..0000000000 --- a/core/src/scheduler/task/TestTask.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -#include "SearchTask.h" - -namespace milvus { -namespace scheduler { - -class TestTask : public XSearchTask { - public: - explicit TestTask(const std::shared_ptr& context, SegmentSchemaPtr& file, TaskLabelPtr label); - - public: - void - Load(LoadType type, uint8_t device_id) override; - - void - Execute() override; - - void - Wait(); - - public: - uint64_t load_count_ = 0; - uint64_t exec_count_ = 0; - - bool done_ = false; - std::mutex mutex_; - std::condition_variable cv_; -}; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/tasklabel/BroadcastLabel.h b/core/src/scheduler/tasklabel/BroadcastLabel.h deleted file mode 100644 index 36bb519b91..0000000000 --- a/core/src/scheduler/tasklabel/BroadcastLabel.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "TaskLabel.h" - -#include - -namespace milvus { -namespace scheduler { - -class BroadcastLabel : public TaskLabel { - public: - BroadcastLabel() : TaskLabel(TaskLabelType::BROADCAST) { - } -}; - -using BroadcastLabelPtr = std::shared_ptr; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/tasklabel/SpecResLabel.h b/core/src/scheduler/tasklabel/SpecResLabel.h deleted file mode 100644 index 000cee9a55..0000000000 --- a/core/src/scheduler/tasklabel/SpecResLabel.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "TaskLabel.h" -#include "scheduler/ResourceMgr.h" - -#include -#include - -// class Resource; -// -// using ResourceWPtr = std::weak_ptr; - -namespace milvus { -namespace scheduler { - -class SpecResLabel : public TaskLabel { - public: - explicit SpecResLabel(const ResourceWPtr& resource) - : TaskLabel(TaskLabelType::SPECIFIED_RESOURCE), resource_(resource) { - } - - inline ResourceWPtr& - resource() { - return resource_; - } - - inline std::string - name() const override { - return resource_.lock()->name(); - } - - private: - ResourceWPtr resource_; -}; - -using SpecResLabelPtr = std::shared_ptr(); - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/scheduler/tasklabel/TaskLabel.h b/core/src/scheduler/tasklabel/TaskLabel.h deleted file mode 100644 index 38c100901a..0000000000 --- a/core/src/scheduler/tasklabel/TaskLabel.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -namespace milvus { -namespace scheduler { - -enum class TaskLabelType { - SPECIFIED_RESOURCE, // means must executing in special resource - BROADCAST, // means all enable-executor resource must execute task -}; - -class TaskLabel { - public: - inline TaskLabelType - Type() const { - return type_; - } - - virtual inline std::string - name() const { - return ""; - } - - protected: - explicit TaskLabel(TaskLabelType type) : type_(type) { - } - - private: - TaskLabelType type_; -}; - -using TaskLabelPtr = std::shared_ptr; - -} // namespace scheduler -} // namespace milvus diff --git a/core/src/search/Task.cpp b/core/src/search/Task.cpp deleted file mode 100644 index 58adeeb358..0000000000 --- a/core/src/search/Task.cpp +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#if 0 -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "db/Utils.h" -#include "db/engine/EngineFactory.h" -#include "search/Task.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" - -namespace milvus { -namespace search { - -Task::Task(const std::shared_ptr& context, SegmentSchemaPtr& file, - milvus::query::GeneralQueryPtr general_query, std::unordered_map& attr_type, - context::HybridSearchContextPtr hybrid_search_context) - : context_(context), - file_(file), - general_query_(general_query), - attr_type_(attr_type), - hybrid_search_context_(hybrid_search_context) { - if (file_) { - // distance -- value 0 means two vectors equal, ascending reduce, L2/HAMMING/JACCARD/TONIMOTO ... - // similarity -- infinity value means two vectors equal, descending reduce, IP - if (file_->metric_type_ == static_cast(engine::MetricType::IP) && - file_->engine_type_ != static_cast(engine::EngineType::FAISS_PQ)) { - ascending_reduce = false; - } - - engine::EngineType engine_type; - if (file->file_type_ == engine::meta::SegmentSchema::FILE_TYPE::RAW || - file->file_type_ == engine::meta::SegmentSchema::FILE_TYPE::TO_INDEX || - file->file_type_ == engine::meta::SegmentSchema::FILE_TYPE::BACKUP) { - engine_type = engine::utils::IsBinaryMetricType(file->metric_type_) ? engine::EngineType::FAISS_BIN_IDMAP - : engine::EngineType::FAISS_IDMAP; - } else { - engine_type = (engine::EngineType)file->engine_type_; - } - - milvus::json json_params; - if (!file_->index_params_.empty()) { - json_params = milvus::json::parse(file_->index_params_); - } - - index_engine_ = engine::EngineFactory::Build(file_->dimension_, file_->location_, engine_type, - (engine::MetricType)file_->metric_type_, json_params); - } -} - -void -Task::Load() { - auto load_ctx = context_->Follower("XSearchTask::Load " + std::to_string(file_->id_)); - - Status stat = Status::OK(); - std::string error_msg; - std::string type_str; - - try { - stat = index_engine_->Load(); - type_str = "IDSK2CPU"; - } catch (std::exception& ex) { - // typical error: out of disk space or permition denied - error_msg = "Failed to load index file: " + std::string(ex.what()); - stat = Status(SERVER_UNEXPECTED_ERROR, error_msg); - } - - if (!stat.ok()) { - return; - } -} - -void -Task::Execute() { - auto execute_ctx = context_->Follower("XSearchTask::Execute " + std::to_string(index_id_)); - - if (index_engine_ == nullptr) { - return; - } - - TimeRecorder rc("DoSearch file id:" + std::to_string(index_id_)); - - std::vector output_ids; - std::vector output_distance; - - // step 1: allocate memory - - try { - // step 2: search - Status s; - if (general_query_ != nullptr) { - faiss::ConcurrentBitsetPtr bitset; - uint64_t nq, topk; - s = index_engine_->ExecBinaryQuery(general_query_, bitset, attr_type_, nq, topk, output_distance, - output_ids); - - if (!s.ok()) { - return; - } - - auto spec_k = file_->row_count_ < topk ? file_->row_count_ : topk; - if (spec_k == 0) { - ENGINE_LOG_WARNING << "Searching in an empty file. file location = " << file_->location_; - } - - { - if (result_ids_.size() > spec_k) { - if (result_ids_.front() == -1) { - result_ids_.resize(spec_k * nq); - result_distances_.resize(spec_k * nq); - } - } - Task::MergeTopkToResultSet(output_ids, output_distance, spec_k, nq, topk, ascending_reduce, result_ids_, - result_distances_); - } - index_engine_ = nullptr; - execute_ctx->GetTraceContext()->GetSpan()->Finish(); - return; - } - - if (!s.ok()) { - return; - } - } catch (std::exception& ex) { - ENGINE_LOG_ERROR << "SearchTask encounter exception: " << ex.what(); - // search_job->IndexSearchDone(index_id_);//mark as done avoid dead lock, even search failed - } - - rc.ElapseFromBegin("totally cost"); - - // release index in resource - index_engine_ = nullptr; - - execute_ctx->GetTraceContext()->GetSpan()->Finish(); -} - -void -Task::MergeTopkToResultSet(const milvus::search::ResultIds& src_ids, - const milvus::search::ResultDistances& src_distances, size_t src_k, size_t nq, size_t topk, - bool ascending, milvus::search::ResultIds& tar_ids, - milvus::search::ResultDistances& tar_distances) { - if (src_ids.empty()) { - return; - } - - size_t tar_k = tar_ids.size() / nq; - size_t buf_k = std::min(topk, src_k + tar_k); - - ResultIds buf_ids(nq * buf_k, -1); - ResultDistances buf_distances(nq * buf_k, 0.0); - - for (uint64_t i = 0; i < nq; i++) { - size_t buf_k_j = 0, src_k_j = 0, tar_k_j = 0; - size_t buf_idx, src_idx, tar_idx; - - size_t buf_k_multi_i = buf_k * i; - size_t src_k_multi_i = topk * i; - size_t tar_k_multi_i = tar_k * i; - - while (buf_k_j < buf_k && src_k_j < src_k && tar_k_j < tar_k) { - src_idx = src_k_multi_i + src_k_j; - tar_idx = tar_k_multi_i + tar_k_j; - buf_idx = buf_k_multi_i + buf_k_j; - - if ((tar_ids[tar_idx] == -1) || // initialized value - (ascending && src_distances[src_idx] < tar_distances[tar_idx]) || - (!ascending && src_distances[src_idx] > tar_distances[tar_idx])) { - buf_ids[buf_idx] = src_ids[src_idx]; - buf_distances[buf_idx] = src_distances[src_idx]; - src_k_j++; - } else { - buf_ids[buf_idx] = tar_ids[tar_idx]; - buf_distances[buf_idx] = tar_distances[tar_idx]; - tar_k_j++; - } - buf_k_j++; - } - - if (buf_k_j < buf_k) { - if (src_k_j < src_k) { - while (buf_k_j < buf_k && src_k_j < src_k) { - buf_idx = buf_k_multi_i + buf_k_j; - src_idx = src_k_multi_i + src_k_j; - buf_ids[buf_idx] = src_ids[src_idx]; - buf_distances[buf_idx] = src_distances[src_idx]; - src_k_j++; - buf_k_j++; - } - } else { - while (buf_k_j < buf_k && tar_k_j < tar_k) { - buf_idx = buf_k_multi_i + buf_k_j; - tar_idx = tar_k_multi_i + tar_k_j; - buf_ids[buf_idx] = tar_ids[tar_idx]; - buf_distances[buf_idx] = tar_distances[tar_idx]; - tar_k_j++; - buf_k_j++; - } - } - } - } - tar_ids.swap(buf_ids); - tar_distances.swap(buf_distances); -} - -} // namespace search -} // namespace milvus - -#endif diff --git a/core/src/search/Task.h b/core/src/search/Task.h deleted file mode 100644 index 8bec31ac1a..0000000000 --- a/core/src/search/Task.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#if 0 - -#pragma once - -#include -#include -#include -#include -#include - -#include "context/HybridSearchContext.h" -#include "db/Types.h" -#include "db/engine/ExecutionEngine.h" -#include "db/meta/MetaTypes.h" -#include "server/context/Context.h" -#include "utils/Status.h" - -namespace milvus { - -namespace context { -struct HybridSearchContext; -using HybridSearchContextPtr = std::shared_ptr; -} // namespace context - -namespace search { - -using SegmentSchemaPtr = engine::meta::SegmentSchemaPtr; - -using Id2IndexMap = std::unordered_map; - -using ResultIds = engine::ResultIds; -using ResultDistances = engine::ResultDistances; - -class Task { - public: - explicit Task(const std::shared_ptr& context, SegmentSchemaPtr& file, - query::GeneralQueryPtr general_query, std::unordered_map& attr_type, - context::HybridSearchContextPtr hybrid_search_context); - - void - Load(); - - void - Execute(); - - public: - static void - MergeTopkToResultSet(const ResultIds& src_ids, const ResultDistances& src_distances, size_t src_k, size_t nq, - size_t topk, bool ascending, ResultIds& tar_ids, ResultDistances& tar_distances); - - const std::string& - GetLocation() const; - - size_t - GetIndexId() const; - - public: - const std::shared_ptr context_; - - SegmentSchemaPtr file_; - - size_t index_id_ = 0; - int index_type_ = 0; - engine::ExecutionEnginePtr index_engine_ = nullptr; - - // distance -- value 0 means two vectors equal, ascending reduce, L2/HAMMING/JACCARD/TONIMOTO ... - // similarity -- infinity value means two vectors equal, descending reduce, IP - bool ascending_reduce = true; - - query::GeneralQueryPtr general_query_; - std::unordered_map attr_type_; - context::HybridSearchContextPtr hybrid_search_context_; - - ResultIds result_ids_; - ResultDistances result_distances_; -}; - -using TaskPtr = std::shared_ptr; - -} // namespace search -} // namespace milvus - -#endif diff --git a/core/src/search/TaskInst.cpp b/core/src/search/TaskInst.cpp deleted file mode 100644 index 00e28bc05f..0000000000 --- a/core/src/search/TaskInst.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#if 0 - -#pragma once - -#include -#include -#include -#include - -#include "search/TaskInst.h" - -namespace milvus { -namespace search { - -void -TaskInst::Start() { - running_ = true; - load_thread_ = std::make_shared(&TaskInst::StartLoadTask, this); - exec_thread_ = std::make_shared(&TaskInst::StartExecuteTask, this); -} - -void -TaskInst::Stop() { - running_ = false; - StopExecuteTask(); - StopLoadTask(); -} - -std::queue& -TaskInst::load_queue() { - return load_queue_; -} - -std::queue& -TaskInst::exec_queue() { - return exec_queue_; -} - -std::condition_variable& -TaskInst::load_cv() { - return load_cv_; -} - -std::condition_variable& -TaskInst::exec_cv() { - return exec_cv_; -} - -void -TaskInst::StartLoadTask() { - while (running_) { - std::unique_lock lock(load_mutex_); - load_cv_.wait(lock, [this] { return !load_queue_.empty(); }); - while (!load_queue_.empty()) { - auto task = load_queue_.front(); - task->Load(); - load_queue_.pop(); - exec_queue_.push(task); - exec_cv_.notify_one(); - } - } -} - -void -TaskInst::StartExecuteTask() { - while (running_) { - std::unique_lock lock(exec_mutex_); - exec_cv_.wait(lock, [this] { return !exec_queue_.empty(); }); - while (!exec_queue_.empty()) { - auto task = exec_queue_.front(); - task->Execute(); - exec_queue_.pop(); - } - } -} - -void -TaskInst::StopLoadTask() { - { - std::lock_guard lock(load_mutex_); - load_queue_.push(nullptr); - load_cv_.notify_one(); - if (load_thread_->joinable()) { - load_thread_->join(); - } - load_thread_ = nullptr; - } -} - -void -TaskInst::StopExecuteTask() { - { - std::lock_guard lock(exec_mutex_); - exec_queue_.push(nullptr); - exec_cv_.notify_one(); - if (exec_thread_->joinable()) { - exec_thread_->join(); - } - exec_thread_ = nullptr; - } -} - -} // namespace search -} // namespace milvus - -#endif diff --git a/core/src/search/TaskInst.h b/core/src/search/TaskInst.h deleted file mode 100644 index 2331436ce7..0000000000 --- a/core/src/search/TaskInst.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#if 0 - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "Task.h" - -namespace milvus { -namespace search { - -class TaskInst { - public: - static TaskInst& - GetInstance() { - static TaskInst instance; - return instance; - } - - void - Start(); - void - Stop(); - - std::queue& - load_queue(); - std::queue& - exec_queue(); - - std::condition_variable& - load_cv(); - std::condition_variable& - exec_cv(); - - private: - TaskInst() = default; - ~TaskInst() = default; - - void - StartLoadTask(); - void - StartExecuteTask(); - void - StopLoadTask(); - void - StopExecuteTask(); - - private: - bool running_; - - std::shared_ptr load_thread_; - std::shared_ptr exec_thread_; - - std::queue load_queue_; - std::queue exec_queue_; - - std::condition_variable load_cv_; - std::condition_variable exec_cv_; - std::mutex exec_mutex_; - std::mutex load_mutex_; -}; - -} // namespace search -} // namespace milvus - -#endif diff --git a/core/src/segment/Attr.cpp b/core/src/segment/Attr.cpp deleted file mode 100644 index 30d4b91a04..0000000000 --- a/core/src/segment/Attr.cpp +++ /dev/null @@ -1,166 +0,0 @@ -// 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 limitations -// under the License. - -#include -#include -#include -#include -#include -#include -#include - -#include "Vectors.h" -#include "segment/Attr.h" -#include "utils/Log.h" - -namespace milvus { -namespace segment { - -Attr::Attr(const std::vector& data, size_t nbytes, const std::vector uids, const std::string& name) - : data_(std::move(data)), nbytes_(nbytes), uids_(std::move(uids)), name_(name) { -} - -// void -// Attr::AddAttr(const std::vector& data, size_t nbytes) { -// data_.reserve(data_.size() + data.size()); -// data_.insert(data_.end(), std::make_move_iterator(data.begin()), std::make_move_iterator(data.end())); -// nbytes_ += nbytes; -//} -// -// void -// Attr::AddUids(const std::vector& uids) { -// uids_.reserve(uids_.size() + uids.size()); -// uids_.insert(uids_.end(), std::make_move_iterator(uids.begin()), std::make_move_iterator(uids.end())); -//} - -// void -// Attr::SetName(const std::string& name) { -// name_ = name; -//} - -const std::vector& -Attr::GetData() const { - return data_; -} - -const std::string& -Attr::GetName() const { - return name_; -} - -const std::string& -Attr::GetCollectionId() { - return collection_id_; -} - -const size_t& -Attr::GetNbytes() const { - return nbytes_; -} - -const std::vector& -Attr::GetUids() const { - return uids_; -} - -size_t -Attr::GetCount() const { - return uids_.size(); -} - -size_t -Attr::GetCodeLength() const { - return uids_.size() == 0 ? 0 : nbytes_ / uids_.size(); -} - -// void -// Attr::Erase(int32_t offset) { -// auto code_length = GetCodeLength(); -// if (code_length != 0) { -// auto step = offset * code_length; -// data_.erase(data_.begin() + step, data_.begin() + step + code_length); -// uids_.erase(uids_.begin() + offset, uids_.begin() + offset + 1); -// } -//} - -void -Attr::Erase(std::vector& offsets) { - if (offsets.empty()) { - return; - } - - // Sort and remove duplicates - auto start = std::chrono::high_resolution_clock::now(); - - std::sort(offsets.begin(), offsets.end()); - - auto end = std::chrono::high_resolution_clock::now(); - std::chrono::duration diff = end - start; - LOG_ENGINE_DEBUG_ << "Sorting " << offsets.size() << " offsets to delete took " << diff.count() << " s"; - - start = std::chrono::high_resolution_clock::now(); - - offsets.erase(std::unique(offsets.begin(), offsets.end()), offsets.end()); - - end = std::chrono::high_resolution_clock::now(); - diff = end - start; - LOG_ENGINE_DEBUG_ << "Deduplicating " << offsets.size() << " offsets to delete took " << diff.count() << " s"; - - LOG_ENGINE_DEBUG_ << "Attributes begin erasing..."; - - size_t new_size = uids_.size() - offsets.size(); - std::vector new_uids(new_size); - auto code_length = GetCodeLength(); - std::vector new_data(new_size * code_length); - - auto count = 0; - auto skip = offsets.cbegin(); - auto loop_size = uids_.size(); - - for (size_t i = 0; i < loop_size;) { - while (skip != offsets.cend() && i == (size_t)(*skip)) { - ++i; - ++skip; - } - - if (i == loop_size) { - break; - } - - new_uids[count] = uids_[i]; - - for (size_t j = 0; j < code_length; ++j) { - new_data[count * code_length + j] = data_[i * code_length + j]; - } - - ++count; - ++i; - } - - data_.clear(); - uids_.clear(); - data_.swap(new_data); - uids_.swap(new_uids); - - end = std::chrono::high_resolution_clock::now(); - diff = end - start; - LOG_ENGINE_DEBUG_ << "Erasing " << offsets.size() << " vectors out of " << loop_size << " vectors took " - << diff.count() << " s"; -} - -} // namespace segment -} // namespace milvus diff --git a/core/src/segment/Attr.h b/core/src/segment/Attr.h deleted file mode 100644 index f88a5300ac..0000000000 --- a/core/src/segment/Attr.h +++ /dev/null @@ -1,89 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include -#include - -namespace milvus { -namespace segment { - -class Attr { - public: - Attr(const std::vector& data, size_t nbytes, const std::vector uids, const std::string& name); - - Attr(); - - // void - // AddAttr(const std::vector& data, size_t nbytes); - // - // void - // AddUids(const std::vector& uids); - // - // void - // SetName(const std::string& name); - - const std::vector& - GetData() const; - - const std::string& - GetName() const; - - const std::string& - GetCollectionId(); - - const size_t& - GetNbytes() const; - - const std::vector& - GetUids() const; - - size_t - GetCount() const; - - size_t - GetCodeLength() const; - - // void - // Erase(int32_t offset); - - void - Erase(std::vector& offsets); - - // No copy and move - Attr(const Attr&) = delete; - Attr(Attr&&) = delete; - - Attr& - operator=(const Attr&) = delete; - Attr& - operator=(Attr&&) = delete; - - private: - std::vector data_; - size_t nbytes_; - std::vector uids_; - std::string name_; - std::string collection_id_; -}; - -using AttrPtr = std::shared_ptr; - -} // namespace segment -} // namespace milvus diff --git a/core/src/segment/AttrIndex.h b/core/src/segment/AttrIndex.h deleted file mode 100644 index ad11eb9e94..0000000000 --- a/core/src/segment/AttrIndex.h +++ /dev/null @@ -1,49 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include - -namespace milvus { -namespace segment { - -class AttrIndex { - public: - explicit AttrIndex(AttrIndexWrapper attr_index_wrapper); - - void - Get(AttrIndexWrapper& attr_index_wrapper); - - // No copy and move - AttrIndex(const AttrIndex&) = delete; - AttrIndex(AttrIndex&&) = delete; - - AttrIndex& - operator=(const AttrIndex&) = delete; - AttrIndex& - operator=(AttrIndex&&) = delete; - - private: - AttrIndexWrapper attr_index_wrapper_; -}; - -using AttrIndexPtr = std::shared_ptr; - -} // namespace segment -} // namespace milvus diff --git a/core/src/segment/Attrs.h b/core/src/segment/Attrs.h deleted file mode 100644 index 26d9507c2e..0000000000 --- a/core/src/segment/Attrs.h +++ /dev/null @@ -1,38 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include -#include - -#include "Attr.h" - -namespace milvus { -namespace segment { - -struct Attrs { - // Attrs() = default; - - std::unordered_map attrs; -}; - -using AttrsPtr = std::shared_ptr; - -} // namespace segment -} // namespace milvus diff --git a/core/src/segment/AttrsIndex.h b/core/src/segment/AttrsIndex.h deleted file mode 100644 index 73f6730578..0000000000 --- a/core/src/segment/AttrsIndex.h +++ /dev/null @@ -1,33 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include - -#include "AttrIndex.h" - -namespace milvus { -namespace segment { - -struct AttrsIndex { - std::unordered_map attr_indexes; -}; - -} // namespace segment -} // namespace milvus diff --git a/core/src/segment/DeletedDocs.cpp b/core/src/segment/DeletedDocs.cpp deleted file mode 100644 index 26ca61c0c3..0000000000 --- a/core/src/segment/DeletedDocs.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// 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 limitations -// under the License. - -#include "segment/DeletedDocs.h" - -namespace milvus { -namespace segment { - -DeletedDocs::DeletedDocs(const std::vector& deleted_doc_offsets) : deleted_doc_offsets_(deleted_doc_offsets) { -} - -void -DeletedDocs::AddDeletedDoc(offset_t offset) { - deleted_doc_offsets_.emplace_back(offset); -} - -const std::vector& -DeletedDocs::GetDeletedDocs() const { - return deleted_doc_offsets_; -} - -// const std::string& -// DeletedDocs::GetName() const { -// return name_; -//} - -size_t -DeletedDocs::GetSize() const { - return deleted_doc_offsets_.size(); -} - -} // namespace segment -} // namespace milvus diff --git a/core/src/segment/DeletedDocs.h b/core/src/segment/DeletedDocs.h deleted file mode 100644 index 288d4e9587..0000000000 --- a/core/src/segment/DeletedDocs.h +++ /dev/null @@ -1,68 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include - -namespace milvus { -namespace segment { - -using offset_t = int32_t; - -class DeletedDocs { - public: - explicit DeletedDocs(const std::vector& deleted_doc_offsets); - - DeletedDocs() = default; - - void - AddDeletedDoc(offset_t offset); - - const std::vector& - GetDeletedDocs() const; - - // // TODO - // const std::string& - // GetName() const; - - size_t - GetSize() const; - - // void - // GetBitset(faiss::ConcurrentBitsetPtr& bitset); - - // No copy and move - DeletedDocs(const DeletedDocs&) = delete; - DeletedDocs(DeletedDocs&&) = delete; - - DeletedDocs& - operator=(const DeletedDocs&) = delete; - DeletedDocs& - operator=(DeletedDocs&&) = delete; - - private: - std::vector deleted_doc_offsets_; - // faiss::ConcurrentBitsetPtr bitset_; - // const std::string name_ = "deleted_docs"; -}; - -using DeletedDocsPtr = std::shared_ptr; - -} // namespace segment -} // namespace milvus diff --git a/core/src/segment/IdBloomFilter.cpp b/core/src/segment/IdBloomFilter.cpp deleted file mode 100644 index 890faa0054..0000000000 --- a/core/src/segment/IdBloomFilter.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// 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 limitations -// under the License. - -#include "segment/IdBloomFilter.h" -#include "utils/Log.h" -#include "utils/Status.h" - -#include - -namespace milvus { -namespace segment { - -IdBloomFilter::IdBloomFilter(scaling_bloom_t* bloom_filter) : bloom_filter_(bloom_filter) { -} - -IdBloomFilter::~IdBloomFilter() { - const std::lock_guard lock(mutex_); - if (bloom_filter_) { - free_scaling_bloom(bloom_filter_); - } -} - -scaling_bloom_t* -IdBloomFilter::GetBloomFilter() { - const std::lock_guard lock(mutex_); - return bloom_filter_; -} - -bool -IdBloomFilter::Check(doc_id_t uid) { - std::string s = std::to_string(uid); - const std::lock_guard lock(mutex_); - return scaling_bloom_check(bloom_filter_, s.c_str(), s.size()); -} - -Status -IdBloomFilter::Add(doc_id_t uid) { - std::string s = std::to_string(uid); - const std::lock_guard lock(mutex_); - if (scaling_bloom_add(bloom_filter_, s.c_str(), s.size(), uid) == -1) { - // Counter overflow does not affect bloom filter's normal functionality - LOG_ENGINE_WARNING_ << "Warning adding id=" << s << " to bloom filter: 4 bit counter Overflow"; - // return Status(DB_BLOOM_FILTER_ERROR, "Bloom filter error: 4 bit counter Overflow"); - } - return Status::OK(); -} - -Status -IdBloomFilter::Remove(doc_id_t uid) { - std::string s = std::to_string(uid); - const std::lock_guard lock(mutex_); - if (scaling_bloom_remove(bloom_filter_, s.c_str(), s.size(), uid) == -1) { - // Should never go in here, but just to be safe - LOG_ENGINE_WARNING_ << "Warning removing id=" << s << " in bloom filter: Decrementing zero in counter"; - // return Status(DB_BLOOM_FILTER_ERROR, "Error removing in bloom filter: Decrementing zero in counter"); - } - return Status::OK(); -} - -// const std::string& -// IdBloomFilter::GetName() const { -// return name_; -//} - -size_t -IdBloomFilter::Size() { - return bloom_filter_->num_bytes; -} - -} // namespace segment -} // namespace milvus diff --git a/core/src/segment/IdBloomFilter.h b/core/src/segment/IdBloomFilter.h deleted file mode 100644 index 6c3c891a4f..0000000000 --- a/core/src/segment/IdBloomFilter.h +++ /dev/null @@ -1,73 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include - -#include "dablooms/dablooms.h" -#include "utils/Status.h" - -namespace milvus { -namespace segment { - -using doc_id_t = int64_t; - -class IdBloomFilter { - public: - explicit IdBloomFilter(scaling_bloom_t* bloom_filter); - - ~IdBloomFilter(); - - scaling_bloom_t* - GetBloomFilter(); - - bool - Check(doc_id_t uid); - - Status - Add(doc_id_t uid); - - Status - Remove(doc_id_t uid); - - size_t - Size(); - - // const std::string& - // GetName() const; - - // No copy and move - IdBloomFilter(const IdBloomFilter&) = delete; - IdBloomFilter(IdBloomFilter&&) = delete; - - IdBloomFilter& - operator=(const IdBloomFilter&) = delete; - IdBloomFilter& - operator=(IdBloomFilter&&) = delete; - - private: - scaling_bloom_t* bloom_filter_; - // const std::string name_ = "bloom_filter"; - std::mutex mutex_; -}; - -using IdBloomFilterPtr = std::shared_ptr; - -} // namespace segment -} // namespace milvus diff --git a/core/src/segment/IdIndex.h b/core/src/segment/IdIndex.h deleted file mode 100644 index 02dab78074..0000000000 --- a/core/src/segment/IdIndex.h +++ /dev/null @@ -1,32 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include - -#include "AttrIndex.h" - -namespace milvus { -namespace segment { - -class IdIndex : public AttrIndex {}; - -using IdIndexPtr = std::shared_ptr; - -} // namespace segment -} // namespace milvus diff --git a/core/src/segment/SegmentReader.cpp b/core/src/segment/SegmentReader.cpp deleted file mode 100644 index d717bf863b..0000000000 --- a/core/src/segment/SegmentReader.cpp +++ /dev/null @@ -1,152 +0,0 @@ -// 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 limitations -// under the License. - -#include "segment/SegmentReader.h" - -#include - -#include "Vectors.h" -#include "codecs/default/DefaultCodec.h" -#include "storage/disk/DiskIOReader.h" -#include "storage/disk/DiskIOWriter.h" -#include "storage/disk/DiskOperation.h" -#include "utils/Log.h" - -namespace milvus { -namespace segment { - -SegmentReader::SegmentReader(const std::string& directory) { - storage::IOReaderPtr reader_ptr = std::make_shared(); - storage::IOWriterPtr writer_ptr = std::make_shared(); - storage::OperationPtr operation_ptr = std::make_shared(directory); - fs_ptr_ = std::make_shared(reader_ptr, writer_ptr, operation_ptr); - segment_ptr_ = std::make_shared(); -} - -Status -SegmentReader::LoadCache(bool& in_cache) { - in_cache = false; - return Status::OK(); -} - -Status -SegmentReader::Load() { - // TODO(zhiru) - codec::DefaultCodec default_codec; - try { - fs_ptr_->operation_ptr_->CreateDirectory(); - default_codec.GetVectorsFormat()->read(fs_ptr_, segment_ptr_->vectors_ptr_); - default_codec.GetAttrsFormat()->read(fs_ptr_, segment_ptr_->attrs_ptr_); - // default_codec.GetVectorIndexFormat()->read(fs_ptr_, segment_ptr_->vector_index_ptr_); - default_codec.GetDeletedDocsFormat()->read(fs_ptr_, segment_ptr_->deleted_docs_ptr_); - } catch (std::exception& e) { - return Status(DB_ERROR, e.what()); - } - return Status::OK(); -} - -Status -SegmentReader::LoadVectors(off_t offset, size_t num_bytes, std::vector& raw_vectors) { - codec::DefaultCodec default_codec; - try { - fs_ptr_->operation_ptr_->CreateDirectory(); - default_codec.GetVectorsFormat()->read_vectors(fs_ptr_, offset, num_bytes, raw_vectors); - } catch (std::exception& e) { - std::string err_msg = "Failed to load raw vectors: " + std::string(e.what()); - LOG_ENGINE_ERROR_ << err_msg; - return Status(DB_ERROR, err_msg); - } - return Status::OK(); -} - -Status -SegmentReader::LoadUids(std::vector& uids) { - codec::DefaultCodec default_codec; - try { - fs_ptr_->operation_ptr_->CreateDirectory(); - default_codec.GetVectorsFormat()->read_uids(fs_ptr_, uids); - } catch (std::exception& e) { - std::string err_msg = "Failed to load uids: " + std::string(e.what()); - LOG_ENGINE_ERROR_ << err_msg; - return Status(DB_ERROR, err_msg); - } - return Status::OK(); -} - -Status -SegmentReader::GetSegment(SegmentPtr& segment_ptr) { - segment_ptr = segment_ptr_; - return Status::OK(); -} - -Status -SegmentReader::LoadVectorIndex(const std::string& location, segment::VectorIndexPtr& vector_index_ptr) { - codec::DefaultCodec default_codec; - try { - fs_ptr_->operation_ptr_->CreateDirectory(); - default_codec.GetVectorIndexFormat()->read(fs_ptr_, location, vector_index_ptr); - } catch (std::exception& e) { - std::string err_msg = "Failed to load vector index: " + std::string(e.what()); - LOG_ENGINE_ERROR_ << err_msg; - return Status(DB_ERROR, err_msg); - } - return Status::OK(); -} - -Status -SegmentReader::LoadBloomFilter(segment::IdBloomFilterPtr& id_bloom_filter_ptr) { - codec::DefaultCodec default_codec; - try { - fs_ptr_->operation_ptr_->CreateDirectory(); - default_codec.GetIdBloomFilterFormat()->read(fs_ptr_, id_bloom_filter_ptr); - } catch (std::exception& e) { - std::string err_msg = "Failed to load bloom filter: " + std::string(e.what()); - LOG_ENGINE_ERROR_ << err_msg; - return Status(DB_ERROR, err_msg); - } - return Status::OK(); -} - -Status -SegmentReader::LoadDeletedDocs(segment::DeletedDocsPtr& deleted_docs_ptr) { - codec::DefaultCodec default_codec; - try { - fs_ptr_->operation_ptr_->CreateDirectory(); - default_codec.GetDeletedDocsFormat()->read(fs_ptr_, deleted_docs_ptr); - } catch (std::exception& e) { - std::string err_msg = "Failed to load deleted docs: " + std::string(e.what()); - LOG_ENGINE_ERROR_ << err_msg; - return Status(DB_ERROR, err_msg); - } - return Status::OK(); -} - -Status -SegmentReader::ReadDeletedDocsSize(size_t& size) { - codec::DefaultCodec default_codec; - try { - fs_ptr_->operation_ptr_->CreateDirectory(); - default_codec.GetDeletedDocsFormat()->readSize(fs_ptr_, size); - } catch (std::exception& e) { - std::string err_msg = "Failed to read deleted docs size: " + std::string(e.what()); - LOG_ENGINE_ERROR_ << err_msg; - return Status(DB_ERROR, err_msg); - } - return Status::OK(); -} -} // namespace segment -} // namespace milvus diff --git a/core/src/segment/SegmentReader.h b/core/src/segment/SegmentReader.h deleted file mode 100644 index d260bbc3d1..0000000000 --- a/core/src/segment/SegmentReader.h +++ /dev/null @@ -1,71 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include -#include - -#include "segment/Types.h" -#include "storage/FSHandler.h" -#include "utils/Status.h" - -namespace milvus { -namespace segment { - -class SegmentReader { - public: - explicit SegmentReader(const std::string& directory); - - // TODO(zhiru) - Status - LoadCache(bool& in_cache); - - Status - Load(); - - Status - LoadVectors(off_t offset, size_t num_bytes, std::vector& raw_vectors); - - Status - LoadUids(std::vector& uids); - - Status - LoadVectorIndex(const std::string& location, segment::VectorIndexPtr& vector_index_ptr); - - Status - LoadBloomFilter(segment::IdBloomFilterPtr& id_bloom_filter_ptr); - - Status - LoadDeletedDocs(segment::DeletedDocsPtr& deleted_docs_ptr); - - Status - GetSegment(SegmentPtr& segment_ptr); - - Status - ReadDeletedDocsSize(size_t& size); - - private: - storage::FSHandlerPtr fs_ptr_; - SegmentPtr segment_ptr_; -}; - -using SegmentReaderPtr = std::shared_ptr; - -} // namespace segment -} // namespace milvus diff --git a/core/src/segment/SegmentWriter.cpp b/core/src/segment/SegmentWriter.cpp deleted file mode 100644 index f6bc412eb3..0000000000 --- a/core/src/segment/SegmentWriter.cpp +++ /dev/null @@ -1,355 +0,0 @@ -// 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 limitations -// under the License. - -#include "segment/SegmentWriter.h" - -#include -#include -#include - -#include "SegmentReader.h" -#include "Vectors.h" -#include "codecs/default/DefaultCodec.h" -#include "db/Utils.h" -#include "storage/disk/DiskIOReader.h" -#include "storage/disk/DiskIOWriter.h" -#include "storage/disk/DiskOperation.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" - -namespace milvus { -namespace segment { - -SegmentWriter::SegmentWriter(const std::string& directory) { - storage::IOReaderPtr reader_ptr = std::make_shared(); - storage::IOWriterPtr writer_ptr = std::make_shared(); - storage::OperationPtr operation_ptr = std::make_shared(directory); - fs_ptr_ = std::make_shared(reader_ptr, writer_ptr, operation_ptr); - segment_ptr_ = std::make_shared(); -} - -Status -SegmentWriter::AddVectors(const std::string& name, const std::vector& data, - const std::vector& uids) { - segment_ptr_->vectors_ptr_->AddData(data); - segment_ptr_->vectors_ptr_->AddUids(uids); - segment_ptr_->vectors_ptr_->SetName(name); - - return Status::OK(); -} - -Status -SegmentWriter::AddVectors(const std::string& name, const uint8_t* data, uint64_t size, - const std::vector& uids) { - segment_ptr_->vectors_ptr_->AddData(data, size); - segment_ptr_->vectors_ptr_->AddUids(uids); - segment_ptr_->vectors_ptr_->SetName(name); - - return Status::OK(); -} - -Status -SegmentWriter::AddAttrs(const std::string& name, const std::unordered_map& attr_nbytes, - const std::unordered_map>& attr_data, - const std::vector& uids) { - auto attr_data_it = attr_data.begin(); - auto attrs = segment_ptr_->attrs_ptr_->attrs; - for (; attr_data_it != attr_data.end(); ++attr_data_it) { - AttrPtr attr = std::make_shared(attr_data_it->second, attr_nbytes.at(attr_data_it->first), uids, - attr_data_it->first); - segment_ptr_->attrs_ptr_->attrs.insert(std::make_pair(attr_data_it->first, attr)); - - // if (attrs.find(attr_data_it->first) != attrs.end()) { - // segment_ptr_->attrs_ptr_->attrs.at(attr_data_it->first) - // ->AddAttr(attr_data_it->second, attr_nbytes.at(attr_data_it->first)); - // segment_ptr_->attrs_ptr_->attrs.at(attr_data_it->first)->AddUids(uids); - // } else { - // AttrPtr attr = std::make_shared(attr_data_it->second, attr_nbytes.at(attr_data_it->first), - // uids, - // attr_data_it->first); - // segment_ptr_->attrs_ptr_->attrs.insert(std::make_pair(attr_data_it->first, attr)); - // } - } - return Status::OK(); -} - -Status -SegmentWriter::SetVectorIndex(const milvus::knowhere::VecIndexPtr& index) { - segment_ptr_->vector_index_ptr_->SetVectorIndex(index); - return Status::OK(); -} - -Status -SegmentWriter::Serialize() { - TimeRecorder recorder("SegmentWriter::Serialize"); - - auto status = WriteBloomFilter(); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << status.message(); - return status; - } - - recorder.RecordSection("Writing bloom filter done"); - - status = WriteVectors(); - if (!status.ok()) { - LOG_ENGINE_ERROR_ << "Write vectors fail: " << status.message(); - return status; - } - - status = WriteAttrs(); - if (!status.ok()) { - return status; - } - - recorder.RecordSection("Writing vectors and uids done"); - - // Write an empty deleted doc - status = WriteDeletedDocs(); - - recorder.RecordSection("Writing deleted docs done"); - - return status; -} - -Status -SegmentWriter::WriteVectors() { - codec::DefaultCodec default_codec; - try { - fs_ptr_->operation_ptr_->CreateDirectory(); - default_codec.GetVectorsFormat()->write(fs_ptr_, segment_ptr_->vectors_ptr_); - } catch (std::exception& e) { - std::string err_msg = "Failed to write vectors: " + std::string(e.what()); - LOG_ENGINE_ERROR_ << err_msg; - - engine::utils::SendExitSignal(); - return Status(SERVER_WRITE_ERROR, err_msg); - } - return Status::OK(); -} - -Status -SegmentWriter::WriteAttrs() { - codec::DefaultCodec default_codec; - try { - fs_ptr_->operation_ptr_->CreateDirectory(); - default_codec.GetAttrsFormat()->write(fs_ptr_, segment_ptr_->attrs_ptr_); - } catch (std::exception& e) { - std::string err_msg = "Failed to write vectors: " + std::string(e.what()); - LOG_ENGINE_ERROR_ << err_msg; - - engine::utils::SendExitSignal(); - return Status(SERVER_WRITE_ERROR, err_msg); - } - return Status::OK(); -} - -Status -SegmentWriter::WriteVectorIndex(const std::string& location) { - if (location.empty()) { - return Status(SERVER_WRITE_ERROR, "Invalid parameter of WriteVectorIndex"); - } - - codec::DefaultCodec default_codec; - try { - fs_ptr_->operation_ptr_->CreateDirectory(); - default_codec.GetVectorIndexFormat()->write(fs_ptr_, location, segment_ptr_->vector_index_ptr_); - } catch (std::exception& e) { - std::string err_msg = "Failed to write vector index: " + std::string(e.what()); - LOG_ENGINE_ERROR_ << err_msg; - - engine::utils::SendExitSignal(); - return Status(SERVER_WRITE_ERROR, err_msg); - } - return Status::OK(); -} - -Status -SegmentWriter::WriteBloomFilter() { - codec::DefaultCodec default_codec; - try { - fs_ptr_->operation_ptr_->CreateDirectory(); - - TimeRecorder recorder("SegmentWriter::WriteBloomFilter"); - - default_codec.GetIdBloomFilterFormat()->create(fs_ptr_, segment_ptr_->id_bloom_filter_ptr_); - - recorder.RecordSection("Initializing bloom filter"); - - auto& uids = segment_ptr_->vectors_ptr_->GetUids(); - for (auto& uid : uids) { - segment_ptr_->id_bloom_filter_ptr_->Add(uid); - } - - recorder.RecordSection("Adding " + std::to_string(uids.size()) + " ids to bloom filter"); - - default_codec.GetIdBloomFilterFormat()->write(fs_ptr_, segment_ptr_->id_bloom_filter_ptr_); - - recorder.RecordSection("Writing bloom filter"); - } catch (std::exception& e) { - std::string err_msg = "Failed to write vectors: " + std::string(e.what()); - LOG_ENGINE_ERROR_ << err_msg; - - engine::utils::SendExitSignal(); - return Status(SERVER_WRITE_ERROR, err_msg); - } - return Status::OK(); -} - -Status -SegmentWriter::WriteDeletedDocs() { - codec::DefaultCodec default_codec; - try { - fs_ptr_->operation_ptr_->CreateDirectory(); - DeletedDocsPtr deleted_docs_ptr = std::make_shared(); - default_codec.GetDeletedDocsFormat()->write(fs_ptr_, deleted_docs_ptr); - } catch (std::exception& e) { - std::string err_msg = "Failed to write deleted docs: " + std::string(e.what()); - LOG_ENGINE_ERROR_ << err_msg; - - engine::utils::SendExitSignal(); - return Status(SERVER_WRITE_ERROR, err_msg); - } - return Status::OK(); -} - -Status -SegmentWriter::WriteDeletedDocs(const DeletedDocsPtr& deleted_docs) { - codec::DefaultCodec default_codec; - try { - fs_ptr_->operation_ptr_->CreateDirectory(); - default_codec.GetDeletedDocsFormat()->write(fs_ptr_, deleted_docs); - } catch (std::exception& e) { - std::string err_msg = "Failed to write deleted docs: " + std::string(e.what()); - LOG_ENGINE_ERROR_ << err_msg; - - engine::utils::SendExitSignal(); - return Status(SERVER_WRITE_ERROR, err_msg); - } - return Status::OK(); -} - -Status -SegmentWriter::WriteBloomFilter(const IdBloomFilterPtr& id_bloom_filter_ptr) { - codec::DefaultCodec default_codec; - try { - fs_ptr_->operation_ptr_->CreateDirectory(); - default_codec.GetIdBloomFilterFormat()->write(fs_ptr_, id_bloom_filter_ptr); - } catch (std::exception& e) { - std::string err_msg = "Failed to write bloom filter: " + std::string(e.what()); - LOG_ENGINE_ERROR_ << err_msg; - - engine::utils::SendExitSignal(); - return Status(SERVER_WRITE_ERROR, err_msg); - } - return Status::OK(); -} - -Status -SegmentWriter::Cache() { - // TODO(zhiru) - return Status::OK(); -} - -Status -SegmentWriter::GetSegment(SegmentPtr& segment_ptr) { - segment_ptr = segment_ptr_; - return Status::OK(); -} - -Status -SegmentWriter::Merge(const std::string& dir_to_merge, const std::string& name) { - if (dir_to_merge == fs_ptr_->operation_ptr_->GetDirectory()) { - return Status(DB_ERROR, "Cannot Merge Self"); - } - - LOG_ENGINE_DEBUG_ << "Merging from " << dir_to_merge << " to " << fs_ptr_->operation_ptr_->GetDirectory(); - - TimeRecorder recorder("SegmentWriter::Merge"); - - SegmentReader segment_reader_to_merge(dir_to_merge); - bool in_cache; - auto status = segment_reader_to_merge.LoadCache(in_cache); - if (!in_cache) { - status = segment_reader_to_merge.Load(); - if (!status.ok()) { - std::string msg = "Failed to load segment from " + dir_to_merge; - LOG_ENGINE_ERROR_ << msg; - return Status(DB_ERROR, msg); - } - } - SegmentPtr segment_to_merge; - segment_reader_to_merge.GetSegment(segment_to_merge); - // auto& uids = segment_to_merge->vectors_ptr_->GetUids(); - - recorder.RecordSection("Loading segment"); - - if (segment_to_merge->deleted_docs_ptr_ != nullptr) { - auto offsets_to_delete = segment_to_merge->deleted_docs_ptr_->GetDeletedDocs(); - - // Erase from raw data - segment_to_merge->vectors_ptr_->Erase(offsets_to_delete); - } - - recorder.RecordSection("erase"); - - AddVectors(name, segment_to_merge->vectors_ptr_->GetData(), segment_to_merge->vectors_ptr_->GetUids()); - - auto rows = segment_to_merge->vectors_ptr_->GetCount(); - recorder.RecordSection("Adding " + std::to_string(rows) + " vectors and uids"); - - std::unordered_map attr_nbytes; - std::unordered_map> attr_data; - auto attr_it = segment_to_merge->attrs_ptr_->attrs.begin(); - for (; attr_it != segment_to_merge->attrs_ptr_->attrs.end(); attr_it++) { - attr_nbytes.insert(std::make_pair(attr_it->first, attr_it->second->GetNbytes())); - attr_data.insert(std::make_pair(attr_it->first, attr_it->second->GetData())); - - if (segment_to_merge->deleted_docs_ptr_ != nullptr) { - auto offsets_to_delete = segment_to_merge->deleted_docs_ptr_->GetDeletedDocs(); - - // Erase from field data - attr_it->second->Erase(offsets_to_delete); - } - } - AddAttrs(name, attr_nbytes, attr_data, segment_to_merge->vectors_ptr_->GetUids()); - - LOG_ENGINE_DEBUG_ << "Merging completed from " << dir_to_merge << " to " << fs_ptr_->operation_ptr_->GetDirectory(); - - return Status::OK(); -} - -size_t -SegmentWriter::Size() { - // TODO(zhiru): switch to actual directory size - size_t vectors_size = segment_ptr_->vectors_ptr_->VectorsSize(); - size_t uids_size = segment_ptr_->vectors_ptr_->UidsSize(); - /* - if (segment_ptr_->id_bloom_filter_ptr_) { - ret += segment_ptr_->id_bloom_filter_ptr_->Size(); - } - */ - return (vectors_size * sizeof(uint8_t) + uids_size * sizeof(doc_id_t)); -} - -size_t -SegmentWriter::VectorCount() { - return segment_ptr_->vectors_ptr_->GetCount(); -} - -} // namespace segment -} // namespace milvus diff --git a/core/src/segment/SegmentWriter.h b/core/src/segment/SegmentWriter.h deleted file mode 100644 index d3be407075..0000000000 --- a/core/src/segment/SegmentWriter.h +++ /dev/null @@ -1,97 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include -#include -#include - -#include "segment/Types.h" -#include "storage/FSHandler.h" -#include "utils/Status.h" - -namespace milvus { -namespace segment { - -class SegmentWriter { - public: - explicit SegmentWriter(const std::string& directory); - - Status - AddVectors(const std::string& name, const std::vector& data, const std::vector& uids); - - Status - AddVectors(const std::string& name, const uint8_t* data, uint64_t size, const std::vector& uids); - - Status - AddAttrs(const std::string& name, const std::unordered_map& attr_nbytes, - const std::unordered_map>& attr_data, const std::vector& uids); - - Status - SetVectorIndex(const knowhere::VecIndexPtr& index); - - Status - WriteBloomFilter(const IdBloomFilterPtr& bloom_filter_ptr); - - Status - WriteDeletedDocs(const DeletedDocsPtr& deleted_docs); - - Status - Serialize(); - - Status - Cache(); - - Status - GetSegment(SegmentPtr& segment_ptr); - - Status - Merge(const std::string& segment_dir_to_merge, const std::string& name); - - size_t - Size(); - - size_t - VectorCount(); - - Status - WriteVectorIndex(const std::string& location); - - private: - Status - WriteVectors(); - - Status - WriteAttrs(); - - Status - WriteBloomFilter(); - - Status - WriteDeletedDocs(); - - private: - storage::FSHandlerPtr fs_ptr_; - SegmentPtr segment_ptr_; -}; - -using SegmentWriterPtr = std::shared_ptr; - -} // namespace segment -} // namespace milvus diff --git a/core/src/segment/Types.h b/core/src/segment/Types.h deleted file mode 100644 index dc0982fced..0000000000 --- a/core/src/segment/Types.h +++ /dev/null @@ -1,44 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include - -#include "segment/Attrs.h" -#include "segment/DeletedDocs.h" -#include "segment/IdBloomFilter.h" -#include "segment/VectorIndex.h" -#include "segment/Vectors.h" - -namespace milvus { -namespace segment { - -typedef int64_t doc_id_t; - -struct Segment { - VectorsPtr vectors_ptr_ = std::make_shared(); - AttrsPtr attrs_ptr_ = std::make_shared(); - VectorIndexPtr vector_index_ptr_ = std::make_shared(); - DeletedDocsPtr deleted_docs_ptr_ = nullptr; - IdBloomFilterPtr id_bloom_filter_ptr_ = nullptr; -}; - -using SegmentPtr = std::shared_ptr; - -} // namespace segment -} // namespace milvus diff --git a/core/src/segment/VectorIndex.h b/core/src/segment/VectorIndex.h deleted file mode 100644 index ccb7067501..0000000000 --- a/core/src/segment/VectorIndex.h +++ /dev/null @@ -1,60 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include -#include "knowhere/index/vector_index/VecIndex.h" - -namespace milvus { -namespace segment { - -class VectorIndex { - public: - explicit VectorIndex(knowhere::VecIndexPtr index_ptr) : index_ptr_(index_ptr) { - } - - VectorIndex() = default; - - knowhere::VecIndexPtr - GetVectorIndex() const { - return index_ptr_; - } - - void - SetVectorIndex(const knowhere::VecIndexPtr& index_ptr) { - index_ptr_ = index_ptr; - } - - // No copy and move - VectorIndex(const VectorIndex&) = delete; - VectorIndex(VectorIndex&&) = delete; - - VectorIndex& - operator=(const VectorIndex&) = delete; - VectorIndex& - operator=(VectorIndex&&) = delete; - - private: - knowhere::VecIndexPtr index_ptr_ = nullptr; -}; - -using VectorIndexPtr = std::shared_ptr; - -} // namespace segment -} // namespace milvus diff --git a/core/src/segment/Vectors.cpp b/core/src/segment/Vectors.cpp deleted file mode 100644 index e9410e87c7..0000000000 --- a/core/src/segment/Vectors.cpp +++ /dev/null @@ -1,178 +0,0 @@ -// 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 limitations -// under the License. - -#include "segment/Vectors.h" - -#include -#include -#include -#include - -#include "utils/Log.h" -#include "utils/TimeRecorder.h" - -namespace milvus { -namespace segment { - -void -Vectors::AddData(const std::vector& data) { - data_.reserve(data_.size() + data.size()); - data_.insert(data_.end(), std::make_move_iterator(data.begin()), std::make_move_iterator(data.end())); -} - -void -Vectors::AddData(const uint8_t* data, uint64_t size) { - int64_t old_size = data_.size(); - data_.resize(data_.size() + size); - memcpy(data_.data() + old_size, data, size); -} - -void -Vectors::AddUids(const std::vector& uids) { - uids_.reserve(uids_.size() + uids.size()); - uids_.insert(uids_.end(), std::make_move_iterator(uids.begin()), std::make_move_iterator(uids.end())); -} - -void -Vectors::Erase(int32_t offset) { - auto code_length = GetCodeLength(); - if (code_length != 0) { - auto step = offset * code_length; - data_.erase(data_.begin() + step, data_.begin() + step + code_length); - uids_.erase(uids_.begin() + offset, uids_.begin() + offset + 1); - } -} - -void -Vectors::Erase(std::vector& offsets) { - if (offsets.empty()) { - return; - } - - // Sort and remove duplicates - TimeRecorder recorder("Vectors::Erase"); - - std::sort(offsets.begin(), offsets.end()); - - recorder.RecordSection("Sorting " + std::to_string(offsets.size()) + " offsets to delete"); - - offsets.erase(std::unique(offsets.begin(), offsets.end()), offsets.end()); - - recorder.RecordSection("Deduplicating " + std::to_string(offsets.size()) + " offsets to delete"); - - // Reconstruct raw vectors and uids - LOG_ENGINE_DEBUG_ << "Begin erasing..."; - - size_t new_size = uids_.size() - offsets.size(); - std::vector new_uids(new_size); - auto code_length = GetCodeLength(); - std::vector new_data(new_size * code_length); - - auto count = 0; - auto skip = offsets.cbegin(); - auto loop_size = uids_.size(); - - for (size_t i = 0; i < loop_size;) { - while (i == (size_t)(*skip) && skip != offsets.cend()) { - ++i; - ++skip; - } - - if (i == loop_size) { - break; - } - - new_uids[count] = uids_[i]; - - for (size_t j = 0; j < code_length; ++j) { - new_data[count * code_length + j] = data_[i * code_length + j]; - } - - ++count; - ++i; - } - - data_.clear(); - uids_.clear(); - data_.swap(new_data); - uids_.swap(new_uids); - - std::string msg = - "Erasing " + std::to_string(offsets.size()) + " vectors out of " + std::to_string(loop_size) + " vectors"; - recorder.RecordSection(msg); -} - -std::vector& -Vectors::GetMutableData() { - return data_; -} - -std::vector& -Vectors::GetMutableUids() { - return uids_; -} - -const std::vector& -Vectors::GetData() const { - return data_; -} - -const std::vector& -Vectors::GetUids() const { - return uids_; -} - -size_t -Vectors::GetCount() const { - return uids_.size(); -} - -size_t -Vectors::GetCodeLength() const { - return uids_.empty() ? 0 : data_.size() / uids_.size(); -} - -size_t -Vectors::VectorsSize() { - return data_.size(); -} - -size_t -Vectors::UidsSize() { - return uids_.size(); -} - -void -Vectors::SetName(const std::string& name) { - name_ = name; -} - -const std::string& -Vectors::GetName() const { - return name_; -} - -void -Vectors::Clear() { - data_.clear(); - data_.shrink_to_fit(); - uids_.clear(); - uids_.shrink_to_fit(); -} - -} // namespace segment -} // namespace milvus diff --git a/core/src/segment/Vectors.h b/core/src/segment/Vectors.h deleted file mode 100644 index cc7369398e..0000000000 --- a/core/src/segment/Vectors.h +++ /dev/null @@ -1,99 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include -#include - -namespace milvus { -namespace segment { - -using doc_id_t = int64_t; - -class Vectors { - public: - Vectors() = default; - - void - AddData(const std::vector& data); - - void - AddData(const uint8_t* data, uint64_t size); - - void - AddUids(const std::vector& uids); - - void - SetName(const std::string& name); - - std::vector& - GetMutableData(); - - std::vector& - GetMutableUids(); - - const std::vector& - GetData() const; - - const std::vector& - GetUids() const; - - const std::string& - GetName() const; - - size_t - GetCount() const; - - size_t - GetCodeLength() const; - - void - Erase(int32_t offset); - - void - Erase(std::vector& offsets); - - size_t - VectorsSize(); - - size_t - UidsSize(); - - void - Clear(); - - // No copy and move - Vectors(const Vectors&) = delete; - Vectors(Vectors&&) = delete; - - Vectors& - operator=(const Vectors&) = delete; - Vectors& - operator=(Vectors&&) = delete; - - private: - std::vector data_; - std::vector uids_; - std::string name_; -}; - -using VectorsPtr = std::shared_ptr; - -} // namespace segment -} // namespace milvus diff --git a/core/src/segment/VectorsIndex.h b/core/src/segment/VectorsIndex.h deleted file mode 100644 index e5f1aabee8..0000000000 --- a/core/src/segment/VectorsIndex.h +++ /dev/null @@ -1,33 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include - -#include "VectorIndex.h" - -namespace milvus { -namespace segment { - -struct VectorsIndex { - std::unordered_map vector_indexes; -}; - -} // namespace segment -} // namespace milvus diff --git a/core/src/server/DBWrapper.cpp b/core/src/server/DBWrapper.cpp deleted file mode 100644 index adc3acbff4..0000000000 --- a/core/src/server/DBWrapper.cpp +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/DBWrapper.h" - -#include -#include -#include -#include - -#include - -#include "config/Config.h" -#include "db/DBFactory.h" -#include "utils/CommonUtil.h" -#include "utils/Log.h" -#include "utils/StringHelpFunctions.h" - -namespace milvus { -namespace server { - -Status -DBWrapper::StartService() { - Config& config = Config::GetInstance(); - Status s; - - // db config - engine::DBOptions opt; - s = config.GetGeneralConfigMetaURI(opt.meta_.backend_uri_); - if (!s.ok()) { - std::cerr << s.ToString() << std::endl; - return s; - } - - std::string path; - s = config.GetStorageConfigPath(path); - if (!s.ok()) { - std::cerr << s.ToString() << std::endl; - return s; - } - opt.meta_.path_ = path + "/db"; - - s = config.GetStorageConfigAutoFlushInterval(opt.auto_flush_interval_); - if (!s.ok()) { - std::cerr << s.ToString() << std::endl; - return s; - } - - s = config.GetStorageConfigFileCleanupTimeup(opt.file_cleanup_timeout_); - if (!s.ok()) { - std::cerr << s.ToString() << std::endl; - return s; - } - - // metric config - s = config.GetMetricConfigEnableMonitor(opt.metric_enable_); - if (!s.ok()) { - std::cerr << s.ToString() << std::endl; - return s; - } - - // cache config - s = config.GetCacheConfigCacheInsertData(opt.insert_cache_immediately_); - if (!s.ok()) { - std::cerr << s.ToString() << std::endl; - return s; - } - - int64_t insert_buffer_size = 1 * engine::GB; - s = config.GetCacheConfigInsertBufferSize(insert_buffer_size); - if (!s.ok()) { - std::cerr << s.ToString() << std::endl; - return s; - } - opt.insert_buffer_size_ = insert_buffer_size; - -#if 1 - bool cluster_enable = false; - std::string cluster_role; - STATUS_CHECK(config.GetClusterConfigEnable(cluster_enable)); - STATUS_CHECK(config.GetClusterConfigRole(cluster_role)); - if (not cluster_enable) { - opt.mode_ = engine::DBOptions::MODE::SINGLE; - } else if (cluster_role == "ro") { - opt.mode_ = engine::DBOptions::MODE::CLUSTER_READONLY; - } else if (cluster_role == "rw") { - opt.mode_ = engine::DBOptions::MODE::CLUSTER_WRITABLE; - } else { - std::cerr << "Error: cluster.role is not one of rw and ro." << std::endl; - kill(0, SIGUSR1); - } - -#else - std::string mode; - s = config.GetServerConfigDeployMode(mode); - if (!s.ok()) { - std::cerr << s.ToString() << std::endl; - return s; - } - - if (mode == "single") { - opt.mode_ = engine::DBOptions::MODE::SINGLE; - } else if (mode == "cluster_readonly") { - opt.mode_ = engine::DBOptions::MODE::CLUSTER_READONLY; - } else if (mode == "cluster_writable") { - opt.mode_ = engine::DBOptions::MODE::CLUSTER_WRITABLE; - } else { - std::cerr << "Error: server_config.deploy_mode in server_config.yaml is not one of " - << "single, cluster_readonly, and cluster_writable." << std::endl; - kill(0, SIGUSR1); - } -#endif - - // get wal configurations - s = config.GetWalConfigEnable(opt.wal_enable_); - if (!s.ok()) { - std::cerr << "ERROR! Failed to get wal_enable configuration." << std::endl; - std::cerr << s.ToString() << std::endl; - kill(0, SIGUSR1); - } - - if (opt.wal_enable_) { - s = config.GetWalConfigRecoveryErrorIgnore(opt.recovery_error_ignore_); - if (!s.ok()) { - std::cerr << "ERROR! Failed to get recovery_error_ignore configuration." << std::endl; - std::cerr << s.ToString() << std::endl; - kill(0, SIGUSR1); - } - - int64_t wal_buffer_size = 0; - s = config.GetWalConfigBufferSize(wal_buffer_size); - if (!s.ok()) { - std::cerr << "ERROR! Failed to get buffer_size configuration." << std::endl; - std::cerr << s.ToString() << std::endl; - kill(0, SIGUSR1); - } - wal_buffer_size /= (1024 * 1024); - opt.buffer_size_ = wal_buffer_size; - - s = config.GetWalConfigWalPath(opt.mxlog_path_); - if (!s.ok()) { - std::cerr << "ERROR! Failed to get mxlog_path configuration." << std::endl; - std::cerr << s.ToString() << std::endl; - kill(0, SIGUSR1); - } - } - - // engine config - int64_t omp_thread; - s = config.GetEngineConfigOmpThreadNum(omp_thread); - if (!s.ok()) { - std::cerr << s.ToString() << std::endl; - return s; - } - - if (omp_thread > 0) { - omp_set_num_threads(omp_thread); - LOG_SERVER_DEBUG_ << "Specify openmp thread number: " << omp_thread; - } else { - int64_t sys_thread_cnt = 8; - if (CommonUtil::GetSystemAvailableThreads(sys_thread_cnt)) { - omp_thread = static_cast(ceil(sys_thread_cnt * 0.5)); - omp_set_num_threads(omp_thread); - } - } - - // init faiss global variable - int64_t use_blas_threshold; - s = config.GetEngineConfigUseBlasThreshold(use_blas_threshold); - if (!s.ok()) { - std::cerr << s.ToString() << std::endl; - return s; - } - faiss::distance_compute_blas_threshold = use_blas_threshold; - - // set archive config - engine::ArchiveConf::CriteriaT criterial; - int64_t disk, days; - s = config.GetDBConfigArchiveDiskThreshold(disk); - if (!s.ok()) { - std::cerr << s.ToString() << std::endl; - return s; - } - - if (disk > 0) { - criterial[engine::ARCHIVE_CONF_DISK] = disk; - } - - s = config.GetDBConfigArchiveDaysThreshold(days); - if (!s.ok()) { - std::cerr << s.ToString() << std::endl; - return s; - } - - if (days > 0) { - criterial[engine::ARCHIVE_CONF_DAYS] = days; - } - opt.meta_.archive_conf_.SetCriterias(criterial); - - // create db root folder - s = CommonUtil::CreateDirectory(opt.meta_.path_); - if (!s.ok()) { - std::cerr << "Error: Failed to create database primary path: " << path - << ". Possible reason: db_config.primary_path is wrong in server_config.yaml or not available." - << std::endl; - kill(0, SIGUSR1); - } - - for (auto& path : opt.meta_.slave_paths_) { - s = CommonUtil::CreateDirectory(path); - if (!s.ok()) { - std::cerr << "Error: Failed to create database secondary path: " << path - << ". Possible reason: db_config.secondary_path is wrong in server_config.yaml or not available." - << std::endl; - kill(0, SIGUSR1); - } - } - - // create db instance - try { - db_ = engine::DBFactory::Build(opt); - } catch (std::exception& ex) { - std::cerr << "Error: failed to open database: " << ex.what() - << ". Possible reason: out of storage, meta schema is damaged " - << "or created by in-compatible Milvus version." << std::endl; - kill(0, SIGUSR1); - } - - db_->Start(); - - // preload collection - std::string preload_collections; - s = config.GetCacheConfigPreloadCollection(preload_collections); - if (!s.ok()) { - std::cerr << s.ToString() << std::endl; - return s; - } - - s = PreloadCollections(preload_collections); - if (!s.ok()) { - std::cerr << "ERROR! Failed to preload tables: " << preload_collections << std::endl; - std::cerr << s.ToString() << std::endl; - kill(0, SIGUSR1); - } - - return Status::OK(); -} - -Status -DBWrapper::StopService() { - if (db_) { - db_->Stop(); - } - - return Status::OK(); -} - -Status -DBWrapper::PreloadCollections(const std::string& preload_collections) { - if (preload_collections.empty()) { - // do nothing - } else if (preload_collections == "*") { - // load all tables - std::vector table_schema_array; - db_->AllCollections(table_schema_array); - - for (auto& schema : table_schema_array) { - auto status = db_->PreloadCollection(nullptr, schema.collection_id_); - if (!status.ok()) { - return status; - } - } - } else { - std::vector collection_names; - StringHelpFunctions::SplitStringByDelimeter(preload_collections, ",", collection_names); - for (auto& name : collection_names) { - auto status = db_->PreloadCollection(nullptr, name); - if (!status.ok()) { - return status; - } - } - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/DBWrapper.h b/core/src/server/DBWrapper.h deleted file mode 100644 index 0f66262d55..0000000000 --- a/core/src/server/DBWrapper.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -#include "db/DB.h" -#include "utils/Status.h" - -namespace milvus { -namespace server { - -class DBWrapper { - private: - DBWrapper() = default; - ~DBWrapper() = default; - - public: - static DBWrapper& - GetInstance() { - static DBWrapper wrapper; - return wrapper; - } - - static engine::DBPtr - DB() { - return GetInstance().EngineDB(); - } - - Status - StartService(); - Status - StopService(); - - engine::DBPtr - EngineDB() { - return db_; - } - - private: - Status - PreloadCollections(const std::string& preload_collections); - - private: - engine::DBPtr db_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/Server.cpp b/core/src/server/Server.cpp deleted file mode 100644 index cdbdb08873..0000000000 --- a/core/src/server/Server.cpp +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/Server.h" -#include "server/init/InstanceLockCheck.h" - -#include -#include -#include -#include -#include - -#include "config/Config.h" -#include "index/archive/KnowhereResource.h" -#include "metrics/Metrics.h" -#include "scheduler/SchedInst.h" -#include "server/DBWrapper.h" -#include "server/grpc_impl/GrpcServer.h" -#include "server/init/CpuChecker.h" -#include "server/init/GpuChecker.h" -#include "server/init/StorageChecker.h" -#include "server/web_impl/WebServer.h" -#include "src/version.h" -//#include "storage/s3/S3ClientWrapper.h" -#include "tracing/TracerUtil.h" -#include "utils/Log.h" -#include "utils/LogUtil.h" -#include "utils/SignalUtil.h" -#include "utils/TimeRecorder.h" - -#include "search/TaskInst.h" - -namespace milvus { -namespace server { - -Server& -Server::GetInstance() { - static Server server; - return server; -} - -void -Server::Init(int64_t daemonized, const std::string& pid_filename, const std::string& config_filename) { - daemonized_ = daemonized; - pid_filename_ = pid_filename; - config_filename_ = config_filename; -} - -void -Server::Daemonize() { - if (daemonized_ == 0) { - return; - } - - std::cout << "Milvus server run in daemonize mode"; - - pid_t pid = 0; - - // Fork off the parent process - pid = fork(); - - // An error occurred - if (pid < 0) { - exit(EXIT_FAILURE); - } - - // Success: terminate parent - if (pid > 0) { - exit(EXIT_SUCCESS); - } - - // On success: The child process becomes session leader - if (setsid() < 0) { - exit(EXIT_FAILURE); - } - - // Ignore signal sent from child to parent process - signal(SIGCHLD, SIG_IGN); - - // Fork off for the second time - pid = fork(); - - // An error occurred - if (pid < 0) { - exit(EXIT_FAILURE); - } - - // Terminate the parent - if (pid > 0) { - exit(EXIT_SUCCESS); - } - - // Set new file permissions - umask(0); - - // Change the working directory to root - int ret = chdir("/"); - if (ret != 0) { - return; - } - - // Close all open fd - for (int64_t fd = sysconf(_SC_OPEN_MAX); fd > 0; fd--) { - close(fd); - } - - std::cout << "Redirect stdin/stdout/stderr to /dev/null"; - - // Redirect stdin/stdout/stderr to /dev/null - stdin = fopen("/dev/null", "r"); - stdout = fopen("/dev/null", "w+"); - stderr = fopen("/dev/null", "w+"); - // Try to write PID of daemon to lockfile - if (!pid_filename_.empty()) { - pid_fd_ = open(pid_filename_.c_str(), O_RDWR | O_CREAT, 0640); - if (pid_fd_ < 0) { - std::cerr << "Can't open filename: " + pid_filename_ + ", Error: " + strerror(errno); - exit(EXIT_FAILURE); - } - if (lockf(pid_fd_, F_TLOCK, 0) < 0) { - std::cerr << "Can't lock filename: " + pid_filename_ + ", Error: " + strerror(errno); - exit(EXIT_FAILURE); - } - - std::string pid_file_context = std::to_string(getpid()); - ssize_t res = write(pid_fd_, pid_file_context.c_str(), pid_file_context.size()); - if (res != 0) { - return; - } - } -} - -Status -Server::Start() { - if (daemonized_ != 0) { - Daemonize(); - } - - try { - /* Read config file */ - Status s = LoadConfig(); - if (!s.ok()) { - std::cerr << "ERROR: Milvus server fail to load config file" << std::endl; - return s; - } - - Config& config = Config::GetInstance(); - - std::string meta_uri; - STATUS_CHECK(config.GetGeneralConfigMetaURI(meta_uri)); - if (meta_uri.length() > 6 && strcasecmp("sqlite", meta_uri.substr(0, 6).c_str()) == 0) { - std::cout << "WARNNING: You are using SQLite as the meta data management, " - "which can't be used in production. Please change it to MySQL!" - << std::endl; - } - - /* Init opentracing tracer from config */ - std::string tracing_config_path; - s = config.GetTracingConfigJsonConfigPath(tracing_config_path); - tracing_config_path.empty() ? tracing::TracerUtil::InitGlobal() - : tracing::TracerUtil::InitGlobal(tracing_config_path); - - /* log path is defined in Config file, so InitLog must be called after LoadConfig */ - std::string time_zone; - s = config.GetGeneralConfigTimezone(time_zone); - if (!s.ok()) { - std::cerr << "Fail to get server config timezone" << std::endl; - return s; - } - - if (time_zone.length() == 3) { - time_zone = "CUT"; - } else { - int time_bias = std::stoi(time_zone.substr(3, std::string::npos)); - if (time_bias == 0) { - time_zone = "CUT"; - } else if (time_bias > 0) { - time_zone = "CUT" + std::to_string(-time_bias); - } else { - time_zone = "CUT+" + std::to_string(-time_bias); - } - } - - if (setenv("TZ", time_zone.c_str(), 1) != 0) { - return Status(SERVER_UNEXPECTED_ERROR, "Fail to setenv"); - } - tzset(); - - { - std::unordered_map level_to_int{ - {"debug", 5}, {"info", 4}, {"warning", 3}, {"error", 2}, {"fatal", 1}, - }; - - std::string level; - bool trace_enable = false; - bool debug_enable = false; - bool info_enable = false; - bool warning_enable = false; - bool error_enable = false; - bool fatal_enable = false; - std::string logs_path; - int64_t max_log_file_size = 0; - int64_t delete_exceeds = 0; - - STATUS_CHECK(config.GetLogsLevel(level)); - switch (level_to_int[level]) { - case 5: - debug_enable = true; - case 4: - info_enable = true; - case 3: - warning_enable = true; - case 2: - error_enable = true; - case 1: - fatal_enable = true; - break; - default: - return Status(SERVER_UNEXPECTED_ERROR, "invalid log level"); - } - - STATUS_CHECK(config.GetLogsTraceEnable(trace_enable)); - STATUS_CHECK(config.GetLogsPath(logs_path)); - STATUS_CHECK(config.GetLogsMaxLogFileSize(max_log_file_size)); - STATUS_CHECK(config.GetLogsLogRotateNum(delete_exceeds)); - InitLog(trace_enable, debug_enable, info_enable, warning_enable, error_enable, fatal_enable, logs_path, - max_log_file_size, delete_exceeds); - } - - bool cluster_enable = false; - std::string cluster_role; - STATUS_CHECK(config.GetClusterConfigEnable(cluster_enable)); - STATUS_CHECK(config.GetClusterConfigRole(cluster_role)); - - // std::string deploy_mode; - // STATUS_CHECK(config.GetServerConfigDeployMode(deploy_mode)); - - // if (deploy_mode == "single" || deploy_mode == "cluster_writable") { - if ((not cluster_enable) || cluster_role == "rw") { - std::string db_path; - STATUS_CHECK(config.GetStorageConfigPath(db_path)); - - try { - // True if a new directory was created, otherwise false. - boost::filesystem::create_directories(db_path); - } catch (...) { - return Status(SERVER_UNEXPECTED_ERROR, "Cannot create db directory"); - } - - s = InstanceLockCheck::Check(db_path); - if (!s.ok()) { - if (not cluster_enable) { - std::cerr << "single instance lock db path failed." << s.message() << std::endl; - } else { - std::cerr << cluster_role << " instance lock db path failed." << s.message() << std::endl; - } - return s; - } - - bool wal_enable = false; - STATUS_CHECK(config.GetWalConfigEnable(wal_enable)); - - if (wal_enable) { - std::string wal_path; - STATUS_CHECK(config.GetWalConfigWalPath(wal_path)); - - try { - // True if a new directory was created, otherwise false. - boost::filesystem::create_directories(wal_path); - } catch (...) { - return Status(SERVER_UNEXPECTED_ERROR, "Cannot create wal directory"); - } - s = InstanceLockCheck::Check(wal_path); - if (!s.ok()) { - if (not cluster_enable) { - std::cerr << "single instance lock wal path failed." << s.message() << std::endl; - } else { - std::cerr << cluster_role << " instance lock wal path failed." << s.message() << std::endl; - } - return s; - } - } - } - - // print version information - LOG_SERVER_INFO_ << "Milvus " << BUILD_TYPE << " version: v" << MILVUS_VERSION << ", built at " << BUILD_TIME; -#ifdef MILVUS_GPU_VERSION - LOG_SERVER_INFO_ << "GPU edition"; -#else - LOG_SERVER_INFO_ << "CPU edition"; -#endif - STATUS_CHECK(StorageChecker::CheckStoragePermission()); - STATUS_CHECK(CpuChecker::CheckCpuInstructionSet()); -#ifdef MILVUS_GPU_VERSION - STATUS_CHECK(GpuChecker::CheckGpuEnvironment()); -#endif - /* record config and hardware information into log */ - LogConfigInFile(config_filename_); - LogCpuInfo(); - LogConfigInMem(); - - server::Metrics::GetInstance().Init(); - server::SystemInfo::GetInstance().Init(); - - return StartService(); - } catch (std::exception& ex) { - std::string str = "Milvus server encounter exception: " + std::string(ex.what()); - return Status(SERVER_UNEXPECTED_ERROR, str); - } -} - -void -Server::Stop() { - std::cerr << "Milvus server is going to shutdown ..." << std::endl; - - /* Unlock and close lockfile */ - if (pid_fd_ != -1) { - int ret = lockf(pid_fd_, F_ULOCK, 0); - if (ret != 0) { - std::cerr << "ERROR: Can't lock file: " << strerror(errno) << std::endl; - exit(0); - } - ret = close(pid_fd_); - if (ret != 0) { - std::cerr << "ERROR: Can't close file: " << strerror(errno) << std::endl; - exit(0); - } - } - - /* delete lockfile */ - if (!pid_filename_.empty()) { - int ret = unlink(pid_filename_.c_str()); - if (ret != 0) { - std::cerr << "ERROR: Can't unlink file: " << strerror(errno) << std::endl; - exit(0); - } - } - - StopService(); - - std::cerr << "Milvus server exit..." << std::endl; -} - -Status -Server::LoadConfig() { - Config& config = Config::GetInstance(); - Status s = config.LoadConfigFile(config_filename_); - if (!s.ok()) { - std::cerr << s.message() << std::endl; - return s; - } - - s = config.ValidateConfig(); - if (!s.ok()) { - std::cerr << "Config check fail: " << s.message() << std::endl; - return s; - } - return milvus::Status::OK(); -} - -Status -Server::StartService() { - Status stat; - stat = engine::KnowhereResource::Initialize(); - if (!stat.ok()) { - LOG_SERVER_ERROR_ << "KnowhereResource initialize fail: " << stat.message(); - goto FAIL; - } - - scheduler::StartSchedulerService(); - - stat = DBWrapper::GetInstance().StartService(); - if (!stat.ok()) { - LOG_SERVER_ERROR_ << "DBWrapper start service fail: " << stat.message(); - goto FAIL; - } - - grpc::GrpcServer::GetInstance().Start(); - web::WebServer::GetInstance().Start(); - - // stat = storage::S3ClientWrapper::GetInstance().StartService(); - // if (!stat.ok()) { - // LOG_SERVER_ERROR_ << "S3Client start service fail: " << stat.message(); - // goto FAIL; - // } - - // search::TaskInst::GetInstance().Start(); - - return Status::OK(); -FAIL: - std::cerr << "Milvus initializes fail: " << stat.message() << std::endl; - return stat; -} - -void -Server::StopService() { - // search::TaskInst::GetInstance().Stop(); - // storage::S3ClientWrapper::GetInstance().StopService(); - web::WebServer::GetInstance().Stop(); - grpc::GrpcServer::GetInstance().Stop(); - DBWrapper::GetInstance().StopService(); - scheduler::StopSchedulerService(); - engine::KnowhereResource::Finalize(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/Server.h b/core/src/server/Server.h deleted file mode 100644 index 01a8e82ba7..0000000000 --- a/core/src/server/Server.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include "utils/Status.h" - -namespace milvus { -namespace server { - -class Server { - public: - static Server& - GetInstance(); - - void - Init(int64_t daemonized, const std::string& pid_filename, const std::string& config_filename); - - Status - Start(); - void - Stop(); - - private: - Server() = default; - ~Server() = default; - - void - Daemonize(); - - Status - LoadConfig(); - - Status - StartService(); - void - StopService(); - - private: - int64_t daemonized_ = 0; - int pid_fd_ = -1; - std::string pid_filename_; - std::string config_filename_; -}; // Server - -} // namespace server -} // namespace milvus diff --git a/core/src/server/context/ConnectionContext.h b/core/src/server/context/ConnectionContext.h deleted file mode 100644 index f1f2248c7d..0000000000 --- a/core/src/server/context/ConnectionContext.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -namespace milvus { -namespace server { - -class ConnectionContext { - public: - virtual ~ConnectionContext() { - } - virtual bool - IsConnectionBroken() const = 0; -}; - -using ConnectionContextPtr = std::shared_ptr; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/context/Context.cpp b/core/src/server/context/Context.cpp deleted file mode 100644 index 424ab98849..0000000000 --- a/core/src/server/context/Context.cpp +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/context/Context.h" - -namespace milvus { -namespace server { - -Context::Context(const std::string& request_id) : request_id_(request_id) { -} - -const std::shared_ptr& -Context::GetTraceContext() const { - return trace_context_; -} - -void -Context::SetTraceContext(const std::shared_ptr& trace_context) { - trace_context_ = trace_context; -} -std::shared_ptr -Context::Child(const std::string& operation_name) const { - auto new_context = std::make_shared(request_id_); - new_context->SetTraceContext(trace_context_->Child(operation_name)); - return new_context; -} - -std::shared_ptr -Context::Follower(const std::string& operation_name) const { - auto new_context = std::make_shared(request_id_); - new_context->SetTraceContext(trace_context_->Follower(operation_name)); - return new_context; -} - -void -Context::SetConnectionContext(ConnectionContextPtr& context) { - context_ = context; -} - -bool -Context::IsConnectionBroken() const { - if (context_ == nullptr) { - return false; - } - - return context_->IsConnectionBroken(); -} - -BaseRequest::RequestType -Context::GetRequestType() const { - return request_type_; -} - -void -Context::SetRequestType(BaseRequest::RequestType type) { - request_type_ = type; -} - -///////////////////////////////////////////////////////////////////////////////////////////////// -ContextChild::ContextChild(const ContextPtr& context, const std::string& operation_name) { - if (context) { - context_ = context->Child(operation_name); - } -} - -ContextChild::~ContextChild() { - Finish(); -} - -void -ContextChild::Finish() { - if (context_) { - context_->GetTraceContext()->GetSpan()->Finish(); - context_ = nullptr; - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////// -ContextFollower::ContextFollower(const ContextPtr& context, const std::string& operation_name) { - if (context) { - context_ = context->Follower(operation_name); - } -} - -ContextFollower::~ContextFollower() { - if (context_) { - context_->GetTraceContext()->GetSpan()->Finish(); - } -} - -void -ContextFollower::Finish() { - if (context_) { - context_->GetTraceContext()->GetSpan()->Finish(); - context_ = nullptr; - } -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/context/Context.h b/core/src/server/context/Context.h deleted file mode 100644 index 72892d335d..0000000000 --- a/core/src/server/context/Context.h +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include - -#include - -#include "server/context/ConnectionContext.h" -#include "server/delivery/request/BaseRequest.h" -#include "tracing/TraceContext.h" - -namespace milvus { -namespace server { - -class Context { - public: - explicit Context(const std::string& request_id); - - inline std::string - RequestID() const { - return request_id_; - } - - std::shared_ptr - Child(const std::string& operation_name) const; - - std::shared_ptr - Follower(const std::string& operation_name) const; - - void - SetTraceContext(const std::shared_ptr& trace_context); - - const std::shared_ptr& - GetTraceContext() const; - - void - SetConnectionContext(ConnectionContextPtr& context); - - bool - IsConnectionBroken() const; - - BaseRequest::RequestType - GetRequestType() const; - - void - SetRequestType(BaseRequest::RequestType type); - - private: - std::string request_id_; - BaseRequest::RequestType request_type_; - std::shared_ptr trace_context_; - ConnectionContextPtr context_; -}; - -using ContextPtr = std::shared_ptr; - -class ContextChild { - public: - explicit ContextChild(const ContextPtr& context, const std::string& operation_name); - ~ContextChild(); - - ContextPtr - Context() { - return context_; - } - - void - Finish(); - - private: - ContextPtr context_; -}; - -class ContextFollower { - public: - explicit ContextFollower(const ContextPtr& context, const std::string& operation_name); - ~ContextFollower(); - - ContextPtr - Context() { - return context_; - } - - void - Finish(); - - private: - ContextPtr context_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/RequestHandler.cpp b/core/src/server/delivery/RequestHandler.cpp deleted file mode 100644 index 5a69148391..0000000000 --- a/core/src/server/delivery/RequestHandler.cpp +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/RequestHandler.h" - -#include - -#include "server/delivery/RequestScheduler.h" -#include "server/delivery/request/BaseRequest.h" -#include "server/delivery/request/CmdRequest.h" -#include "server/delivery/request/CompactRequest.h" -#include "server/delivery/request/CountCollectionRequest.h" -#include "server/delivery/request/CreateCollectionRequest.h" -#include "server/delivery/request/CreateIndexRequest.h" -#include "server/delivery/request/CreatePartitionRequest.h" -#include "server/delivery/request/DeleteByIDRequest.h" -#include "server/delivery/request/DescribeCollectionRequest.h" -#include "server/delivery/request/DescribeIndexRequest.h" -#include "server/delivery/request/DropCollectionRequest.h" -#include "server/delivery/request/DropIndexRequest.h" -#include "server/delivery/request/DropPartitionRequest.h" -#include "server/delivery/request/FlushRequest.h" -#include "server/delivery/request/GetVectorIDsRequest.h" -#include "server/delivery/request/GetVectorsByIDRequest.h" -#include "server/delivery/request/HasCollectionRequest.h" -#include "server/delivery/request/HasPartitionRequest.h" -#include "server/delivery/request/InsertRequest.h" -#include "server/delivery/request/PreloadCollectionRequest.h" -#include "server/delivery/request/ReLoadSegmentsRequest.h" -#include "server/delivery/request/SearchByIDRequest.h" -#include "server/delivery/request/SearchRequest.h" -#include "server/delivery/request/ShowCollectionInfoRequest.h" -#include "server/delivery/request/ShowCollectionsRequest.h" -#include "server/delivery/request/ShowPartitionsRequest.h" - -#include "server/delivery/hybrid_request/CreateHybridCollectionRequest.h" -#include "server/delivery/hybrid_request/DescribeHybridCollectionRequest.h" -#include "server/delivery/hybrid_request/HybridSearchRequest.h" -#include "server/delivery/hybrid_request/InsertEntityRequest.h" - -namespace milvus { -namespace server { - -Status -RequestHandler::CreateCollection(const std::shared_ptr& context, const std::string& collection_name, - int64_t dimension, int64_t index_file_size, int64_t metric_type) { - BaseRequestPtr request_ptr = - CreateCollectionRequest::Create(context, collection_name, dimension, index_file_size, metric_type); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::HasCollection(const std::shared_ptr& context, const std::string& collection_name, - bool& has_collection) { - BaseRequestPtr request_ptr = HasCollectionRequest::Create(context, collection_name, has_collection); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::DropCollection(const std::shared_ptr& context, const std::string& collection_name) { - BaseRequestPtr request_ptr = DropCollectionRequest::Create(context, collection_name); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::CreateIndex(const std::shared_ptr& context, const std::string& collection_name, - int64_t index_type, const milvus::json& json_params) { - BaseRequestPtr request_ptr = CreateIndexRequest::Create(context, collection_name, index_type, json_params); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::Insert(const std::shared_ptr& context, const std::string& collection_name, - engine::VectorsData& vectors, const std::string& partition_tag) { - BaseRequestPtr request_ptr = InsertRequest::Create(context, collection_name, vectors, partition_tag); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::GetVectorsByID(const std::shared_ptr& context, const std::string& collection_name, - const std::vector& ids, std::vector& vectors) { - BaseRequestPtr request_ptr = GetVectorsByIDRequest::Create(context, collection_name, ids, vectors); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::GetVectorIDs(const std::shared_ptr& context, const std::string& collection_name, - const std::string& segment_name, std::vector& vector_ids) { - BaseRequestPtr request_ptr = GetVectorIDsRequest::Create(context, collection_name, segment_name, vector_ids); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::ShowCollections(const std::shared_ptr& context, std::vector& collections) { - BaseRequestPtr request_ptr = ShowCollectionsRequest::Create(context, collections); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::ShowCollectionInfo(const std::shared_ptr& context, const std::string& collection_name, - std::string& collection_info) { - BaseRequestPtr request_ptr = ShowCollectionInfoRequest::Create(context, collection_name, collection_info); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::Search(const std::shared_ptr& context, const std::string& collection_name, - const engine::VectorsData& vectors, int64_t topk, const milvus::json& extra_params, - const std::vector& partition_list, const std::vector& file_id_list, - TopKQueryResult& result) { - BaseRequestPtr request_ptr = SearchRequest::Create(context, collection_name, vectors, topk, extra_params, - partition_list, file_id_list, result); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::SearchByID(const std::shared_ptr& context, const std::string& collection_name, - const std::vector& id_array, int64_t topk, const milvus::json& extra_params, - const std::vector& partition_list, TopKQueryResult& result) { - BaseRequestPtr request_ptr = - SearchByIDRequest::Create(context, collection_name, id_array, topk, extra_params, partition_list, result); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::DescribeCollection(const std::shared_ptr& context, const std::string& collection_name, - CollectionSchema& collection_schema) { - BaseRequestPtr request_ptr = DescribeCollectionRequest::Create(context, collection_name, collection_schema); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::CountCollection(const std::shared_ptr& context, const std::string& collection_name, - int64_t& count) { - BaseRequestPtr request_ptr = CountCollectionRequest::Create(context, collection_name, count); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::Cmd(const std::shared_ptr& context, const std::string& cmd, std::string& reply) { - BaseRequestPtr request_ptr = CmdRequest::Create(context, cmd, reply); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::DeleteByID(const std::shared_ptr& context, const std::string& collection_name, - const std::vector& vector_ids) { - BaseRequestPtr request_ptr = DeleteByIDRequest::Create(context, collection_name, vector_ids); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::PreloadCollection(const std::shared_ptr& context, const std::string& collection_name) { - BaseRequestPtr request_ptr = PreloadCollectionRequest::Create(context, collection_name); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::ReLoadSegments(const std::shared_ptr& context, const std::string& collection_name, - const std::vector& segment_ids) { - BaseRequestPtr request_ptr = ReLoadSegmentsRequest::Create(context, collection_name, segment_ids); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::DescribeIndex(const std::shared_ptr& context, const std::string& collection_name, - IndexParam& param) { - BaseRequestPtr request_ptr = DescribeIndexRequest::Create(context, collection_name, param); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::DropIndex(const std::shared_ptr& context, const std::string& collection_name) { - BaseRequestPtr request_ptr = DropIndexRequest::Create(context, collection_name); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::CreatePartition(const std::shared_ptr& context, const std::string& collection_name, - const std::string& tag) { - BaseRequestPtr request_ptr = CreatePartitionRequest::Create(context, collection_name, tag); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::HasPartition(const std::shared_ptr& context, const std::string& collection_name, - const std::string& tag, bool& has_partition) { - BaseRequestPtr request_ptr = HasPartitionRequest::Create(context, collection_name, tag, has_partition); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::ShowPartitions(const std::shared_ptr& context, const std::string& collection_name, - std::vector& partitions) { - BaseRequestPtr request_ptr = ShowPartitionsRequest::Create(context, collection_name, partitions); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::DropPartition(const std::shared_ptr& context, const std::string& collection_name, - const std::string& tag) { - BaseRequestPtr request_ptr = DropPartitionRequest::Create(context, collection_name, tag); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::Flush(const std::shared_ptr& context, const std::vector& collection_names) { - BaseRequestPtr request_ptr = FlushRequest::Create(context, collection_names); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -Status -RequestHandler::Compact(const std::shared_ptr& context, const std::string& collection_name, - double compact_threshold) { - BaseRequestPtr request_ptr = CompactRequest::Create(context, collection_name, compact_threshold); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -/*******************************************New Interface*********************************************/ - -Status -RequestHandler::CreateHybridCollection(const std::shared_ptr& context, const std::string& collection_name, - std::vector>& field_types, - std::vector>& vector_dimensions, - std::vector>& field_extra_params) { - BaseRequestPtr request_ptr = CreateHybridCollectionRequest::Create(context, collection_name, field_types, - vector_dimensions, field_extra_params); - - RequestScheduler::ExecRequest(request_ptr); - return request_ptr->status(); -} - -Status -RequestHandler::DescribeHybridCollection(const std::shared_ptr& context, const std::string& collection_name, - std::unordered_map& field_types) { - BaseRequestPtr request_ptr = DescribeHybridCollectionRequest::Create(context, collection_name, field_types); - - RequestScheduler::ExecRequest(request_ptr); - return request_ptr->status(); -} - -Status -RequestHandler::HasHybridCollection(const std::shared_ptr& context, std::string& collection_name, - bool& has_collection) { - return Status::OK(); -} - -Status -RequestHandler::InsertEntity(const std::shared_ptr& context, const std::string& collection_name, - const std::string& partition_tag, uint64_t& row_num, std::vector& field_names, - std::vector& attr_values, - std::unordered_map& vector_datas) { - BaseRequestPtr request_ptr = InsertEntityRequest::Create(context, collection_name, partition_tag, row_num, - field_names, attr_values, vector_datas); - - RequestScheduler::ExecRequest(request_ptr); - return request_ptr->status(); -} - -Status -RequestHandler::HybridSearch(const std::shared_ptr& context, - context::HybridSearchContextPtr hybrid_search_context, const std::string& collection_name, - std::vector& partition_list, milvus::query::GeneralQueryPtr& general_query, - TopKQueryResult& result) { - BaseRequestPtr request_ptr = HybridSearchRequest::Create(context, hybrid_search_context, collection_name, - partition_list, general_query, result); - RequestScheduler::ExecRequest(request_ptr); - - return request_ptr->status(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/RequestHandler.h b/core/src/server/delivery/RequestHandler.h deleted file mode 100644 index d1795b2aa6..0000000000 --- a/core/src/server/delivery/RequestHandler.h +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include - -#include "context/HybridSearchContext.h" -#include "query/BooleanQuery.h" -#include "server/delivery/request/BaseRequest.h" -#include "utils/Status.h" - -namespace milvus { -namespace server { - -class RequestHandler { - public: - RequestHandler() = default; - - Status - CreateCollection(const std::shared_ptr& context, const std::string& collection_name, int64_t dimension, - int64_t index_file_size, int64_t metric_type); - - Status - HasCollection(const std::shared_ptr& context, const std::string& collection_name, bool& has_collection); - - Status - DropCollection(const std::shared_ptr& context, const std::string& collection_name); - - Status - CreateIndex(const std::shared_ptr& context, const std::string& collection_name, int64_t index_type, - const milvus::json& json_params); - - Status - Insert(const std::shared_ptr& context, const std::string& collection_name, engine::VectorsData& vectors, - const std::string& partition_tag); - - Status - GetVectorsByID(const std::shared_ptr& context, const std::string& collection_name, - const std::vector& ids, std::vector& vectors); - - Status - GetVectorIDs(const std::shared_ptr& context, const std::string& collection_name, - const std::string& segment_name, std::vector& vector_ids); - - Status - ShowCollections(const std::shared_ptr& context, std::vector& collections); - - Status - ShowCollectionInfo(const std::shared_ptr& context, const std::string& collection_name, - std::string& collection_info); - - Status - Search(const std::shared_ptr& context, const std::string& collection_name, - const engine::VectorsData& vectors, int64_t topk, const milvus::json& extra_params, - const std::vector& partition_list, const std::vector& file_id_list, - TopKQueryResult& result); - - Status - SearchByID(const std::shared_ptr& context, const std::string& collection_name, - const std::vector& id_array, int64_t topk, const milvus::json& extra_params, - const std::vector& partition_list, TopKQueryResult& result); - - Status - DescribeCollection(const std::shared_ptr& context, const std::string& collection_name, - CollectionSchema& collection_schema); - - Status - CountCollection(const std::shared_ptr& context, const std::string& collection_name, int64_t& count); - - Status - Cmd(const std::shared_ptr& context, const std::string& cmd, std::string& reply); - - Status - DeleteByID(const std::shared_ptr& context, const std::string& collection_name, - const std::vector& vector_ids); - - Status - PreloadCollection(const std::shared_ptr& context, const std::string& collection_name); - - Status - ReLoadSegments(const std::shared_ptr& context, const std::string& collection_name, - const std::vector& segment_ids); - - Status - DescribeIndex(const std::shared_ptr& context, const std::string& collection_name, IndexParam& param); - - Status - DropIndex(const std::shared_ptr& context, const std::string& collection_name); - - Status - CreatePartition(const std::shared_ptr& context, const std::string& collection_name, - const std::string& tag); - - Status - HasPartition(const std::shared_ptr& context, const std::string& collection_name, const std::string& tag, - bool& has_partition); - - Status - ShowPartitions(const std::shared_ptr& context, const std::string& collection_name, - std::vector& partitions); - - Status - DropPartition(const std::shared_ptr& context, const std::string& collection_name, const std::string& tag); - - Status - Flush(const std::shared_ptr& context, const std::vector& collection_names); - - Status - Compact(const std::shared_ptr& context, const std::string& collection_name, double compact_threshold); - - /*******************************************New Interface*********************************************/ - - Status - CreateHybridCollection(const std::shared_ptr& context, const std::string& collection_name, - std::vector>& field_types, - std::vector>& vector_dimensions, - std::vector>& field_extra_params); - - Status - DescribeHybridCollection(const std::shared_ptr& context, const std::string& collection_name, - std::unordered_map& field_types); - - Status - HasHybridCollection(const std::shared_ptr& context, std::string& collection_name, bool& has_collection); - - Status - DropHybridCollection(const std::shared_ptr& context, std::string& collection_name); - - Status - InsertEntity(const std::shared_ptr& context, const std::string& collection_name, - const std::string& partition_tag, uint64_t& row_num, std::vector& field_names, - std::vector& attr_values, std::unordered_map& vector_datas); - - Status - HybridSearch(const std::shared_ptr& context, context::HybridSearchContextPtr hybrid_search_context, - const std::string& collection_name, std::vector& partition_list, - query::GeneralQueryPtr& boolean_query, TopKQueryResult& result); -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/RequestQueue.cpp b/core/src/server/delivery/RequestQueue.cpp deleted file mode 100644 index a35f02cf42..0000000000 --- a/core/src/server/delivery/RequestQueue.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/RequestQueue.h" -#include "server/delivery/strategy/RequestStrategy.h" -#include "server/delivery/strategy/SearchReqStrategy.h" -#include "utils/Log.h" - -#include -#include -#include -#include - -namespace milvus { -namespace server { - -namespace { -Status -ScheduleRequest(const BaseRequestPtr& request, std::queue& queue) { -#if 1 - if (request == nullptr) { - return Status(SERVER_NULL_POINTER, "request schedule cannot handle null object"); - } - - if (queue.empty()) { - queue.push(request); - return Status::OK(); - } - - static std::map s_schedulers = { - {BaseRequest::kSearch, std::make_shared()}}; - - auto iter = s_schedulers.find(request->GetRequestType()); - if (iter == s_schedulers.end() || iter->second == nullptr) { - queue.push(request); - } else { - iter->second->ReScheduleQueue(request, queue); - } -#else - queue.push(request); -#endif - - return Status::OK(); -} -} // namespace - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -RequestQueue::RequestQueue() { -} - -RequestQueue::~RequestQueue() { -} - -BaseRequestPtr -RequestQueue::TakeRequest() { - return Take(); -} - -Status -RequestQueue::PutRequest(const BaseRequestPtr& request_ptr) { - std::unique_lock lock(mtx); - full_.wait(lock, [this] { return (queue_.size() < capacity_); }); - auto status = ScheduleRequest(request_ptr, queue_); - empty_.notify_all(); - return status; -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/RequestQueue.h b/core/src/server/delivery/RequestQueue.h deleted file mode 100644 index 65292cc47c..0000000000 --- a/core/src/server/delivery/RequestQueue.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/delivery/request/BaseRequest.h" -#include "utils/BlockingQueue.h" -#include "utils/Status.h" - -#include -#include -#include -#include -#include - -namespace milvus { -namespace server { - -using BlockingRequestQueue = BlockingQueue; - -class RequestQueue : public BlockingRequestQueue { - public: - RequestQueue(); - virtual ~RequestQueue(); - - BaseRequestPtr - TakeRequest(); - - Status - PutRequest(const BaseRequestPtr& request_ptr); -}; - -using RequestQueuePtr = std::shared_ptr; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/RequestScheduler.cpp b/core/src/server/delivery/RequestScheduler.cpp deleted file mode 100644 index 264c7f1419..0000000000 --- a/core/src/server/delivery/RequestScheduler.cpp +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/RequestScheduler.h" -#include "utils/Log.h" - -#include -#include -#include - -namespace milvus { -namespace server { - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -RequestScheduler::RequestScheduler() : stopped_(false) { - Start(); -} - -RequestScheduler::~RequestScheduler() { - Stop(); -} - -void -RequestScheduler::ExecRequest(BaseRequestPtr& request_ptr) { - if (request_ptr == nullptr) { - return; - } - - RequestScheduler& scheduler = RequestScheduler::GetInstance(); - scheduler.ExecuteRequest(request_ptr); -} - -void -RequestScheduler::Start() { - if (!stopped_) { - return; - } - - stopped_ = false; -} - -void -RequestScheduler::Stop() { - if (stopped_ && request_groups_.empty() && execute_threads_.empty()) { - return; - } - - LOG_SERVER_INFO_ << "Scheduler gonna stop..."; - { - std::lock_guard lock(queue_mtx_); - for (auto& iter : request_groups_) { - if (iter.second != nullptr) { - iter.second->Put(nullptr); - } - } - } - - for (auto& iter : execute_threads_) { - if (iter == nullptr) - continue; - iter->join(); - } - request_groups_.clear(); - execute_threads_.clear(); - stopped_ = true; - LOG_SERVER_INFO_ << "Scheduler stopped"; -} - -Status -RequestScheduler::ExecuteRequest(const BaseRequestPtr& request_ptr) { - if (request_ptr == nullptr) { - return Status::OK(); - } - - auto status = request_ptr->PreExecute(); - if (!status.ok()) { - request_ptr->Done(); - return status; - } - - status = PutToQueue(request_ptr); - fiu_do_on("RequestScheduler.ExecuteRequest.push_queue_fail", status = Status(SERVER_INVALID_ARGUMENT, "")); - - if (!status.ok()) { - LOG_SERVER_ERROR_ << "Put request to queue failed with code: " << status.ToString(); - request_ptr->Done(); - return status; - } - - if (request_ptr->IsAsync()) { - return Status::OK(); // async execution, caller need to call WaitToFinish at somewhere - } - - status = request_ptr->WaitToFinish(); // sync execution - if (!status.ok()) { - return status; - } - - return request_ptr->PostExecute(); -} - -void -RequestScheduler::TakeToExecute(RequestQueuePtr request_queue) { - SetThreadName("reqsched_thread"); - if (request_queue == nullptr) { - return; - } - - while (true) { - BaseRequestPtr request = request_queue->TakeRequest(); - if (request == nullptr) { - LOG_SERVER_ERROR_ << "Take null from request queue, stop thread"; - break; // stop the thread - } - - try { - fiu_do_on("RequestScheduler.TakeToExecute.throw_std_exception1", throw std::exception()); - auto status = request->Execute(); - fiu_do_on("RequestScheduler.TakeToExecute.throw_std_exception", throw std::exception()); - fiu_do_on("RequestScheduler.TakeToExecute.execute_fail", status = Status(SERVER_INVALID_ARGUMENT, "")); - if (!status.ok()) { - LOG_SERVER_ERROR_ << "Request failed with code: " << status.ToString(); - } - } catch (std::exception& ex) { - LOG_SERVER_ERROR_ << "Request failed to execute: " << ex.what(); - } - } -} - -Status -RequestScheduler::PutToQueue(const BaseRequestPtr& request_ptr) { - std::lock_guard lock(queue_mtx_); - - std::string group_name = request_ptr->RequestGroup(); - if (request_groups_.count(group_name) > 0) { - request_groups_[group_name]->PutRequest(request_ptr); - } else { - RequestQueuePtr queue = std::make_shared(); - queue->PutRequest(request_ptr); - request_groups_.insert(std::make_pair(group_name, queue)); - fiu_do_on("RequestScheduler.PutToQueue.null_queue", queue = nullptr); - - // start a thread - ThreadPtr thread = std::make_shared(&RequestScheduler::TakeToExecute, this, queue); - - fiu_do_on("RequestScheduler.PutToQueue.push_null_thread", execute_threads_.push_back(nullptr)); - execute_threads_.push_back(thread); - LOG_SERVER_INFO_ << "Create new thread for request group: " << group_name; - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/RequestScheduler.h b/core/src/server/delivery/RequestScheduler.h deleted file mode 100644 index c7e2a32369..0000000000 --- a/core/src/server/delivery/RequestScheduler.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/delivery/RequestQueue.h" -#include "utils/Status.h" - -#include -#include -#include -#include -#include - -namespace milvus { -namespace server { - -using ThreadPtr = std::shared_ptr; - -class RequestScheduler { - public: - static RequestScheduler& - GetInstance() { - static RequestScheduler scheduler; - return scheduler; - } - - void - Start(); - - void - Stop(); - - Status - ExecuteRequest(const BaseRequestPtr& request_ptr); - - static void - ExecRequest(BaseRequestPtr& request_ptr); - - protected: - RequestScheduler(); - - virtual ~RequestScheduler(); - - void - TakeToExecute(RequestQueuePtr request_queue); - - Status - PutToQueue(const BaseRequestPtr& request_ptr); - - private: - mutable std::mutex queue_mtx_; - - std::map request_groups_; - - std::vector execute_threads_; - - bool stopped_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/hybrid_request/CreateHybridCollectionRequest.cpp b/core/src/server/delivery/hybrid_request/CreateHybridCollectionRequest.cpp deleted file mode 100644 index f39e97e23b..0000000000 --- a/core/src/server/delivery/hybrid_request/CreateHybridCollectionRequest.cpp +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/hybrid_request/CreateHybridCollectionRequest.h" -#include "db/Utils.h" -#include "server/DBWrapper.h" -#include "server/delivery/request/BaseRequest.h" -#include "server/web_impl/Constants.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include -#include -#include -#include - -namespace milvus { -namespace server { - -CreateHybridCollectionRequest::CreateHybridCollectionRequest( - const std::shared_ptr& context, const std::string& collection_name, - std::vector>& field_types, - std::vector>& vector_dimensions, - std::vector>& field_params) - : BaseRequest(context, BaseRequest::kCreateHybridCollection), - collection_name_(collection_name), - field_types_(field_types), - vector_dimensions_(vector_dimensions), - field_params_(field_params) { -} - -BaseRequestPtr -CreateHybridCollectionRequest::Create(const std::shared_ptr& context, - const std::string& collection_name, - std::vector>& field_types, - std::vector>& vector_dimensions, - std::vector>& field_params) { - return std::shared_ptr( - new CreateHybridCollectionRequest(context, collection_name, field_types, vector_dimensions, field_params)); -} - -Status -CreateHybridCollectionRequest::OnExecute() { - std::string hdr = "CreateCollectionRequest(collection=" + collection_name_ + ")"; - TimeRecorderAuto rc(hdr); - - try { - // step 1: check arguments - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - fiu_do_on("CreateHybridCollectionRequest.OnExecute.invalid_collection_name", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - return status; - } - - rc.RecordSection("check validation"); - - // step 2: construct collection schema and vector schema - engine::meta::CollectionSchema collection_info; - engine::meta::hybrid::FieldsSchema fields_schema; - - auto size = field_types_.size(); - collection_info.collection_id_ = collection_name_; - fields_schema.fields_schema_.resize(size + 1); - for (uint64_t i = 0; i < size; ++i) { - fields_schema.fields_schema_[i].collection_id_ = collection_name_; - fields_schema.fields_schema_[i].field_name_ = field_types_[i].first; - fields_schema.fields_schema_[i].field_type_ = (int32_t)field_types_[i].second; - fields_schema.fields_schema_[i].field_params_ = field_params_[i].second; - } - fields_schema.fields_schema_[size].collection_id_ = collection_name_; - fields_schema.fields_schema_[size].field_name_ = vector_dimensions_[0].first; - fields_schema.fields_schema_[size].field_type_ = (int32_t)engine::meta::hybrid::DataType::VECTOR; - auto vector_param = field_params_[size].second; - fields_schema.fields_schema_[size].field_params_ = vector_param; - - collection_info.dimension_ = vector_dimensions_[0].second; - - if (vector_param != "") { - auto json_param = nlohmann::json::parse(vector_param); - if (json_param.contains("metric_type")) { - int32_t metric_type = json_param["metric_type"]; - collection_info.metric_type_ = metric_type; - } - if (json_param.contains("engine_type")) { - int32_t engine_type = json_param["engine_type"]; - collection_info.engine_type_ = engine_type; - } - } - - // step 3: create collection - status = DBWrapper::DB()->CreateHybridCollection(collection_info, fields_schema); - fiu_do_on("CreateHybridCollectionRequest.OnExecute.invalid_db_execute", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - // collection could exist - if (status.code() == DB_ALREADY_EXIST) { - return Status(SERVER_INVALID_COLLECTION_NAME, status.message()); - } - return status; - } - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/hybrid_request/CreateHybridCollectionRequest.h b/core/src/server/delivery/hybrid_request/CreateHybridCollectionRequest.h deleted file mode 100644 index f31caced38..0000000000 --- a/core/src/server/delivery/hybrid_request/CreateHybridCollectionRequest.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include - -#include "server/delivery/request/BaseRequest.h" - -namespace milvus { -namespace server { - -class CreateHybridCollectionRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - std::vector>& field_types, - std::vector>& vector_dimensions, - std::vector>& field_params); - - protected: - CreateHybridCollectionRequest(const std::shared_ptr& context, - const std::string& collection_name, - std::vector>& field_types, - std::vector>& vector_dimensions, - std::vector>& field_arams); - - Status - OnExecute() override; - - private: - const std::string collection_name_; - std::vector>& field_types_; - std::vector> vector_dimensions_; - std::vector>& field_params_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/hybrid_request/DescribeHybridCollectionRequest.cpp b/core/src/server/delivery/hybrid_request/DescribeHybridCollectionRequest.cpp deleted file mode 100644 index 2c50cc7573..0000000000 --- a/core/src/server/delivery/hybrid_request/DescribeHybridCollectionRequest.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/hybrid_request/DescribeHybridCollectionRequest.h" -#include "db/Utils.h" -#include "server/DBWrapper.h" -#include "server/delivery/request/BaseRequest.h" -#include "server/web_impl/Constants.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include -#include -#include -#include -#include - -namespace milvus { -namespace server { - -DescribeHybridCollectionRequest::DescribeHybridCollectionRequest( - const std::shared_ptr& context, const std::string& collection_name, - std::unordered_map& field_types) - : BaseRequest(context, BaseRequest::kDescribeHybridCollection), - collection_name_(collection_name), - field_types_(field_types) { -} - -BaseRequestPtr -DescribeHybridCollectionRequest::Create(const std::shared_ptr& context, - const std::string& collection_name, - std::unordered_map& field_types) { - return std::shared_ptr(new DescribeHybridCollectionRequest(context, collection_name, field_types)); -} - -Status -DescribeHybridCollectionRequest::OnExecute() { - std::string hdr = "CreateCollectionRequest(collection=" + collection_name_ + ")"; - TimeRecorderAuto rc(hdr); - - try { - engine::meta::CollectionSchema collection_schema; - engine::meta::hybrid::FieldsSchema fields_schema; - collection_schema.collection_id_ = collection_name_; - auto status = DBWrapper::DB()->DescribeHybridCollection(collection_schema, fields_schema); - fiu_do_on("DescribeHybridCollectionRequest.OnExecute.invalid_db_execute", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - return status; - } - - for (auto schema : fields_schema.fields_schema_) { - field_types_.insert(std::make_pair(schema.field_name_, (engine::meta::hybrid::DataType)schema.field_type_)); - } - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/hybrid_request/DescribeHybridCollectionRequest.h b/core/src/server/delivery/hybrid_request/DescribeHybridCollectionRequest.h deleted file mode 100644 index ce6ab47aaa..0000000000 --- a/core/src/server/delivery/hybrid_request/DescribeHybridCollectionRequest.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include - -#include "server/delivery/request/BaseRequest.h" - -namespace milvus { -namespace server { - -class DescribeHybridCollectionRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - std::unordered_map& field_types); - - protected: - DescribeHybridCollectionRequest(const std::shared_ptr& context, - const std::string& collection_name, - std::unordered_map& field_types); - - Status - OnExecute() override; - - private: - const std::string collection_name_; - std::unordered_map& field_types_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/hybrid_request/HybridSearchRequest.cpp b/core/src/server/delivery/hybrid_request/HybridSearchRequest.cpp deleted file mode 100644 index 8ef7731f69..0000000000 --- a/core/src/server/delivery/hybrid_request/HybridSearchRequest.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/hybrid_request/HybridSearchRequest.h" -#include "db/Utils.h" -#include "server/DBWrapper.h" -#include "utils/CommonUtil.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include -#include -#include -#include -#include -#ifdef ENABLE_CPU_PROFILING -#include -#endif - -namespace milvus { -namespace server { - -HybridSearchRequest::HybridSearchRequest(const std::shared_ptr& context, - context::HybridSearchContextPtr& hybrid_search_context, - const std::string& collection_name, std::vector& partition_list, - milvus::query::GeneralQueryPtr& general_query, TopKQueryResult& result) - : BaseRequest(context, BaseRequest::kHybridSearch), - hybrid_search_contxt_(hybrid_search_context), - collection_name_(collection_name), - partition_list_(partition_list), - general_query_(general_query), - result_(result) { -} - -BaseRequestPtr -HybridSearchRequest::Create(const std::shared_ptr& context, - context::HybridSearchContextPtr& hybrid_search_context, const std::string& collection_name, - std::vector& partition_list, milvus::query::GeneralQueryPtr& general_query, - TopKQueryResult& result) { - return std::shared_ptr(new HybridSearchRequest(context, hybrid_search_context, collection_name, - partition_list, general_query, result)); -} - -Status -HybridSearchRequest::OnExecute() { - try { - fiu_do_on("HybridSearchRequest.OnExecute.throw_std_exception", throw std::exception()); - std::string hdr = "SearchRequest(table=" + collection_name_; - - TimeRecorder rc(hdr); - - // step 1: check table name - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - return status; - } - - // step 2: check table existence - // only process root table, ignore partition table - engine::meta::CollectionSchema collection_schema; - engine::meta::hybrid::FieldsSchema fields_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeHybridCollection(collection_schema, fields_schema); - fiu_do_on("HybridSearchRequest.OnExecute.describe_table_fail", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - std::unordered_map attr_type; - for (uint64_t i = 0; i < fields_schema.fields_schema_.size(); ++i) { - attr_type.insert( - std::make_pair(fields_schema.fields_schema_[i].field_name_, - (engine::meta::hybrid::DataType)fields_schema.fields_schema_[i].field_type_)); - } - - engine::ResultIds result_ids; - engine::ResultDistances result_distances; - uint64_t nq; - - status = DBWrapper::DB()->HybridQuery(context_, collection_name_, partition_list_, hybrid_search_contxt_, - general_query_, attr_type, nq, result_ids, result_distances); - -#ifdef ENABLE_CPU_PROFILING - ProfilerStop(); -#endif - - fiu_do_on("HybridSearchRequest.OnExecute.query_fail", status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - return status; - } - fiu_do_on("HybridSearchRequest.OnExecute.empty_result_ids", result_ids.clear()); - if (result_ids.empty()) { - return Status::OK(); // empty table - } - - auto post_query_ctx = context_->Child("Constructing result"); - - // step 7: construct result array - result_.row_num_ = nq; - result_.distance_list_ = result_distances; - result_.id_list_ = result_ids; - - post_query_ctx->GetTraceContext()->GetSpan()->Finish(); - - // step 8: print time cost percent - rc.RecordSection("construct result and send"); - rc.ElapseFromBegin("totally cost"); - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/hybrid_request/HybridSearchRequest.h b/core/src/server/delivery/hybrid_request/HybridSearchRequest.h deleted file mode 100644 index a0f91a50a5..0000000000 --- a/core/src/server/delivery/hybrid_request/HybridSearchRequest.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/delivery/request/BaseRequest.h" - -#include -#include -#include -#include - -namespace milvus { -namespace server { - -class HybridSearchRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, - context::HybridSearchContextPtr& hybrid_search_context, const std::string& collection_name, - std::vector& partition_list, milvus::query::GeneralQueryPtr& general_query, - TopKQueryResult& result); - - protected: - HybridSearchRequest(const std::shared_ptr& context, - context::HybridSearchContextPtr& hybrid_search_context, const std::string& collection_name, - std::vector& partition_list, milvus::query::GeneralQueryPtr& general_query, - TopKQueryResult& result); - - Status - OnExecute() override; - - private: - context::HybridSearchContextPtr hybrid_search_contxt_; - const std::string collection_name_; - std::vector& partition_list_; - milvus::query::GeneralQueryPtr& general_query_; - TopKQueryResult& result_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/hybrid_request/InsertEntityRequest.cpp b/core/src/server/delivery/hybrid_request/InsertEntityRequest.cpp deleted file mode 100644 index 687a1d5bd6..0000000000 --- a/core/src/server/delivery/hybrid_request/InsertEntityRequest.cpp +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/hybrid_request/InsertEntityRequest.h" -#include "db/Utils.h" -#include "server/DBWrapper.h" -#include "utils/CommonUtil.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include -#include -#include -#include -#include -#ifdef ENABLE_CPU_PROFILING -#include -#endif - -namespace milvus { -namespace server { - -InsertEntityRequest::InsertEntityRequest(const std::shared_ptr& context, - const std::string& collection_name, const std::string& partition_tag, - uint64_t& row_num, std::vector& field_names, - std::vector& attr_values, - std::unordered_map& vector_datas) - : BaseRequest(context, BaseRequest::kInsertEntity), - collection_name_(collection_name), - partition_tag_(partition_tag), - row_num_(row_num), - field_names_(field_names), - attr_values_(attr_values), - vector_datas_(vector_datas) { -} - -BaseRequestPtr -InsertEntityRequest::Create(const std::shared_ptr& context, const std::string& collection_name, - const std::string& partition_tag, uint64_t& row_num, std::vector& field_names, - std::vector& attr_values, - std::unordered_map& vector_datas) { - return std::shared_ptr(new InsertEntityRequest(context, collection_name, partition_tag, row_num, - field_names, attr_values, vector_datas)); -} - -Status -InsertEntityRequest::OnExecute() { - try { - fiu_do_on("InsertEntityRequest.OnExecute.throw_std_exception", throw std::exception()); - std::string hdr = "InsertEntityRequest(table=" + collection_name_ + ", partition_tag=" + partition_tag_ + ")"; - TimeRecorder rc(hdr); - - // step 1: check arguments - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - return status; - } - - auto vector_datas_it = vector_datas_.begin(); - if (vector_datas_it->second.float_data_.empty() && vector_datas_it->second.binary_data_.empty()) { - return Status(SERVER_INVALID_ROWRECORD_ARRAY, - "The vector array is emp ty. Make sure you have entered vector records."); - } - - // step 2: check table existence - // only process root table, ignore partition table - engine::meta::CollectionSchema collection_schema; - engine::meta::hybrid::FieldsSchema fields_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeHybridCollection(collection_schema, fields_schema); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - std::unordered_map field_types; - auto size = fields_schema.fields_schema_.size(); - for (auto field_name : field_names_) { - for (uint64_t i = 0; i < size; ++i) { - if (fields_schema.fields_schema_[i].field_name_ == field_name) { - field_types.insert(std::make_pair( - field_name, (engine::meta::hybrid::DataType)fields_schema.fields_schema_[i].field_type_)); - } - } - } - - // step 3: check table flag - // all user provide id, or all internal id - bool user_provide_ids = !vector_datas_it->second.id_array_.empty(); - fiu_do_on("InsertEntityRequest.OnExecute.illegal_vector_id", user_provide_ids = false; - collection_schema.flag_ = engine::meta::FLAG_MASK_HAS_USERID); - // user already provided id before, all insert action require user id - if ((collection_schema.flag_ & engine::meta::FLAG_MASK_HAS_USERID) != 0 && !user_provide_ids) { - return Status(SERVER_ILLEGAL_VECTOR_ID, - "Collection vector IDs are user-defined. Please provide IDs for all vectors of this table."); - } - - fiu_do_on("InsertRequest.OnExecute.illegal_vector_id2", user_provide_ids = true; - collection_schema.flag_ = engine::meta::FLAG_MASK_NO_USERID); - // user didn't provided id before, no need to provide user id - if ((collection_schema.flag_ & engine::meta::FLAG_MASK_NO_USERID) != 0 && user_provide_ids) { - return Status( - SERVER_ILLEGAL_VECTOR_ID, - "Table vector IDs are auto-generated. All vectors of this table must use auto-generated IDs."); - } - - rc.RecordSection("check validation"); - -#ifdef ENABLE_CPU_PROFILING - std::string fname = "/tmp/insert_" + CommonUtil::GetCurrentTimeStr() + ".profiling"; - ProfilerStart(fname.c_str()); -#endif - // step 4: some metric type doesn't support float vectors - - // TODO(yukun): check dimension and metric_type - - // step 5: insert entities - // auto vec_count = static_cast(vector_datas_it->second.vector_count_); - - engine::Entity entity; - entity.entity_count_ = row_num_; - - entity.attr_value_ = attr_values_; - entity.vector_data_.insert(std::make_pair(vector_datas_it->first, vector_datas_it->second)); - - rc.RecordSection("prepare vectors data"); - status = DBWrapper::DB()->InsertEntities(collection_name_, partition_tag_, field_names_, entity, field_types); - fiu_do_on("InsertRequest.OnExecute.insert_fail", status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - return status; - } - vector_datas_it->second.id_array_ = entity.id_array_; - - // auto ids_size = vectors_data_.id_array_.size(); - // fiu_do_on("InsertRequest.OnExecute.invalid_ids_size", ids_size = vec_count - 1); - // if (ids_size != vec_count) { - // std::string msg = - // "Add " + std::to_string(vec_count) + " vectors but only return " + std::to_string(ids_size) + - // " id"; - // return Status(SERVER_ILLEGAL_VECTOR_ID, msg); - // } - - // step 6: update table flag - user_provide_ids ? collection_schema.flag_ |= engine::meta::FLAG_MASK_HAS_USERID - : collection_schema.flag_ |= engine::meta::FLAG_MASK_NO_USERID; - status = DBWrapper::DB()->UpdateCollectionFlag(collection_name_, collection_schema.flag_); - -#ifdef ENABLE_CPU_PROFILING - ProfilerStop(); -#endif - - rc.RecordSection("add vectors to engine"); - rc.ElapseFromBegin("total cost"); - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/hybrid_request/InsertEntityRequest.h b/core/src/server/delivery/hybrid_request/InsertEntityRequest.h deleted file mode 100644 index 3970a4ef08..0000000000 --- a/core/src/server/delivery/hybrid_request/InsertEntityRequest.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/delivery/request/BaseRequest.h" - -#include -#include -#include -#include - -namespace milvus { -namespace server { - -class InsertEntityRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - const std::string& partition_tag, uint64_t& row_num, std::vector& field_names, - std::vector& attr_values, std::unordered_map& vector_datas); - - protected: - InsertEntityRequest(const std::shared_ptr& context, const std::string& collection_name, - const std::string& partition_tag, uint64_t& row_num, std::vector& field_names, - std::vector& attr_values, - std::unordered_map& vector_datas); - - Status - OnExecute() override; - - private: - const std::string collection_name_; - const std::string partition_tag_; - uint64_t row_num_; - std::vector& field_names_; - std::vector& attr_values_; - std::unordered_map& vector_datas_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/BaseRequest.cpp b/core/src/server/delivery/request/BaseRequest.cpp deleted file mode 100644 index bac0015763..0000000000 --- a/core/src/server/delivery/request/BaseRequest.cpp +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/BaseRequest.h" - -#include - -#include "server/context/Context.h" -#include "utils/CommonUtil.h" -#include "utils/Exception.h" -#include "utils/Log.h" - -namespace milvus { -namespace server { - -static const char* DQL_REQUEST_GROUP = "dql"; -static const char* DDL_DML_REQUEST_GROUP = "ddl_dml"; -static const char* INFO_REQUEST_GROUP = "info"; - -namespace { -std::string -RequestGroup(BaseRequest::RequestType type) { - static std::map s_map_type_group = { - // general operations - {BaseRequest::kCmd, INFO_REQUEST_GROUP}, - - // data operations - {BaseRequest::kInsert, DDL_DML_REQUEST_GROUP}, - {BaseRequest::kCompact, DDL_DML_REQUEST_GROUP}, - {BaseRequest::kFlush, DDL_DML_REQUEST_GROUP}, - {BaseRequest::kDeleteByID, DDL_DML_REQUEST_GROUP}, - {BaseRequest::kGetVectorByID, INFO_REQUEST_GROUP}, - {BaseRequest::kGetVectorIDs, INFO_REQUEST_GROUP}, - {BaseRequest::kInsertEntity, DDL_DML_REQUEST_GROUP}, - - // collection operations - {BaseRequest::kShowCollections, INFO_REQUEST_GROUP}, - {BaseRequest::kCreateCollection, DDL_DML_REQUEST_GROUP}, - {BaseRequest::kHasCollection, INFO_REQUEST_GROUP}, - {BaseRequest::kDescribeCollection, INFO_REQUEST_GROUP}, - {BaseRequest::kCountCollection, INFO_REQUEST_GROUP}, - {BaseRequest::kShowCollectionInfo, INFO_REQUEST_GROUP}, - {BaseRequest::kDropCollection, DDL_DML_REQUEST_GROUP}, - {BaseRequest::kPreloadCollection, DQL_REQUEST_GROUP}, - {BaseRequest::kCreateHybridCollection, DDL_DML_REQUEST_GROUP}, - {BaseRequest::kDescribeHybridCollection, INFO_REQUEST_GROUP}, - {BaseRequest::kReloadSegments, DQL_REQUEST_GROUP}, - - // partition operations - {BaseRequest::kCreatePartition, DDL_DML_REQUEST_GROUP}, - {BaseRequest::kShowPartitions, INFO_REQUEST_GROUP}, - {BaseRequest::kDropPartition, DDL_DML_REQUEST_GROUP}, - - // index operations - {BaseRequest::kCreateIndex, DDL_DML_REQUEST_GROUP}, - {BaseRequest::kDescribeIndex, INFO_REQUEST_GROUP}, - {BaseRequest::kDropIndex, DDL_DML_REQUEST_GROUP}, - - // search operations - {BaseRequest::kSearchByID, DQL_REQUEST_GROUP}, - {BaseRequest::kSearch, DQL_REQUEST_GROUP}, - {BaseRequest::kSearchCombine, DQL_REQUEST_GROUP}, - {BaseRequest::kHybridSearch, DQL_REQUEST_GROUP}, - }; - - auto iter = s_map_type_group.find(type); - if (iter == s_map_type_group.end()) { - LOG_SERVER_ERROR_ << "Unsupported request type: " << type; - throw Exception(SERVER_NOT_IMPLEMENT, "request group undefined"); - } - return iter->second; -} -} // namespace - -BaseRequest::BaseRequest(const std::shared_ptr& context, BaseRequest::RequestType type, - bool async) - : context_(context), type_(type), async_(async), done_(false) { - request_group_ = milvus::server::RequestGroup(type); - if (nullptr != context_) { - context_->SetRequestType(type_); - } -} - -BaseRequest::~BaseRequest() { - WaitToFinish(); -} - -Status -BaseRequest::PreExecute() { - status_ = OnPreExecute(); - if (!status_.ok()) { - Done(); - } - return status_; -} - -Status -BaseRequest::Execute() { - status_ = OnExecute(); - Done(); - return status_; -} - -Status -BaseRequest::PostExecute() { - status_ = OnPostExecute(); - return status_; -} - -Status -BaseRequest::OnPreExecute() { - return Status::OK(); -} - -Status -BaseRequest::OnPostExecute() { - return Status::OK(); -} - -void -BaseRequest::Done() { - std::unique_lock lock(finish_mtx_); - done_ = true; - finish_cond_.notify_all(); -} - -void -BaseRequest::set_status(const Status& status) { - status_ = status; - if (!status_.ok()) { - LOG_SERVER_ERROR_ << status_.message(); - } -} - -std::string -BaseRequest::CollectionNotExistMsg(const std::string& collection_name) { - return "Collection " + collection_name + - " does not exist. Use milvus.has_collection to verify whether the collection exists. " - "You also can check whether the collection name exists."; -} - -Status -BaseRequest::WaitToFinish() { - std::unique_lock lock(finish_mtx_); - finish_cond_.wait(lock, [this] { return done_; }); - return status_; -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/BaseRequest.h b/core/src/server/delivery/request/BaseRequest.h deleted file mode 100644 index 5ed4002800..0000000000 --- a/core/src/server/delivery/request/BaseRequest.h +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "db/Types.h" -#include "db/meta/MetaTypes.h" -#include "grpc/gen-milvus/milvus.grpc.pb.h" -#include "grpc/gen-status/status.grpc.pb.h" -#include "grpc/gen-status/status.pb.h" -#include "query/GeneralQuery.h" -#include "utils/Json.h" -#include "utils/Status.h" - -#include -//#include -#include -#include -#include -#include -#include - -namespace milvus { -namespace server { - -struct CollectionSchema { - std::string collection_name_; - int64_t dimension_; - int64_t index_file_size_; - int64_t metric_type_; - - CollectionSchema() { - dimension_ = 0; - index_file_size_ = 0; - metric_type_ = 0; - } - - CollectionSchema(const std::string& collection_name, int64_t dimension, int64_t index_file_size, - int64_t metric_type) { - collection_name_ = collection_name; - dimension_ = dimension; - index_file_size_ = index_file_size; - metric_type_ = metric_type; - } -}; - -struct TopKQueryResult { - int64_t row_num_; - engine::ResultIds id_list_; - engine::ResultDistances distance_list_; - - TopKQueryResult() { - row_num_ = 0; - } - - TopKQueryResult(int64_t row_num, const engine::ResultIds& id_list, const engine::ResultDistances& distance_list) { - row_num_ = row_num; - id_list_ = id_list; - distance_list_ = distance_list; - } -}; - -struct HybridQueryResult { - int64_t row_num_; - engine::ResultIds id_list_; - engine::ResultDistances distance_list_; - engine::Entity entities_; -}; - -struct IndexParam { - std::string collection_name_; - int64_t index_type_; - std::string extra_params_; - - IndexParam() { - index_type_ = 0; - } - - IndexParam(const std::string& collection_name, int64_t index_type) { - collection_name_ = collection_name; - index_type_ = index_type; - } -}; - -struct PartitionParam { - std::string collection_name_; - std::string tag_; - - PartitionParam() = default; - - PartitionParam(const std::string& collection_name, const std::string& tag) { - collection_name_ = collection_name; - tag_ = tag; - } -}; - -class Context; - -class BaseRequest { - public: - enum RequestType { - // general operations - kCmd = 100, - - // data operations - kInsert = 200, - kCompact, - kFlush, - kDeleteByID, - kGetVectorByID, - kGetVectorIDs, - kInsertEntity, - - // collection operations - kShowCollections = 300, - kCreateCollection, - kHasCollection, - kDescribeCollection, - kCountCollection, - kShowCollectionInfo, - kDropCollection, - kPreloadCollection, - kCreateHybridCollection, - kHasHybridCollection, - kDescribeHybridCollection, - kReloadSegments, - - // partition operations - kCreatePartition = 400, - kShowPartitions, - kDropPartition, - - // index operations - kCreateIndex = 500, - kDescribeIndex, - kDropIndex, - - // search operations - kSearchByID = 600, - kSearch, - kSearchCombine, - kHybridSearch, - }; - - protected: - BaseRequest(const std::shared_ptr& context, BaseRequest::RequestType type, - bool async = false); - - virtual ~BaseRequest(); - - public: - Status - PreExecute(); - - Status - Execute(); - - Status - PostExecute(); - - void - Done(); - - Status - WaitToFinish(); - - RequestType - GetRequestType() const { - return type_; - } - - std::string - RequestGroup() const { - return request_group_; - } - - const Status& - status() const { - return status_; - } - - void - set_status(const Status& status); - - bool - IsAsync() const { - return async_; - } - - protected: - virtual Status - OnPreExecute(); - - virtual Status - OnExecute() = 0; - - virtual Status - OnPostExecute(); - - std::string - CollectionNotExistMsg(const std::string& collection_name); - - protected: - const std::shared_ptr context_; - - RequestType type_; - std::string request_group_; - bool async_; - Status status_; - - private: - mutable std::mutex finish_mtx_; - std::condition_variable finish_cond_; - bool done_; - - public: - const std::shared_ptr& - Context() const { - return context_; - } -}; - -using BaseRequestPtr = std::shared_ptr; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/CmdRequest.cpp b/core/src/server/delivery/request/CmdRequest.cpp deleted file mode 100644 index d262288486..0000000000 --- a/core/src/server/delivery/request/CmdRequest.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/CmdRequest.h" -#include "config/Config.h" -#include "metrics/SystemInfo.h" -#include "scheduler/SchedInst.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" - -#include - -namespace milvus { -namespace server { - -CmdRequest::CmdRequest(const std::shared_ptr& context, const std::string& cmd, - std::string& result) - : BaseRequest(context, BaseRequest::kCmd), cmd_(cmd), result_(result) { -} - -BaseRequestPtr -CmdRequest::Create(const std::shared_ptr& context, const std::string& cmd, - std::string& result) { - return std::shared_ptr(new CmdRequest(context, cmd, result)); -} - -Status -CmdRequest::OnExecute() { - std::string hdr = "CmdRequest(cmd=" + cmd_ + ")"; - TimeRecorderAuto rc(hdr); - Status stat = Status::OK(); - - if (cmd_ == "version") { - result_ = MILVUS_VERSION; - } else if (cmd_ == "status") { - result_ = "OK"; - } else if (cmd_ == "tasktable") { - result_ = scheduler::ResMgrInst::GetInstance()->DumpTaskTables(); - } else if (cmd_ == "mode") { -#ifdef MILVUS_GPU_VERSION - result_ = "GPU"; -#else - result_ = "CPU"; -#endif - } else if (cmd_ == "get_system_info") { - server::SystemInfo& sys_info_inst = server::SystemInfo::GetInstance(); - sys_info_inst.GetSysInfoJsonStr(result_); - } else if (cmd_ == "build_commit_id") { - result_ = LAST_COMMIT_ID; - } else if (cmd_.substr(0, 10) == "set_config" || cmd_.substr(0, 10) == "get_config") { - server::Config& config = server::Config::GetInstance(); - stat = config.ProcessConfigCli(result_, cmd_); - } else { - result_ = "Unknown command"; - } - - return stat; -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/CmdRequest.h b/core/src/server/delivery/request/CmdRequest.h deleted file mode 100644 index efb840fdf1..0000000000 --- a/core/src/server/delivery/request/CmdRequest.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "BaseRequest.h" - -#include -#include - -namespace milvus { -namespace server { - -class CmdRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& cmd, std::string& result); - - protected: - CmdRequest(const std::shared_ptr& context, const std::string& cmd, std::string& result); - - Status - OnExecute() override; - - private: - const std::string cmd_; - std::string& result_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/CompactRequest.cpp b/core/src/server/delivery/request/CompactRequest.cpp deleted file mode 100644 index 58ff387487..0000000000 --- a/core/src/server/delivery/request/CompactRequest.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// 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 limitations -// under the License. - -#include "server/delivery/request/CompactRequest.h" -#include "server/DBWrapper.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include - -namespace milvus { -namespace server { - -CompactRequest::CompactRequest(const std::shared_ptr& context, - const std::string& collection_name, double compact_threshold) - : BaseRequest(context, BaseRequest::kCompact), - collection_name_(collection_name), - compact_threshold_(compact_threshold) { -} - -BaseRequestPtr -CompactRequest::Create(const std::shared_ptr& context, const std::string& collection_name, - double compact_threshold) { - return std::shared_ptr(new CompactRequest(context, collection_name, compact_threshold)); -} - -Status -CompactRequest::OnExecute() { - try { - std::string hdr = "CompactRequest(collection=" + collection_name_ + ")"; - TimeRecorderAuto rc(hdr); - - // step 1: check arguments - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - return status; - } - - // only process root collection, ignore partition collection - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeCollection(collection_schema); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - rc.RecordSection("check validation"); - - // step 2: check collection existence - status = DBWrapper::DB()->Compact(context_, collection_name_, compact_threshold_); - if (!status.ok()) { - return status; - } - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/CompactRequest.h b/core/src/server/delivery/request/CompactRequest.h deleted file mode 100644 index e2a1e998fa..0000000000 --- a/core/src/server/delivery/request/CompactRequest.h +++ /dev/null @@ -1,47 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include "server/delivery/request/BaseRequest.h" - -#include -#include - -namespace milvus { -namespace server { - -class CompactRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - double compact_threshold); - - protected: - CompactRequest(const std::shared_ptr& context, const std::string& collection_name, - double compact_threshold); - - Status - OnExecute() override; - - private: - const std::string collection_name_; - double compact_threshold_ = 0.0; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/CountCollectionRequest.cpp b/core/src/server/delivery/request/CountCollectionRequest.cpp deleted file mode 100644 index f8288d18f7..0000000000 --- a/core/src/server/delivery/request/CountCollectionRequest.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/CountCollectionRequest.h" -#include "BaseRequest.h" -#include "server/DBWrapper.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include - -namespace milvus { -namespace server { - -CountCollectionRequest::CountCollectionRequest(const std::shared_ptr& context, - const std::string& collection_name, int64_t& row_count) - : BaseRequest(context, BaseRequest::kCountCollection), collection_name_(collection_name), row_count_(row_count) { -} - -BaseRequestPtr -CountCollectionRequest::Create(const std::shared_ptr& context, - const std::string& collection_name, int64_t& row_count) { - return std::shared_ptr(new CountCollectionRequest(context, collection_name, row_count)); -} - -Status -CountCollectionRequest::OnExecute() { - try { - std::string hdr = "CountCollectionRequest(collection=" + collection_name_ + ")"; - TimeRecorderAuto rc(hdr); - - // step 1: check arguments - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - return status; - } - - // only process root collection, ignore partition collection - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeCollection(collection_schema); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - rc.RecordSection("check validation"); - - // step 2: get row count - uint64_t row_count = 0; - status = DBWrapper::DB()->GetCollectionRowCount(collection_name_, row_count); - fiu_do_on("CountCollectionRequest.OnExecute.db_not_found", status = Status(DB_NOT_FOUND, "")); - fiu_do_on("CountCollectionRequest.OnExecute.status_error", status = Status(SERVER_UNEXPECTED_ERROR, "")); - fiu_do_on("CountCollectionRequest.OnExecute.throw_std_exception", throw std::exception()); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } - - row_count_ = static_cast(row_count); - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/CountCollectionRequest.h b/core/src/server/delivery/request/CountCollectionRequest.h deleted file mode 100644 index 7b26c15849..0000000000 --- a/core/src/server/delivery/request/CountCollectionRequest.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/delivery/request/BaseRequest.h" - -#include -#include - -namespace milvus { -namespace server { - -class CountCollectionRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - int64_t& row_count); - - protected: - CountCollectionRequest(const std::shared_ptr& context, const std::string& collection_name, - int64_t& row_count); - - Status - OnExecute() override; - - private: - const std::string collection_name_; - int64_t& row_count_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/CreateCollectionRequest.cpp b/core/src/server/delivery/request/CreateCollectionRequest.cpp deleted file mode 100644 index d138f7979a..0000000000 --- a/core/src/server/delivery/request/CreateCollectionRequest.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/CreateCollectionRequest.h" -#include "db/Utils.h" -#include "server/DBWrapper.h" -#include "server/delivery/request/BaseRequest.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include -#include - -namespace milvus { -namespace server { - -CreateCollectionRequest::CreateCollectionRequest(const std::shared_ptr& context, - const std::string& collection_name, int64_t dimension, - int64_t index_file_size, int64_t metric_type) - : BaseRequest(context, BaseRequest::kCreateCollection), - collection_name_(collection_name), - dimension_(dimension), - index_file_size_(index_file_size), - metric_type_(metric_type) { -} - -BaseRequestPtr -CreateCollectionRequest::Create(const std::shared_ptr& context, - const std::string& collection_name, int64_t dimension, int64_t index_file_size, - int64_t metric_type) { - return std::shared_ptr( - new CreateCollectionRequest(context, collection_name, dimension, index_file_size, metric_type)); -} - -Status -CreateCollectionRequest::OnExecute() { - std::string hdr = - "CreateCollectionRequest(collection=" + collection_name_ + ", dimension=" + std::to_string(dimension_) + ")"; - TimeRecorderAuto rc(hdr); - - try { - // step 1: check arguments - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - return status; - } - - status = ValidationUtil::ValidateTableDimension(dimension_, metric_type_); - if (!status.ok()) { - return status; - } - - status = ValidationUtil::ValidateCollectionIndexFileSize(index_file_size_); - fiu_do_on("CreateCollectionRequest.OnExecute.invalid_index_file_size", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - return status; - } - - status = ValidationUtil::ValidateCollectionIndexMetricType(metric_type_); - if (!status.ok()) { - return status; - } - - rc.RecordSection("check validation"); - - // step 2: construct collection schema - engine::meta::CollectionSchema collection_info; - collection_info.collection_id_ = collection_name_; - collection_info.dimension_ = static_cast(dimension_); - collection_info.index_file_size_ = index_file_size_; - collection_info.metric_type_ = metric_type_; - - // some metric type only support binary vector, adapt the index type - if (engine::utils::IsBinaryMetricType(metric_type_)) { - if (collection_info.engine_type_ == static_cast(engine::EngineType::FAISS_IDMAP)) { - collection_info.engine_type_ = static_cast(engine::EngineType::FAISS_BIN_IDMAP); - } else if (collection_info.engine_type_ == static_cast(engine::EngineType::FAISS_IVFFLAT)) { - collection_info.engine_type_ = static_cast(engine::EngineType::FAISS_BIN_IVFFLAT); - } - } - - // step 3: create collection - status = DBWrapper::DB()->CreateCollection(collection_info); - fiu_do_on("CreateCollectionRequest.OnExecute.db_already_exist", status = Status(milvus::DB_ALREADY_EXIST, "")); - fiu_do_on("CreateCollectionRequest.OnExecute.create_collection_fail", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - fiu_do_on("CreateCollectionRequest.OnExecute.throw_std_exception", throw std::exception()); - if (!status.ok()) { - // collection could exist - if (status.code() == DB_ALREADY_EXIST) { - return Status(SERVER_INVALID_COLLECTION_NAME, status.message()); - } - return status; - } - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/CreateCollectionRequest.h b/core/src/server/delivery/request/CreateCollectionRequest.h deleted file mode 100644 index dda8a8bc66..0000000000 --- a/core/src/server/delivery/request/CreateCollectionRequest.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -#include "server/delivery/request/BaseRequest.h" - -namespace milvus { -namespace server { - -class CreateCollectionRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - int64_t dimension, int64_t index_file_size, int64_t metric_type); - - protected: - CreateCollectionRequest(const std::shared_ptr& context, const std::string& collection_name, - int64_t dimension, int64_t index_file_size, int64_t metric_type); - - Status - OnExecute() override; - - private: - const std::string collection_name_; - int64_t dimension_; - int64_t index_file_size_; - int64_t metric_type_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/CreateIndexRequest.cpp b/core/src/server/delivery/request/CreateIndexRequest.cpp deleted file mode 100644 index eaba58cde6..0000000000 --- a/core/src/server/delivery/request/CreateIndexRequest.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/CreateIndexRequest.h" -#include "config/Config.h" -#include "db/Utils.h" -#include "server/DBWrapper.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include -#include - -namespace milvus { -namespace server { - -CreateIndexRequest::CreateIndexRequest(const std::shared_ptr& context, - const std::string& collection_name, int64_t index_type, - const milvus::json& json_params) - : BaseRequest(context, BaseRequest::kCreateIndex), - collection_name_(collection_name), - index_type_(index_type), - json_params_(json_params) { -} - -BaseRequestPtr -CreateIndexRequest::Create(const std::shared_ptr& context, const std::string& collection_name, - int64_t index_type, const milvus::json& json_params) { - return std::shared_ptr(new CreateIndexRequest(context, collection_name, index_type, json_params)); -} - -Status -CreateIndexRequest::OnExecute() { - try { - std::string hdr = "CreateIndexRequest(collection=" + collection_name_ + ")"; - TimeRecorderAuto rc(hdr); - - // step 1: check arguments - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - return status; - } - - // only process root collection, ignore partition collection - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeCollection(collection_schema); - fiu_do_on("CreateIndexRequest.OnExecute.not_has_collection", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - fiu_do_on("CreateIndexRequest.OnExecute.throw_std.exception", throw std::exception()); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - status = ValidationUtil::ValidateCollectionIndexType(index_type_); - if (!status.ok()) { - return status; - } - - status = ValidationUtil::ValidateIndexParams(json_params_, collection_schema, index_type_); - if (!status.ok()) { - return status; - } - - // step 2: binary and float vector support different index/metric type, need to adapt here - engine::meta::CollectionSchema collection_info; - collection_info.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeCollection(collection_info); - - int32_t adapter_index_type = index_type_; - if (engine::utils::IsBinaryMetricType(collection_info.metric_type_)) { // binary vector not allow - if (adapter_index_type == static_cast(engine::EngineType::FAISS_IDMAP)) { - adapter_index_type = static_cast(engine::EngineType::FAISS_BIN_IDMAP); - } else if (adapter_index_type == static_cast(engine::EngineType::FAISS_IVFFLAT)) { - adapter_index_type = static_cast(engine::EngineType::FAISS_BIN_IVFFLAT); - } else { - return Status(SERVER_INVALID_INDEX_TYPE, "Invalid index type for collection metric type"); - } - } - - rc.RecordSection("check validation"); - - // step 3: create index - engine::CollectionIndex index; - index.engine_type_ = adapter_index_type; - index.extra_params_ = json_params_; - status = DBWrapper::DB()->CreateIndex(context_, collection_name_, index); - fiu_do_on("CreateIndexRequest.OnExecute.create_index_fail", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - return status; - } - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/CreateIndexRequest.h b/core/src/server/delivery/request/CreateIndexRequest.h deleted file mode 100644 index 070031f17e..0000000000 --- a/core/src/server/delivery/request/CreateIndexRequest.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include "server/delivery/request/BaseRequest.h" - -namespace milvus { -namespace server { - -class CreateIndexRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - int64_t index_type, const milvus::json& json_params); - - protected: - CreateIndexRequest(const std::shared_ptr& context, const std::string& collection_name, - int64_t index_type, const milvus::json& json_params); - - Status - OnExecute() override; - - private: - const std::string collection_name_; - const int64_t index_type_; - milvus::json json_params_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/CreatePartitionRequest.cpp b/core/src/server/delivery/request/CreatePartitionRequest.cpp deleted file mode 100644 index 0b0687063c..0000000000 --- a/core/src/server/delivery/request/CreatePartitionRequest.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/CreatePartitionRequest.h" -#include "server/DBWrapper.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include -#include -#include - -namespace milvus { -namespace server { - -constexpr uint64_t MAX_PARTITION_LIMIT = 4096; - -CreatePartitionRequest::CreatePartitionRequest(const std::shared_ptr& context, - const std::string& collection_name, const std::string& tag) - : BaseRequest(context, BaseRequest::kCreatePartition), collection_name_(collection_name), tag_(tag) { -} - -BaseRequestPtr -CreatePartitionRequest::Create(const std::shared_ptr& context, - const std::string& collection_name, const std::string& tag) { - return std::shared_ptr(new CreatePartitionRequest(context, collection_name, tag)); -} - -Status -CreatePartitionRequest::OnExecute() { - std::string hdr = "CreatePartitionRequest(collection=" + collection_name_ + ", partition_tag=" + tag_ + ")"; - TimeRecorderAuto rc(hdr); - - try { - // step 1: check arguments - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - fiu_do_on("CreatePartitionRequest.OnExecute.invalid_collection_name", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - return status; - } - - if (tag_ == milvus::engine::DEFAULT_PARTITON_TAG) { - return Status(SERVER_INVALID_PARTITION_TAG, "'_default' is built-in partition tag"); - } - - status = ValidationUtil::ValidatePartitionTags({tag_}); - fiu_do_on("CreatePartitionRequest.OnExecute.invalid_partition_name", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - return status; - } - - // only process root collection, ignore partition collection - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeCollection(collection_schema); - fiu_do_on("CreatePartitionRequest.OnExecute.invalid_partition_tags", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - // check partition total count - std::vector schema_array; - status = DBWrapper::DB()->ShowPartitions(collection_name_, schema_array); - if (schema_array.size() >= MAX_PARTITION_LIMIT) { - return Status(SERVER_UNSUPPORTED_ERROR, "The number of partitions exceeds the upper limit(4096)"); - } - - rc.RecordSection("check validation"); - - // step 2: create partition - status = DBWrapper::DB()->CreatePartition(collection_name_, "", tag_); - fiu_do_on("CreatePartitionRequest.OnExecute.db_already_exist", status = Status(milvus::DB_ALREADY_EXIST, "")); - fiu_do_on("CreatePartitionRequest.OnExecute.create_partition_fail", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - fiu_do_on("CreatePartitionRequest.OnExecute.throw_std_exception", throw std::exception()); - if (!status.ok()) { - // partition could exist - if (status.code() == DB_ALREADY_EXIST) { - return Status(SERVER_INVALID_COLLECTION_NAME, status.message()); - } - return status; - } - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/CreatePartitionRequest.h b/core/src/server/delivery/request/CreatePartitionRequest.h deleted file mode 100644 index c1809ae416..0000000000 --- a/core/src/server/delivery/request/CreatePartitionRequest.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include "server/delivery/request/BaseRequest.h" - -namespace milvus { -namespace server { - -class CreatePartitionRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& partition_name, - const std::string& tag); - - protected: - CreatePartitionRequest(const std::shared_ptr& context, const std::string& collection_name, - const std::string& tag); - - Status - OnExecute() override; - - private: - const std::string collection_name_; - const std::string tag_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/DeleteByIDRequest.cpp b/core/src/server/delivery/request/DeleteByIDRequest.cpp deleted file mode 100644 index 5d4603d876..0000000000 --- a/core/src/server/delivery/request/DeleteByIDRequest.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// 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 limitations -// under the License. - -#include "server/delivery/request/DeleteByIDRequest.h" - -#include -#include -#include - -#include "server/DBWrapper.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -namespace milvus { -namespace server { - -DeleteByIDRequest::DeleteByIDRequest(const std::shared_ptr& context, - const std::string& collection_name, const std::vector& vector_ids) - : BaseRequest(context, BaseRequest::kDeleteByID), collection_name_(collection_name), vector_ids_(vector_ids) { -} - -BaseRequestPtr -DeleteByIDRequest::Create(const std::shared_ptr& context, const std::string& collection_name, - const std::vector& vector_ids) { - return std::shared_ptr(new DeleteByIDRequest(context, collection_name, vector_ids)); -} - -Status -DeleteByIDRequest::OnExecute() { - try { - TimeRecorderAuto rc("DeleteByIDRequest"); - - // step 1: check arguments - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - return status; - } - - // step 2: check collection existence - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeCollection(collection_schema); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - // Check collection's index type supports delete - if (collection_schema.engine_type_ == (int32_t)engine::EngineType::SPTAG_BKT || - collection_schema.engine_type_ == (int32_t)engine::EngineType::SPTAG_KDT) { - std::string err_msg = - "Index type " + std::to_string(collection_schema.engine_type_) + " does not support delete operation"; - LOG_SERVER_ERROR_ << err_msg; - return Status(SERVER_UNSUPPORTED_ERROR, err_msg); - } - - rc.RecordSection("check validation"); - - status = DBWrapper::DB()->DeleteVectors(collection_name_, vector_ids_); - if (!status.ok()) { - return status; - } - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/DeleteByIDRequest.h b/core/src/server/delivery/request/DeleteByIDRequest.h deleted file mode 100644 index 2ebadd5f56..0000000000 --- a/core/src/server/delivery/request/DeleteByIDRequest.h +++ /dev/null @@ -1,48 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include -#include - -#include "server/delivery/request/BaseRequest.h" - -namespace milvus { -namespace server { - -class DeleteByIDRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - const std::vector& vector_ids); - - protected: - DeleteByIDRequest(const std::shared_ptr& context, const std::string& collection_name, - const std::vector& vector_ids); - - Status - OnExecute() override; - - private: - const std::string collection_name_; - const std::vector& vector_ids_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/DescribeCollectionRequest.cpp b/core/src/server/delivery/request/DescribeCollectionRequest.cpp deleted file mode 100644 index 0a29a3bcd1..0000000000 --- a/core/src/server/delivery/request/DescribeCollectionRequest.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/DescribeCollectionRequest.h" -#include "server/DBWrapper.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include - -namespace milvus { -namespace server { - -DescribeCollectionRequest::DescribeCollectionRequest(const std::shared_ptr& context, - const std::string& collection_name, CollectionSchema& schema) - : BaseRequest(context, BaseRequest::kDescribeCollection), collection_name_(collection_name), schema_(schema) { -} - -BaseRequestPtr -DescribeCollectionRequest::Create(const std::shared_ptr& context, - const std::string& collection_name, CollectionSchema& schema) { - return std::shared_ptr(new DescribeCollectionRequest(context, collection_name, schema)); -} - -Status -DescribeCollectionRequest::OnExecute() { - std::string hdr = "DescribeCollectionRequest(collection=" + collection_name_ + ")"; - TimeRecorderAuto rc(hdr); - - try { - // step 1: check arguments - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - return status; - } - - // step 2: get collection info - // only process root collection, ignore partition collection - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeCollection(collection_schema); - fiu_do_on("DescribeCollectionRequest.OnExecute.describe_collection_fail", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - fiu_do_on("DescribeCollectionRequest.OnExecute.throw_std_exception", throw std::exception()); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - schema_.collection_name_ = collection_schema.collection_id_; - schema_.dimension_ = static_cast(collection_schema.dimension_); - schema_.index_file_size_ = collection_schema.index_file_size_; - schema_.metric_type_ = collection_schema.metric_type_; - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/DescribeCollectionRequest.h b/core/src/server/delivery/request/DescribeCollectionRequest.h deleted file mode 100644 index d763989690..0000000000 --- a/core/src/server/delivery/request/DescribeCollectionRequest.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -#include "server/delivery/request/BaseRequest.h" - -namespace milvus { -namespace server { - -class DescribeCollectionRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - CollectionSchema& schema); - - protected: - DescribeCollectionRequest(const std::shared_ptr& context, - const std::string& collection_name, CollectionSchema& schema); - - Status - OnExecute() override; - - private: - const std::string collection_name_; - CollectionSchema& schema_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/DescribeIndexRequest.cpp b/core/src/server/delivery/request/DescribeIndexRequest.cpp deleted file mode 100644 index 5949d1106c..0000000000 --- a/core/src/server/delivery/request/DescribeIndexRequest.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/DescribeIndexRequest.h" -#include "server/DBWrapper.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include - -namespace milvus { -namespace server { - -DescribeIndexRequest::DescribeIndexRequest(const std::shared_ptr& context, - const std::string& collection_name, IndexParam& index_param) - : BaseRequest(context, BaseRequest::kDescribeIndex), collection_name_(collection_name), index_param_(index_param) { -} - -BaseRequestPtr -DescribeIndexRequest::Create(const std::shared_ptr& context, - const std::string& collection_name, IndexParam& index_param) { - return std::shared_ptr(new DescribeIndexRequest(context, collection_name, index_param)); -} - -Status -DescribeIndexRequest::OnExecute() { - try { - fiu_do_on("DescribeIndexRequest.OnExecute.throw_std_exception", throw std::exception()); - std::string hdr = "DescribeIndexRequest(collection=" + collection_name_ + ")"; - TimeRecorderAuto rc(hdr); - - // step 1: check arguments - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - return status; - } - - // only process root collection, ignore partition collection - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeCollection(collection_schema); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - // step 2: check collection existence - engine::CollectionIndex index; - status = DBWrapper::DB()->DescribeIndex(collection_name_, index); - if (!status.ok()) { - return status; - } - - // for binary vector, IDMAP and IVFLAT will be treated as BIN_IDMAP and BIN_IVFLAT internally - // return IDMAP and IVFLAT for outside caller - if (index.engine_type_ == (int32_t)engine::EngineType::FAISS_BIN_IDMAP) { - index.engine_type_ = (int32_t)engine::EngineType::FAISS_IDMAP; - } else if (index.engine_type_ == (int32_t)engine::EngineType::FAISS_BIN_IVFFLAT) { - index.engine_type_ = (int32_t)engine::EngineType::FAISS_IVFFLAT; - } - - index_param_.collection_name_ = collection_name_; - index_param_.index_type_ = index.engine_type_; - index_param_.extra_params_ = index.extra_params_.dump(); - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/DescribeIndexRequest.h b/core/src/server/delivery/request/DescribeIndexRequest.h deleted file mode 100644 index db0eb47935..0000000000 --- a/core/src/server/delivery/request/DescribeIndexRequest.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -#include "server/delivery/request/BaseRequest.h" - -namespace milvus { -namespace server { - -class DescribeIndexRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - IndexParam& index_param); - - protected: - DescribeIndexRequest(const std::shared_ptr& context, const std::string& collection_name, - IndexParam& index_param); - - Status - OnExecute() override; - - private: - const std::string collection_name_; - IndexParam& index_param_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/DropCollectionRequest.cpp b/core/src/server/delivery/request/DropCollectionRequest.cpp deleted file mode 100644 index 52de5596aa..0000000000 --- a/core/src/server/delivery/request/DropCollectionRequest.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/DropCollectionRequest.h" -#include "server/DBWrapper.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include -#include - -namespace milvus { -namespace server { - -DropCollectionRequest::DropCollectionRequest(const std::shared_ptr& context, - const std::string& collection_name) - : BaseRequest(context, BaseRequest::kDropCollection), collection_name_(collection_name) { -} - -BaseRequestPtr -DropCollectionRequest::Create(const std::shared_ptr& context, - const std::string& collection_name) { - return std::shared_ptr(new DropCollectionRequest(context, collection_name)); -} - -Status -DropCollectionRequest::OnExecute() { - try { - std::string hdr = "DropCollectionRequest(collection=" + collection_name_ + ")"; - TimeRecorder rc(hdr); - - // step 1: check arguments - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - return status; - } - - // step 2: check collection existence - // only process root collection, ignore partition collection - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeCollection(collection_schema); - fiu_do_on("DropCollectionRequest.OnExecute.db_not_found", status = Status(milvus::DB_NOT_FOUND, "")); - fiu_do_on("DropCollectionRequest.OnExecute.describe_collection_fail", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - fiu_do_on("DropCollectionRequest.OnExecute.throw_std_exception", throw std::exception()); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - rc.RecordSection("check validation"); - - // step 3: Drop collection - status = DBWrapper::DB()->DropCollection(collection_name_); - fiu_do_on("DropCollectionRequest.OnExecute.drop_collection_fail", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - return status; - } - - // step 4: flush to trigger CleanUpFilesWithTTL - status = DBWrapper::DB()->Flush(); - - rc.ElapseFromBegin("total cost"); - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/DropCollectionRequest.h b/core/src/server/delivery/request/DropCollectionRequest.h deleted file mode 100644 index 15a607c153..0000000000 --- a/core/src/server/delivery/request/DropCollectionRequest.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/delivery/request/BaseRequest.h" - -#include -#include - -namespace milvus { -namespace server { - -class DropCollectionRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name); - - protected: - DropCollectionRequest(const std::shared_ptr& context, const std::string& collection_name); - - Status - OnExecute() override; - - private: - std::string collection_name_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/DropIndexRequest.cpp b/core/src/server/delivery/request/DropIndexRequest.cpp deleted file mode 100644 index 1a0406ef73..0000000000 --- a/core/src/server/delivery/request/DropIndexRequest.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/DropIndexRequest.h" -#include "server/DBWrapper.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include - -namespace milvus { -namespace server { - -DropIndexRequest::DropIndexRequest(const std::shared_ptr& context, - const std::string& collection_name) - : BaseRequest(context, BaseRequest::kDropIndex), collection_name_(collection_name) { -} - -BaseRequestPtr -DropIndexRequest::Create(const std::shared_ptr& context, const std::string& collection_name) { - return std::shared_ptr(new DropIndexRequest(context, collection_name)); -} - -Status -DropIndexRequest::OnExecute() { - try { - fiu_do_on("DropIndexRequest.OnExecute.throw_std_exception", throw std::exception()); - std::string hdr = "DropIndexRequest(collection=" + collection_name_ + ")"; - TimeRecorderAuto rc(hdr); - - // step 1: check arguments - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - return status; - } - - // only process root collection, ignore partition collection - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeCollection(collection_schema); - fiu_do_on("DropIndexRequest.OnExecute.collection_not_exist", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - rc.RecordSection("check validation"); - - // step 2: drop index - status = DBWrapper::DB()->DropIndex(collection_name_); - fiu_do_on("DropIndexRequest.OnExecute.drop_index_fail", status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - return status; - } - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/DropIndexRequest.h b/core/src/server/delivery/request/DropIndexRequest.h deleted file mode 100644 index f604378667..0000000000 --- a/core/src/server/delivery/request/DropIndexRequest.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/delivery/request/BaseRequest.h" - -#include -#include - -namespace milvus { -namespace server { - -class DropIndexRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name); - - protected: - DropIndexRequest(const std::shared_ptr& context, const std::string& collection_name); - - Status - OnExecute() override; - - private: - const std::string collection_name_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/DropPartitionRequest.cpp b/core/src/server/delivery/request/DropPartitionRequest.cpp deleted file mode 100644 index 1eda9bd385..0000000000 --- a/core/src/server/delivery/request/DropPartitionRequest.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/DropPartitionRequest.h" -#include "server/DBWrapper.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include -#include - -namespace milvus { -namespace server { - -DropPartitionRequest::DropPartitionRequest(const std::shared_ptr& context, - const std::string& collection_name, const std::string& tag) - : BaseRequest(context, BaseRequest::kDropPartition), collection_name_(collection_name), tag_(tag) { -} - -BaseRequestPtr -DropPartitionRequest::Create(const std::shared_ptr& context, - const std::string& collection_name, const std::string& tag) { - return std::shared_ptr(new DropPartitionRequest(context, collection_name, tag)); -} - -Status -DropPartitionRequest::OnExecute() { - std::string hdr = "DropPartitionRequest(collection=" + collection_name_ + ", partition_tag=" + tag_ + ")"; - TimeRecorderAuto rc(hdr); - - std::string collection_name = collection_name_; - std::string partition_tag = tag_; - - // step 1: check collection name - auto status = ValidationUtil::ValidateCollectionName(collection_name); - fiu_do_on("DropPartitionRequest.OnExecute.invalid_collection_name", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - return status; - } - - // step 2: check partition tag - if (partition_tag == milvus::engine::DEFAULT_PARTITON_TAG) { - std::string msg = "Default partition cannot be dropped."; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_COLLECTION_NAME, msg); - } - - status = ValidationUtil::ValidatePartitionTags({partition_tag}); - if (!status.ok()) { - return status; - } - - // step 3: check collection - // only process root collection, ignore partition collection - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeCollection(collection_schema); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - rc.RecordSection("check validation"); - - // step 4: drop partition - return DBWrapper::DB()->DropPartitionByTag(collection_name, partition_tag); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/DropPartitionRequest.h b/core/src/server/delivery/request/DropPartitionRequest.h deleted file mode 100644 index 0e74fa208d..0000000000 --- a/core/src/server/delivery/request/DropPartitionRequest.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include "server/delivery/request/BaseRequest.h" - -namespace milvus { -namespace server { - -class DropPartitionRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - const std::string& tag); - - protected: - DropPartitionRequest(const std::shared_ptr& context, const std::string& collection_name, - const std::string& tag); - - Status - OnExecute() override; - - private: - const std::string collection_name_; - const std::string tag_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/FlushRequest.cpp b/core/src/server/delivery/request/FlushRequest.cpp deleted file mode 100644 index f8bec5effa..0000000000 --- a/core/src/server/delivery/request/FlushRequest.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// 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 limitations -// under the License. - -#include "server/delivery/request/FlushRequest.h" - -#include - -#include "server/DBWrapper.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" - -namespace milvus { -namespace server { - -FlushRequest::FlushRequest(const std::shared_ptr& context, - const std::vector& collection_names) - : BaseRequest(context, BaseRequest::kFlush), collection_names_(collection_names) { -} - -BaseRequestPtr -FlushRequest::Create(const std::shared_ptr& context, - const std::vector& collection_names) { - return std::shared_ptr(new FlushRequest(context, collection_names)); -} - -Status -FlushRequest::OnExecute() { - std::string hdr = "FlushRequest flush collections: "; - for (auto& name : collection_names_) { - hdr += name; - hdr += ", "; - } - - TimeRecorderAuto rc(hdr); - Status status = Status::OK(); - LOG_SERVER_DEBUG_ << hdr; - - // flush all collections - if (collection_names_.empty()) { - status = DBWrapper::DB()->Flush(); - return status; - } - - // flush specified collections - for (auto& name : collection_names_) { - // only process root collection, ignore partition collection - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = name; - status = DBWrapper::DB()->DescribeCollection(collection_schema); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(name)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(name)); - } - } - - status = DBWrapper::DB()->Flush(name); - if (!status.ok()) { - return status; - } - } - - return status; -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/FlushRequest.h b/core/src/server/delivery/request/FlushRequest.h deleted file mode 100644 index 7e865a98ca..0000000000 --- a/core/src/server/delivery/request/FlushRequest.h +++ /dev/null @@ -1,46 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include "BaseRequest.h" - -#include -#include -#include - -namespace milvus { -namespace server { - -class FlushRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::vector& collection_names); - - protected: - FlushRequest(const std::shared_ptr& context, - const std::vector& collection_names); - - Status - OnExecute() override; - - private: - std::vector collection_names_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/GetVectorIDsRequest.cpp b/core/src/server/delivery/request/GetVectorIDsRequest.cpp deleted file mode 100644 index 298ca4d7be..0000000000 --- a/core/src/server/delivery/request/GetVectorIDsRequest.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// 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 limitations -// under the License. - -#include "server/delivery/request/GetVectorIDsRequest.h" -#include "server/DBWrapper.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include - -namespace milvus { -namespace server { - -GetVectorIDsRequest::GetVectorIDsRequest(const std::shared_ptr& context, - const std::string& collection_name, const std::string& segment_name, - std::vector& vector_ids) - : BaseRequest(context, BaseRequest::kGetVectorIDs), - collection_name_(collection_name), - segment_name_(segment_name), - vector_ids_(vector_ids) { -} - -BaseRequestPtr -GetVectorIDsRequest::Create(const std::shared_ptr& context, const std::string& collection_name, - const std::string& segment_name, std::vector& vector_ids) { - return std::shared_ptr(new GetVectorIDsRequest(context, collection_name, segment_name, vector_ids)); -} - -Status -GetVectorIDsRequest::OnExecute() { - try { - std::string hdr = "GetVectorIDsRequest(collection=" + collection_name_ + " segment=" + segment_name_ + ")"; - TimeRecorderAuto rc(hdr); - - // step 1: check arguments - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - return status; - } - - // only process root collection, ignore partition collection - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeCollection(collection_schema); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - // step 2: get vector data, now only support get one id - vector_ids_.clear(); - return DBWrapper::DB()->GetVectorIDs(collection_name_, segment_name_, vector_ids_); - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/GetVectorIDsRequest.h b/core/src/server/delivery/request/GetVectorIDsRequest.h deleted file mode 100644 index 3a87587d06..0000000000 --- a/core/src/server/delivery/request/GetVectorIDsRequest.h +++ /dev/null @@ -1,49 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include "server/delivery/request/BaseRequest.h" - -#include -#include -#include - -namespace milvus { -namespace server { - -class GetVectorIDsRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - const std::string& segment_name, std::vector& vector_ids); - - protected: - GetVectorIDsRequest(const std::shared_ptr& context, const std::string& collection_name, - const std::string& segment_name, std::vector& vector_ids); - - Status - OnExecute() override; - - private: - std::string collection_name_; - std::string segment_name_; - std::vector& vector_ids_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/GetVectorsByIDRequest.cpp b/core/src/server/delivery/request/GetVectorsByIDRequest.cpp deleted file mode 100644 index f78441a849..0000000000 --- a/core/src/server/delivery/request/GetVectorsByIDRequest.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// 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 limitations -// under the License. - -#include "server/delivery/request/GetVectorsByIDRequest.h" -#include "server/DBWrapper.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include - -namespace milvus { -namespace server { - -constexpr uint64_t MAX_COUNT_RETURNED = 1000; - -GetVectorsByIDRequest::GetVectorsByIDRequest(const std::shared_ptr& context, - const std::string& collection_name, const std::vector& ids, - std::vector& vectors) - : BaseRequest(context, BaseRequest::kGetVectorByID), - collection_name_(collection_name), - ids_(ids), - vectors_(vectors) { -} - -BaseRequestPtr -GetVectorsByIDRequest::Create(const std::shared_ptr& context, - const std::string& collection_name, const std::vector& ids, - std::vector& vectors) { - return std::shared_ptr(new GetVectorsByIDRequest(context, collection_name, ids, vectors)); -} - -Status -GetVectorsByIDRequest::OnExecute() { - try { - std::string hdr = "GetVectorsByIDRequest(collection=" + collection_name_ + ")"; - TimeRecorderAuto rc(hdr); - - // step 1: check arguments - if (ids_.empty()) { - return Status(SERVER_INVALID_ARGUMENT, "No vector id specified"); - } - - if (ids_.size() > MAX_COUNT_RETURNED) { - std::string msg = "Input id array size cannot exceed: " + std::to_string(MAX_COUNT_RETURNED); - return Status(SERVER_INVALID_ARGUMENT, msg); - } - - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - return status; - } - - // only process root collection, ignore partition collection - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeCollection(collection_schema); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - // step 2: get vector data, now only support get one id - return DBWrapper::DB()->GetVectorsByID(collection_schema, ids_, vectors_); - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/GetVectorsByIDRequest.h b/core/src/server/delivery/request/GetVectorsByIDRequest.h deleted file mode 100644 index 3b805ef4de..0000000000 --- a/core/src/server/delivery/request/GetVectorsByIDRequest.h +++ /dev/null @@ -1,49 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include "server/delivery/request/BaseRequest.h" - -#include -#include -#include - -namespace milvus { -namespace server { - -class GetVectorsByIDRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - const std::vector& ids, std::vector& vectors); - - protected: - GetVectorsByIDRequest(const std::shared_ptr& context, const std::string& collection_name, - const std::vector& ids, std::vector& vectors); - - Status - OnExecute() override; - - private: - std::string collection_name_; - std::vector ids_; - std::vector& vectors_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/HasCollectionRequest.cpp b/core/src/server/delivery/request/HasCollectionRequest.cpp deleted file mode 100644 index 10f4c7d5d2..0000000000 --- a/core/src/server/delivery/request/HasCollectionRequest.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/HasCollectionRequest.h" -#include "server/DBWrapper.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include - -namespace milvus { -namespace server { - -HasCollectionRequest::HasCollectionRequest(const std::shared_ptr& context, - const std::string& collection_name, bool& has_collection) - : BaseRequest(context, BaseRequest::kHasCollection), - collection_name_(collection_name), - has_collection_(has_collection) { -} - -BaseRequestPtr -HasCollectionRequest::Create(const std::shared_ptr& context, - const std::string& collection_name, bool& has_collection) { - return std::shared_ptr(new HasCollectionRequest(context, collection_name, has_collection)); -} - -Status -HasCollectionRequest::OnExecute() { - try { - std::string hdr = "HasCollectionRequest(collection=" + collection_name_ + ")"; - TimeRecorderAuto rc(hdr); - - // step 1: check arguments - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - return status; - } - - // step 2: check collection existence - status = DBWrapper::DB()->HasNativeCollection(collection_name_, has_collection_); - fiu_do_on("HasCollectionRequest.OnExecute.throw_std_exception", throw std::exception()); - - return status; - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/HasCollectionRequest.h b/core/src/server/delivery/request/HasCollectionRequest.h deleted file mode 100644 index b800dfe7bf..0000000000 --- a/core/src/server/delivery/request/HasCollectionRequest.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/delivery/request/BaseRequest.h" - -#include -#include - -namespace milvus { -namespace server { - -class HasCollectionRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - bool& has_collection); - - protected: - HasCollectionRequest(const std::shared_ptr& context, const std::string& collection_name, - bool& has_collection); - - Status - OnExecute() override; - - private: - std::string collection_name_; - bool& has_collection_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/HasPartitionRequest.cpp b/core/src/server/delivery/request/HasPartitionRequest.cpp deleted file mode 100644 index bfba1b5fd1..0000000000 --- a/core/src/server/delivery/request/HasPartitionRequest.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/HasPartitionRequest.h" -#include "server/DBWrapper.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include -#include - -namespace milvus { -namespace server { - -HasPartitionRequest::HasPartitionRequest(const std::shared_ptr& context, - const std::string& collection_name, const std::string& tag, - bool& has_partition) - : BaseRequest(context, BaseRequest::kHasCollection), - collection_name_(collection_name), - partition_tag_(tag), - has_partition_(has_partition) { -} - -BaseRequestPtr -HasPartitionRequest::Create(const std::shared_ptr& context, const std::string& collection_name, - const std::string& tag, bool& has_partition) { - return std::shared_ptr(new HasPartitionRequest(context, collection_name, tag, has_partition)); -} - -Status -HasPartitionRequest::OnExecute() { - try { - std::string hdr = "HasPartitionRequest(collection=" + collection_name_ + " tag=" + partition_tag_ + ")"; - TimeRecorderAuto rc(hdr); - - has_partition_ = false; - - // step 1: check arguments - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - return status; - } - - // only process root collection, ignore partition collection - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeCollection(collection_schema); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - status = DBWrapper::DB()->HasPartition(collection_name_, partition_tag_, has_partition_); - if (!status.ok()) { - return status; - } - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/HasPartitionRequest.h b/core/src/server/delivery/request/HasPartitionRequest.h deleted file mode 100644 index 7e1e1412e6..0000000000 --- a/core/src/server/delivery/request/HasPartitionRequest.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/delivery/request/BaseRequest.h" - -#include -#include - -namespace milvus { -namespace server { - -class HasPartitionRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - const std::string& tag, bool& has_partition); - - protected: - HasPartitionRequest(const std::shared_ptr& context, const std::string& collection_name, - const std::string& tag, bool& has_partition); - - Status - OnExecute() override; - - private: - std::string collection_name_; - std::string partition_tag_; - bool& has_partition_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/InsertRequest.cpp b/core/src/server/delivery/request/InsertRequest.cpp deleted file mode 100644 index 08cab94172..0000000000 --- a/core/src/server/delivery/request/InsertRequest.cpp +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/InsertRequest.h" -#include "db/Utils.h" -#include "server/DBWrapper.h" -#include "utils/CommonUtil.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include -#include -#include - -#ifdef ENABLE_CPU_PROFILING -#include -#endif - -namespace milvus { -namespace server { - -InsertRequest::InsertRequest(const std::shared_ptr& context, - const std::string& collection_name, engine::VectorsData& vectors, - const std::string& partition_tag) - : BaseRequest(context, BaseRequest::kInsert), - collection_name_(collection_name), - vectors_data_(vectors), - partition_tag_(partition_tag) { -} - -BaseRequestPtr -InsertRequest::Create(const std::shared_ptr& context, const std::string& collection_name, - engine::VectorsData& vectors, const std::string& partition_tag) { - return std::shared_ptr(new InsertRequest(context, collection_name, vectors, partition_tag)); -} - -Status -InsertRequest::OnExecute() { - LOG_SERVER_INFO_ << LogOut("[%s][%ld] ", "insert", 0) << "Execute insert request."; - try { - int64_t vector_count = vectors_data_.vector_count_; - fiu_do_on("InsertRequest.OnExecute.throw_std_exception", throw std::exception()); - std::string hdr = "InsertRequest(collection=" + collection_name_ + ", n=" + std::to_string(vector_count) + - ", partition_tag=" + partition_tag_ + ")"; - TimeRecorder rc(LogOut("[%s][%ld] %s", "insert", 0, hdr.c_str())); - - // step 1: check arguments - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] Invalid collection name: %s", "insert", 0, status.message().c_str()); - return status; - } - if (vectors_data_.float_data_.empty() && vectors_data_.binary_data_.empty()) { - std::string msg = "The vector array is empty. Make sure you have entered vector records."; - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] Invalid records: %s", "insert", 0, msg.c_str()); - return Status(SERVER_INVALID_ROWRECORD_ARRAY, msg); - } - - fiu_do_on("InsertRequest.OnExecute.id_array_error", vectors_data_.id_array_.resize(vector_count + 1)); - if (!vectors_data_.id_array_.empty()) { - if (vectors_data_.id_array_.size() != (size_t)vector_count) { - std::string msg = "The size of vector ID array must be equal to the size of the vector."; - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] Invalid id array: %s", "insert", 0, msg.c_str()); - return Status(SERVER_ILLEGAL_VECTOR_ID, msg); - } - } - - // step 2: check collection existence - // only process root collection, ignore partition collection - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeCollection(collection_schema); - fiu_do_on("InsertRequest.OnExecute.db_not_found", status = Status(milvus::DB_NOT_FOUND, "")); - fiu_do_on("InsertRequest.OnExecute.describe_collection_fail", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] Collection %s not found", "insert", 0, collection_name_.c_str()); - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] Describe collection %s fail: %s", "insert", 0, - collection_name_.c_str(), status.message().c_str()); - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] owner collection of %s is empty", "insert", 0, - collection_name_.c_str()); - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - // step 3: check collection flag - // all user provide id, or all internal id - bool user_provide_ids = !vectors_data_.id_array_.empty(); - fiu_do_on("InsertRequest.OnExecute.illegal_vector_id", user_provide_ids = false; - collection_schema.flag_ = engine::meta::FLAG_MASK_HAS_USERID); - // user already provided id before, all insert action require user id - if ((collection_schema.flag_ & engine::meta::FLAG_MASK_HAS_USERID) != 0 && !user_provide_ids) { - std::string msg = "Entities IDs are user-defined. Please provide IDs for all entities of the collection."; - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] %s", "insert", 0, msg.c_str()); - return Status(SERVER_ILLEGAL_VECTOR_ID, msg); - } - - fiu_do_on("InsertRequest.OnExecute.illegal_vector_id2", user_provide_ids = true; - collection_schema.flag_ = engine::meta::FLAG_MASK_NO_USERID); - // user didn't provided id before, no need to provide user id - if ((collection_schema.flag_ & engine::meta::FLAG_MASK_NO_USERID) != 0 && user_provide_ids) { - std::string msg = - "Entities IDs are auto-generated. All vectors of this collection must use auto-generated IDs."; - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] %s", "insert", 0, msg.c_str()); - return Status(SERVER_ILLEGAL_VECTOR_ID, msg); - } - - rc.RecordSection("check validation"); - -#ifdef ENABLE_CPU_PROFILING - std::string fname = "/tmp/insert_" + CommonUtil::GetCurrentTimeStr() + ".profiling"; - ProfilerStart(fname.c_str()); -#endif - // step 4: some metric type doesn't support float vectors - status = ValidationUtil::ValidateVectorData(vectors_data_, collection_schema); - if (!status.ok()) { - LOG_SERVER_ERROR_ << LogOut("[%s][%d] Invalid vector data: %s", "insert", 0, status.message().c_str()); - return status; - } - - // step 5: check insert data limitation - status = ValidationUtil::ValidateVectorDataSize(vectors_data_, collection_schema); - if (!status.ok()) { - LOG_SERVER_ERROR_ << LogOut("[%s][%d] Invalid vector data: %s", "insert", 0, status.message().c_str()); - return status; - } - - // step 6: insert vectors - auto vec_count = static_cast(vector_count); - - rc.RecordSection("prepare vectors data"); - status = DBWrapper::DB()->InsertVectors(collection_name_, partition_tag_, vectors_data_); - fiu_do_on("InsertRequest.OnExecute.insert_fail", status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] Insert fail: %s", "insert", 0, status.message().c_str()); - return status; - } - - auto ids_size = vectors_data_.id_array_.size(); - fiu_do_on("InsertRequest.OnExecute.invalid_ids_size", ids_size = vec_count - 1); - if (ids_size != vec_count) { - std::string msg = - "Add " + std::to_string(vec_count) + " vectors but only return " + std::to_string(ids_size) + " id"; - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] Insert fail: %s", "insert", 0, msg.c_str()); - return Status(SERVER_ILLEGAL_VECTOR_ID, msg); - } - - // step 7: update collection flag - user_provide_ids ? collection_schema.flag_ |= engine::meta::FLAG_MASK_HAS_USERID - : collection_schema.flag_ |= engine::meta::FLAG_MASK_NO_USERID; - status = DBWrapper::DB()->UpdateCollectionFlag(collection_name_, collection_schema.flag_); - -#ifdef ENABLE_CPU_PROFILING - ProfilerStop(); -#endif - - rc.RecordSection("add vectors to engine"); - rc.ElapseFromBegin("total cost"); - } catch (std::exception& ex) { - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] Encounter exception: %s", "insert", 0, ex.what()); - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/InsertRequest.h b/core/src/server/delivery/request/InsertRequest.h deleted file mode 100644 index 2110e9f1db..0000000000 --- a/core/src/server/delivery/request/InsertRequest.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/delivery/request/BaseRequest.h" - -#include -#include -#include - -namespace milvus { -namespace server { - -class InsertRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - engine::VectorsData& vectors, const std::string& partition_tag); - - protected: - InsertRequest(const std::shared_ptr& context, const std::string& collection_name, - engine::VectorsData& vectors, const std::string& partition_tag); - - Status - OnExecute() override; - - private: - const std::string collection_name_; - engine::VectorsData& vectors_data_; - const std::string partition_tag_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/PreloadCollectionRequest.cpp b/core/src/server/delivery/request/PreloadCollectionRequest.cpp deleted file mode 100644 index f015d5d6ec..0000000000 --- a/core/src/server/delivery/request/PreloadCollectionRequest.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/PreloadCollectionRequest.h" -#include "server/DBWrapper.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include - -namespace milvus { -namespace server { - -PreloadCollectionRequest::PreloadCollectionRequest(const std::shared_ptr& context, - const std::string& collection_name) - : BaseRequest(context, BaseRequest::kPreloadCollection), collection_name_(collection_name) { -} - -BaseRequestPtr -PreloadCollectionRequest::Create(const std::shared_ptr& context, - const std::string& collection_name) { - return std::shared_ptr(new PreloadCollectionRequest(context, collection_name)); -} - -Status -PreloadCollectionRequest::OnExecute() { - try { - std::string hdr = "PreloadCollectionRequest(collection=" + collection_name_ + ")"; - TimeRecorderAuto rc(hdr); - - // step 1: check arguments - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - return status; - } - - // only process root collection, ignore partition collection - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeCollection(collection_schema); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - // step 2: force load collection data into cache - // load each segment and insert into cache even cache capacity is not enough - status = DBWrapper::DB()->PreloadCollection(context_, collection_name_, true); - fiu_do_on("PreloadCollectionRequest.OnExecute.preload_collection_fail", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - fiu_do_on("PreloadCollectionRequest.OnExecute.throw_std_exception", throw std::exception()); - if (!status.ok()) { - return status; - } - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/PreloadCollectionRequest.h b/core/src/server/delivery/request/PreloadCollectionRequest.h deleted file mode 100644 index aa41964efd..0000000000 --- a/core/src/server/delivery/request/PreloadCollectionRequest.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/delivery/request/BaseRequest.h" - -#include -#include - -namespace milvus { -namespace server { - -class PreloadCollectionRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name); - - protected: - PreloadCollectionRequest(const std::shared_ptr& context, - const std::string& collection_name); - - Status - OnExecute() override; - - private: - const std::string collection_name_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/ReLoadSegmentsRequest.cpp b/core/src/server/delivery/request/ReLoadSegmentsRequest.cpp deleted file mode 100644 index f871ef5b95..0000000000 --- a/core/src/server/delivery/request/ReLoadSegmentsRequest.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/ReLoadSegmentsRequest.h" - -#include - -#include "config/Config.h" -#include "server/DBWrapper.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -namespace milvus { -namespace server { - -ReLoadSegmentsRequest::ReLoadSegmentsRequest(const std::shared_ptr& context, - const std::string& collection_name, - const std::vector& segment_ids) - : BaseRequest(context, BaseRequest::kReloadSegments), collection_name_(collection_name), segment_ids_(segment_ids) { -} - -BaseRequestPtr -ReLoadSegmentsRequest::Create(const std::shared_ptr& context, - const std::string& collection_name, const std::vector& segment_ids) { - return std::shared_ptr(new ReLoadSegmentsRequest(context, collection_name, segment_ids)); -} - -Status -ReLoadSegmentsRequest::OnExecute() { - auto& config = Config::GetInstance(); - -#if 1 - bool cluster_enable = false; - std::string cluster_role; - STATUS_CHECK(config.GetClusterConfigEnable(cluster_enable)); - STATUS_CHECK(config.GetClusterConfigRole(cluster_role)); - - if ((not cluster_enable) || cluster_role == "rw") { - // TODO: No need to reload segment files - return Status(SERVER_SUCCESS, ""); - } -#else - std::string deploy_mode; - auto status = config.GetServerConfigDeployMode(deploy_mode); - if (!status.ok()) { - return status; - } - - fiu_do_on("ReLoadSegmentsRequest.OnExecute.readonly", deploy_mode = "cluster_readonly"); - if (deploy_mode == "single" || deploy_mode == "cluster_writable") { - // TODO: No need to reload segment files - return Status(SERVER_SUCCESS, ""); - } -#endif - - try { - std::string hdr = "ReloadSegmentsRequest(collection=" + collection_name_ + ")"; - TimeRecorderAuto rc(hdr); - - // step 1: check arguments - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - return status; - } - - std::vector segment_ids; - for (auto& id : segment_ids_) { - std::string::size_type sz; - segment_ids.push_back(std::stoul(id, &sz)); - } - - return DBWrapper::DB()->ReLoadSegmentsDeletedDocs(collection_name_, segment_ids); - } catch (std::exception& exp) { - return Status(SERVER_UNEXPECTED_ERROR, exp.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/ReLoadSegmentsRequest.h b/core/src/server/delivery/request/ReLoadSegmentsRequest.h deleted file mode 100644 index 77a18a2393..0000000000 --- a/core/src/server/delivery/request/ReLoadSegmentsRequest.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include - -#include "server/delivery/request/BaseRequest.h" - -namespace milvus { -namespace server { - -class ReLoadSegmentsRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - const std::vector& segment_ids); - - protected: - ReLoadSegmentsRequest(const std::shared_ptr& context, const std::string& collection_name, - const std::vector& segment_ids); - - Status - OnExecute() override; - - private: - const std::string collection_name_; - const std::vector segment_ids_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/SearchByIDRequest.cpp b/core/src/server/delivery/request/SearchByIDRequest.cpp deleted file mode 100644 index 471bfa77ed..0000000000 --- a/core/src/server/delivery/request/SearchByIDRequest.cpp +++ /dev/null @@ -1,152 +0,0 @@ -// 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 limitations -// under the License. - -#include "server/delivery/request/SearchByIDRequest.h" - -#include - -#include "config/Config.h" -#include "server/DBWrapper.h" -#include "utils/CommonUtil.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#ifdef ENABLE_CPU_PROFILING -#include -#endif - -namespace milvus { -namespace server { - -SearchByIDRequest::SearchByIDRequest(const std::shared_ptr& context, - const std::string& collection_name, const std::vector& id_array, - int64_t topk, const milvus::json& extra_params, - const std::vector& partition_list, TopKQueryResult& result) - : BaseRequest(context, BaseRequest::kSearchByID), - collection_name_(collection_name), - id_array_(id_array), - topk_(topk), - extra_params_(extra_params), - partition_list_(partition_list), - result_(result) { -} - -BaseRequestPtr -SearchByIDRequest::Create(const std::shared_ptr& context, const std::string& collection_name, - const std::vector& id_array, int64_t topk, const milvus::json& extra_params, - const std::vector& partition_list, TopKQueryResult& result) { - return std::shared_ptr( - new SearchByIDRequest(context, collection_name, id_array, topk, extra_params, partition_list, result)); -} - -Status -SearchByIDRequest::OnExecute() { - try { - milvus::server::ContextChild pre_tracer(context_, "Pre query"); - - std::string hdr = "SearchByIDRequest(collection=" + collection_name_ + ", k=" + std::to_string(topk_) + - ", extra_params=" + extra_params_.dump() + ")"; - - TimeRecorder rc(hdr); - - // step 1: check empty id array - if (id_array_.empty()) { - return Status(SERVER_INVALID_ARGUMENT, "No vector id specified"); - } - - // step 2: check collection name - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - return status; - } - - // step 3: check search topk - status = ValidationUtil::ValidateSearchTopk(topk_); - if (!status.ok()) { - return status; - } - - // step 4: check collection existence - // only process root collection, ignore partition collection - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeCollection(collection_schema); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - // step 5: check search parameters - status = ValidationUtil::ValidateSearchParams(extra_params_, collection_schema, topk_); - if (!status.ok()) { - return status; - } - - rc.RecordSection("check validation"); - - // step 6: search vectors - engine::ResultIds result_ids; - engine::ResultDistances result_distances; - -#ifdef ENABLE_CPU_PROFILING - std::string fname = "/tmp/search_by_id_" + CommonUtil::GetCurrentTimeStr() + ".profiling"; - ProfilerStart(fname.c_str()); -#endif - - pre_tracer.Finish(); - - status = DBWrapper::DB()->QueryByIDs(context_, collection_name_, partition_list_, (size_t)topk_, extra_params_, - id_array_, result_ids, result_distances); - -#ifdef ENABLE_CPU_PROFILING - ProfilerStop(); -#endif - - rc.RecordSection("search vectors from engine"); - if (!status.ok()) { - return status; - } - - if (result_ids.empty()) { - return Status::OK(); // empty collection - } - - // step 7: construct result array - milvus::server::ContextChild tracer(context_, "Constructing result"); - result_.row_num_ = id_array_.size(); - result_.distance_list_.swap(result_distances); - result_.id_list_.swap(result_ids); - - rc.RecordSection("construct result and send"); - rc.ElapseFromBegin("totally cost"); - } catch (std::exception& ex) { - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/SearchByIDRequest.h b/core/src/server/delivery/request/SearchByIDRequest.h deleted file mode 100644 index a83361c170..0000000000 --- a/core/src/server/delivery/request/SearchByIDRequest.h +++ /dev/null @@ -1,55 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include -#include - -#include "server/delivery/request/BaseRequest.h" - -namespace milvus { -namespace server { - -class SearchByIDRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - const std::vector& id_array, int64_t topk, const milvus::json& extra_params, - const std::vector& partition_list, TopKQueryResult& result); - - protected: - SearchByIDRequest(const std::shared_ptr& context, const std::string& collection_name, - const std::vector& id_array, int64_t topk, const milvus::json& extra_params, - const std::vector& partition_list, TopKQueryResult& result); - - Status - OnExecute() override; - - private: - const std::string collection_name_; - std::vector id_array_; - int64_t topk_; - milvus::json extra_params_; - const std::vector partition_list_; - - TopKQueryResult& result_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/SearchCombineRequest.cpp b/core/src/server/delivery/request/SearchCombineRequest.cpp deleted file mode 100644 index a9569ea89c..0000000000 --- a/core/src/server/delivery/request/SearchCombineRequest.cpp +++ /dev/null @@ -1,434 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/SearchCombineRequest.h" -#include "db/Utils.h" -#include "server/DBWrapper.h" -#include "server/context/Context.h" -#include "utils/CommonUtil.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include - -namespace milvus { -namespace server { - -namespace { - -constexpr int64_t MAX_TOPK_GAP = 200; - -void -GetUniqueList(const std::vector& list, std::set& unique_list) { - for (const std::string& item : list) { - unique_list.insert(item); - } -} - -bool -IsSameList(const std::set& left, const std::set& right) { - if (left.size() != right.size()) { - return false; - } - - std::set::const_iterator iter_left; - std::set::const_iterator iter_right; - for (iter_left = left.begin(), iter_right = right.begin(); iter_left != left.end(); iter_left++, iter_right++) { - if ((*iter_left) != (*iter_right)) { - return false; - } - } - - return true; -} - -void -FreeRequest(SearchRequestPtr& request, const Status& status) { - request->set_status(status); - request->Done(); -} - -class TracingContextList { - public: - TracingContextList() = default; - - ~TracingContextList() { - Finish(); - } - - void - CreateChild(std::vector& requests, const std::string& operation_name) { - Finish(); - for (auto& request : requests) { - auto parent_context = request->Context(); - if (parent_context) { - auto child_context = request->Context()->Child(operation_name); - context_list_.emplace_back(child_context); - } - } - } - - void - Finish() { - for (auto& context : context_list_) { - context->GetTraceContext()->GetSpan()->Finish(); - } - context_list_.clear(); - } - - private: - std::vector context_list_; -}; - -} // namespace - -SearchCombineRequest::SearchCombineRequest(int64_t max_nq) - : BaseRequest(nullptr, BaseRequest::kSearchCombine), combine_max_nq_(max_nq) { -} - -Status -SearchCombineRequest::Combine(const SearchRequestPtr& request) { - if (request == nullptr) { - return Status(SERVER_NULL_POINTER, ""); - } - - // the request must be tested by CanCombine before invoke this function - // reset some parameters in necessary - if (request_list_.empty()) { - // validate first request input - auto status = ValidationUtil::ValidateCollectionName(request->CollectionName()); - if (!status.ok()) { - return status; - } - - status = ValidationUtil::ValidateSearchTopk(request->TopK()); - if (!status.ok()) { - return status; - } - - // assign base parameters - collection_name_ = request->CollectionName(); - min_topk_ = request->TopK() - MAX_TOPK_GAP / 2; - if (min_topk_ < 0) { - min_topk_ = 0; - } - max_topk_ = request->TopK() + MAX_TOPK_GAP / 2; - if (max_topk_ > QUERY_MAX_TOPK) { - max_topk_ = QUERY_MAX_TOPK; - } - extra_params_ = request->ExtraParams(); - - GetUniqueList(request->PartitionList(), partition_list_); - GetUniqueList(request->FileIDList(), file_id_list_); - } - - request_list_.push_back(request); - vectors_data_.vector_count_ += request->VectorsData().vector_count_; - - return Status::OK(); -} - -bool -SearchCombineRequest::CanCombine(const SearchRequestPtr& request) { - if (collection_name_ != request->CollectionName()) { - return false; - } - - if (extra_params_ != request->ExtraParams()) { - return false; - } - - // topk must within certain range - if (request->TopK() < min_topk_ || request->TopK() > max_topk_) { - return false; - } - - // sum of nq must less-equal than MAX_NQ - if (vectors_data_.vector_count_ > combine_max_nq_ || request->VectorsData().vector_count_ > combine_max_nq_) { - return false; - } - uint64_t total_nq = vectors_data_.vector_count_ + request->VectorsData().vector_count_; - if (total_nq > combine_max_nq_) { - return false; - } - - // partition list must be equal for each one - std::set partition_list; - GetUniqueList(request->PartitionList(), partition_list); - if (!IsSameList(partition_list_, partition_list)) { - return false; - } - - // file id list must be equal for each one - std::set file_id_list; - GetUniqueList(request->FileIDList(), file_id_list); - if (!IsSameList(file_id_list_, file_id_list)) { - return false; - } - - return true; -} - -bool -SearchCombineRequest::CanCombine(const SearchRequestPtr& left, const SearchRequestPtr& right, int64_t max_nq) { - if (left->CollectionName() != right->CollectionName()) { - return false; - } - - if (left->ExtraParams() != right->ExtraParams()) { - return false; - } - - // topk must within certain range - if (abs(left->TopK() - right->TopK() > MAX_TOPK_GAP)) { - return false; - } - - // sum of nq must less-equal than MAX_NQ - if (left->VectorsData().vector_count_ > max_nq || right->VectorsData().vector_count_ > max_nq) { - return false; - } - uint64_t total_nq = left->VectorsData().vector_count_ + right->VectorsData().vector_count_; - if (total_nq > max_nq) { - return false; - } - - // partition list must be equal for each one - std::set left_partition_list, right_partition_list; - GetUniqueList(left->PartitionList(), left_partition_list); - GetUniqueList(right->PartitionList(), right_partition_list); - if (!IsSameList(left_partition_list, right_partition_list)) { - return false; - } - - // file id list must be equal for each one - std::set left_file_id_list, right_file_id_list; - GetUniqueList(left->FileIDList(), left_file_id_list); - GetUniqueList(right->FileIDList(), right_file_id_list); - if (!IsSameList(left_file_id_list, right_file_id_list)) { - return false; - } - - return true; -} - -Status -SearchCombineRequest::FreeRequests(const Status& status) { - for (auto request : request_list_) { - FreeRequest(request, status); - } - - return Status::OK(); -} - -Status -SearchCombineRequest::OnExecute() { - try { - size_t combined_request = request_list_.size(); - LOG_SERVER_DEBUG_ << "SearchCombineRequest execute, request count=" << combined_request - << ", extra_params=" << extra_params_.dump(); - std::string hdr = "SearchCombineRequest(collection=" + collection_name_ + ")"; - - TimeRecorderAuto rc(hdr); - - // step 1: check collection existence - // only process root collection, ignore partition collection - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_name_; - auto status = DBWrapper::DB()->DescribeCollection(collection_schema); - - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - status = Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - FreeRequests(status); - return status; - } else { - FreeRequests(status); - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - status = Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - FreeRequests(status); - return status; - } - } - - // step 2: check input - size_t run_request = 0; - std::vector::iterator iter = request_list_.begin(); - for (; iter != request_list_.end();) { - SearchRequestPtr& request = *iter; - status = ValidationUtil::ValidateSearchTopk(request->TopK()); - if (!status.ok()) { - // check failed, erase request and let it return error status - FreeRequest(request, status); - iter = request_list_.erase(iter); - continue; - } - - status = ValidationUtil::ValidateSearchParams(extra_params_, collection_schema, request->TopK()); - if (!status.ok()) { - // check failed, erase request and let it return error status - FreeRequest(request, status); - iter = request_list_.erase(iter); - continue; - } - - status = ValidationUtil::ValidateVectorData(request->VectorsData(), collection_schema); - if (!status.ok()) { - // check failed, erase request and let it return error status - FreeRequest(request, status); - iter = request_list_.erase(iter); - continue; - } - - status = ValidationUtil::ValidatePartitionTags(request->PartitionList()); - if (!status.ok()) { - // check failed, erase request and let it return error status - FreeRequest(request, status); - iter = request_list_.erase(iter); - continue; - } - - // reset topk - search_topk_ = request->TopK() > search_topk_ ? request->TopK() : search_topk_; - - // next one - run_request++; - iter++; - } - - // all requests are skipped - if (request_list_.empty()) { - LOG_SERVER_DEBUG_ << "all combined requests were skipped"; - return Status::OK(); - } - - LOG_SERVER_DEBUG_ << (combined_request - run_request) << " requests were skipped"; - LOG_SERVER_DEBUG_ << "reset topk to " << search_topk_; - rc.RecordSection("check validation"); - - // step 3: construct vectors_data - SearchRequestPtr& first_request = *request_list_.begin(); - uint64_t total_count = 0; - for (auto& request : request_list_) { - total_count += request->VectorsData().vector_count_; - } - vectors_data_.vector_count_ = total_count; - - uint16_t dimension = collection_schema.dimension_; - bool is_float = true; - if (!first_request->VectorsData().float_data_.empty()) { - vectors_data_.float_data_.resize(total_count * dimension); - } else { - vectors_data_.binary_data_.resize(total_count * dimension / 8); - is_float = false; - } - - int64_t offset = 0; - for (auto& request : request_list_) { - const engine::VectorsData& src = request->VectorsData(); - if (is_float) { - size_t element_cnt = src.vector_count_ * dimension; - memcpy(vectors_data_.float_data_.data() + offset, src.float_data_.data(), element_cnt * sizeof(float)); - offset += element_cnt; - } else { - size_t element_cnt = src.vector_count_ * dimension / 8; - memcpy(vectors_data_.binary_data_.data() + offset, src.binary_data_.data(), element_cnt); - offset += element_cnt; - } - } - - LOG_SERVER_DEBUG_ << total_count << " query vectors combined"; - rc.RecordSection("combined query vectors"); - - // step 4: search vectors - const std::vector& partition_list = first_request->PartitionList(); - const std::vector& file_id_list = first_request->FileIDList(); - - engine::ResultIds result_ids; - engine::ResultDistances result_distances; - { - TracingContextList context_list; - context_list.CreateChild(request_list_, "Combine Query"); - - if (file_id_list_.empty()) { - status = DBWrapper::DB()->Query(nullptr, collection_name_, partition_list, (size_t)search_topk_, - extra_params_, vectors_data_, result_ids, result_distances); - } else { - status = DBWrapper::DB()->QueryByFileID(nullptr, file_id_list, (size_t)search_topk_, extra_params_, - vectors_data_, result_ids, result_distances); - } - } - - rc.RecordSection("search vectors from engine"); - - if (!status.ok()) { - // let all request return - FreeRequests(status); - return status; - } - if (result_ids.empty()) { - status = Status(DB_ERROR, "no result returned for combined request"); - // let all request return - FreeRequests(status); - return status; - } - - // avoid memcpy crash, check distance count = id count - if (result_distances.size() != result_ids.size()) { - status = Status(DB_ERROR, "Result distance and id count doesn't match"); - // let all request return - FreeRequests(status); - return status; - } - - // step 5: construct result array - // engine ensure each target vector has same count of id/distance pairs - size_t pair_each_vector = result_ids.size() / vectors_data_.vector_count_; - offset = 0; - for (auto& request : request_list_) { - uint64_t count = request->VectorsData().vector_count_; - int64_t topk = request->TopK(); - uint64_t pair_cnt = (pair_each_vector > topk) ? topk : pair_each_vector; - TopKQueryResult& result = request->QueryResult(); - result.row_num_ = count; - result.id_list_.resize(count * pair_cnt); - result.distance_list_.resize(count * pair_cnt); - - for (uint64_t i = 0; i < count; i++) { - uint64_t poz = i * pair_cnt; - memcpy(result.id_list_.data() + poz, result_ids.data() + offset + poz, pair_cnt * sizeof(int64_t)); - memcpy(result.distance_list_.data() + poz, result_distances.data() + offset + poz, - pair_cnt * sizeof(float)); - } - - offset += count * pair_cnt; - - // let request return - FreeRequest(request, Status::OK()); - } - - rc.RecordSection("construct result and send"); - } catch (std::exception& ex) { - Status status = Status(SERVER_UNEXPECTED_ERROR, ex.what()); - FreeRequests(status); - return status; - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/SearchCombineRequest.h b/core/src/server/delivery/request/SearchCombineRequest.h deleted file mode 100644 index d71c4c70c7..0000000000 --- a/core/src/server/delivery/request/SearchCombineRequest.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/delivery/request/BaseRequest.h" -#include "server/delivery/request/SearchRequest.h" - -#include -#include -#include -#include - -namespace milvus { -namespace server { - -constexpr int64_t COMBINE_MAX_NQ = 64; - -class SearchCombineRequest : public BaseRequest { - public: - explicit SearchCombineRequest(int64_t max_nq = COMBINE_MAX_NQ); - - Status - Combine(const SearchRequestPtr& request); - - bool - CanCombine(const SearchRequestPtr& request); - - static bool - CanCombine(const SearchRequestPtr& left, const SearchRequestPtr& right, int64_t max_nq = COMBINE_MAX_NQ); - - protected: - Status - OnExecute() override; - - private: - Status - FreeRequests(const Status& status); - - private: - std::string collection_name_; - engine::VectorsData vectors_data_; - int64_t min_topk_ = 0; - int64_t search_topk_ = 0; - int64_t max_topk_ = 0; - milvus::json extra_params_; - std::set partition_list_; - std::set file_id_list_; - - std::vector request_list_; - - int64_t combine_max_nq_ = COMBINE_MAX_NQ; -}; - -using SearchCombineRequestPtr = std::shared_ptr; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/SearchRequest.cpp b/core/src/server/delivery/request/SearchRequest.cpp deleted file mode 100644 index 55f66f3431..0000000000 --- a/core/src/server/delivery/request/SearchRequest.cpp +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/SearchRequest.h" - -#include - -#include - -#include "db/Utils.h" -#include "server/DBWrapper.h" -#include "utils/CommonUtil.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#ifdef ENABLE_CPU_PROFILING -#include -#endif - -namespace milvus { -namespace server { - -SearchRequest::SearchRequest(const std::shared_ptr& context, - const std::string& collection_name, const engine::VectorsData& vectors, int64_t topk, - const milvus::json& extra_params, const std::vector& partition_list, - const std::vector& file_id_list, TopKQueryResult& result) - : BaseRequest(context, BaseRequest::kSearch), - collection_name_(collection_name), - vectors_data_(vectors), - topk_(topk), - extra_params_(extra_params), - partition_list_(partition_list), - file_id_list_(file_id_list), - result_(result) { -} - -BaseRequestPtr -SearchRequest::Create(const std::shared_ptr& context, const std::string& collection_name, - const engine::VectorsData& vectors, int64_t topk, const milvus::json& extra_params, - const std::vector& partition_list, const std::vector& file_id_list, - TopKQueryResult& result) { - return std::shared_ptr( - new SearchRequest(context, collection_name, vectors, topk, extra_params, partition_list, file_id_list, result)); -} - -Status -SearchRequest::OnPreExecute() { - LOG_SERVER_INFO_ << LogOut("[%s][%ld] ", "search", 0) << "Search pre-execute. Check search parameters"; - std::string hdr = "SearchRequest pre-execute(collection=" + collection_name_ + ")"; - TimeRecorderAuto rc(LogOut("[%s][%ld] %s", "search", 0, hdr.c_str())); - - milvus::server::ContextChild tracer_pre(context_, "Pre Query"); - // step 1: check collection name - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] %s", "search", 0, status.message().c_str()); - return status; - } - - // step 2: check search topk - status = ValidationUtil::ValidateSearchTopk(topk_); - if (!status.ok()) { - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] %s", "search", 0, status.message().c_str()); - return status; - } - - // step 3: check partition tags - status = ValidationUtil::ValidatePartitionTags(partition_list_); - fiu_do_on("SearchRequest.OnExecute.invalid_partition_tags", status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] %s", "search", 0, status.message().c_str()); - return status; - } - - return Status::OK(); -} - -Status -SearchRequest::OnExecute() { - LOG_SERVER_INFO_ << LogOut("[%s][%ld] ", "search", 0) << "Search execute."; - try { - uint64_t vector_count = vectors_data_.vector_count_; - fiu_do_on("SearchRequest.OnExecute.throw_std_exception", throw std::exception()); - std::string hdr = "SearchRequest execute(collection=" + collection_name_ + - ", nq=" + std::to_string(vector_count) + ", k=" + std::to_string(topk_) + ")"; - TimeRecorderAuto rc(LogOut("[%s][%ld] %s", "search", 0, hdr.c_str())); - - // step 4: check collection existence - // only process root collection, ignore partition collection - collection_schema_.collection_id_ = collection_name_; - auto status = DBWrapper::DB()->DescribeCollection(collection_schema_); - - fiu_do_on("SearchRequest.OnExecute.describe_collection_fail", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] Collection %s not found: %s", "search", 0, - collection_name_.c_str(), status.message().c_str()); - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] Error occurred when describing collection %s: %s", "search", 0, - collection_name_.c_str(), status.message().c_str()); - return status; - } - } else { - if (!collection_schema_.owner_collection_.empty()) { - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] %s", "search", 0, - CollectionNotExistMsg(collection_name_).c_str()); - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - // step 5: check search parameters - status = ValidationUtil::ValidateSearchParams(extra_params_, collection_schema_, topk_); - if (!status.ok()) { - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] Invalid search params: %s", "search", 0, status.message().c_str()); - return status; - } - - // step 6: check vector data according to metric type - status = ValidationUtil::ValidateVectorData(vectors_data_, collection_schema_); - if (!status.ok()) { - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] Invalid vector data: %s", "search", 0, status.message().c_str()); - return status; - } - - rc.RecordSection("check validation"); - - // step 7: search vectors -#ifdef ENABLE_CPU_PROFILING - std::string fname = "/tmp/search_" + CommonUtil::GetCurrentTimeStr() + ".profiling"; - ProfilerStart(fname.c_str()); -#endif - - engine::ResultIds result_ids; - engine::ResultDistances result_distances; - - if (file_id_list_.empty()) { - status = DBWrapper::DB()->Query(context_, collection_name_, partition_list_, (size_t)topk_, extra_params_, - vectors_data_, result_ids, result_distances); - } else { - status = DBWrapper::DB()->QueryByFileID(context_, file_id_list_, (size_t)topk_, extra_params_, - vectors_data_, result_ids, result_distances); - } - - rc.RecordSection("query vectors from engine"); - -#ifdef ENABLE_CPU_PROFILING - ProfilerStop(); -#endif - fiu_do_on("SearchRequest.OnExecute.query_fail", status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] Query fail: %s", "search", 0, status.message().c_str()); - return status; - } - fiu_do_on("SearchRequest.OnExecute.empty_result_ids", result_ids.clear()); - if (result_ids.empty()) { - return Status::OK(); // empty collection - } - - // step 8: construct result array - milvus::server::ContextChild tracer(context_, "Constructing result"); - result_.row_num_ = vectors_data_.vector_count_; - result_.id_list_.swap(result_ids); - result_.distance_list_.swap(result_distances); - rc.RecordSection("construct result"); - } catch (std::exception& ex) { - LOG_SERVER_ERROR_ << LogOut("[%s][%ld] Encounter exception: %s", "search", 0, ex.what()); - return Status(SERVER_UNEXPECTED_ERROR, ex.what()); - } - - return Status::OK(); -} -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/SearchRequest.h b/core/src/server/delivery/request/SearchRequest.h deleted file mode 100644 index 13cea49b2a..0000000000 --- a/core/src/server/delivery/request/SearchRequest.h +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/delivery/request/BaseRequest.h" - -#include -#include -#include - -namespace milvus { -namespace server { - -class SearchRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - const engine::VectorsData& vectors, int64_t topk, const milvus::json& extra_params, - const std::vector& partition_list, const std::vector& file_id_list, - TopKQueryResult& result); - - const std::string& - CollectionName() const { - return collection_name_; - } - - const engine::VectorsData& - VectorsData() const { - return vectors_data_; - } - - int64_t - TopK() const { - return topk_; - } - - const milvus::json& - ExtraParams() const { - return extra_params_; - } - - const std::vector& - PartitionList() const { - return partition_list_; - } - - const std::vector& - FileIDList() const { - return file_id_list_; - } - - TopKQueryResult& - QueryResult() { - return result_; - } - - const milvus::engine::meta::CollectionSchema& - TableSchema() const { - return collection_schema_; - } - - protected: - SearchRequest(const std::shared_ptr& context, const std::string& collection_name, - const engine::VectorsData& vectors, int64_t topk, const milvus::json& extra_params, - const std::vector& partition_list, const std::vector& file_id_list, - TopKQueryResult& result); - - Status - OnPreExecute() override; - - Status - OnExecute() override; - - private: - const std::string collection_name_; - const engine::VectorsData vectors_data_; - int64_t topk_; - milvus::json extra_params_; - const std::vector partition_list_; - const std::vector file_id_list_; - - TopKQueryResult& result_; - - // for validation - milvus::engine::meta::CollectionSchema collection_schema_; -}; - -using SearchRequestPtr = std::shared_ptr; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/ShowCollectionInfoRequest.cpp b/core/src/server/delivery/request/ShowCollectionInfoRequest.cpp deleted file mode 100644 index 1f1acbfa7e..0000000000 --- a/core/src/server/delivery/request/ShowCollectionInfoRequest.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// 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 limitations -// under the License. - -#include "server/delivery/request/ShowCollectionInfoRequest.h" -#include "server/DBWrapper.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include - -namespace milvus { -namespace server { - -ShowCollectionInfoRequest::ShowCollectionInfoRequest(const std::shared_ptr& context, - const std::string& collection_name, std::string& collection_info) - : BaseRequest(context, BaseRequest::kShowCollectionInfo), - collection_name_(collection_name), - collection_info_(collection_info) { -} - -BaseRequestPtr -ShowCollectionInfoRequest::Create(const std::shared_ptr& context, - const std::string& collection_name, std::string& collection_info) { - return std::shared_ptr(new ShowCollectionInfoRequest(context, collection_name, collection_info)); -} - -Status -ShowCollectionInfoRequest::OnExecute() { - std::string hdr = "ShowCollectionInfoRequest(collection=" + collection_name_ + ")"; - TimeRecorderAuto rc(hdr); - - // step 1: check collection name - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - if (!status.ok()) { - return status; - } - - // step 2: check collection existence - // only process root collection, ignore partition collection - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeCollection(collection_schema); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - // step 3: get partitions - status = DBWrapper::DB()->GetCollectionInfo(collection_name_, collection_info_); - if (!status.ok()) { - return status; - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/ShowCollectionInfoRequest.h b/core/src/server/delivery/request/ShowCollectionInfoRequest.h deleted file mode 100644 index c6982bd289..0000000000 --- a/core/src/server/delivery/request/ShowCollectionInfoRequest.h +++ /dev/null @@ -1,48 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include "server/delivery/request/BaseRequest.h" - -#include -#include -#include - -namespace milvus { -namespace server { - -class ShowCollectionInfoRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - std::string& collection_info); - - protected: - ShowCollectionInfoRequest(const std::shared_ptr& context, - const std::string& collection_name, std::string& collection_info); - - Status - OnExecute() override; - - private: - const std::string collection_name_; - std::string& collection_info_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/ShowCollectionsRequest.cpp b/core/src/server/delivery/request/ShowCollectionsRequest.cpp deleted file mode 100644 index 8115e8f7e8..0000000000 --- a/core/src/server/delivery/request/ShowCollectionsRequest.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/ShowCollectionsRequest.h" -#include "server/DBWrapper.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" - -#include -#include -#include -#include - -namespace milvus { -namespace server { - -ShowCollectionsRequest::ShowCollectionsRequest(const std::shared_ptr& context, - std::vector& collection_name_list) - : BaseRequest(context, BaseRequest::kShowCollections), collection_name_list_(collection_name_list) { -} - -BaseRequestPtr -ShowCollectionsRequest::Create(const std::shared_ptr& context, - std::vector& collection_name_list) { - return std::shared_ptr(new ShowCollectionsRequest(context, collection_name_list)); -} - -Status -ShowCollectionsRequest::OnExecute() { - TimeRecorderAuto rc("ShowCollectionsRequest"); - - std::vector schema_array; - auto status = DBWrapper::DB()->AllCollections(schema_array); - fiu_do_on("ShowCollectionsRequest.OnExecute.show_collections_fail", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - return status; - } - - for (auto& schema : schema_array) { - collection_name_list_.push_back(schema.collection_id_); - } - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/ShowCollectionsRequest.h b/core/src/server/delivery/request/ShowCollectionsRequest.h deleted file mode 100644 index f053d5ac34..0000000000 --- a/core/src/server/delivery/request/ShowCollectionsRequest.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include - -#include "server/delivery/request/BaseRequest.h" - -namespace milvus { -namespace server { - -class ShowCollectionsRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, std::vector& collection_name_list); - - protected: - ShowCollectionsRequest(const std::shared_ptr& context, - std::vector& collection_name_list); - - Status - OnExecute() override; - - private: - std::vector& collection_name_list_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/ShowPartitionsRequest.cpp b/core/src/server/delivery/request/ShowPartitionsRequest.cpp deleted file mode 100644 index 4b82ffeca1..0000000000 --- a/core/src/server/delivery/request/ShowPartitionsRequest.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/request/ShowPartitionsRequest.h" -#include "server/DBWrapper.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" -#include "utils/ValidationUtil.h" - -#include -#include -#include - -namespace milvus { -namespace server { - -ShowPartitionsRequest::ShowPartitionsRequest(const std::shared_ptr& context, - const std::string& collection_name, - std::vector& partition_list) - : BaseRequest(context, BaseRequest::kShowPartitions), - collection_name_(collection_name), - partition_list_(partition_list) { -} - -BaseRequestPtr -ShowPartitionsRequest::Create(const std::shared_ptr& context, - const std::string& collection_name, std::vector& partition_list) { - return std::shared_ptr(new ShowPartitionsRequest(context, collection_name, partition_list)); -} - -Status -ShowPartitionsRequest::OnExecute() { - std::string hdr = "ShowPartitionsRequest(collection=" + collection_name_ + ")"; - TimeRecorderAuto rc(hdr); - - // step 1: check collection name - auto status = ValidationUtil::ValidateCollectionName(collection_name_); - fiu_do_on("ShowPartitionsRequest.OnExecute.invalid_collection_name", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - return status; - } - - // step 2: check collection existence - // only process root collection, ignore partition collection - engine::meta::CollectionSchema collection_schema; - collection_schema.collection_id_ = collection_name_; - status = DBWrapper::DB()->DescribeCollection(collection_schema); - if (!status.ok()) { - if (status.code() == DB_NOT_FOUND) { - return Status(SERVER_COLLECTION_NOT_EXIST, CollectionNotExistMsg(collection_name_)); - } else { - return status; - } - } else { - if (!collection_schema.owner_collection_.empty()) { - return Status(SERVER_INVALID_COLLECTION_NAME, CollectionNotExistMsg(collection_name_)); - } - } - - // step 3: get partitions - std::vector schema_array; - status = DBWrapper::DB()->ShowPartitions(collection_name_, schema_array); - fiu_do_on("ShowPartitionsRequest.OnExecute.show_partition_fail", - status = Status(milvus::SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - return status; - } - - partition_list_.clear(); - partition_list_.emplace_back(collection_name_, milvus::engine::DEFAULT_PARTITON_TAG); - for (auto& schema : schema_array) { - partition_list_.emplace_back(schema.owner_collection_, schema.partition_tag_); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/request/ShowPartitionsRequest.h b/core/src/server/delivery/request/ShowPartitionsRequest.h deleted file mode 100644 index 1ef1b99ef2..0000000000 --- a/core/src/server/delivery/request/ShowPartitionsRequest.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/delivery/request/BaseRequest.h" - -#include -#include -#include - -namespace milvus { -namespace server { - -class ShowPartitionsRequest : public BaseRequest { - public: - static BaseRequestPtr - Create(const std::shared_ptr& context, const std::string& collection_name, - std::vector& partition_list); - - protected: - ShowPartitionsRequest(const std::shared_ptr& context, const std::string& collection_name, - std::vector& partition_list); - - Status - OnExecute() override; - - private: - const std::string collection_name_; - std::vector& partition_list_; -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/strategy/RequestStrategy.h b/core/src/server/delivery/strategy/RequestStrategy.h deleted file mode 100644 index 3456deb5f6..0000000000 --- a/core/src/server/delivery/strategy/RequestStrategy.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/delivery/request/BaseRequest.h" -#include "utils/BlockingQueue.h" -#include "utils/Status.h" - -#include -#include -#include -#include -#include - -namespace milvus { -namespace server { - -class RequestStrategy { - protected: - RequestStrategy() = default; - - public: - virtual Status - ReScheduleQueue(const BaseRequestPtr& request, std::queue& queue) = 0; -}; - -using RequestStrategyPtr = std::shared_ptr; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/strategy/SearchReqStrategy.cpp b/core/src/server/delivery/strategy/SearchReqStrategy.cpp deleted file mode 100644 index 0b66ca7b57..0000000000 --- a/core/src/server/delivery/strategy/SearchReqStrategy.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/delivery/strategy/SearchReqStrategy.h" -#include "config/Config.h" -#include "server/delivery/request/SearchCombineRequest.h" -#include "server/delivery/request/SearchRequest.h" -#include "utils/CommonUtil.h" -#include "utils/Error.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" - -#include -#include - -namespace milvus { -namespace server { - -SearchReqStrategy::SearchReqStrategy() { - SetIdentity("SearchReqStrategy"); - AddSearchCombineMaxNqListener(); -} - -Status -SearchReqStrategy::ReScheduleQueue(const BaseRequestPtr& request, std::queue& queue) { - if (request->GetRequestType() != BaseRequest::kSearch) { - std::string msg = "search strategy can only handle search request"; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_UNSUPPORTED_ERROR, msg); - } - - // if config set to 0, neve combine - if (search_combine_nq_ <= 0) { - queue.push(request); - return Status::OK(); - } - - // TimeRecorderAuto rc("SearchReqStrategy::ReScheduleQueue"); - SearchRequestPtr new_search_req = std::static_pointer_cast(request); - - BaseRequestPtr last_req = queue.back(); - if (last_req->GetRequestType() == BaseRequest::kSearch) { - SearchRequestPtr last_search_req = std::static_pointer_cast(last_req); - if (SearchCombineRequest::CanCombine(last_search_req, new_search_req, search_combine_nq_)) { - // combine request - SearchCombineRequestPtr combine_request = std::make_shared(search_combine_nq_); - combine_request->Combine(last_search_req); - combine_request->Combine(new_search_req); - queue.back() = combine_request; // replace the last request to combine request - LOG_SERVER_DEBUG_ << "Combine 2 search request"; - } else { - // directly put to queue - queue.push(request); - } - } else if (last_req->GetRequestType() == BaseRequest::kSearchCombine) { - SearchCombineRequestPtr combine_req = std::static_pointer_cast(last_req); - if (combine_req->CanCombine(new_search_req)) { - // combine request - combine_req->Combine(new_search_req); - LOG_SERVER_DEBUG_ << "Combine more search request"; - } else { - // directly put to queue - queue.push(request); - } - } else { - std::string msg = "unsupported request type for search strategy"; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_UNSUPPORTED_ERROR, msg); - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/delivery/strategy/SearchReqStrategy.h b/core/src/server/delivery/strategy/SearchReqStrategy.h deleted file mode 100644 index 3d2c3de03b..0000000000 --- a/core/src/server/delivery/strategy/SearchReqStrategy.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "config/handler/EngineConfigHandler.h" -#include "server/delivery/strategy/RequestStrategy.h" -#include "utils/Status.h" - -#include -#include - -namespace milvus { -namespace server { - -class SearchReqStrategy : public RequestStrategy, public EngineConfigHandler { - public: - SearchReqStrategy(); - - Status - ReScheduleQueue(const BaseRequestPtr& request, std::queue& queue) override; -}; - -using RequestStrategyPtr = std::shared_ptr; -} // namespace server -} // namespace milvus diff --git a/core/src/server/grpc_impl/GrpcRequestHandler.cpp b/core/src/server/grpc_impl/GrpcRequestHandler.cpp deleted file mode 100644 index a0d072891d..0000000000 --- a/core/src/server/grpc_impl/GrpcRequestHandler.cpp +++ /dev/null @@ -1,1132 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/grpc_impl/GrpcRequestHandler.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "context/HybridSearchContext.h" -#include "query/BinaryQuery.h" -#include "server/context/ConnectionContext.h" -#include "tracing/TextMapCarrier.h" -#include "tracing/TracerUtil.h" -#include "utils/Log.h" -#include "utils/LogUtil.h" -#include "utils/TimeRecorder.h" - -namespace milvus { -namespace server { -namespace grpc { - -const char* EXTRA_PARAM_KEY = "params"; - -::milvus::grpc::ErrorCode -ErrorMap(ErrorCode code) { - static const std::map code_map = { - {SERVER_UNEXPECTED_ERROR, ::milvus::grpc::ErrorCode::UNEXPECTED_ERROR}, - {SERVER_UNSUPPORTED_ERROR, ::milvus::grpc::ErrorCode::UNEXPECTED_ERROR}, - {SERVER_NULL_POINTER, ::milvus::grpc::ErrorCode::UNEXPECTED_ERROR}, - {SERVER_INVALID_ARGUMENT, ::milvus::grpc::ErrorCode::ILLEGAL_ARGUMENT}, - {SERVER_FILE_NOT_FOUND, ::milvus::grpc::ErrorCode::FILE_NOT_FOUND}, - {SERVER_NOT_IMPLEMENT, ::milvus::grpc::ErrorCode::UNEXPECTED_ERROR}, - {SERVER_CANNOT_CREATE_FOLDER, ::milvus::grpc::ErrorCode::CANNOT_CREATE_FOLDER}, - {SERVER_CANNOT_CREATE_FILE, ::milvus::grpc::ErrorCode::CANNOT_CREATE_FILE}, - {SERVER_CANNOT_DELETE_FOLDER, ::milvus::grpc::ErrorCode::CANNOT_DELETE_FOLDER}, - {SERVER_CANNOT_DELETE_FILE, ::milvus::grpc::ErrorCode::CANNOT_DELETE_FILE}, - {SERVER_COLLECTION_NOT_EXIST, ::milvus::grpc::ErrorCode::COLLECTION_NOT_EXISTS}, - {SERVER_INVALID_COLLECTION_NAME, ::milvus::grpc::ErrorCode::ILLEGAL_COLLECTION_NAME}, - {SERVER_INVALID_COLLECTION_DIMENSION, ::milvus::grpc::ErrorCode::ILLEGAL_DIMENSION}, - {SERVER_INVALID_VECTOR_DIMENSION, ::milvus::grpc::ErrorCode::ILLEGAL_DIMENSION}, - - {SERVER_INVALID_INDEX_TYPE, ::milvus::grpc::ErrorCode::ILLEGAL_INDEX_TYPE}, - {SERVER_INVALID_ROWRECORD, ::milvus::grpc::ErrorCode::ILLEGAL_ROWRECORD}, - {SERVER_INVALID_ROWRECORD_ARRAY, ::milvus::grpc::ErrorCode::ILLEGAL_ROWRECORD}, - {SERVER_INVALID_TOPK, ::milvus::grpc::ErrorCode::ILLEGAL_TOPK}, - {SERVER_INVALID_NPROBE, ::milvus::grpc::ErrorCode::ILLEGAL_ARGUMENT}, - {SERVER_INVALID_INDEX_NLIST, ::milvus::grpc::ErrorCode::ILLEGAL_NLIST}, - {SERVER_INVALID_INDEX_METRIC_TYPE, ::milvus::grpc::ErrorCode::ILLEGAL_METRIC_TYPE}, - {SERVER_INVALID_INDEX_FILE_SIZE, ::milvus::grpc::ErrorCode::ILLEGAL_ARGUMENT}, - {SERVER_ILLEGAL_VECTOR_ID, ::milvus::grpc::ErrorCode::ILLEGAL_VECTOR_ID}, - {SERVER_ILLEGAL_SEARCH_RESULT, ::milvus::grpc::ErrorCode::ILLEGAL_SEARCH_RESULT}, - {SERVER_CACHE_FULL, ::milvus::grpc::ErrorCode::CACHE_FAILED}, - {DB_META_TRANSACTION_FAILED, ::milvus::grpc::ErrorCode::META_FAILED}, - {SERVER_BUILD_INDEX_ERROR, ::milvus::grpc::ErrorCode::BUILD_INDEX_ERROR}, - {SERVER_OUT_OF_MEMORY, ::milvus::grpc::ErrorCode::OUT_OF_MEMORY}, - }; - - if (code_map.find(code) != code_map.end()) { - return code_map.at(code); - } else { - return ::milvus::grpc::ErrorCode::UNEXPECTED_ERROR; - } -} - -std::string -RequestMap(BaseRequest::RequestType request_type) { - static const std::unordered_map request_map = { - {BaseRequest::kInsert, "Insert"}, - {BaseRequest::kCreateIndex, "CreateIndex"}, - {BaseRequest::kSearch, "Search"}, - {BaseRequest::kSearchByID, "SearchByID"}, - {BaseRequest::kHybridSearch, "HybridSearch"}, - {BaseRequest::kFlush, "Flush"}, - {BaseRequest::kCompact, "Compact"}, - }; - - if (request_map.find(request_type) != request_map.end()) { - return request_map.at(request_type); - } else { - return "OtherRequest"; - } -} - -namespace { -void -CopyRowRecords(const google::protobuf::RepeatedPtrField<::milvus::grpc::RowRecord>& grpc_records, - const google::protobuf::RepeatedField& grpc_id_array, - engine::VectorsData& vectors) { - // step 1: copy vector data - int64_t float_data_size = 0, binary_data_size = 0; - for (auto& record : grpc_records) { - float_data_size += record.float_data_size(); - binary_data_size += record.binary_data().size(); - } - - std::vector float_array(float_data_size, 0.0f); - std::vector binary_array(binary_data_size, 0); - int64_t float_offset = 0, binary_offset = 0; - if (float_data_size > 0) { - for (auto& record : grpc_records) { - memcpy(&float_array[float_offset], record.float_data().data(), record.float_data_size() * sizeof(float)); - float_offset += record.float_data_size(); - } - } else if (binary_data_size > 0) { - for (auto& record : grpc_records) { - memcpy(&binary_array[binary_offset], record.binary_data().data(), record.binary_data().size()); - binary_offset += record.binary_data().size(); - } - } - - // step 2: copy id array - std::vector id_array; - if (grpc_id_array.size() > 0) { - id_array.resize(grpc_id_array.size()); - memcpy(id_array.data(), grpc_id_array.data(), grpc_id_array.size() * sizeof(int64_t)); - } - - // step 3: contruct vectors - vectors.vector_count_ = grpc_records.size(); - vectors.float_data_.swap(float_array); - vectors.binary_data_.swap(binary_array); - vectors.id_array_.swap(id_array); -} - -void -ConstructResults(const TopKQueryResult& result, ::milvus::grpc::TopKQueryResult* response) { - if (!response) { - return; - } - - response->set_row_num(result.row_num_); - - response->mutable_ids()->Resize(static_cast(result.id_list_.size()), 0); - memcpy(response->mutable_ids()->mutable_data(), result.id_list_.data(), result.id_list_.size() * sizeof(int64_t)); - - response->mutable_distances()->Resize(static_cast(result.distance_list_.size()), 0.0); - memcpy(response->mutable_distances()->mutable_data(), result.distance_list_.data(), - result.distance_list_.size() * sizeof(float)); -} - -class GrpcConnectionContext : public milvus::server::ConnectionContext { - public: - explicit GrpcConnectionContext(::grpc::ServerContext* context) : context_(context) { - } - - bool - IsConnectionBroken() const override { - if (context_ == nullptr) { - return true; - } - - return context_->IsCancelled(); - } - - private: - ::grpc::ServerContext* context_ = nullptr; -}; - -} // namespace - -namespace { - -#define REQ_ID ("request_id") - -std::atomic _sequential_id; - -int64_t -get_sequential_id() { - return _sequential_id++; -} - -void -set_request_id(::grpc::ServerContext* context, const std::string& request_id) { - if (not context) { - // error - LOG_SERVER_ERROR_ << "set_request_id: grpc::ServerContext is nullptr" << std::endl; - return; - } - - context->AddInitialMetadata(REQ_ID, request_id); -} - -std::string -get_request_id(::grpc::ServerContext* context) { - if (not context) { - // error - LOG_SERVER_ERROR_ << "get_request_id: grpc::ServerContext is nullptr" << std::endl; - return "INVALID_ID"; - } - - auto server_metadata = context->server_metadata(); - - auto request_id_kv = server_metadata.find(REQ_ID); - if (request_id_kv == server_metadata.end()) { - // error - LOG_SERVER_ERROR_ << std::string(REQ_ID) << " not found in grpc.server_metadata" << std::endl; - return "INVALID_ID"; - } - - return request_id_kv->second.data(); -} - -} // namespace - -GrpcRequestHandler::GrpcRequestHandler(const std::shared_ptr& tracer) - : tracer_(tracer), random_num_generator_() { - std::random_device random_device; - random_num_generator_.seed(random_device()); -} - -void -GrpcRequestHandler::OnPostRecvInitialMetaData( - ::grpc::experimental::ServerRpcInfo* server_rpc_info, - ::grpc::experimental::InterceptorBatchMethods* interceptor_batch_methods) { - std::unordered_map text_map; - auto* metadata_map = interceptor_batch_methods->GetRecvInitialMetadata(); - auto context_kv = metadata_map->find(tracing::TracerUtil::GetTraceContextHeaderName()); - if (context_kv != metadata_map->end()) { - text_map[std::string(context_kv->first.data(), context_kv->first.length())] = - std::string(context_kv->second.data(), context_kv->second.length()); - } - // test debug mode - // if (std::string(server_rpc_info->method()).find("Search") != std::string::npos) { - // text_map["demo-debug-id"] = "debug-id"; - // } - - tracing::TextMapCarrier carrier{text_map}; - auto span_context_maybe = tracer_->Extract(carrier); - if (!span_context_maybe) { - std::cerr << span_context_maybe.error().message() << std::endl; - return; - } - auto span = tracer_->StartSpan(server_rpc_info->method(), {opentracing::ChildOf(span_context_maybe->get())}); - - auto server_context = server_rpc_info->server_context(); - auto client_metadata = server_context->client_metadata(); - - // if client provide request_id in metadata, milvus just use it, - // else milvus generate a sequential id. - std::string request_id; - auto request_id_kv = client_metadata.find("request_id"); - if (request_id_kv != client_metadata.end()) { - request_id = request_id_kv->second.data(); - LOG_SERVER_DEBUG_ << "client provide request_id: " << request_id; - - // if request_id is being used by another request, - // convert it to request_id_n. - std::lock_guard lock(context_map_mutex_); - if (context_map_.find(request_id) == context_map_.end()) { - // if not found exist, mark - context_map_[request_id] = nullptr; - } else { - // Finding a unused suffix - int64_t suffix = 1; - std::string try_request_id; - bool exist = true; - do { - try_request_id = request_id + "_" + std::to_string(suffix); - exist = context_map_.find(try_request_id) != context_map_.end(); - suffix++; - } while (exist); - context_map_[try_request_id] = nullptr; - } - } else { - request_id = std::to_string(get_sequential_id()); - set_request_id(server_context, request_id); - LOG_SERVER_DEBUG_ << "milvus generate request_id: " << request_id; - } - - auto trace_context = std::make_shared(span); - auto context = std::make_shared(request_id); - context->SetTraceContext(trace_context); - SetContext(server_rpc_info->server_context(), context); -} - -void -GrpcRequestHandler::OnPreSendMessage(::grpc::experimental::ServerRpcInfo* server_rpc_info, - ::grpc::experimental::InterceptorBatchMethods* interceptor_batch_methods) { - std::lock_guard lock(context_map_mutex_); - auto request_id = get_request_id(server_rpc_info->server_context()); - - if (context_map_.find(request_id) == context_map_.end()) { - // error - LOG_SERVER_ERROR_ << "request_id " << request_id << " not found in context_map_"; - return; - } - context_map_[request_id]->GetTraceContext()->GetSpan()->Finish(); - context_map_.erase(request_id); -} - -std::shared_ptr -GrpcRequestHandler::GetContext(::grpc::ServerContext* server_context) { - std::lock_guard lock(context_map_mutex_); - auto request_id = get_request_id(server_context); - - auto iter = context_map_.find(request_id); - if (iter == context_map_.end()) { - LOG_SERVER_ERROR_ << "GetContext: request_id " << request_id << " not found in context_map_"; - return nullptr; - } - - if (iter->second != nullptr) { - ConnectionContextPtr connection_context = std::make_shared(server_context); - iter->second->SetConnectionContext(connection_context); - } - return iter->second; -} - -void -GrpcRequestHandler::SetContext(::grpc::ServerContext* server_context, const std::shared_ptr& context) { - std::lock_guard lock(context_map_mutex_); - auto request_id = get_request_id(server_context); - context_map_[request_id] = context; -} - -uint64_t -GrpcRequestHandler::random_id() const { - std::lock_guard lock(random_mutex_); - auto value = random_num_generator_(); - while (value == 0) { - value = random_num_generator_(); - } - return value; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -::grpc::Status -GrpcRequestHandler::CreateCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionSchema* request, - ::milvus::grpc::Status* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - Status status = - request_handler_.CreateCollection(GetContext(context), request->collection_name(), request->dimension(), - request->index_file_size(), request->metric_type()); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response, status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::HasCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::BoolReply* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - bool has_collection = false; - - Status status = request_handler_.HasCollection(GetContext(context), request->collection_name(), has_collection); - response->set_bool_reply(has_collection); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response->mutable_status(), status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::DropCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::Status* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - Status status = request_handler_.DropCollection(GetContext(context), request->collection_name()); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response, status, context); - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::CreateIndex(::grpc::ServerContext* context, const ::milvus::grpc::IndexParam* request, - ::milvus::grpc::Status* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - milvus::json json_params; - for (int i = 0; i < request->extra_params_size(); i++) { - const ::milvus::grpc::KeyValuePair& extra = request->extra_params(i); - if (extra.key() == EXTRA_PARAM_KEY) { - json_params = json::parse(extra.value()); - } - } - - Status status = request_handler_.CreateIndex(GetContext(context), request->collection_name(), request->index_type(), - json_params); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response, status, context); - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::Insert(::grpc::ServerContext* context, const ::milvus::grpc::InsertParam* request, - ::milvus::grpc::VectorIds* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - // step 1: copy vector data - engine::VectorsData vectors; - CopyRowRecords(request->row_record_array(), request->row_id_array(), vectors); - - // step 2: insert vectors - Status status = - request_handler_.Insert(GetContext(context), request->collection_name(), vectors, request->partition_tag()); - - // step 3: return id array - response->mutable_vector_id_array()->Resize(static_cast(vectors.id_array_.size()), 0); - memcpy(response->mutable_vector_id_array()->mutable_data(), vectors.id_array_.data(), - vectors.id_array_.size() * sizeof(int64_t)); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response->mutable_status(), status, context); - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::GetVectorsByID(::grpc::ServerContext* context, const ::milvus::grpc::VectorsIdentity* request, - ::milvus::grpc::VectorsData* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - std::vector vector_ids; - vector_ids.reserve(request->id_array_size()); - for (int i = 0; i < request->id_array_size(); i++) { - vector_ids.push_back(request->id_array(i)); - } - - std::vector vectors; - Status status = - request_handler_.GetVectorsByID(GetContext(context), request->collection_name(), vector_ids, vectors); - - for (auto& vector : vectors) { - auto grpc_data = response->add_vectors_data(); - if (!vector.float_data_.empty()) { - grpc_data->mutable_float_data()->Resize(vector.float_data_.size(), 0); - memcpy(grpc_data->mutable_float_data()->mutable_data(), vector.float_data_.data(), - vector.float_data_.size() * sizeof(float)); - } else if (!vector.binary_data_.empty()) { - grpc_data->mutable_binary_data()->resize(vector.binary_data_.size()); - memcpy(grpc_data->mutable_binary_data()->data(), vector.binary_data_.data(), - vector.binary_data_.size() * sizeof(uint8_t)); - } - } - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response->mutable_status(), status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::GetVectorIDs(::grpc::ServerContext* context, const ::milvus::grpc::GetVectorIDsParam* request, - ::milvus::grpc::VectorIds* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - std::vector vector_ids; - Status status = request_handler_.GetVectorIDs(GetContext(context), request->collection_name(), - request->segment_name(), vector_ids); - - if (!vector_ids.empty()) { - response->mutable_vector_id_array()->Resize(vector_ids.size(), -1); - memcpy(response->mutable_vector_id_array()->mutable_data(), vector_ids.data(), - vector_ids.size() * sizeof(int64_t)); - } - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response->mutable_status(), status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::Search(::grpc::ServerContext* context, const ::milvus::grpc::SearchParam* request, - ::milvus::grpc::TopKQueryResult* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - // step 1: copy vector data - engine::VectorsData vectors; - CopyRowRecords(request->query_record_array(), google::protobuf::RepeatedField(), vectors); - - // step 2: partition tags - std::vector partitions; - std::copy(request->partition_tag_array().begin(), request->partition_tag_array().end(), - std::back_inserter(partitions)); - - // step 3: parse extra parameters - milvus::json json_params; - for (int i = 0; i < request->extra_params_size(); i++) { - const ::milvus::grpc::KeyValuePair& extra = request->extra_params(i); - if (extra.key() == EXTRA_PARAM_KEY) { - json_params = json::parse(extra.value()); - } - } - - // step 4: search vectors - std::vector file_ids; - TopKQueryResult result; - fiu_do_on("GrpcRequestHandler.Search.not_empty_file_ids", file_ids.emplace_back("test_file_id")); - - Status status = request_handler_.Search(GetContext(context), request->collection_name(), vectors, request->topk(), - json_params, partitions, file_ids, result); - - LOG_SERVER_DEBUG_C << "row num = " << result.row_num_ << ", id list length = " << result.id_list_.size() - << ", distance list length = " << result.distance_list_.size(); - - // step 5: construct and return result - ConstructResults(result, response); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response->mutable_status(), status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::SearchByID(::grpc::ServerContext* context, const ::milvus::grpc::SearchByIDParam* request, - ::milvus::grpc::TopKQueryResult* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - // step 1: partition tags - std::vector partitions; - std::copy(request->partition_tag_array().begin(), request->partition_tag_array().end(), - std::back_inserter(partitions)); - - // step 2: partition tags - std::vector id_array; - for (int i = 0; i < request->id_array_size(); i++) { - id_array.push_back(request->id_array(i)); - } - - // step 3: parse extra parameters - milvus::json json_params; - for (int i = 0; i < request->extra_params_size(); i++) { - const ::milvus::grpc::KeyValuePair& extra = request->extra_params(i); - if (extra.key() == EXTRA_PARAM_KEY) { - json_params = json::parse(extra.value()); - } - } - - // step 4: search vectors - TopKQueryResult result; - Status status = request_handler_.SearchByID(GetContext(context), request->collection_name(), id_array, - request->topk(), json_params, partitions, result); - - // step 5: construct and return result - ConstructResults(result, response); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response->mutable_status(), status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::SearchInFiles(::grpc::ServerContext* context, const ::milvus::grpc::SearchInFilesParam* request, - ::milvus::grpc::TopKQueryResult* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - auto* search_request = &request->search_param(); - - // step 1: copy vector data - engine::VectorsData vectors; - CopyRowRecords(search_request->query_record_array(), google::protobuf::RepeatedField(), - vectors); - - // step 2: copy file id array - std::vector file_ids; - std::copy(request->file_id_array().begin(), request->file_id_array().end(), std::back_inserter(file_ids)); - - // step 3: partition tags - std::vector partitions; - std::copy(search_request->partition_tag_array().begin(), search_request->partition_tag_array().end(), - std::back_inserter(partitions)); - - // step 4: parse extra parameters - milvus::json json_params; - for (int i = 0; i < search_request->extra_params_size(); i++) { - const ::milvus::grpc::KeyValuePair& extra = search_request->extra_params(i); - if (extra.key() == EXTRA_PARAM_KEY) { - json_params = json::parse(extra.value()); - } - } - - // step 5: search vectors - TopKQueryResult result; - Status status = request_handler_.Search(GetContext(context), search_request->collection_name(), vectors, - search_request->topk(), json_params, partitions, file_ids, result); - - // step 6: construct and return result - ConstructResults(result, response); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response->mutable_status(), status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::DescribeCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::CollectionSchema* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - CollectionSchema collection_schema; - Status status = - request_handler_.DescribeCollection(GetContext(context), request->collection_name(), collection_schema); - response->set_collection_name(collection_schema.collection_name_); - response->set_dimension(collection_schema.dimension_); - response->set_index_file_size(collection_schema.index_file_size_); - response->set_metric_type(collection_schema.metric_type_); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response->mutable_status(), status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::CountCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::CollectionRowCount* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - int64_t row_count = 0; - Status status = request_handler_.CountCollection(GetContext(context), request->collection_name(), row_count); - response->set_collection_row_count(row_count); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response->mutable_status(), status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::ShowCollections(::grpc::ServerContext* context, const ::milvus::grpc::Command* request, - ::milvus::grpc::CollectionNameList* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - std::vector collections; - Status status = request_handler_.ShowCollections(GetContext(context), collections); - for (auto& collection : collections) { - response->add_collection_names(collection); - } - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response->mutable_status(), status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::ShowCollectionInfo(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::CollectionInfo* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - std::string collection_info; - Status status = - request_handler_.ShowCollectionInfo(GetContext(context), request->collection_name(), collection_info); - response->set_json_info(collection_info); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response->mutable_status(), status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::Cmd(::grpc::ServerContext* context, const ::milvus::grpc::Command* request, - ::milvus::grpc::StringReply* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - std::string reply; - Status status; - - std::string cmd = request->cmd(); - std::vector requests; - if (cmd == "requests") { - std::lock_guard lock(context_map_mutex_); - for (auto& iter : context_map_) { - if (nullptr == iter.second) { - continue; - } - if (iter.second->RequestID() == get_request_id(context)) { - continue; - } - auto request_str = RequestMap(iter.second->GetRequestType()) + "-" + iter.second->RequestID(); - requests.emplace_back(request_str); - } - nlohmann::json reply_json; - reply_json["requests"] = requests; - reply = reply_json.dump(); - response->set_string_reply(reply); - } else { - status = request_handler_.Cmd(GetContext(context), cmd, reply); - response->set_string_reply(reply); - } - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response->mutable_status(), status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::DeleteByID(::grpc::ServerContext* context, const ::milvus::grpc::DeleteByIDParam* request, - ::milvus::grpc::Status* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - // step 1: prepare id array - std::vector vector_ids; - for (int i = 0; i < request->id_array_size(); i++) { - vector_ids.push_back(request->id_array(i)); - } - - // step 2: delete vector - Status status = request_handler_.DeleteByID(GetContext(context), request->collection_name(), vector_ids); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response, status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::PreloadCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::Status* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - Status status = request_handler_.PreloadCollection(GetContext(context), request->collection_name()); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response, status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::ReloadSegments(::grpc::ServerContext* context, const ::milvus::grpc::ReLoadSegmentsParam* request, - ::milvus::grpc::Status* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - std::vector file_ids; - for (size_t i = 0; i < request->segment_id_array_size(); i++) { - file_ids.push_back(request->segment_id_array(i)); - } - - Status status = request_handler_.ReLoadSegments(GetContext(context), request->collection_name(), file_ids); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response, status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::DescribeIndex(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::IndexParam* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - IndexParam param; - Status status = request_handler_.DescribeIndex(GetContext(context), request->collection_name(), param); - response->set_collection_name(param.collection_name_); - response->set_index_type(param.index_type_); - ::milvus::grpc::KeyValuePair* kv = response->add_extra_params(); - kv->set_key(EXTRA_PARAM_KEY); - kv->set_value(param.extra_params_); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response->mutable_status(), status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::DropIndex(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::Status* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - Status status = request_handler_.DropIndex(GetContext(context), request->collection_name()); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response, status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::CreatePartition(::grpc::ServerContext* context, const ::milvus::grpc::PartitionParam* request, - ::milvus::grpc::Status* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - Status status = request_handler_.CreatePartition(GetContext(context), request->collection_name(), request->tag()); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response, status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::HasPartition(::grpc::ServerContext* context, const ::milvus::grpc::PartitionParam* request, - ::milvus::grpc::BoolReply* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - bool has_collection = false; - - Status status = - request_handler_.HasPartition(GetContext(context), request->collection_name(), request->tag(), has_collection); - response->set_bool_reply(has_collection); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response->mutable_status(), status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::ShowPartitions(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::PartitionList* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - std::vector partitions; - Status status = request_handler_.ShowPartitions(GetContext(context), request->collection_name(), partitions); - for (auto& partition : partitions) { - response->add_partition_tag_array(partition.tag_); - } - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response->mutable_status(), status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::DropPartition(::grpc::ServerContext* context, const ::milvus::grpc::PartitionParam* request, - ::milvus::grpc::Status* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - Status status = request_handler_.DropPartition(GetContext(context), request->collection_name(), request->tag()); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response, status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::Flush(::grpc::ServerContext* context, const ::milvus::grpc::FlushParam* request, - ::milvus::grpc::Status* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - std::vector collection_names; - for (int32_t i = 0; i < request->collection_name_array().size(); i++) { - collection_names.push_back(request->collection_name_array(i)); - } - Status status = request_handler_.Flush(GetContext(context), collection_names); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response, status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::Compact(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::Status* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - double compact_threshold = 0.1; // compact trigger threshold: delete_counts/segment_counts - Status status = request_handler_.Compact(GetContext(context), request->collection_name(), compact_threshold); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response, status, context); - - return ::grpc::Status::OK; -} - -/*******************************************New Interface*********************************************/ - -::grpc::Status -GrpcRequestHandler::CreateHybridCollection(::grpc::ServerContext* context, const ::milvus::grpc::Mapping* request, - ::milvus::grpc::Status* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - std::vector> field_types; - std::vector> vector_dimensions; - std::vector> field_params; - for (int i = 0; i < request->fields_size(); ++i) { - if (request->fields(i).type().has_vector_param()) { - auto vector_dimension = - std::make_pair(request->fields(i).name(), request->fields(i).type().vector_param().dimension()); - vector_dimensions.emplace_back(vector_dimension); - } else { - auto type = std::make_pair(request->fields(i).name(), - (engine::meta::hybrid::DataType)request->fields(i).type().data_type()); - field_types.emplace_back(type); - } - // Currently only one extra_param - if (request->fields(i).extra_params_size() != 0) { - auto extra_params = std::make_pair(request->fields(i).name(), request->fields(i).extra_params(0).value()); - field_params.emplace_back(extra_params); - } else { - auto extra_params = std::make_pair(request->fields(i).name(), ""); - field_params.emplace_back(extra_params); - } - } - - Status status = request_handler_.CreateHybridCollection(GetContext(context), request->collection_name(), - field_types, vector_dimensions, field_params); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response, status, context); - - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::DescribeHybridCollection(::grpc::ServerContext* context, - const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::Mapping* response) { - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - std::unordered_map field_types; - Status status = - request_handler_.DescribeHybridCollection(GetContext(context), request->collection_name(), field_types); - - response->mutable_status()->set_error_code((milvus::grpc::ErrorCode)status.code()); - response->mutable_status()->set_reason(status.message()); - response->set_collection_name(request->collection_name()); - auto field_it = field_types.begin(); - for (; field_it != field_types.end(); field_it++) { - auto field = response->add_fields(); - field->set_name(field_it->first); - field->mutable_type()->set_data_type((milvus::grpc::DataType)field_it->second); - } - - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - return ::grpc::Status::OK; -} - -::grpc::Status -GrpcRequestHandler::InsertEntity(::grpc::ServerContext* context, const ::milvus::grpc::HInsertParam* request, - ::milvus::grpc::HEntityIDs* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - auto attr_size = request->entities().attr_records().size(); - std::vector attr_values(attr_size, 0); - std::unordered_map vector_datas; - - memcpy(attr_values.data(), request->entities().attr_records().data(), attr_size); - - uint64_t row_num = request->entities().row_num(); - - std::vector field_names; - auto field_size = request->entities().field_names_size(); - field_names.resize(field_size - 1); - for (int i = 0; i < field_size - 1; ++i) { - field_names[i] = request->entities().field_names(i); - } - - auto vector_size = request->entities().result_values_size(); - for (int i = 0; i < vector_size; ++i) { - engine::VectorsData vectors; - CopyRowRecords(request->entities().result_values(i).vector_value().value(), request->entity_id_array(), - vectors); - vector_datas.insert(std::make_pair(request->entities().field_names(field_size - 1), vectors)); - } - - std::string collection_name = request->collection_name(); - std::string partition_tag = request->partition_tag(); - Status status = request_handler_.InsertEntity(GetContext(context), collection_name, partition_tag, row_num, - field_names, attr_values, vector_datas); - - response->mutable_entity_id_array()->Resize(static_cast(vector_datas.begin()->second.id_array_.size()), 0); - memcpy(response->mutable_entity_id_array()->mutable_data(), vector_datas.begin()->second.id_array_.data(), - vector_datas.begin()->second.id_array_.size() * sizeof(int64_t)); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response->mutable_status(), status, context); - - return ::grpc::Status::OK; -} - -void -DeSerialization(const ::milvus::grpc::GeneralQuery& general_query, query::BooleanQueryPtr& boolean_clause) { - if (general_query.has_boolean_query()) { - boolean_clause->SetOccur((query::Occur)general_query.boolean_query().occur()); - for (int i = 0; i < general_query.boolean_query().general_query_size(); ++i) { - if (general_query.boolean_query().general_query(i).has_boolean_query()) { - query::BooleanQueryPtr query = std::make_shared(); - DeSerialization(general_query.boolean_query().general_query(i), query); - boolean_clause->AddBooleanQuery(query); - } else { - auto leaf_query = std::make_shared(); - auto query = general_query.boolean_query().general_query(i); - if (query.has_term_query()) { - query::TermQueryPtr term_query = std::make_shared(); - term_query->field_name = query.term_query().field_name(); - term_query->boost = query.term_query().boost(); - auto size = query.term_query().values().size(); - term_query->field_value.resize(size); - memcpy(term_query->field_value.data(), query.term_query().values().data(), size); - leaf_query->term_query = term_query; - boolean_clause->AddLeafQuery(leaf_query); - } - if (query.has_range_query()) { - query::RangeQueryPtr range_query = std::make_shared(); - range_query->field_name = query.range_query().field_name(); - range_query->boost = query.range_query().boost(); - range_query->compare_expr.resize(query.range_query().operand_size()); - for (int j = 0; j < query.range_query().operand_size(); ++j) { - range_query->compare_expr[j].compare_operator = - query::CompareOperator(query.range_query().operand(j).operator_()); - range_query->compare_expr[j].operand = query.range_query().operand(j).operand(); - } - leaf_query->range_query = range_query; - boolean_clause->AddLeafQuery(leaf_query); - } - if (query.has_vector_query()) { - query::VectorQueryPtr vector_query = std::make_shared(); - - engine::VectorsData vectors; - CopyRowRecords(query.vector_query().records(), - google::protobuf::RepeatedField(), vectors); - - vector_query->query_vector.float_data = vectors.float_data_; - vector_query->query_vector.binary_data = vectors.binary_data_; - - vector_query->boost = query.vector_query().query_boost(); - vector_query->field_name = query.vector_query().field_name(); - vector_query->topk = query.vector_query().topk(); - - milvus::json json_params; - for (int j = 0; j < query.vector_query().extra_params_size(); j++) { - const ::milvus::grpc::KeyValuePair& extra = query.vector_query().extra_params(j); - if (extra.key() == EXTRA_PARAM_KEY) { - json_params = json::parse(extra.value()); - } - } - vector_query->extra_params = json_params; - leaf_query->vector_query = vector_query; - boolean_clause->AddLeafQuery(leaf_query); - } - } - } - } -} - -::grpc::Status -GrpcRequestHandler::HybridSearch(::grpc::ServerContext* context, const ::milvus::grpc::HSearchParam* request, - ::milvus::grpc::TopKQueryResult* response) { - CHECK_NULLPTR_RETURN(request); - LOG_SERVER_INFO_ << LogOut("Request [%s] %s begin.", GetContext(context)->RequestID().c_str(), __func__); - - context::HybridSearchContextPtr hybrid_search_context = std::make_shared(); - - query::BooleanQueryPtr boolean_query = std::make_shared(); - DeSerialization(request->general_query(), boolean_query); - - query::GeneralQueryPtr general_query = std::make_shared(); - query::GenBinaryQuery(boolean_query, general_query->bin); - - Status status; - - if (!query::ValidateBinaryQuery(general_query->bin)) { - status = Status{SERVER_INVALID_BINARY_QUERY, "Generate wrong binary query tree"}; - SET_RESPONSE(response->mutable_status(), status, context); - return ::grpc::Status::OK; - } - - hybrid_search_context->general_query_ = general_query; - - std::vector partition_list; - partition_list.resize(request->partition_tag_array_size()); - for (int i = 0; i < request->partition_tag_array_size(); ++i) { - partition_list[i] = request->partition_tag_array(i); - } - - TopKQueryResult result; - - status = request_handler_.HybridSearch(GetContext(context), hybrid_search_context, request->collection_name(), - partition_list, general_query, result); - - // step 6: construct and return result - ConstructResults(result, response); - - LOG_SERVER_INFO_ << LogOut("Request [%s] %s end.", GetContext(context)->RequestID().c_str(), __func__); - SET_RESPONSE(response->mutable_status(), status, context); - - return ::grpc::Status::OK; -} - -} // namespace grpc -} // namespace server -} // namespace milvus diff --git a/core/src/server/grpc_impl/GrpcRequestHandler.h b/core/src/server/grpc_impl/GrpcRequestHandler.h deleted file mode 100644 index 2b8364d73c..0000000000 --- a/core/src/server/grpc_impl/GrpcRequestHandler.h +++ /dev/null @@ -1,413 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -#include -#include -#include -#include -#include -#include - -#include "grpc/gen-milvus/milvus.grpc.pb.h" -#include "grpc/gen-status/status.pb.h" -#include "opentracing/tracer.h" -#include "server/context/Context.h" -#include "server/delivery/RequestHandler.h" -#include "server/grpc_impl/interceptor/GrpcInterceptorHookHandler.h" -#include "src/utils/Status.h" - -namespace milvus { -namespace server { -namespace grpc { - -#define CHECK_NULLPTR_RETURN(PTR) \ - if (nullptr == request) { \ - return ::grpc::Status::OK; \ - } - -#define SET_TRACING_TAG(STATUS, SERVER_CONTEXT) \ - if ((STATUS).code() != ::milvus::grpc::ErrorCode::SUCCESS) { \ - GetContext((SERVER_CONTEXT))->GetTraceContext()->GetSpan()->SetTag("error", true); \ - GetContext((SERVER_CONTEXT))->GetTraceContext()->GetSpan()->SetTag("error_message", (STATUS).message()); \ - } - -#define SET_RESPONSE(RESPONSE, STATUS, SERVER_CONTEXT) \ - do { \ - if ((STATUS).ok()) { \ - (RESPONSE)->set_error_code(::milvus::grpc::ErrorCode::SUCCESS); \ - } else { \ - (RESPONSE)->set_error_code(ErrorMap((STATUS).code())); \ - } \ - (RESPONSE)->set_reason((STATUS).message()); \ - SET_TRACING_TAG(STATUS, SERVER_CONTEXT); \ - } while (false); - -::milvus::grpc::ErrorCode -ErrorMap(ErrorCode code); - -extern const char* EXTRA_PARAM_KEY; - -class GrpcRequestHandler final : public ::milvus::grpc::MilvusService::Service, public GrpcInterceptorHookHandler { - public: - explicit GrpcRequestHandler(const std::shared_ptr& tracer); - - void - OnPostRecvInitialMetaData(::grpc::experimental::ServerRpcInfo* server_rpc_info, - ::grpc::experimental::InterceptorBatchMethods* interceptor_batch_methods) override; - - void - OnPreSendMessage(::grpc::experimental::ServerRpcInfo* server_rpc_info, - ::grpc::experimental::InterceptorBatchMethods* interceptor_batch_methods) override; - - std::shared_ptr - GetContext(::grpc::ServerContext* server_context); - - void - SetContext(::grpc::ServerContext* server_context, const std::shared_ptr& context); - - uint64_t - random_id() const; - - // * - // @brief This method is used to create collection - // - // @param CollectionSchema, use to provide collection information to be created. - // - // @return Status - ::grpc::Status - CreateCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionSchema* request, - ::milvus::grpc::Status* response) override; - // * - // @brief This method is used to test collection existence. - // - // @param CollectionName, collection name is going to be tested. - // - // @return BoolReply - ::grpc::Status - HasCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::BoolReply* response) override; - // * - // @brief This method is used to get collection schema. - // - // @param CollectionName, target collection name. - // - // @return CollectionSchema - ::grpc::Status - DescribeCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::CollectionSchema* response) override; - // * - // @brief This method is used to get collection schema. - // - // @param CollectionName, target collection name. - // - // @return CollectionRowCount - ::grpc::Status - CountCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::CollectionRowCount* response) override; - // * - // @brief This method is used to list all collections. - // - // @param Command, dummy parameter. - // - // @return CollectionNameList - ::grpc::Status - ShowCollections(::grpc::ServerContext* context, const ::milvus::grpc::Command* request, - ::milvus::grpc::CollectionNameList* response) override; - // * - // @brief This method is used to get collection detail information. - // - // @param CollectionName, target collection name. - // - // @return CollectionInfo - ::grpc::Status - ShowCollectionInfo(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::CollectionInfo* response); - - // * - // @brief This method is used to delete collection. - // - // @param CollectionName, collection name is going to be deleted. - // - // @return CollectionNameList - ::grpc::Status - DropCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::Status* response) override; - // * - // @brief This method is used to build index by collection in sync mode. - // - // @param IndexParam, index paramters. - // - // @return Status - ::grpc::Status - CreateIndex(::grpc::ServerContext* context, const ::milvus::grpc::IndexParam* request, - ::milvus::grpc::Status* response) override; - // * - // @brief This method is used to describe index - // - // @param CollectionName, target collection name. - // - // @return IndexParam - ::grpc::Status - DescribeIndex(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::IndexParam* response) override; - // * - // @brief This method is used to drop index - // - // @param CollectionName, target collection name. - // - // @return Status - ::grpc::Status - DropIndex(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::Status* response) override; - // * - // @brief This method is used to create partition - // - // @param PartitionParam, partition parameters. - // - // @return Status - ::grpc::Status - CreatePartition(::grpc::ServerContext* context, const ::milvus::grpc::PartitionParam* request, - ::milvus::grpc::Status* response) override; - - // * - // @brief This method is used to test partition existence. - // - // @param PartitionParam, target partition. - // - // @return BoolReply - ::grpc::Status - HasPartition(::grpc::ServerContext* context, const ::milvus::grpc::PartitionParam* request, - ::milvus::grpc::BoolReply* response); - - // * - // @brief This method is used to show partition information - // - // @param CollectionName, target collection name. - // - // @return PartitionList - ::grpc::Status - ShowPartitions(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::PartitionList* response) override; - // * - // @brief This method is used to drop partition - // - // @param PartitionName, target partition name. - // - // @return Status - ::grpc::Status - DropPartition(::grpc::ServerContext* context, const ::milvus::grpc::PartitionParam* request, - ::milvus::grpc::Status* response) override; - // * - // @brief This method is used to add vector array to collection. - // - // @param InsertParam, insert parameters. - // - // @return VectorIds - ::grpc::Status - Insert(::grpc::ServerContext* context, const ::milvus::grpc::InsertParam* request, - ::milvus::grpc::VectorIds* response) override; - // * - // @brief This method is used to get vectors data by id array. - // - // @param VectorsIdentity, target vector id array. - // - // @return VectorsData - ::grpc::Status - GetVectorsByID(::grpc::ServerContext* context, const ::milvus::grpc::VectorsIdentity* request, - ::milvus::grpc::VectorsData* response); - - // * - // @brief This method is used to get vector ids from a segment - // - // @param GetVectorIDsParam, target collection and segment - // - // @return VectorIds - ::grpc::Status - GetVectorIDs(::grpc::ServerContext* context, const ::milvus::grpc::GetVectorIDsParam* request, - ::milvus::grpc::VectorIds* response); - // * - // @brief This method is used to query vector in collection. - // - // @param SearchParam, search parameters. - // - // @return TopKQueryResultList - ::grpc::Status - Search(::grpc::ServerContext* context, const ::milvus::grpc::SearchParam* request, - ::milvus::grpc::TopKQueryResult* response) override; - - // * - // @brief This method is used to query vector by id. - // - // @param SearchByIDParam, search parameters. - // - // @return TopKQueryResult - ::grpc::Status - SearchByID(::grpc::ServerContext* context, const ::milvus::grpc::SearchByIDParam* request, - ::milvus::grpc::TopKQueryResult* response); - - // * - // @brief This method is used to query vector in specified files. - // - // @param SearchInFilesParam, search in files paremeters. - // - // @return TopKQueryResultList - ::grpc::Status - SearchInFiles(::grpc::ServerContext* context, const ::milvus::grpc::SearchInFilesParam* request, - ::milvus::grpc::TopKQueryResult* response) override; - - // * - // @brief This method is used to give the server status. - // - // @param Command, command string - // - // @return StringReply - ::grpc::Status - Cmd(::grpc::ServerContext* context, const ::milvus::grpc::Command* request, - ::milvus::grpc::StringReply* response) override; - - // * - // @brief This method is used to delete vector by id - // - // @param DeleteByIDParam, delete parameters. - // - // @return status - ::grpc::Status - DeleteByID(::grpc::ServerContext* context, const ::milvus::grpc::DeleteByIDParam* request, - ::milvus::grpc::Status* response); - - // * - // @brief This method is used to preload collection - // - // @param CollectionName, target collection name. - // - // @return Status - ::grpc::Status - PreloadCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::Status* response) override; - - // * - ::grpc::Status - ReloadSegments(::grpc::ServerContext* context, const ::milvus::grpc::ReLoadSegmentsParam* request, - ::milvus::grpc::Status* response) override; - - // * - // @brief This method is used to flush buffer into storage. - // - // @param FlushParam, flush parameters - // - // @return Status - ::grpc::Status - Flush(::grpc::ServerContext* context, const ::milvus::grpc::FlushParam* request, ::milvus::grpc::Status* response); - - // * - // @brief This method is used to compact collection - // - // @param CollectionName, target collection name. - // - // @return Status - ::grpc::Status - Compact(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::Status* response); - - /*******************************************New Interface*********************************************/ - - ::grpc::Status - CreateHybridCollection(::grpc::ServerContext* context, const ::milvus::grpc::Mapping* request, - ::milvus::grpc::Status* response) override; - - // ::grpc::Status - // HasCollection(::grpc::ServerContext* context, - // const ::milvus::grpc::CollectionName* request, - // ::milvus::grpc::BoolReply* response) override; - // - // ::grpc::Status - // DropCollection(::grpc::ServerContext* context, - // const ::milvus::grpc::CollectionName* request, - // ::milvus::grpc::Status* response) override; - // - ::grpc::Status - DescribeHybridCollection(::grpc::ServerContext* context, const ::milvus::grpc::CollectionName* request, - ::milvus::grpc::Mapping* response) override; - // - // ::grpc::Status - // CountCollection(::grpc::ServerContext* context, - // const ::milvus::grpc::CollectionName* request, - // ::milvus::grpc::CollectionRowCount* response) override; - // - // ::grpc::Status - // ShowCollections(::grpc::ServerContext* context, - // const ::milvus::grpc::Command* request, - // ::milvus::grpc::MappingList* response) override; - // - // ::grpc::Status - // ShowCollectionInfo(::grpc::ServerContext* context, - // const ::milvus::grpc::CollectionName* request, - // ::milvus::grpc::CollectionInfo* response) override; - // - // ::grpc::Status - // PreloadCollection(::grpc::ServerContext* context, - // const ::milvus::grpc::CollectionName* request, - // ::milvus::grpc::Status* response) override; - // - ::grpc::Status - InsertEntity(::grpc::ServerContext* context, const ::milvus::grpc::HInsertParam* request, - ::milvus::grpc::HEntityIDs* response) override; - - ::grpc::Status - HybridSearch(::grpc::ServerContext* context, const ::milvus::grpc::HSearchParam* request, - ::milvus::grpc::TopKQueryResult* response) override; - // - // ::grpc::Status - // HybridSearchInSegments(::grpc::ServerContext* context, - // const ::milvus:: - // grpc::HSearchInSegmentsParam* request, - // ::milvus::grpc::HQueryResult* response) override; - // - // ::grpc::Status - // GetEntityByID(::grpc::ServerContext* context, - // const ::milvus::grpc::HEntityIdentity* request, - // ::milvus::grpc::HEntity* response) override; - // - // ::grpc::Status - // GetEntityIDs(::grpc::ServerContext* context, - // const ::milvus::grpc::HGetEntityIDsParam* request, - // ::milvus::grpc::HEntityIDs* response) override; - // - // ::grpc::Status - // DeleteEntitiesByID(::grpc::ServerContext* context, - // const ::milvus::grpc::HDeleteByIDParam* request, - // ::milvus::grpc::Status* response) override; - - void - RegisterRequestHandler(const RequestHandler& handler) { - request_handler_ = handler; - } - - private: - RequestHandler request_handler_; - - // std::unordered_map<::grpc::ServerContext*, std::shared_ptr> context_map_; - std::unordered_map> context_map_; - std::shared_ptr tracer_; - // std::unordered_map<::grpc::ServerContext*, std::unique_ptr> span_map_; - - mutable std::mt19937_64 random_num_generator_; - mutable std::mutex random_mutex_; - mutable std::mutex context_map_mutex_; -}; - -} // namespace grpc -} // namespace server -} // namespace milvus diff --git a/core/src/server/grpc_impl/GrpcServer.cpp b/core/src/server/grpc_impl/GrpcServer.cpp deleted file mode 100644 index 8717739e5c..0000000000 --- a/core/src/server/grpc_impl/GrpcServer.cpp +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/grpc_impl/GrpcServer.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "GrpcRequestHandler.h" -#include "config/Config.h" -#include "grpc/gen-milvus/milvus.grpc.pb.h" -#include "server/DBWrapper.h" -#include "server/grpc_impl/interceptor/SpanInterceptor.h" -#include "utils/Log.h" - -namespace milvus { -namespace server { -namespace grpc { - -constexpr int64_t MESSAGE_SIZE = -1; - -// this class is to check port occupation during server start -class NoReusePortOption : public ::grpc::ServerBuilderOption { - public: - void - UpdateArguments(::grpc::ChannelArguments* args) override { - args->SetInt(GRPC_ARG_ALLOW_REUSEPORT, 0); - int grpc_concurrency = 4 * std::thread::hardware_concurrency(); - grpc_concurrency = std::max(32, grpc_concurrency); - grpc_concurrency = std::min(256, grpc_concurrency); - args->SetInt(GRPC_ARG_MAX_CONCURRENT_STREAMS, grpc_concurrency); - } - - void - UpdatePlugins(std::vector>* plugins) override { - } -}; - -void -GrpcServer::Start() { - thread_ptr_ = std::make_shared(&GrpcServer::StartService, this); -} - -void -GrpcServer::Stop() { - StopService(); - if (thread_ptr_) { - thread_ptr_->join(); - thread_ptr_ = nullptr; - } -} - -Status -GrpcServer::StartService() { - SetThreadName("grpcserv_thread"); - Config& config = Config::GetInstance(); - std::string address, port; - - STATUS_CHECK(config.GetNetworkConfigBindAddress(address)); - STATUS_CHECK(config.GetNetworkConfigBindPort(port)); - - std::string server_address(address + ":" + port); - - ::grpc::ServerBuilder builder; - builder.SetOption(std::unique_ptr<::grpc::ServerBuilderOption>(new NoReusePortOption)); - builder.SetMaxReceiveMessageSize(MESSAGE_SIZE); // default 4 * 1024 * 1024 - builder.SetMaxSendMessageSize(MESSAGE_SIZE); - - builder.SetCompressionAlgorithmSupportStatus(GRPC_COMPRESS_STREAM_GZIP, true); - builder.SetDefaultCompressionAlgorithm(GRPC_COMPRESS_STREAM_GZIP); - builder.SetDefaultCompressionLevel(GRPC_COMPRESS_LEVEL_NONE); - - GrpcRequestHandler service(opentracing::Tracer::Global()); - service.RegisterRequestHandler(RequestHandler()); - - builder.AddListeningPort(server_address, ::grpc::InsecureServerCredentials()); - builder.RegisterService(&service); - - // Add gRPC interceptor - using InterceptorI = ::grpc::experimental::ServerInterceptorFactoryInterface; - using InterceptorIPtr = std::unique_ptr; - std::vector creators; - - creators.push_back( - std::unique_ptr<::grpc::experimental::ServerInterceptorFactoryInterface>(new SpanInterceptorFactory(&service))); - - builder.experimental().SetInterceptorCreators(std::move(creators)); - - server_ptr_ = builder.BuildAndStart(); - server_ptr_->Wait(); - - return Status::OK(); -} - -Status -GrpcServer::StopService() { - if (server_ptr_ != nullptr) { - server_ptr_->Shutdown(); - } - - return Status::OK(); -} - -} // namespace grpc -} // namespace server -} // namespace milvus diff --git a/core/src/server/grpc_impl/GrpcServer.h b/core/src/server/grpc_impl/GrpcServer.h deleted file mode 100644 index 99968ad9f5..0000000000 --- a/core/src/server/grpc_impl/GrpcServer.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "utils/Status.h" - -#include -#include -#include -#include -#include - -namespace milvus { -namespace server { -namespace grpc { - -class GrpcServer { - public: - static GrpcServer& - GetInstance() { - static GrpcServer grpc_server; - return grpc_server; - } - - void - Start(); - void - Stop(); - - private: - GrpcServer() = default; - ~GrpcServer() = default; - - Status - StartService(); - Status - StopService(); - - private: - std::unique_ptr<::grpc::Server> server_ptr_; - std::shared_ptr thread_ptr_; -}; - -} // namespace grpc -} // namespace server -} // namespace milvus diff --git a/core/src/server/grpc_impl/interceptor/GrpcInterceptorHookHandler.cpp b/core/src/server/grpc_impl/interceptor/GrpcInterceptorHookHandler.cpp deleted file mode 100644 index fa2d29341c..0000000000 --- a/core/src/server/grpc_impl/interceptor/GrpcInterceptorHookHandler.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/grpc_impl/interceptor/GrpcInterceptorHookHandler.h" - -namespace milvus { -namespace server { -namespace grpc { - -void -GrpcInterceptorHookHandler::OnPostRecvInitialMetaData( - ::grpc::experimental::ServerRpcInfo* server_rpc_info, - ::grpc::experimental::InterceptorBatchMethods* interceptor_batch_methods) { -} - -void -GrpcInterceptorHookHandler::OnPreSendMessage(::grpc::experimental::ServerRpcInfo* server_rpc_info, - ::grpc::experimental::InterceptorBatchMethods* interceptor_batch_methods) { -} - -} // namespace grpc -} // namespace server -} // namespace milvus diff --git a/core/src/server/grpc_impl/interceptor/GrpcInterceptorHookHandler.h b/core/src/server/grpc_impl/interceptor/GrpcInterceptorHookHandler.h deleted file mode 100644 index c9618a5e0e..0000000000 --- a/core/src/server/grpc_impl/interceptor/GrpcInterceptorHookHandler.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -namespace milvus { -namespace server { -namespace grpc { - -class GrpcInterceptorHookHandler { - public: - virtual void - OnPostRecvInitialMetaData(::grpc::experimental::ServerRpcInfo* server_rpc_info, - ::grpc::experimental::InterceptorBatchMethods* interceptor_batch_methods); - - virtual void - OnPreSendMessage(::grpc::experimental::ServerRpcInfo* server_rpc_info, - ::grpc::experimental::InterceptorBatchMethods* interceptor_batch_methods); -}; - -} // namespace grpc -} // namespace server -} // namespace milvus diff --git a/core/src/server/grpc_impl/interceptor/SpanInterceptor.cpp b/core/src/server/grpc_impl/interceptor/SpanInterceptor.cpp deleted file mode 100644 index 2515fdd390..0000000000 --- a/core/src/server/grpc_impl/interceptor/SpanInterceptor.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/grpc_impl/interceptor/SpanInterceptor.h" -#include "tracing/TracerUtil.h" - -namespace milvus { -namespace server { -namespace grpc { - -SpanInterceptor::SpanInterceptor(::grpc::experimental::ServerRpcInfo* info, GrpcInterceptorHookHandler* hook_handler) - : info_(info), hook_handler_(hook_handler) { -} - -void -SpanInterceptor::Intercept(::grpc::experimental::InterceptorBatchMethods* methods) { - if (methods->QueryInterceptionHookPoint(::grpc::experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA)) { - hook_handler_->OnPostRecvInitialMetaData(info_, methods); - - } else if (methods->QueryInterceptionHookPoint(::grpc::experimental::InterceptionHookPoints::PRE_SEND_MESSAGE)) { - hook_handler_->OnPreSendMessage(info_, methods); - } - - methods->Proceed(); -} - -::grpc::experimental::Interceptor* -SpanInterceptorFactory::CreateServerInterceptor(::grpc::experimental::ServerRpcInfo* info) { - return new SpanInterceptor(info, hook_handler_); -} - -} // namespace grpc -} // namespace server -} // namespace milvus diff --git a/core/src/server/grpc_impl/interceptor/SpanInterceptor.h b/core/src/server/grpc_impl/interceptor/SpanInterceptor.h deleted file mode 100644 index 1b241953e3..0000000000 --- a/core/src/server/grpc_impl/interceptor/SpanInterceptor.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include - -#include "GrpcInterceptorHookHandler.h" - -namespace milvus { -namespace server { -namespace grpc { - -class SpanInterceptor : public ::grpc::experimental::Interceptor { - public: - SpanInterceptor(::grpc::experimental::ServerRpcInfo* info, GrpcInterceptorHookHandler* hook_handler); - - void - Intercept(::grpc::experimental::InterceptorBatchMethods* methods) override; - - private: - ::grpc::experimental::ServerRpcInfo* info_; - GrpcInterceptorHookHandler* hook_handler_; - // std::shared_ptr tracer_; - // std::unique_ptr span_; -}; - -class SpanInterceptorFactory : public ::grpc::experimental::ServerInterceptorFactoryInterface { - public: - explicit SpanInterceptorFactory(GrpcInterceptorHookHandler* hook_handler) : hook_handler_(hook_handler) { - } - - ::grpc::experimental::Interceptor* - CreateServerInterceptor(::grpc::experimental::ServerRpcInfo* info) override; - - private: - GrpcInterceptorHookHandler* hook_handler_; -}; - -} // namespace grpc -} // namespace server -} // namespace milvus diff --git a/core/src/server/init/CpuChecker.cpp b/core/src/server/init/CpuChecker.cpp deleted file mode 100644 index 0f55059c9e..0000000000 --- a/core/src/server/init/CpuChecker.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/init/CpuChecker.h" - -#include -#include -#include - -#include - -#include "faiss/FaissHook.h" -#include "faiss/utils/instruction_set.h" -#include "utils/Log.h" -#include "utils/StringHelpFunctions.h" - -namespace milvus { -namespace server { - -Status -CpuChecker::CheckCpuInstructionSet() { - std::vector instruction_sets; - - bool support_avx512 = faiss::support_avx512(); - fiu_do_on("CpuChecker.CheckCpuInstructionSet.not_support_avx512", support_avx512 = false); - if (support_avx512) { - instruction_sets.emplace_back("avx512"); - } - - bool support_axv2 = faiss::support_avx2(); - fiu_do_on("CpuChecker.CheckCpuInstructionSet.not_support_avx2", support_axv2 = false); - if (support_axv2) { - instruction_sets.emplace_back("avx2"); - } - - bool support_sse4_2 = faiss::support_sse(); - fiu_do_on("CpuChecker.CheckCpuInstructionSet.not_support_sse4_2", support_sse4_2 = false); - if (support_sse4_2) { - instruction_sets.emplace_back("sse4_2"); - } - - fiu_do_on("CpuChecker.CheckCpuInstructionSet.instruction_sets_empty", instruction_sets.clear()); - if (instruction_sets.empty()) { - std::string msg = - "CPU instruction sets are not supported. Ensure the CPU supports at least one of the following instruction " - "sets: sse4_2, avx2, avx512"; - LOG_SERVER_FATAL_ << msg; - std::cerr << msg << std::endl; - return Status(SERVER_UNEXPECTED_ERROR, msg); - } - - std::string instruction_sets_msg; - StringHelpFunctions::MergeStringWithDelimeter(instruction_sets, ", ", instruction_sets_msg); - std::string msg = "Supported CPU instruction sets: " + instruction_sets_msg; - LOG_SERVER_INFO_ << msg; - LOG_ENGINE_DEBUG_ << msg; - std::cout << msg << std::endl; - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/init/CpuChecker.h b/core/src/server/init/CpuChecker.h deleted file mode 100644 index f459f63740..0000000000 --- a/core/src/server/init/CpuChecker.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "utils/Status.h" - -namespace milvus { -namespace server { - -class CpuChecker { - public: - static Status - CheckCpuInstructionSet(); -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/init/GpuChecker.cpp b/core/src/server/init/GpuChecker.cpp deleted file mode 100644 index 15e0cbe1c7..0000000000 --- a/core/src/server/init/GpuChecker.cpp +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#ifdef MILVUS_GPU_VERSION -#include "server/init/GpuChecker.h" - -#include -#include -#include - -#include - -#include "config/Config.h" -#include "utils/Log.h" - -namespace milvus { -namespace server { - -namespace { -std::string -ConvertCudaVersion(int version) { - return std::to_string(version / 1000) + "." + std::to_string((version % 100) / 10); -} -} // namespace - -const int CUDA_MIN_VERSION = 10000; // 10.0 -const float GPU_MIN_COMPUTE_CAPACITY = 6.0; -const char* NVIDIA_MIN_DRIVER_VERSION = "418.00"; - -std::string -GpuChecker::NvmlErrorString(nvmlReturn_t error_no) { - return "code: " + std::to_string(error_no) + ", message: " + nvmlErrorString(error_no); -} - -std::string -GpuChecker::CudaErrorString(cudaError_t error_no) { - return "code: " + std::to_string(error_no) + ", message: " + cudaGetErrorString(error_no); -} - -Status -GpuChecker::GetGpuComputeCapacity(nvmlDevice_t device, int& major, int& minor) { - nvmlReturn_t code = nvmlDeviceGetCudaComputeCapability(device, &major, &minor); - if (NVML_SUCCESS != code) { - return Status(SERVER_UNEXPECTED_ERROR, NvmlErrorString(code)); - } - - return Status::OK(); -} - -Status -GpuChecker::GetGpuNvidiaDriverVersion(std::string& version) { - char driver_version[NVML_SYSTEM_DRIVER_VERSION_BUFFER_SIZE]; - memset(driver_version, 0, NVML_SYSTEM_DRIVER_VERSION_BUFFER_SIZE); - auto nvml_code = nvmlSystemGetDriverVersion(driver_version, NVML_SYSTEM_DRIVER_VERSION_BUFFER_SIZE); - if (NVML_SUCCESS != nvml_code) { - return Status(SERVER_UNEXPECTED_ERROR, NvmlErrorString(nvml_code)); - } - - version = std::string(driver_version); - return Status::OK(); -} - -Status -GpuChecker::GetGpuCudaDriverVersion(int& version) { - auto cuda_code = cudaDriverGetVersion(&version); - if (cudaSuccess != cuda_code) { - std::string error_msg = "Check cuda driver version failed. " + CudaErrorString(cuda_code); - return Status(SERVER_UNEXPECTED_ERROR, error_msg); - } - return Status::OK(); -} - -Status -GpuChecker::GetGpuCudaRuntimeVersion(int& version) { - auto cuda_code = cudaRuntimeGetVersion(&version); - if (cudaSuccess != cuda_code) { - std::string error_msg = "Check cuda runtime version failed. " + CudaErrorString(cuda_code); - return Status(SERVER_UNEXPECTED_ERROR, error_msg); - } - return Status::OK(); -} - -Status -GpuChecker::CheckGpuEnvironment() { - std::string err_msg; - - auto& config = Config::GetInstance(); - bool gpu_enable = true; - auto status = config.GetGpuResourceConfigEnable(gpu_enable); - if (!status.ok()) { - err_msg = "Cannot check if GPUs are enable from configuration. " + status.message(); - LOG_SERVER_FATAL_ << err_msg; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - if (!gpu_enable) { - return Status::OK(); - } - - std::vector build_gpus; - status = config.GetGpuResourceConfigBuildIndexResources(build_gpus); - if (!status.ok()) { - err_msg = "Get GPU resources of building index failed. " + status.message(); - LOG_SERVER_FATAL_ << err_msg; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - - std::vector search_gpus; - status = config.GetGpuResourceConfigSearchResources(search_gpus); - if (!status.ok()) { - err_msg = "Get GPU resources of search failed. " + status.message(); - LOG_SERVER_FATAL_ << err_msg; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - - std::set gpu_sets(build_gpus.begin(), build_gpus.end()); - gpu_sets.insert(search_gpus.begin(), search_gpus.end()); - - nvmlReturn_t nvmlresult = nvmlInit(); - fiu_do_on("GpuChecker.CheckGpuEnvironment.nvml_init_fail", nvmlresult = NVML_ERROR_UNKNOWN); - if (NVML_SUCCESS != nvmlresult) { - err_msg = "nvml initialize failed. " + NvmlErrorString(nvmlresult); - LOG_SERVER_FATAL_ << err_msg; - std::cerr << err_msg << std::endl; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - - /* Check nvidia driver version */ - std::string nvidia_version; - status = GetGpuNvidiaDriverVersion(nvidia_version); - fiu_do_on("GpuChecker.CheckGpuEnvironment.get_nvidia_driver_fail", status = Status(SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - err_msg = " Check nvidia driver failed. " + status.message(); - LOG_SERVER_FATAL_ << err_msg; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - - fiu_do_on("GpuChecker.CheckGpuEnvironment.nvidia_driver_too_slow", - nvidia_version = std::to_string(std::stof(NVIDIA_MIN_DRIVER_VERSION) - 1)); - if (nvidia_version.compare(NVIDIA_MIN_DRIVER_VERSION) < 0) { - err_msg = "Nvidia driver version " + std::string(nvidia_version) + " is slower than " + - std::string(NVIDIA_MIN_DRIVER_VERSION); - LOG_SERVER_FATAL_ << err_msg; - std::cerr << err_msg << std::endl; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - - /* Check Cuda version */ - int cuda_driver_version = 0; - status = GetGpuCudaDriverVersion(cuda_driver_version); - fiu_do_on("GpuChecker.CheckGpuEnvironment.cuda_driver_fail", status = Status(SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - err_msg = " Check Cuda driver failed. " + status.message(); - LOG_SERVER_FATAL_ << err_msg; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - fiu_do_on("GpuChecker.CheckGpuEnvironment.cuda_driver_too_slow", cuda_driver_version = CUDA_MIN_VERSION - 1); - if (cuda_driver_version < CUDA_MIN_VERSION) { - err_msg = "Cuda driver version is " + ConvertCudaVersion(cuda_driver_version) + - ", slower than minimum required version " + ConvertCudaVersion(CUDA_MIN_VERSION); - LOG_SERVER_FATAL_ << err_msg; - std::cerr << err_msg << std::endl; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - - int cuda_runtime_version = 0; - status = GetGpuCudaRuntimeVersion(cuda_runtime_version); - fiu_do_on("GpuChecker.CheckGpuEnvironment.cuda_runtime_driver_fail", status = Status(SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - err_msg = " Check Cuda runtime driver failed. " + status.message(); - LOG_SERVER_FATAL_ << err_msg; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - fiu_do_on("GpuChecker.CheckGpuEnvironment.cuda_runtime_driver_too_slow", - cuda_runtime_version = CUDA_MIN_VERSION - 1); - if (cuda_runtime_version < CUDA_MIN_VERSION) { - err_msg = "Cuda runtime version is " + ConvertCudaVersion(cuda_runtime_version) + - ", slow than minimum required version " + ConvertCudaVersion(CUDA_MIN_VERSION); - LOG_SERVER_FATAL_ << err_msg; - std::cerr << err_msg << std::endl; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - - /* Compute capacity */ - uint32_t device_count = 0; - nvmlresult = nvmlDeviceGetCount(&device_count); - fiu_do_on("GpuChecker.CheckGpuEnvironment.nvml_get_device_count_fail", nvmlresult = NVML_ERROR_UNKNOWN); - if (NVML_SUCCESS != nvmlresult) { - err_msg = "Obtain GPU count failed. " + NvmlErrorString(nvmlresult); - LOG_SERVER_FATAL_ << err_msg; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - - fiu_do_on("GpuChecker.CheckGpuEnvironment.nvml_device_count_zero", device_count = 0); - if (device_count == 0) { - err_msg = "GPU count is zero. Make sure there are available GPUs in host machine"; - LOG_SERVER_FATAL_ << err_msg; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - - char device_name[NVML_DEVICE_NAME_BUFFER_SIZE]; - int major, minor; - for (uint32_t i = 0; i < device_count; i++) { - if (gpu_sets.find(i) == gpu_sets.end()) { - continue; - } - - nvmlDevice_t device; - nvmlresult = nvmlDeviceGetHandleByIndex(i, &device); - fiu_do_on("GpuChecker.CheckGpuEnvironment.nvml_get_device_handle_fail", nvmlresult = NVML_ERROR_UNKNOWN); - if (NVML_SUCCESS != nvmlresult) { - err_msg = "Obtain GPU " + std::to_string(i) + " handle failed. " + NvmlErrorString(nvmlresult); - LOG_SERVER_FATAL_ << err_msg; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - memset(device_name, 0, NVML_DEVICE_NAME_BUFFER_SIZE); - nvmlresult = nvmlDeviceGetName(device, device_name, NVML_DEVICE_NAME_BUFFER_SIZE); - fiu_do_on("GpuChecker.CheckGpuEnvironment.nvml_get_device_name_fail", nvmlresult = NVML_ERROR_UNKNOWN); - if (NVML_SUCCESS != nvmlresult) { - err_msg = "Obtain GPU " + std::to_string(i) + " name failed. " + NvmlErrorString(nvmlresult); - LOG_SERVER_FATAL_ << err_msg; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - - major = 0; - minor = 0; - status = GetGpuComputeCapacity(device, major, minor); - fiu_do_on("GpuChecker.CheckGpuEnvironment.device_compute_capacity_fail", - status = Status(SERVER_UNEXPECTED_ERROR, "")); - if (!status.ok()) { - err_msg = "Obtain GPU " + std::to_string(i) + " compute capacity failed. " + status.message(); - LOG_SERVER_FATAL_ << err_msg; - std::cerr << err_msg << std::endl; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - float cc = major + minor / 1.0f; - fiu_do_on("GpuChecker.CheckGpuEnvironment.device_compute_capacity_too_weak", cc = GPU_MIN_COMPUTE_CAPACITY - 1); - if (cc < GPU_MIN_COMPUTE_CAPACITY) { - err_msg = "GPU " + std::to_string(i) + " compute capability " + std::to_string(cc) + - " is too weak. Required least GPU compute capability is " + - std::to_string(GPU_MIN_COMPUTE_CAPACITY); - LOG_SERVER_FATAL_ << err_msg; - std::cerr << err_msg << std::endl; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - - LOG_SERVER_INFO_ << "GPU" << i << ": name=" << device_name << ", compute capacity=" << cc; - } - - nvmlresult = nvmlShutdown(); - fiu_do_on("GpuChecker.CheckGpuEnvironment.nvml_shutdown_fail", nvmlresult = NVML_ERROR_UNKNOWN); - if (NVML_SUCCESS != nvmlresult) { - err_msg = "nvml shutdown handle failed. " + NvmlErrorString(nvmlresult); - LOG_SERVER_FATAL_ << err_msg; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - - std::cout << "Nvidia driver version: " << nvidia_version << "\n" - << "CUDA Driver Version / Runtime Version : " << ConvertCudaVersion(cuda_driver_version) << " / " - << ConvertCudaVersion(cuda_runtime_version) << std::endl; - - return Status::OK(); -} - -} // namespace server -} // namespace milvus -#endif diff --git a/core/src/server/init/GpuChecker.h b/core/src/server/init/GpuChecker.h deleted file mode 100644 index 0c3ec078d3..0000000000 --- a/core/src/server/init/GpuChecker.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#ifdef MILVUS_GPU_VERSION - -#pragma once - -#include - -#include -#include - -#include "utils/Status.h" - -namespace milvus { -namespace server { - -extern const int CUDA_MIN_VERSION; -extern const float GPU_MIN_COMPUTE_CAPACITY; -extern const char* NVIDIA_MIN_DRIVER_VERSION; - -class GpuChecker { - private: - static std::string - NvmlErrorString(nvmlReturn_t error_no); - - static std::string - CudaErrorString(cudaError_t error_no); - - private: - static Status - GetGpuComputeCapacity(nvmlDevice_t device, int& major, int& minor); - - static Status - GetGpuNvidiaDriverVersion(std::string& version); - - static Status - GetGpuCudaDriverVersion(int& version); - - static Status - GetGpuCudaRuntimeVersion(int& version); - - public: - static Status - CheckGpuEnvironment(); -}; - -} // namespace server -} // namespace milvus -#endif diff --git a/core/src/server/init/InstanceLockCheck.cpp b/core/src/server/init/InstanceLockCheck.cpp deleted file mode 100644 index df04e4408d..0000000000 --- a/core/src/server/init/InstanceLockCheck.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/init/InstanceLockCheck.h" - -#include -#include -#include -#include - -#include - -#include "utils/Log.h" - -namespace milvus { -namespace server { - -InstanceLockCheck* -InstanceLockCheck::GetInstance() { - static InstanceLockCheck lk; - return &lk; -} - -Status -InstanceLockCheck::Check(const std::string& path) { - std::string lock_path = path + "/lock"; - auto fd = open(lock_path.c_str(), O_RDWR | O_CREAT | O_NOFOLLOW, 0640); - fiu_do_on("InstanceLockCheck.Check.fd", fd = -1); - if (fd < 0) { - std::string msg; - if (errno == EROFS) { - // Not using locking for read-only lock file - msg += "Lock file is read-only."; - } - msg += "Could not open file: " + lock_path + ", " + strerror(errno); - return Status(SERVER_UNEXPECTED_ERROR, msg); - } - InstanceLockCheck::GetInstance()->lk_path = lock_path; - - // Acquire a write lock - struct flock fl; - // exclusive lock - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - auto fcl = fcntl(fd, F_SETLK, &fl); - fiu_do_on("InstanceLockCheck.Check.fcntl", fcl = -1); - if (fcl == -1) { - std::string msg = "Can't lock file: " + lock_path + ", due to "; - if (errno == EACCES || errno == EAGAIN) { - msg += "permission denied. "; - } else if (errno == ENOLCK) { - // Not using locking for nfs mounted lock file - msg += "using nfs. "; - } else { - msg += std::string(strerror(errno)) + ". "; - } - close(fd); - return Status(SERVER_UNEXPECTED_ERROR, msg); - } - - LOG_SERVER_INFO_ << "InstanceLockCheck passed."; - - return Status::OK(); -} - -void -InstanceLockCheck::Release() { - auto fd = open(InstanceLockCheck::GetInstance()->lk_path.c_str(), O_RDWR | O_CREAT | O_NOFOLLOW, 0640); - if (fd < 0) { - std::string msg; - if (errno == EROFS) { - // Not using locking for read-only lock file - msg += "Lock file is read-only."; - } - msg += "Could not open file for release: " + InstanceLockCheck::GetInstance()->lk_path + ", " + strerror(errno); - LOG_SERVER_ERROR_ << msg; - return; - } - close(fd); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/init/InstanceLockCheck.h b/core/src/server/init/InstanceLockCheck.h deleted file mode 100644 index 8f5cb3d09d..0000000000 --- a/core/src/server/init/InstanceLockCheck.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include "utils/Status.h" - -namespace milvus { -namespace server { - -class InstanceLockCheck { - public: - static Status - Check(const std::string& path); - - static void - Release(); - - static InstanceLockCheck* - GetInstance(); - - public: - std::string lk_path; -}; // InstanceLockCheck - -} // namespace server -} // namespace milvus diff --git a/core/src/server/init/StorageChecker.cpp b/core/src/server/init/StorageChecker.cpp deleted file mode 100644 index 431aba5003..0000000000 --- a/core/src/server/init/StorageChecker.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/init/StorageChecker.h" - -#include - -#include -#include - -#include - -#include "config/Config.h" -#include "utils/Log.h" -#include "utils/StringHelpFunctions.h" - -namespace milvus { -namespace server { - -Status -StorageChecker::CheckStoragePermission() { - auto& config = Config::GetInstance(); - /* Check log file write permission */ - std::string logs_path; - auto status = config.GetLogsPath(logs_path); - if (!status.ok()) { - return status; - } - int ret = access(logs_path.c_str(), F_OK | R_OK | W_OK); - fiu_do_on("StorageChecker.CheckStoragePermission.logs_path_access_fail", ret = -1); - if (0 != ret) { - std::string err_msg = - " Access log path " + logs_path + " fail. " + strerror(errno) + "(code: " + std::to_string(errno) + ")"; - LOG_SERVER_FATAL_ << err_msg; - std::cerr << err_msg << std::endl; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - -#if 1 - bool cluster_enable = false; - std::string cluster_role; - STATUS_CHECK(config.GetClusterConfigEnable(cluster_enable)); - STATUS_CHECK(config.GetClusterConfigRole(cluster_role)); - - if (cluster_enable && cluster_role == "ro") { - return Status::OK(); - } -#else - std::string deploy_mode; - status = config.GetServerConfigDeployMode(deploy_mode); - if (!status.ok()) { - return status; - } - - if (deploy_mode == "cluster_readonly") { - return Status::OK(); - } -#endif - - /* Check db directory write permission */ - std::string primary_path; - status = config.GetStorageConfigPath(primary_path); - if (!status.ok()) { - return status; - } - - ret = access(primary_path.c_str(), F_OK | R_OK | W_OK); - fiu_do_on("StorageChecker.CheckStoragePermission.db_primary_path_access_fail", ret = -1); - if (0 != ret) { - std::string err_msg = " Access DB storage path " + primary_path + " fail. " + strerror(errno) + - "(code: " + std::to_string(errno) + ")"; - LOG_SERVER_FATAL_ << err_msg; - std::cerr << err_msg << std::endl; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - - /* Check wal directory write permission */ - bool wal_enable = false; - status = config.GetWalConfigEnable(wal_enable); - if (!status.ok()) { - return status; - } - - if (wal_enable) { - std::string wal_path; - status = config.GetWalConfigWalPath(wal_path); - if (!status.ok()) { - return status; - } - ret = access(wal_path.c_str(), F_OK | R_OK | W_OK); - fiu_do_on("StorageChecker.CheckStoragePermission.wal_path_access_fail", ret = -1); - if (0 != ret) { - std::string err_msg = " Access WAL storage path " + wal_path + " fail. " + strerror(errno) + - "(code: " + std::to_string(errno) + ")"; - LOG_SERVER_FATAL_ << err_msg; - std::cerr << err_msg << std::endl; - return Status(SERVER_UNEXPECTED_ERROR, err_msg); - } - } - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/server/init/StorageChecker.h b/core/src/server/init/StorageChecker.h deleted file mode 100644 index b55ffdc705..0000000000 --- a/core/src/server/init/StorageChecker.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "utils/Status.h" - -namespace milvus { -namespace server { - -class StorageChecker { - public: - static Status - CheckStoragePermission(); -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/server/web_impl/Constants.cpp b/core/src/server/web_impl/Constants.cpp deleted file mode 100644 index b975b44b68..0000000000 --- a/core/src/server/web_impl/Constants.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/web_impl/Constants.h" - -namespace milvus { -namespace server { -namespace web { - -const char* NAME_ENGINE_TYPE_FLAT = "FLAT"; -const char* NAME_ENGINE_TYPE_IVFFLAT = "IVFFLAT"; -const char* NAME_ENGINE_TYPE_IVFSQ8 = "IVFSQ8"; -const char* NAME_ENGINE_TYPE_IVFSQ8H = "IVFSQ8H"; -const char* NAME_ENGINE_TYPE_RNSG = "RNSG"; -const char* NAME_ENGINE_TYPE_IVFPQ = "IVFPQ"; -const char* NAME_ENGINE_TYPE_HNSW = "HNSW"; -const char* NAME_ENGINE_TYPE_ANNOY = "ANNOY"; - -const char* NAME_METRIC_TYPE_L2 = "L2"; -const char* NAME_METRIC_TYPE_IP = "IP"; -const char* NAME_METRIC_TYPE_HAMMING = "HAMMING"; -const char* NAME_METRIC_TYPE_JACCARD = "JACCARD"; -const char* NAME_METRIC_TYPE_TANIMOTO = "TANIMOTO"; -const char* NAME_METRIC_TYPE_SUBSTRUCTURE = "SUBSTRUCTURE"; -const char* NAME_METRIC_TYPE_SUPERSTRUCTURE = "SUPERSTRUCTURE"; - -//////////////////////////////////////////////////// -const int64_t VALUE_COLLECTION_INDEX_FILE_SIZE_DEFAULT = 1024; -const char* VALUE_COLLECTION_METRIC_TYPE_DEFAULT = "L2"; - -const char* VALUE_PARTITION_TAG_DEFAULT = ""; - -const char* VALUE_INDEX_INDEX_TYPE_DEFAULT = NAME_ENGINE_TYPE_FLAT; -const int64_t VALUE_INDEX_NLIST_DEFAULT = 16384; - -const int64_t VALUE_CONFIG_CPU_CACHE_CAPACITY_DEFAULT = 4; -const bool VALUE_CONFIG_CACHE_INSERT_DATA_DEFAULT = false; - -///////////////////////////////////////////////////// -const std::unordered_map IndexMap = { - {engine::EngineType::FAISS_IDMAP, NAME_ENGINE_TYPE_FLAT}, - {engine::EngineType::FAISS_IVFFLAT, NAME_ENGINE_TYPE_IVFFLAT}, - {engine::EngineType::FAISS_IVFSQ8, NAME_ENGINE_TYPE_IVFSQ8}, - {engine::EngineType::FAISS_IVFSQ8H, NAME_ENGINE_TYPE_IVFSQ8H}, - {engine::EngineType::NSG_MIX, NAME_ENGINE_TYPE_RNSG}, - {engine::EngineType::FAISS_PQ, NAME_ENGINE_TYPE_IVFPQ}, - {engine::EngineType::HNSW, NAME_ENGINE_TYPE_HNSW}, - {engine::EngineType::ANNOY, NAME_ENGINE_TYPE_ANNOY}, -}; - -const std::unordered_map IndexNameMap = { - {NAME_ENGINE_TYPE_FLAT, engine::EngineType::FAISS_IDMAP}, - {NAME_ENGINE_TYPE_IVFFLAT, engine::EngineType::FAISS_IVFFLAT}, - {NAME_ENGINE_TYPE_IVFSQ8, engine::EngineType::FAISS_IVFSQ8}, - {NAME_ENGINE_TYPE_IVFSQ8H, engine::EngineType::FAISS_IVFSQ8H}, - {NAME_ENGINE_TYPE_RNSG, engine::EngineType::NSG_MIX}, - {NAME_ENGINE_TYPE_IVFPQ, engine::EngineType::FAISS_PQ}, - {NAME_ENGINE_TYPE_HNSW, engine::EngineType::HNSW}, - {NAME_ENGINE_TYPE_ANNOY, engine::EngineType::ANNOY}, -}; - -const std::unordered_map MetricMap = { - {engine::MetricType::L2, NAME_METRIC_TYPE_L2}, - {engine::MetricType::IP, NAME_METRIC_TYPE_IP}, - {engine::MetricType::HAMMING, NAME_METRIC_TYPE_HAMMING}, - {engine::MetricType::JACCARD, NAME_METRIC_TYPE_JACCARD}, - {engine::MetricType::TANIMOTO, NAME_METRIC_TYPE_TANIMOTO}, - {engine::MetricType::SUBSTRUCTURE, NAME_METRIC_TYPE_SUBSTRUCTURE}, - {engine::MetricType::SUPERSTRUCTURE, NAME_METRIC_TYPE_SUPERSTRUCTURE}, -}; - -const std::unordered_map MetricNameMap = { - {NAME_METRIC_TYPE_L2, engine::MetricType::L2}, - {NAME_METRIC_TYPE_IP, engine::MetricType::IP}, - {NAME_METRIC_TYPE_HAMMING, engine::MetricType::HAMMING}, - {NAME_METRIC_TYPE_JACCARD, engine::MetricType::JACCARD}, - {NAME_METRIC_TYPE_TANIMOTO, engine::MetricType::TANIMOTO}, - {NAME_METRIC_TYPE_SUBSTRUCTURE, engine::MetricType::SUBSTRUCTURE}, - {NAME_METRIC_TYPE_SUPERSTRUCTURE, engine::MetricType::SUPERSTRUCTURE}, -}; -} // namespace web -} // namespace server -} // namespace milvus diff --git a/core/src/server/web_impl/Constants.h b/core/src/server/web_impl/Constants.h deleted file mode 100644 index 474b2e4e67..0000000000 --- a/core/src/server/web_impl/Constants.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "db/engine/ExecutionEngine.h" - -#include -#include - -namespace milvus { -namespace server { -namespace web { - -extern const char* NAME_ENGINE_TYPE_FLAT; -extern const char* NAME_ENGINE_TYPE_IVFFLAT; -extern const char* NAME_ENGINE_TYPE_IVFSQ8; -extern const char* NAME_ENGINE_TYPE_IVFSQ8H; -extern const char* NAME_ENGINE_TYPE_RNSG; -extern const char* NAME_ENGINE_TYPE_IVFPQ; -extern const char* NAME_ENGINE_TYPE_HNSW; -extern const char* NAME_ENGINE_TYPE_ANNOY; - -extern const char* NAME_METRIC_TYPE_L2; -extern const char* NAME_METRIC_TYPE_IP; -extern const char* NAME_METRIC_TYPE_HAMMING; -extern const char* NAME_METRIC_TYPE_JACCARD; -extern const char* NAME_METRIC_TYPE_TANIMOTO; -extern const char* NAME_METRIC_TYPE_SUBSTRUCTURE; -extern const char* NAME_METRIC_TYPE_SUPERSTRUCTURE; - -//////////////////////////////////////////////////// -extern const int64_t VALUE_COLLECTION_INDEX_FILE_SIZE_DEFAULT; -extern const char* VALUE_COLLECTION_METRIC_TYPE_DEFAULT; -extern const char* VALUE_PARTITION_TAG_DEFAULT; -extern const char* VALUE_INDEX_INDEX_TYPE_DEFAULT; -extern const int64_t VALUE_INDEX_NLIST_DEFAULT; -extern const int64_t VALUE_CONFIG_CPU_CACHE_CAPACITY_DEFAULT; -extern const bool VALUE_CONFIG_CACHE_INSERT_DATA_DEFAULT; - -///////////////////////////////////////////////////// -extern const std::unordered_map IndexMap; -extern const std::unordered_map IndexNameMap; -extern const std::unordered_map MetricMap; -extern const std::unordered_map MetricNameMap; - -} // namespace web -} // namespace server -} // namespace milvus diff --git a/core/src/server/web_impl/README.md b/core/src/server/web_impl/README.md deleted file mode 100644 index d277313789..0000000000 --- a/core/src/server/web_impl/README.md +++ /dev/null @@ -1,1604 +0,0 @@ -# Milvus RESTful API - - - -- [Overview](#overview) -- [API Reference](#api-reference) - - [`/state`](#state) - - [`/devices`](#devices) - - [`/config/advanced` (GET)](#configadvanced-get) - - [`/config/advanced` (PUT)](#configadvanced-put) - - [`/config/advanced` (OPTIONS)](#configadvanced-options) - - [`/config/gpu_resources` (GET)](#configgpu_resources-get) - - [`/config/gpu_resources` (PUT)](#configgpu_resources-put) - - [`/config/gpu_resources` (OPTIONS)](#configgpu_resources-options) - - [`/collections` (GET)](#collections-get) - - [`/collections` (POST)](#collections-post) - - [`/collections` (OPTIONS)](#collections-options) - - [`/collections/{collection_name}` (GET)](#collectionscollection_name-get) - - [`/collections/{collection_name}` (DELETE)](#collectionscollection_name-delete) - - [`/collections/{collection_name}` (OPTIONS)](#collectionscollection_name-options) - - [`/collections/{collection_name}/indexes` (GET)](#collectionscollection_nameindexes-get) - - [`/collections/{collection_name}/indexes` (POST)](#collectionscollection_nameindexes-post) - - [`/collections/{collection_name}/indexes` (DELETE)](#collectionscollection_nameindexes-delete) - - [`/collections/{collection_name}/indexes` (OPTIONS)](#collectionscollection_nameindexes-options) - - [`/collections/{collection_name}/partitions` (GET)](#collectionscollection_namepartitions-get) - - [`/collections/{collection_name}/partitions` (POST)](#collectionscollection_namepartitions-post) - - [`/collections/{collection_name}/partitions` (OPTIONS)](#collectionscollection_namepartitions-options) - - [`/collections/{collection_name}/partitions` (DELETE)](#collectionscollection_namepartitions-delete) - - [`/collections/{collection_name}/segments` (GET)](#collectionscollection_namesegments-get) - - [`/collections/{collection_name}/segments/{segment_name}/vectors` (GET)](#collectionscollection_namesegmentssegment_namevectors-get) - - [`/collections/{collection_name}/segments/{segment_name}/ids` (GET)](#collectionscollection_namesegmentssegment_nameids-get) - - [`/collections/{collection_name}/vectors` (PUT)](#collectionscollection_namevectors-put) - - [`/collections/{collection_name}/vectors` (POST)](#collectionscollection_namevectors-post) - - [`/collections/{collection_name}/vectors` (GET)](#collectionscollection_namevectorsidvector_id-get) - - [`/collections/{collection_name}/vectors` (OPTIONS)](#collectionscollection_namevectors-options) - - [`/system/{msg}` (GET)](#systemmsg-get) - - [`system/{op}` (PUT)](#systemop-put) -- [Error Codes](#error-codes) - - - -## Overview - -With the RESTful API, you can use Milvus by sending HTTP requests to the Milvus server web port. The RESTful API is available as long as you have a running Milvus server. You can set the web port in the Milvus configuration file. Refer to [Milvus Configuration](https://www.milvus.io/docs/reference/milvus_config.md) for more information. - -## API Reference - -### `/state` - -Checks whether the web server is running. - -#### Request - -| Request Component | Value | -| ----------------- | -------------------------- | -| Name | `/state` | -| Header | `accept: application/json` | -| Body | N/A | -| Method | GET | - -#### Response - -| Status code | Description | -| ----------- | -------------------------- | -| 200 | The request is successful. | - -#### Example - -##### Request - -```shell -$ curl -X GET "http://127.0.0.1:19121/state" -H "accept: application/json" -``` - -##### Response - -```json -{ "message": "Success", "code": 0 } -``` - -### `/devices` - -Gets CPU/GPU information from the host. - -#### Request - -| Request Component | Value | -| ----------------- | -------------------------- | -| Name | `/devices` | -| Header | `accept: application/json` | -| Body | N/A | -| Method | GET | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 200 | The request is successful. | -| 400 | The request is incorrect. Refer to the error message for details. | - -#### Example - -##### Request - -```shell -$ curl -X GET "http://127.0.0.1:19121/devices" -H "accept: application/json" -``` - -##### Response - -```json -{ "cpu": { "memory": 31 }, "gpus": { "GPU0": { "memory": 5 } } } -``` - -### `/config/advanced` (GET) - -Gets the values of parameters in `cache_config` and `engine_config` of the Milvus configuration file. - -#### Request - -| Request Component | Value | -| ----------------- | -------------------------- | -| Name | `/config/advanced` | -| Header | `accept: application/json` | -| Body | N/A | -| Method | GET | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 200 | The request is successful. | -| 400 | The request is incorrect. Refer to the error message for details. | - -#### Example - -##### Request - -```shell -$ curl -X GET "http://127.0.0.1:19121/config/advanced" -H "accept: application/json" -``` - -##### Response - -```json -{ - "cpu_cache_capacity": 4, - "cache_insert_data": false, - "use_blas_threshold": 1100, - "gpu_search_threshold": 1000 -} -``` - -### `/config/advanced` (PUT) - -Updates the values of parameters in `cache_config` and `engine_config` of the Milvus configuration file. - -#### Request - - - - - - - - -
      Request ComponentValue
      Name
      /config/advanced
      Header
      accept: application/json
      Body
      
      -{
      -  "cpu_cache_capacity": integer($int64),
      -  "cache_insert_data": boolean,
      -  "use_blas_threshold": integer($int64),
      -  "gpu_search_threshold": integer($int64)
      -} 
      -
      MethodPUT
      - -> Note: `gpu_search_config` is available only in GPU-supported Milvus. - -##### Body Parameters - -| Parameter | Description | Required? | -| ---------------------- | -------------------------------------------------------------------------------------- | --------- | -| `cpu_cache_capacity` | Value of `cpu_cache_capacity` in the Milvus configuration file. The default is 4. | No | -| `cache_insert_data` | Value of `cache_insert_data` in the Milvus configuration file. The default is false. | No | -| `use_blas_threshold` | Value of `use_blas_threshold` in the Milvus configuration file. The default is 1100. | No | -| `gpu_search_threshold` | Value of `gpu_search_threshold` in the Milvus configuration file. The default is 1000. | No | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 200 | The request is successful. | -| 400 | The request is incorrect. Refer to the error message for details. | - -#### Example - -##### Request - -```shell -$ curl -X PUT "http://127.0.0.1:19121/config/advanced" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"cpu_cache_capacity\":4,\"cache_insert_data\":false,\"use_blas_threshold\":1100,\"gpu_search_threshold\":1000}" -``` - -##### Response - -```json -{ "message": "OK", "code": 0 } -``` - -### `/config/advanced` (OPTIONS) - -Use this API for Cross-Origin Resource Sharing (CORS). - -#### Request - -| Request Component | Value | -| ----------------- | ------------------ | -| Name | `/config/advanced` | -| Header | N/A | -| Body | N/A | -| Method | OPTIONS | - -#### Example - -##### Request - -```shell -$ curl -X OPTIONS "http://127.0.0.1:19121/config/advanced" -``` - -### `/config/gpu_resources` (GET) - -Gets the parameter values in `gpu_resource_config` of the Milvus configuration file. - -> Note: This method is available only for GPU-supported Milvus. - -#### Request - -| Request Component | Value | -| ----------------- | -------------------------- | -| Name | `/config/gpu_resources` | -| Header | `accept: application/json` | -| Body | N/A | -| Method | GET | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 200 | The request is successful. | -| 400 | The request is incorrect. Refer to the error message for details. | - -#### Example - -##### Request - -```shell -$ curl -X GET "http://127.0.0.1:19121/config/gpu_resources" -H "accept: application/json" -``` - -##### Response - -```json -{ - "enable": true, - "cache_capacity": 1, - "search_resources": ["GPU0"], - "build_index_resources": ["GPU0"] -} -``` - -### `/config/gpu_resources` (PUT) - -Updates the parameter values in `gpu_resource_config` of the Milvus configuration file. - -> Note: This method is available only for GPU-supported Milvus. - -#### Request - - - - - - - - -
      Request ComponentValue
      Name
      /config/gpu_resources
      Header
      accept: application/json
      Body
      
      -{
      -  "enable": boolean,
      -  "cache_capacity": integer($int64),
      -  "search_resources": [string],
      -  "build_index_resources": [string]
      -}
      -
      MethodPUT
      - -##### Body Parameters - -| Parameter | Description | Required? | -| ----------------------- | ------------------------------------------------------------------ | --------- | -| `enable` | Specifies whether to enable GPU resources. | Yes | -| `cache_capacity` | Size of GPU memory per card used for cache in GBs. | Yes | -| `search_resources` | GPU devices used for search computation, must be in format `gpux`. | Yes | -| `build_index_resources` | GPU devices used for index building, must be in format `gpux`. | Yes | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 200 | The request is successful. | -| 400 | The request is incorrect. Refer to the error message for details. | - -#### Example - -##### Request - -```shell -$ curl -X PUT "http://127.0.0.1:19121/config/gpu_resources" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"enable\":true,\"cache_capacity\":1,\"search_resources\":[\"GPU0\"],\"build_index_resources\":[\"GPU0\"]}" -``` - -##### Response - -```json -{ "message": "OK", "code": 0 } -``` - -### `/config/gpu_resources` (OPTIONS) - -Use this API for Cross-Origin Resource Sharing (CORS). - -> Note: This method is available only for GPU-supported Milvus. - -#### Request - -| Request Component | Value | -| ----------------- | ----------------------- | -| Name | `/config/gpu_resources` | -| Header | N/A | -| Body | N/A | -| Method | OPTIONS | - -#### Example - -##### Request - -```shell -$ curl -X OPTIONS "http://127.0.0.1:19121/config/gpu_resources" -``` - -### `/collections` (GET) - -Gets all collections starting from `offset` and ends with `page_size`. - -#### Request - -| Request Component | Value | -| ----------------- | -------------------------- | -| Name | `/collections` | -| Header | `accept: application/json` | -| Body | N/A | -| Method | GET | - -##### Query Parameters - -| Parameter | Description | Required? | -| ----------- | ------------------------------------------------------------- | --------- | -| `offset` | Row offset from which the data page starts. The default is 0. | No | -| `page_size` | Size of the data page. The default is 10. | No | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 200 | The request is successful. | -| 400 | The request is incorrect. Refer to the error message for details. | - -#### Example - -##### Request - -```shell -$ curl -X GET "http://127.0.0.1:19121/collections?offset=0&page_size=1" -H "accept: application/json" -``` - -##### Response - -```json -{ - "collections": [ - { - "collection_name": "test_collection", - "dimension": 1, - "index_file_size": 10, - "metric_type": "L2", - "count": 0, - "index": "FLAT", - "index_params": {"nlist": 4096} - } - ], - "count": 58 -} -``` - -### `/collections` (POST) - -Creates a collection. - -#### Request - - - - - - - - -
      Request ComponentValue
      Name
      /tables
      Header
      accept: application/json
      Body
      
      -{
      -  "collection_name": string,
      -  "dimension": integer($int64),
      -  "index_file_size": integer($int64),
      -  "metric_type": string
      -}
      -
      MethodPOST
      - -##### Body Parameters - -| Parameter | Description | Required? | -| ----------------- | ----------------------------------------------------------------------------------------- | --------- | -| `collection_name` | The name of the collection to create, which must be unique within its database. | Yes | -| `dimension` | The dimension of the vectors that are to be inserted into the created collection. | Yes | -| `index_file_size` | Threshold value that triggers index building for raw data files. The default is 1024. | No | -| `metric_type` | The method vector distances are compared in Milvus. The default is L2. | No | - -* Currently supported metrics include: - - `L2` (Euclidean distance), - - `IP` (Inner Product) - - `HAMMING` (Hamming distance) - - `JACCARD` (Jaccard distance) - - `TANIMOTO` (Tanomoto distance) - - `SUBSTRUCTURE` (Sub structure distance) - - `SUPERSTRUCTURE` (Super structure distance) - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 201 | Created | -| 400 | The request is incorrect. Refer to the error message for details. | - -#### Example - -##### Request - -```shell -$ curl -X POST "http://127.0.0.1:19121/collections" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"collection_name\":\"test_collection\",\"dimension\":1,\"index_file_size\":10,\"metric_type\":\"L2\"}" -``` - -##### Response - -```json -{ "message": "OK", "code": 0 } -``` - -### `/collections` (OPTIONS) - -Use this API for Cross-Origin Resource Sharing (CORS). - -#### Request - -| Request Component | Value | -| ----------------- | -------------- | -| Name | `/collections` | -| Header | N/A | -| Body | N/A | -| Method | OPTIONS | - -#### Example - -##### Request - -```shell -$ curl -X OPTIONS "http://127.0.0.1:19121/collections" -``` - -### `/collections/{collection_name}` (GET) - -Gets all information about a collection by name. - -#### Request - -| Request Component | Value | -| ----------------- | -------------------------------- | -| Name | `/collections/{collection_name}` | -| Header | `accept: application/json` | -| Body | N/A | -| Method | GET | - -##### Query Parameters - -| Parameter | Description | Required? | -| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -| `collection_name` | Name of the collection. | Yes | -| `info` | Type of information to acquire. `info` must either be empty or `stat`. When `info` is empty, Milvus returns collection name, dimension, index file size, metric type, offset, index type, and nlist of the collection. When `info` is `stat`, Milvus returns the collection offset, partition status, and segment status. | No | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 200 | The request is successful. | -| 400 | The request is incorrect. Refer to the error message for details. | -| 404 | The required resource does not exist. | - -#### Example - -##### Request - -```shell -$ curl -X GET "http://127.0.0.1:19121/collections/test_collection" -H "accept: application/json" -``` - -##### Response - -```json -{ - "collection_name": "test_collection", - "dimension": 1, - "index_file_size": 10, - "metric_type": "L2", - "count": 0, - "index": "FLAT", - "index_params": {"nprobe": 16384} -} -``` - -##### Request - -```shell -$ curl -X GET "http://127.0.0.1:19121/collections/test_collection?info=stat" -H "accept: application/json" -``` - -##### Response - -```json -{ - "partitions":[ - { - "row_count":10000, - "segments":[ - { - "data_size":5284922, - "index_name":"IVFFLAT", - "name":"1589468453228582000", - "row_count":10000 - } - ], - "tag":"_default" - } - ], - "row_count":10000 -} - -``` - -### `/collections/{collection_name}` (DELETE) - -Drops a collection by name. - -#### Request - -| Request Component | Value | -| ----------------- | -------------------------------- | -| Name | `/collections/{collection_name}` | -| Header | `accept: application/json` | -| Body | N/A | -| Method | DELETE | - -##### Query Parameters - -| Parameter | Description | Required? | -| ----------------- | ----------------------- | --------- | -| `collection_name` | Name of the collection. | Yes | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 204 | Deleted | -| 400 | The request is incorrect. Refer to the error message for details. | -| 404 | The required resource does not exist. | - -#### Example - -##### Request - -```shell -$ curl -X DELETE "http://127.0.0.1:19121/collections/test_collection" -H "accept: application/json" -``` - -If the deletion is successful, no message will be returned. - -### `/collections/{collection_name}` (OPTIONS) - -Use this API for Cross-Origin Resource Sharing (CORS). - -#### Request - -| Request Component | Value | -| ----------------- | -------------------------------- | -| Name | `/collections/{collection_name}` | -| Header | N/A | -| Body | N/A | -| Method | OPTIONS | - -#### Query Parameters - -| Parameter | Description | Required? | -| ----------------- | ----------------------- | --------- | -| `collection_name` | Name of the collection. | Yes | - -#### Example - -##### Request - -```shell -$ curl -X OPTIONS "http://127.0.0.1:19121/collections/test_collection" -``` - -### `/collections/{collection_name}/indexes` (GET) - -Gets the index type and nlist of a collection. - -#### Request - -| Request Component | Value | -| ----------------- | ---------------------------------------- | -| Name | `/collections/{collection_name}/indexes` | -| Header | `accept: application/json` | -| Body | N/A | -| Method | GET | - -##### Query Parameters - -| Parameter | Description | Required? | -| ----------------- | ----------------------- | --------- | -| `collection_name` | Name of the collection. | Yes | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 200 | The request is successful. | -| 400 | The request is incorrect. Refer to the error message for details. | -| 404 | The required resource does not exist. | - -#### Example - -##### Request - -```shell -$ curl -X GET "http://127.0.0.1:19121/collections/test_collection/indexes" -H "accept: application/json" -``` - -##### Response - -```json -{ "index_type": "FLAT", "params": { "nlist": 4096 } } -``` - -### `/collections/{collection_name}/indexes` (POST) - -Updates the index type and nlist of a collection. - -#### Request - - - - - - - - -
      Request ComponentValue
      Name
      /tables
      Header
      accept: application/json
      Body
      
      -{
      -  "index_type": string,
      -  "params": {
      -      ......
      -  }
      -}
      -
      MethodPOST
      - -##### Body Parameters - -| Parameter | Description | Required? | -| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -| `index_type` | The type of indexing method to query the collection. Please refer to [Milvus Indexes](https://www.milvus.io/docs/guides/index.md) for detailed introduction of supported indexes. The default is "FLAT". | No | -| `params` | The extra params of indexing method to query the collection. Please refer to [Index and search parameters](#Index-and-search-parameters) for detailed introduction of supported indexes. | No | - -##### Query Parameters - -| Parameter | Description | Required? | -| ----------------- | ----------------------- | --------- | -| `collection_name` | Name of the collection. | Yes | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 201 | Created | -| 400 | The request is incorrect. Refer to the error message for details. | -| 404 | The required resource does not exist. | - -#### Example - -##### Request - -```shell -$ curl -X POST "http://127.0.0.1:19121/collections/test_collection/indexes" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"index_type\":\"IVFFLAT\",\"params\": {\"nlist\":4096}}" -``` - -##### Response - -```json -{ "message": "OK", "code": 0 } -``` - -### `/collections/{collection_name}/indexes` (DELETE) - -Drops an index for a collection. - -#### Request - -| Request Component | Value | -| ----------------- | ---------------------------------------- | -| Name | `/collections/{collection_name}/indexes` | -| Header | `accept: application/json` | -| Body | N/A | -| Method | DELETE | - -##### Query Parameters - -| Parameter | Description | Required? | -| ----------------- | ----------------------- | --------- | -| `collection_name` | Name of the collection. | Yes | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 204 | Deleted | -| 400 | The request is incorrect. Refer to the error message for details. | -| 404 | Resource not available | - -#### Example - -##### Request - -```shell -$ curl -X DELETE "http://127.0.0.1:19121/collections/test_collection/indexes" -H "accept: application/json" -``` - -If the deletion is successful, no message will be returned. - -### `/collections/{collection_name}/indexes` (OPTIONS) - -Use this API for Cross-Origin Resource Sharing (CORS). - -#### Request - -| Request Component | Value | -| ----------------- | ---------------------------------------- | -| Name | `/collections/{collection_name}/indexes` | -| Header | N/A | -| Body | N/A | -| Method | OPTIONS | - -##### Query Parameters - -| Parameter | Description | Required? | -| ----------------- | ----------------------- | --------- | -| `collection_name` | Name of the collection. | Yes | - -#### Example - -##### Request - -```shell -$ curl -X OPTIONS "http://127.0.0.1:19121/collections/test_collection/indexes" -``` - -### `/collections/{collection_name}/partitions` (GET) - -Gets all partitions in a collection starting from `offset` and ends with `page_size`. - -#### Request - -| Request Component | Value | -| ----------------- | ------------------------------------------- | -| Name | `/collections/{collection_name}/partitions` | -| Header | `accept: application/json` | -| Body | N/A | -| Method | GET | - -##### Query Parameters - -| Parameter | Description | Required? | -| ----------------- | ------------------------------------------------------------- | --------- | -| `collection_name` | Name of the collection. | Yes | -| `offset` | Row offset from which the data page starts. The default is 0. | No | -| `page_size` | Size of the data page. The default is 10. | No | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 200 | The request is successful. | -| 400 | The request is incorrect. Refer to the error message for details. | -| 404 | The required resource does not exist. | - -#### Example - -##### Request - -```shell -$ curl -X GET "http://127.0.0.1:19121/collections/test_collection/partitions?offset=0&page_size=3" -H "accept: application/json" -``` - -##### Response - -```json -{ - "partitions": [ - { "partition_tag": "_default" }, - { "partition_tag": "test_tag" }, - { "partition_tag": "test_2" } - ], - "count": 10 -} -``` - -### `/collections/{collection_name}/partitions` (POST) - -Creates a partition in a collection. - -#### Request - -| Request Component | Value | -| ----------------- | ------------------------------------------- | -| Name | `/collections/{collection_name}/partitions` | -| Header | `accept: application/json` | -| Body | N/A | -| Method | POST | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 201 | Created | -| 400 | The request is incorrect. Refer to the error message for details. | -| 404 | The required resource does not exist. | - -#### Example - -##### Request - -```shell -$ curl -X POST "http://127.0.0.1:19121/collections/test_collection/partitions" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"partition_tag\": \"test\"}" -``` - -##### Response - -```json -{ "message": "OK", "code": 0 } -``` - -### `/collections/{collection_name}/partitions` (OPTIONS) - -Use this API for Cross-Origin Resource Sharing (CORS). - -#### Request - -| Request Component | Value | -| ----------------- | ------------------------------------------- | -| Name | `/collections/{collection_name}/partitions` | -| Header | N/A | -| Body | N/A | -| Method | OPTIONS | - -##### Query Parameters - -| Parameter | Description | Required? | -| ----------------- | ----------------------- | --------- | -| `collection_name` | Name of the collection. | Yes | - -#### Example - -##### Request - -```shell -$ curl -X OPTIONS "http://127.0.0.1:19121/collections/test_collection/partitions" -``` - -### `/collections/{collection_name}/partitions` (DELETE) - -Deletes a partition by tag. - -#### Request - - - - - - - - -
      Request ComponentValue
      Name
      /collections/{collection_name}/partitions
      Header
      accept: application/json
      Body
      
      -{
      -  "partition_tag": string
      -}
      -
      MethodPOST
      - -##### Query Parameters - -| Parameter | Description | Required? | -| ----------------- | --------------------------------------------------- | --------- | -| `collection_name` | Name of the collection that contains the partition. | Yes | -| `partition_tag` | Tag of the partition to delete. | yes | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 204 | Deleted | -| 400 | The request is incorrect. Refer to the error message for details. | -| 404 | The requested resource does not exist. | - -#### Example - -##### Request - -```shell -$ curl -X DELETE "http://127.0.0.1:19121/collections/test_collection/partitions" -H "accept: application/json" -d "{\"partition_tag\": \"tags_01\"}" -``` - -The deletion is successful if no information is returned. - -### `/collections/{collection_name}/segments` (GET) - -Gets all segments in a collection starting from `offset` and ends with `page_size`. - -#### Request - -| Request Component | Value | -| ----------------- | ----------------------------------------- | -| Name | `/collections/{collection_name}/segments` | -| Header | `accept: application/json` | -| Body | N/A | -| Method | GET | - -##### Query Parameters - -| Parameter | Description | Required? | -| ----------------- | ------------------------------------------------------------- | --------- | -| `collection_name` | Name of the collection. | Yes | -| `offset` | Row offset from which the data page starts. The default is 0. | No | -| `page_size` | Size of the data page. The default is 10. | No | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 200 | The request is successful. | -| 400 | The request is incorrect. Refer to the error message for details. | -| 404 | The required resource does not exist. | - -#### Example - -##### Request - -```shell -$ curl -X GET "http://127.0.0.1:19121/collections/test_collection/segments?offset=0&page_size=1" -H "accept: application/json" -``` - -##### Response - -```json -{ - "code":0, - "message":"OK", - "count":1, - "segments":[ - { - "data_size":5284922, - "index_name":"IVFFLAT", - "name":"1589468453228582000", - "partition_tag":"_default", - "row_count":10000 - } - ] -} -``` - -### `/collections/{collection_name}/segments/{segment_name}/vectors` (GET) - -Gets all vectors of segment in a collection starting from `offset` and ends with `page_size`. - -#### Request - -| Request Component | Value | -| ----------------- | ----------------------------------------- | -| Name | `/collections/{collection_name}/segments` | -| Header | `accept: application/json` | -| Body | N/A | -| Method | GET | - -##### Query Parameters - -| Parameter | Description | Required? | -| ----------------- | ------------------------------------------------------------- | --------- | -| `collection_name` | Name of the collection. | Yes | -| `segment_name` | Name of the segment. | Yes | -| `offset` | Row offset from which the data page starts. The default is 0. | No | -| `page_size` | Size of the data page. The default is 10. | No | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 200 | The request is successful. | -| 400 | The request is incorrect. Refer to the error message for details. | -| 404 | The required resource does not exist. | - -#### Example - -##### Request - -```shell -$ curl -X GET "http://127.0.0.1:19121/collections/test_collection/segments/1583727470444700000/vectors?offset=0&page_size=1" -H "accept: application/json" -``` - -##### Response - -```json -{ - "code": 0, - "message": "OK", - "count": 2, - "vectors": [ - { - "vector": [0.1], - "id": "1583727470435045000" - } - ] -} -``` - -### `/collections/{collection_name}/segments/{segment_name}/ids` (GET) - -Gets all vector ids of segment in a collection starting from `offset` and ends with `page_size`. - -#### Request - -| Request Component | Value | -| ----------------- | ----------------------------------------- | -| Name | `/collections/{collection_name}/segments` | -| Header | `accept: application/json` | -| Body | N/A | -| Method | GET | - -##### Query Parameters - -| Parameter | Description | Required? | -| ----------------- | ------------------------------------------------------------- | --------- | -| `collection_name` | Name of the collection. | Yes | -| `segment_name` | Name of the segment. | Yes | -| `offset` | Row offset from which the data page starts. The default is 0. | No | -| `page_size` | Size of the data page. The default is 10. | No | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 200 | The request is successful. | -| 400 | The request is incorrect. Refer to the error message for details. | -| 404 | The required resource does not exist. | - -#### Example - -##### Request - -```shell -$ curl -X GET "http://127.0.0.1:19121/collections/test_collection/segments/1583727470444700000/ids?offset=0&page_size=1" -H "accept: application/json" -``` - -##### Response - -```json -{ - "ids": ["1583727470435045000"], - "count": 10000 -} -``` - -### `/collections/{collection_name}/vectors` (PUT) - -1. Searches vectors in a collection. - -#### Request - - - - - - - -
      Request ComponentValue
      Name
      /tables/{table_name}/vectors
      Header
      accept: application/json
      Body
      
      -{
      -  "search": {
      -      "topk": integer($int64),
      -      "partition_tags": [string],
      -      "file_ids": [string],
      -      "vectors": [[number($float/$uint8)]]
      -      "params": {
      -          "nprobe": 16
      -      }
      -  }
      -}
      -
      MethodPUT
      - -##### Body Parameters - -| Parameter | Description | Required? | -| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -| `topk` | The top k most similar results of each query vector. | Yes | -| `tags` | Tags of partitions that you need to search. You do not have to specify this value if the collection is not partitioned or you wish to search the whole collection. | No | -| `file_ids` | IDs of the vector files. You do not have to specify this value if you do not use Milvus in distributed scenarios. Also, if you assign a value to `file_ids`, the value of `tags` is ignored. | No | -| `vectors` | Vectors to query. | Yes | -| `params` | Extra params for search. Please refer to [Index and search parameters](#Index-and-search-parameters) to get more detail information. | Yes | - -> Note: Type of items of vectors depends on the metric used by the collection. If the collection uses `L2` or `IP`, you must use `float`. If the collection uses `HAMMING`, `JACCARD`, or `TANIMOTO`, you must use `uint8`. - -##### Query Parameters - -| Parameter | Description | Required? | -| ----------------- | ----------------------- | --------- | -| `collection_name` | Name of the collection. | Yes | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 200 | The request is successful. | -| 400 | The request is incorrect. Refer to the error message for details. | -| 404 | The required resource does not exist. | - -#### Example - -##### Request - -```shell -$ curl -X PUT "http://127.0.0.1:19121/collections/test_collection/vectors" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"search\":{\"topk\":2,\"vectors\":[[0.1]],\"params\":{\"nprobe\":16}}}" -``` - -##### Response - -```json -{ - "num": 1, - "results": [ - [ - { "id": "1578989029645098000", "distance": "0.000000" }, - { "id": "1578989029645098001", "distance": "0.010000" } - ] - ] -} -``` - -2. Delete vectors - -#### Request - - - - - - - -
      Request ComponentValue
      Name
      /tables/{table_name}/vectors
      Header
      accept: application/json
      Body
      
      -{
      -  "delete": {
      -     "ids": [$string]
      -  }
      -}
      -
      MethodPUT
      - -##### Body Parameters - -| Parameter | Description | Required? | -| --------- | --------------- | --------- | -| ids | IDs of vectors. | Yes | - -##### Query Parameters - -| Parameter | Description | Required? | -| ----------------- | ----------------------- | --------- | -| `collection_name` | Name of the collection. | Yes | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 200 | The request is successful. | -| 400 | The request is incorrect. Refer to the error message for details. | -| 404 | The required resource does not exist. | - -#### Example - -##### Request - -```shell -$ curl -X PUT "http://127.0.0.1:19121/collections/test_collection/vectors" -H "accept: application/json" -H "Content-Type: application/json" -d "{"delete": {"ids": ["1578989029645098000"]}}" -``` - -##### Response - -```json -{ "code": 0, "message": "success" } -``` - -### `/collections/{collection_name}/vectors` (POST) - -Inserts vectors to a collection. - -> Note: It is recommended that you do not insert more than 1 million vectors per request. - -#### Request - - - - - - - -
      Request ComponentValue
      Name
      /tables/{table_name}/vectors
      Header
      accept: application/json
      Body
      
      -{
      -  "partition_tag": $string,
      -  "vectors": [[number($float/$uint8)]],
      -  "ids": [$string]
      -}
      -
      MethodPOST
      - -##### Body Parameters - -| Parameter | Description | Required? | -| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -| `partition_tag` | Tag of the partition to insert vectors to. | No | -| `vectors` | Vectors to insert to the collection. | Yes | -| `ids` | IDs of the vectors to insert to the collection. If you assign IDs to the vectors, you must provide IDs for all vectors in the collection. If you do not specify this parameter, Milvus automatically assigns IDs to the vectors. | No | - -> Note: Type of items of `vectors` depends on the metric used by the collection. If the collection uses `L2` or `IP`, you must use `float`. If the collection uses `HAMMING`, `JACCARD`, or `TANIMOTO`, you must use `uint8`. - -##### Query Parameters - -| Parameter | Description | Required? | -| ----------------- | ----------------------- | --------- | -| `collection_name` | Name of the collection. | Yes | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 201 | Created | -| 400 | The request is incorrect. Refer to the error message for details. | -| 404 | The required resource does not exist. | - -#### Example - -##### Request - -```shell -$ curl -X POST "http://127.0.0.1:19121/collections/test_collection/vectors" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"vectors\":[[0.1],[0.2],[0.3],[0.4]]}" -``` - -##### Response - -```json -{ - "ids": [ - "1578989029645098000", - "1578989029645098001", - "1578989029645098002", - "1578989029645098003" - ] -} -``` - -### `/collections/{collection_name}/vectors?ids={vector_id_list}` (GET) - -Obtain vectors by ID. - -#### Request - -| Request Component | Value | -| ----------------- | ---------------------------------------- | -| Name | `/collections/{collection_name}/vectors` | -| Header | `accept: application/json` | -| Body | N/A | -| Method | GET | - -#### Query Parameters - -| Parameter | Description | Required? | -| ----------------- | ------------------------------------- | --------- | -| `collection_name` | Name of the collection. | Yes | -| `vector_id_list` | Vector id list, separated by commas. | Yes | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 201 | Created | -| 400 | The request is incorrect. Refer to the error message for details. | -| 404 | The required resource does not exist. | - -#### Example - -##### Request - -```shell -$ curl -X GET "http://127.0.0.1:19121/collections/test_collection/vectors?ids=1578989029645098000,1578989029645098001" -H "accept: application/json" -H "Content-Type: application/json" -``` - -##### Response - -```json -{ - "vectors": [ - { - "id": "1578989029645098000", - "vector": [0.1] - }, - { - "id": "1578989029645098001", - "vector": [0.2] - } - ] -} -``` - -### `/collections/{collection_name}/vectors` (OPTIONS) - -Use this API for Cross-Origin Resource Sharing (CORS). - -#### Request - -| Request Component | Value | -| ----------------- | ---------------------------------------- | -| Name | `/collections/{collection_name}/vectors` | -| Header | N/A | -| Body | N/A | -| Method | OPTIONS | - -#### Example - -##### Request - -```shell -$ curl -X OPTIONS "http://127.0.0.1:19121/collections/test_collection/vectors" -``` - -### `/system/{msg}` (GET) - -Gets information about the Milvus server. - -#### Request - -| Request Component | Value | -| ----------------- | -------------------------- | -| Name | `/system/{msg}` | -| Header | `accept: application/json` | -| Body | N/A | -| Method | GET | - -##### Query Parameters - -| Parameter | Description | Required? | -| --------- | ----------------------------------------------------------------- | --------- | -| `msg` | Type of the message to return. You can use `status` or `version`. | Yes | - -#### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 200 | The request is successful. | -| 400 | The request is incorrect. Refer to the error message for details. | - -#### Example - -##### Request - -```shell -$ curl -X GET "http://127.0.0.1:19121/system/version" -H "accept: application/json" -``` - -##### Response - -```json -{"code":0,"message":"OK","reply": "0.8.0" } -``` - -### `system/{op}` (PUT) - -#### Flush a collection - -##### Request - - - - - - - -
      Request ComponentValue
      Name
      /system/task
      Header
      accept: application/json
      Body
      
      -{
      -  "flush": {
      -     "collection_names": [$string]
      -  }
      -}
      -
      MethodPUT
      - -##### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 200 | The request is successful. | -| 400 | The request is incorrect. Refer to the error message for details. | - -##### Example - -###### Request - -```shell -$ curl -X PUT "http://127.0.0.1:19121/system/task" -H "accept: application/json" -d "{\"flush\": {\"collection_names\": [\"test_collection\"]}}" -``` - -###### Response - -```json -{ "code": 0, "message": "success" } -``` - -#### Compact segments in a collection - -##### Request - - - - - - - -
      Request ComponentValue
      Name
      /system/task
      Header
      accept: application/json
      Body
      
      -{
      -  "compact": {
      -     "collection_name": $string
      -  }
      -}
      -
      MethodPUT
      - -##### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 200 | The request is successful. | -| 400 | The request is incorrect. Refer to the error message for details. | - -##### Example - -###### Request - -```shell -$ curl -X PUT "http://127.0.0.1:19121/system/task" -H "accept: application/json" -d "{\"compact\": {\"collection_name\": \"test_collection\"}}" -``` - -###### Response - -```json -{ "code": 0, "message": "success" } -``` - -#### Load a collection to memory - -##### Request - - - - - - - -
      Request ComponentValue
      Name
      /system/task
      Header
      accept: application/json
      Body
      
      -{
      -  "load": {
      -     "collection_name": $string
      -  }
      -}
      -
      MethodPUT
      - -##### Response - -| Status code | Description | -| ----------- | ----------------------------------------------------------------- | -| 200 | The request is successful. | -| 400 | The request is incorrect. Refer to the error message for details. | - -##### Example - -###### Request - -```shell -$ curl -X PUT "http://127.0.0.1:19121/system/task" -H "accept: application/json" -d "{\"load\": {\"collection_name\": \"test_collection\"}}" -``` - -###### Response - -```json -{ "code": 0, "message": "success" } -``` - -## Index and search parameters - -For each index type, the RESTful API has specific index parameters and search parameters. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      Index typeCreate index paramSearch param
      IVFFLAT
      {"nlist": $int}
      {"nprobe": $int}
      IVFPQ
      {"m": $int, "nlist": $int}
      {"nprobe": $int}
      IVFSQ8
      {"nlist": $int}
      {"nprobe": $int}
      IVFSQ8H
      {"nlist": $int}
      {"nprobe": $int}
      HNSW
      {"M": $int, "efConstruction": $int}
      {"ef": $int}
      ANNOY
      {"n_trees": $int}
      {"search_k": $int}
      - -For detailed information about the parameters above, refer to [Milvus Indexes](https://milvus.io/docs/guides/index.md) - -## Error Codes - -The RESTful API returns error messages as JSON text. Each type of error message has a specific error code. - -| Type | Code | -| --------------------- | ---- | -| SUCCESS | 0 | -| UNEXPECTED_ERROR | 1 | -| CONNECT_FAILED | 2 | -| PERMISSION_DENIED | 3 | -| TABLE_NOT_EXISTS | 4 | -| ILLEGAL_ARGUMENT | 5 | -| ILLEGAL_RANGE | 6 | -| ILLEGAL_DIMENSION | 7 | -| ILLEGAL_INDEX_TYPE | 8 | -| ILLEGAL_TABLE_NAME | 9 | -| ILLEGAL_TOPK | 10 | -| ILLEGAL_ROWRECORD | 11 | -| ILLEGAL_VECTOR_ID | 12 | -| ILLEGAL_SEARCH_RESULT | 13 | -| FILE_NOT_FOUND | 14 | -| META_FAILED | 15 | -| CACHE_FAILED | 16 | -| CANNOT_CREATE_FOLDER | 17 | -| CANNOT_CREATE_FILE | 18 | -| CANNOT_DELETE_FOLDER | 19 | -| CANNOT_DELETE_FILE | 20 | -| BUILD_INDEX_ERROR | 21 | -| ILLEGAL_NLIST | 22 | -| ILLEGAL_METRIC_TYPE | 23 | -| OUT_OF_MEMORY | 24 | -| PATH_PARAM_LOSS | 31 | -| UNKNOWN_PATH | 32 | -| QUERY_PARAM_LOSS | 33 | -| BODY_FIELD_LOSS | 34 | -| ILLEGAL_BODY | 35 | -| BODY_PARSE_FAIL | 36 | -| ILLEGAL_QUERY_PARAM | 37 | diff --git a/core/src/server/web_impl/Types.h b/core/src/server/web_impl/Types.h deleted file mode 100644 index 60e31ebec6..0000000000 --- a/core/src/server/web_impl/Types.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -#include -#include - -#include "server/web_impl/Constants.h" - -namespace milvus { -namespace server { -namespace web { - -using OString = oatpp::data::mapping::type::String; -using OInt8 = oatpp::data::mapping::type::Int8; -using OInt16 = oatpp::data::mapping::type::Int16; -using OInt64 = oatpp::data::mapping::type::Int64; -using OFloat32 = oatpp::data::mapping::type::Float32; -template -using OList = oatpp::data::mapping::type::List; - -using OQueryParams = oatpp::web::protocol::http::QueryParams; - -enum StatusCode : int { - SUCCESS = 0, - UNEXPECTED_ERROR = 1, - CONNECT_FAILED = 2, // reserved. - PERMISSION_DENIED = 3, - COLLECTION_NOT_EXISTS = 4, // DB_NOT_FOUND || COLLECTION_NOT_EXISTS - ILLEGAL_ARGUMENT = 5, - ILLEGAL_RANGE = 6, - ILLEGAL_DIMENSION = 7, - ILLEGAL_INDEX_TYPE = 8, - ILLEGAL_COLLECTION_NAME = 9, - ILLEGAL_TOPK = 10, - ILLEGAL_ROWRECORD = 11, - ILLEGAL_VECTOR_ID = 12, - ILLEGAL_SEARCH_RESULT = 13, - FILE_NOT_FOUND = 14, - META_FAILED = 15, - CACHE_FAILED = 16, - CANNOT_CREATE_FOLDER = 17, - CANNOT_CREATE_FILE = 18, - CANNOT_DELETE_FOLDER = 19, - CANNOT_DELETE_FILE = 20, - BUILD_INDEX_ERROR = 21, - ILLEGAL_NLIST = 22, - ILLEGAL_METRIC_TYPE = 23, - OUT_OF_MEMORY = 24, - - // HTTP error code - PATH_PARAM_LOSS = 31, - UNKNOWN_PATH = 32, - QUERY_PARAM_LOSS = 33, - BODY_FIELD_LOSS = 34, - ILLEGAL_BODY = 35, - BODY_PARSE_FAIL = 36, - ILLEGAL_QUERY_PARAM = 37, - - MAX = ILLEGAL_QUERY_PARAM -}; - -} // namespace web -} // namespace server -} // namespace milvus diff --git a/core/src/server/web_impl/WebServer.cpp b/core/src/server/web_impl/WebServer.cpp deleted file mode 100644 index 40ddcdb35e..0000000000 --- a/core/src/server/web_impl/WebServer.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include - -#include "config/Config.h" -#include "server/web_impl/WebServer.h" -#include "server/web_impl/controller/WebController.hpp" - -namespace milvus { -namespace server { -namespace web { - -void -WebServer::Start() { - auto& config = Config::GetInstance(); - bool enable = true; - config.GetNetworkConfigHTTPEnable(enable); - if (enable && nullptr == thread_ptr_) { - thread_ptr_ = std::make_shared(&WebServer::StartService, this); - } -} - -void -WebServer::Stop() { - StopService(); - - if (thread_ptr_ != nullptr) { - thread_ptr_->join(); - thread_ptr_ = nullptr; - } -} - -Status -WebServer::StartService() { - SetThreadName("webserv_thread"); - oatpp::base::Environment::init(); - - Config& config = Config::GetInstance(); - std::string port; - STATUS_CHECK(config.GetNetworkConfigHTTPPort(port)); - - { - AppComponent components = AppComponent(std::stoi(port)); - - /* create ApiControllers and add endpoints to router */ - auto user_controller = WebController::createShared(); - auto router = components.http_router_.getObject(); - user_controller->addEndpointsToRouter(router); - - /* Get connection handler component */ - auto connection_handler = components.server_connection_handler_.getObject(); - - /* Get connection provider component */ - auto connection_provider = components.server_connection_provider_.getObject(); - - /* create server */ - auto server = oatpp::network::server::Server(connection_provider, connection_handler); - - std::thread stop_thread([&server, this] { - while (!this->try_stop_.load()) { - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } - - server.stop(); - OATPP_COMPONENT(std::shared_ptr, client_provider); - client_provider->getConnection(); - }); - - // start synchronously - server.run(); - connection_handler->stop(); - stop_thread.join(); - } - oatpp::base::Environment::destroy(); - - return Status::OK(); -} - -Status -WebServer::StopService() { - try_stop_.store(true); - - return Status::OK(); -} - -} // namespace web -} // namespace server -} // namespace milvus diff --git a/core/src/server/web_impl/WebServer.h b/core/src/server/web_impl/WebServer.h deleted file mode 100644 index a87463dfa6..0000000000 --- a/core/src/server/web_impl/WebServer.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include - -#include "server/web_impl/component/AppComponent.hpp" - -#include "utils/Status.h" - -namespace milvus { -namespace server { -namespace web { - -class WebServer { - public: - static WebServer& - GetInstance() { - static WebServer web_server; - return web_server; - } - - void - Start(); - - void - Stop(); - - private: - WebServer() { - try_stop_.store(false); - } - - ~WebServer() = default; - - Status - StartService(); - Status - StopService(); - - private: - std::atomic_bool try_stop_; - - std::shared_ptr thread_ptr_; -}; - -} // namespace web -} // namespace server -} // namespace milvus diff --git a/core/src/server/web_impl/component/AppComponent.hpp b/core/src/server/web_impl/component/AppComponent.hpp deleted file mode 100644 index b4af0dab22..0000000000 --- a/core/src/server/web_impl/component/AppComponent.hpp +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace milvus { -namespace server { -namespace web { - -class AppComponent { - - public: - - explicit AppComponent(int port) : port_(port) { - } - - private: - const int port_; - - public: - OATPP_CREATE_COMPONENT(std::shared_ptr, server_connection_provider_) - ([this] { - try { - return oatpp::network::server::SimpleTCPConnectionProvider::createShared(this->port_); - } catch (std::exception& e) { - std::string error_msg = "Cannot bind http port " + std::to_string(this->port_) + ". " + e.what() + - "(errno: " + std::to_string(errno) + ", details: " + strerror(errno) + ")"; - std::cerr << error_msg << std::endl; - throw std::runtime_error(error_msg); - } - }()); - - OATPP_CREATE_COMPONENT(std::shared_ptr, client_connection_provider_) - ([this] { - return oatpp::network::client::SimpleTCPConnectionProvider::createShared("localhost", this->port_); - }()); - - OATPP_CREATE_COMPONENT(std::shared_ptr, http_router_)([] { - return oatpp::web::server::HttpRouter::createShared(); - }()); - - OATPP_CREATE_COMPONENT(std::shared_ptr, server_connection_handler_)([] { - OATPP_COMPONENT(std::shared_ptr, router); - return oatpp::web::server::HttpConnectionHandler::createShared(router); - }()); - - OATPP_CREATE_COMPONENT(std::shared_ptr, api_object_mapper_)([] { - auto serializerConfig = oatpp::parser::json::mapping::Serializer::Config::createShared(); - auto deserializerConfig = oatpp::parser::json::mapping::Deserializer::Config::createShared(); - deserializerConfig->allowUnknownFields = false; - return oatpp::parser::json::mapping::ObjectMapper::createShared(serializerConfig, deserializerConfig); - }()); -}; - -} //namespace web -} //namespace server -} //namespace milvus diff --git a/core/src/server/web_impl/controller/WebController.hpp b/core/src/server/web_impl/controller/WebController.hpp deleted file mode 100644 index 9d7a1ab749..0000000000 --- a/core/src/server/web_impl/controller/WebController.hpp +++ /dev/null @@ -1,773 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -#include -#include -#include -#include - -#include "server/web_impl/Constants.h" -#include "server/web_impl/dto/CollectionDto.hpp" -#include "server/web_impl/dto/ConfigDto.hpp" -#include "server/web_impl/dto/IndexDto.hpp" -#include "server/web_impl/dto/PartitionDto.hpp" -#include "server/web_impl/dto/VectorDto.hpp" -#include "server/web_impl/handler/WebRequestHandler.h" -#include "utils/Log.h" -#include "utils/TimeRecorder.h" - -#define WEB_LOG_PREFIX "[Web] " - -namespace milvus { -namespace server { -namespace web { - -class WebController : public oatpp::web::server::api::ApiController { - public: - WebController(const std::shared_ptr& objectMapper) - : oatpp::web::server::api::ApiController(objectMapper) { - } - - public: - static std::shared_ptr - createShared(OATPP_COMPONENT(std::shared_ptr, objectMapper)) { - return std::make_shared(objectMapper); - } - - /** - * Begin ENDPOINTs generation ('ApiController' codegen) - */ -#include OATPP_CODEGEN_BEGIN(ApiController) - - ADD_CORS(root) - - ENDPOINT("GET", "/", root) { - auto response = createResponse(Status::CODE_200, "Welcome to milvus"); - response->putHeader(Header::CONTENT_TYPE, "text/plain"); - return response; - } - - ADD_CORS(State) - - ENDPOINT("GET", "/state", State) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "GET \'/state\'"); - tr.ElapseFromBegin("Total cost "); - return createDtoResponse(Status::CODE_200, StatusDto::createShared()); - } - - ADD_CORS(GetDevices) - - ENDPOINT("GET", "/devices", GetDevices) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "GET \'/devices\'"); - tr.RecordSection("Receive request"); - - auto devices_dto = DevicesDto::createShared(); - WebRequestHandler handler = WebRequestHandler(); - auto status_dto = handler.GetDevices(devices_dto); - std::shared_ptr response; - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createDtoResponse(Status::CODE_200, devices_dto); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - tr.ElapseFromBegin("Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"); - - return response; - } - - ADD_CORS(AdvancedConfigOptions) - - ENDPOINT("OPTIONS", "/config/advanced", AdvancedConfigOptions) { - return createResponse(Status::CODE_204, "No Content"); - } - - ADD_CORS(GetAdvancedConfig) - - ENDPOINT("GET", "/config/advanced", GetAdvancedConfig) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "GET \'/config/advanced\'"); - tr.RecordSection("Received request."); - - auto config_dto = AdvancedConfigDto::createShared(); - WebRequestHandler handler = WebRequestHandler(); - auto status_dto = handler.GetAdvancedConfig(config_dto); - - std::shared_ptr response; - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createDtoResponse(Status::CODE_200, config_dto); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - tr.ElapseFromBegin("Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"); - - return response; - } - - ADD_CORS(SetAdvancedConfig) - - ENDPOINT("PUT", "/config/advanced", SetAdvancedConfig, BODY_DTO(AdvancedConfigDto::ObjectWrapper, body)) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "PUT \'/config/advanced\'"); - tr.RecordSection("Received request."); - - WebRequestHandler handler = WebRequestHandler(); - - std::shared_ptr response; - auto status_dto = handler.SetAdvancedConfig(body); - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createDtoResponse(Status::CODE_200, status_dto); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - tr.ElapseFromBegin("Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"); - - return response; - } - -#ifdef MILVUS_GPU_VERSION - - ADD_CORS(GPUConfigOptions) - - ENDPOINT("OPTIONS", "/config/gpu_resources", GPUConfigOptions) { - return createResponse(Status::CODE_204, "No Content"); - } - - ADD_CORS(GetGPUConfig) - - ENDPOINT("GET", "/config/gpu_resources", GetGPUConfig) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "GET \'/config/gpu_resources\'"); - tr.RecordSection("Received request"); - - auto gpu_config_dto = GPUConfigDto::createShared(); - WebRequestHandler handler = WebRequestHandler(); - - std::shared_ptr response; - auto status_dto = handler.GetGpuConfig(gpu_config_dto); - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createDtoResponse(Status::CODE_200, gpu_config_dto); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - std::string ttr = "Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"; - tr.ElapseFromBegin(ttr); - - return response; - } - - ADD_CORS(SetGPUConfig) - - ENDPOINT("PUT", "/config/gpu_resources", SetGPUConfig, BODY_DTO(GPUConfigDto::ObjectWrapper, body)) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "PUT \'/config/gpu_resources\'"); - tr.RecordSection("Received request."); - - WebRequestHandler handler = WebRequestHandler(); - auto status_dto = handler.SetGpuConfig(body); - - std::shared_ptr response; - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createDtoResponse(Status::CODE_200, status_dto); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - std::string ttr = "Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"; - tr.ElapseFromBegin(ttr); - return response; - } - -#endif - - ADD_CORS(CollectionsOptions) - - ENDPOINT("OPTIONS", "/collections", CollectionsOptions) { - return createResponse(Status::CODE_204, "No Content"); - } - - ADD_CORS(CreateCollection) - - ENDPOINT("POST", "/collections", CreateCollection, BODY_DTO(CollectionRequestDto::ObjectWrapper, body)) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "POST \'/collections\'"); - tr.RecordSection("Received request."); - - WebRequestHandler handler = WebRequestHandler(); - - std::shared_ptr response; - auto status_dto = handler.CreateCollection(body); - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createDtoResponse(Status::CODE_201, status_dto); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - std::string ttr = "Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"; - tr.ElapseFromBegin(ttr); - return response; - } - - ADD_CORS(ShowCollections) - - ENDPOINT("GET", "/collections", ShowCollections, QUERIES(const QueryParams&, query_params)) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "GET \'/collections\'"); - tr.RecordSection("Received request."); - - WebRequestHandler handler = WebRequestHandler(); - - String result; - auto status_dto = handler.ShowCollections(query_params, result); - std::shared_ptr response; - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createResponse(Status::CODE_200, result); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - std::string ttr = "Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"; - tr.ElapseFromBegin(ttr); - - return response; - } - - ADD_CORS(CollectionOptions) - - ENDPOINT("OPTIONS", "/collections/{collection_name}", CollectionOptions) { - return createResponse(Status::CODE_204, "No Content"); - } - - ADD_CORS(GetCollection) - - ENDPOINT("GET", "/collections/{collection_name}", GetCollection, PATH(String, collection_name), - QUERIES(const QueryParams&, query_params)) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "GET \'/collections/" + collection_name->std_str() + "\'"); - tr.RecordSection("Received request."); - - WebRequestHandler handler = WebRequestHandler(); - - String response_str; - auto status_dto = handler.GetCollection(collection_name, query_params, response_str); - - std::shared_ptr response; - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createResponse(Status::CODE_200, response_str); - break; - case StatusCode::COLLECTION_NOT_EXISTS: - response = createDtoResponse(Status::CODE_404, status_dto); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - std::string ttr = "Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"; - tr.ElapseFromBegin(ttr); - - return response; - } - - ADD_CORS(DropCollection) - - ENDPOINT("DELETE", "/collections/{collection_name}", DropCollection, PATH(String, collection_name)) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "DELETE \'/collections/" + collection_name->std_str() + "\'"); - tr.RecordSection("Received request."); - - WebRequestHandler handler = WebRequestHandler(); - - std::shared_ptr response; - auto status_dto = handler.DropCollection(collection_name); - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createDtoResponse(Status::CODE_204, status_dto); - break; - case StatusCode::COLLECTION_NOT_EXISTS: - response = createDtoResponse(Status::CODE_404, status_dto); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - std::string ttr = "Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"; - tr.ElapseFromBegin(ttr); - - return response; - } - - ADD_CORS(IndexOptions) - - ENDPOINT("OPTIONS", "/collections/{collection_name}/indexes", IndexOptions) { - return createResponse(Status::CODE_204, "No Content"); - } - - ADD_CORS(CreateIndex) - - ENDPOINT("POST", "/collections/{collection_name}/indexes", CreateIndex, PATH(String, collection_name), - BODY_STRING(String, body)) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "POST \'/tables/" + collection_name->std_str() + "/indexes\'"); - tr.RecordSection("Received request."); - - auto handler = WebRequestHandler(); - - std::shared_ptr response; - auto status_dto = handler.CreateIndex(collection_name, body); - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createDtoResponse(Status::CODE_201, status_dto); - break; - case StatusCode::COLLECTION_NOT_EXISTS: - response = createDtoResponse(Status::CODE_404, status_dto); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - std::string ttr = "Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"; - tr.ElapseFromBegin(ttr); - - return response; - } - - ADD_CORS(GetIndex) - - ENDPOINT("GET", "/collections/{collection_name}/indexes", GetIndex, PATH(String, collection_name)) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "GET \'/collections/" + collection_name->std_str() + - "/indexes\'"); - tr.RecordSection("Received request."); - - auto handler = WebRequestHandler(); - - OString result; - auto status_dto = handler.GetIndex(collection_name, result); - - std::shared_ptr response; - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createResponse(Status::CODE_200, result); - break; - case StatusCode::COLLECTION_NOT_EXISTS: - response = createDtoResponse(Status::CODE_404, status_dto); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - std::string ttr = "Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"; - tr.ElapseFromBegin(ttr); - - return response; - } - - ADD_CORS(DropIndex) - - ENDPOINT("DELETE", "/collections/{collection_name}/indexes", DropIndex, PATH(String, collection_name)) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "DELETE \'/collections/" + collection_name->std_str() + - "/indexes\'"); - tr.RecordSection("Received request."); - - auto handler = WebRequestHandler(); - - std::shared_ptr response; - auto status_dto = handler.DropIndex(collection_name); - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createDtoResponse(Status::CODE_204, status_dto); - break; - case StatusCode::COLLECTION_NOT_EXISTS: - response = createDtoResponse(Status::CODE_404, status_dto); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - std::string ttr = "Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"; - tr.ElapseFromBegin(ttr); - - return response; - } - - ADD_CORS(PartitionsOptions) - - ENDPOINT("OPTIONS", "/collections/{collection_name}/partitions", PartitionsOptions) { - return createResponse(Status::CODE_204, "No Content"); - } - - ADD_CORS(CreatePartition) - - ENDPOINT("POST", "/collections/{collection_name}/partitions", CreatePartition, PATH(String, collection_name), - BODY_DTO(PartitionRequestDto::ObjectWrapper, body)) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "POST \'/collections/" + collection_name->std_str() + - "/partitions\'"); - tr.RecordSection("Received request."); - - auto handler = WebRequestHandler(); - - std::shared_ptr response; - auto status_dto = handler.CreatePartition(collection_name, body); - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createDtoResponse(Status::CODE_201, status_dto); - break; - case StatusCode::COLLECTION_NOT_EXISTS: - response = createDtoResponse(Status::CODE_404, status_dto); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - tr.ElapseFromBegin("Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"); - - return response; - } - - ADD_CORS(ShowPartitions) - - ENDPOINT("GET", "/collections/{collection_name}/partitions", ShowPartitions, PATH(String, collection_name), - QUERIES(const QueryParams&, query_params)) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "GET \'/collections/" + collection_name->std_str() + - "/partitions\'"); - tr.RecordSection("Received request."); - - auto offset = query_params.get("offset"); - auto page_size = query_params.get("page_size"); - - auto partition_list_dto = PartitionListDto::createShared(); - auto handler = WebRequestHandler(); - - std::shared_ptr response; - auto status_dto = handler.ShowPartitions(collection_name, query_params, partition_list_dto); - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createDtoResponse(Status::CODE_200, partition_list_dto); - break; - case StatusCode::COLLECTION_NOT_EXISTS: - response = createDtoResponse(Status::CODE_404, status_dto); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - tr.ElapseFromBegin("Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"); - - return response; - } - - ADD_CORS(DropPartition) - - ENDPOINT("DELETE", "/collections/{collection_name}/partitions", DropPartition, PATH(String, collection_name), - BODY_STRING(String, body)) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "DELETE \'/collections/" + collection_name->std_str() + - "/partitions\'"); - tr.RecordSection("Received request."); - - auto handler = WebRequestHandler(); - - std::shared_ptr response; - auto status_dto = handler.DropPartition(collection_name, body); - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createDtoResponse(Status::CODE_204, status_dto); - break; - case StatusCode::COLLECTION_NOT_EXISTS: - response = createDtoResponse(Status::CODE_404, status_dto); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - tr.ElapseFromBegin("Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"); - - return response; - } - - ADD_CORS(ShowSegments) - - ENDPOINT("GET", "/collections/{collection_name}/segments", ShowSegments, PATH(String, collection_name), - QUERIES(const QueryParams&, query_params)) { - auto offset = query_params.get("offset"); - auto page_size = query_params.get("page_size"); - - auto handler = WebRequestHandler(); - String response; - auto status_dto = handler.ShowSegments(collection_name, query_params, response); - - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - return createResponse(Status::CODE_200, response); - case StatusCode::COLLECTION_NOT_EXISTS: - return createDtoResponse(Status::CODE_404, status_dto); - default: - return createDtoResponse(Status::CODE_400, status_dto); - } - } - - ADD_CORS(GetSegmentInfo) - /** - * - * GetSegmentVector - */ - ENDPOINT("GET", "/collections/{collection_name}/segments/{segment_name}/{info}", GetSegmentInfo, - PATH(String, collection_name), PATH(String, segment_name), PATH(String, info), - QUERIES(const QueryParams&, query_params)) { - auto offset = query_params.get("offset"); - auto page_size = query_params.get("page_size"); - - auto handler = WebRequestHandler(); - String response; - auto status_dto = handler.GetSegmentInfo(collection_name, segment_name, info, query_params, response); - - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - return createResponse(Status::CODE_200, response); - case StatusCode::COLLECTION_NOT_EXISTS: - return createDtoResponse(Status::CODE_404, status_dto); - default: - return createDtoResponse(Status::CODE_400, status_dto); - } - } - - ADD_CORS(VectorsOptions) - - ENDPOINT("OPTIONS", "/collections/{collection_name}/vectors", VectorsOptions) { - return createResponse(Status::CODE_204, "No Content"); - } - - ADD_CORS(GetVectors) - /** - * - * GetVectorByID ?id= - */ - ENDPOINT("GET", "/collections/{collection_name}/vectors", GetVectors, - PATH(String, collection_name), QUERIES(const QueryParams&, query_params)) { - auto handler = WebRequestHandler(); - String response; - auto status_dto = handler.GetVector(collection_name, query_params, response); - - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - return createResponse(Status::CODE_200, response); - case StatusCode::COLLECTION_NOT_EXISTS: - return createDtoResponse(Status::CODE_404, status_dto); - default: - return createDtoResponse(Status::CODE_400, status_dto); - } - } - - ADD_CORS(Insert) - - ENDPOINT("POST", "/collections/{collection_name}/vectors", Insert, PATH(String, collection_name), - BODY_STRING(String, body)) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "POST \'/collections/" + collection_name->std_str() + - "/vectors\'"); - tr.RecordSection("Received request."); - - auto ids_dto = VectorIdsDto::createShared(); - WebRequestHandler handler = WebRequestHandler(); - - std::shared_ptr response; - auto status_dto = handler.Insert(collection_name, body, ids_dto); - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createDtoResponse(Status::CODE_201, ids_dto); - break; - case StatusCode::COLLECTION_NOT_EXISTS: - response = createDtoResponse(Status::CODE_404, status_dto); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - tr.ElapseFromBegin("Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"); - - return response; - } - - ADD_CORS(InsertEntity) - - ENDPOINT("POST", "/hybrid_collections/{collection_name}/entities", InsertEntity, PATH(String, collection_name), - BODY_STRING(String, body)) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "POST \'/hybrid_collections/" + collection_name->std_str() + - "/entities\'"); - tr.RecordSection("Received request."); - - auto ids_dto = VectorIdsDto::createShared(); - WebRequestHandler handler = WebRequestHandler(); - - std::shared_ptr response; - auto status_dto = handler.InsertEntity(collection_name, body, ids_dto); - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createDtoResponse(Status::CODE_201, ids_dto); - break; - case StatusCode::COLLECTION_NOT_EXISTS: - response = createDtoResponse(Status::CODE_404, status_dto); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - tr.ElapseFromBegin("Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"); - - return response; - } - - ADD_CORS(VectorsOp) - - ENDPOINT("PUT", "/collections/{collection_name}/vectors", VectorsOp, PATH(String, collection_name), - BODY_STRING(String, body)) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "PUT \'/collections/" + collection_name->std_str() + - "/vectors\'"); - tr.RecordSection("Received request."); - - WebRequestHandler handler = WebRequestHandler(); - - OString result; - std::shared_ptr response; - auto status_dto = handler.VectorsOp(collection_name, body, result); - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createResponse(Status::CODE_200, result); - break; - case StatusCode::COLLECTION_NOT_EXISTS: - response = createDtoResponse(Status::CODE_404, status_dto); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - tr.ElapseFromBegin("Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"); - - return response; - } - - ADD_CORS(SystemOptions) - - ENDPOINT("OPTIONS", "/system/{info}", SystemOptions) { - return createResponse(Status::CODE_204, "No Content"); - } - - ADD_CORS(SystemInfo) - - ENDPOINT("GET", "/system/{info}", SystemInfo, PATH(String, info), QUERIES(const QueryParams&, query_params)) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "GET \'/system/" + info->std_str() + "\'"); - tr.RecordSection("Received request."); - - WebRequestHandler handler = WebRequestHandler(); - OString result = ""; - auto status_dto = handler.SystemInfo(info, query_params, result); - std::shared_ptr response; - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createResponse(Status::CODE_200, result); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - tr.ElapseFromBegin("Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"); - - return response; - } - - ADD_CORS(SystemOp) - - ENDPOINT("PUT", "/system/{op}", SystemOp, PATH(String, op), BODY_STRING(String, body_str)) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "PUT \'/system/" + op->std_str() + "\'"); - tr.RecordSection("Received request."); - - WebRequestHandler handler = WebRequestHandler(); - handler.RegisterRequestHandler(::milvus::server::RequestHandler()); - - String response_str; - auto status_dto = handler.SystemOp(op, body_str, response_str); - - std::shared_ptr response; - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createResponse(Status::CODE_200, response_str); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - tr.ElapseFromBegin("Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"); - - return response; - } - - ADD_CORS(CreateHybridCollection) - - ENDPOINT("POST", "/hybrid_collections", CreateHybridCollection, BODY_STRING(String, body_str)) { - TimeRecorder tr(std::string(WEB_LOG_PREFIX) + "POST \'/hybrid_collections\'"); - tr.RecordSection("Received request."); - WebRequestHandler handler = WebRequestHandler(); - - std::shared_ptr response; - auto status_dto = handler.CreateHybridCollection(body_str); - switch (status_dto->code->getValue()) { - case StatusCode::SUCCESS: - response = createDtoResponse(Status::CODE_201, status_dto); - break; - default: - response = createDtoResponse(Status::CODE_400, status_dto); - } - - std::string ttr = "Done. Status: code = " + std::to_string(status_dto->code->getValue()) + - ", reason = " + status_dto->message->std_str() + ". Total cost"; - tr.ElapseFromBegin(ttr); - return response; - } - -/** - * Finish ENDPOINTs generation ('ApiController' codegen) - */ -#include OATPP_CODEGEN_END(ApiController) -}; - -} // namespace web -} // namespace server -} // namespace milvus diff --git a/core/src/server/web_impl/dto/CollectionDto.hpp b/core/src/server/web_impl/dto/CollectionDto.hpp deleted file mode 100644 index a61327073c..0000000000 --- a/core/src/server/web_impl/dto/CollectionDto.hpp +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/web_impl/dto/Dto.h" -#include "server/web_impl/dto/StatusDto.hpp" -#include "server/web_impl/Constants.h" - -namespace milvus { -namespace server { -namespace web { - -#include OATPP_CODEGEN_BEGIN(DTO) - -class CollectionRequestDto : public oatpp::data::mapping::type::Object { - DTO_INIT(CollectionRequestDto, Object) - - DTO_FIELD(String, collection_name, "collection_name"); - DTO_FIELD(Int64, dimension, "dimension"); - DTO_FIELD(Int64, index_file_size, "index_file_size") = VALUE_COLLECTION_INDEX_FILE_SIZE_DEFAULT; - DTO_FIELD(String, metric_type, "metric_type") = VALUE_COLLECTION_METRIC_TYPE_DEFAULT; -}; - -class CollectionFieldsDto : public oatpp::data::mapping::type::Object { - DTO_INIT(CollectionFieldsDto, Object) - - DTO_FIELD(String, collection_name); - DTO_FIELD(Int64, dimension); - DTO_FIELD(Int64, index_file_size); - DTO_FIELD(String, metric_type); - DTO_FIELD(Int64, count); - DTO_FIELD(String, index); - DTO_FIELD(String, index_params); -}; - -class CollectionListDto : public OObject { - DTO_INIT(CollectionListDto, Object) - - DTO_FIELD(List::ObjectWrapper, collection_names); -}; - -class CollectionListFieldsDto : public OObject { - DTO_INIT(CollectionListFieldsDto, Object) - - DTO_FIELD(List::ObjectWrapper, collections); - DTO_FIELD(Int64, count) = 0L; -}; - -#include OATPP_CODEGEN_END(DTO) - -} // namespace web -} // namespace server -} // namespace milvus \ No newline at end of file diff --git a/core/src/server/web_impl/dto/ConfigDto.hpp b/core/src/server/web_impl/dto/ConfigDto.hpp deleted file mode 100644 index dc857ee369..0000000000 --- a/core/src/server/web_impl/dto/ConfigDto.hpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/web_impl/Constants.h" -#include "server/web_impl/dto/Dto.h" - -namespace milvus { -namespace server { -namespace web { - -#include OATPP_CODEGEN_BEGIN(DTO) - -class AdvancedConfigDto : public OObject { - DTO_INIT(AdvancedConfigDto, Object); - - DTO_FIELD(Int64, cpu_cache_capacity) = VALUE_CONFIG_CPU_CACHE_CAPACITY_DEFAULT; - DTO_FIELD(Boolean, cache_insert_data) = VALUE_CONFIG_CACHE_INSERT_DATA_DEFAULT; - DTO_FIELD(Int64, use_blas_threshold) = 1100; - -#ifdef MILVUS_GPU_VERSION - DTO_FIELD(Int64, gpu_search_threshold) = 1000; - -#endif -}; - -class GPUConfigDto : public OObject { - DTO_INIT(GPUConfigDto, Object); - - DTO_FIELD(Boolean, enable) = true; - DTO_FIELD(Int64, cache_capacity) = 1; - DTO_FIELD(List::ObjectWrapper, search_resources); - DTO_FIELD(List::ObjectWrapper, build_index_resources); -}; - -#include OATPP_CODEGEN_END(DTO) - -} // namespace web -} // namespace server -} // namespace milvus diff --git a/core/src/server/web_impl/dto/DevicesDto.hpp b/core/src/server/web_impl/dto/DevicesDto.hpp deleted file mode 100644 index de9c737093..0000000000 --- a/core/src/server/web_impl/dto/DevicesDto.hpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/web_impl/dto/Dto.h" - -namespace milvus { -namespace server { -namespace web { - -#include OATPP_CODEGEN_BEGIN(DTO) - -class DeviceInfoDto : public OObject { - DTO_INIT(DeviceInfoDto, Object); - - DTO_FIELD(Int64, memory); -}; - -class DevicesDto : public OObject { - DTO_INIT(DevicesDto, Object); - - DTO_FIELD(DeviceInfoDto::ObjectWrapper, cpu); - DTO_FIELD(Fields::ObjectWrapper, gpus); -}; - -#include OATPP_CODEGEN_END(DTO) - -} // namespace web -} // namespace server -} // namespace milvus diff --git a/core/src/server/web_impl/dto/Dto.h b/core/src/server/web_impl/dto/Dto.h deleted file mode 100644 index feb814d14b..0000000000 --- a/core/src/server/web_impl/dto/Dto.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "oatpp/core/data/mapping/type/Object.hpp" -#include "oatpp/core/macro/codegen.hpp" - -namespace milvus { -namespace server { -namespace web { - -using OObject = oatpp::data::mapping::type::Object; -} -} // namespace server -} // namespace milvus diff --git a/core/src/server/web_impl/dto/IndexDto.hpp b/core/src/server/web_impl/dto/IndexDto.hpp deleted file mode 100644 index 9b2176561f..0000000000 --- a/core/src/server/web_impl/dto/IndexDto.hpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/web_impl/dto/Dto.h" -#include "server/web_impl/dto/StatusDto.hpp" -#include "server/web_impl/Constants.h" - -namespace milvus { -namespace server { -namespace web { - -#include OATPP_CODEGEN_BEGIN(DTO) - -class IndexRequestDto : public oatpp::data::mapping::type::Object { - DTO_INIT(IndexRequestDto, Object) - - DTO_FIELD(String, index_type) = VALUE_INDEX_INDEX_TYPE_DEFAULT; - - DTO_FIELD(String, params) = VALUE_INDEX_NLIST_DEFAULT; -}; - -using IndexDto = IndexRequestDto; - -#include OATPP_CODEGEN_END(DTO) - -} // namespace web -} // namespace server -} // namespace milvus diff --git a/core/src/server/web_impl/dto/PartitionDto.hpp b/core/src/server/web_impl/dto/PartitionDto.hpp deleted file mode 100644 index 01d6717eae..0000000000 --- a/core/src/server/web_impl/dto/PartitionDto.hpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/web_impl/dto/Dto.h" -#include "server/web_impl/dto/StatusDto.hpp" - -namespace milvus { -namespace server { -namespace web { - -#include OATPP_CODEGEN_BEGIN(DTO) - -class PartitionRequestDto : public oatpp::data::mapping::type::Object { - DTO_INIT(PartitionRequestDto, Object) - - DTO_FIELD(String, partition_tag); -}; - -using PartitionFieldsDto = PartitionRequestDto; - -class PartitionListDto : public oatpp::data::mapping::type::Object { - DTO_INIT(PartitionListDto, Object) - - DTO_FIELD(List::ObjectWrapper, partitions); - DTO_FIELD(Int64, count) = 0L; -}; - -#include OATPP_CODEGEN_END(DTO) - -} // namespace web -} // namespace server -} // namespace milvus diff --git a/core/src/server/web_impl/dto/StatusDto.hpp b/core/src/server/web_impl/dto/StatusDto.hpp deleted file mode 100644 index d818afa3db..0000000000 --- a/core/src/server/web_impl/dto/StatusDto.hpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/web_impl/dto/Dto.h" - -namespace milvus { -namespace server { -namespace web { - -#include OATPP_CODEGEN_BEGIN(DTO) - -class StatusDto: public oatpp::data::mapping::type::Object { - - DTO_INIT(StatusDto, Object) - - DTO_FIELD(String, message) = "Success"; - DTO_FIELD(Int64, code) = 0L; -}; - -#include OATPP_CODEGEN_END(DTO) - -} // namespace web -} // namespace server -} // namespace milvus diff --git a/core/src/server/web_impl/dto/VectorDto.hpp b/core/src/server/web_impl/dto/VectorDto.hpp deleted file mode 100644 index 54f3a1ac11..0000000000 --- a/core/src/server/web_impl/dto/VectorDto.hpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "server/web_impl/dto/Dto.h" -#include "server/web_impl/Constants.h" - -namespace milvus { -namespace server { -namespace web { - -#include OATPP_CODEGEN_BEGIN(DTO) - -class VectorIdsDto : public oatpp::data::mapping::type::Object { - DTO_INIT(VectorIdsDto, Object) - - DTO_FIELD(List::ObjectWrapper, ids); -}; - -#include OATPP_CODEGEN_END(DTO) - -} // namespace web -} // namespace server -} // namespace milvus diff --git a/core/src/server/web_impl/handler/WebRequestHandler.cpp b/core/src/server/web_impl/handler/WebRequestHandler.cpp deleted file mode 100644 index 69adb6e76c..0000000000 --- a/core/src/server/web_impl/handler/WebRequestHandler.cpp +++ /dev/null @@ -1,1818 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/web_impl/handler/WebRequestHandler.h" - -#include -#include -#include -#include -#include - -#include - -#include "config/Config.h" -#include "metrics/SystemInfo.h" -#include "server/delivery/request/BaseRequest.h" -#include "server/web_impl/Constants.h" -#include "server/web_impl/Types.h" -#include "server/web_impl/dto/PartitionDto.hpp" -#include "server/web_impl/utils/Util.h" -#include "thirdparty/nlohmann/json.hpp" -#include "utils/StringHelpFunctions.h" -#include "utils/ValidationUtil.h" - -namespace milvus { -namespace server { -namespace web { - -StatusCode -WebErrorMap(ErrorCode code) { - static const std::map code_map = { - {SERVER_UNEXPECTED_ERROR, StatusCode::UNEXPECTED_ERROR}, - {SERVER_UNSUPPORTED_ERROR, StatusCode::UNEXPECTED_ERROR}, - {SERVER_NULL_POINTER, StatusCode::UNEXPECTED_ERROR}, - {SERVER_INVALID_ARGUMENT, StatusCode::ILLEGAL_ARGUMENT}, - {SERVER_FILE_NOT_FOUND, StatusCode::FILE_NOT_FOUND}, - {SERVER_NOT_IMPLEMENT, StatusCode::UNEXPECTED_ERROR}, - {SERVER_CANNOT_CREATE_FOLDER, StatusCode::CANNOT_CREATE_FOLDER}, - {SERVER_CANNOT_CREATE_FILE, StatusCode::CANNOT_CREATE_FILE}, - {SERVER_CANNOT_DELETE_FOLDER, StatusCode::CANNOT_DELETE_FOLDER}, - {SERVER_CANNOT_DELETE_FILE, StatusCode::CANNOT_DELETE_FILE}, - {SERVER_COLLECTION_NOT_EXIST, StatusCode::COLLECTION_NOT_EXISTS}, - {SERVER_INVALID_COLLECTION_NAME, StatusCode::ILLEGAL_COLLECTION_NAME}, - {SERVER_INVALID_COLLECTION_DIMENSION, StatusCode::ILLEGAL_DIMENSION}, - {SERVER_INVALID_VECTOR_DIMENSION, StatusCode::ILLEGAL_DIMENSION}, - - {SERVER_INVALID_INDEX_TYPE, StatusCode::ILLEGAL_INDEX_TYPE}, - {SERVER_INVALID_ROWRECORD, StatusCode::ILLEGAL_ROWRECORD}, - {SERVER_INVALID_ROWRECORD_ARRAY, StatusCode::ILLEGAL_ROWRECORD}, - {SERVER_INVALID_TOPK, StatusCode::ILLEGAL_TOPK}, - {SERVER_INVALID_NPROBE, StatusCode::ILLEGAL_ARGUMENT}, - {SERVER_INVALID_INDEX_NLIST, StatusCode::ILLEGAL_NLIST}, - {SERVER_INVALID_INDEX_METRIC_TYPE, StatusCode::ILLEGAL_METRIC_TYPE}, - {SERVER_INVALID_INDEX_FILE_SIZE, StatusCode::ILLEGAL_ARGUMENT}, - {SERVER_ILLEGAL_VECTOR_ID, StatusCode::ILLEGAL_VECTOR_ID}, - {SERVER_ILLEGAL_SEARCH_RESULT, StatusCode::ILLEGAL_SEARCH_RESULT}, - {SERVER_CACHE_FULL, StatusCode::CACHE_FAILED}, - {SERVER_BUILD_INDEX_ERROR, StatusCode::BUILD_INDEX_ERROR}, - {SERVER_OUT_OF_MEMORY, StatusCode::OUT_OF_MEMORY}, - - {DB_NOT_FOUND, StatusCode::COLLECTION_NOT_EXISTS}, - {DB_META_TRANSACTION_FAILED, StatusCode::META_FAILED}, - }; - if (code < StatusCode::MAX) { - return StatusCode(code); - } else if (code_map.find(code) != code_map.end()) { - return code_map.at(code); - } else { - return StatusCode::UNEXPECTED_ERROR; - } -} - -using FloatJson = nlohmann::basic_json; - -/////////////////////////////////// Private methods /////////////////////////////////////// -void -WebRequestHandler::AddStatusToJson(nlohmann::json& json, int64_t code, const std::string& msg) { - json["code"] = (int64_t)code; - json["message"] = msg; -} - -Status -WebRequestHandler::IsBinaryCollection(const std::string& collection_name, bool& bin) { - CollectionSchema schema; - auto status = request_handler_.DescribeCollection(context_ptr_, collection_name, schema); - if (status.ok()) { - auto metric = engine::MetricType(schema.metric_type_); - bin = engine::MetricType::HAMMING == metric || engine::MetricType::JACCARD == metric || - engine::MetricType::TANIMOTO == metric || engine::MetricType::SUPERSTRUCTURE == metric || - engine::MetricType::SUBSTRUCTURE == metric; - } - - return status; -} - -Status -WebRequestHandler::CopyRecordsFromJson(const nlohmann::json& json, engine::VectorsData& vectors, bool bin) { - if (!json.is_array()) { - return Status(ILLEGAL_BODY, "field \"vectors\" must be a array"); - } - - vectors.vector_count_ = json.size(); - - if (!bin) { - for (auto& vec : json) { - if (!vec.is_array()) { - return Status(ILLEGAL_BODY, "A vector in field \"vectors\" must be a float array"); - } - for (auto& data : vec) { - vectors.float_data_.emplace_back(data.get()); - } - } - } else { - for (auto& vec : json) { - if (!vec.is_array()) { - return Status(ILLEGAL_BODY, "A vector in field \"vectors\" must be a float array"); - } - for (auto& data : vec) { - vectors.binary_data_.emplace_back(data.get()); - } - } - } - - return Status::OK(); -} - -///////////////////////// WebRequestHandler methods /////////////////////////////////////// -Status -WebRequestHandler::GetCollectionMetaInfo(const std::string& collection_name, nlohmann::json& json_out) { - CollectionSchema schema; - auto status = request_handler_.DescribeCollection(context_ptr_, collection_name, schema); - if (!status.ok()) { - return status; - } - - int64_t count; - status = request_handler_.CountCollection(context_ptr_, collection_name, count); - if (!status.ok()) { - return status; - } - - IndexParam index_param; - status = request_handler_.DescribeIndex(context_ptr_, collection_name, index_param); - if (!status.ok()) { - return status; - } - - json_out["collection_name"] = schema.collection_name_; - json_out["dimension"] = schema.dimension_; - json_out["index_file_size"] = schema.index_file_size_; - json_out["index"] = IndexMap.at(engine::EngineType(index_param.index_type_)); - json_out["index_params"] = index_param.extra_params_; - json_out["metric_type"] = MetricMap.at(engine::MetricType(schema.metric_type_)); - json_out["count"] = count; - - return Status::OK(); -} - -Status -WebRequestHandler::GetCollectionStat(const std::string& collection_name, nlohmann::json& json_out) { - std::string collection_info; - auto status = request_handler_.ShowCollectionInfo(context_ptr_, collection_name, collection_info); - - if (status.ok()) { - try { - json_out = nlohmann::json::parse(collection_info); - } catch (std::exception& e) { - return Status(SERVER_UNEXPECTED_ERROR, - "Error occurred when parsing collection stat information: " + std::string(e.what())); - } - } - - return status; -} - -Status -WebRequestHandler::GetSegmentVectors(const std::string& collection_name, const std::string& segment_name, - int64_t page_size, int64_t offset, nlohmann::json& json_out) { - std::vector vector_ids; - auto status = request_handler_.GetVectorIDs(context_ptr_, collection_name, segment_name, vector_ids); - if (!status.ok()) { - return status; - } - - auto ids_begin = std::min(vector_ids.size(), (size_t)offset); - auto ids_end = std::min(vector_ids.size(), (size_t)(offset + page_size)); - - auto ids = std::vector(vector_ids.begin() + ids_begin, vector_ids.begin() + ids_end); - nlohmann::json vectors_json; - status = GetVectorsByIDs(collection_name, ids, vectors_json); - - nlohmann::json result_json; - if (vectors_json.empty()) { - json_out["vectors"] = std::vector(); - } else { - json_out["vectors"] = vectors_json; - } - json_out["count"] = vector_ids.size(); - - AddStatusToJson(json_out, status.code(), status.message()); - - return Status::OK(); -} - -Status -WebRequestHandler::GetSegmentIds(const std::string& collection_name, const std::string& segment_name, int64_t page_size, - int64_t offset, nlohmann::json& json_out) { - std::vector vector_ids; - auto status = request_handler_.GetVectorIDs(context_ptr_, collection_name, segment_name, vector_ids); - if (status.ok()) { - auto ids_begin = std::min(vector_ids.size(), (size_t)offset); - auto ids_end = std::min(vector_ids.size(), (size_t)(offset + page_size)); - - if (ids_begin >= ids_end) { - json_out["ids"] = std::vector(); - } else { - for (size_t i = ids_begin; i < ids_end; i++) { - json_out["ids"].push_back(std::to_string(vector_ids.at(i))); - } - } - json_out["count"] = vector_ids.size(); - } - - return status; -} - -Status -WebRequestHandler::CommandLine(const std::string& cmd, std::string& reply) { - return request_handler_.Cmd(context_ptr_, cmd, reply); -} - -Status -WebRequestHandler::Cmd(const std::string& cmd, std::string& result_str) { - std::string reply; - auto status = CommandLine(cmd, reply); - - if (status.ok()) { - nlohmann::json result; - AddStatusToJson(result, status.code(), status.message()); - result["reply"] = reply; - result_str = result.dump(); - } - - return status; -} - -Status -WebRequestHandler::PreLoadCollection(const nlohmann::json& json, std::string& result_str) { - if (!json.contains("collection_name")) { - return Status(BODY_FIELD_LOSS, "Field \"load\" must contains collection_name"); - } - - auto collection_name = json["collection_name"]; - auto status = request_handler_.PreloadCollection(context_ptr_, collection_name.get()); - if (status.ok()) { - nlohmann::json result; - AddStatusToJson(result, status.code(), status.message()); - result_str = result.dump(); - } - - return status; -} - -Status -WebRequestHandler::Flush(const nlohmann::json& json, std::string& result_str) { - if (!json.contains("collection_names")) { - return Status(BODY_FIELD_LOSS, "Field \"flush\" must contains collection_names"); - } - - auto collection_names = json["collection_names"]; - if (!collection_names.is_array()) { - return Status(BODY_FIELD_LOSS, "Field \"collection_names\" must be and array"); - } - - std::vector names; - for (auto& name : collection_names) { - names.emplace_back(name.get()); - } - - auto status = request_handler_.Flush(context_ptr_, names); - if (status.ok()) { - nlohmann::json result; - AddStatusToJson(result, status.code(), status.message()); - result_str = result.dump(); - } - - return status; -} - -Status -WebRequestHandler::Compact(const nlohmann::json& json, std::string& result_str) { - if (!json.contains("collection_name")) { - return Status(BODY_FIELD_LOSS, "Field \"compact\" must contains collection_names"); - } - - auto collection_name = json["collection_name"]; - if (!collection_name.is_string()) { - return Status(BODY_FIELD_LOSS, "Field \"collection_names\" must be a string"); - } - - auto name = collection_name.get(); - - double compact_threshold = 0.1; // compact trigger threshold: delete_counts/segment_counts - auto status = request_handler_.Compact(context_ptr_, name, compact_threshold); - - if (status.ok()) { - nlohmann::json result; - AddStatusToJson(result, status.code(), status.message()); - result_str = result.dump(); - } - - return status; -} - -Status -WebRequestHandler::GetConfig(std::string& result_str) { - std::string cmd = "get_config *"; - std::string reply; - auto status = CommandLine(cmd, reply); - if (status.ok()) { - nlohmann::json j = nlohmann::json::parse(reply); -#ifdef MILVUS_GPU_VERSION - if (j.contains("gpu_resource_config")) { - std::vector gpus; - if (j["gpu_resource_config"].contains("search_resources")) { - auto gpu_search_res = j["gpu_resource_config"]["search_resources"].get(); - StringHelpFunctions::SplitStringByDelimeter(gpu_search_res, ",", gpus); - j["gpu_resource_config"]["search_resources"] = gpus; - } - if (j["gpu_resource_config"].contains("build_index_resources")) { - auto gpu_build_res = j["gpu_resource_config"]["build_index_resources"].get(); - gpus.clear(); - StringHelpFunctions::SplitStringByDelimeter(gpu_build_res, ",", gpus); - j["gpu_resource_config"]["build_index_resources"] = gpus; - } - } -#endif - // check if server require start - Config& config = Config::GetInstance(); - bool required = false; - config.GetServerRestartRequired(required); - j["restart_required"] = required; - result_str = j.dump(); - } - - return Status::OK(); -} - -Status -WebRequestHandler::SetConfig(const nlohmann::json& json, std::string& result_str) { - if (!json.is_object()) { - return Status(ILLEGAL_BODY, "Payload must be a map"); - } - - std::vector cmds; - for (auto& el : json.items()) { - auto evalue = el.value(); - if (!evalue.is_object()) { - return Status(ILLEGAL_BODY, "Invalid payload format, the root value must be json map"); - } - - for (auto& iel : el.value().items()) { - auto ievalue = iel.value(); - if (!(ievalue.is_string() || ievalue.is_number() || ievalue.is_boolean())) { - return Status(ILLEGAL_BODY, "Config value must be one of string, numeric or boolean"); - } - std::ostringstream ss; - if (ievalue.is_string()) { - std::string vle = ievalue; - ss << "set_config " << el.key() << "." << iel.key() << " " << vle; - } else { - ss << "set_config " << el.key() << "." << iel.key() << " " << ievalue; - } - cmds.emplace_back(ss.str()); - } - } - - std::string msg; - - for (auto& c : cmds) { - std::string reply; - auto status = CommandLine(c, reply); - if (!status.ok()) { - return status; - } - msg += c + " successfully;"; - } - - nlohmann::json result; - AddStatusToJson(result, StatusCode::SUCCESS, msg); - - bool required = false; - Config& config = Config::GetInstance(); - config.GetServerRestartRequired(required); - result["restart_required"] = required; - - result_str = result.dump(); - - return Status::OK(); -} - -Status -WebRequestHandler::Search(const std::string& collection_name, const nlohmann::json& json, std::string& result_str) { - if (!json.contains("topk")) { - return Status(BODY_FIELD_LOSS, "Field \'topk\' is required"); - } - int64_t topk = json["topk"]; - - if (!json.contains("params")) { - return Status(BODY_FIELD_LOSS, "Field \'params\' is required"); - } - - std::vector partition_tags; - if (json.contains("partition_tags")) { - auto tags = json["partition_tags"]; - if (!tags.is_null() && !tags.is_array()) { - return Status(BODY_PARSE_FAIL, "Field \"partition_tags\" must be a array"); - } - - for (auto& tag : tags) { - partition_tags.emplace_back(tag.get()); - } - } - - TopKQueryResult result; - Status status; - if (json.contains("ids")) { - auto vec_ids = json["ids"]; - if (!vec_ids.is_array()) { - return Status(BODY_PARSE_FAIL, "Field \"ids\" must be ad array"); - } - - std::vector id_array; - for (auto& id_str : vec_ids) { - id_array.emplace_back(std::stol(id_str.get())); - } - // std::vector id_array(vec_ids.begin(), vec_ids.end()); - status = request_handler_.SearchByID(context_ptr_, collection_name, id_array, topk, json["params"], - partition_tags, result); - } else { - std::vector file_id_vec; - if (json.contains("file_ids")) { - auto ids = json["file_ids"]; - if (!ids.is_null() && !ids.is_array()) { - return Status(BODY_PARSE_FAIL, "Field \"file_ids\" must be a array"); - } - for (auto& id : ids) { - file_id_vec.emplace_back(id.get()); - } - } - - bool bin_flag = false; - status = IsBinaryCollection(collection_name, bin_flag); - if (!status.ok()) { - return status; - } - - if (!json.contains("vectors")) { - return Status(BODY_FIELD_LOSS, "Field \"vectors\" is required"); - } - - engine::VectorsData vectors_data; - status = CopyRecordsFromJson(json["vectors"], vectors_data, bin_flag); - if (!status.ok()) { - return status; - } - - status = request_handler_.Search(context_ptr_, collection_name, vectors_data, topk, json["params"], - partition_tags, file_id_vec, result); - } - if (!status.ok()) { - return status; - } - - nlohmann::json result_json; - result_json["num"] = result.row_num_; - if (result.row_num_ == 0) { - result_json["result"] = std::vector(); - result_str = result_json.dump(); - return Status::OK(); - } - - auto step = result.id_list_.size() / result.row_num_; - nlohmann::json search_result_json; - for (int64_t i = 0; i < result.row_num_; i++) { - nlohmann::json raw_result_json; - for (size_t j = 0; j < step; j++) { - nlohmann::json one_result_json; - one_result_json["id"] = std::to_string(result.id_list_.at(i * step + j)); - one_result_json["distance"] = std::to_string(result.distance_list_.at(i * step + j)); - raw_result_json.emplace_back(one_result_json); - } - search_result_json.emplace_back(raw_result_json); - } - result_json["result"] = search_result_json; - result_str = result_json.dump(); - - return Status::OK(); -} - -Status -WebRequestHandler::ProcessLeafQueryJson(const nlohmann::json& json, milvus::query::BooleanQueryPtr& query) { - if (json.contains("term")) { - auto leaf_query = std::make_shared(); - auto term_json = json["term"]; - std::string field_name = term_json["field_name"]; - auto term_value_json = term_json["values"]; - if (!term_value_json.is_array()) { - std::string msg = "Term json string is not an array"; - return Status{BODY_PARSE_FAIL, msg}; - } - - auto term_size = term_value_json.size(); - auto term_query = std::make_shared(); - term_query->field_name = field_name; - term_query->field_value.resize(term_size * sizeof(int64_t)); - - switch (field_type_.at(field_name)) { - case engine::meta::hybrid::DataType::INT8: - case engine::meta::hybrid::DataType::INT16: - case engine::meta::hybrid::DataType::INT32: - case engine::meta::hybrid::DataType::INT64: { - std::vector term_value(term_size, 0); - for (uint64_t i = 0; i < term_size; ++i) { - term_value[i] = term_value_json[i].get(); - } - memcpy(term_query->field_value.data(), term_value.data(), term_size * sizeof(int64_t)); - break; - } - case engine::meta::hybrid::DataType::FLOAT: - case engine::meta::hybrid::DataType::DOUBLE: { - std::vector term_value(term_size, 0); - for (uint64_t i = 0; i < term_size; ++i) { - term_value[i] = term_value_json[i].get(); - } - memcpy(term_query->field_value.data(), term_value.data(), term_size * sizeof(double)); - break; - } - default: - break; - } - - leaf_query->term_query = term_query; - query->AddLeafQuery(leaf_query); - } else if (json.contains("range")) { - auto leaf_query = std::make_shared(); - auto range_query = std::make_shared(); - - auto range_json = json["range"]; - std::string field_name = range_json["field_name"]; - range_query->field_name = field_name; - - auto range_value_json = range_json["values"]; - if (range_value_json.contains("lt")) { - query::CompareExpr compare_expr; - compare_expr.compare_operator = query::CompareOperator::LT; - compare_expr.operand = range_value_json["lt"].get(); - range_query->compare_expr.emplace_back(compare_expr); - } - if (range_value_json.contains("lte")) { - query::CompareExpr compare_expr; - compare_expr.compare_operator = query::CompareOperator::LTE; - compare_expr.operand = range_value_json["lte"].get(); - range_query->compare_expr.emplace_back(compare_expr); - } - if (range_value_json.contains("eq")) { - query::CompareExpr compare_expr; - compare_expr.compare_operator = query::CompareOperator::EQ; - compare_expr.operand = range_value_json["eq"].get(); - range_query->compare_expr.emplace_back(compare_expr); - } - if (range_value_json.contains("ne")) { - query::CompareExpr compare_expr; - compare_expr.compare_operator = query::CompareOperator::NE; - compare_expr.operand = range_value_json["ne"].get(); - range_query->compare_expr.emplace_back(compare_expr); - } - if (range_value_json.contains("gt")) { - query::CompareExpr compare_expr; - compare_expr.compare_operator = query::CompareOperator::GT; - compare_expr.operand = range_value_json["gt"].get(); - range_query->compare_expr.emplace_back(compare_expr); - } - if (range_value_json.contains("gte")) { - query::CompareExpr compare_expr; - compare_expr.compare_operator = query::CompareOperator::GTE; - compare_expr.operand = range_value_json["gte"].get(); - range_query->compare_expr.emplace_back(compare_expr); - } - - leaf_query->range_query = range_query; - query->AddLeafQuery(leaf_query); - } else if (json.contains("vector")) { - auto leaf_query = std::make_shared(); - auto vector_query = std::make_shared(); - - auto vector_json = json["vector"]; - std::string field_name = vector_json["field_name"]; - vector_query->field_name = field_name; - - engine::VectorsData vectors; - // TODO(yukun): process binary vector - CopyRecordsFromJson(vector_json["values"], vectors, false); - - vector_query->query_vector.float_data = vectors.float_data_; - vector_query->query_vector.binary_data = vectors.binary_data_; - - vector_query->topk = vector_json["topk"].get(); - vector_query->extra_params = vector_json["extra_params"]; - - leaf_query->vector_query = vector_query; - query->AddLeafQuery(leaf_query); - } - return Status::OK(); -} - -Status -WebRequestHandler::ProcessBoolQueryJson(const nlohmann::json& query_json, query::BooleanQueryPtr& boolean_query) { - if (query_json.contains("must")) { - boolean_query->SetOccur(query::Occur::MUST); - auto must_json = query_json["must"]; - if (!must_json.is_array()) { - std::string msg = "Must json string is not an array"; - return Status{BODY_PARSE_FAIL, msg}; - } - - for (auto& json : must_json) { - auto must_query = std::make_shared(); - if (json.contains("must") || json.contains("should") || json.contains("must_not")) { - ProcessBoolQueryJson(json, must_query); - boolean_query->AddBooleanQuery(must_query); - } else { - ProcessLeafQueryJson(json, boolean_query); - } - } - return Status::OK(); - } else if (query_json.contains("should")) { - boolean_query->SetOccur(query::Occur::SHOULD); - auto should_json = query_json["should"]; - if (!should_json.is_array()) { - std::string msg = "Should json string is not an array"; - return Status{BODY_PARSE_FAIL, msg}; - } - - for (auto& json : should_json) { - if (json.contains("must") || json.contains("should") || json.contains("must_not")) { - auto should_query = std::make_shared(); - ProcessBoolQueryJson(json, should_query); - boolean_query->AddBooleanQuery(should_query); - } else { - ProcessLeafQueryJson(json, boolean_query); - } - } - return Status::OK(); - } else if (query_json.contains("must_not")) { - boolean_query->SetOccur(query::Occur::MUST_NOT); - auto should_json = query_json["must_not"]; - if (!should_json.is_array()) { - std::string msg = "Must_not json string is not an array"; - return Status{BODY_PARSE_FAIL, msg}; - } - - for (auto& json : should_json) { - if (json.contains("must") || json.contains("should") || json.contains("must_not")) { - auto must_not_query = std::make_shared(); - ProcessBoolQueryJson(json, must_not_query); - boolean_query->AddBooleanQuery(must_not_query); - } else { - ProcessLeafQueryJson(json, boolean_query); - } - } - return Status::OK(); - } else { - std::string msg = "Must json string doesnot include right query"; - return Status{BODY_PARSE_FAIL, msg}; - } -} - -Status -WebRequestHandler::HybridSearch(const std::string& collection_name, const nlohmann::json& json, - std::string& result_str) { - Status status; - - status = request_handler_.DescribeHybridCollection(context_ptr_, collection_name, field_type_); - if (!status.ok()) { - return Status{UNEXPECTED_ERROR, "DescribeHybridCollection failed"}; - } - - std::vector partition_tags; - if (json.contains("partition_tags")) { - auto tags = json["partition_tags"]; - if (!tags.is_null() && !tags.is_array()) { - return Status(BODY_PARSE_FAIL, "Field \"partition_tags\" must be a array"); - } - - for (auto& tag : tags) { - partition_tags.emplace_back(tag.get()); - } - } - - if (json.contains("bool")) { - auto boolean_query_json = json["bool"]; - query::BooleanQueryPtr boolean_query = std::make_shared(); - - status = ProcessBoolQueryJson(boolean_query_json, boolean_query); - if (!status.ok()) { - return status; - } - query::GeneralQueryPtr general_query = std::make_shared(); - query::GenBinaryQuery(boolean_query, general_query->bin); - - context::HybridSearchContextPtr hybrid_search_context = std::make_shared(); - TopKQueryResult result; - status = request_handler_.HybridSearch(context_ptr_, hybrid_search_context, collection_name, partition_tags, - general_query, result); - - if (!status.ok()) { - return status; - } - - nlohmann::json result_json; - result_json["num"] = result.row_num_; - if (result.row_num_ == 0) { - result_json["result"] = std::vector(); - result_str = result_json.dump(); - return Status::OK(); - } - - auto step = result.id_list_.size() / result.row_num_; - nlohmann::json search_result_json; - for (int64_t i = 0; i < result.row_num_; i++) { - nlohmann::json raw_result_json; - for (size_t j = 0; j < step; j++) { - nlohmann::json one_result_json; - one_result_json["id"] = std::to_string(result.id_list_.at(i * step + j)); - one_result_json["distance"] = std::to_string(result.distance_list_.at(i * step + j)); - raw_result_json.emplace_back(one_result_json); - } - search_result_json.emplace_back(raw_result_json); - } - result_json["result"] = search_result_json; - result_str = result_json.dump(); - } - - return Status::OK(); -} - -Status -WebRequestHandler::DeleteByIDs(const std::string& collection_name, const nlohmann::json& json, - std::string& result_str) { - std::vector vector_ids; - if (!json.contains("ids")) { - return Status(BODY_FIELD_LOSS, "Field \"delete\" must contains \"ids\""); - } - auto ids = json["ids"]; - if (!ids.is_array()) { - return Status(BODY_FIELD_LOSS, "\"ids\" must be an array"); - } - - for (auto& id : ids) { - auto id_str = id.get(); - if (!ValidationUtil::ValidateStringIsNumber(id_str).ok()) { - return Status(ILLEGAL_BODY, "Members in \"ids\" must be integer string"); - } - vector_ids.emplace_back(std::stol(id_str)); - } - - auto status = request_handler_.DeleteByID(context_ptr_, collection_name, vector_ids); - - nlohmann::json result_json; - AddStatusToJson(result_json, status.code(), status.message()); - result_str = result_json.dump(); - - return status; -} - -Status -WebRequestHandler::GetVectorsByIDs(const std::string& collection_name, const std::vector& ids, - nlohmann::json& json_out) { - std::vector vector_batch; - auto status = request_handler_.GetVectorsByID(context_ptr_, collection_name, ids, vector_batch); - if (!status.ok()) { - return status; - } - - bool bin; - status = IsBinaryCollection(collection_name, bin); - if (!status.ok()) { - return status; - } - - nlohmann::json vectors_json; - for (size_t i = 0; i < vector_batch.size(); i++) { - nlohmann::json vector_json; - if (bin) { - vector_json["vector"] = vector_batch.at(i).binary_data_; - } else { - vector_json["vector"] = vector_batch.at(i).float_data_; - } - vector_json["id"] = std::to_string(ids[i]); - json_out.push_back(vector_json); - } - - return Status::OK(); -} - -////////////////////////////////// Router methods //////////////////////////////////////////// -StatusDto::ObjectWrapper -WebRequestHandler::GetDevices(DevicesDto::ObjectWrapper& devices_dto) { - auto system_info = SystemInfo::GetInstance(); - - devices_dto->cpu = devices_dto->cpu->createShared(); - devices_dto->cpu->memory = system_info.GetPhysicalMemory() >> 30; - - devices_dto->gpus = devices_dto->gpus->createShared(); - -#ifdef MILVUS_GPU_VERSION - size_t count = system_info.num_device(); - std::vector device_mems = system_info.GPUMemoryTotal(); - - if (count != device_mems.size()) { - RETURN_STATUS_DTO(UNEXPECTED_ERROR, "Can't obtain GPU info"); - } - - for (size_t i = 0; i < count; i++) { - auto device_dto = DeviceInfoDto::createShared(); - device_dto->memory = device_mems.at(i) >> 30; - devices_dto->gpus->put("GPU" + OString(std::to_string(i).c_str()), device_dto); - } -#endif - - ASSIGN_RETURN_STATUS_DTO(Status::OK()); -} - -StatusDto::ObjectWrapper -WebRequestHandler::GetAdvancedConfig(AdvancedConfigDto::ObjectWrapper& advanced_config) { - std::string reply; - std::string cache_cmd_prefix = "get_config " + std::string(CONFIG_CACHE) + "."; - - std::string cache_cmd_string = cache_cmd_prefix + std::string(CONFIG_CACHE_CPU_CACHE_CAPACITY); - auto status = CommandLine(cache_cmd_string, reply); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status) - } - advanced_config->cpu_cache_capacity = std::stol(reply); - - cache_cmd_string = cache_cmd_prefix + std::string(CONFIG_CACHE_CACHE_INSERT_DATA); - CommandLine(cache_cmd_string, reply); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status) - } - advanced_config->cache_insert_data = ("1" == reply || "true" == reply); - - auto engine_cmd_prefix = "get_config " + std::string(CONFIG_ENGINE) + "."; - auto engine_cmd_string = engine_cmd_prefix + std::string(CONFIG_ENGINE_USE_BLAS_THRESHOLD); - CommandLine(engine_cmd_string, reply); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status) - } - advanced_config->use_blas_threshold = std::stol(reply); - -#ifdef MILVUS_GPU_VERSION - engine_cmd_string = engine_cmd_prefix + std::string(CONFIG_GPU_RESOURCE_GPU_SEARCH_THRESHOLD); - CommandLine(engine_cmd_string, reply); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status) - } - advanced_config->gpu_search_threshold = std::stol(reply); -#endif - - ASSIGN_RETURN_STATUS_DTO(status) -} - -StatusDto::ObjectWrapper -WebRequestHandler::SetAdvancedConfig(const AdvancedConfigDto::ObjectWrapper& advanced_config) { - if (nullptr == advanced_config->cpu_cache_capacity.get()) { - RETURN_STATUS_DTO(BODY_FIELD_LOSS, "Field \'cpu_cache_capacity\' miss."); - } - - if (nullptr == advanced_config->cache_insert_data.get()) { - RETURN_STATUS_DTO(BODY_FIELD_LOSS, "Field \'cache_insert_data\' miss."); - } - - if (nullptr == advanced_config->use_blas_threshold.get()) { - RETURN_STATUS_DTO(BODY_FIELD_LOSS, "Field \'use_blas_threshold\' miss."); - } - -#ifdef MILVUS_GPU_VERSION - if (nullptr == advanced_config->gpu_search_threshold.get()) { - RETURN_STATUS_DTO(BODY_FIELD_LOSS, "Field \'gpu_search_threshold\' miss."); - } -#endif - - std::string reply; - std::string cache_cmd_prefix = "set_config " + std::string(CONFIG_CACHE) + "."; - - std::string cache_cmd_string = cache_cmd_prefix + std::string(CONFIG_CACHE_CPU_CACHE_CAPACITY) + " " + - std::to_string(advanced_config->cpu_cache_capacity->getValue()); - auto status = CommandLine(cache_cmd_string, reply); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status) - } - - cache_cmd_string = cache_cmd_prefix + std::string(CONFIG_CACHE_CACHE_INSERT_DATA) + " " + - std::to_string(advanced_config->cache_insert_data->getValue()); - status = CommandLine(cache_cmd_string, reply); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status) - } - - auto engine_cmd_prefix = "set_config " + std::string(CONFIG_ENGINE) + "."; - - auto engine_cmd_string = engine_cmd_prefix + std::string(CONFIG_ENGINE_USE_BLAS_THRESHOLD) + " " + - std::to_string(advanced_config->use_blas_threshold->getValue()); - status = CommandLine(engine_cmd_string, reply); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status) - } - -#ifdef MILVUS_GPU_VERSION - auto gpu_cmd_prefix = "set_config " + std::string(CONFIG_GPU_RESOURCE) + "."; - auto gpu_cmd_string = gpu_cmd_prefix + std::string(CONFIG_GPU_RESOURCE_GPU_SEARCH_THRESHOLD) + " " + - std::to_string(advanced_config->gpu_search_threshold->getValue()); - status = CommandLine(gpu_cmd_string, reply); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status) - } -#endif - - ASSIGN_RETURN_STATUS_DTO(status) -} - -#ifdef MILVUS_GPU_VERSION -StatusDto::ObjectWrapper -WebRequestHandler::GetGpuConfig(GPUConfigDto::ObjectWrapper& gpu_config_dto) { - std::string reply; - std::string gpu_cmd_prefix = "get_config " + std::string(CONFIG_GPU_RESOURCE) + "."; - - std::string gpu_cmd_request = gpu_cmd_prefix + std::string(CONFIG_GPU_RESOURCE_ENABLE); - auto status = CommandLine(gpu_cmd_request, reply); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status); - } - gpu_config_dto->enable = reply == "1" || reply == "true"; - - if (!gpu_config_dto->enable->getValue()) { - ASSIGN_RETURN_STATUS_DTO(Status::OK()); - } - - gpu_cmd_request = gpu_cmd_prefix + std::string(CONFIG_GPU_RESOURCE_CACHE_CAPACITY); - status = CommandLine(gpu_cmd_request, reply); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status); - } - gpu_config_dto->cache_capacity = std::stol(reply); - - gpu_cmd_request = gpu_cmd_prefix + std::string(CONFIG_GPU_RESOURCE_SEARCH_RESOURCES); - status = CommandLine(gpu_cmd_request, reply); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status); - } - - std::vector gpu_entry; - StringHelpFunctions::SplitStringByDelimeter(reply, ",", gpu_entry); - - gpu_config_dto->search_resources = gpu_config_dto->search_resources->createShared(); - for (auto& device_id : gpu_entry) { - gpu_config_dto->search_resources->pushBack(OString(device_id.c_str())->toUpperCase()); - } - gpu_entry.clear(); - - gpu_cmd_request = gpu_cmd_prefix + std::string(CONFIG_GPU_RESOURCE_BUILD_INDEX_RESOURCES); - status = CommandLine(gpu_cmd_request, reply); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status); - } - - StringHelpFunctions::SplitStringByDelimeter(reply, ",", gpu_entry); - gpu_config_dto->build_index_resources = gpu_config_dto->build_index_resources->createShared(); - for (auto& device_id : gpu_entry) { - gpu_config_dto->build_index_resources->pushBack(OString(device_id.c_str())->toUpperCase()); - } - - ASSIGN_RETURN_STATUS_DTO(Status::OK()); -} - -StatusDto::ObjectWrapper -WebRequestHandler::SetGpuConfig(const GPUConfigDto::ObjectWrapper& gpu_config_dto) { - // Step 1: Check config param - if (nullptr == gpu_config_dto->enable.get()) { - RETURN_STATUS_DTO(BODY_FIELD_LOSS, "Field \'enable\' miss") - } - - if (nullptr == gpu_config_dto->cache_capacity.get()) { - RETURN_STATUS_DTO(BODY_FIELD_LOSS, "Field \'cache_capacity\' miss") - } - - if (nullptr == gpu_config_dto->search_resources.get()) { - gpu_config_dto->search_resources = gpu_config_dto->search_resources->createShared(); - gpu_config_dto->search_resources->pushBack("GPU0"); - } - - if (nullptr == gpu_config_dto->build_index_resources.get()) { - gpu_config_dto->build_index_resources = gpu_config_dto->build_index_resources->createShared(); - gpu_config_dto->build_index_resources->pushBack("GPU0"); - } - - // Step 2: Set config - std::string reply; - std::string gpu_cmd_prefix = "set_config " + std::string(CONFIG_GPU_RESOURCE) + "."; - std::string gpu_cmd_request = gpu_cmd_prefix + std::string(CONFIG_GPU_RESOURCE_ENABLE) + " " + - std::to_string(gpu_config_dto->enable->getValue()); - auto status = CommandLine(gpu_cmd_request, reply); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status); - } - - if (!gpu_config_dto->enable->getValue()) { - RETURN_STATUS_DTO(SUCCESS, "Set Gpu resources to false"); - } - - gpu_cmd_request = gpu_cmd_prefix + std::string(CONFIG_GPU_RESOURCE_CACHE_CAPACITY) + " " + - std::to_string(gpu_config_dto->cache_capacity->getValue()); - status = CommandLine(gpu_cmd_request, reply); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status); - } - - std::vector search_resources; - gpu_config_dto->search_resources->forEach( - [&search_resources](const OString& res) { search_resources.emplace_back(res->toLowerCase()->std_str()); }); - - std::string search_resources_value; - for (auto& res : search_resources) { - search_resources_value += res + ","; - } - auto len = search_resources_value.size(); - if (len > 0) { - search_resources_value.erase(len - 1); - } - - gpu_cmd_request = gpu_cmd_prefix + std::string(CONFIG_GPU_RESOURCE_SEARCH_RESOURCES) + " " + search_resources_value; - status = CommandLine(gpu_cmd_request, reply); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status); - } - - std::vector build_resources; - gpu_config_dto->build_index_resources->forEach( - [&build_resources](const OString& res) { build_resources.emplace_back(res->toLowerCase()->std_str()); }); - - std::string build_resources_value; - for (auto& res : build_resources) { - build_resources_value += res + ","; - } - len = build_resources_value.size(); - if (len > 0) { - build_resources_value.erase(len - 1); - } - - gpu_cmd_request = - gpu_cmd_prefix + std::string(CONFIG_GPU_RESOURCE_BUILD_INDEX_RESOURCES) + " " + build_resources_value; - status = CommandLine(gpu_cmd_request, reply); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status); - } - - ASSIGN_RETURN_STATUS_DTO(Status::OK()); -} -#endif - -/************* - * - * Collection { - */ -StatusDto::ObjectWrapper -WebRequestHandler::CreateCollection(const CollectionRequestDto::ObjectWrapper& collection_schema) { - if (nullptr == collection_schema->collection_name.get()) { - RETURN_STATUS_DTO(BODY_FIELD_LOSS, "Field \'collection_name\' is missing") - } - - if (nullptr == collection_schema->dimension.get()) { - RETURN_STATUS_DTO(BODY_FIELD_LOSS, "Field \'dimension\' is missing") - } - - if (nullptr == collection_schema->index_file_size.get()) { - RETURN_STATUS_DTO(BODY_FIELD_LOSS, "Field \'index_file_size\' is missing") - } - - if (nullptr == collection_schema->metric_type.get()) { - RETURN_STATUS_DTO(BODY_FIELD_LOSS, "Field \'metric_type\' is missing") - } - - if (MetricNameMap.find(collection_schema->metric_type->std_str()) == MetricNameMap.end()) { - RETURN_STATUS_DTO(ILLEGAL_METRIC_TYPE, "metric_type is illegal") - } - - auto status = request_handler_.CreateCollection( - context_ptr_, collection_schema->collection_name->std_str(), collection_schema->dimension, - collection_schema->index_file_size, - static_cast(MetricNameMap.at(collection_schema->metric_type->std_str()))); - - ASSIGN_RETURN_STATUS_DTO(status) -} - -StatusDto::ObjectWrapper -WebRequestHandler::CreateHybridCollection(const milvus::server::web::OString& body) { - auto json_str = nlohmann::json::parse(body->c_str()); - std::string collection_name = json_str["collection_name"]; - - // TODO(yukun): do checking - std::vector> field_types; - std::vector> field_extra_params; - std::vector> vector_dimensions; - for (auto& field : json_str["fields"]) { - std::string field_name = field["field_name"]; - std::string field_type = field["field_type"]; - auto extra_params = field["extra_params"]; - if (field_type == "int8") { - field_types.emplace_back(std::make_pair(field_name, engine::meta::hybrid::DataType::INT8)); - } else if (field_type == "int16") { - field_types.emplace_back(std::make_pair(field_name, engine::meta::hybrid::DataType::INT16)); - } else if (field_type == "int32") { - field_types.emplace_back(std::make_pair(field_name, engine::meta::hybrid::DataType::INT32)); - } else if (field_type == "int64") { - field_types.emplace_back(std::make_pair(field_name, engine::meta::hybrid::DataType::INT64)); - } else if (field_type == "float") { - field_types.emplace_back(std::make_pair(field_name, engine::meta::hybrid::DataType::FLOAT)); - } else if (field_type == "double") { - field_types.emplace_back(std::make_pair(field_name, engine::meta::hybrid::DataType::DOUBLE)); - } else if (field_type == "vector") { - } else { - std::string msg = field_name + " has wrong field_type"; - RETURN_STATUS_DTO(BODY_PARSE_FAIL, msg.c_str()); - } - - field_extra_params.emplace_back(std::make_pair(field_name, extra_params.dump())); - - if (extra_params.contains("dimension")) { - vector_dimensions.emplace_back(std::make_pair(field_name, extra_params["dimension"].get())); - } - } - - auto status = request_handler_.CreateHybridCollection(context_ptr_, collection_name, field_types, vector_dimensions, - field_extra_params); - - ASSIGN_RETURN_STATUS_DTO(status) -} - -StatusDto::ObjectWrapper -WebRequestHandler::ShowCollections(const OQueryParams& query_params, OString& result) { - int64_t offset = 0; - auto status = ParseQueryInteger(query_params, "offset", offset); - if (!status.ok()) { - RETURN_STATUS_DTO(status.code(), status.message().c_str()); - } - - int64_t page_size = 10; - status = ParseQueryInteger(query_params, "page_size", page_size); - if (!status.ok()) { - RETURN_STATUS_DTO(status.code(), status.message().c_str()); - } - - if (offset < 0 || page_size < 0) { - RETURN_STATUS_DTO(ILLEGAL_QUERY_PARAM, "Query param 'offset' or 'page_size' should equal or bigger than 0"); - } - - bool all_required = false; - ParseQueryBool(query_params, "all_required", all_required); - if (!status.ok()) { - RETURN_STATUS_DTO(status.code(), status.message().c_str()); - } - - std::vector collections; - status = request_handler_.ShowCollections(context_ptr_, collections); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status) - } - - if (all_required) { - offset = 0; - page_size = collections.size(); - } else { - offset = std::min((size_t)offset, collections.size()); - page_size = std::min(collections.size() - offset, (size_t)page_size); - } - - nlohmann::json collections_json; - for (int64_t i = offset; i < page_size + offset; i++) { - nlohmann::json collection_json; - status = GetCollectionMetaInfo(collections.at(i), collection_json); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status) - } - collections_json.push_back(collection_json); - } - - nlohmann::json result_json; - result_json["count"] = collections.size(); - if (collections_json.empty()) { - result_json["collections"] = std::vector(); - } else { - result_json["collections"] = collections_json; - } - - result = result_json.dump().c_str(); - - ASSIGN_RETURN_STATUS_DTO(status) -} - -StatusDto::ObjectWrapper -WebRequestHandler::GetCollection(const OString& collection_name, const OQueryParams& query_params, OString& result) { - if (nullptr == collection_name.get()) { - RETURN_STATUS_DTO(PATH_PARAM_LOSS, "Path param \'collection_name\' is required!"); - } - - std::string stat; - auto status = ParseQueryStr(query_params, "info", stat); - if (!status.ok()) { - RETURN_STATUS_DTO(status.code(), status.message().c_str()); - } - - if (!stat.empty() && stat == "stat") { - nlohmann::json json; - status = GetCollectionStat(collection_name->std_str(), json); - result = status.ok() ? json.dump().c_str() : "NULL"; - } else { - nlohmann::json json; - status = GetCollectionMetaInfo(collection_name->std_str(), json); - result = status.ok() ? json.dump().c_str() : "NULL"; - } - - ASSIGN_RETURN_STATUS_DTO(status); -} - -StatusDto::ObjectWrapper -WebRequestHandler::DropCollection(const OString& collection_name) { - auto status = request_handler_.DropCollection(context_ptr_, collection_name->std_str()); - - ASSIGN_RETURN_STATUS_DTO(status) -} - -/*********** - * - * Index { - */ - -StatusDto::ObjectWrapper -WebRequestHandler::CreateIndex(const OString& collection_name, const OString& body) { - try { - auto request_json = nlohmann::json::parse(body->std_str()); - if (!request_json.contains("index_type")) { - RETURN_STATUS_DTO(BODY_FIELD_LOSS, "Field \'index_type\' is required"); - } - - std::string index_type = request_json["index_type"]; - if (IndexNameMap.find(index_type) == IndexNameMap.end()) { - RETURN_STATUS_DTO(ILLEGAL_INDEX_TYPE, "The index type is invalid.") - } - auto index = static_cast(IndexNameMap.at(index_type)); - if (!request_json.contains("params")) { - RETURN_STATUS_DTO(BODY_FIELD_LOSS, "Field \'params\' is required") - } - auto status = - request_handler_.CreateIndex(context_ptr_, collection_name->std_str(), index, request_json["params"]); - ASSIGN_RETURN_STATUS_DTO(status); - } catch (nlohmann::detail::parse_error& e) { - RETURN_STATUS_DTO(BODY_PARSE_FAIL, e.what()) - } catch (nlohmann::detail::type_error& e) { - RETURN_STATUS_DTO(BODY_PARSE_FAIL, e.what()) - } - - ASSIGN_RETURN_STATUS_DTO(Status::OK()) -} - -StatusDto::ObjectWrapper -WebRequestHandler::GetIndex(const OString& collection_name, OString& result) { - IndexParam param; - auto status = request_handler_.DescribeIndex(context_ptr_, collection_name->std_str(), param); - - if (status.ok()) { - nlohmann::json json_out; - auto index_type = IndexMap.at(engine::EngineType(param.index_type_)); - json_out["index_type"] = index_type; - json_out["params"] = nlohmann::json::parse(param.extra_params_); - result = json_out.dump().c_str(); - } - - ASSIGN_RETURN_STATUS_DTO(status) -} - -StatusDto::ObjectWrapper -WebRequestHandler::DropIndex(const OString& collection_name) { - auto status = request_handler_.DropIndex(context_ptr_, collection_name->std_str()); - - ASSIGN_RETURN_STATUS_DTO(status) -} - -StatusDto::ObjectWrapper -WebRequestHandler::CreatePartition(const OString& collection_name, const PartitionRequestDto::ObjectWrapper& param) { - if (nullptr == param->partition_tag.get()) { - RETURN_STATUS_DTO(BODY_FIELD_LOSS, "Field \'partition_tag\' is required") - } - - auto status = - request_handler_.CreatePartition(context_ptr_, collection_name->std_str(), param->partition_tag->std_str()); - - ASSIGN_RETURN_STATUS_DTO(status) -} - -StatusDto::ObjectWrapper -WebRequestHandler::ShowPartitions(const OString& collection_name, const OQueryParams& query_params, - PartitionListDto::ObjectWrapper& partition_list_dto) { - int64_t offset = 0; - auto status = ParseQueryInteger(query_params, "offset", offset); - if (!status.ok()) { - RETURN_STATUS_DTO(status.code(), status.message().c_str()); - } - - int64_t page_size = 10; - status = ParseQueryInteger(query_params, "page_size", page_size); - if (!status.ok()) { - RETURN_STATUS_DTO(status.code(), status.message().c_str()); - } - - if (offset < 0 || page_size < 0) { - ASSIGN_RETURN_STATUS_DTO( - Status(SERVER_UNEXPECTED_ERROR, "Query param 'offset' or 'page_size' should equal or bigger than 0")); - } - - bool all_required = false; - auto required = query_params.get("all_required"); - if (nullptr != required.get()) { - auto required_str = required->std_str(); - if (!ValidationUtil::ValidateStringIsBool(required_str).ok()) { - RETURN_STATUS_DTO(ILLEGAL_QUERY_PARAM, "Query param \'all_required\' must be a bool") - } - all_required = required_str == "True" || required_str == "true"; - } - - std::vector partitions; - status = request_handler_.ShowPartitions(context_ptr_, collection_name->std_str(), partitions); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status) - } - - if (all_required) { - offset = 0; - page_size = partitions.size(); - } else { - offset = std::min((size_t)offset, partitions.size()); - page_size = std::min(partitions.size() - offset, (size_t)page_size); - } - - partition_list_dto->count = partitions.size(); - partition_list_dto->partitions = partition_list_dto->partitions->createShared(); - - if (offset < (int64_t)(partitions.size())) { - for (int64_t i = offset; i < page_size + offset; i++) { - auto partition_dto = PartitionFieldsDto::createShared(); - partition_dto->partition_tag = partitions.at(i).tag_.c_str(); - partition_list_dto->partitions->pushBack(partition_dto); - } - } - - ASSIGN_RETURN_STATUS_DTO(status) -} - -StatusDto::ObjectWrapper -WebRequestHandler::DropPartition(const OString& collection_name, const OString& body) { - std::string tag; - try { - auto json = nlohmann::json::parse(body->std_str()); - tag = json["partition_tag"].get(); - } catch (nlohmann::detail::parse_error& e) { - RETURN_STATUS_DTO(BODY_PARSE_FAIL, e.what()) - } catch (nlohmann::detail::type_error& e) { - RETURN_STATUS_DTO(BODY_PARSE_FAIL, e.what()) - } - auto status = request_handler_.DropPartition(context_ptr_, collection_name->std_str(), tag); - - ASSIGN_RETURN_STATUS_DTO(status) -} - -/*********** - * - * Segment { - */ -StatusDto::ObjectWrapper -WebRequestHandler::ShowSegments(const OString& collection_name, const OQueryParams& query_params, OString& response) { - int64_t offset = 0; - auto status = ParseQueryInteger(query_params, "offset", offset); - if (!status.ok()) { - RETURN_STATUS_DTO(status.code(), status.message().c_str()); - } - - int64_t page_size = 10; - status = ParseQueryInteger(query_params, "page_size", page_size); - if (!status.ok()) { - RETURN_STATUS_DTO(status.code(), status.message().c_str()); - } - - if (offset < 0 || page_size < 0) { - RETURN_STATUS_DTO(ILLEGAL_QUERY_PARAM, "Query param 'offset' or 'page_size' should equal or bigger than 0"); - } - - bool all_required = false; - auto required = query_params.get("all_required"); - if (nullptr != required.get()) { - auto required_str = required->std_str(); - if (!ValidationUtil::ValidateStringIsBool(required_str).ok()) { - RETURN_STATUS_DTO(ILLEGAL_QUERY_PARAM, "Query param \'all_required\' must be a bool") - } - all_required = required_str == "True" || required_str == "true"; - } - - std::string tag; - if (nullptr != query_params.get("partition_tag").get()) { - tag = query_params.get("partition_tag")->std_str(); - } - - std::string info; - status = request_handler_.ShowCollectionInfo(context_ptr_, collection_name->std_str(), info); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status) - } - - nlohmann::json info_json = nlohmann::json::parse(info); - nlohmann::json segments_json = nlohmann::json::array(); - for (auto& par : info_json["partitions"]) { - if (!(all_required || tag.empty() || tag == par["tag"])) { - continue; - } - - auto segments = par["segments"]; - if (!segments.is_null()) { - for (auto& seg : segments) { - seg["partition_tag"] = par["tag"]; - segments_json.push_back(seg); - } - } - } - nlohmann::json result_json; - if (!all_required) { - int64_t size = segments_json.size(); - int iter_begin = std::min(size, offset); - int iter_end = std::min(size, offset + page_size); - - nlohmann::json segments_slice_json = nlohmann::json::array(); - segments_slice_json.insert(segments_slice_json.begin(), segments_json.begin() + iter_begin, - segments_json.begin() + iter_end); - result_json["segments"] = segments_slice_json; // segments_json; - } else { - result_json["segments"] = segments_json; - } - result_json["count"] = segments_json.size(); - AddStatusToJson(result_json, status.code(), status.message()); - response = result_json.dump().c_str(); - - ASSIGN_RETURN_STATUS_DTO(status) -} - -StatusDto::ObjectWrapper -WebRequestHandler::GetSegmentInfo(const OString& collection_name, const OString& segment_name, const OString& info, - const OQueryParams& query_params, OString& result) { - int64_t offset = 0; - auto status = ParseQueryInteger(query_params, "offset", offset); - if (!status.ok()) { - RETURN_STATUS_DTO(status.code(), status.message().c_str()); - } - - int64_t page_size = 10; - status = ParseQueryInteger(query_params, "page_size", page_size); - if (!status.ok()) { - RETURN_STATUS_DTO(status.code(), status.message().c_str()); - } - - if (offset < 0 || page_size < 0) { - ASSIGN_RETURN_STATUS_DTO( - Status(SERVER_UNEXPECTED_ERROR, "Query param 'offset' or 'page_size' should equal or bigger than 0")); - } - - std::string re = info->std_str(); - status = Status::OK(); - nlohmann::json json; - // Get vectors - if (re == "vectors") { - status = GetSegmentVectors(collection_name->std_str(), segment_name->std_str(), page_size, offset, json); - // Get vector ids - } else if (re == "ids") { - status = GetSegmentIds(collection_name->std_str(), segment_name->std_str(), page_size, offset, json); - } - - result = status.ok() ? json.dump().c_str() : "NULL"; - - ASSIGN_RETURN_STATUS_DTO(status) -} - -/********** - * - * Vector { - */ -StatusDto::ObjectWrapper -WebRequestHandler::Insert(const OString& collection_name, const OString& body, VectorIdsDto::ObjectWrapper& ids_dto) { - if (nullptr == body.get() || body->getSize() == 0) { - RETURN_STATUS_DTO(BODY_FIELD_LOSS, "Request payload is required.") - } - - // step 1: copy vectors - bool bin_flag; - auto status = IsBinaryCollection(collection_name->std_str(), bin_flag); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status) - } - - auto body_json = nlohmann::json::parse(body->std_str()); - if (!body_json.contains("vectors")) { - RETURN_STATUS_DTO(BODY_FIELD_LOSS, "Field \'vectors\' is required"); - } - engine::VectorsData vectors; - CopyRecordsFromJson(body_json["vectors"], vectors, bin_flag); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status) - } - - // step 2: copy id array - if (body_json.contains("ids")) { - auto& ids_json = body_json["ids"]; - if (!ids_json.is_array()) { - RETURN_STATUS_DTO(ILLEGAL_BODY, "Field \"ids\" must be a array"); - } - auto& id_array = vectors.id_array_; - id_array.clear(); - try { - for (auto& id_str : ids_json) { - int64_t id = std::stol(id_str.get()); - id_array.emplace_back(id); - } - } catch (std::exception& e) { - std::string err_msg = std::string("Cannot convert vectors id. details: ") + e.what(); - RETURN_STATUS_DTO(SERVER_UNEXPECTED_ERROR, err_msg.c_str()); - } - } - - // step 3: copy partition tag - std::string tag; - if (body_json.contains("partition_tag")) { - tag = body_json["partition_tag"]; - } - - // step 4: construct result - status = request_handler_.Insert(context_ptr_, collection_name->std_str(), vectors, tag); - if (status.ok()) { - ids_dto->ids = ids_dto->ids->createShared(); - for (auto& id : vectors.id_array_) { - ids_dto->ids->pushBack(std::to_string(id).c_str()); - } - } - - ASSIGN_RETURN_STATUS_DTO(status) -} - -StatusDto::ObjectWrapper -WebRequestHandler::InsertEntity(const OString& collection_name, const milvus::server::web::OString& body, - VectorIdsDto::ObjectWrapper& ids_dto) { - if (nullptr == body.get() || body->getSize() == 0) { - RETURN_STATUS_DTO(BODY_FIELD_LOSS, "Request payload is required.") - } - - auto body_json = nlohmann::json::parse(body->c_str()); - std::string partition_tag = body_json["partition_tag"]; - - uint64_t row_num = body_json["row_num"]; - - std::unordered_map field_types; - auto status = request_handler_.DescribeHybridCollection(context_ptr_, collection_name->c_str(), field_types); - - auto entities = body_json["entity"]; - if (!entities.is_array()) { - RETURN_STATUS_DTO(ILLEGAL_BODY, "An entity must be an array"); - } - - std::vector field_names; - std::vector> attr_values; - size_t attr_size = 0; - std::unordered_map vector_datas; - for (auto& entity : entities) { - std::string field_name = entity["field_name"]; - field_names.emplace_back(field_name); - auto field_value = entity["field_value"]; - std::vector attr_value; - switch (field_types.at(field_name)) { - case engine::meta::hybrid::DataType::INT8: - case engine::meta::hybrid::DataType::INT16: - case engine::meta::hybrid::DataType::INT32: - case engine::meta::hybrid::DataType::INT64: { - std::vector value; - auto size = field_value.size(); - value.resize(size); - attr_value.resize(size * sizeof(int64_t)); - size_t offset = 0; - for (auto data : field_value) { - value[offset] = data.get(); - ++offset; - } - memcpy(attr_value.data(), value.data(), size * sizeof(int64_t)); - attr_size += size * sizeof(int64_t); - attr_values.emplace_back(attr_value); - break; - } - case engine::meta::hybrid::DataType::FLOAT: - case engine::meta::hybrid::DataType::DOUBLE: { - std::vector value; - auto size = field_value.size(); - value.resize(size); - attr_value.resize(size * sizeof(double)); - size_t offset = 0; - for (auto data : field_value) { - value[offset] = data.get(); - ++offset; - } - memcpy(attr_value.data(), value.data(), size * sizeof(double)); - attr_size += size * sizeof(double); - - attr_values.emplace_back(attr_value); - break; - } - case engine::meta::hybrid::DataType::VECTOR: { - bool bin_flag; - status = IsBinaryCollection(collection_name->c_str(), bin_flag); - if (!status.ok()) { - ASSIGN_RETURN_STATUS_DTO(status) - } - - engine::VectorsData vectors; - CopyRecordsFromJson(field_value, vectors, bin_flag); - vector_datas.insert(std::make_pair(field_name, vectors)); - } - default: {} - } - } - - std::vector attrs(attr_size, 0); - size_t attr_offset = 0; - for (auto& data : attr_values) { - memcpy(attrs.data() + attr_offset, data.data(), data.size()); - attr_offset += data.size(); - } - - status = request_handler_.InsertEntity(context_ptr_, collection_name->c_str(), partition_tag, row_num, field_names, - attrs, vector_datas); - - if (status.ok()) { - ids_dto->ids = ids_dto->ids->createShared(); - for (auto& id : vector_datas.begin()->second.id_array_) { - ids_dto->ids->pushBack(std::to_string(id).c_str()); - } - } - - ASSIGN_RETURN_STATUS_DTO(status) -} - -StatusDto::ObjectWrapper -WebRequestHandler::GetVector(const OString& collection_name, const OQueryParams& query_params, OString& response) { - auto status = Status::OK(); - try { - auto query_ids = query_params.get("ids"); - if (query_ids == nullptr || query_ids.get() == nullptr) { - RETURN_STATUS_DTO(QUERY_PARAM_LOSS, "Query param ids is required."); - } - - std::vector ids; - StringHelpFunctions::SplitStringByDelimeter(query_ids->c_str(), ",", ids); - - std::vector vector_ids; - for (auto& id : ids) { - vector_ids.push_back(std::stol(id)); - } - engine::VectorsData vectors; - nlohmann::json vectors_json; - status = GetVectorsByIDs(collection_name->std_str(), vector_ids, vectors_json); - if (!status.ok()) { - response = "NULL"; - ASSIGN_RETURN_STATUS_DTO(status) - } - - FloatJson json; - json["code"] = (int64_t)status.code(); - json["message"] = status.message(); - if (vectors_json.empty()) { - json["vectors"] = std::vector(); - } else { - json["vectors"] = vectors_json; - } - response = json.dump().c_str(); - } catch (std::exception& e) { - RETURN_STATUS_DTO(SERVER_UNEXPECTED_ERROR, e.what()); - } - - ASSIGN_RETURN_STATUS_DTO(status); -} - -StatusDto::ObjectWrapper -WebRequestHandler::VectorsOp(const OString& collection_name, const OString& payload, OString& response) { - auto status = Status::OK(); - std::string result_str; - - try { - nlohmann::json payload_json = nlohmann::json::parse(payload->std_str()); - - if (payload_json.contains("delete")) { - status = DeleteByIDs(collection_name->std_str(), payload_json["delete"], result_str); - } else if (payload_json.contains("search")) { - status = Search(collection_name->std_str(), payload_json["search"], result_str); - } else if (payload_json.contains("query")) { - status = HybridSearch(collection_name->c_str(), payload_json["query"], result_str); - } else { - status = Status(ILLEGAL_BODY, "Unknown body"); - } - } catch (nlohmann::detail::parse_error& e) { - std::string emsg = "json error: code=" + std::to_string(e.id) + ", reason=" + e.what(); - RETURN_STATUS_DTO(BODY_PARSE_FAIL, emsg.c_str()); - } catch (nlohmann::detail::type_error& e) { - std::string emsg = "json error: code=" + std::to_string(e.id) + ", reason=" + e.what(); - RETURN_STATUS_DTO(BODY_PARSE_FAIL, emsg.c_str()); - } catch (std::exception& e) { - RETURN_STATUS_DTO(SERVER_UNEXPECTED_ERROR, e.what()); - } - - response = status.ok() ? result_str.c_str() : "NULL"; - - ASSIGN_RETURN_STATUS_DTO(status) -} - -/********** - * - * System { - */ -StatusDto::ObjectWrapper -WebRequestHandler::SystemInfo(const OString& cmd, const OQueryParams& query_params, OString& response_str) { - std::string info = cmd->std_str(); - - auto status = Status::OK(); - std::string result_str; - - try { - if (info == "config") { - status = GetConfig(result_str); - } else { - if ("info" == info) { - info = "get_system_info"; - } - status = Cmd(info, result_str); - } - } catch (nlohmann::detail::parse_error& e) { - std::string emsg = "json error: code=" + std::to_string(e.id) + ", reason=" + e.what(); - RETURN_STATUS_DTO(BODY_PARSE_FAIL, emsg.c_str()); - } catch (nlohmann::detail::type_error& e) { - std::string emsg = "json error: code=" + std::to_string(e.id) + ", reason=" + e.what(); - RETURN_STATUS_DTO(BODY_PARSE_FAIL, emsg.c_str()); - } - - response_str = status.ok() ? result_str.c_str() : "NULL"; - - ASSIGN_RETURN_STATUS_DTO(status); -} - -StatusDto::ObjectWrapper -WebRequestHandler::SystemOp(const OString& op, const OString& body_str, OString& response_str) { - if (nullptr == body_str.get() || body_str->getSize() == 0) { - RETURN_STATUS_DTO(BODY_FIELD_LOSS, "Payload is empty."); - } - - Status status = Status::OK(); - std::string result_str; - try { - fiu_do_on("WebRequestHandler.SystemOp.raise_parse_error", - throw nlohmann::detail::parse_error::create(0, 0, "")); - fiu_do_on("WebRequestHandler.SystemOp.raise_type_error", throw nlohmann::detail::type_error::create(0, "")); - nlohmann::json j = nlohmann::json::parse(body_str->c_str()); - if (op->equals("task")) { - if (j.contains("load")) { - status = PreLoadCollection(j["load"], result_str); - } else if (j.contains("flush")) { - status = Flush(j["flush"], result_str); - } - if (j.contains("compact")) { - status = Compact(j["compact"], result_str); - } - } else if (op->equals("config")) { - status = SetConfig(j, result_str); - } else { - status = Status(UNKNOWN_PATH, "Unknown path: /system/" + op->std_str()); - } - } catch (nlohmann::detail::parse_error& e) { - std::string emsg = "json error: code=" + std::to_string(e.id) + ", reason=" + e.what(); - RETURN_STATUS_DTO(BODY_PARSE_FAIL, emsg.c_str()); - } catch (nlohmann::detail::type_error& e) { - std::string emsg = "json error: code=" + std::to_string(e.id) + ", reason=" + e.what(); - RETURN_STATUS_DTO(BODY_PARSE_FAIL, emsg.c_str()); - } - - response_str = status.ok() ? result_str.c_str() : "NULL"; - - ASSIGN_RETURN_STATUS_DTO(status); -} - -} // namespace web -} // namespace server -} // namespace milvus diff --git a/core/src/server/web_impl/handler/WebRequestHandler.h b/core/src/server/web_impl/handler/WebRequestHandler.h deleted file mode 100644 index 4cc3e34f11..0000000000 --- a/core/src/server/web_impl/handler/WebRequestHandler.h +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "db/Types.h" -#include "server/context/Context.h" -#include "server/delivery/RequestHandler.h" -#include "server/web_impl/Types.h" -#include "server/web_impl/dto/CollectionDto.hpp" -#include "server/web_impl/dto/ConfigDto.hpp" -#include "server/web_impl/dto/DevicesDto.hpp" -#include "server/web_impl/dto/IndexDto.hpp" -#include "server/web_impl/dto/PartitionDto.hpp" -#include "server/web_impl/dto/VectorDto.hpp" -#include "thirdparty/nlohmann/json.hpp" -#include "utils/Status.h" - -namespace milvus { -namespace server { -namespace web { - -#define RETURN_STATUS_DTO(STATUS_CODE, MESSAGE) \ - do { \ - auto status_dto = StatusDto::createShared(); \ - status_dto->code = (STATUS_CODE); \ - status_dto->message = (MESSAGE); \ - return status_dto; \ - } while (false); - -#define ASSIGN_RETURN_STATUS_DTO(STATUS) \ - do { \ - int code; \ - if (0 != (STATUS).code()) { \ - code = WebErrorMap((STATUS).code()); \ - } else { \ - code = 0; \ - } \ - RETURN_STATUS_DTO(code, (STATUS).message().c_str()) \ - } while (false); - -StatusCode -WebErrorMap(ErrorCode code); - -class WebRequestHandler { - private: - std::shared_ptr - GenContextPtr(const std::string& context_str) { - auto context_ptr = std::make_shared("dummy_request_id"); - opentracing::mocktracer::MockTracerOptions tracer_options; - auto mock_tracer = - std::shared_ptr{new opentracing::mocktracer::MockTracer{std::move(tracer_options)}}; - auto mock_span = mock_tracer->StartSpan("mock_span"); - auto trace_context = std::make_shared(mock_span); - context_ptr->SetTraceContext(trace_context); - - return context_ptr; - } - - private: - void - AddStatusToJson(nlohmann::json& json, int64_t code, const std::string& msg); - - Status - IsBinaryCollection(const std::string& collection_name, bool& bin); - - Status - CopyRecordsFromJson(const nlohmann::json& json, engine::VectorsData& vectors, bool bin); - - protected: - Status - GetCollectionMetaInfo(const std::string& collection_name, nlohmann::json& json_out); - - Status - GetCollectionStat(const std::string& collection_name, nlohmann::json& json_out); - - Status - GetSegmentVectors(const std::string& collection_name, const std::string& segment_name, int64_t page_size, - int64_t offset, nlohmann::json& json_out); - - Status - GetSegmentIds(const std::string& collection_name, const std::string& segment_name, int64_t page_size, - int64_t offset, nlohmann::json& json_out); - - Status - CommandLine(const std::string& cmd, std::string& reply); - - Status - Cmd(const std::string& cmd, std::string& result_str); - - Status - PreLoadCollection(const nlohmann::json& json, std::string& result_str); - - Status - Flush(const nlohmann::json& json, std::string& result_str); - - Status - Compact(const nlohmann::json& json, std::string& result_str); - - Status - GetConfig(std::string& result_str); - - Status - SetConfig(const nlohmann::json& json, std::string& result_str); - - Status - Search(const std::string& collection_name, const nlohmann::json& json, std::string& result_str); - - Status - ProcessLeafQueryJson(const nlohmann::json& json, query::BooleanQueryPtr& boolean_query); - - Status - ProcessBoolQueryJson(const nlohmann::json& query_json, query::BooleanQueryPtr& boolean_query); - - Status - HybridSearch(const std::string& collection_name, const nlohmann::json& json, std::string& result_str); - - Status - DeleteByIDs(const std::string& collection_name, const nlohmann::json& json, std::string& result_str); - - Status - GetVectorsByIDs(const std::string& collection_name, const std::vector& ids, nlohmann::json& json_out); - - public: - WebRequestHandler() { - context_ptr_ = GenContextPtr("Web Handler"); - request_handler_ = RequestHandler(); - } - - public: - StatusDto::ObjectWrapper - GetDevices(DevicesDto::ObjectWrapper& devices); - - StatusDto::ObjectWrapper - GetAdvancedConfig(AdvancedConfigDto::ObjectWrapper& config); - - StatusDto::ObjectWrapper - SetAdvancedConfig(const AdvancedConfigDto::ObjectWrapper& config); - -#ifdef MILVUS_GPU_VERSION - StatusDto::ObjectWrapper - GetGpuConfig(GPUConfigDto::ObjectWrapper& gpu_config_dto); - - StatusDto::ObjectWrapper - SetGpuConfig(const GPUConfigDto::ObjectWrapper& gpu_config_dto); -#endif - - StatusDto::ObjectWrapper - CreateCollection(const CollectionRequestDto::ObjectWrapper& table_schema); - StatusDto::ObjectWrapper - ShowCollections(const OQueryParams& query_params, OString& result); - - StatusDto::ObjectWrapper - CreateHybridCollection(const OString& body); - - StatusDto::ObjectWrapper - GetCollection(const OString& collection_name, const OQueryParams& query_params, OString& result); - - StatusDto::ObjectWrapper - DropCollection(const OString& collection_name); - - StatusDto::ObjectWrapper - CreateIndex(const OString& collection_name, const OString& body); - - StatusDto::ObjectWrapper - GetIndex(const OString& collection_name, OString& result); - - StatusDto::ObjectWrapper - DropIndex(const OString& collection_name); - - StatusDto::ObjectWrapper - CreatePartition(const OString& collection_name, const PartitionRequestDto::ObjectWrapper& param); - - StatusDto::ObjectWrapper - ShowPartitions(const OString& collection_name, const OQueryParams& query_params, - PartitionListDto::ObjectWrapper& partition_list_dto); - - StatusDto::ObjectWrapper - DropPartition(const OString& collection_name, const OString& body); - - /*********** - * - * Segment - */ - StatusDto::ObjectWrapper - ShowSegments(const OString& collection_name, const OQueryParams& query_params, OString& response); - - StatusDto::ObjectWrapper - GetSegmentInfo(const OString& collection_name, const OString& segment_name, const OString& info, - const OQueryParams& query_params, OString& result); - - /** - * - * Vector - */ - StatusDto::ObjectWrapper - Insert(const OString& collection_name, const OString& body, VectorIdsDto::ObjectWrapper& ids_dto); - - StatusDto::ObjectWrapper - InsertEntity(const OString& collection_name, const OString& body, VectorIdsDto::ObjectWrapper& ids_dto); - - StatusDto::ObjectWrapper - GetVector(const OString& collection_name, const OQueryParams& query_params, OString& response); - - StatusDto::ObjectWrapper - VectorsOp(const OString& collection_name, const OString& payload, OString& response); - - /** - * - * System - */ - StatusDto::ObjectWrapper - SystemInfo(const OString& cmd, const OQueryParams& query_params, OString& response_str); - - StatusDto::ObjectWrapper - SystemOp(const OString& op, const OString& body_str, OString& response_str); - - public: - void - RegisterRequestHandler(const RequestHandler& handler) { - request_handler_ = handler; - } - - private: - std::shared_ptr context_ptr_; - RequestHandler request_handler_; - std::unordered_map field_type_; -}; - -} // namespace web -} // namespace server -} // namespace milvus diff --git a/core/src/server/web_impl/utils/Util.cpp b/core/src/server/web_impl/utils/Util.cpp deleted file mode 100644 index ee05beebbb..0000000000 --- a/core/src/server/web_impl/utils/Util.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "server/web_impl/utils/Util.h" -#include - -#include "utils/ValidationUtil.h" - -namespace milvus { -namespace server { -namespace web { - -Status -ParseQueryInteger(const OQueryParams& query_params, const std::string& key, int64_t& value, bool nullable) { - auto query = query_params.get(key.c_str()); - fiu_do_on("WebUtils.ParseQueryInteger.null_query_get", query = ""); - if (nullptr != query.get() && query->getSize() > 0) { - std::string value_str = query->std_str(); - if (!ValidationUtil::ValidateStringIsNumber(value_str).ok()) { - return Status(ILLEGAL_QUERY_PARAM, - "Query param \'offset\' is illegal, only non-negative integer supported"); - } - - value = std::stol(value_str); - } else if (!nullable) { - return Status(QUERY_PARAM_LOSS, "Query param \"" + key + "\" is required"); - } - - return Status::OK(); -} - -Status -ParseQueryStr(const OQueryParams& query_params, const std::string& key, std::string& value, bool nullable) { - auto query = query_params.get(key.c_str()); - fiu_do_on("WebUtils.ParseQueryStr.null_query_get", query = ""); - if (nullptr != query.get() && query->getSize() > 0) { - value = query->std_str(); - } else if (!nullable) { - return Status(QUERY_PARAM_LOSS, "Query param \"" + key + "\" is required"); - } - - return Status::OK(); -} - -Status -ParseQueryBool(const OQueryParams& query_params, const std::string& key, bool& value, bool nullable) { - auto query = query_params.get(key.c_str()); - fiu_do_on("WebUtils.ParseQueryBool.null_query_get", query = ""); - if (nullptr != query.get() && query->getSize() > 0) { - std::string value_str = query->std_str(); - if (!ValidationUtil::ValidateStringIsBool(value_str).ok()) { - return Status(ILLEGAL_QUERY_PARAM, "Query param \'all_required\' must be a bool"); - } - value = value_str == "True" || value_str == "true"; - return Status::OK(); - } - - if (!nullable) { - return Status(QUERY_PARAM_LOSS, "Query param \"" + key + "\" is required"); - } - - return Status::OK(); -} - -} // namespace web -} // namespace server -} // namespace milvus diff --git a/core/src/server/web_impl/utils/Util.h b/core/src/server/web_impl/utils/Util.h deleted file mode 100644 index d337862987..0000000000 --- a/core/src/server/web_impl/utils/Util.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -#include "db/Types.h" -#include "server/web_impl/Types.h" -#include "utils/Status.h" - -namespace milvus { -namespace server { -namespace web { - -Status -ParseQueryInteger(const OQueryParams& query_params, const std::string& key, int64_t& value, bool nullable = true); - -Status -ParseQueryStr(const OQueryParams& query_params, const std::string& key, std::string& value, bool nullable = true); - -Status -ParseQueryBool(const OQueryParams& query_params, const std::string& key, bool& value, bool nullable = true); - -} // namespace web -} // namespace server -} // namespace milvus diff --git a/core/src/storage/FSHandler.h b/core/src/storage/FSHandler.h deleted file mode 100644 index 8b0f175bf9..0000000000 --- a/core/src/storage/FSHandler.h +++ /dev/null @@ -1,42 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include - -#include "storage/IOReader.h" -#include "storage/IOWriter.h" -#include "storage/Operation.h" - -namespace milvus { -namespace storage { - -struct FSHandler { - IOReaderPtr reader_ptr_ = nullptr; - IOWriterPtr writer_ptr_ = nullptr; - OperationPtr operation_ptr_ = nullptr; - - FSHandler(IOReaderPtr& reader_ptr, IOWriterPtr& writer_ptr, OperationPtr& operation_ptr) - : reader_ptr_(reader_ptr), writer_ptr_(writer_ptr), operation_ptr_(operation_ptr) { - } -}; - -using FSHandlerPtr = std::shared_ptr; - -} // namespace storage -} // namespace milvus diff --git a/core/src/storage/IOReader.h b/core/src/storage/IOReader.h deleted file mode 100644 index 98fabcefc4..0000000000 --- a/core/src/storage/IOReader.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -namespace milvus { -namespace storage { - -class IOReader { - public: - virtual bool - open(const std::string& name) = 0; - - virtual void - read(void* ptr, int64_t size) = 0; - - virtual void - seekg(int64_t pos) = 0; - - virtual int64_t - length() = 0; - - virtual void - close() = 0; -}; - -using IOReaderPtr = std::shared_ptr; - -} // namespace storage -} // namespace milvus diff --git a/core/src/storage/IOWriter.h b/core/src/storage/IOWriter.h deleted file mode 100644 index 763174b11c..0000000000 --- a/core/src/storage/IOWriter.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include - -namespace milvus { -namespace storage { - -class IOWriter { - public: - virtual bool - open(const std::string& name) = 0; - - virtual void - write(void* ptr, int64_t size) = 0; - - virtual int64_t - length() = 0; - - virtual void - close() = 0; -}; - -using IOWriterPtr = std::shared_ptr; - -} // namespace storage -} // namespace milvus diff --git a/core/src/storage/Operation.h b/core/src/storage/Operation.h deleted file mode 100644 index a29069c7da..0000000000 --- a/core/src/storage/Operation.h +++ /dev/null @@ -1,51 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include -#include - -namespace milvus { -namespace storage { - -class Operation { - public: - virtual void - CreateDirectory() = 0; - - virtual const std::string& - GetDirectory() const = 0; - - virtual void - ListDirectory(std::vector& file_paths) = 0; - - virtual bool - DeleteFile(const std::string& file_path) = 0; - - // TODO(zhiru): - // open(), sync(), close() - // function that opens a stream for reading file - // function that creates a new, empty file and returns an stream for appending data to this file - // function that creates a new, empty, temporary file and returns an stream for appending data to this file -}; - -using OperationPtr = std::shared_ptr; - -} // namespace storage -} // namespace milvus diff --git a/core/src/storage/disk/DiskIOReader.cpp b/core/src/storage/disk/DiskIOReader.cpp deleted file mode 100644 index ba5aa30dd3..0000000000 --- a/core/src/storage/disk/DiskIOReader.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "storage/disk/DiskIOReader.h" - -namespace milvus { -namespace storage { - -bool -DiskIOReader::open(const std::string& name) { - name_ = name; - fs_ = std::fstream(name_, std::ios::in | std::ios::binary); - return fs_.good(); -} - -void -DiskIOReader::read(void* ptr, int64_t size) { - fs_.read(reinterpret_cast(ptr), size); -} - -void -DiskIOReader::seekg(int64_t pos) { - fs_.seekg(pos); -} - -int64_t -DiskIOReader::length() { - fs_.seekg(0, fs_.end); - int64_t len = fs_.tellg(); - fs_.seekg(0, fs_.beg); - return len; -} - -void -DiskIOReader::close() { - fs_.close(); -} - -} // namespace storage -} // namespace milvus diff --git a/core/src/storage/disk/DiskIOReader.h b/core/src/storage/disk/DiskIOReader.h deleted file mode 100644 index 363564c5a9..0000000000 --- a/core/src/storage/disk/DiskIOReader.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include "storage/IOReader.h" - -namespace milvus { -namespace storage { - -class DiskIOReader : public IOReader { - public: - DiskIOReader() = default; - ~DiskIOReader() = default; - - // No copy and move - DiskIOReader(const DiskIOReader&) = delete; - DiskIOReader(DiskIOReader&&) = delete; - - DiskIOReader& - operator=(const DiskIOReader&) = delete; - DiskIOReader& - operator=(DiskIOReader&&) = delete; - - bool - open(const std::string& name) override; - - void - read(void* ptr, int64_t size) override; - - void - seekg(int64_t pos) override; - - int64_t - length() override; - - void - close() override; - - public: - std::string name_; - std::fstream fs_; -}; - -using DiskIOReaderPtr = std::shared_ptr; - -} // namespace storage -} // namespace milvus diff --git a/core/src/storage/disk/DiskIOWriter.cpp b/core/src/storage/disk/DiskIOWriter.cpp deleted file mode 100644 index 75bc84e82e..0000000000 --- a/core/src/storage/disk/DiskIOWriter.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "storage/disk/DiskIOWriter.h" - -namespace milvus { -namespace storage { - -bool -DiskIOWriter::open(const std::string& name) { - name_ = name; - len_ = 0; - fs_ = std::fstream(name_, std::ios::out | std::ios::binary); - return fs_.good(); -} - -void -DiskIOWriter::write(void* ptr, int64_t size) { - fs_.write(reinterpret_cast(ptr), size); - len_ += size; -} - -int64_t -DiskIOWriter::length() { - return len_; -} - -void -DiskIOWriter::close() { - fs_.close(); -} - -} // namespace storage -} // namespace milvus diff --git a/core/src/storage/disk/DiskIOWriter.h b/core/src/storage/disk/DiskIOWriter.h deleted file mode 100644 index 051c9fb5fe..0000000000 --- a/core/src/storage/disk/DiskIOWriter.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include "storage/IOWriter.h" - -namespace milvus { -namespace storage { - -class DiskIOWriter : public IOWriter { - public: - DiskIOWriter() = default; - ~DiskIOWriter() = default; - - // No copy and move - DiskIOWriter(const DiskIOWriter&) = delete; - DiskIOWriter(DiskIOWriter&&) = delete; - - DiskIOWriter& - operator=(const DiskIOWriter&) = delete; - DiskIOWriter& - operator=(DiskIOWriter&&) = delete; - - bool - open(const std::string& name) override; - - void - write(void* ptr, int64_t size) override; - - int64_t - length() override; - - void - close() override; - - public: - std::string name_; - int64_t len_; - std::fstream fs_; -}; - -using DiskIOWriterPtr = std::shared_ptr; - -} // namespace storage -} // namespace milvus diff --git a/core/src/storage/disk/DiskOperation.cpp b/core/src/storage/disk/DiskOperation.cpp deleted file mode 100644 index 8ce5af10ce..0000000000 --- a/core/src/storage/disk/DiskOperation.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// 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 limitations -// under the License. - -#include - -#include - -#include "storage/disk/DiskOperation.h" -#include "utils/Exception.h" -#include "utils/Log.h" - -namespace milvus { -namespace storage { - -DiskOperation::DiskOperation(const std::string& dir_path) : dir_path_(dir_path) { -} - -void -DiskOperation::CreateDirectory() { - bool is_dir = boost::filesystem::is_directory(dir_path_); - fiu_do_on("DiskOperation.CreateDirectory.is_directory", is_dir = false); - if (!is_dir) { - auto ret = boost::filesystem::create_directory(dir_path_); - fiu_do_on("DiskOperation.CreateDirectory.create_directory", ret = false); - if (!ret) { - std::string err_msg = "Failed to create directory: " + dir_path_; - LOG_ENGINE_ERROR_ << err_msg; - throw Exception(SERVER_CANNOT_CREATE_FOLDER, err_msg); - } - } -} - -const std::string& -DiskOperation::GetDirectory() const { - return dir_path_; -} - -void -DiskOperation::ListDirectory(std::vector& file_paths) { - boost::filesystem::path target_path(dir_path_); - typedef boost::filesystem::directory_iterator d_it; - d_it it_end; - d_it it(target_path); - if (boost::filesystem::is_directory(dir_path_)) { - for (; it != it_end; ++it) { - file_paths.emplace_back(it->path().c_str()); - } - } -} - -bool -DiskOperation::DeleteFile(const std::string& file_path) { - return boost::filesystem::remove(file_path); -} - -} // namespace storage -} // namespace milvus diff --git a/core/src/storage/disk/DiskOperation.h b/core/src/storage/disk/DiskOperation.h deleted file mode 100644 index 50c1a4ba3d..0000000000 --- a/core/src/storage/disk/DiskOperation.h +++ /dev/null @@ -1,58 +0,0 @@ -// 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 limitations -// under the License. - -#pragma once - -#include -#include -#include - -#include "storage/Operation.h" - -namespace milvus { -namespace storage { - -class DiskOperation : public Operation { - public: - explicit DiskOperation(const std::string& dir_path); - - void - CreateDirectory(); - - const std::string& - GetDirectory() const; - - void - ListDirectory(std::vector& file_paths); - - bool - DeleteFile(const std::string& file_path); - - // TODO(zhiru): - // open(), sync(), close() - // function that opens a stream for reading file - // function that creates a new, empty file and returns an stream for appending data to this file - // function that creates a new, empty, temporary file and returns an stream for appending data to this file - - private: - const std::string dir_path_; -}; - -using DiskOperationPtr = std::shared_ptr; - -} // namespace storage -} // namespace milvus diff --git a/core/src/storage/s3/S3ClientMock.h b/core/src/storage/s3/S3ClientMock.h deleted file mode 100644 index 0f85d363b9..0000000000 --- a/core/src/storage/s3/S3ClientMock.h +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace milvus { -namespace storage { - -/* - * This is a class that represents a S3 Client which is used to mimic the put/get operations of a actual s3 client. - * During a put object, the body of the request is stored as well as the metadata of the request. This data is then - * populated into a get object result when a get operation is called. - */ -class S3ClientMock : public Aws::S3::S3Client { - public: - explicit S3ClientMock(Aws::Client::ClientConfiguration clientConfiguration = Aws::Client::ClientConfiguration()) - : S3Client(Aws::Auth::AWSCredentials("", ""), clientConfiguration) { - } - - Aws::S3::Model::CreateBucketOutcome - CreateBucket(const Aws::S3::Model::CreateBucketRequest& request) const override { - Aws::S3::Model::CreateBucketResult result; - return Aws::S3::Model::CreateBucketOutcome(std::move(result)); - } - - Aws::S3::Model::DeleteBucketOutcome - DeleteBucket(const Aws::S3::Model::DeleteBucketRequest& request) const override { - Aws::NoResult result; - return Aws::S3::Model::DeleteBucketOutcome(std::move(result)); - } - - Aws::S3::Model::PutObjectOutcome - PutObject(const Aws::S3::Model::PutObjectRequest& request) const override { - Aws::String key = request.GetKey(); - std::shared_ptr body = request.GetBody(); - aws_map_[key] = body; - - Aws::S3::Model::PutObjectResult result; - return Aws::S3::Model::PutObjectOutcome(std::move(result)); - } - - Aws::S3::Model::GetObjectOutcome - GetObject(const Aws::S3::Model::GetObjectRequest& request) const override { - auto factory = request.GetResponseStreamFactory(); - Aws::Utils::Stream::ResponseStream resp_stream(factory); - - try { - std::shared_ptr body = aws_map_.at(request.GetKey()); - Aws::String body_str((Aws::IStreamBufIterator(*body)), Aws::IStreamBufIterator()); - - resp_stream.GetUnderlyingStream().write(body_str.c_str(), body_str.length()); - resp_stream.GetUnderlyingStream().flush(); - Aws::AmazonWebServiceResult awsStream( - std::move(resp_stream), Aws::Http::HeaderValueCollection()); - - Aws::S3::Model::GetObjectResult result(std::move(awsStream)); - return Aws::S3::Model::GetObjectOutcome(std::move(result)); - } catch (...) { - return Aws::S3::Model::GetObjectOutcome(); - } - } - - Aws::S3::Model::ListObjectsOutcome - ListObjects(const Aws::S3::Model::ListObjectsRequest& request) const override { - /* TODO: add object key list into ListObjectsOutcome */ - - Aws::S3::Model::ListObjectsResult result; - return Aws::S3::Model::ListObjectsOutcome(std::move(result)); - } - - Aws::S3::Model::DeleteObjectOutcome - DeleteObject(const Aws::S3::Model::DeleteObjectRequest& request) const override { - Aws::String key = request.GetKey(); - aws_map_.erase(key); - Aws::S3::Model::DeleteObjectResult result; - Aws::S3::Model::DeleteObjectOutcome(std::move(result)); - return result; - } - - mutable Aws::Map> aws_map_; -}; - -} // namespace storage -} // namespace milvus diff --git a/core/src/storage/s3/S3ClientWrapper.cpp b/core/src/storage/s3/S3ClientWrapper.cpp deleted file mode 100644 index bb1e4c0a35..0000000000 --- a/core/src/storage/s3/S3ClientWrapper.cpp +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "config/Config.h" -#include "storage/s3/S3ClientMock.h" -#include "storage/s3/S3ClientWrapper.h" -#include "utils/Error.h" -#include "utils/Log.h" - -namespace milvus { -namespace storage { - -Status -S3ClientWrapper::StartService() { - server::Config& config = server::Config::GetInstance(); - bool s3_enable = false; - CONFIG_CHECK(config.GetStorageConfigS3Enable(s3_enable)); - fiu_do_on("S3ClientWrapper.StartService.s3_disable", s3_enable = false); - if (!s3_enable) { - LOG_STORAGE_INFO_ << "S3 not enabled!"; - return Status::OK(); - } - - CONFIG_CHECK(config.GetStorageConfigS3Address(s3_address_)); - CONFIG_CHECK(config.GetStorageConfigS3Port(s3_port_)); - CONFIG_CHECK(config.GetStorageConfigS3AccessKey(s3_access_key_)); - CONFIG_CHECK(config.GetStorageConfigS3SecretKey(s3_secret_key_)); - CONFIG_CHECK(config.GetStorageConfigS3Bucket(s3_bucket_)); - - Aws::InitAPI(options_); - - Aws::Client::ClientConfiguration cfg; - cfg.endpointOverride = s3_address_ + ":" + s3_port_; - cfg.scheme = Aws::Http::Scheme::HTTP; - cfg.verifySSL = false; - client_ptr_ = - std::make_shared(Aws::Auth::AWSCredentials(s3_access_key_, s3_secret_key_), cfg, - Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Always, false); - - bool mock_enable = false; - fiu_do_on("S3ClientWrapper.StartService.mock_enable", mock_enable = true); - if (mock_enable) { - client_ptr_ = std::make_shared(); - } - - std::cout << "S3 service connection check ...... " << std::flush; - Status stat = CreateBucket(); - std::cout << (stat.ok() ? "OK" : "FAIL") << std::endl; - return stat; -} - -void -S3ClientWrapper::StopService() { - client_ptr_ = nullptr; - Aws::ShutdownAPI(options_); -} - -Status -S3ClientWrapper::CreateBucket() { - Aws::S3::Model::CreateBucketRequest request; - request.WithBucket(s3_bucket_); - - auto outcome = client_ptr_->CreateBucket(request); - - fiu_do_on("S3ClientWrapper.CreateBucket.outcome.fail", outcome = Aws::S3::Model::CreateBucketOutcome()); - if (!outcome.IsSuccess()) { - auto err = outcome.GetError(); - if (err.GetErrorType() != Aws::S3::S3Errors::BUCKET_ALREADY_OWNED_BY_YOU) { - LOG_STORAGE_ERROR_ << "ERROR: CreateBucket: " << err.GetExceptionName() << ": " << err.GetMessage(); - return Status(SERVER_UNEXPECTED_ERROR, err.GetMessage()); - } - } - - LOG_STORAGE_DEBUG_ << "CreateBucket '" << s3_bucket_ << "' successfully!"; - return Status::OK(); -} - -Status -S3ClientWrapper::DeleteBucket() { - Aws::S3::Model::DeleteBucketRequest request; - request.WithBucket(s3_bucket_); - - auto outcome = client_ptr_->DeleteBucket(request); - - fiu_do_on("S3ClientWrapper.DeleteBucket.outcome.fail", outcome = Aws::S3::Model::DeleteBucketOutcome()); - if (!outcome.IsSuccess()) { - auto err = outcome.GetError(); - LOG_STORAGE_ERROR_ << "ERROR: DeleteBucket: " << err.GetExceptionName() << ": " << err.GetMessage(); - return Status(SERVER_UNEXPECTED_ERROR, err.GetMessage()); - } - - LOG_STORAGE_DEBUG_ << "DeleteBucket '" << s3_bucket_ << "' successfully!"; - return Status::OK(); -} - -Status -S3ClientWrapper::PutObjectFile(const std::string& object_name, const std::string& file_path) { - struct stat buffer; - if (stat(file_path.c_str(), &buffer) != 0) { - std::string str = "File '" + file_path + "' not exist!"; - LOG_STORAGE_ERROR_ << "ERROR: " << str; - return Status(SERVER_UNEXPECTED_ERROR, str); - } - - Aws::S3::Model::PutObjectRequest request; - request.WithBucket(s3_bucket_).WithKey(object_name); - - auto input_data = - Aws::MakeShared("PutObjectFile", file_path.c_str(), std::ios_base::in | std::ios_base::binary); - request.SetBody(input_data); - - auto outcome = client_ptr_->PutObject(request); - - fiu_do_on("S3ClientWrapper.PutObjectFile.outcome.fail", outcome = Aws::S3::Model::PutObjectOutcome()); - if (!outcome.IsSuccess()) { - auto err = outcome.GetError(); - LOG_STORAGE_ERROR_ << "ERROR: PutObject: " << err.GetExceptionName() << ": " << err.GetMessage(); - return Status(SERVER_UNEXPECTED_ERROR, err.GetMessage()); - } - - LOG_STORAGE_DEBUG_ << "PutObjectFile '" << file_path << "' successfully!"; - return Status::OK(); -} - -Status -S3ClientWrapper::PutObjectStr(const std::string& object_name, const std::string& content) { - Aws::S3::Model::PutObjectRequest request; - request.WithBucket(s3_bucket_).WithKey(object_name); - - const std::shared_ptr input_data = Aws::MakeShared(""); - input_data->write(content.data(), content.length()); - request.SetBody(input_data); - - auto outcome = client_ptr_->PutObject(request); - - fiu_do_on("S3ClientWrapper.PutObjectStr.outcome.fail", outcome = Aws::S3::Model::PutObjectOutcome()); - if (!outcome.IsSuccess()) { - auto err = outcome.GetError(); - LOG_STORAGE_ERROR_ << "ERROR: PutObject: " << err.GetExceptionName() << ": " << err.GetMessage(); - return Status(SERVER_UNEXPECTED_ERROR, err.GetMessage()); - } - - LOG_STORAGE_DEBUG_ << "PutObjectStr successfully!"; - return Status::OK(); -} - -Status -S3ClientWrapper::GetObjectFile(const std::string& object_name, const std::string& file_path) { - Aws::S3::Model::GetObjectRequest request; - request.WithBucket(s3_bucket_).WithKey(object_name); - - auto outcome = client_ptr_->GetObject(request); - - fiu_do_on("S3ClientWrapper.GetObjectFile.outcome.fail", outcome = Aws::S3::Model::GetObjectOutcome()); - if (!outcome.IsSuccess()) { - auto err = outcome.GetError(); - LOG_STORAGE_ERROR_ << "ERROR: GetObject: " << err.GetExceptionName() << ": " << err.GetMessage(); - return Status(SERVER_UNEXPECTED_ERROR, err.GetMessage()); - } - - auto& retrieved_file = outcome.GetResultWithOwnership().GetBody(); - std::ofstream output_file(file_path, std::ios::binary); - output_file << retrieved_file.rdbuf(); - output_file.close(); - - LOG_STORAGE_DEBUG_ << "GetObjectFile '" << file_path << "' successfully!"; - return Status::OK(); -} - -Status -S3ClientWrapper::GetObjectStr(const std::string& object_name, std::string& content) { - Aws::S3::Model::GetObjectRequest request; - request.WithBucket(s3_bucket_).WithKey(object_name); - - auto outcome = client_ptr_->GetObject(request); - - fiu_do_on("S3ClientWrapper.GetObjectStr.outcome.fail", outcome = Aws::S3::Model::GetObjectOutcome()); - if (!outcome.IsSuccess()) { - auto err = outcome.GetError(); - LOG_STORAGE_ERROR_ << "ERROR: GetObject: " << err.GetExceptionName() << ": " << err.GetMessage(); - return Status(SERVER_UNEXPECTED_ERROR, err.GetMessage()); - } - - auto& retrieved_file = outcome.GetResultWithOwnership().GetBody(); - std::stringstream ss; - ss << retrieved_file.rdbuf(); - content = std::move(ss.str()); - - LOG_STORAGE_DEBUG_ << "GetObjectStr successfully!"; - return Status::OK(); -} - -Status -S3ClientWrapper::ListObjects(std::vector& object_list, const std::string& marker) { - Aws::S3::Model::ListObjectsRequest request; - request.WithBucket(s3_bucket_); - - if (!marker.empty()) { - request.WithMarker(marker); - } - - auto outcome = client_ptr_->ListObjects(request); - - fiu_do_on("S3ClientWrapper.ListObjects.outcome.fail", outcome = Aws::S3::Model::ListObjectsOutcome()); - if (!outcome.IsSuccess()) { - auto err = outcome.GetError(); - LOG_STORAGE_ERROR_ << "ERROR: ListObjects: " << err.GetExceptionName() << ": " << err.GetMessage(); - return Status(SERVER_UNEXPECTED_ERROR, err.GetMessage()); - } - - Aws::Vector result_list = outcome.GetResult().GetContents(); - - for (auto const& s3_object : result_list) { - object_list.push_back(s3_object.GetKey()); - } - - if (marker.empty()) { - LOG_STORAGE_DEBUG_ << "ListObjects '" << s3_bucket_ << "' successfully!"; - } else { - LOG_STORAGE_DEBUG_ << "ListObjects '" << s3_bucket_ << ":" << marker << "' successfully!"; - } - return Status::OK(); -} - -Status -S3ClientWrapper::DeleteObject(const std::string& object_name) { - Aws::S3::Model::DeleteObjectRequest request; - request.WithBucket(s3_bucket_).WithKey(object_name); - - auto outcome = client_ptr_->DeleteObject(request); - - fiu_do_on("S3ClientWrapper.DeleteObject.outcome.fail", outcome = Aws::S3::Model::DeleteObjectOutcome()); - if (!outcome.IsSuccess()) { - auto err = outcome.GetError(); - LOG_STORAGE_ERROR_ << "ERROR: DeleteObject: " << err.GetExceptionName() << ": " << err.GetMessage(); - return Status(SERVER_UNEXPECTED_ERROR, err.GetMessage()); - } - - LOG_STORAGE_DEBUG_ << "DeleteObject '" << object_name << "' successfully!"; - return Status::OK(); -} - -Status -S3ClientWrapper::DeleteObjects(const std::string& marker) { - std::vector object_list; - - Status stat = ListObjects(object_list, marker); - if (!stat.ok()) { - return stat; - } - - for (std::string& obj_name : object_list) { - stat = DeleteObject(obj_name); - if (!stat.ok()) { - return stat; - } - } - - return Status::OK(); -} - -} // namespace storage -} // namespace milvus diff --git a/core/src/storage/s3/S3ClientWrapper.h b/core/src/storage/s3/S3ClientWrapper.h deleted file mode 100644 index f0e2d8cfe7..0000000000 --- a/core/src/storage/s3/S3ClientWrapper.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include - -#include "utils/Status.h" - -namespace milvus { -namespace storage { - -class S3ClientWrapper { - public: - static S3ClientWrapper& - GetInstance() { - static S3ClientWrapper wrapper; - return wrapper; - } - - Status - StartService(); - void - StopService(); - - Status - CreateBucket(); - Status - DeleteBucket(); - Status - PutObjectFile(const std::string& object_key, const std::string& file_path); - Status - PutObjectStr(const std::string& object_key, const std::string& content); - Status - GetObjectFile(const std::string& object_key, const std::string& file_path); - Status - GetObjectStr(const std::string& object_key, std::string& content); - Status - ListObjects(std::vector& object_list, const std::string& marker = ""); - Status - DeleteObject(const std::string& object_key); - Status - DeleteObjects(const std::string& marker); - - private: - std::shared_ptr client_ptr_; - Aws::SDKOptions options_; - - std::string s3_address_; - std::string s3_port_; - std::string s3_access_key_; - std::string s3_secret_key_; - std::string s3_bucket_; -}; - -} // namespace storage -} // namespace milvus diff --git a/core/src/storage/s3/S3IOReader.cpp b/core/src/storage/s3/S3IOReader.cpp deleted file mode 100644 index a2b33ef3f9..0000000000 --- a/core/src/storage/s3/S3IOReader.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "storage/s3/S3IOReader.h" -#include "storage/s3/S3ClientWrapper.h" - -namespace milvus { -namespace storage { - -bool -S3IOReader::open(const std::string& name) { - name_ = name; - pos_ = 0; - return (S3ClientWrapper::GetInstance().GetObjectStr(name_, buffer_).ok()); -} - -void -S3IOReader::read(void* ptr, int64_t size) { - memcpy(ptr, buffer_.data() + pos_, size); -} - -void -S3IOReader::seekg(int64_t pos) { - pos_ = pos; -} - -int64_t -S3IOReader::length() { - return buffer_.length(); -} - -void -S3IOReader::close() { -} - -} // namespace storage -} // namespace milvus diff --git a/core/src/storage/s3/S3IOReader.h b/core/src/storage/s3/S3IOReader.h deleted file mode 100644 index 623a3ff878..0000000000 --- a/core/src/storage/s3/S3IOReader.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include "storage/IOReader.h" - -namespace milvus { -namespace storage { - -class S3IOReader : public IOReader { - public: - S3IOReader() = default; - ~S3IOReader() = default; - - // No copy and move - S3IOReader(const S3IOReader&) = delete; - S3IOReader(S3IOReader&&) = delete; - - S3IOReader& - operator=(const S3IOReader&) = delete; - S3IOReader& - operator=(S3IOReader&&) = delete; - - bool - open(const std::string& name) override; - - void - read(void* ptr, int64_t size) override; - - void - seekg(int64_t pos) override; - - int64_t - length() override; - - void - close() override; - - public: - std::string name_; - std::string buffer_; - int64_t pos_; -}; - -using S3IOReaderPtr = std::shared_ptr; - -} // namespace storage -} // namespace milvus diff --git a/core/src/storage/s3/S3IOWriter.cpp b/core/src/storage/s3/S3IOWriter.cpp deleted file mode 100644 index 68acb374a3..0000000000 --- a/core/src/storage/s3/S3IOWriter.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "storage/s3/S3IOWriter.h" -#include "storage/s3/S3ClientWrapper.h" - -namespace milvus { -namespace storage { - -bool -S3IOWriter::open(const std::string& name) { - name_ = name; - len_ = 0; - buffer_ = ""; - return true; -} - -void -S3IOWriter::write(void* ptr, int64_t size) { - buffer_ += std::string(reinterpret_cast(ptr), size); - len_ += size; -} - -int64_t -S3IOWriter::length() { - return len_; -} - -void -S3IOWriter::close() { - S3ClientWrapper::GetInstance().PutObjectStr(name_, buffer_); -} - -} // namespace storage -} // namespace milvus diff --git a/core/src/storage/s3/S3IOWriter.h b/core/src/storage/s3/S3IOWriter.h deleted file mode 100644 index ba1b3510ae..0000000000 --- a/core/src/storage/s3/S3IOWriter.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include "storage/IOWriter.h" - -namespace milvus { -namespace storage { - -class S3IOWriter : public IOWriter { - public: - S3IOWriter() = default; - ~S3IOWriter() = default; - - // No copy and move - S3IOWriter(const S3IOWriter&) = delete; - S3IOWriter(S3IOWriter&&) = delete; - - S3IOWriter& - operator=(const S3IOWriter&) = delete; - S3IOWriter& - operator=(S3IOWriter&&) = delete; - - bool - open(const std::string& name) override; - - void - write(void* ptr, int64_t size) override; - - int64_t - length() override; - - void - close() override; - - public: - std::string name_; - int64_t len_; - std::string buffer_; -}; - -using S3IOWriterPtr = std::shared_ptr; - -} // namespace storage -} // namespace milvus diff --git a/core/src/tracing/TextMapCarrier.cpp b/core/src/tracing/TextMapCarrier.cpp deleted file mode 100644 index 5d6316dc70..0000000000 --- a/core/src/tracing/TextMapCarrier.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "tracing/TextMapCarrier.h" - -namespace milvus { -namespace tracing { - -TextMapCarrier::TextMapCarrier(std::unordered_map& text_map) : text_map_(text_map) { -} - -opentracing::expected -TextMapCarrier::Set(opentracing::string_view key, opentracing::string_view value) const { - // text_map_[key] = value; - // return {}; - opentracing::expected result; - - auto was_successful = text_map_.emplace(key, value); - if (was_successful.second) { - // Use a default constructed opentracing::expected to indicate - // success. - return result; - } else { - // `key` clashes with existing data, so the span context can't be encoded - // successfully; set opentracing::expected to an std::error_code. - return opentracing::make_unexpected(std::make_error_code(std::errc::not_supported)); - } -} - -opentracing::expected -TextMapCarrier::ForeachKey(F f) const { - // Iterate through all key-value pairs, the tracer will use the relevant keys - // to extract a span context. - for (auto& key_value : text_map_) { - auto was_successful = f(key_value.first, key_value.second); - if (!was_successful) { - // If the callback returns and unexpected value, bail out of the loop. - return was_successful; - } - } - - // Indicate successful iteration. - return {}; -} - -// Optional, define TextMapReader::LookupKey to allow for faster extraction. -opentracing::expected -TextMapCarrier::LookupKey(opentracing::string_view key) const { - auto iter = text_map_.find(key); - if (iter != text_map_.end()) { - return opentracing::make_unexpected(opentracing::key_not_found_error); - } - return opentracing::string_view{iter->second}; -} - -} // namespace tracing -} // namespace milvus diff --git a/core/src/tracing/TextMapCarrier.h b/core/src/tracing/TextMapCarrier.h deleted file mode 100644 index 95a96d9ef0..0000000000 --- a/core/src/tracing/TextMapCarrier.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -#include -#include - -namespace milvus { -namespace tracing { - -class TextMapCarrier : public opentracing::TextMapReader, public opentracing::TextMapWriter { - public: - explicit TextMapCarrier(std::unordered_map& text_map); - - opentracing::expected - Set(opentracing::string_view key, opentracing::string_view value) const override; - - using F = std::function(opentracing::string_view, opentracing::string_view)>; - - opentracing::expected - ForeachKey(F f) const override; - - // Optional, define TextMapReader::LookupKey to allow for faster extraction. - opentracing::expected - LookupKey(opentracing::string_view key) const override; - - private: - std::unordered_map& text_map_; -}; - -} // namespace tracing -} // namespace milvus diff --git a/core/src/tracing/TraceContext.cpp b/core/src/tracing/TraceContext.cpp deleted file mode 100644 index 6efb4e426f..0000000000 --- a/core/src/tracing/TraceContext.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "tracing/TraceContext.h" - -#include - -namespace milvus { -namespace tracing { - -TraceContext::TraceContext(std::unique_ptr& span) : span_(std::move(span)) { -} - -std::unique_ptr -TraceContext::Child(const std::string& operation_name) const { - auto child_span = span_->tracer().StartSpan(operation_name, {opentracing::ChildOf(&(span_->context()))}); - return std::make_unique(child_span); -} - -std::unique_ptr -TraceContext::Follower(const std::string& operation_name) const { - auto follower_span = span_->tracer().StartSpan(operation_name, {opentracing::FollowsFrom(&(span_->context()))}); - return std::make_unique(follower_span); -} - -const std::unique_ptr& -TraceContext::GetSpan() const { - return span_; -} - -} // namespace tracing -} // namespace milvus diff --git a/core/src/tracing/TraceContext.h b/core/src/tracing/TraceContext.h deleted file mode 100644 index 84de27fc9c..0000000000 --- a/core/src/tracing/TraceContext.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -#include -#include - -namespace milvus { -namespace tracing { - -class TraceContext { - public: - explicit TraceContext(std::unique_ptr& span); - - std::unique_ptr - Child(const std::string& operation_name) const; - - std::unique_ptr - Follower(const std::string& operation_name) const; - - const std::unique_ptr& - GetSpan() const; - - private: - // std::unique_ptr span_context_; - std::unique_ptr span_; -}; - -} // namespace tracing -} // namespace milvus diff --git a/core/src/tracing/TracerUtil.cpp b/core/src/tracing/TracerUtil.cpp deleted file mode 100644 index cd4662c757..0000000000 --- a/core/src/tracing/TracerUtil.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "tracing/TracerUtil.h" - -#include -#include - -#include -#include - -#include "thirdparty/nlohmann/json.hpp" - -namespace milvus { -namespace tracing { - -const char* TRACER_LIBRARY_CONFIG_NAME = "tracer_library"; -const char* TRACER_CONFIGURATION_CONFIG_NAME = "tracer_configuration"; -const char* TRACE_CONTEXT_HEADER_CONFIG_NAME = "TraceContextHeaderName"; - -const char* TracerUtil::tracer_context_header_name_; - -void -TracerUtil::InitGlobal(const std::string& config_path) { - if (!config_path.empty()) { - LoadConfig(config_path); - } else { - tracer_context_header_name_ = ""; - } -} - -void -TracerUtil::LoadConfig(const std::string& config_path) { - // Parse JSON config - std::ifstream tracer_config(config_path); - if (!tracer_config.good()) { - std::cerr << "Failed to open tracer config file " << config_path << ": " << std::strerror(errno) << std::endl; - return; - } - using json = nlohmann::json; - json tracer_config_json; - tracer_config >> tracer_config_json; - std::string tracing_shared_lib = tracer_config_json[TRACER_LIBRARY_CONFIG_NAME]; - std::string tracer_config_str = tracer_config_json[TRACER_CONFIGURATION_CONFIG_NAME].dump(); - tracer_context_header_name_ = tracer_config_json[TRACE_CONTEXT_HEADER_CONFIG_NAME].dump().c_str(); - - // Load the tracer library. - std::string error_message; - auto handle_maybe = opentracing::DynamicallyLoadTracingLibrary(tracing_shared_lib.c_str(), error_message); - if (!handle_maybe) { - std::cerr << "Failed to load tracer library: " << error_message << std::endl; - return; - } - - // Construct a tracer. - auto& tracer_factory = handle_maybe->tracer_factory(); - auto tracer_maybe = tracer_factory.MakeTracer(tracer_config_str.c_str(), error_message); - if (!tracer_maybe) { - std::cerr << "Failed to create tracer: " << error_message << std::endl; - return; - } - auto& tracer = *tracer_maybe; - - opentracing::Tracer::InitGlobal(tracer); -} - -std::string -TracerUtil::GetTraceContextHeaderName() { - return tracer_context_header_name_; -} - -} // namespace tracing -} // namespace milvus diff --git a/core/src/tracing/TracerUtil.h b/core/src/tracing/TracerUtil.h deleted file mode 100644 index 6d9279b9dd..0000000000 --- a/core/src/tracing/TracerUtil.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -namespace milvus { -namespace tracing { - -extern const char* TRACER_LIBRARY_CONFIG_NAME; -extern const char* TRACER_CONFIGURATION_CONFIG_NAME; -extern const char* TRACE_CONTEXT_HEADER_CONFIG_NAME; - -class TracerUtil { - public: - static void - InitGlobal(const std::string& config_path = ""); - - static std::string - GetTraceContextHeaderName(); - - private: - static void - LoadConfig(const std::string& config_path); - - static const char* tracer_context_header_name_; -}; - -} // namespace tracing -} // namespace milvus diff --git a/core/src/utils/BlockingQueue.h b/core/src/utils/BlockingQueue.h deleted file mode 100644 index 0003e594e5..0000000000 --- a/core/src/utils/BlockingQueue.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include - -namespace milvus { -namespace server { - -template -class BlockingQueue { - public: - BlockingQueue() : mtx(), full_(), empty_() { - } - - virtual ~BlockingQueue() { - } - - BlockingQueue(const BlockingQueue& rhs) = delete; - - BlockingQueue& - operator=(const BlockingQueue& rhs) = delete; - - void - Put(const T& task); - - T - Take(); - - T - Front(); - - T - Back(); - - size_t - Size(); - - bool - Empty(); - - void - SetCapacity(const size_t capacity); - - protected: - mutable std::mutex mtx; - std::condition_variable full_; - std::condition_variable empty_; - std::queue queue_; - size_t capacity_ = 32; -}; - -} // namespace server -} // namespace milvus - -#include "./BlockingQueue.inl" diff --git a/core/src/utils/BlockingQueue.inl b/core/src/utils/BlockingQueue.inl deleted file mode 100644 index 2cd6979cf7..0000000000 --- a/core/src/utils/BlockingQueue.inl +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -namespace milvus { -namespace server { - -template -void -BlockingQueue::Put(const T& task) { - std::unique_lock lock(mtx); - full_.wait(lock, [this] { return (queue_.size() < capacity_); }); - - queue_.push(task); - empty_.notify_all(); -} - -template -T -BlockingQueue::Take() { - std::unique_lock lock(mtx); - empty_.wait(lock, [this] { return !queue_.empty(); }); - - T front(queue_.front()); - queue_.pop(); - full_.notify_all(); - return front; -} - -template -size_t -BlockingQueue::Size() { - std::lock_guard lock(mtx); - return queue_.size(); -} - -template -T -BlockingQueue::Front() { - std::unique_lock lock(mtx); - empty_.wait(lock, [this] { return !queue_.empty(); }); - - T front(queue_.front()); - return front; -} - -template -T -BlockingQueue::Back() { - std::unique_lock lock(mtx); - empty_.wait(lock, [this] { return !queue_.empty(); }); - - T back(queue_.back()); - return back; -} - -template -bool -BlockingQueue::Empty() { - std::unique_lock lock(mtx); - return queue_.empty(); -} - -template -void -BlockingQueue::SetCapacity(const size_t capacity) { - capacity_ = (capacity > 0 ? capacity : capacity_); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/utils/CommonUtil.cpp b/core/src/utils/CommonUtil.cpp deleted file mode 100644 index 210e6603e9..0000000000 --- a/core/src/utils/CommonUtil.cpp +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "utils/CommonUtil.h" -#include "cache/CpuCacheMgr.h" -#include "cache/GpuCacheMgr.h" -#include "config/Config.h" -#include "utils/Log.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "boost/filesystem.hpp" - -#if defined(__x86_64__) -#define THREAD_MULTIPLY_CPU 1 -#elif defined(__powerpc64__) -#define THREAD_MULTIPLY_CPU 4 -#else -#define THREAD_MULTIPLY_CPU 1 -#endif - -#include - -namespace milvus { -namespace server { - -namespace fs = boost::filesystem; - -bool -CommonUtil::GetSystemMemInfo(int64_t& total_mem, int64_t& free_mem) { - struct sysinfo info; - int ret = sysinfo(&info); - total_mem = info.totalram; - free_mem = info.freeram; - - return ret == 0; // succeed 0, failed -1 -} - -bool -CommonUtil::GetSysCgroupMemLimit(int64_t& limit_in_bytes) { - try { - std::ifstream file("/sys/fs/cgroup/memory/memory.limit_in_bytes"); - file >> limit_in_bytes; - } catch (std::exception& ex) { - std::string msg = - "Failed to read /sys/fs/cgroup/memory/memory.limit_in_bytes, reason: " + std::string(ex.what()); - LOG_SERVER_ERROR_ << msg; - return 0; - } -} - -bool -CommonUtil::GetSystemAvailableThreads(int64_t& thread_count) { - // threadCnt = std::thread::hardware_concurrency(); - thread_count = sysconf(_SC_NPROCESSORS_CONF); - thread_count *= THREAD_MULTIPLY_CPU; - fiu_do_on("CommonUtil.GetSystemAvailableThreads.zero_thread", thread_count = 0); - - if (thread_count == 0) { - thread_count = 8; - } - - return true; -} - -bool -CommonUtil::IsDirectoryExist(const std::string& path) { - DIR* dp = nullptr; - if ((dp = opendir(path.c_str())) == nullptr) { - return false; - } - - closedir(dp); - return true; -} - -Status -CommonUtil::CreateDirectory(const std::string& path) { - if (path.empty()) { - return Status::OK(); - } - - struct stat directory_stat; - int status = stat(path.c_str(), &directory_stat); - if (status == 0) { - return Status::OK(); // already exist - } - - fs::path fs_path(path); - fs::path parent_path = fs_path.parent_path(); - Status err_status = CreateDirectory(parent_path.string()); - fiu_do_on("CommonUtil.CreateDirectory.create_parent_fail", err_status = Status(SERVER_INVALID_ARGUMENT, "")); - if (!err_status.ok()) { - return err_status; - } - - status = stat(path.c_str(), &directory_stat); - if (status == 0) { - return Status::OK(); // already exist - } - - int makeOK = mkdir(path.c_str(), S_IRWXU | S_IRGRP | S_IROTH); - fiu_do_on("CommonUtil.CreateDirectory.create_dir_fail", makeOK = 1); - if (makeOK != 0) { - return Status(SERVER_UNEXPECTED_ERROR, "failed to create directory: " + path); - } - - return Status::OK(); -} - -namespace { -void -RemoveDirectory(const std::string& path) { - DIR* dir = nullptr; - struct dirent* dmsg; - const int32_t buf_size = 256; - char file_name[buf_size]; - - std::string folder_name = path + "/%s"; - if ((dir = opendir(path.c_str())) != nullptr) { - while ((dmsg = readdir(dir)) != nullptr) { - if (strcmp(dmsg->d_name, ".") != 0 && strcmp(dmsg->d_name, "..") != 0) { - snprintf(file_name, buf_size, folder_name.c_str(), dmsg->d_name); - std::string tmp = file_name; - if (tmp.find(".") == std::string::npos) { - RemoveDirectory(file_name); - } - remove(file_name); - } - } - } - - if (dir != nullptr) { - closedir(dir); - } - remove(path.c_str()); -} -} // namespace - -Status -CommonUtil::DeleteDirectory(const std::string& path) { - if (path.empty()) { - return Status::OK(); - } - - struct stat directory_stat; - int statOK = stat(path.c_str(), &directory_stat); - if (statOK != 0) { - return Status::OK(); - } - - RemoveDirectory(path); - return Status::OK(); -} - -bool -CommonUtil::IsFileExist(const std::string& path) { - return (access(path.c_str(), F_OK) == 0); -} - -uint64_t -CommonUtil::GetFileSize(const std::string& path) { - struct stat file_info; - if (stat(path.c_str(), &file_info) < 0) { - return 0; - } - - return static_cast(file_info.st_size); -} - -std::string -CommonUtil::GetFileName(std::string filename) { - int pos = filename.find_last_of('/'); - return filename.substr(pos + 1); -} - -std::string -CommonUtil::GetExePath() { - const int64_t buf_len = 1024; - char buf[buf_len]; - int64_t cnt = readlink("/proc/self/exe", buf, buf_len); - fiu_do_on("CommonUtil.GetExePath.readlink_fail", cnt = -1); - if (cnt < 0 || cnt >= buf_len) { - return ""; - } - - buf[cnt] = '\0'; - - std::string exe_path = buf; - fiu_do_on("CommonUtil.GetExePath.exe_path_error", exe_path = "/"); - if (exe_path.rfind('/') != exe_path.length() - 1) { - std::string sub_str = exe_path.substr(0, exe_path.rfind('/')); - return sub_str + "/"; - } - return exe_path; -} - -bool -CommonUtil::TimeStrToTime(const std::string& time_str, time_t& time_integer, tm& time_struct, - const std::string& format) { - time_integer = 0; - memset(&time_struct, 0, sizeof(tm)); - - int ret = sscanf(time_str.c_str(), format.c_str(), &(time_struct.tm_year), &(time_struct.tm_mon), - &(time_struct.tm_mday), &(time_struct.tm_hour), &(time_struct.tm_min), &(time_struct.tm_sec)); - if (ret <= 0) { - return false; - } - - time_struct.tm_year -= 1900; - time_struct.tm_mon--; - time_integer = mktime(&time_struct); - - return true; -} - -void -CommonUtil::ConvertTime(time_t time_integer, tm& time_struct) { - localtime_r(&time_integer, &time_struct); -} - -void -CommonUtil::ConvertTime(tm time_struct, time_t& time_integer) { - time_integer = mktime(&time_struct); -} - -#ifdef ENABLE_CPU_PROFILING -std::string -CommonUtil::GetCurrentTimeStr() { - time_t tt; - time(&tt); - tt = tt + 8 * 60; - tm t; - gmtime_r(&tt, &t); - - std::string str = std::to_string(t.tm_year + 1900) + "_" + std::to_string(t.tm_mon + 1) + "_" + - std::to_string(t.tm_mday) + "_" + std::to_string(t.tm_hour) + "_" + std::to_string(t.tm_min) + - "_" + std::to_string(t.tm_sec); - return str; -} -#endif - -void -CommonUtil::EraseFromCache(const std::string& item_key) { - if (item_key.empty()) { - LOG_SERVER_ERROR_ << "Empty key cannot be erased from cache"; - return; - } - - cache::CpuCacheMgr::GetInstance()->EraseItem(item_key); - -#ifdef MILVUS_GPU_VERSION - server::Config& config = server::Config::GetInstance(); - std::vector gpus; - config.GetGpuResourceConfigSearchResources(gpus); - for (auto& gpu : gpus) { - cache::GpuCacheMgr::GetInstance(gpu)->EraseItem(item_key); - } -#endif -} - -} // namespace server -} // namespace milvus diff --git a/core/src/utils/CommonUtil.h b/core/src/utils/CommonUtil.h deleted file mode 100644 index e81b402a27..0000000000 --- a/core/src/utils/CommonUtil.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "utils/Status.h" - -#include -#include - -namespace milvus { -namespace server { - -class CommonUtil { - public: - static bool - GetSystemMemInfo(int64_t& total_mem, int64_t& free_mem); - static bool - GetSysCgroupMemLimit(int64_t& limit_in_bytes); - static bool - GetSystemAvailableThreads(int64_t& thread_count); - - static bool - IsFileExist(const std::string& path); - static uint64_t - GetFileSize(const std::string& path); - static bool - IsDirectoryExist(const std::string& path); - static Status - CreateDirectory(const std::string& path); - static Status - DeleteDirectory(const std::string& path); - - static std::string - GetFileName(std::string filename); - static std::string - GetExePath(); - - static bool - TimeStrToTime(const std::string& time_str, time_t& time_integer, tm& time_struct, - const std::string& format = "%d-%d-%d %d:%d:%d"); - - static void - ConvertTime(time_t time_integer, tm& time_struct); - static void - ConvertTime(tm time_struct, time_t& time_integer); - -#ifdef ENABLE_CPU_PROFILING - static std::string - GetCurrentTimeStr(); -#endif - - static void - EraseFromCache(const std::string& item_key); -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/utils/Error.h b/core/src/utils/Error.h deleted file mode 100644 index d24ff8b373..0000000000 --- a/core/src/utils/Error.h +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include - -namespace milvus { - -using ErrorCode = int32_t; - -constexpr ErrorCode SERVER_SUCCESS = 0; -constexpr ErrorCode SERVER_ERROR_CODE_BASE = 30000; - -constexpr ErrorCode -ToServerErrorCode(const ErrorCode error_code) { - return SERVER_ERROR_CODE_BASE + error_code; -} - -constexpr ErrorCode DB_SUCCESS = 0; -constexpr ErrorCode DB_ERROR_CODE_BASE = 40000; - -constexpr ErrorCode -ToDbErrorCode(const ErrorCode error_code) { - return DB_ERROR_CODE_BASE + error_code; -} - -constexpr ErrorCode KNOWHERE_SUCCESS = 0; -constexpr ErrorCode KNOWHERE_ERROR_CODE_BASE = 50000; - -constexpr ErrorCode -ToKnowhereErrorCode(const ErrorCode error_code) { - return KNOWHERE_ERROR_CODE_BASE + error_code; -} - -constexpr ErrorCode WAL_SUCCESS = 0; -constexpr ErrorCode WAL_ERROR_CODE_BASE = 60000; - -constexpr ErrorCode -ToWalErrorCode(const ErrorCode error_code) { - return WAL_ERROR_CODE_BASE + error_code; -} - -// server error code -constexpr ErrorCode SERVER_UNEXPECTED_ERROR = ToServerErrorCode(1); -constexpr ErrorCode SERVER_UNSUPPORTED_ERROR = ToServerErrorCode(2); -constexpr ErrorCode SERVER_NULL_POINTER = ToServerErrorCode(3); -constexpr ErrorCode SERVER_INVALID_ARGUMENT = ToServerErrorCode(4); -constexpr ErrorCode SERVER_FILE_NOT_FOUND = ToServerErrorCode(5); -constexpr ErrorCode SERVER_NOT_IMPLEMENT = ToServerErrorCode(6); -constexpr ErrorCode SERVER_CANNOT_CREATE_FOLDER = ToServerErrorCode(8); -constexpr ErrorCode SERVER_CANNOT_CREATE_FILE = ToServerErrorCode(9); -constexpr ErrorCode SERVER_CANNOT_DELETE_FOLDER = ToServerErrorCode(10); -constexpr ErrorCode SERVER_CANNOT_DELETE_FILE = ToServerErrorCode(11); -constexpr ErrorCode SERVER_BUILD_INDEX_ERROR = ToServerErrorCode(12); -constexpr ErrorCode SERVER_CANNOT_OPEN_FILE = ToServerErrorCode(13); - -constexpr ErrorCode SERVER_COLLECTION_NOT_EXIST = ToServerErrorCode(100); -constexpr ErrorCode SERVER_INVALID_COLLECTION_NAME = ToServerErrorCode(101); -constexpr ErrorCode SERVER_INVALID_COLLECTION_DIMENSION = ToServerErrorCode(102); -constexpr ErrorCode SERVER_INVALID_VECTOR_DIMENSION = ToServerErrorCode(104); -constexpr ErrorCode SERVER_INVALID_INDEX_TYPE = ToServerErrorCode(105); -constexpr ErrorCode SERVER_INVALID_ROWRECORD = ToServerErrorCode(106); -constexpr ErrorCode SERVER_INVALID_ROWRECORD_ARRAY = ToServerErrorCode(107); -constexpr ErrorCode SERVER_INVALID_TOPK = ToServerErrorCode(108); -constexpr ErrorCode SERVER_ILLEGAL_VECTOR_ID = ToServerErrorCode(109); -constexpr ErrorCode SERVER_ILLEGAL_SEARCH_RESULT = ToServerErrorCode(110); -constexpr ErrorCode SERVER_CACHE_FULL = ToServerErrorCode(111); -constexpr ErrorCode SERVER_WRITE_ERROR = ToServerErrorCode(112); -constexpr ErrorCode SERVER_INVALID_NPROBE = ToServerErrorCode(113); -constexpr ErrorCode SERVER_INVALID_INDEX_NLIST = ToServerErrorCode(114); -constexpr ErrorCode SERVER_INVALID_INDEX_METRIC_TYPE = ToServerErrorCode(115); -constexpr ErrorCode SERVER_INVALID_INDEX_FILE_SIZE = ToServerErrorCode(116); -constexpr ErrorCode SERVER_OUT_OF_MEMORY = ToServerErrorCode(117); -constexpr ErrorCode SERVER_INVALID_PARTITION_TAG = ToServerErrorCode(118); -constexpr ErrorCode SERVER_INVALID_BINARY_QUERY = ToServerErrorCode(119); - -// db error code -constexpr ErrorCode DB_META_TRANSACTION_FAILED = ToDbErrorCode(1); -constexpr ErrorCode DB_ERROR = ToDbErrorCode(2); -constexpr ErrorCode DB_NOT_FOUND = ToDbErrorCode(3); -constexpr ErrorCode DB_ALREADY_EXIST = ToDbErrorCode(4); -constexpr ErrorCode DB_INVALID_PATH = ToDbErrorCode(5); -constexpr ErrorCode DB_INCOMPATIB_META = ToDbErrorCode(6); -constexpr ErrorCode DB_INVALID_META_URI = ToDbErrorCode(7); -constexpr ErrorCode DB_EMPTY_COLLECTION = ToDbErrorCode(8); -constexpr ErrorCode DB_BLOOM_FILTER_ERROR = ToDbErrorCode(9); -constexpr ErrorCode DB_PARTITION_NOT_FOUND = ToDbErrorCode(10); -constexpr ErrorCode DB_OUT_OF_STORAGE = ToDbErrorCode(11); - -// knowhere error code -constexpr ErrorCode KNOWHERE_ERROR = ToKnowhereErrorCode(1); -constexpr ErrorCode KNOWHERE_INVALID_ARGUMENT = ToKnowhereErrorCode(2); -constexpr ErrorCode KNOWHERE_UNEXPECTED_ERROR = ToKnowhereErrorCode(3); -constexpr ErrorCode KNOWHERE_NO_SPACE = ToKnowhereErrorCode(4); - -// knowhere error code -constexpr ErrorCode WAL_ERROR = ToWalErrorCode(1); -constexpr ErrorCode WAL_META_ERROR = ToWalErrorCode(2); -constexpr ErrorCode WAL_FILE_ERROR = ToWalErrorCode(3); -constexpr ErrorCode WAL_PATH_ERROR = ToWalErrorCode(4); - -namespace server { -class ServerException : public std::exception { - public: - explicit ServerException(ErrorCode error_code, const std::string& message = std::string()) - : error_code_(error_code), message_(message) { - } - - public: - ErrorCode - error_code() const { - return error_code_; - } - - virtual const char* - what() const noexcept { - return message_.c_str(); - } - - private: - ErrorCode error_code_; - std::string message_; -}; -} // namespace server - -} // namespace milvus diff --git a/core/src/utils/Exception.h b/core/src/utils/Exception.h deleted file mode 100644 index 231b04d243..0000000000 --- a/core/src/utils/Exception.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "utils/Error.h" - -#include -#include - -namespace milvus { - -class Exception : public std::exception { - public: - Exception(ErrorCode code, const std::string& message) : code_(code), message_(message) { - } - - ErrorCode - code() const noexcept { - return code_; - } - - const char* - what() const noexcept override { - if (message_.empty()) { - return "Default Exception."; - } else { - return message_.c_str(); - } - } - - ~Exception() noexcept override = default; - - protected: - ErrorCode code_; - std::string message_; -}; - -class InvalidArgumentException : public Exception { - public: - InvalidArgumentException() : Exception(SERVER_INVALID_ARGUMENT, "Invalid Argument") { - } - - explicit InvalidArgumentException(const std::string& message) : Exception(SERVER_INVALID_ARGUMENT, message) { - } -}; - -} // namespace milvus diff --git a/core/src/utils/Json.h b/core/src/utils/Json.h deleted file mode 100644 index 90a2a1e681..0000000000 --- a/core/src/utils/Json.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#pragma once - -#include "nlohmann/json.hpp" - -namespace milvus { -using json = nlohmann::json; -} diff --git a/core/src/utils/Log.cpp b/core/src/utils/Log.cpp deleted file mode 100644 index 500a997d57..0000000000 --- a/core/src/utils/Log.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. -#include "utils/Log.h" - -#include -#include -#include -#include - -namespace milvus { - -std::string -LogOut(const char* pattern, ...) { - size_t len = strnlen(pattern, 1024) + 256; - auto str_p = std::make_unique(len); - memset(str_p.get(), 0, len); - - va_list vl; - va_start(vl, pattern); - vsnprintf(str_p.get(), len, pattern, vl); - va_end(vl); - - return std::string(str_p.get()); -} - -void -SetThreadName(const std::string& name) { - pthread_setname_np(pthread_self(), name.c_str()); -} - -std::string -GetThreadName() { - std::string thread_name = "unamed"; - char name[16]; - size_t len = 16; - auto err = pthread_getname_np(pthread_self(), name, len); - if (not err) { - thread_name = name; - } - - return thread_name; -} - -} // namespace milvus diff --git a/core/src/utils/Log.h b/core/src/utils/Log.h deleted file mode 100644 index 68e7d6f7b7..0000000000 --- a/core/src/utils/Log.h +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include - -#include "easyloggingpp/easylogging++.h" - -namespace milvus { - -/* - * Please use LOG_MODULE_LEVEL_C macro in member function of class - * and LOG_MODULE_LEVEL_ macro in other functions. - */ - -///////////////////////////////////////////////////////////////////////////////////////////////// -#define SERVER_MODULE_NAME "SERVER" -#define SERVER_MODULE_CLASS_FUNCTION \ - LogOut("[%s][%s::%s][%s] ", SERVER_MODULE_NAME, (typeid(*this).name()), __FUNCTION__, GetThreadName().c_str()) -#define SERVER_MODULE_FUNCTION LogOut("[%s][%s][%s] ", SERVER_MODULE_NAME, __FUNCTION__, GetThreadName().c_str()) - -#define LOG_SERVER_TRACE_C LOG(TRACE) << SERVER_MODULE_CLASS_FUNCTION -#define LOG_SERVER_DEBUG_C LOG(DEBUG) << SERVER_MODULE_CLASS_FUNCTION -#define LOG_SERVER_INFO_C LOG(INFO) << SERVER_MODULE_CLASS_FUNCTION -#define LOG_SERVER_WARNING_C LOG(WARNING) << SERVER_MODULE_CLASS_FUNCTION -#define LOG_SERVER_ERROR_C LOG(ERROR) << SERVER_MODULE_CLASS_FUNCTION -#define LOG_SERVER_FATAL_C LOG(FATAL) << SERVER_MODULE_CLASS_FUNCTION - -#define LOG_SERVER_TRACE_ LOG(TRACE) << SERVER_MODULE_FUNCTION -#define LOG_SERVER_DEBUG_ LOG(DEBUG) << SERVER_MODULE_FUNCTION -#define LOG_SERVER_INFO_ LOG(INFO) << SERVER_MODULE_FUNCTION -#define LOG_SERVER_WARNING_ LOG(WARNING) << SERVER_MODULE_FUNCTION -#define LOG_SERVER_ERROR_ LOG(ERROR) << SERVER_MODULE_FUNCTION -#define LOG_SERVER_FATAL_ LOG(FATAL) << SERVER_MODULE_FUNCTION - -///////////////////////////////////////////////////////////////////////////////////////////////// -#define ENGINE_MODULE_NAME "ENGINE" -#define ENGINE_MODULE_CLASS_FUNCTION \ - LogOut("[%s][%s::%s][%s] ", ENGINE_MODULE_NAME, (typeid(*this).name()), __FUNCTION__, GetThreadName().c_str()) -#define ENGINE_MODULE_FUNCTION LogOut("[%s][%s][%s] ", ENGINE_MODULE_NAME, __FUNCTION__, GetThreadName().c_str()) - -#define LOG_ENGINE_TRACE_C LOG(TRACE) << ENGINE_MODULE_CLASS_FUNCTION -#define LOG_ENGINE_DEBUG_C LOG(DEBUG) << ENGINE_MODULE_CLASS_FUNCTION -#define LOG_ENGINE_INFO_C LOG(INFO) << ENGINE_MODULE_CLASS_FUNCTION -#define LOG_ENGINE_WARNING_C LOG(WARNING) << ENGINE_MODULE_CLASS_FUNCTION -#define LOG_ENGINE_ERROR_C LOG(ERROR) << ENGINE_MODULE_CLASS_FUNCTION -#define LOG_ENGINE_FATAL_C LOG(FATAL) << ENGINE_MODULE_CLASS_FUNCTION - -#define LOG_ENGINE_TRACE_ LOG(TRACE) << ENGINE_MODULE_FUNCTION -#define LOG_ENGINE_DEBUG_ LOG(DEBUG) << ENGINE_MODULE_FUNCTION -#define LOG_ENGINE_INFO_ LOG(INFO) << ENGINE_MODULE_FUNCTION -#define LOG_ENGINE_WARNING_ LOG(WARNING) << ENGINE_MODULE_FUNCTION -#define LOG_ENGINE_ERROR_ LOG(ERROR) << ENGINE_MODULE_FUNCTION -#define LOG_ENGINE_FATAL_ LOG(FATAL) << ENGINE_MODULE_FUNCTION - -///////////////////////////////////////////////////////////////////////////////////////////////// -#define WRAPPER_MODULE_NAME "WRAPPER" -#define WRAPPER_MODULE_CLASS_FUNCTION \ - LogOut("[%s][%s::%s][%s] ", WRAPPER_MODULE_NAME, (typeid(*this).name()), __FUNCTION__, GetThreadName().c_str()) -#define WRAPPER_MODULE_FUNCTION LogOut("[%s][%s][%s] ", WRAPPER_MODULE_NAME, __FUNCTION__, GetThreadName().c_str()) - -#define LOG_WRAPPER_TRACE_C LOG(TRACE) << WRAPPER_MODULE_CLASS_FUNCTION -#define LOG_WRAPPER_DEBUG_C LOG(DEBUG) << WRAPPER_MODULE_CLASS_FUNCTION -#define LOG_WRAPPER_INFO_C LOG(INFO) << WRAPPER_MODULE_CLASS_FUNCTION -#define LOG_WRAPPER_WARNING_C LOG(WARNING) << WRAPPER_MODULE_CLASS_FUNCTION -#define LOG_WRAPPER_ERROR_C LOG(ERROR) << WRAPPER_MODULE_CLASS_FUNCTION -#define LOG_WRAPPER_FATAL_C LOG(FATAL) << WRAPPER_MODULE_CLASS_FUNCTION - -#define LOG_WRAPPER_TRACE_ LOG(TRACE) << WRAPPER_MODULE_FUNCTION -#define LOG_WRAPPER_DEBUG_ LOG(DEBUG) << WRAPPER_MODULE_FUNCTION -#define LOG_WRAPPER_INFO_ LOG(INFO) << WRAPPER_MODULE_FUNCTION -#define LOG_WRAPPER_WARNING_ LOG(WARNING) << WRAPPER_MODULE_FUNCTION -#define LOG_WRAPPER_ERROR_ LOG(ERROR) << WRAPPER_MODULE_FUNCTION -#define LOG_WRAPPER_FATAL_ LOG(FATAL) << WRAPPER_MODULE_FUNCTION - -///////////////////////////////////////////////////////////////////////////////////////////////// -#define STORAGE_MODULE_NAME "STORAGE" -#define STORAGE_MODULE_CLASS_FUNCTION \ - LogOut("[%s][%s::%s][%s] ", STORAGE_MODULE_NAME, (typeid(*this).name()), __FUNCTION__, GetThreadName().c_str()) -#define STORAGE_MODULE_FUNCTION LogOut("[%s][%s][%s] ", STORAGE_MODULE_NAME, __FUNCTION__, GetThreadName().c_str()) - -#define LOG_STORAGE_TRACE_C LOG(TRACE) << STORAGE_MODULE_CLASS_FUNCTION -#define LOG_STORAGE_DEBUG_C LOG(DEBUG) << STORAGE_MODULE_CLASS_FUNCTION -#define LOG_STORAGE_INFO_C LOG(INFO) << STORAGE_MODULE_CLASS_FUNCTION -#define LOG_STORAGE_WARNING_C LOG(WARNING) << STORAGE_MODULE_CLASS_FUNCTION -#define LOG_STORAGE_ERROR_C LOG(ERROR) << STORAGE_MODULE_CLASS_FUNCTION -#define LOG_STORAGE_FATAL_C LOG(FATAL) << STORAGE_MODULE_CLASS_FUNCTION - -#define LOG_STORAGE_TRACE_ LOG(TRACE) << STORAGE_MODULE_FUNCTION -#define LOG_STORAGE_DEBUG_ LOG(DEBUG) << STORAGE_MODULE_FUNCTION -#define LOG_STORAGE_INFO_ LOG(INFO) << STORAGE_MODULE_FUNCTION -#define LOG_STORAGE_WARNING_ LOG(WARNING) << STORAGE_MODULE_FUNCTION -#define LOG_STORAGE_ERROR_ LOG(ERROR) << STORAGE_MODULE_FUNCTION -#define LOG_STORAGE_FATAL_ LOG(FATAL) << STORAGE_MODULE_FUNCTION - -///////////////////////////////////////////////////////////////////////////////////////////////// -#define WAL_MODULE_NAME "WAL" -#define WAL_MODULE_CLASS_FUNCTION \ - LogOut("[%s][%s::%s][%s] ", WAL_MODULE_NAME, (typeid(*this).name()), __FUNCTION__, GetThreadName().c_str()) -#define WAL_MODULE_FUNCTION LogOut("[%s][%s][%s] ", WAL_MODULE_NAME, __FUNCTION__, GetThreadName().c_str()) - -#define LOG_WAL_TRACE_C LOG(TRACE) << WAL_MODULE_CLASS_FUNCTION -#define LOG_WAL_DEBUG_C LOG(DEBUG) << WAL_MODULE_CLASS_FUNCTION -#define LOG_WAL_INFO_C LOG(INFO) << WAL_MODULE_CLASS_FUNCTION -#define LOG_WAL_WARNING_C LOG(WARNING) << WAL_MODULE_CLASS_FUNCTION -#define LOG_WAL_ERROR_C LOG(ERROR) << WAL_MODULE_CLASS_FUNCTION -#define LOG_WAL_FATAL_C LOG(FATAL) << WAL_MODULE_CLASS_FUNCTION - -#define LOG_WAL_TRACE_ LOG(TRACE) << WAL_MODULE_FUNCTION -#define LOG_WAL_DEBUG_ LOG(DEBUG) << WAL_MODULE_FUNCTION -#define LOG_WAL_INFO_ LOG(INFO) << WAL_MODULE_FUNCTION -#define LOG_WAL_WARNING_ LOG(WARNING) << WAL_MODULE_FUNCTION -#define LOG_WAL_ERROR_ LOG(ERROR) << WAL_MODULE_FUNCTION -#define LOG_WAL_FATAL_ LOG(FATAL) << WAL_MODULE_FUNCTION - -/* - * Deprecated - */ - -///////////////////////////////////////////////////////////////////////////////////////////////// -// #define SERVER_DOMAIN_NAME "[SERVER]" - -// #define LOG_SERVER_TRACE_ LOG(TRACE) << SERVER_DOMAIN_NAME -// #define LOG_SERVER_DEBUG_ LOG(DEBUG) << SERVER_DOMAIN_NAME -// #define LOG_SERVER_INFO_ LOG(INFO) << SERVER_DOMAIN_NAME -// #define LOG_SERVER_WARNING_ LOG(WARNING) << SERVER_DOMAIN_NAME -// #define LOG_SERVER_ERROR_ LOG(ERROR) << SERVER_DOMAIN_NAME -// #define LOG_SERVER_FATAL_ LOG(FATAL) << SERVER_DOMAIN_NAME - -///////////////////////////////////////////////////////////////////////////////////////////////// -// #define ENGINE_DOMAIN_NAME "[ENGINE]" - -// #define LOG_ENGINE_TRACE_ LOG(TRACE) << ENGINE_DOMAIN_NAME -// #define LOG_ENGINE_DEBUG_ LOG(DEBUG) << ENGINE_DOMAIN_NAME -// #define LOG_ENGINE_INFO_ LOG(INFO) << ENGINE_DOMAIN_NAME -// #define LOG_ENGINE_WARNING_ LOG(WARNING) << ENGINE_DOMAIN_NAME -// #define LOG_ENGINE_ERROR_ LOG(ERROR) << ENGINE_DOMAIN_NAME -// #define LOG_ENGINE_FATAL_ LOG(FATAL) << ENGINE_DOMAIN_NAME - -///////////////////////////////////////////////////////////////////////////////////////////////// -// #define WRAPPER_DOMAIN_NAME "[WRAPPER]" - -// #define WRAPPER_LOG_TRACE LOG(TRACE) << WRAPPER_DOMAIN_NAME -// #define WRAPPER_LOG_DEBUG LOG(DEBUG) << WRAPPER_DOMAIN_NAME -// #define WRAPPER_LOG_INFO LOG(INFO) << WRAPPER_DOMAIN_NAME -// #define WRAPPER_LOG_WARNING LOG(WARNING) << WRAPPER_DOMAIN_NAME -// #define WRAPPER_LOG_ERROR LOG(ERROR) << WRAPPER_DOMAIN_NAME -// #define WRAPPER_LOG_FATAL LOG(FATAL) << WRAPPER_DOMAIN_NAME - -///////////////////////////////////////////////////////////////////////////////////////////////// -// #define STORAGE_DOMAIN_NAME "[STORAGE]" - -// #define LOG_STORAGE_TRACE_ LOG(TRACE) << STORAGE_DOMAIN_NAME -// #define LOG_STORAGE_DEBUG_ LOG(DEBUG) << STORAGE_DOMAIN_NAME -// #define LOG_STORAGE_INFO_ LOG(INFO) << STORAGE_DOMAIN_NAME -// #define LOG_STORAGE_WARNING_ LOG(WARNING) << STORAGE_DOMAIN_NAME -// #define LOG_STORAGE_ERROR_ LOG(ERROR) << STORAGE_DOMAIN_NAME -// #define LOG_STORAGE_FATAL_ LOG(FATAL) << STORAGE_DOMAIN_NAME - -// #define WAL_DOMAIN_NAME "[WAL]" - -// #define LOG_WAL_TRACE_ LOG(TRACE) << WAL_DOMAIN_NAME -// #define LOG_WAL_DEBUG_ LOG(DEBUG) << WAL_DOMAIN_NAME -// #define LOG_WAL_INFO_ LOG(INFO) << WAL_DOMAIN_NAME -// #define LOG_WAL_WARNING_ LOG(WARNING) << WAL_DOMAIN_NAME -// #define LOG_WAL_ERROR_ LOG(ERROR) << WAL_DOMAIN_NAME -// #define LOG_WAL_FATAL_ LOG(FATAL) << WAL_DOMAIN_NAME - -///////////////////////////////////////////////////////////////////////////////////////////////////// -std::string -LogOut(const char* pattern, ...); - -void -SetThreadName(const std::string& name); - -std::string -GetThreadName(); - -} // namespace milvus diff --git a/core/src/utils/LogUtil.cpp b/core/src/utils/LogUtil.cpp deleted file mode 100644 index 1491c73852..0000000000 --- a/core/src/utils/LogUtil.cpp +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "utils/LogUtil.h" - -#include -#include -#include -#include - -#include -#include - -#include "config/Config.h" -#include "utils/Log.h" - -namespace milvus { -namespace server { - -namespace { -static int global_idx = 0; -static int debug_idx = 0; -static int warning_idx = 0; -static int trace_idx = 0; -static int error_idx = 0; -static int fatal_idx = 0; -static int64_t logs_delete_exceeds = 1; -static bool enable_log_delete = false; -} // namespace - -// TODO(yzb) : change the easylogging library to get the log level from parameter rather than filename -void -RolloutHandler(const char* filename, std::size_t size, el::Level level) { - char* dirc = strdup(filename); - char* basec = strdup(filename); - char* dir = dirname(dirc); - char* base = basename(basec); - - std::string s(base); - std::string list[] = {"\\", " ", "\'", "\"", "*", "\?", "{", "}", ";", "<", - ">", "|", "^", "&", "$", "#", "!", "`", "~"}; - std::string::size_type position; - for (auto substr : list) { - position = 0; - while ((position = s.find_first_of(substr, position)) != std::string::npos) { - s.insert(position, "\\"); - position += 2; - } - } - std::string m(std::string(dir) + "/" + s); - s = m; - try { - switch (level) { - case el::Level::Debug: { - s.append("." + std::to_string(++debug_idx)); - rename(m.c_str(), s.c_str()); - if (enable_log_delete && debug_idx - logs_delete_exceeds > 0) { - std::string to_delete = m + "." + std::to_string(debug_idx - logs_delete_exceeds); - // std::cout << "remote " << to_delete << std::endl; - boost::filesystem::remove(to_delete); - } - break; - } - case el::Level::Warning: { - s.append("." + std::to_string(++warning_idx)); - rename(m.c_str(), s.c_str()); - if (enable_log_delete && warning_idx - logs_delete_exceeds > 0) { - std::string to_delete = m + "." + std::to_string(warning_idx - logs_delete_exceeds); - boost::filesystem::remove(to_delete); - } - break; - } - case el::Level::Trace: { - s.append("." + std::to_string(++trace_idx)); - rename(m.c_str(), s.c_str()); - if (enable_log_delete && trace_idx - logs_delete_exceeds > 0) { - std::string to_delete = m + "." + std::to_string(trace_idx - logs_delete_exceeds); - boost::filesystem::remove(to_delete); - } - break; - } - case el::Level::Error: { - s.append("." + std::to_string(++error_idx)); - rename(m.c_str(), s.c_str()); - if (enable_log_delete && error_idx - logs_delete_exceeds > 0) { - std::string to_delete = m + "." + std::to_string(error_idx - logs_delete_exceeds); - boost::filesystem::remove(to_delete); - } - break; - } - case el::Level::Fatal: { - s.append("." + std::to_string(++fatal_idx)); - rename(m.c_str(), s.c_str()); - if (enable_log_delete && fatal_idx - logs_delete_exceeds > 0) { - std::string to_delete = m + "." + std::to_string(fatal_idx - logs_delete_exceeds); - boost::filesystem::remove(to_delete); - } - break; - } - default: { - s.append("." + std::to_string(++global_idx)); - rename(m.c_str(), s.c_str()); - if (enable_log_delete && global_idx - logs_delete_exceeds > 0) { - std::string to_delete = m + "." + std::to_string(global_idx - logs_delete_exceeds); - boost::filesystem::remove(to_delete); - } - break; - } - } - } catch (const std::exception& exc) { - std::cerr << exc.what() << ". Exception throws from RolloutHandler." << std::endl; - } -} - -Status -InitLog(bool trace_enable, bool debug_enable, bool info_enable, bool warning_enable, bool error_enable, - bool fatal_enable, const std::string& logs_path, int64_t max_log_file_size, int64_t delete_exceeds) { - el::Configurations defaultConf; - defaultConf.setToDefault(); - defaultConf.setGlobally(el::ConfigurationType::Format, "[%datetime][%level]%msg"); - defaultConf.setGlobally(el::ConfigurationType::ToFile, "true"); - defaultConf.setGlobally(el::ConfigurationType::ToStandardOutput, "false"); - defaultConf.setGlobally(el::ConfigurationType::SubsecondPrecision, "3"); - defaultConf.setGlobally(el::ConfigurationType::PerformanceTracking, "false"); - - std::string logs_reg_path = logs_path.rfind('/') == logs_path.length() - 1 ? logs_path : logs_path + "/"; - std::string global_log_path = logs_reg_path + "milvus-%datetime{%y-%M-%d-%H:%m}-global.log"; - defaultConf.set(el::Level::Global, el::ConfigurationType::Filename, global_log_path.c_str()); - defaultConf.set(el::Level::Global, el::ConfigurationType::Enabled, "true"); - - std::string info_log_path = logs_reg_path + "milvus-%datetime{%y-%M-%d-%H:%m}-info.log"; - defaultConf.set(el::Level::Info, el::ConfigurationType::Filename, info_log_path.c_str()); - fiu_do_on("LogUtil.InitLog.info_enable_to_false", info_enable = false); - if (info_enable) { - defaultConf.set(el::Level::Info, el::ConfigurationType::Enabled, "true"); - } else { - defaultConf.set(el::Level::Info, el::ConfigurationType::Enabled, "false"); - } - - std::string debug_log_path = logs_reg_path + "milvus-%datetime{%y-%M-%d-%H:%m}-debug.log"; - defaultConf.set(el::Level::Debug, el::ConfigurationType::Filename, debug_log_path.c_str()); - fiu_do_on("LogUtil.InitLog.debug_enable_to_false", debug_enable = false); - if (debug_enable) { - defaultConf.set(el::Level::Debug, el::ConfigurationType::Enabled, "true"); - } else { - defaultConf.set(el::Level::Debug, el::ConfigurationType::Enabled, "false"); - } - - std::string warning_log_path = logs_reg_path + "milvus-%datetime{%y-%M-%d-%H:%m}-warning.log"; - defaultConf.set(el::Level::Warning, el::ConfigurationType::Filename, warning_log_path.c_str()); - fiu_do_on("LogUtil.InitLog.warning_enable_to_false", warning_enable = false); - if (warning_enable) { - defaultConf.set(el::Level::Warning, el::ConfigurationType::Enabled, "true"); - } else { - defaultConf.set(el::Level::Warning, el::ConfigurationType::Enabled, "false"); - } - - std::string trace_log_path = logs_reg_path + "milvus-%datetime{%y-%M-%d-%H:%m}-trace.log"; - defaultConf.set(el::Level::Trace, el::ConfigurationType::Filename, trace_log_path.c_str()); - fiu_do_on("LogUtil.InitLog.trace_enable_to_false", trace_enable = false); - if (trace_enable) { - defaultConf.set(el::Level::Trace, el::ConfigurationType::Enabled, "true"); - } else { - defaultConf.set(el::Level::Trace, el::ConfigurationType::Enabled, "false"); - } - - std::string error_log_path = logs_reg_path + "milvus-%datetime{%y-%M-%d-%H:%m}-error.log"; - defaultConf.set(el::Level::Error, el::ConfigurationType::Filename, error_log_path.c_str()); - fiu_do_on("LogUtil.InitLog.error_enable_to_false", error_enable = false); - if (error_enable) { - defaultConf.set(el::Level::Error, el::ConfigurationType::Enabled, "true"); - } else { - defaultConf.set(el::Level::Error, el::ConfigurationType::Enabled, "false"); - } - - std::string fatal_log_path = logs_reg_path + "milvus-%datetime{%y-%M-%d-%H:%m}-fatal.log"; - defaultConf.set(el::Level::Fatal, el::ConfigurationType::Filename, fatal_log_path.c_str()); - fiu_do_on("LogUtil.InitLog.fatal_enable_to_false", fatal_enable = false); - if (fatal_enable) { - defaultConf.set(el::Level::Fatal, el::ConfigurationType::Enabled, "true"); - } else { - defaultConf.set(el::Level::Fatal, el::ConfigurationType::Enabled, "false"); - } - - fiu_do_on("LogUtil.InitLog.set_max_log_size_small_than_min", - max_log_file_size = CONFIG_LOGS_MAX_LOG_FILE_SIZE_MIN - 1); - if (max_log_file_size < CONFIG_LOGS_MAX_LOG_FILE_SIZE_MIN || - max_log_file_size > CONFIG_LOGS_MAX_LOG_FILE_SIZE_MAX) { - return Status(SERVER_UNEXPECTED_ERROR, "max_log_file_size must in range[" + - std::to_string(CONFIG_LOGS_MAX_LOG_FILE_SIZE_MIN) + ", " + - std::to_string(CONFIG_LOGS_MAX_LOG_FILE_SIZE_MAX) + "], now is " + - std::to_string(max_log_file_size)); - } - defaultConf.setGlobally(el::ConfigurationType::MaxLogFileSize, std::to_string(max_log_file_size)); - el::Loggers::addFlag(el::LoggingFlag::StrictLogFileSizeCheck); - el::Helpers::installPreRollOutCallback(RolloutHandler); - el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog); - - // set delete_exceeds = 0 means disable throw away log file even they reach certain limit. - if (delete_exceeds != 0) { - fiu_do_on("LogUtil.InitLog.delete_exceeds_small_than_min", delete_exceeds = CONFIG_LOGS_LOG_ROTATE_NUM_MIN - 1); - if (delete_exceeds < CONFIG_LOGS_LOG_ROTATE_NUM_MIN || delete_exceeds > CONFIG_LOGS_LOG_ROTATE_NUM_MAX) { - return Status(SERVER_UNEXPECTED_ERROR, "delete_exceeds must in range[" + - std::to_string(CONFIG_LOGS_LOG_ROTATE_NUM_MIN) + ", " + - std::to_string(CONFIG_LOGS_LOG_ROTATE_NUM_MAX) + "], now is " + - std::to_string(delete_exceeds)); - } - enable_log_delete = true; - logs_delete_exceeds = delete_exceeds; - } - - el::Loggers::reconfigureLogger("default", defaultConf); - - return Status::OK(); -} - -void -LogConfigInFile(const std::string& path) { - // TODO(yhz): Check if file exists - auto node = YAML::LoadFile(path); - YAML::Emitter out; - out << node; - LOG_SERVER_INFO_ << "\n\n" - << std::string(15, '*') << "Config in file" << std::string(15, '*') << "\n\n" - << out.c_str(); -} - -void -LogConfigInMem() { - auto& config = Config::GetInstance(); - std::string config_str; - config.GetConfigJsonStr(config_str, 3); - LOG_SERVER_INFO_ << "\n\n" - << std::string(15, '*') << "Config in memory" << std::string(15, '*') << "\n\n" - << config_str; -} - -void -LogCpuInfo() { - /*CPU information*/ - std::fstream fcpu("/proc/cpuinfo", std::ios::in); - if (!fcpu.is_open()) { - LOG_SERVER_WARNING_ << "Cannot obtain CPU information. Open file /proc/cpuinfo fail: " << strerror(errno) - << "(errno: " << errno << ")"; - return; - } - std::stringstream cpu_info_ss; - cpu_info_ss << fcpu.rdbuf(); - fcpu.close(); - std::string cpu_info = cpu_info_ss.str(); - - auto processor_pos = cpu_info.rfind("processor"); - if (std::string::npos == processor_pos) { - LOG_SERVER_WARNING_ << "Cannot obtain CPU information. No sub string \'processor\'"; - return; - } - - auto sub_str = cpu_info.substr(processor_pos); - LOG_SERVER_INFO_ << "\n\n" << std::string(15, '*') << "CPU" << std::string(15, '*') << "\n\n" << sub_str; -} - -} // namespace server -} // namespace milvus diff --git a/core/src/utils/LogUtil.h b/core/src/utils/LogUtil.h deleted file mode 100644 index 7c9b2533cb..0000000000 --- a/core/src/utils/LogUtil.h +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "easyloggingpp/easylogging++.h" -#include "utils/Status.h" - -#include -#include - -namespace milvus { -namespace server { - -Status -InitLog(bool trace_enable, bool debug_enable, bool info_enable, bool warning_enable, bool error_enable, - bool fatal_enable, const std::string& logs_path, int64_t max_log_file_size, int64_t delete_exceeds); - -void -RolloutHandler(const char* filename, std::size_t size, el::Level level); - -#define SHOW_LOCATION -#ifdef SHOW_LOCATION -#define LOCATION_INFO "[" << sql::server::GetFileName(__FILE__) << ":" << __LINE__ << "] " -#else -#define LOCATION_INFO "" -#endif - -void -LogConfigInFile(const std::string& path); - -void -LogConfigInMem(); - -void -LogCpuInfo(); - -} // namespace server -} // namespace milvus diff --git a/core/src/utils/SignalUtil.cpp b/core/src/utils/SignalUtil.cpp deleted file mode 100644 index cfb36ca592..0000000000 --- a/core/src/utils/SignalUtil.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "utils/SignalUtil.h" -#include "src/server/Server.h" -#include "src/server/init/InstanceLockCheck.h" -#include "utils/Log.h" - -#include -#include -#include - -namespace milvus { -namespace server { - -void -SignalUtil::HandleSignal(int signum) { - InstanceLockCheck::Release(); - LOG_SERVER_INFO_ << "Release lock!" << signum; - switch (signum) { - case SIGINT: - case SIGUSR2: { - LOG_SERVER_INFO_ << "Server received signal: " << signum; - - server::Server& server = server::Server::GetInstance(); - server.Stop(); - - exit(0); - } - default: { - LOG_SERVER_INFO_ << "Server received critical signal: " << signum; - SignalUtil::PrintStacktrace(); - - server::Server& server = server::Server::GetInstance(); - server.Stop(); - - exit(1); - } - } -} - -void -SignalUtil::PrintStacktrace() { - LOG_SERVER_INFO_ << "Call stack:"; - - const int size = 32; - void* array[size]; - int stack_num = backtrace(array, size); - char** stacktrace = backtrace_symbols(array, stack_num); - for (int i = 0; i < stack_num; ++i) { - std::string info = stacktrace[i]; - LOG_SERVER_INFO_ << info; - } - free(stacktrace); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/utils/SignalUtil.h b/core/src/utils/SignalUtil.h deleted file mode 100644 index 2c235fb1a9..0000000000 --- a/core/src/utils/SignalUtil.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -namespace milvus { -namespace server { - -class SignalUtil { - public: - static void - HandleSignal(int signum); - static void - PrintStacktrace(); -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/utils/Status.cpp b/core/src/utils/Status.cpp deleted file mode 100644 index a4f987bdf2..0000000000 --- a/core/src/utils/Status.cpp +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "utils/Status.h" - -#include - -namespace milvus { - -constexpr int CODE_WIDTH = sizeof(StatusCode); - -Status::Status(StatusCode code, const std::string& msg) { - // 4 bytes store code - // 4 bytes store message length - // the left bytes store message string - const uint32_t length = (uint32_t)msg.size(); - auto result = new char[length + sizeof(length) + CODE_WIDTH]; - std::memcpy(result, &code, CODE_WIDTH); - std::memcpy(result + CODE_WIDTH, &length, sizeof(length)); - memcpy(result + sizeof(length) + CODE_WIDTH, msg.data(), length); - - state_ = result; -} - -Status::Status() : state_(nullptr) { -} - -Status::~Status() { - delete state_; -} - -Status::Status(const Status& s) : state_(nullptr) { - CopyFrom(s); -} - -Status& -Status::operator=(const Status& s) { - CopyFrom(s); - return *this; -} - -Status::Status(Status&& s) : state_(nullptr) { - MoveFrom(s); -} - -Status& -Status::operator=(Status&& s) { - MoveFrom(s); - return *this; -} - -void -Status::CopyFrom(const Status& s) { - delete state_; - state_ = nullptr; - if (s.state_ == nullptr) { - return; - } - - uint32_t length = 0; - memcpy(&length, s.state_ + CODE_WIDTH, sizeof(length)); - int buff_len = length + sizeof(length) + CODE_WIDTH; - state_ = new char[buff_len]; - memcpy(state_, s.state_, buff_len); -} - -void -Status::MoveFrom(Status& s) { - delete state_; - state_ = s.state_; - s.state_ = nullptr; -} - -std::string -Status::message() const { - if (state_ == nullptr) { - return "OK"; - } - - std::string msg; - uint32_t length = 0; - memcpy(&length, state_ + CODE_WIDTH, sizeof(length)); - if (length > 0) { - msg.append(state_ + sizeof(length) + CODE_WIDTH, length); - } - - return msg; -} - -std::string -Status::ToString() const { - if (state_ == nullptr) { - return "OK"; - } - - std::string result; - switch (code()) { - case DB_SUCCESS: - result = "OK "; - break; - case DB_ERROR: - result = "Error: "; - break; - case DB_META_TRANSACTION_FAILED: - result = "Database error: "; - break; - case DB_NOT_FOUND: - result = "Not found: "; - break; - case DB_ALREADY_EXIST: - result = "Already exist: "; - break; - case DB_INVALID_PATH: - result = "Invalid path: "; - break; - default: - result = "Error code(" + std::to_string(code()) + "): "; - break; - } - - result += message(); - return result; -} - -} // namespace milvus diff --git a/core/src/utils/Status.h b/core/src/utils/Status.h deleted file mode 100644 index e67d6ed048..0000000000 --- a/core/src/utils/Status.h +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "utils/Error.h" - -#include - -namespace milvus { - -class Status; -#define STATUS_CHECK(func) \ - do { \ - Status s = func; \ - if (!s.ok()) { \ - return s; \ - } \ - } while (false) - -using StatusCode = ErrorCode; - -class Status { - public: - Status(StatusCode code, const std::string& msg); - Status(); - ~Status(); - - Status(const Status& s); - - Status& - operator=(const Status& s); - - Status(Status&& s); - - Status& - operator=(Status&& s); - - static Status - OK() { - return Status(); - } - - bool - ok() const { - return state_ == nullptr || code() == 0; - } - - StatusCode - code() const { - return (state_ == nullptr) ? 0 : *(StatusCode*)(state_); - } - - std::string - message() const; - - std::string - ToString() const; - - private: - inline void - CopyFrom(const Status& s); - - inline void - MoveFrom(Status& s); - - private: - char* state_ = nullptr; -}; // Status - -} // namespace milvus diff --git a/core/src/utils/StringHelpFunctions.cpp b/core/src/utils/StringHelpFunctions.cpp deleted file mode 100644 index cd2a74712b..0000000000 --- a/core/src/utils/StringHelpFunctions.cpp +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "utils/StringHelpFunctions.h" - -#include -#include -#include -#include - -#include "utils/ValidationUtil.h" - -namespace milvus { -namespace server { - -void -StringHelpFunctions::TrimStringBlank(std::string& string) { - if (!string.empty()) { - static std::string s_format(" \n\r\t"); - string.erase(0, string.find_first_not_of(s_format)); - string.erase(string.find_last_not_of(s_format) + 1); - } -} - -void -StringHelpFunctions::TrimStringQuote(std::string& string, const std::string& qoute) { - if (!string.empty()) { - string.erase(0, string.find_first_not_of(qoute)); - string.erase(string.find_last_not_of(qoute) + 1); - } -} - -void -StringHelpFunctions::SplitStringByDelimeter(const std::string& str, const std::string& delimeter, - std::vector& result) { - if (str.empty()) { - return; - } - - size_t prev = 0, pos = 0; - while (true) { - pos = str.find_first_of(delimeter, prev); - if (pos == std::string::npos) { - result.emplace_back(str.substr(prev)); - break; - } else { - result.emplace_back(str.substr(prev, pos - prev)); - prev = pos + 1; - } - } -} - -void -StringHelpFunctions::MergeStringWithDelimeter(const std::vector& strs, const std::string& delimeter, - std::string& result) { - if (strs.empty()) { - result = ""; - return; - } - - result = strs[0]; - for (size_t i = 1; i < strs.size(); i++) { - result = result + delimeter + strs[i]; - } -} - -Status -StringHelpFunctions::SplitStringByQuote(const std::string& str, const std::string& delimeter, const std::string& quote, - std::vector& result) { - if (quote.empty()) { - SplitStringByDelimeter(str, delimeter, result); - return Status::OK(); - } - - size_t last = 0; - size_t index = str.find_first_of(quote, last); - if (index == std::string::npos) { - SplitStringByDelimeter(str, delimeter, result); - return Status::OK(); - } - - std::string process_str = str; - while (index != std::string::npos) { - std::string prefix = process_str.substr(last, index - last); - std::string append_prefix; - if (!prefix.empty()) { - std::vector prefix_split; - SplitStringByDelimeter(prefix, delimeter, prefix_split); - for (size_t i = 0; i < prefix_split.size() - 1; i++) { - result.push_back(prefix_split[i]); - } - append_prefix = prefix_split[prefix_split.size() - 1]; - } - last = index + 1; - std::string postfix = process_str.substr(last); - index = postfix.find_first_of(quote, 0); - fiu_do_on("StringHelpFunctions.SplitStringByQuote.invalid_index", index = std::string::npos); - - if (index == std::string::npos) { - return Status(SERVER_UNEXPECTED_ERROR, ""); - } - std::string quoted_text = postfix.substr(0, index); - append_prefix += quoted_text; - - last = index + 1; - index = postfix.find_first_of(delimeter, last); - fiu_do_on("StringHelpFunctions.SplitStringByQuote.index_gt_last", last = 0); - fiu_do_on("StringHelpFunctions.SplitStringByQuote.invalid_index2", index = std::string::npos); - - if (index != std::string::npos) { - if (index > last) { - append_prefix += postfix.substr(last, index - last); - } - } else { - append_prefix += postfix.substr(last); - } - result.emplace_back(append_prefix); - fiu_do_on("StringHelpFunctions.SplitStringByQuote.last_is_end", last = postfix.length()); - - if (last == postfix.length()) { - return Status::OK(); - } - - process_str = postfix.substr(index + 1); - last = 0; - index = process_str.find_first_of(quote, last); - } - - if (!process_str.empty()) { - SplitStringByDelimeter(process_str, delimeter, result); - } - - return Status::OK(); -} - -bool -StringHelpFunctions::IsRegexMatch(const std::string& target_str, const std::string& pattern_str) { - // if target_str equals pattern_str, return true - if (target_str == pattern_str) { - return true; - } - - // regex match - std::regex pattern(pattern_str); - std::smatch results; - return std::regex_match(target_str, results, pattern); -} - -Status -StringHelpFunctions::ConvertToBoolean(const std::string& str, bool& value) { - auto status = ValidationUtil::ValidateStringIsBool(str); - if (!status.ok()) { - return status; - } - - std::string s = str; - std::transform(s.begin(), s.end(), s.begin(), ::tolower); - value = s == "true" || s == "on" || s == "yes" || s == "1"; - - return Status::OK(); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/utils/StringHelpFunctions.h b/core/src/utils/StringHelpFunctions.h deleted file mode 100644 index 48dd9ceb77..0000000000 --- a/core/src/utils/StringHelpFunctions.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "utils/Status.h" - -#include -#include - -namespace milvus { -namespace server { - -class StringHelpFunctions { - private: - StringHelpFunctions() = default; - - public: - // trim blanks from begin and end - // " a b c " => "a b c" - static void - TrimStringBlank(std::string& string); - - // trim quotes from begin and end - // "'abc'" => "abc" - static void - TrimStringQuote(std::string& string, const std::string& qoute); - - // split string by delimeter ',' - // a,b,c a | b | c - // a,b, a | b | - // ,b,c | b | c - // ,b, | b | - // ,, | | - // a a - static void - SplitStringByDelimeter(const std::string& str, const std::string& delimeter, std::vector& result); - - // merge strings with delimeter - // "a", "b", "c" => "a,b,c" - static void - MergeStringWithDelimeter(const std::vector& strs, const std::string& delimeter, std::string& result); - - // assume the collection has two columns, quote='\"', delimeter=',' - // a,b a | b - // "aa,gg,yy",b aa,gg,yy | b - // aa"dd,rr"kk,pp aadd,rrkk | pp - // "aa,bb" aa,bb - // 55,1122\"aa,bb\",yyy,\"kkk\" 55 | 1122aa,bb | yyy | kkk - // "55,1122"aa,bb",yyy,"kkk" illegal - static Status - SplitStringByQuote(const std::string& str, const std::string& delimeter, const std::string& quote, - std::vector& result); - - // std regex match function - // regex grammar reference: http://www.cplusplus.com/reference/regex/ECMAScript/ - static bool - IsRegexMatch(const std::string& target_str, const std::string& pattern); - - // conversion rules refer to ValidationUtil::ValidateStringIsBool() - // "true", "on", "yes", "1" ==> true - // "false", "off", "no", "0", "" ==> false - static Status - ConvertToBoolean(const std::string& str, bool& value); -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/utils/ThreadPool.h b/core/src/utils/ThreadPool.h deleted file mode 100644 index 0d3fe6ff26..0000000000 --- a/core/src/utils/ThreadPool.h +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MAX_THREADS_NUM 32 - -namespace milvus { - -class ThreadPool { - public: - explicit ThreadPool(size_t threads, size_t queue_size = 1000); - - template - auto - enqueue(F&& f, Args&&... args) -> std::future::type>; - - ~ThreadPool(); - - private: - // need to keep track of threads so we can join them - std::vector workers_; - - // the task queue - std::queue > tasks_; - - size_t max_queue_size_; - - // synchronization - std::mutex queue_mutex_; - - std::condition_variable condition_; - - bool stop; -}; - -// the constructor just launches some amount of workers -inline ThreadPool::ThreadPool(size_t threads, size_t queue_size) : max_queue_size_(queue_size), stop(false) { - for (size_t i = 0; i < threads; ++i) - workers_.emplace_back([this] { - for (;;) { - std::function task; - - { - std::unique_lock lock(this->queue_mutex_); - this->condition_.wait(lock, [this] { return this->stop || !this->tasks_.empty(); }); - if (this->stop && this->tasks_.empty()) - return; - task = std::move(this->tasks_.front()); - this->tasks_.pop(); - } - this->condition_.notify_all(); - - task(); - } - }); -} - -// add new work item to the pool -template -auto -ThreadPool::enqueue(F&& f, Args&&... args) -> std::future::type> { - using return_type = typename std::result_of::type; - - auto task = std::make_shared >( - std::bind(std::forward(f), std::forward(args)...)); - fiu_do_on("ThreadPool.enqueue.stop_is_true", stop = true); - std::future res = task->get_future(); - { - std::unique_lock lock(queue_mutex_); - this->condition_.wait(lock, [this] { return this->tasks_.size() < max_queue_size_; }); - // don't allow enqueueing after stopping the pool - if (stop) - throw std::runtime_error("enqueue on stopped ThreadPool"); - - tasks_.emplace([task]() { (*task)(); }); - } - condition_.notify_all(); - return res; -} - -// the destructor joins all threads -inline ThreadPool::~ThreadPool() { - { - std::unique_lock lock(queue_mutex_); - stop = true; - } - condition_.notify_all(); - for (std::thread& worker : workers_) { - worker.join(); - } -} - -} // namespace milvus diff --git a/core/src/utils/TimeRecorder.cpp b/core/src/utils/TimeRecorder.cpp deleted file mode 100644 index cb8b674753..0000000000 --- a/core/src/utils/TimeRecorder.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "utils/TimeRecorder.h" -#include "utils/Log.h" - -namespace milvus { - -TimeRecorder::TimeRecorder(const std::string& header, int64_t log_level) : header_(header), log_level_(log_level) { - start_ = last_ = stdclock::now(); -} - -TimeRecorder::~TimeRecorder() = default; - -std::string -TimeRecorder::GetTimeSpanStr(double span) { - std::string str_sec = std::to_string(span * 0.000001) + ((span > 1000000) ? " seconds" : " second"); - std::string str_ms = std::to_string(span * 0.001) + " ms"; - - return str_sec + " [" + str_ms + "]"; -} - -void -TimeRecorder::PrintTimeRecord(const std::string& msg, double span) { - std::string str_log; - if (!header_.empty()) - str_log += header_ + ": "; - str_log += msg; - str_log += " ("; - str_log += TimeRecorder::GetTimeSpanStr(span); - str_log += ")"; - - switch (log_level_) { - case 0: { - LOG_SERVER_TRACE_ << str_log; - break; - } - case 1: { - LOG_SERVER_DEBUG_ << str_log; - break; - } - case 2: { - LOG_SERVER_INFO_ << str_log; - break; - } - case 3: { - LOG_SERVER_WARNING_ << str_log; - break; - } - case 4: { - LOG_SERVER_ERROR_ << str_log; - break; - } - case 5: { - LOG_SERVER_FATAL_ << str_log; - break; - } - default: { - LOG_SERVER_INFO_ << str_log; - break; - } - } -} - -double -TimeRecorder::RecordSection(const std::string& msg) { - stdclock::time_point curr = stdclock::now(); - double span = (std::chrono::duration(curr - last_)).count(); - last_ = curr; - - PrintTimeRecord(msg, span); - return span; -} - -double -TimeRecorder::ElapseFromBegin(const std::string& msg) { - stdclock::time_point curr = stdclock::now(); - double span = (std::chrono::duration(curr - start_)).count(); - - PrintTimeRecord(msg, span); - return span; -} - -TimeRecorderAuto::TimeRecorderAuto(const std::string& header, int64_t log_level) : TimeRecorder(header, log_level) { -} - -TimeRecorderAuto::~TimeRecorderAuto() { - ElapseFromBegin("totally cost"); -} - -} // namespace milvus diff --git a/core/src/utils/TimeRecorder.h b/core/src/utils/TimeRecorder.h deleted file mode 100644 index ac11414970..0000000000 --- a/core/src/utils/TimeRecorder.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include -#include -#include "utils/Log.h" - -namespace milvus { -inline void -print_timestamp(const std::string& message) { - std::chrono::time_point now = std::chrono::system_clock::now(); - auto duration = now.time_since_epoch(); - auto micros = std::chrono::duration_cast(duration).count(); - micros %= 1000000; - double millisecond = (double)micros / 1000.0; - - LOG_SERVER_DEBUG_ << std::fixed << " " << millisecond << "(ms) [timestamp]" << message; -} - -class TimeRecorder { - using stdclock = std::chrono::high_resolution_clock; - - public: - explicit TimeRecorder(const std::string& header, int64_t log_level = 1); - - virtual ~TimeRecorder(); // trace = 0, debug = 1, info = 2, warn = 3, error = 4, critical = 5 - - double - RecordSection(const std::string& msg); - - double - ElapseFromBegin(const std::string& msg); - - static std::string - GetTimeSpanStr(double span); - - private: - void - PrintTimeRecord(const std::string& msg, double span); - - private: - std::string header_; - stdclock::time_point start_; - stdclock::time_point last_; - int64_t log_level_; -}; - -class TimeRecorderAuto : public TimeRecorder { - public: - explicit TimeRecorderAuto(const std::string& header, int64_t log_level = 1); - - ~TimeRecorderAuto() override; -}; - -} // namespace milvus diff --git a/core/src/utils/ValidationUtil.cpp b/core/src/utils/ValidationUtil.cpp deleted file mode 100644 index d2b4b39624..0000000000 --- a/core/src/utils/ValidationUtil.cpp +++ /dev/null @@ -1,670 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#include "utils/ValidationUtil.h" -#include "Log.h" -#include "db/Types.h" -#include "db/Utils.h" -#include "db/engine/ExecutionEngine.h" -#include "knowhere/index/vector_index/ConfAdapter.h" -#include "knowhere/index/vector_index/helpers/IndexParameter.h" -#include "utils/StringHelpFunctions.h" - -#include - -#ifdef MILVUS_GPU_VERSION - -#include - -#endif - -#include -#include -#include -#include -#include -#include -#include - -namespace milvus { -namespace server { - -namespace { -constexpr size_t COLLECTION_NAME_SIZE_LIMIT = 255; -constexpr int64_t COLLECTION_DIMENSION_LIMIT = 32768; -constexpr int32_t INDEX_FILE_SIZE_LIMIT = 4096; // index trigger size max = 4096 MB -constexpr int64_t M_BYTE = 1024 * 1024; -constexpr int64_t MAX_INSERT_DATA_SIZE = 256 * M_BYTE; - -Status -CheckParameterRange(const milvus::json& json_params, const std::string& param_name, int64_t min, int64_t max, - bool min_close = true, bool max_closed = true) { - if (json_params.find(param_name) == json_params.end()) { - std::string msg = "Parameter list must contain: "; - msg += param_name; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - - try { - int64_t value = json_params[param_name]; - bool min_err = min_close ? value < min : value <= min; - bool max_err = max_closed ? value > max : value >= max; - if (min_err || max_err) { - std::string msg = "Invalid " + param_name + " value: " + std::to_string(value) + ". Valid range is " + - (min_close ? "[" : "(") + std::to_string(min) + ", " + std::to_string(max) + - (max_closed ? "]" : ")"); - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - } catch (std::exception& e) { - std::string msg = "Invalid " + param_name + ": "; - msg += e.what(); - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - - return Status::OK(); -} - -Status -CheckParameterExistence(const milvus::json& json_params, const std::string& param_name) { - if (json_params.find(param_name) == json_params.end()) { - std::string msg = "Parameter list must contain: "; - msg += param_name; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - - try { - int64_t value = json_params[param_name]; - if (value < 0) { - std::string msg = "Invalid " + param_name + " value: " + std::to_string(value); - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - } catch (std::exception& e) { - std::string msg = "Invalid " + param_name + ": "; - msg += e.what(); - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - - return Status::OK(); -} - -} // namespace - -Status -ValidationUtil::ValidateCollectionName(const std::string& collection_name) { - // Collection name shouldn't be empty. - if (collection_name.empty()) { - std::string msg = "Collection name should not be empty."; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_COLLECTION_NAME, msg); - } - - std::string invalid_msg = "Invalid collection name: " + collection_name + ". "; - // Collection name size shouldn't exceed 255. - if (collection_name.size() > COLLECTION_NAME_SIZE_LIMIT) { - std::string msg = invalid_msg + "The length of a collection name must be less than 255 characters."; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_COLLECTION_NAME, msg); - } - - // Collection name first character should be underscore or character. - char first_char = collection_name[0]; - if (first_char != '_' && std::isalpha(first_char) == 0) { - std::string msg = invalid_msg + "The first character of a collection name must be an underscore or letter."; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_COLLECTION_NAME, msg); - } - - int64_t table_name_size = collection_name.size(); - for (int64_t i = 1; i < table_name_size; ++i) { - char name_char = collection_name[i]; - if (name_char != '_' && std::isalnum(name_char) == 0) { - std::string msg = invalid_msg + "Collection name can only contain numbers, letters, and underscores."; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_COLLECTION_NAME, msg); - } - } - - return Status::OK(); -} - -Status -ValidationUtil::ValidateTableDimension(int64_t dimension, int64_t metric_type) { - if (dimension <= 0 || dimension > COLLECTION_DIMENSION_LIMIT) { - std::string msg = "Invalid collection dimension: " + std::to_string(dimension) + ". " + - "The collection dimension must be within the range of 1 ~ " + - std::to_string(COLLECTION_DIMENSION_LIMIT) + "."; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_VECTOR_DIMENSION, msg); - } - - if (milvus::engine::utils::IsBinaryMetricType(metric_type)) { - if ((dimension % 8) != 0) { - std::string msg = "Invalid collection dimension: " + std::to_string(dimension) + ". " + - "The collection dimension must be a multiple of 8"; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_VECTOR_DIMENSION, msg); - } - } - - return Status::OK(); -} - -Status -ValidationUtil::ValidateCollectionIndexType(int32_t index_type) { - int engine_type = static_cast(engine::EngineType(index_type)); - if (engine_type <= 0 || engine_type > static_cast(engine::EngineType::MAX_VALUE)) { - std::string msg = "Invalid index type: " + std::to_string(index_type) + ". " + - "Make sure the index type is in IndexType list."; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_INDEX_TYPE, msg); - } - -#ifndef MILVUS_GPU_VERSION - // special case, hybird index only available in customize faiss library - if (engine_type == static_cast(engine::EngineType::FAISS_IVFSQ8H)) { - std::string msg = "Unsupported index type: " + std::to_string(index_type); - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_INDEX_TYPE, msg); - } -#endif - - return Status::OK(); -} - -Status -ValidationUtil::ValidateIndexParams(const milvus::json& index_params, - const engine::meta::CollectionSchema& collection_schema, int32_t index_type) { - switch (index_type) { - case (int32_t)engine::EngineType::FAISS_IDMAP: - case (int32_t)engine::EngineType::FAISS_BIN_IDMAP: { - break; - } - case (int32_t)engine::EngineType::FAISS_IVFFLAT: - case (int32_t)engine::EngineType::FAISS_IVFSQ8: - case (int32_t)engine::EngineType::FAISS_IVFSQ8H: - case (int32_t)engine::EngineType::FAISS_BIN_IVFFLAT: { - auto status = CheckParameterRange(index_params, knowhere::IndexParams::nlist, 1, 999999); - if (!status.ok()) { - return status; - } - break; - } - case (int32_t)engine::EngineType::FAISS_PQ: { - auto status = CheckParameterRange(index_params, knowhere::IndexParams::nlist, 1, 999999); - if (!status.ok()) { - return status; - } - - status = CheckParameterExistence(index_params, knowhere::IndexParams::m); - if (!status.ok()) { - return status; - } - - // special check for 'm' parameter - std::vector resset; - milvus::knowhere::IVFPQConfAdapter::GetValidMList(collection_schema.dimension_, resset); - int64_t m_value = index_params[knowhere::IndexParams::m]; - if (resset.empty()) { - std::string msg = "Invalid collection dimension, unable to get reasonable values for 'm'"; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_COLLECTION_DIMENSION, msg); - } - - auto iter = std::find(std::begin(resset), std::end(resset), m_value); - if (iter == std::end(resset)) { - std::string msg = - "Invalid " + std::string(knowhere::IndexParams::m) + ", must be one of the following values: "; - for (size_t i = 0; i < resset.size(); i++) { - if (i != 0) { - msg += ","; - } - msg += std::to_string(resset[i]); - } - - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - - break; - } - case (int32_t)engine::EngineType::NSG_MIX: { - auto status = CheckParameterRange(index_params, knowhere::IndexParams::search_length, 10, 300); - if (!status.ok()) { - return status; - } - status = CheckParameterRange(index_params, knowhere::IndexParams::out_degree, 5, 300); - if (!status.ok()) { - return status; - } - status = CheckParameterRange(index_params, knowhere::IndexParams::candidate, 50, 1000); - if (!status.ok()) { - return status; - } - status = CheckParameterRange(index_params, knowhere::IndexParams::knng, 5, 300); - if (!status.ok()) { - return status; - } - break; - } - case (int32_t)engine::EngineType::HNSW: { - auto status = CheckParameterRange(index_params, knowhere::IndexParams::M, 4, 64); - if (!status.ok()) { - return status; - } - status = CheckParameterRange(index_params, knowhere::IndexParams::efConstruction, 8, 512); - if (!status.ok()) { - return status; - } - break; - } - case (int32_t)engine::EngineType::ANNOY: { - auto status = CheckParameterRange(index_params, knowhere::IndexParams::n_trees, 1, 1024); - if (!status.ok()) { - return status; - } - break; - } - } - return Status::OK(); -} - -Status -ValidationUtil::ValidateSearchParams(const milvus::json& search_params, - const engine::meta::CollectionSchema& collection_schema, int64_t topk) { - switch (collection_schema.engine_type_) { - case (int32_t)engine::EngineType::FAISS_IDMAP: - case (int32_t)engine::EngineType::FAISS_BIN_IDMAP: { - break; - } - case (int32_t)engine::EngineType::FAISS_IVFFLAT: - case (int32_t)engine::EngineType::FAISS_IVFSQ8: - case (int32_t)engine::EngineType::FAISS_IVFSQ8H: - case (int32_t)engine::EngineType::FAISS_BIN_IVFFLAT: - case (int32_t)engine::EngineType::FAISS_PQ: { - auto status = CheckParameterRange(search_params, knowhere::IndexParams::nprobe, 1, 999999); - if (!status.ok()) { - return status; - } - break; - } - case (int32_t)engine::EngineType::NSG_MIX: { - auto status = CheckParameterRange(search_params, knowhere::IndexParams::search_length, 10, 300); - if (!status.ok()) { - return status; - } - break; - } - case (int32_t)engine::EngineType::HNSW: { - auto status = CheckParameterRange(search_params, knowhere::IndexParams::ef, topk, 4096); - if (!status.ok()) { - return status; - } - break; - } - case (int32_t)engine::EngineType::ANNOY: { - auto status = CheckParameterRange(search_params, knowhere::IndexParams::search_k, - std::numeric_limits::min(), std::numeric_limits::max()); - if (!status.ok()) { - return status; - } - break; - } - } - return Status::OK(); -} - -Status -ValidationUtil::ValidateVectorData(const engine::VectorsData& vectors, - const engine::meta::CollectionSchema& collection_schema) { - uint64_t vector_count = vectors.vector_count_; - if ((vectors.float_data_.empty() && vectors.binary_data_.empty()) || vector_count == 0) { - return Status(SERVER_INVALID_ROWRECORD_ARRAY, - "The vector array is empty. Make sure you have entered vector records."); - } - - if (engine::utils::IsBinaryMetricType(collection_schema.metric_type_)) { - // check prepared binary data - if (vectors.binary_data_.size() % vector_count != 0) { - return Status(SERVER_INVALID_ROWRECORD_ARRAY, - "The vector dimension must be equal to the collection dimension."); - } - - if (vectors.binary_data_.size() * 8 / vector_count != collection_schema.dimension_) { - return Status(SERVER_INVALID_VECTOR_DIMENSION, - "The vector dimension must be equal to the collection dimension."); - } - } else { - // check prepared float data - fiu_do_on("SearchRequest.OnExecute.invalod_rowrecord_array", vector_count = vectors.float_data_.size() + 1); - if (vectors.float_data_.size() % vector_count != 0) { - return Status(SERVER_INVALID_ROWRECORD_ARRAY, - "The vector dimension must be equal to the collection dimension."); - } - if (vectors.float_data_.size() / vector_count != collection_schema.dimension_) { - return Status(SERVER_INVALID_VECTOR_DIMENSION, - "The vector dimension must be equal to the collection dimension."); - } - } - - return Status::OK(); -} - -Status -ValidationUtil::ValidateVectorDataSize(const engine::VectorsData& vectors, - const engine::meta::CollectionSchema& collection_schema) { - std::string msg = - "The amount of data inserted each time cannot exceed " + std::to_string(MAX_INSERT_DATA_SIZE / M_BYTE) + " MB"; - if (engine::utils::IsBinaryMetricType(collection_schema.metric_type_)) { - if (vectors.binary_data_.size() > MAX_INSERT_DATA_SIZE) { - return Status(SERVER_INVALID_ROWRECORD_ARRAY, msg); - } - - } else { - if (vectors.float_data_.size() * sizeof(float) > MAX_INSERT_DATA_SIZE) { - return Status(SERVER_INVALID_ROWRECORD_ARRAY, msg); - } - } - - return Status::OK(); -} - -Status -ValidationUtil::ValidateCollectionIndexFileSize(int64_t index_file_size) { - if (index_file_size <= 0 || index_file_size > INDEX_FILE_SIZE_LIMIT) { - std::string msg = "Invalid index file size: " + std::to_string(index_file_size) + ". " + - "The index file size must be within the range of 1 ~ " + - std::to_string(INDEX_FILE_SIZE_LIMIT) + "."; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_INDEX_FILE_SIZE, msg); - } - - return Status::OK(); -} - -Status -ValidationUtil::ValidateCollectionIndexMetricType(int32_t metric_type) { - if (metric_type <= 0 || metric_type > static_cast(engine::MetricType::MAX_VALUE)) { - std::string msg = "Invalid index metric type: " + std::to_string(metric_type) + ". " + - "Make sure the metric type is in MetricType list."; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_INDEX_METRIC_TYPE, msg); - } - return Status::OK(); -} - -Status -ValidationUtil::ValidateSearchTopk(int64_t top_k) { - if (top_k <= 0 || top_k > QUERY_MAX_TOPK) { - std::string msg = - "Invalid topk: " + std::to_string(top_k) + ". " + "The topk must be within the range of 1 ~ 2048."; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_TOPK, msg); - } - - return Status::OK(); -} - -Status -ValidationUtil::ValidatePartitionName(const std::string& partition_name) { - if (partition_name.empty()) { - std::string msg = "Partition name should not be empty."; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_COLLECTION_NAME, msg); - } - - std::string invalid_msg = "Invalid partition name: " + partition_name + ". "; - // Collection name size shouldn't exceed 255. - if (partition_name.size() > COLLECTION_NAME_SIZE_LIMIT) { - std::string msg = invalid_msg + "The length of a partition name must be less than 255 characters."; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_COLLECTION_NAME, msg); - } - - // Collection name first character should be underscore or character. - char first_char = partition_name[0]; - if (first_char != '_' && std::isalpha(first_char) == 0) { - std::string msg = invalid_msg + "The first character of a partition name must be an underscore or letter."; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_COLLECTION_NAME, msg); - } - - int64_t table_name_size = partition_name.size(); - for (int64_t i = 1; i < table_name_size; ++i) { - char name_char = partition_name[i]; - if (name_char != '_' && std::isalnum(name_char) == 0) { - std::string msg = invalid_msg + "Partition name can only contain numbers, letters, and underscores."; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_COLLECTION_NAME, msg); - } - } - - return Status::OK(); -} - -Status -ValidationUtil::ValidatePartitionTags(const std::vector& partition_tags) { - for (const std::string& tag : partition_tags) { - // trim side-blank of tag, only compare valid characters - // for example: " ab cd " is treated as "ab cd" - std::string valid_tag = tag; - StringHelpFunctions::TrimStringBlank(valid_tag); - if (valid_tag.empty()) { - std::string msg = "Invalid partition tag: " + valid_tag + ". " + "Partition tag should not be empty."; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_PARTITION_TAG, msg); - } - - // max length of partition tag - if (valid_tag.length() > 255) { - std::string msg = "Invalid partition tag: " + valid_tag + ". " + "Partition tag exceed max length(255)."; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_PARTITION_TAG, msg); - } - } - - return Status::OK(); -} - -Status -ValidationUtil::ValidateGpuIndex(int32_t gpu_index) { -#ifdef MILVUS_GPU_VERSION - int num_devices = 0; - auto cuda_err = cudaGetDeviceCount(&num_devices); - fiu_do_on("ValidationUtil.ValidateGpuIndex.get_device_count_fail", cuda_err = cudaError::cudaErrorUnknown); - - if (cuda_err != cudaSuccess) { - std::string msg = "Failed to get gpu card number, cuda error:" + std::to_string(cuda_err); - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_UNEXPECTED_ERROR, msg); - } - - if (gpu_index >= num_devices) { - std::string msg = "Invalid gpu index: " + std::to_string(gpu_index); - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_ARGUMENT, msg); - } -#endif - - return Status::OK(); -} - -#ifdef MILVUS_GPU_VERSION - -Status -ValidationUtil::GetGpuMemory(int32_t gpu_index, int64_t& memory) { - fiu_return_on("ValidationUtil.GetGpuMemory.return_error", Status(SERVER_UNEXPECTED_ERROR, "")); - - cudaDeviceProp deviceProp; - auto cuda_err = cudaGetDeviceProperties(&deviceProp, gpu_index); - if (cuda_err) { - std::string msg = "Failed to get gpu properties for gpu" + std::to_string(gpu_index) + - " , cuda error:" + std::to_string(cuda_err); - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_UNEXPECTED_ERROR, msg); - } - - memory = deviceProp.totalGlobalMem; - return Status::OK(); -} - -#endif - -Status -ValidationUtil::ValidateIpAddress(const std::string& ip_address) { - struct in_addr address; - - int result = inet_pton(AF_INET, ip_address.c_str(), &address); - fiu_do_on("ValidationUtil.ValidateIpAddress.error_ip_result", result = 2); - - switch (result) { - case 1: - return Status::OK(); - case 0: { - std::string msg = "Invalid IP address: " + ip_address; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_INVALID_ARGUMENT, msg); - } - default: { - std::string msg = "IP address conversion error: " + ip_address; - LOG_SERVER_ERROR_ << msg; - return Status(SERVER_UNEXPECTED_ERROR, msg); - } - } -} - -Status -ValidationUtil::ValidateStringIsNumber(const std::string& str) { - if (str.empty() || !std::all_of(str.begin(), str.end(), ::isdigit)) { - return Status(SERVER_INVALID_ARGUMENT, "Invalid number"); - } - try { - int64_t value = std::stol(str); - fiu_do_on("ValidationUtil.ValidateStringIsNumber.throw_exception", throw std::exception()); - if (value < 0) { - return Status(SERVER_INVALID_ARGUMENT, "Negative number"); - } - } catch (...) { - return Status(SERVER_INVALID_ARGUMENT, "Invalid number"); - } - return Status::OK(); -} - -Status -ValidationUtil::ValidateStringIsBool(const std::string& str) { - fiu_return_on("ValidateStringNotBool", Status(SERVER_INVALID_ARGUMENT, "Invalid boolean: " + str)); - std::string s = str; - std::transform(s.begin(), s.end(), s.begin(), ::tolower); - if (s == "true" || s == "on" || s == "yes" || s == "1" || s == "false" || s == "off" || s == "no" || s == "0" || - s.empty()) { - return Status::OK(); - } - return Status(SERVER_INVALID_ARGUMENT, "Invalid boolean: " + str); -} - -Status -ValidationUtil::ValidateStringIsFloat(const std::string& str) { - try { - float val = std::stof(str); - if (val < 0.0) { - return Status(SERVER_INVALID_ARGUMENT, "Negative float: " + str); - } - } catch (...) { - return Status(SERVER_INVALID_ARGUMENT, "Invalid float: " + str); - } - return Status::OK(); -} - -Status -ValidationUtil::ValidateDbURI(const std::string& uri) { - std::string dialectRegex = "(.*)"; - std::string usernameRegex = "(.*)"; - std::string passwordRegex = "(.*)"; - std::string hostRegex = "(.*)"; - std::string portRegex = "(.*)"; - std::string dbNameRegex = "(.*)"; - std::string uriRegexStr = dialectRegex + "\\:\\/\\/" + usernameRegex + "\\:" + passwordRegex + "\\@" + hostRegex + - "\\:" + portRegex + "\\/" + dbNameRegex; - std::regex uriRegex(uriRegexStr); - std::smatch pieces_match; - - bool okay = true; - - if (std::regex_match(uri, pieces_match, uriRegex)) { - std::string dialect = pieces_match[1].str(); - std::transform(dialect.begin(), dialect.end(), dialect.begin(), ::tolower); - if (dialect.find("mysql") == std::string::npos && dialect.find("sqlite") == std::string::npos) { - LOG_SERVER_ERROR_ << "Invalid dialect in URI: dialect = " << dialect; - okay = false; - } - - /* - * Could be DNS, skip checking - * - std::string host = pieces_match[4].str(); - if (!host.empty() && host != "localhost") { - if (ValidateIpAddress(host) != SERVER_SUCCESS) { - LOG_SERVER_ERROR_ << "Invalid host ip address in uri = " << host; - okay = false; - } - } - */ - - std::string port = pieces_match[5].str(); - if (!port.empty()) { - auto status = ValidateStringIsNumber(port); - if (!status.ok()) { - LOG_SERVER_ERROR_ << "Invalid port in uri = " << port; - okay = false; - } - } - } else { - LOG_SERVER_ERROR_ << "Wrong URI format: URI = " << uri; - okay = false; - } - - return (okay ? Status::OK() : Status(SERVER_INVALID_ARGUMENT, "Invalid db backend uri")); -} - -Status -ValidationUtil::ValidateStoragePath(const std::string& path) { - // Validate storage path if is valid, only correct absolute path will be validated pass - // Invalid path only contain character[a-zA-Z], number[0-9], '-', and '_', - // and path must start with '/'. - // examples below are invalid - // '/a//a', '/a--/a', '/-a/a', '/a@#/a', 'aaa/sfs' - std::string path_pattern = "^\\/(\\w+-?\\/?)+$"; - std::regex regex(path_pattern); - - return std::regex_match(path, regex) ? Status::OK() : Status(SERVER_INVALID_ARGUMENT, "Invalid file path"); -} - -Status -ValidationUtil::ValidateLogLevel(const std::string& level) { - std::set supported_level{"debug", "info", "warning", "error", "fatal"}; - - return supported_level.find(level) != supported_level.end() - ? Status::OK() - : Status(SERVER_INVALID_ARGUMENT, "Log level must be one of debug, info, warning, error and fatal."); -} - -bool -ValidationUtil::IsNumber(const std::string& s) { - return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit); -} - -} // namespace server -} // namespace milvus diff --git a/core/src/utils/ValidationUtil.h b/core/src/utils/ValidationUtil.h deleted file mode 100644 index 27a9f85566..0000000000 --- a/core/src/utils/ValidationUtil.h +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#pragma once - -#include "db/Types.h" -#include "db/meta/MetaTypes.h" -#include "utils/Json.h" -#include "utils/Status.h" - -#include -#include - -namespace milvus { -namespace server { - -constexpr int64_t QUERY_MAX_TOPK = 2048; - -class ValidationUtil { - private: - ValidationUtil() = default; - - public: - static Status - ValidateCollectionName(const std::string& collection_name); - - static Status - ValidateTableDimension(int64_t dimension, int64_t metric_type); - - static Status - ValidateCollectionIndexType(int32_t index_type); - - static Status - ValidateIndexParams(const milvus::json& index_params, const engine::meta::CollectionSchema& collection_schema, - int32_t index_type); - - static Status - ValidateSearchParams(const milvus::json& search_params, const engine::meta::CollectionSchema& collection_schema, - int64_t topk); - - static Status - ValidateVectorData(const engine::VectorsData& vectors, const engine::meta::CollectionSchema& collection_schema); - - static Status - ValidateVectorDataSize(const engine::VectorsData& vectors, const engine::meta::CollectionSchema& collection_schema); - - static Status - ValidateCollectionIndexFileSize(int64_t index_file_size); - - static Status - ValidateCollectionIndexMetricType(int32_t metric_type); - - static Status - ValidateSearchTopk(int64_t top_k); - - static Status - ValidatePartitionName(const std::string& partition_name); - - static Status - ValidatePartitionTags(const std::vector& partition_tags); - - static Status - ValidateGpuIndex(int32_t gpu_index); - -#ifdef MILVUS_GPU_VERSION - static Status - GetGpuMemory(int32_t gpu_index, int64_t& memory); -#endif - - static Status - ValidateIpAddress(const std::string& ip_address); - - static Status - ValidateStringIsNumber(const std::string& str); - - static Status - ValidateStringIsBool(const std::string& str); - - static Status - ValidateStringIsFloat(const std::string& str); - - static Status - ValidateDbURI(const std::string& uri); - - static Status - ValidateStoragePath(const std::string& path); - - static Status - ValidateLogLevel(const std::string& level); - - static bool - IsNumber(const std::string& s); -}; - -} // namespace server -} // namespace milvus diff --git a/core/src/version.h.in b/core/src/version.h.in deleted file mode 100644 index 32c30fbe45..0000000000 --- a/core/src/version.h.in +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (C) 2019-2020 Zilliz. All rights reserved. -// -// Licensed 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 limitations under the License. - -#cmakedefine MILVUS_VERSION "@MILVUS_VERSION@" -#cmakedefine BUILD_TYPE "@BUILD_TYPE@" -#cmakedefine BUILD_TIME @BUILD_TIME@ -#cmakedefine LAST_COMMIT_ID "@LAST_COMMIT_ID@" diff --git a/core/start_server.sh b/core/start_server.sh deleted file mode 100755 index 0ae47e6bdf..0000000000 --- a/core/start_server.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -./cmake_build/src/milvus_server -c ./conf/server_config.yaml & - diff --git a/core/stop_server.sh b/core/stop_server.sh deleted file mode 100755 index f15e48a954..0000000000 --- a/core/stop_server.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -function kill_progress() -{ - kill -s SIGUSR2 $(pgrep $1) - - sleep 2 -} - -STATUS=$(kill_progress "milvus_server" ) - -if [[ ${STATUS} == "false" ]];then - echo "Milvus server closed abnormally!" -else - echo "Milvus server closed successfully!" -fi diff --git a/core/thirdparty/dablooms/dablooms.cpp b/core/thirdparty/dablooms/dablooms.cpp deleted file mode 100644 index e79e52a3b6..0000000000 --- a/core/thirdparty/dablooms/dablooms.cpp +++ /dev/null @@ -1,596 +0,0 @@ -/* Copyright @2012 by Justin Hines at Bitly under a very liberal license. See LICENSE in the source distribution. */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "murmur.h" -#include "dablooms.h" - -#define DABLOOMS_VERSION "0.9.1" - -#define ERROR_TIGHTENING_RATIO 0.5 -#define SALT_CONSTANT 0x97c29b3a - -const char *dablooms_version(void) -{ - return DABLOOMS_VERSION; -} - -void free_bitmap(bitmap_t *bitmap) -{ - if ((munmap(bitmap->array, bitmap->bytes)) < 0) { - perror("Error, unmapping memory"); - } - close(bitmap->fd); - free(bitmap); -} - -bitmap_t *bitmap_resize(bitmap_t *bitmap, size_t old_size, size_t new_size) -{ - int fd = bitmap->fd; - struct stat fileStat; - - fstat(fd, &fileStat); - size_t size = fileStat.st_size; - - /* grow file if necessary */ - if (size < new_size) { - if (ftruncate(fd, new_size) < 0) { - perror("Error increasing file size with ftruncate"); - free_bitmap(bitmap); - close(fd); - return NULL; - } - } - lseek(fd, 0, SEEK_SET); - - /* resize if mmap exists and possible on this os, else new mmap */ - if (bitmap->array != NULL) { -#if __linux - bitmap->array = (char *)mremap(bitmap->array, old_size, new_size, MREMAP_MAYMOVE); - if (bitmap->array == MAP_FAILED) { - perror("Error resizing mmap"); - free_bitmap(bitmap); - close(fd); - return NULL; - } -#else - if (munmap(bitmap->array, bitmap->bytes) < 0) { - perror("Error unmapping memory"); - free_bitmap(bitmap); - close(fd); - return NULL; - } - bitmap->array = NULL; -#endif - } - if (bitmap->array == NULL) { - bitmap->array = (char *)mmap(0, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (bitmap->array == MAP_FAILED) { - perror("Error init mmap"); - free_bitmap(bitmap); - close(fd); - return NULL; - } - } - - bitmap->bytes = new_size; - return bitmap; -} - -/* Create a new bitmap, not full featured, simple to give - * us a means of interacting with the 4 bit counters */ -bitmap_t *new_bitmap(int fd, size_t bytes) -{ - bitmap_t *bitmap; - - if ((bitmap = (bitmap_t *)malloc(sizeof(bitmap_t))) == NULL) { - return NULL; - } - - bitmap->bytes = bytes; - bitmap->fd = fd; - bitmap->array = NULL; - - if ((bitmap = bitmap_resize(bitmap, 0, bytes)) == NULL) { - return NULL; - } - - return bitmap; -} - -int bitmap_increment(bitmap_t *bitmap, unsigned int index, long offset) -{ - long access = index / 2 + offset; - uint8_t temp; - uint8_t n = bitmap->array[access]; - if (index % 2 != 0) { - temp = (n & 0x0f); - n = (n & 0xf0) + ((n & 0x0f) + 0x01); - } else { - temp = (n & 0xf0) >> 4; - n = (n & 0x0f) + ((n & 0xf0) + 0x10); - } - - if (temp == 0x0f) { -// fprintf(stderr, "Error, 4 bit int Overflow\n"); - return -1; - } - - bitmap->array[access] = n; - return 0; -} - -/* increments the four bit counter */ -int bitmap_decrement(bitmap_t *bitmap, unsigned int index, long offset) -{ - long access = index / 2 + offset; - uint8_t temp; - uint8_t n = bitmap->array[access]; - - if (index % 2 != 0) { - temp = (n & 0x0f); - n = (n & 0xf0) + ((n & 0x0f) - 0x01); - } else { - temp = (n & 0xf0) >> 4; - n = (n & 0x0f) + ((n & 0xf0) - 0x10); - } - - if (temp == 0x00) { -// fprintf(stderr, "Error, Decrementing zero\n"); -// fprintf(stderr, "Bloom filter Error: you have deleted the same id more than 15 times!\n"); - return -1; - } - - bitmap->array[access] = n; - return 0; -} - -/* decrements the four bit counter */ -int bitmap_check(bitmap_t *bitmap, unsigned int index, long offset) -{ - long access = index / 2 + offset; - if (index % 2 != 0 ) { - return bitmap->array[access] & 0x0f; - } else { - return bitmap->array[access] & 0xf0; - } -} - -int bitmap_flush(bitmap_t *bitmap) -{ - if ((msync(bitmap->array, bitmap->bytes, MS_ASYNC) < 0)) { - perror("Error, flushing bitmap to disk"); - return -1; - } else { - return 0; - } -} - -/* - * Perform the actual hashing for `key` - * - * Only call the hash once to get a pair of initial values (h1 and - * h2). Use these values to generate all hashes in a quick loop. - * - * See paper by Kirsch, Mitzenmacher [2006] - * http://www.eecs.harvard.edu/~michaelm/postscripts/rsa2008.pdf - */ -void hash_func(counting_bloom_t *bloom, const char *key, size_t key_len, uint32_t *hashes) -{ - int i; - uint32_t checksum[4]; - - MurmurHash3_x64_128(key, key_len, SALT_CONSTANT, checksum); - uint32_t h1 = checksum[0]; - uint32_t h2 = checksum[1]; - - for (i = 0; i < bloom->nfuncs; i++) { - hashes[i] = (h1 + i * h2) % bloom->counts_per_func; - } -} - -int free_counting_bloom(counting_bloom_t *bloom) -{ - if (bloom != NULL) { - free(bloom->hashes); - bloom->hashes = NULL; - free_bitmap(bloom->bitmap); - free(bloom); - bloom = NULL; - } - return 0; -} - -counting_bloom_t *counting_bloom_init(unsigned int capacity, double error_rate, long offset) -{ - counting_bloom_t *bloom; - - if ((bloom = (counting_bloom_t *)malloc(sizeof(counting_bloom_t))) == NULL) { - fprintf(stderr, "Error, could not realloc a new bloom filter\n"); - return NULL; - } - bloom->bitmap = NULL; - bloom->capacity = capacity; - bloom->error_rate = error_rate; - bloom->offset = offset + sizeof(counting_bloom_header_t); - bloom->nfuncs = (int) ceil(log(1 / error_rate) / log(2)); - bloom->counts_per_func = (int) ceil(capacity * fabs(log(error_rate)) / (bloom->nfuncs * pow(log(2), 2))); - bloom->size = bloom->nfuncs * bloom->counts_per_func; - /* rounding-up integer divide by 2 of bloom->size */ - bloom->num_bytes = ((bloom->size + 1) / 2) + sizeof(counting_bloom_header_t); - bloom->hashes = (uint32_t *)calloc(bloom->nfuncs, sizeof(uint32_t)); - - return bloom; -} - -counting_bloom_t *new_counting_bloom(unsigned int capacity, double error_rate, const char *filename) -{ - counting_bloom_t *cur_bloom; - int fd; - - if ((fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600)) < 0) { - perror("Error, Opening File Failed"); - fprintf(stderr, " %s \n", filename); - return NULL; - } - - cur_bloom = counting_bloom_init(capacity, error_rate, 0); - cur_bloom->bitmap = new_bitmap(fd, cur_bloom->num_bytes); - cur_bloom->header = (counting_bloom_header_t *)(cur_bloom->bitmap->array); - return cur_bloom; -} - -int counting_bloom_add(counting_bloom_t *bloom, const char *s, size_t len) -{ - unsigned int index, i, offset; - unsigned int *hashes = bloom->hashes; - - hash_func(bloom, s, len, hashes); - - bool error = false; - for (i = 0; i < bloom->nfuncs; i++) { - offset = i * bloom->counts_per_func; - index = hashes[i] + offset; - if (bitmap_increment(bloom->bitmap, index, bloom->offset) == -1) { - error = true; - } - } - bloom->header->count++; - - //return 0; - return error ? -1 : 0; -} - -int counting_bloom_remove(counting_bloom_t *bloom, const char *s, size_t len) -{ - unsigned int index, i, offset; - unsigned int *hashes = bloom->hashes; - - hash_func(bloom, s, len, hashes); - - bool error = false; - for (i = 0; i < bloom->nfuncs; i++) { - offset = i * bloom->counts_per_func; - index = hashes[i] + offset; - if (bitmap_decrement(bloom->bitmap, index, bloom->offset) == -1) { - error = true; - } - } - bloom->header->count--; - - //return 0; - return error ? -1 : 0; -} - -int counting_bloom_check(counting_bloom_t *bloom, const char *s, size_t len) -{ - unsigned int index, i, offset; - unsigned int *hashes = bloom->hashes; - - hash_func(bloom, s, len, hashes); - - for (i = 0; i < bloom->nfuncs; i++) { - offset = i * bloom->counts_per_func; - index = hashes[i] + offset; - if (!(bitmap_check(bloom->bitmap, index, bloom->offset))) { - return 0; - } - } - return 1; -} - -int free_scaling_bloom(scaling_bloom_t *bloom) -{ - int i; - for (i = bloom->num_blooms - 1; i >= 0; i--) { - free(bloom->blooms[i]->hashes); - bloom->blooms[i]->hashes = NULL; - free(bloom->blooms[i]); - bloom->blooms[i] = NULL; - } - free(bloom->blooms); - free_bitmap(bloom->bitmap); - free(bloom); - return 0; -} - -/* creates a new counting bloom filter from a given scaling bloom filter, with count and id */ -counting_bloom_t *new_counting_bloom_from_scale(scaling_bloom_t *bloom) -{ - int i; - long offset; - double error_rate; - counting_bloom_t *cur_bloom; - - error_rate = bloom->error_rate * (pow(ERROR_TIGHTENING_RATIO, bloom->num_blooms + 1)); - - if ((bloom->blooms = (counting_bloom_t **)realloc(bloom->blooms, (bloom->num_blooms + 1) * sizeof(counting_bloom_t *))) == NULL) { - fprintf(stderr, "Error, could not realloc a new bloom filter\n"); - return NULL; - } - - cur_bloom = counting_bloom_init(bloom->capacity, error_rate, bloom->num_bytes); - bloom->blooms[bloom->num_blooms] = cur_bloom; - - bloom->bitmap = bitmap_resize(bloom->bitmap, bloom->num_bytes, bloom->num_bytes + cur_bloom->num_bytes); - - /* reset header pointer, as mmap may have moved */ - bloom->header = (scaling_bloom_header_t *) bloom->bitmap->array; - - /* Set the pointers for these header structs to the right location since mmap may have moved */ - bloom->num_blooms++; - for (i = 0; i < bloom->num_blooms; i++) { - offset = bloom->blooms[i]->offset - sizeof(counting_bloom_header_t); - bloom->blooms[i]->header = (counting_bloom_header_t *) (bloom->bitmap->array + offset); - } - - bloom->num_bytes += cur_bloom->num_bytes; - cur_bloom->bitmap = bloom->bitmap; - - return cur_bloom; -} - -counting_bloom_t *new_counting_bloom_from_file(unsigned int capacity, double error_rate, const char *filename) -{ - int fd; - off_t size; - - counting_bloom_t *bloom; - - if ((fd = open(filename, O_RDWR, (mode_t)0600)) < 0) { - fprintf(stderr, "Error, Could not open file %s: %s\n", filename, strerror(errno)); - return NULL; - } - if ((size = lseek(fd, 0, SEEK_END)) < 0) { - perror("Error, calling lseek() to tell file size"); - close(fd); - return NULL; - } - if (size == 0) { - fprintf(stderr, "Error, File size zero\n"); - } - - bloom = counting_bloom_init(capacity, error_rate, 0); - - if (size != bloom->num_bytes) { - free_counting_bloom(bloom); - fprintf(stderr, "Error, Actual filesize and expected filesize are not equal\n"); - return NULL; - } - if ((bloom->bitmap = new_bitmap(fd, size)) == NULL) { - fprintf(stderr, "Error, Could not create bitmap with file\n"); - free_counting_bloom(bloom); - return NULL; - } - - bloom->header = (counting_bloom_header_t *)(bloom->bitmap->array); - - return bloom; -} - -uint64_t scaling_bloom_clear_seqnums(scaling_bloom_t *bloom) -{ - uint64_t seqnum; - - if (bloom->header->disk_seqnum != 0) { - // disk_seqnum cleared on disk before any other changes - bloom->header->disk_seqnum = 0; - bitmap_flush(bloom->bitmap); - } - seqnum = bloom->header->mem_seqnum; - bloom->header->mem_seqnum = 0; - return seqnum; -} - -int scaling_bloom_add(scaling_bloom_t *bloom, const char *s, size_t len, uint64_t id) -{ - int i; - uint64_t seqnum; - - counting_bloom_t *cur_bloom = NULL; - for (i = bloom->num_blooms - 1; i >= 0; i--) { - cur_bloom = bloom->blooms[i]; - if (id >= cur_bloom->header->id) { - break; - } - } - - seqnum = scaling_bloom_clear_seqnums(bloom); - - if ((id > bloom->header->max_id) && (cur_bloom->header->count >= cur_bloom->capacity - 1)) { - cur_bloom = new_counting_bloom_from_scale(bloom); - cur_bloom->header->count = 0; - cur_bloom->header->id = bloom->header->max_id + 1; - } - if (bloom->header->max_id < id) { - bloom->header->max_id = id; - } - bool error = false; - if (counting_bloom_add(cur_bloom, s, len) == -1) { - error = true; - } - - bloom->header->mem_seqnum = seqnum + 1; - - //return 1; - return error ? -1 : 1; -} - -int scaling_bloom_remove(scaling_bloom_t *bloom, const char *s, size_t len, uint64_t id) -{ - counting_bloom_t *cur_bloom; - int i; - uint64_t seqnum; - - bool error = false; - for (i = bloom->num_blooms - 1; i >= 0; i--) { - cur_bloom = bloom->blooms[i]; - if (id >= cur_bloom->header->id) { - seqnum = scaling_bloom_clear_seqnums(bloom); - - if (counting_bloom_remove(cur_bloom, s, len) == -1) { - error = true; - } - - bloom->header->mem_seqnum = seqnum + 1; - //return 1; - return error ? -1 : 1; - } - } - return 0; -} - -int scaling_bloom_check(scaling_bloom_t *bloom, const char *s, size_t len) -{ - int i; - counting_bloom_t *cur_bloom; - for (i = bloom->num_blooms - 1; i >= 0; i--) { - cur_bloom = bloom->blooms[i]; - if (counting_bloom_check(cur_bloom, s, len)) { - return 1; - } - } - return 0; -} - -int scaling_bloom_flush(scaling_bloom_t *bloom) -{ - if (bitmap_flush(bloom->bitmap) != 0) { - return -1; - } - // all changes written to disk before disk_seqnum set - if (bloom->header->disk_seqnum == 0) { - bloom->header->disk_seqnum = bloom->header->mem_seqnum; - return bitmap_flush(bloom->bitmap); - } - return 0; -} - -uint64_t scaling_bloom_mem_seqnum(scaling_bloom_t *bloom) -{ - return bloom->header->mem_seqnum; -} - -uint64_t scaling_bloom_disk_seqnum(scaling_bloom_t *bloom) -{ - return bloom->header->disk_seqnum; -} - -scaling_bloom_t *scaling_bloom_init(unsigned int capacity, double error_rate, const char *filename, int fd) -{ - scaling_bloom_t *bloom; - - if ((bloom = (scaling_bloom_t *)malloc(sizeof(scaling_bloom_t))) == NULL) { - return NULL; - } - if ((bloom->bitmap = new_bitmap(fd, sizeof(scaling_bloom_header_t))) == NULL) { - fprintf(stderr, "Error, Could not create bitmap with file\n"); - free_scaling_bloom(bloom); - return NULL; - } - - bloom->header = (scaling_bloom_header_t *) bloom->bitmap->array; - bloom->capacity = capacity; - bloom->error_rate = error_rate; - bloom->num_blooms = 0; - bloom->num_bytes = sizeof(scaling_bloom_header_t); - bloom->fd = fd; - bloom->blooms = NULL; - - return bloom; -} - -scaling_bloom_t *new_scaling_bloom(unsigned int capacity, double error_rate, const char *filename) -{ - - scaling_bloom_t *bloom; - counting_bloom_t *cur_bloom; - int fd; - - if ((fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0600)) < 0) { - perror("Error, Opening File Failed"); - fprintf(stderr, " %s \n", filename); - return NULL; - } - - bloom = scaling_bloom_init(capacity, error_rate, filename, fd); - - if (!(cur_bloom = new_counting_bloom_from_scale(bloom))) { - fprintf(stderr, "Error, Could not create counting bloom\n"); - free_scaling_bloom(bloom); - return NULL; - } - cur_bloom->header->count = 0; - cur_bloom->header->id = 0; - - bloom->header->mem_seqnum = 1; - return bloom; -} - -scaling_bloom_t *new_scaling_bloom_from_file(unsigned int capacity, double error_rate, const char *filename) -{ - int fd; - off_t size; - - scaling_bloom_t *bloom; - counting_bloom_t *cur_bloom; - - if ((fd = open(filename, O_RDWR, (mode_t)0600)) < 0) { - fprintf(stderr, "Error, Could not open file %s: %s\n", filename, strerror(errno)); - return NULL; - } - if ((size = lseek(fd, 0, SEEK_END)) < 0) { - perror("Error, calling lseek() to tell file size"); - close(fd); - return NULL; - } - if (size == 0) { - fprintf(stderr, "Error, File size zero\n"); - } - - bloom = scaling_bloom_init(capacity, error_rate, filename, fd); - - size -= sizeof(scaling_bloom_header_t); - while (size) { - cur_bloom = new_counting_bloom_from_scale(bloom); - // leave count and id as they were set in the file - size -= cur_bloom->num_bytes; - if (size < 0) { - free_scaling_bloom(bloom); - fprintf(stderr, "Error, Actual filesize and expected filesize are not equal\n"); - return NULL; - } - } - return bloom; -} diff --git a/core/thirdparty/dablooms/dablooms.h b/core/thirdparty/dablooms/dablooms.h deleted file mode 100644 index c0585cf88e..0000000000 --- a/core/thirdparty/dablooms/dablooms.h +++ /dev/null @@ -1,80 +0,0 @@ -/* Copyright @2012 by Justin Hines at Bitly under a very liberal license. See LICENSE in the source distribution. */ - -#ifndef __BLOOM_H__ -#define __BLOOM_H__ -#include -#include - -const char *dablooms_version(void); - -typedef struct { - size_t bytes; - int fd; - char *array; -} bitmap_t; - - -bitmap_t *bitmap_resize(bitmap_t *bitmap, size_t old_size, size_t new_size); -bitmap_t *new_bitmap(int fd, size_t bytes); - -int bitmap_increment(bitmap_t *bitmap, unsigned int index, long offset); -int bitmap_decrement(bitmap_t *bitmap, unsigned int index, long offset); -int bitmap_check(bitmap_t *bitmap, unsigned int index, long offset); -int bitmap_flush(bitmap_t *bitmap); - -void free_bitmap(bitmap_t *bitmap); - -typedef struct { - uint64_t id; - uint32_t count; - uint32_t _pad; -} counting_bloom_header_t; - - -typedef struct { - counting_bloom_header_t *header; - unsigned int capacity; - long offset; - unsigned int counts_per_func; - uint32_t *hashes; - size_t nfuncs; - size_t size; - size_t num_bytes; - double error_rate; - bitmap_t *bitmap; -} counting_bloom_t; - -int free_counting_bloom(counting_bloom_t *bloom); -counting_bloom_t *new_counting_bloom(unsigned int capacity, double error_rate, const char *filename); -counting_bloom_t *new_counting_bloom_from_file(unsigned int capacity, double error_rate, const char *filename); -int counting_bloom_add(counting_bloom_t *bloom, const char *s, size_t len); -int counting_bloom_remove(counting_bloom_t *bloom, const char *s, size_t len); -int counting_bloom_check(counting_bloom_t *bloom, const char *s, size_t len); - -typedef struct { - uint64_t max_id; - uint64_t mem_seqnum; - uint64_t disk_seqnum; -} scaling_bloom_header_t; - -typedef struct { - scaling_bloom_header_t *header; - unsigned int capacity; - unsigned int num_blooms; - size_t num_bytes; - double error_rate; - int fd; - counting_bloom_t **blooms; - bitmap_t *bitmap; -} scaling_bloom_t; - -scaling_bloom_t *new_scaling_bloom(unsigned int capacity, double error_rate, const char *filename); -scaling_bloom_t *new_scaling_bloom_from_file(unsigned int capacity, double error_rate, const char *filename); -int free_scaling_bloom(scaling_bloom_t *bloom); -int scaling_bloom_add(scaling_bloom_t *bloom, const char *s, size_t len, uint64_t id); -int scaling_bloom_remove(scaling_bloom_t *bloom, const char *s, size_t len, uint64_t id); -int scaling_bloom_check(scaling_bloom_t *bloom, const char *s, size_t len); -int scaling_bloom_flush(scaling_bloom_t *bloom); -uint64_t scaling_bloom_mem_seqnum(scaling_bloom_t *bloom); -uint64_t scaling_bloom_disk_seqnum(scaling_bloom_t *bloom); -#endif diff --git a/core/thirdparty/dablooms/murmur.cpp b/core/thirdparty/dablooms/murmur.cpp deleted file mode 100644 index fcf1dc1bcc..0000000000 --- a/core/thirdparty/dablooms/murmur.cpp +++ /dev/null @@ -1,120 +0,0 @@ -//----------------------------------------------------------------------------- -// MurmurHash3 was written by Austin Appleby, and is placed in the public -// domain. The author hereby disclaims copyright to this source code. - -// Note - The x86 and x64 versions do _not_ produce the same results, as the -// algorithms are optimized for their respective platforms. You can still -// compile and run any of them on any platform, but your performance with the -// non-native version will be less than optimal. - -#include "murmur.h" - -#define FORCE_INLINE inline static - -FORCE_INLINE uint64_t rotl64 ( uint64_t x, int8_t r ) -{ - return (x << r) | (x >> (64 - r)); -} - -#define ROTL64(x,y) rotl64(x,y) - -#define BIG_CONSTANT(x) (x##LLU) - -#define getblock(x, i) (x[i]) - -//----------------------------------------------------------------------------- -// Finalization mix - force all bits of a hash block to avalanche - -FORCE_INLINE uint64_t fmix64(uint64_t k) -{ - k ^= k >> 33; - k *= BIG_CONSTANT(0xff51afd7ed558ccd); - k ^= k >> 33; - k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53); - k ^= k >> 33; - - return k; -} - -//----------------------------------------------------------------------------- - -void MurmurHash3_x64_128 ( const void * key, const int len, - const uint32_t seed, void * out ) -{ - const uint8_t * data = (const uint8_t*)key; - const int nblocks = len / 16; - - uint64_t h1 = seed; - uint64_t h2 = seed; - - uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5); - uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f); - - int i; - - //---------- - // body - - const uint64_t * blocks = (const uint64_t *)(data); - - for(i = 0; i < nblocks; i++) { - uint64_t k1 = getblock(blocks,i*2+0); - uint64_t k2 = getblock(blocks,i*2+1); - - k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; - - h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729; - - k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; - - h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5; - } - - //---------- - // tail - - const uint8_t * tail = (const uint8_t*)(data + nblocks*16); - - uint64_t k1 = 0; - uint64_t k2 = 0; - - switch(len & 15) { - case 15: k2 ^= ((uint64_t)tail[14]) << 48; - case 14: k2 ^= ((uint64_t)tail[13]) << 40; - case 13: k2 ^= ((uint64_t)tail[12]) << 32; - case 12: k2 ^= ((uint64_t)tail[11]) << 24; - case 11: k2 ^= ((uint64_t)tail[10]) << 16; - case 10: k2 ^= ((uint64_t)tail[ 9]) << 8; - case 9: k2 ^= ((uint64_t)tail[ 8]) << 0; - k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; - - case 8: k1 ^= ((uint64_t)tail[ 7]) << 56; - case 7: k1 ^= ((uint64_t)tail[ 6]) << 48; - case 6: k1 ^= ((uint64_t)tail[ 5]) << 40; - case 5: k1 ^= ((uint64_t)tail[ 4]) << 32; - case 4: k1 ^= ((uint64_t)tail[ 3]) << 24; - case 3: k1 ^= ((uint64_t)tail[ 2]) << 16; - case 2: k1 ^= ((uint64_t)tail[ 1]) << 8; - case 1: k1 ^= ((uint64_t)tail[ 0]) << 0; - k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; - } - - //---------- - // finalization - - h1 ^= len; h2 ^= len; - - h1 += h2; - h2 += h1; - - h1 = fmix64(h1); - h2 = fmix64(h2); - - h1 += h2; - h2 += h1; - - ((uint64_t*)out)[0] = h1; - ((uint64_t*)out)[1] = h2; -} - -//----------------------------------------------------------------------------- diff --git a/core/thirdparty/dablooms/murmur.h b/core/thirdparty/dablooms/murmur.h deleted file mode 100644 index c7547dbe74..0000000000 --- a/core/thirdparty/dablooms/murmur.h +++ /dev/null @@ -1,12 +0,0 @@ -//----------------------------------------------------------------------------- -// MurmurHash3 was written by Austin Appleby, and is placed in the public -// domain. The author hereby disclaims copyright to this source code. - -#ifndef _MURMURHASH3_H_ -#define _MURMURHASH3_H_ - -#include - -void MurmurHash3_x64_128 ( const void * key, int len, uint32_t seed, void * out ); - -#endif // _MURMURHASH3_H_ diff --git a/core/thirdparty/easyloggingpp/easylogging++.cc b/core/thirdparty/easyloggingpp/easylogging++.cc deleted file mode 100644 index 4c6df12686..0000000000 --- a/core/thirdparty/easyloggingpp/easylogging++.cc +++ /dev/null @@ -1,3299 +0,0 @@ -// -// Bismillah ar-Rahmaan ar-Raheem -// -// Easylogging++ v9.96.7 -// Cross-platform logging library for C++ applications -// -// Copyright (c) 2012-2018 Zuhd Web Services -// Copyright (c) 2012-2018 @abumusamq -// -// This library is released under the MIT Licence. -// https://github.com/zuhd-org/easyloggingpp/blob/master/LICENSE -// -// https://zuhd.org -// http://muflihun.com -// - -#include "easylogging++.h" - -#if defined(AUTO_INITIALIZE_EASYLOGGINGPP) -INITIALIZE_EASYLOGGINGPP -#endif - -namespace el { - -// el::base -namespace base { -// el::base::consts -namespace consts { - -// Level log values - These are values that are replaced in place of %level format specifier -// Extra spaces after format specifiers are only for readability purposes in log files -static const base::type::char_t* kInfoLevelLogValue = ELPP_LITERAL("INFO"); -static const base::type::char_t* kDebugLevelLogValue = ELPP_LITERAL("DEBUG"); -static const base::type::char_t* kWarningLevelLogValue = ELPP_LITERAL("WARNING"); -static const base::type::char_t* kErrorLevelLogValue = ELPP_LITERAL("ERROR"); -static const base::type::char_t* kFatalLevelLogValue = ELPP_LITERAL("FATAL"); -static const base::type::char_t* kVerboseLevelLogValue = - ELPP_LITERAL("VERBOSE"); // will become VERBOSE-x where x = verbose level -static const base::type::char_t* kTraceLevelLogValue = ELPP_LITERAL("TRACE"); -static const base::type::char_t* kInfoLevelShortLogValue = ELPP_LITERAL("I"); -static const base::type::char_t* kDebugLevelShortLogValue = ELPP_LITERAL("D"); -static const base::type::char_t* kWarningLevelShortLogValue = ELPP_LITERAL("W"); -static const base::type::char_t* kErrorLevelShortLogValue = ELPP_LITERAL("E"); -static const base::type::char_t* kFatalLevelShortLogValue = ELPP_LITERAL("F"); -static const base::type::char_t* kVerboseLevelShortLogValue = ELPP_LITERAL("V"); -static const base::type::char_t* kTraceLevelShortLogValue = ELPP_LITERAL("T"); -// Format specifiers - These are used to define log format -static const base::type::char_t* kAppNameFormatSpecifier = ELPP_LITERAL("%app"); -static const base::type::char_t* kLoggerIdFormatSpecifier = ELPP_LITERAL("%logger"); -static const base::type::char_t* kThreadIdFormatSpecifier = ELPP_LITERAL("%thread"); -static const base::type::char_t* kSeverityLevelFormatSpecifier = ELPP_LITERAL("%level"); -static const base::type::char_t* kSeverityLevelShortFormatSpecifier = ELPP_LITERAL("%levshort"); -static const base::type::char_t* kDateTimeFormatSpecifier = ELPP_LITERAL("%datetime"); -static const base::type::char_t* kLogFileFormatSpecifier = ELPP_LITERAL("%file"); -static const base::type::char_t* kLogFileBaseFormatSpecifier = ELPP_LITERAL("%fbase"); -static const base::type::char_t* kLogLineFormatSpecifier = ELPP_LITERAL("%line"); -static const base::type::char_t* kLogLocationFormatSpecifier = ELPP_LITERAL("%loc"); -static const base::type::char_t* kLogFunctionFormatSpecifier = ELPP_LITERAL("%func"); -static const base::type::char_t* kCurrentUserFormatSpecifier = ELPP_LITERAL("%user"); -static const base::type::char_t* kCurrentHostFormatSpecifier = ELPP_LITERAL("%host"); -static const base::type::char_t* kMessageFormatSpecifier = ELPP_LITERAL("%msg"); -static const base::type::char_t* kVerboseLevelFormatSpecifier = ELPP_LITERAL("%vlevel"); -static const char* kDateTimeFormatSpecifierForFilename = "%datetime"; -// Date/time -static const char* kDays[7] = {"Sundayaaa", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; -static const char* kDaysAbbrev[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; -static const char* kMonths[12] = {"January", "February", "March", "Apri", "May", "June", - "July", "August", "September", "October", "November", "December"}; -static const char* kMonthsAbbrev[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; -static const char* kDefaultDateTimeFormat = "%Y-%M-%d %H:%m:%s,%g"; -static const char* kDefaultDateTimeFormatInFilename = "%Y-%M-%d_%H-%m"; -static const int kYearBase = 1900; -static const char* kAm = "AM"; -static const char* kPm = "PM"; -// Miscellaneous constants - -static const char* kNullPointer = "nullptr"; -#if ELPP_VARIADIC_TEMPLATES_SUPPORTED -#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED -static const base::type::VerboseLevel kMaxVerboseLevel = 9; -static const char* kUnknownUser = "user"; -static const char* kUnknownHost = "unknown-host"; - -//---------------- DEFAULT LOG FILE ----------------------- - -#if defined(ELPP_NO_DEFAULT_LOG_FILE) -#if ELPP_OS_UNIX -static const char* kDefaultLogFile = "/dev/null"; -#elif ELPP_OS_WINDOWS -static const char* kDefaultLogFile = "nul"; -#endif // ELPP_OS_UNIX -#elif defined(ELPP_DEFAULT_LOG_FILE) -static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE; -#else -static const char* kDefaultLogFile = "myeasylog.log"; -#endif // defined(ELPP_NO_DEFAULT_LOG_FILE) - -#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) -static const char* kDefaultLogFileParam = "--default-log-file"; -#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) -#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) -static const char* kLoggingFlagsParam = "--logging-flags"; -#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) -static const char* kValidLoggerIdSymbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._"; -static const char* kConfigurationComment = "##"; -static const char* kConfigurationLevel = "*"; -static const char* kConfigurationLoggerId = "--"; -} // namespace consts -// el::base::utils -namespace utils { - -/// @brief Aborts application due with user-defined status -static void -abort(int status, const std::string& reason) { - // Both status and reason params are there for debugging with tools like gdb etc - ELPP_UNUSED(status); - ELPP_UNUSED(reason); -#if defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) - // Ignore msvc critical error dialog - break instead (on debug mode) - _asm int 3 -#else - ::abort(); -#endif // defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) -} - -} // namespace utils -} // namespace base - -// el - -// LevelHelper - -const char* -LevelHelper::convertToString(Level level) { - // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. - if (level == Level::Global) - return "GLOBAL"; - if (level == Level::Debug) - return "DEBUG"; - if (level == Level::Info) - return "INFO"; - if (level == Level::Warning) - return "WARNING"; - if (level == Level::Error) - return "ERROR"; - if (level == Level::Fatal) - return "FATAL"; - if (level == Level::Verbose) - return "VERBOSE"; - if (level == Level::Trace) - return "TRACE"; - return "UNKNOWN"; -} - -struct StringToLevelItem { - const char* levelString; - Level level; -}; - -static struct StringToLevelItem stringToLevelMap[] = { - {"global", Level::Global}, {"debug", Level::Debug}, {"info", Level::Info}, {"warning", Level::Warning}, - {"error", Level::Error}, {"fatal", Level::Fatal}, {"verbose", Level::Verbose}, {"trace", Level::Trace}}; - -Level -LevelHelper::convertFromString(const char* levelStr) { - for (auto& item : stringToLevelMap) { - if (base::utils::Str::cStringCaseEq(levelStr, item.levelString)) { - return item.level; - } - } - return Level::Unknown; -} - -void -LevelHelper::forEachLevel(base::type::EnumType* startIndex, const std::function& fn) { - base::type::EnumType lIndexMax = LevelHelper::kMaxValid; - do { - if (fn()) { - break; - } - *startIndex = static_cast(*startIndex << 1); - } while (*startIndex <= lIndexMax); -} - -// ConfigurationTypeHelper - -const char* -ConfigurationTypeHelper::convertToString(ConfigurationType configurationType) { - // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. - if (configurationType == ConfigurationType::Enabled) - return "ENABLED"; - if (configurationType == ConfigurationType::Filename) - return "FILENAME"; - if (configurationType == ConfigurationType::Format) - return "FORMAT"; - if (configurationType == ConfigurationType::ToFile) - return "TO_FILE"; - if (configurationType == ConfigurationType::ToStandardOutput) - return "TO_STANDARD_OUTPUT"; - if (configurationType == ConfigurationType::SubsecondPrecision) - return "SUBSECOND_PRECISION"; - if (configurationType == ConfigurationType::PerformanceTracking) - return "PERFORMANCE_TRACKING"; - if (configurationType == ConfigurationType::MaxLogFileSize) - return "MAX_LOG_FILE_SIZE"; - if (configurationType == ConfigurationType::LogFlushThreshold) - return "LOG_FLUSH_THRESHOLD"; - return "UNKNOWN"; -} - -struct ConfigurationStringToTypeItem { - const char* configString; - ConfigurationType configType; -}; - -static struct ConfigurationStringToTypeItem configStringToTypeMap[] = { - {"enabled", ConfigurationType::Enabled}, - {"to_file", ConfigurationType::ToFile}, - {"to_standard_output", ConfigurationType::ToStandardOutput}, - {"format", ConfigurationType::Format}, - {"filename", ConfigurationType::Filename}, - {"subsecond_precision", ConfigurationType::SubsecondPrecision}, - {"milliseconds_width", ConfigurationType::MillisecondsWidth}, - {"performance_tracking", ConfigurationType::PerformanceTracking}, - {"max_log_file_size", ConfigurationType::MaxLogFileSize}, - {"log_flush_threshold", ConfigurationType::LogFlushThreshold}, -}; - -ConfigurationType -ConfigurationTypeHelper::convertFromString(const char* configStr) { - for (auto& item : configStringToTypeMap) { - if (base::utils::Str::cStringCaseEq(configStr, item.configString)) { - return item.configType; - } - } - return ConfigurationType::Unknown; -} - -void -ConfigurationTypeHelper::forEachConfigType(base::type::EnumType* startIndex, const std::function& fn) { - base::type::EnumType cIndexMax = ConfigurationTypeHelper::kMaxValid; - do { - if (fn()) { - break; - } - *startIndex = static_cast(*startIndex << 1); - } while (*startIndex <= cIndexMax); -} - -// Configuration - -Configuration::Configuration(const Configuration& c) - : m_level(c.m_level), m_configurationType(c.m_configurationType), m_value(c.m_value) { -} - -Configuration& -Configuration::operator=(const Configuration& c) { - if (&c != this) { - m_level = c.m_level; - m_configurationType = c.m_configurationType; - m_value = c.m_value; - } - return *this; -} - -/// @brief Full constructor used to sets value of configuration -Configuration::Configuration(Level level, ConfigurationType configurationType, const std::string& value) - : m_level(level), m_configurationType(configurationType), m_value(value) { -} - -void -Configuration::log(el::base::type::ostream_t& os) const { - os << LevelHelper::convertToString(m_level) << ELPP_LITERAL(" ") - << ConfigurationTypeHelper::convertToString(m_configurationType) << ELPP_LITERAL(" = ") << m_value.c_str(); -} - -/// @brief Used to find configuration from configuration (pointers) repository. Avoid using it. -Configuration::Predicate::Predicate(Level level, ConfigurationType configurationType) - : m_level(level), m_configurationType(configurationType) { -} - -bool -Configuration::Predicate::operator()(const Configuration* conf) const { - return ((conf != nullptr) && (conf->level() == m_level) && (conf->configurationType() == m_configurationType)); -} - -// Configurations - -Configurations::Configurations(void) : m_configurationFile(std::string()), m_isFromFile(false) { -} - -Configurations::Configurations(const std::string& configurationFile, bool useDefaultsForRemaining, Configurations* base) - : m_configurationFile(configurationFile), m_isFromFile(false) { - parseFromFile(configurationFile, base); - if (useDefaultsForRemaining) { - setRemainingToDefault(); - } -} - -bool -Configurations::parseFromFile(const std::string& configurationFile, Configurations* base) { - // We initial assertion with true because if we have assertion diabled, we want to pass this - // check and if assertion is enabled we will have values re-assigned any way. - bool assertionPassed = true; - ELPP_ASSERT((assertionPassed = base::utils::File::pathExists(configurationFile.c_str(), true)) == true, - "Configuration file [" << configurationFile << "] does not exist!"); - if (!assertionPassed) { - return false; - } - bool success = Parser::parseFromFile(configurationFile, this, base); - m_isFromFile = success; - return success; -} - -bool -Configurations::parseFromText(const std::string& configurationsString, Configurations* base) { - bool success = Parser::parseFromText(configurationsString, this, base); - if (success) { - m_isFromFile = false; - } - return success; -} - -void -Configurations::setFromBase(Configurations* base) { - if (base == nullptr || base == this) { - return; - } - base::threading::ScopedLock scopedLock(base->lock()); - for (Configuration*& conf : base->list()) { - set(conf); - } -} - -bool -Configurations::hasConfiguration(ConfigurationType configurationType) { - base::type::EnumType lIndex = LevelHelper::kMinValid; - bool result = false; - LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { - if (hasConfiguration(LevelHelper::castFromInt(lIndex), configurationType)) { - result = true; - } - return result; - }); - return result; -} - -bool -Configurations::hasConfiguration(Level level, ConfigurationType configurationType) { - base::threading::ScopedLock scopedLock(lock()); -#if ELPP_COMPILER_INTEL - // We cant specify template types here, Intel C++ throws compilation error - // "error: type name is not allowed" - return RegistryWithPred::get(level, configurationType) != nullptr; -#else - return RegistryWithPred::get(level, configurationType) != nullptr; -#endif // ELPP_COMPILER_INTEL -} - -void -Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) { - base::threading::ScopedLock scopedLock(lock()); - unsafeSet(level, configurationType, value); // This is not unsafe anymore as we have locked mutex - if (level == Level::Global) { - unsafeSetGlobally(configurationType, value, false); // Again this is not unsafe either - } -} - -void -Configurations::set(Configuration* conf) { - if (conf == nullptr) { - return; - } - set(conf->level(), conf->configurationType(), conf->value()); -} - -void -Configurations::setToDefault(void) { - setGlobally(ConfigurationType::Enabled, std::string("true"), true); - setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true); -#if defined(ELPP_NO_LOG_TO_FILE) - setGlobally(ConfigurationType::ToFile, std::string("false"), true); -#else - setGlobally(ConfigurationType::ToFile, std::string("true"), true); -#endif // defined(ELPP_NO_LOG_TO_FILE) - setGlobally(ConfigurationType::ToStandardOutput, std::string("true"), true); - setGlobally(ConfigurationType::SubsecondPrecision, std::string("3"), true); - setGlobally(ConfigurationType::PerformanceTracking, std::string("true"), true); - setGlobally(ConfigurationType::MaxLogFileSize, std::string("0"), true); - setGlobally(ConfigurationType::LogFlushThreshold, std::string("0"), true); - - setGlobally(ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"), true); - set(Level::Debug, ConfigurationType::Format, - std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); - // INFO and WARNING are set to default by Level::Global - set(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); - set(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); - set(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg")); - set(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg")); -} - -void -Configurations::setRemainingToDefault(void) { - base::threading::ScopedLock scopedLock(lock()); -#if defined(ELPP_NO_LOG_TO_FILE) - unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("false")); -#else - unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true")); -#endif // defined(ELPP_NO_LOG_TO_FILE) - unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile)); - unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true")); - unsafeSetIfNotExist(Level::Global, ConfigurationType::SubsecondPrecision, std::string("3")); - unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true")); - unsafeSetIfNotExist(Level::Global, ConfigurationType::MaxLogFileSize, std::string("0")); - unsafeSetIfNotExist(Level::Global, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); - unsafeSetIfNotExist(Level::Debug, ConfigurationType::Format, - std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); - // INFO and WARNING are set to default by Level::Global - unsafeSetIfNotExist(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); - unsafeSetIfNotExist(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); - unsafeSetIfNotExist(Level::Verbose, ConfigurationType::Format, - std::string("%datetime %level-%vlevel [%logger] %msg")); - unsafeSetIfNotExist(Level::Trace, ConfigurationType::Format, - std::string("%datetime %level [%logger] [%func] [%loc] %msg")); -} - -bool -Configurations::Parser::parseFromFile(const std::string& configurationFile, Configurations* sender, - Configurations* base) { - sender->setFromBase(base); - std::ifstream fileStream_(configurationFile.c_str(), std::ifstream::in); - ELPP_ASSERT(fileStream_.is_open(), "Unable to open configuration file [" << configurationFile << "] for parsing."); - bool parsedSuccessfully = false; - std::string line = std::string(); - Level currLevel = Level::Unknown; - std::string currConfigStr = std::string(); - std::string currLevelStr = std::string(); - while (fileStream_.good()) { - std::getline(fileStream_, line); - parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); - ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); - } - return parsedSuccessfully; -} - -bool -Configurations::Parser::parseFromText(const std::string& configurationsString, Configurations* sender, - Configurations* base) { - sender->setFromBase(base); - bool parsedSuccessfully = false; - std::stringstream ss(configurationsString); - std::string line = std::string(); - Level currLevel = Level::Unknown; - std::string currConfigStr = std::string(); - std::string currLevelStr = std::string(); - while (std::getline(ss, line)) { - parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); - ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); - } - return parsedSuccessfully; -} - -void -Configurations::Parser::ignoreComments(std::string* line) { - std::size_t foundAt = 0; - std::size_t quotesStart = line->find("\""); - std::size_t quotesEnd = std::string::npos; - if (quotesStart != std::string::npos) { - quotesEnd = line->find("\"", quotesStart + 1); - while (quotesEnd != std::string::npos && line->at(quotesEnd - 1) == '\\') { - // Do not erase slash yet - we will erase it in parseLine(..) while loop - quotesEnd = line->find("\"", quotesEnd + 2); - } - } - if ((foundAt = line->find(base::consts::kConfigurationComment)) != std::string::npos) { - if (foundAt < quotesEnd) { - foundAt = line->find(base::consts::kConfigurationComment, quotesEnd + 1); - } - *line = line->substr(0, foundAt); - } -} - -bool -Configurations::Parser::isLevel(const std::string& line) { - return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLevel)); -} - -bool -Configurations::Parser::isComment(const std::string& line) { - return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationComment)); -} - -bool -Configurations::Parser::isConfig(const std::string& line) { - std::size_t assignment = line.find('='); - return line != "" && ((line[0] >= 'A' && line[0] <= 'Z') || (line[0] >= 'a' && line[0] <= 'z')) && - (assignment != std::string::npos) && (line.size() > assignment); -} - -bool -Configurations::Parser::parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, - Level* currLevel, Configurations* conf) { - ConfigurationType currConfig = ConfigurationType::Unknown; - std::string currValue = std::string(); - *line = base::utils::Str::trim(*line); - if (isComment(*line)) - return true; - ignoreComments(line); - *line = base::utils::Str::trim(*line); - if (line->empty()) { - // Comment ignored - return true; - } - if (isLevel(*line)) { - if (line->size() <= 2) { - return true; - } - *currLevelStr = line->substr(1, line->size() - 2); - *currLevelStr = base::utils::Str::toUpper(*currLevelStr); - *currLevelStr = base::utils::Str::trim(*currLevelStr); - *currLevel = LevelHelper::convertFromString(currLevelStr->c_str()); - return true; - } - if (isConfig(*line)) { - std::size_t assignment = line->find('='); - *currConfigStr = line->substr(0, assignment); - *currConfigStr = base::utils::Str::toUpper(*currConfigStr); - *currConfigStr = base::utils::Str::trim(*currConfigStr); - currConfig = ConfigurationTypeHelper::convertFromString(currConfigStr->c_str()); - currValue = line->substr(assignment + 1); - currValue = base::utils::Str::trim(currValue); - std::size_t quotesStart = currValue.find("\"", 0); - std::size_t quotesEnd = std::string::npos; - if (quotesStart != std::string::npos) { - quotesEnd = currValue.find("\"", quotesStart + 1); - while (quotesEnd != std::string::npos && currValue.at(quotesEnd - 1) == '\\') { - currValue = currValue.erase(quotesEnd - 1, 1); - quotesEnd = currValue.find("\"", quotesEnd + 2); - } - } - if (quotesStart != std::string::npos && quotesEnd != std::string::npos) { - // Quote provided - check and strip if valid - ELPP_ASSERT((quotesStart < quotesEnd), - "Configuration error - No ending quote found in [" << currConfigStr << "]"); - ELPP_ASSERT((quotesStart + 1 != quotesEnd), "Empty configuration value for [" << currConfigStr << "]"); - if ((quotesStart != quotesEnd) && (quotesStart + 1 != quotesEnd)) { - // Explicit check in case if assertion is disabled - currValue = currValue.substr(quotesStart + 1, quotesEnd - 1); - } - } - } - ELPP_ASSERT(*currLevel != Level::Unknown, "Unrecognized severity level [" << *currLevelStr << "]"); - ELPP_ASSERT(currConfig != ConfigurationType::Unknown, "Unrecognized configuration [" << *currConfigStr << "]"); - if (*currLevel == Level::Unknown || currConfig == ConfigurationType::Unknown) { - return false; // unrecognizable level or config - } - conf->set(*currLevel, currConfig, currValue); - return true; -} - -void -Configurations::unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value) { - Configuration* conf = RegistryWithPred::get(level, configurationType); - if (conf == nullptr) { - unsafeSet(level, configurationType, value); - } -} - -void -Configurations::unsafeSet(Level level, ConfigurationType configurationType, const std::string& value) { - Configuration* conf = RegistryWithPred::get(level, configurationType); - if (conf == nullptr) { - registerNew(new Configuration(level, configurationType, value)); - } else { - conf->setValue(value); - } - if (level == Level::Global) { - unsafeSetGlobally(configurationType, value, false); - } -} - -void -Configurations::setGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel) { - if (includeGlobalLevel) { - set(Level::Global, configurationType, value); - } - base::type::EnumType lIndex = LevelHelper::kMinValid; - LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { - set(LevelHelper::castFromInt(lIndex), configurationType, value); - return false; // Do not break lambda function yet as we need to set all levels regardless - }); -} - -void -Configurations::unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, - bool includeGlobalLevel) { - if (includeGlobalLevel) { - unsafeSet(Level::Global, configurationType, value); - } - base::type::EnumType lIndex = LevelHelper::kMinValid; - LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { - unsafeSet(LevelHelper::castFromInt(lIndex), configurationType, value); - return false; // Do not break lambda function yet as we need to set all levels regardless - }); -} - -// LogBuilder - -void -LogBuilder::convertToColoredOutput(base::type::string_t* logLine, Level level) { - if (!m_termSupportsColor) - return; - const base::type::char_t* resetColor = ELPP_LITERAL("\x1b[0m"); - if (level == Level::Error || level == Level::Fatal) - *logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor; - else if (level == Level::Warning) - *logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor; - else if (level == Level::Debug) - *logLine = ELPP_LITERAL("\x1b[32m") + *logLine + resetColor; - else if (level == Level::Info) - *logLine = ELPP_LITERAL("\x1b[36m") + *logLine + resetColor; - else if (level == Level::Trace) - *logLine = ELPP_LITERAL("\x1b[35m") + *logLine + resetColor; -} - -// Logger - -Logger::Logger(const std::string& id, base::LogStreamsReferenceMap* logStreamsReference) - : m_id(id), - m_typedConfigurations(nullptr), - m_parentApplicationName(std::string()), - m_isConfigured(false), - m_logStreamsReference(logStreamsReference) { - initUnflushedCount(); -} - -Logger::Logger(const std::string& id, const Configurations& configurations, - base::LogStreamsReferenceMap* logStreamsReference) - : m_id(id), - m_typedConfigurations(nullptr), - m_parentApplicationName(std::string()), - m_isConfigured(false), - m_logStreamsReference(logStreamsReference) { - initUnflushedCount(); - configure(configurations); -} - -Logger::Logger(const Logger& logger) { - base::utils::safeDelete(m_typedConfigurations); - m_id = logger.m_id; - m_typedConfigurations = logger.m_typedConfigurations; - m_parentApplicationName = logger.m_parentApplicationName; - m_isConfigured = logger.m_isConfigured; - m_configurations = logger.m_configurations; - m_unflushedCount = logger.m_unflushedCount; - m_logStreamsReference = logger.m_logStreamsReference; -} - -Logger& -Logger::operator=(const Logger& logger) { - if (&logger != this) { - base::utils::safeDelete(m_typedConfigurations); - m_id = logger.m_id; - m_typedConfigurations = logger.m_typedConfigurations; - m_parentApplicationName = logger.m_parentApplicationName; - m_isConfigured = logger.m_isConfigured; - m_configurations = logger.m_configurations; - m_unflushedCount = logger.m_unflushedCount; - m_logStreamsReference = logger.m_logStreamsReference; - } - return *this; -} - -void -Logger::configure(const Configurations& configurations) { - m_isConfigured = false; // we set it to false in case if we fail - initUnflushedCount(); - if (m_typedConfigurations != nullptr) { - Configurations* c = const_cast(m_typedConfigurations->configurations()); - if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) { - flush(); - } - } - base::threading::ScopedLock scopedLock(lock()); - if (m_configurations != configurations) { - m_configurations.setFromBase(const_cast(&configurations)); - } - base::utils::safeDelete(m_typedConfigurations); - m_typedConfigurations = new base::TypedConfigurations(&m_configurations, m_logStreamsReference); - resolveLoggerFormatSpec(); - m_isConfigured = true; -} - -void -Logger::reconfigure(void) { - ELPP_INTERNAL_INFO(1, "Reconfiguring logger [" << m_id << "]"); - configure(m_configurations); -} - -bool -Logger::isValidId(const std::string& id) { - for (std::string::const_iterator it = id.begin(); it != id.end(); ++it) { - if (!base::utils::Str::contains(base::consts::kValidLoggerIdSymbols, *it)) { - return false; - } - } - return true; -} - -void -Logger::flush(void) { - ELPP_INTERNAL_INFO(3, "Flushing logger [" << m_id << "] all levels"); - base::threading::ScopedLock scopedLock(lock()); - base::type::EnumType lIndex = LevelHelper::kMinValid; - LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { - flush(LevelHelper::castFromInt(lIndex), nullptr); - return false; - }); -} - -void -Logger::flush(Level level, base::type::fstream_t* fs) { - if (fs == nullptr && m_typedConfigurations->toFile(level)) { - fs = m_typedConfigurations->fileStream(level); - } - if (fs != nullptr) { - fs->flush(); - std::unordered_map::iterator iter = m_unflushedCount.find(level); - if (iter != m_unflushedCount.end()) { - iter->second = 0; - } - Helpers::validateFileRolling(this, level); - } -} - -void -Logger::initUnflushedCount(void) { - m_unflushedCount.clear(); - base::type::EnumType lIndex = LevelHelper::kMinValid; - LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { - m_unflushedCount.insert(std::make_pair(LevelHelper::castFromInt(lIndex), 0)); - return false; - }); -} - -void -Logger::resolveLoggerFormatSpec(void) const { - base::type::EnumType lIndex = LevelHelper::kMinValid; - LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { - base::LogFormat* logFormat = - const_cast(&m_typedConfigurations->logFormat(LevelHelper::castFromInt(lIndex))); - base::utils::Str::replaceFirstWithEscape(logFormat->m_format, base::consts::kLoggerIdFormatSpecifier, m_id); - return false; - }); -} - -// el::base -namespace base { - -// el::base::utils -namespace utils { - -// File - -base::type::fstream_t* -File::newFileStream(const std::string& filename) { - base::type::fstream_t* fs = new base::type::fstream_t(filename.c_str(), base::type::fstream_t::out -#if !defined(ELPP_FRESH_LOG_FILE) - | base::type::fstream_t::app -#endif - ); -#if defined(ELPP_UNICODE) - std::locale elppUnicodeLocale(""); -#if ELPP_OS_WINDOWS - std::locale elppUnicodeLocaleWindows(elppUnicodeLocale, new std::codecvt_utf8_utf16); - elppUnicodeLocale = elppUnicodeLocaleWindows; -#endif // ELPP_OS_WINDOWS - fs->imbue(elppUnicodeLocale); -#endif // defined(ELPP_UNICODE) - if (fs->is_open()) { - fs->flush(); - } else { - base::utils::safeDelete(fs); - ELPP_INTERNAL_ERROR("Bad file [" << filename << "]", true); - } - return fs; -} - -std::size_t -File::getSizeOfFile(base::type::fstream_t* fs) { - if (fs == nullptr) { - return 0; - } - // Since the file stream is appended to or truncated, the current - // offset is the file size. - std::size_t size = static_cast(fs->tellg()); - return size; -} - -bool -File::pathExists(const char* path, bool considerFile) { - if (path == nullptr) { - return false; - } -#if ELPP_OS_UNIX - ELPP_UNUSED(considerFile); - struct stat st; - return (stat(path, &st) == 0); -#elif ELPP_OS_WINDOWS - DWORD fileType = GetFileAttributesA(path); - if (fileType == INVALID_FILE_ATTRIBUTES) { - return false; - } - return considerFile ? true : ((fileType & FILE_ATTRIBUTE_DIRECTORY) == 0 ? false : true); -#endif // ELPP_OS_UNIX -} - -bool -File::createPath(const std::string& path) { - if (path.empty()) { - return false; - } - if (base::utils::File::pathExists(path.c_str())) { - return true; - } - int status = -1; - - char* currPath = const_cast(path.c_str()); - std::string builtPath = std::string(); -#if ELPP_OS_UNIX - if (path[0] == '/') { - builtPath = "/"; - } - currPath = STRTOK(currPath, base::consts::kFilePathSeperator, 0); -#elif ELPP_OS_WINDOWS - // Use secure functions API - char* nextTok_ = nullptr; - currPath = STRTOK(currPath, base::consts::kFilePathSeperator, &nextTok_); - ELPP_UNUSED(nextTok_); -#endif // ELPP_OS_UNIX - while (currPath != nullptr) { - builtPath.append(currPath); - builtPath.append(base::consts::kFilePathSeperator); -#if ELPP_OS_UNIX - status = mkdir(builtPath.c_str(), ELPP_LOG_PERMS); - currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, 0); -#elif ELPP_OS_WINDOWS - status = _mkdir(builtPath.c_str()); - currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, &nextTok_); -#endif // ELPP_OS_UNIX - } - if (status == -1) { - ELPP_INTERNAL_ERROR("Error while creating path [" << path << "]", true); - return false; - } - return true; -} - -std::string -File::extractPathFromFilename(const std::string& fullPath, const char* separator) { - if ((fullPath == "") || (fullPath.find(separator) == std::string::npos)) { - return fullPath; - } - std::size_t lastSlashAt = fullPath.find_last_of(separator); - if (lastSlashAt == 0) { - return std::string(separator); - } - return fullPath.substr(0, lastSlashAt + 1); -} - -void -File::buildStrippedFilename(const char* filename, char buff[], std::size_t limit) { - std::size_t sizeOfFilename = strlen(filename); - if (sizeOfFilename >= limit) { - filename += (sizeOfFilename - limit); - if (filename[0] != '.' && filename[1] != '.') { // prepend if not already - filename += 3; // 3 = '..' - STRCAT(buff, "..", limit); - } - } - STRCAT(buff, filename, limit); -} - -void -File::buildBaseFilename(const std::string& fullPath, char buff[], std::size_t limit, const char* separator) { - const char* filename = fullPath.c_str(); - std::size_t lastSlashAt = fullPath.find_last_of(separator); - filename += lastSlashAt ? lastSlashAt + 1 : 0; - std::size_t sizeOfFilename = strlen(filename); - if (sizeOfFilename >= limit) { - filename += (sizeOfFilename - limit); - if (filename[0] != '.' && filename[1] != '.') { // prepend if not already - filename += 3; // 3 = '..' - STRCAT(buff, "..", limit); - } - } - STRCAT(buff, filename, limit); -} - -// Str - -bool -Str::wildCardMatch(const char* str, const char* pattern) { - while (*pattern) { - switch (*pattern) { - case '?': - if (!*str) - return false; - ++str; - ++pattern; - break; - case '*': - if (wildCardMatch(str, pattern + 1)) - return true; - if (*str && wildCardMatch(str + 1, pattern)) - return true; - return false; - default: - if (*str++ != *pattern++) - return false; - break; - } - } - return !*str && !*pattern; -} - -std::string& -Str::ltrim(std::string& str) { - str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](char c) { return !std::isspace(c); })); - return str; -} - -std::string& -Str::rtrim(std::string& str) { - str.erase(std::find_if(str.rbegin(), str.rend(), [](char c) { return !std::isspace(c); }).base(), str.end()); - return str; -} - -std::string& -Str::trim(std::string& str) { - return ltrim(rtrim(str)); -} - -bool -Str::startsWith(const std::string& str, const std::string& start) { - return (str.length() >= start.length()) && (str.compare(0, start.length(), start) == 0); -} - -bool -Str::endsWith(const std::string& str, const std::string& end) { - return (str.length() >= end.length()) && (str.compare(str.length() - end.length(), end.length(), end) == 0); -} - -std::string& -Str::replaceAll(std::string& str, char replaceWhat, char replaceWith) { - std::replace(str.begin(), str.end(), replaceWhat, replaceWith); - return str; -} - -std::string& -Str::replaceAll(std::string& str, const std::string& replaceWhat, const std::string& replaceWith) { - if (replaceWhat == replaceWith) - return str; - std::size_t foundAt = std::string::npos; - while ((foundAt = str.find(replaceWhat, foundAt + 1)) != std::string::npos) { - str.replace(foundAt, replaceWhat.length(), replaceWith); - } - return str; -} - -void -Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, - const base::type::string_t& replaceWith) { - std::size_t foundAt = base::type::string_t::npos; - while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) { - if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) { - str.erase(foundAt - 1, 1); - ++foundAt; - } else { - str.replace(foundAt, replaceWhat.length(), replaceWith); - return; - } - } -} -#if defined(ELPP_UNICODE) -void -Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, - const std::string& replaceWith) { - replaceFirstWithEscape(str, replaceWhat, base::type::string_t(replaceWith.begin(), replaceWith.end())); -} -#endif // defined(ELPP_UNICODE) - -std::string& -Str::toUpper(std::string& str) { - std::transform(str.begin(), str.end(), str.begin(), [](char c) { return static_cast(::toupper(c)); }); - return str; -} - -bool -Str::cStringEq(const char* s1, const char* s2) { - if (s1 == nullptr && s2 == nullptr) - return true; - if (s1 == nullptr || s2 == nullptr) - return false; - return strcmp(s1, s2) == 0; -} - -bool -Str::cStringCaseEq(const char* s1, const char* s2) { - if (s1 == nullptr && s2 == nullptr) - return true; - if (s1 == nullptr || s2 == nullptr) - return false; - - // With thanks to cygwin for this code - int d = 0; - - while (true) { - const int c1 = toupper(*s1++); - const int c2 = toupper(*s2++); - - if (((d = c1 - c2) != 0) || (c2 == '\0')) { - break; - } - } - - return d == 0; -} - -bool -Str::contains(const char* str, char c) { - for (; *str; ++str) { - if (*str == c) - return true; - } - return false; -} - -char* -Str::convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded) { - char localBuff[10] = ""; - char* p = localBuff + sizeof(localBuff) - 2; - if (n > 0) { - for (; n > 0 && p > localBuff && len > 0; n /= 10, --len) *--p = static_cast(n % 10 + '0'); - } else { - *--p = '0'; - --len; - } - if (zeroPadded) - while (p > localBuff && len-- > 0) *--p = static_cast('0'); - return addToBuff(p, buf, bufLim); -} - -char* -Str::addToBuff(const char* str, char* buf, const char* bufLim) { - while ((buf < bufLim) && ((*buf = *str++) != '\0')) ++buf; - return buf; -} - -char* -Str::clearBuff(char buff[], std::size_t lim) { - STRCPY(buff, "", lim); - ELPP_UNUSED(lim); // For *nix we dont have anything using lim in above STRCPY macro - return buff; -} - -/// @brief Converst wchar* to char* -/// NOTE: Need to free return value after use! -char* -Str::wcharPtrToCharPtr(const wchar_t* line) { - std::size_t len_ = wcslen(line) + 1; - char* buff_ = static_cast(malloc(len_ + 1)); -#if ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) - std::wcstombs(buff_, line, len_); -#elif ELPP_OS_WINDOWS - std::size_t convCount_ = 0; - mbstate_t mbState_; - ::memset(static_cast(&mbState_), 0, sizeof(mbState_)); - wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_); -#endif // ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) - return buff_; -} - -// OS - -#if ELPP_OS_WINDOWS -/// @brief Gets environment variables for Windows based OS. -/// We are not using getenv(const char*) because of CRT deprecation -/// @param varname Variable name to get environment variable value for -/// @return If variable exist the value of it otherwise nullptr -const char* -OS::getWindowsEnvironmentVariable(const char* varname) { - const DWORD bufferLen = 50; - static char buffer[bufferLen]; - if (GetEnvironmentVariableA(varname, buffer, bufferLen)) { - return buffer; - } - return nullptr; -} -#endif // ELPP_OS_WINDOWS -#if ELPP_OS_ANDROID -std::string -OS::getProperty(const char* prop) { - char propVal[PROP_VALUE_MAX + 1]; - int ret = __system_property_get(prop, propVal); - return ret == 0 ? std::string() : std::string(propVal); -} - -std::string -OS::getDeviceName(void) { - std::stringstream ss; - std::string manufacturer = getProperty("ro.product.manufacturer"); - std::string model = getProperty("ro.product.model"); - if (manufacturer.empty() || model.empty()) { - return std::string(); - } - ss << manufacturer << "-" << model; - return ss.str(); -} -#endif // ELPP_OS_ANDROID - -const std::string -OS::getBashOutput(const char* command) { -#if (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) - if (command == nullptr) { - return std::string(); - } - FILE* proc = nullptr; - if ((proc = popen(command, "r")) == nullptr) { - ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true); - return std::string(); - } - char hBuff[4096]; - if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) { - pclose(proc); - const std::size_t buffLen = strlen(hBuff); - if (buffLen > 0 && hBuff[buffLen - 1] == '\n') { - hBuff[buffLen - 1] = '\0'; - } - return std::string(hBuff); - } else { - pclose(proc); - } - return std::string(); -#else - ELPP_UNUSED(command); - return std::string(); -#endif // (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) -} - -std::string -OS::getEnvironmentVariable(const char* variableName, const char* defaultVal, const char* alternativeBashCommand) { -#if ELPP_OS_UNIX - const char* val = getenv(variableName); -#elif ELPP_OS_WINDOWS - const char* val = getWindowsEnvironmentVariable(variableName); -#endif // ELPP_OS_UNIX - if ((val == nullptr) || ((strcmp(val, "") == 0))) { -#if ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) - // Try harder on unix-based systems - std::string valBash = base::utils::OS::getBashOutput(alternativeBashCommand); - if (valBash.empty()) { - return std::string(defaultVal); - } else { - return valBash; - } -#elif ELPP_OS_WINDOWS || ELPP_OS_UNIX - ELPP_UNUSED(alternativeBashCommand); - return std::string(defaultVal); -#endif // ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) - } - return std::string(val); -} - -std::string -OS::currentUser(void) { -#if ELPP_OS_UNIX && !ELPP_OS_ANDROID - return getEnvironmentVariable("USER", base::consts::kUnknownUser, "whoami"); -#elif ELPP_OS_WINDOWS - return getEnvironmentVariable("USERNAME", base::consts::kUnknownUser); -#elif ELPP_OS_ANDROID - ELPP_UNUSED(base::consts::kUnknownUser); - return std::string("android"); -#else - return std::string(); -#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID -} - -std::string -OS::currentHost(void) { -#if ELPP_OS_UNIX && !ELPP_OS_ANDROID - return getEnvironmentVariable("HOSTNAME", base::consts::kUnknownHost, "hostname"); -#elif ELPP_OS_WINDOWS - return getEnvironmentVariable("COMPUTERNAME", base::consts::kUnknownHost); -#elif ELPP_OS_ANDROID - ELPP_UNUSED(base::consts::kUnknownHost); - return getDeviceName(); -#else - return std::string(); -#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID -} - -bool -OS::termSupportsColor(void) { - std::string term = getEnvironmentVariable("TERM", ""); - return term == "xterm" || term == "xterm-color" || term == "xterm-256color" || term == "screen" || - term == "linux" || term == "cygwin" || term == "screen-256color"; -} - -// DateTime - -void -DateTime::gettimeofday(struct timeval* tv) { -#if ELPP_OS_WINDOWS - if (tv != nullptr) { -#if ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) - const unsigned __int64 delta_ = 11644473600000000Ui64; -#else - const unsigned __int64 delta_ = 11644473600000000ULL; -#endif // ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) - const double secOffSet = 0.000001; - const unsigned long usecOffSet = 1000000; - FILETIME fileTime; - GetSystemTimeAsFileTime(&fileTime); - unsigned __int64 present = 0; - present |= fileTime.dwHighDateTime; - present = present << 32; - present |= fileTime.dwLowDateTime; - present /= 10; // mic-sec - // Subtract the difference - present -= delta_; - tv->tv_sec = static_cast(present * secOffSet); - tv->tv_usec = static_cast(present % usecOffSet); - } -#else - ::gettimeofday(tv, nullptr); -#endif // ELPP_OS_WINDOWS -} - -std::string -DateTime::getDateTime(const char* format, const base::SubsecondPrecision* ssPrec) { - struct timeval currTime; - gettimeofday(&currTime); - return timevalToString(currTime, format, ssPrec); -} - -std::string -DateTime::timevalToString(struct timeval tval, const char* format, const el::base::SubsecondPrecision* ssPrec) { - struct ::tm timeInfo; - buildTimeInfo(&tval, &timeInfo); - const int kBuffSize = 30; - char buff_[kBuffSize] = ""; - parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast(tval.tv_usec / ssPrec->m_offset), ssPrec); - return std::string(buff_); -} - -base::type::string_t -DateTime::formatTime(unsigned long long time, base::TimestampUnit timestampUnit) { - base::type::EnumType start = static_cast(timestampUnit); - const base::type::char_t* unit = base::consts::kTimeFormats[start].unit; - for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) { - if (time <= base::consts::kTimeFormats[i].value) { - break; - } - if (base::consts::kTimeFormats[i].value == 1000.0f && time / 1000.0f < 1.9f) { - break; - } - time /= static_cast(base::consts::kTimeFormats[i].value); - unit = base::consts::kTimeFormats[i + 1].unit; - } - base::type::stringstream_t ss; - ss << time << " " << unit; - return ss.str(); -} - -unsigned long long -DateTime::getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, - base::TimestampUnit timestampUnit) { - if (timestampUnit == base::TimestampUnit::Microsecond) { - return static_cast( - static_cast(1000000 * endTime.tv_sec + endTime.tv_usec) - - static_cast(1000000 * startTime.tv_sec + startTime.tv_usec)); - } - // milliseconds - auto conv = [](const struct timeval& tim) { - return static_cast((tim.tv_sec * 1000) + (tim.tv_usec / 1000)); - }; - return static_cast(conv(endTime) - conv(startTime)); -} - -struct ::tm* -DateTime::buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) { -#if ELPP_OS_UNIX - time_t rawTime = currTime->tv_sec; - ::elpptime_r(&rawTime, timeInfo); - return timeInfo; -#else -#if ELPP_COMPILER_MSVC - ELPP_UNUSED(currTime); - time_t t; -#if defined(_USE_32BIT_TIME_T) - _time32(&t); -#else - _time64(&t); -#endif - elpptime_s(timeInfo, &t); - return timeInfo; -#else - // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method - time_t rawTime = currTime->tv_sec; - struct tm* tmInf = elpptime(&rawTime); - *timeInfo = *tmInf; - return timeInfo; -#endif // ELPP_COMPILER_MSVC -#endif // ELPP_OS_UNIX -} - -char* -DateTime::parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, std::size_t msec, - const base::SubsecondPrecision* ssPrec) { - const char* bufLim = buf + bufSz; - for (; *format; ++format) { - if (*format == base::consts::kFormatSpecifierChar) { - switch (*++format) { - case base::consts::kFormatSpecifierChar: // Escape - break; - case '\0': // End - --format; - break; - case 'd': // Day - buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mday, 2, buf, bufLim); - continue; - case 'a': // Day of week (short) - buf = base::utils::Str::addToBuff(base::consts::kDaysAbbrev[tInfo->tm_wday], buf, bufLim); - continue; - case 'A': // Day of week (long) - buf = base::utils::Str::addToBuff(base::consts::kDays[tInfo->tm_wday], buf, bufLim); - continue; - case 'M': // month - buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mon + 1, 2, buf, bufLim); - continue; - case 'b': // month (short) - buf = base::utils::Str::addToBuff(base::consts::kMonthsAbbrev[tInfo->tm_mon], buf, bufLim); - continue; - case 'B': // month (long) - buf = base::utils::Str::addToBuff(base::consts::kMonths[tInfo->tm_mon], buf, bufLim); - continue; - case 'y': // year (two digits) - buf = - base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 2, buf, bufLim); - continue; - case 'Y': // year (four digits) - buf = - base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 4, buf, bufLim); - continue; - case 'h': // hour (12-hour) - buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour % 12, 2, buf, bufLim); - continue; - case 'H': // hour (24-hour) - buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour, 2, buf, bufLim); - continue; - case 'm': // minute - buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_min, 2, buf, bufLim); - continue; - case 's': // second - buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_sec, 2, buf, bufLim); - continue; - case 'z': // subsecond part - case 'g': - buf = base::utils::Str::convertAndAddToBuff(msec, ssPrec->m_width, buf, bufLim); - continue; - case 'F': // AM/PM - buf = base::utils::Str::addToBuff((tInfo->tm_hour >= 12) ? base::consts::kPm : base::consts::kAm, - buf, bufLim); - continue; - default: - continue; - } - } - if (buf == bufLim) - break; - *buf++ = *format; - } - return buf; -} - -// CommandLineArgs - -void -CommandLineArgs::setArgs(int argc, char** argv) { - m_params.clear(); - m_paramsWithValue.clear(); - if (argc == 0 || argv == nullptr) { - return; - } - m_argc = argc; - m_argv = argv; - for (int i = 1; i < m_argc; ++i) { - const char* v = (strstr(m_argv[i], "=")); - if (v != nullptr && strlen(v) > 0) { - std::string key = std::string(m_argv[i]); - key = key.substr(0, key.find_first_of('=')); - if (hasParamWithValue(key.c_str())) { - ELPP_INTERNAL_INFO(1, "Skipping [" << key << "] arg since it already has value [" - << getParamValue(key.c_str()) << "]"); - } else { - m_paramsWithValue.insert(std::make_pair(key, std::string(v + 1))); - } - } - if (v == nullptr) { - if (hasParam(m_argv[i])) { - ELPP_INTERNAL_INFO(1, "Skipping [" << m_argv[i] << "] arg since it already exists"); - } else { - m_params.push_back(std::string(m_argv[i])); - } - } - } -} - -bool -CommandLineArgs::hasParamWithValue(const char* paramKey) const { - return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end(); -} - -const char* -CommandLineArgs::getParamValue(const char* paramKey) const { - std::unordered_map::const_iterator iter = m_paramsWithValue.find(std::string(paramKey)); - return iter != m_paramsWithValue.end() ? iter->second.c_str() : ""; -} - -bool -CommandLineArgs::hasParam(const char* paramKey) const { - return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end(); -} - -bool -CommandLineArgs::empty(void) const { - return m_params.empty() && m_paramsWithValue.empty(); -} - -std::size_t -CommandLineArgs::size(void) const { - return m_params.size() + m_paramsWithValue.size(); -} - -base::type::ostream_t& -operator<<(base::type::ostream_t& os, const CommandLineArgs& c) { - for (int i = 1; i < c.m_argc; ++i) { - os << ELPP_LITERAL("[") << c.m_argv[i] << ELPP_LITERAL("]"); - if (i < c.m_argc - 1) { - os << ELPP_LITERAL(" "); - } - } - return os; -} - -} // namespace utils - -// el::base::threading -namespace threading { - -#if ELPP_THREADING_ENABLED -#if ELPP_USE_STD_THREADING -#if ELPP_ASYNC_LOGGING -static void -msleep(int ms) { - // Only when async logging enabled - this is because async is strict on compiler -#if defined(ELPP_NO_SLEEP_FOR) - usleep(ms * 1000); -#else - std::this_thread::sleep_for(std::chrono::milliseconds(ms)); -#endif // defined(ELPP_NO_SLEEP_FOR) -} -#endif // ELPP_ASYNC_LOGGING -#endif // !ELPP_USE_STD_THREADING -#endif // ELPP_THREADING_ENABLED - -} // namespace threading - -// el::base - -// SubsecondPrecision - -void -SubsecondPrecision::init(int width) { - if (width < 1 || width > 6) { - width = base::consts::kDefaultSubsecondPrecision; - } - m_width = width; - switch (m_width) { - case 3: - m_offset = 1000; - break; - case 4: - m_offset = 100; - break; - case 5: - m_offset = 10; - break; - case 6: - m_offset = 1; - break; - default: - m_offset = 1000; - break; - } -} - -// LogFormat - -LogFormat::LogFormat(void) - : m_level(Level::Unknown), - m_userFormat(base::type::string_t()), - m_format(base::type::string_t()), - m_dateTimeFormat(std::string()), - m_flags(0x0), - m_currentUser(base::utils::OS::currentUser()), - m_currentHost(base::utils::OS::currentHost()) { -} - -LogFormat::LogFormat(Level level, const base::type::string_t& format) - : m_level(level), - m_userFormat(format), - m_currentUser(base::utils::OS::currentUser()), - m_currentHost(base::utils::OS::currentHost()) { - parseFromFormat(m_userFormat); -} - -LogFormat::LogFormat(const LogFormat& logFormat) - : m_level(logFormat.m_level), - m_userFormat(logFormat.m_userFormat), - m_format(logFormat.m_format), - m_dateTimeFormat(logFormat.m_dateTimeFormat), - m_flags(logFormat.m_flags), - m_currentUser(logFormat.m_currentUser), - m_currentHost(logFormat.m_currentHost) { -} - -LogFormat::LogFormat(LogFormat&& logFormat) { - m_level = std::move(logFormat.m_level); - m_userFormat = std::move(logFormat.m_userFormat); - m_format = std::move(logFormat.m_format); - m_dateTimeFormat = std::move(logFormat.m_dateTimeFormat); - m_flags = std::move(logFormat.m_flags); - m_currentUser = std::move(logFormat.m_currentUser); - m_currentHost = std::move(logFormat.m_currentHost); -} - -LogFormat& -LogFormat::operator=(const LogFormat& logFormat) { - if (&logFormat != this) { - m_level = logFormat.m_level; - m_userFormat = logFormat.m_userFormat; - m_dateTimeFormat = logFormat.m_dateTimeFormat; - m_flags = logFormat.m_flags; - m_currentUser = logFormat.m_currentUser; - m_currentHost = logFormat.m_currentHost; - } - return *this; -} - -bool -LogFormat::operator==(const LogFormat& other) { - return m_level == other.m_level && m_userFormat == other.m_userFormat && m_format == other.m_format && - m_dateTimeFormat == other.m_dateTimeFormat && m_flags == other.m_flags; -} - -/// @brief Updates format to be used while logging. -/// @param userFormat User provided format -void -LogFormat::parseFromFormat(const base::type::string_t& userFormat) { - // We make copy because we will be changing the format - // i.e, removing user provided date format from original format - // and then storing it. - base::type::string_t formatCopy = userFormat; - m_flags = 0x0; - auto conditionalAddFlag = [&](const base::type::char_t* specifier, base::FormatFlags flag) { - std::size_t foundAt = base::type::string_t::npos; - while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos) { - if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar) { - if (hasFlag(flag)) { - // If we already have flag we remove the escape chars so that '%%' is turned to '%' - // even after specifier resolution - this is because we only replaceFirst specifier - formatCopy.erase(foundAt - 1, 1); - ++foundAt; - } - } else { - if (!hasFlag(flag)) - addFlag(flag); - } - } - }; - conditionalAddFlag(base::consts::kAppNameFormatSpecifier, base::FormatFlags::AppName); - conditionalAddFlag(base::consts::kSeverityLevelFormatSpecifier, base::FormatFlags::Level); - conditionalAddFlag(base::consts::kSeverityLevelShortFormatSpecifier, base::FormatFlags::LevelShort); - conditionalAddFlag(base::consts::kLoggerIdFormatSpecifier, base::FormatFlags::LoggerId); - conditionalAddFlag(base::consts::kThreadIdFormatSpecifier, base::FormatFlags::ThreadId); - conditionalAddFlag(base::consts::kLogFileFormatSpecifier, base::FormatFlags::File); - conditionalAddFlag(base::consts::kLogFileBaseFormatSpecifier, base::FormatFlags::FileBase); - conditionalAddFlag(base::consts::kLogLineFormatSpecifier, base::FormatFlags::Line); - conditionalAddFlag(base::consts::kLogLocationFormatSpecifier, base::FormatFlags::Location); - conditionalAddFlag(base::consts::kLogFunctionFormatSpecifier, base::FormatFlags::Function); - conditionalAddFlag(base::consts::kCurrentUserFormatSpecifier, base::FormatFlags::User); - conditionalAddFlag(base::consts::kCurrentHostFormatSpecifier, base::FormatFlags::Host); - conditionalAddFlag(base::consts::kMessageFormatSpecifier, base::FormatFlags::LogMessage); - conditionalAddFlag(base::consts::kVerboseLevelFormatSpecifier, base::FormatFlags::VerboseLevel); - // For date/time we need to extract user's date format first - std::size_t dateIndex = std::string::npos; - if ((dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier)) != std::string::npos) { - while (dateIndex > 0 && formatCopy[dateIndex - 1] == base::consts::kFormatSpecifierChar) { - dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier, dateIndex + 1); - } - if (dateIndex != std::string::npos) { - addFlag(base::FormatFlags::DateTime); - updateDateFormat(dateIndex, formatCopy); - } - } - m_format = formatCopy; - updateFormatSpec(); -} - -void -LogFormat::updateDateFormat(std::size_t index, base::type::string_t& currFormat) { - if (hasFlag(base::FormatFlags::DateTime)) { - index += ELPP_STRLEN(base::consts::kDateTimeFormatSpecifier); - } - const base::type::char_t* ptr = currFormat.c_str() + index; - if ((currFormat.size() > index) && (ptr[0] == '{')) { - // User has provided format for date/time - ++ptr; - int count = 1; // Start by 1 in order to remove starting brace - std::stringstream ss; - for (; *ptr; ++ptr, ++count) { - if (*ptr == '}') { - ++count; // In order to remove ending brace - break; - } - ss << static_cast(*ptr); - } - currFormat.erase(index, count); - m_dateTimeFormat = ss.str(); - } else { - // No format provided, use default - if (hasFlag(base::FormatFlags::DateTime)) { - m_dateTimeFormat = std::string(base::consts::kDefaultDateTimeFormat); - } - } -} - -void -LogFormat::updateFormatSpec(void) { - // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. - if (m_level == Level::Debug) { - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, - base::consts::kDebugLevelLogValue); - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, - base::consts::kDebugLevelShortLogValue); - } else if (m_level == Level::Info) { - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, - base::consts::kInfoLevelLogValue); - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, - base::consts::kInfoLevelShortLogValue); - } else if (m_level == Level::Warning) { - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, - base::consts::kWarningLevelLogValue); - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, - base::consts::kWarningLevelShortLogValue); - } else if (m_level == Level::Error) { - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, - base::consts::kErrorLevelLogValue); - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, - base::consts::kErrorLevelShortLogValue); - } else if (m_level == Level::Fatal) { - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, - base::consts::kFatalLevelLogValue); - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, - base::consts::kFatalLevelShortLogValue); - } else if (m_level == Level::Verbose) { - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, - base::consts::kVerboseLevelLogValue); - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, - base::consts::kVerboseLevelShortLogValue); - } else if (m_level == Level::Trace) { - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, - base::consts::kTraceLevelLogValue); - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, - base::consts::kTraceLevelShortLogValue); - } - if (hasFlag(base::FormatFlags::User)) { - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentUserFormatSpecifier, m_currentUser); - } - if (hasFlag(base::FormatFlags::Host)) { - base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentHostFormatSpecifier, m_currentHost); - } - // Ignore Level::Global and Level::Unknown -} - -// TypedConfigurations - -TypedConfigurations::TypedConfigurations(Configurations* configurations, - base::LogStreamsReferenceMap* logStreamsReference) { - m_configurations = configurations; - m_logStreamsReference = logStreamsReference; - build(m_configurations); -} - -TypedConfigurations::TypedConfigurations(const TypedConfigurations& other) { - this->m_configurations = other.m_configurations; - this->m_logStreamsReference = other.m_logStreamsReference; - build(m_configurations); -} - -bool -TypedConfigurations::enabled(Level level) { - return getConfigByVal(level, &m_enabledMap, "enabled"); -} - -bool -TypedConfigurations::toFile(Level level) { - return getConfigByVal(level, &m_toFileMap, "toFile"); -} - -const std::string& -TypedConfigurations::filename(Level level) { - return getConfigByRef(level, &m_filenameMap, "filename"); -} - -bool -TypedConfigurations::toStandardOutput(Level level) { - return getConfigByVal(level, &m_toStandardOutputMap, "toStandardOutput"); -} - -const base::LogFormat& -TypedConfigurations::logFormat(Level level) { - return getConfigByRef(level, &m_logFormatMap, "logFormat"); -} - -const base::SubsecondPrecision& -TypedConfigurations::subsecondPrecision(Level level) { - return getConfigByRef(level, &m_subsecondPrecisionMap, "subsecondPrecision"); -} - -const base::MillisecondsWidth& -TypedConfigurations::millisecondsWidth(Level level) { - return getConfigByRef(level, &m_subsecondPrecisionMap, "millisecondsWidth"); -} - -bool -TypedConfigurations::performanceTracking(Level level) { - return getConfigByVal(level, &m_performanceTrackingMap, "performanceTracking"); -} - -base::type::fstream_t* -TypedConfigurations::fileStream(Level level) { - return getConfigByRef(level, &m_fileStreamMap, "fileStream").get(); -} - -std::size_t -TypedConfigurations::maxLogFileSize(Level level) { - return getConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize"); -} - -std::size_t -TypedConfigurations::logFlushThreshold(Level level) { - return getConfigByVal(level, &m_logFlushThresholdMap, "logFlushThreshold"); -} - -void -TypedConfigurations::build(Configurations* configurations) { - base::threading::ScopedLock scopedLock(lock()); - auto getBool = [](std::string boolStr) -> bool { // Pass by value for trimming - base::utils::Str::trim(boolStr); - return (boolStr == "TRUE" || boolStr == "true" || boolStr == "1"); - }; - std::vector withFileSizeLimit; - for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { - Configuration* conf = *it; - // We cannot use switch on strong enums because Intel C++ dont support them yet - if (conf->configurationType() == ConfigurationType::Enabled) { - setValue(conf->level(), getBool(conf->value()), &m_enabledMap); - } else if (conf->configurationType() == ConfigurationType::ToFile) { - setValue(conf->level(), getBool(conf->value()), &m_toFileMap); - } else if (conf->configurationType() == ConfigurationType::ToStandardOutput) { - setValue(conf->level(), getBool(conf->value()), &m_toStandardOutputMap); - } else if (conf->configurationType() == ConfigurationType::Filename) { - // We do not yet configure filename but we will configure in another - // loop. This is because if file cannot be created, we will force ToFile - // to be false. Because configuring logger is not necessarily performance - // sensative operation, we can live with another loop; (by the way this loop - // is not very heavy either) - } else if (conf->configurationType() == ConfigurationType::Format) { - setValue(conf->level(), - base::LogFormat(conf->level(), base::type::string_t(conf->value().begin(), conf->value().end())), - &m_logFormatMap); - } else if (conf->configurationType() == ConfigurationType::SubsecondPrecision) { - setValue(Level::Global, base::SubsecondPrecision(static_cast(getULong(conf->value()))), - &m_subsecondPrecisionMap); - } else if (conf->configurationType() == ConfigurationType::PerformanceTracking) { - setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap); - } else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) { - auto v = getULong(conf->value()); - setValue(conf->level(), static_cast(v), &m_maxLogFileSizeMap); - if (v != 0) { - withFileSizeLimit.push_back(conf); - } - } else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) { - setValue(conf->level(), static_cast(getULong(conf->value())), &m_logFlushThresholdMap); - } - } - // As mentioned earlier, we will now set filename configuration in separate loop to deal with non-existent files - for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { - Configuration* conf = *it; - if (conf->configurationType() == ConfigurationType::Filename) { - insertFile(conf->level(), conf->value()); - } - } - for (std::vector::iterator conf = withFileSizeLimit.begin(); conf != withFileSizeLimit.end(); - ++conf) { - // This is not unsafe as mutex is locked in currect scope - unsafeValidateFileRolling((*conf)->level(), base::defaultPreRollOutCallback); - } -} - -unsigned long -TypedConfigurations::getULong(std::string confVal) { - bool valid = true; - base::utils::Str::trim(confVal); - valid = !confVal.empty() && std::find_if(confVal.begin(), confVal.end(), - [](char c) { return !base::utils::Str::isDigit(c); }) == confVal.end(); - if (!valid) { - valid = false; - ELPP_ASSERT(valid, "Configuration value not a valid integer [" << confVal << "]"); - return 0; - } - return atol(confVal.c_str()); -} - -std::string -TypedConfigurations::resolveFilename(const std::string& filename) { - std::string resultingFilename = filename; - std::size_t dateIndex = std::string::npos; - std::string dateTimeFormatSpecifierStr = std::string(base::consts::kDateTimeFormatSpecifierForFilename); - if ((dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str())) != std::string::npos) { - while (dateIndex > 0 && resultingFilename[dateIndex - 1] == base::consts::kFormatSpecifierChar) { - dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str(), dateIndex + 1); - } - if (dateIndex != std::string::npos) { - const char* ptr = resultingFilename.c_str() + dateIndex; - // Goto end of specifier - ptr += dateTimeFormatSpecifierStr.size(); - std::string fmt; - if ((resultingFilename.size() > dateIndex) && (ptr[0] == '{')) { - // User has provided format for date/time - ++ptr; - int count = 1; // Start by 1 in order to remove starting brace - std::stringstream ss; - for (; *ptr; ++ptr, ++count) { - if (*ptr == '}') { - ++count; // In order to remove ending brace - break; - } - ss << *ptr; - } - resultingFilename.erase(dateIndex + dateTimeFormatSpecifierStr.size(), count); - fmt = ss.str(); - } else { - fmt = std::string(base::consts::kDefaultDateTimeFormatInFilename); - } - base::SubsecondPrecision ssPrec(3); - std::string now = base::utils::DateTime::getDateTime(fmt.c_str(), &ssPrec); - base::utils::Str::replaceAll(now, '/', '-'); // Replace path element since we are dealing with filename - base::utils::Str::replaceAll(resultingFilename, dateTimeFormatSpecifierStr, now); - } - } - return resultingFilename; -} - -void -TypedConfigurations::insertFile(Level level, const std::string& fullFilename) { - std::string resolvedFilename = resolveFilename(fullFilename); - if (resolvedFilename.empty()) { - std::cerr << "Could not load empty file for logging, please re-check your configurations for level [" - << LevelHelper::convertToString(level) << "]"; - } - std::string filePath = - base::utils::File::extractPathFromFilename(resolvedFilename, base::consts::kFilePathSeperator); - if (filePath.size() < resolvedFilename.size()) { - base::utils::File::createPath(filePath); - } - auto create = [&](Level level) { - base::LogStreamsReferenceMap::iterator filestreamIter = m_logStreamsReference->find(resolvedFilename); - base::type::fstream_t* fs = nullptr; - if (filestreamIter == m_logStreamsReference->end()) { - // We need a completely new stream, nothing to share with - fs = base::utils::File::newFileStream(resolvedFilename); - m_filenameMap.insert(std::make_pair(level, resolvedFilename)); - m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(fs))); - m_logStreamsReference->insert( - std::make_pair(resolvedFilename, base::FileStreamPtr(m_fileStreamMap.at(level)))); - } else { - // Woops! we have an existing one, share it! - m_filenameMap.insert(std::make_pair(level, filestreamIter->first)); - m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(filestreamIter->second))); - fs = filestreamIter->second.get(); - } - if (fs == nullptr) { - // We display bad file error from newFileStream() - ELPP_INTERNAL_ERROR("Setting [TO_FILE] of [" << LevelHelper::convertToString(level) << "] to FALSE", false); - setValue(level, false, &m_toFileMap); - } - }; - // If we dont have file conf for any level, create it for Level::Global first - // otherwise create for specified level - create(m_filenameMap.empty() && m_fileStreamMap.empty() ? Level::Global : level); -} - -bool -TypedConfigurations::unsafeValidateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback) { - base::type::fstream_t* fs = unsafeGetConfigByRef(level, &m_fileStreamMap, "fileStream").get(); - if (fs == nullptr) { - return true; - } - std::size_t maxLogFileSize = unsafeGetConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize"); - std::size_t currFileSize = base::utils::File::getSizeOfFile(fs); - if (maxLogFileSize != 0 && currFileSize >= maxLogFileSize) { - std::string fname = unsafeGetConfigByRef(level, &m_filenameMap, "filename"); - ELPP_INTERNAL_INFO(1, "Truncating log file [" << fname << "] as a result of configurations for level [" - << LevelHelper::convertToString(level) << "]"); - fs->close(); - preRollOutCallback(fname.c_str(), currFileSize, level); - fs->open(fname, std::fstream::out | std::fstream::trunc); - return true; - } - return false; -} - -// RegisteredHitCounters - -bool -RegisteredHitCounters::validateEveryN(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { - base::threading::ScopedLock scopedLock(lock()); - base::HitCounter* counter = get(filename, lineNumber); - if (counter == nullptr) { - registerNew(counter = new base::HitCounter(filename, lineNumber)); - } - counter->validateHitCounts(n); - bool result = (n >= 1 && counter->hitCounts() != 0 && counter->hitCounts() % n == 0); - return result; -} - -/// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one -/// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned -bool -RegisteredHitCounters::validateAfterN(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { - base::threading::ScopedLock scopedLock(lock()); - base::HitCounter* counter = get(filename, lineNumber); - if (counter == nullptr) { - registerNew(counter = new base::HitCounter(filename, lineNumber)); - } - // Do not use validateHitCounts here since we do not want to reset counter here - // Note the >= instead of > because we are incrementing - // after this check - if (counter->hitCounts() >= n) - return true; - counter->increment(); - return false; -} - -/// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one -/// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned -bool -RegisteredHitCounters::validateNTimes(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { - base::threading::ScopedLock scopedLock(lock()); - base::HitCounter* counter = get(filename, lineNumber); - if (counter == nullptr) { - registerNew(counter = new base::HitCounter(filename, lineNumber)); - } - counter->increment(); - // Do not use validateHitCounts here since we do not want to reset counter here - if (counter->hitCounts() <= n) - return true; - return false; -} - -// RegisteredLoggers - -RegisteredLoggers::RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder) : m_defaultLogBuilder(defaultLogBuilder) { - m_defaultConfigurations.setToDefault(); -} - -Logger* -RegisteredLoggers::get(const std::string& id, bool forceCreation) { - base::threading::ScopedLock scopedLock(lock()); - Logger* logger_ = base::utils::Registry::get(id); - if (logger_ == nullptr && forceCreation) { - bool validId = Logger::isValidId(id); - if (!validId) { - ELPP_ASSERT(validId, "Invalid logger ID [" << id << "]. Not registering this logger."); - return nullptr; - } - logger_ = new Logger(id, m_defaultConfigurations, &m_logStreamsReference); - logger_->m_logBuilder = m_defaultLogBuilder; - registerNew(id, logger_); - LoggerRegistrationCallback* callback = nullptr; - for (const std::pair& h : - m_loggerRegistrationCallbacks) { - callback = h.second.get(); - if (callback != nullptr && callback->enabled()) { - callback->handle(logger_); - } - } - } - return logger_; -} - -bool -RegisteredLoggers::remove(const std::string& id) { - if (id == base::consts::kDefaultLoggerId) { - return false; - } - // get has internal lock - Logger* logger = base::utils::Registry::get(id); - if (logger != nullptr) { - // unregister has internal lock - unregister(logger); - } - return true; -} - -void -RegisteredLoggers::unsafeFlushAll(void) { - ELPP_INTERNAL_INFO(1, "Flushing all log files"); - for (base::LogStreamsReferenceMap::iterator it = m_logStreamsReference.begin(); it != m_logStreamsReference.end(); - ++it) { - if (it->second.get() == nullptr) - continue; - it->second->flush(); - } -} - -// VRegistry - -VRegistry::VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags) : m_level(level), m_pFlags(pFlags) { -} - -/// @brief Sets verbose level. Accepted range is 0-9 -void -VRegistry::setLevel(base::type::VerboseLevel level) { - base::threading::ScopedLock scopedLock(lock()); - if (level > 9) - m_level = base::consts::kMaxVerboseLevel; - else - m_level = level; -} - -void -VRegistry::setModules(const char* modules) { - base::threading::ScopedLock scopedLock(lock()); - auto addSuffix = [](std::stringstream& ss, const char* sfx, const char* prev) { - if (prev != nullptr && base::utils::Str::endsWith(ss.str(), std::string(prev))) { - std::string chr(ss.str().substr(0, ss.str().size() - strlen(prev))); - ss.str(std::string("")); - ss << chr; - } - if (base::utils::Str::endsWith(ss.str(), std::string(sfx))) { - std::string chr(ss.str().substr(0, ss.str().size() - strlen(sfx))); - ss.str(std::string("")); - ss << chr; - } - ss << sfx; - }; - auto insert = [&](std::stringstream& ss, base::type::VerboseLevel level) { - if (!base::utils::hasFlag(LoggingFlag::DisableVModulesExtensions, *m_pFlags)) { - addSuffix(ss, ".h", nullptr); - m_modules.insert(std::make_pair(ss.str(), level)); - addSuffix(ss, ".c", ".h"); - m_modules.insert(std::make_pair(ss.str(), level)); - addSuffix(ss, ".cpp", ".c"); - m_modules.insert(std::make_pair(ss.str(), level)); - addSuffix(ss, ".cc", ".cpp"); - m_modules.insert(std::make_pair(ss.str(), level)); - addSuffix(ss, ".cxx", ".cc"); - m_modules.insert(std::make_pair(ss.str(), level)); - addSuffix(ss, ".-inl.h", ".cxx"); - m_modules.insert(std::make_pair(ss.str(), level)); - addSuffix(ss, ".hxx", ".-inl.h"); - m_modules.insert(std::make_pair(ss.str(), level)); - addSuffix(ss, ".hpp", ".hxx"); - m_modules.insert(std::make_pair(ss.str(), level)); - addSuffix(ss, ".hh", ".hpp"); - } - m_modules.insert(std::make_pair(ss.str(), level)); - }; - bool isMod = true; - bool isLevel = false; - std::stringstream ss; - int level = -1; - for (; *modules; ++modules) { - switch (*modules) { - case '=': - isLevel = true; - isMod = false; - break; - case ',': - isLevel = false; - isMod = true; - if (!ss.str().empty() && level != -1) { - insert(ss, static_cast(level)); - ss.str(std::string("")); - level = -1; - } - break; - default: - if (isMod) { - ss << *modules; - } else if (isLevel) { - if (isdigit(*modules)) { - level = static_cast(*modules) - 48; - } - } - break; - } - } - if (!ss.str().empty() && level != -1) { - insert(ss, static_cast(level)); - } -} - -bool -VRegistry::allowed(base::type::VerboseLevel vlevel, const char* file) { - base::threading::ScopedLock scopedLock(lock()); - if (m_modules.empty() || file == nullptr) { - return vlevel <= m_level; - } else { - char baseFilename[base::consts::kSourceFilenameMaxLength] = ""; - base::utils::File::buildBaseFilename(file, baseFilename); - std::unordered_map::iterator it = m_modules.begin(); - for (; it != m_modules.end(); ++it) { - if (base::utils::Str::wildCardMatch(baseFilename, it->first.c_str())) { - return vlevel <= it->second; - } - } - if (base::utils::hasFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified, *m_pFlags)) { - return true; - } - return false; - } -} - -void -VRegistry::setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) { - if (commandLineArgs->hasParam("-v") || commandLineArgs->hasParam("--verbose") || commandLineArgs->hasParam("-V") || - commandLineArgs->hasParam("--VERBOSE")) { - setLevel(base::consts::kMaxVerboseLevel); - } else if (commandLineArgs->hasParamWithValue("--v")) { - setLevel(static_cast(atoi(commandLineArgs->getParamValue("--v")))); - } else if (commandLineArgs->hasParamWithValue("--V")) { - setLevel(static_cast(atoi(commandLineArgs->getParamValue("--V")))); - } else if ((commandLineArgs->hasParamWithValue("-vmodule")) && vModulesEnabled()) { - setModules(commandLineArgs->getParamValue("-vmodule")); - } else if (commandLineArgs->hasParamWithValue("-VMODULE") && vModulesEnabled()) { - setModules(commandLineArgs->getParamValue("-VMODULE")); - } -} - -#if !defined(ELPP_DEFAULT_LOGGING_FLAGS) -#define ELPP_DEFAULT_LOGGING_FLAGS 0x0 -#endif // !defined(ELPP_DEFAULT_LOGGING_FLAGS) -// Storage -#if ELPP_ASYNC_LOGGING -Storage::Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) - : -#else -Storage::Storage(const LogBuilderPtr& defaultLogBuilder) - : -#endif // ELPP_ASYNC_LOGGING - m_registeredHitCounters(new base::RegisteredHitCounters()), - m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)), - m_flags(ELPP_DEFAULT_LOGGING_FLAGS), - m_vRegistry(new base::VRegistry(0, &m_flags)), - -#if ELPP_ASYNC_LOGGING - m_asyncLogQueue(new base::AsyncLogQueue()), - m_asyncDispatchWorker(asyncDispatchWorker), -#endif // ELPP_ASYNC_LOGGING - - m_preRollOutCallback(base::defaultPreRollOutCallback) { - // Register default logger - m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId)); - // We register default logger anyway (worse case it's not going to register) just in case - m_registeredLoggers->get("default"); - -#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) - // Register performance logger and reconfigure format - Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId)); - m_registeredLoggers->get("performance"); - performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg")); - performanceLogger->reconfigure(); -#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) - -#if defined(ELPP_SYSLOG) - // Register syslog logger and reconfigure format - Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId)); - sysLogLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%level: %msg")); - sysLogLogger->reconfigure(); -#endif // defined(ELPP_SYSLOG) - addFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified); -#if ELPP_ASYNC_LOGGING - installLogDispatchCallback(std::string("AsyncLogDispatchCallback")); -#else - installLogDispatchCallback(std::string("DefaultLogDispatchCallback")); -#endif // ELPP_ASYNC_LOGGING -#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) - installPerformanceTrackingCallback( - std::string("DefaultPerformanceTrackingCallback")); -#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) - ELPP_INTERNAL_INFO(1, "Easylogging++ has been initialized"); -#if ELPP_ASYNC_LOGGING - m_asyncDispatchWorker->start(); -#endif // ELPP_ASYNC_LOGGING -} - -Storage::~Storage(void) { - ELPP_INTERNAL_INFO(4, "Destroying storage"); -#if ELPP_ASYNC_LOGGING - ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous"); - uninstallLogDispatchCallback(std::string("AsyncLogDispatchCallback")); - installLogDispatchCallback(std::string("DefaultLogDispatchCallback")); - ELPP_INTERNAL_INFO(5, "Destroying asyncDispatchWorker"); - base::utils::safeDelete(m_asyncDispatchWorker); - ELPP_INTERNAL_INFO(5, "Destroying asyncLogQueue"); - base::utils::safeDelete(m_asyncLogQueue); -#endif // ELPP_ASYNC_LOGGING - ELPP_INTERNAL_INFO(5, "Destroying registeredHitCounters"); - base::utils::safeDelete(m_registeredHitCounters); - ELPP_INTERNAL_INFO(5, "Destroying registeredLoggers"); - base::utils::safeDelete(m_registeredLoggers); - ELPP_INTERNAL_INFO(5, "Destroying vRegistry"); - base::utils::safeDelete(m_vRegistry); -} - -bool -Storage::hasCustomFormatSpecifier(const char* formatSpecifier) { - base::threading::ScopedLock scopedLock(customFormatSpecifiersLock()); - return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(), formatSpecifier) != - m_customFormatSpecifiers.end(); -} - -void -Storage::installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { - if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) { - return; - } - base::threading::ScopedLock scopedLock(customFormatSpecifiersLock()); - m_customFormatSpecifiers.push_back(customFormatSpecifier); -} - -bool -Storage::uninstallCustomFormatSpecifier(const char* formatSpecifier) { - base::threading::ScopedLock scopedLock(customFormatSpecifiersLock()); - std::vector::iterator it = - std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(), formatSpecifier); - if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) { - m_customFormatSpecifiers.erase(it); - return true; - } - return false; -} - -void -Storage::setApplicationArguments(int argc, char** argv) { - m_commandLineArgs.setArgs(argc, argv); - m_vRegistry->setFromArgs(commandLineArgs()); - // default log file -#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) - if (m_commandLineArgs.hasParamWithValue(base::consts::kDefaultLogFileParam)) { - Configurations c; - c.setGlobally(ConfigurationType::Filename, - std::string(m_commandLineArgs.getParamValue(base::consts::kDefaultLogFileParam))); - registeredLoggers()->setDefaultConfigurations(c); - for (base::RegisteredLoggers::iterator it = registeredLoggers()->begin(); it != registeredLoggers()->end(); - ++it) { - it->second->configure(c); - } - } -#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) -#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) - if (m_commandLineArgs.hasParamWithValue(base::consts::kLoggingFlagsParam)) { - int userInput = atoi(m_commandLineArgs.getParamValue(base::consts::kLoggingFlagsParam)); - if (ELPP_DEFAULT_LOGGING_FLAGS == 0x0) { - m_flags = userInput; - } else { - base::utils::addFlag(userInput, &m_flags); - } - } -#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) -} - -} // namespace base - -// LogDispatchCallback -void -LogDispatchCallback::handle(const LogDispatchData* data) { -#if defined(ELPP_THREAD_SAFE) - base::threading::ScopedLock scopedLock(m_fileLocksMapLock); - std::string filename = data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level()); - auto lock = m_fileLocks.find(filename); - if (lock == m_fileLocks.end()) { - m_fileLocks.emplace( - std::make_pair(filename, std::unique_ptr(new base::threading::Mutex))); - } -#endif -} - -base::threading::Mutex& -LogDispatchCallback::fileHandle(const LogDispatchData* data) { - auto it = - m_fileLocks.find(data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level())); - return *(it->second.get()); -} - -namespace base { -// DefaultLogDispatchCallback - -void -DefaultLogDispatchCallback::handle(const LogDispatchData* data) { -#if defined(ELPP_THREAD_SAFE) - LogDispatchCallback::handle(data); - base::threading::ScopedLock scopedLock(fileHandle(data)); -#endif - m_data = data; - dispatch(m_data->logMessage()->logger()->logBuilder()->build( - m_data->logMessage(), m_data->dispatchAction() == base::DispatchAction::NormalLog)); -} - -void -DefaultLogDispatchCallback::dispatch(base::type::string_t&& logLine) { - if (m_data->dispatchAction() == base::DispatchAction::NormalLog) { - if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) { - base::type::fstream_t* fs = - m_data->logMessage()->logger()->m_typedConfigurations->fileStream(m_data->logMessage()->level()); - if (fs != nullptr) { - fs->write(logLine.c_str(), logLine.size()); - if (fs->fail()) { - ELPP_INTERNAL_ERROR("Unable to write log to file [" - << m_data->logMessage()->logger()->m_typedConfigurations->filename( - m_data->logMessage()->level()) - << "].\n" - << "Few possible reasons (could be something else):\n" - << " * Permission denied\n" - << " * Disk full\n" - << " * Disk is not writable", - true); - } else { - if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || - (m_data->logMessage()->logger()->isFlushNeeded(m_data->logMessage()->level()))) { - m_data->logMessage()->logger()->flush(m_data->logMessage()->level(), fs); - } - } - } else { - ELPP_INTERNAL_ERROR("Log file for [" - << LevelHelper::convertToString(m_data->logMessage()->level()) << "] " - << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " - << m_data->logMessage()->logger()->id() << "]", - false); - } - } - if (m_data->logMessage()->logger()->m_typedConfigurations->toStandardOutput(m_data->logMessage()->level())) { - if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) - m_data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, - m_data->logMessage()->level()); - ELPP_COUT << ELPP_COUT_LINE(logLine); - } - } -#if defined(ELPP_SYSLOG) - else if (m_data->dispatchAction() == base::DispatchAction::SysLog) { - // Determine syslog priority - int sysLogPriority = 0; - if (m_data->logMessage()->level() == Level::Fatal) - sysLogPriority = LOG_EMERG; - else if (m_data->logMessage()->level() == Level::Error) - sysLogPriority = LOG_ERR; - else if (m_data->logMessage()->level() == Level::Warning) - sysLogPriority = LOG_WARNING; - else if (m_data->logMessage()->level() == Level::Info) - sysLogPriority = LOG_INFO; - else if (m_data->logMessage()->level() == Level::Debug) - sysLogPriority = LOG_DEBUG; - else - sysLogPriority = LOG_NOTICE; -#if defined(ELPP_UNICODE) - char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); - syslog(sysLogPriority, "%s", line); - free(line); -#else - syslog(sysLogPriority, "%s", logLine.c_str()); -#endif - } -#endif // defined(ELPP_SYSLOG) -} - -#if ELPP_ASYNC_LOGGING - -// AsyncLogDispatchCallback - -void -AsyncLogDispatchCallback::handle(const LogDispatchData* data) { - base::type::string_t logLine = data->logMessage()->logger()->logBuilder()->build( - data->logMessage(), data->dispatchAction() == base::DispatchAction::NormalLog); - if (data->dispatchAction() == base::DispatchAction::NormalLog && - data->logMessage()->logger()->typedConfigurations()->toStandardOutput(data->logMessage()->level())) { - if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) - data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, data->logMessage()->level()); - ELPP_COUT << ELPP_COUT_LINE(logLine); - } - // Save resources and only queue if we want to write to file otherwise just ignore handler - if (data->logMessage()->logger()->typedConfigurations()->toFile(data->logMessage()->level())) { - ELPP->asyncLogQueue()->push(AsyncLogItem(*(data->logMessage()), *data, logLine)); - } -} - -// AsyncDispatchWorker -AsyncDispatchWorker::AsyncDispatchWorker() { - setContinueRunning(false); -} - -AsyncDispatchWorker::~AsyncDispatchWorker() { - setContinueRunning(false); - ELPP_INTERNAL_INFO(6, "Stopping dispatch worker - Cleaning log queue"); - clean(); - ELPP_INTERNAL_INFO(6, "Log queue cleaned"); -} - -bool -AsyncDispatchWorker::clean(void) { - std::mutex m; - std::unique_lock lk(m); - cv.wait(lk, [] { return !ELPP->asyncLogQueue()->empty(); }); - emptyQueue(); - lk.unlock(); - cv.notify_one(); - return ELPP->asyncLogQueue()->empty(); -} - -void -AsyncDispatchWorker::emptyQueue(void) { - while (!ELPP->asyncLogQueue()->empty()) { - AsyncLogItem data = ELPP->asyncLogQueue()->next(); - handle(&data); - base::threading::msleep(100); - } -} - -void -AsyncDispatchWorker::start(void) { - base::threading::msleep(5000); // 5s (why?) - setContinueRunning(true); - std::thread t1(&AsyncDispatchWorker::run, this); - t1.join(); -} - -void -AsyncDispatchWorker::handle(AsyncLogItem* logItem) { - LogDispatchData* data = logItem->data(); - LogMessage* logMessage = logItem->logMessage(); - Logger* logger = logMessage->logger(); - base::TypedConfigurations* conf = logger->typedConfigurations(); - base::type::string_t logLine = logItem->logLine(); - if (data->dispatchAction() == base::DispatchAction::NormalLog) { - if (conf->toFile(logMessage->level())) { - base::type::fstream_t* fs = conf->fileStream(logMessage->level()); - if (fs != nullptr) { - fs->write(logLine.c_str(), logLine.size()); - if (fs->fail()) { - ELPP_INTERNAL_ERROR("Unable to write log to file [" - << conf->filename(logMessage->level()) << "].\n" - << "Few possible reasons (could be something else):\n" - << " * Permission denied\n" - << " * Disk full\n" - << " * Disk is not writable", - true); - } else { - if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (logger->isFlushNeeded(logMessage->level()))) { - logger->flush(logMessage->level(), fs); - } - } - } else { - ELPP_INTERNAL_ERROR("Log file for [" - << LevelHelper::convertToString(logMessage->level()) << "] " - << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " - << logger->id() << "]", - false); - } - } - } -#if defined(ELPP_SYSLOG) - else if (data->dispatchAction() == base::DispatchAction::SysLog) { - // Determine syslog priority - int sysLogPriority = 0; - if (logMessage->level() == Level::Fatal) - sysLogPriority = LOG_EMERG; - else if (logMessage->level() == Level::Error) - sysLogPriority = LOG_ERR; - else if (logMessage->level() == Level::Warning) - sysLogPriority = LOG_WARNING; - else if (logMessage->level() == Level::Info) - sysLogPriority = LOG_INFO; - else if (logMessage->level() == Level::Debug) - sysLogPriority = LOG_DEBUG; - else - sysLogPriority = LOG_NOTICE; -#if defined(ELPP_UNICODE) - char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); - syslog(sysLogPriority, "%s", line); - free(line); -#else - syslog(sysLogPriority, "%s", logLine.c_str()); -#endif - } -#endif // defined(ELPP_SYSLOG) -} - -void -AsyncDispatchWorker::run(void) { - while (continueRunning()) { - emptyQueue(); - base::threading::msleep(10); // 10ms - } -} -#endif // ELPP_ASYNC_LOGGING - -// DefaultLogBuilder - -base::type::string_t -DefaultLogBuilder::build(const LogMessage* logMessage, bool appendNewLine) const { - base::TypedConfigurations* tc = logMessage->logger()->typedConfigurations(); - const base::LogFormat* logFormat = &tc->logFormat(logMessage->level()); - base::type::string_t logLine = logFormat->format(); - char buff[base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength] = ""; - const char* bufLim = buff + sizeof(buff); - if (logFormat->hasFlag(base::FormatFlags::AppName)) { - // App name - base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kAppNameFormatSpecifier, - logMessage->logger()->parentApplicationName()); - } - if (logFormat->hasFlag(base::FormatFlags::ThreadId)) { - // Thread ID - base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kThreadIdFormatSpecifier, - ELPP->getThreadName(base::threading::getCurrentThreadId())); - } - if (logFormat->hasFlag(base::FormatFlags::DateTime)) { - // DateTime - base::utils::Str::replaceFirstWithEscape( - logLine, base::consts::kDateTimeFormatSpecifier, - base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(), - &tc->subsecondPrecision(logMessage->level()))); - } - if (logFormat->hasFlag(base::FormatFlags::Function)) { - // Function - base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFunctionFormatSpecifier, - logMessage->func()); - } - if (logFormat->hasFlag(base::FormatFlags::File)) { - // File - base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); - base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff); - base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileFormatSpecifier, std::string(buff)); - } - if (logFormat->hasFlag(base::FormatFlags::FileBase)) { - // FileBase - base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); - base::utils::File::buildBaseFilename(logMessage->file(), buff); - base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileBaseFormatSpecifier, std::string(buff)); - } - if (logFormat->hasFlag(base::FormatFlags::Line)) { - // Line - char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceLineMaxLength); - buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim, - false); - base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLineFormatSpecifier, std::string(buff)); - } - if (logFormat->hasFlag(base::FormatFlags::Location)) { - // Location - char* buf = base::utils::Str::clearBuff( - buff, base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength); - base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff); - buf = base::utils::Str::addToBuff(buff, buf, bufLim); - buf = base::utils::Str::addToBuff(":", buf, bufLim); - buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim, - false); - base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLocationFormatSpecifier, std::string(buff)); - } - if (logMessage->level() == Level::Verbose && logFormat->hasFlag(base::FormatFlags::VerboseLevel)) { - // Verbose level - char* buf = base::utils::Str::clearBuff(buff, 1); - buf = base::utils::Str::convertAndAddToBuff(logMessage->verboseLevel(), 1, buf, bufLim, false); - base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kVerboseLevelFormatSpecifier, - std::string(buff)); - } - if (logFormat->hasFlag(base::FormatFlags::LogMessage)) { - // Log message - base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message()); - } -#if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) - el::base::threading::ScopedLock lock_(ELPP->customFormatSpecifiersLock()); - ELPP_UNUSED(lock_); - for (std::vector::const_iterator it = ELPP->customFormatSpecifiers()->begin(); - it != ELPP->customFormatSpecifiers()->end(); ++it) { - std::string fs(it->formatSpecifier()); - base::type::string_t wcsFormatSpecifier(fs.begin(), fs.end()); - base::utils::Str::replaceFirstWithEscape(logLine, wcsFormatSpecifier, it->resolver()(logMessage)); - } -#endif // !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) - if (appendNewLine) - logLine += ELPP_LITERAL("\n"); - return logLine; -} - -// LogDispatcher - -void -LogDispatcher::dispatch(void) { - if (m_proceed && m_dispatchAction == base::DispatchAction::None) { - m_proceed = false; - } - if (!m_proceed) { - return; - } -#ifndef ELPP_NO_GLOBAL_LOCK - // see https://github.com/muflihun/easyloggingpp/issues/580 - // global lock is turned off by default unless - // ELPP_NO_GLOBAL_LOCK is defined - base::threading::ScopedLock scopedLock(ELPP->lock()); -#endif - base::TypedConfigurations* tc = m_logMessage->logger()->m_typedConfigurations; - if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) { - tc->validateFileRolling(m_logMessage->level(), ELPP->preRollOutCallback()); - } - LogDispatchCallback* callback = nullptr; - LogDispatchData data; - for (const std::pair& h : ELPP->m_logDispatchCallbacks) { - callback = h.second.get(); - if (callback != nullptr && callback->enabled()) { - data.setLogMessage(m_logMessage); - data.setDispatchAction(m_dispatchAction); - callback->handle(&data); - } - } -} - -// MessageBuilder - -void -MessageBuilder::initialize(Logger* logger) { - m_logger = logger; - m_containerLogSeperator = - ELPP->hasFlag(LoggingFlag::NewLineForContainer) ? ELPP_LITERAL("\n ") : ELPP_LITERAL(", "); -} - -MessageBuilder& -MessageBuilder::operator<<(const wchar_t* msg) { - if (msg == nullptr) { - m_logger->stream() << base::consts::kNullPointer; - return *this; - } -#if defined(ELPP_UNICODE) - m_logger->stream() << msg; -#else - char* buff_ = base::utils::Str::wcharPtrToCharPtr(msg); - m_logger->stream() << buff_; - free(buff_); -#endif - if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { - m_logger->stream() << " "; - } - return *this; -} - -// Writer - -Writer& -Writer::construct(Logger* logger, bool needLock) { - m_logger = logger; - initializeLogger(logger->id(), false, needLock); - m_messageBuilder.initialize(m_logger); - return *this; -} - -Writer& -Writer::construct(int count, const char* loggerIds, ...) { - if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { - va_list loggersList; - va_start(loggersList, loggerIds); - const char* id = loggerIds; - m_loggerIds.reserve(count); - for (int i = 0; i < count; ++i) { - m_loggerIds.push_back(std::string(id)); - id = va_arg(loggersList, const char*); - } - va_end(loggersList); - initializeLogger(m_loggerIds.at(0)); - } else { - initializeLogger(std::string(loggerIds)); - } - m_messageBuilder.initialize(m_logger); - return *this; -} - -void -Writer::initializeLogger(const std::string& loggerId, bool lookup, bool needLock) { - if (lookup) { - m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically)); - } - if (m_logger == nullptr) { - { - if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) { - // Somehow default logger has been unregistered. Not good! Register again - ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId)); - } - } - Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) - << "Logger [" << loggerId << "] is not registered yet!"; - m_proceed = false; - } else { - if (needLock) { - m_logger->acquireLock(); // This should not be unlocked by checking m_proceed because - // m_proceed can be changed by lines below - } - if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging)) { - m_proceed = m_level == Level::Verbose - ? m_logger->enabled(m_level) - : LevelHelper::castToInt(m_level) >= LevelHelper::castToInt(ELPP->m_loggingLevel); - } else { - m_proceed = m_logger->enabled(m_level); - } - } -} - -void -Writer::processDispatch() { -#if ELPP_LOGGING_ENABLED - if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { - bool firstDispatched = false; - base::type::string_t logMessage; - std::size_t i = 0; - do { - if (m_proceed) { - if (firstDispatched) { - m_logger->stream() << logMessage; - } else { - firstDispatched = true; - if (m_loggerIds.size() > 1) { - logMessage = m_logger->stream().str(); - } - } - triggerDispatch(); - } else if (m_logger != nullptr) { - m_logger->stream().str(ELPP_LITERAL("")); - m_logger->releaseLock(); - } - if (i + 1 < m_loggerIds.size()) { - initializeLogger(m_loggerIds.at(i + 1)); - } - } while (++i < m_loggerIds.size()); - } else { - if (m_proceed) { - triggerDispatch(); - } else if (m_logger != nullptr) { - m_logger->stream().str(ELPP_LITERAL("")); - m_logger->releaseLock(); - } - } -#else - if (m_logger != nullptr) { - m_logger->stream().str(ELPP_LITERAL("")); - m_logger->releaseLock(); - } -#endif // ELPP_LOGGING_ENABLED -} - -void -Writer::triggerDispatch(void) { - if (m_proceed) { - if (m_msg == nullptr) { - LogMessage msg(m_level, m_file, m_line, m_func, m_verboseLevel, m_logger); - base::LogDispatcher(m_proceed, &msg, m_dispatchAction).dispatch(); - } else { - base::LogDispatcher(m_proceed, m_msg, m_dispatchAction).dispatch(); - } - } - if (m_logger != nullptr) { - m_logger->stream().str(ELPP_LITERAL("")); - m_logger->releaseLock(); - } - if (m_proceed && m_level == Level::Fatal && !ELPP->hasFlag(LoggingFlag::DisableApplicationAbortOnFatalLog)) { - base::Writer(Level::Warning, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) - << "Aborting application. Reason: Fatal log at [" << m_file << ":" << m_line << "]"; - std::stringstream reasonStream; - reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]" - << " If you wish to disable 'abort on fatal log' please use " - << "el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)"; - base::utils::abort(1, reasonStream.str()); - } - m_proceed = false; -} - -// PErrorWriter - -PErrorWriter::~PErrorWriter(void) { - if (m_proceed) { -#if ELPP_COMPILER_MSVC - char buff[256]; - strerror_s(buff, 256, errno); - m_logger->stream() << ": " << buff << " [" << errno << "]"; -#else - m_logger->stream() << ": " << strerror(errno) << " [" << errno << "]"; -#endif - } -} - -// PerformanceTracker - -#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) - -PerformanceTracker::PerformanceTracker(const std::string& blockName, base::TimestampUnit timestampUnit, - const std::string& loggerId, bool scopedLog, Level level) - : m_blockName(blockName), - m_timestampUnit(timestampUnit), - m_loggerId(loggerId), - m_scopedLog(scopedLog), - m_level(level), - m_hasChecked(false), - m_lastCheckpointId(std::string()), - m_enabled(false) { -#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED - // We store it locally so that if user happen to change configuration by the end of scope - // or before calling checkpoint, we still depend on state of configuraton at time of construction - el::Logger* loggerPtr = ELPP->registeredLoggers()->get(loggerId, false); - m_enabled = loggerPtr != nullptr && loggerPtr->m_typedConfigurations->performanceTracking(m_level); - if (m_enabled) { - base::utils::DateTime::gettimeofday(&m_startTime); - } -#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED -} - -PerformanceTracker::~PerformanceTracker(void) { -#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED - if (m_enabled) { - base::threading::ScopedLock scopedLock(lock()); - if (m_scopedLog) { - base::utils::DateTime::gettimeofday(&m_endTime); - base::type::string_t formattedTime = getFormattedTimeTaken(); - PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete); - data.init(this); - data.m_formattedTimeTaken = formattedTime; - PerformanceTrackingCallback* callback = nullptr; - for (const std::pair& h : - ELPP->m_performanceTrackingCallbacks) { - callback = h.second.get(); - if (callback != nullptr && callback->enabled()) { - callback->handle(&data); - } - } - } - } -#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) -} - -void -PerformanceTracker::checkpoint(const std::string& id, const char* file, base::type::LineNumber line, const char* func) { -#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED - if (m_enabled) { - base::threading::ScopedLock scopedLock(lock()); - base::utils::DateTime::gettimeofday(&m_endTime); - base::type::string_t formattedTime = - m_hasChecked ? getFormattedTimeTaken(m_lastCheckpointTime) : ELPP_LITERAL(""); - PerformanceTrackingData data(PerformanceTrackingData::DataType::Checkpoint); - data.init(this); - data.m_checkpointId = id; - data.m_file = file; - data.m_line = line; - data.m_func = func; - data.m_formattedTimeTaken = formattedTime; - PerformanceTrackingCallback* callback = nullptr; - for (const std::pair& h : - ELPP->m_performanceTrackingCallbacks) { - callback = h.second.get(); - if (callback != nullptr && callback->enabled()) { - callback->handle(&data); - } - } - base::utils::DateTime::gettimeofday(&m_lastCheckpointTime); - m_hasChecked = true; - m_lastCheckpointId = id; - } -#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED - ELPP_UNUSED(id); - ELPP_UNUSED(file); - ELPP_UNUSED(line); - ELPP_UNUSED(func); -} - -const base::type::string_t -PerformanceTracker::getFormattedTimeTaken(struct timeval startTime) const { - if (ELPP->hasFlag(LoggingFlag::FixedTimeFormat)) { - base::type::stringstream_t ss; - ss << base::utils::DateTime::getTimeDifference(m_endTime, startTime, m_timestampUnit) << " " - << base::consts::kTimeFormats[static_cast(m_timestampUnit)].unit; - return ss.str(); - } - return base::utils::DateTime::formatTime( - base::utils::DateTime::getTimeDifference(m_endTime, startTime, m_timestampUnit), m_timestampUnit); -} - -#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) - -namespace debug { -#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) - -// StackTrace - -StackTrace::StackTraceEntry::StackTraceEntry(std::size_t index, const std::string& loc, const std::string& demang, - const std::string& hex, const std::string& addr) - : m_index(index), m_location(loc), m_demangled(demang), m_hex(hex), m_addr(addr) { -} - -std::ostream& -operator<<(std::ostream& ss, const StackTrace::StackTraceEntry& si) { - ss << "[" << si.m_index << "] " << si.m_location << (si.m_hex.empty() ? "" : "+") << si.m_hex << " " << si.m_addr - << (si.m_demangled.empty() ? "" : ":") << si.m_demangled; - return ss; -} - -std::ostream& -operator<<(std::ostream& os, const StackTrace& st) { - std::vector::const_iterator it = st.m_stack.begin(); - while (it != st.m_stack.end()) { - os << " " << *it++ << "\n"; - } - return os; -} - -void -StackTrace::generateNew(void) { -#if ELPP_STACKTRACE - m_stack.clear(); - void* stack[kMaxStack]; - unsigned int size = backtrace(stack, kMaxStack); - char** strings = backtrace_symbols(stack, size); - if (size > kStackStart) { // Skip StackTrace c'tor and generateNew - for (std::size_t i = kStackStart; i < size; ++i) { - std::string mangName; - std::string location; - std::string hex; - std::string addr; - - // entry: 2 crash.cpp.bin 0x0000000101552be5 _ZN2el4base5debug10StackTraceC1Ev + 21 - const std::string line(strings[i]); - auto p = line.find("_"); - if (p != std::string::npos) { - mangName = line.substr(p); - mangName = mangName.substr(0, mangName.find(" +")); - } - p = line.find("0x"); - if (p != std::string::npos) { - addr = line.substr(p); - addr = addr.substr(0, addr.find("_")); - } - // Perform demangling if parsed properly - if (!mangName.empty()) { - int status = 0; - char* demangName = abi::__cxa_demangle(mangName.data(), 0, 0, &status); - // if demangling is successful, output the demangled function name - if (status == 0) { - // Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html) - StackTraceEntry entry(i - 1, location, demangName, hex, addr); - m_stack.push_back(entry); - } else { - // Not successful - we will use mangled name - StackTraceEntry entry(i - 1, location, mangName, hex, addr); - m_stack.push_back(entry); - } - free(demangName); - } else { - StackTraceEntry entry(i - 1, line); - m_stack.push_back(entry); - } - } - } - free(strings); -#else - ELPP_INTERNAL_INFO(1, "Stacktrace generation not supported for selected compiler"); -#endif // ELPP_STACKTRACE -} - -// Static helper functions - -static std::string -crashReason(int sig) { - std::stringstream ss; - bool foundReason = false; - for (int i = 0; i < base::consts::kCrashSignalsCount; ++i) { - if (base::consts::kCrashSignals[i].numb == sig) { - ss << "Application has crashed due to [" << base::consts::kCrashSignals[i].name << "] signal"; - if (ELPP->hasFlag(el::LoggingFlag::LogDetailedCrashReason)) { - ss << std::endl - << " " << base::consts::kCrashSignals[i].brief << std::endl - << " " << base::consts::kCrashSignals[i].detail; - } - foundReason = true; - } - } - if (!foundReason) { - ss << "Application has crashed due to unknown signal [" << sig << "]"; - } - return ss.str(); -} -/// @brief Logs reason of crash from sig -static void -logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { - if (sig == SIGINT && ELPP->hasFlag(el::LoggingFlag::IgnoreSigInt)) { - return; - } - std::stringstream ss; - ss << "CRASH HANDLED; "; - ss << crashReason(sig); -#if ELPP_STACKTRACE - if (stackTraceIfAvailable) { - ss << std::endl << " ======= Backtrace: =========" << std::endl << base::debug::StackTrace(); - } -#else - ELPP_UNUSED(stackTraceIfAvailable); -#endif // ELPP_STACKTRACE - ELPP_WRITE_LOG(el::base::Writer, level, base::DispatchAction::NormalLog, logger) << ss.str(); -} - -static inline void -crashAbort(int sig) { - base::utils::abort(sig, std::string()); -} - -/// @brief Default application crash handler -/// -/// @detail This function writes log using 'default' logger, prints stack trace for GCC based compilers and aborts -/// program. -static inline void -defaultCrashHandler(int sig) { - base::debug::logCrashReason(sig, true, Level::Fatal, base::consts::kDefaultLoggerId); - base::debug::crashAbort(sig); -} - -// CrashHandler - -CrashHandler::CrashHandler(bool useDefault) { - if (useDefault) { - setHandler(defaultCrashHandler); - } -} - -void -CrashHandler::setHandler(const Handler& cHandler) { - m_handler = cHandler; -#if defined(ELPP_HANDLE_SIGABRT) - int i = 0; // SIGABRT is at base::consts::kCrashSignals[0] -#else - int i = 1; -#endif // defined(ELPP_HANDLE_SIGABRT) - for (; i < base::consts::kCrashSignalsCount; ++i) { - m_handler = signal(base::consts::kCrashSignals[i].numb, cHandler); - } -} - -#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) -} // namespace debug -} // namespace base - -// el - -// Helpers - -#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) - -void -Helpers::crashAbort(int sig, const char* sourceFile, unsigned int long line) { - std::stringstream ss; - ss << base::debug::crashReason(sig).c_str(); - ss << " - [Called el::Helpers::crashAbort(" << sig << ")]"; - if (sourceFile != nullptr && strlen(sourceFile) > 0) { - ss << " - Source: " << sourceFile; - if (line > 0) - ss << ":" << line; - else - ss << " (line number not specified)"; - } - base::utils::abort(sig, ss.str()); -} - -void -Helpers::logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { - el::base::debug::logCrashReason(sig, stackTraceIfAvailable, level, logger); -} - -#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) - -// Loggers - -Logger* -Loggers::getLogger(const std::string& identity, bool registerIfNotAvailable) { - return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable); -} - -void -Loggers::setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr) { - ELPP->registeredLoggers()->setDefaultLogBuilder(logBuilderPtr); -} - -bool -Loggers::unregisterLogger(const std::string& identity) { - return ELPP->registeredLoggers()->remove(identity); -} - -bool -Loggers::hasLogger(const std::string& identity) { - return ELPP->registeredLoggers()->has(identity); -} - -Logger* -Loggers::reconfigureLogger(Logger* logger, const Configurations& configurations) { - if (!logger) - return nullptr; - logger->configure(configurations); - return logger; -} - -Logger* -Loggers::reconfigureLogger(const std::string& identity, const Configurations& configurations) { - return Loggers::reconfigureLogger(Loggers::getLogger(identity), configurations); -} - -Logger* -Loggers::reconfigureLogger(const std::string& identity, ConfigurationType configurationType, const std::string& value) { - Logger* logger = Loggers::getLogger(identity); - if (logger == nullptr) { - return nullptr; - } - logger->configurations()->set(Level::Global, configurationType, value); - logger->reconfigure(); - return logger; -} - -void -Loggers::reconfigureAllLoggers(const Configurations& configurations) { - for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); - it != ELPP->registeredLoggers()->end(); ++it) { - Loggers::reconfigureLogger(it->second, configurations); - } -} - -void -Loggers::reconfigureAllLoggers(Level level, ConfigurationType configurationType, const std::string& value) { - for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); - it != ELPP->registeredLoggers()->end(); ++it) { - Logger* logger = it->second; - logger->configurations()->set(level, configurationType, value); - logger->reconfigure(); - } -} - -void -Loggers::setDefaultConfigurations(const Configurations& configurations, bool reconfigureExistingLoggers) { - ELPP->registeredLoggers()->setDefaultConfigurations(configurations); - if (reconfigureExistingLoggers) { - Loggers::reconfigureAllLoggers(configurations); - } -} - -const Configurations* -Loggers::defaultConfigurations(void) { - return ELPP->registeredLoggers()->defaultConfigurations(); -} - -const base::LogStreamsReferenceMap* -Loggers::logStreamsReference(void) { - return ELPP->registeredLoggers()->logStreamsReference(); -} - -base::TypedConfigurations -Loggers::defaultTypedConfigurations(void) { - return base::TypedConfigurations(ELPP->registeredLoggers()->defaultConfigurations(), - ELPP->registeredLoggers()->logStreamsReference()); -} - -std::vector* -Loggers::populateAllLoggerIds(std::vector* targetList) { - targetList->clear(); - for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->list().begin(); - it != ELPP->registeredLoggers()->list().end(); ++it) { - targetList->push_back(it->first); - } - return targetList; -} - -void -Loggers::configureFromGlobal(const char* globalConfigurationFilePath) { - std::ifstream gcfStream(globalConfigurationFilePath, std::ifstream::in); - ELPP_ASSERT(gcfStream.is_open(), - "Unable to open global configuration file [" << globalConfigurationFilePath << "] for parsing."); - std::string line = std::string(); - std::stringstream ss; - Logger* logger = nullptr; - auto configure = [&](void) { - ELPP_INTERNAL_INFO(8, "Configuring logger: '" << logger->id() << "' with configurations \n" - << ss.str() << "\n--------------"); - Configurations c; - c.parseFromText(ss.str()); - logger->configure(c); - }; - while (gcfStream.good()) { - std::getline(gcfStream, line); - ELPP_INTERNAL_INFO(1, "Parsing line: " << line); - base::utils::Str::trim(line); - if (Configurations::Parser::isComment(line)) - continue; - Configurations::Parser::ignoreComments(&line); - base::utils::Str::trim(line); - if (line.size() > 2 && base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLoggerId))) { - if (!ss.str().empty() && logger != nullptr) { - configure(); - } - ss.str(std::string("")); - line = line.substr(2); - base::utils::Str::trim(line); - if (line.size() > 1) { - ELPP_INTERNAL_INFO(1, "Getting logger: '" << line << "'"); - logger = getLogger(line); - } - } else { - ss << line << "\n"; - } - } - if (!ss.str().empty() && logger != nullptr) { - configure(); - } -} - -bool -Loggers::configureFromArg(const char* argKey) { -#if defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) - ELPP_UNUSED(argKey); -#else - if (!Helpers::commandLineArgs()->hasParamWithValue(argKey)) { - return false; - } - configureFromGlobal(Helpers::commandLineArgs()->getParamValue(argKey)); -#endif // defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) - return true; -} - -void -Loggers::flushAll(void) { - ELPP->registeredLoggers()->flushAll(); -} - -void -Loggers::setVerboseLevel(base::type::VerboseLevel level) { - ELPP->vRegistry()->setLevel(level); -} - -base::type::VerboseLevel -Loggers::verboseLevel(void) { - return ELPP->vRegistry()->level(); -} - -void -Loggers::setVModules(const char* modules) { - if (ELPP->vRegistry()->vModulesEnabled()) { - ELPP->vRegistry()->setModules(modules); - } -} - -void -Loggers::clearVModules(void) { - ELPP->vRegistry()->clearModules(); -} - -// VersionInfo - -const std::string -VersionInfo::version(void) { - return std::string("9.96.7"); -} -/// @brief Release date of current version -const std::string -VersionInfo::releaseDate(void) { - return std::string("24-11-2018 0728hrs"); -} - -} // namespace el diff --git a/core/thirdparty/easyloggingpp/easylogging++.h b/core/thirdparty/easyloggingpp/easylogging++.h deleted file mode 100644 index ce6c7ece48..0000000000 --- a/core/thirdparty/easyloggingpp/easylogging++.h +++ /dev/null @@ -1,5193 +0,0 @@ -// -// Bismillah ar-Rahmaan ar-Raheem -// -// Easylogging++ v9.96.7 -// Single-header only, cross-platform logging library for C++ applications -// -// Copyright (c) 2012-2018 Zuhd Web Services -// Copyright (c) 2012-2018 @abumusamq -// -// This library is released under the MIT Licence. -// https://github.com/zuhd-org/easyloggingpp/blob/master/LICENSE -// -// https://zuhd.org -// http://muflihun.com -// - -#ifndef EASYLOGGINGPP_H -#define EASYLOGGINGPP_H -// Compilers and C++0x/C++11 Evaluation -#if __cplusplus >= 201103L -#define ELPP_CXX11 1 -#endif // __cplusplus >= 201103L -#if (defined(__GNUC__)) -#define ELPP_COMPILER_GCC 1 -#else -#define ELPP_COMPILER_GCC 0 -#endif -#if ELPP_COMPILER_GCC -#define ELPP_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) -#if defined(__GXX_EXPERIMENTAL_CXX0X__) -#define ELPP_CXX0X 1 -#endif -#endif -// Visual C++ -#if defined(_MSC_VER) -#define ELPP_COMPILER_MSVC 1 -#else -#define ELPP_COMPILER_MSVC 0 -#endif -#define ELPP_CRT_DBG_WARNINGS ELPP_COMPILER_MSVC -#if ELPP_COMPILER_MSVC -#if (_MSC_VER == 1600) -#define ELPP_CXX0X 1 -#elif (_MSC_VER >= 1700) -#define ELPP_CXX11 1 -#endif -#endif -// Clang++ -#if (defined(__clang__) && (__clang__ == 1)) -#define ELPP_COMPILER_CLANG 1 -#else -#define ELPP_COMPILER_CLANG 0 -#endif -#if ELPP_COMPILER_CLANG -#if __has_include() -#include // Make __GLIBCXX__ defined when using libstdc++ -#if !defined(__GLIBCXX__) || __GLIBCXX__ >= 20150426 -#define ELPP_CLANG_SUPPORTS_THREAD -#endif // !defined(__GLIBCXX__) || __GLIBCXX__ >= 20150426 -#endif // __has_include() -#endif -#if (defined(__MINGW32__) || defined(__MINGW64__)) -#define ELPP_MINGW 1 -#else -#define ELPP_MINGW 0 -#endif -#if (defined(__CYGWIN__) && (__CYGWIN__ == 1)) -#define ELPP_CYGWIN 1 -#else -#define ELPP_CYGWIN 0 -#endif -#if (defined(__INTEL_COMPILER)) -#define ELPP_COMPILER_INTEL 1 -#else -#define ELPP_COMPILER_INTEL 0 -#endif -// Operating System Evaluation -// Windows -#if (defined(_WIN32) || defined(_WIN64)) -#define ELPP_OS_WINDOWS 1 -#else -#define ELPP_OS_WINDOWS 0 -#endif -// Linux -#if (defined(__linux) || defined(__linux__)) -#define ELPP_OS_LINUX 1 -#else -#define ELPP_OS_LINUX 0 -#endif -#if (defined(__APPLE__)) -#define ELPP_OS_MAC 1 -#else -#define ELPP_OS_MAC 0 -#endif -#if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) -#define ELPP_OS_FREEBSD 1 -#else -#define ELPP_OS_FREEBSD 0 -#endif -#if (defined(__sun)) -#define ELPP_OS_SOLARIS 1 -#else -#define ELPP_OS_SOLARIS 0 -#endif -#if (defined(_AIX)) -#define ELPP_OS_AIX 1 -#else -#define ELPP_OS_AIX 0 -#endif -#if (defined(__NetBSD__)) -#define ELPP_OS_NETBSD 1 -#else -#define ELPP_OS_NETBSD 0 -#endif -#if defined(__EMSCRIPTEN__) -#define ELPP_OS_EMSCRIPTEN 1 -#else -#define ELPP_OS_EMSCRIPTEN 0 -#endif -// Unix -#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_NETBSD || ELPP_OS_SOLARIS || ELPP_OS_AIX || \ - ELPP_OS_EMSCRIPTEN) && \ - (!ELPP_OS_WINDOWS)) -#define ELPP_OS_UNIX 1 -#else -#define ELPP_OS_UNIX 0 -#endif -#if (defined(__ANDROID__)) -#define ELPP_OS_ANDROID 1 -#else -#define ELPP_OS_ANDROID 0 -#endif -// Evaluating Cygwin as *nix OS -#if !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN -#undef ELPP_OS_UNIX -#undef ELPP_OS_LINUX -#define ELPP_OS_UNIX 1 -#define ELPP_OS_LINUX 1 -#endif // !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN -#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_INFO) -#define ELPP_INTERNAL_DEBUGGING_OUT_INFO std::cout -#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) -#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_ERROR) -#define ELPP_INTERNAL_DEBUGGING_OUT_ERROR std::cerr -#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) -#if !defined(ELPP_INTERNAL_DEBUGGING_ENDL) -#define ELPP_INTERNAL_DEBUGGING_ENDL std::endl -#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) -#if !defined(ELPP_INTERNAL_DEBUGGING_MSG) -#define ELPP_INTERNAL_DEBUGGING_MSG(msg) msg -#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) -// Internal Assertions and errors -#if !defined(ELPP_DISABLE_ASSERT) -#if (defined(ELPP_DEBUG_ASSERT_FAILURE)) -#define ELPP_ASSERT(expr, msg) \ - if (!(expr)) { \ - std::stringstream internalInfoStream; \ - internalInfoStream << msg; \ - ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ - << "EASYLOGGING++ ASSERTION FAILED (LINE: " << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" \ - << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" << ELPP_INTERNAL_DEBUGGING_ENDL; \ - base::utils::abort(1, "ELPP Assertion failure, please define ELPP_DEBUG_ASSERT_FAILURE"); \ - } -#else -#define ELPP_ASSERT(expr, msg) \ - if (!(expr)) { \ - std::stringstream internalInfoStream; \ - internalInfoStream << msg; \ - ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ - << "ASSERTION FAILURE FROM EASYLOGGING++ (LINE: " << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" \ - << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" << ELPP_INTERNAL_DEBUGGING_ENDL; \ - } -#endif // (defined(ELPP_DEBUG_ASSERT_FAILURE)) -#else -#define ELPP_ASSERT(x, y) -#endif //(!defined(ELPP_DISABLE_ASSERT) -#if ELPP_COMPILER_MSVC -#define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ - { \ - char buff[256]; \ - strerror_s(buff, 256, errno); \ - ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << buff << " [" << errno << "]"; \ - } \ - (void)0 -#else -#define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ - ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << strerror(errno) << " [" << errno << "]"; \ - (void)0 -#endif // ELPP_COMPILER_MSVC -#if defined(ELPP_DEBUG_ERRORS) -#if !defined(ELPP_INTERNAL_ERROR) -#define ELPP_INTERNAL_ERROR(msg, pe) \ - { \ - std::stringstream internalInfoStream; \ - internalInfoStream << " " << msg; \ - ELPP_INTERNAL_DEBUGGING_OUT_ERROR << "ERROR FROM EASYLOGGING++ (LINE: " << __LINE__ << ") " \ - << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) \ - << ELPP_INTERNAL_DEBUGGING_ENDL; \ - if (pe) { \ - ELPP_INTERNAL_DEBUGGING_OUT_ERROR << " "; \ - ELPP_INTERNAL_DEBUGGING_WRITE_PERROR; \ - } \ - } \ - (void)0 -#endif -#else -#undef ELPP_INTERNAL_INFO -#define ELPP_INTERNAL_ERROR(msg, pe) -#endif // defined(ELPP_DEBUG_ERRORS) -#if (defined(ELPP_DEBUG_INFO)) -#if !(defined(ELPP_INTERNAL_INFO_LEVEL)) -#define ELPP_INTERNAL_INFO_LEVEL 9 -#endif // !(defined(ELPP_INTERNAL_INFO_LEVEL)) -#if !defined(ELPP_INTERNAL_INFO) -#define ELPP_INTERNAL_INFO(lvl, msg) \ - { \ - if (lvl <= ELPP_INTERNAL_INFO_LEVEL) { \ - std::stringstream internalInfoStream; \ - internalInfoStream << " " << msg; \ - ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) \ - << ELPP_INTERNAL_DEBUGGING_ENDL; \ - } \ - } -#endif -#else -#undef ELPP_INTERNAL_INFO -#define ELPP_INTERNAL_INFO(lvl, msg) -#endif // (defined(ELPP_DEBUG_INFO)) -#if (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG)) -#if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_OS_ANDROID && !ELPP_OS_EMSCRIPTEN) -#define ELPP_STACKTRACE 1 -#else -#if ELPP_COMPILER_MSVC -#pragma message("Stack trace not available for this compiler") -#else -#warning "Stack trace not available for this compiler"; -#endif // ELPP_COMPILER_MSVC -#define ELPP_STACKTRACE 0 -#endif // ELPP_COMPILER_GCC -#else -#define ELPP_STACKTRACE 0 -#endif // (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG)) -// Miscellaneous macros -#define ELPP_UNUSED(x) (void)x -#if ELPP_OS_UNIX -// Log file permissions for unix-based systems -#define ELPP_LOG_PERMS S_IRUSR | S_IWUSR | S_IXUSR | S_IWGRP | S_IRGRP | S_IXGRP | S_IWOTH | S_IXOTH -#endif // ELPP_OS_UNIX -#if defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC -#if defined(ELPP_EXPORT_SYMBOLS) -#define ELPP_EXPORT __declspec(dllexport) -#else -#define ELPP_EXPORT __declspec(dllimport) -#endif // defined(ELPP_EXPORT_SYMBOLS) -#else -#define ELPP_EXPORT -#endif // defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC -// Some special functions that are VC++ specific -#undef STRTOK -#undef STRERROR -#undef STRCAT -#undef STRCPY -#if ELPP_CRT_DBG_WARNINGS -#define STRTOK(a, b, c) strtok_s(a, b, c) -#define STRERROR(a, b, c) strerror_s(a, b, c) -#define STRCAT(a, b, len) strcat_s(a, len, b) -#define STRCPY(a, b, len) strcpy_s(a, len, b) -#else -#define STRTOK(a, b, c) strtok(a, b) -#define STRERROR(a, b, c) strerror(c) -#define STRCAT(a, b, len) strcat(a, b) -#define STRCPY(a, b, len) strcpy(a, b) -#endif -// Compiler specific support evaluations -#if (ELPP_MINGW && !defined(ELPP_FORCE_USE_STD_THREAD)) -#define ELPP_USE_STD_THREADING 0 -#else -#if ((ELPP_COMPILER_CLANG && defined(ELPP_CLANG_SUPPORTS_THREAD)) || (!ELPP_COMPILER_CLANG && defined(ELPP_CXX11)) || \ - defined(ELPP_FORCE_USE_STD_THREAD)) -#define ELPP_USE_STD_THREADING 1 -#else -#define ELPP_USE_STD_THREADING 0 -#endif -#endif -#undef ELPP_FINAL -#if ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) -#define ELPP_FINAL -#else -#define ELPP_FINAL final -#endif // ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) -#if defined(ELPP_EXPERIMENTAL_ASYNC) -#define ELPP_ASYNC_LOGGING 1 -#else -#define ELPP_ASYNC_LOGGING 0 -#endif // defined(ELPP_EXPERIMENTAL_ASYNC) -#if defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING -#define ELPP_THREADING_ENABLED 1 -#else -#define ELPP_THREADING_ENABLED 0 -#endif // defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING -// Function macro ELPP_FUNC -#undef ELPP_FUNC -#if ELPP_COMPILER_MSVC // Visual C++ -#define ELPP_FUNC __FUNCSIG__ -#elif ELPP_COMPILER_GCC // GCC -#define ELPP_FUNC __PRETTY_FUNCTION__ -#elif ELPP_COMPILER_INTEL // Intel C++ -#define ELPP_FUNC __PRETTY_FUNCTION__ -#elif ELPP_COMPILER_CLANG // Clang++ -#define ELPP_FUNC __PRETTY_FUNCTION__ -#else -#if defined(__func__) -#define ELPP_FUNC __func__ -#else -#define ELPP_FUNC "" -#endif // defined(__func__) -#endif // defined(_MSC_VER) -#undef ELPP_VARIADIC_TEMPLATES_SUPPORTED -// Keep following line commented until features are fixed -#define ELPP_VARIADIC_TEMPLATES_SUPPORTED \ - (ELPP_COMPILER_GCC || ELPP_COMPILER_CLANG || ELPP_COMPILER_INTEL || (ELPP_COMPILER_MSVC && _MSC_VER >= 1800)) -// Logging Enable/Disable macros -#if defined(ELPP_DISABLE_LOGS) -#define ELPP_LOGGING_ENABLED 0 -#else -#define ELPP_LOGGING_ENABLED 1 -#endif -#if (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED)) -#define ELPP_DEBUG_LOG 1 -#else -#define ELPP_DEBUG_LOG 0 -#endif // (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED)) -#if (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) -#define ELPP_INFO_LOG 1 -#else -#define ELPP_INFO_LOG 0 -#endif // (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) -#if (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) -#define ELPP_WARNING_LOG 1 -#else -#define ELPP_WARNING_LOG 0 -#endif // (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) -#if (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) -#define ELPP_ERROR_LOG 1 -#else -#define ELPP_ERROR_LOG 0 -#endif // (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) -#if (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) -#define ELPP_FATAL_LOG 1 -#else -#define ELPP_FATAL_LOG 0 -#endif // (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) -#if (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) -#define ELPP_TRACE_LOG 1 -#else -#define ELPP_TRACE_LOG 0 -#endif // (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) -#if (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) -#define ELPP_VERBOSE_LOG 1 -#else -#define ELPP_VERBOSE_LOG 0 -#endif // (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) -#if (!(ELPP_CXX0X || ELPP_CXX11)) -#error "C++0x (or higher) support not detected! (Is `-std=c++11' missing?)" -#endif // (!(ELPP_CXX0X || ELPP_CXX11)) -// Headers -#if defined(ELPP_SYSLOG) -#include -#endif // defined(ELPP_SYSLOG) -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(ELPP_UNICODE) -#include -#if ELPP_OS_WINDOWS -#include -#endif // ELPP_OS_WINDOWS -#endif // defined(ELPP_UNICODE) -#if ELPP_STACKTRACE -#include -#include -#endif // ELPP_STACKTRACE -#if ELPP_OS_ANDROID -#include -#endif // ELPP_OS_ANDROID -#if ELPP_OS_UNIX -#include -#include -#elif ELPP_OS_WINDOWS -#include -#include -#if defined(WIN32_LEAN_AND_MEAN) -#if defined(ELPP_WINSOCK2) -#include -#else -#include -#endif // defined(ELPP_WINSOCK2) -#endif // defined(WIN32_LEAN_AND_MEAN) -#endif // ELPP_OS_UNIX -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if ELPP_THREADING_ENABLED -#if ELPP_USE_STD_THREADING -#include -#include -#else -#if ELPP_OS_UNIX -#include -#endif // ELPP_OS_UNIX -#endif // ELPP_USE_STD_THREADING -#endif // ELPP_THREADING_ENABLED -#if ELPP_ASYNC_LOGGING -#if defined(ELPP_NO_SLEEP_FOR) -#include -#endif // defined(ELPP_NO_SLEEP_FOR) -#include -#include -#include -#endif // ELPP_ASYNC_LOGGING -#if defined(ELPP_STL_LOGGING) -// For logging STL based templates -#include -#include -#include -#include -#include -#include -#if defined(ELPP_LOG_STD_ARRAY) -#include -#endif // defined(ELPP_LOG_STD_ARRAY) -#if defined(ELPP_LOG_UNORDERED_SET) -#include -#endif // defined(ELPP_UNORDERED_SET) -#endif // defined(ELPP_STL_LOGGING) -#if defined(ELPP_QT_LOGGING) -// For logging Qt based classes & templates -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif // defined(ELPP_QT_LOGGING) -#if defined(ELPP_BOOST_LOGGING) -// For logging boost based classes & templates -#include -#include -#include -#include -#include -#include -#include -#include -#endif // defined(ELPP_BOOST_LOGGING) -#if defined(ELPP_WXWIDGETS_LOGGING) -// For logging wxWidgets based classes & templates -#include -#endif // defined(ELPP_WXWIDGETS_LOGGING) -#if defined(ELPP_UTC_DATETIME) -#define elpptime_r gmtime_r -#define elpptime_s gmtime_s -#define elpptime gmtime -#else -#define elpptime_r localtime_r -#define elpptime_s localtime_s -#define elpptime localtime -#endif // defined(ELPP_UTC_DATETIME) -// Forward declarations -namespace el { -class Logger; -class LogMessage; -class PerformanceTrackingData; -class Loggers; -class Helpers; -template -class Callback; -class LogDispatchCallback; -class PerformanceTrackingCallback; -class LoggerRegistrationCallback; -class LogDispatchData; -namespace base { -class Storage; -class RegisteredLoggers; -class PerformanceTracker; -class MessageBuilder; -class Writer; -class PErrorWriter; -class LogDispatcher; -class DefaultLogBuilder; -class DefaultLogDispatchCallback; -#if ELPP_ASYNC_LOGGING -class AsyncLogDispatchCallback; -class AsyncDispatchWorker; -#endif // ELPP_ASYNC_LOGGING -class DefaultPerformanceTrackingCallback; -} // namespace base -} // namespace el -/// @brief Easylogging++ entry namespace -namespace el { -/// @brief Namespace containing base/internal functionality used by Easylogging++ -namespace base { -/// @brief Data types used by Easylogging++ -namespace type { -#undef ELPP_LITERAL -#undef ELPP_STRLEN -#undef ELPP_COUT -#if defined(ELPP_UNICODE) -#define ELPP_LITERAL(txt) L##txt -#define ELPP_STRLEN wcslen -#if defined ELPP_CUSTOM_COUT -#define ELPP_COUT ELPP_CUSTOM_COUT -#else -#define ELPP_COUT std::wcout -#endif // defined ELPP_CUSTOM_COUT -typedef wchar_t char_t; -typedef std::wstring string_t; -typedef std::wstringstream stringstream_t; -typedef std::wfstream fstream_t; -typedef std::wostream ostream_t; -#else -#define ELPP_LITERAL(txt) txt -#define ELPP_STRLEN strlen -#if defined ELPP_CUSTOM_COUT -#define ELPP_COUT ELPP_CUSTOM_COUT -#else -#define ELPP_COUT std::cout -#endif // defined ELPP_CUSTOM_COUT -typedef char char_t; -typedef std::string string_t; -typedef std::stringstream stringstream_t; -typedef std::fstream fstream_t; -typedef std::ostream ostream_t; -#endif // defined(ELPP_UNICODE) -#if defined(ELPP_CUSTOM_COUT_LINE) -#define ELPP_COUT_LINE(logLine) ELPP_CUSTOM_COUT_LINE(logLine) -#else -#define ELPP_COUT_LINE(logLine) logLine << std::flush -#endif // defined(ELPP_CUSTOM_COUT_LINE) -typedef unsigned int EnumType; -typedef unsigned short VerboseLevel; -typedef unsigned long int LineNumber; -typedef std::shared_ptr StoragePointer; -typedef std::shared_ptr LogDispatchCallbackPtr; -typedef std::shared_ptr PerformanceTrackingCallbackPtr; -typedef std::shared_ptr LoggerRegistrationCallbackPtr; -typedef std::unique_ptr PerformanceTrackerPtr; -} // namespace type -/// @brief Internal helper class that prevent copy constructor for class -/// -/// @detail When using this class simply inherit it privately -class NoCopy { - protected: - NoCopy(void) { - } - - private: - NoCopy(const NoCopy&); - NoCopy& - operator=(const NoCopy&); -}; -/// @brief Internal helper class that makes all default constructors private. -/// -/// @detail This prevents initializing class making it static unless an explicit constructor is declared. -/// When using this class simply inherit it privately -class StaticClass { - private: - StaticClass(void); - StaticClass(const StaticClass&); - StaticClass& - operator=(const StaticClass&); -}; -} // namespace base -/// @brief Represents enumeration for severity level used to determine level of logging -/// -/// @detail With Easylogging++, developers may disable or enable any level regardless of -/// what the severity is. Or they can choose to log using hierarchical logging flag -enum class Level : base::type::EnumType { - /// @brief Generic level that represents all the levels. Useful when setting global configuration for all levels - Global = 1, - /// @brief Information that can be useful to back-trace certain events - mostly useful than debug logs. - Trace = 2, - /// @brief Informational events most useful for developers to debug application - Debug = 4, - /// @brief Severe error information that will presumably abort application - Fatal = 8, - /// @brief Information representing errors in application but application will keep running - Error = 16, - /// @brief Useful when application has potentially harmful situtaions - Warning = 32, - /// @brief Information that can be highly useful and vary with verbose logging level. - Verbose = 64, - /// @brief Mainly useful to represent current progress of application - Info = 128, - /// @brief Represents unknown level - Unknown = 1010 -}; -} // namespace el -namespace std { -template <> -struct hash { - public: - std::size_t - operator()(const el::Level& l) const { - return hash{}(static_cast(l)); - } -}; -} // namespace std -namespace el { -/// @brief Static class that contains helper functions for el::Level -class LevelHelper : base::StaticClass { - public: - /// @brief Represents minimum valid level. Useful when iterating through enum. - static const base::type::EnumType kMinValid = static_cast(Level::Trace); - /// @brief Represents maximum valid level. This is used internally and you should not need it. - static const base::type::EnumType kMaxValid = static_cast(Level::Info); - /// @brief Casts level to int, useful for iterating through enum. - static base::type::EnumType - castToInt(Level level) { - return static_cast(level); - } - /// @brief Casts int(ushort) to level, useful for iterating through enum. - static Level - castFromInt(base::type::EnumType l) { - return static_cast(l); - } - /// @brief Converts level to associated const char* - /// @return Upper case string based level. - static const char* - convertToString(Level level); - /// @brief Converts from levelStr to Level - /// @param levelStr Upper case string based level. - /// Lower case is also valid but providing upper case is recommended. - static Level - convertFromString(const char* levelStr); - /// @brief Applies specified function to each level starting from startIndex - /// @param startIndex initial value to start the iteration from. This is passed as pointer and - /// is left-shifted so this can be used inside function (fn) to represent current level. - /// @param fn function to apply with each level. This bool represent whether or not to stop iterating through - /// levels. - static void - forEachLevel(base::type::EnumType* startIndex, const std::function& fn); -}; -/// @brief Represents enumeration of ConfigurationType used to configure or access certain aspect -/// of logging -enum class ConfigurationType : base::type::EnumType { - /// @brief Determines whether or not corresponding level and logger of logging is enabled - /// You may disable all logs by using el::Level::Global - Enabled = 1, - /// @brief Whether or not to write corresponding log to log file - ToFile = 2, - /// @brief Whether or not to write corresponding level and logger log to standard output. - /// By standard output meaning termnal, command prompt etc - ToStandardOutput = 4, - /// @brief Determines format of logging corresponding level and logger. - Format = 8, - /// @brief Determines log file (full path) to write logs to for correponding level and logger - Filename = 16, - /// @brief Specifies precision of the subsecond part. It should be within range (1-6). - SubsecondPrecision = 32, - /// @brief Alias of SubsecondPrecision (for backward compatibility) - MillisecondsWidth = SubsecondPrecision, - /// @brief Determines whether or not performance tracking is enabled. - /// - /// @detail This does not depend on logger or level. Performance tracking always uses 'performance' logger - PerformanceTracking = 64, - /// @brief Specifies log file max size. - /// - /// @detail If file size of corresponding log file (for corresponding level) is >= specified size, log file will - /// be truncated and re-initiated. - MaxLogFileSize = 128, - /// @brief Specifies number of log entries to hold until we flush pending log data - LogFlushThreshold = 256, - /// @brief Represents unknown configuration - Unknown = 1010 -}; -/// @brief Static class that contains helper functions for el::ConfigurationType -class ConfigurationTypeHelper : base::StaticClass { - public: - /// @brief Represents minimum valid configuration type. Useful when iterating through enum. - static const base::type::EnumType kMinValid = static_cast(ConfigurationType::Enabled); - /// @brief Represents maximum valid configuration type. This is used internally and you should not need it. - static const base::type::EnumType kMaxValid = static_cast(ConfigurationType::MaxLogFileSize); - /// @brief Casts configuration type to int, useful for iterating through enum. - static base::type::EnumType - castToInt(ConfigurationType configurationType) { - return static_cast(configurationType); - } - /// @brief Casts int(ushort) to configurationt type, useful for iterating through enum. - static ConfigurationType - castFromInt(base::type::EnumType c) { - return static_cast(c); - } - /// @brief Converts configuration type to associated const char* - /// @returns Upper case string based configuration type. - static const char* - convertToString(ConfigurationType configurationType); - /// @brief Converts from configStr to ConfigurationType - /// @param configStr Upper case string based configuration type. - /// Lower case is also valid but providing upper case is recommended. - static ConfigurationType - convertFromString(const char* configStr); - /// @brief Applies specified function to each configuration type starting from startIndex - /// @param startIndex initial value to start the iteration from. This is passed by pointer and is left-shifted - /// so this can be used inside function (fn) to represent current configuration type. - /// @param fn function to apply with each configuration type. - /// This bool represent whether or not to stop iterating through configurations. - static inline void - forEachConfigType(base::type::EnumType* startIndex, const std::function& fn); -}; -/// @brief Flags used while writing logs. This flags are set by user -enum class LoggingFlag : base::type::EnumType { - /// @brief Makes sure we have new line for each container log entry - NewLineForContainer = 1, - /// @brief Makes sure if -vmodule is used and does not specifies a module, then verbose - /// logging is allowed via that module. - AllowVerboseIfModuleNotSpecified = 2, - /// @brief When handling crashes by default, detailed crash reason will be logged as well - LogDetailedCrashReason = 4, - /// @brief Allows to disable application abortion when logged using FATAL level - DisableApplicationAbortOnFatalLog = 8, - /// @brief Flushes log with every log-entry (performance sensative) - Disabled by default - ImmediateFlush = 16, - /// @brief Enables strict file rolling - StrictLogFileSizeCheck = 32, - /// @brief Make terminal output colorful for supported terminals - ColoredTerminalOutput = 64, - /// @brief Supports use of multiple logging in same macro, e.g, CLOG(INFO, "default", "network") - MultiLoggerSupport = 128, - /// @brief Disables comparing performance tracker's checkpoints - DisablePerformanceTrackingCheckpointComparison = 256, - /// @brief Disable VModules - DisableVModules = 512, - /// @brief Disable VModules extensions - DisableVModulesExtensions = 1024, - /// @brief Enables hierarchical logging - HierarchicalLogging = 2048, - /// @brief Creates logger automatically when not available - CreateLoggerAutomatically = 4096, - /// @brief Adds spaces b/w logs that separated by left-shift operator - AutoSpacing = 8192, - /// @brief Preserves time format and does not convert it to sec, hour etc (performance tracking only) - FixedTimeFormat = 16384, - // @brief Ignore SIGINT or crash - IgnoreSigInt = 32768, -}; -namespace base { -/// @brief Namespace containing constants used internally. -namespace consts { -static const char kFormatSpecifierCharValue = 'v'; -static const char kFormatSpecifierChar = '%'; -static const unsigned int kMaxLogPerCounter = 100000; -static const unsigned int kMaxLogPerContainer = 100; -static const unsigned int kDefaultSubsecondPrecision = 3; - -#ifdef ELPP_DEFAULT_LOGGER -static const char* kDefaultLoggerId = ELPP_DEFAULT_LOGGER; -#else -static const char* kDefaultLoggerId = "default"; -#endif - -#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) -#ifdef ELPP_DEFAULT_PERFORMANCE_LOGGER -static const char* kPerformanceLoggerId = ELPP_DEFAULT_PERFORMANCE_LOGGER; -#else -static const char* kPerformanceLoggerId = "performance"; -#endif // ELPP_DEFAULT_PERFORMANCE_LOGGER -#endif - -#if defined(ELPP_SYSLOG) -static const char* kSysLogLoggerId = "syslog"; -#endif // defined(ELPP_SYSLOG) - -#if ELPP_OS_WINDOWS -static const char* kFilePathSeperator = "\\"; -#else -static const char* kFilePathSeperator = "/"; -#endif // ELPP_OS_WINDOWS - -static const std::size_t kSourceFilenameMaxLength = 100; -static const std::size_t kSourceLineMaxLength = 10; -static const Level kPerformanceTrackerDefaultLevel = Level::Info; -const struct { - double value; - const base::type::char_t* unit; -} kTimeFormats[] = {{1000.0f, ELPP_LITERAL("us")}, {1000.0f, ELPP_LITERAL("ms")}, {60.0f, ELPP_LITERAL("seconds")}, - {60.0f, ELPP_LITERAL("minutes")}, {24.0f, ELPP_LITERAL("hours")}, {7.0f, ELPP_LITERAL("days")}}; -static const int kTimeFormatsCount = sizeof(kTimeFormats) / sizeof(kTimeFormats[0]); -const struct { - int numb; - const char* name; - const char* brief; - const char* detail; -} kCrashSignals[] = { - // NOTE: Do not re-order, if you do please check CrashHandler(bool) constructor and CrashHandler::setHandler(..) - {SIGABRT, "SIGABRT", "Abnormal termination", "Program was abnormally terminated."}, - {SIGFPE, "SIGFPE", "Erroneous arithmetic operation", - "Arithemetic operation issue such as division by zero or operation resulting in overflow."}, - {SIGILL, "SIGILL", "Illegal instruction", - "Generally due to a corruption in the code or to an attempt to execute data."}, - {SIGSEGV, "SIGSEGV", "Invalid access to memory", - "Program is trying to read an invalid (unallocated, deleted or corrupted) or inaccessible memory."}, - {SIGINT, "SIGINT", "Interactive attention signal", - "Interruption generated (generally) by user or operating system."}, -}; -static const int kCrashSignalsCount = sizeof(kCrashSignals) / sizeof(kCrashSignals[0]); -} // namespace consts -} // namespace base -typedef std::function PreRollOutCallback; -namespace base { -static inline void -defaultPreRollOutCallback(const char*, std::size_t, Level level) { -} -/// @brief Enum to represent timestamp unit -enum class TimestampUnit : base::type::EnumType { - Microsecond = 0, - Millisecond = 1, - Second = 2, - Minute = 3, - Hour = 4, - Day = 5 -}; -/// @brief Format flags used to determine specifiers that are active for performance improvements. -enum class FormatFlags : base::type::EnumType { - DateTime = 1 << 1, - LoggerId = 1 << 2, - File = 1 << 3, - Line = 1 << 4, - Location = 1 << 5, - Function = 1 << 6, - User = 1 << 7, - Host = 1 << 8, - LogMessage = 1 << 9, - VerboseLevel = 1 << 10, - AppName = 1 << 11, - ThreadId = 1 << 12, - Level = 1 << 13, - FileBase = 1 << 14, - LevelShort = 1 << 15 -}; -/// @brief A subsecond precision class containing actual width and offset of the subsecond part -class SubsecondPrecision { - public: - SubsecondPrecision(void) { - init(base::consts::kDefaultSubsecondPrecision); - } - explicit SubsecondPrecision(int width) { - init(width); - } - bool - operator==(const SubsecondPrecision& ssPrec) { - return m_width == ssPrec.m_width && m_offset == ssPrec.m_offset; - } - int m_width; - unsigned int m_offset; - - private: - void - init(int width); -}; -/// @brief Type alias of SubsecondPrecision -typedef SubsecondPrecision MillisecondsWidth; -/// @brief Namespace containing utility functions/static classes used internally -namespace utils { -/// @brief Deletes memory safely and points to null -template -static typename std::enable_if::value, void>::type -safeDelete(T*& pointer) { - if (pointer == nullptr) - return; - delete pointer; - pointer = nullptr; -} -/// @brief Bitwise operations for C++11 strong enum class. This casts e into Flag_T and returns value after bitwise -/// operation Use these function as

)D3D6aeRS^1gx}*UsKC(VBek<^B64 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