310 lines
10 KiB
C
310 lines
10 KiB
C
// Copyright 2025-present the zvec project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include "zvec/c_api.h"
|
|
|
|
/**
|
|
* @brief Print error message and return error code
|
|
*/
|
|
static zvec_error_code_t handle_error(zvec_error_code_t error,
|
|
const char *context) {
|
|
if (error != ZVEC_OK) {
|
|
char *error_msg = NULL;
|
|
zvec_get_last_error(&error_msg);
|
|
fprintf(stderr, "Error in %s: %d - %s\n", context, error,
|
|
error_msg ? error_msg : "Unknown error");
|
|
zvec_free(error_msg);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
/**
|
|
* @brief Create test vector data
|
|
*/
|
|
static float *create_test_vector(size_t dimension) {
|
|
float *vector = malloc(dimension * sizeof(float));
|
|
if (!vector) {
|
|
return NULL;
|
|
}
|
|
|
|
for (size_t i = 0; i < dimension; i++) {
|
|
vector[i] = (float)rand() / RAND_MAX;
|
|
}
|
|
|
|
return vector;
|
|
}
|
|
|
|
/**
|
|
* @brief Optimized C API usage example with performance considerations
|
|
*/
|
|
int main() {
|
|
printf("=== ZVec Optimized C API Example ===\n\n");
|
|
|
|
// Get version information
|
|
const char *version = zvec_get_version();
|
|
printf("ZVec Version: %s\n\n", version ? version : "Unknown");
|
|
|
|
zvec_error_code_t error;
|
|
|
|
// 1. Create optimized collection schema
|
|
zvec_collection_schema_t *schema =
|
|
zvec_collection_schema_create("optimized_example_collection");
|
|
if (!schema) {
|
|
fprintf(stderr, "Failed to create collection schema\n");
|
|
return -1;
|
|
}
|
|
printf("✓ Collection schema created\n");
|
|
|
|
// 2. Create optimized index parameters
|
|
zvec_index_params_t *hnsw_params =
|
|
zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW);
|
|
if (!hnsw_params) {
|
|
fprintf(stderr, "Failed to create HNSW index parameters\n");
|
|
zvec_collection_schema_destroy(schema);
|
|
return -1;
|
|
}
|
|
zvec_index_params_set_metric_type(hnsw_params, ZVEC_METRIC_TYPE_L2);
|
|
zvec_index_params_set_hnsw_params(hnsw_params, 32, 200);
|
|
|
|
// 3. Create fields with optimized configuration
|
|
zvec_field_schema_t *id_field =
|
|
zvec_field_schema_create("id", ZVEC_DATA_TYPE_STRING, false, 0);
|
|
zvec_field_schema_t *text_field =
|
|
zvec_field_schema_create("text", ZVEC_DATA_TYPE_STRING, true, 0);
|
|
zvec_field_schema_t *embedding_field = zvec_field_schema_create(
|
|
"embedding", ZVEC_DATA_TYPE_VECTOR_FP32, false, 128);
|
|
|
|
if (!id_field || !text_field || !embedding_field) {
|
|
fprintf(stderr, "Failed to create field schemas\n");
|
|
goto cleanup_params;
|
|
}
|
|
|
|
// Set indexes
|
|
zvec_field_schema_set_index_params(embedding_field, hnsw_params);
|
|
|
|
// Add fields to schema
|
|
error = zvec_collection_schema_add_field(schema, id_field);
|
|
if (handle_error(error, "adding ID field") != ZVEC_OK) goto cleanup_fields;
|
|
|
|
error = zvec_collection_schema_add_field(schema, text_field);
|
|
if (handle_error(error, "adding text field") != ZVEC_OK) goto cleanup_fields;
|
|
|
|
error = zvec_collection_schema_add_field(schema, embedding_field);
|
|
if (handle_error(error, "adding embedding field") != ZVEC_OK)
|
|
goto cleanup_fields;
|
|
|
|
printf("✓ Fields configured with indexes\n");
|
|
|
|
// 4. Create collection with optimized options
|
|
zvec_collection_options_t *options = zvec_collection_options_create();
|
|
if (!options) {
|
|
fprintf(stderr, "Failed to create collection options\n");
|
|
goto cleanup_fields;
|
|
}
|
|
zvec_collection_options_set_enable_mmap(
|
|
options, true); // Enable memory mapping for better performance
|
|
|
|
zvec_collection_t *collection = NULL;
|
|
error = zvec_collection_create_and_open("./optimized_example_collection",
|
|
schema, options, &collection);
|
|
zvec_collection_options_destroy(options);
|
|
if (handle_error(error, "creating collection") != ZVEC_OK) {
|
|
goto cleanup_fields;
|
|
}
|
|
printf("✓ Collection created with optimized settings\n");
|
|
|
|
// 5. Bulk insert test data
|
|
const size_t DOC_COUNT = 1000;
|
|
const size_t BATCH_SIZE = 100;
|
|
|
|
printf("Inserting %zu documents in batches of %zu...\n", DOC_COUNT,
|
|
BATCH_SIZE);
|
|
|
|
clock_t start_time = clock();
|
|
|
|
for (size_t batch_start = 0; batch_start < DOC_COUNT;
|
|
batch_start += BATCH_SIZE) {
|
|
size_t current_batch_size = (batch_start + BATCH_SIZE > DOC_COUNT)
|
|
? DOC_COUNT - batch_start
|
|
: BATCH_SIZE;
|
|
|
|
zvec_doc_t **batch_docs = malloc(current_batch_size * sizeof(zvec_doc_t *));
|
|
if (!batch_docs) {
|
|
fprintf(stderr, "Failed to allocate batch documents\n");
|
|
break;
|
|
}
|
|
|
|
// Create batch documents
|
|
for (size_t i = 0; i < current_batch_size; i++) {
|
|
batch_docs[i] = zvec_doc_create();
|
|
if (!batch_docs[i]) {
|
|
fprintf(stderr, "Failed to create document\n");
|
|
// Cleanup previous documents in batch
|
|
for (size_t j = 0; j < i; j++) {
|
|
zvec_doc_destroy(batch_docs[j]);
|
|
}
|
|
free(batch_docs);
|
|
goto cleanup_collection;
|
|
}
|
|
|
|
size_t doc_id = batch_start + i;
|
|
char pk[32];
|
|
snprintf(pk, sizeof(pk), "doc_%zu", doc_id);
|
|
zvec_doc_set_pk(batch_docs[i], pk);
|
|
|
|
// Add ID field
|
|
char id_str[32];
|
|
snprintf(id_str, sizeof(id_str), "ID_%zu", doc_id);
|
|
zvec_doc_add_field_by_value(batch_docs[i], "id", ZVEC_DATA_TYPE_STRING,
|
|
id_str, strlen(id_str));
|
|
|
|
// Add text field
|
|
char text_str[64];
|
|
snprintf(text_str, sizeof(text_str),
|
|
"Document number %zu with sample text", doc_id);
|
|
zvec_doc_add_field_by_value(batch_docs[i], "text", ZVEC_DATA_TYPE_STRING,
|
|
text_str, strlen(text_str));
|
|
|
|
// Add vector field
|
|
float *vector = create_test_vector(128);
|
|
if (vector) {
|
|
zvec_doc_add_field_by_value(batch_docs[i], "embedding",
|
|
ZVEC_DATA_TYPE_VECTOR_FP32, vector,
|
|
128 * sizeof(float));
|
|
free(vector);
|
|
}
|
|
}
|
|
|
|
// Insert batch
|
|
size_t success_count, error_count;
|
|
error = zvec_collection_insert(collection, (const zvec_doc_t **)batch_docs,
|
|
current_batch_size, &success_count,
|
|
&error_count);
|
|
if (handle_error(error, "inserting batch") != ZVEC_OK) {
|
|
// Cleanup batch documents
|
|
for (size_t i = 0; i < current_batch_size; i++) {
|
|
zvec_doc_destroy(batch_docs[i]);
|
|
}
|
|
free(batch_docs);
|
|
goto cleanup_collection;
|
|
}
|
|
|
|
printf(" Batch %zu-%zu: %zu successful, %zu failed\n", batch_start,
|
|
batch_start + current_batch_size - 1, success_count, error_count);
|
|
|
|
// Cleanup batch documents
|
|
for (size_t i = 0; i < current_batch_size; i++) {
|
|
zvec_doc_destroy(batch_docs[i]);
|
|
}
|
|
free(batch_docs);
|
|
}
|
|
|
|
clock_t insert_end_time = clock();
|
|
double insert_time =
|
|
((double)(insert_end_time - start_time)) / CLOCKS_PER_SEC;
|
|
printf("✓ Bulk insertion completed in %.3f seconds (%.0f docs/sec)\n",
|
|
insert_time, DOC_COUNT / insert_time);
|
|
|
|
// 6. Flush and optimize collection
|
|
printf("Flushing and optimizing collection...\n");
|
|
zvec_collection_flush(collection);
|
|
zvec_collection_optimize(collection);
|
|
printf("✓ Collection optimized\n");
|
|
|
|
// 7. Performance query test
|
|
printf("Testing query performance...\n");
|
|
|
|
float *query_vector = create_test_vector(128);
|
|
if (!query_vector) {
|
|
fprintf(stderr, "Failed to create query vector\n");
|
|
goto cleanup_collection;
|
|
}
|
|
|
|
zvec_vector_query_t *query = zvec_vector_query_create();
|
|
if (!query) {
|
|
fprintf(stderr, "Failed to create vector query\n");
|
|
free(query_vector);
|
|
goto cleanup_collection;
|
|
}
|
|
zvec_vector_query_set_field_name(query, "embedding");
|
|
zvec_vector_query_set_query_vector(query, query_vector, 128 * sizeof(float));
|
|
zvec_vector_query_set_topk(query, 10);
|
|
zvec_vector_query_set_filter(query, "");
|
|
zvec_vector_query_set_include_vector(query, false);
|
|
zvec_vector_query_set_include_doc_id(query, true);
|
|
|
|
const int QUERY_COUNT = 100;
|
|
start_time = clock();
|
|
|
|
for (int q = 0; q < QUERY_COUNT; q++) {
|
|
zvec_doc_t **results = NULL;
|
|
size_t result_count = 0;
|
|
|
|
error =
|
|
zvec_collection_query(collection, (const zvec_vector_query_t *)query,
|
|
&results, &result_count);
|
|
if (error != ZVEC_OK) {
|
|
char *error_msg = NULL;
|
|
zvec_get_last_error(&error_msg);
|
|
printf("Query %d failed: %s\n", q,
|
|
error_msg ? error_msg : "Unknown error");
|
|
zvec_free(error_msg);
|
|
continue;
|
|
}
|
|
|
|
if (results) {
|
|
zvec_docs_free(results, result_count);
|
|
}
|
|
}
|
|
|
|
clock_t query_end_time = clock();
|
|
double query_time = ((double)(query_end_time - start_time)) / CLOCKS_PER_SEC;
|
|
double avg_query_time = (query_time * 1000) / QUERY_COUNT;
|
|
|
|
printf("✓ Performance test completed\n");
|
|
printf(" Average query time: %.2f ms\n", avg_query_time);
|
|
printf(" Queries per second: %.0f\n", 1000.0 / avg_query_time);
|
|
|
|
free(query_vector);
|
|
zvec_vector_query_destroy(query);
|
|
|
|
// 8. Memory usage information
|
|
zvec_collection_stats_t *stats = NULL;
|
|
error = zvec_collection_get_stats(collection, &stats);
|
|
if (error == ZVEC_OK && stats) {
|
|
printf("Collection Statistics:\n");
|
|
printf(" Document count: %llu\n",
|
|
(unsigned long long)zvec_collection_stats_get_doc_count(stats));
|
|
zvec_collection_stats_destroy(stats);
|
|
}
|
|
|
|
// 9. Cleanup
|
|
cleanup_collection:
|
|
zvec_collection_destroy(collection);
|
|
|
|
cleanup_fields:
|
|
// Field schemas are managed by the collection schema, no need to destroy
|
|
// individually
|
|
|
|
cleanup_params:
|
|
zvec_collection_schema_destroy(schema);
|
|
|
|
printf("✓ Optimized example completed\n");
|
|
|
|
return 0;
|
|
} |