Loading src/common/common.c +377 −0 Viewed Changes for src/common/common.c: 377 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ #include "mpi.h" #include <stdlib.h> #include <time.h> #include "../adp/adp.h" #define ARRAY_INCREMENT 100 Loading @@ -26,6 +27,7 @@ void get_context(global_context_t* ctx) ctx -> dims = 0; ctx -> k = 300; ctx -> z = 3; ctx -> parallel_dump = MY_FALSE; } void get_dataset_diagnostics(global_context_t* ctx, float_t* data) Loading Loading @@ -741,3 +743,378 @@ void test_distributed_file_path(global_context_t* ctx, const char* fname) fclose(file); } static void* gather_ordered_buffer(global_context_t* ctx, const void* buffer, size_t el_size, uint64_t n, uint64_t* total_out) { void* out = NULL; uint64_t total = 0; uint64_t local_n = n; uint64_t* counts = NULL; uint64_t* displs = NULL; uint64_t* already_recv = NULL; MPI_Barrier(ctx->mpi_communicator); MPI_Reduce(&local_n, &total, 1, MPI_UINT64_T, MPI_SUM, 0, ctx->mpi_communicator); if(I_AM_MASTER) { out = malloc(el_size * total); counts = (uint64_t*)malloc(ctx->world_size * sizeof(uint64_t)); displs = (uint64_t*)malloc(ctx->world_size * sizeof(uint64_t)); already_recv = (uint64_t*)calloc(ctx->world_size, sizeof(uint64_t)); CHECK_ALLOCATION_NO_CTX(out); CHECK_ALLOCATION_NO_CTX(counts); CHECK_ALLOCATION_NO_CTX(displs); CHECK_ALLOCATION_NO_CTX(already_recv); } MPI_Gather(&local_n, 1, MPI_UINT64_T, counts, 1, MPI_UINT64_T, 0, ctx->mpi_communicator); if(I_AM_MASTER) { displs[0] = 0; for(int i = 0; i < ctx->world_size; ++i) { counts[i] *= el_size; } for(int i = 1; i < ctx->world_size; ++i) { displs[i] = displs[i - 1] + counts[i - 1]; } if(counts[0] > 0) { memcpy(out, buffer, counts[0]); } for(int r = 1; r < ctx->world_size; ++r) { while(already_recv[r] < counts[r]) { MPI_Status status; int count_recv; MPI_Probe(r, r, ctx->mpi_communicator, &status); MPI_Get_count(&status, MPI_BYTE, &count_recv); MPI_Recv((char*)out + displs[r] + already_recv[r], count_recv, MPI_BYTE, r, r, ctx->mpi_communicator, MPI_STATUS_IGNORE); already_recv[r] += (uint64_t)count_recv; } } } else { uint64_t already_sent = 0; uint64_t bytes_to_send = n * el_size; uint64_t default_msg_len = 100000000; while(already_sent < bytes_to_send) { int count_send = (int)MIN(default_msg_len, bytes_to_send - already_sent); MPI_Send((char*)buffer + already_sent, count_send, MPI_BYTE, 0, ctx->mpi_rank, ctx->mpi_communicator); already_sent += (uint64_t)count_send; } } if(I_AM_MASTER) { free(counts); free(displs); free(already_recv); } if(total_out) *total_out = total; MPI_Barrier(ctx->mpi_communicator); return out; } static void remap_sparse_border(sparse_border_t* b, clusters_t* clusters, idx_t* global_og_idxs, uint64_t n_points) { b->i = clusters->centers.data[b->i]; b->j = clusters->centers.data[b->j]; if(b->idx < n_points) { b->idx = global_og_idxs[b->idx]; } } typedef struct assignment_pair_t { idx_t idx; int assignment; } assignment_pair_t; static idx_t original_rank_start(global_context_t* ctx, int rank) { idx_t base = ctx->n_points / ctx->world_size; idx_t rem = ctx->n_points % ctx->world_size; return rank * base + MIN((idx_t)rank, rem); } static idx_t original_rank_count(global_context_t* ctx, int rank) { idx_t base = ctx->n_points / ctx->world_size; idx_t rem = ctx->n_points % ctx->world_size; return base + ((idx_t)rank < rem); } static int original_owner(global_context_t* ctx, idx_t original_idx) { idx_t base = ctx->n_points / ctx->world_size; idx_t rem = ctx->n_points % ctx->world_size; idx_t larger_count = base + 1; idx_t larger_total = rem * larger_count; if(original_idx < larger_total) { return original_idx / larger_count; } return rem + (original_idx - larger_total) / base; } static void write_reordered_assignments_binary(global_context_t* ctx, int* local_assignments, const char* filepath) { int* send_counts = (int*)calloc(ctx->world_size, sizeof(int)); int* recv_counts = (int*)calloc(ctx->world_size, sizeof(int)); int* send_displs = (int*)calloc(ctx->world_size, sizeof(int)); int* recv_displs = (int*)calloc(ctx->world_size, sizeof(int)); CHECK_ALLOCATION_NO_CTX(send_counts); CHECK_ALLOCATION_NO_CTX(recv_counts); CHECK_ALLOCATION_NO_CTX(send_displs); CHECK_ALLOCATION_NO_CTX(recv_displs); for(idx_t i = 0; i < ctx->local_n_points; ++i) { int owner = original_owner(ctx, ctx->og_idxs[i]); send_counts[owner]++; } MPI_Alltoall(send_counts, 1, MPI_INT, recv_counts, 1, MPI_INT, ctx->mpi_communicator); for(int i = 1; i < ctx->world_size; ++i) { send_displs[i] = send_displs[i - 1] + send_counts[i - 1]; recv_displs[i] = recv_displs[i - 1] + recv_counts[i - 1]; } int total_send = 0; int total_recv = 0; for(int i = 0; i < ctx->world_size; ++i) { total_send += send_counts[i]; total_recv += recv_counts[i]; } assignment_pair_t* send_pairs = (assignment_pair_t*)malloc(total_send * sizeof(assignment_pair_t)); assignment_pair_t* recv_pairs = (assignment_pair_t*)malloc(total_recv * sizeof(assignment_pair_t)); int* offsets = (int*)calloc(ctx->world_size, sizeof(int)); CHECK_ALLOCATION_NO_CTX(send_pairs); CHECK_ALLOCATION_NO_CTX(recv_pairs); CHECK_ALLOCATION_NO_CTX(offsets); for(idx_t i = 0; i < ctx->local_n_points; ++i) { idx_t original_idx = ctx->og_idxs[i]; int owner = original_owner(ctx, original_idx); int pos = send_displs[owner] + offsets[owner]++; send_pairs[pos].idx = original_idx; send_pairs[pos].assignment = local_assignments[i]; } int* send_counts_bytes = (int*)malloc(ctx->world_size * sizeof(int)); int* recv_counts_bytes = (int*)malloc(ctx->world_size * sizeof(int)); int* send_displs_bytes = (int*)malloc(ctx->world_size * sizeof(int)); int* recv_displs_bytes = (int*)malloc(ctx->world_size * sizeof(int)); CHECK_ALLOCATION_NO_CTX(send_counts_bytes); CHECK_ALLOCATION_NO_CTX(recv_counts_bytes); CHECK_ALLOCATION_NO_CTX(send_displs_bytes); CHECK_ALLOCATION_NO_CTX(recv_displs_bytes); for(int i = 0; i < ctx->world_size; ++i) { send_counts_bytes[i] = send_counts[i] * sizeof(assignment_pair_t); recv_counts_bytes[i] = recv_counts[i] * sizeof(assignment_pair_t); send_displs_bytes[i] = send_displs[i] * sizeof(assignment_pair_t); recv_displs_bytes[i] = recv_displs[i] * sizeof(assignment_pair_t); } MPI_Alltoallv(send_pairs, send_counts_bytes, send_displs_bytes, MPI_BYTE, recv_pairs, recv_counts_bytes, recv_displs_bytes, MPI_BYTE, ctx->mpi_communicator); idx_t original_start = original_rank_start(ctx, ctx->mpi_rank); idx_t original_count = original_rank_count(ctx, ctx->mpi_rank); int* reordered = (int*)malloc(original_count * sizeof(int)); CHECK_ALLOCATION_NO_CTX(reordered); for(idx_t i = 0; i < original_count; ++i) { reordered[i] = -1; } for(int i = 0; i < total_recv; ++i) { idx_t pos = recv_pairs[i].idx - original_start; if(pos < original_count) { reordered[pos] = recv_pairs[i].assignment; } } big_ordered_buffer_to_file(ctx, reordered, sizeof(int), original_count, filepath); free(send_counts); free(recv_counts); free(send_displs); free(recv_displs); free(send_pairs); free(recv_pairs); free(offsets); free(send_counts_bytes); free(recv_counts_bytes); free(send_displs_bytes); free(recv_displs_bytes); free(reordered); } void write_metadata_json(global_context_t* ctx, clusters_t* clusters, const char* output_dir) { if(!(I_AM_MASTER)) return; char filepath[PATH_LEN]; snprintf(filepath, PATH_LEN, "%s/metadata.json", output_dir); FILE* f = fopen(filepath, "w"); if(!f) { printf("Cannot open file %s\n", filepath); return; } time_t now = time(NULL); const char* tree_type_str = (ctx->local_tree_type == KD) ? "kd" : "vp"; fprintf(f, "{\n"); fprintf(f, " \"n_points\": %lu,\n", ctx->n_points); fprintf(f, " \"dims\": %u,\n", ctx->dims); fprintf(f, " \"k\": %lu,\n", ctx->k); fprintf(f, " \"z\": %f,\n", ctx->z); fprintf(f, " \"n_clusters\": %lu,\n", clusters->centers.count); fprintf(f, " \"timestamp\": %ld,\n", (long)now); fprintf(f, " \"input_file\": \"%s\",\n", ctx->input_data_file); fprintf(f, " \"n_mpi_procs\": %d,\n", ctx->world_size); fprintf(f, " \"tree_type\": \"%s\"\n", tree_type_str); fprintf(f, "}\n"); fclose(f); } void write_centers_binary(global_context_t* ctx, clusters_t* clusters, const char* output_dir) { if(!(I_AM_MASTER)) return; char filepath[PATH_LEN]; snprintf(filepath, PATH_LEN, "%s/centers", output_dir); FILE* f = fopen(filepath, "wb"); if(!f) { printf("Cannot open file %s\n", filepath); return; } fwrite(clusters->centers.data, sizeof(idx_t), clusters->centers.count, f); fclose(f); } void write_borders_binary(global_context_t* ctx, clusters_t* clusters, const char* output_dir) { uint64_t total_og_idxs = 0; idx_t* global_og_idxs = (idx_t*)gather_ordered_buffer(ctx, ctx->og_idxs, sizeof(idx_t), ctx->local_n_points, &total_og_idxs); if(!(I_AM_MASTER)) return; if(total_og_idxs != ctx->n_points) { printf("Expected %lu original indices, gathered %lu\n", (uint64_t)ctx->n_points, total_og_idxs); } char filepath[PATH_LEN]; snprintf(filepath, PATH_LEN, "%s/borders", output_dir); FILE* f = fopen(filepath, "wb"); if(!f) { printf("Cannot open file %s\n", filepath); free(global_og_idxs); return; } for(idx_t c = 0; c < clusters->centers.count; ++c) { idx_t count = clusters->sparse_borders[c].count; fwrite(&count, sizeof(idx_t), 1, f); if(count > 0) { sparse_border_t* remapped = (sparse_border_t*)malloc(count * sizeof(sparse_border_t)); for(idx_t i = 0; i < count; ++i) { remapped[i] = clusters->sparse_borders[c].data[i]; remap_sparse_border(&remapped[i], clusters, global_og_idxs, total_og_idxs); } fwrite(remapped, sizeof(sparse_border_t), count, f); free(remapped); } } fclose(f); free(global_og_idxs); } void write_assignments_binary(global_context_t* ctx, int* local_assignments, const char* output_dir, int parallel_dump) { char filepath[PATH_LEN]; if(parallel_dump) { snprintf(filepath, PATH_LEN, "%s/assignments", output_dir); distributed_buffer_to_file(ctx, local_assignments, sizeof(int), ctx->local_n_points, filepath); } else { snprintf(filepath, PATH_LEN, "%s/assignments", output_dir); write_reordered_assignments_binary(ctx, local_assignments, filepath); } } void write_cluster_results(global_context_t* ctx, clusters_t* clusters, int* local_assignments, const char* output_dir, int parallel_dump) { write_metadata_json(ctx, clusters, output_dir); write_centers_binary(ctx, clusters, output_dir); write_borders_binary(ctx, clusters, output_dir); write_assignments_binary(ctx, local_assignments, output_dir, parallel_dump); if(parallel_dump) { char filepath[PATH_LEN]; snprintf(filepath, PATH_LEN, "%s/og_idxs", output_dir); distributed_buffer_to_file(ctx, ctx -> og_idxs, sizeof(idx_t), ctx -> local_n_points, filepath); } } src/common/common.h +6 −0 Viewed Changes for src/common/common.h: 6 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -194,6 +194,7 @@ typedef struct global_context_t char input_data_file[DEFAULT_STR_LEN]; char output_assignment_file[DEFAULT_STR_LEN]; char output_data_file[DEFAULT_STR_LEN]; int parallel_dump; } global_context_t; typedef struct pointset_t Loading Loading @@ -243,3 +244,8 @@ void get_dataset_diagnostics(global_context_t* ctx, float_t* data); void test_distributed_file_path(global_context_t* ctx, const char* fname); void distributed_buffer_to_file(global_context_t* ctx, void* buffer, size_t el_size, uint64_t n, const char* fname); typedef struct clusters_t clusters_t; void write_cluster_results(global_context_t* ctx, clusters_t* clusters, int* local_assignments, const char* output_dir, int parallel_dump); src/main/main.c +57 −37 Viewed Changes for src/main/main.c: 57 added lines, 37 removed lines. Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ struct option long_options[] = {"kngbh", optional_argument, 0, 'k'}, {"z", optional_argument, 0, 'z'}, {"knn-strategy", optional_argument, 0, 's'}, {"parallel-out", no_argument, 0, 'p'}, {"help", optional_argument, 0, 'h'}, {0, 0, 0, 0} }; Loading @@ -39,7 +40,8 @@ const char* help = "Distributed Advanced Density Peak\n"\ " of the datapoints. Produces files like `data.[rank]`, `og_idx.[rank]` and `assignment.[rank]`\n"\ " -k --kngbh (optional) number of nearest neighbors to compute\n"\ " -s --knn-strategy (optional) strategy for local knn (allowed values 'kd' for kdtree, 'vp' for ball-tree (vantage point)\n"\ " -z --z (optional) number of nearest neighbors to compute\n"; " -z --z (optional) z parameter for density correction\n" " -p --parallel-out (optional) parallel dump of assignments\n"; void parse_args(global_context_t* ctx, int argc, char** argv) { Loading @@ -48,7 +50,7 @@ void parse_args(global_context_t* ctx, int argc, char** argv) int input_file_set = 0; int input_type_set = 0; while((opt = getopt_long(argc, argv, "i:t:d:o:k:z:hs:", long_options, NULL)) != -1) while((opt = getopt_long(argc, argv, "i:t:d:o:k:z:hs:p", long_options, NULL)) != -1) { switch(opt) { Loading Loading @@ -105,6 +107,10 @@ void parse_args(global_context_t* ctx, int argc, char** argv) } break; case 'p': ctx -> parallel_dump = MY_TRUE; break; default: mpi_printf(ctx, "%s\n", help); MPI_Finalize(); Loading Loading @@ -243,6 +249,26 @@ void mpiio_master_read_and_scatter(int dims, size_t n, global_context_t *ctx) int halo = MY_TRUE; float_t tol = 0.002; if(I_AM_MASTER) { char out_file[200]; snprintf(out_file, 200, "%s/metadata.json", ctx->output_data_file); test_file_path(out_file); snprintf(out_file, 200, "%s/centers", ctx->output_data_file); test_file_path(out_file); snprintf(out_file, 200, "%s/borders", ctx->output_data_file); test_file_path(out_file); if(!(ctx->parallel_dump)) { snprintf(out_file, 200, "%s/assignments", ctx->output_data_file); test_file_path(out_file); } } if(ctx -> world_size <= 32) { if(I_AM_MASTER) Loading @@ -252,9 +278,6 @@ void mpiio_master_read_and_scatter(int dims, size_t n, global_context_t *ctx) snprintf(out_file, 200, "%s/og_idx", ctx->output_data_file); test_file_path(out_file); snprintf(out_file, 200, "%s/assignment", ctx->output_data_file); test_file_path(out_file); snprintf(out_file, 200, "%s/data", ctx->output_data_file); test_file_path(out_file); } Loading @@ -265,13 +288,16 @@ void mpiio_master_read_and_scatter(int dims, size_t n, global_context_t *ctx) snprintf(out_file, 200, "%s/og_idx", ctx->output_data_file); test_distributed_file_path(ctx, out_file); snprintf(out_file, 200, "%s/assignment", ctx->output_data_file); test_distributed_file_path(ctx, out_file); snprintf(out_file, 200, "%s/data", ctx->output_data_file); test_distributed_file_path(ctx, out_file); } if(ctx->parallel_dump) { char out_file[200]; snprintf(out_file, 200, "%s/assignments", ctx->output_data_file); test_distributed_file_path(ctx, out_file); } TIME_START; ctx->local_data = mpiio_read_data_file(ctx, ctx->input_data_file, ctx->dims, Loading Loading @@ -408,7 +434,7 @@ void mpiio_master_read_and_scatter(int dims, size_t n, global_context_t *ctx) TIME_START; int* cl = (int*)MY_MALLOC(2 * ctx -> local_n_points * sizeof(int)); float_t* data_to_write = (float_t*)MY_MALLOC(ctx -> local_n_points * ctx -> k * sizeof(float_t)); float_t* data_to_write = (float_t*)MY_MALLOC(ctx -> local_n_points * ctx -> dims * sizeof(float_t)); switch(ctx -> local_tree_type) { Loading Loading @@ -442,34 +468,28 @@ void mpiio_master_read_and_scatter(int dims, size_t n, global_context_t *ctx) break; } if(ctx -> world_size <= 32) { char out_file[200]; snprintf(out_file, 200, "%s/og_idx", ctx->output_data_file); big_ordered_buffer_to_file(ctx, ctx->og_idxs, sizeof(idx_t), ctx -> local_n_points, out_file); snprintf(out_file, 200, "%s/assignment", ctx->output_data_file); big_ordered_buffer_to_file(ctx, cl, sizeof(int), ctx -> local_n_points, out_file); snprintf(out_file, 200, "%s/data", ctx->output_data_file); big_ordered_buffer_to_file(ctx, data_to_write, sizeof(double), ctx -> local_n_points * ctx -> dims, out_file); } else { char out_file[200]; snprintf(out_file, 200, "%s/og_idx", ctx->output_data_file); distributed_buffer_to_file(ctx, ctx->og_idxs, sizeof(idx_t), ctx -> local_n_points, out_file); snprintf(out_file, 200, "%s/assignment", ctx->output_data_file); distributed_buffer_to_file(ctx, cl, sizeof(int), ctx -> local_n_points, out_file); snprintf(out_file, 200, "%s/data", ctx->output_data_file); distributed_buffer_to_file(ctx, data_to_write, sizeof(double), ctx -> local_n_points * ctx -> dims, out_file); } // if(ctx -> world_size <= 32) // { // char out_file[200]; // // snprintf(out_file, 200, "%s/og_idx", ctx->output_data_file); // big_ordered_buffer_to_file(ctx, ctx->og_idxs, sizeof(idx_t), ctx -> local_n_points, out_file); // // snprintf(out_file, 200, "%s/data", ctx->output_data_file); // big_ordered_buffer_to_file(ctx, data_to_write, sizeof(float_t), ctx -> local_n_points * ctx -> dims, out_file); // } // else // { // char out_file[200]; // // snprintf(out_file, 200, "%s/og_idx", ctx->output_data_file); // distributed_buffer_to_file(ctx, ctx->og_idxs, sizeof(idx_t), ctx -> local_n_points, out_file); // // snprintf(out_file, 200, "%s/data", ctx->output_data_file); // distributed_buffer_to_file(ctx, data_to_write, sizeof(float_t), ctx -> local_n_points * ctx -> dims, out_file); // } write_cluster_results(ctx, &clusters, cl, ctx->output_data_file, ctx->parallel_dump); elapsed_time = TIME_STOP; LOG_WRITE("Write results to file", elapsed_time); Loading src/tree/tree.c +15 −66 Viewed Changes for src/tree/tree.c: 15 added lines, 66 removed lines. Original line number Diff line number Diff line Loading @@ -290,42 +290,17 @@ void compute_bounding_box_data(global_context_t *ctx, float_t *data, float_t* lb /* compute minimum and maximum for each dimensions, store them in local bb */ /* each processor on its own */ // this moves memory maybe there is a better way #pragma omp parallel { float_t* pvt_lb = (float_t*)MY_MALLOC(ctx -> dims * sizeof(float_t)); float_t* pvt_ub = (float_t*)MY_MALLOC(ctx -> dims * sizeof(float_t)); for (size_t d = 0; d < ctx->dims; ++d) { pvt_lb[d] = FLT_MAX; pvt_ub[d] = -FLT_MAX; } #pragma omp for // Use OpenMP 4.5+ array reduction with runtime size #pragma omp parallel for reduction(min:lb[:ctx->dims]) reduction(max:ub[:ctx->dims]) for (size_t i = 0; i < n_points; ++i) { for (size_t d = 0; d < ctx->dims; ++d) { pvt_lb[d] = MIN(data[i*ctx->dims + d], pvt_lb[d]); pvt_ub[d] = MAX(data[i*ctx->dims + d], pvt_ub[d]); } } #pragma omp critical (bounding_box_reduction) { for (size_t d = 0; d < ctx->dims; ++d) { lb[d] = MIN(pvt_lb[d], lb[d]); ub[d] = MAX(pvt_ub[d], ub[d]); lb[d] = MIN(data[i*ctx->dims + d], lb[d]); ub[d] = MAX(data[i*ctx->dims + d], ub[d]); } } free(pvt_lb); free(pvt_ub); } /*get the bounding box */ MPI_Allreduce(MPI_IN_PLACE, lb, ctx->dims, MPI_MY_FLOAT, MPI_MIN, ctx->mpi_communicator); Loading @@ -342,54 +317,28 @@ void compute_bounding_box_data(global_context_t *ctx, float_t *data, float_t* lb } void compute_bounding_box_pointset(global_context_t *ctx, pointset_t *ps) { #define local_data ps->datapoints #define lb ps->lb_box #define ub ps->ub_box // Use local pointers instead of macros to enable efficient OpenMP reduction point_t* local_data = ps->datapoints; float_t* lb = ps->lb_box; float_t* ub = ps->ub_box; for (size_t d = 0; d < ps->dims; ++d) { ps->lb_box[d] = FLT_MAX; ps->ub_box[d] = -FLT_MAX; // Initialize bounding boxes for (size_t d = 0; d < ps->dims; ++d) { lb[d] = FLT_MAX; ub[d] = -FLT_MAX; } /* compute minimum and maximum for each dimensions, store them in local bb */ /* each processor on its own */ // this moves memory maybe there is a better way #pragma omp parallel { float_t* pvt_lb = (float_t*)MY_MALLOC(ctx -> dims * sizeof(float_t)); float_t* pvt_ub = (float_t*)MY_MALLOC(ctx -> dims * sizeof(float_t)); for (size_t d = 0; d < ps->dims; ++d) { pvt_lb[d] = FLT_MAX; pvt_ub[d] = -FLT_MAX; } #pragma omp for #pragma omp parallel for reduction(min:lb[:ps->dims]) reduction(max:ub[:ps->dims]) for (size_t i = 0; i < ps->n_points; ++i) { for (size_t d = 0; d < ps->dims; ++d) { pvt_lb[d] = MIN(local_data[i].data[d], pvt_lb[d]); pvt_ub[d] = MAX(local_data[i].data[d], pvt_ub[d]); } lb[d] = MIN(local_data[i].data[d], lb[d]); ub[d] = MAX(local_data[i].data[d], ub[d]); } #pragma omp critical (bounding_box_reduction) { for (size_t d = 0; d < ps->dims; ++d) { lb[d] = MIN(pvt_lb[d], lb[d]); ub[d] = MAX(pvt_ub[d], ub[d]); } } free(pvt_lb); free(pvt_ub); } /*get the bounding box */ Loading .gitignore +5 −0 Viewed Changes for .gitignore: 5 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -7,3 +7,8 @@ bb scalability_results check.py var.py test** .agents/ .codex/ __pycache__/ prompt.txt Loading
src/common/common.c +377 −0 Viewed Changes for src/common/common.c: 377 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ #include "mpi.h" #include <stdlib.h> #include <time.h> #include "../adp/adp.h" #define ARRAY_INCREMENT 100 Loading @@ -26,6 +27,7 @@ void get_context(global_context_t* ctx) ctx -> dims = 0; ctx -> k = 300; ctx -> z = 3; ctx -> parallel_dump = MY_FALSE; } void get_dataset_diagnostics(global_context_t* ctx, float_t* data) Loading Loading @@ -741,3 +743,378 @@ void test_distributed_file_path(global_context_t* ctx, const char* fname) fclose(file); } static void* gather_ordered_buffer(global_context_t* ctx, const void* buffer, size_t el_size, uint64_t n, uint64_t* total_out) { void* out = NULL; uint64_t total = 0; uint64_t local_n = n; uint64_t* counts = NULL; uint64_t* displs = NULL; uint64_t* already_recv = NULL; MPI_Barrier(ctx->mpi_communicator); MPI_Reduce(&local_n, &total, 1, MPI_UINT64_T, MPI_SUM, 0, ctx->mpi_communicator); if(I_AM_MASTER) { out = malloc(el_size * total); counts = (uint64_t*)malloc(ctx->world_size * sizeof(uint64_t)); displs = (uint64_t*)malloc(ctx->world_size * sizeof(uint64_t)); already_recv = (uint64_t*)calloc(ctx->world_size, sizeof(uint64_t)); CHECK_ALLOCATION_NO_CTX(out); CHECK_ALLOCATION_NO_CTX(counts); CHECK_ALLOCATION_NO_CTX(displs); CHECK_ALLOCATION_NO_CTX(already_recv); } MPI_Gather(&local_n, 1, MPI_UINT64_T, counts, 1, MPI_UINT64_T, 0, ctx->mpi_communicator); if(I_AM_MASTER) { displs[0] = 0; for(int i = 0; i < ctx->world_size; ++i) { counts[i] *= el_size; } for(int i = 1; i < ctx->world_size; ++i) { displs[i] = displs[i - 1] + counts[i - 1]; } if(counts[0] > 0) { memcpy(out, buffer, counts[0]); } for(int r = 1; r < ctx->world_size; ++r) { while(already_recv[r] < counts[r]) { MPI_Status status; int count_recv; MPI_Probe(r, r, ctx->mpi_communicator, &status); MPI_Get_count(&status, MPI_BYTE, &count_recv); MPI_Recv((char*)out + displs[r] + already_recv[r], count_recv, MPI_BYTE, r, r, ctx->mpi_communicator, MPI_STATUS_IGNORE); already_recv[r] += (uint64_t)count_recv; } } } else { uint64_t already_sent = 0; uint64_t bytes_to_send = n * el_size; uint64_t default_msg_len = 100000000; while(already_sent < bytes_to_send) { int count_send = (int)MIN(default_msg_len, bytes_to_send - already_sent); MPI_Send((char*)buffer + already_sent, count_send, MPI_BYTE, 0, ctx->mpi_rank, ctx->mpi_communicator); already_sent += (uint64_t)count_send; } } if(I_AM_MASTER) { free(counts); free(displs); free(already_recv); } if(total_out) *total_out = total; MPI_Barrier(ctx->mpi_communicator); return out; } static void remap_sparse_border(sparse_border_t* b, clusters_t* clusters, idx_t* global_og_idxs, uint64_t n_points) { b->i = clusters->centers.data[b->i]; b->j = clusters->centers.data[b->j]; if(b->idx < n_points) { b->idx = global_og_idxs[b->idx]; } } typedef struct assignment_pair_t { idx_t idx; int assignment; } assignment_pair_t; static idx_t original_rank_start(global_context_t* ctx, int rank) { idx_t base = ctx->n_points / ctx->world_size; idx_t rem = ctx->n_points % ctx->world_size; return rank * base + MIN((idx_t)rank, rem); } static idx_t original_rank_count(global_context_t* ctx, int rank) { idx_t base = ctx->n_points / ctx->world_size; idx_t rem = ctx->n_points % ctx->world_size; return base + ((idx_t)rank < rem); } static int original_owner(global_context_t* ctx, idx_t original_idx) { idx_t base = ctx->n_points / ctx->world_size; idx_t rem = ctx->n_points % ctx->world_size; idx_t larger_count = base + 1; idx_t larger_total = rem * larger_count; if(original_idx < larger_total) { return original_idx / larger_count; } return rem + (original_idx - larger_total) / base; } static void write_reordered_assignments_binary(global_context_t* ctx, int* local_assignments, const char* filepath) { int* send_counts = (int*)calloc(ctx->world_size, sizeof(int)); int* recv_counts = (int*)calloc(ctx->world_size, sizeof(int)); int* send_displs = (int*)calloc(ctx->world_size, sizeof(int)); int* recv_displs = (int*)calloc(ctx->world_size, sizeof(int)); CHECK_ALLOCATION_NO_CTX(send_counts); CHECK_ALLOCATION_NO_CTX(recv_counts); CHECK_ALLOCATION_NO_CTX(send_displs); CHECK_ALLOCATION_NO_CTX(recv_displs); for(idx_t i = 0; i < ctx->local_n_points; ++i) { int owner = original_owner(ctx, ctx->og_idxs[i]); send_counts[owner]++; } MPI_Alltoall(send_counts, 1, MPI_INT, recv_counts, 1, MPI_INT, ctx->mpi_communicator); for(int i = 1; i < ctx->world_size; ++i) { send_displs[i] = send_displs[i - 1] + send_counts[i - 1]; recv_displs[i] = recv_displs[i - 1] + recv_counts[i - 1]; } int total_send = 0; int total_recv = 0; for(int i = 0; i < ctx->world_size; ++i) { total_send += send_counts[i]; total_recv += recv_counts[i]; } assignment_pair_t* send_pairs = (assignment_pair_t*)malloc(total_send * sizeof(assignment_pair_t)); assignment_pair_t* recv_pairs = (assignment_pair_t*)malloc(total_recv * sizeof(assignment_pair_t)); int* offsets = (int*)calloc(ctx->world_size, sizeof(int)); CHECK_ALLOCATION_NO_CTX(send_pairs); CHECK_ALLOCATION_NO_CTX(recv_pairs); CHECK_ALLOCATION_NO_CTX(offsets); for(idx_t i = 0; i < ctx->local_n_points; ++i) { idx_t original_idx = ctx->og_idxs[i]; int owner = original_owner(ctx, original_idx); int pos = send_displs[owner] + offsets[owner]++; send_pairs[pos].idx = original_idx; send_pairs[pos].assignment = local_assignments[i]; } int* send_counts_bytes = (int*)malloc(ctx->world_size * sizeof(int)); int* recv_counts_bytes = (int*)malloc(ctx->world_size * sizeof(int)); int* send_displs_bytes = (int*)malloc(ctx->world_size * sizeof(int)); int* recv_displs_bytes = (int*)malloc(ctx->world_size * sizeof(int)); CHECK_ALLOCATION_NO_CTX(send_counts_bytes); CHECK_ALLOCATION_NO_CTX(recv_counts_bytes); CHECK_ALLOCATION_NO_CTX(send_displs_bytes); CHECK_ALLOCATION_NO_CTX(recv_displs_bytes); for(int i = 0; i < ctx->world_size; ++i) { send_counts_bytes[i] = send_counts[i] * sizeof(assignment_pair_t); recv_counts_bytes[i] = recv_counts[i] * sizeof(assignment_pair_t); send_displs_bytes[i] = send_displs[i] * sizeof(assignment_pair_t); recv_displs_bytes[i] = recv_displs[i] * sizeof(assignment_pair_t); } MPI_Alltoallv(send_pairs, send_counts_bytes, send_displs_bytes, MPI_BYTE, recv_pairs, recv_counts_bytes, recv_displs_bytes, MPI_BYTE, ctx->mpi_communicator); idx_t original_start = original_rank_start(ctx, ctx->mpi_rank); idx_t original_count = original_rank_count(ctx, ctx->mpi_rank); int* reordered = (int*)malloc(original_count * sizeof(int)); CHECK_ALLOCATION_NO_CTX(reordered); for(idx_t i = 0; i < original_count; ++i) { reordered[i] = -1; } for(int i = 0; i < total_recv; ++i) { idx_t pos = recv_pairs[i].idx - original_start; if(pos < original_count) { reordered[pos] = recv_pairs[i].assignment; } } big_ordered_buffer_to_file(ctx, reordered, sizeof(int), original_count, filepath); free(send_counts); free(recv_counts); free(send_displs); free(recv_displs); free(send_pairs); free(recv_pairs); free(offsets); free(send_counts_bytes); free(recv_counts_bytes); free(send_displs_bytes); free(recv_displs_bytes); free(reordered); } void write_metadata_json(global_context_t* ctx, clusters_t* clusters, const char* output_dir) { if(!(I_AM_MASTER)) return; char filepath[PATH_LEN]; snprintf(filepath, PATH_LEN, "%s/metadata.json", output_dir); FILE* f = fopen(filepath, "w"); if(!f) { printf("Cannot open file %s\n", filepath); return; } time_t now = time(NULL); const char* tree_type_str = (ctx->local_tree_type == KD) ? "kd" : "vp"; fprintf(f, "{\n"); fprintf(f, " \"n_points\": %lu,\n", ctx->n_points); fprintf(f, " \"dims\": %u,\n", ctx->dims); fprintf(f, " \"k\": %lu,\n", ctx->k); fprintf(f, " \"z\": %f,\n", ctx->z); fprintf(f, " \"n_clusters\": %lu,\n", clusters->centers.count); fprintf(f, " \"timestamp\": %ld,\n", (long)now); fprintf(f, " \"input_file\": \"%s\",\n", ctx->input_data_file); fprintf(f, " \"n_mpi_procs\": %d,\n", ctx->world_size); fprintf(f, " \"tree_type\": \"%s\"\n", tree_type_str); fprintf(f, "}\n"); fclose(f); } void write_centers_binary(global_context_t* ctx, clusters_t* clusters, const char* output_dir) { if(!(I_AM_MASTER)) return; char filepath[PATH_LEN]; snprintf(filepath, PATH_LEN, "%s/centers", output_dir); FILE* f = fopen(filepath, "wb"); if(!f) { printf("Cannot open file %s\n", filepath); return; } fwrite(clusters->centers.data, sizeof(idx_t), clusters->centers.count, f); fclose(f); } void write_borders_binary(global_context_t* ctx, clusters_t* clusters, const char* output_dir) { uint64_t total_og_idxs = 0; idx_t* global_og_idxs = (idx_t*)gather_ordered_buffer(ctx, ctx->og_idxs, sizeof(idx_t), ctx->local_n_points, &total_og_idxs); if(!(I_AM_MASTER)) return; if(total_og_idxs != ctx->n_points) { printf("Expected %lu original indices, gathered %lu\n", (uint64_t)ctx->n_points, total_og_idxs); } char filepath[PATH_LEN]; snprintf(filepath, PATH_LEN, "%s/borders", output_dir); FILE* f = fopen(filepath, "wb"); if(!f) { printf("Cannot open file %s\n", filepath); free(global_og_idxs); return; } for(idx_t c = 0; c < clusters->centers.count; ++c) { idx_t count = clusters->sparse_borders[c].count; fwrite(&count, sizeof(idx_t), 1, f); if(count > 0) { sparse_border_t* remapped = (sparse_border_t*)malloc(count * sizeof(sparse_border_t)); for(idx_t i = 0; i < count; ++i) { remapped[i] = clusters->sparse_borders[c].data[i]; remap_sparse_border(&remapped[i], clusters, global_og_idxs, total_og_idxs); } fwrite(remapped, sizeof(sparse_border_t), count, f); free(remapped); } } fclose(f); free(global_og_idxs); } void write_assignments_binary(global_context_t* ctx, int* local_assignments, const char* output_dir, int parallel_dump) { char filepath[PATH_LEN]; if(parallel_dump) { snprintf(filepath, PATH_LEN, "%s/assignments", output_dir); distributed_buffer_to_file(ctx, local_assignments, sizeof(int), ctx->local_n_points, filepath); } else { snprintf(filepath, PATH_LEN, "%s/assignments", output_dir); write_reordered_assignments_binary(ctx, local_assignments, filepath); } } void write_cluster_results(global_context_t* ctx, clusters_t* clusters, int* local_assignments, const char* output_dir, int parallel_dump) { write_metadata_json(ctx, clusters, output_dir); write_centers_binary(ctx, clusters, output_dir); write_borders_binary(ctx, clusters, output_dir); write_assignments_binary(ctx, local_assignments, output_dir, parallel_dump); if(parallel_dump) { char filepath[PATH_LEN]; snprintf(filepath, PATH_LEN, "%s/og_idxs", output_dir); distributed_buffer_to_file(ctx, ctx -> og_idxs, sizeof(idx_t), ctx -> local_n_points, filepath); } }
src/common/common.h +6 −0 Viewed Changes for src/common/common.h: 6 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -194,6 +194,7 @@ typedef struct global_context_t char input_data_file[DEFAULT_STR_LEN]; char output_assignment_file[DEFAULT_STR_LEN]; char output_data_file[DEFAULT_STR_LEN]; int parallel_dump; } global_context_t; typedef struct pointset_t Loading Loading @@ -243,3 +244,8 @@ void get_dataset_diagnostics(global_context_t* ctx, float_t* data); void test_distributed_file_path(global_context_t* ctx, const char* fname); void distributed_buffer_to_file(global_context_t* ctx, void* buffer, size_t el_size, uint64_t n, const char* fname); typedef struct clusters_t clusters_t; void write_cluster_results(global_context_t* ctx, clusters_t* clusters, int* local_assignments, const char* output_dir, int parallel_dump);
src/main/main.c +57 −37 Viewed Changes for src/main/main.c: 57 added lines, 37 removed lines. Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ struct option long_options[] = {"kngbh", optional_argument, 0, 'k'}, {"z", optional_argument, 0, 'z'}, {"knn-strategy", optional_argument, 0, 's'}, {"parallel-out", no_argument, 0, 'p'}, {"help", optional_argument, 0, 'h'}, {0, 0, 0, 0} }; Loading @@ -39,7 +40,8 @@ const char* help = "Distributed Advanced Density Peak\n"\ " of the datapoints. Produces files like `data.[rank]`, `og_idx.[rank]` and `assignment.[rank]`\n"\ " -k --kngbh (optional) number of nearest neighbors to compute\n"\ " -s --knn-strategy (optional) strategy for local knn (allowed values 'kd' for kdtree, 'vp' for ball-tree (vantage point)\n"\ " -z --z (optional) number of nearest neighbors to compute\n"; " -z --z (optional) z parameter for density correction\n" " -p --parallel-out (optional) parallel dump of assignments\n"; void parse_args(global_context_t* ctx, int argc, char** argv) { Loading @@ -48,7 +50,7 @@ void parse_args(global_context_t* ctx, int argc, char** argv) int input_file_set = 0; int input_type_set = 0; while((opt = getopt_long(argc, argv, "i:t:d:o:k:z:hs:", long_options, NULL)) != -1) while((opt = getopt_long(argc, argv, "i:t:d:o:k:z:hs:p", long_options, NULL)) != -1) { switch(opt) { Loading Loading @@ -105,6 +107,10 @@ void parse_args(global_context_t* ctx, int argc, char** argv) } break; case 'p': ctx -> parallel_dump = MY_TRUE; break; default: mpi_printf(ctx, "%s\n", help); MPI_Finalize(); Loading Loading @@ -243,6 +249,26 @@ void mpiio_master_read_and_scatter(int dims, size_t n, global_context_t *ctx) int halo = MY_TRUE; float_t tol = 0.002; if(I_AM_MASTER) { char out_file[200]; snprintf(out_file, 200, "%s/metadata.json", ctx->output_data_file); test_file_path(out_file); snprintf(out_file, 200, "%s/centers", ctx->output_data_file); test_file_path(out_file); snprintf(out_file, 200, "%s/borders", ctx->output_data_file); test_file_path(out_file); if(!(ctx->parallel_dump)) { snprintf(out_file, 200, "%s/assignments", ctx->output_data_file); test_file_path(out_file); } } if(ctx -> world_size <= 32) { if(I_AM_MASTER) Loading @@ -252,9 +278,6 @@ void mpiio_master_read_and_scatter(int dims, size_t n, global_context_t *ctx) snprintf(out_file, 200, "%s/og_idx", ctx->output_data_file); test_file_path(out_file); snprintf(out_file, 200, "%s/assignment", ctx->output_data_file); test_file_path(out_file); snprintf(out_file, 200, "%s/data", ctx->output_data_file); test_file_path(out_file); } Loading @@ -265,13 +288,16 @@ void mpiio_master_read_and_scatter(int dims, size_t n, global_context_t *ctx) snprintf(out_file, 200, "%s/og_idx", ctx->output_data_file); test_distributed_file_path(ctx, out_file); snprintf(out_file, 200, "%s/assignment", ctx->output_data_file); test_distributed_file_path(ctx, out_file); snprintf(out_file, 200, "%s/data", ctx->output_data_file); test_distributed_file_path(ctx, out_file); } if(ctx->parallel_dump) { char out_file[200]; snprintf(out_file, 200, "%s/assignments", ctx->output_data_file); test_distributed_file_path(ctx, out_file); } TIME_START; ctx->local_data = mpiio_read_data_file(ctx, ctx->input_data_file, ctx->dims, Loading Loading @@ -408,7 +434,7 @@ void mpiio_master_read_and_scatter(int dims, size_t n, global_context_t *ctx) TIME_START; int* cl = (int*)MY_MALLOC(2 * ctx -> local_n_points * sizeof(int)); float_t* data_to_write = (float_t*)MY_MALLOC(ctx -> local_n_points * ctx -> k * sizeof(float_t)); float_t* data_to_write = (float_t*)MY_MALLOC(ctx -> local_n_points * ctx -> dims * sizeof(float_t)); switch(ctx -> local_tree_type) { Loading Loading @@ -442,34 +468,28 @@ void mpiio_master_read_and_scatter(int dims, size_t n, global_context_t *ctx) break; } if(ctx -> world_size <= 32) { char out_file[200]; snprintf(out_file, 200, "%s/og_idx", ctx->output_data_file); big_ordered_buffer_to_file(ctx, ctx->og_idxs, sizeof(idx_t), ctx -> local_n_points, out_file); snprintf(out_file, 200, "%s/assignment", ctx->output_data_file); big_ordered_buffer_to_file(ctx, cl, sizeof(int), ctx -> local_n_points, out_file); snprintf(out_file, 200, "%s/data", ctx->output_data_file); big_ordered_buffer_to_file(ctx, data_to_write, sizeof(double), ctx -> local_n_points * ctx -> dims, out_file); } else { char out_file[200]; snprintf(out_file, 200, "%s/og_idx", ctx->output_data_file); distributed_buffer_to_file(ctx, ctx->og_idxs, sizeof(idx_t), ctx -> local_n_points, out_file); snprintf(out_file, 200, "%s/assignment", ctx->output_data_file); distributed_buffer_to_file(ctx, cl, sizeof(int), ctx -> local_n_points, out_file); snprintf(out_file, 200, "%s/data", ctx->output_data_file); distributed_buffer_to_file(ctx, data_to_write, sizeof(double), ctx -> local_n_points * ctx -> dims, out_file); } // if(ctx -> world_size <= 32) // { // char out_file[200]; // // snprintf(out_file, 200, "%s/og_idx", ctx->output_data_file); // big_ordered_buffer_to_file(ctx, ctx->og_idxs, sizeof(idx_t), ctx -> local_n_points, out_file); // // snprintf(out_file, 200, "%s/data", ctx->output_data_file); // big_ordered_buffer_to_file(ctx, data_to_write, sizeof(float_t), ctx -> local_n_points * ctx -> dims, out_file); // } // else // { // char out_file[200]; // // snprintf(out_file, 200, "%s/og_idx", ctx->output_data_file); // distributed_buffer_to_file(ctx, ctx->og_idxs, sizeof(idx_t), ctx -> local_n_points, out_file); // // snprintf(out_file, 200, "%s/data", ctx->output_data_file); // distributed_buffer_to_file(ctx, data_to_write, sizeof(float_t), ctx -> local_n_points * ctx -> dims, out_file); // } write_cluster_results(ctx, &clusters, cl, ctx->output_data_file, ctx->parallel_dump); elapsed_time = TIME_STOP; LOG_WRITE("Write results to file", elapsed_time); Loading
src/tree/tree.c +15 −66 Viewed Changes for src/tree/tree.c: 15 added lines, 66 removed lines. Original line number Diff line number Diff line Loading @@ -290,42 +290,17 @@ void compute_bounding_box_data(global_context_t *ctx, float_t *data, float_t* lb /* compute minimum and maximum for each dimensions, store them in local bb */ /* each processor on its own */ // this moves memory maybe there is a better way #pragma omp parallel { float_t* pvt_lb = (float_t*)MY_MALLOC(ctx -> dims * sizeof(float_t)); float_t* pvt_ub = (float_t*)MY_MALLOC(ctx -> dims * sizeof(float_t)); for (size_t d = 0; d < ctx->dims; ++d) { pvt_lb[d] = FLT_MAX; pvt_ub[d] = -FLT_MAX; } #pragma omp for // Use OpenMP 4.5+ array reduction with runtime size #pragma omp parallel for reduction(min:lb[:ctx->dims]) reduction(max:ub[:ctx->dims]) for (size_t i = 0; i < n_points; ++i) { for (size_t d = 0; d < ctx->dims; ++d) { pvt_lb[d] = MIN(data[i*ctx->dims + d], pvt_lb[d]); pvt_ub[d] = MAX(data[i*ctx->dims + d], pvt_ub[d]); } } #pragma omp critical (bounding_box_reduction) { for (size_t d = 0; d < ctx->dims; ++d) { lb[d] = MIN(pvt_lb[d], lb[d]); ub[d] = MAX(pvt_ub[d], ub[d]); lb[d] = MIN(data[i*ctx->dims + d], lb[d]); ub[d] = MAX(data[i*ctx->dims + d], ub[d]); } } free(pvt_lb); free(pvt_ub); } /*get the bounding box */ MPI_Allreduce(MPI_IN_PLACE, lb, ctx->dims, MPI_MY_FLOAT, MPI_MIN, ctx->mpi_communicator); Loading @@ -342,54 +317,28 @@ void compute_bounding_box_data(global_context_t *ctx, float_t *data, float_t* lb } void compute_bounding_box_pointset(global_context_t *ctx, pointset_t *ps) { #define local_data ps->datapoints #define lb ps->lb_box #define ub ps->ub_box // Use local pointers instead of macros to enable efficient OpenMP reduction point_t* local_data = ps->datapoints; float_t* lb = ps->lb_box; float_t* ub = ps->ub_box; for (size_t d = 0; d < ps->dims; ++d) { ps->lb_box[d] = FLT_MAX; ps->ub_box[d] = -FLT_MAX; // Initialize bounding boxes for (size_t d = 0; d < ps->dims; ++d) { lb[d] = FLT_MAX; ub[d] = -FLT_MAX; } /* compute minimum and maximum for each dimensions, store them in local bb */ /* each processor on its own */ // this moves memory maybe there is a better way #pragma omp parallel { float_t* pvt_lb = (float_t*)MY_MALLOC(ctx -> dims * sizeof(float_t)); float_t* pvt_ub = (float_t*)MY_MALLOC(ctx -> dims * sizeof(float_t)); for (size_t d = 0; d < ps->dims; ++d) { pvt_lb[d] = FLT_MAX; pvt_ub[d] = -FLT_MAX; } #pragma omp for #pragma omp parallel for reduction(min:lb[:ps->dims]) reduction(max:ub[:ps->dims]) for (size_t i = 0; i < ps->n_points; ++i) { for (size_t d = 0; d < ps->dims; ++d) { pvt_lb[d] = MIN(local_data[i].data[d], pvt_lb[d]); pvt_ub[d] = MAX(local_data[i].data[d], pvt_ub[d]); } lb[d] = MIN(local_data[i].data[d], lb[d]); ub[d] = MAX(local_data[i].data[d], ub[d]); } #pragma omp critical (bounding_box_reduction) { for (size_t d = 0; d < ps->dims; ++d) { lb[d] = MIN(pvt_lb[d], lb[d]); ub[d] = MAX(pvt_ub[d], ub[d]); } } free(pvt_lb); free(pvt_ub); } /*get the bounding box */ Loading
.gitignore +5 −0 Viewed Changes for .gitignore: 5 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -7,3 +7,8 @@ bb scalability_results check.py var.py test** .agents/ .codex/ __pycache__/ prompt.txt