Какова наиболее эффективная стратегия анализа файла .STL?
Важной частью моего кода является импорт файла .STL (общий формат файла CAD), и это в целом ограничивает производительность.
Формат файла .STL приведен здесь https://en.wikipedia.org/wiki/STL_(file_format)
Использование формата ASCII требуется для этого приложения.
Общий формат:
solid name
facet normal ni nj nk
outer loop
vertex v1x v1y v1z
vertex v2x v2y v2z
vertex v3x v3y v3z
endloop
endfacet
endsolid
Однако я заметил, что нет строгих требований к форматированию. И функция импорта должна выполнять минимальную проверку ошибок. Я сделал некоторые измерения производительности (используя хронограф), который для файла с 43 000 строк дает:
stl_import () — 1,177568 с
цикл разбора — 3.894250 с
Цикл разбора:
cout << "Importing " << stl_path << "... ";
auto file_vec = import_stl(stl_path);
for (auto& l : file_vec) {
trim(l);
if (solid_state) {
if (facet_state) {
if (starts_with(l, "vertex")) {
//---------ADD FACE----------//
l.erase(0, 6);
trim(l);
vector<string> strs;
split(strs, l, is_any_of(" "));
point p = { stod(strs[0]), stod(strs[1]), stod(strs[2]) };
facet_points.push_back(p);
//---------------------------//
}
else {
if (starts_with(l, "endfacet")) {
facet_state = false;
}
}
}
else {
if (starts_with(l, "facet")) {
facet_state = true;
//assert(facet_points.size() == 0);
//---------------------------//
// Normals can be ignored //
//---------------------------//
}
if (starts_with(l, "endsolid")) {
solid_state = false;
}
}
}
else {
if (starts_with(l, "solid")) {
solid_state = true;
}
}
if (facet_points.size() == 3) {
triangle facet(facet_points[0], facet_points[1], facet_points[2]);
stl_solid.add_facet(facet);
facet_points.clear();
//check normal
facet.normal();
}
}
Функция stl_import:
std::vector<std::string> import_stl(const std::string& file_path)
{
std::ifstream infile(file_path);
SkipBOM(infile);
std::vector<std::string> file_vec;
std::string line;
while (std::getline(infile, line))
{
file_vec.push_back(line);
}
return file_vec;
}
Я искал способы оптимизации чтения файлов и т. Д. И я вижу, что использование mmap может улучшить скорость чтения файлов.
Быстрое чтение текстовых файлов в C ++
Этот вопрос является вопросом о том, какова лучшая стратегия синтаксического анализа для файла .STL?
Без данных, которые могут быть использованы для измерения того, на что тратится время, сложно определить, что на самом деле повышает производительность. Приличная библиотека, уже выполняющая работу, может быть самым простым подходом. Тем не менее, текущий код использует несколько подходов, которые могут быть легко выиграть для повышения производительности. Есть вещи, которые я заметил:
std::getline(infile >> std::ws, line)
: std::ws
манипулятор пропускает ведущие пробелы.starts_with()
со строковыми литералами я предпочел бы прочитать каждую строку в «команду» и хвост строки и сравнить команды с std::string const
объекты: вместо сравнения символов может быть достаточно сравнить размер.Вместо split()
в std::string
в std::vector<std::string>
на пустом месте я бы предпочел сбросить подходящий поток (вероятно, std::istringstream
но, чтобы предотвратить копирование, возможно, пользовательский поток памяти) и читать прямо из этого:
std::istringstream in; // declared outside the reading loop
// ...
point p;
in.clear(); // get rid of potentially existing errors
in.str(line);
if (in >> p.x >> p.y >> p.z) {
facet_points.push_back(p);
}
Этот подход имеет дополнительное преимущество, заключающееся в том, что он позволяет проверять формат: я всегда не доверяю полученным данным, даже если они получены из надежного источника.
std::string_view
(или, в случае, если этот класс C ++ 17 недоступен аналогичного класса), чтобы избежать перемещения символов вокруг.std::vector<std::string>
а затем анализировать его. Вместо этого я бы проанализировал файл на лету: таким образом, горячая память немедленно используется повторно, а не перемещается из кэша для последующей постобработки. Таким образом можно избежать вспомогательного потока (см. Пункт 3 выше). Чтобы предотвратить слишком сложный цикл чтения, я бы разбил вложенные разделы на соответствующие функции, возвращая их по закрывающим тегам. Кроме того, я бы определил функции ввода для таких структур, как point
просто читать их из потока.std::ios_base::sync_with_stdio(false)
перед чтением файла: раньше была хотя бы одна часто используемая реализация потоков, которая выиграла бы от этого вызова.Других решений пока нет …