Skip to content

Commit afb8ea3

Browse files
committed
[[ Perf ]] Improve performance of 'repeat counted'.
The 'repeat for' and 'repeat ... times' execution methods have been split into two resulting in a significant speed boost to the 'n times' form.
1 parent 1bb4fc1 commit afb8ea3

File tree

5 files changed

+377
-141
lines changed

5 files changed

+377
-141
lines changed

benchmarks/lcs/control/repeat.livecodescript

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,29 @@ for more details.
1616
You should have received a copy of the GNU General Public License
1717
along with LiveCode. If not see <http://www.gnu.org/licenses/>. */
1818

19+
on BenchmarkRepeatForever
20+
local tCount
21+
put 10000000 into tCount
22+
BenchmarkStartTiming
23+
repeat forever
24+
subtract 1 from tCount
25+
if tCount is 0 then
26+
exit repeat
27+
end if
28+
end repeat
29+
BenchmarkStopTiming
30+
end BenchmarkRepeatForever
31+
1932
on BenchmarkRepeatCount
2033
BenchmarkStartTiming
21-
repeat 100000000 times
34+
repeat 100000000 times
2235
end repeat
2336
BenchmarkStopTiming
2437
end BenchmarkRepeatCount
2538

2639
on BenchmarkRepeatWith
2740
BenchmarkStartTiming
28-
repeat with i = 1 to 100000000
41+
repeat with i = 1 to 10000000
2942
end repeat
3043
BenchmarkStopTiming
3144
end BenchmarkRepeatWith

engine/src/exec-keywords.cpp

Lines changed: 140 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,33 @@ void MCKeywordsExecuteRepeatStatements(MCExecContext& ctxt, MCStatement *stateme
368368
}
369369
}
370370

