Skip to content

Implement UTF16 to UTF8 length with replacement for rvv architecture - #890

Open
sleepingeight wants to merge 2 commits into
simdutf:masterfrom
sleepingeight:surya/rvv-f1
Open

Implement UTF16 to UTF8 length with replacement for rvv architecture#890
sleepingeight wants to merge 2 commits into
simdutf:masterfrom
sleepingeight:surya/rvv-f1

Conversation

@sleepingeight

Copy link
Copy Markdown
Contributor

Short title (summary):
Implement UTF16 to UTF8 length with replacement for rvv architecture

Description
Related to #853.
The function is implemented in the following manner, invalid surrogates are found by right shifting and left shifting the data vector and checking whether high surrogate is followed by low surrogate, this count is added to the original count from utf8_to_utf16_length to get the total length.

Type of change

  • Bug fix
  • Optimization
  • New feature
  • Refactor / cleanup
  • Documentation / tests
  • Other (please describe):

Checklist before submitting

  • I added/updated tests covering my change (if applicable)
  • Code builds locally and passes my check
  • Documentation / README updated if needed
  • Commits are atomic and messages are clear
  • I linked the related issue (if applicable)

@sleepingeight

Copy link
Copy Markdown
Contributor Author

Although the idea seemed simple, I've found that the function is performing poorly in benchmarks. On my machine Apple Mac M1 Pro (ARM), the benchmarks are as follows -

> ./build/benchmarks/benchmark  -P utf8_length_from_utf16le  utf8_length_from_utf16le_with_replacement   -F ~/emoji.txt -I 100000
We define the number of bytes to be the number of *input* bytes.
We define a 'char' to be a code point (between 1 and 4 bytes).
Using ICU version 78.1
Using iconv version 267
Compiler: Clang 16.0.0
SIMDUTF version: 7.7.1
System: arm64
===========================
testcases: 1
input detected as UTF8
===========================
utf8_length_from_utf16le+arm64, input size: 3150, iterations: 100000, dataset: ~/emoji.txt
  16.573 GB/s (5.1 %)   16.573 Gc/s     1.00 byte/char     95.0 ns
utf8_length_from_utf16le_with_replacement+arm64, input size: 3150, iterations: 100000, dataset: ~/emoji.txt
  12.472 GB/s (3.0 %)   12.472 Gc/s     1.00 byte/char    126.3 ns

I see a 25% performance decrease. Whereas the rvv is performing in the following way -

$ qemu-riscv64-static   -L /lib/riscv64-linux-gnu   -E LD_LIBRARY_PATH=/opt/icu-riscv64/lib:/lib/riscv64-linux-gnu   ./build/benchmarks/benchmark -P  utf8_length_from_utf16le  utf8_length_from_utf16le_with_replacement   -F ~/emoji.txt -I 100000
We define the number of bytes to be the number of *input* bytes.
We define a 'char' to be a code point (between 1 and 4 bytes).
Using ICU version 74.2
Compiler: Clang 18.1.3
SIMDUTF version: 7.7.1
System: rvv
===========================
testcases: 1
input detected as UTF8
===========================
utf8_length_from_utf16le+rvv, input size: 3150, iterations: 100000, dataset: ~/emoji.txt
   0.043 GB/s (9.7 %)    0.043 Gc/s     1.00 byte/char  36709.0 ns
utf8_length_from_utf16le_with_replacement+rvv, input size: 3150, iterations: 100000, dataset: ~/emoji.txt
   0.010 GB/s (1.8 %)    0.010 Gc/s     1.00 byte/char 150793.0 ns

Here, I've found a 75% decrease. Please suggest ways to improve the performance of this procedure.
cc: @lemire

@sleepingeight

Copy link
Copy Markdown
Contributor Author

The scalar code is performing way better -

$ qemu-riscv64-static   -L /lib/riscv64-linux-gnu   -E LD_LIBRARY_PATH=/opt/icu-riscv64/lib:/lib/riscv64-linux-gnu   ./build/benchmarks/benchmark -P  utf8_length_from_utf16le  utf8_length_from_utf16le_with_replacement   -F ~/emoji.txt -I 100000
We define the number of bytes to be the number of *input* bytes.
We define a 'char' to be a code point (between 1 and 4 bytes).
Using ICU version 74.2
Compiler: Clang 18.1.3
SIMDUTF version: 7.7.1
System: rvv
===========================
testcases: 1
input detected as UTF8
===========================
utf8_length_from_utf16le+rvv, input size: 3150, iterations: 100000, dataset: ~/emoji.txt
   0.042 GB/s (1.6 %)    0.042 Gc/s     1.00 byte/char  37250.0 ns
utf8_length_from_utf16le_with_replacement+rvv, input size: 3150, iterations: 100000, dataset: ~/emoji.txt
   0.450 GB/s (4.6 %)    0.450 Gc/s     1.00 byte/char   3500.0 ns

Is this regression due to inefficient emulation?

@lemire

lemire commented Jan 1, 2026

Copy link
Copy Markdown
Member

@sleepingeight I assume you are running this through an emulator ? If so, we can't really tell much about your benchmark results.

I would be happy to merge this in any case... we can always revise the implementation given actual hardware we care about.

@sleepingeight

Copy link
Copy Markdown
Contributor Author

Hi @lemire, could you please suggest next steps to take this PR forward.

@lemire

lemire commented Feb 5, 2026

Copy link
Copy Markdown
Member

@sleepingeight I am game to merge your PR if you recommend it.

In any case, the whole RVV thing is experimental as far as performance goes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants