feature vector search (FV)

Principles

This function is used to verify the feature retrieval function, generate a random base library, and randomly generate feature data for feature retrieval. (Currently, the 1:N and M:N retrieval modes are supported. The following uses the 1:N retrieval mode as an example.) The process can be broken into the following steps: initialization, adding features to the repository, repository search, precise modification or deletion of the features in the repository, and deinitialization. The API calls are described as follows:

  • Initialization: Call aclInit for initialization and aclfvCreateInitPara to create data of the aclfvInitPara type to specify the initialization parameters for feature vector search.
  • Add a feature to the base library: Call aclfvCreateFeatureInfo to create data of the aclfvFeatureInfo type to describe the feature, and then call aclfvRepoAdd to add a base library.
  • Base library search: The aclfvSearch API is called to implement search.
  • Precisely modifying or deleting a base library feature: Call the aclfvDel and aclfvModify APIs to delete or modify a feature in the base library. The following code uses feature deletion as an example.
  • Deinitialization: releases runtime resources, calls the aclfvDestroyInitPara API to destroy data of the aclfvInitPara type, and calls the aclfvRelease API to deinitialize the feature retrieval module and release memory space.

Sample Code

The example in this section focuses on the code logic of feature vector search. It is for reference only and cannot be directly copied, compiled, and run. To view the complete sample code, click Link.

After APIs are called, you need to add exception handling branches and record error logs and info logs.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
//Set the default run mode to ACL_HOST.
aclrtRunMode runMode = ACL_HOST;

// 1. Perform initialization.
// 1.1 Initialize the feature retrieval module. The following uses the number of features in the base library (100000) as an example.
size_t fsNum = 100000;
fvInitPara = aclfvCreateInitPara(fsNum);

// 1.2 Specify the initialization parameters for feature vector search.
ret = aclfvInit(fvInitPara);

// 2. Add the base library and feature vectors.
// 2.1 Add the first feature. When creating the feature description, set the offset parameter to 0.
uint32_t offset = 0;
uint32_t featureCount = 1000;
uint32_t featureLen = 36;

//The user-defined function BaseShortFeaAlloc is used to generate random feature data.
void *featureData = BaseShortFeaAlloc(1000, static_cast<size_t>(featureCount), 0);
std::shared_ptr<void> feaBufPtr(featureData, [](void *p){(void)aclrtFreeHost(p);});
void *inputData = featureData;
std::shared_ptr<void> inputDataPtr = nullptr;

// If the run mode is ACL_HOST, allocate memory and call aclrtMemcpy to transfer the random feature data of the host to the device. Otherwise, directly read the random feature data into the device memory.
if (aclrtGetRunMode(&runMode) == ACL_HOST) {
      //Allocate memory for input data.
      ret = aclrtMalloc(&inputData, featureLen * featureCount, 
                        ACL_MEM_MALLOC_HUGE_FIRST);
      //Read the random feature data to the device memory.
      inputDataPtr.reset(inputData, [](void *p) {(void)aclrtFree(p);});
      //Copy the feature data from the host to the device.
      ret = aclrtMemcpy(inputData,
                        featureLen * featureCount,
                        featureData,
                        featureLen * featureCount,
                        ACL_MEMCPY_HOST_TO_DEVICE);
}

// Create a feature description. inputData indicates the random feature data in the previous step.
auto featureInfo = aclfvCreateFeatureInfo(id0, id1, offset, featureLen, featureCount, 
                        reinterpret_cast<uint8_t *>(inputData), featureLen * featureCount);

// Add a base library and add features to the base library. featureInfo indicates the feature description in the previous step.
aclError ret = aclfvRepoAdd(SEARCH_1_N, featureInfo);

// Destroy the aclfvFeatureInfo feature description.
aclfvDestroyFeatureInfo(featureInfo);

// 2.2 Add the second feature. When creating the feature description information, ensure that the offset value is the same as the number of added features in the library and precisely delete or modify a feature in the base library.
offset += featureCount;

//For details about how to add a feature to the repository, see step 4.1.
// ....

uint8_t featureData[36];
for (size_t i = 0; i < 36; i++) {
        featureData[i] = static_cast<uint8_t>(i);
}

