Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(827)

Side by Side Diff: Source/bindings/core/v8/V8ScriptRunner.cpp

Issue 886713003: Add histograms for cacheable and noncacheable compilation times. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2009 Google Inc. All rights reserved. 2 * Copyright (C) 2009 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
(...skipping 19 matching lines...) Expand all
30 #include "bindings/core/v8/ScriptStreamer.h" 30 #include "bindings/core/v8/ScriptStreamer.h"
31 #include "bindings/core/v8/V8Binding.h" 31 #include "bindings/core/v8/V8Binding.h"
32 #include "bindings/core/v8/V8GCController.h" 32 #include "bindings/core/v8/V8GCController.h"
33 #include "bindings/core/v8/V8RecursionScope.h" 33 #include "bindings/core/v8/V8RecursionScope.h"
34 #include "bindings/core/v8/V8ThrowException.h" 34 #include "bindings/core/v8/V8ThrowException.h"
35 #include "core/dom/ExecutionContext.h" 35 #include "core/dom/ExecutionContext.h"
36 #include "core/fetch/CachedMetadata.h" 36 #include "core/fetch/CachedMetadata.h"
37 #include "core/fetch/ScriptResource.h" 37 #include "core/fetch/ScriptResource.h"
38 #include "platform/ScriptForbiddenScope.h" 38 #include "platform/ScriptForbiddenScope.h"
39 #include "platform/TraceEvent.h" 39 #include "platform/TraceEvent.h"
40 #include "public/platform/Platform.h"
40 #include "third_party/snappy/src/snappy.h" 41 #include "third_party/snappy/src/snappy.h"
41 #include "wtf/CurrentTime.h" 42 #include "wtf/CurrentTime.h"
42 43
43 #if defined(WTF_OS_WIN) 44 #if defined(WTF_OS_WIN)
44 #include <malloc.h> 45 #include <malloc.h>
45 #else 46 #else
46 #include <alloca.h> 47 #include <alloca.h>
47 #endif 48 #endif
48 49
49 namespace blink { 50 namespace blink {
50 51
51 namespace { 52 namespace {
52 53
53 const int kMaxRecursionDepth = 22; 54 const int kMaxRecursionDepth = 22;
54 55
56 class V8CompileHistogram {
57 public:
58 enum Cacheability { Cacheable, Noncacheable };
59 explicit V8CompileHistogram(Cacheability);
60 ~V8CompileHistogram();
61
62 private:
63 Cacheability m_cacheability;
64 double m_timeStamp;
65 };
66
67 V8CompileHistogram::V8CompileHistogram(V8CompileHistogram::Cacheability cacheabi lity)
68 : m_cacheability(cacheability)
69 , m_timeStamp(WTF::currentTime())
70 {
71 }
72
73 V8CompileHistogram::~V8CompileHistogram()
74 {
75 int64_t elapsedMicroSeconds = static_cast<int64_t>((WTF::currentTime() - m_t imeStamp) * 1000000);
76 const char* name = (m_cacheability == Cacheable) ? "V8.CompileCacheableMicro Seconds" : "V8.CompileNoncacheableMicroSeconds";
77 blink::Platform::current()->histogramCustomCounts(name, elapsedMicroSeconds, 0, 1000000, 50);
78 }
79
55 // In order to make sure all pending messages to be processed in 80 // In order to make sure all pending messages to be processed in
56 // v8::Function::Call, we don't call handleMaxRecursionDepthExceeded 81 // v8::Function::Call, we don't call handleMaxRecursionDepthExceeded
57 // directly. Instead, we create a v8::Function of 82 // directly. Instead, we create a v8::Function of
58 // throwStackOverflowException and call it. 83 // throwStackOverflowException and call it.
59 void throwStackOverflowException(const v8::FunctionCallbackInfo<v8::Value>& info ) 84 void throwStackOverflowException(const v8::FunctionCallbackInfo<v8::Value>& info )
60 { 85 {
61 V8ThrowException::throwRangeError(info.GetIsolate(), "Maximum call stack siz e exceeded."); 86 V8ThrowException::throwRangeError(info.GetIsolate(), "Maximum call stack siz e exceeded.");
62 } 87 }
63 88
64 v8::Local<v8::Value> throwStackOverflowExceptionIfNeeded(v8::Isolate* isolate) 89 v8::Local<v8::Value> throwStackOverflowExceptionIfNeeded(v8::Isolate* isolate)
65 { 90 {
66 if (V8PerIsolateData::from(isolate)->isHandlingRecursionLevelError()) { 91 if (V8PerIsolateData::from(isolate)->isHandlingRecursionLevelError()) {
67 // If we are already handling a recursion level error, we should 92 // If we are already handling a recursion level error, we should
68 // not invoke v8::Function::Call. 93 // not invoke v8::Function::Call.
69 return v8::Undefined(isolate); 94 return v8::Undefined(isolate);
70 } 95 }
71 V8PerIsolateData::from(isolate)->setIsHandlingRecursionLevelError(true); 96 V8PerIsolateData::from(isolate)->setIsHandlingRecursionLevelError(true);
72 v8::Local<v8::Value> result = v8::Function::New(isolate, throwStackOverflowE xception)->Call(v8::Undefined(isolate), 0, 0); 97 v8::Local<v8::Value> result = v8::Function::New(isolate, throwStackOverflowE xception)->Call(v8::Undefined(isolate), 0, 0);
73 V8PerIsolateData::from(isolate)->setIsHandlingRecursionLevelError(false); 98 V8PerIsolateData::from(isolate)->setIsHandlingRecursionLevelError(false);
74 return result; 99 return result;
75 } 100 }
76 101
77 // Compile a script without any caching or compile options. 102 // Compile a script without any caching or compile options.
78 v8::Local<v8::Script> compileWithoutOptions(v8::Isolate* isolate, v8::Handle<v8: :String> code, v8::ScriptOrigin origin) 103 v8::Local<v8::Script> compileWithoutOptions(V8CompileHistogram::Cacheability cac heability, v8::Isolate* isolate, v8::Handle<v8::String> code, v8::ScriptOrigin o rigin)
79 { 104 {
105 V8CompileHistogram histogramScope(cacheability);
80 v8::ScriptCompiler::Source source(code, origin); 106 v8::ScriptCompiler::Source source(code, origin);
81 return v8::ScriptCompiler::Compile(isolate, &source, v8::ScriptCompiler::kNo CompileOptions); 107 return v8::ScriptCompiler::Compile(isolate, &source, v8::ScriptCompiler::kNo CompileOptions);
82 } 108 }
83 109
84 // Compile a script, and consume a V8 cache that was generated previously. 110 // Compile a script, and consume a V8 cache that was generated previously.
85 v8::Local<v8::Script> compileAndConsumeCache(ScriptResource* resource, unsigned tag, v8::ScriptCompiler::CompileOptions compileOptions, bool compressed, v8::Iso late* isolate, v8::Handle<v8::String> code, v8::ScriptOrigin origin) 111 v8::Local<v8::Script> compileAndConsumeCache(ScriptResource* resource, unsigned tag, v8::ScriptCompiler::CompileOptions compileOptions, bool compressed, v8::Iso late* isolate, v8::Handle<v8::String> code, v8::ScriptOrigin origin)
86 { 112 {
113 V8CompileHistogram histogramScope(V8CompileHistogram::Cacheable);
87 CachedMetadata* cachedMetadata = resource->cachedMetadata(tag); 114 CachedMetadata* cachedMetadata = resource->cachedMetadata(tag);
88 const char* data = cachedMetadata->data(); 115 const char* data = cachedMetadata->data();
89 int length = cachedMetadata->size(); 116 int length = cachedMetadata->size();
90 std::string uncompressedOutput; 117 std::string uncompressedOutput;
91 bool invalidCache = false; 118 bool invalidCache = false;
92 if (compressed) { 119 if (compressed) {
93 if (snappy::Uncompress(data, length, &uncompressedOutput)) { 120 if (snappy::Uncompress(data, length, &uncompressedOutput)) {
94 data = uncompressedOutput.data(); 121 data = uncompressedOutput.data();
95 length = uncompressedOutput.length(); 122 length = uncompressedOutput.length();
96 } else { 123 } else {
97 invalidCache = true; 124 invalidCache = true;
98 } 125 }
99 } 126 }
100 v8::Local<v8::Script> script; 127 v8::Local<v8::Script> script;
101 if (invalidCache) { 128 if (invalidCache) {
102 script = compileWithoutOptions(isolate, code, origin); 129 v8::ScriptCompiler::Source source(code, origin);
130 script = v8::ScriptCompiler::Compile(isolate, &source, v8::ScriptCompile r::kNoCompileOptions);
103 } else { 131 } else {
104 v8::ScriptCompiler::CachedData* cachedData = new v8::ScriptCompiler::Cac hedData( 132 v8::ScriptCompiler::CachedData* cachedData = new v8::ScriptCompiler::Cac hedData(
105 reinterpret_cast<const uint8_t*>(data), length, v8::ScriptCompiler:: CachedData::BufferNotOwned); 133 reinterpret_cast<const uint8_t*>(data), length, v8::ScriptCompiler:: CachedData::BufferNotOwned);
106 v8::ScriptCompiler::Source source(code, origin, cachedData); 134 v8::ScriptCompiler::Source source(code, origin, cachedData);
107 script = v8::ScriptCompiler::Compile(isolate, &source, compileOptions); 135 script = v8::ScriptCompiler::Compile(isolate, &source, compileOptions);
108 invalidCache = cachedData->rejected; 136 invalidCache = cachedData->rejected;
109 } 137 }
110 if (invalidCache) 138 if (invalidCache)
111 resource->clearCachedMetadata(); 139 resource->clearCachedMetadata();
112 return script; 140 return script;
113 } 141 }
114 142
115 // Compile a script, and produce a V8 cache for future use. 143 // Compile a script, and produce a V8 cache for future use.
116 v8::Local<v8::Script> compileAndProduceCache(ScriptResource* resource, unsigned tag, v8::ScriptCompiler::CompileOptions compileOptions, bool compressed, Resourc e::MetadataCacheType cacheType, v8::Isolate* isolate, v8::Handle<v8::String> cod e, v8::ScriptOrigin origin) 144 v8::Local<v8::Script> compileAndProduceCache(ScriptResource* resource, unsigned tag, v8::ScriptCompiler::CompileOptions compileOptions, bool compressed, Resourc e::MetadataCacheType cacheType, v8::Isolate* isolate, v8::Handle<v8::String> cod e, v8::ScriptOrigin origin)
117 { 145 {
146 V8CompileHistogram histogramScope(V8CompileHistogram::Cacheable);
118 v8::ScriptCompiler::Source source(code, origin); 147 v8::ScriptCompiler::Source source(code, origin);
119 v8::Local<v8::Script> script = v8::ScriptCompiler::Compile(isolate, &source, compileOptions); 148 v8::Local<v8::Script> script = v8::ScriptCompiler::Compile(isolate, &source, compileOptions);
120 const v8::ScriptCompiler::CachedData* cachedData = source.GetCachedData(); 149 const v8::ScriptCompiler::CachedData* cachedData = source.GetCachedData();
121 if (cachedData) { 150 if (cachedData) {
122 const char* data = reinterpret_cast<const char*>(cachedData->data); 151 const char* data = reinterpret_cast<const char*>(cachedData->data);
123 int length = cachedData->length; 152 int length = cachedData->length;
124 std::string compressedOutput; 153 std::string compressedOutput;
125 if (compressed) { 154 if (compressed) {
126 snappy::Compress(data, length, &compressedOutput); 155 snappy::Compress(data, length, &compressedOutput);
127 data = compressedOutput.data(); 156 data = compressedOutput.data();
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
162 // depending on the page it appears in. The cache doesn't know anything 191 // depending on the page it appears in. The cache doesn't know anything
163 // about encodings, but the cached data is specific to one encoding. If we 192 // about encodings, but the cached data is specific to one encoding. If we
164 // later load the script from the cache and interpret it with a different 193 // later load the script from the cache and interpret it with a different
165 // encoding, the cached data is not valid for that encoding. 194 // encoding, the cached data is not valid for that encoding.
166 return (v8CacheDataVersion | kind) + StringHash::hash(resource->encoding()); 195 return (v8CacheDataVersion | kind) + StringHash::hash(resource->encoding());
167 } 196 }
168 197
169 // Store a timestamp to the cache as hint. 198 // Store a timestamp to the cache as hint.
170 void setCacheTimeStamp(ScriptResource* resource) 199 void setCacheTimeStamp(ScriptResource* resource)
171 { 200 {
172 double now = currentTime(); 201 double now = WTF::currentTime();
173 unsigned tag = cacheTag(CacheTagTimeStamp, resource); 202 unsigned tag = cacheTag(CacheTagTimeStamp, resource);
174 resource->setCachedMetadata(tag, reinterpret_cast<char*>(&now), sizeof(now), Resource::SendToPlatform); 203 resource->setCachedMetadata(tag, reinterpret_cast<char*>(&now), sizeof(now), Resource::SendToPlatform);
175 } 204 }
176 205
177 // Check previously stored timestamp. 206 // Check previously stored timestamp.
178 bool isResourceHotForCaching(ScriptResource* resource) 207 bool isResourceHotForCaching(ScriptResource* resource)
179 { 208 {
180 const double kCacheWithinSeconds = 36 * 60 * 60; 209 const double kCacheWithinSeconds = 36 * 60 * 60;
181 unsigned tag = cacheTag(CacheTagTimeStamp, resource); 210 unsigned tag = cacheTag(CacheTagTimeStamp, resource);
182 CachedMetadata* cachedMetadata = resource->cachedMetadata(tag); 211 CachedMetadata* cachedMetadata = resource->cachedMetadata(tag);
183 if (!cachedMetadata) 212 if (!cachedMetadata)
184 return false; 213 return false;
185 double timeStamp; 214 double timeStamp;
186 const int size = sizeof(timeStamp); 215 const int size = sizeof(timeStamp);
187 ASSERT(cachedMetadata->size() == size); 216 ASSERT(cachedMetadata->size() == size);
188 memcpy(&timeStamp, cachedMetadata->data(), size); 217 memcpy(&timeStamp, cachedMetadata->data(), size);
189 return (currentTime() - timeStamp) < kCacheWithinSeconds; 218 return (WTF::currentTime() - timeStamp) < kCacheWithinSeconds;
190 } 219 }
191 220
192 // Final compile call for a streamed compilation. Most decisions have already 221 // Final compile call for a streamed compilation. Most decisions have already
193 // been made, but we need to write back data into the cache. 222 // been made, but we need to write back data into the cache.
194 v8::Local<v8::Script> postStreamCompile(ScriptResource* resource, ScriptStreamer * streamer, v8::Isolate* isolate, v8::Handle<v8::String> code, v8::ScriptOrigin origin) 223 v8::Local<v8::Script> postStreamCompile(ScriptResource* resource, ScriptStreamer * streamer, v8::Isolate* isolate, v8::Handle<v8::String> code, v8::ScriptOrigin origin)
195 { 224 {
225 V8CompileHistogram histogramScope(V8CompileHistogram::Noncacheable);
196 v8::Local<v8::Script> script = v8::ScriptCompiler::Compile(isolate, streamer ->source(), code, origin); 226 v8::Local<v8::Script> script = v8::ScriptCompiler::Compile(isolate, streamer ->source(), code, origin);
197 227
198 // Whether to produce the cached data or not is decided when the 228 // Whether to produce the cached data or not is decided when the
199 // streamer is started. Here we only need to get the data out. 229 // streamer is started. Here we only need to get the data out.
200 const v8::ScriptCompiler::CachedData* newCachedData = streamer->source()->Ge tCachedData(); 230 const v8::ScriptCompiler::CachedData* newCachedData = streamer->source()->Ge tCachedData();
201 if (newCachedData) { 231 if (newCachedData) {
202 resource->clearCachedMetadata(); 232 resource->clearCachedMetadata();
203 v8::ScriptCompiler::CompileOptions options = streamer->compileOptions(); 233 v8::ScriptCompiler::CompileOptions options = streamer->compileOptions();
204 switch (options) { 234 switch (options) {
205 case v8::ScriptCompiler::kProduceParserCache: 235 case v8::ScriptCompiler::kProduceParserCache:
(...skipping 26 matching lines...) Expand all
232 262
233 // Select a compile function from any of the above, mainly depending on 263 // Select a compile function from any of the above, mainly depending on
234 // cacheOptions. 264 // cacheOptions.
235 PassOwnPtr<CompileFn> selectCompileFunction(V8CacheOptions cacheOptions, ScriptR esource* resource, v8::Handle<v8::String> code) 265 PassOwnPtr<CompileFn> selectCompileFunction(V8CacheOptions cacheOptions, ScriptR esource* resource, v8::Handle<v8::String> code)
236 { 266 {
237 static const int minimalCodeLength = 1024; 267 static const int minimalCodeLength = 1024;
238 static const int mediumCodeLength = 300000; 268 static const int mediumCodeLength = 300000;
239 269
240 if (cacheOptions == V8CacheOptionsNone 270 if (cacheOptions == V8CacheOptionsNone
241 || !resource 271 || !resource
242 || !resource->url().protocolIsInHTTPFamily() 272 || !resource->url().protocolIsInHTTPFamily()) {
243 || code->Length() < minimalCodeLength) { 273 // Caching is not available in this case.
244 // Never generate or use the cache in these circumstances. 274 return bind(compileWithoutOptions, V8CompileHistogram::Noncacheable);
245 return bind(compileWithoutOptions); 275 }
276
277 if (code->Length() < minimalCodeLength) {
278 // Do not cache for small scripts, though caching is available.
279 return bind(compileWithoutOptions, V8CompileHistogram::Cacheable);
246 } 280 }
247 281
248 // The cacheOptions will guide our strategy: 282 // The cacheOptions will guide our strategy:
249 switch (cacheOptions) { 283 switch (cacheOptions) {
250 case V8CacheOptionsDefault: 284 case V8CacheOptionsDefault:
251 case V8CacheOptionsParseMemory: 285 case V8CacheOptionsParseMemory:
252 // Use parser-cache; in-memory only. 286 // Use parser-cache; in-memory only.
253 return bind(compileAndConsumeOrProduce, resource, cacheTag(CacheTagParse r, resource), v8::ScriptCompiler::kConsumeParserCache, v8::ScriptCompiler::kProd uceParserCache, false, Resource::CacheLocally); 287 return bind(compileAndConsumeOrProduce, resource, cacheTag(CacheTagParse r, resource), v8::ScriptCompiler::kConsumeParserCache, v8::ScriptCompiler::kProd uceParserCache, false, Resource::CacheLocally);
254 break; 288 break;
255 289
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
294 case V8CacheOptionsRecent: 328 case V8CacheOptionsRecent:
295 case V8CacheOptionsRecentSmall: { 329 case V8CacheOptionsRecentSmall: {
296 if (cacheOptions == V8CacheOptionsRecentSmall && code->Length() >= mediu mCodeLength) 330 if (cacheOptions == V8CacheOptionsRecentSmall && code->Length() >= mediu mCodeLength)
297 return bind(compileAndConsumeOrProduce, resource, cacheTag(CacheTagP arser, resource), v8::ScriptCompiler::kConsumeParserCache, v8::ScriptCompiler::k ProduceParserCache, false, Resource::CacheLocally); 331 return bind(compileAndConsumeOrProduce, resource, cacheTag(CacheTagP arser, resource), v8::ScriptCompiler::kConsumeParserCache, v8::ScriptCompiler::k ProduceParserCache, false, Resource::CacheLocally);
298 unsigned codeCacheTag = cacheTag(CacheTagCode, resource); 332 unsigned codeCacheTag = cacheTag(CacheTagCode, resource);
299 CachedMetadata* codeCache = resource->cachedMetadata(codeCacheTag); 333 CachedMetadata* codeCache = resource->cachedMetadata(codeCacheTag);
300 if (codeCache) 334 if (codeCache)
301 return bind(compileAndConsumeCache, resource, codeCacheTag, v8::Scri ptCompiler::kConsumeCodeCache, false); 335 return bind(compileAndConsumeCache, resource, codeCacheTag, v8::Scri ptCompiler::kConsumeCodeCache, false);
302 if (!isResourceHotForCaching(resource)) { 336 if (!isResourceHotForCaching(resource)) {
303 setCacheTimeStamp(resource); 337 setCacheTimeStamp(resource);
304 return bind(compileWithoutOptions); 338 return bind(compileWithoutOptions, V8CompileHistogram::Cacheable);
305 } 339 }
306 return bind(compileAndProduceCache, resource, codeCacheTag, v8::ScriptCo mpiler::kProduceCodeCache, false, Resource::SendToPlatform); 340 return bind(compileAndProduceCache, resource, codeCacheTag, v8::ScriptCo mpiler::kProduceCodeCache, false, Resource::SendToPlatform);
307 break; 341 break;
308 } 342 }
309 343
310 case V8CacheOptionsNone: 344 case V8CacheOptionsNone:
311 // Shouldn't happen, as this is handled above. 345 // Shouldn't happen, as this is handled above.
312 // Case is here so that compiler can check all cases are handles. 346 // Case is here so that compiler can check all cases are handles.
313 ASSERT_NOT_REACHED(); 347 ASSERT_NOT_REACHED();
314 return bind(compileWithoutOptions); 348 return bind(compileWithoutOptions, V8CompileHistogram::Cacheable);
315 break; 349 break;
316 } 350 }
317 351
318 // All switch branches should return and we should never get here. 352 // All switch branches should return and we should never get here.
319 // But some compilers aren't sure, hence this default. 353 // But some compilers aren't sure, hence this default.
320 ASSERT_NOT_REACHED(); 354 ASSERT_NOT_REACHED();
321 return bind(compileWithoutOptions); 355 return bind(compileWithoutOptions, V8CompileHistogram::Cacheable);
322 } 356 }
323 357
324 // Select a compile function for a streaming compile. 358 // Select a compile function for a streaming compile.
325 PassOwnPtr<CompileFn> selectCompileFunction(ScriptResource* resource, ScriptStre amer* streamer) 359 PassOwnPtr<CompileFn> selectCompileFunction(ScriptResource* resource, ScriptStre amer* streamer)
326 { 360 {
327 // We don't stream scripts which don't have a Resource. 361 // We don't stream scripts which don't have a Resource.
328 ASSERT(resource); 362 ASSERT(resource);
329 // Failed resources should never get this far. 363 // Failed resources should never get this far.
330 ASSERT(!resource->errorOccurred()); 364 ASSERT(!resource->errorOccurred());
331 ASSERT(streamer->isFinished()); 365 ASSERT(streamer->isFinished());
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after
488 { 522 {
489 return cacheTag(CacheTagParser, resource); 523 return cacheTag(CacheTagParser, resource);
490 } 524 }
491 525
492 unsigned V8ScriptRunner::tagForCodeCache(Resource* resource) 526 unsigned V8ScriptRunner::tagForCodeCache(Resource* resource)
493 { 527 {
494 return cacheTag(CacheTagCode, resource); 528 return cacheTag(CacheTagCode, resource);
495 } 529 }
496 530
497 } // namespace blink 531 } // namespace blink
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698