10
\$\begingroup\$

I've writen a C# JSON parser, but its performance is not as good as JSON.NET.

Running the same test, my parser takes 278 ms and JSON.NET takes 24 ms. What should I do to optimize it? It seems that the dynamic type slows down the lib.

I know this parser doesn't support floats and negative numbers, but it doesn't matter.

JsonObject.cs

public class JsonObject
{
    private readonly Dictionary<string, dynamic> dict = new Dictionary<string, dynamic>();

    public void AddKeyValue(string key, dynamic value) {
        dict.Add(key, value);
    }

    public void AddKeyValue(KeyValuePair<string, dynamic>? pair) {
        if (pair.HasValue)
            dict.Add(pair.Value.Key, pair.Value.Value);
    }

    public dynamic this[string key] {
        get {
            return dict[key];
        }
        set {
            dict[key] = value;
        }
    }

    public static dynamic FromFile(string filename) {
        var lexer = Lexer.FromFile(filename);
        var parser = new Parser(lexer);
        return parser.Parse();
    }

    public static dynamic FromString(string content) {
        var lexer = Lexer.FromString(content);
        var parser = new Parser(lexer);
        return parser.Parse();
    }
}

Parser.cs

public class Parser
{
    private readonly ParseSupporter _;

    public Parser(Lexer lexer) {
        _ = new ParseSupporter(lexer);
    }

    public dynamic Parse() {
        if (_.MatchToken(TokenType.SyntaxType, "{")) {
            return ParseJsonObject();
        }
        if (_.MatchToken(TokenType.SyntaxType, "[")) {
            return ParseJsonArray();
        }
        throw new FormatException();
    }

    private List<dynamic> ParseJsonArray() {
        var result = new List<dynamic>();
        _.UsingToken(TokenType.SyntaxType, "[");

        var value = ParseValue();
        while (value != null) {
            result.Add(value);
            _.UsingToken(TokenType.SyntaxType, ",");
            value = ParseValue();
        }

        _.UsingToken(TokenType.SyntaxType, "]");

        return result;
    }

    private JsonObject ParseJsonObject() {
        var j = new JsonObject();

        _.UsingToken(TokenType.SyntaxType, "{");

        var pair = ParsePair();
        while (pair != null) {
            j.AddKeyValue(pair);
            _.UsingToken(TokenType.SyntaxType, ",");
            pair = ParsePair();
        }

        _.UsingToken(TokenType.SyntaxType, "}");
        return j;
    }

    private KeyValuePair<string, dynamic>? ParsePair() {
        var key = string.Empty;
        {
            var token = _.UsingToken(TokenType.StringType);
            if (token == null) {
                return null;
            }
            key = token.Value.Value;
        }
        _.UsingToken(TokenType.SyntaxType, ":");
        var value = ParseValue();
        if (value == null) {
            return null;
        }
        return new KeyValuePair<string, dynamic>(key, value);
    }

    private dynamic ParseValue() {
        if (_.MatchToken(TokenType.SyntaxType, "{")) {
            return ParseJsonObject();
        }
        if (_.MatchToken(TokenType.SyntaxType, "[")) {
            return ParseJsonArray();
        }
        {
            var token = _.UsingTokenExpect(TokenType.SyntaxType);
            return token != null ? token.Value.RealValue : null;
        }
    }
}
\$\endgroup\$
  • 2
    \$\begingroup\$ Are your timings averaged over several runs using a release build? Dynamic has some first time costs associated with it. \$\endgroup\$ – RobH Sep 3 '15 at 9:35
  • 4
    \$\begingroup\$ Unless you're doing this for fun / education, why not just use JSON.NET? \$\endgroup\$ – craftworkgames Sep 3 '15 at 10:46
  • \$\begingroup\$ So, when you benchmark your solution, where is the majority of time spent? During run: 25% spent in functionA, 50% spent in functionB ... Is ParseSupporter some kind of regex-thingy if so, that is likely VERY slow \$\endgroup\$ – Daniel MesSer Sep 3 '15 at 12:04
  • 4
    \$\begingroup\$ This --> _ is the most freaky variable name that I've ever seen ;-] \$\endgroup\$ – t3chb0t Sep 3 '15 at 14:50
  • 1
    \$\begingroup\$ All right, I'll rename _ \$\endgroup\$ – YangFan Sep 4 '15 at 4:01
2
\$\begingroup\$

(It seems like you've improved performance but I hate to see a good question go unanswered)

Questions about performance can really only be answered with measurements, and unfortunately your question doesn't provide a complete enough program to measure. Furthermore, the missing lexer code presents opportunities for sub-optimal performance, e.g. inefficient regular expressions.

One thing that does stand out in your code is the use of dynamic. I don't see you calling any functions on references of type dynamic so you might just be able to replace them with Object or your own json object root type. If you really need to take advantage of dynamic, there is an overhead. As this answer explains, there is a per-site cost and a smaller per-run cost. Benchmark code with too few iterations could exaggerate the setup cost, though real-world usage might not have enough runs through to amortize that.

\$\endgroup\$
1
\$\begingroup\$

As pointed in the comments, _ is a terrible variable name. Don't do that. :p

Overall, your variable names need to be reworked. Why is your dict named dict? Why not dictionary? Then again, what does this dictionary hold? It should be named accordingly. It's a set of dynamic properties, or something like that? Name it dynamicProperties! It might be off, but you get the point, good naming is important.

You never check your parameters for null. You should. This way you'll catch the problem as soon as possible instead of potentially receiving a NullReferenceException at some point in your execution.

I'm not sure there's a use to your JsonObject. All it does is hold a dictionary of properties. Why wouldn't you simply use a dynamic variable and extract the FromFile and FromString methods to helper static methods?

Your bracket style doesn't respect C#'s standard. I guess that's not too bad, but you need to be consistent. Use the same bracket style everywhere (Right now your class's brackets are on a different style).

Also, you have useless brackets at some place, don't do that. It's confusing and serves no purpose.

EX :

{
    var token = _.UsingTokenExpect(TokenType.SyntaxType);
    return token != null ? token.Value.RealValue : null;
}

I can't help much about performance, but if I remember correctly the JSON.Net maker has a blog post where he explains how he improved the JSON.Net's performance. Otherwise, you can see how he's done with the source code!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.