371+
void MCKeywordsExecRepeatCount(MCExecContext& ctxt, MCStatement *statements, MCExpression *endcond, uint2 line, uint2 pos)
372+
{
373+
MCAutoValueRef t_condition;
374+
375+
if (!ctxt . TryToEvaluateExpression(endcond, line, pos, EE_REPEAT_BADFORCOND, &t_condition))
376+
return;
377+
378+
// SN-2015-01-14: [[ Bug 14377 ]] Throw an error as it used to be done
379+
integer_t t_count;
380+
if (!ctxt . ConvertToInteger(*t_condition, t_count) && (MCtrace || MCnbreakpoints)
381+
&& !MCtrylock && !MClockerrors)
382+
{
383+
MCB_error(ctxt, line, pos, EE_REPEAT_BADFORCOND);
384+
return;
385+
}
386+
387+
while(t_count > 0)
388+
{
389+
bool t_done;
390+
MCKeywordsExecuteRepeatStatements(ctxt, statements, line, pos, t_done);
391+
if (t_done)
392+
break;
393+
394+
t_count -= 1;
395+
}
396+
}
397+
371398
void MCKeywordsExecRepeatFor(MCExecContext& ctxt, MCStatement *statements, MCExpression *endcond, MCVarref *loopvar, File_unit each, uint2 line, uint2 pos)
372399
{
373400
MCAutoArrayRef t_array;
@@ -382,10 +409,7 @@ void MCKeywordsExecRepeatFor(MCExecContext& ctxt, MCStatement *statements, MCExp
382409
uintptr_t t_iterator;
383410
// SN2015-06-15: [[ Bug 15457 ]] The index can be a negative index.
384411
index_t t_sequenced_iterator;
385-
const byte_t *t_data_ptr, *t_data_end;
386-
Parse_stat ps;
387-
MCScriptPoint *sp = nil;
388-
int4 count = 0;
412+
const byte_t *t_data_ptr;
389413

390414
MCTextChunkIterator *tci = nil;
391415

@@ -394,88 +418,74 @@ void MCKeywordsExecRepeatFor(MCExecContext& ctxt, MCStatement *statements, MCExp
394418

395419
bool t_sequence_array;
396420
t_sequence_array = false;
397-
398-
if (loopvar != NULL)
421+
422+
if (each == FU_ELEMENT || each == FU_KEY)
399423
{
400-
if (each == FU_ELEMENT || each == FU_KEY)
401-
{
402-
if (!ctxt . ConvertToArray(*t_condition, &t_array))
403-
return;
404-
405-
// SN-2015-06-15: [[ Bug 15457 ]] If this is a numerical array, do
406-
// it in order - even if it does not start at 1
407-
if (each == FU_ELEMENT && MCArrayIsNumericSequence(*t_array, t_sequenced_iterator))
408-
{
409-
t_sequence_array = true;
410-
if (!MCArrayFetchValueAtIndex(*t_array, t_sequenced_iterator, t_value))
411-
return;
412-
}
413-
else
414-
{
415-
t_iterator = 0;
416-
if (!MCArrayIterate(*t_array, t_iterator, t_key, t_value))
417-
return;
418-
}
419-
}
420-
else if (each == FU_BYTE)
424+
if (!ctxt . ConvertToArray(*t_condition, &t_array))
425+
return;
426+
427+
// SN-2015-06-15: [[ Bug 15457 ]] If this is a numerical array, do
428+
// it in order - even if it does not start at 1
429+
if (each == FU_ELEMENT && MCArrayIsNumericSequence(*t_array, t_sequenced_iterator))
421430
{
422-
if (!ctxt . ConvertToData(*t_condition, &t_data))
431+
t_sequence_array = true;
432+
if (!MCArrayFetchValueAtIndex(*t_array, t_sequenced_iterator, t_value))
423433
return;
424-
425-
t_length = MCDataGetLength(*t_data);
426-
t_data_ptr = MCDataGetBytePtr(*t_data);
427434
}
428435
else
429436
{
430-
if (!ctxt . ConvertToString(*t_condition, &t_string))
437+
t_iterator = 0;
438+
if (!MCArrayIterate(*t_array, t_iterator, t_key, t_value))
431439
return;
432-
433-
switch (each)
434-
{
435-
case FU_LINE:
436-
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_LINE);
437-
break;
438-
case FU_PARAGRAPH:
439-
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_PARAGRAPH);
440-
break;
441-
case FU_SENTENCE:
442-
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_SENTENCE);
443-
break;
444-
case FU_ITEM:
445-
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_ITEM);
446-
break;
447-
case FU_WORD:
448-
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_WORD);
449-
break;
450-
case FU_TRUEWORD:
451-
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_TRUEWORD);
452-
break;
453-
case FU_TOKEN:
454-
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_TOKEN);
455-
break;
456-
case FU_CODEPOINT:
457-
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_CODEPOINT);
458-
break;
459-
case FU_CODEUNIT:
460-
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_CODEUNIT);
461-
break;
462-
case FU_CHARACTER:
463-
default:
464-
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_CHARACTER);
465-
break;
466-
}
467440
}
468441
}
442+
else if (each == FU_BYTE)
443+
{
444+
if (!ctxt . ConvertToData(*t_condition, &t_data))
445+
return;
446+
447+
t_length = MCDataGetLength(*t_data);
448+
t_data_ptr = MCDataGetBytePtr(*t_data);
449+
}
469450
else
470451
{
471-
// SN-2015-01-14: [[ Bug 14377 ]] Throw an error as it used to be done
472-
if (!ctxt . ConvertToInteger(*t_condition, count) && (MCtrace || MCnbreakpoints)
473-
&& !MCtrylock && !MClockerrors)
474-
{
475-
MCB_error(ctxt, line, pos, EE_REPEAT_BADFORCOND);
452+
if (!ctxt . ConvertToString(*t_condition, &t_string))
476453
return;
477-
}
478-
count = MCU_max(count, 0);
454+
455+
switch (each)
456+
{
457+
case FU_LINE:
458+
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_LINE);
459+
break;
460+
case FU_PARAGRAPH:
461+
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_PARAGRAPH);
462+
break;
463+
case FU_SENTENCE:
464+
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_SENTENCE);
465+
break;
466+
case FU_ITEM:
467+
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_ITEM);
468+
break;
469+
case FU_WORD:
470+
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_WORD);
471+
break;
472+
case FU_TRUEWORD:
473+
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_TRUEWORD);
474+
break;
475+
case FU_TOKEN:
476+
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_TOKEN);
477+
break;
478+
case FU_CODEPOINT:
479+
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_CODEPOINT);
480+
break;
481+
case FU_CODEUNIT:
482+
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_CODEUNIT);
483+
break;
484+
case FU_CHARACTER:
485+
default:
486+
tci = MCStringsTextChunkIteratorCreate(ctxt, *t_string, CT_CHARACTER);
487+
break;
488+
}
479489
}
480490

