Haskell Thrift 库在性能测试中比 C++ 慢 300 倍

2024-03-12

我正在构建一个包含两个组件的应用程序 - 用 Haskell 编写的服务器和用 Qt (C++) 编写的客户端。我正在使用 thrift 来传达它们,我想知道为什么它运行得这么慢。

我做了性能测试,这是我机器上的结果

Results

C++ server and C++ client:

Sending 100 pings                    -    13.37 ms
Transfering 1000000 size vector      -   433.58 ms
Recieved: 3906.25 kB
Transfering 100000 items from server -  1090.19 ms
Transfering 100000 items to server   -   631.98 ms

Haskell server and C++ client:

Sending 100 pings                       3959.97 ms
Transfering 1000000 size vector      - 12481.40 ms
Recieved: 3906.25 kB
Transfering 100000 items from server - 26066.80 ms
Transfering 100000 items to server   -  1805.44 ms

为什么 Haskell 在这个测试中这么慢?我怎样才能提高它的性能?

以下是文件:

Files

绩效节俭

namespace hs test
namespace cpp test

struct Item {
    1: optional string    name
    2: optional list<i32> coordinates
}

struct ItemPack {
    1: optional list<Item>     items
    2: optional map<i32, Item> mappers
}


service ItemStore {
    void ping()
    ItemPack getItems(1:string name, 2: i32 count) 
    bool     setItems(1: ItemPack items)

    list<i32> getVector(1: i32 count)
}

Main.hs

{-# LANGUAGE ScopedTypeVariables #-}   
module Main where

import           Data.Int  
import           Data.Maybe (fromJust) 
import qualified Data.Vector as Vector
import qualified Data.HashMap.Strict  as HashMap
import           Network

-- Thrift libraries
import           Thrift.Server

-- Generated Thrift modules
import Performance_Types
import ItemStore_Iface
import ItemStore


i32toi :: Int32 -> Int
i32toi = fromIntegral

itoi32 :: Int -> Int32
itoi32 = fromIntegral

port :: PortNumber
port = 9090

data ItemHandler = ItemHandler

instance ItemStore_Iface ItemHandler where
    ping _                   = return () --putStrLn "ping"
    getItems _ mtname mtsize = do 
        let size = i32toi $ fromJust mtsize
            item i = Item mtname (Just $ Vector.fromList $ map itoi32 [i..100])
            items = map item [0..(size-1)]
            itemsv = Vector.fromList items 
            mappers = zip (map itoi32 [0..(size-1)]) items 
            mappersh = HashMap.fromList mappers
            itemPack = ItemPack (Just itemsv) (Just mappersh)
        putStrLn "getItems"
        return itemPack

    setItems _ _             = do putStrLn "setItems"
                                  return True

    getVector _ mtsize       = do putStrLn "getVector"
                                  let size = i32toi $ fromJust mtsize
                                  return $ Vector.generate size itoi32

main :: IO ()
main = do
    _ <- runBasicServer ItemHandler process port 
    putStrLn "Server stopped"

ItemStore_client.cpp

#include <iostream>
#include <chrono>
#include "gen-cpp/ItemStore.h"

#include <transport/TSocket.h>
#include <transport/TBufferTransports.h>
#include <protocol/TBinaryProtocol.h>

using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;

using namespace test;
using namespace std;

#define TIME_INIT  std::chrono::_V2::steady_clock::time_point start, stop; \
                   std::chrono::duration<long long int, std::ratio<1ll, 1000000000ll> > duration;
#define TIME_START start = std::chrono::steady_clock::now(); 
#define TIME_END   duration = std::chrono::steady_clock::now() - start; \
                   std::cout << chrono::duration <double, std::milli> (duration).count() << " ms" << std::endl;

int main(int argc, char **argv) {

    boost::shared_ptr<TSocket> socket(new TSocket("localhost", 9090));
    boost::shared_ptr<TTransport> transport(new TBufferedTransport(socket));
    boost::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));

    ItemStoreClient server(protocol);
    transport->open();

    TIME_INIT

    long pings = 100;
    cout << "Sending " << pings << " pings" << endl;
    TIME_START
    for(auto i = 0 ; i< pings ; ++i)
        server.ping();
    TIME_END


    long vectorSize = 1000000;

    cout << "Transfering " << vectorSize << " size vector" << endl;
    std::vector<int> v;
    TIME_START
    server.getVector(v, vectorSize);
    TIME_END
    cout << "Recieved: " << v.size()*sizeof(int) / 1024.0 << " kB" << endl;


    long itemsSize = 100000;

    cout << "Transfering " << itemsSize << " items from server" << endl;
    ItemPack items;
    TIME_START
    server.getItems(items, "test", itemsSize);
    TIME_END


    cout << "Transfering " << itemsSize << " items to server" << endl;
    TIME_START
    server.setItems(items);
    TIME_END

    transport->close();

    return 0;
}

