-
-
Notifications
You must be signed in to change notification settings - Fork 739
Make import of std.internal.unicode_tables lazy. #5945
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
Conversation
There's no need to open and open and parse the gigantic `std.internal.unicode_tables` if it's never needed.
Thanks for your pull request, @wilzbach! Bugzilla referencesYour PR doesn't reference any Bugzilla issue. If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog. |
std/uni.d
Outdated
alias UpperTriple = AliasSeq!(toUpperIndex, MAX_SIMPLE_UPPER, toUpperTab); | ||
|
||
enum MAX_SIMPLE_LOWER = 1043; | ||
alias LowerTriple = AliasSeq!(toLowerIndex, MAX_SIMPLE_LOWER, toLowerTab); |
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.
This is the only bit I couldn't figure out how to do lazily. Better ideas?
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.
Where are these aliases used? Since they were originally private, you may be able to just rewrite the places where they're used to lazily instantiate the definition. Or maybe something like:
private alias UpperTriple() = ...
might do the trick? Not 100% this syntax is supported, or whether it will do what I think it will do.
Another module:
With
|
I managed to make a couple of other imports lazy and now Before: parse foo
importall foo
import object (/usr/include/dlang/dmd/object.d)
import std.uni (/usr/include/dlang/dmd/std/uni.d)
import std.meta (/usr/include/dlang/dmd/std/meta.d)
import std.traits (/usr/include/dlang/dmd/std/traits.d)
import std.functional (/usr/include/dlang/dmd/std/functional.d)
import std.range.primitives (/usr/include/dlang/dmd/std/range/primitives.d)
import std.internal.unicode_tables (/usr/include/dlang/dmd/std/internal/unicode_tables.d)
semantic foo
import std.range (/usr/include/dlang/dmd/std/range/package.d)
import std.typecons (/usr/include/dlang/dmd/std/typecons.d)
import core.stdc.stdint (/usr/include/dlang/dmd/core/stdc/stdint.d)
import core.stdc.stddef (/usr/include/dlang/dmd/core/stdc/stddef.d)
import core.stdc.signal (/usr/include/dlang/dmd/core/stdc/signal.d)
import core.stdc.wchar_ (/usr/include/dlang/dmd/core/stdc/wchar_.d)
import core.stdc.config (/usr/include/dlang/dmd/core/stdc/config.d)
import core.stdc.stdarg (/usr/include/dlang/dmd/core/stdc/stdarg.d)
import core.stdc.stdlib (/usr/include/dlang/dmd/core/stdc/stdlib.d)
import core.stdc.stdio (/usr/include/dlang/dmd/core/stdc/stdio.d)
import core.stdc.time (/usr/include/dlang/dmd/core/stdc/time.d)
import core.sys.posix.sys.types (/usr/include/dlang/dmd/core/sys/posix/sys/types.d)
import core.sys.posix.config (/usr/include/dlang/dmd/core/sys/posix/config.d)
import std.array (/usr/include/dlang/dmd/std/array.d)
import std.algorithm.iteration (/usr/include/dlang/dmd/std/algorithm/iteration.d)
import std.range.interfaces (/usr/include/dlang/dmd/std/range/interfaces.d)
import std.format (/usr/include/dlang/dmd/std/format.d)
import core.vararg (/usr/include/dlang/dmd/core/vararg.d)
import std.exception (/usr/include/dlang/dmd/std/exception.d)
semantic2 foo
semantic3 foo
code foo After parse foo
importall foo
import object (/home/seb/dlang/druntime/import/object.d)
import std.uni (/home/seb/dlang/phobos/std/uni.d)
import std.meta (/home/seb/dlang/phobos/std/meta.d)
import std.traits (/home/seb/dlang/phobos/std/traits.d)
import std.functional (/home/seb/dlang/phobos/std/functional.d)
import std.range.primitives (/home/seb/dlang/phobos/std/range/primitives.d)
semantic foo
semantic2 foo
semantic3 foo
code foo |
@@ -1588,7 +1588,7 @@ private auto packedArrayView(T)(inout(size_t)* ptr, size_t items) @trusted pure | |||
// Partially unrolled binary search using Shar's method | |||
//============================================================================ | |||
|
|||
string genUnrolledSwitchSearch(size_t size) @safe pure nothrow | |||
auto genUnrolledSwitchSearch(size_t size) @safe pure nothrow |
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.
What's the reason for this change?
} | ||
|
||
@safe pure nothrow @nogc @property | ||
{ | ||
import std.internal.unicode_tables; // generated file | ||
|
||
// It's important to use auto return here, so that the compiler | ||
// only runs semantic on the return type if the function gets | ||
// used. Also these are functions rather than templates to not | ||
// increase the object size of the caller. |
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.
I'm not sure I understand the logic of the comment here. Why would templates increase the object size of the caller if they never get used? And does returning auto
really suppress the import inside the function body until the function is called?
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.
And does returning auto really suppress the import inside the function body until the function is called?
Yes it does, the compiler needs to have the full function body and run semantic3 on it to determine the return type. Auto return functions behave a lot like templates in that sense, they don't get compiled into the call-site though (AFAIR).
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.
In general I uge you to check how it performs after this surgery.
Secondly putting all tables to templates means we get to see them duplicated in many object files. As Martin I think he was the last to optimize tgis w.r.t. dynamic libraries
{ | ||
/// Run-time checked search. | ||
static auto opCall(C)(in C[] name) | ||
if (is(C : dchar)) | ||
{ | ||
// lazily import the big unicode_table | ||
mixin(q{ |
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.
OMG, this going beyond acceptable mutilation imho.
@@ -6095,6 +6099,10 @@ template SetSearcher(alias table, string kind) | |||
/// Compile-time checked search. | |||
static @property auto opDispatch(string name)() | |||
{ | |||
// lazily import the big unicode_table | |||
mixin(q{ | |||
import std.internal.unicode_tables; |
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.
Same here
Maybe another approach that will speed up imports without massive mutilation of |
ping @wilzbach @DmitryOlshansky Any ideas how to move this forward? |
Can we use |
Yeah that should work in theory, but then we would probably have to change the layout of Phobos a bit (e.g. like Druntime with it's |
Tried it here: #6123. |
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.
Looks OKish though I'm slightly wondering about some function -> template conversions.
Functions have the advantage that they are shipped pre-compiled with the phobos release.
|
||
ushort toUpperIndex(dchar c) { return toUpperIndexTrie[c]; } | ||
ushort toUpperSimpleIndex(dchar c) { return toUpperSimpleIndexTrie[c]; } | ||
dchar toUpperTab(size_t idx) { return toUpperTable[idx]; } | ||
dchar toUpperTab()(size_t idx) { |
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.
Why not a function?
Actually making things a function above was a decision to also save on codegen time when sth. gets used, because the binary is already in phobos. Imports in function bodies do not leak to the call site, so you also save those when merely calling.
The compiler can still inline things if found appropriate at which point you pay the same price as for instantiating the template.
// lazily import the big unicode_table | ||
mixin(q{ | ||
import std.internal.unicode_tables; | ||
alias table = } ~ tableName ~ ";"); |
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.
How about
mixin("std.internal.unicode_tables : table="~tableName~";")
instead?
@@ -6218,7 +6226,7 @@ auto caseEnclose(CodepointSet set) | |||
/+ | |||
fetch codepoint set corresponding to a name (InBlock or binary property) | |||
+/ | |||
@trusted CodepointSet getUnicodeSet(in char[] name, bool negated, bool casefold) |
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.
Why make it a template?
Dmd prefers importing .di over .d files, so you could have them side by side. But our experience with .di files in druntime was fairly bad, try to avoid them. |
PR closed as stalled. If you wish to resurrect it you are welcome to do so. |
There's no need to open and open and parse the gigantic
std.internal.unicode_tables
if it's never needed.My journey:
std.net.curl
->etc.c.curl
->std.socket
->std.stdio
->std.uni
->std.internal.unicode_tables
.Follow-up to #5916
Alternatively a benchmark with
avgtime
: