├── .gitignore ├── CMakeLists.txt ├── .gitmodules ├── src ├── uwebsockets │ ├── test_def.h │ ├── CMakeLists.txt │ ├── echo_client.cpp │ └── echo_server.cpp ├── boost-beast │ ├── CMakeLists.txt │ └── echo_server.cpp ├── websocketpp │ ├── CMakeLists.txt │ └── echo_server.cpp ├── flashws │ ├── test_def.h │ ├── CMakeLists.txt │ ├── echo_server.cpp │ └── echo_client.cpp └── usockets-tcp │ ├── CMakeLists.txt │ ├── test_def.h │ ├── https_client.cpp │ ├── echo_server.cpp │ └── flat_hash_map.h ├── README.md ├── certs ├── server.crt ├── ca.pem └── server.key ├── tools ├── bench_ws.py └── plot_data.ipynb └── configs ├── config_la1.ini └── config_la6.ini /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | cmake-* 3 | .DS_Store 4 | 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(test_uwebsockets) 3 | 4 | add_subdirectory(src/flashws) 5 | add_subdirectory(src/uwebsockets) 6 | add_subdirectory(src/websocketpp) 7 | add_subdirectory(src/usockets-tcp) 8 | add_subdirectory(src/boost-beast) -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/uWebSockets"] 2 | path = third_party/uWebSockets 3 | url = https://github.com/uNetworking/uWebSockets.git 4 | [submodule "third_party/flashws"] 5 | path = third_party/flashws 6 | url = https://github.com/renzibei/flashws.git 7 | [submodule "third_party/websocketpp"] 8 | path = third_party/websocketpp 9 | url = https://github.com/zaphoyd/websocketpp.git 10 | -------------------------------------------------------------------------------- /src/uwebsockets/test_def.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace test { 4 | 5 | // constexpr const char* const listen_addr = "10.5.96.3"; 6 | // constexpr const char* const listen_addr = "10.5.96.7"; 7 | inline constexpr const char* cert_file_path = "./certs/server.crt"; 8 | inline constexpr const char* key_file_path = "./certs/server.key"; 9 | inline constexpr const char* ca_file_path = "./certs/ca.pem"; 10 | 11 | // inline constexpr size_t MAX_DATA_LEN = 1 << 16; 12 | // inline constexpr size_t MAX_DATA_LEN = 64; 13 | 14 | } // namespace test -------------------------------------------------------------------------------- /src/boost-beast/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(test_boost_beast) 3 | 4 | # Set the required C++ standard 5 | set(CMAKE_CXX_STANDARD 17) 6 | 7 | 8 | 9 | # Find the Boost package and its required components (e.g., system and filesystem) 10 | find_package(Boost 1.71 REQUIRED) 11 | 12 | # Add your executable target (e.g., main.cpp) 13 | #add_executable(${PROJECT_NAME} main.cpp) 14 | 15 | # Link the Boost libraries to your target 16 | #target_link_libraries(boost_echo_server Boost::beast Boost::asio) 17 | include_directories({Boost_INCLUDE_DIRS}) 18 | add_executable(boost_echo_server echo_server.cpp) 19 | target_link_options(boost_echo_server 20 | PRIVATE 21 | -pthread 22 | ) -------------------------------------------------------------------------------- /src/websocketpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(test_websocketpp) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(tpp_echo_server echo_server.cpp) 7 | 8 | 9 | 10 | set(TPP_SOURCE_DIR ../../third_party/websocketpp) 11 | 12 | add_library(tpp INTERFACE) 13 | target_include_directories(tpp INTERFACE ${TPP_SOURCE_DIR}) 14 | 15 | target_link_libraries(tpp_echo_server PRIVATE tpp) 16 | 17 | 18 | 19 | 20 | 21 | if(NOT MSVC) 22 | 23 | target_compile_options( 24 | tpp_echo_server 25 | PRIVATE 26 | -O3 27 | -Wall -Wextra 28 | # -fsanitize=address 29 | -flto 30 | ) 31 | 32 | target_link_options(tpp_echo_server 33 | PRIVATE 34 | -pthread 35 | ) 36 | endif() 37 | 38 | 39 | 40 | 41 | target_link_options(tpp_echo_server 42 | PRIVATE 43 | # -fsanitize=address 44 | -flto 45 | ) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WebSocket Benchmark 2 | 3 | This is a benchmark to test the performance of WebSocket libraries. 4 | 5 | ## Build 6 | 7 | First clone this repo. 8 | ``` 9 | cd ws-benchmark 10 | ``` 11 | 12 | Pull all the submodules. 13 | ``` 14 | git submodule update --init --recursive 15 | ``` 16 | 17 | Then you can build the repo. There a few cmake options. 18 | Regards flashws, if you want to build it without the f-stack and use the system 19 | tcp stack instead, add `-DFWS_ENABLE_FSTACK=OFF` in cmake commands. If you want 20 | to use f-stack and DPDK, please compile and install f-stack first. 21 | 22 | ``` 23 | mkdir build 24 | cd build 25 | cmake .. (options e.g. -DFWS_ENABLE_FSTACK=OFF) 26 | make -j6 27 | ``` 28 | You should be able to compile it if no more problems happen. 29 | 30 | ## Usage 31 | 32 | You can choose which server and which client to use. And we provide a python script 33 | for you to test it. 34 | 35 | ``` 36 | Usage:\npython3 tools/bench_ws.py c|s (client or server) lib_name host_ip host_port \ 37 | process_cnt client_cnt_per_proc client_restart_cnt \ 38 | output_data_file [other arguments for dpdk] 39 | ``` 40 | 41 | The data will be exported as a csv file. -------------------------------------------------------------------------------- /certs/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDWjCCAkKgAwIBAgIUKoFQwwPgJQj7uGsWGXQSjWK8ohowDQYJKoZIhvcNAQEL 3 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDAyMjYwNTQ1MzBaFw0yNTA3 5 | MTAwNTQ1MzBaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 6 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB 7 | AQUAA4IBDwAwggEKAoIBAQCnz++pcao6I/ofOA+1hqp8ChFjozIFNqLc+qShbpZi 8 | w+88TnGODrumOFIQ8XLYy6BVXic303q5S8tuF4RLNynKvmHjWCJvrEBKV3MQWAu/ 9 | MPTg7a+sx2jfb056tTim7vvrdyY1zBQOgA6nZm8GsDnF/LaeepHEsElQgffoluwk 10 | 09LAYi5CoTnnYxDZipCn9kQd0ji8BLnjm/pa/9+lu5NkpPADnDBtl4R3kd1JlZP+ 11 | MlWwkrto2h6nBWRJp9AjeLkSmZLC8jB+RDUHGlKomKf099yFzgKb7U7+S2UfTrQq 12 | KvU/z7AXPM7TmgB/pMNL44MsPf02He/m94WCsl7hhmHPAgMBAAGjQjBAMB0GA1Ud 13 | DgQWBBQupQ4V+OKddBL2jHWsBzA3VAcXujAfBgNVHSMEGDAWgBQuEUzvSdTzEKAo 14 | jnRXpmFdBuEeYjANBgkqhkiG9w0BAQsFAAOCAQEAa4iMxfu1++MsUA/HcGtt0/Vh 15 | GAadV6qEieQMwTtT/6VvGVQSBxZRI+UMypTu8y+IBebk3Hu4HdlZcoWcyp/7SPRS 16 | JsrVmVhwsdrSbeFa3pilV64n2W0shDTQ+RFI2ayRFJ6R95WAwR3SqC+tWXaCY25G 17 | 4F/DnG0oGJ/L3R2Q9LfPG9uUBNhcNqmA0RY/eQJ245+tRnC6pBnSdPOZmfvJ/P1p 18 | lyizCmBx4MkwNDMudNskJzw57h+U+2zPRTp7r8KBE8usLW971QE0LUuZG/QbWd2s 19 | 55aBeEOv5wU10IkDyCovwoZfq2fvGzSguzA71qJwV2lM/A2yi7DOik4EE+aesg== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /certs/ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDazCCAlOgAwIBAgIUSRhHBOLPLPdhnQk7Zdi+xisPuSUwDQYJKoZIhvcNAQEL 3 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDAyMjYwNTQ0MzNaFw0yNjEy 5 | MTYwNTQ0MzNaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 6 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB 7 | AQUAA4IBDwAwggEKAoIBAQDsR39Av1jT96syihhyQYy83zV+fzSapNre7EMpuZnr 8 | xqLUebwnNx2LuyuqF2HptN+m0ZNLAOv+NoebPGmay/XCvI2EOtaxvppbUKCKMsqv 9 | CDsZj6LYqrol20VtDPOtUS65Bsn9W3shjSLQaVdIf0v7XL86WTGr4Ot4BnV3OGC2 10 | iO+GZ0ZEt/uQ8IvG89JrBIAjzXsfK0scTaEXR9Lx95ssD2tEK1aprfb8BGKs+URo 11 | exOvflviC+FMvXgGcLVNedn4MJCBjhq10T6kqTC+ZGedTdQmDLfAxZWIfg7kUBRT 12 | cHNSViGvo+A9AepyG/hxStxTxWYs/1NlofsnxgDqwjJjAgMBAAGjUzBRMB0GA1Ud 13 | DgQWBBQuEUzvSdTzEKAojnRXpmFdBuEeYjAfBgNVHSMEGDAWgBQuEUzvSdTzEKAo 14 | jnRXpmFdBuEeYjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQA0 15 | jH+wXorXXQKDhaqaS10mj94qajG9rax4bweH/6K9RPxVTNqej/kOtSek2HUBYhhC 16 | iDmU/QXuRBJ5FP75uXzjXs7eidNEHln65ziwVRhuXnYWND0ees7cN7nRbhjZzE9r 17 | tO+uz/ARk4OU/ytgsNwlitQo3UcWz4WWGThUekrHFjS6c/ms94gm01GyjtlQ0RC2 18 | P8ioBGkLHvY9a/hlBVHemKny+eRF0b47Tw9dupAUs8MIuMPHsBf1zeX+QHy7HWFj 19 | LPyGsuWdSca3sdzbbyROwMVAmPYZtL/zjWE7D0LBMGesM5Bg6TV1QkAE9p+KMw1i 20 | EsW6yhXCanesf4WCu3wB 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /src/flashws/test_def.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace test { 6 | 7 | // vu-la01 8 | // inline constexpr const char* const SERVER_IP = "10.5.96.3"; 9 | // vu-la06 10 | // inline constexpr const char* const SERVER_IP = "10.5.96.7"; 11 | 12 | // aws-vi01 13 | // inline constexpr const char* const SERVER_IP = "172.31.99.99"; 14 | // inline constexpr const char* const SERVER_IP = "172.31.98.130"; 15 | 16 | 17 | // inline constexpr uint16_t SERVER_PORT = 58600; 18 | 19 | 20 | // inline constexpr size_t TEST_TIMES = 100'000; 21 | // inline constexpr size_t MAX_DATA_LEN = 64; 22 | // inline constexpr size_t MAX_DATA_LEN = 1UL << 18; 23 | 24 | constexpr size_t MAX_CLIENT_EVENT_NUM = 65536; 25 | constexpr size_t MAX_SERVER_EVENT_NUM = 65536; 26 | 27 | inline constexpr bool USE_BUSY_POLL = true; 28 | inline constexpr int BUSY_POLL_US = 800; 29 | 30 | inline constexpr int LISTEN_BACKLOG = 128; 31 | 32 | inline constexpr bool ENABLE_TLS = true; 33 | inline constexpr bool SHOULD_VERIFY_CERT = true; 34 | inline constexpr const char* hostname = ""; 35 | inline constexpr const char* cert_file_path = "./certs/server.crt"; 36 | inline constexpr const char* key_file_path = "./certs/server.key"; 37 | inline constexpr const char* ca_file_path = "./certs/ca.pem"; 38 | 39 | #define ENABLE_NO_DELAY 1 40 | 41 | } // namespace test -------------------------------------------------------------------------------- /certs/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCnz++pcao6I/of 3 | OA+1hqp8ChFjozIFNqLc+qShbpZiw+88TnGODrumOFIQ8XLYy6BVXic303q5S8tu 4 | F4RLNynKvmHjWCJvrEBKV3MQWAu/MPTg7a+sx2jfb056tTim7vvrdyY1zBQOgA6n 5 | Zm8GsDnF/LaeepHEsElQgffoluwk09LAYi5CoTnnYxDZipCn9kQd0ji8BLnjm/pa 6 | /9+lu5NkpPADnDBtl4R3kd1JlZP+MlWwkrto2h6nBWRJp9AjeLkSmZLC8jB+RDUH 7 | GlKomKf099yFzgKb7U7+S2UfTrQqKvU/z7AXPM7TmgB/pMNL44MsPf02He/m94WC 8 | sl7hhmHPAgMBAAECggEABgv6lU74tM6eESjqe/6FvyF3vfDf+QsedDr92qlK9vUb 9 | dj9XXSiPv24RGvYwTrqLpPfXDu5qYinmgymKxVEQCvOwQEppNz/sMyGSjli116ZS 10 | dJZr+ZxX3ZE7sTU9fNpornPlJxQa9ujLXmHVB1crRcSU6gH9pao6ghGk1rc1fCAs 11 | 4sgnAxZKXGrP0ezrl2QiXFxQFWW4C6P4MlYmy+L/MXZw+O7oLSEEsHUi0TNTrxZK 12 | 3yY0wOiU2Dh8a5vwFCGEV5PFSk4t/HWuFLmdw6BEioN10KBCNbkn6UIuU+j63Fc/ 13 | KM1NrNTIwkUh9VLw9SQXs0M3Gz1xMhTBqqJgY5JilQKBgQDek+OVTKJue034ntlY 14 | fGnYc5mnn1CyXD6jVrDabVr4aE689pPO75ncEKB7BZU6kjDqKKCETIwHHxE0zUbn 15 | ea9DT3fsZ3MAjGdsapbXiunhcDDQOZe01XraLIs9gfB+UV4gbpp8KJZVSpNStCK3 16 | 7LTg1NqgC+ilGpLaEewAKe8dLQKBgQDBAs7yzaK1qhJb2UOC0+6pukzmIjL6iQsT 17 | 4WsjItO5PDou/9IA5hfpZNkSOcutOpM66tufjAj0szngA6FIDYKC7F4ZsVt9fnJT 18 | KgQNHLQfmP67onDcVKK8ifnfFNoWsx4U01NldYLobapYwWIIwtsSEbFEgecxX/BE 19 | 9G3MzwfwawKBgCvlZMPkGkDJxqXaxiiiWMmcethJHkQe1yeliDl7ya4l/CleQ+ZS 20 | 8Lgq+0tn3Xdwr37uxkFxS0h4Qw9z0KEEKaGPwan4+QBJsHf8SYV22wtf8fCO+zFp 21 | zNHMaSWXQ9PVvkeNKKCF7KgjjgyIhDRb1Yxlzjw0PQyawg2J7U5W9tZ9AoGAObVo 22 | QQqg6qY2JQY5/ET3qAHO+kQhb+1HxaLxkKjaGxlZ4DGXDbO+VIdVq/Bh6KKJ4OTV 23 | 08df6iIoXMVZFJ0Nd63KmO4Bh+0lq531ZiI97PCmwV1ZV1dq7OpoLn4ikwTtKpbg 24 | 6YYQJQSdGTqA4FO6gpWNpgkdIhMoWMWoisMgAY8CgYAa2RxlaiLYEl7en7Ke5p9w 25 | 3xRZjJY+AE7tXPM1V6tLhG6uU7SiqwAdiqjMwXHfnOxxzgRKT2owrJApyjUteT3T 26 | Urs5Onc5SPXL0/MiHXA6N1iuTqAztn9V0Q1/eo23hPsQmArJB/8H3l8hmtrGk3Ai 27 | b0ddv61DqQLZG8035+HmoA== 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /src/flashws/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(test_flashws) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | add_executable(fws_echo_client echo_client.cpp) 7 | add_executable(fws_echo_server echo_server.cpp) 8 | 9 | set(FLASHWS_SOURCE_DIR ../../third_party/flashws) 10 | add_subdirectory(${FLASHWS_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/flashws) 11 | 12 | 13 | target_link_libraries(fws_echo_client PRIVATE fws::flashws) 14 | target_link_libraries(fws_echo_server PRIVATE fws::flashws) 15 | 16 | OPTION(USE_RAW_HISTOGRAM "Use raw_histogram, otherwise use HdrHistogram_c" OFF) 17 | 18 | if(USE_RAW_HISTOGRAM) 19 | ADD_DEFINITIONS(-DUSE_RAW_HISTOGRAM) 20 | SET(HDR_HIST_LIB "") 21 | else() 22 | # get_target_property(FLASHWS_SOURCE_DIR fws::flashws SOURCE_DIR) 23 | 24 | add_subdirectory(${FLASHWS_SOURCE_DIR}/thirdparty/HdrHistogram_c ${CMAKE_CURRENT_BINARY_DIR}/HdrHistogram_c) 25 | SET(HDR_HIST_LIB hdr_histogram_static) 26 | endif() 27 | 28 | target_link_libraries(fws_echo_client 29 | PRIVATE ${HDR_HIST_LIB} 30 | ) 31 | 32 | 33 | 34 | if(NOT MSVC) 35 | target_compile_options( 36 | fws_echo_client 37 | PRIVATE 38 | -O3 39 | -g 40 | -Wall -Wextra 41 | # -fsanitize=address 42 | -flto 43 | ) 44 | 45 | target_compile_options( 46 | fws_echo_server 47 | PRIVATE 48 | -O3 49 | -g 50 | -Wall -Wextra 51 | # -fsanitize=address 52 | -flto 53 | ) 54 | endif() 55 | 56 | 57 | target_link_options(fws_echo_client 58 | PRIVATE 59 | # -fsanitize=address 60 | -flto 61 | ) 62 | 63 | 64 | 65 | target_link_options(fws_echo_server 66 | PRIVATE 67 | # -fsanitize=address 68 | -flto 69 | ) -------------------------------------------------------------------------------- /src/usockets-tcp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(test_usockets_tcp) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | find_package(OpenSSL REQUIRED) 7 | 8 | set(U_WS_DIR "../../third_party/uWebSockets") 9 | set(U_SOCKETS_DIR "${U_WS_DIR}/uSockets") 10 | 11 | file(GLOB u_sockets_src 12 | "${U_SOCKETS_DIR}/src/*.c" 13 | "${U_SOCKETS_DIR}/src/crypto/*.c" 14 | "${U_SOCKETS_DIR}/src/crypto/*.cpp" 15 | "${U_SOCKETS_DIR}/src/eventing/*.c" 16 | ) 17 | 18 | file(GLOB u_sockets_hdr 19 | "${U_SOCKETS_DIR}/src" 20 | ) 21 | 22 | add_library(us_sockets ${u_sockets_src}) 23 | #include_directories(${u_socket}) 24 | target_include_directories(us_sockets 25 | PUBLIC 26 | ${u_sockets_hdr} 27 | ) 28 | 29 | target_compile_definitions(us_sockets 30 | PRIVATE 31 | -DLIBUS_USE_OPENSSL 32 | 33 | ) 34 | 35 | target_link_libraries(us_sockets 36 | PRIVATE 37 | OpenSSL::SSL 38 | ) 39 | 40 | #target_compile_options(us_sockets 41 | # PRIVATE 42 | # -std=c11 43 | #) 44 | set_target_properties(us_sockets 45 | PROPERTIES 46 | C_STANDARD 11 47 | CXX_STANDARD 20 48 | 49 | ) 50 | 51 | add_executable(us_echo_server echo_server.cpp) 52 | 53 | target_link_libraries(us_echo_server 54 | PRIVATE 55 | us_sockets 56 | ) 57 | 58 | target_link_options(us_echo_server 59 | PRIVATE 60 | -flto 61 | ) 62 | 63 | add_executable(us_https_client https_client.cpp) 64 | 65 | target_link_libraries(us_https_client 66 | PRIVATE 67 | us_sockets 68 | ) 69 | 70 | target_link_options(us_https_client 71 | PRIVATE 72 | -flto 73 | ) 74 | 75 | #add_executable(echo_client echo_client.cpp) 76 | #target_link_libraries(echo_client 77 | # PRIVATE 78 | # uws 79 | # ) 80 | # 81 | #target_link_options(echo_client 82 | # PRIVATE 83 | # -flto 84 | # ) -------------------------------------------------------------------------------- /src/usockets-tcp/test_def.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #define SERVER_PORT 58600 6 | //#define MAX_DATA_LEN (1 << 16) 7 | #define MAX_DATA_LEN 64 8 | 9 | //#define TEST_TIMES 1'00'000 10 | 11 | #define ENABLE_NO_DELAY 1 12 | #define ENABLE_BUSY_POLL 1 13 | #define BUSY_POLL_US 800 14 | #define SET_NON_BLOCK 1 15 | #define SET_LINGER_ZERO_TIMEOUT 0 16 | 17 | #define MAX_WRITE_EVENT_WRITE_SIZE 65536 18 | #define FSTACK_ONE_TIME_FULL_THRES 16384 19 | #define MAX_FSTACK_ONE_TIME_WRITE_SIZE 16384 20 | 21 | #define SEND_LATENCY_DATA 1 22 | 23 | inline constexpr int SSL = 1; 24 | inline constexpr const char* cert_file_path = "/root/codes/ws-benchmark/certs/server.crt"; 25 | inline constexpr const char* key_file_path = "/root/codes/ws-benchmark/certs/server.key"; 26 | inline constexpr const char* ca_file_path = "/root/codes/ws-benchmark/certs/ca.pem"; 27 | 28 | namespace test { 29 | 30 | 31 | // aws-vi01 32 | //inline constexpr const char * SERVER_IP = "172.31.86.246"; 33 | //inline constexpr const char* SERVER_IP = "172.31.99.99"; 34 | //inline constexpr const char * SERVER_IP = "127.0.0.1"; 35 | 36 | // vu-la01 37 | //inline constexpr const char* const SERVER_IP = "10.5.96.3"; 38 | //inline constexpr const char* const SERVER_IP = "108.61.219.248"; 39 | 40 | // vu-la06 41 | 42 | inline constexpr const char *SERVER_IP = "10.5.96.7"; 43 | 44 | inline constexpr size_t MSG_LIMIT_PER_CLIENT = 50; 45 | // inline constexpr size_t MSG_LIMIT_PER_CLIENT = 1; 46 | 47 | inline constexpr size_t CON_CLIENT_NUM = 500; 48 | 49 | inline constexpr size_t REBORN_LIMIT_FOR_CLIENT = 10; 50 | 51 | inline constexpr size_t TOTAL_MSG_CNT = MSG_LIMIT_PER_CLIENT * CON_CLIENT_NUM * REBORN_LIMIT_FOR_CLIENT; 52 | 53 | #if SEND_LATENCY_DATA 54 | inline constexpr size_t LATENCY_DATA_SIZE = sizeof(uint64_t) * 2; 55 | #else 56 | inline constexpr size_t LATENCY_DATA_SIZE = 0; 57 | #endif 58 | 59 | static_assert(LATENCY_DATA_SIZE <= MAX_DATA_LEN); 60 | 61 | 62 | #define MAX_EVENT_NUM 512 63 | 64 | } //namespace test -------------------------------------------------------------------------------- /src/uwebsockets/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(test_uwebsockets) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | find_package(OpenSSL REQUIRED) 7 | 8 | set(U_WS_DIR "../../third_party/uWebSockets") 9 | set(U_SOCKETS_DIR "${U_WS_DIR}/uSockets") 10 | 11 | file(GLOB u_sockets_src 12 | "${U_SOCKETS_DIR}/src/*.c" 13 | "${U_SOCKETS_DIR}/src/crypto/*.c" 14 | "${U_SOCKETS_DIR}/src/crypto/*.cpp" 15 | "${U_SOCKETS_DIR}/src/eventing/*.c" 16 | ) 17 | 18 | file(GLOB u_sockets_hdr 19 | "${U_SOCKETS_DIR}/src" 20 | ) 21 | 22 | add_library(u_sockets ${u_sockets_src}) 23 | #include_directories(${u_socket}) 24 | #target_include_directories(u_sockets 25 | # PUBLIC 26 | # ${u_sockets_hdr} 27 | # ) 28 | # 29 | #target_compile_definitions(u_sockets 30 | # PRIVATE 31 | # -DLIBUS_USE_OPENSSL 32 | ## -DLIBUS_NO_SSL 33 | # ) 34 | # 35 | #target_compile_options(u_sockets 36 | # PRIVATE 37 | # -std=c11 38 | #) 39 | 40 | target_include_directories(u_sockets 41 | PUBLIC 42 | ${u_sockets_hdr} 43 | ) 44 | 45 | target_compile_definitions(u_sockets 46 | PRIVATE 47 | -DLIBUS_USE_OPENSSL 48 | ) 49 | 50 | target_link_libraries(u_sockets 51 | PRIVATE 52 | OpenSSL::SSL 53 | ) 54 | set_target_properties(u_sockets 55 | PROPERTIES 56 | C_STANDARD 11 57 | CXX_STANDARD 20 58 | 59 | ) 60 | 61 | 62 | add_library(uws INTERFACE) 63 | target_include_directories(uws INTERFACE ${U_WS_DIR}/src) 64 | target_compile_definitions(uws 65 | INTERFACE 66 | -DUWS_NO_ZLIB 67 | ) 68 | 69 | target_compile_options(uws 70 | INTERFACE 71 | -flto 72 | ) 73 | 74 | 75 | target_link_libraries(uws 76 | INTERFACE 77 | u_sockets 78 | ) 79 | 80 | add_executable(uws_echo_server echo_server.cpp) 81 | 82 | target_link_libraries(uws_echo_server 83 | PRIVATE 84 | uws 85 | ) 86 | 87 | target_link_options(uws_echo_server 88 | PRIVATE 89 | -flto 90 | ) 91 | 92 | 93 | #add_executable(echo_client echo_client.cpp) 94 | #target_link_libraries(echo_client 95 | # PRIVATE 96 | # uws 97 | # ) 98 | # 99 | #target_link_options(echo_client 100 | # PRIVATE 101 | # -flto 102 | # ) -------------------------------------------------------------------------------- /src/uwebsockets/echo_client.cpp: -------------------------------------------------------------------------------- 1 | /* We simply call the root header file "App.h", giving you uWS::App and uWS::SSLApp */ 2 | #include "App.h" 3 | #include "test_def.h" 4 | #include 5 | #include 6 | 7 | // client of uwebsockets is not usable now! Use ws clients from other libraries 8 | 9 | int main(int argc, const char** argv) { 10 | 11 | if (argc < 4) { 12 | printf("Invalid parameters!\nUsage: ./echo_server port max_msg_len msg_cnt\n"); 13 | return -1; 14 | } 15 | int port = atoi(argv[1]); 16 | if (port <= 0) { 17 | printf("Invalid port: %s\n", argv[1]); 18 | return -1; 19 | } 20 | long long max_msg_len = atoll(argv[2]); 21 | if (max_msg_len <= 0) { 22 | printf("invalid max_msg_len: %s\n", argv[2]); 23 | return -1; 24 | } 25 | 26 | int max_msg_cnt = atoi(argv[3]); 27 | if (max_msg_cnt <= 0) { 28 | printf("invalid max_msg_cnt: %s\n", argv[3]); 29 | } 30 | 31 | std::string host_addr = std::string("ws://") + std::string(test::listen_addr) + ":" + std::to_string(port); 32 | printf("Will connect to %s\n", host_addr.c_str()); 33 | 34 | std::string first_msg(max_msg_len, 0); 35 | for (int i = 0; i < max_msg_len; ++i) { 36 | first_msg[i] = rand() % 10 + '0'; 37 | } 38 | /* ws->getUserData returns one of these */ 39 | struct PerSocketData { 40 | int64_t msg_cnt = 0; 41 | /* Fill with user data */ 42 | int64_t last_ns = 0; 43 | int64_t start_ns = 0; 44 | }; 45 | 46 | 47 | 48 | /* Keep in mind that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support. 49 | * You may swap to using uWS:App() if you don't need SSL */ 50 | uWS::App( 51 | // { 52 | /* There are example certificates in uWebSockets.js repo */ 53 | // .key_file_name = "misc/key.pem", 54 | // .cert_file_name = "misc/cert.pem", 55 | // .passphrase = "1234" 56 | // } 57 | ).ws("/", { 58 | /* Settings */ 59 | // .compression = uWS::CompressOptions(uWS::DEDICATED_COMPRESSOR_4KB | uWS::DEDICATED_DECOMPRESSOR), 60 | .compression = uWS::CompressOptions(uWS::DISABLED), 61 | .maxPayloadLength = (uint32_t)max_msg_len, 62 | .idleTimeout = 16, 63 | .maxBackpressure = 100 * 1024 * 1024, 64 | .closeOnBackpressureLimit = false, 65 | .resetIdleTimeoutOnSend = false, 66 | .sendPingsAutomatically = true, 67 | /* Handlers */ 68 | .upgrade = nullptr, 69 | .open = [&](auto * ws) { 70 | /* Open event here, you may access ws->getUserData() which points to a PerSocketData struct */ 71 | std::cout << "connection opened\n"; 72 | auto* user_data = ws->getUserData(); 73 | user_data->start_ns = (user_data->last_ns = std::chrono::high_resolution_clock::now().time_since_epoch().count()); 74 | ws->send(first_msg, uWS::OpCode::TEXT, true); 75 | }, 76 | .message = [&](auto *ws, std::string_view message, uWS::OpCode opCode) { 77 | auto *user_data = ws->getUserData(); 78 | auto now_msg_cnt = ++user_data->msg_cnt; 79 | if (now_msg_cnt == max_msg_cnt) { 80 | auto now_ns = std::chrono::high_resolution_clock::now().time_since_epoch().count(); 81 | auto pass_ns = now_ns - user_data->start_ns; 82 | double rtt_ns = double(pass_ns) / now_msg_cnt; 83 | printf("Total avg rtt latency: %.3f us\n", rtt_ns / 1000.0); 84 | } 85 | ws->send(message, opCode, true); 86 | }, 87 | .drain = [](auto */*ws*/) { 88 | /* Check ws->getBufferedAmount() here */ 89 | }, 90 | .ping = [](auto */*ws*/, std::string_view) { 91 | /* Not implemented yet */ 92 | }, 93 | .pong = [](auto */*ws*/, std::string_view) { 94 | /* Not implemented yet */ 95 | }, 96 | .close = [](auto */*ws*/, int /*code*/, std::string_view /*message*/) { 97 | /* You may access ws->getUserData() here */ 98 | std::cout << "connection closed\n"; 99 | } 100 | }).connect(host_addr, [&](auto *, auto *) { 101 | printf("connect call\n"); 102 | // if (listen_socket) { 103 | // std::cout << "Listening on port " << port << std::endl; 104 | // } 105 | }).run(); 106 | } 107 | -------------------------------------------------------------------------------- /tools/bench_ws.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import subprocess 4 | import time 5 | import multiprocessing 6 | 7 | def get_work_dir_paths(): 8 | tools_dir_path = os.path.dirname(os.path.realpath(__file__)) 9 | root_dir_path = os.path.dirname(tools_dir_path) 10 | build_dir_path = os.path.join(root_dir_path, "build") 11 | return root_dir_path, build_dir_path 12 | 13 | 14 | def get_lib_names(lib): 15 | fws_list = ("flashws", "fws") 16 | uws_list = ("uwebsockets", "uws") 17 | tpp_list = ("websocketpp", "tpp") 18 | if lib in fws_list: 19 | return fws_list 20 | if lib in uws_list: 21 | return uws_list 22 | if lib in tpp_list: 23 | return tpp_list 24 | print("lib %s not valid" % lib) 25 | exit(-1) 26 | 27 | 28 | def get_server_or_client_exe_path(is_server, lib_name, build_dir_path): 29 | l_name, s_name = get_lib_names(lib_name) 30 | build_src_dir_path = os.path.join(build_dir_path, "src") 31 | build_dir = os.path.join(build_src_dir_path, l_name) 32 | file_name_self = "echo_server" if is_server else "echo_client" 33 | s_exe_path = os.path.join(build_dir, s_name + "_" + file_name_self) 34 | return s_exe_path 35 | 36 | 37 | def run_one_proc(cmd): 38 | subprocess.run(cmd) 39 | 40 | def test_echo(): 41 | least_arg_num = 9 42 | if len(sys.argv) < least_arg_num: 43 | print("Usage:\npython3 tools/bench_ws.py c|s (client or server) " 44 | "lib_name host_ip host_port" 45 | " process_cnt " 46 | " client_cnt_per_proc client_restart_cnt " 47 | # " min_msg_size=64 max_msg_size=2^21" 48 | " output_data_file [other arguments for dpdk]") 49 | return 50 | c_or_s = sys.argv[1] 51 | is_server = False 52 | if c_or_s == "c": 53 | is_server = False 54 | elif c_or_s == 's': 55 | is_server = True 56 | else: 57 | print("client or server should be c or s, but input: %s" % c_or_s) 58 | exit(-1) 59 | lib_name = sys.argv[2] 60 | host_ip = sys.argv[3] 61 | host_port = sys.argv[4] 62 | process_cnt = int(sys.argv[5]) 63 | print("Process cnt: %d" % process_cnt) 64 | 65 | c_num_per_proc = int(sys.argv[6]) 66 | c_restart_num = int(sys.argv[7]) 67 | 68 | # msg_cnt = sys.argv[5] 69 | output_filename = sys.argv[8] 70 | # min_msg_size = 64 71 | # max_msg_size = 1 << 21 72 | # msg_cnt = 100000 73 | total_msg_size = 1 << 33 74 | # if len(sys.argv) >= 7: 75 | # min_msg_size = int(sys.argv[6]) 76 | # if len(sys.argv) >= 8: 77 | # max_msg_size = int(sys.argv[7]) 78 | 79 | if not is_server: 80 | 81 | for i in range(process_cnt): 82 | temp_filename = output_filename + "_%d" % i 83 | with open(temp_filename, "w") as out_f: 84 | out_f.write("msg_size,avg_goodput,P0_latency,P50_latency,P99_latency," 85 | "P999_latency,P100_latency\n") 86 | 87 | root_dir_path, build_dir_path = get_work_dir_paths() 88 | exe_file_path = get_server_or_client_exe_path(is_server, lib_name=lib_name, 89 | build_dir_path=build_dir_path) 90 | print("will run %s" % exe_file_path) 91 | 92 | min_msg_size_log = 6 93 | # min_msg_size_log = 12 94 | # max_msg_size_log = 21 95 | max_msg_size_log = 13 96 | # if is_server: 97 | # msg_size = 1 << max_msg_size_log 98 | # arg_list = [exe_file_path, host_ip, host_port, str(msg_size)] 99 | # if len(sys.argv) > 7: 100 | # arg_list.extend(sys.argv[7:]) 101 | # subprocess.run(arg_list) 102 | # return 103 | msg_size_list = [] 104 | if is_server: 105 | msg_size_list.append(1 << max_msg_size_log) 106 | else: 107 | msg_size_list = [(1 << i) for i in range(min_msg_size_log, max_msg_size_log + 1)] 108 | 109 | print("msg size will be tested: ", msg_size_list) 110 | 111 | for msg_size in msg_size_list: 112 | test_msg_cnt = total_msg_size // max(1, (1 << 15) // msg_size) // msg_size 113 | 114 | arg_list_list = [] 115 | for i in range(process_cnt): 116 | arg_list = [] 117 | if is_server: 118 | arg_list = [exe_file_path, host_ip, host_port, str(msg_size), 119 | output_filename + ("_%d" % i)] 120 | else: 121 | arg_list = [exe_file_path, host_ip, host_port, str(msg_size), 122 | str(test_msg_cnt), # msg num per client 123 | str(c_num_per_proc), 124 | str(c_restart_num), 125 | output_filename + ("_%d" % i)] 126 | if len(sys.argv) > least_arg_num: 127 | arg_list.extend(sys.argv[least_arg_num:]) 128 | if i == 0: 129 | arg_list.extend(["--proc-type=primary"]) 130 | else: 131 | arg_list.extend(["--proc-type=secondary"]) 132 | arg_list.append("--proc-id=%d" % i) 133 | arg_list_list.append(arg_list) 134 | # with multiprocessing.Pool(process_cnt) as p: 135 | # p.map(run_one_proc, arg_list_list) 136 | process_list = [] 137 | # first_proc = subprocess.Popen(arg_list_list[0]) 138 | # time.sleep(0.1) 139 | # Maybe need to let the C++ program wait 140 | for i, arg_list in enumerate(arg_list_list): 141 | print("Will run: ", arg_list) 142 | proc = subprocess.Popen(arg_list) 143 | process_list.append(proc) 144 | if i == 0: 145 | time.sleep(2.0) 146 | # reversed to make sure primary proc exit at last 147 | for i, proc in enumerate(reversed(process_list)): 148 | proc.wait() 149 | # if i == 0: 150 | # time.sleep(8.0) 151 | # subprocess.run(arg_list) 152 | 153 | 154 | def main(): 155 | test_echo() 156 | 157 | 158 | if __name__ == "__main__": 159 | main() -------------------------------------------------------------------------------- /src/usockets-tcp/https_client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | const int SSL = 1; 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | //#define REAL_HOST "www.google.com" 9 | #define REAL_HOST "10.5.96.3" 10 | 11 | const char* host = REAL_HOST; 12 | const char request[] = "GET / HTTP/1.1\r\nHost: " REAL_HOST "\r\n\r\n"; 13 | 14 | //int port; 15 | //int connections; 16 | 17 | //int responses; 18 | 19 | struct ClientContext { 20 | int has_written_bytes{0}; 21 | int received_bytes{0}; 22 | }; 23 | 24 | /* We don't need any of these */ 25 | void on_wakeup(struct us_loop_t *loop) { 26 | 27 | } 28 | 29 | void on_pre(struct us_loop_t *loop) { 30 | 31 | } 32 | 33 | /* This is not HTTP POST, it is merely an event emitted post loop iteration */ 34 | void on_post(struct us_loop_t *loop) { 35 | 36 | } 37 | 38 | us_socket_t * try_request(us_socket_t *s) { 39 | ClientContext *client_context = (ClientContext *) us_socket_ext(SSL, s); 40 | int has_written = client_context->has_written_bytes; 41 | int target_written = sizeof(request) - 1; 42 | if (has_written < target_written) { 43 | int written = us_socket_write(SSL, s, request + has_written, target_written - has_written, 0); 44 | client_context->has_written_bytes += written; 45 | } 46 | printf("Write %d length request to server\n", client_context->has_written_bytes); 47 | return s; 48 | } 49 | 50 | struct us_socket_t *on_http_socket_writable(struct us_socket_t *s) { 51 | try_request(s); 52 | return s; 53 | } 54 | 55 | 56 | 57 | struct us_socket_t *on_http_socket_close(struct us_socket_t *s, int code, void *reason) { 58 | printf("HTTPS connection closed, code:%d\n", code); 59 | return s; 60 | } 61 | 62 | struct us_socket_t *on_http_socket_end(struct us_socket_t *s) { 63 | printf("HTTPS connection end\n"); 64 | return us_socket_close(SSL, s, 0, NULL); 65 | } 66 | 67 | struct us_socket_t *on_http_socket_data(struct us_socket_t *s, char *data, int length) { 68 | 69 | // us_socket_write(SSL, s, request, sizeof(request) - 1, 0); 70 | ClientContext *client_context = (ClientContext *) us_socket_ext(SSL, s); 71 | client_context->received_bytes += length; 72 | printf("Receive data from server, length: %d\n", length); 73 | if (length > 3) { 74 | printf("Char1: %c, Char2: %c, Char3: %c, Char4: %c\n", data[0], data[1], data[2], data[3]); 75 | } 76 | fwrite(data, 1, length, stdout); 77 | printf("\n"); 78 | // responses++; 79 | 80 | return s; 81 | } 82 | 83 | struct us_socket_t *on_http_socket_open(struct us_socket_t *s, int is_client, char *ip, int ip_length) { 84 | printf("Connect on open\n"); 85 | ClientContext *client_context = (ClientContext *) us_socket_ext(SSL, s); 86 | client_context->has_written_bytes = 0; 87 | client_context->received_bytes = 0; 88 | return try_request(s); 89 | /* Send a request */ 90 | // us_socket_write(SSL, s, request, sizeof(request) - 1, 0); 91 | 92 | // if (--connections) { 93 | // us_socket_context_connect(SSL, us_socket_context(SSL, s), host, port, NULL, 0, 0); 94 | // } else { 95 | // printf("Running benchmark now...\n"); 96 | 97 | // us_socket_timeout(SSL, s, LIBUS_TIMEOUT_GRANULARITY); 98 | // us_socket_long_timeout(SSL, s, 1); 99 | // } 100 | 101 | // return s; 102 | } 103 | 104 | struct us_socket_t *on_http_socket_long_timeout(struct us_socket_t *s) { 105 | /* Print current statistics */ 106 | printf("--- Minute mark ---\n"); 107 | 108 | us_socket_long_timeout(SSL, s, 1); 109 | 110 | return s; 111 | } 112 | 113 | struct us_socket_t *on_http_socket_timeout(struct us_socket_t *s) { 114 | /* Print current statistics */ 115 | // printf("Req/sec: %f\n", ((float)responses) / LIBUS_TIMEOUT_GRANULARITY); 116 | printf("On Timeout\n"); 117 | // responses = 0; 118 | us_socket_timeout(SSL, s, LIBUS_TIMEOUT_GRANULARITY); 119 | 120 | return s; 121 | } 122 | 123 | struct us_socket_t *on_http_socket_connect_error(struct us_socket_t *s, int code) { 124 | printf("Cannot connect to server\n"); 125 | 126 | return s; 127 | } 128 | 129 | int main(int argc, char **argv) { 130 | 131 | /* Parse host and port */ 132 | // if (argc != 4) { 133 | // printf("Usage: connections host port\n"); 134 | // return 0; 135 | // } 136 | // const int port = 443; 137 | const int port = 58600; 138 | // port = atoi(argv[3]); 139 | // host = (char*)malloc(strlen(argv[2]) + 1); 140 | // const char* host = "www.google.com"; 141 | 142 | // memcpy(host, argv[2], strlen(argv[2]) + 1); 143 | // connections = atoi(argv[1]); 144 | 145 | /* Create the event loop */ 146 | struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); 147 | 148 | /* Create a socket context for HTTP */ 149 | struct us_socket_context_options_t options = { 150 | // .ca_file_name = "./google.pem", 151 | // .ca_file_name = "/etc/ssl/certs/ca-certificates.crt", 152 | }; 153 | struct us_socket_context_t *client_context = us_create_socket_context(SSL, loop, sizeof(ClientContext), options); 154 | 155 | if (!client_context) { 156 | printf("Could not load SSL cert/key\n"); 157 | exit(0); 158 | } 159 | 160 | /* Set up event handlers */ 161 | us_socket_context_on_open(SSL, client_context, on_http_socket_open); 162 | us_socket_context_on_data(SSL, client_context, on_http_socket_data); 163 | us_socket_context_on_writable(SSL, client_context, on_http_socket_writable); 164 | us_socket_context_on_close(SSL, client_context, on_http_socket_close); 165 | us_socket_context_on_timeout(SSL, client_context, on_http_socket_timeout); 166 | us_socket_context_on_long_timeout(SSL, client_context, on_http_socket_long_timeout); 167 | us_socket_context_on_end(SSL, client_context, on_http_socket_end); 168 | us_socket_context_on_connect_error(SSL, client_context, on_http_socket_connect_error); 169 | 170 | /* Start making HTTP connections */ 171 | if (!us_socket_context_connect(SSL, client_context, host, port, NULL, 0, sizeof(ClientContext))) { 172 | printf("Cannot connect to server\n"); 173 | } 174 | 175 | us_loop_run(loop); 176 | 177 | us_socket_context_free(SSL, client_context); 178 | us_loop_free(loop); 179 | } 180 | -------------------------------------------------------------------------------- /configs/config_la1.ini: -------------------------------------------------------------------------------- 1 | [dpdk] 2 | # Hexadecimal bitmask of cores to run on. 3 | lcore_mask=8 4 | #lcore_mask=3f 5 | 6 | # Number of memory channels. 7 | channel=2 8 | 9 | # Specify base virtual address to map. 10 | #base_virtaddr=0x7f0000000000 11 | 12 | # Promiscuous mode of nic, defualt: enabled. 13 | promiscuous=1 14 | numa_on=1 15 | 16 | # TX checksum offload skip, default: disabled. 17 | # We need this switch enabled in the following cases: 18 | # -> The application want to enforce wrong checksum for testing purposes 19 | # -> Some cards advertize the offload capability. However, doesn't calculate checksum. 20 | tx_csum_offoad_skip=0 21 | 22 | # TCP segment offload, default: disabled. 23 | tso=1 24 | 25 | # HW vlan strip, default: enabled. 26 | vlan_strip=1 27 | 28 | # sleep when no pkts incomming 29 | # unit: microseconds 30 | idle_sleep=0 31 | 32 | # sent packet delay time(0-100) while send less than 32 pkts. 33 | # default 100 us. 34 | # if set 0, means send pkts immediately. 35 | # if set >100, will dealy 100 us. 36 | # unit: microseconds 37 | pkt_tx_delay=0 38 | 39 | # use symmetric Receive-side Scaling(RSS) key, default: disabled. 40 | symmetric_rss=0 41 | 42 | # PCI device enable list. 43 | # And driver options 44 | #pci_whitelist=02:00.0 45 | # for multiple PCI devices 46 | #pci_whitelist=02:00.0,03:00.0 47 | 48 | # enabled port list 49 | # 50 | # EBNF grammar: 51 | # 52 | # exp ::= num_list {"," num_list} 53 | # num_list ::= | 54 | # range ::= "-" 55 | # num ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' 56 | # 57 | # examples 58 | # 0-3 ports 0, 1,2,3 are enabled 59 | # 1-3,4,7 ports 1,2,3,4,7 are enabled 60 | # 61 | # If use bonding, shoule config the bonding port id in port_list 62 | # and not config slave port id in port_list 63 | # such as, port 0 and port 1 trank to a bonding port 2, 64 | # should set `port_list=2` and config `[port2]` section 65 | 66 | port_list=0 67 | 68 | # Number of vdev. 69 | nb_vdev=0 70 | 71 | # Number of bond. 72 | nb_bond=0 73 | 74 | # log level for dpdk, optional 75 | # log_level=0 76 | 77 | # Each core write into own pcap file, which is open one time, close one time if enough. 78 | # Support dump the first snaplen bytes of each packet. 79 | # if pcap file is lager than savelen bytes, it will be closed and next file was dumped into. 80 | [pcap] 81 | enable=0 82 | snaplen=256 83 | savelen=33554432 84 | savepath=. 85 | 86 | # Port config section 87 | # Correspond to dpdk.port_list's index: port0, port1... 88 | [port0] 89 | addr=10.5.96.3 90 | netmask=255.255.255.0 91 | broadcast=10.5.111.255 92 | gateway=10.5.96.1 93 | # set interface name, Optional parameter. 94 | #if_name=eno7 95 | 96 | # IPv6 net addr, Optional parameters. 97 | #addr6=ff::02 98 | #prefix_len=64 99 | #gateway6=ff::01 100 | 101 | # Multi virtual IPv4/IPv6 net addr, Optional parameters. 102 | # `vip_ifname`: default `f-stack-x` 103 | # `vip_addr`: Separated by semicolons, MAX number 64; 104 | # Only support netmask 255.255.255.255, broadcast x.x.x.255 now, hard code in `ff_veth_setvaddr`. 105 | # `vip_addr6`: Separated by semicolons, MAX number 64. 106 | # `vip_prefix_len`: All addr6 use the same prefix now, default 64. 107 | #vip_ifname=lo0 108 | #vip_addr=192.168.1.3;192.168.1.4;192.168.1.5;192.168.1.6 109 | #vip_addr6=ff::03;ff::04;ff::05;ff::06;ff::07 110 | #vip_prefix_len=64 111 | 112 | # lcore list used to handle this port 113 | # the format is same as port_list 114 | #lcore_list=0 115 | 116 | # bonding slave port list used to handle this port 117 | # need to config while this port is a bonding port 118 | # the format is same as port_list 119 | #slave_port_list=0,1 120 | 121 | # Vdev config section 122 | # orrespond to dpdk.nb_vdev's index: vdev0, vdev1... 123 | # iface : Shouldn't set always. 124 | # path : The vuser device path in container. Required. 125 | # queues : The max queues of vuser. Optional, default 1, greater or equal to the number of processes. 126 | # queue_size : Queue size.Optional, default 256. 127 | # mac : The mac address of vuser. Optional, default random, if vhost use phy NIC, it should be set to the phy NIC's mac. 128 | # cq : Optional, if queues = 1, default 0; if queues > 1 default 1. 129 | #[vdev0] 130 | ##iface=/usr/local/var/run/openvswitch/vhost-user0 131 | #path=/var/run/openvswitch/vhost-user0 132 | #queues=1 133 | #queue_size=256 134 | #mac=00:00:00:00:00:01 135 | #cq=0 136 | 137 | # bond config section 138 | # See http://doc.dpdk.org/guides/prog_guide/link_bonding_poll_mode_drv_lib.html 139 | #[bond0] 140 | #mode=4 141 | #slave=0000:0a:00.0,slave=0000:0a:00.1 142 | #primary=0000:0a:00.0 143 | #mac=f0:98:38:xx:xx:xx 144 | ## opt argument 145 | #socket_id=0 146 | #xmit_policy=l23 147 | #lsc_poll_period_ms=100 148 | #up_delay=10 149 | #down_delay=50 150 | 151 | # Kni config: if enabled and method=reject, 152 | # all packets that do not belong to the following tcp_port and udp_port 153 | # will transmit to kernel; if method=accept, all packets that belong to 154 | # the following tcp_port and udp_port will transmit to kernel. 155 | #[kni] 156 | #enable=1 157 | #method=reject 158 | # The format is same as port_list 159 | #tcp_port=80,443 160 | #udp_port=53 161 | 162 | # FreeBSD network performance tuning configurations. 163 | # Most native FreeBSD configurations are supported. 164 | [freebsd.boot] 165 | # If use rack/bbr which depend HPTS, you should set a greater value of hz, such as 100000 means a tick is 10us. 166 | hz=100 167 | 168 | # Block out a range of descriptors to avoid overlap 169 | # with the kernel's descriptor space. 170 | # You can increase this value according to your app. 171 | fd_reserve=1024 172 | 173 | kern.ipc.maxsockets=262144 174 | 175 | net.inet.tcp.syncache.hashsize=4096 176 | net.inet.tcp.syncache.bucketlimit=100 177 | 178 | net.inet.tcp.tcbhashsize=65536 179 | 180 | kern.ncallout=262144 181 | 182 | kern.features.inet6=1 183 | net.inet6.ip6.auto_linklocal=1 184 | net.inet6.ip6.accept_rtadv=2 185 | net.inet6.icmp6.rediraccept=1 186 | net.inet6.ip6.forwarding=0 187 | 188 | [freebsd.sysctl] 189 | kern.ipc.somaxconn=32768 190 | kern.ipc.maxsockbuf=16777216 191 | 192 | net.link.ether.inet.maxhold=5 193 | 194 | net.inet.tcp.fast_finwait2_recycle=1 195 | net.inet.tcp.sendspace=16384 196 | net.inet.tcp.recvspace=8192 197 | #net.inet.tcp.nolocaltimewait=1 198 | net.inet.tcp.cc.algorithm=cubic 199 | net.inet.tcp.sendbuf_max=16777216 200 | net.inet.tcp.recvbuf_max=16777216 201 | net.inet.tcp.sendbuf_auto=1 202 | net.inet.tcp.recvbuf_auto=1 203 | net.inet.tcp.sendbuf_inc=16384 204 | #net.inet.tcp.recvbuf_inc=524288 205 | net.inet.tcp.sack.enable=1 206 | net.inet.tcp.blackhole=1 207 | net.inet.tcp.msl=2000 208 | net.inet.tcp.delayed_ack=1 209 | net.inet.tcp.rfc1323=1 210 | 211 | net.inet.udp.blackhole=1 212 | net.inet.ip.redirect=0 213 | net.inet.ip.forwarding=0 214 | 215 | # set default stacks:freebsd, rack or bbr, may be you need increase the value of parameter 'freebsd.boot.hz' while use rack or bbr. 216 | net.inet.tcp.functions_default=freebsd 217 | # need by bbr, should enable it. 218 | net.inet.tcp.hpts.skip_swi=1 219 | -------------------------------------------------------------------------------- /configs/config_la6.ini: -------------------------------------------------------------------------------- 1 | [dpdk] 2 | # Hexadecimal bitmask of cores to run on. 3 | lcore_mask=10 4 | #lcore_mask=30 5 | #lcore_mask=3f 6 | #lcore_mask=fff 7 | 8 | # Number of memory channels. 9 | channel=2 10 | 11 | # Specify base virtual address to map. 12 | #base_virtaddr=0x7f0000000000 13 | 14 | # Promiscuous mode of nic, defualt: enabled. 15 | promiscuous=1 16 | numa_on=1 17 | 18 | # TX checksum offload skip, default: disabled. 19 | # We need this switch enabled in the following cases: 20 | # -> The application want to enforce wrong checksum for testing purposes 21 | # -> Some cards advertize the offload capability. However, doesn't calculate checksum. 22 | tx_csum_offoad_skip=0 23 | 24 | # TCP segment offload, default: disabled. 25 | tso=1 26 | 27 | # HW vlan strip, default: enabled. 28 | vlan_strip=1 29 | 30 | # sleep when no pkts incomming 31 | # unit: microseconds 32 | idle_sleep=0 33 | 34 | # sent packet delay time(0-100) while send less than 32 pkts. 35 | # default 100 us. 36 | # if set 0, means send pkts immediately. 37 | # if set >100, will dealy 100 us. 38 | # unit: microseconds 39 | pkt_tx_delay=0 40 | 41 | # use symmetric Receive-side Scaling(RSS) key, default: disabled. 42 | symmetric_rss=0 43 | 44 | # PCI device enable list. 45 | # And driver options 46 | #pci_whitelist=02:00.0 47 | # for multiple PCI devices 48 | #pci_whitelist=02:00.0,03:00.0 49 | 50 | # enabled port list 51 | # 52 | # EBNF grammar: 53 | # 54 | # exp ::= num_list {"," num_list} 55 | # num_list ::= | 56 | # range ::= "-" 57 | # num ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' 58 | # 59 | # examples 60 | # 0-3 ports 0, 1,2,3 are enabled 61 | # 1-3,4,7 ports 1,2,3,4,7 are enabled 62 | # 63 | # If use bonding, shoule config the bonding port id in port_list 64 | # and not config slave port id in port_list 65 | # such as, port 0 and port 1 trank to a bonding port 2, 66 | # should set `port_list=2` and config `[port2]` section 67 | 68 | port_list=0 69 | 70 | # Number of vdev. 71 | nb_vdev=0 72 | 73 | # Number of bond. 74 | nb_bond=0 75 | 76 | # log level for dpdk, optional 77 | # log_level=0 78 | 79 | # Each core write into own pcap file, which is open one time, close one time if enough. 80 | # Support dump the first snaplen bytes of each packet. 81 | # if pcap file is lager than savelen bytes, it will be closed and next file was dumped into. 82 | [pcap] 83 | enable=0 84 | snaplen=256 85 | savelen=33554432 86 | savepath=. 87 | 88 | # Port config section 89 | # Correspond to dpdk.port_list's index: port0, port1... 90 | [port0] 91 | addr=10.5.96.7 92 | netmask=255.255.255.0 93 | broadcast=10.5.111.255 94 | gateway=10.5.96.1 95 | # set interface name, Optional parameter. 96 | #if_name=eno7 97 | 98 | # IPv6 net addr, Optional parameters. 99 | #addr6=ff::02 100 | #prefix_len=64 101 | #gateway6=ff::01 102 | 103 | # Multi virtual IPv4/IPv6 net addr, Optional parameters. 104 | # `vip_ifname`: default `f-stack-x` 105 | # `vip_addr`: Separated by semicolons, MAX number 64; 106 | # Only support netmask 255.255.255.255, broadcast x.x.x.255 now, hard code in `ff_veth_setvaddr`. 107 | # `vip_addr6`: Separated by semicolons, MAX number 64. 108 | # `vip_prefix_len`: All addr6 use the same prefix now, default 64. 109 | #vip_ifname=lo0 110 | #vip_addr=192.168.1.3;192.168.1.4;192.168.1.5;192.168.1.6 111 | #vip_addr6=ff::03;ff::04;ff::05;ff::06;ff::07 112 | #vip_prefix_len=64 113 | 114 | # lcore list used to handle this port 115 | # the format is same as port_list 116 | #lcore_list=0 117 | 118 | # bonding slave port list used to handle this port 119 | # need to config while this port is a bonding port 120 | # the format is same as port_list 121 | #slave_port_list=0,1 122 | 123 | # Vdev config section 124 | # orrespond to dpdk.nb_vdev's index: vdev0, vdev1... 125 | # iface : Shouldn't set always. 126 | # path : The vuser device path in container. Required. 127 | # queues : The max queues of vuser. Optional, default 1, greater or equal to the number of processes. 128 | # queue_size : Queue size.Optional, default 256. 129 | # mac : The mac address of vuser. Optional, default random, if vhost use phy NIC, it should be set to the phy NIC's mac. 130 | # cq : Optional, if queues = 1, default 0; if queues > 1 default 1. 131 | #[vdev0] 132 | ##iface=/usr/local/var/run/openvswitch/vhost-user0 133 | #path=/var/run/openvswitch/vhost-user0 134 | #queues=1 135 | #queue_size=256 136 | #mac=00:00:00:00:00:01 137 | #cq=0 138 | 139 | # bond config section 140 | # See http://doc.dpdk.org/guides/prog_guide/link_bonding_poll_mode_drv_lib.html 141 | #[bond0] 142 | #mode=4 143 | #slave=0000:0a:00.0,slave=0000:0a:00.1 144 | #primary=0000:0a:00.0 145 | #mac=f0:98:38:xx:xx:xx 146 | ## opt argument 147 | #socket_id=0 148 | #xmit_policy=l23 149 | #lsc_poll_period_ms=100 150 | #up_delay=10 151 | #down_delay=50 152 | 153 | # Kni config: if enabled and method=reject, 154 | # all packets that do not belong to the following tcp_port and udp_port 155 | # will transmit to kernel; if method=accept, all packets that belong to 156 | # the following tcp_port and udp_port will transmit to kernel. 157 | #[kni] 158 | #enable=1 159 | #method=reject 160 | # The format is same as port_list 161 | #tcp_port=80,443 162 | #udp_port=53 163 | 164 | # FreeBSD network performance tuning configurations. 165 | # Most native FreeBSD configurations are supported. 166 | [freebsd.boot] 167 | # If use rack/bbr which depend HPTS, you should set a greater value of hz, such as 100000 means a tick is 10us. 168 | hz=100 169 | 170 | # Block out a range of descriptors to avoid overlap 171 | # with the kernel's descriptor space. 172 | # You can increase this value according to your app. 173 | fd_reserve=1024 174 | 175 | kern.ipc.maxsockets=262144 176 | 177 | net.inet.tcp.syncache.hashsize=4096 178 | net.inet.tcp.syncache.bucketlimit=100 179 | 180 | net.inet.tcp.tcbhashsize=65536 181 | 182 | kern.ncallout=262144 183 | 184 | kern.features.inet6=1 185 | net.inet6.ip6.auto_linklocal=1 186 | net.inet6.ip6.accept_rtadv=2 187 | net.inet6.icmp6.rediraccept=1 188 | net.inet6.ip6.forwarding=0 189 | 190 | [freebsd.sysctl] 191 | kern.ipc.somaxconn=32768 192 | kern.ipc.maxsockbuf=16777216 193 | 194 | net.link.ether.inet.maxhold=5 195 | 196 | net.inet.tcp.fast_finwait2_recycle=1 197 | net.inet.tcp.sendspace=16384 198 | net.inet.tcp.recvspace=8192 199 | #net.inet.tcp.nolocaltimewait=1 200 | net.inet.tcp.cc.algorithm=cubic 201 | net.inet.tcp.sendbuf_max=16777216 202 | net.inet.tcp.recvbuf_max=16777216 203 | net.inet.tcp.sendbuf_auto=1 204 | net.inet.tcp.recvbuf_auto=1 205 | net.inet.tcp.sendbuf_inc=16384 206 | #net.inet.tcp.recvbuf_inc=524288 207 | net.inet.tcp.sack.enable=1 208 | net.inet.tcp.blackhole=1 209 | net.inet.tcp.msl=2000 210 | net.inet.tcp.delayed_ack=1 211 | net.inet.tcp.rfc1323=1 212 | 213 | net.inet.udp.blackhole=1 214 | net.inet.ip.redirect=0 215 | net.inet.ip.forwarding=0 216 | 217 | # set default stacks:freebsd, rack or bbr, may be you need increase the value of parameter 'freebsd.boot.hz' while use rack or bbr. 218 | net.inet.tcp.functions_default=freebsd 219 | # need by bbr, should enable it. 220 | net.inet.tcp.hpts.skip_swi=1 221 | -------------------------------------------------------------------------------- /src/websocketpp/echo_server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | 8 | 9 | uint64_t last_interval_send_bytes = 0; 10 | uint64_t last_interval_recv_bytes = 0; 11 | uint64_t last_interval_recv_msg_cnt = 0; 12 | uint64_t last_interval_send_msg_cnt = 0; 13 | size_t last_msg_size = 0; 14 | int64_t interval_start_ns = 0; 15 | int64_t active_connections = 0; 16 | 17 | size_t MAX_DATA_LEN = 0; 18 | FILE* output_fp = nullptr; 19 | 20 | void count_stats(){ 21 | if (((last_interval_recv_msg_cnt & 0xfffffUL) 22 | && (last_interval_recv_bytes <= MAX_DATA_LEN * 4096UL) 23 | ) || last_interval_recv_msg_cnt == 0) { 24 | return; 25 | } 26 | int64_t now_ns = std::chrono::duration_cast( 27 | std::chrono::high_resolution_clock::now().time_since_epoch()).count(); 28 | int64_t interval_ns = now_ns - interval_start_ns; 29 | constexpr int64_t BITS_PER_BYTE = 8; 30 | double interval_sec = double(interval_ns) / (1e+9); 31 | double recv_throughput_mbps = double((last_interval_recv_bytes) * 32 | BITS_PER_BYTE) / (1e+6) / interval_sec; 33 | double send_throughput_mbps = double((last_interval_send_bytes) * 34 | BITS_PER_BYTE) / (1e+6) / interval_sec; 35 | 36 | double recv_mm_msg_per_sec = double(last_interval_recv_msg_cnt) 37 | / (1e+6) / interval_sec; 38 | double send_mm_msg_per_sec = double(last_interval_send_msg_cnt) 39 | / (1e+6) / interval_sec; 40 | // if (last_interval_send_msg_cnt != last_interval_recv_msg_cnt) { 41 | // printf("Warning, last interval recv: %zu msg, send %zu msg\n", 42 | // last_interval_recv_msg_cnt, last_interval_send_msg_cnt); 43 | // } 44 | printf("last_msg_size: %zu, avg rx+tx goodput: %.2lf Mbps, %.4lf 10^6 msg/sec," 45 | " active conn: %ld\n", 46 | last_msg_size, 47 | recv_throughput_mbps + send_throughput_mbps, 48 | recv_mm_msg_per_sec + send_mm_msg_per_sec, 49 | active_connections); 50 | if (output_fp != nullptr) { 51 | // msg_size, rx goodput, tx goodput, rx mm mps, tx mm mps, conn cnt 52 | fprintf(output_fp, "%zu,%.3lf,%.3lf,%lf,%lf,%zu\n", 53 | last_msg_size, 54 | recv_throughput_mbps, 55 | send_throughput_mbps, 56 | recv_mm_msg_per_sec, 57 | send_mm_msg_per_sec, 58 | active_connections 59 | ); 60 | fflush(output_fp); 61 | } 62 | 63 | last_interval_recv_bytes = 0; 64 | last_interval_send_bytes = 0; 65 | last_interval_recv_msg_cnt = 0; 66 | last_interval_send_msg_cnt = 0; 67 | interval_start_ns = std::chrono::duration_cast( 68 | std::chrono::high_resolution_clock::now().time_since_epoch()).count(); 69 | 70 | }; 71 | 72 | 73 | 74 | 75 | 76 | typedef websocketpp::server server; 77 | 78 | using websocketpp::lib::placeholders::_1; 79 | using websocketpp::lib::placeholders::_2; 80 | using websocketpp::lib::bind; 81 | 82 | // pull out the type of messages sent by our config 83 | typedef server::message_ptr message_ptr; 84 | 85 | void on_open(websocketpp::connection_hdl /*hdl*/) { 86 | ++active_connections; 87 | } 88 | 89 | void on_close(websocketpp::connection_hdl /*hdl*/) { 90 | --active_connections; 91 | } 92 | 93 | // Define a callback to handle incoming messages 94 | void on_message(server* s, websocketpp::connection_hdl hdl, message_ptr msg) { 95 | // std::cout << "on_message called with hdl: " << hdl.lock().get() 96 | // << " and message: " << msg->get_payload() 97 | // << std::endl; 98 | 99 | // check for a special command to instruct the server to stop listening so 100 | // it can be cleanly exited. 101 | // if (msg->get_payload() == "stop-listening") { 102 | // s->stop_listening(); 103 | // return; 104 | // } 105 | size_t msg_size = msg->get_payload().size(); 106 | if (interval_start_ns == 0) { 107 | interval_start_ns = std::chrono::duration_cast( 108 | std::chrono::high_resolution_clock::now().time_since_epoch()).count(); 109 | } 110 | last_interval_recv_bytes += msg_size; 111 | ++last_interval_recv_msg_cnt; 112 | 113 | 114 | try { 115 | s->send(hdl, msg->get_payload(), msg->get_opcode()); 116 | // please note that send does not mean all sent to the wire 117 | last_msg_size = msg_size; 118 | last_interval_send_bytes += msg_size; 119 | ++last_interval_send_msg_cnt; 120 | count_stats(); 121 | } catch (websocketpp::exception const & e) { 122 | std::cout << "Echo failed because: " 123 | << "(" << e.what() << ")" << std::endl; 124 | } 125 | } 126 | 127 | int main(int argc, const char** argv) { 128 | 129 | 130 | if (argc < 5) { 131 | printf("Invalid parameters!\nUsage: ./echo_server ip_address port max_msg_len export_filename\n"); 132 | return -1; 133 | } 134 | 135 | const char* SERVER_IP = argv[1]; 136 | 137 | int SERVER_PORT = atoi(argv[2]); 138 | if (SERVER_PORT <= 0) { 139 | printf("Invalid port: %s\n", argv[2]); 140 | return -1; 141 | } 142 | long long max_msg_len = atoll(argv[3]); 143 | if (max_msg_len <= 0) { 144 | printf("invalid max_msg_len: %s\n", argv[3]); 145 | return -1; 146 | } 147 | MAX_DATA_LEN = size_t(max_msg_len); 148 | 149 | const char* export_file_path = argv[4]; 150 | output_fp = fopen(export_file_path, "w"); 151 | if (output_fp == nullptr) { 152 | printf("Cannot create file at %s\n", export_file_path); 153 | return -1; 154 | } 155 | printf("Will output to %s\n", export_file_path); 156 | // msg_size, rx goodput, tx goodput, rx mm mps, tx mm mps 157 | fprintf(output_fp, "msg_size,rx_goodput,tx_goodput,rx_mm_mps,tx_mm_mps,connection_cnt\n"); 158 | 159 | 160 | 161 | printf("Prepare to run\n"); 162 | // Create a server endpoint 163 | server echo_server; 164 | 165 | try { 166 | // Set logging settings 167 | 168 | echo_server.clear_access_channels(websocketpp::log::alevel::all); 169 | // echo_server.set_access_channels(websocketpp::log::alevel::fail); 170 | 171 | // Initialize Asio 172 | echo_server.init_asio(); 173 | echo_server.set_reuse_addr(true); 174 | 175 | // Register our message handler 176 | echo_server.set_message_handler(bind(&on_message,&echo_server,::_1,::_2)); 177 | // echo_server.set_message_handler(on_message); 178 | 179 | echo_server.set_open_handler(on_open); 180 | echo_server.set_close_handler(on_close); 181 | 182 | // Listen on port 9002 183 | echo_server.listen(std::string(SERVER_IP), std::to_string(SERVER_PORT)); 184 | 185 | // Start the server accept loop 186 | echo_server.start_accept(); 187 | 188 | // Start the ASIO io_service run loop 189 | echo_server.run(); 190 | } catch (websocketpp::exception const & e) { 191 | std::cout << e.what() << std::endl; 192 | } catch (...) { 193 | std::cout << "other exception" << std::endl; 194 | } 195 | 196 | return 0; 197 | } -------------------------------------------------------------------------------- /src/uwebsockets/echo_server.cpp: -------------------------------------------------------------------------------- 1 | // This file is modified from uwebsockets example 2 | /* We simply call the root header file "App.h", giving you uWS::App and uWS::SSLApp */ 3 | #include "App.h" 4 | #include "test_def.h" 5 | 6 | /* This is a simple WebSocket echo server example. 7 | * You may compile it with "WITH_OPENSSL=1 make" or with "make" */ 8 | 9 | int main(int argc, const char** argv) { 10 | 11 | if (argc < 5) { 12 | printf("Invalid parameters!\nUsage: ./echo_server ip_address port max_msg_len export_filename\n"); 13 | return -1; 14 | } 15 | 16 | const char* SERVER_IP = argv[1]; 17 | 18 | int SERVER_PORT = atoi(argv[2]); 19 | if (SERVER_PORT <= 0) { 20 | printf("Invalid port: %s\n", argv[2]); 21 | return -1; 22 | } 23 | long long max_msg_len = atoll(argv[3]); 24 | if (max_msg_len <= 0) { 25 | printf("invalid max_msg_len: %s\n", argv[3]); 26 | return -1; 27 | } 28 | size_t MAX_DATA_LEN = size_t(max_msg_len); 29 | // size_t MAX_DATA_LEN = test::MAX_DATA_LEN; 30 | 31 | const char* export_file_path = argv[4]; 32 | FILE *output_fp = fopen(export_file_path, "w"); 33 | if (output_fp == nullptr) { 34 | printf("Cannot create file at %s\n", export_file_path); 35 | return -1; 36 | } 37 | printf("Will output to %s\n", export_file_path); 38 | // msg_size, rx goodput, tx goodput, rx mm mps, tx mm mps 39 | fprintf(output_fp, "msg_size,rx_goodput,tx_goodput,rx_mm_mps,tx_mm_mps,connection_cnt\n"); 40 | 41 | uint64_t last_interval_send_bytes = 0; 42 | uint64_t last_interval_recv_bytes = 0; 43 | uint64_t last_interval_recv_msg_cnt = 0; 44 | uint64_t last_interval_send_msg_cnt = 0; 45 | size_t last_msg_size = 0; 46 | int64_t interval_start_ns = 0; 47 | int64_t active_connections = 0; 48 | 49 | /* ws->getUserData returns one of these */ 50 | struct PerSocketData { 51 | PerSocketData() {}; 52 | PerSocketData(int x) {} 53 | /* Fill with user data */ 54 | }; 55 | 56 | auto count_stats = [&](){ 57 | if (((last_interval_recv_msg_cnt & 0xffffUL) 58 | // && (last_interval_recv_bytes <= MAX_DATA_LEN * 4096UL) 59 | ) || last_interval_recv_msg_cnt == 0) { 60 | return; 61 | } 62 | int64_t now_ns = std::chrono::duration_cast( 63 | std::chrono::high_resolution_clock::now().time_since_epoch()).count(); 64 | int64_t interval_ns = now_ns - interval_start_ns; 65 | constexpr int64_t BITS_PER_BYTE = 8; 66 | double interval_sec = double(interval_ns) / (1e+9); 67 | double recv_throughput_mbps = double((last_interval_recv_bytes) * 68 | BITS_PER_BYTE) / (1e+6) / interval_sec; 69 | double send_throughput_mbps = double((last_interval_send_bytes) * 70 | BITS_PER_BYTE) / (1e+6) / interval_sec; 71 | 72 | double recv_mm_msg_per_sec = double(last_interval_recv_msg_cnt) 73 | / (1e+6) / interval_sec; 74 | double send_mm_msg_per_sec = double(last_interval_send_msg_cnt) 75 | / (1e+6) / interval_sec; 76 | // if (last_interval_send_msg_cnt != last_interval_recv_msg_cnt) { 77 | // printf("Warning, last interval recv: %zu msg, send %zu msg\n", 78 | // last_interval_recv_msg_cnt, last_interval_send_msg_cnt); 79 | // } 80 | printf("last_msg_size: %zu, avg rx+tx goodput: %.2lf Mbps, %.4lf 10^6 msg/sec," 81 | " active conn: %ld\n", 82 | last_msg_size, 83 | recv_throughput_mbps + send_throughput_mbps, 84 | recv_mm_msg_per_sec + send_mm_msg_per_sec, 85 | active_connections); 86 | if (output_fp != nullptr) { 87 | // msg_size, rx goodput, tx goodput, rx mm mps, tx mm mps, conn cnt 88 | fprintf(output_fp, "%zu,%.3lf,%.3lf,%lf,%lf,%zu\n", 89 | last_msg_size, 90 | recv_throughput_mbps, 91 | send_throughput_mbps, 92 | recv_mm_msg_per_sec, 93 | send_mm_msg_per_sec, 94 | active_connections 95 | ); 96 | fflush(output_fp); 97 | } 98 | 99 | last_interval_recv_bytes = 0; 100 | last_interval_send_bytes = 0; 101 | last_interval_recv_msg_cnt = 0; 102 | last_interval_send_msg_cnt = 0; 103 | interval_start_ns = std::chrono::duration_cast( 104 | std::chrono::high_resolution_clock::now().time_since_epoch()).count(); 105 | 106 | }; 107 | 108 | printf("Prepare to run\n"); 109 | 110 | /* Keep in mind that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support. 111 | * You may swap to using uWS:App() if you don't need SSL */ 112 | uWS::SSLApp( 113 | { 114 | .key_file_name = test::key_file_path, 115 | .cert_file_name = test::cert_file_path, 116 | 117 | /* There are example certificates in uWebSockets.js repo */ 118 | // .key_file_name = "misc/key.pem", 119 | // .cert_file_name = "misc/cert.pem", 120 | // .passphrase = "1234" 121 | } 122 | ).ws("/", { 123 | /* Settings */ 124 | // .compression = uWS::CompressOptions(uWS::DEDICATED_COMPRESSOR_4KB | uWS::DEDICATED_DECOMPRESSOR), 125 | .compression = uWS::CompressOptions(uWS::DISABLED), 126 | .maxPayloadLength = (uint32_t)MAX_DATA_LEN, 127 | .idleTimeout = 16, 128 | .maxBackpressure = 100 * 1024 * 1024, 129 | .closeOnBackpressureLimit = false, 130 | .resetIdleTimeoutOnSend = false, 131 | .sendPingsAutomatically = true, 132 | /* Handlers */ 133 | .upgrade = nullptr, 134 | .open = [&](auto */*ws*/) { 135 | ++active_connections; 136 | /* Open event here, you may access ws->getUserData() which points to a PerSocketData struct */ 137 | // std::cout << "connection opened\n"; 138 | }, 139 | .message = [&](auto *ws, std::string_view message, uWS::OpCode opCode) { 140 | size_t msg_size = message.size(); 141 | if (interval_start_ns == 0) { 142 | interval_start_ns = std::chrono::duration_cast( 143 | std::chrono::high_resolution_clock::now().time_since_epoch()).count(); 144 | } 145 | last_interval_recv_bytes += msg_size; 146 | ++last_interval_recv_msg_cnt; 147 | // printf("recv msg size: %zu\n", msg_size); 148 | ws->send(message, opCode, true); 149 | // print("send msg size: %zu\n", msg_size); 150 | // please note that send does not mean all sent to the wire 151 | last_msg_size = msg_size; 152 | last_interval_send_bytes += msg_size; 153 | ++last_interval_send_msg_cnt; 154 | count_stats(); 155 | }, 156 | .drain = [](auto */*ws*/) { 157 | /* Check ws->getBufferedAmount() here */ 158 | }, 159 | .ping = [](auto */*ws*/, std::string_view) { 160 | /* Not implemented yet */ 161 | }, 162 | .pong = [](auto */*ws*/, std::string_view) { 163 | /* Not implemented yet */ 164 | }, 165 | .close = [&](auto */*ws*/, int /*code*/, std::string_view /*message*/) { 166 | --active_connections; 167 | /* You may access ws->getUserData() here */ 168 | // std::cout << "connection closed\n"; 169 | } 170 | }).listen(SERVER_IP, SERVER_PORT, [&](auto *listen_socket) { 171 | if (listen_socket) { 172 | std::cout << "Listening on port " << SERVER_PORT << std::endl; 173 | 174 | } 175 | }).run(); 176 | } 177 | -------------------------------------------------------------------------------- /src/boost-beast/echo_server.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | // 7 | // Official repository: https://github.com/boostorg/beast 8 | // 9 | 10 | //------------------------------------------------------------------------------ 11 | // 12 | // Example: WebSocket server, asynchronous 13 | // 14 | //------------------------------------------------------------------------------ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | namespace beast = boost::beast; // from 30 | namespace http = beast::http; // from 31 | namespace websocket = beast::websocket; // from 32 | namespace net = boost::asio; // from 33 | using tcp = boost::asio::ip::tcp; // from 34 | 35 | //------------------------------------------------------------------------------ 36 | 37 | // Report a failure 38 | void 39 | fail(beast::error_code ec, char const* what) 40 | { 41 | std::cerr << what << ": " << ec.message() << "\n"; 42 | } 43 | 44 | // Echoes back all received WebSocket messages 45 | class session : public std::enable_shared_from_this 46 | { 47 | websocket::stream ws_; 48 | beast::flat_buffer buffer_; 49 | 50 | public: 51 | // Take ownership of the socket 52 | explicit 53 | session(tcp::socket&& socket) 54 | : ws_(std::move(socket)) 55 | { 56 | } 57 | 58 | // Get on the correct executor 59 | void 60 | run() 61 | { 62 | // We need to be executing within a strand to perform async operations 63 | // on the I/O objects in this session. Although not strictly necessary 64 | // for single-threaded contexts, this example code is written to be 65 | // thread-safe by default. 66 | net::dispatch(ws_.get_executor(), 67 | beast::bind_front_handler( 68 | &session::on_run, 69 | shared_from_this())); 70 | } 71 | 72 | // Start the asynchronous operation 73 | void 74 | on_run() 75 | { 76 | // Set suggested timeout settings for the websocket 77 | ws_.set_option( 78 | websocket::stream_base::timeout::suggested( 79 | beast::role_type::server)); 80 | 81 | // Set a decorator to change the Server of the handshake 82 | ws_.set_option(websocket::stream_base::decorator( 83 | [](websocket::response_type& res) 84 | { 85 | res.set(http::field::server, 86 | std::string(BOOST_BEAST_VERSION_STRING) + 87 | " websocket-server-async"); 88 | })); 89 | // Accept the websocket handshake 90 | ws_.async_accept( 91 | beast::bind_front_handler( 92 | &session::on_accept, 93 | shared_from_this())); 94 | } 95 | 96 | void 97 | on_accept(beast::error_code ec) 98 | { 99 | if(ec) 100 | return fail(ec, "accept"); 101 | 102 | // Read a message 103 | do_read(); 104 | } 105 | 106 | void 107 | do_read() 108 | { 109 | // Read a message into our buffer 110 | ws_.async_read( 111 | buffer_, 112 | beast::bind_front_handler( 113 | &session::on_read, 114 | shared_from_this())); 115 | } 116 | 117 | void 118 | on_read( 119 | beast::error_code ec, 120 | std::size_t bytes_transferred) 121 | { 122 | boost::ignore_unused(bytes_transferred); 123 | 124 | // This indicates that the session was closed 125 | if(ec == websocket::error::closed) 126 | return; 127 | 128 | if(ec) 129 | return fail(ec, "read"); 130 | 131 | // Echo the message 132 | ws_.text(ws_.got_text()); 133 | ws_.async_write( 134 | buffer_.data(), 135 | beast::bind_front_handler( 136 | &session::on_write, 137 | shared_from_this())); 138 | } 139 | 140 | void 141 | on_write( 142 | beast::error_code ec, 143 | std::size_t bytes_transferred) 144 | { 145 | boost::ignore_unused(bytes_transferred); 146 | 147 | if(ec) 148 | return fail(ec, "write"); 149 | 150 | // Clear the buffer 151 | buffer_.consume(buffer_.size()); 152 | 153 | // Do another read 154 | do_read(); 155 | } 156 | }; 157 | 158 | //------------------------------------------------------------------------------ 159 | 160 | // Accepts incoming connections and launches the sessions 161 | class listener : public std::enable_shared_from_this 162 | { 163 | net::io_context& ioc_; 164 | tcp::acceptor acceptor_; 165 | 166 | public: 167 | listener( 168 | net::io_context& ioc, 169 | tcp::endpoint endpoint) 170 | : ioc_(ioc) 171 | , acceptor_(ioc) 172 | { 173 | beast::error_code ec; 174 | 175 | // Open the acceptor 176 | acceptor_.open(endpoint.protocol(), ec); 177 | if(ec) 178 | { 179 | fail(ec, "open"); 180 | return; 181 | } 182 | 183 | // Allow address reuse 184 | acceptor_.set_option(net::socket_base::reuse_address(true), ec); 185 | if(ec) 186 | { 187 | fail(ec, "set_option"); 188 | return; 189 | } 190 | 191 | // Bind to the server address 192 | acceptor_.bind(endpoint, ec); 193 | if(ec) 194 | { 195 | fail(ec, "bind"); 196 | return; 197 | } 198 | 199 | // Start listening for connections 200 | acceptor_.listen( 201 | net::socket_base::max_listen_connections, ec); 202 | if(ec) 203 | { 204 | fail(ec, "listen"); 205 | return; 206 | } 207 | } 208 | 209 | // Start accepting incoming connections 210 | void 211 | run() 212 | { 213 | do_accept(); 214 | } 215 | 216 | private: 217 | void 218 | do_accept() 219 | { 220 | // The new connection gets its own strand 221 | acceptor_.async_accept( 222 | net::make_strand(ioc_), 223 | beast::bind_front_handler( 224 | &listener::on_accept, 225 | shared_from_this())); 226 | } 227 | 228 | void 229 | on_accept(beast::error_code ec, tcp::socket socket) 230 | { 231 | if(ec) 232 | { 233 | fail(ec, "accept"); 234 | } 235 | else 236 | { 237 | // Create the session and run it 238 | std::make_shared(std::move(socket))->run(); 239 | } 240 | 241 | // Accept another connection 242 | do_accept(); 243 | } 244 | }; 245 | 246 | //------------------------------------------------------------------------------ 247 | 248 | int main(int argc, char* argv[]) 249 | { 250 | // Check command line arguments. 251 | if (argc != 4) 252 | { 253 | std::cerr << 254 | "Usage: websocket-server-async
\n" << 255 | "Example:\n" << 256 | " websocket-server-async 0.0.0.0 8080 1\n"; 257 | return EXIT_FAILURE; 258 | } 259 | auto const address = net::ip::make_address(argv[1]); 260 | auto const port = static_cast(std::atoi(argv[2])); 261 | auto const threads = std::max(1, std::atoi(argv[3])); 262 | 263 | // The io_context is required for all I/O 264 | net::io_context ioc{threads}; 265 | 266 | // Create and launch a listening port 267 | std::make_shared(ioc, tcp::endpoint{address, port})->run(); 268 | 269 | // Run the I/O service on the requested number of threads 270 | std::vector v; 271 | v.reserve(threads - 1); 272 | for(auto i = threads - 1; i > 0; --i) 273 | v.emplace_back( 274 | [&ioc] 275 | { 276 | ioc.run(); 277 | }); 278 | ioc.run(); 279 | 280 | return EXIT_SUCCESS; 281 | } -------------------------------------------------------------------------------- /tools/plot_data.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import os\n", 10 | "import numpy as np\n", 11 | "import matplotlib.pyplot as plt\n", 12 | "import pandas as pd\n", 13 | "import re\n", 14 | "import itertools" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "tools_dir_path = os.path.abspath('')\n", 24 | "root_dir_path = os.path.dirname(tools_dir_path)\n", 25 | "print(root_dir_path)\n", 26 | "data_dir_path = os.path.join(root_dir_path, 'results')" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": null, 32 | "metadata": {}, 33 | "outputs": [], 34 | "source": [] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": null, 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "def get_color(index):\n", 43 | " color_table = [\n", 44 | " \n", 45 | " \"#27ae60\",\"#2980b9\",\"#8e44ad\",\"#2c3e50\",\n", 46 | " \"#f1c40f\",\"#e67e22\",\"#e74c3c\",\n", 47 | " \"#d63031\",\"#feca57\",\"#5f27cd\",\"#54a0ff\",\"#01a3a4\",\n", 48 | " \"#16a085\",\n", 49 | " \"#25CCF7\",\"#FD7272\",\"#54a0ff\",\"#00d2d3\",\n", 50 | " \"#1abc9c\",\"#2ecc71\",\"#3498db\",\"#9b59b6\",\"#34495e\",\n", 51 | " # \"#ecf0f1\", \"#bdc3c7\", \"#dfe6e9\", \n", 52 | " \"#00b894\",\"#00cec9\",\"#0984e3\",\"#6c5ce7\",\"#ffeaa7\",\n", 53 | " \"#fab1a0\",\"#ff7675\",\"#fd79a8\",\"#fdcb6e\",\"#e17055\",\n", 54 | " \"#95a5a6\",\"#f39c12\",\"#d35400\",\"#c0392b\",\n", 55 | " \"#55efc4\",\"#81ecec\",\"#74b9ff\",\"#a29bfe\",\n", 56 | " \"#7f8c8d\", # grey\n", 57 | " \n", 58 | " \n", 59 | " ]\n", 60 | " color_num = len(color_table)\n", 61 | " index %= color_num\n", 62 | " color_hex = color_table[index]\n", 63 | " color_int = int(color_hex.lstrip('#'),16)\n", 64 | " blue_int = color_int % 256\n", 65 | " color_int //= 256\n", 66 | " green_int = color_int % 256\n", 67 | " red_int = color_int // 256\n", 68 | " # return red_int, green_int, blue_int\n", 69 | " return (red_int / 256, green_int / 256, blue_int / 256)" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "import matplotlib.ticker as plticker\n", 79 | "from matplotlib.ticker import FormatStrFormatter\n", 80 | "def plot_lines(title, x_arr_list, y_arr_list, label_list, x_axis_label, y_axis_label, use_log_xscale=True, use_log_yscale=False, show_fig=True):\n", 81 | " fig, ax = plt.subplots(figsize=(8, 6), dpi=300)\n", 82 | " fig.suptitle(title, fontsize='small')\n", 83 | " assert(len(x_arr_list) == len(y_arr_list))\n", 84 | " arr_num = len(x_arr_list)\n", 85 | "\n", 86 | " marker = itertools.cycle(('v','o','^','<','>','s','p','*','+','x','D'))\n", 87 | " for i in range(arr_num):\n", 88 | " color = get_color(i)\n", 89 | " ax.plot(x_arr_list[i], y_arr_list[i], label=label_list[i], color=color, marker=next(marker), markersize=2.5, linewidth=1.0)\n", 90 | " ax.set_xlabel(x_axis_label)\n", 91 | " ax.set_ylabel(y_axis_label)\n", 92 | " if use_log_xscale:\n", 93 | " ax.set_xscale('log')\n", 94 | " if use_log_yscale:\n", 95 | " ax.set_yscale('log')\n", 96 | " \n", 97 | " subs = [1.0, 2.0, 4.0, 6.0]\n", 98 | " ax.yaxis.set_minor_locator(plticker.LogLocator(subs=subs))\n", 99 | "\n", 100 | " box = ax.get_position()\n", 101 | " ax.set_position([box.x0, box.y0, box.width * 0.95, box.height])\n", 102 | "\n", 103 | " fig.legend(loc='center right', fontsize='xx-small')\n", 104 | "\n", 105 | " if show_fig:\n", 106 | " plt.show()\n", 107 | " plt.close(fig)\n", 108 | " " 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": null, 114 | "metadata": {}, 115 | "outputs": [], 116 | "source": [ 117 | "import copy\n", 118 | "latency_filename_list = [\"flashws_latency.csv\", \"tpp_latency.csv\", \"uws_poll_latency.csv\", \"uws_latency.csv\"]\n", 119 | "msg_size_arr_list = []\n", 120 | "goodput_arr_list = []\n", 121 | "p50_arr_list = []\n", 122 | "p99_arr_list = []\n", 123 | "test_item_name_list = []\n", 124 | "for latency_filename in latency_filename_list:\n", 125 | " print(latency_filename)\n", 126 | " latency_file_path = os.path.join(data_dir_path, latency_filename)\n", 127 | " temp_df = pd.read_csv(latency_file_path, sep=',')\n", 128 | " # print(temp_df.head())\n", 129 | " msg_size_arr = temp_df[\"msg_size\"].to_list()\n", 130 | " msg_size_arr_list.append(msg_size_arr)\n", 131 | " goodput_arr = np.array(temp_df[\"avg_goodput\"].to_list())\n", 132 | " #make it Gbps\n", 133 | " goodput_arr /= 1000.0 \n", 134 | " goodput_arr_list.append(goodput_arr)\n", 135 | " p50_latency_arr = temp_df[\"P50_latency\"].to_list()\n", 136 | " p50_arr_list.append(p50_latency_arr)\n", 137 | " p99_latency_arr = temp_df[\"P99_latency\"].to_list()\n", 138 | " p99_arr_list.append(p99_latency_arr)\n", 139 | " m = re.findall(r'(.+)_latency.csv', latency_filename)\n", 140 | " # print(m)\n", 141 | " temp_test_item = m[0]\n", 142 | " test_item_name_list.append(temp_test_item)\n", 143 | "\n", 144 | "\n", 145 | "\n", 146 | "\n", 147 | "all_latency_label_list = []\n", 148 | "all_latency_arr_list = []\n", 149 | "for i, test_item_name in enumerate(test_item_name_list):\n", 150 | " all_latency_label_list.append(test_item_name + \"-P50\")\n", 151 | " all_latency_arr_list.append(p50_arr_list[i])\n", 152 | "\n", 153 | " all_latency_label_list.append(test_item_name + \"-P99\")\n", 154 | " all_latency_arr_list.append(p99_arr_list[i])\n", 155 | "# print(all_latency_label_list)\n", 156 | "double_msg_size_arr_list = copy.deepcopy(msg_size_arr_list)\n", 157 | "double_msg_size_arr_list.extend(msg_size_arr_list)\n", 158 | "plot_lines(\"\", double_msg_size_arr_list, all_latency_arr_list, all_latency_label_list, \"Message Size (Byte)\", \"Latency (us)\", use_log_xscale=True, use_log_yscale=True, show_fig=True)\n", 159 | "plot_lines(\"\", msg_size_arr_list, goodput_arr_list, test_item_name_list, \"Message Size (Byte)\", \"Goodput (Gbps)\", use_log_xscale=True, use_log_yscale=False, show_fig=True)" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": null, 165 | "metadata": {}, 166 | "outputs": [], 167 | "source": [ 168 | "throughput_filename_list = [\"flashws_throughput.csv\", \"tpp_poll_throughput.csv\", \"uws_poll_throughput.csv\"]\n", 169 | "\n", 170 | "msg_size_arr_list = []\n", 171 | "goodput_arr_list = []\n", 172 | "test_item_name_list = []\n", 173 | "msg_speed_arr_list = []\n", 174 | "\n", 175 | "for throughput_name in throughput_filename_list:\n", 176 | " put_file_path = os.path.join(data_dir_path, throughput_name)\n", 177 | " temp_df = pd.read_csv(put_file_path, sep=',')\n", 178 | " msg_size_arr = temp_df[\"msg_size\"].to_list()\n", 179 | " msg_size_arr_list.append(msg_size_arr)\n", 180 | " goodput_arr = np.array(temp_df[\"rx_goodput\"].to_list())\n", 181 | " goodput_arr /= 1000.0 \n", 182 | " goodput_arr_list.append(goodput_arr)\n", 183 | " msg_speed_arr = temp_df[\"rx_mm_mps\"].to_list()\n", 184 | " msg_speed_arr_list.append(msg_speed_arr)\n", 185 | " connection_cnt_arr = temp_df['connection_cnt'].to_list()\n", 186 | "\n", 187 | " m = re.findall(r'(.+)_throughput.csv', throughput_name)\n", 188 | " # print(m)\n", 189 | " temp_test_item = m[0]\n", 190 | " test_item_name_list.append(temp_test_item)\n", 191 | "\n", 192 | "plot_lines(\"\", msg_size_arr_list, goodput_arr_list, test_item_name_list, \"Message Size (Byte)\", \"Goodput (Gbps)\", use_log_xscale=True, use_log_yscale=False, show_fig=True)\n", 193 | "plot_lines(\"\", msg_size_arr_list, msg_speed_arr_list, test_item_name_list, \"Message Size (Byte)\", \"Messages/sec (Mpps)\", use_log_xscale=True, use_log_yscale=False, show_fig=True)" 194 | ] 195 | }, 196 | { 197 | "cell_type": "code", 198 | "execution_count": null, 199 | "metadata": {}, 200 | "outputs": [], 201 | "source": [] 202 | } 203 | ], 204 | "metadata": { 205 | "kernelspec": { 206 | "display_name": "Python 3.x", 207 | "language": "python", 208 | "name": "python3" 209 | }, 210 | "language_info": { 211 | "codemirror_mode": { 212 | "name": "ipython", 213 | "version": 3 214 | }, 215 | "file_extension": ".py", 216 | "mimetype": "text/x-python", 217 | "name": "python", 218 | "nbconvert_exporter": "python", 219 | "pygments_lexer": "ipython3", 220 | "version": "3.8.9" 221 | }, 222 | "orig_nbformat": 4, 223 | "vscode": { 224 | "interpreter": { 225 | "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" 226 | } 227 | } 228 | }, 229 | "nbformat": 4, 230 | "nbformat_minor": 2 231 | } 232 | -------------------------------------------------------------------------------- /src/usockets-tcp/echo_server.cpp: -------------------------------------------------------------------------------- 1 | /* This is a basic TCP/TLS echo server. */ 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "test_def.h" 12 | #include "flat_hash_map.h" 13 | 14 | //struct ClientContext { 15 | // char data[MAX_DATA_LEN]; 16 | // size_t start_pos = 0; 17 | // size_t size = 0; 18 | //}; 19 | 20 | 21 | /* Our socket extension */ 22 | //struct echo_socket { 23 | //// char *backpressure; 24 | //// int length; 25 | //// std::vector data_vec; 26 | // int64_t active_conns_in_ctx = 0; 27 | // ska::flat_hash_map fd_to_ctx; 28 | // 29 | //}; 30 | struct Buffer { 31 | char data[MAX_DATA_LEN]; 32 | size_t start_pos = 0; 33 | size_t size = 0; 34 | 35 | }; 36 | 37 | /* Our socket context extension */ 38 | struct echo_socket { 39 | Buffer buf; 40 | int64_t active_conns_in_ctx = 0; 41 | }; 42 | 43 | /* Loop wakeup handler */ 44 | void on_wakeup(struct us_loop_t *loop) { 45 | 46 | } 47 | 48 | /* Loop pre iteration handler */ 49 | void on_pre(struct us_loop_t *loop) { 50 | 51 | } 52 | 53 | /* Loop post iteration handler */ 54 | void on_post(struct us_loop_t *loop) { 55 | 56 | } 57 | 58 | uint64_t last_interval_send_bytes = 0; 59 | uint64_t last_interval_recv_bytes = 0; 60 | uint64_t last_interval_recv_msg_cnt = 0; 61 | uint64_t last_interval_send_msg_cnt = 0; 62 | 63 | int64_t interval_start_ns = 0; 64 | int64_t active_connections = 0; 65 | int64_t dup_act_conns = 0; 66 | 67 | void CountStats() { 68 | if (((last_interval_recv_msg_cnt & 0xffffUL) 69 | // && (last_interval_recv_bytes <= MAX_DATA_LEN * 4096UL) 70 | ) || last_interval_recv_msg_cnt == 0) { 71 | return; 72 | } 73 | int64_t now_ns = std::chrono::duration_cast( 74 | std::chrono::high_resolution_clock::now().time_since_epoch()).count(); 75 | int64_t interval_ns = now_ns - interval_start_ns; 76 | constexpr int64_t BITS_PER_BYTE = 8; 77 | double interval_sec = double(interval_ns) / (1e+9); 78 | double recv_throughput_mbps = double((last_interval_recv_bytes) * 79 | BITS_PER_BYTE) / (1e+6) / interval_sec; 80 | double send_throughput_mbps = double((last_interval_send_bytes) * 81 | BITS_PER_BYTE) / (1e+6) / interval_sec; 82 | 83 | double recv_mm_msg_per_sec = double(last_interval_recv_msg_cnt) 84 | / (1e+6) / interval_sec; 85 | double send_mm_msg_per_sec = double(last_interval_send_msg_cnt) 86 | / (1e+6) / interval_sec; 87 | // if (last_interval_send_msg_cnt != last_interval_recv_msg_cnt) { 88 | // printf("Warning, last interval recv: %zu msg, send %zu msg\n", 89 | // last_interval_recv_msg_cnt, last_interval_send_msg_cnt); 90 | // } 91 | printf("avg rx+tx goodput: %.2lf Mbps, %.4lf 10^6 msg/sec," 92 | "active_client_cnt: %ld, active_cnt in ctx: %ld\n", 93 | recv_throughput_mbps + send_throughput_mbps, 94 | recv_mm_msg_per_sec + send_mm_msg_per_sec, 95 | active_connections, dup_act_conns); 96 | 97 | last_interval_recv_bytes = 0; 98 | last_interval_send_bytes = 0; 99 | last_interval_recv_msg_cnt = 0; 100 | last_interval_send_msg_cnt = 0; 101 | interval_start_ns = std::chrono::duration_cast( 102 | std::chrono::high_resolution_clock::now().time_since_epoch()).count(); 103 | 104 | } 105 | 106 | /* Socket writable handler */ 107 | struct us_socket_t *on_echo_socket_writable(struct us_socket_t *s) { 108 | struct echo_socket *es = (struct echo_socket *) us_socket_ext(SSL, s); 109 | 110 | /* Continue writing out our backpressure */ 111 | int written = us_socket_write(SSL, s, es->buf.data + es->buf.start_pos, es->buf.size, 0); 112 | es->buf.size -= written; 113 | es->buf.start_pos += written; 114 | last_interval_send_bytes += written; 115 | if (es->buf.size == 0) { 116 | ++last_interval_send_msg_cnt; 117 | es->buf.start_pos = 0; 118 | CountStats(); 119 | } 120 | 121 | // if (written != es->buf.size) { 122 | // char *new_buffer = (char *) malloc(es->length - written); 123 | // memcpy(new_buffer, es->backpressure, es->length - written); 124 | // free(es->backpressure); 125 | // es->backpressure = new_buffer; 126 | // es->length -= written; 127 | // } else { 128 | // free(es->backpressure); 129 | // es->length = 0; 130 | // } 131 | 132 | /* Client is not boring */ 133 | // us_socket_timeout(SSL, s, 30); 134 | 135 | return s; 136 | } 137 | 138 | /* Socket closed handler */ 139 | struct us_socket_t *on_echo_socket_close(struct us_socket_t *s, int code, void *reason) { 140 | struct echo_socket *es = (struct echo_socket *) us_socket_ext(SSL, s); 141 | --active_connections; 142 | dup_act_conns = --es->active_conns_in_ctx; 143 | // printf("Client disconnected\n"); 144 | // free(es->buf.data); 145 | // free(es->backpressure); 146 | 147 | return s; 148 | } 149 | 150 | /* Socket half-closed handler */ 151 | struct us_socket_t *on_echo_socket_end(struct us_socket_t *s) { 152 | us_socket_shutdown(SSL, s); 153 | return us_socket_close(SSL, s, 0, NULL); 154 | } 155 | 156 | /* Socket data handler */ 157 | struct us_socket_t *on_echo_socket_data(struct us_socket_t *s, char *data, int length) { 158 | struct echo_socket *es = (struct echo_socket *) us_socket_ext(SSL, s); 159 | last_interval_recv_bytes += length; 160 | if (length > 0) { 161 | ++last_interval_recv_msg_cnt; 162 | } 163 | /* Print the data we received */ 164 | // printf("Client sent <%.*s>\n", length, data); 165 | if (length == MAX_DATA_LEN) { 166 | int written = us_socket_write(SSL, s, data, length, 0); 167 | last_interval_send_bytes += written; 168 | if (written != length) { 169 | memcpy(es->buf.data, data + written, length - written); 170 | es->buf.size = length - written; 171 | } 172 | else { 173 | ++last_interval_send_msg_cnt; 174 | 175 | CountStats(); 176 | } 177 | 178 | } 179 | else { 180 | memcpy(es->buf.data + es->buf.size, data, length); 181 | es->buf.size += length; 182 | if (es->buf.size == MAX_DATA_LEN) { 183 | int written = us_socket_write(SSL, s, es->buf.data, es->buf.size, 0); 184 | es->buf.start_pos += written; 185 | last_interval_send_bytes += written; 186 | es->buf.size -= written; 187 | if (es->buf.size == 0) { 188 | ++last_interval_send_msg_cnt; 189 | es->buf.start_pos = 0; 190 | CountStats(); 191 | } 192 | } 193 | 194 | } 195 | /* Send it back or buffer it up */ 196 | 197 | 198 | 199 | /* Client is not boring */ 200 | // us_socket_timeout(SSL, s, 30); 201 | 202 | return s; 203 | } 204 | 205 | /* Socket opened handler */ 206 | struct us_socket_t *on_echo_socket_open(struct us_socket_t *s, int is_client, char *ip, int ip_length) { 207 | struct echo_socket *es = (struct echo_socket *) us_socket_ext(SSL, s); 208 | int fd = (int64_t)us_socket_get_native_handle(SSL, s); 209 | /* Initialize the new socket's extension */ 210 | // es->backpressure = 0; 211 | // es->length = 0; 212 | // es->buf.data = (char*)malloc(MAX_DATA_LEN); 213 | es->buf.start_pos = 0; 214 | es->buf.size = 0; 215 | es->active_conns_in_ctx = 0; 216 | dup_act_conns = ++es->active_conns_in_ctx; 217 | ++active_connections; 218 | /* Start a timeout to close the socket if boring */ 219 | // us_socket_timeout(SSL, s, 30); 220 | 221 | // printf("Client connected\n"); 222 | 223 | return s; 224 | } 225 | 226 | /* Socket timeout handler */ 227 | struct us_socket_t *on_echo_socket_timeout(struct us_socket_t *s) { 228 | printf("Client was idle for too long\n"); 229 | return us_socket_close(SSL, s, 0, NULL); 230 | } 231 | 232 | int main() { 233 | /* The event loop */ 234 | struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0); 235 | 236 | /* Socket context */ 237 | struct us_socket_context_options_t options = { 238 | }; 239 | // options.key_file_name = "/home/alexhultman/uWebSockets.js/misc/key.pem"; 240 | // options.cert_file_name = "/home/alexhultman/uWebSockets.js/misc/cert.pem"; 241 | // options.passphrase = "1234"; 242 | if constexpr (SSL) { 243 | options.cert_file_name = cert_file_path; 244 | options.key_file_name = key_file_path; 245 | } 246 | 247 | struct us_socket_context_t *echo_context = us_create_socket_context(SSL, loop, sizeof(struct echo_socket), options); 248 | 249 | 250 | /* Registering event handlers */ 251 | us_socket_context_on_open(SSL, echo_context, on_echo_socket_open); 252 | us_socket_context_on_data(SSL, echo_context, on_echo_socket_data); 253 | us_socket_context_on_writable(SSL, echo_context, on_echo_socket_writable); 254 | us_socket_context_on_close(SSL, echo_context, on_echo_socket_close); 255 | us_socket_context_on_timeout(SSL, echo_context, on_echo_socket_timeout); 256 | us_socket_context_on_end(SSL, echo_context, on_echo_socket_end); 257 | 258 | /* Start accepting echo sockets */ 259 | struct us_listen_socket_t *listen_socket = us_socket_context_listen(SSL, echo_context, 260 | test::SERVER_IP, SERVER_PORT, 0, sizeof(struct echo_socket)); 261 | 262 | if (listen_socket) { 263 | printf("Listening on port %d...\n", SERVER_PORT); 264 | us_loop_run(loop); 265 | } else { 266 | printf("Failed to listen! %s\n", 267 | std::strerror(errno)); 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /src/flashws/echo_server.cpp: -------------------------------------------------------------------------------- 1 | #include "flashws/flashws.h" 2 | #include "flashws/net/ws_server_socket.h" 3 | #include "flashws/net/floop.h" 4 | #include "test_def.h" 5 | #include 6 | #include 7 | 8 | namespace test { 9 | 10 | 11 | 12 | using WSSocket = fws::WSServerSocket; 13 | 14 | 15 | struct BufferStatus { 16 | uint8_t opcode; 17 | uint8_t is_msg_end; 18 | }; 19 | struct ConnectCtx { 20 | fws::IOBuffer io_buf; 21 | BufferStatus status; 22 | }; 23 | 24 | struct Context { 25 | fws::FLoop> loop; 26 | 27 | uint64_t last_interval_send_bytes = 0; 28 | uint64_t last_interval_recv_bytes = 0; 29 | uint64_t last_interval_recv_msg_cnt = 0; 30 | uint64_t last_interval_send_msg_cnt = 0; 31 | size_t last_msg_size = 0; 32 | int64_t interval_start_ns = 0; 33 | FILE* output_fp = nullptr; 34 | 35 | 36 | 37 | void CountStats() { 38 | if FWS_LIKELY(((last_interval_recv_msg_cnt & 0x3fffUL) 39 | // && (last_interval_recv_bytes <= MAX_DATA_LEN * 4096UL) 40 | ) || last_interval_recv_msg_cnt == 0) { 41 | return; 42 | } 43 | int64_t now_ns = std::chrono::duration_cast( 44 | std::chrono::high_resolution_clock::now().time_since_epoch()).count(); 45 | int64_t interval_ns = now_ns - interval_start_ns; 46 | constexpr int64_t BITS_PER_BYTE = 8; 47 | double interval_sec = double(interval_ns) / (1e+9); 48 | double recv_throughput_mbps = double((last_interval_recv_bytes) * 49 | BITS_PER_BYTE) / (1e+6) / interval_sec; 50 | double send_throughput_mbps = double((last_interval_send_bytes) * 51 | BITS_PER_BYTE) / (1e+6) / interval_sec; 52 | 53 | double recv_mm_msg_per_sec = double(last_interval_recv_msg_cnt) 54 | / (1e+6) / interval_sec; 55 | double send_mm_msg_per_sec = double(last_interval_send_msg_cnt) 56 | / (1e+6) / interval_sec; 57 | // if (last_interval_send_msg_cnt != last_interval_recv_msg_cnt) { 58 | // printf("Warning, last interval recv: %zu msg, send %zu msg\n", 59 | // last_interval_recv_msg_cnt, last_interval_send_msg_cnt); 60 | // } 61 | printf("last_msg_size: %zu, avg rx+tx goodput: %.2lf Mbps, %.4lf 10^6 msg/sec," 62 | "active_client_cnt: %zu\n", 63 | last_msg_size, 64 | recv_throughput_mbps + send_throughput_mbps, 65 | recv_mm_msg_per_sec + send_mm_msg_per_sec, 66 | loop.socket_count() - 1U); 67 | if (output_fp != nullptr) { 68 | // msg_size, rx goodput, tx goodput, rx mm mps, tx mm mps, connection cnt 69 | fprintf(output_fp, "%zu,%.3lf,%.3lf,%lf,%lf,%zu\n", 70 | last_msg_size, 71 | recv_throughput_mbps, 72 | send_throughput_mbps, 73 | recv_mm_msg_per_sec, 74 | send_mm_msg_per_sec, 75 | loop.socket_count() - 1U 76 | ); 77 | fflush(output_fp); 78 | } 79 | 80 | last_interval_recv_bytes = 0; 81 | last_interval_send_bytes = 0; 82 | last_interval_recv_msg_cnt = 0; 83 | last_interval_send_msg_cnt = 0; 84 | interval_start_ns = std::chrono::duration_cast( 85 | std::chrono::high_resolution_clock::now().time_since_epoch()).count(); 86 | 87 | } 88 | 89 | 90 | }; 91 | 92 | 93 | 94 | 95 | int TestWsServer(int argc, char** argv) { 96 | printf("Prepare to init fws env\n"); 97 | // { 98 | // int code = fws::InitEnv(argc, argv); 99 | // if (code < 0) { 100 | // printf("Error in Init Env\n"); 101 | // return code; 102 | // } 103 | // } 104 | 105 | if (argc < 5) { 106 | printf("Invalid parameters!\nUsage: ./echo_server ip_address port max_msg_len export_filename\n"); 107 | return -1; 108 | } 109 | 110 | const char* SERVER_IP = argv[1]; 111 | 112 | int SERVER_PORT = atoi(argv[2]); 113 | if (SERVER_PORT <= 0) { 114 | printf("Invalid port: %s\n", argv[2]); 115 | return -1; 116 | } 117 | long long max_msg_len = atoll(argv[3]); 118 | if (max_msg_len <= 0) { 119 | printf("invalid max_msg_len: %s\n", argv[3]); 120 | return -1; 121 | } 122 | int MAX_DATA_LEN = size_t(max_msg_len); 123 | 124 | const char* export_file_path = argv[4]; 125 | 126 | 127 | fws::InitEnv(argc - 3, argv + 3); 128 | Context ctx{}; 129 | ctx.loop = fws::FLoop{}; 130 | if (ctx.loop.Init() < 0) { 131 | printf("Failed to init loop\n"); 132 | return -1; 133 | } 134 | 135 | if constexpr (ENABLE_TLS) { 136 | if (fws::SSLManager::instance().Init(SHOULD_VERIFY_CERT, cert_file_path, key_file_path, 137 | nullptr) < 0) { 138 | printf("Failed to init ssl manager, %s\n", fws::GetErrorStrP()); 139 | std::abort(); 140 | } 141 | } 142 | 143 | // const char* export_file_path = log_data_file_path; 144 | 145 | ctx.output_fp = fopen(export_file_path, "w"); 146 | if (ctx.output_fp == nullptr) { 147 | printf("Cannot create file at %s\n", export_file_path); 148 | return -1; 149 | } 150 | printf("Will output to %s\n", export_file_path); 151 | // msg_size, rx goodput, tx goodput, rx mm mps, tx mm mps 152 | fprintf(ctx.output_fp, "msg_size,rx_goodput,tx_goodput,rx_mm_mps,tx_mm_mps,connection_cnt\n"); 153 | 154 | WSSocket ws_socket{}; 155 | if (ws_socket.Init() < 0) { 156 | printf("Failed to init ws_socket\n"); 157 | return -1; 158 | } 159 | 160 | ws_socket.SetOnNewConnection([MAX_DATA_LEN](WSSocket &w_socket, std::string_view req_uri, 161 | std::string_view host, std::string_view origin, 162 | std::string_view sub_protocols, 163 | std::string_view extensions, 164 | std::string_view &resp_sub_protocol, 165 | std::string_view &resp_extensions, void *user_data) { 166 | // printf("OnNewWsConnection called, req_uri: %s, host: %s\n", 167 | // std::string(req_uri).c_str(), std::string(host).c_str()); 168 | auto new_buf = fws::RequestBuf(MAX_DATA_LEN + fws::constants::SUGGEST_RESERVE_WS_HDR_SIZE); 169 | new_buf.start_pos = fws::constants::SUGGEST_RESERVE_WS_HDR_SIZE; 170 | ConnectCtx *con_ctx = (ConnectCtx*)user_data; 171 | new (con_ctx) ConnectCtx{std::move(new_buf), {}}; 172 | std::string sub_protocols_str(sub_protocols); 173 | std::string extensions_str(extensions); 174 | // printf("Client provide protocols: %s, extensions: %s\n", 175 | // sub_protocols_str.c_str(), 176 | // extensions_str.c_str()); 177 | return 0; 178 | }); 179 | 180 | ws_socket.SetOnRead([&](WSSocket &ws_socket, uint32_t opcode, fws::IOBuffer io_buf, 181 | bool is_frame_end, bool is_msg_end, bool is_control_msg, void *user_data) { 182 | 183 | 184 | // printf("OnRecvWSPart called, fd: %d, opcode: %u, start_pos: %zu, size: %zu, cap: %zu," 185 | // "is_frame_end: %d, is_msg_end: %d, is_control_msg: %d\n", 186 | // ws_socket.tcp_socket().fd(), opcode, io_buf.start_pos, io_buf.size, io_buf.capacity, 187 | // is_frame_end, is_msg_end, is_control_msg); 188 | if (!is_control_msg) { 189 | // constexpr size_t MAX_DISPLAY_LEN = 128; 190 | // std::string str((char*)(io_buf.data + io_buf.start_pos), std::min(MAX_DISPLAY_LEN, io_buf.size)); 191 | // if (io_buf.size > 0) { 192 | // printf("data:\n%s\n", str.c_str()); 193 | // if (io_buf.size > MAX_DISPLAY_LEN) { 194 | // printf("......\n"); 195 | // } 196 | // } 197 | char* FWS_RESTRICT data = (char*)(io_buf.data + io_buf.start_pos); 198 | auto &con_ctx = *(ConnectCtx*)user_data; 199 | auto &io_buf_ = con_ctx.io_buf; 200 | memcpy(io_buf_.data + io_buf_.start_pos + io_buf_.size, data, io_buf.size); 201 | io_buf_.size += io_buf.size; 202 | if FWS_UNLIKELY(ctx.interval_start_ns == 0) { 203 | ctx.interval_start_ns = std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); 204 | } 205 | 206 | ctx.last_interval_recv_bytes += io_buf.size; 207 | 208 | con_ctx.status = {uint8_t(opcode), uint8_t(is_msg_end)}; 209 | if (is_msg_end) { 210 | ++ctx.last_interval_recv_msg_cnt; 211 | ctx.last_msg_size = io_buf_.size; 212 | size_t target_size = io_buf_.size; 213 | { 214 | ssize_t write_ret = ws_socket.WriteFrame(std::move(io_buf_), 215 | (fws::WSTxFrameType)con_ctx.status.opcode, true); 216 | FWS_ASSERT(write_ret >= 0); 217 | } 218 | ctx.last_interval_send_bytes += target_size; 219 | 220 | ++ctx.last_interval_send_msg_cnt; 221 | ctx.CountStats(); 222 | io_buf_ = fws::RequestBuf(MAX_DATA_LEN + fws::constants::SUGGEST_RESERVE_WS_HDR_SIZE); 223 | io_buf_.start_pos = fws::constants::SUGGEST_RESERVE_WS_HDR_SIZE; 224 | 225 | 226 | } 227 | 228 | } 229 | else { 230 | if (opcode == fws::WS_OPCODE_PONG) { 231 | FWS_ASSERT(is_frame_end); 232 | FWS_ASSERT(is_msg_end); 233 | } 234 | } 235 | 236 | }); 237 | 238 | ws_socket.SetOnClose([&](WSSocket &w_socket, uint32_t status_code, std::string_view reason, void *user_data) { 239 | 240 | if (status_code != 1000U) { 241 | std::string reason_str(reason); 242 | printf("OnCloseConnection called, fd: %d, status_code %u, reason: %s\n", 243 | w_socket.under_socket().fd(), status_code, reason_str.c_str()); 244 | } 245 | ConnectCtx *con_ctx = (ConnectCtx*)user_data; 246 | std::destroy_at(con_ctx); 247 | }); 248 | 249 | if (ws_socket.StartListen(SERVER_IP, SERVER_PORT, LISTEN_BACKLOG, fws::TCPSocket::REUSE_ADDR_MODE) < 0) { 250 | printf("Failed to start listen\n"); 251 | return -1; 252 | } 253 | 254 | auto [add_ret, new_sock_ptr] = ctx.loop.AddSocket(std::move(ws_socket), sizeof(ConnectCtx), true); 255 | if (add_ret < 0) { 256 | printf("Failed to add ws_socket to loop\n"); 257 | return -1; 258 | } 259 | printf("start to run loop\n"); 260 | ctx.loop.Run(); 261 | return 0; 262 | 263 | } 264 | 265 | } // namespace test 266 | 267 | 268 | int main(int argc, char** argv) { 269 | return test::TestWsServer(argc, argv); 270 | } -------------------------------------------------------------------------------- /src/flashws/echo_client.cpp: -------------------------------------------------------------------------------- 1 | #include "flashws/net/ws_client_socket.h" 2 | #include "flashws/flashws.h" 3 | #include "flashws/utils/histogram_wrapper.h" 4 | #include "flashws/utils/cpu_timer.h" 5 | #include "flashws/net/floop.h" 6 | #include "test_def.h" 7 | #include 8 | #include 9 | namespace test { 10 | 11 | 12 | static size_t MAX_DATA_LEN = 0; 13 | static size_t TOTAL_MSG_CNT = 0; 14 | static size_t MSG_LIMIT_PER_CLIENT = 0; 15 | static int REBORN_LIMIT_FOR_CLIENT = 0; 16 | static size_t CON_CLIENT_NUM = 0; 17 | size_t TEST_TIMES = 0; 18 | static constexpr size_t MAX_EVENT_NUM = MAX_CLIENT_EVENT_NUM; 19 | 20 | template 21 | struct ContextClass { 22 | hist::HistWrapper rtt_hist; 23 | fws::FLoop> loop; 24 | cpu_t::CpuTimer cpu_timer{}; 25 | 26 | struct ClientCtx{ 27 | int reborn_cnt = 1; 28 | uint64_t msg_cnt = 0; 29 | fws::IOBuffer temp_buf; 30 | uint64_t start_write_tick = 0; 31 | }; 32 | size_t data_hash; 33 | uint64_t loop_cnt = 0; 34 | int64_t send_bytes_sum = 0; 35 | int64_t recv_bytes_sum = 0; 36 | 37 | 38 | int64_t start_ns_from_epoch = 0; 39 | int64_t last_record_ns = 0; 40 | bool wait_shutdown = false; 41 | 42 | std::string server_ip; 43 | std::string request_uri; 44 | std::string host; 45 | uint16_t server_port; 46 | 47 | FILE *out_fp = nullptr; 48 | 49 | static size_t HashArr(const uint8_t* FWS_RESTRICT data, size_t size) { 50 | size_t hash_value = 0; 51 | for (size_t i = 0; i < size; ++i) { 52 | hash_value ^= data[i]; 53 | hash_value = fws::RotateR(hash_value, 5); 54 | } 55 | return hash_value; 56 | } 57 | 58 | // New WS connection established 59 | 60 | 61 | 62 | 63 | void EndClient() { 64 | loop.StopRun(); 65 | auto now_ns_from_epoch = std::chrono::high_resolution_clock::now().time_since_epoch().count(); 66 | auto to_now_ns = now_ns_from_epoch - start_ns_from_epoch; 67 | printf("INFO! write read finish! per msg len = %zu times=%zu sendsum=%ld recvsum=%ld cost=%.3lf ms\n", 68 | MAX_DATA_LEN, TOTAL_MSG_CNT, send_bytes_sum, recv_bytes_sum, 69 | (double)to_now_ns / (1e+6)); 70 | printf("INFO! round trip latency histogram (ns)\n"); 71 | rtt_hist.PrintHdr(30UL); 72 | 73 | 74 | wait_shutdown = true; 75 | if (out_fp != nullptr) { 76 | // msg_size, avg throughput, P0 latency, P50 latency, P99 latency, P999 latency, P100 latency 77 | constexpr size_t BITS_PER_BYTE = 8; 78 | double avg_goodput_mbps = double((recv_bytes_sum + send_bytes_sum) * 79 | BITS_PER_BYTE) / (1e+6) / (double(to_now_ns) / (1e+9)); 80 | double p0_t = rtt_hist.Quantile(0.0), p50_t = rtt_hist.Quantile(0.5), 81 | p99_t = rtt_hist.Quantile(0.99), p999_t = rtt_hist.Quantile(0.999), 82 | p100_t = rtt_hist.Quantile(1.0); 83 | printf("Msg size(bytes): %zu\n" 84 | "avg (rx+tx) goodput: %.4lf Mbps\nLatency (us): min: %.3lf, " 85 | "P50: %.3lf, P99: %.3lf, P999: %.3lf, P100: %.3lf\n", 86 | MAX_DATA_LEN, 87 | avg_goodput_mbps, p0_t / 1000.0, p50_t / 1000.0, p99_t / 1000.0, 88 | p999_t / 1000.0, p100_t / 1000.0); 89 | fprintf(out_fp, "%zu,%.4lf,%.3lf,%.3lf,%.3lf,%.3lf,%.3lf\n", 90 | MAX_DATA_LEN, avg_goodput_mbps, p0_t / 1000.0, p50_t / 1000.0, p99_t / 1000.0, 91 | p999_t / 1000.0, p100_t / 1000.0); 92 | fclose(out_fp); 93 | } 94 | std::this_thread::sleep_for(std::chrono::seconds(4)); 95 | } 96 | 97 | 98 | 99 | 100 | int WriteFirstMsg(WSSocket &w_socket, ClientCtx &client_ctx) { 101 | 102 | auto& buf = client_ctx.temp_buf; 103 | size_t target_size = buf.size; 104 | 105 | if (size_t(buf.size) == MAX_DATA_LEN) { 106 | if FWS_UNLIKELY(send_bytes_sum == 0) { 107 | start_ns_from_epoch = std::chrono::high_resolution_clock::now().time_since_epoch().count(); 108 | } 109 | client_ctx.start_write_tick = cpu_timer.Start(); 110 | } 111 | FWS_ASSERT(buf.data != nullptr); 112 | ssize_t write_ret = w_socket.WriteFrame(std::move(buf), 113 | static_cast(2U), true); 114 | if FWS_UNLIKELY(write_ret < 0) { 115 | printf("Error, write return %zd, %s\n", write_ret, fws::GetErrorStrP()); 116 | std::abort(); 117 | } 118 | send_bytes_sum += target_size; 119 | client_ctx.temp_buf = fws::RequestBuf(MAX_DATA_LEN + fws::constants::SUGGEST_RESERVE_WS_HDR_SIZE); 120 | client_ctx.temp_buf.start_pos = fws::constants::SUGGEST_RESERVE_WS_HDR_SIZE; 121 | return 0; 122 | } 123 | 124 | 125 | 126 | std::optional NewWSClient() { 127 | WSSocket ws_client{}; 128 | int init_ret = 0; 129 | if constexpr (ENABLE_TLS) { 130 | init_ret = ws_client.Init(hostname); 131 | } 132 | else { 133 | init_ret = ws_client.Init(); 134 | } 135 | if FWS_UNLIKELY(init_ret < 0) { 136 | printf("ws_client init return %d, %s\n", 137 | init_ret, std::string(fws::GetErrorStrV()).c_str()); 138 | return std::nullopt; 139 | } 140 | 141 | 142 | { 143 | 144 | int con_ret = ws_client.Connect(server_ip.c_str(), server_port, request_uri, host); 145 | if (con_ret < 0 && errno != EINPROGRESS) { 146 | printf("Error in connect, return %d, %s\n", 147 | con_ret, std::string(fws::GetErrorStrV()).c_str()); 148 | 149 | return std::nullopt; 150 | } 151 | } 152 | 153 | // If the client is added to the floop by the user, the user_data will 154 | // be the moved user_data object. But if the socket is created by 155 | // `accept` method, then the user_data memory is not initialized and 156 | // need to be constructed by the user in on_open. 157 | ws_client.SetOnOpen([&](WSSocket &w_socket, 158 | std::string_view resp_sub_protocol, 159 | std::string_view resp_extensions, void *user_data) { 160 | // printf("OnConnected called fd: %d\n", w_socket.under_socket().fd()); 161 | std::string sub_protocols_str(resp_sub_protocol); 162 | std::string extensions_str(resp_extensions); 163 | auto *client_ctx= static_cast(user_data); 164 | // printf("Write first msg\n"); 165 | FWS_ASSERT(this->WriteFirstMsg(w_socket, *client_ctx) == 0); 166 | // printf("accept protocols: %s, extensions: %s\n", 167 | // sub_protocols_str.c_str(), 168 | // extensions_str.c_str()); 169 | }); 170 | 171 | // Both client and server should destroy the user_data in on_close callback 172 | ws_client.SetOnClose([&](WSSocket &w_socket, uint32_t status_code, std::string_view reason, void* user_data) { 173 | auto &client_ctx = *static_cast(user_data); 174 | if (status_code != 1000U) { 175 | std::string reason_str(reason); 176 | printf("OnCloseConnection called, fd: %d, status_code %u, reason: %s\n", 177 | w_socket.under_socket().fd(), status_code, reason_str.c_str()); 178 | } 179 | std::destroy_at(&client_ctx); 180 | if (loop_cnt >= TOTAL_MSG_CNT) { 181 | printf("loop cnt reach TOTAL_MSG_CNT, prepare to end\n"); 182 | EndClient(); 183 | return ; 184 | } 185 | }); 186 | 187 | ws_client.SetOnRead([&](WSSocket &ws_socket, uint32_t opcode, fws::IOBuffer io_buf, 188 | bool is_frame_end, bool is_msg_end, bool is_control_msg, void *user_data) { 189 | 190 | 191 | // printf("OnRecvWSPart called, opcode: %u, start_pos: %zu, size: %zu, cap: %zu," 192 | // "is_frame_end: %d, is_msg_end: %d, is_control_msg: %d\n", 193 | // opcode, io_buf.start_pos, io_buf.size, io_buf.capacity, 194 | // is_frame_end, is_msg_end, is_control_msg); 195 | // auto *cur_sock_p = &ws_socket; 196 | if (!is_control_msg) { 197 | auto &client_ctx = *static_cast(user_data); 198 | auto* temp_buf_ = &client_ctx.temp_buf; 199 | 200 | uint8_t* FWS_RESTRICT start_data = temp_buf_->data + temp_buf_->start_pos + temp_buf_->size; 201 | memcpy(start_data, io_buf.data + io_buf.start_pos, io_buf.size); 202 | temp_buf_->size += io_buf.size; 203 | recv_bytes_sum += io_buf.size; 204 | 205 | 206 | if (is_msg_end) { 207 | 208 | 209 | auto read_end_tick = cpu_timer.Stop(); 210 | auto pass_tick = read_end_tick - client_ctx.start_write_tick; 211 | int64_t round_trip_ns = std::llround(pass_tick * cpu_timer.ns_per_tick()); 212 | rtt_hist.AddValue(round_trip_ns); 213 | 214 | 215 | ++loop_cnt; 216 | auto client_msg_cnt = ++client_ctx.msg_cnt; 217 | if (client_msg_cnt == MSG_LIMIT_PER_CLIENT) { 218 | auto old_buf = std::move(*temp_buf_); 219 | FWS_ASSERT(old_buf.data != nullptr); 220 | FWS_ASSERT(old_buf.size == (ssize_t)MAX_DATA_LEN); 221 | auto cur_reborn_cnt = client_ctx.reborn_cnt; 222 | ws_socket.Close(fws::WS_NORMAL_CLOSE, {}); 223 | 224 | if FWS_LIKELY(cur_reborn_cnt < REBORN_LIMIT_FOR_CLIENT) { 225 | auto new_opt_sock = NewWSClient(); 226 | if FWS_UNLIKELY(!new_opt_sock.has_value()) { 227 | std::abort(); 228 | } 229 | auto& new_sock = new_opt_sock.value(); 230 | ++cur_reborn_cnt; 231 | 232 | auto new_ctx = ClientCtx{cur_reborn_cnt, 0, 233 | std::move(old_buf), read_end_tick}; 234 | 235 | auto [add_sock_ret, new_sock_ptr] = loop.AddSocket(std::move(new_sock), sizeof(ClientCtx), false, std::move(new_ctx)); 236 | if FWS_UNLIKELY(add_sock_ret < 0) { 237 | printf("Failed to add new socket to loop, %s\n", fws::GetErrorStrP()); 238 | std::abort(); 239 | } 240 | 241 | } 242 | else { 243 | if (loop.socket_count() == 0) { 244 | EndClient(); 245 | } 246 | } 247 | return ; 248 | } 249 | 250 | 251 | if (loop_cnt >= TOTAL_MSG_CNT) { 252 | printf("loop cnt reach TOTAL_MSG_CNT, prepare to end\n"); 253 | EndClient(); 254 | return ; 255 | } 256 | 257 | if FWS_UNLIKELY(!(loop_cnt & 0x3fffUL)) { 258 | size_t temp_hash = HashArr(temp_buf_->data + temp_buf_->start_pos, temp_buf_->size); 259 | // size_t temp_hash = HashBufArr(buf_deque.begin(), buf_deque.end()); 260 | double round_trip_us = double(round_trip_ns) / 1000.0; 261 | constexpr int64_t BITS_PER_BYTE = 8; 262 | auto now_ns_from_epoch = std::chrono::high_resolution_clock::now().time_since_epoch().count(); 263 | last_record_ns = now_ns_from_epoch; 264 | auto to_now_ns = now_ns_from_epoch - start_ns_from_epoch; 265 | double avg_recv_throughput_mbps = double((recv_bytes_sum) * 266 | BITS_PER_BYTE) / (1e+6) / (double(to_now_ns) / (1e+9)); 267 | double avg_send_throughput_mbps = double((send_bytes_sum) * 268 | BITS_PER_BYTE) / (1e+6) / (double(to_now_ns) / (1e+9)); 269 | 270 | if FWS_UNLIKELY(temp_hash != data_hash) { 271 | printf("Hash not the same, original: %zu, now: %zu\n", data_hash, temp_hash); 272 | std::abort(); 273 | } 274 | printf("Avg round trip latency: %.3lf us, throughput " 275 | "rx + tx: %.2lf Mbit/s, hash value: %zu, active fd cnt: %zu\n", 276 | round_trip_us, avg_recv_throughput_mbps + 277 | avg_send_throughput_mbps,temp_hash, 278 | loop.socket_count()); 279 | } 280 | FWS_ASSERT(is_msg_end); 281 | 282 | 283 | 284 | client_ctx.start_write_tick = cpu_timer.Start(); 285 | size_t target_size = temp_buf_->size; 286 | { 287 | ssize_t write_ret = ws_socket.WriteFrame(std::move(*temp_buf_), 288 | static_cast(2U), true); 289 | // FWS_ASSERT(write_ret >= 0); 290 | if FWS_UNLIKELY(write_ret < 0) { 291 | printf("WriteFrame return %zd, %s\n", 292 | write_ret, std::strerror(errno)); 293 | std::abort(); 294 | } 295 | 296 | } 297 | // We add target_size directly because unsent message will be buffered by 298 | // ws_socket 299 | send_bytes_sum += target_size; 300 | *temp_buf_ = fws::RequestBuf(MAX_DATA_LEN + fws::constants::SUGGEST_RESERVE_WS_HDR_SIZE); 301 | temp_buf_->start_pos = fws::constants::SUGGEST_RESERVE_WS_HDR_SIZE; 302 | 303 | 304 | 305 | 306 | } 307 | if (is_msg_end) { 308 | } 309 | 310 | } 311 | else { 312 | if (opcode == fws::WS_OPCODE_PONG) { 313 | FWS_ASSERT(is_frame_end); 314 | FWS_ASSERT(is_msg_end); 315 | } 316 | // fws::ReclaimBuf(io_buf); 317 | } 318 | // 319 | // fws::ReclaimBuf(io_buf); 320 | return ; 321 | }); // SetOnRead 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | return ws_client; 331 | } 332 | }; 333 | 334 | using ClientContext = ContextClass>; 335 | 336 | 337 | int TestWsClient(int argc, char** argv) { 338 | // printf("Prepare to init fws env\n"); 339 | // { 340 | // int code = fws::InitEnv(argc, argv); 341 | // if (code < 0) { 342 | // printf("Error in Init Env\n"); 343 | // return code; 344 | // } 345 | // } 346 | // setbuf(stdout, 0); 347 | // setbuf(stderr, 0); 348 | if (argc < 8) { 349 | printf("Invalid parameters!\nUsage: ./echo_client ip_addr port" 350 | " msg_len msg_cnt_per_client client_cnt client_reborn_cnt" 351 | " output_data_filename\n"); 352 | return -1; 353 | } 354 | 355 | const char* SERVER_IP = argv[1]; 356 | 357 | 358 | int SERVER_PORT = atoi(argv[2]); 359 | if (SERVER_PORT <= 0) { 360 | printf("Invalid port: %s\n", argv[2]); 361 | return -1; 362 | } 363 | long long max_msg_len = atoll(argv[3]); 364 | if (max_msg_len <= 0) { 365 | printf("invalid msg_len: %s\n", argv[3]); 366 | return -1; 367 | } 368 | MAX_DATA_LEN = size_t(max_msg_len); 369 | 370 | long long msg_cnt_per_c = atoll(argv[4]); 371 | if (msg_cnt_per_c <= 0) { 372 | printf("invalid msg_cnt_per_client: %s\n", argv[4]); 373 | return -1; 374 | } 375 | 376 | MSG_LIMIT_PER_CLIENT = msg_cnt_per_c; 377 | TEST_TIMES = size_t(msg_cnt_per_c); 378 | 379 | 380 | int client_cnt = atoi(argv[5]); 381 | if (client_cnt <= 0) { 382 | printf("invalid client_cnt: %s\n", argv[5]); 383 | return -1; 384 | } 385 | 386 | CON_CLIENT_NUM = client_cnt; 387 | 388 | int client_reborn_time = atoi(argv[6]); 389 | if (client_reborn_time <= 0) { 390 | printf("invalid client_reborn_cnt: %s\n", argv[6]); 391 | return -1; 392 | } 393 | REBORN_LIMIT_FOR_CLIENT = client_reborn_time; 394 | 395 | TOTAL_MSG_CNT = msg_cnt_per_c * client_cnt * client_reborn_time; 396 | 397 | const char* data_file_path = argv[7]; 398 | 399 | printf("Set host: %s, port: %d, msg_size: %zu, msg_cnt_per_client: %lld," 400 | "data file path: %s\n", 401 | SERVER_IP, SERVER_PORT, MAX_DATA_LEN, msg_cnt_per_c, data_file_path); 402 | 403 | FWS_ASSERT(fws::InitEnv(argc - 7, argv + 7) >= 0); 404 | // const char* SERVER_IP = argv[1]; 405 | 406 | // int64_t msg_cnt_per_c = MSG_LIMIT_PER_CLIENT; 407 | // int client_cnt = CON_CLIENT_NUM; 408 | // const char* data_file_path = log_data_file_path; 409 | 410 | printf("Set host: %s, port: %d, msg_size: %zu, msg_cnt_per_client: %lld," 411 | "data file path: %s\n", 412 | SERVER_IP, SERVER_PORT, MAX_DATA_LEN, msg_cnt_per_c, data_file_path); 413 | 414 | FILE* output_fp = fopen(data_file_path, "a"); 415 | if (output_fp == nullptr) { 416 | printf("Failed to open %s, %s\n", data_file_path, std::strerror(errno)); 417 | return -1; 418 | } 419 | 420 | std::string request_uri = "/"; 421 | std::string host = std::string(SERVER_IP) + ":" + std::to_string(SERVER_PORT); 422 | 423 | ContextClass> ctx{ 424 | hist::HistWrapper(TOTAL_MSG_CNT + 2, 1LL, 10000000LL)}; 425 | printf("CpuTimer overhead cycles: %lu cycles, tick per ns: %lf\n", 426 | ctx.cpu_timer.overhead_ticks(), 1.0 / ctx.cpu_timer.ns_per_tick()); 427 | 428 | ctx.server_ip = SERVER_IP; 429 | ctx.server_port = SERVER_PORT; 430 | ctx.request_uri = request_uri; 431 | ctx.host = host; 432 | 433 | // fws::WsServer ws_server{}; 434 | // const char* const listen_addr = "10.5.96.3"; 435 | // const char* const listen_addr = "10.5.96.7"; 436 | // uint16_t port = 58600; 437 | // auto fq = fws::CreateFQueue(); 438 | // ctx.fq = std::move(fq); 439 | 440 | // ctx.no_delay = false; 441 | // ctx.busy_poll_us = 0; 442 | //#if ENABLE_NO_DELAY 443 | // ctx.no_delay = true; 444 | //#endif 445 | // if (USE_BUSY_POLL) { 446 | // ctx.busy_poll_us = BUSY_POLL_US; 447 | // } 448 | 449 | ctx.loop = fws::FLoop{}; 450 | if (ctx.loop.Init() < 0) { 451 | printf("Failed to init loop, %s\n", fws::GetErrorStrP()); 452 | return -1; 453 | } 454 | 455 | if constexpr (ENABLE_TLS) { 456 | if (fws::SSLManager::instance().Init(SHOULD_VERIFY_CERT, nullptr, nullptr, 457 | ca_file_path) < 0) { 458 | printf("Failed to init ssl manager, %s\n", fws::GetErrorStrP()); 459 | std::abort(); 460 | } 461 | } 462 | 463 | for (int k = 0; k < client_cnt; ++k) { 464 | auto opt_ws_client = ctx.NewWSClient(); 465 | if FWS_UNLIKELY(!opt_ws_client.has_value()) { 466 | return -1; 467 | } 468 | fws::WSClientSocket ws_client = std::move(opt_ws_client.value()); 469 | 470 | 471 | 472 | 473 | constexpr size_t RESERVE_SIZE = fws::constants::SUGGEST_RESERVE_WS_HDR_SIZE; 474 | auto temp_buf = fws::RequestBuf(MAX_DATA_LEN + RESERVE_SIZE); 475 | temp_buf.start_pos = RESERVE_SIZE; 476 | std::srand(1); 477 | for (size_t i = 0; i < MAX_DATA_LEN; ++i) { 478 | temp_buf.data[RESERVE_SIZE + i] = rand() % 10 + '0'; 479 | } 480 | temp_buf.size = MAX_DATA_LEN; 481 | ctx.data_hash = ctx.HashArr(temp_buf.data + temp_buf.start_pos, temp_buf.size); 482 | // ctx.temp_buf_ = std::move(temp_buf); 483 | // ctx.data_hash = ctx.HashBufArr(&temp_buf, &temp_buf + 1); 484 | // ctx.buf_deque.push_back(std::move(temp_buf)); 485 | // ctx.status_deque.push_back({1U, 1U}); 486 | // 487 | if (k == 0) { 488 | printf("data hash: %lu\n", ctx.data_hash); 489 | } 490 | // int client_fd = ws_client.under_socket().fd(); 491 | 492 | ClientContext::ClientCtx client_ctx{ 1, 0, std::move(temp_buf), 0U}; 493 | auto [add_sock_ret, new_sock_ptr] = ctx.loop.AddSocket(std::move(ws_client), sizeof(ClientContext::ClientCtx), false, std::move(client_ctx)); 494 | if FWS_UNLIKELY(add_sock_ret < 0) { 495 | printf("Failed to add socket to loop, %s\n", fws::GetErrorStrP()); 496 | return -1; 497 | } 498 | // ctx.fd_to_socks.emplace(client_fd, ClientContext::ClientCtx{ 499 | // std::move(ws_client), 500 | // 1, 0, std::move(temp_buf) }); 501 | 502 | } 503 | // ctx.client_fd = client_fd; 504 | 505 | // ctx.fd_to_socks[client_fd] = std::move(ws_client); 506 | // ctx.wait_evs = {MAX_EVENT_NUM, fws::FEvent()}; 507 | // ctx.recv_status_ = {uint8_t(2), uint8_t(1)}; 508 | ctx.out_fp = output_fp; 509 | printf("start to run loop\n"); 510 | ctx.loop.Run(); 511 | // fws::StartRunLoop(OneLoop, &ctx); 512 | return 0; 513 | 514 | } 515 | 516 | } // namespace test 517 | 518 | 519 | int main(int argc, char** argv) { 520 | return test::TestWsClient(argc, argv); 521 | } -------------------------------------------------------------------------------- /src/usockets-tcp/flat_hash_map.h: -------------------------------------------------------------------------------- 1 | // Copyright Malte Skarupke 2017. 2 | // Distributed under the Boost Software License, Version 1.0. 3 | // (See http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #ifdef _MSC_VER 17 | #define SKA_NOINLINE(...) __declspec(noinline) __VA_ARGS__ 18 | #else 19 | #define SKA_NOINLINE(...) __VA_ARGS__ __attribute__((noinline)) 20 | #endif 21 | 22 | namespace ska 23 | { 24 | struct prime_number_hash_policy; 25 | struct power_of_two_hash_policy; 26 | struct fibonacci_hash_policy; 27 | 28 | namespace detailv3 29 | { 30 | template 31 | struct functor_storage : Functor 32 | { 33 | functor_storage() = default; 34 | functor_storage(const Functor & functor) 35 | : Functor(functor) 36 | { 37 | } 38 | template 39 | Result operator()(Args &&... args) 40 | { 41 | return static_cast(*this)(std::forward(args)...); 42 | } 43 | template 44 | Result operator()(Args &&... args) const 45 | { 46 | return static_cast(*this)(std::forward(args)...); 47 | } 48 | }; 49 | template 50 | struct functor_storage 51 | { 52 | typedef Result (*function_ptr)(Args...); 53 | function_ptr function; 54 | functor_storage(function_ptr function) 55 | : function(function) 56 | { 57 | } 58 | Result operator()(Args... args) const 59 | { 60 | return function(std::forward(args)...); 61 | } 62 | operator function_ptr &() 63 | { 64 | return function; 65 | } 66 | operator const function_ptr &() 67 | { 68 | return function; 69 | } 70 | }; 71 | template 72 | struct KeyOrValueHasher : functor_storage 73 | { 74 | typedef functor_storage hasher_storage; 75 | KeyOrValueHasher() = default; 76 | KeyOrValueHasher(const hasher & hash) 77 | : hasher_storage(hash) 78 | { 79 | } 80 | size_t operator()(const key_type & key) 81 | { 82 | return static_cast(*this)(key); 83 | } 84 | size_t operator()(const key_type & key) const 85 | { 86 | return static_cast(*this)(key); 87 | } 88 | size_t operator()(const value_type & value) 89 | { 90 | return static_cast(*this)(value.first); 91 | } 92 | size_t operator()(const value_type & value) const 93 | { 94 | return static_cast(*this)(value.first); 95 | } 96 | template 97 | size_t operator()(const std::pair & value) 98 | { 99 | return static_cast(*this)(value.first); 100 | } 101 | template 102 | size_t operator()(const std::pair & value) const 103 | { 104 | return static_cast(*this)(value.first); 105 | } 106 | }; 107 | template 108 | struct KeyOrValueEquality : functor_storage 109 | { 110 | typedef functor_storage equality_storage; 111 | KeyOrValueEquality() = default; 112 | KeyOrValueEquality(const key_equal & equality) 113 | : equality_storage(equality) 114 | { 115 | } 116 | bool operator()(const key_type & lhs, const key_type & rhs) 117 | { 118 | return static_cast(*this)(lhs, rhs); 119 | } 120 | bool operator()(const key_type & lhs, const value_type & rhs) 121 | { 122 | return static_cast(*this)(lhs, rhs.first); 123 | } 124 | bool operator()(const value_type & lhs, const key_type & rhs) 125 | { 126 | return static_cast(*this)(lhs.first, rhs); 127 | } 128 | bool operator()(const value_type & lhs, const value_type & rhs) 129 | { 130 | return static_cast(*this)(lhs.first, rhs.first); 131 | } 132 | template 133 | bool operator()(const key_type & lhs, const std::pair & rhs) 134 | { 135 | return static_cast(*this)(lhs, rhs.first); 136 | } 137 | template 138 | bool operator()(const std::pair & lhs, const key_type & rhs) 139 | { 140 | return static_cast(*this)(lhs.first, rhs); 141 | } 142 | template 143 | bool operator()(const value_type & lhs, const std::pair & rhs) 144 | { 145 | return static_cast(*this)(lhs.first, rhs.first); 146 | } 147 | template 148 | bool operator()(const std::pair & lhs, const value_type & rhs) 149 | { 150 | return static_cast(*this)(lhs.first, rhs.first); 151 | } 152 | template 153 | bool operator()(const std::pair & lhs, const std::pair & rhs) 154 | { 155 | return static_cast(*this)(lhs.first, rhs.first); 156 | } 157 | }; 158 | static constexpr int8_t min_lookups = 4; 159 | template 160 | struct sherwood_v3_entry 161 | { 162 | sherwood_v3_entry() 163 | { 164 | } 165 | sherwood_v3_entry(int8_t distance_from_desired) 166 | : distance_from_desired(distance_from_desired) 167 | { 168 | } 169 | ~sherwood_v3_entry() 170 | { 171 | } 172 | static sherwood_v3_entry * empty_default_table() 173 | { 174 | static sherwood_v3_entry result[min_lookups] = { {}, {}, {}, {special_end_value} }; 175 | return result; 176 | } 177 | 178 | bool has_value() const 179 | { 180 | return distance_from_desired >= 0; 181 | } 182 | bool is_empty() const 183 | { 184 | return distance_from_desired < 0; 185 | } 186 | bool is_at_desired_position() const 187 | { 188 | return distance_from_desired <= 0; 189 | } 190 | template 191 | void emplace(int8_t distance, Args &&... args) 192 | { 193 | new (std::addressof(value)) T(std::forward(args)...); 194 | distance_from_desired = distance; 195 | } 196 | 197 | void destroy_value() 198 | { 199 | value.~T(); 200 | distance_from_desired = -1; 201 | } 202 | 203 | int8_t distance_from_desired = -1; 204 | static constexpr int8_t special_end_value = 0; 205 | union { T value; }; 206 | }; 207 | 208 | inline int8_t log2(size_t value) 209 | { 210 | static constexpr int8_t table[64] = 211 | { 212 | 63, 0, 58, 1, 59, 47, 53, 2, 213 | 60, 39, 48, 27, 54, 33, 42, 3, 214 | 61, 51, 37, 40, 49, 18, 28, 20, 215 | 55, 30, 34, 11, 43, 14, 22, 4, 216 | 62, 57, 46, 52, 38, 26, 32, 41, 217 | 50, 36, 17, 19, 29, 10, 13, 21, 218 | 56, 45, 25, 31, 35, 16, 9, 12, 219 | 44, 24, 15, 8, 23, 7, 6, 5 220 | }; 221 | value |= value >> 1; 222 | value |= value >> 2; 223 | value |= value >> 4; 224 | value |= value >> 8; 225 | value |= value >> 16; 226 | value |= value >> 32; 227 | return table[((value - (value >> 1)) * 0x07EDD5E59A4E28C2) >> 58]; 228 | } 229 | 230 | template 231 | struct AssignIfTrue 232 | { 233 | void operator()(T & lhs, const T & rhs) 234 | { 235 | lhs = rhs; 236 | } 237 | void operator()(T & lhs, T && rhs) 238 | { 239 | lhs = std::move(rhs); 240 | } 241 | }; 242 | template 243 | struct AssignIfTrue 244 | { 245 | void operator()(T &, const T &) 246 | { 247 | } 248 | void operator()(T &, T &&) 249 | { 250 | } 251 | }; 252 | 253 | inline size_t next_power_of_two(size_t i) 254 | { 255 | --i; 256 | i |= i >> 1; 257 | i |= i >> 2; 258 | i |= i >> 4; 259 | i |= i >> 8; 260 | i |= i >> 16; 261 | i |= i >> 32; 262 | ++i; 263 | return i; 264 | } 265 | 266 | template using void_t = void; 267 | 268 | template 269 | struct HashPolicySelector 270 | { 271 | typedef fibonacci_hash_policy type; 272 | }; 273 | template 274 | struct HashPolicySelector> 275 | { 276 | typedef typename T::hash_policy type; 277 | }; 278 | 279 | template 280 | class sherwood_v3_table : private EntryAlloc, private Hasher, private Equal 281 | { 282 | using Entry = detailv3::sherwood_v3_entry; 283 | using AllocatorTraits = std::allocator_traits; 284 | using EntryPointer = typename AllocatorTraits::pointer; 285 | struct convertible_to_iterator; 286 | 287 | public: 288 | 289 | using value_type = T; 290 | using size_type = size_t; 291 | using difference_type = std::ptrdiff_t; 292 | using hasher = ArgumentHash; 293 | using key_equal = ArgumentEqual; 294 | using allocator_type = EntryAlloc; 295 | using reference = value_type &; 296 | using const_reference = const value_type &; 297 | using pointer = value_type *; 298 | using const_pointer = const value_type *; 299 | 300 | sherwood_v3_table() 301 | { 302 | } 303 | explicit sherwood_v3_table(size_type bucket_count, const ArgumentHash & hash = ArgumentHash(), const ArgumentEqual & equal = ArgumentEqual(), const ArgumentAlloc & alloc = ArgumentAlloc()) 304 | : EntryAlloc(alloc), Hasher(hash), Equal(equal) 305 | { 306 | rehash(bucket_count); 307 | } 308 | sherwood_v3_table(size_type bucket_count, const ArgumentAlloc & alloc) 309 | : sherwood_v3_table(bucket_count, ArgumentHash(), ArgumentEqual(), alloc) 310 | { 311 | } 312 | sherwood_v3_table(size_type bucket_count, const ArgumentHash & hash, const ArgumentAlloc & alloc) 313 | : sherwood_v3_table(bucket_count, hash, ArgumentEqual(), alloc) 314 | { 315 | } 316 | explicit sherwood_v3_table(const ArgumentAlloc & alloc) 317 | : EntryAlloc(alloc) 318 | { 319 | } 320 | template 321 | sherwood_v3_table(It first, It last, size_type bucket_count = 0, const ArgumentHash & hash = ArgumentHash(), const ArgumentEqual & equal = ArgumentEqual(), const ArgumentAlloc & alloc = ArgumentAlloc()) 322 | : sherwood_v3_table(bucket_count, hash, equal, alloc) 323 | { 324 | insert(first, last); 325 | } 326 | template 327 | sherwood_v3_table(It first, It last, size_type bucket_count, const ArgumentAlloc & alloc) 328 | : sherwood_v3_table(first, last, bucket_count, ArgumentHash(), ArgumentEqual(), alloc) 329 | { 330 | } 331 | template 332 | sherwood_v3_table(It first, It last, size_type bucket_count, const ArgumentHash & hash, const ArgumentAlloc & alloc) 333 | : sherwood_v3_table(first, last, bucket_count, hash, ArgumentEqual(), alloc) 334 | { 335 | } 336 | sherwood_v3_table(std::initializer_list il, size_type bucket_count = 0, const ArgumentHash & hash = ArgumentHash(), const ArgumentEqual & equal = ArgumentEqual(), const ArgumentAlloc & alloc = ArgumentAlloc()) 337 | : sherwood_v3_table(bucket_count, hash, equal, alloc) 338 | { 339 | if (bucket_count == 0) 340 | rehash(il.size()); 341 | insert(il.begin(), il.end()); 342 | } 343 | sherwood_v3_table(std::initializer_list il, size_type bucket_count, const ArgumentAlloc & alloc) 344 | : sherwood_v3_table(il, bucket_count, ArgumentHash(), ArgumentEqual(), alloc) 345 | { 346 | } 347 | sherwood_v3_table(std::initializer_list il, size_type bucket_count, const ArgumentHash & hash, const ArgumentAlloc & alloc) 348 | : sherwood_v3_table(il, bucket_count, hash, ArgumentEqual(), alloc) 349 | { 350 | } 351 | sherwood_v3_table(const sherwood_v3_table & other) 352 | : sherwood_v3_table(other, AllocatorTraits::select_on_container_copy_construction(other.get_allocator())) 353 | { 354 | } 355 | sherwood_v3_table(const sherwood_v3_table & other, const ArgumentAlloc & alloc) 356 | : EntryAlloc(alloc), Hasher(other), Equal(other), _max_load_factor(other._max_load_factor) 357 | { 358 | rehash_for_other_container(other); 359 | try 360 | { 361 | insert(other.begin(), other.end()); 362 | } 363 | catch(...) 364 | { 365 | clear(); 366 | deallocate_data(entries, num_slots_minus_one, max_lookups); 367 | throw; 368 | } 369 | } 370 | sherwood_v3_table(sherwood_v3_table && other) noexcept 371 | : EntryAlloc(std::move(other)), Hasher(std::move(other)), Equal(std::move(other)) 372 | { 373 | swap_pointers(other); 374 | } 375 | sherwood_v3_table(sherwood_v3_table && other, const ArgumentAlloc & alloc) noexcept 376 | : EntryAlloc(alloc), Hasher(std::move(other)), Equal(std::move(other)) 377 | { 378 | swap_pointers(other); 379 | } 380 | sherwood_v3_table & operator=(const sherwood_v3_table & other) 381 | { 382 | if (this == std::addressof(other)) 383 | return *this; 384 | 385 | clear(); 386 | if (AllocatorTraits::propagate_on_container_copy_assignment::value) 387 | { 388 | if (static_cast(*this) != static_cast(other)) 389 | { 390 | reset_to_empty_state(); 391 | } 392 | AssignIfTrue()(*this, other); 393 | } 394 | _max_load_factor = other._max_load_factor; 395 | static_cast(*this) = other; 396 | static_cast(*this) = other; 397 | rehash_for_other_container(other); 398 | insert(other.begin(), other.end()); 399 | return *this; 400 | } 401 | sherwood_v3_table & operator=(sherwood_v3_table && other) noexcept 402 | { 403 | if (this == std::addressof(other)) 404 | return *this; 405 | else if (AllocatorTraits::propagate_on_container_move_assignment::value) 406 | { 407 | clear(); 408 | reset_to_empty_state(); 409 | AssignIfTrue()(*this, std::move(other)); 410 | swap_pointers(other); 411 | } 412 | else if (static_cast(*this) == static_cast(other)) 413 | { 414 | swap_pointers(other); 415 | } 416 | else 417 | { 418 | clear(); 419 | _max_load_factor = other._max_load_factor; 420 | rehash_for_other_container(other); 421 | for (T & elem : other) 422 | emplace(std::move(elem)); 423 | other.clear(); 424 | } 425 | static_cast(*this) = std::move(other); 426 | static_cast(*this) = std::move(other); 427 | return *this; 428 | } 429 | ~sherwood_v3_table() 430 | { 431 | clear(); 432 | deallocate_data(entries, num_slots_minus_one, max_lookups); 433 | } 434 | 435 | const allocator_type & get_allocator() const 436 | { 437 | return static_cast(*this); 438 | } 439 | const ArgumentEqual & key_eq() const 440 | { 441 | return static_cast(*this); 442 | } 443 | const ArgumentHash & hash_function() const 444 | { 445 | return static_cast(*this); 446 | } 447 | 448 | template 449 | struct templated_iterator 450 | { 451 | templated_iterator() = default; 452 | templated_iterator(EntryPointer current) 453 | : current(current) 454 | { 455 | } 456 | EntryPointer current = EntryPointer(); 457 | 458 | using iterator_category = std::forward_iterator_tag; 459 | using value_type = ValueType; 460 | using difference_type = ptrdiff_t; 461 | using pointer = ValueType *; 462 | using reference = ValueType &; 463 | 464 | friend bool operator==(const templated_iterator & lhs, const templated_iterator & rhs) 465 | { 466 | return lhs.current == rhs.current; 467 | } 468 | friend bool operator!=(const templated_iterator & lhs, const templated_iterator & rhs) 469 | { 470 | return !(lhs == rhs); 471 | } 472 | 473 | templated_iterator & operator++() 474 | { 475 | do 476 | { 477 | ++current; 478 | } 479 | while(current->is_empty()); 480 | return *this; 481 | } 482 | templated_iterator operator++(int) 483 | { 484 | templated_iterator copy(*this); 485 | ++*this; 486 | return copy; 487 | } 488 | 489 | ValueType & operator*() const 490 | { 491 | return current->value; 492 | } 493 | ValueType * operator->() const 494 | { 495 | return std::addressof(current->value); 496 | } 497 | 498 | operator templated_iterator() const 499 | { 500 | return { current }; 501 | } 502 | }; 503 | using iterator = templated_iterator; 504 | using const_iterator = templated_iterator; 505 | 506 | iterator begin() 507 | { 508 | for (EntryPointer it = entries;; ++it) 509 | { 510 | if (it->has_value()) 511 | return { it }; 512 | } 513 | } 514 | const_iterator begin() const 515 | { 516 | for (EntryPointer it = entries;; ++it) 517 | { 518 | if (it->has_value()) 519 | return { it }; 520 | } 521 | } 522 | const_iterator cbegin() const 523 | { 524 | return begin(); 525 | } 526 | iterator end() 527 | { 528 | return { entries + static_cast(num_slots_minus_one + max_lookups) }; 529 | } 530 | const_iterator end() const 531 | { 532 | return { entries + static_cast(num_slots_minus_one + max_lookups) }; 533 | } 534 | const_iterator cend() const 535 | { 536 | return end(); 537 | } 538 | 539 | iterator find(const FindKey & key) 540 | { 541 | size_t index = hash_policy.index_for_hash(hash_object(key), num_slots_minus_one); 542 | EntryPointer it = entries + ptrdiff_t(index); 543 | for (int8_t distance = 0; it->distance_from_desired >= distance; ++distance, ++it) 544 | { 545 | if (compares_equal(key, it->value)) 546 | return { it }; 547 | } 548 | return end(); 549 | } 550 | const_iterator find(const FindKey & key) const 551 | { 552 | return const_cast(this)->find(key); 553 | } 554 | size_t count(const FindKey & key) const 555 | { 556 | return find(key) == end() ? 0 : 1; 557 | } 558 | std::pair equal_range(const FindKey & key) 559 | { 560 | iterator found = find(key); 561 | if (found == end()) 562 | return { found, found }; 563 | else 564 | return { found, std::next(found) }; 565 | } 566 | std::pair equal_range(const FindKey & key) const 567 | { 568 | const_iterator found = find(key); 569 | if (found == end()) 570 | return { found, found }; 571 | else 572 | return { found, std::next(found) }; 573 | } 574 | 575 | template 576 | std::pair emplace(Key && key, Args &&... args) 577 | { 578 | size_t index = hash_policy.index_for_hash(hash_object(key), num_slots_minus_one); 579 | EntryPointer current_entry = entries + ptrdiff_t(index); 580 | int8_t distance_from_desired = 0; 581 | for (; current_entry->distance_from_desired >= distance_from_desired; ++current_entry, ++distance_from_desired) 582 | { 583 | if (compares_equal(key, current_entry->value)) 584 | return { { current_entry }, false }; 585 | } 586 | return emplace_new_key(distance_from_desired, current_entry, std::forward(key), std::forward(args)...); 587 | } 588 | 589 | std::pair insert(const value_type & value) 590 | { 591 | return emplace(value); 592 | } 593 | std::pair insert(value_type && value) 594 | { 595 | return emplace(std::move(value)); 596 | } 597 | template 598 | iterator emplace_hint(const_iterator, Args &&... args) 599 | { 600 | return emplace(std::forward(args)...).first; 601 | } 602 | iterator insert(const_iterator, const value_type & value) 603 | { 604 | return emplace(value).first; 605 | } 606 | iterator insert(const_iterator, value_type && value) 607 | { 608 | return emplace(std::move(value)).first; 609 | } 610 | 611 | template 612 | void insert(It begin, It end) 613 | { 614 | for (; begin != end; ++begin) 615 | { 616 | emplace(*begin); 617 | } 618 | } 619 | void insert(std::initializer_list il) 620 | { 621 | insert(il.begin(), il.end()); 622 | } 623 | 624 | void rehash(size_t num_buckets) 625 | { 626 | num_buckets = std::max(num_buckets, static_cast(std::ceil(num_elements / static_cast(_max_load_factor)))); 627 | if (num_buckets == 0) 628 | { 629 | reset_to_empty_state(); 630 | return; 631 | } 632 | auto new_prime_index = hash_policy.next_size_over(num_buckets); 633 | if (num_buckets == bucket_count()) 634 | return; 635 | int8_t new_max_lookups = compute_max_lookups(num_buckets); 636 | EntryPointer new_buckets(AllocatorTraits::allocate(*this, num_buckets + new_max_lookups)); 637 | EntryPointer special_end_item = new_buckets + static_cast(num_buckets + new_max_lookups - 1); 638 | for (EntryPointer it = new_buckets; it != special_end_item; ++it) 639 | it->distance_from_desired = -1; 640 | special_end_item->distance_from_desired = Entry::special_end_value; 641 | std::swap(entries, new_buckets); 642 | std::swap(num_slots_minus_one, num_buckets); 643 | --num_slots_minus_one; 644 | hash_policy.commit(new_prime_index); 645 | int8_t old_max_lookups = max_lookups; 646 | max_lookups = new_max_lookups; 647 | num_elements = 0; 648 | for (EntryPointer it = new_buckets, end = it + static_cast(num_buckets + old_max_lookups); it != end; ++it) 649 | { 650 | if (it->has_value()) 651 | { 652 | emplace(std::move(it->value)); 653 | it->destroy_value(); 654 | } 655 | } 656 | deallocate_data(new_buckets, num_buckets, old_max_lookups); 657 | } 658 | 659 | void reserve(size_t num_elements) 660 | { 661 | size_t required_buckets = num_buckets_for_reserve(num_elements); 662 | if (required_buckets > bucket_count()) 663 | rehash(required_buckets); 664 | } 665 | 666 | // the return value is a type that can be converted to an iterator 667 | // the reason for doing this is that it's not free to find the 668 | // iterator pointing at the next element. if you care about the 669 | // next iterator, turn the return value into an iterator 670 | convertible_to_iterator erase(const_iterator to_erase) 671 | { 672 | EntryPointer current = to_erase.current; 673 | current->destroy_value(); 674 | --num_elements; 675 | for (EntryPointer next = current + ptrdiff_t(1); !next->is_at_desired_position(); ++current, ++next) 676 | { 677 | current->emplace(next->distance_from_desired - 1, std::move(next->value)); 678 | next->destroy_value(); 679 | } 680 | return { to_erase.current }; 681 | } 682 | 683 | iterator erase(const_iterator begin_it, const_iterator end_it) 684 | { 685 | if (begin_it == end_it) 686 | return { begin_it.current }; 687 | for (EntryPointer it = begin_it.current, end = end_it.current; it != end; ++it) 688 | { 689 | if (it->has_value()) 690 | { 691 | it->destroy_value(); 692 | --num_elements; 693 | } 694 | } 695 | if (end_it == this->end()) 696 | return this->end(); 697 | ptrdiff_t num_to_move = std::min(static_cast(end_it.current->distance_from_desired), end_it.current - begin_it.current); 698 | EntryPointer to_return = end_it.current - num_to_move; 699 | for (EntryPointer it = end_it.current; !it->is_at_desired_position();) 700 | { 701 | EntryPointer target = it - num_to_move; 702 | target->emplace(it->distance_from_desired - num_to_move, std::move(it->value)); 703 | it->destroy_value(); 704 | ++it; 705 | num_to_move = std::min(static_cast(it->distance_from_desired), num_to_move); 706 | } 707 | return { to_return }; 708 | } 709 | 710 | size_t erase(const FindKey & key) 711 | { 712 | auto found = find(key); 713 | if (found == end()) 714 | return 0; 715 | else 716 | { 717 | erase(found); 718 | return 1; 719 | } 720 | } 721 | 722 | void clear() 723 | { 724 | for (EntryPointer it = entries, end = it + static_cast(num_slots_minus_one + max_lookups); it != end; ++it) 725 | { 726 | if (it->has_value()) 727 | it->destroy_value(); 728 | } 729 | num_elements = 0; 730 | } 731 | 732 | void shrink_to_fit() 733 | { 734 | rehash_for_other_container(*this); 735 | } 736 | 737 | void swap(sherwood_v3_table & other) 738 | { 739 | using std::swap; 740 | swap_pointers(other); 741 | swap(static_cast(*this), static_cast(other)); 742 | swap(static_cast(*this), static_cast(other)); 743 | if (AllocatorTraits::propagate_on_container_swap::value) 744 | swap(static_cast(*this), static_cast(other)); 745 | } 746 | 747 | size_t size() const 748 | { 749 | return num_elements; 750 | } 751 | size_t max_size() const 752 | { 753 | return (AllocatorTraits::max_size(*this)) / sizeof(Entry); 754 | } 755 | size_t bucket_count() const 756 | { 757 | return num_slots_minus_one ? num_slots_minus_one + 1 : 0; 758 | } 759 | size_type max_bucket_count() const 760 | { 761 | return (AllocatorTraits::max_size(*this) - min_lookups) / sizeof(Entry); 762 | } 763 | size_t bucket(const FindKey & key) const 764 | { 765 | return hash_policy.index_for_hash(hash_object(key), num_slots_minus_one); 766 | } 767 | float load_factor() const 768 | { 769 | size_t buckets = bucket_count(); 770 | if (buckets) 771 | return static_cast(num_elements) / bucket_count(); 772 | else 773 | return 0; 774 | } 775 | void max_load_factor(float value) 776 | { 777 | _max_load_factor = value; 778 | } 779 | float max_load_factor() const 780 | { 781 | return _max_load_factor; 782 | } 783 | 784 | bool empty() const 785 | { 786 | return num_elements == 0; 787 | } 788 | 789 | private: 790 | EntryPointer entries = Entry::empty_default_table(); 791 | size_t num_slots_minus_one = 0; 792 | typename HashPolicySelector::type hash_policy; 793 | int8_t max_lookups = detailv3::min_lookups - 1; 794 | float _max_load_factor = 0.5f; 795 | size_t num_elements = 0; 796 | 797 | static int8_t compute_max_lookups(size_t num_buckets) 798 | { 799 | int8_t desired = detailv3::log2(num_buckets); 800 | return std::max(detailv3::min_lookups, desired); 801 | } 802 | 803 | size_t num_buckets_for_reserve(size_t num_elements) const 804 | { 805 | return static_cast(std::ceil(num_elements / std::min(0.5, static_cast(_max_load_factor)))); 806 | } 807 | void rehash_for_other_container(const sherwood_v3_table & other) 808 | { 809 | rehash(std::min(num_buckets_for_reserve(other.size()), other.bucket_count())); 810 | } 811 | 812 | void swap_pointers(sherwood_v3_table & other) 813 | { 814 | using std::swap; 815 | swap(hash_policy, other.hash_policy); 816 | swap(entries, other.entries); 817 | swap(num_slots_minus_one, other.num_slots_minus_one); 818 | swap(num_elements, other.num_elements); 819 | swap(max_lookups, other.max_lookups); 820 | swap(_max_load_factor, other._max_load_factor); 821 | } 822 | 823 | template 824 | SKA_NOINLINE(std::pair) emplace_new_key(int8_t distance_from_desired, EntryPointer current_entry, Key && key, Args &&... args) 825 | { 826 | using std::swap; 827 | if (num_slots_minus_one == 0 || distance_from_desired == max_lookups || num_elements + 1 > (num_slots_minus_one + 1) * static_cast(_max_load_factor)) 828 | { 829 | grow(); 830 | return emplace(std::forward(key), std::forward(args)...); 831 | } 832 | else if (current_entry->is_empty()) 833 | { 834 | current_entry->emplace(distance_from_desired, std::forward(key), std::forward(args)...); 835 | ++num_elements; 836 | return { { current_entry }, true }; 837 | } 838 | value_type to_insert(std::forward(key), std::forward(args)...); 839 | swap(distance_from_desired, current_entry->distance_from_desired); 840 | swap(to_insert, current_entry->value); 841 | iterator result = { current_entry }; 842 | for (++distance_from_desired, ++current_entry;; ++current_entry) 843 | { 844 | if (current_entry->is_empty()) 845 | { 846 | current_entry->emplace(distance_from_desired, std::move(to_insert)); 847 | ++num_elements; 848 | return { result, true }; 849 | } 850 | else if (current_entry->distance_from_desired < distance_from_desired) 851 | { 852 | swap(distance_from_desired, current_entry->distance_from_desired); 853 | swap(to_insert, current_entry->value); 854 | ++distance_from_desired; 855 | } 856 | else 857 | { 858 | ++distance_from_desired; 859 | if (distance_from_desired == max_lookups) 860 | { 861 | swap(to_insert, result.current->value); 862 | grow(); 863 | return emplace(std::move(to_insert)); 864 | } 865 | } 866 | } 867 | } 868 | 869 | void grow() 870 | { 871 | rehash(std::max(size_t(4), 2 * bucket_count())); 872 | } 873 | 874 | void deallocate_data(EntryPointer begin, size_t num_slots_minus_one, int8_t max_lookups) 875 | { 876 | if (begin != Entry::empty_default_table()) 877 | { 878 | AllocatorTraits::deallocate(*this, begin, num_slots_minus_one + max_lookups + 1); 879 | } 880 | } 881 | 882 | void reset_to_empty_state() 883 | { 884 | deallocate_data(entries, num_slots_minus_one, max_lookups); 885 | entries = Entry::empty_default_table(); 886 | num_slots_minus_one = 0; 887 | hash_policy.reset(); 888 | max_lookups = detailv3::min_lookups - 1; 889 | } 890 | 891 | template 892 | size_t hash_object(const U & key) 893 | { 894 | return static_cast(*this)(key); 895 | } 896 | template 897 | size_t hash_object(const U & key) const 898 | { 899 | return static_cast(*this)(key); 900 | } 901 | template 902 | bool compares_equal(const L & lhs, const R & rhs) 903 | { 904 | return static_cast(*this)(lhs, rhs); 905 | } 906 | 907 | struct convertible_to_iterator 908 | { 909 | EntryPointer it; 910 | 911 | operator iterator() 912 | { 913 | if (it->has_value()) 914 | return { it }; 915 | else 916 | return ++iterator{it}; 917 | } 918 | operator const_iterator() 919 | { 920 | if (it->has_value()) 921 | return { it }; 922 | else 923 | return ++const_iterator{it}; 924 | } 925 | }; 926 | 927 | }; 928 | } 929 | 930 | struct prime_number_hash_policy 931 | { 932 | static size_t mod0(size_t) { return 0llu; } 933 | static size_t mod2(size_t hash) { return hash % 2llu; } 934 | static size_t mod3(size_t hash) { return hash % 3llu; } 935 | static size_t mod5(size_t hash) { return hash % 5llu; } 936 | static size_t mod7(size_t hash) { return hash % 7llu; } 937 | static size_t mod11(size_t hash) { return hash % 11llu; } 938 | static size_t mod13(size_t hash) { return hash % 13llu; } 939 | static size_t mod17(size_t hash) { return hash % 17llu; } 940 | static size_t mod23(size_t hash) { return hash % 23llu; } 941 | static size_t mod29(size_t hash) { return hash % 29llu; } 942 | static size_t mod37(size_t hash) { return hash % 37llu; } 943 | static size_t mod47(size_t hash) { return hash % 47llu; } 944 | static size_t mod59(size_t hash) { return hash % 59llu; } 945 | static size_t mod73(size_t hash) { return hash % 73llu; } 946 | static size_t mod97(size_t hash) { return hash % 97llu; } 947 | static size_t mod127(size_t hash) { return hash % 127llu; } 948 | static size_t mod151(size_t hash) { return hash % 151llu; } 949 | static size_t mod197(size_t hash) { return hash % 197llu; } 950 | static size_t mod251(size_t hash) { return hash % 251llu; } 951 | static size_t mod313(size_t hash) { return hash % 313llu; } 952 | static size_t mod397(size_t hash) { return hash % 397llu; } 953 | static size_t mod499(size_t hash) { return hash % 499llu; } 954 | static size_t mod631(size_t hash) { return hash % 631llu; } 955 | static size_t mod797(size_t hash) { return hash % 797llu; } 956 | static size_t mod1009(size_t hash) { return hash % 1009llu; } 957 | static size_t mod1259(size_t hash) { return hash % 1259llu; } 958 | static size_t mod1597(size_t hash) { return hash % 1597llu; } 959 | static size_t mod2011(size_t hash) { return hash % 2011llu; } 960 | static size_t mod2539(size_t hash) { return hash % 2539llu; } 961 | static size_t mod3203(size_t hash) { return hash % 3203llu; } 962 | static size_t mod4027(size_t hash) { return hash % 4027llu; } 963 | static size_t mod5087(size_t hash) { return hash % 5087llu; } 964 | static size_t mod6421(size_t hash) { return hash % 6421llu; } 965 | static size_t mod8089(size_t hash) { return hash % 8089llu; } 966 | static size_t mod10193(size_t hash) { return hash % 10193llu; } 967 | static size_t mod12853(size_t hash) { return hash % 12853llu; } 968 | static size_t mod16193(size_t hash) { return hash % 16193llu; } 969 | static size_t mod20399(size_t hash) { return hash % 20399llu; } 970 | static size_t mod25717(size_t hash) { return hash % 25717llu; } 971 | static size_t mod32401(size_t hash) { return hash % 32401llu; } 972 | static size_t mod40823(size_t hash) { return hash % 40823llu; } 973 | static size_t mod51437(size_t hash) { return hash % 51437llu; } 974 | static size_t mod64811(size_t hash) { return hash % 64811llu; } 975 | static size_t mod81649(size_t hash) { return hash % 81649llu; } 976 | static size_t mod102877(size_t hash) { return hash % 102877llu; } 977 | static size_t mod129607(size_t hash) { return hash % 129607llu; } 978 | static size_t mod163307(size_t hash) { return hash % 163307llu; } 979 | static size_t mod205759(size_t hash) { return hash % 205759llu; } 980 | static size_t mod259229(size_t hash) { return hash % 259229llu; } 981 | static size_t mod326617(size_t hash) { return hash % 326617llu; } 982 | static size_t mod411527(size_t hash) { return hash % 411527llu; } 983 | static size_t mod518509(size_t hash) { return hash % 518509llu; } 984 | static size_t mod653267(size_t hash) { return hash % 653267llu; } 985 | static size_t mod823117(size_t hash) { return hash % 823117llu; } 986 | static size_t mod1037059(size_t hash) { return hash % 1037059llu; } 987 | static size_t mod1306601(size_t hash) { return hash % 1306601llu; } 988 | static size_t mod1646237(size_t hash) { return hash % 1646237llu; } 989 | static size_t mod2074129(size_t hash) { return hash % 2074129llu; } 990 | static size_t mod2613229(size_t hash) { return hash % 2613229llu; } 991 | static size_t mod3292489(size_t hash) { return hash % 3292489llu; } 992 | static size_t mod4148279(size_t hash) { return hash % 4148279llu; } 993 | static size_t mod5226491(size_t hash) { return hash % 5226491llu; } 994 | static size_t mod6584983(size_t hash) { return hash % 6584983llu; } 995 | static size_t mod8296553(size_t hash) { return hash % 8296553llu; } 996 | static size_t mod10453007(size_t hash) { return hash % 10453007llu; } 997 | static size_t mod13169977(size_t hash) { return hash % 13169977llu; } 998 | static size_t mod16593127(size_t hash) { return hash % 16593127llu; } 999 | static size_t mod20906033(size_t hash) { return hash % 20906033llu; } 1000 | static size_t mod26339969(size_t hash) { return hash % 26339969llu; } 1001 | static size_t mod33186281(size_t hash) { return hash % 33186281llu; } 1002 | static size_t mod41812097(size_t hash) { return hash % 41812097llu; } 1003 | static size_t mod52679969(size_t hash) { return hash % 52679969llu; } 1004 | static size_t mod66372617(size_t hash) { return hash % 66372617llu; } 1005 | static size_t mod83624237(size_t hash) { return hash % 83624237llu; } 1006 | static size_t mod105359939(size_t hash) { return hash % 105359939llu; } 1007 | static size_t mod132745199(size_t hash) { return hash % 132745199llu; } 1008 | static size_t mod167248483(size_t hash) { return hash % 167248483llu; } 1009 | static size_t mod210719881(size_t hash) { return hash % 210719881llu; } 1010 | static size_t mod265490441(size_t hash) { return hash % 265490441llu; } 1011 | static size_t mod334496971(size_t hash) { return hash % 334496971llu; } 1012 | static size_t mod421439783(size_t hash) { return hash % 421439783llu; } 1013 | static size_t mod530980861(size_t hash) { return hash % 530980861llu; } 1014 | static size_t mod668993977(size_t hash) { return hash % 668993977llu; } 1015 | static size_t mod842879579(size_t hash) { return hash % 842879579llu; } 1016 | static size_t mod1061961721(size_t hash) { return hash % 1061961721llu; } 1017 | static size_t mod1337987929(size_t hash) { return hash % 1337987929llu; } 1018 | static size_t mod1685759167(size_t hash) { return hash % 1685759167llu; } 1019 | static size_t mod2123923447(size_t hash) { return hash % 2123923447llu; } 1020 | static size_t mod2675975881(size_t hash) { return hash % 2675975881llu; } 1021 | static size_t mod3371518343(size_t hash) { return hash % 3371518343llu; } 1022 | static size_t mod4247846927(size_t hash) { return hash % 4247846927llu; } 1023 | static size_t mod5351951779(size_t hash) { return hash % 5351951779llu; } 1024 | static size_t mod6743036717(size_t hash) { return hash % 6743036717llu; } 1025 | static size_t mod8495693897(size_t hash) { return hash % 8495693897llu; } 1026 | static size_t mod10703903591(size_t hash) { return hash % 10703903591llu; } 1027 | static size_t mod13486073473(size_t hash) { return hash % 13486073473llu; } 1028 | static size_t mod16991387857(size_t hash) { return hash % 16991387857llu; } 1029 | static size_t mod21407807219(size_t hash) { return hash % 21407807219llu; } 1030 | static size_t mod26972146961(size_t hash) { return hash % 26972146961llu; } 1031 | static size_t mod33982775741(size_t hash) { return hash % 33982775741llu; } 1032 | static size_t mod42815614441(size_t hash) { return hash % 42815614441llu; } 1033 | static size_t mod53944293929(size_t hash) { return hash % 53944293929llu; } 1034 | static size_t mod67965551447(size_t hash) { return hash % 67965551447llu; } 1035 | static size_t mod85631228929(size_t hash) { return hash % 85631228929llu; } 1036 | static size_t mod107888587883(size_t hash) { return hash % 107888587883llu; } 1037 | static size_t mod135931102921(size_t hash) { return hash % 135931102921llu; } 1038 | static size_t mod171262457903(size_t hash) { return hash % 171262457903llu; } 1039 | static size_t mod215777175787(size_t hash) { return hash % 215777175787llu; } 1040 | static size_t mod271862205833(size_t hash) { return hash % 271862205833llu; } 1041 | static size_t mod342524915839(size_t hash) { return hash % 342524915839llu; } 1042 | static size_t mod431554351609(size_t hash) { return hash % 431554351609llu; } 1043 | static size_t mod543724411781(size_t hash) { return hash % 543724411781llu; } 1044 | static size_t mod685049831731(size_t hash) { return hash % 685049831731llu; } 1045 | static size_t mod863108703229(size_t hash) { return hash % 863108703229llu; } 1046 | static size_t mod1087448823553(size_t hash) { return hash % 1087448823553llu; } 1047 | static size_t mod1370099663459(size_t hash) { return hash % 1370099663459llu; } 1048 | static size_t mod1726217406467(size_t hash) { return hash % 1726217406467llu; } 1049 | static size_t mod2174897647073(size_t hash) { return hash % 2174897647073llu; } 1050 | static size_t mod2740199326961(size_t hash) { return hash % 2740199326961llu; } 1051 | static size_t mod3452434812973(size_t hash) { return hash % 3452434812973llu; } 1052 | static size_t mod4349795294267(size_t hash) { return hash % 4349795294267llu; } 1053 | static size_t mod5480398654009(size_t hash) { return hash % 5480398654009llu; } 1054 | static size_t mod6904869625999(size_t hash) { return hash % 6904869625999llu; } 1055 | static size_t mod8699590588571(size_t hash) { return hash % 8699590588571llu; } 1056 | static size_t mod10960797308051(size_t hash) { return hash % 10960797308051llu; } 1057 | static size_t mod13809739252051(size_t hash) { return hash % 13809739252051llu; } 1058 | static size_t mod17399181177241(size_t hash) { return hash % 17399181177241llu; } 1059 | static size_t mod21921594616111(size_t hash) { return hash % 21921594616111llu; } 1060 | static size_t mod27619478504183(size_t hash) { return hash % 27619478504183llu; } 1061 | static size_t mod34798362354533(size_t hash) { return hash % 34798362354533llu; } 1062 | static size_t mod43843189232363(size_t hash) { return hash % 43843189232363llu; } 1063 | static size_t mod55238957008387(size_t hash) { return hash % 55238957008387llu; } 1064 | static size_t mod69596724709081(size_t hash) { return hash % 69596724709081llu; } 1065 | static size_t mod87686378464759(size_t hash) { return hash % 87686378464759llu; } 1066 | static size_t mod110477914016779(size_t hash) { return hash % 110477914016779llu; } 1067 | static size_t mod139193449418173(size_t hash) { return hash % 139193449418173llu; } 1068 | static size_t mod175372756929481(size_t hash) { return hash % 175372756929481llu; } 1069 | static size_t mod220955828033581(size_t hash) { return hash % 220955828033581llu; } 1070 | static size_t mod278386898836457(size_t hash) { return hash % 278386898836457llu; } 1071 | static size_t mod350745513859007(size_t hash) { return hash % 350745513859007llu; } 1072 | static size_t mod441911656067171(size_t hash) { return hash % 441911656067171llu; } 1073 | static size_t mod556773797672909(size_t hash) { return hash % 556773797672909llu; } 1074 | static size_t mod701491027718027(size_t hash) { return hash % 701491027718027llu; } 1075 | static size_t mod883823312134381(size_t hash) { return hash % 883823312134381llu; } 1076 | static size_t mod1113547595345903(size_t hash) { return hash % 1113547595345903llu; } 1077 | static size_t mod1402982055436147(size_t hash) { return hash % 1402982055436147llu; } 1078 | static size_t mod1767646624268779(size_t hash) { return hash % 1767646624268779llu; } 1079 | static size_t mod2227095190691797(size_t hash) { return hash % 2227095190691797llu; } 1080 | static size_t mod2805964110872297(size_t hash) { return hash % 2805964110872297llu; } 1081 | static size_t mod3535293248537579(size_t hash) { return hash % 3535293248537579llu; } 1082 | static size_t mod4454190381383713(size_t hash) { return hash % 4454190381383713llu; } 1083 | static size_t mod5611928221744609(size_t hash) { return hash % 5611928221744609llu; } 1084 | static size_t mod7070586497075177(size_t hash) { return hash % 7070586497075177llu; } 1085 | static size_t mod8908380762767489(size_t hash) { return hash % 8908380762767489llu; } 1086 | static size_t mod11223856443489329(size_t hash) { return hash % 11223856443489329llu; } 1087 | static size_t mod14141172994150357(size_t hash) { return hash % 14141172994150357llu; } 1088 | static size_t mod17816761525534927(size_t hash) { return hash % 17816761525534927llu; } 1089 | static size_t mod22447712886978529(size_t hash) { return hash % 22447712886978529llu; } 1090 | static size_t mod28282345988300791(size_t hash) { return hash % 28282345988300791llu; } 1091 | static size_t mod35633523051069991(size_t hash) { return hash % 35633523051069991llu; } 1092 | static size_t mod44895425773957261(size_t hash) { return hash % 44895425773957261llu; } 1093 | static size_t mod56564691976601587(size_t hash) { return hash % 56564691976601587llu; } 1094 | static size_t mod71267046102139967(size_t hash) { return hash % 71267046102139967llu; } 1095 | static size_t mod89790851547914507(size_t hash) { return hash % 89790851547914507llu; } 1096 | static size_t mod113129383953203213(size_t hash) { return hash % 113129383953203213llu; } 1097 | static size_t mod142534092204280003(size_t hash) { return hash % 142534092204280003llu; } 1098 | static size_t mod179581703095829107(size_t hash) { return hash % 179581703095829107llu; } 1099 | static size_t mod226258767906406483(size_t hash) { return hash % 226258767906406483llu; } 1100 | static size_t mod285068184408560057(size_t hash) { return hash % 285068184408560057llu; } 1101 | static size_t mod359163406191658253(size_t hash) { return hash % 359163406191658253llu; } 1102 | static size_t mod452517535812813007(size_t hash) { return hash % 452517535812813007llu; } 1103 | static size_t mod570136368817120201(size_t hash) { return hash % 570136368817120201llu; } 1104 | static size_t mod718326812383316683(size_t hash) { return hash % 718326812383316683llu; } 1105 | static size_t mod905035071625626043(size_t hash) { return hash % 905035071625626043llu; } 1106 | static size_t mod1140272737634240411(size_t hash) { return hash % 1140272737634240411llu; } 1107 | static size_t mod1436653624766633509(size_t hash) { return hash % 1436653624766633509llu; } 1108 | static size_t mod1810070143251252131(size_t hash) { return hash % 1810070143251252131llu; } 1109 | static size_t mod2280545475268481167(size_t hash) { return hash % 2280545475268481167llu; } 1110 | static size_t mod2873307249533267101(size_t hash) { return hash % 2873307249533267101llu; } 1111 | static size_t mod3620140286502504283(size_t hash) { return hash % 3620140286502504283llu; } 1112 | static size_t mod4561090950536962147(size_t hash) { return hash % 4561090950536962147llu; } 1113 | static size_t mod5746614499066534157(size_t hash) { return hash % 5746614499066534157llu; } 1114 | static size_t mod7240280573005008577(size_t hash) { return hash % 7240280573005008577llu; } 1115 | static size_t mod9122181901073924329(size_t hash) { return hash % 9122181901073924329llu; } 1116 | static size_t mod11493228998133068689(size_t hash) { return hash % 11493228998133068689llu; } 1117 | static size_t mod14480561146010017169(size_t hash) { return hash % 14480561146010017169llu; } 1118 | static size_t mod18446744073709551557(size_t hash) { return hash % 18446744073709551557llu; } 1119 | 1120 | using mod_function = size_t (*)(size_t); 1121 | 1122 | mod_function next_size_over(size_t & size) const 1123 | { 1124 | // prime numbers generated by the following method: 1125 | // 1. start with a prime p = 2 1126 | // 2. go to wolfram alpha and get p = NextPrime(2 * p) 1127 | // 3. repeat 2. until you overflow 64 bits 1128 | // you now have large gaps which you would hit if somebody called reserve() with an unlucky number. 1129 | // 4. to fill the gaps for every prime p go to wolfram alpha and get ClosestPrime(p * 2^(1/3)) and ClosestPrime(p * 2^(2/3)) and put those in the gaps 1130 | // 5. get PrevPrime(2^64) and put it at the end 1131 | static constexpr const size_t prime_list[] = 1132 | { 1133 | 2llu, 3llu, 5llu, 7llu, 11llu, 13llu, 17llu, 23llu, 29llu, 37llu, 47llu, 1134 | 59llu, 73llu, 97llu, 127llu, 151llu, 197llu, 251llu, 313llu, 397llu, 1135 | 499llu, 631llu, 797llu, 1009llu, 1259llu, 1597llu, 2011llu, 2539llu, 1136 | 3203llu, 4027llu, 5087llu, 6421llu, 8089llu, 10193llu, 12853llu, 16193llu, 1137 | 20399llu, 25717llu, 32401llu, 40823llu, 51437llu, 64811llu, 81649llu, 1138 | 102877llu, 129607llu, 163307llu, 205759llu, 259229llu, 326617llu, 1139 | 411527llu, 518509llu, 653267llu, 823117llu, 1037059llu, 1306601llu, 1140 | 1646237llu, 2074129llu, 2613229llu, 3292489llu, 4148279llu, 5226491llu, 1141 | 6584983llu, 8296553llu, 10453007llu, 13169977llu, 16593127llu, 20906033llu, 1142 | 26339969llu, 33186281llu, 41812097llu, 52679969llu, 66372617llu, 1143 | 83624237llu, 105359939llu, 132745199llu, 167248483llu, 210719881llu, 1144 | 265490441llu, 334496971llu, 421439783llu, 530980861llu, 668993977llu, 1145 | 842879579llu, 1061961721llu, 1337987929llu, 1685759167llu, 2123923447llu, 1146 | 2675975881llu, 3371518343llu, 4247846927llu, 5351951779llu, 6743036717llu, 1147 | 8495693897llu, 10703903591llu, 13486073473llu, 16991387857llu, 1148 | 21407807219llu, 26972146961llu, 33982775741llu, 42815614441llu, 1149 | 53944293929llu, 67965551447llu, 85631228929llu, 107888587883llu, 1150 | 135931102921llu, 171262457903llu, 215777175787llu, 271862205833llu, 1151 | 342524915839llu, 431554351609llu, 543724411781llu, 685049831731llu, 1152 | 863108703229llu, 1087448823553llu, 1370099663459llu, 1726217406467llu, 1153 | 2174897647073llu, 2740199326961llu, 3452434812973llu, 4349795294267llu, 1154 | 5480398654009llu, 6904869625999llu, 8699590588571llu, 10960797308051llu, 1155 | 13809739252051llu, 17399181177241llu, 21921594616111llu, 27619478504183llu, 1156 | 34798362354533llu, 43843189232363llu, 55238957008387llu, 69596724709081llu, 1157 | 87686378464759llu, 110477914016779llu, 139193449418173llu, 1158 | 175372756929481llu, 220955828033581llu, 278386898836457llu, 1159 | 350745513859007llu, 441911656067171llu, 556773797672909llu, 1160 | 701491027718027llu, 883823312134381llu, 1113547595345903llu, 1161 | 1402982055436147llu, 1767646624268779llu, 2227095190691797llu, 1162 | 2805964110872297llu, 3535293248537579llu, 4454190381383713llu, 1163 | 5611928221744609llu, 7070586497075177llu, 8908380762767489llu, 1164 | 11223856443489329llu, 14141172994150357llu, 17816761525534927llu, 1165 | 22447712886978529llu, 28282345988300791llu, 35633523051069991llu, 1166 | 44895425773957261llu, 56564691976601587llu, 71267046102139967llu, 1167 | 89790851547914507llu, 113129383953203213llu, 142534092204280003llu, 1168 | 179581703095829107llu, 226258767906406483llu, 285068184408560057llu, 1169 | 359163406191658253llu, 452517535812813007llu, 570136368817120201llu, 1170 | 718326812383316683llu, 905035071625626043llu, 1140272737634240411llu, 1171 | 1436653624766633509llu, 1810070143251252131llu, 2280545475268481167llu, 1172 | 2873307249533267101llu, 3620140286502504283llu, 4561090950536962147llu, 1173 | 5746614499066534157llu, 7240280573005008577llu, 9122181901073924329llu, 1174 | 11493228998133068689llu, 14480561146010017169llu, 18446744073709551557llu 1175 | }; 1176 | static constexpr size_t (* const mod_functions[])(size_t) = 1177 | { 1178 | &mod0, &mod2, &mod3, &mod5, &mod7, &mod11, &mod13, &mod17, &mod23, &mod29, &mod37, 1179 | &mod47, &mod59, &mod73, &mod97, &mod127, &mod151, &mod197, &mod251, &mod313, &mod397, 1180 | &mod499, &mod631, &mod797, &mod1009, &mod1259, &mod1597, &mod2011, &mod2539, &mod3203, 1181 | &mod4027, &mod5087, &mod6421, &mod8089, &mod10193, &mod12853, &mod16193, &mod20399, 1182 | &mod25717, &mod32401, &mod40823, &mod51437, &mod64811, &mod81649, &mod102877, 1183 | &mod129607, &mod163307, &mod205759, &mod259229, &mod326617, &mod411527, &mod518509, 1184 | &mod653267, &mod823117, &mod1037059, &mod1306601, &mod1646237, &mod2074129, 1185 | &mod2613229, &mod3292489, &mod4148279, &mod5226491, &mod6584983, &mod8296553, 1186 | &mod10453007, &mod13169977, &mod16593127, &mod20906033, &mod26339969, &mod33186281, 1187 | &mod41812097, &mod52679969, &mod66372617, &mod83624237, &mod105359939, &mod132745199, 1188 | &mod167248483, &mod210719881, &mod265490441, &mod334496971, &mod421439783, 1189 | &mod530980861, &mod668993977, &mod842879579, &mod1061961721, &mod1337987929, 1190 | &mod1685759167, &mod2123923447, &mod2675975881, &mod3371518343, &mod4247846927, 1191 | &mod5351951779, &mod6743036717, &mod8495693897, &mod10703903591, &mod13486073473, 1192 | &mod16991387857, &mod21407807219, &mod26972146961, &mod33982775741, &mod42815614441, 1193 | &mod53944293929, &mod67965551447, &mod85631228929, &mod107888587883, &mod135931102921, 1194 | &mod171262457903, &mod215777175787, &mod271862205833, &mod342524915839, 1195 | &mod431554351609, &mod543724411781, &mod685049831731, &mod863108703229, 1196 | &mod1087448823553, &mod1370099663459, &mod1726217406467, &mod2174897647073, 1197 | &mod2740199326961, &mod3452434812973, &mod4349795294267, &mod5480398654009, 1198 | &mod6904869625999, &mod8699590588571, &mod10960797308051, &mod13809739252051, 1199 | &mod17399181177241, &mod21921594616111, &mod27619478504183, &mod34798362354533, 1200 | &mod43843189232363, &mod55238957008387, &mod69596724709081, &mod87686378464759, 1201 | &mod110477914016779, &mod139193449418173, &mod175372756929481, &mod220955828033581, 1202 | &mod278386898836457, &mod350745513859007, &mod441911656067171, &mod556773797672909, 1203 | &mod701491027718027, &mod883823312134381, &mod1113547595345903, &mod1402982055436147, 1204 | &mod1767646624268779, &mod2227095190691797, &mod2805964110872297, &mod3535293248537579, 1205 | &mod4454190381383713, &mod5611928221744609, &mod7070586497075177, &mod8908380762767489, 1206 | &mod11223856443489329, &mod14141172994150357, &mod17816761525534927, 1207 | &mod22447712886978529, &mod28282345988300791, &mod35633523051069991, 1208 | &mod44895425773957261, &mod56564691976601587, &mod71267046102139967, 1209 | &mod89790851547914507, &mod113129383953203213, &mod142534092204280003, 1210 | &mod179581703095829107, &mod226258767906406483, &mod285068184408560057, 1211 | &mod359163406191658253, &mod452517535812813007, &mod570136368817120201, 1212 | &mod718326812383316683, &mod905035071625626043, &mod1140272737634240411, 1213 | &mod1436653624766633509, &mod1810070143251252131, &mod2280545475268481167, 1214 | &mod2873307249533267101, &mod3620140286502504283, &mod4561090950536962147, 1215 | &mod5746614499066534157, &mod7240280573005008577, &mod9122181901073924329, 1216 | &mod11493228998133068689, &mod14480561146010017169, &mod18446744073709551557 1217 | }; 1218 | const size_t * found = std::lower_bound(std::begin(prime_list), std::end(prime_list) - 1, size); 1219 | size = *found; 1220 | return mod_functions[1 + found - prime_list]; 1221 | } 1222 | void commit(mod_function new_mod_function) 1223 | { 1224 | current_mod_function = new_mod_function; 1225 | } 1226 | void reset() 1227 | { 1228 | current_mod_function = &mod0; 1229 | } 1230 | 1231 | size_t index_for_hash(size_t hash, size_t /*num_slots_minus_one*/) const 1232 | { 1233 | return current_mod_function(hash); 1234 | } 1235 | size_t keep_in_range(size_t index, size_t num_slots_minus_one) const 1236 | { 1237 | return index > num_slots_minus_one ? current_mod_function(index) : index; 1238 | } 1239 | 1240 | private: 1241 | mod_function current_mod_function = &mod0; 1242 | }; 1243 | 1244 | struct power_of_two_hash_policy 1245 | { 1246 | size_t index_for_hash(size_t hash, size_t num_slots_minus_one) const 1247 | { 1248 | return hash & num_slots_minus_one; 1249 | } 1250 | size_t keep_in_range(size_t index, size_t num_slots_minus_one) const 1251 | { 1252 | return index_for_hash(index, num_slots_minus_one); 1253 | } 1254 | int8_t next_size_over(size_t & size) const 1255 | { 1256 | size = detailv3::next_power_of_two(size); 1257 | return 0; 1258 | } 1259 | void commit(int8_t) 1260 | { 1261 | } 1262 | void reset() 1263 | { 1264 | } 1265 | 1266 | }; 1267 | 1268 | struct fibonacci_hash_policy 1269 | { 1270 | size_t index_for_hash(size_t hash, size_t /*num_slots_minus_one*/) const 1271 | { 1272 | return (11400714819323198485ull * hash) >> shift; 1273 | } 1274 | size_t keep_in_range(size_t index, size_t num_slots_minus_one) const 1275 | { 1276 | return index & num_slots_minus_one; 1277 | } 1278 | 1279 | int8_t next_size_over(size_t & size) const 1280 | { 1281 | size = std::max(size_t(2), detailv3::next_power_of_two(size)); 1282 | return 64 - detailv3::log2(size); 1283 | } 1284 | void commit(int8_t shift) 1285 | { 1286 | this->shift = shift; 1287 | } 1288 | void reset() 1289 | { 1290 | shift = 63; 1291 | } 1292 | 1293 | private: 1294 | int8_t shift = 63; 1295 | }; 1296 | 1297 | template, typename E = std::equal_to, typename A = std::allocator > > 1298 | class flat_hash_map 1299 | : public detailv3::sherwood_v3_table 1300 | < 1301 | std::pair, 1302 | K, 1303 | H, 1304 | detailv3::KeyOrValueHasher, H>, 1305 | E, 1306 | detailv3::KeyOrValueEquality, E>, 1307 | A, 1308 | typename std::allocator_traits::template rebind_alloc>> 1309 | > 1310 | { 1311 | using Table = detailv3::sherwood_v3_table 1312 | < 1313 | std::pair, 1314 | K, 1315 | H, 1316 | detailv3::KeyOrValueHasher, H>, 1317 | E, 1318 | detailv3::KeyOrValueEquality, E>, 1319 | A, 1320 | typename std::allocator_traits::template rebind_alloc>> 1321 | >; 1322 | public: 1323 | 1324 | using key_type = K; 1325 | using mapped_type = V; 1326 | 1327 | using Table::Table; 1328 | flat_hash_map() 1329 | { 1330 | } 1331 | 1332 | inline V & operator[](const K & key) 1333 | { 1334 | return emplace(key, convertible_to_value()).first->second; 1335 | } 1336 | inline V & operator[](K && key) 1337 | { 1338 | return emplace(std::move(key), convertible_to_value()).first->second; 1339 | } 1340 | V & at(const K & key) 1341 | { 1342 | auto found = this->find(key); 1343 | if (found == this->end()) 1344 | throw std::out_of_range("Argument passed to at() was not in the map."); 1345 | return found->second; 1346 | } 1347 | const V & at(const K & key) const 1348 | { 1349 | auto found = this->find(key); 1350 | if (found == this->end()) 1351 | throw std::out_of_range("Argument passed to at() was not in the map."); 1352 | return found->second; 1353 | } 1354 | 1355 | using Table::emplace; 1356 | std::pair emplace() 1357 | { 1358 | return emplace(key_type(), convertible_to_value()); 1359 | } 1360 | template 1361 | std::pair insert_or_assign(const key_type & key, M && m) 1362 | { 1363 | auto emplace_result = emplace(key, std::forward(m)); 1364 | if (!emplace_result.second) 1365 | emplace_result.first->second = std::forward(m); 1366 | return emplace_result; 1367 | } 1368 | template 1369 | std::pair insert_or_assign(key_type && key, M && m) 1370 | { 1371 | auto emplace_result = emplace(std::move(key), std::forward(m)); 1372 | if (!emplace_result.second) 1373 | emplace_result.first->second = std::forward(m); 1374 | return emplace_result; 1375 | } 1376 | template 1377 | typename Table::iterator insert_or_assign(typename Table::const_iterator, const key_type & key, M && m) 1378 | { 1379 | return insert_or_assign(key, std::forward(m)).first; 1380 | } 1381 | template 1382 | typename Table::iterator insert_or_assign(typename Table::const_iterator, key_type && key, M && m) 1383 | { 1384 | return insert_or_assign(std::move(key), std::forward(m)).first; 1385 | } 1386 | 1387 | friend bool operator==(const flat_hash_map & lhs, const flat_hash_map & rhs) 1388 | { 1389 | if (lhs.size() != rhs.size()) 1390 | return false; 1391 | for (const typename Table::value_type & value : lhs) 1392 | { 1393 | auto found = rhs.find(value.first); 1394 | if (found == rhs.end()) 1395 | return false; 1396 | else if (value.second != found->second) 1397 | return false; 1398 | } 1399 | return true; 1400 | } 1401 | friend bool operator!=(const flat_hash_map & lhs, const flat_hash_map & rhs) 1402 | { 1403 | return !(lhs == rhs); 1404 | } 1405 | 1406 | private: 1407 | struct convertible_to_value 1408 | { 1409 | operator V() const 1410 | { 1411 | return V(); 1412 | } 1413 | }; 1414 | }; 1415 | 1416 | template, typename E = std::equal_to, typename A = std::allocator > 1417 | class flat_hash_set 1418 | : public detailv3::sherwood_v3_table 1419 | < 1420 | T, 1421 | T, 1422 | H, 1423 | detailv3::functor_storage, 1424 | E, 1425 | detailv3::functor_storage, 1426 | A, 1427 | typename std::allocator_traits::template rebind_alloc> 1428 | > 1429 | { 1430 | using Table = detailv3::sherwood_v3_table 1431 | < 1432 | T, 1433 | T, 1434 | H, 1435 | detailv3::functor_storage, 1436 | E, 1437 | detailv3::functor_storage, 1438 | A, 1439 | typename std::allocator_traits::template rebind_alloc> 1440 | >; 1441 | public: 1442 | 1443 | using key_type = T; 1444 | 1445 | using Table::Table; 1446 | flat_hash_set() 1447 | { 1448 | } 1449 | 1450 | template 1451 | std::pair emplace(Args &&... args) 1452 | { 1453 | return Table::emplace(T(std::forward(args)...)); 1454 | } 1455 | std::pair emplace(const key_type & arg) 1456 | { 1457 | return Table::emplace(arg); 1458 | } 1459 | std::pair emplace(key_type & arg) 1460 | { 1461 | return Table::emplace(arg); 1462 | } 1463 | std::pair emplace(const key_type && arg) 1464 | { 1465 | return Table::emplace(std::move(arg)); 1466 | } 1467 | std::pair emplace(key_type && arg) 1468 | { 1469 | return Table::emplace(std::move(arg)); 1470 | } 1471 | 1472 | friend bool operator==(const flat_hash_set & lhs, const flat_hash_set & rhs) 1473 | { 1474 | if (lhs.size() != rhs.size()) 1475 | return false; 1476 | for (const T & value : lhs) 1477 | { 1478 | if (rhs.find(value) == rhs.end()) 1479 | return false; 1480 | } 1481 | return true; 1482 | } 1483 | friend bool operator!=(const flat_hash_set & lhs, const flat_hash_set & rhs) 1484 | { 1485 | return !(lhs == rhs); 1486 | } 1487 | }; 1488 | 1489 | 1490 | template 1491 | struct power_of_two_std_hash : std::hash 1492 | { 1493 | typedef ska::power_of_two_hash_policy hash_policy; 1494 | }; 1495 | 1496 | } // end namespace ska --------------------------------------------------------------------------------