Why (actually) does it take me so long to make 1000 cubes? Creating 1000's of objects is slower than I thought it should be. Based on a suggestion here it could be related to name collision checking so I thought I would look into it. Here is some data, and the script I used. As a reference, I also did a simple name comparison between all pairs of objects using an extremely sloowwwww loop in python. That is almost 100 times faster than the creation (which is happening in C, right?) So something else is going on. 

Both of these times are clearly proportional to n^2, so it does seem to be checking the new shape against all the others. The times don't change much when I use icospheres (80 faces) instead of cubes(6 faces), so it's based on objects, not faces. That rules out some kinds of checking.

My question is, first, why (actually) is it so slow, and second, how can I create thousands of objects (e.g. cubes) in a script, without waiting minutes each time I tweak some parameter, while still seeing all of them once it's done. I'm using 2.74.

NOTE: I did some spot checking by running the loop only once, or over and over with the same nxy, and there does not appear to be contamination of the measurements due to memory issues (see my comments about garbage collection in the script). 



import bpy
import numpy as np
import time

wxy = 15.0
sx, sy, sz = 0.9, 0.9, 0.2
t_create_list, t_check_list = [], []
n_create_list, n_check_list, n_same_list = [], [], []
nxy_list = [10, 15, 20, 25, 30, 35, 40]
listlen = len(nxy_list)

for ii, nxy in enumerate(nxy_list):
    rcube = 0.5 * wxy / float(nxy)
    q = 0.5 * wxy * (1. - 1. / float(nxy))
    xyc = np.linspace(-q, q, nxy)
    XC, YC = np.meshgrid(xyc, xyc)
    ZC = np.zeros_like(XC)
    centers = zip(XC.flatten(), YC.flatten(), ZC.flatten())

    start = time.clock()
    for ic, center in enumerate(centers):
        ok = bpy.ops.mesh.primitive_cube_add(radius=rcube, location=center)
        ao = bpy.context.active_object
        ao.scale = (sx, sy, sz)
    stop = time.clock()
    time_to_create = stop - start

    so = bpy.context.selectable_objects
    number = len(so)

    same, count = 0, 0
    start = time.clock()
    for i in range(number):  #very sloowwwww way to compare names
        for j in range(i, number):
            count += 1
            if so[i].name == so[j].name:
                same += 1
    stop = time.clock()
    time_to_check = stop - start
    t_create_list.append(time_to_create)
    t_check_list.append(time_to_check)
    n_create_list.append(number)
    n_check_list.append(count)
    n_same_list.append(same)

    for thing in so:
        thing.select = True
        bpy.ops.object.delete(use_global=True)
    # but can I invoke garbage collection inside this loop?
    # for example, can I somehow do a save .blend which should cause garbage collection?

    print("number ", ii+1, " of ", listlen)

t_create_list = [round(thing,4) for thing in t_create_list]
t_check_list =  [round(thing,4) for thing in t_check_list]

lists = [n_create_list, t_create_list, n_check_list,
         t_check_list, n_same_list]

save = np.array(lists[0],dtype='float')
for thing in lists[1:]:
    save = np.vstack((save, thing))
np.save("check_it_out", save)
I followed the code from bpy.data.objects.new and will put this here.

Call Hierarchy

rna_Main_objects_new               /* called by python - bpy.data.objects.new */
    BKE_object_add_only_object     /* set default values for the object */
        BKE_libblock_alloc         /* allocate memory for the object */
            new_id                 /* name validation - fallback, not \0, utf8 */
                check_for_dupid    /* check for collisions and find new name*/
                    is_dupid       /* linear search for duplicate name */
                id_sort_by_name    /* sort list */


Links


rna_Main_objects_new
BKE_object_add_only_object
BKE_libblock_alloc
new_id
check_for_dupid
id_sort_by_name


Condensed body of new_id

bool new_id(ListBase *lb, ID *id, const char *tname)
{
    bool result;
    char name[MAX_ID_NAME - 2];

    BLI_strncpy(name, tname, sizeof(name));

    result = check_for_dupid(lb, id, name);
    strcpy(id-&gt;name + 2, name);

    /* however all data in blender should be sorted, not just duplicate names
     * sorting should not hurt, but noting just incase it alters the way other
     * functions work, so sort every time */

    id_sort_by_name(lb, id);

    return result;
}


Body of is_dupid called in check_for_dupid

static ID *is_dupid(ListBase *lb, ID *id, const char *name)
{
    ID *idtest = NULL;

    for (idtest = lb-&gt;first; idtest; idtest = idtest-&gt;next) {
        /* if idtest is not a lib */ 
        if (id != idtest &amp;&amp; idtest-&gt;lib == NULL) {
            /* do not test alphabetic! */
            /* optimized */
            if (idtest-&gt;name[2] == name[0]) {
                /* STREQ - macro for strcmp */
                if (STREQ(name, idtest-&gt;name + 2)) break;
            }
        }
    }

    return idtest;
}


Body of id_sort_by_name

void id_sort_by_name(ListBase *lb, ID *id)
{
    ID *idtest;

    /* insert alphabetically */
    if (lb-&gt;first != lb-&gt;last) {
        BLI_remlink(lb, id);

        idtest = lb-&gt;first;
        while (idtest) {
            if (BLI_strcasecmp(idtest-&gt;name, id-&gt;name) &gt; 0 || 
               (idtest-&gt;lib &amp;&amp; !id-&gt;lib)) {
                BLI_insertlinkbefore(lb, idtest, id);
                break;
            }
            idtest = idtest-&gt;next;
        }
        /* as last */
        if (idtest == NULL) {
            BLI_addtail(lb, id);
        }
    }
}


So indeed blender checks for duplicates and furthermore removes the id from the list again and inserts it alphabetically. 

You can't even trick it by choosing names in the right order C, B, A because is_dupid will search the full list regardless. 

Though if you specify a non colliding name it exits check_for_dupid early and does not have to find a name on its own.

Additionally as others have mentioned you should not use operators unless you have to and only need to create the mesh once.