1 (2015-07-27 19:13:30 отредактировано CyberTrader)

Тема: Скорость работы Lua vs С++

Есть мнение, что программа на Си работает быстрее, нежели на Lua.
Мне захотелось проверить насколько код С++ будет производительнее Lua при работе с массивом данных.
Написал тестовый скрипт, считывающий из файла архив графиков (формат взят с Finam) в массив и рассчитывающий по нему SMA ([url=http://rghost.ru/download/private/8KjpCwX8f/53d7ded4efd7acc8c40a239886597a7d/ea306813bfa2c8645b89b89755964005f83a1850/tester.rar]скачать архив с исходниками[/url]):
Calc_SMA.lua

local sScriptPath = getScriptPath()
package.path = sScriptPath..'\\?.lua;'..package.path
require('tester')

function main()
  -- Засекаем время:
  local nClock = os.clock()
  -- Считываем данные из файла и заносим их в массив в памяти в виде свечей (файл данных приложен к архиву):
  local sError = tester.CreateCandles(sScriptPath..'\\data.txt')
  -- Если функция завершилась с ошибкой, выводим сообщение об ошибке и выходим:
  if sError then message(sError, 3) error(sError, 0) end
  -- Выводим время работы функции:
  message(string.format('Данные прочитаны за %0.3f сек', os.clock()-nClock), 2)
  nClock = os.clock()
  tester.CreateCandles2(sScriptPath..'\\data.txt')
  message(string.format('Данные скопированы за %0.3f сек', os.clock()-nClock), 2)
  -- Создаём таблицу, куда будем выводить ход выполнения расчёта индикатора:
  hID = AllocTable()
  AddColumn(hID, 1, 'Дата', true, QTABLE_DATE_TYPE, 12)
  AddColumn(hID, 2, 'Продолжительность', true, QTABLE_TIME_TYPE, 12)
  CreateWindow(hID)
  SetWindowPos(hID, 0, 0, 161, 60)
  InsertRow(hID, -1)
  -- Засекаем время:
  nClock = os.clock()
  -- Расчитываем индикатор и заносим значения в массив
  -- Расчёт будет вестись юпо параметру Weighted Close ('W')
  -- Дополнительно передаём стартовое время и ID таблицы для вывода хода расчёта
  tester.CalcSMA(50, 'W', nClock, hID)
  -- Выводим время работы функции:
  message(string.format('Индикатор расчитан за %0.3f сек', os.clock()-nClock), 2)
end

tester.lua

-- Заголовок файла с данными свечей:
local sHeader = '<TICKER>,<PER>,<DATE>,<TIME>,<OPEN>,<HIGH>,<LOW>,<CLOSE>,<VOL>'
local io_open, os_time, os_clock, pcall = io.open, os.time, os.clock, pcall
local string_format, string_match = string.format, string.match
local SetCell = SetCell

module('tester')

-- Обявляем массивы для хранения значений индикатора и данных свечей:
local tSMA, tCandles = {}

function CreateCandles(sFileName)
-- Функция чтения файла данных
  local hFile, sError = io_open(sFileName, 'r')
  if not hFile then return false, sError end
  local sLine = hFile:read()
  if sLine ~= sHeader then
    -- Если заголовок файла неверный - выходим с ошибкой:
    sError = 'Неверный заголовок в файле "'..sFileName..'": "'..(sLine or 'nil')..'"'
  else
    local tData, tPrice, nNum = {}, {}
    for sLine in hFile:lines() do
        -- Парсим считанную строку:
        local Year, Month, Day, Hour, Min, Sec, Open, High, Low, Close, Volume = string_match(sLine, '^.+,[%d]+,(%d%d%d%d)(%d%d)(%d%d),(%d?%d)(%d%d)(%d%d),(%d+%.?%d-),(%d+%.?%d-),(%d+%.?%d-),(%d+%.?%d-),(%d+)')
        nNum, Close = (nNum or 0) + 1, Close+0
        -- Заносим в массив в виде свечей:
        tData[nNum] = {open=Open+0, close=Close, high=High+0, low=Low+0, volume=Volume+0, datetime={year=Year, month=Month, day=Day, hour=Hour, min=Min or '00', sec=Sec or '00'}}
        -- Заносим цену закрытия в отдельный массив (использовалось в предыдущей версии, оставил "до кучи"):
        tPrice[nNum] = Close
    end
    tCandles, tData, tPrice = {tData, tPrice}, nil
  end
  hFile:close() hFile = nil
  return sError
end
local function _GetValue(data, sParam)
-- Функция получения значения заданного параметра из массива данных
-- (Close, Open, High, Low, Volume, Median Price, Typical Price, Weighted Close)
  if not sParam or sParam == 'C' then return data.close
  elseif sParam == 'O' then return data.open
  elseif sParam == 'H' then return data.high
  elseif sParam == 'L' then return data.low
  elseif sParam == 'V' then return data.volume
  elseif sParam == 'M' then return (data.high + data.low) / 2
  elseif sParam == 'T' then return (data.high + data.low + data.close) / 3
  elseif sParam == 'W' then return (data.high + data.low + 2*data.close) / 4
  else return data.close end
end
local function SMA(nIndex, nPeriod, tData, sParam)
-- Расчёт среднего значения по заданному периоду
  if nIndex < nPeriod then return nil end
  local nSum = 0
  for i = nIndex-nPeriod+1, nIndex do
    nSum = nSum + _GetValue(tData[i], sParam)
  end
  return nSum / nPeriod
end
function CalcSMA(nPeriod, sParam, nClock, hID)
-- Расчёт простой скользящей средней
  local tData, Month = tCandles[1]
  for nIndex = 1, #tData do
    -- Заносим полученное значение в массив:
    tSMA[nIndex] = SMA(nIndex, nPeriod, tData, sParam)
    local data = tData[nIndex]
    local tTime = data.datetime
    local month = tTime.month
    -- Выводим ход выполнения расчёта в визуальную таблицу
    -- При необходимости функции вывода в таблицу SetCell можно закомментировать
    -- Но они не оказывают сколько-нибудь заметного влияния на общую скорость выполнения расчёта
    if month ~= Month then  -- В таблицу выводится не каждый результат, а только при смене месяца свечных данных
      Day, Month, Year = tTime.day, month, tTime.year
      -- Выводим текущий месяц, по которому производится расчёт:
      SetCell(hID, 1, 1, Day..'.'..Month..'.'..Year, (Year..Month..Day)+0)
      local nClock = os_clock()-nClock
      -- Выводим продолжительность работы функции:
      SetCell(hID, 1, 2, string_format('%0.3f', nClock), nClock)
    end
  end
end

Но мне сложно это сделать ввиду незнания языка. Может кто-нибудь помочь переписать библиотеку tester на C++?

2 (2015-07-27 11:27:21 отредактировано sam063rus)

Re: Скорость работы Lua vs С++

--

3 (2015-07-27 11:27:04 отредактировано sam063rus)

Re: Скорость работы Lua vs С++

--

4 (2015-07-27 11:45:06 отредактировано CyberTrader)

Re: Скорость работы Lua vs С++

Справедливая критика приветствуется. Но по теме.

sam063rus, уберите весь текст, не относящийся к теме, под спойлер

[+]Spoiler

Вы так много написали и ничего по существу...

sam063rus пишет:

"Открою" Вам "секрет" - во многих современных проприетарных 3d-движках с поддержкой LUA - вся персонализация игры строится на скриптах. Т.е. "тупо" идёт обращение из лаунчера к функциям движка, отвечающим за скриптинг, далее движок "ныряет" в игровой архив и начинает последовательно исполнять скрипты (несжатые и не байткод-компилированные). Последовательность персонализации игры (то есть того, что делает игру конкретной игрой) задаётся в лаунчере и в скриптах.

"Открою" вам "секрет": то, как будут выполняться те или иные действия (анимация), скомпилировано в байт-коде. А последовательность действий (команды) можно хоть в текстовом файлике указать - это не окажет значительного влияния на производительность. Чувствуете разницу?

sam063rus пишет:

пора забыть про "os.clock" для измерения скорости/профайлинга и уж тем более делать из этого какие-то выводы. Вам для этого нужны "прямые" функции winapi и, желательно с "вставками" timeBeginPeriod(1).

Так возьмите модифицируйте код.
Чтобы сравнить, два временных промежутка, значительно отличающихся по величине, суперточность не нужна. (Кстати, таймер, можно уменьшить и без timeBeginPeriod(1).) Главное понимание, что один метод быстрее другого. Или вы можете привести конкретный пример, когда, скажем при разнице в 30% os.clock() не даёт такого понимания?

sam063rus пишет:

Кроме того, файловые операции могут выдавать каждый раз разные тайминги в профайлере

Меня не столько интересует скорость чтения файла (поскольку данные из файла считываюся однократно), сколько дальнейшая работа с массивом данных. Но для общего понимания добавил таймер.

sam063rus пишет:

С другой стороны, для определённых примеров будет действительно быстрее и переход будет оправдан. И вот такие примеры действительно стоит обсуждать.

Вы можете предложить свой пример. Я с удовольствием приму участие в обсуждении. А то так прийти и [s]нас[/s] охаять каждый может. По теме есть что-то?

5 (2016-09-29 11:41:52 отредактировано sam063rus)

Re: Скорость работы Lua vs С++

-

6

Re: Скорость работы Lua vs С++

Я понимаю "проблему" так.
Если у вас есть большой вычислительный кусок, работа с файлами, работа с сетью - это это можно написать на С++ и это будет быстрее.
Если вы на С++ напишете кусок кода, в котором по сути через Lua-интерфейсы идёт работа с терминалом, с интерфейсом терминала - то врятли можно ожидать сколь-нибудь заметного приращения скорости. По сути вы уберёте лишь интерпретацию Lua-кода, что, вообще говоря, в данном случае весьма незначительный объём работы занимает.

А именно это производится в приведённом вами примере, на сколько я сумел понять: вы заполняете визуальную таблицу. Плюс делаете это из кода отдельного потока main(), что еще несколько замедляет всю эту бедуиз-за синхронизаций.

Так что врятли что-то тут ускорится из-за переписывания на С++, при этом написать и отладить всё
это в С++ кода - достаточно затрато и занудно...

7 (2015-07-27 18:31:59 отредактировано CyberTrader)

Re: Скорость работы Lua vs С++

Изменена функция расчёта SMA: добавлены параметры, по которым рассчитывается значение SMA: Open, High, Low, Close, Median Price, Typical Price, Weighted Close, Volume. Скачать исходники можно по ссылке в первом сообщении.


sam063rus пишет:

Кроме того, файловые операции могут выдавать каждый раз разные тайминги в профайлере

Файловые операции занимают ~9-12% от общего процессорного времени, затрачиваемого функцией CreateCandles (чтение данных из файла и заполнение массива). Основное же время тратится на парсинг строк и заполнение массива данных.
Но, если не нравится чтение с диска, можно считывать данные из файла на логическом диске в оперативной памяти. Так нормально будет?


swerg,
вы всё не правильно поняли. В визуальную таблицу QUIK выводится только ход расчёта индикатора (не более 10% от общей функции расчёта CalcSMA, измерено). Остальное - извлечение данных из массива и математические операции с ними. Но если визуализация смущает - можно убрать.
И да, я готов к конструктивному общению. Пример можно переписать вообще без использования QUIK.

8 (2016-09-29 11:41:41 отредактировано sam063rus)

Re: Скорость работы Lua vs С++

-

9 (2015-07-27 18:29:42 отредактировано CyberTrader)

Re: Скорость работы Lua vs С++

Как я понял, участники данной темы, не изучив представленный для теста код, сразу вынесли свой "приговор".
Добавил комментарии, чтобы было понятней.


sam063rus,

[+]Spoiler

я привёл аргументы в защиту своего "исследования" в ответ на ваши, местами не по теме. Это называется "потроллить"?
Вы не смогли ничего ответить. А только обиделись и потёрли все свои сообщения.
Если не нравится мой пример - приведите свой. Если что-то делаю неправильно - покажите, как правильно.
Или вы зашли сюда только сообщить, что моя затея бессмысленна? Так кто кого "троллит"?
Аргументы, типа "я много знаю, поэтому прав" не принимаются без доказательств.

sam063rus пишет:

признаться, только сейчас понял, что Вы своими вырываниями фраз из текста - просто меня удачно потроллили.

Не сочтите за труд: покажите, где я не прав? Не спора ради, а истины для.

10

Re: Скорость работы Lua vs С++

Добрый день.
Если хотите значительно повысить быстродействие, то необходимо максимально исключить передачу данных через стек Lua в хост-программу. В Вашем случае, наверное, оптимально было бы написать как минимум две функции:
1. функция загрузки данных из файла в память хост-программы
2. расчет индикатора на уже сформированных данных.
И по алгоритму расчета средней. Если у Вас всегда средняя считается для всех индексов массива последовательно, то можно изменить алгоритм значительно его ускорив даже в чистом Lua. Для этого можно использовать замыкание и запоминать уже посчитанное значение средней на предыдущем индексе. Это даст хороший прирост скорости на больших интервалах.

11

Re: Скорость работы Lua vs С++

mbul пишет:

Если хотите значительно повысить быстродействие, то необходимо максимально исключить передачу данных через стек Lua в хост-программу.

У меня данные считываются "внутри" модуля и "наружу" не передаются. В нём же расчитывается индикатор.

mbul пишет:

И по алгоритму расчета средней. Если у Вас всегда средняя считается для всех индексов массива последовательно, то можно изменить алгоритм значительно его ускорив даже в чистом Lua. Для этого можно использовать замыкание и запоминать уже посчитанное значение средней на предыдущем индексе. Это даст хороший прирост скорости на больших интервалах.

Согласен: правильно написанный алгоритм позволяет ускорить вычисления в разы. Но речь сейчас не про оптимизацию алгоритма расчёта, а про скорость работы с массивом данных в принципе. В качестве примера я взял "классический" SMA.

Начал писать модуль на C++:

static int lua_CreateCandles(lua_State *L) {
  const char * sFileName = lua_tostring(L, 1);
  ifstream hFile (sFileName);  //Открываем файл для считывания информации
  if (hFile.is_open()) {
    char sLine[100];  //В sLine будут считываться строки
    while (!hFile.eof()) {  //Будем читать информацию пока не дойдем до конца файла
      hFile >> sLine;  //Построчное считывание информации в sLine
    }
    hFile.close();  // Закрываем файл
    lua_pushstring(L, NULL);
  }
  else {
    lua_pushstring(L, "Не удалось открыть файл");  // Как сюда добавить имя файла?
  }
  return(1);
}

Результат меня не радует: файл с виртуального диска в оперативной памяти считывается за 3.7 сек, при том что в Lua этот же файл считывается за 0.3 сек (вместе с парсингом и заполнением массива операция занимает 3.0 сек).
Это нормально, что в C++ так медленно? Или я что-то не так делаю?

12

Re: Скорость работы Lua vs С++

Это, скорее, ifstream так медленно работает.

#include "stdafx.h"
using namespace std;
void read_fopen ( const char* f_name )
{
    FILE* fp = NULL;
    fopen_s ( &fp, f_name, "rt" );
    if ( fp )
    {
        char line[1024];
        while ( !feof ( fp ) )
        {
            fgets ( line, 1023, fp );
        }
        fclose ( fp );
    }
    else
        return;
}
void read_ifstream ( const char* fname )
{
    ifstream hFile ( fname ); //Открываем файл для считывания информации
    if ( hFile.is_open() ) {
        char sLine[1024];  //В sLine будут считываться строки
        while ( !hFile.eof() ) { //Будем читать информацию пока не дойдем до конца файла
            hFile >> sLine;  //Построчное считывание информации в sLine
        }
        hFile.close();  // Закрываем файл
    }
    else
        return;
}
int _tmain ( int argc, _TCHAR* argv[] )
{
    const char f_name[MAX_PATH] = "D:\\data.txt";
    int MAX_N = 100;
    long start = GetTickCount();
    for ( int i = 0; i < MAX_N; i++ )
    {
        read_fopen ( f_name );
    }
    long end = GetTickCount();
    printf ( "read_fopen : %d ticks\n", end - start );

    start = GetTickCount();
    for ( int i = 0; i < MAX_N; i++ )
    {
        read_ifstream ( f_name );
    }
    end = GetTickCount();
    printf ( "read_ifstream : %d ticks\n", end - start );

}

На моей машине вот такой результат:
read_fopen : 7300 ticks
read_ifstream : 17519 ticks

13 (2015-07-31 12:00:39 отредактировано CyberTrader)

Re: Скорость работы Lua vs С++

mbul пишет:

Это, скорее, ifstream так медленно работает.

Но нам ведь мало прочитать файл. Надо ещё сохранить прочитанные значения в массив.
Вот функция на C++:

#include <fstream>
#include <vector>

using namespace std;

vector<vector<double> > aData;

static int lua_CreateCandles(lua_State *L) {
  const char *sFileName = lua_tostring(L, 1);
  ifstream hFile (sFileName);
  if (!hFile.is_open()) lua_pushstring(L, "Не удалось открыть файл");  // Как добавить сюда имя файла?
  else {
    char sLine[100];
    hFile >> sLine;
    if (strcmp(sLine, "<TICKER>,<PER>,<DATE>,<TIME>,<OPEN>,<HIGH>,<LOW>,<CLOSE>,<VOL>") != 0) {
      lua_pushstring(L, "Неверный заголовок в файле");  // Как добавить сюда имя файла и считанную строку?
    } else {
      char s;
      double d;
      vector<double> v;
      while (!hFile.eof()) {
        if ( !hFile.get(sLine, 99, ',') ) break;
        for ( size_t i=0; i<=2; i++ ) hFile >> s >> d;
        for ( size_t i=3; i<=7; i++ ) {
          hFile >> s >> d;
          v.push_back(d);
        }
        hFile.get();
        aData.push_back(v);
        v.clear();
      }
      lua_pushnil(L);
    }
    hFile.close();
  }
  return(1);
}

Результат таков:
Lua: 2.4 сек
C++: 18.4 сек

И это ещё без проверки правильности формата считываемых строк. В Lua эта проверка делается одним оператором.

Возможно, на C++ можно и быстрее, но пока так...

14 (2015-07-31 13:20:53 отредактировано mbul)

Re: Скорость работы Lua vs С++

на самом деле, тут речь уже не о быстродействии Lua vs. C++. В примерах соревнуются std-контейнеры и С. да и с контейнерами можно работать оптимальнее.
P.S.
Ради интереса запустил код под профилировщиком:
http://i.imgur.com/JJfzk1E.png

15

Re: Скорость работы Lua vs С++

mbul пишет:

на самом деле, тут речь уже не о быстродействии Lua vs. C++. В примерах соревнуются std-контейнеры и С. да и с контейнерами можно работать оптимальнее.

Как? Если знаете, подскажите.

16 (2015-08-03 00:21:17 отредактировано CyberTrader)

Re: Скорость работы Lua vs С++

Наконец, я подошёл к тому, для чего всё затевалось: к работе с массивом данных в C++.

Calc_SMA.lua

local sScriptPath = getScriptPath()
package.path = sScriptPath..'\\?.lua;'..package.path
require('mod_lua')
package.cpath = sScriptPath..'\\?.dll;'..package.cpath
require('mod_cpp')

function main()
  -- Засекаем время:
  local nClock = os.clock()
  -- Считываем данные из файла и заносим их в массив в памяти в виде свечей
  local sError = mod_lua.CreateCandles('R:\\data.csv')
  -- Если функция завершилась с ошибкой, выводим сообщение об ошибке и выходим:
  if sError then message(sError, 3) error(sError, 0) end
  -- Выводим время работы функции:
  message(string.format('Чтение данных (Lua): %0.1f сек', os.clock()-nClock), 2)

  nClock = os.clock()
  sError = mod_cpp.CreateCandles('R:\\data.csv')
  if sError then message(sError, 3) error(sError, 0) end
  message(string.format('Чтение данных (C++): %0.1f сек', os.clock()-nClock), 2)

  nClock = os.clock()
  -- Расчитываем индикатор и заносим значения в массив
  -- Расчёт будет вестись юпо параметру Weighted Close ('W')
  -- Дополнительно передаём стартовое время и ID таблицы для вывода хода расчёта
  mod_lua.CalcSMA(50, 'W')
  -- Выводим время работы функции:
  message(string.format('Расчёт индикатора (Lua): %0.1f сек', os.clock()-nClock), 2)

  nClock = os.clock()
  mod_cpp.CalcSMA(50, 'W')
  message(string.format('Расчёт индикатора (C++): %0.1f сек', os.clock()-nClock), 2)
end

mod_lua.lua

-- Заголовок файла с данными свечей:
local sHeader = '<TICKER>,<PER>,<DATE>,<TIME>,<OPEN>,<HIGH>,<LOW>,<CLOSE>,<VOL>'
local io_open, os_time, os_clock, pcall = io.open, os.time, os.clock, pcall
local string_format, string_match = string.format, string.match
local SetCell = SetCell

module('mod_lua')

-- Обявляем массивы для хранения значений индикатора и данных свечей:
local tSMA, tCandles = {}, {}

function CreateCandles(sFileName)
-- Функция чтения файла данных
  local hFile, sError = io_open(sFileName, 'r')
  if not hFile then return false, sError end
  local sLine = hFile:read()
  if sLine ~= sHeader then
    -- Если заголовок файла неверный - выходим с ошибкой:
    sError = 'Неверный заголовок в файле "'..sFileName..'": "'..(sLine or 'nil')..'"'
  else
    local tData, tPrice, nNum = {}, {}
    for sLine in hFile:lines() do
        -- Парсим считанную строку:
        local Year, Month, Day, Hour, Min, Sec, Open, High, Low, Close, Volume = string_match(sLine, '^[^,]+,%d+,(%d%d%d%d)(%d%d)(%d%d),(%d?%d)(%d%d)(%d%d),(%d+%.?%d-),(%d+%.?%d-),(%d+%.?%d-),(%d+%.?%d-),(%d+)')
        nNum, Close = (nNum or 0) + 1, Close+0
        -- Заносим в массив в виде свечей:
        tCandles[nNum] = {Open+0, High+0, Low+0, Close, Volume+0}
    end
  end
  hFile:close() hFile = nil
  return sError
end
local function _GetValue(data, sParam)
-- Функция получения значения заданного параметра из массива данных
-- (Close, Open, High, Low, Volume, Median Price, Typical Price, Weighted Close)
  if not sParam or sParam == 'C' then return data[4]
  elseif sParam == 'O' then return data[1]
  elseif sParam == 'H' then return data[2]
  elseif sParam == 'L' then return data[3]
  elseif sParam == 'V' then return data[5]
  elseif sParam == 'M' then return (data[2] + data[3]) / 2
  elseif sParam == 'T' then return (data[2] + data[3] + data[4]) / 3
  elseif sParam == 'W' then return (data[2] + data[3] + 2*data[4]) / 4
  else return data[4] end
end
local function SMA(nIndex, nPeriod, tData, sParam)
-- Расчёт среднего значения по заданному периоду
  if nIndex < nPeriod then return nil end
  local nSum = 0
  for i = nIndex-nPeriod+1, nIndex do
    nSum = nSum + _GetValue(tData[i], sParam)
  end
  return nSum / nPeriod
end
function CalcSMA(nPeriod, sParam, nClock, hID)
-- Расчёт простой скользящей средней
  for nIndex = 1, #tCandles do
    -- Заносим полученное значение в массив:
    tSMA[nIndex] = SMA(nIndex, nPeriod, tCandles, sParam)
  end
end

mod_cpp.cpp

#include <windows.h>
#include <fstream>
#include <vector>

// в случае вызова функций из LUA-кода во внешней DLL
// необходимо определить эти константы до подключения заголовочных файлов LUA
#define LUA_LIB
#define LUA_BUILD_AS_DLL

// заголовочные файлы LUA из дистрибутива LUA
extern "C" {
  #include "../contrib/lauxlib.h"
  #include "../contrib/lua.h"
}

// стандартная точка входа для DLL
BOOL APIENTRY DllMain(HANDLE hmod_cppule, DWORD  ul_reason_for_call, LPVOID lpReserved) {
  return TRUE;
}

using namespace std;

vector<vector<double> > aData;
vector<double> aSMA;

double _GetValue(vector<double> &data, const char *sParam) {
// Функция получения значения заданного параметра из массива данных
// (Close, Open, High, Low, Volume, Median Price, Typical Price, Weighted Close)
  switch (*sParam) {
    case NULL:
      return data[3];
    case 'C':
      return data[3];
    case 'O':
      return data[0];
    case 'H':
      return data[1];
    case 'L':
      return data[2];
    case 'V':
      return data[4];
    case 'M':
      return (data[1] + data[2]) / 2;
    case 'T':
      return (data[1] + data[2] + data[3]) / 3;
    case 'W':
      return (data[1] + data[2] + 2*data[3]) / 4;
    default:
      return data[3];
  }
}

double _SMA(const unsigned int &iIndex, const unsigned short int &iPeriod, vector<vector<double> > &aData, const char *sParam) {
// Расчёт среднего значения по заданному периоду
  if (iIndex < iPeriod-1) return NULL;
  double nSum = 0;
  for ( size_t i = iIndex-iPeriod+1; i<=iIndex; i++ ) {
    nSum = nSum + _GetValue(aData[i], sParam);
  }
  return nSum / iPeriod;
}

// реализация функций, вызываемых из LUA

static int lua_CalcSMA(lua_State *L) {
// Расчёт простой скользящей средней
  const unsigned short int iPeriod = luaL_checknumber(L, 1);
  const char *sParam = lua_tostring(L, 2);
  for ( size_t i=0; i<aData.size(); i++ ) {
    // Заносим полученное значение в массив:
    aSMA.push_back(_SMA(i, iPeriod, aData, sParam));
  }
  lua_pushnil(L);
  return(1);
}

static int lua_CreateCandles(lua_State *L) {
  const char *sFileName = lua_tostring(L, 1);
  ifstream hFile (sFileName);
  if (!hFile.is_open()) lua_pushstring(L, "Не удалось открыть файл");  // Как добавить сюда имя файла?
  else {
    char sLine[100];
    hFile >> sLine;
    if (strcmp(sLine, "<TICKER>,<PER>,<DATE>,<TIME>,<OPEN>,<HIGH>,<LOW>,<CLOSE>,<VOL>") != 0) {
      lua_pushstring(L, "Неверный заголовок в файле");  // Как добавить сюда имя файла и считанную строку?
    } else {
      char s;
      double n;
      vector<double> v;
      while (!hFile.eof()) {
        if ( !hFile.get(sLine, 99, ',') ) break;
        for ( size_t i=0; i<=2; i++ ) hFile >> s >> n;
        for ( size_t i=3; i<=7; i++ ) {
          hFile >> s >> n;
          v.push_back(n);
        }
        hFile.get();
         aData.push_back(v);
        v.clear();
      }
      lua_pushnil(L);
    }
    hFile.close();
  }
  return(1);
}

// регистрация реализованных в dll функций, чтобы они стали "видимы" для LUA

static struct luaL_reg lib_functions[] = {
    {"CreateCandles", lua_CreateCandles},
    {"CalcSMA", lua_CalcSMA},
    {NULL, NULL}
};

extern "C" LUALIB_API int luaopen_mod_cpp(lua_State *L) {
    luaL_openlib(L, "mod_cpp", lib_functions, 0);
    return 0;
}

Результаты:
Чтение данных (Lua): 2.0 сек
Чтение данных (C++): 2.8 сек
Расчёт индикатора (Lua): 6.9 сек
Расчёт индикатора (C++): 0.0 сек

Как и предполагалось, C++ работает с массивами немного пошустрее Lua. К слову, в Lua - скорость бработы с массивами является узким местом.
Осталось ещё ускорить чтение данные из файлов.

PS: Предыдущие результаты чтения файла были следствием компиляции библиотеки в конфигурации Debug. Текущий результат - в Release.