DBD::Pg, DBD::Oracle and others are slow with threads due to DBIS

2012-03-13 Thread Tim Bunce
On Fri, Mar 02, 2012 at 03:11:17PM +, Tim Bunce wrote:
> > 
> > The second patch, which makes DBIS more efficient, is a lot more complex,
> > and more likely to break things (especially as it's changing a bunch of
> > macros that are directly #included by the DBD drivers. You may need to
> > bump API version numbers; I don't understand that bit.
> 
> I'm concerned that changing the API, and thus forcing all compiled
> drivers to be recompiled at the same time the DBI is installed,
> isn't worth the gain. Especially as drivers shouldn't be using DBIS in
> any hot code anyway.

I finally got around to looking at DBD::Pg and was horrified to discover
the number of DBIS calls made by hot paths in the code. Most are hidden
behind various tracing macros. Even fetch calls 3 + 3 * num_of_fields!

Then I looked at DBD::Oracle and discovered the same kind of thing.
Which is particularly disappointing for me as I wrote the tracing
mechanism it uses (though maybe that pre-dated thread support).

Anyway, the upshot is that getting DBIS calls out of major drivers will
require a fair bit of work. It's just grunt-work really, nothing complicated.
As you noted with DBD::mysql, Dave, the performance gains are worth it.

Any volunteers? Naturally I'll be happy to suggest an approach I think
will work well (basically an extension of that used by DBD::Oracle).

Dave, I'll get back to you about the DBIS changes soon, I hope.

Tim.


Re: DBD::Pg, DBD::Oracle and others are slow with threads due to DBIS

2012-03-14 Thread H.Merijn Brand
On Tue, 13 Mar 2012 21:44:06 +, Tim Bunce 
wrote:

> On Fri, Mar 02, 2012 at 03:11:17PM +, Tim Bunce wrote:
> > > 
> > > The second patch, which makes DBIS more efficient, is a lot more complex,
> > > and more likely to break things (especially as it's changing a bunch of
> > > macros that are directly #included by the DBD drivers. You may need to
> > > bump API version numbers; I don't understand that bit.
> > 
> > I'm concerned that changing the API, and thus forcing all compiled
> > drivers to be recompiled at the same time the DBI is installed,
> > isn't worth the gain. Especially as drivers shouldn't be using DBIS in
> > any hot code anyway.
> 
> I finally got around to looking at DBD::Pg and was horrified to discover
> the number of DBIS calls made by hot paths in the code. Most are hidden
> behind various tracing macros. Even fetch calls 3 + 3 * num_of_fields!

When hidden, it won't stand out to the maintainers.

How do we/I see where it happens? Do you have a (short) guide to check
if my/a DBD (CSV, Unify, File) uses those too?

> Then I looked at DBD::Oracle and discovered the same kind of thing.
> Which is particularly disappointing for me as I wrote the tracing
> mechanism it uses (though maybe that pre-dated thread support).
> 
> Anyway, the upshot is that getting DBIS calls out of major drivers will
> require a fair bit of work. It's just grunt-work really, nothing complicated.
> As you noted with DBD::mysql, Dave, the performance gains are worth it.
> 
> Any volunteers? Naturally I'll be happy to suggest an approach I think
> will work well (basically an extension of that used by DBD::Oracle).
> 
> Dave, I'll get back to you about the DBIS changes soon, I hope.

-- 
H.Merijn Brand  http://tux.nl   Perl Monger  http://amsterdam.pm.org/
using perl5.00307 .. 5.14   porting perl5 on HP-UX, AIX, and openSUSE
http://mirrors.develooper.com/hpux/http://www.test-smoke.org/
http://qa.perl.org   http://www.goldmark.org/jeff/stupid-disclaimers/


Re: DBD::Pg, DBD::Oracle and others are slow with threads due to DBIS

2012-03-14 Thread Martin J. Evans

On 13/03/12 21:44, Tim Bunce wrote:

On Fri, Mar 02, 2012 at 03:11:17PM +, Tim Bunce wrote:


The second patch, which makes DBIS more efficient, is a lot more complex,
and more likely to break things (especially as it's changing a bunch of
macros that are directly #included by the DBD drivers. You may need to
bump API version numbers; I don't understand that bit.


I'm concerned that changing the API, and thus forcing all compiled
drivers to be recompiled at the same time the DBI is installed,
isn't worth the gain. Especially as drivers shouldn't be using DBIS in
any hot code anyway.


On the other hand, I want whatever speed I can get so I am happy to recompile 
any DBDs to get it. Do people like me have to do without improvements for the 
sake of those who don't want to recompile their DBDs or can it be done in some 
other way or opt in.


I finally got around to looking at DBD::Pg and was horrified to discover
the number of DBIS calls made by hot paths in the code. Most are hidden
behind various tracing macros. Even fetch calls 3 + 3 * num_of_fields!

Then I looked at DBD::Oracle and discovered the same kind of thing.
Which is particularly disappointing for me as I wrote the tracing
mechanism it uses (though maybe that pre-dated thread support).


I am happy to change DBD::Oracle or may be Yanick will.


Anyway, the upshot is that getting DBIS calls out of major drivers will
require a fair bit of work. It's just grunt-work really, nothing complicated.
As you noted with DBD::mysql, Dave, the performance gains are worth it.

Any volunteers? Naturally I'll be happy to suggest an approach I think
will work well (basically an extension of that used by DBD::Oracle).

Dave, I'll get back to you about the DBIS changes soon, I hope.

Tim.


I don't think DBD::ODBC uses any DBIS stuff but to be sure I'd echo Merijn's 
reply; if you could point out what to look for and an approach I'd happily 
change any DBD I could run and provide patches for it (currently I have ODBC, 
Pg, Oracle, CSV, SQLite, mysql and all those you get with DBI).

Martin
--
Martin J. Evans
Easysoft Limited
http://www.easysoft.com


Re: DBD::Pg, DBD::Oracle and others are slow with threads due to DBIS

2012-03-14 Thread Tim Bunce
On Wed, Mar 14, 2012 at 08:24:36AM +0100, H.Merijn Brand wrote:
> On Tue, 13 Mar 2012 21:44:06 +, Tim Bunce 
> wrote:
> 
> > On Fri, Mar 02, 2012 at 03:11:17PM +, Tim Bunce wrote:
> > > > 
> > > > The second patch, which makes DBIS more efficient, is a lot more 
> > > > complex,
> > > > and more likely to break things (especially as it's changing a bunch of
> > > > macros that are directly #included by the DBD drivers. You may need to
> > > > bump API version numbers; I don't understand that bit.
> > > 
> > > I'm concerned that changing the API, and thus forcing all compiled
> > > drivers to be recompiled at the same time the DBI is installed,
> > > isn't worth the gain. Especially as drivers shouldn't be using DBIS in
> > > any hot code anyway.
> > 
> > I finally got around to looking at DBD::Pg and was horrified to discover
> > the number of DBIS calls made by hot paths in the code. Most are hidden
> > behind various tracing macros. Even fetch calls 3 + 3 * num_of_fields!
> 
> When hidden, it won't stand out to the maintainers.
> 
> How do we/I see where it happens? Do you have a (short) guide to check
> if my/a DBD (CSV, Unify, File) uses those too?

The slowness of the DBIS macro is only relevant for compiled drivers.
The DBIS macro is used to get a pointer to the DBI's struct dbistate_st.
In non-threaded perls it's simply a static variable initialised once.
In threaded perls it's a macro that has to do more expensive work.

For many years now all DBI handles have had their own pointer to
dbistate_st. Since almost all code in the driver has access to a handle
that's the best way to get the dbistate_st struct pointer.

Essentially it means changing all instances of DBIS to DBIc_DBISTATE(imp_xxh)
(where imp_xxh can be any of the imp_dbh, imp_sth etc variables).

Looking at DBIXS.h I see these other macros that use DBIS and thus
should be avoided in hot code:

DBILOGFP=> DBIc_LOGPIO(imp_xxh)
DBILOGMSG(...)  => DBIc_DBISTATE(imp_xxh)->logmsg(...)
neatsvpv(...)   => DBIc_DBISTATE(imp_xxh)->neatsvpv(...)

however, usually those are only called in tracing code so it may not be
worth the effort to change them.

The DBIh_COM(h) macro also uses DBIS but there's a chicken-and-egg
problem here since DBIh_COM is the underlying mechanism of the
D_imp_xxh(h) macros used to get the imp_xxh pointer. So it can't be
avoided. Yet more reason to apply Dave's changes.

Tim.

p.s. For the logic to control tracing I recommend the DBIc_TRACE macro
(or something driver-specific like it or built using it):
DBIc_TRACE(imp, flags, flag_level, fallback_level)

That's true if flags match the handle trace flags & handle trace
level >= flag_level, OR if handle trace_level > fallback_level

   DBIc_TRACE(imp,  0, 0, 4) = if handle trace level >= 4
   DBIc_TRACE(imp, DBDf_TRACE_FOO, 2, 4) = if tracing DBDf_TRACE_FOO & level>=2 
OR level>=4
   DBIc_TRACE(imp, DBDf_TRACE_FOO, 2, 0) = as above but never trace just due to 
level

For example:

if (DBIc_TRACE(imp_xxh, DBIf_TRACE_SQL|DBIf_TRACE_xxx, 3, 7)) {
PerlIO_printf(DBIc_LOGPIO(imp_sth), "\tThe %s wibbled the %s\n", ...);
}


Re: DBD::Pg, DBD::Oracle and others are slow with threads due to DBIS

2012-03-14 Thread Tim Bunce
On Wed, Mar 14, 2012 at 08:52:38AM +, Martin J. Evans wrote:
> On 13/03/12 21:44, Tim Bunce wrote:
> >On Fri, Mar 02, 2012 at 03:11:17PM +, Tim Bunce wrote:
> >>>
> >>>The second patch, which makes DBIS more efficient, is a lot more complex,
> >>>and more likely to break things (especially as it's changing a bunch of
> >>>macros that are directly #included by the DBD drivers. You may need to
> >>>bump API version numbers; I don't understand that bit.
> >>
> >>I'm concerned that changing the API, and thus forcing all compiled
> >>drivers to be recompiled at the same time the DBI is installed,
> >>isn't worth the gain. Especially as drivers shouldn't be using DBIS in
> >>any hot code anyway.
> 
> On the other hand, I want whatever speed I can get so I am happy to
> recompile any DBDs to get it. Do people like me have to do without
> improvements for the sake of those who don't want to recompile their
> DBDs or can it be done in some other way or opt in.

Per my recent message, implicit usage of DBIS is higher than I'd thought.
The DBI will certain get Dave's improvements in some form, and yes,
drivers will need to be recompiled. In fact they may *have* to be
recompiled.

> I don't think DBD::ODBC uses any DBIS stuff

I had a (very) quick look and it loosk good. It's even using DBIc_TRACE() :)

There are cases where an imp_sth is passed as a parameter yet
D_imp_xxh(sth) is being called. So you end up with imp_sth and imp_xxh
pointers that point to the same thing. See dbd_st_fetch and dbd_bind_ph for
example.  Just delete the D_imp_xxh(sth); line and change all imp_xxh in
the function to be imp_sth instead. There may be cases where the same
applies with imp_dbh.

There are other places where internal functions use the D_imp_xxh(h)
initializer but that could be avoided by passing in imp_xxh from the
caller. (And by *_xxh I mean _dbh, _sth - whichever is appropriate.)

> but to be sure I'd echo
> Merijn's reply; if you could point out what to look for and an
> approach I'd happily change any DBD I could run and provide patches
> for it (currently I have ODBC, Pg, Oracle, CSV, SQLite, mysql and all
> those you get with DBI).

Let me know if my reply to Merijn wasn't clear or complete enough.

Tim.


Re: DBD::Pg, DBD::Oracle and others are slow with threads due to DBIS

2012-03-14 Thread Martin J. Evans

On 14/03/12 10:37, Tim Bunce wrote:

On Wed, Mar 14, 2012 at 08:52:38AM +, Martin J. Evans wrote:

On 13/03/12 21:44, Tim Bunce wrote:

On Fri, Mar 02, 2012 at 03:11:17PM +, Tim Bunce wrote:


The second patch, which makes DBIS more efficient, is a lot more complex,
and more likely to break things (especially as it's changing a bunch of
macros that are directly #included by the DBD drivers. You may need to
bump API version numbers; I don't understand that bit.


I'm concerned that changing the API, and thus forcing all compiled
drivers to be recompiled at the same time the DBI is installed,
isn't worth the gain. Especially as drivers shouldn't be using DBIS in
any hot code anyway.


On the other hand, I want whatever speed I can get so I am happy to
recompile any DBDs to get it. Do people like me have to do without
improvements for the sake of those who don't want to recompile their
DBDs or can it be done in some other way or opt in.


Per my recent message, implicit usage of DBIS is higher than I'd thought.
The DBI will certain get Dave's improvements in some form, and yes,
drivers will need to be recompiled. In fact they may *have* to be
recompiled.


I don't think DBD::ODBC uses any DBIS stuff


I had a (very) quick look and it loosk good. It's even using DBIc_TRACE() :)


I thought I was pretty up to date.


There are cases where an imp_sth is passed as a parameter yet
D_imp_xxh(sth) is being called. So you end up with imp_sth and imp_xxh
pointers that point to the same thing. See dbd_st_fetch and dbd_bind_ph for
example.  Just delete the D_imp_xxh(sth); line and change all imp_xxh in
the function to be imp_sth instead. There may be cases where the same
applies with imp_dbh.


Yeah, thanks, I'll fix those.
 

There are other places where internal functions use the D_imp_xxh(h)
initializer but that could be avoided by passing in imp_xxh from the
caller. (And by *_xxh I mean _dbh, _sth - whichever is appropriate.)


I'll do that too.
 

but to be sure I'd echo
Merijn's reply; if you could point out what to look for and an
approach I'd happily change any DBD I could run and provide patches
for it (currently I have ODBC, Pg, Oracle, CSV, SQLite, mysql and all
those you get with DBI).


Let me know if my reply to Merijn wasn't clear or complete enough.


It was fine.
 
I'll try and find time to sort Oracle out unless Yanick steps in.

I also meant to change Oracle to use DBD trace flag etc which gets rid of loads 
of ora_verbose code all over the place but I've not had time as yet as the 
level or rts reported for Oracle keeps me pretty busy.

Martin
--
Martin J. Evans
Easysoft Limited
http://www.easysoft.com


Re: DBD::Pg, DBD::Oracle and others are slow with threads due to DBIS

2012-03-14 Thread Martin J. Evans

On 14/03/12 11:37, Martin J. Evans wrote:

On 14/03/12 10:37, Tim Bunce wrote:

On Wed, Mar 14, 2012 at 08:52:38AM +, Martin J. Evans wrote:

On 13/03/12 21:44, Tim Bunce wrote:

On Fri, Mar 02, 2012 at 03:11:17PM +, Tim Bunce wrote:


The second patch, which makes DBIS more efficient, is a lot more complex,
and more likely to break things (especially as it's changing a bunch of
macros that are directly #included by the DBD drivers. You may need to
bump API version numbers; I don't understand that bit.


I'm concerned that changing the API, and thus forcing all compiled
drivers to be recompiled at the same time the DBI is installed,
isn't worth the gain. Especially as drivers shouldn't be using DBIS in
any hot code anyway.


On the other hand, I want whatever speed I can get so I am happy to
recompile any DBDs to get it. Do people like me have to do without
improvements for the sake of those who don't want to recompile their
DBDs or can it be done in some other way or opt in.


Per my recent message, implicit usage of DBIS is higher than I'd thought.
The DBI will certain get Dave's improvements in some form, and yes,
drivers will need to be recompiled. In fact they may *have* to be
recompiled.


Good.


I don't think DBD::ODBC uses any DBIS stuff


I had a (very) quick look and it loosk good. It's even using DBIc_TRACE() :)


I thought I was pretty up to date.


There are cases where an imp_sth is passed as a parameter yet
D_imp_xxh(sth) is being called. So you end up with imp_sth and imp_xxh
pointers that point to the same thing. See dbd_st_fetch and dbd_bind_ph for
example. Just delete the D_imp_xxh(sth); line and change all imp_xxh in
the function to be imp_sth instead. There may be cases where the same
applies with imp_dbh.


Yeah, thanks, I'll fix those.


Those D_imp_xxh calls in dbd_st_fetch and dbd_bind_ph are simply there because 
those fns call DBIh_SET_ERR_CHAR and that takes an imp_xxh_t. I have removed 
them but then I need to cast imp_dbh/imp_sth to imp_xxh_t before calling 
DBIh_SET_ERR_CHAR so that then makes we wonder why imp_xxh_t is really 
required? just so you can pass an imp_dbh_t or an imp_sth_t to it?

The only places DBD::ODBC using imp_xxh_t is in calls to DBIh_SET_ERR_CHAR and 
DBIc_TRACE (and a few calls to DBIc_TYPE). So are you effectively saying 
imp_xxh_t exists for the simple reason it could be either an 
imp_sth_t/imp_dbh_t and if that is so I presume I can just delete all D_imp_xxh 
macros where the imp_xxh is passed to DBIh_SET_ERR_CHAR/DBIc_TRACE, pass the 
real imp_*h and cast it and avoid a call to dbih_getcom2?


There are other places where internal functions use the D_imp_xxh(h)
initializer but that could be avoided by passing in imp_xxh from the
caller. (And by *_xxh I mean _dbh, _sth - whichever is appropriate.)


I'll do that too.


That is a bigger job but one I've started.
 

but to be sure I'd echo
Merijn's reply; if you could point out what to look for and an
approach I'd happily change any DBD I could run and provide patches
for it (currently I have ODBC, Pg, Oracle, CSV, SQLite, mysql and all
those you get with DBI).


Let me know if my reply to Merijn wasn't clear or complete enough.


It was fine.

I'll try and find time to sort Oracle out unless Yanick steps in.
I also meant to change Oracle to use DBD trace flag etc which gets rid of loads 
of ora_verbose code all over the place but I've not had time as yet as the 
level or rts reported for Oracle keeps me pretty busy.

Martin


Martin
--
Martin J. Evans
Easysoft Limited
http://www.easysoft.com


Re: DBD::Pg, DBD::Oracle and others are slow with threads due to DBIS

2012-03-14 Thread Yanick Champoux

On 03/14/12 07:37, Martin J. Evans wrote:

I'll try and find time to sort Oracle out unless Yanick steps in.


I'll see if I can give it a go, but as of now that's more a pledge 
of goodwill than decent skills. The deep guts of DBI are still very 
murky to me. But I'll do my best. :-)



Joy,
`/anick


Re: DBD::Pg, DBD::Oracle and others are slow with threads due to DBIS

2012-03-14 Thread Tim Bunce
On Wed, Mar 14, 2012 at 02:00:19PM +, Martin J. Evans wrote:
> >
> >>There are cases where an imp_sth is passed as a parameter yet
> >>D_imp_xxh(sth) is being called. So you end up with imp_sth and imp_xxh
> >>pointers that point to the same thing. See dbd_st_fetch and dbd_bind_ph for
> >>example. Just delete the D_imp_xxh(sth); line and change all imp_xxh in
> >>the function to be imp_sth instead. There may be cases where the same
> >>applies with imp_dbh.
> >
> >Yeah, thanks, I'll fix those.
> 
> Those D_imp_xxh calls in dbd_st_fetch and dbd_bind_ph are simply there
> because those fns call DBIh_SET_ERR_CHAR and that takes an imp_xxh_t.
> I have removed them but then I need to cast imp_dbh/imp_sth to
> imp_xxh_t before calling DBIh_SET_ERR_CHAR so that then makes we
> wonder why imp_xxh_t is really required? just so you can pass an
> imp_dbh_t or an imp_sth_t to it?
>
> The only places DBD::ODBC using imp_xxh_t is in calls to
> DBIh_SET_ERR_CHAR and DBIc_TRACE (and a few calls to DBIc_TYPE). So
> are you effectively saying imp_xxh_t exists for the simple reason it
> could be either an imp_sth_t/imp_dbh_t and if that is so I presume I
> can just delete all D_imp_xxh macros where the imp_xxh is passed to
> DBIh_SET_ERR_CHAR/DBIc_TRACE, pass the real imp_*h and cast it and
> avoid a call to dbih_getcom2?

Yes. imp_xxh_t gives access to those struct elements that are common
to all handles. It's always safe to cast a imp_??h_t to imp_xxh_t.

Tim.


Re: DBD::Pg, DBD::Oracle and others are slow with threads due to DBIS

2012-03-15 Thread Martin J. Evans

On 14/03/2012 10:20, Tim Bunce wrote:

On Wed, Mar 14, 2012 at 08:24:36AM +0100, H.Merijn Brand wrote:

On Tue, 13 Mar 2012 21:44:06 +, Tim Bunce
wrote:


On Fri, Mar 02, 2012 at 03:11:17PM +, Tim Bunce wrote:

The second patch, which makes DBIS more efficient, is a lot more complex,
and more likely to break things (especially as it's changing a bunch of
macros that are directly #included by the DBD drivers. You may need to
bump API version numbers; I don't understand that bit.

I'm concerned that changing the API, and thus forcing all compiled
drivers to be recompiled at the same time the DBI is installed,
isn't worth the gain. Especially as drivers shouldn't be using DBIS in
any hot code anyway.

I finally got around to looking at DBD::Pg and was horrified to discover
the number of DBIS calls made by hot paths in the code. Most are hidden
behind various tracing macros. Even fetch calls 3 + 3 * num_of_fields!

When hidden, it won't stand out to the maintainers.

How do we/I see where it happens? Do you have a (short) guide to check
if my/a DBD (CSV, Unify, File) uses those too?

The slowness of the DBIS macro is only relevant for compiled drivers.
The DBIS macro is used to get a pointer to the DBI's struct dbistate_st.
In non-threaded perls it's simply a static variable initialised once.
In threaded perls it's a macro that has to do more expensive work.

For many years now all DBI handles have had their own pointer to
dbistate_st. Since almost all code in the driver has access to a handle
that's the best way to get the dbistate_st struct pointer.

Essentially it means changing all instances of DBIS to DBIc_DBISTATE(imp_xxh)
(where imp_xxh can be any of the imp_dbh, imp_sth etc variables).

Looking at DBIXS.h I see these other macros that use DBIS and thus
should be avoided in hot code:

 DBILOGFP=>  DBIc_LOGPIO(imp_xxh)
 DBILOGMSG(...)  =>  DBIc_DBISTATE(imp_xxh)->logmsg(...)

DBD::ODBC had none of the above.

 neatsvpv(...)   =>  DBIc_DBISTATE(imp_xxh)->neatsvpv(...)

DBD::ODBC had a number of these however, the suggested change does not work:

original:

  if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 4)) {
  TRACE2(imp_sth, "ODBC_CLEAR_RESULTS '%s' => %s\n",
 key, neatsvpv(value,0));
  }
new:
  if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 4)) {
  TRACE2(imp_sth, "ODBC_CLEAR_RESULTS '%s' => %s\n",
 key, (DBIc_DBISTATE(imp_sth)->neatsvpv(value,0)));
  }

compiler error:

dbdimp.c: In function 'odbc_clear_result_set':
dbdimp.c:233: error: expected identifier before '(' token
dbdimp.c: In function 'odbc_db_STORE_attrib':
dbdimp.c:4672: warning: cast to pointer from integer of different size
dbdimp.c:4677: warning: cast to pointer from integer of different size
dmake:  Error code 129, while making 'dbdimp.o'

I've not really looked into it yet.


however, usually those are only called in tracing code so it may not be
worth the effort to change them.

ok, but I'm happy to do it in my case.

I've now changed DBD::ODBC to remove all unnecessary calls to D_imp_whatever.


The DBIh_COM(h) macro also uses DBIS but there's a chicken-and-egg
problem here since DBIh_COM is the underlying mechanism of the
D_imp_xxh(h) macros used to get the imp_xxh pointer. So it can't be
avoided. Yet more reason to apply Dave's changes.

Tim.

p.s. For the logic to control tracing I recommend the DBIc_TRACE macro
(or something driver-specific like it or built using it):
 DBIc_TRACE(imp, flags, flag_level, fallback_level)

I will change DBD::Oracle to use DBIc_TRACE at the weekend. I've started 
getting rid of DBIS from DBD::Oracle but as you said it is a bit of a PITA to 
do although not difficult.

That's true if flags match the handle trace flags&  handle trace
level>= flag_level, OR if handle trace_level>  fallback_level

DBIc_TRACE(imp,  0, 0, 4) = if handle trace level>= 4
DBIc_TRACE(imp, DBDf_TRACE_FOO, 2, 4) = if tracing DBDf_TRACE_FOO&  level>=2 
OR level>=4
DBIc_TRACE(imp, DBDf_TRACE_FOO, 2, 0) = as above but never trace just due 
to level

For example:

 if (DBIc_TRACE(imp_xxh, DBIf_TRACE_SQL|DBIf_TRACE_xxx, 3, 7)) {
 PerlIO_printf(DBIc_LOGPIO(imp_sth), "\tThe %s wibbled the %s\n", ...);
 }

Martin


Re: DBD::Pg, DBD::Oracle and others are slow with threads due to DBIS

2012-03-15 Thread Martin J. Evans

On 15/03/12 10:16, Martin J. Evans wrote:

On 14/03/2012 10:20, Tim Bunce wrote:

On Wed, Mar 14, 2012 at 08:24:36AM +0100, H.Merijn Brand wrote:

On Tue, 13 Mar 2012 21:44:06 +, Tim Bunce
wrote:


On Fri, Mar 02, 2012 at 03:11:17PM +, Tim Bunce wrote:

The second patch, which makes DBIS more efficient, is a lot more complex,
and more likely to break things (especially as it's changing a bunch of
macros that are directly #included by the DBD drivers. You may need to
bump API version numbers; I don't understand that bit.

I'm concerned that changing the API, and thus forcing all compiled
drivers to be recompiled at the same time the DBI is installed,
isn't worth the gain. Especially as drivers shouldn't be using DBIS in
any hot code anyway.

I finally got around to looking at DBD::Pg and was horrified to discover
the number of DBIS calls made by hot paths in the code. Most are hidden
behind various tracing macros. Even fetch calls 3 + 3 * num_of_fields!

When hidden, it won't stand out to the maintainers.

How do we/I see where it happens? Do you have a (short) guide to check
if my/a DBD (CSV, Unify, File) uses those too?

The slowness of the DBIS macro is only relevant for compiled drivers.
The DBIS macro is used to get a pointer to the DBI's struct dbistate_st.
In non-threaded perls it's simply a static variable initialised once.
In threaded perls it's a macro that has to do more expensive work.

For many years now all DBI handles have had their own pointer to
dbistate_st. Since almost all code in the driver has access to a handle
that's the best way to get the dbistate_st struct pointer.

Essentially it means changing all instances of DBIS to DBIc_DBISTATE(imp_xxh)
(where imp_xxh can be any of the imp_dbh, imp_sth etc variables).

Looking at DBIXS.h I see these other macros that use DBIS and thus
should be avoided in hot code:

DBILOGFP => DBIc_LOGPIO(imp_xxh)
DBILOGMSG(...) => DBIc_DBISTATE(imp_xxh)->logmsg(...)

DBD::ODBC had none of the above.

neatsvpv(...) => DBIc_DBISTATE(imp_xxh)->neatsvpv(...)

DBD::ODBC had a number of these however, the suggested change does not work:

original:

if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 4)) {
TRACE2(imp_sth, " ODBC_CLEAR_RESULTS '%s' => %s\n",
key, neatsvpv(value,0));
}
new:
if (DBIc_TRACE(imp_sth, DBD_TRACING, 0, 4)) {
TRACE2(imp_sth, " ODBC_CLEAR_RESULTS '%s' => %s\n",
key, (DBIc_DBISTATE(imp_sth)->neatsvpv(value,0)));
}

compiler error:

dbdimp.c: In function 'odbc_clear_result_set':
dbdimp.c:233: error: expected identifier before '(' token
dbdimp.c: In function 'odbc_db_STORE_attrib':
dbdimp.c:4672: warning: cast to pointer from integer of different size
dbdimp.c:4677: warning: cast to pointer from integer of different size
dmake: Error code 129, while making 'dbdimp.o'

I've not really looked into it yet.


however, usually those are only called in tracing code so it may not be
worth the effort to change them.

ok, but I'm happy to do it in my case.

I've now changed DBD::ODBC to remove all unnecessary calls to D_imp_whatever.


The DBIh_COM(h) macro also uses DBIS but there's a chicken-and-egg
problem here since DBIh_COM is the underlying mechanism of the
D_imp_xxh(h) macros used to get the imp_xxh pointer. So it can't be
avoided. Yet more reason to apply Dave's changes.

Tim.

p.s. For the logic to control tracing I recommend the DBIc_TRACE macro
(or something driver-specific like it or built using it):
DBIc_TRACE(imp, flags, flag_level, fallback_level)

I will change DBD::Oracle to use DBIc_TRACE at the weekend. I've started 
getting rid of DBIS from DBD::Oracle but as you said it is a bit of a PITA to 
do although not difficult.

That's true if flags match the handle trace flags& handle trace
level>= flag_level, OR if handle trace_level> fallback_level

DBIc_TRACE(imp, 0, 0, 4) = if handle trace level>= 4
DBIc_TRACE(imp, DBDf_TRACE_FOO, 2, 4) = if tracing DBDf_TRACE_FOO& level>=2 OR 
level>=4
DBIc_TRACE(imp, DBDf_TRACE_FOO, 2, 0) = as above but never trace just due to 
level

For example:

if (DBIc_TRACE(imp_xxh, DBIf_TRACE_SQL|DBIf_TRACE_xxx, 3, 7)) {
PerlIO_printf(DBIc_LOGPIO(imp_sth), "\tThe %s wibbled the %s\n", ...);
}

Martin


Argh, DBD::Oracle tracing is a mess wrt to this discussion:

ocitrace.h:

#define DBD_OCI_TRACEON (DBIS->debug >= 6 || dbd_verbose>=6)
#define DBD_OCI_TRACEFP (DBILOGFP)

#define OCIServerRelease_log_stat(sc,errhp,b,bl,ht,ver,stat)\
stat =OCIServerRelease(sc,errhp,b,bl,ht,ver);\
(DBD_OCI_TRACEON) \
? PerlIO_printf(DBD_OCI_TRACEFP,\
 "%sOCIServerRelease(%p)=%s\n",\
 OciTp, sc,oci_status_name(stat)),stat \
: stat

Every single OCI call uses DBD_OCI_TRACEON which in turn uses DBIS->debug and non have a 
imp_xxx handle so this is a very large change. Ensuring the code at each point an OCI call 
is made has an imp_xxh and getting the right one is going to be an awful job especially when 
a load of funcs in oci

Re: DBD::Pg, DBD::Oracle and others are slow with threads due to DBIS

2012-03-15 Thread Tim Bunce
On Thu, Mar 15, 2012 at 10:33:43AM +, Martin J. Evans wrote:
> 
> Argh, DBD::Oracle tracing is a mess wrt to this discussion:
> 
> ocitrace.h:
> 
> #define DBD_OCI_TRACEON   (DBIS->debug >= 6 || dbd_verbose>=6)
> #define DBD_OCI_TRACEFP   (DBILOGFP)
> 
> #define OCIServerRelease_log_stat(sc,errhp,b,bl,ht,ver,stat)\
>   stat =OCIServerRelease(sc,errhp,b,bl,ht,ver);\
>   (DBD_OCI_TRACEON) \
>   ? PerlIO_printf(DBD_OCI_TRACEFP,\
>"%sOCIServerRelease(%p)=%s\n",\
>OciTp, sc,oci_status_name(stat)),stat \
>   : stat
> 
> Every single OCI call uses DBD_OCI_TRACEON which in turn uses
> DBIS->debug and non have a imp_xxx handle so this is a very large
> change. Ensuring the code at each point an OCI call is made has an
> imp_xxh and getting the right one is going to be an awful job
> especially when a load of funcs in oci8.c don't even have a handle. I
> don't see an easy way to automate this change so I'm not sure I've got
> the stomach for this. If I do this will I really "see" some speed up
> as it is a lot of work.

Maybe just do the OCI* function calls that are used in the main fetch
code path, i.e., dbd_st_fetch. Add an imp parameter

-#define OCIServerRelease_log_stat(sc,errhp,b,bl,ht,ver,stat)
+#define OCIServerRelease_log_stat(imp,sc,errhp,b,bl,ht,ver,stat)

stat =OCIServerRelease(sc,errhp,b,bl,ht,ver);\
!   (DBIc_TRACE(imp,...)) \
!   ? PerlIO_printf(DBIc_LOGPIO(imp),\
 "%sOCIServerRelease(%p)=%s\n",\
 OciTp, sc,oci_status_name(stat)),stat \
: stat

Tim.


Re: DBD::Pg, DBD::Oracle and others are slow with threads due to DBIS

2012-03-16 Thread Martin J. Evans

On 15/03/12 21:18, Tim Bunce wrote:

On Thu, Mar 15, 2012 at 10:33:43AM +, Martin J. Evans wrote:


Argh, DBD::Oracle tracing is a mess wrt to this discussion:

ocitrace.h:

#define DBD_OCI_TRACEON (DBIS->debug>= 6 || dbd_verbose>=6)
#define DBD_OCI_TRACEFP (DBILOGFP)

#define OCIServerRelease_log_stat(sc,errhp,b,bl,ht,ver,stat)\
stat =OCIServerRelease(sc,errhp,b,bl,ht,ver);\
(DBD_OCI_TRACEON) \
? PerlIO_printf(DBD_OCI_TRACEFP,\
 "%sOCIServerRelease(%p)=%s\n",\
 OciTp, sc,oci_status_name(stat)),stat \
: stat

Every single OCI call uses DBD_OCI_TRACEON which in turn uses
DBIS->debug and non have a imp_xxx handle so this is a very large
change. Ensuring the code at each point an OCI call is made has an
imp_xxh and getting the right one is going to be an awful job
especially when a load of funcs in oci8.c don't even have a handle. I
don't see an easy way to automate this change so I'm not sure I've got
the stomach for this. If I do this will I really "see" some speed up
as it is a lot of work.


Maybe just do the OCI* function calls that are used in the main fetch
code path, i.e., dbd_st_fetch. Add an imp parameter

-#define OCIServerRelease_log_stat(sc,errhp,b,bl,ht,ver,stat)
+#define OCIServerRelease_log_stat(imp,sc,errhp,b,bl,ht,ver,stat)

stat =OCIServerRelease(sc,errhp,b,bl,ht,ver);\
!   (DBIc_TRACE(imp,...)) \
!   ? PerlIO_printf(DBIc_LOGPIO(imp),\
 "%sOCIServerRelease(%p)=%s\n",\
 OciTp, sc,oci_status_name(stat)),stat \
: stat

Tim.


Last night I finished changing DBD::Oracle to eradicate all DBIS usage. I ran 
into quite a few problems along the way but all DBIS usage is gone except for a 
few calls in functions passed to Oracle. I've not moved to DBIc_TRACE yet but 
when I get time I will do that and add the new DBD trace flag as well 
(hopefully to replace ora_verbose which seems pointless to me simply adding yet 
another test when no-one really uses it other than as on/off).

As a side note there is a phenomenal amount of tracing in DBD::Oracle which it 
would be nice to noop the whole lot out for people who don't want the code 
continually testing whether tracing is on - me.

A quick benchmark:

use DBI;
use strict;
use warnings;
use Benchmark;

my $h = DBI->connect("dbi:Oracle:host=xxx.easysoft.local;sid=test",
 "xxx", "xxx", {RaiseError => 1});
if (@ARGV) {
eval {
$h->do(q/drop table dbis/);
};
setup($h);
}

timethese(10, {
'read' => sub {readit($h)},
'readperrow' => sub {readitperrow($h)}});

#readit($h);

sub setup {
$h->do(q/create table dbis (a int, b varchar(100))/);
$h->begin_work;
my $s = $h->prepare(q/insert into dbis values(?,?)/);
foreach (1..100) {
$s->execute($_, "the quick brown fox jumps over the lazy dog");
}
$h->commit;
print "Table populated\n";
}

sub readit {
my $h = shift;
my $s = $h->prepare(q/select * from dbis/);
$s->execute;
my $d = $s->fetchall_arrayref;
print "Read ", scalar(@$d), " rows\n";
}
sub readitperrow {
my $h = shift;
my $s = $h->prepare(q/select * from dbis/);
$s->execute;
my $rows = 0;
while(my $d = $s->fetchrow_arrayref) {
$rows++;
}
print "Read $rows rows\n";
}

perl 5.14.2

Perl without threads:
 1.42:
  read: 83 wallclock secs (37.55 usr +  5.77 sys = 43.32 CPU) @  0.23/s (n=10)
  readperrow 85 wallclock secs (39.53 usr +  4.98 sys = 44.51 CPU) @  0.22/s 
(n=10)

 subversion trunk:
  read: 85 wallclock secs (40.23 usr +  6.22 sys = 46.45 CPU) @  0.22/s (n=10)
  readperrow: 85 wallclock secs (40.06 usr +  5.32 sys = 45.38 CPU) @  0.22/s 
(n=10)

Perl with threads:
 1.42
  read: 128 wallclock secs (86.11 usr +  5.41 sys = 91.52 CPU) @  0.11/s (n=10)
  readperrow: 137 wallclock secs (95.33 usr +  4.86 sys = 100.19 CPU) @  0.10/s 
(n=10)

subversion trunk:
  read: 94 wallclock secs (52.55 usr +  5.68 sys = 58.23 CPU) @  0.17/s (n=10)
  readperrow: 104 wallclock secs (62.74 usr +  5.06 sys = 67.80 CPU) @  0.15/s 
(n=10)

which only goes to remind me why I don't use a Perl with threads but if you do, 
DBD::Oracle should be a fair bit faster now.

BTW, this change is literally thousands of lines of code so if you depend on 
DBD::Oracle I'd get a copy of the subversion trunk and try it.

Martin
--
Martin J. Evans
Easysoft Limited
http://www.easysoft.com


Re: DBD::Pg, DBD::Oracle and others are slow with threads due to DBIS

2012-03-17 Thread Tim Bunce
On Fri, Mar 16, 2012 at 02:18:00PM +, Martin J. Evans wrote:
> 
> Perl with threads:
>  1.42
>   read: 128 wallclock secs (86.11 usr +  5.41 sys = 91.52 CPU) @  0.11/s 
> (n=10)
>   readperrow: 137 wallclock secs (95.33 usr +  4.86 sys = 100.19 CPU) @  
> 0.10/s (n=10)
> 
> subversion trunk:
>   read: 94 wallclock secs (52.55 usr +  5.68 sys = 58.23 CPU) @  0.17/s (n=10)
>   readperrow: 104 wallclock secs (62.74 usr +  5.06 sys = 67.80 CPU) @  
> 0.15/s (n=10)
> 
> which only goes to remind me why I don't use a Perl with threads but if you 
> do, DBD::Oracle should be a fair bit faster now.

So 92 reduced to 58 cpu, and 100 reduced to 68.

Those are impressive gains!

> BTW, this change is literally thousands of lines of code so if you depend on 
> DBD::Oracle I'd get a copy of the subversion trunk and try it.

Thanks for doing the work Martin!

Tim.


Re: DBD::Pg, DBD::Oracle and others are slow with threads due to DBIS

2012-03-17 Thread Yanick Champoux

On 12-03-16 10:18 AM, Martin J. Evans wrote:

Last night I finished changing DBD::Oracle to eradicate all DBIS usage.

Holy schmollee, you're a beast.

I've synced the Git repo with your work (branch master with the raw 
goodness, branch DBIS-less for a version that can be directly be 
installed from the checkout).


Joy,
`/anick