Skip to content
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

RFC: represent length of short strings using 1 byte instead of 8 #40727

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

JeffBezanson
Copy link
Sponsor Member

Since the dawn of time, mankind has sought to make things smaller. Currently a String on 64-bit is at least 32 bytes: type tag, length, data, plus alignment to the next multiple of 16. This PR uses a 1-byte length with the low bit as a discriminator for short strings, allowing <= 6 byte strings to fit in 16 bytes.

  • Might be slower due to more complex representation; obviously need to run all string benchmarks
  • Beginning of small string data is no longer aligned, causing problems for potential SIMD optimizations

@JeffBezanson JeffBezanson added performance Must go faster strings "Strings!" labels May 5, 2021
@oscardssmith
Copy link
Member

oscardssmith commented May 5, 2021

Copying a benchmark from slack (by Harmen Stopples)

julia> function example()
         for j = 1:20, i = 1:1_000_000
           string(i)
         end
       end
example (generic function with 1 method)

# jb/shortstring
julia> @time example()
  0.542978 seconds (40.00 M allocations: 1.490 GiB, 6.31% gc time)

# 1.6.1
julia> @time example()
  0.592058 seconds (40.00 M allocations: 1.788 GiB, 2.76% gc time)

@JeffBezanson
Copy link
Sponsor Member Author

Ah, I guess MPFR might depend on the data being aligned on some architectures.

@StefanKarpinski
Copy link
Sponsor Member

Very cool! I only caught the tail end of your twitch stream but wish I'd tuned in earlier. This still doesn't allow an array of Strings to be stored inline, right? That would still be a vector of pointers since Strings don't have a fixed size.

@JeffBezanson
Copy link
Sponsor Member Author

Right.

It would be interesting to explore pointer tagging for this, though a more dramatic change of course. Would pose challenges for code that uses the address of the data, when to the compiler it's just a value in a register. The MPFR code is a good example: the struct stores both a string and its address. In the inlined case, the address is inside the enclosing object, and so can't be computed from just the string itself. Solvable of course, but produces various non-obvious cases like that. (Ok, a BigFloat would always be more than 8 bytes, but you get the idea 😄 ).

src/array.c Outdated
size_t sz = len + 1 + (len < 128 ? 1 : sizeof(size_t));
#else
size_t sz = len + 1 + sizeof(size_t);
#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This size getting should probably be extracted to a static function.

@JeffreySarnoff
Copy link
Contributor

We should not feel bound to MPFR's design decisions where it possible to continue in a manner that better fits our forward. MPFR is wonderfully well cared-for by a very few; this does not allow the project time to use for design arcs.

@JeffBezanson
Copy link
Sponsor Member Author

JeffBezanson commented May 6, 2021

Fortunately we can hack around it for MPFR by over-allocating and aligning manually. We could even have a call to request an aligned string (i.e. request the full-word-length representation).

Here's another microbenchmark that shows some time overhead:

before:

julia> @time for i = 1:2^24
       "a"*"b"
       end
  0.307642 seconds (16.78 M allocations: 512.000 MiB, 4.62% gc time)

julia> @btime "a"*"b"
  14.522 ns (1 allocation: 32 bytes)

after:

julia> @time for i = 1:2^24
       "a"*"b"
       end
  0.330849 seconds (16.78 M allocations: 256.000 MiB, 1.74% gc time)

julia> @btime "a"*"b"
  17.319 ns (1 allocation: 16 bytes)

@JeffBezanson
Copy link
Sponsor Member Author

@nanosoldier runbenchmarks("string", vs=":master")

@nanosoldier
Copy link
Collaborator

Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. cc @christopher-dG

@JeffBezanson
Copy link
Sponsor Member Author

Yeah, there are some slowdowns...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
performance Must go faster strings "Strings!"
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants