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.

Reply via email to