// Create memory and transfer feature data.
void *inputData = nullptr;
aclrtMalloc(&inputData, 36, ACL_MEM_MALLOC_HUGE_FIRST);
std::shared_ptr<void> inputDataPtr(inputData, [](void *p){(void)aclrtFree(p);});
aclrtMemcpyKind kind = ACL_MEMCPY_DEVICE_TO_DEVICE;

// If the running mode is ACL_HOST, copy the feature data to the device. Otherwise, you do not need to copy the feature data. dataLen indicates the length of the memory allocated by the featureData pointer.
if (aclrtGetRunMode(&runMode) == ACL_HOST) {
        kind = ACL_MEMCPY_HOST_TO_DEVICE;
}
aclrtMemcpy(inputData, 36, featureData, dataLen, kind);

// Create feature description information.
uint32_t id0 = 0;
uint32_t id1 = 0;
auto featureInfo1 = aclfvCreateFeatureInfo(id0, id1, offset, 36, 1,
                                          reinterpret_cast<uint8_t *>(inputData), 36);
std::shared_ptr<aclfvFeatureInfo> featureInfoPtr(featureInfo1,
                                          [](aclfvFeatureInfo *p){(void)aclfvDestroyFeatureInfo(p);});

//Delete a feature.
aclfvDel(featureInfo1);

// 2.3 Add features to other base libraries. The value is 1 for the level-1 base library and 1 for the level-2 base library.
id0 = 1;
id1 = 1;
offset = 0; 

// Add features to the library. For details, see the code in section 2.1.
// ....

// 3 Base library search (1:N search is used as an example), including feature search preprocessing, 1:N feature search, and feature search result processing.
// 3.1 Feature retrieval preprocessing. For 1:N, queryCnt must be 1.
uint32_t queryCnt = 1;
uint32_t topK = 5;
uint32_t dataLen = queryCnt * topK * sizeof(uint32_t);
uint32_t resultNumDataLen = queryCnt * sizeof(uint32_t);
const uint32_t tableLen = 32 * 1024; 
uint32_t tableDataLen = queryCnt * tableLen;

// Generate a data table. You can search for and compare data based on the data table. The user-defined function AdcTabInit is used to initialize the input ADC table for feature retrieval.
uint8_t *tableDataTmp = (uint8_t *)AdcTabInit(1000, queryCnt * 1024);
std::shared_ptr<void> tableDataTmpPtr(tableDataTmp,[](void *p){(void)aclrtFreeHost(p);});

// Allocate memory for the data table. tableDataDev is used to create the retrieval input table information.
void *devPtr = nullptr;
aclrtMalloc(&devPtr, tableDataLen, ACL_MEM_MALLOC_HUGE_FIRST);
tableDataDev.reset(devPtr, [](void *p) {(void)aclrtFree(p);});

// Copy the table data to the device.
uint8_t *devPtrTmp = reinterpret_cast<uint8_t *>(devPtr);
for (uint32_t i = 0; i < queryCnt; ++i) {
     for (uint32_t j = 0; j < 32; ++j) {
          uint8_t *dst = devPtrTmp + i * 32 * 1024 + j * 1024;
          uint8_t *src = tableDataTmp + i * 1024;
          aclrtMemcpy(dst, 1024, src, 1024, ACL_MEMCPY_HOST_TO_DEVICE);
     }
}

//Allocate memory for the search result resultNumDev, id0Dev, id1Dev, resultOffsetDev, and resultDistanceDev.
aclrtMalloc(&devPtr, resultNumDataLen, ACL_MEM_MALLOC_HUGE_FIRST);
resultNumDev.reset(devPtr, [](void *p) {(void)aclrtFree(p);});
aclrtMalloc(&devPtr, dataLen, ACL_MEM_MALLOC_HUGE_FIRST);
id0Dev.reset(devPtr, [](void *p) {(void)aclrtFree(p);});
aclrtMalloc(&devPtr, dataLen, ACL_MEM_MALLOC_HUGE_FIRST);
id1Dev.reset(devPtr, [](void *p) {(void)aclrtFree(p);});
aclrtMalloc(&devPtr, dataLen, ACL_MEM_MALLOC_HUGE_FIRST);              
resultOffsetDev.reset(devPtr, [](void *p) {(void)aclrtFree(p);});
aclrtMalloc(&devPtr, dataLen, ACL_MEM_MALLOC_HUGE_FIRST);
resultDistanceDev.reset(devPtr, [](void *p) {(void)aclrtFree(p);}); 
 
