Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sclang consumes high CPU when opening a session with multiple files #980

Closed
jamshark70 opened this issue Oct 20, 2013 · 7 comments
Closed
Labels
bug Issues that relate to unexpected/unwanted behavior. Don't use for PRs. comp: sclang sclang C++ implementation (primitives, etc.). for changes to class lib use "comp: class library" env: SCIDE

Comments

@jamshark70
Copy link
Contributor

Consistently reproducible behavior here: Whenever I switch to a session in the IDE that contains, say, 9-10 files, sclang pushes one of the cores to 100% for several seconds. I've watched processes in top -- it's definitely sclang, not scide.

The attached screenshot was taken a few seconds after I reproduced the problem. I switched the session at what looks like about 12 seconds in the graph (reading from the right). CPU 2 (red line) spikes up for about 4 seconds after that.

The session's files add up to 159154 characters, computed as follows:

var size = 0;
Document.allDocuments.do { |doc|
    if(doc.title.asString.containsi("untitled").not) {
        size = size + doc.string.size;
    };
};
size;

There must be some GC or other internal issue (memory management, perhaps?)... but I have no idea how to reproduce it using only sclang code. This doesn't do it:

bench { x = String.newClear(159154); }
time to run: 0.00016593933105469 seconds.

cpu-spike-switch-session

@telephon
Copy link
Member

On Mac OS 10.8, using the latest master, I can't reproduce this behaviour. when testing, I used top -pid <process-id-of-sclang>.

I noticed that when I run this bit of unrelated code:
fork { loop { 0.01.wait; 1000.rand } } I get a buildup of CPU till almost 80 % that falls again, in a cycle of 10 seconds or so.

@jamshark70
Copy link
Contributor Author

On Mac OS 10.8, using the latest master, I can't reproduce this behaviour.

How long are your code files? The problem does not manifest with short files.

Did some benchmarking -- the culprit is chars = String.fill(chars.size, {|i| chars[i].asAscii})

Usually this is very quick, but it seems quite time consuming for long files (here, I'm omitting the short files that took less than 0.1 sec):

>> syncFromIDE: b-vs-d.scd
chars asAscii -- time to run: 0.23945093154907 seconds.
>> syncFromIDE: mel3-4.scd
chars asAscii -- time to run: 0.16929697990417 seconds.
>> syncFromIDE: ScIDE.sc
chars asAscii -- time to run: 0.16332912445068 seconds.
>> syncFromIDE: 04-defs.scd
chars asAscii -- time to run: 2.1364738941193 seconds.
>> syncFromIDE: perc-92bpm.scd
chars asAscii -- time to run: 1.0300500392914 seconds.

The worst of them, 04-defs.scd, contains 59314 characters. So let's assume syncFromIDE receives an Int8Array.

x = Array.fill(59314, { rrand(32, 127) });
bench { y = String.fill(x.size, { |i| x[i].asAscii }) };
--> time to run: 2.9793999195099 seconds.

Ouch.

bench { y = String.fill(59314, $ ) };
--> time to run: 2.9324381351471 seconds.

Ouch.

That's as far as I can go with it this morning...

@jamshark70
Copy link
Contributor Author

While I was out for lunch, I started to suspect that maybe String.fill uses concatenation. Indeed...

    *fill { | size, function | // any output for function is accepted
        var obj = "";
        size.do { | i | obj = obj ++ function.value(i); };
        ^obj
    }

That makes sense, if we aren't assuming that every item will be a Char.

If I rewrite syncFromIDE as follows:

    *syncFromIDE {|quuid, title, chars, isEdited|
        var doc, argChars;
        isEdited = isEdited.booleanValue;

        // this bit is new
        argChars = chars;
        chars = String.new(argChars.size);
        argChars.do { |ch| chars.add(ch.asAscii) };

        if((doc = this.findByQUuid(quuid)).isNil, {
            doc = super.prBasicNew.initFromIDE(quuid, title, chars, isEdited);
            allDocuments = allDocuments.add(doc);
        }, { doc.initFromIDE(quuid, title, chars, isEdited) });
    }

... then 2.1364738941193 seconds to process 04-defs.scd in my earlier test turns into 0.012862205505371, some 166 times faster.

I suppose it would be useful to add a *fillChars method to String? So that people would be less tempted to use the general-case but massively slower String.fill?

@jamshark70
Copy link
Contributor Author

Oh..... wslib overwrites String *fill with the slower version. So that's why nobody else could reproduce it.

(Insert boilerplate lecture about overwriting core methods...)

So I guess the best fix is the above, since quarks will not be able to interfere with it then (unless some...one decides that syncFromIDE would be a good candidate for "improvement"...).

@muellmusik
Copy link
Contributor

I think the best solution would be to have Wouter (or somebody else) switch that to a new method in wslib.

Closing for now.

@woutersnoei
Copy link
Contributor

ok, fixed in wslib. String:fill is now renamed to String:fillJoin (and String:collect to String:collectJoin)

@muellmusik
Copy link
Contributor

Thanks!

On 3 Nov 2013, at 12:20, Wouter Snoei notifications@github.com wrote:

ok, fixed in wslib. String:fill is now renamed to String:fillJoin (and String:collect to String:collectJoin)


Reply to this email directly or view it on GitHub.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issues that relate to unexpected/unwanted behavior. Don't use for PRs. comp: sclang sclang C++ implementation (primitives, etc.). for changes to class lib use "comp: class library" env: SCIDE
Projects
None yet
Development

No branches or pull requests

4 participants