所以我使用yaml-cpp
能够将yaml用于c ++中的游戏数据文件,但是我遇到了一些主要的性能问题。
我想测试一个有点大的文件,所以我创建了一些虚拟数据来写出来:
Player newPlayer = Player();
newPlayer.name = "new player";
newPlayer.maximumHealth = 1000;
newPlayer.currentHealth = 1;
Inventory newInventory;
newInventory.maximumWeight = 10.9f;
for (int z = 0; z < 10000; z++) {
InventoryItem* newItem = new InventoryItem();
newItem->name = "Stone";
newItem->baseValue = 1;
newItem->weight = 0.1f;
newInventory.items.push_back(newItem);
}
YAML::Node newSavedGame;
newSavedGame["player"] = newPlayer;
newSavedGame["inventory"] = newInventory;
然后我编写了这个函数,以便能够获取数据并将其写入文件:
void YamlUtility::saveAsFile(YAML::Node node, std::string filePath) {
std::ofstream myfile;
myfile.open(filePath);
myfile << node << std::endl;
myfile.close();
}
在我添加此代码之前,我的游戏的内存使用量约为22MB。在我添加newPlayer
,newInventory
和InventoryItems
之后,它增加了大约23MB。然后,当我添加YAML::Node newSavedGame
时,内存增加到108MB。写出来的文件只有570KB,所以我无法想象为什么它会将内存增加到85MB。
第二个问题是此代码写入文件大约需要8秒钟。这对我来说似乎有些偏差。
我决定使用YAML::Emitter
重写保存功能,该代码如下所示:
static void buildYamlManually(std::ofstream& file, YAML::Node node) {
YAML::Emitter out;
out << YAML::BeginMap << YAML::Key << "player" << YAML::Value << YAML::BeginMap << YAML::Key << "name" << YAML::Value
<< node["player"]["name"].as<std::string>() << YAML::Key << "maximumHealth" << YAML::Value
<< node["player"]["maximumHealth"].as<int>() << YAML::Key << "currentHealth" << YAML::Value
<< node["player"]["currentHealth"].as<int>() << YAML::EndMap;
out << YAML::BeginSeq;
std::vector<InventoryItem*> items = node["inventory"]["items"].as<std::vector<InventoryItem*>>();
for (InventoryItem* const value : items) {
out << YAML::BeginMap << YAML::Key << "name" << YAML::Value << value->name << YAML::Key << "baseValue"
<< YAML::Value << value->baseValue << YAML::Key << "weight" << YAML::Value << value->weight << YAML::EndMap;
}
out << YAML::EndSeq;
out << YAML::EndMap;
file << out.c_str() << std::endl;
}
这似乎对性能影响很小,但保存文件的时间仍然接近7秒(而不是8秒)。
然后我决定只看一下如果我在没有yaml-cpp
的情况下手动编写文件会是什么样子,代码如下:
static void buildYamlManually(std::ofstream& file, SavedGame savedGame) {
file << "player: \n"
<< " name: " << savedGame.player.name << "\n maximumHealth: " << savedGame.player.maximumHealth
<< "\n currentHealth: " << savedGame.player.currentHealth << "\ninventory:"
<< "\n maximumWeight: " << savedGame.inventory.maximumWeight << "\n items:";
for (InventoryItem* const value : savedGame.inventory.items) {
file << "\n - name: " << value->name << "\n baseValue: " << value->baseValue
<< "\n weight: " << value->weight;
}
}
删除此代码并删除所有yaml-cpp
代码后,内存从23MB变为24MB,写入文件大约需要0.15秒。
虽然我理解使用yaml-cpp
vs手动处理文件会产生一些开销,但这种性能差异似乎是错误的。
我想说我做错了但是基于yaml-cpp
文档,我无法看到它可能是什么。
答案 0 :(得分:2)
您需要提供一个实际演示问题的完整示例。我一直想尝试yaml-cpp,所以今天早上我试图重现你的问题,但却无法这样做。使用下面的代码与您提供的代码段非常相似,在我的VM中编写文件需要大约0.06秒。看起来问题不是yaml-cpp中固有的问题,而是代码中的某个地方。
#include <string>
#include <vector>
#include <iostream>
#include <yaml-cpp/yaml.h>
#include <fstream>
#include <chrono>
class Player
{
public:
Player(const std::string& name, int maxHealth, int curHealth) :
m_name(name),
m_maxHealth(maxHealth),
m_currentHealth(curHealth)
{
}
const std::string& name() const { return m_name;}
int maxHealth() const { return m_maxHealth; }
int currentHealth() const { return m_currentHealth; }
private:
const std::string m_name;
int m_maxHealth;
int m_currentHealth;
};
class Item
{
public:
Item(const std::string& name, int value, double weight) :
m_name(name),
m_value(value),
m_weight(weight)
{
}
const std::string& name() const { return m_name; }
int value() const { return m_value; }
double maxWeight() const { return m_weight; }
private:
const std::string m_name;
int m_value;
double m_weight;
};
class Inventory
{
public:
Inventory(double maxWeight) :
m_maxWeight(maxWeight)
{
m_items.reserve(10'000);
}
std::vector<Item>& items() { return m_items;}
const std::vector<Item>& items() const { return m_items;}
double maxWeight() const { return m_maxWeight; };
private:
double m_maxWeight;
std::vector<Item> m_items;
};
namespace YAML
{
template<>
struct convert<Inventory>
{
static Node encode(const Inventory& rhs)
{
Node node;
node.push_back(rhs.maxWeight());
for(const auto& item : rhs.items())
{
node.push_back(item.name());
node.push_back(item.value());
node.push_back(item.maxWeight());
}
return node;
}
// TODO decode Inventory
};
template<>
struct convert<Player>
{
static Node encode(const Player& rhs)
{
Node node;
node.push_back(rhs.name());
node.push_back(rhs.maxHealth());
node.push_back(rhs.currentHealth());
return node;
}
//TODO Decode Player
};
}
void saveAsFile(const YAML::Node& node, const std::string& filePath)
{
std::ofstream myFile(filePath);
myFile << node << std::endl;
}
int main(int arg, char **argv)
{
Player newPlayer("new player", 1'000, 1);
Inventory newInventory(10.9f);
for(int z = 0; z < 10'000; z++)
{
newInventory.items().emplace_back("Stone", 1, 0.1f);
}
std::cout << "Inventory has " << newInventory.items().size() << " items\n";
YAML::Node newSavedGame;
newSavedGame["player"] = newPlayer;
newSavedGame["inventory"] = newInventory;
//Measure it
auto start = std::chrono::high_resolution_clock::now();
saveAsFile(newSavedGame, "/tmp/save.yaml");
auto end = std::chrono::high_resolution_clock::now();
std::cout << "Wrote to file in "
<< std::chrono::duration_cast<std::chrono::duration<double>>(end - start).count()
<< " seconds\n";
return 0;
}
输出:
user@mintvm ~/Desktop/yaml $ g++ -std=c++14 -o test main.cpp -lyaml-cpp
user@mintvm ~/Desktop/yaml $ ./test
Inventory has 10000 items
Wrote to file in 0.0628495 second
更新编辑(Michael Goldshteyn):
我想在本机而不是VM上运行它,以显示实际上上面的代码在使用适当的优化构建,正确定时并运行本机(即不在VM中)时更快:
$ # yaml-cpp built from source commit: * c90c08cThu Aug 9 10:05:07 2018 -0500
$ # (HEAD -> master, origin/master, origin/HEAD)
$ # Revert "Improvements to CMake buildsystem (#563)"
$ # - Lib was built Release with flags: -std=c++17 -O3 -march=native -mtune=native
$ # Benchmark hardware info
$ # -----------------------
$ # CPU: Intel(R) Xeon(R) CPU E5-1650 v4 @ 3.60GHz
$ # Kernel: 4.4.0-131-generic #157-Ubuntu SMP
$ # gcc: gcc (Debian 8.1.0-9) 8.1.0
$
$ # And away we go:
$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
performance
$ g++ -std=c++17 -O3 -march=native -mtune=native -o yamltest yamltest.cpp -lyaml-cpp
$ ./yamltest
Inventory has 10000 items
After 100 saveAsFile() iterations, the average execution time
per iteration was 0.0521697 seconds.