I'm writing a lot of String constants for a new Cobra parser in support of better autocompletion in the MonoDevelop addin when syntax errors exist in the source code. I have a style question.
In .NET, it is recommended to use PascalCase.
Since Python doesn't have constants, it is recommended to use ALL_CAPS to indicate intent.
In the Cobra compiler source code, camelCase is used for constants.
However, in all those languages, any convention is allowed. So what do you guys use? I've been using camelCase but now I'm thinking I should be using ALL_CAPS (even if it is uglier) as the intent is more clear when just looking at the code in an editor or in a browser that doesn't have support for tooltips. Opinions?
Forums
Naming convention for constants
13 posts
• Page 1 of 2 • 1, 2
Re: Naming convention for constants
The reason I use .fooBar for constants is that I don't want to care very much that they are constants as opposed to vars, properties or methods. If down the road, I change .fooBar from a constant to a property backed by a config file, it will still be called .fooBar. If I never change it to be backed by a config file, it's still perfectly readable as .fooBar.
I kind of regret not going that route with enums members.
I kind of regret not going that route with enums members.
- Charles
- Posts: 2515
- Location: Los Angeles, CA
Re: Naming convention for constants
Ah, that's interesting and makes sense in that case. I see now why no strict naming convention is enforced. In this case though, I am hoping to leverage the fact that string constants are interned so I can just do a nice and fast reference equality check when comparing. So, I wouldn't plan on ever changing them from constants otherwise it would break the equality checks.
- nerdzero
- Posts: 286
- Location: Chicago, IL
Re: Naming convention for constants
If you could do any benchmarks between the interned string comparisons vs. enums vs. int constants, that could be interesting. Or maybe such things are already out there for C# though we'd have to pay attention to what version of .NET or Mono they ran.
- Charles
- Posts: 2515
- Location: Los Angeles, CA
Re: Naming convention for constants
Heh, okay so I did a quick benchmark and interned string comparison is faster but it doesn't matter and I'll explain why. Here's the code I ended up using. One method uses the "==" operator and the other uses the "is" operator.
Here's some results on a decently beefy .NET machine
However, like most benchmarks, these results are artificial. When I actually try to use this approach using a token list obtained from a CobraTokenizer instance I run into an issue. Not all the strings are interned in that case and so the process of determining if a string is interned (and then retrieving the interned version if not) makes the equals operator approach faster. A LOT faster in fact. Adding this code before doing any 'is' comparisons...
...made the time shoot up from 29 ms to 414 ms. I even tried getting creative with a local dictionary mapping each string constant to itself, but the best I could do was bring it down to 119 ms. Still slower than ==. I also tried a third approach using kind.equals(.SOME_CONST, StringComparison.Ordinal) for grins which clocked in around 110 ms.
So, == it is! I always know I shouldn't do any premature optimizations but I just can't help myself sometimes Too clever for my own good!
class Program
const NAMESPACE = "NAMESPACE"
const CLASS = "CLASS"
const DEF = "DEF"
const PRINT = "PRINT"
const ID = "ID"
const DOT = "DOT"
const STRING_SINGLE = "STRING_SINGLE"
const INDENT = "INDENT"
const DEDENT = "DEDENT"
const EOL = "EOL"
const EOF = "EOF"
var iterations = 100_000
def main
tokenKinds = [
'NAMESPACE', 'ID', 'DOT', 'ID', 'EOL', 'EOL', 'INDENT', 'CLASS', 'ID', 'EOL',
'EOL', 'INDENT', 'DEF', 'ID', 'EOL', 'EOL', 'INDENT', 'PRINT', 'STRING_SINGLE',
'EOL', 'DEDENT', 'DEDENT', 'DEDENT', 'EOF'
]
.benchmarkEqualsOperator(tokenKinds)
.benchmarkIsOperator(tokenKinds)
def benchmarkEqualsOperator(tokenKinds as List<of String>)
sw = Diagnostics.Stopwatch()
sw.start
for i in .iterations
for kind in tokenKinds
if kind == .NAMESPACE
pass
else if kind == .CLASS
pass
else if kind == .DEF
pass
else if kind == .PRINT
pass
else if kind == .ID
pass
else if kind == .DOT
pass
else if kind == .STRING_SINGLE
pass
else if kind == .INDENT
pass
else if kind == .DEDENT
pass
else if kind == .EOL
pass
else if kind == .EOF
pass
else
throw FallThroughException("No clause for '[kind]'")
sw.stop
print "Using '==' operator: [sw.elapsedMilliseconds] ms"
def benchmarkIsOperator(tokenKinds as List<of String>)
sw = Diagnostics.Stopwatch()
sw.start
for i in .iterations
for kind in tokenKinds
if kind is .NAMESPACE
pass
else if kind is .CLASS
pass
else if kind is .DEF
pass
else if kind is .PRINT
pass
else if kind is .ID
pass
else if kind is .DOT
pass
else if kind is .STRING_SINGLE
pass
else if kind is .INDENT
pass
else if kind is .DEDENT
pass
else if kind is .EOL
pass
else if kind is .EOF
pass
else
throw FallThroughException("No clause for '[kind]' or it is not interned")
sw.stop
print "Using 'is' operator: [sw.elapsedMilliseconds] ms"
Here's some results on a decently beefy .NET machine
- Code: Select all
Using '==' operator: 72 ms
Using 'is' operator: 29 ms
However, like most benchmarks, these results are artificial. When I actually try to use this approach using a token list obtained from a CobraTokenizer instance I run into an issue. Not all the strings are interned in that case and so the process of determining if a string is interned (and then retrieving the interned version if not) makes the equals operator approach faster. A LOT faster in fact. Adding this code before doing any 'is' comparisons...
if not String.isInterned(kind)
kind = String.intern(kind) to !
...made the time shoot up from 29 ms to 414 ms. I even tried getting creative with a local dictionary mapping each string constant to itself, but the best I could do was bring it down to 119 ms. Still slower than ==. I also tried a third approach using kind.equals(.SOME_CONST, StringComparison.Ordinal) for grins which clocked in around 110 ms.
So, == it is! I always know I shouldn't do any premature optimizations but I just can't help myself sometimes Too clever for my own good!
- nerdzero
- Posts: 286
- Location: Chicago, IL
Re: Naming convention for constants
Are enums or ints an option?
- Charles
- Posts: 2515
- Location: Los Angeles, CA
Re: Naming convention for constants
In what way? Dictionary that maps token strings to id values?
- nerdzero
- Posts: 286
- Location: Chicago, IL
Re: Naming convention for constants
No I was thinking Token.which could be an int or enum instead of a string. This is actually the more common approach if you look at various lexers in various languages.
- Charles
- Posts: 2515
- Location: Los Angeles, CA
Re: Naming convention for constants
Oh, I see. I wasn't planning on modifying the tokenizer, but yeah both NRefactory and Roslyn use enums.
I added this code to the benchmark:
Here's results I can't explain though:
Why's it so slow? Did I do something inefficiently? Is boxing and unboxing adding a significant overhead here?
I added this code to the benchmark:
enum TokenKind
Namespace
Class
Def
Print
Id
Dot
StringSingle
Indent
Dedent
Eol
Eof
class Program
...
def main
...
tokenKinds2 as List<of TokenKind> = [
TokenKind.Namespace, TokenKind.Id, TokenKind.Dot, TokenKind.Id, TokenKind.Eol,
TokenKind.Eol, TokenKind.Indent, TokenKind.Class, TokenKind.Id, TokenKind.Eol,
TokenKind.Eol, TokenKind.Indent, TokenKind.Def, TokenKind.Id, TokenKind.Eol,
TokenKind.Eol, TokenKind.Indent, TokenKind.Print, TokenKind.StringSingle,
TokenKind.Eol, TokenKind.Dedent, TokenKind.Dedent, TokenKind.Dedent,
TokenKind.Eof
]
.benchmarkEnum(tokenKinds2)
...
def benchmarkEnum(tokenKinds as List<of TokenKind>)
sw = Diagnostics.Stopwatch()
sw.start
for i in .iterations
for kind in tokenKinds
if kind == TokenKind.Namespace
pass
else if kind == TokenKind.Class
pass
else if kind == TokenKind.Def
pass
else if kind == TokenKind.Print
pass
else if kind == TokenKind.Id
pass
else if kind == TokenKind.Dot
pass
else if kind == TokenKind.StringSingle
pass
else if kind == TokenKind.Indent
pass
else if kind == TokenKind.Dedent
pass
else if kind == TokenKind.Eol
pass
else if kind == TokenKind.Eof
pass
else
throw FallThroughException("No clause for '[kind]'")
sw.stop
print "Using 'enum' comparison: [sw.elapsedMilliseconds] ms"
Here's results I can't explain though:
- Code: Select all
Using '==' operator: 80 ms
Using 'is' operator: 26 ms
Using 'enum' comparison: 241 ms
Why's it so slow? Did I do something inefficiently? Is boxing and unboxing adding a significant overhead here?
- nerdzero
- Posts: 286
- Location: Chicago, IL
Re: Naming convention for constants
I saw a post on enums being slow under some circumstances in .NET, but don't recall the details.
Two things you can try quickly though:
1) Run with -turbo and see if there is any improvement
2) Change "kind == TokenKind.Namespace" to "kind is TokenKind.Namespace"
3) Change the if-else ladder to a "branch"
Note that regarding 3) some C compilers can do some fancy optimizations for switch() statements involving JMPing to the code location for the case. I don't know if .NET's JIT will do that or not.
Also, without investigating further, I wonder if Cobra is generating a "CobraImp.Equals()" call for your enum comparisons instead of "==". If so, we can fix this. You can check with -kif. See cobra -help.
Two things you can try quickly though:
1) Run with -turbo and see if there is any improvement
2) Change "kind == TokenKind.Namespace" to "kind is TokenKind.Namespace"
3) Change the if-else ladder to a "branch"
Note that regarding 3) some C compilers can do some fancy optimizations for switch() statements involving JMPing to the code location for the case. I don't know if .NET's JIT will do that or not.
Also, without investigating further, I wonder if Cobra is generating a "CobraImp.Equals()" call for your enum comparisons instead of "==". If so, we can fix this. You can check with -kif. See cobra -help.
- Charles
- Posts: 2515
- Location: Los Angeles, CA
13 posts
• Page 1 of 2 • 1, 2
Who is online
Users browsing this forum: No registered users and 19 guests