forked from dlang/dlang.org
-
Notifications
You must be signed in to change notification settings - Fork 0
/
migrate-to-shared.dd
229 lines (184 loc) · 5.8 KB
/
migrate-to-shared.dd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
Ddoc
$(D_S $(TITLE),
$(P Starting with $(LINK2 changelog/2.030.html, dmd version 2.030),
the default storage class
for statics and globals will be
$(LINK2 glossary.html#tls, thread local storage (TLS)), rather
than the classic global data segment.
While most D code should just compile and run successfully without
change, there can be some issues.
)
$(OL
$(LI $(RELATIVE_LINK2 performance, Performance of TLS variables).)
$(LI $(RELATIVE_LINK2 vtls, Identifying TLS variables).)
$(LI $(RELATIVE_LINK2 immutable, Switch to Immutable).)
$(LI $(RELATIVE_LINK2 shared, Marking as shared).)
$(LI $(RELATIVE_LINK2 gshared, Cowboying with __gshared).)
$(LI $(RELATIVE_LINK2 compile-errors, Compile errors).)
$(LI $(RELATIVE_LINK2 link-errors, Link errors).)
)
$(H3 $(LNAME2 performance, Performance of TLS variables))
$(P Yes, reading or writing TLS variables will be slower
than for classic global variables. It'll be about 3
instructions rather than one.
But on Linux, at least, TLS will be slightly faster
than accessing classic globals using PIC (position
independent code) compiler code generation settings.
So it's hard to see that this would be an unacceptable
problem. But let's presume it is. What can we do about it?
)
$(OL
$(LI Minimize use of global variables. Reducing the use
of globals can improve the modularization and maintainability
of the code anyway, so this is a worthy goal.)
$(LI Make them immutable. Immutable data doesn't have
synchronization problems, so the compiler doesn't place it
in TLS.)
$(LI Cache a reference to the global. Making a local cache
of it, and then accessing the cached value rather than the
original, can speed things up especially if the cached value
gets enregistered by the compiler.)
$(LI Cowboy it with $(D __gshared).)
)
$(H3 $(LNAME2 vtls, Identifying TLS variables))
$(P The first step is to find all the global variables,
so they can be reviewed for disposition.
)
$(P Given the complexity of source code, it isn't always
easy to identify the global variables. Since it (used to be)
implicit, there's no way to grep for them. It's hard
to be sure you've found them all.
)
$(P A new dmd compiler switch was added, $(B -vtls).
Compiling with it on will print a list of all the global
variables that are now defaulting to thread local storage.
)
---
int x;
void main()
{
static int y;
}
---
$(CONSOLE
dmd test $(B -vtls)
test.d(2): x is thread local
test.d(6): y is thread local
)
$(H3 $(LNAME2 immutable, Switch to Immutable))
$(P Immutable data, once initialized, never changes.
This means that there are no synchronization issues
with multithreading, and so no need to put immutable data
into TLS. The compiler will put immutable data into
classic global storage, not TLS.
)
$(P A good chunk of global data falls into this category.
Just mark it as $(D immutable) and it's done.
)
---
int[3] table = [6, 123, 0x87];
---
$(P becomes:)
---
immutable int[3] table = [6, 123, 0x87];
---
$(P Immutability is also nice because it opens the door
for further compiler optimizations.
)
$(H3 $(LNAME2 shared, Marking As Shared))
$(P Global data that is meant to be shared among multiple
threads should be marked with the $(D shared) keyword:
)
---
shared int flag;
---
$(P Not only does this cause $(CODE flag) to be put into
classic global storage, it is also typed as being shared:
)
---
int* p = &flag; // error, flag is shared
shared(int)* q = &flag; // ok
---
$(P The $(CODE shared) type attribute is transitive (like
$(CODE const) and $(CODE immutable)) are. This enables
static checking and sharing correctness.
)
$(H3 $(LNAME2 gshared, Cowboying With __gshared))
$(P Sometimes, none of the above solutions will be
acceptable:
)
$(OL
$(LI interfacing with C code that uses classic globals)
$(LI just get it working, and go back and fix it later)
$(LI the app is single threaded only, so no sharing issues)
$(LI you need every erg of performance)
$(LI you want to handle all the synchronization issues
yourself)
)
$(P As D is a systems language, of course there's a way to
do this. Use the storage class $(D __gshared):
)
---
__gshared int x;
void main()
{
__gshared int y;
}
---
$(P $(D __gshared) stores the variable in the classic global
data segment.
)
$(P Naturally, $(D __gshared) is not allowed in safe mode.
)
$(P Using $(D __gshared) makes such code easily searchable
when doing QA code reviews or when going back later to
fix any workarounds.
)
$(H3 $(LNAME2 compile-errors, Compile Errors))
$(P The most common compiler error related to TLS will be:
)
---
int x;
int* p = &x;
---
$(CONSOLE
test.d(2): Error: non-constant expression & x
)
$(P While this works with classic global variables, it
won't work with TLS variables. The reason is because
TLS variables don't have a location in memory known to
either the linker or the loader. It's a runtime computed
value.)
$(P The solution is to initialize such things in
a static constructor.)
$(H3 $(LNAME2 link-errors, Link Errors))
$(P Sometimes you may encounter strange error messages from the linker
about the global variables.
These are nearly always caused by one module putting the variable
in TLS, and another putting that same variable in classic global
storage.
This can happen when linking code with libraries that were built
with earlier versions of dmd. Check that libphobos2.a was properly
replaced with the latest.
It can also happen when interfacing with C. C globals default
to being classic global, although C does support TLS declarations.
Make sure the corresponding D declarations match the C ones in
terms of TLS or classic global.
)
$(CCODE
int x;
extern int y;
__thread int z;
)
---
extern (C)
{
extern shared int x;
shared int y;
extern int z;
}
---
)
Macros:
TITLE=Migrating to Shared
SUBNAV=$(SUBNAV_ARTICLES)