This improves the benchmark result for long strings and does not
degrade it on short strings.
And fix the benchmark's randomizer. The hacky attempt to avoid the
surrogate pair range inadvertently prevented random strings from
containing any code point requiring exactly 2 encoding bytes.
It comes in two-and-a-half variations: accepting a SAP or ub8 vector,
and if a SAP then either counted or null-terminated. In the case where
it returns base-string as determined by a pre-scan of the input, it runs
nearly twice as fast as octets-to-string
The #+ultrafutex feature which is nearly ready for use can acquire + release
a mutex in 1/3rd the time of WITH-MUTEX. This is an improvement upon the old
results in benchmarks/grab-mutex which showed maybe a 1.5x typical speedup.
The new code is NLX-protected but *NOT* async-unwind or interrupt-safe, nor can
it detect deadlocks. It did pass the entire regression suite, less one file.
The rationale is that a debug build of code might demand maximum robustness
while a release build wants sheer speed. This patch is a counterpoint to the
adage that Lisp programmers know the value of everything but the cost of
nothing; you should be able to choose your price for mutexes.
This was performance-neutral in my limited testing- the regression
suite consumes the same amount of time before and after. Given that the
compiler probably takes a little longer due to deciding to emit barriers,
the runtime of the code under test seems as good if not better.
Thanks to Stas for some help with Darwin changes.
On x86, the stack guard page could get missed when allocating large
objects on the stack (over 1 page). Instead of giving up or only
allowing this on low safety, just check explicitly whether an overflow
could happen and signal an appropriate condition.
Other platforms don't need this overflow check as urgently, since they
do zero-filling which ensures that they will in fact hit the guard
page eventually before the stack is manipulated again. (Although there
may not be any space left for the handler to do its thing. In any
case, a similar check could be added.)
We can remove the TRULY-DYNAMIC-EXTENT declaration now, since its only
purpose was to facilitate the old way of just avoiding allocations
that could silently overflow the stack. If we really want to skip the
overflow check during self-build always, that could just be achieved
via a special variable. No need to have an extra "internal-only"
declaration to do that. This simplifies frontend processing of dynamic
extent.
Add a test exhibiting large stack allocation interacting with the
limited stack size.
A single multiplication will suffice. Also change MASKED-HASH to avoid
use of the large constant - instead require that the the hash function
return only non-negative fixnums.
The benchmark shows that for 20 concurrent readers, solist outperforms
a synchronized hash-table by up to 50x now.
This shows a few things:
* S-O lists are terribly wasteful of memory, using about 8 words per key
though producing very little garbage during construction.
* Balanced trees need about 3 words per key in the final tree,
but produce lots of intermediate garbage.
* Red/Black can be faster at lookup than brother trees, probably because
the latter requires a test of each node type before descending.
The Red/Black algorithm lacked the optimization to omit storage of the
left and right children of fringe nodes. I thought adding that in might
make the code as efficient as brothertrees, but it doesn't.
This replaces the usual hash-table with a new storage representation
that is almost always lockfree, and removes all complexity around
assigning a unique ID to each finalized object.
The ugly hack in cull_weak_hash_table_bucket() is no more.
As the benchmark shows, with 4 threads we can create finalizers
about 3x faster at the cost of about 1.5x more memory.
Fixes lp#1998064 where finalizers are concerned, but if the bug
exists more generally, this doesn't do anything for it.
This got about 5% speedup in the FIND-SYMBOL benchmark for those CPUs,
less than on x86-64, but I'll bet they run the string hashing step more
slowly, so the remainder operation in %LOOKUP-SYMBOL is a smaller
contributor to the total time. Amdahl's law pertains.
Also implement fastrem-64 in case we need it.
The net effect on FIND-SYMBOL is dependent on package-use-list of the argument
package, but certainly for CL and KEYWORD the improvement is very obvious.
We can get at least 25% speedup by eschewing all the async unwind crap.
There's also some improvement possible by using the mutex3 algorithm
from the "Futexes are Tricky" paper. These tests also show that it is
theoretically possible to retain synchronous NLX safety by chaining all
owned mutexes onto a dynamic-extent list; when unwinding the binding
stack due to NLX we would just need to release each held mutex.
As to async unwind safety, it is mostly unreliable garbage anyway,
and nobody should need it in production code. Performance would presumably
not be a concern for a subset of users. It's just a question of how to
convert from the old API to a new one that does not promise asynchronous
interrupt safety where users have come to expect it.