On Thursday, November 14, 2013 5:28:58 AM UTC-8, Dmitry Lomov wrote: > > Is it still true in the latest releases (3.20 and up)? I am curious as to > what is the bottleneck here. > I remember you pointing out (in a different bug) that allocating weak > handles is costly - non-externalized ArrayBuffers do not require any weak > handles anymore, and we can think of ways to avoid weak handle allocation > for some externalized array buffers as well. >
I'm just curious. What was the change where you don't have to allocate weak handles for ArrayBuffers? > It feels like this scenario can be adequately captured by having an > ArrayBuffer wrapping external data. It also aligns closely to the goals of > typed objects proposal (formerly binary data: > http://wiki.ecmascript.org/doku.php?id=harmony:typed_objects), and again > that is probably what V8 will be optimizing for. > > Looking through Buffer API ( > http://nodejs.org/api/buffer.html#buffer_buffer), I think that everything > there can be implemented by subclassing Uint8Array (admittedly, we do not > support subclassing of builtin objects yet! :)) > > Anyway, given how deep this external data feature had taken deep roots in > node.js code, I agree that completely deprecating this API is premature. > Here are the steps I would still like to do though: > - Does node.js use any ExternalArrayType besides > kExternalUnsignedByteArray? I'd love to reduce at least this part of the > zoo. > - I would dearly love to make sure node.js can switch to standard JS > features instead of inventing your own. Can haz some benchmarks? :) I > definitely do not want to force it down your throat though - I would like > to get the perf to the point where the switch will be a perf win. > Here's a table of benchmarks. The "Buffer 3.19" column is allocating a Buffer object at v8 v3.19.x (before we upgraded). Then the "smalloc" column is our internal allocator for Buffers. It's there twice to show that between 3.19 and 3.22 something happened that caused the API to become slower than it used to be for small allocations (maybe you know why this happened?). The values are nanoseconds per operation. size Buffer 3.19 smalloc 3.19 smalloc 3.22 Uint8Array Uint8Array slice ----------------------------------------------------------------------------------- 1 byte: 165.2 293.0 401.4 193.9 113.2 16 bytes: 170.9 280.4 384.8 191.2 111.7 256 bytes: 210.9 312.7 434.6 250.9 117.0 1 KB: 332.3 453.4 547.2 747.8 222.4 16 KB: 531.5 454.2 581.8 1980.5 1883.9 256 KB: 1977.9 1937.1 2183.7 2054.2 1878.1 1 MB: 6626.9 6744.8 7562.2 7040.7 6797.0 16 MB: 95037.3 88270.4 104992.8 108304.6 109316.1 So here you can see that using a similar slicing technique, Uint8Array is faster than our current implementation for small allocations. The "Uint8Array slice" uses the same approach as Node's Buffers by creating an 8KB pool and handing back chunks of that for allocations < 4KB. Though as you can see there's a block of sizes where it's strangely slower. Here's a table with more information: size Uint8Array smalloc ----------------------------- 1 byte: 195.3 407.6 16 bytes: 208.5 380.8 256 bytes: 257.9 435.8 1 KB: 741.0 536.0 2 KB: 1116.8 557.2 4 KB: 1823.9 602.1 8 KB: 1828.9 567.3 16 KB: 1848.9 555.7 32 KB: 461.5 648.3 64 KB: 686.3 894.3 128 KB: 1085.0 1351.0 256 KB: 1858.9 2166.0 512 KB: 3792.9 3992.7 1 MB: 6664.3 6997.9 Time cost of instantiation is pretty much linear with smalloc, but not so with Uint8Array. And there's a large block in there were Node spends a lot of time instantiating new objects. Maybe you could also shed some light on why this is happening. What's stranger still is that if I make a call to a native module myself, say like this: void Allocate(const FunctionCallbackInfo<Value>& args) { uint32_t size = args[0]->Uint32Value(); args.GetReturnValue().Set(Uint8Array::New( ArrayBuffer::New(size), 0, size)); } instead of just calling new Uint8Array(size), the performance problem demonstrated above doesn't occur. Anyways. I'd be sad to see the external array data API disappear. It's been very helpful in creating APIs where you can attach data directly to a constructed instance. This has been useful many things. One example is a math library I'm working on where you can specify what type of data you want to use (e.g. a kExternalDoubleArray), and it returns a new instance where you can access the indexed data but also have many methods you can run directly on the data. It's much faster to do even simple things in a C++ module than in JS (e.g. take the sum of the array). Most of this thanks to performance improvements you all have made to calling a native method (thanks for that :). -- -- v8-users mailing list [email protected] http://groups.google.com/group/v8-users --- You received this message because you are subscribed to the Google Groups "v8-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/groups/opt_out.
