Please bear with me since I'm a new programmer with a Python background.
I'm trying to speed-up the code below, and using "ref object" greatly helps. I've noticed that the compiler will give warnings -- even more often when using "ref object" -- similar to "Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]".
If my Item and Items objects become too complex, then the compiler also wants constructors, to initialize attributes. So I created convenience constructors -- getItem() and getItemSeq() -- and that silenced the warnings. The code below is a minimal example showing that my getItem() initializes an Item object with a TimeInfo variable set to the unix epoch, and getItemSeq() initializes a seq[Item] without setting the TimeInfo variable.
It's possible to silence the nim warnings simply by creating a constructor that creates the object type that you need to create, without a full initialization. But is this safe to do? The warning says a proper initialization will be enforced in the future. I can create a var recs:seq[Item] object with a sequence of 10_000_000 Items, and the newSeq[Item](len) construct is much faster than calling my getItem() (about 2 secs vs 45 secs).
Background and question: initially nim wanted me to create a constructor to initialize dti, and then I found I could create a proc that silenced nim's warnings, but didn't actually initialize dti (when using ".add()" to extend the sequence). I'm very happy with that, since it's much faster, and I can properly initialize my object myself before it's used. In the future, will things change and nim will get more strict about initializations? I'm happy with the current setup that leaves one free to do a basic object creation now and later initialize it fully (newSeq does this initialization to 'zero' fine and is fast).
Thanks.
import times, sequtils
type
Item = ref object
dti: TimeInfo
dmonth: int
Items = ref object
recs: seq[Item]
proc getItem(): Item =
## We remove compiler warning, and set dti.
var dti:TimeInfo= getGMTime(fromSeconds(0))
return Item(dti:dti)
proc getItemSeq(len: int= 1): seq[Item] =
return newSeq[Item](len)
# var r= new(Item) # get a compiler warning.
var r= getItem() # fix compiler warning.
# var recs:seq[Item]= @[] # empty sequence.
var recs:seq[Item]= newSeqWith(10, getItem()) # dti initialized to epoch for 0-9.
# recs.add(newSeq[Item](10_000_000)) # dti not initialized to epoch for 10-(recs.high-1).
recs.add(getItemSeq(10_000_000)) # dti not initialized to epoch for 10-(recs.high-1).
var series:Items= Items(recs:recs) # with Item and Items "ref object" type, this is
# copy by reference and fast: note month is 'mApr'
# for both recs[0] and series.recs[0] below, and their
# address is identical.
#
# BUT if Item and Items are "object" types, it's copy
# by value and slow; month is different for recs[0]
# and series.recs[0] below, and their addresses differ.
echo "recs[0] (DTI INIT TO EPOCH)= \n", repr(recs[0])
echo "\nrecs[high-1] (DTI UNINITIALIZED)= \n", repr(recs[recs.high-1])
recs[recs.high-1].dti= getGMTime(fromSeconds(0))
echo "\nrecs[high-1] AFTER DTI MANUALLY ASSIGNED TO EPOCH: \n", repr(recs[recs.high-1])
echo "\nseries.recs.high= ", $series.recs.high
series.recs[0].dti.month= mApr
echo "\nADDRESS recs[0]= \n", repr(addr(recs[0])),
"\n ;ADDRESS series.recs[0]= \n", repr(addr(series.recs[0]))
Thank you. My "dti" field is a TimeInfo with many range[..] subrange types, which caused the problem.
Currently I can create 20 million objects containing TimeInfo fields in under a sec (see code below), but it hangs infinitely when I initialize them myself with a getItem() constructor. I feel like I'm fooling the compiler using newSeq[T](len = 0): seq[T] and am wondering if that is an acceptable method, or will it cause problems later?
Is there a better way to speedily create containers with subrange types, which doesn't hang? The TimeInfo value is just one small part of my container, and I don't mind initializing those later during idle CPU cycles, if that helps my program start immediately with my container (mostly) pre-initialized, other than TimeInfo subrange types.
import times, sequtils
type
Item = ref object
dti: TimeInfo
let GTI:TimeInfo= getGMTime(fromSeconds(0))
proc getItem(): Item =
return Item(dti:GTI)
# var rec:Item # get [ProveInit] warning.
var rec2= getItem() # no warning.
# var recs1:seq[Item]= newSeqWith(20_000_000, getItem()) # doesn't finish within 70 minutes.
var recs2:seq[Item]= @[]; recs2.add(newSeq[Item](20_000_000)) # finishes within 1 sec.