481491
bool done;
@@ -491,83 +501,76 @@ void MCKeywordsExecRepeatFor(MCExecContext& ctxt, MCStatement *statements, MCExp
491501
{
492502
MCAutoStringRef t_unit;
493503
MCAutoDataRef t_byte;
494-
if (loopvar != NULL)
504+
switch (each)
495505
{
496-
switch (each)
506+
case FU_KEY:
507+
{
508+
loopvar -> set(ctxt, t_key);
509+
if (!MCArrayIterate(*t_array, t_iterator, t_key, t_value))
510+
endnext = true;
511+
}
512+
break;
513+
514+
case FU_ELEMENT:
497515
{
498-
case FU_KEY:
516+
loopvar -> set(ctxt, t_value);
517+
// SN-2015-06-15: [[ Bug 15457 ]] Sequenced, numeric arrays
518+
// have their own iterator
519+
if (t_sequence_array)
499520
{
500-
// MW-2010-12-15: [[ Bug 9218 ]] Make a copy of the key so that it can't be mutated
501-
// accidentally.
502-
MCNewAutoNameRef t_key_copy;
503-
MCNameClone(t_key, &t_key_copy);
504-
loopvar -> set(ctxt, *t_key_copy);
505-
if (!MCArrayIterate(*t_array, t_iterator, t_key, t_value))
521+
if (!MCArrayFetchValueAtIndex(*t_array, ++t_sequenced_iterator, t_value))
506522
endnext = true;
507523
}
508-
break;
509-
case FU_ELEMENT:
510-
{
511-
loopvar -> set(ctxt, t_value);
512-
// SN-2015-06-15: [[ Bug 15457 ]] Sequenced, numeric arrays
513-
// have their own iterator
514-
if (t_sequence_array)
515-
{
516-
if (!MCArrayFetchValueAtIndex(*t_array, ++t_sequenced_iterator, t_value))
517-
endnext = true;
518-
}
519-
else
520-
{
521-
if (!MCArrayIterate(*t_array, t_iterator, t_key, t_value))
522-
endnext = true;
523-
}
524-
}
525-
break;
526-
527-
case FU_BYTE:
524+
else
528525
{
529-
// SN-2014-04-14 [[ Bug 12184 ]] If we have no data at all, we don't want to start the loop
530-
if (t_length)
531-
{
532-
MCDataCreateWithBytes(t_data_ptr++, 1, &t_byte);
533-
534-
endnext = (--t_length) == 0;
535-
}
536-
else
537-
done = true;
526+
if (!MCArrayIterate(*t_array, t_iterator, t_key, t_value))
527+
endnext = true;
538528
}
539-
break;
540-
541-
default:
529+
}
530+
break;
531+
532+
case FU_BYTE:
533+
{
534+
// SN-2014-04-14 [[ Bug 12184 ]] If we have no data at all, we don't want to start the loop
535+
if (t_length)
542536
{
543-
t_found = MCStringsTextChunkIteratorNext(ctxt, tci);
544-
endnext = tci -> IsExhausted();
537+
MCDataCreateWithBytes(t_data_ptr++, 1, &t_byte);
545538

546-
if (!t_found)
547-
{
548-
t_unit = kMCEmptyString;
549-
done = true;
550-
}
551-
else
552-
tci -> CopyString(&t_unit);
539+
endnext = (--t_length) == 0;
553540
}
541+
else
542+
done = true;
554543
}
555-
// MW-2010-12-15: [[ Bug 9218 ]] Added KEY to the type of repeat that already
556-
// copies the value.
557-
// MW-2011-02-08: [[ Bug ]] Make sure we don't use 't_unit' if the repeat type is 'key' or
558-
// 'element'.
559-
// Set the loop variable to whatever the value was in the last iteration.
560-
if (each == FU_BYTE)
544+
break;
545+
546+
default:
561547
{
562-
// SN-2014-04-14 [[ Bug 12184 ]] We don't need to set anything since we are not going in the loop
563-
if (!done)
564-
loopvar -> set(ctxt, *t_byte);
548+
t_found = MCStringsTextChunkIteratorNext(ctxt, tci);
549+
endnext = tci -> IsExhausted();
550+
551+
if (!t_found)
552+
{
553+
t_unit = kMCEmptyString;
554+
done = true;
555+
}
556+
else
557+
tci -> CopyString(&t_unit);
565558
}
566-
else if (each != FU_ELEMENT && each != FU_KEY)
567-
loopvar -> set(ctxt, *t_unit);
559+
break;
568560
}
569-
else
570-
done = count-- == 0;
561+
// MW-2010-12-15: [[ Bug 9218 ]] Added KEY to the type of repeat that already
562+
// copies the value.
563+
// MW-2011-02-08: [[ Bug ]] Make sure we don't use 't_unit' if the repeat type is 'key' or
564+
// 'element'.
565+
// Set the loop variable to whatever the value was in the last iteration.
566+
if (each == FU_BYTE)
567+
{
568+
// SN-2014-04-14 [[ Bug 12184 ]] We don't need to set anything since we are not going in the loop
569+
if (!done)
570+
loopvar -> set(ctxt, *t_byte);
571+
}
572+
else if (each != FU_ELEMENT && each != FU_KEY)
573+
loopvar -> set(ctxt, *t_unit);
571574

572575
if (!done)
573576
MCKeywordsExecuteRepeatStatements(ctxt, statements, line, pos, done);
@@ -593,7 +596,6 @@ void MCKeywordsExecRepeatFor(MCExecContext& ctxt, MCStatement *statements, MCExp
593596
void MCKeywordsExecRepeatWith(MCExecContext& ctxt, MCStatement *statements, MCExpression *step, MCExpression *startcond, MCExpression *endcond, MCVarref *loopvar, real8 stepval, uint2 line, uint2 pos)
594597
{
595598
real8 endn = 0.0;
596-
MCExecValue t_condition;
597599

598600
if (step != NULL)
599601
{

engine/src/exec.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1817,6 +1817,7 @@ class MCExecContext
18171817

18181818
void MCKeywordsExecSwitch(MCExecContext& ctxt, MCExpression *condition, MCExpression **cases, uindex_t case_count, int2 default_case, uint2 *case_offsets, MCStatement *statements, uint2 line, uint2 pos);
18191819
void MCKeywordsExecIf(MCExecContext& ctxt, MCExpression *condition, MCStatement *thenstatements, MCStatement *elsestatements, uint2 line, uint2 pos);
1820+
void MCKeywordsExecRepeatCount(MCExecContext& ctxt, MCStatement *statements, MCExpression *endcond, uint2 line, uint2 pos);
18201821
void MCKeywordsExecRepeatFor(MCExecContext& ctxt, MCStatement *statements, MCExpression *endcond, MCVarref *loopvar, File_unit each, uint2 line, uint2 pos);
18211822
void MCKeywordsExecRepeatWith(MCExecContext& ctxt, MCStatement *statements, MCExpression *step, MCExpression *startcond, MCExpression *endcond, MCVarref *loopvar, real8 stepval, uint2 line, uint2 pos);
18221823
void MCKeywordsExecRepeatForever(MCExecContext& ctxt, MCStatement *statements, uint2 line, uint2 pos);

engine/src/keywords.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1296,7 +1296,10 @@ void MCRepeat::exec_ctxt(MCExecContext& ctxt)
12961296
switch (form)
12971297
{
12981298
case RF_FOR:
1299-
MCKeywordsExecRepeatFor(ctxt, statements, endcond, loopvar, each, line, pos);
1299+
if (loopvar != nil)
1300+
MCKeywordsExecRepeatFor(ctxt, statements, endcond, loopvar, each, line, pos);
1301+
else
1302+
MCKeywordsExecRepeatCount(ctxt, statements, endcond, line, pos);
13001303
break;
13011304
case RF_WITH:
13021305
MCKeywordsExecRepeatWith(ctxt, statements, step, startcond, endcond, loopvar, stepval, line, pos);

0 commit comments

Comments
 (0)