在 C++ 中读取镶木地板文件比在 python 中慢
Reading parquet file is slower in c++ than in python
我已经编写了代码来使用 C++ 和 python 读取同一个镶木地板文件。 python 读取文件所花费的时间比 c++ 少得多,但众所周知,c++ 的执行速度比 python 快。我在这里附上了代码 -
#include <arrow/api.h>
#include <parquet/arrow/reader.h>
#include <arrow/filesystem/localfs.h>
#include <chrono>
#include <iostream>
int main(){
// ...
arrow::Status st;
arrow::MemoryPool* pool = arrow::default_memory_pool();
arrow::fs::LocalFileSystem file_system;
std::shared_ptr<arrow::io::RandomAccessFile> input = file_system.OpenInputFile("data.parquet").ValueOrDie();
// Open Parquet file reader
std::unique_ptr<parquet::arrow::FileReader> arrow_reader;
st = parquet::arrow::OpenFile(input, pool, &arrow_reader);
if (!st.ok()) {
// Handle error instantiating file reader...
}
// Read entire file as a single Arrow table
std::shared_ptr<arrow::Table> table;
auto t1 = std::chrono::high_resolution_clock::now();
st = arrow_reader->ReadTable(&table);
auto t2 = std::chrono::high_resolution_clock::now();
if (!st.ok()) {
// Handle error reading Parquet data...
}
else{
auto ms_int = std::chrono::duration_cast<std::chrono::milliseconds> (t2 - t1);
std::cout << "Time taken to read parquet file is : " << ms_int.count() << "ms\n";
}
}
我在python中使用的代码是-
#!/usr/bin/env python3
import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pq
import time
start_time = time.time()
table = pq.read_table('data.parquet')
end_time = time.time()
print("Time taken to read parquet is : ",(end_time - start_time)*1000, "ms")
在 运行 一个大小约为 87mb 的文件的 c++ 代码上,c++ 的输出是 -
Time taken to read parquet file is : 186ms
而 python 的输出是 -
Time taken to read parquet is : 108.66141319274902 ms
为什么 c++ 中的函数 read_table 和 python 的执行时间有这么大的差异?
很可能Python模块绑定了用c++或cython等语言编译的函数。因此,python 模块的实现可能具有更好的性能,具体取决于它如何从文件读取或处理数据。
1 秒是 1000 毫秒。所以差别没有那么大。除此之外,python 的许多功能经常使用 CPython,这使它们处于一个非常公平的竞争环境中。那么就看函数写的好不好,优化的好不好。在这种情况下,python 函数可能比 C++ 函数更优化。
python pq.read_table
基于与您在示例中使用的完全相同的 C++ API(在幕后它也使用 C++ parquet::arrow::FileReader
),因为两者Python 和 C++ API 来自同一个 Arrow 项目。
因此,除了一小部分 Python 调用堆栈开销外,预计两种方式的执行效果相同。
但是,您可以指定/调整几个选项来提高性能,这可以解释您的情况的不同之处。例如,python 函数默认会并行读取文件(您可以指定 use_threads=False
来禁用此功能)。另一方面,C++ FileReader 默认情况下不这样做(检查 set_use_threads
)。 python reader 可能还默认设置了其他选项。
此外,编译 C++ 示例时的确切构建标志也会产生影响。
如果您想进行比较,请尝试此 CPP 代码:
#include <cassert>
#include <chrono>
#include <cstdlib>
#include <iostream>
using namespace std::chrono;
#include <arrow/api.h>
#include <arrow/filesystem/api.h>
#include <parquet/arrow/reader.h>
using arrow::Result;
using arrow::Status;
namespace {
Result<std::unique_ptr<parquet::arrow::FileReader>> OpenReader() {
arrow::fs::LocalFileSystem file_system;
ARROW_ASSIGN_OR_RAISE(auto input, file_system.OpenInputFile("data.parquet"));
parquet::ArrowReaderProperties arrow_reader_properties =
parquet::default_arrow_reader_properties();
arrow_reader_properties.set_pre_buffer(true);
arrow_reader_properties.set_use_threads(true);
parquet::ReaderProperties reader_properties =
parquet::default_reader_properties();
// Open Parquet file reader
std::unique_ptr<parquet::arrow::FileReader> arrow_reader;
auto reader_builder = parquet::arrow::FileReaderBuilder();
reader_builder.properties(arrow_reader_properties);
ARROW_RETURN_NOT_OK(reader_builder.Open(std::move(input), reader_properties));
ARROW_RETURN_NOT_OK(reader_builder.Build(&arrow_reader));
return arrow_reader;
}
Status RunMain(int argc, char **argv) {
// Read entire file as a single Arrow table
std::shared_ptr<arrow::Table> table;
for (auto i = 0; i < 10; i++) {
ARROW_ASSIGN_OR_RAISE(auto arrow_reader, OpenReader());
auto t1 = std::chrono::high_resolution_clock::now();
ARROW_RETURN_NOT_OK(arrow_reader->ReadTable(&table));
std::cout << table->num_rows() << "," << table->num_columns() << std::endl;
auto t2 = std::chrono::high_resolution_clock::now();
auto ms_int =
std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1);
std::cout << "Time taken to read parquet file is : " << ms_int.count()
<< "ms\n";
}
return Status::OK();
}
} // namespace
int main(int argc, char **argv) {
Status st = RunMain(argc, argv);
if (!st.ok()) {
std::cerr << st << std::endl;
return 1;
}
return 0;
}
然后与这个python代码比较:
#!/usr/bin/env python3
import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pq
import time
for i in range(10):
parquet_file = pq.ParquetFile('/home/pace/experiments/so4/data.parquet', pre_buffer=True)
start_time = time.time()
table = parquet_file.read()
end_time = time.time()
print("Time taken to read parquet is : ",(end_time - start_time)*1000, "ms")
在我的系统上运行 10 次后,t 检验无法区分这两个分布 (p=0.64)。
我已经编写了代码来使用 C++ 和 python 读取同一个镶木地板文件。 python 读取文件所花费的时间比 c++ 少得多,但众所周知,c++ 的执行速度比 python 快。我在这里附上了代码 -
#include <arrow/api.h>
#include <parquet/arrow/reader.h>
#include <arrow/filesystem/localfs.h>
#include <chrono>
#include <iostream>
int main(){
// ...
arrow::Status st;
arrow::MemoryPool* pool = arrow::default_memory_pool();
arrow::fs::LocalFileSystem file_system;
std::shared_ptr<arrow::io::RandomAccessFile> input = file_system.OpenInputFile("data.parquet").ValueOrDie();
// Open Parquet file reader
std::unique_ptr<parquet::arrow::FileReader> arrow_reader;
st = parquet::arrow::OpenFile(input, pool, &arrow_reader);
if (!st.ok()) {
// Handle error instantiating file reader...
}
// Read entire file as a single Arrow table
std::shared_ptr<arrow::Table> table;
auto t1 = std::chrono::high_resolution_clock::now();
st = arrow_reader->ReadTable(&table);
auto t2 = std::chrono::high_resolution_clock::now();
if (!st.ok()) {
// Handle error reading Parquet data...
}
else{
auto ms_int = std::chrono::duration_cast<std::chrono::milliseconds> (t2 - t1);
std::cout << "Time taken to read parquet file is : " << ms_int.count() << "ms\n";
}
}
我在python中使用的代码是-
#!/usr/bin/env python3
import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pq
import time
start_time = time.time()
table = pq.read_table('data.parquet')
end_time = time.time()
print("Time taken to read parquet is : ",(end_time - start_time)*1000, "ms")
在 运行 一个大小约为 87mb 的文件的 c++ 代码上,c++ 的输出是 -
Time taken to read parquet file is : 186ms
而 python 的输出是 -
Time taken to read parquet is : 108.66141319274902 ms
为什么 c++ 中的函数 read_table 和 python 的执行时间有这么大的差异?
很可能Python模块绑定了用c++或cython等语言编译的函数。因此,python 模块的实现可能具有更好的性能,具体取决于它如何从文件读取或处理数据。
1 秒是 1000 毫秒。所以差别没有那么大。除此之外,python 的许多功能经常使用 CPython,这使它们处于一个非常公平的竞争环境中。那么就看函数写的好不好,优化的好不好。在这种情况下,python 函数可能比 C++ 函数更优化。
python pq.read_table
基于与您在示例中使用的完全相同的 C++ API(在幕后它也使用 C++ parquet::arrow::FileReader
),因为两者Python 和 C++ API 来自同一个 Arrow 项目。
因此,除了一小部分 Python 调用堆栈开销外,预计两种方式的执行效果相同。
但是,您可以指定/调整几个选项来提高性能,这可以解释您的情况的不同之处。例如,python 函数默认会并行读取文件(您可以指定 use_threads=False
来禁用此功能)。另一方面,C++ FileReader 默认情况下不这样做(检查 set_use_threads
)。 python reader 可能还默认设置了其他选项。
此外,编译 C++ 示例时的确切构建标志也会产生影响。
如果您想进行比较,请尝试此 CPP 代码:
#include <cassert>
#include <chrono>
#include <cstdlib>
#include <iostream>
using namespace std::chrono;
#include <arrow/api.h>
#include <arrow/filesystem/api.h>
#include <parquet/arrow/reader.h>
using arrow::Result;
using arrow::Status;
namespace {
Result<std::unique_ptr<parquet::arrow::FileReader>> OpenReader() {
arrow::fs::LocalFileSystem file_system;
ARROW_ASSIGN_OR_RAISE(auto input, file_system.OpenInputFile("data.parquet"));
parquet::ArrowReaderProperties arrow_reader_properties =
parquet::default_arrow_reader_properties();
arrow_reader_properties.set_pre_buffer(true);
arrow_reader_properties.set_use_threads(true);
parquet::ReaderProperties reader_properties =
parquet::default_reader_properties();
// Open Parquet file reader
std::unique_ptr<parquet::arrow::FileReader> arrow_reader;
auto reader_builder = parquet::arrow::FileReaderBuilder();
reader_builder.properties(arrow_reader_properties);
ARROW_RETURN_NOT_OK(reader_builder.Open(std::move(input), reader_properties));
ARROW_RETURN_NOT_OK(reader_builder.Build(&arrow_reader));
return arrow_reader;
}
Status RunMain(int argc, char **argv) {
// Read entire file as a single Arrow table
std::shared_ptr<arrow::Table> table;
for (auto i = 0; i < 10; i++) {
ARROW_ASSIGN_OR_RAISE(auto arrow_reader, OpenReader());
auto t1 = std::chrono::high_resolution_clock::now();
ARROW_RETURN_NOT_OK(arrow_reader->ReadTable(&table));
std::cout << table->num_rows() << "," << table->num_columns() << std::endl;
auto t2 = std::chrono::high_resolution_clock::now();
auto ms_int =
std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1);
std::cout << "Time taken to read parquet file is : " << ms_int.count()
<< "ms\n";
}
return Status::OK();
}
} // namespace
int main(int argc, char **argv) {
Status st = RunMain(argc, argv);
if (!st.ok()) {
std::cerr << st << std::endl;
return 1;
}
return 0;
}
然后与这个python代码比较:
#!/usr/bin/env python3
import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pq
import time
for i in range(10):
parquet_file = pq.ParquetFile('/home/pace/experiments/so4/data.parquet', pre_buffer=True)
start_time = time.time()
table = parquet_file.read()
end_time = time.time()
print("Time taken to read parquet is : ",(end_time - start_time)*1000, "ms")
在我的系统上运行 10 次后,t 检验无法区分这两个分布 (p=0.64)。