ItemStore_server.cpp

#include "gen-cpp/ItemStore.h"
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TBufferTransports.h>

#include <map>
#include <vector>

using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using namespace ::apache::thrift::server;


using namespace test;
using boost::shared_ptr;

class ItemStoreHandler : virtual public ItemStoreIf {
  public:
    ItemStoreHandler() {
    }

    void ping() {
        // printf("ping\n");
    }

    void getItems(ItemPack& _return, const std::string& name, const int32_t count) {

        std::vector <Item> items;
        std::map<int, Item> mappers;

        for(auto i = 0 ; i < count ; ++i){
            std::vector<int> coordinates;
            for(auto c = i ; c< 100 ; ++c)
                coordinates.push_back(c);

            Item item;
            item.__set_name(name);
            item.__set_coordinates(coordinates);

            items.push_back(item);
            mappers[i] = item;
        }

        _return.__set_items(items);
        _return.__set_mappers(mappers);
        printf("getItems\n");
    }

    bool setItems(const ItemPack& items) {
        printf("setItems\n");
        return true;
    }

    void getVector(std::vector<int32_t> & _return, const int32_t count) {
        for(auto i = 0 ; i < count ; ++i)
            _return.push_back(i);
        printf("getVector\n");
    }
};

int main(int argc, char **argv) {
    int port = 9090;
    shared_ptr<ItemStoreHandler> handler(new ItemStoreHandler());
    shared_ptr<TProcessor> processor(new ItemStoreProcessor(handler));
    shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
    shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
    shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());

    TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
    server.serve();
    return 0;
}

Makefile

GEN_SRC := gen-cpp/ItemStore.cpp gen-cpp/performance_constants.cpp gen-cpp/performance_types.cpp
GEN_OBJ := $(patsubst %.cpp,%.o, $(GEN_SRC))

THRIFT_DIR := /usr/local/include/thrift
BOOST_DIR := /usr/local/include

INC := -I$(THRIFT_DIR) -I$(BOOST_DIR)

.PHONY: all clean

all:   ItemStore_server ItemStore_client

%.o: %.cpp
    $(CXX) --std=c++11 -Wall -DHAVE_INTTYPES_H -DHAVE_NETINET_IN_H $(INC) -c $< -o $@

ItemStore_server: ItemStore_server.o $(GEN_OBJ) 
    $(CXX) $^ -o $@ -L/usr/local/lib -lthrift -DHAVE_INTTYPES_H -DHAVE_NETINET_IN_H

ItemStore_client: ItemStore_client.o $(GEN_OBJ)
    $(CXX) $^ -o $@ -L/usr/local/lib -lthrift -DHAVE_INTTYPES_H -DHAVE_NETINET_IN_H

clean:
    $(RM) *.o ItemStore_server ItemStore_client

编译并运行

