Skip to content

Commit 22ae99a

Browse files
committed
Perf - Shift coverage handling to whole-word bit math.
1 parent d3029a6 commit 22ae99a

File tree

4 files changed

+153
-16
lines changed

4 files changed

+153
-16
lines changed

c/examples/Makefile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ TSKIT_SOURCE=../tskit/*.c ../subprojects/kastore/kastore.c
2323
targets = api_structure error_handling \
2424
haploid_wright_fisher streaming \
2525
tree_iteration tree_traversal \
26-
take_ownership
26+
take_ownership haplotype_benchmark
2727

2828
all: $(targets)
2929

@@ -32,4 +32,3 @@ $(targets): %: %.c
3232

3333
clean:
3434
rm -f $(targets)
35-

c/examples/haplotype_benchmark.c

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <stdint.h>
4+
#include <time.h>
5+
6+
#include <tskit.h>
7+
#include <tskit/genotypes.h>
8+
#include <tskit/tables.h>
9+
10+
#define CHECK_TSK(err) \
11+
do { \
12+
if ((err) < 0) { \
13+
fprintf(stderr, "Error: line %d: %s\n", __LINE__, tsk_strerror(err)); \
14+
exit(EXIT_FAILURE); \
15+
} \
16+
} while (0)
17+
18+
#define NUM_ITERATIONS 1
19+
#define MAX_BENCHMARK_NODES 500
20+
21+
int
22+
main(int argc, char **argv)
23+
{
24+
int ret;
25+
tsk_table_collection_t tables;
26+
tsk_treeseq_t treeseq;
27+
tsk_haplotype_t haplotype_decoder;
28+
int8_t *haplotype = NULL;
29+
double elapsed_seconds;
30+
clock_t start_clock, end_clock;
31+
uint64_t checksum = 0;
32+
33+
const char *filename = "../../simulated_chrom_21_100k.ts";
34+
if (argc > 1) {
35+
filename = argv[1];
36+
}
37+
38+
ret = tsk_table_collection_init(&tables, 0);
39+
CHECK_TSK(ret);
40+
41+
ret = tsk_table_collection_load(&tables, filename, 0);
42+
CHECK_TSK(ret);
43+
44+
ret = tsk_treeseq_init(&treeseq, &tables, 0);
45+
CHECK_TSK(ret);
46+
47+
tsk_size_t num_nodes = tsk_treeseq_get_num_nodes(&treeseq);
48+
tsk_size_t num_sites = tsk_treeseq_get_num_sites(&treeseq);
49+
if (num_sites == 0) {
50+
fprintf(stderr, "Tree sequence has no sites\n");
51+
exit(EXIT_FAILURE);
52+
}
53+
54+
tsk_id_t node_limit
55+
= (tsk_id_t) (num_nodes < MAX_BENCHMARK_NODES ? num_nodes : MAX_BENCHMARK_NODES);
56+
57+
ret = tsk_haplotype_init(&haplotype_decoder, &treeseq, 0, (tsk_id_t) num_sites);
58+
CHECK_TSK(ret);
59+
60+
haplotype = malloc(num_sites * sizeof(*haplotype));
61+
if (haplotype == NULL) {
62+
fprintf(stderr, "Failed to allocate haplotype buffer\n");
63+
exit(EXIT_FAILURE);
64+
}
65+
66+
start_clock = clock();
67+
for (int iter = 0; iter < NUM_ITERATIONS; iter++) {
68+
for (tsk_id_t node = 0; node < node_limit; node++) {
69+
ret = tsk_haplotype_decode(&haplotype_decoder, node, haplotype);
70+
CHECK_TSK(ret);
71+
for (tsk_id_t site = 0; site < (tsk_id_t) num_sites; site++) {
72+
checksum += (uint64_t) haplotype[site];
73+
}
74+
}
75+
}
76+
end_clock = clock();
77+
78+
elapsed_seconds = (double) (end_clock - start_clock) / CLOCKS_PER_SEC;
79+
80+
printf("Loaded tree sequence from %s\n", filename);
81+
printf("Decoded %d iterations over %lld nodes × %lld sites in %.3f seconds\n",
82+
NUM_ITERATIONS, (long long) node_limit, (long long) num_sites, elapsed_seconds);
83+
printf("Checksummed haplotypes: %llu\n", (unsigned long long) checksum);
84+
85+
free(haplotype);
86+
tsk_haplotype_free(&haplotype_decoder);
87+
tsk_treeseq_free(&treeseq);
88+
tsk_table_collection_free(&tables);
89+
return EXIT_SUCCESS;
90+
}

c/meson.build

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ if not meson.is_subproject()
113113
executable('tree_traversal',
114114
sources: ['examples/tree_traversal.c'],
115115
link_with: [tskit_lib], dependencies: lib_deps)
116+
executable('haplotype_benchmark',
117+
sources: ['examples/haplotype_benchmark.c'],
118+
link_with: [tskit_lib], dependencies: lib_deps)
116119
executable('streaming',
117120
sources: ['examples/streaming.c'],
118121
link_with: [tskit_lib], dependencies: lib_deps)

c/tskit/genotypes.c

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,21 @@ tsk_haplotype_bitset_next(
123123
return start < limit ? start : limit;
124124
}
125125

126+
static inline uint64_t
127+
tsk_haplotype_mask_from_offsets(uint32_t start_offset, uint32_t end_offset)
128+
{
129+
if (start_offset >= end_offset) {
130+
return 0;
131+
}
132+
if (start_offset == 0 && end_offset >= 64) {
133+
return UINT64_MAX;
134+
}
135+
uint64_t high_mask
136+
= end_offset >= 64 ? UINT64_MAX : ((UINT64_C(1) << end_offset) - 1);
137+
uint64_t low_mask = start_offset == 0 ? 0 : ((UINT64_C(1) << start_offset) - 1);
138+
return high_mask & ~low_mask;
139+
}
140+
126141
static bool
127142
tsk_haplotype_find_next_uncovered(tsk_haplotype_t *self, tsk_size_t start,
128143
tsk_size_t end, const int32_t *interval_start, const int32_t *interval_end,
@@ -139,6 +154,7 @@ tsk_haplotype_find_next_uncovered(tsk_haplotype_t *self, tsk_size_t start,
139154
uint64_t start_mask = UINT64_MAX << (start & 63);
140155
for (; word <= last_word && word < self->num_bit_words; word++) {
141156
if (self->unresolved_counts[word] == 0) {
157+
start_mask = UINT64_MAX;
142158
continue;
143159
}
144160
uint64_t word_bits = self->unresolved_bits[word];
@@ -149,26 +165,55 @@ tsk_haplotype_find_next_uncovered(tsk_haplotype_t *self, tsk_size_t start,
149165
uint64_t end_mask = UINT64_MAX >> (63 - ((end - 1) & 63));
150166
word_bits &= end_mask;
151167
}
168+
if (word_bits == 0) {
169+
start_mask = UINT64_MAX;
170+
continue;
171+
}
172+
if (interval_count > 0) {
173+
int32_t word_left = (int32_t)(word << 6);
174+
int32_t word_right = word_left + 64;
175+
uint64_t coverage_mask = 0;
176+
for (tsk_size_t p = 0; p < interval_count; p++) {
177+
int32_t interval_left = interval_start[p];
178+
int32_t interval_right = interval_end[p];
179+
if (interval_left >= interval_right) {
180+
continue;
181+
}
182+
if (interval_right <= word_left || interval_left >= word_right) {
183+
continue;
184+
}
185+
int32_t clipped_left
186+
= interval_left > word_left ? interval_left : word_left;
187+
int32_t clipped_right
188+
= interval_right < word_right ? interval_right : word_right;
189+
if ((int32_t) start > clipped_left) {
190+
clipped_left = (int32_t) start;
191+
}
192+
if ((int32_t) end < clipped_right) {
193+
clipped_right = (int32_t) end;
194+
}
195+
if (clipped_left >= clipped_right) {
196+
continue;
197+
}
198+
uint32_t start_offset = (uint32_t)(clipped_left - word_left);
199+
uint32_t end_offset = (uint32_t)(clipped_right - word_left);
200+
coverage_mask
201+
|= tsk_haplotype_mask_from_offsets(start_offset, end_offset);
202+
if (coverage_mask == UINT64_MAX) {
203+
break;
204+
}
205+
}
206+
word_bits &= ~coverage_mask;
207+
}
152208
while (word_bits != 0) {
153-
uint64_t lowest_bit = word_bits & (~word_bits + 1);
154209
tsk_size_t bit = tsk_haplotype_ctz64(word_bits);
155-
word_bits ^= lowest_bit;
210+
word_bits &= word_bits - 1;
156211
tsk_size_t bit_index = (word << 6) + bit;
157212
if (bit_index >= end) {
158213
break;
159214
}
160-
bool covered = false;
161-
for (tsk_size_t p = 0; p < interval_count; p++) {
162-
if (interval_start[p] <= (int32_t) bit_index
163-
&& (int32_t) bit_index < interval_end[p]) {
164-
covered = true;
165-
break;
166-
}
167-
}
168-
if (!covered) {
169-
*out_index = bit_index;
170-
return true;
171-
}
215+
*out_index = bit_index;
216+
return true;
172217
}
173218
start_mask = UINT64_MAX;
174219
}

0 commit comments

Comments
 (0)