//Create a search input table. The result is used as the input information for creating a search task.
aclfvQueryTable *searchQueryTable = aclfvCreateQueryTable(queryCnt, tableLen, reinterpret_cast<uint8_t *>
                                   (tableDataDev.get()), tableDataLen);
searchQueryTable.reset(searchQueryTable, [](aclfvQueryTable *p){(void)aclfvDestroyQueryTable(p);});

//Create a feature repository range parameter. The result is used as the input information for creating a search task.
aclfvRepoRange *searchRange = aclfvCreateRepoRange(0, 1023, 0, 1023); 
searchRange.reset(searchRange, [](aclfvRepoRange *p){(void)aclfvDestroyRepoRange(p);});

// Create a search task. The result is used for 1:N feature search.
aclfvSearchInput *searchInput = aclfvCreateSearchInput(searchQueryTable, searchRange, topK);
searchInput.reset(searchInput, [](aclfvSearchInput *p){(void)aclfvDestroySearchInput(p);});

// Create a search result for 1:N feature search.
aclfvSearchResult *searchResult = aclfvCreateSearchResult(queryCnt,
                                   reinterpret_cast<uint32_t *>(resultNumDev.get()),
                                   resultNumDataLen,
                                   reinterpret_cast<uint32_t *>(id0Dev.get()),
                                   reinterpret_cast<uint32_t *>(id1Dev.get()),
                                   reinterpret_cast<uint32_t *>(resultOffsetDev.get()),
                                   reinterpret_cast<float *>(resultDistanceDev.get()),
                                   dataLen);
searchResult.reset(searchResult, [](aclfvSearchResult *p){(void)aclfvDestroySearchResult(p);});

// 3.2 1:N feature search
aclfvSearch(SEARCH_1_N, searchInput.get(), searchResult.get());

// 3.3 Process the feature retrieval result.
// Obtain the search result.
uint32_t dataLen = queryCnt * topK * sizeof(uint32_t);
uint32_t *id0 = (uint32_t *)id0Dev.get();
uint32_t *id1 = (uint32_t *)id1Dev.get();
uint32_t *resultOffset= (uint32_t *)resultOffsetDev.get();
float *resultDistance = (float *)resultDistanceDev.get();

//If the run mode is ACL_HOST, the search result on the device needs to be sent back to the host through the aclrtMemcpy call.
if (aclrtGetRunMode(&runMode) == ACL_HOST) {
// Copy data from the device to the host.
        id0 = (uint32_t *)malloc(dataLen);
        id0Ptr.reset(id0);
        id1 = (uint32_t *)malloc(dataLen);
        id1Ptr.reset(id1);
        resultOffset = (uint32_t *)malloc(dataLen);
        resultOffsetPtr.reset(resultOffset);
        resultDistance = (float *)malloc(dataLen);
        resultDistancePtr.reset(resultDistance);
        aclrtMemcpy(id0, dataLen, id0Dev.get(), dataLen, ACL_MEMCPY_DEVICE_TO_HOST);
        aclrtMemcpy(id1, dataLen, id0Dev.get(), dataLen, ACL_MEMCPY_DEVICE_TO_HOST);
        aclrtMemcpy(resultOffset, dataLen, resultOffsetDev.get(), dataLen, ACL_MEMCPY_DEVICE_TO_HOST);
        aclrtMemcpy(resultDistance, dataLen, resultDistanceDev.get(), dataLen, ACL_MEMCPY_DEVICE_TO_HOST);
}

 // Display data in the base library.
for (uint32_t i = 0; i < queryCnt; i++) {
     for (uint32_t j = 0; j < topK; ++j) {
          uint32_t i0 = id0[i * topK + j];
          uint32_t i1 = id1[i * topK + j];
          uint32_t offset = resultOffset[i * topK + j];
          float distance = resultDistance[i * topK + j];
    }
}

// 4. Delete the base library and data.
//Create a feature repository range and delete the repository in the specified range.
uint32_t id0Min = 0;
uint32_t id0Max = 1023;
uint32_t id1Min = 0;
uint32_t id1Max = 1023;
aclfvRepoRange *repoRange = aclfvCreateRepoRange(id0Min, id0Max, id1Min, id1Max);
aclfvRepoDel(SEARCH_1_N, repoRange);
//Destroy data of the aclfvInitPara type.
aclfvDestroyInitPara(fvInitPara);

......