hi all,
Pd uses a symbol-table for practically all of its string handling. the main purpose of the the symbol table is, as i understand it, to make string comparison super-fast (as there's no need to compare each character but just a simple address).
now, looking at the code i see quite a number of places¹ where something like this is used:
strcmp(atom_getsymbolarg(i, argc, argv)->s_name, refstring);
what is the reason for this? is it just out of habit (because in all the other projects you usually use strcmp()) or because this is an attempt to not pollute the symbol table needlessly?
or am i missing something? or should they just be replaced with symbol comparisions?
gmsadr IOhannes
¹ i've counted about 135 of such uses (automatically; so I certainly missed a few)
the main purpose of the the symbol table is, as i understand it, to make string comparison super-fast (as there's no need to compare each character but just a simple address).
It is only fast if both sides are `t_symbol *`, but if one side includes a call to gensym(), it will be *slower* than a simple strcmp()!
For example,
flagsym == gensym("-lib")
is my all means worse than
!strcmp(flag, "-lib")
looking at the code i see quite a number of places¹ where something like this is used:
The vast majority of the cases I've found are in settings/flag parsing.
First of all, these are not the performance critical parts of Pd and most of the strings in question are very short, anyway.
Secondly, for symbol comparison to make sense, you would have to create all those symbols upfront and store them somewhere. This would indeed pollute the symbol table and add many lines of extra code for no real benefit.
or should they just be replaced with symbol comparisions?
I'd say: no :-)
Christof
On 11.03.2021 19:26, IOhannes m zmölnig wrote:
hi all,
Pd uses a symbol-table for practically all of its string handling. the main purpose of the the symbol table is, as i understand it, to make string comparison super-fast (as there's no need to compare each character but just a simple address).
now, looking at the code i see quite a number of places¹ where something like this is used:
strcmp(atom_getsymbolarg(i, argc, argv)->s_name, refstring);
what is the reason for this? is it just out of habit (because in all the other projects you usually use strcmp()) or because this is an attempt to not pollute the symbol table needlessly?
or am i missing something? or should they just be replaced with symbol comparisions?
gmsadr IOhannes
¹ i've counted about 135 of such uses (automatically; so I certainly missed a few)
Pd-dev mailing list Pd-dev@lists.iem.at https://lists.puredata.info/listinfo/pd-dev
On 3/11/21 9:34 PM, Christof Ressi wrote:
the main purpose of the the symbol table is, as i understand it, to make string comparison super-fast (as there's no need to compare each character but just a simple address).
It is only fast if both sides are `t_symbol *`, but if one side includes a call to gensym(), it will be *slower* than a simple strcmp()!
For example,
flagsym == gensym("-lib")
funnily enough, this was exactly the line that triggered my query. how did you know? (probably because you checked the background of my other post)
is my all means worse than
!strcmp(flag, "-lib")
well, no. all those specific comparisions for "-lib" are done in loop, so you would probably put the gensym("-lib") outside of that loop.
First of all, these are not the performance critical parts of Pd and most of the strings in question are very short, anyway.
Secondly, for symbol comparison to make sense, you would have to create all those symbols upfront and store them somewhere. This would indeed pollute the symbol table and add many lines of extra code for no real benefit.
i just find the code mostly more readable. `!strcmp(flag, "-lib")` is of course fine (apart from the inverse logic of strcmp), but `!strcmp(argv[i].a_w.w_symbol->s_name, ">")` is not. in this case i would happily swap that for a slow `(gensym(">") == atom_getsymbol(argv+i))`.
anyhow, most of those symbols (with the exception of the actual flags), are also Pd-objects, so the symtable pollution shouldn't really matter.
gfsmdrt IOhannes
how did you know?
Telepathy :-D
i just find the code mostly more readable.
That's probably a matter of taste. "!strcmp(...)" is a very familiar idiom for every C programmer and I find it perfectly readable.
`!strcmp(flag, "-lib")` is of course fine (apart from the inverse logic of strcmp), but `!strcmp(argv[i].a_w.w_symbol->s_name, ">")` is not.
Sure, but neither is "gensym(">") == argv[i].a_w.w_symbol".
in this case i would happily swap that for a slow `(gensym(">") == atom_getsymbol(argv+i))`.
For a *single* comparison, it might be more readable for some people. But if you want to compare against several strings - maybe even in a loop -, you would naturally save the symbol in a variable, and then the strcmp() version becomes quite readable, IMO.
all those specific comparisions for "-lib" are done in loop, so you would probably put the gensym("-lib") outside of that loop.
That would work, but I think it's overkill. Also, it would decrease readability, because you can't immediately see the actual flag string, but instead only see a variable. Also, the performance gain would be minimal at best, because the strings are very short and strcmp() terminates on the first non-matching character, anyway.
If you consider that "gensym()" needs to
1) hash the whole string,
2) do a (fast) table look-up and
3) iterate over the bucket list and do a strcmp() until it finds an exact match
you would need a couple of loop iterations to make up for those extra initial costs and gain any performance benefit.
---
Anyway, it seem like we're now talking more about readability than performance (which seemed to be your original concern).
I would say that for "ugly" lines like
`!strcmp(argv[i].a_w.w_symbol->s_name, ">")`
saving the string in a local variable would already increase readability significantly:
const char *s = argv[i].a_w.w_symbol->s_name;
if (!strcmp(s, ">")) ...
Christof
On 11.03.2021 22:13, IOhannes m zmölnig wrote:
On 3/11/21 9:34 PM, Christof Ressi wrote:
the main purpose of the the symbol table is, as i understand it, to make string comparison super-fast (as there's no need to compare each character but just a simple address).
It is only fast if both sides are `t_symbol *`, but if one side includes a call to gensym(), it will be *slower* than a simple strcmp()!
For example,
flagsym == gensym("-lib")
funnily enough, this was exactly the line that triggered my query. how did you know? (probably because you checked the background of my other post)
is my all means worse than
!strcmp(flag, "-lib")
well, no. all those specific comparisions for "-lib" are done in loop, so you would probably put the gensym("-lib") outside of that loop.
First of all, these are not the performance critical parts of Pd and most of the strings in question are very short, anyway.
Secondly, for symbol comparison to make sense, you would have to create all those symbols upfront and store them somewhere. This would indeed pollute the symbol table and add many lines of extra code for no real benefit.
i just find the code mostly more readable. `!strcmp(flag, "-lib")` is of course fine (apart from the inverse logic of strcmp), but `!strcmp(argv[i].a_w.w_symbol->s_name, ">")` is not. in this case i would happily swap that for a slow `(gensym(">") == atom_getsymbol(argv+i))`.
anyhow, most of those symbols (with the exception of the actual flags), are also Pd-objects, so the symtable pollution shouldn't really matter.
gfsmdrt IOhannes
Pd-dev mailing list Pd-dev@lists.iem.at https://lists.puredata.info/listinfo/pd-dev