On 08/14/2019 02:43 PM, Rik wrote:
On 08/14/2019 11:21 AM, John W. Eaton wrote:
> On 8/14/19 1:05 PM, Rik wrote: > >> This jibes with my sense from the profiling that we are doing a
lot of >> unnecessary creation/destruction of objects. >> >> *IF*
heavyweight constructors are a problem, then calling them less >>
frequently by using move constructors and move assignment operators
would >> definitely help. However, that is still a hypothesis. Is
there a >> relatively simple way to test this short of re-writing a
bunch of classes? >> >> One option might be to place a counter in the
constructor, assignment >> operator, and destructor routines for
octave_value. >> >> I should also say that I tried implementing a move
constructor after >> OctConf2018 and I didn't see any improvement.
But, I also could have >> screwed it up and I didn't spend a long time
on it. > > Yeah, see the attached simplified version of our Array
class. There are a few cases that seem (to me, at least, with my
limited understanding) like it should be possible for the compiler to
figure out that it can call move constructor or the move assignment
operator but it doesn't. But AFAICT, it is following the rules. OTOH,
in some cases, the compiler may completely remove calls to the copy
constructor or assignment operator even when move constructors are not
explicitly defined. This happens for me regardless of the optimization
level (using GCC 8.3). > > If we see a large penalty for using atomic
reference counting, then we must be performing many operations where
we increment and decrement the count. Are we doing many of those
operations unnecessarily? If so, and given what I see with the example
class, I'm wondering why more of these operations are not eliminated.
Can we easily find where they are happening? At least that would be a
start to understanding what should be done.
Just to get an idea, I modified the destructor in Array.h to
~ArrayRep (void) {
static long int n = 0;
std::cerr << "~ArrayRep: " << ++n << "\n";
delete [] data; }
I find that running an average statement like "x = 1;" causes 40
destructor calls. That does seem excessive.
A similar experiment with modifying the destructor for octave_value_list
shows that we are creating/destroying a lot more of these then I think
is necessary given the complexity of the statements. A simple function
call seems to require 9 octave_value_list objects. An anonymous
function is 21.
--Rik
:DATA:
octave:3> y = x + 1;
~octave_value_list: 7782
octave:4> y = sin (1);
~octave_value_list: 7783
~octave_value_list: 7784
~octave_value_list: 7785
~octave_value_list: 7786
~octave_value_list: 7787
~octave_value_list: 7788
~octave_value_list: 7789
~octave_value_list: 7790
~octave_value_list: 7791
octave:5> f = @(x) 2*x
f =
@(x) 2 * x
~octave_value_list: 7792
~octave_value_list: 7793
~octave_value_list: 7794
~octave_value_list: 7795
~octave_value_list: 7796
~octave_value_list: 7797
octave:6> y = f(1)
~octave_value_list: 7798
~octave_value_list: 7799
~octave_value_list: 7800
~octave_value_list: 7801
~octave_value_list: 7802
~octave_value_list: 7803
~octave_value_list: 7804
~octave_value_list: 7805
~octave_value_list: 7806
~octave_value_list: 7807
~octave_value_list: 7808
~octave_value_list: 7809
~octave_value_list: 7810
~octave_value_list: 7811
~octave_value_list: 7812
~octave_value_list: 7813
y = 2
~octave_value_list: 7814
~octave_value_list: 7815
~octave_value_list: 7816
~octave_value_list: 7817
~octave_value_list: 7818
~octave_value_list: 7819