Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge master #4

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AnnService/IndexBuilder.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<PreprocessorDefinitions>_MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<OpenMPSupport>true</OpenMPSupport>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
Expand All @@ -115,6 +116,7 @@
<PreprocessorDefinitions>_MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ControlFlowGuard>Guard</ControlFlowGuard>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<OpenMPSupport>true</OpenMPSupport>
</ClCompile>
<Link>
<AdditionalOptions>/guard:cf %(AdditionalOptions)</AdditionalOptions>
Expand Down
2 changes: 2 additions & 0 deletions AnnService/IndexSearcher.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<PreprocessorDefinitions>_MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<OpenMPSupport>true</OpenMPSupport>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
Expand All @@ -116,6 +117,7 @@
<PreprocessorDefinitions>_MBCS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ControlFlowGuard>Guard</ControlFlowGuard>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<OpenMPSupport>true</OpenMPSupport>
</ClCompile>
<Link>
<AdditionalOptions>/guard:cf %(AdditionalOptions)</AdditionalOptions>
Expand Down
21 changes: 10 additions & 11 deletions AnnService/inc/Core/BKT/Index.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,16 @@ namespace SPTAG
int m_iNumberOfInitialDynamicPivots;
int m_iNumberOfOtherDynamicPivots;
public:
Index() :
m_sBKTFilename("tree.bin"),
m_sGraphFilename("graph.bin"),
m_sDataPointsFilename("vectors.bin"),
m_iNumberOfThreads(1),
m_iDistCalcMethod(DistCalcMethod::Cosine),
m_fComputeDistance(COMMON::DistanceCalcSelector<T>(DistCalcMethod::Cosine)),
m_iMaxCheck(2048),
m_iThresholdOfNumberOfContinuousNoBetterPropagation(3),
m_iNumberOfInitialDynamicPivots(50),
m_iNumberOfOtherDynamicPivots(4) {}
Index()
{
#define DefineBKTParameter(VarName, VarType, DefaultValue, RepresentStr) \
VarName = DefaultValue; \

#include "inc/Core/BKT/ParameterDefinitionList.h"
#undef DefineBKTParameter

m_fComputeDistance = COMMON::DistanceCalcSelector<T>(m_iDistCalcMethod);
}

~Index() {}

Expand Down
133 changes: 133 additions & 0 deletions AnnService/inc/Core/Common/DistanceUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,33 @@ namespace SPTAG
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));
Expand Down Expand Up @@ -118,6 +145,31 @@ namespace SPTAG

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_epi8(__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));
Expand Down Expand Up @@ -206,6 +258,47 @@ namespace SPTAG
return diff;
}

static float ComputeL2Distance(const std::uint8_t *pX, const std::uint8_t *pY, int 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, int length)
{
const std::int16_t* pEnd16 = pX + ((length >> 4) << 4);
Expand Down Expand Up @@ -343,6 +436,46 @@ namespace SPTAG
return 16129 - diff;
}

static float ComputeCosineDistance(const std::uint8_t *pX, const std::uint8_t *pY, int 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 16129 - diff;
}

static float ComputeCosineDistance(const std::int16_t *pX, const std::int16_t *pY, int length) {
const std::int16_t* pEnd16 = pX + ((length >> 4) << 4);
const std::int16_t* pEnd8 = pX + ((length >> 3) << 3);
Expand Down
4 changes: 2 additions & 2 deletions AnnService/inc/Core/Common/NeighborhoodGraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ namespace SPTAG
m_iSamples(1000),
m_numTopDimensionTPTSplit(5),
m_iNeighborhoodSize(32),
m_iNeighborhoodScale(16),
m_iCEFScale(4),
m_iNeighborhoodScale(2),
m_iCEFScale(2),
m_iRefineIter(0),
m_iCEF(1000),
m_iMaxCheckForRefineGraph(10000) {}
Expand Down
1 change: 1 addition & 0 deletions AnnService/inc/Core/DefinitionList.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#ifdef DefineVectorValueType

DefineVectorValueType(Int8, std::int8_t)
DefineVectorValueType(UInt8, std::uint8_t)
DefineVectorValueType(Int16, std::int16_t)
DefineVectorValueType(Float, float)

Expand Down
21 changes: 10 additions & 11 deletions AnnService/inc/Core/KDT/Index.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,16 @@ namespace SPTAG
int m_iNumberOfInitialDynamicPivots;
int m_iNumberOfOtherDynamicPivots;
public:
Index() :
m_sKDTFilename("tree.bin"),
m_sGraphFilename("graph.bin"),
m_sDataPointsFilename("vectors.bin"),
m_iNumberOfThreads(1),
m_iDistCalcMethod(DistCalcMethod::Cosine),
m_fComputeDistance(COMMON::DistanceCalcSelector<T>(DistCalcMethod::Cosine)),
m_iMaxCheck(2048),
m_iThresholdOfNumberOfContinuousNoBetterPropagation(3),
m_iNumberOfInitialDynamicPivots(50),
m_iNumberOfOtherDynamicPivots(4) {}
Index()
{
#define DefineKDTParameter(VarName, VarType, DefaultValue, RepresentStr) \
VarName = DefaultValue; \

#include "inc/Core/KDT/ParameterDefinitionList.h"
#undef DefineKDTParameter

m_fComputeDistance = COMMON::DistanceCalcSelector<T>(m_iDistCalcMethod);
}

~Index() {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class DefaultReader : public VectorSetReader
++next;
}

bool reachEnd = '\0' == (*next);
bool reachEnd = ('\0' == (*next));
*next = '\0';
if (p_str != next)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ DefaultReader::GetVectorSet() const

std::ifstream inputStream;
inputStream.open(m_vectorOutput, std::ifstream::binary);
inputStream.seekg(sizeof(uint32_t) + sizeof(uint32_t), std::ifstream::beg);
inputStream.read(vecBuf, m_totalRecordVectorBytes);
inputStream.close();

Expand Down Expand Up @@ -399,8 +400,13 @@ DefaultReader::MergeData()
std::unique_ptr<char[]> bufferHolder(new char[bufferSize]);
char* buf = bufferHolder.get();

std::uint32_t uint32Var = m_totalRecordCount;

outputStream.open(m_vectorOutput, std::ofstream::binary);

outputStream.write(reinterpret_cast<char*>(&uint32Var), sizeof(uint32Var));
outputStream.write(reinterpret_cast<char*>(&(m_options->m_dimension)), sizeof(m_options->m_dimension));

for (std::uint32_t i = 0; i < m_subTaskCount; ++i)
{
std::string file = m_vectorOutput;
Expand Down Expand Up @@ -436,7 +442,6 @@ DefaultReader::MergeData()

outputStream.open(m_metadataIndexOutput, std::ofstream::binary);

std::uint32_t uint32Var = m_totalRecordCount;
outputStream.write(reinterpret_cast<char*>(&uint32Var), sizeof(uint32Var));

std::uint64_t totalOffset = 0;
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![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 scenerio, which is written in C++ and wrapped by Python.
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).

<p align="center">
<img src="docs/img/sptag.png" alt="architecture" width="500"/>
Expand All @@ -18,16 +18,16 @@ This library assumes that the samples are represented as vectors and that the ve
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 relatrive neighborhood graph (SPTAG-BKT).
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 conectivity. 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 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.
Expand Down