4

Here's the code I want to speed up. It's getting a value from an ADO recordset and converting it to a char*. But this is slow. Can I skip the creation of the _bstr_t?

                _variant_t var = pRs->Fields->GetItem(i)->GetValue();

                if (V_VT(&var) == VT_BSTR)
                {
                    char* p = (const char*) (_bstr_t) var;
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Corey Trager
  • 22,649
  • 18
  • 83
  • 121
  • I would recommend doing some timings with the methods proposed here for confirmation. Use QueryPerformanceCounter and QueryPerformanceFrequency API's – PiNoYBoY82 Sep 23 '08 at 00:40

5 Answers5

3

Your problem (other than the possibility of a memory copy inside _bstr_t) is that you're converting the UNICODE BSTR into an ANSI char*.

You can use the USES_CONVERSION macros which perform the conversion on the stack, so they might be faster. Alternatively, keep the BSTR value as unicode if possible.

to convert:

USES_CONVERSION;
char* p = strdup(OLE2A(var.bstrVal));

// ...

free(p);

remember - the string returned from OLE2A (and its sister macros) return a string that is allocated on the stack - return from the enclosing scope and you have garbage string unless you copy it (and free it eventually, obviously)

1800 INFORMATION
  • 131,367
  • 29
  • 160
  • 239
gbjbaanb
  • 51,617
  • 12
  • 104
  • 148
3

The first 4 bytes of the BSTR contain the length. You can loop through and get every other character if unicode or every character if multibyte. Some sort of memcpy or other method would work too. IIRC, this can be faster than W2A or casting (LPCSTR)(_bstr_t)

PiNoYBoY82
  • 1,618
  • 2
  • 14
  • 17
2

This creates a temporary on the stack:

USES_CONVERSION;
char *p=W2A(var.bstrVal);

This uses a slightly newer syntax and is probably more robust. It has a configurable size, beyond which it will use the heap so it avoids putting massive strings onto the stack:

char *p=CW2AEX<>(var.bstrVal);
1800 INFORMATION
  • 131,367
  • 29
  • 160
  • 239
  • Well, I liked this approach in theory, except it's not really faster according to my timings. – Corey Trager Sep 22 '08 at 22:25
  • I'm surprised it isn't. Your original method requires the creation of an extra BSTR which has to go through the COM allocator. Both of mine just copy into a buffer onto the stack with conversion. If your original string is long enough, the overhead from the conversion is probably most significant – 1800 INFORMATION Sep 22 '08 at 22:28
  • My strings are short, but I have, oh, 150,000 thousand of them or so in a loop. That is I'm looping thru the rows and columns of an ADO recordset, converting the BSTRs to char*s, then pushing the char*s into an std::vector of std::strings. Takes about 3.2 seconds both ways. – Corey Trager Sep 22 '08 at 22:39
  • Well you still have to perform the conversion from unicode. Have you considered trying to avoid the conversion? Change your whole app to use unicode? Are you sure you have the right bottleneck? maybe it is the Getvalue() methods? – 1800 INFORMATION Sep 23 '08 at 00:27
  • 1800 INFORMATION, thanks for the suggestions. There are other parts of my loop that contribute to the 3.2 seconds, but in this Stackoverflow question, I was focusing on just this narrow point. I'm not free to do things over, but I think going with unicode from end-to-end would have been better. – Corey Trager Sep 23 '08 at 01:10
  • Allocating memory on the heap with alloca(n) is not slow. In fact it can be optimized down to three instructions: SUB ESP, n; MOV EAX, ESP; RET 4; – jmucchiello Oct 04 '09 at 00:32
  • alloca does not allocate memory on the heap – 1800 INFORMATION Oct 04 '09 at 19:24
0
_variant_t var = pRs->Fields->GetItem(i)->GetValue(); 

You can also make this assignment quicker by avoiding the fields collection all together. You should only use the Fields collection when you need to retrieve the item by name. If you know the fields by index you can instead use this.

_variant_t vara = pRs->Collect[i]->Value;

Note i cannot be an integer as ADO does not support VT_INTEGER, so you might as well use a long variable.

Peter Nimmo
  • 1,045
  • 2
  • 12
  • 25
  • OP is asking for a `char*` from `_variant_t`. Your answer returns a `_variant_t` from `recordset`. your answer has nothing to do with question – AaA Apr 22 '16 at 10:30
-2

Ok, my C++ is getting a little rusty... but I don't think the conversion is your problem. That conversion doesn't really do anything except tell the compiler to consider _bstr_t a char*. Then you're just assigning the address of that pointer to p. Nothing's actually being "done."

Are you sure it's not just slow getting stuff from GetValue?

Or is my C++ rustier than I think...

CodeRedick
  • 7,346
  • 7
  • 46
  • 72
  • 2
    Actually there is a bunch of magic going on behind the scenes by the _bstr_t class to convert the string from unicode to ANSI – 1800 INFORMATION Sep 22 '08 at 21:38
  • That depends. I don't know the details of the _variant_t class, but if it defines an operator _bstr_t() then that's the function that will be called if a _variant_t to _bstr_t conversion is needed. – Ferruccio Sep 22 '08 at 21:38
  • 2
    Those aren't mere casts for the compiler. I know that from stepping through the code. Objects are being constructed. – Corey Trager Sep 22 '08 at 21:40