-
Notifications
You must be signed in to change notification settings - Fork 26.1k
Native OSQ scoring #134623
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
Native OSQ scoring #134623
Changes from 2 commits
0e526ce
ebd61aa
203f9d6
1993bef
f03193b
b147d39
68a3d9b
da9aa0e
2e1b33e
c16a04e
0c20cf2
cc9d3db
274e9ba
fbee06a
e21444f
e287632
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| pr: 134623 | ||
| summary: Native OSQ scoring | ||
| area: Vector Search | ||
| type: enhancement | ||
| issues: [] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -299,3 +299,81 @@ EXPORT float sqrf32(const float *a, const float *b, size_t elementCount) { | |
|
|
||
| return result; | ||
| } | ||
|
|
||
| EXPORT int64_t int4Bit(uint8_t* query, uint8_t* doc, int64_t offset, int length) { | ||
| const size_t stride = (length / 8) * 8; | ||
| uint64_t dot_q0 = 0; | ||
| uint64_t dot_q1 = 0; | ||
| uint64_t dot_q2 = 0; | ||
| uint64_t dot_q3 = 0; | ||
| const uint8_t* doc_idx = doc + offset; | ||
| const uint8_t* query_j0 = query; | ||
| const uint8_t* query_j1 = query + length; | ||
| const uint8_t* query_j2 = query + 2 * length; | ||
| const uint8_t* query_j3 = query + 3 * length; | ||
| int i = 0; | ||
| for (; i < stride; i += 8) { | ||
| const uint64_t qv0 = *(const uint64_t*)(query_j0 + i); | ||
| const uint64_t qv1 = *(const uint64_t*)(query_j1 + i); | ||
| const uint64_t qv2 = *(const uint64_t*)(query_j2 + i); | ||
| const uint64_t qv3 = *(const uint64_t*)(query_j3 + i); | ||
| const uint64_t yv = *(const uint64_t*)(doc_idx + i); | ||
| dot_q0 += __builtin_popcountll(qv0 & yv); | ||
| dot_q1 += __builtin_popcountll(qv1 & yv); | ||
| dot_q2 += __builtin_popcountll(qv2 & yv); | ||
| dot_q3 += __builtin_popcountll(qv3 & yv); | ||
| } | ||
| for (; i < length; i++) { | ||
| const uint8_t qv0 = *(query_j0 + i); | ||
| const uint8_t qv1 = *(query_j1 + i); | ||
| const uint8_t qv2 = *(query_j2 + i); | ||
| const uint8_t qv3 = *(query_j3 + i); | ||
| const uint8_t yv = *(doc_idx + i); | ||
| dot_q0 += __builtin_popcountll(qv0 & yv); | ||
| dot_q1 += __builtin_popcountll(qv1 & yv); | ||
| dot_q2 += __builtin_popcountll(qv2 & yv); | ||
| dot_q3 += __builtin_popcountll(qv3 & yv); | ||
| } | ||
| return dot_q0 + (dot_q1 << 1) + (dot_q2 << 2) + (dot_q3 << 3); | ||
| } | ||
|
|
||
| EXPORT int32_t int4BitBulk(uint8_t* query, uint8_t* doc, int64_t offset, float32_t* scores, int count, int length) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, I wonder if we would get even more speedups if we completely switched to also applying the corrections natively in the same function....I realize that this might switch some logic around up stream and possibly require another native method. But I think I would expect this to play big dividends on larger block sizes.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that can be a great follow up. |
||
| const size_t stride = (length / 8) * 8; | ||
| const uint8_t* query_j0 = query; | ||
| const uint8_t* query_j1 = query + length; | ||
| const uint8_t* query_j2 = query + 2 * length; | ||
| const uint8_t* query_j3 = query + 3 * length; | ||
| // assumption that the query bits are 4, and doc bits are singular | ||
| for (size_t idx = 0; idx < count; idx++) { | ||
| uint64_t dot_q0 = 0; | ||
| uint64_t dot_q1 = 0; | ||
| uint64_t dot_q2 = 0; | ||
| uint64_t dot_q3 = 0; | ||
| const uint8_t* doc_idx = doc + offset + idx * length; | ||
| int i = 0; | ||
| for (; i < stride; i += 8) { | ||
| const uint64_t qv0 = *(const uint64_t*)(query_j0 + i); | ||
| const uint64_t qv1 = *(const uint64_t*)(query_j1 + i); | ||
| const uint64_t qv2 = *(const uint64_t*)(query_j2 + i); | ||
| const uint64_t qv3 = *(const uint64_t*)(query_j3 + i); | ||
| const uint64_t yv = *(const uint64_t*)(doc_idx + i); | ||
| dot_q0 += __builtin_popcountll(qv0 & yv); | ||
| dot_q1 += __builtin_popcountll(qv1 & yv); | ||
| dot_q2 += __builtin_popcountll(qv2 & yv); | ||
| dot_q3 += __builtin_popcountll(qv3 & yv); | ||
| } | ||
| for (; i < length; i++) { | ||
| const uint8_t qv0 = *(query_j0 + i); | ||
| const uint8_t qv1 = *(query_j1 + i); | ||
| const uint8_t qv2 = *(query_j2 + i); | ||
| const uint8_t qv3 = *(query_j3 + i); | ||
| const uint8_t yv = *(doc_idx + i); | ||
| dot_q0 += __builtin_popcountll(qv0 & yv); | ||
| dot_q1 += __builtin_popcountll(qv1 & yv); | ||
| dot_q2 += __builtin_popcountll(qv2 & yv); | ||
| dot_q3 += __builtin_popcountll(qv3 & yv); | ||
| } | ||
| scores[idx] = (float32_t)(dot_q0 + (dot_q1 << 1) + (dot_q2 << 2) + (dot_q3 << 3)); | ||
| } | ||
| return count; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -346,3 +346,11 @@ EXPORT float sqrf32(const float *a, const float *b, size_t elementCount) { | |
|
|
||
| return result; | ||
| } | ||
|
|
||
| EXPORT int64_t int4Bit(uint8_t* query, uint8_t* doc, int64_t offset, int length) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you want/need help with the x86 version give me a shout; you can get a look of what's needed for int4 manipulation here: #109238
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure I am going to need help. I have never work on this area and it seems you need docker, and I don't have a license at the moment.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we proceed with this change for AArch64 first (then x64 later) let's add a negative error return here, so that we can assert a non-negative from the caller? |
||
| return 0; | ||
| } | ||
|
|
||
| EXPORT int32_t int4BitBulk(uint8_t* query, uint8_t* doc, int64_t offset, float32_t* scores, size_t count, size_t dims) { | ||
| return 0; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you know, this is good for local testing but will need to be reverted before merging.