我生成文件(使用 thrift 0.9 可用here http://thrift.apache.org/download) with:

$ thrift --gen cpp performance.thrift
$ thrift --gen hs performance.thrift

编译用

$ make
$ ghc Main.hs gen-hs/ItemStore_Client.hs gen-hs/ItemStore.hs gen-hs/ItemStore_Iface.hs gen-hs/Performance_Consts.hs gen-hs/Performance_Types.hs -Wall -O2

运行 Haskell 测试:

$ ./Main& 
$ ./ItemStore_client

运行C++测试:

$ ./ItemStore_server&
$ ./ItemStore_client

每次测试后记得杀死服务器

Update

Edited getVector使用方法Vector.generate代替Vector.fromList,但还是没有效果

Update 2

由于@MdxBhmt的建议我测试了getItems函数如下:

getItems _ mtname mtsize = do let size = i32toi $! fromJust mtsize
                                  item i = Item mtname (Just $!  Vector.enumFromN (i::Int32) (100- (fromIntegral i)))
                                  itemsv = Vector.map item  $ Vector.enumFromN 0  (size-1)
                                  itemPack = ItemPack (Just itemsv) Nothing 
                              putStrLn "getItems"
                              return itemPack

这是严格的,并且根据我最初的实现,与它的替代方案相比,它改进了矢量生成:

getItems _ mtname mtsize = do let size = i32toi $ fromJust mtsize
                                  item i = Item mtname (Just $ Vector.fromList $ map itoi32 [i..100])
                                  items = map item [0..(size-1)]
                                  itemsv = Vector.fromList items 
                                  itemPack = ItemPack (Just itemsv) Nothing
                              putStrLn "getItems"
                              return itemPack

请注意,没有发送 HashMap。第一个版本给出的时间为 12338.2 毫秒,第二个版本为 11698.7 毫秒,没有加速:(

Update 3

我向以下人员报告了问题节俭吉拉 https://issues.apache.org/jira/browse/THRIFT-2236

更新 4,作者:abhinav

这是完全不科学的,但是使用 GHC 7.8.3 与 Thrift 0.9.2 和 @MdxBhmt 的版本getItems,差异显着减小。

C++ server and C++ client:

Sending 100 pings:                     8.56 ms
Transferring 1000000 size vector:      137.97 ms
Recieved:                              3906.25 kB
Transferring 100000 items from server: 467.78 ms
Transferring 100000 items to server:   207.59 ms

Haskell server and C++ client:

Sending 100 pings:                     24.95 ms
Recieved:                              3906.25 kB
Transferring 1000000 size vector:      378.60 ms
Transferring 100000 items from server: 233.74 ms
Transferring 100000 items to server:   913.07 ms

执行了多次,每次都重新启动服务器。结果是可重复的。

请注意,原始问题的源代码(带有@MdxBhmt的getItems实现)不会按原样编译。必须进行以下更改:

getItems _ mtname mtsize = do let size = i32toi $! fromJust mtsize
                                  item i = Item mtname (Just $!  Vector.enumFromN (i::Int32) (100- (fromIntegral i)))
                                  itemsv = Vector.map item  $ Vector.enumFromN 0  (size-1)
                                  itemPack = ItemPack (Just itemsv) Nothing 
                              putStrLn "getItems"
                              return itemPack

getVector _ mtsize       = do putStrLn "getVector"
                              let size = i32toi $ fromJust mtsize
                              return $ Vector.generate size itoi32

每个人都指出罪魁祸首是 thrift 库,但我将重点关注您的代码(以及我可以帮助提高速度的地方)

使用代码的简化版本,您可以在其中计算itemsv:

testfunc mtsize =  itemsv
  where size = i32toi $ fromJust mtsize
        item i = Item (Just $ Vector.fromList $ map itoi32 [i..100])
        items = map item [0..(size-1)]
        itemsv = Vector.fromList items 

首先,您有许多正在创建的中间数据item i。由于懒惰,那些小而计算速度快的向量变得延迟了对数据的思考,而我们可以立即拥有它们。

有2个精心放置$!,代表严格评估:

 item i = Item (Just $! Vector.fromList $! map itoi32 [i..100])

运行时间将减少 25%(对于尺寸 1e5 和 1e6)。

但这里有一个更有问题的模式:您生成一个列表以将其转换为向量,而不是直接构建向量。

看最后两行,创建一个列表 -> 映射一个函数 -> 转换为一个向量。

嗯,向量与列表非常相似,你可以做类似的事情! 所以你必须在它上面生成一个向量->向量.map 并完成。不再需要将列表转换为向量,并且向量上的映射通常比列表更快!

这样你就可以摆脱items并重写以下内容itemsv:

  itemsv = Vector.map item  $ Vector.enumFromN 0  (size-1)

重新应用相同的逻辑item i,我们消除所有列表。

testfunc3 mtsize = itemsv
   where 
      size = i32toi $! fromJust mtsize
      item i = Item (Just $!  Vector.enumFromN (i::Int32) (100- (fromIntegral i)))
      itemsv = Vector.map item  $ Vector.enumFromN 0  (size-1)

这比初始运行时间减少了 50%。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Haskell Thrift 库在性能测试中比 C++ 慢 300 倍 的相关文章

随机推荐

  • Argparse 参数生成帮助,带有选择的“metavar”

    当使用参数时 可选参数和位置参数都有这个problem 与关键字choices 生成的帮助输出显示了这些选择 如果同一参数还包含metavar关键字 选项列表从生成的输出中省略 我的想法是展示metavar in the usage行 但当
  • 将“字符串”作为原始二进制文件写入 Python 文件

    我正在尝试编写一系列我从头开始构建的测试文件 数据有效负载生成器的输出是字符串类型 我正在努力将字符串直接写入文件 有效负载构建器仅使用十六进制值 并且只需为每次迭代添加一个字节 我尝试过的 写入 函数要么都落在字符串的写入上 要么为字符串
  • 字节字符串太长 PyPyOdbc

    使用 FreeTDS 和 Python3 5 库 PyPyodbc 我得到了 Byte string too long 当尝试在 SQL Server 数据库 驻留在 Windows 上 中插入行时 在 FreeTDS conf 文件下有一
  • jQuery mCustomScrollbar“scrollTo”不起作用

    我正在使用基本设置mCustomScrollbar除非我调用以下内容 否则效果很好 jQuery mcs container mCustomScrollbar scrollTo top 什么都没发生 firebug 中没有显示错误 它只是什
  • WPF:如何将树视图放入数据网格中

    如何将树视图类放入数据网格中 我已经有一个已填充的数据网格 但现在我想要每行都有一个树视图 我该怎么做 您想要设计模板的样式DataGridRow显示一个TreeView 看看这个代码项目 http www codeproject com
  • Safari 上的 CSS 背景图像相对路径 var() 不加载图像

    不确定是否有人遇到这个问题 我见过类似的问题 但不完全一样 但在 Mac OSX Safari 浏览器上 当您使用变量作为背景图像的相对图像位置时 会发生以下问题 它不会加载 root lb3 widget icon url images
  • Linux Bash 中双与号 (&&) 和分号 (;) 有什么区别?

    Linux中 号和分号有什么区别Bash http en wikipedia org wiki Bash 28Unix shell 29 例如 command1 command2 vs command1 command2 The 运算符是布
  • Java、Apache HttpClient、TLSv1.2 和 OpenJDK 7

    我们有一小组运行 OpenJDK v1 7 0 111 的 Tomcat 服务器 我们计划在今年夏天升级并迁移它们 但我们发现与我们交互的客户端 API 在短期内将要求使用 TLSv1 2 我的最终愿望是找到一个配置更改来实现这一点 那里托
  • 如何查看 SQL Server 中 varchar 或 char 字段中允许的所有“特殊”字符? [关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我在哪里可以看到存储在一个文件上的所有不可见字符varchar or charSQL Server 中的字段 例如 n 新队 r 回车 和其
  • 为什么 reqwest HTTP 库返回二进制数据而不是文本正文?

    我正在尝试使用 reqwest 执行 HTTP GET 请求并将响应正文打印到 STDOUT 这适用于大多数网站 但它会为 amazon com 返回奇怪的二进制输出 tokio main async fn main run await a
  • xpath 获取表内的行

    我有一个 html 表 例如 table class cars tr class item odd tr tr class item even tr table 如何使用 xpath 获取表行 tr contains class 我可以用
  • Delphi - 使用 TApplicationEvents OnShortCut 事件检测 Alt+C 按键

    我正在使用 TApplicationEvents OnShortCut 事件来获取 Delphi 程序中的应用程序键盘快捷键 使用以下代码 procedure TForm1 ApplicationEvents1ShortCut var Ms
  • Mavericks+ 中的 CGEventTapCreateForPSN(已弃用 GetCurrentProcess)

    我在用CGEventTapCreateForPSN捕获和过滤我的应用程序的密钥 我对拦截其他应用程序的事件不感兴趣 我很确定事件点击对于我的目的来说太严厉了 但我一直无法找到更好的方法 并且使用事件点击是有效的 具体来说 这段代码做了我想要
  • 我可以在 SQLite 命令行上运行脚本吗?

    我可以运行一个包含 SQL 语句和 SQLite 命令 将 SQL 语句与 SQLite 混合 commands可能有点棘手 sqlite3 test db create table X x integer dump Error near
  • HTML5 音频重新开始

    Having var audio new Audio click ogg 我在需要时播放点击声音 audio play 然而 有时用户的速度太快 以至于浏览器根本不播放音频 可能是在仍在播放上一个音频时 play要求 这个问题是否与prel
  • 节点:由于代理配置,npm 安装失败!现在怎么办?

    我尝试使用 angular js 教程应用程序运行 Node 但发现 npm isntall 没有正常运行 我得到了一个奇怪的旋转小角色 它似乎永远不会停止 我运行 npm install verbose 并得到以下调试跟踪 216 err
  • 刷新和清除何时提交?

    我正在使用 JPA EclipseLink 2 0 和 Glassfish 3 1 2 2 我想知道我打电话后是否 em flush em clear 对象立即提交到数据库 我的问题是我进行了太多交易OutOfMemory 我想通过刷新事务
  • 第一次机会例外

    我有一个在windows xp下完美运行的项目 现在我尝试在Windows 7下运行它 并在立即窗口下出现很多异常 A first chance exception of type System ArgumentNullException
  • 通话结束后回拨? (恢复 AVCaptureSession)

    我有一个摄像机应用程序 我希望它允许用户在打电话时捕捉内容 我可以通过在接到电话且会话中断时断开音频捕获来做到这一点 但由于会话不再中断 我现在无法知道电话何时结束并且可以重新连接音频设备 如果我使用这个回调AVCaptureSession
  • Haskell Thrift 库在性能测试中比 C++ 慢 300 倍

    我正在构建一个包含两个组件的应用程序 用 Haskell 编写的服务器和用 Qt C 编写的客户端 我正在使用 thrift 来传达它们 我想知道为什么它运行得这么慢 我做了性能测试 这是我机器上的结果 Results C server a