Browse Source

SC: use sprintf & local buf (10% speedup for gain.scd)

tags/v1.3.0
Brian Heim 5 years ago
parent
commit
6c5449e7d2
1 changed files with 49 additions and 51 deletions
  1. +49
    -51
      src/SuperColliderEngine.cpp

+ 49
- 51
src/SuperColliderEngine.cpp View File

@@ -57,7 +57,7 @@ public:
void flush() override {}

private:
std::string buildScProcessBlockString(const ProcessBlock* block) const noexcept;
const char* buildScProcessBlockString(const ProcessBlock* block) const noexcept;
int getResultAsInt(const char* text) noexcept;
bool isVcvPrototypeProcessBlock(const PyrSlot* slot) const noexcept;

@@ -161,82 +161,80 @@ void SC_VcvPrototypeClient::postText(const char* str, size_t len) {
_engine->display(std::string(str, len));
}

// This should be well above what we ever need to represent a process block.
constexpr unsigned overhead = 512;
constexpr unsigned floatSize = 10;
constexpr unsigned insOutsSize = MAX_BUFFER_SIZE * NUM_ROWS * 2 * floatSize;
constexpr unsigned otherArraysSize = floatSize * NUM_ROWS * 8;
constexpr unsigned bufferSize = insOutsSize + otherArraysSize + overhead;
static char scratchBuf[bufferSize];

std::string SC_VcvPrototypeClient::buildScProcessBlockString(const ProcessBlock* block) const noexcept {
constexpr unsigned bufferSize = overhead + insOutsSize + otherArraysSize;

// Don't write initial string every time
#define PROCESS_BEGIN_STRING "^~vcv_process.(VcvPrototypeProcessBlock.new("
static char processBlockStringScratchBuf[bufferSize] = PROCESS_BEGIN_STRING;
constexpr unsigned processBeginStringOffset = sizeof(PROCESS_BEGIN_STRING);
#undef PROCESS_BEGIN_STRING

template <typename... Ts>
static void doAppend(char*& buf, int& size, const char* fmt, Ts... vals) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-security"
auto result = std::snprintf(buf, size, fmt, vals...);
#pragma GCC diagnostic pop
buf += result;
size -= result;
}

std::ostringstream builder;
const char* SC_VcvPrototypeClient::buildScProcessBlockString(const ProcessBlock* block) const noexcept {
auto* buf = processBlockStringScratchBuf + processBeginStringOffset - 1;
int size = sizeof(processBlockStringScratchBuf) - processBeginStringOffset + 1;

// TODO so expensive
builder << std::fixed; // to ensure floats aren't actually treated as Integers
builder << "^~vcv_process.(VcvPrototypeProcessBlock.new("
<< block->sampleRate << ','
<< block->sampleTime << ','
<< block->bufferSize << ',';
// Perhaps imprudently assuming snprintf never returns a negative code
doAppend(buf, size, "%.6f,%.6f,%d,", block->sampleRate, block->sampleTime, block->bufferSize);

// after this, all floats are printed in float arrays, so we don't need to worry about misinterpreting
builder << std::defaultfloat;
auto&& appendInOutArray = [&builder](const int bufferSize, const float (&data)[NUM_ROWS][MAX_BUFFER_SIZE]) {
builder << '[';
auto&& appendInOutArray = [&buf, &size](const int bufferSize, const float (&data)[NUM_ROWS][MAX_BUFFER_SIZE]) {
doAppend(buf, size, "[");
for (int i = 0; i < NUM_ROWS; ++i) {
builder << "FloatArray[";
doAppend(buf, size, "FloatArray[");
for (int j = 0; j < bufferSize; ++j) {
builder << data[i][j];
if (j != bufferSize - 1)
builder << ',';
doAppend(buf, size, "%g%c", data[i][j], j == bufferSize - 1 ? ' ' : ',');
}
builder << ']';
if (i != NUM_ROWS - 1)
builder << ',';
doAppend(buf, size, "]%c", i == NUM_ROWS - 1 ? ' ' : ',');
}
builder << ']';
doAppend(buf, size, "],");
};

appendInOutArray(block->bufferSize, block->inputs);
builder << ',';
appendInOutArray(block->bufferSize, block->outputs);
builder << ',';

// knobs
builder << "FloatArray[";
for (int i = 0; i < NUM_ROWS; ++i) {
builder << block->knobs[i];
if (i != NUM_ROWS - 1)
builder << ',';
}
builder << "],";
doAppend(buf, size, "FloatArray[");
for (int i = 0; i < NUM_ROWS; ++i)
doAppend(buf, size, "%g%c", block->knobs[i], i == NUM_ROWS - 1 ? ' ' : ',');

// switches
builder << '[' << std::boolalpha;
for (int i = 0; i < NUM_ROWS; ++i) {
builder << block->switches[i];
if (i != NUM_ROWS - 1)
builder << ',';
}
builder << "],";
doAppend(buf, size, "],[");
for (int i = 0; i < NUM_ROWS; ++i)
doAppend(buf, size, "%s%c", block->switches[i] ? "true" : "false", i == NUM_ROWS - 1 ? ' ' : ',');
doAppend(buf, size, "]");

// lights, switchlights
auto&& appendLightsArray = [&builder](const float (&array)[NUM_ROWS][3]) {
builder << '[';
auto&& appendLightsArray = [&buf, &size](const float (&array)[NUM_ROWS][3]) {
doAppend(buf, size, ",[");
for (int i = 0; i < NUM_ROWS; ++i) {
builder << "FloatArray[" << array[i][0] << ',' << array[i][1] << ',' << array[i][2] << ']';
if (i != NUM_ROWS - 1)
builder << ',';
doAppend(buf, size, "FloatArray[%g,%g,%g]%c", array[i][0], array[i][1], array[i][2],
i == NUM_ROWS - 1 ? ' ' : ',');
}
builder << ']';
doAppend(buf, size, "]");
};

appendLightsArray(block->lights);
builder << ',';
appendLightsArray(block->switchLights);

builder << "));\n";
return builder.str();
doAppend(buf, size, "));");

// printf("%s\nWrote %lu, %u remaining", processBlockStringScratchBuf, buf - processBlockStringScratchBuf, size);

return processBlockStringScratchBuf;
}

bool SC_VcvPrototypeClient::isVcvPrototypeProcessBlock(const PyrSlot* slot) const noexcept {
@@ -333,8 +331,8 @@ static unsigned int timesIndex = 0;
void SC_VcvPrototypeClient::evaluateProcessBlock(ProcessBlock* block) noexcept {
// TODO timing test code
auto start = std::chrono::high_resolution_clock::now();
auto&& string = buildScProcessBlockString(block);
interpret(string.c_str());
auto* buf = buildScProcessBlockString(block);
interpret(buf);
readScProcessBlockResult(block);
auto end = std::chrono::high_resolution_clock::now();
auto ticks = (end - start).count();


Loading…
Cancel
Save