From patchwork Wed Sep 14 08:53:06 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Elo, Matias \(Nokia - FI/Espoo\)" X-Patchwork-Id: 76153 Delivered-To: patch@linaro.org Received: by 10.140.106.72 with SMTP id d66csp1766723qgf; Wed, 14 Sep 2016 01:58:54 -0700 (PDT) X-Received: by 10.200.48.156 with SMTP id v28mr1483914qta.49.1473843534659; Wed, 14 Sep 2016 01:58:54 -0700 (PDT) Return-Path: Received: from lists.linaro.org (lists.linaro.org. [54.225.227.206]) by mx.google.com with ESMTP id b79si2378931qkj.224.2016.09.14.01.58.54; Wed, 14 Sep 2016 01:58:54 -0700 (PDT) Received-SPF: pass (google.com: domain of lng-odp-bounces@lists.linaro.org designates 54.225.227.206 as permitted sender) client-ip=54.225.227.206; Authentication-Results: mx.google.com; spf=pass (google.com: domain of lng-odp-bounces@lists.linaro.org designates 54.225.227.206 as permitted sender) smtp.mailfrom=lng-odp-bounces@lists.linaro.org; dmarc=fail (p=NONE dis=NONE) header.from=nokia.com Received: by lists.linaro.org (Postfix, from userid 109) id 46655609EA; Wed, 14 Sep 2016 08:58:54 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on ip-10-142-244-252 X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAD_ENC_HEADER,BAYES_00, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_PASS autolearn=disabled version=3.4.0 Received: from [127.0.0.1] (localhost [127.0.0.1]) by lists.linaro.org (Postfix) with ESMTP id 5EF316168D; Wed, 14 Sep 2016 08:58:13 +0000 (UTC) X-Original-To: lng-odp@lists.linaro.org Delivered-To: lng-odp@lists.linaro.org Received: by lists.linaro.org (Postfix, from userid 109) id 024CC60890; Wed, 14 Sep 2016 08:58:03 +0000 (UTC) Received: from EUR03-DB5-obe.outbound.protection.outlook.com (mail-eopbgr40098.outbound.protection.outlook.com [40.107.4.98]) by lists.linaro.org (Postfix) with ESMTPS id E0D7460890 for ; Wed, 14 Sep 2016 08:53:33 +0000 (UTC) Received: from VI1PR07CA0024.eurprd07.prod.outlook.com (10.163.160.162) by DB5PR0701MB1989.eurprd07.prod.outlook.com (10.167.228.143) with Microsoft SMTP Server (version=TLS1_0, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA_P384) id 15.1.619.10; Wed, 14 Sep 2016 08:53:20 +0000 Received: from DB3FFO11FD010.protection.gbl (2a01:111:f400:7e04::198) by VI1PR07CA0024.outlook.office365.com (2a01:111:e400:533d::34) with Microsoft SMTP Server (version=TLS1_0, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA_P384) id 15.1.619.10 via Frontend Transport; Wed, 14 Sep 2016 08:53:20 +0000 Received-SPF: Pass (protection.outlook.com: domain of nokia.com designates 131.228.2.241 as permitted sender) receiver=protection.outlook.com; client-ip=131.228.2.241; helo=fihe3nok0735.emea.nsn-net.net; Received: from fihe3nok0735.emea.nsn-net.net (131.228.2.241) by DB3FFO11FD010.mail.protection.outlook.com (10.47.216.166) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P384) id 15.1.619.6 via Frontend Transport; Wed, 14 Sep 2016 08:53:19 +0000 Received: from fihe3nok0735.emea.nsn-net.net (localhost [127.0.0.1]) by fihe3nok0735.emea.nsn-net.net (8.14.9/8.14.5) with ESMTP id u8E8r7wh028577 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT) for ; Wed, 14 Sep 2016 11:53:07 +0300 Received: from 10.144.19.15 ([10.144.104.92]) by fihe3nok0735.emea.nsn-net.net (8.14.9/8.14.5) with ESMTP id u8E8r7xG028564 (version=TLSv1/SSLv3 cipher=AES128-SHA256 bits=128 verify=NOT) for ; Wed, 14 Sep 2016 11:53:07 +0300 X-HPESVCS-Source-Ip: 10.144.104.92 From: Matias Elo To: Date: Wed, 14 Sep 2016 11:53:06 +0300 Message-ID: <1473843187-25588-1-git-send-email-matias.elo@nokia.com> X-Mailer: git-send-email 2.7.4 X-EOPAttributedMessage: 0 X-MS-Office365-Filtering-HT: Tenant X-Forefront-Antispam-Report: CIP:131.228.2.241; IPV:NLI; CTRY:FI; EFV:NLI; SFV:NSPM; SFS:(10019020)(6009001)(7916002)(2980300002)(438002)(189002)(51234002)(199003)(87936001)(586003)(19580405001)(110136003)(356003)(2906002)(450100001)(106466001)(19580395003)(11100500001)(50226002)(189998001)(229853001)(107886002)(50986999)(2351001)(47776003)(68736007)(92566002)(626004)(50466002)(5660300001)(36756003)(8676002)(16796002)(81156014)(81166006)(8936002)(7846002)(305945005)(48376002)(5003940100001)(77096005)(97736004)(33646002)(42882005); DIR:OUT; SFP:1102; SCL:1; SRVR:DB5PR0701MB1989; H:fihe3nok0735.emea.nsn-net.net; FPR:; SPF:Pass; PTR:InfoDomainNonexistent; MX:1; A:1; LANG:en; X-Microsoft-Exchange-Diagnostics: 1; DB3FFO11FD010; 1:1kXM0ObMNKSacAlHHI+DDGgRVNijTfuH0HXnh8iTkomescF8zYiYLGslM4jHKGjsFZiQPZI0BM1MObQoh00wg56KrEqs9+D6thSYN28LplirSx/hLR8cjakNEyc8XVbrKgbGamFKRQCcKyJivNlEBBNRUyCgaOSrBgn1e6M6jkw1DLhPPxrTSwA47G74Exq1J6wMXIwZ9Ax3c3CLpoIXqID4Cwi/3q0qwxfxzmj9kw/sPhYmsuV87GlK50nynG3FttC0Z+qjG5S0Brf3/sUi8LSHGn2NUgpc715HL/F1r1u13DwBuxipL9/58WFDasNGCbNiKYB6iY+3A3i46OZWXqNcfRoMTn/gQuqy6QjJowgkn7+Cd/lA7aqB3kHHl9O6N+GCavH1gNmXjjz4u6+QW4hbimqRgyEBCeC8/nKeAUcUwoEUOV1xnMPQ4oHaYIw/r+RSDHXHBfd6t1zzlAcujOLOEjyAqWBvtybrOfDZzZc= MIME-Version: 1.0 X-MS-Office365-Filtering-Correlation-Id: d1d70535-9c23-4fcb-bcbf-08d3dc7c935e X-Microsoft-Exchange-Diagnostics: 1; DB5PR0701MB1989; 2:aF5F4qUxrAf0i8zOlvcfxqhldKffbia1vThZX2i4Aemd6kywlvkJ/kzd0fd7UhkIQAKQboK78PnF2VYd+YIBHQhy4Goq7rEN14Z5h0EPh4QCukNE26EXdILO0ThmxdvF4UphvbNIWf+0zG7TKI5FIEJoheAuwpspJtWsVX9ZT/QW1RdZlEeDTDr/OS0MmCum; 3:sYwZpe6R/JG+pFVHdQGtXIsBZcg6T4X42sdD6icABs++oV+BKfpn1oJao15HmjZ/TSrUwl/HM8iRm1pRw8eTM5vv23MK39O/aQcH6VrXp+Uwys+SildVKVCHaGCOOHV2SrSlvwZUqznwN5d5raGcss3UDZ6SDCiDGBLsHuSuHRLWnbpJUTM0z+uUo8aHl93l+5AOoGFr8mSCosZQrMldYMRb2asw6Udx8gOCaglkbUAmSfERkbrqstAWiyb7Or97AAupPnT+btnGQv52OCtOUA== X-Microsoft-Antispam: UriScan:; BCL:0; PCL:0; RULEID:(8251501002); SRVR:DB5PR0701MB1989; X-Microsoft-Exchange-Diagnostics: 1; DB5PR0701MB1989; 25:CZt3Vtgy84/jZ5QFX//0ITySFWCfGg5JtH3sQkdRSbQi5WhTp1IZqTw6webM44grO/qKo7JUq7iRonA5+cE7Rkud9O0z08STBqEpIcAOZLJ5d82vQ6JrvWPb7xZPrFJ7zWpu6ZkaFncSEOrJYron8X3EGY8kiykHRfTunbdv7PCii/YJyWfT67I724xwJUqXFi/QWBomgJAk/QKaRjVM5MCQhnguF19TC0pA12dvEdQoPonSgXw9lt4yzjSUvpfMvr9XtoCZo0X8gOQAoezLxrdGchgjzasE9ptli38bZDV1/GXIqu9cls4RYhNvKEFMfxLRxHEFibmBl3961pbLe+aWorrs9S6GRwiCm2HH6BxxljAQd1hx23khmkbWr9v4j2bYK5AfpqlGOyMUFA9c9siy3pYEKlhkNblgu8KfhjiYZCRZpGhydMYYEfitj5MPsT02uvVOlu3eUG0Md16xjK6dyWwXL0SAN+r7VpJHPJWoTPXsd+aliAwTxpmPxzc3nGawPjV7Gw6fr28QLN7NeUF7VbE7tR3THiCz/IueK1u4L2SZrw2D5dkeZQd1es2x1s8FYEEgXkzJWckbQmE5lIO5RwPCR3yj9DzntwzA4uvHn8I5+O7sP/w6ot39vHxqCIv+U1RhJ/PS4ESJoZ0jTgnI0zmFwwWQIu9WntUoFIREknTGGi4qnFK4g6MRTpFIfvwOobFRUDbIz5Ct4STieTQynLWs/8XInU7lqmROoY1w6xgQvZsU0G+pYI055xZk X-Microsoft-Exchange-Diagnostics: 1; DB5PR0701MB1989; 31:l/t3uCwNqt+V7VKh5tlFTXraYKSi7UX7TLh8iRYKbHyFoVYxoEnHdyKT1ZpCgZ/8opPEZSaVYtzNrZ6T4MyAEzKbwvlTBCDxgaCOi1lNQbK2VyVSXT739OMI6GGbX6uHxsf3T9JFNuJpWERgh6B5ENYI1aHlAFu/DN4XmY3vZLz7dDut8/B+MmHbeVwdj12gBLnQM8Vzl3eC5i+cAmy84pKZ3ye2em4Sy6jvRDQXthI=; 20:16PdFZWppsXG8qMXS5PL0I4XDNOBcXaBZ5fs1Em3KOk6p334c+Lt5maE1foIxSFwoajsucE7PYYQ1d+GBpTREZsuf0x0X2nI3OmwNwrfSjdZKO4sAh7ZEw/cICem5tBHib9dnC7B7ScX0BSALVoLadf05peyKyQYO3pDuPsCXQU+yEp+SBxOZ43PCVzlGexm8OmSR/cCXDH9yR01FFoa/haMpMChm2SeGXQW6Z1jj3pEvKTtOQeSKHgWEHW3LIKlopIBVMTLvpm7Uzij5YOrogm1yoCfM1BpJ3vGbz9Z+yvK1Vp03Q7Ez/q9y3p4aSzLk3qO0WMLmj7IrJNbVTRBUf1xg1GtyTb9I7AsU3KmxUTx4Bg95NgMUjqCx+BSqs5d2wPN2lSPtmAkZ15+3RrEe6MadXQHSjc/GhfY8StjEPoatt14gYbPPvVzrcETuNIfZGmvyYG4SyiA5zXYgp+poM6Tfs048dulZq6F6rnOqkKA53+WqbxPKh/vSI1ZhyjS7GnVQc/ZYRR1M6IRco+sapPbap/3CS2ZMKuDqyYShCzhYinZOpxd2mJ46tmh6UlES9Z3Ycnm5Ol3hZJscme+XNJXiKn9YIGdlkcL/oHbLbk= X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(82608151540597); X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(6040176)(601004)(2401047)(13018025)(13016025)(8121501046)(5005006)(10201501046)(3002001)(6055026); SRVR:DB5PR0701MB1989; BCL:0; PCL:0; RULEID:; SRVR:DB5PR0701MB1989; X-Microsoft-Exchange-Diagnostics: 1; DB5PR0701MB1989; 4:85O2fODaZFJh+PXdLzMEt3kJIb1j9kAeVEilnXrU1SMGGnXeBuf2amWVa2A8m+zzrMMnhWawzydNUhFG4MynQYmYB567q3jdBkkhpu+QbHYLrYrBN63Us45Vx7MDlntIkDyPO+TSBVSxRgkcHRtksfGlBw37vKoTHdBoEAFJko3O+Z1BEj1zC3xzdjABZjMNroutExWv0jN9vvFyxzgmhF2dynbHb41fbm4jdHHERkEyaxkrKHLN5KGdJ7ct6i/y7TDULymufr6TBjxWGpX6jz6R0JjA8Mli/53SM0QhLPOvaT8mwrw+/3Au/9dCms9tmyaFPQkTnglTpXhQUU195+Fd+6PSjZ4AycQJTpEQueSAMsDbKtUWr8JytNY7PaKGbvvrnDKpOUE1aO0ibXnaNwts7CV3oi2bYw8HwP4/DIFYW0V4PDbq5FJsKjWbECZ6y/2g9X+IAH/6zXEE69Q70tUUnxDJ94S4KO/Zo/WLt68= X-Forefront-PRVS: 006546F32A X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; DB5PR0701MB1989; 23:Tyb0g4CdY3Ak0WipjcC4O0UZ+UDlrnlLLM4A0nA?= =?us-ascii?Q?z8boncauLgTYetyPvyXzpt73+J10FgVP/uOkqH0mVes4RBfc9dLzStqV6zOw?= =?us-ascii?Q?GP10vgHmRhVlA5Hx+WuEoB4hX9AJy5GhsacfrnT4JvAzh55xwPvVSfGP/n+Q?= =?us-ascii?Q?K0nWUCrlBMLwuy2JLhQiWN/PGimf8Ng/5BKaLqia3IQDoRzqL8CXj8QkwkGn?= =?us-ascii?Q?ylw0IurvoN6EMHR0eco3sxGKiQ99MBuy2dGafQIY6Q6iBzPJNcmjiBgk+iia?= =?us-ascii?Q?R7F7DS2O92ICkEDRp9hdHqNMWjUm1uOACaTBDkCjVUgS/w8CLcp6RgRBEnv5?= =?us-ascii?Q?0qh7++525Y82SPP/LtN997vtSq3MjG6S13RW6mxm/sSjTDvSlEbiAaSIbpoM?= =?us-ascii?Q?sz2WyrfqsNUGXnymnEP9d19+wtKycR3Db8pQDdY9jpPZ825+eirbO1Qu09IP?= =?us-ascii?Q?z4erXL2J4rUXhl9LTkZ08D8AvKIjt6zEsDdDG/9fdWzTdi0nob4hN4wtck6b?= =?us-ascii?Q?IENmR8p22gojFptTa0xmdDHB7Q4pgFQv5foS1CSjWLsGFRHxiB2Jtm/Ay7hh?= =?us-ascii?Q?qdAm0eekhaBCj2abec9tfxfYlAHYQCILBT1pZpO4e9yOaHDnrbawqtsxqFaR?= =?us-ascii?Q?/VnMBESIDp5bImCIxCcD4cPMu8qTZkYCbJSheB73jHfBFzIUFXbhK/HpFtbM?= =?us-ascii?Q?VRi9MXEz2gyqFv9FYFtTzxNEbYisowrLWgCYVkx9CB5KyLiF0t0ih4kY3Jym?= =?us-ascii?Q?bkYGQQbnI0xYQ108FgnFECidBI5vig8cagUJDZpNJYSIiaUcfhNXD67LNRFL?= =?us-ascii?Q?1+XQqCgZr4Y9REiO/GvxJp0DCe8q2uLNpPFiO8im9GV5VO+34fb6JAmvwNo1?= =?us-ascii?Q?vMEmSPkx5I7WZAu/NUfneo1lbtTkiDG0Z2y0NLyeUIfr3j/GMhJ2UpIq6Sd4?= =?us-ascii?Q?d3zRZzgKHHHsGun3rcFjlQXM1xTkGVwCL4xuL2+5msjKdQAPIjm9m918OKoM?= =?us-ascii?Q?iqXynX5LLEpZky5/nT2QCq99vPqtlPrIjEQsIDlw4jKh25/RyBzbYfnDUor2?= =?us-ascii?Q?VTcTZeuUApLk16Qo/0Bqjo/fk/YnruMpMp7Zjgddnrkb2u7+lFQ=3D=3D?= X-Microsoft-Exchange-Diagnostics: 1; DB5PR0701MB1989; 6:4MQPN+DndSGXGYkTMXm+cXex6br9bmW+dCHMMO6ZBPiK3T3pGVVVg1JWrYEpz4nZ41/OjDV9aNKffUiXowdyG7Xa87ed2r0cMo2iTV6PtLoeVZViaLFdVHQ9VJOjnPZohNpFiWGEEc1/EFgRHyJVj7t/L3jld53hEbGrQAOKWPdgbOCyMts8pqNpEKo6tceE5kYvelf6FvS/mB6FeMAGlYwzV7PEVcJTTMu0K+lXjFlmO0KUWYGz25mTzQLLNuobr7z6x/0nuhAaOPTNQb8BaaINno5qvrqd8zWIan9KBQTk4GXxX3aM+mjkkps7FDo7ykTNrq91tiEY97CFPEtdNg==; 5:HghZq9ijZ/z/LoTO5FpFVcucT/yIqQs2VKpG5D5dpWpU233+5TxctmHtbJ1CD48ldmBzBEVY3jjv7Vnk+YVWG3G7GNyn+guJ7Gzrsh1RGzxTLruTi6uBR8SWj3wAp1FlTWQ3yv3zPxrRnd6D0O3iZw==; 24:2fSMTnDtWBqQtcRwjpYFZbgZa95eSD6LbTN7t4RouUMEK5oUiXUIKnJkHsldRgcc/F/FM8u/z+GGu08OLBBE6mnqu+xkE5+keztg/mTLsy8=; 7:9t7+QZtlLCVQww4q1AHa1/ITvcwbJ08wi4OvGRQZ0NvV0Hav1vu20HklDzOeODbz1Ongc3PiMQSeMGlRbpxFuWrGiXlgtU0Vczhx5kBIRKWtUx33gD/C7rTlWlW+MG9OXqSq4qnK9vIImX/9vNfedv0vlI7j9dfd2A4L3kAsz70UCrJhN+ngIfEOJtCxJ2P5F7RdMdQ3rZlb2/sJM4yxkXIivKcLawLneEVU5f+LQNRQGs8UyL1rWIIm01+n1Fbt SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-OriginatorOrg: nokia.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 14 Sep 2016 08:53:19.3858 (UTC) X-MS-Exchange-CrossTenant-Id: 5d471751-9675-428d-917b-70f44f9630b0 X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=5d471751-9675-428d-917b-70f44f9630b0; Ip=[131.228.2.241]; Helo=[fihe3nok0735.emea.nsn-net.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB5PR0701MB1989 X-Topics: patch Subject: [lng-odp] [PATCH v2 1/2] test: perf: add new scheduling latency test X-BeenThere: lng-odp@lists.linaro.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: "The OpenDataPlane \(ODP\) List" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: lng-odp-bounces@lists.linaro.org Sender: "lng-odp" Add new scheduling latency benchmark application. The application measures delays (avg, min, max) for high and low priority events. The test has a configurable number of TRAFFIC events and few SAMPLE events (one common or one per priority). The scheduling latency is only measured from the SAMPLE events to minimize measurement overhead. The application's command line arguments enable configuring: - Number of processing threads - Number of high/low priority queues - Number of high/low priority events - Use separate SAMPLE events for each priority - Scheduled queue type (PARALLEL, ATOMIC, ORDERED) Signed-off-by: Matias Elo --- V2: - Remove unnecessary 'num_workers' initialization (Maxim) test/common_plat/performance/.gitignore | 1 + test/common_plat/performance/Makefile.am | 4 + test/common_plat/performance/odp_sched_latency.c | 767 +++++++++++++++++++++++ 3 files changed, 772 insertions(+) create mode 100644 test/common_plat/performance/odp_sched_latency.c -- 2.7.4 diff --git a/test/common_plat/performance/.gitignore b/test/common_plat/performance/.gitignore index edcc832..1527d25 100644 --- a/test/common_plat/performance/.gitignore +++ b/test/common_plat/performance/.gitignore @@ -4,4 +4,5 @@ odp_atomic odp_crypto odp_l2fwd odp_pktio_perf +odp_sched_latency odp_scheduling diff --git a/test/common_plat/performance/Makefile.am b/test/common_plat/performance/Makefile.am index d23bb3e..f5dd8dd 100644 --- a/test/common_plat/performance/Makefile.am +++ b/test/common_plat/performance/Makefile.am @@ -5,6 +5,7 @@ TESTS_ENVIRONMENT += TEST_DIR=${builddir} EXECUTABLES = odp_crypto$(EXEEXT) odp_pktio_perf$(EXEEXT) COMPILE_ONLY = odp_l2fwd$(EXEEXT) \ + odp_sched_latency$(EXEEXT) \ odp_scheduling$(EXEEXT) TESTSCRIPTS = odp_l2fwd_run.sh \ @@ -20,6 +21,8 @@ bin_PROGRAMS = $(EXECUTABLES) $(COMPILE_ONLY) odp_crypto_LDFLAGS = $(AM_LDFLAGS) -static odp_crypto_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test +odp_sched_latency_LDFLAGS = $(AM_LDFLAGS) -static +odp_sched_latency_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test odp_scheduling_LDFLAGS = $(AM_LDFLAGS) -static odp_scheduling_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test @@ -27,6 +30,7 @@ noinst_HEADERS = \ $(top_srcdir)/test/test_debug.h dist_odp_crypto_SOURCES = odp_crypto.c +dist_odp_sched_latency_SOURCES = odp_sched_latency.c dist_odp_scheduling_SOURCES = odp_scheduling.c dist_odp_pktio_perf_SOURCES = odp_pktio_perf.c diff --git a/test/common_plat/performance/odp_sched_latency.c b/test/common_plat/performance/odp_sched_latency.c new file mode 100644 index 0000000..063fb21 --- /dev/null +++ b/test/common_plat/performance/odp_sched_latency.c @@ -0,0 +1,767 @@ +/* Copyright (c) 2016, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/** + * @file + * + * @example odp_sched_latency.c ODP scheduling latency benchmark application + */ + +#include +#include +#include + +#include + +/* ODP main header */ +#include + +/* ODP helper for Linux apps */ +#include + +/* GNU lib C */ +#include + +#define MAX_WORKERS 64 /**< Maximum number of worker threads */ +#define MAX_QUEUES 4096 /**< Maximum number of queues */ +#define EVENT_POOL_SIZE (1024 * 1024) /**< Event pool size */ +#define TEST_ROUNDS (4 * 1024 * 1024) /**< Test rounds for each thread */ +#define MAIN_THREAD 1 /**< Thread ID performing maintenance tasks */ + +/* Default values for command line arguments */ +#define SAMPLE_EVENT_PER_PRIO 0 /**< Allocate a separate sample event for + each priority */ +#define HI_PRIO_EVENTS 0 /**< Number of high priority events */ +#define LO_PRIO_EVENTS 32 /**< Number of low priority events */ +#define HI_PRIO_QUEUES 16 /**< Number of high priority queues */ +#define LO_PRIO_QUEUES 64 /**< Number of low priority queues */ + +#define EVENTS_PER_HI_PRIO_QUEUE 0 /**< Alloc HI_PRIO_QUEUES x HI_PRIO_EVENTS + events */ +#define EVENTS_PER_LO_PRIO_QUEUE 1 /**< Alloc LO_PRIO_QUEUES x LO_PRIO_EVENTS + events */ +ODP_STATIC_ASSERT(HI_PRIO_QUEUES <= MAX_QUEUES, "Too many HI priority queues"); +ODP_STATIC_ASSERT(LO_PRIO_QUEUES <= MAX_QUEUES, "Too many LO priority queues"); + +#define CACHE_ALIGN_ROUNDUP(x)\ + ((ODP_CACHE_LINE_SIZE) * \ + (((x) + ODP_CACHE_LINE_SIZE - 1) / (ODP_CACHE_LINE_SIZE))) + +/* Test priorities */ +#define NUM_PRIOS 2 /**< Number of tested priorities */ +#define HI_PRIO 0 +#define LO_PRIO 1 + +/** Test event types */ +typedef enum { + WARM_UP, /**< Warm up event */ + TRAFFIC, /**< Event used only as traffic load */ + SAMPLE /**< Event used to measure latency */ +} event_type_t; + +/** Test event */ +typedef struct { + uint64_t ts; /**< Send timestamp */ + event_type_t type; /**< Message type */ + int src_idx[NUM_PRIOS]; /**< Source ODP queue */ + int prio; /**< Source queue priority */ +} test_event_t; + +/** Test arguments */ +typedef struct { + int cpu_count; /**< CPU count */ + odp_schedule_sync_t sync_type; /**< Scheduler sync type */ + struct { + int queues; /**< Number of scheduling queues */ + int events; /**< Number of events */ + odp_bool_t events_per_queue; /**< Allocate 'queues' x 'events' + test events */ + } prio[NUM_PRIOS]; + odp_bool_t sample_per_prio; /**< Allocate a separate sample event for + each priority */ +} test_args_t; + +/** Latency measurements statistics */ +typedef struct { + uint64_t events; /**< Total number of received events */ + uint64_t sample_events; /**< Number of received sample events */ + uint64_t tot; /**< Total event latency. Sum of all events. */ + uint64_t min; /**< Minimum event latency */ + uint64_t max; /**< Maximum event latency */ +} test_stat_t; + +/** Performance test statistics (per core) */ +typedef union { + test_stat_t prio[NUM_PRIOS]; /**< Test statistics per priority */ + + uint8_t pad[CACHE_ALIGN_ROUNDUP(NUM_PRIOS * sizeof(test_stat_t))]; +} core_stat_t ODP_ALIGNED_CACHE; + +/** Test global variables */ +typedef struct { + core_stat_t core_stat[MAX_WORKERS]; /**< Core specific stats */ + odp_barrier_t barrier; /**< Barrier for thread synchronization */ + odp_pool_t pool; /**< Pool for allocating test events */ + test_args_t args; /**< Parsed command line arguments */ + odp_queue_t queue[NUM_PRIOS][MAX_QUEUES]; /**< Scheduled queues */ +} test_globals_t; + +/** + * Clear all scheduled queues. + * + * Retry to be sure that all buffers have been scheduled. + */ +static void clear_sched_queues(void) +{ + odp_event_t ev; + + while (1) { + ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT); + + if (ev == ODP_EVENT_INVALID) + break; + + odp_event_free(ev); + } +} + +/** + * Enqueue events into queues + * + * @param prio Queue priority (HI_PRIO/LO_PRIO) + * @param num_queues Number of queues + * @param num_events Number of 'TRAFFIC' events + * @param num_samples Number of 'SAMPLE' events + * @param div_events If true, divide 'num_events' between 'num_queues'. if + * false, enqueue 'num_events' to each queue. + * @param globals Test shared data + * + * @retval 0 on success + * @retval -1 on failure + */ +static int enqueue_events(int prio, int num_queues, int num_events, + int num_samples, odp_bool_t div_events, + test_globals_t *globals) +{ + odp_buffer_t buf[num_events + num_samples]; + odp_event_t ev[num_events + num_samples]; + odp_queue_t queue; + test_event_t *event; + int i, j, ret; + int enq_events; + int events_per_queue; + int tot_events; + int rdy_events = 0; + + tot_events = num_events + num_samples; + + if (!num_queues || !tot_events) + return 0; + + events_per_queue = tot_events; + if (div_events) + events_per_queue = (tot_events + num_queues - 1) / num_queues; + + for (i = 0; i < num_queues; i++) { + queue = globals->queue[prio][i]; + + ret = odp_buffer_alloc_multi(globals->pool, buf, + events_per_queue); + if (ret != events_per_queue) { + LOG_ERR("Buffer alloc failed. Try increasing EVENT_POOL_SIZE.\n"); + ret = ret < 0 ? 0 : ret; + odp_buffer_free_multi(buf, ret); + return -1; + } + for (j = 0; j < events_per_queue; j++) { + if (!odp_buffer_is_valid(buf[j])) { + LOG_ERR("Buffer alloc failed\n"); + odp_buffer_free_multi(buf, events_per_queue); + return -1; + } + + event = odp_buffer_addr(buf[j]); + memset(event, 0, sizeof(test_event_t)); + + /* Latency isn't measured from the first processing + * round. */ + if (num_samples > 0) { + event->type = WARM_UP; + num_samples--; + } else { + event->type = TRAFFIC; + } + event->src_idx[prio] = i; + event->prio = prio; + ev[j] = odp_buffer_to_event(buf[j]); + } + + enq_events = 0; + do { + ret = odp_queue_enq_multi(queue, &ev[enq_events], + events_per_queue - + enq_events); + if (ret < 0) { + LOG_ERR("Queue enqueue failed.\n"); + return -1; + } + enq_events += ret; + } while (enq_events < events_per_queue); + + rdy_events += events_per_queue; + if (div_events && rdy_events >= tot_events) + return 0; + } + return 0; +} + +/** + * Print latency measurement results + * + * @param globals Test shared data + */ +static void print_results(test_globals_t *globals) +{ + test_stat_t *lat; + odp_schedule_sync_t stype; + test_stat_t total; + test_args_t *args; + uint64_t avg; + int i, j; + + args = &globals->args; + stype = globals->args.sync_type; + + printf("\n%s queue scheduling latency\n", + (stype == ODP_SCHED_SYNC_ATOMIC) ? "ATOMIC" : + ((stype == ODP_SCHED_SYNC_ORDERED) ? "ORDERED" : "PARALLEL")); + + printf(" LO_PRIO queues: %i\n", args->prio[LO_PRIO].queues); + if (args->prio[LO_PRIO].events_per_queue) + printf(" LO_PRIO event per queue: %i\n", + args->prio[LO_PRIO].events); + else + printf(" LO_PRIO events: %i\n", args->prio[LO_PRIO].events); + + printf(" HI_PRIO queues: %i\n", args->prio[HI_PRIO].queues); + if (args->prio[HI_PRIO].events_per_queue) + printf(" HI_PRIO event per queue: %i\n\n", + args->prio[HI_PRIO].events); + else + printf(" HI_PRIO events: %i\n\n", args->prio[HI_PRIO].events); + + for (i = 0; i < NUM_PRIOS; i++) { + memset(&total, 0, sizeof(test_stat_t)); + total.min = UINT64_MAX; + + printf("%s priority\n" + "Thread Avg[ns] Min[ns] Max[ns] Samples Total\n" + "---------------------------------------------------------------\n", + i == HI_PRIO ? "HIGH" : "LOW"); + for (j = 1; j <= args->cpu_count; j++) { + lat = &globals->core_stat[j].prio[i]; + + if (lat->sample_events == 0) { + printf("%-8d N/A\n", j); + continue; + } + + if (lat->max > total.max) + total.max = lat->max; + if (lat->min < total.min) + total.min = lat->min; + total.tot += lat->tot; + total.sample_events += lat->sample_events; + total.events += lat->events; + + avg = lat->events ? lat->tot / lat->sample_events : 0; + printf("%-8d %-10" PRIu64 " %-10" PRIu64 " " + "%-10" PRIu64 " %-10" PRIu64 " %-10" PRIu64 "\n", + j, avg, lat->min, lat->max, lat->sample_events, + lat->events); + } + printf("---------------------------------------------------------------\n"); + if (total.sample_events == 0) { + printf("Total N/A\n\n"); + continue; + } + avg = total.events ? total.tot / total.sample_events : 0; + printf("Total %-10" PRIu64 " %-10" PRIu64 " %-10" PRIu64 " " + "%-10" PRIu64 " %-10" PRIu64 "\n\n", avg, total.min, + total.max, total.sample_events, total.events); + } +} + +/** + * Measure latency of scheduled ODP events + * + * Schedule and enqueue events until 'TEST_ROUNDS' events have been processed. + * Scheduling latency is measured only from type 'SAMPLE' events. Other events + * are simply enqueued back to the scheduling queues. + * + * For 'TRAFFIC' type events the destination queue is selected from the same + * priority class as source queue. 'SAMPLE' type event may change priority + * depending on the command line arguments. + * + * @param thr Thread ID + * @param globals Test shared data + * + * @retval 0 on success + * @retval -1 on failure + */ +static int test_schedule(int thr, test_globals_t *globals) +{ + odp_event_t ev; + odp_buffer_t buf; + odp_queue_t src_queue; + odp_queue_t dst_queue; + uint64_t latency; + uint32_t i; + test_event_t *event; + test_stat_t *stats; + int dst_idx; + + memset(&globals->core_stat[thr], 0, sizeof(core_stat_t)); + globals->core_stat[thr].prio[HI_PRIO].min = UINT64_MAX; + globals->core_stat[thr].prio[LO_PRIO].min = UINT64_MAX; + + for (i = 0; i < TEST_ROUNDS; i++) { + ev = odp_schedule(&src_queue, ODP_SCHED_WAIT); + + buf = odp_buffer_from_event(ev); + event = odp_buffer_addr(buf); + + stats = &globals->core_stat[thr].prio[event->prio]; + + if (event->type == SAMPLE) { + latency = odp_time_to_ns(odp_time_global()) - event->ts; + + if (latency > stats->max) + stats->max = latency; + if (latency < stats->min) + stats->min = latency; + stats->tot += latency; + stats->sample_events++; + + /* Move sample event to a different priority */ + if (!globals->args.sample_per_prio && + globals->args.prio[!event->prio].queues) + event->prio = !event->prio; + } + + if (odp_unlikely(event->type == WARM_UP)) + event->type = SAMPLE; + else + stats->events++; + + /* Move event to next queue */ + dst_idx = event->src_idx[event->prio] + 1; + if (dst_idx >= globals->args.prio[event->prio].queues) + dst_idx = 0; + event->src_idx[event->prio] = dst_idx; + dst_queue = globals->queue[event->prio][dst_idx]; + + if (event->type == SAMPLE) + event->ts = odp_time_to_ns(odp_time_global()); + + if (odp_queue_enq(dst_queue, ev)) { + LOG_ERR("[%i] Queue enqueue failed.\n", thr); + odp_event_free(ev); + return -1; + } + } + + /* Clear possible locally stored buffers */ + odp_schedule_pause(); + + while (1) { + ev = odp_schedule(&src_queue, ODP_SCHED_NO_WAIT); + + if (ev == ODP_EVENT_INVALID) + break; + + if (odp_queue_enq(src_queue, ev)) { + LOG_ERR("[%i] Queue enqueue failed.\n", thr); + odp_event_free(ev); + return -1; + } + } + + odp_schedule_resume(); + + odp_barrier_wait(&globals->barrier); + + clear_sched_queues(); + + if (thr == MAIN_THREAD) + print_results(globals); + + return 0; +} + +/** + * Worker thread + * + * @param arg Arguments + * + * @retval 0 on success + * @retval -1 on failure + */ +static int run_thread(void *arg ODP_UNUSED) +{ + odp_shm_t shm; + test_globals_t *globals; + test_args_t *args; + int thr; + int sample_events = 0; + + thr = odp_thread_id(); + + shm = odp_shm_lookup("test_globals"); + globals = odp_shm_addr(shm); + + if (globals == NULL) { + LOG_ERR("Shared mem lookup failed\n"); + return -1; + } + + if (thr == MAIN_THREAD) { + args = &globals->args; + + if (enqueue_events(HI_PRIO, args->prio[HI_PRIO].queues, + args->prio[HI_PRIO].events, 1, + !args->prio[HI_PRIO].events_per_queue, + globals)) + return -1; + + if (!args->prio[HI_PRIO].queues || args->sample_per_prio) + sample_events = 1; + + if (enqueue_events(LO_PRIO, args->prio[LO_PRIO].queues, + args->prio[LO_PRIO].events, sample_events, + !args->prio[LO_PRIO].events_per_queue, + globals)) + return -1; + } + + odp_barrier_wait(&globals->barrier); + + if (test_schedule(thr, globals)) + return -1; + + return 0; +} + +/** + * Print usage information + */ +static void usage(void) +{ + printf("\n" + "OpenDataPlane scheduler latency benchmark application.\n" + "\n" + "Usage: ./odp_sched_latency [options]\n" + "Optional OPTIONS:\n" + " -c, --count CPU count\n" + " -l, --lo-prio-queues Number of low priority scheduled queues\n" + " -t, --hi-prio-queues Number of high priority scheduled queues\n" + " -m, --lo-prio-events-per-queue Number of events per low priority queue\n" + " -n, --hi-prio-events-per-queue Number of events per high priority queues\n" + " -o, --lo-prio-events Total number of low priority events (overrides the\n" + " number of events per queue)\n" + " -p, --hi-prio-events Total number of high priority events (overrides the\n" + " number of events per queue)\n" + " -r --sample-per-prio Allocate a separate sample event for each priority. By default\n" + " a single sample event is used and its priority is changed after\n" + " each processing round.\n" + " -s, --sync Scheduled queues' sync type\n" + " 0: ODP_SCHED_SYNC_PARALLEL (default)\n" + " 1: ODP_SCHED_SYNC_ATOMIC\n" + " 2: ODP_SCHED_SYNC_ORDERED\n" + " -h, --help Display help and exit.\n\n" + ); +} + +/** + * Parse arguments + * + * @param argc Argument count + * @param argv Argument vector + * @param args Test arguments + */ +static void parse_args(int argc, char *argv[], test_args_t *args) +{ + int opt; + int long_index; + int i; + + static const struct option longopts[] = { + {"count", required_argument, NULL, 'c'}, + {"lo-prio-queues", required_argument, NULL, 'l'}, + {"hi-prio-queues", required_argument, NULL, 't'}, + {"lo-prio-events-per-queue", required_argument, NULL, 'm'}, + {"hi-prio-events-per-queue", required_argument, NULL, 'n'}, + {"lo-prio-events", required_argument, NULL, 'o'}, + {"hi-prio-events", required_argument, NULL, 'p'}, + {"sample-per-prio", no_argument, NULL, 'r'}, + {"sync", required_argument, NULL, 's'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + + static const char *shortopts = "+c:s:l:t:m:n:o:p:rh"; + + /* Let helper collect its own arguments (e.g. --odph_proc) */ + odph_parse_options(argc, argv, shortopts, longopts); + + args->sync_type = ODP_SCHED_SYNC_PARALLEL; + args->sample_per_prio = SAMPLE_EVENT_PER_PRIO; + args->prio[LO_PRIO].queues = LO_PRIO_QUEUES; + args->prio[HI_PRIO].queues = HI_PRIO_QUEUES; + args->prio[LO_PRIO].events = LO_PRIO_EVENTS; + args->prio[HI_PRIO].events = HI_PRIO_EVENTS; + args->prio[LO_PRIO].events_per_queue = EVENTS_PER_LO_PRIO_QUEUE; + args->prio[HI_PRIO].events_per_queue = EVENTS_PER_HI_PRIO_QUEUE; + + opterr = 0; /* Do not issue errors on helper options */ + while (1) { + opt = getopt_long(argc, argv, shortopts, longopts, &long_index); + + if (opt == -1) + break; /* No more options */ + + switch (opt) { + case 'c': + args->cpu_count = atoi(optarg); + break; + case 'l': + args->prio[LO_PRIO].queues = atoi(optarg); + break; + case 't': + args->prio[HI_PRIO].queues = atoi(optarg); + break; + case 'm': + args->prio[LO_PRIO].events = atoi(optarg); + args->prio[LO_PRIO].events_per_queue = 1; + break; + case 'n': + args->prio[HI_PRIO].events = atoi(optarg); + args->prio[HI_PRIO].events_per_queue = 1; + break; + case 'o': + args->prio[LO_PRIO].events = atoi(optarg); + args->prio[LO_PRIO].events_per_queue = 0; + break; + case 'p': + args->prio[HI_PRIO].events = atoi(optarg); + args->prio[HI_PRIO].events_per_queue = 0; + break; + case 's': + i = atoi(optarg); + if (i == 1) + args->sync_type = ODP_SCHED_SYNC_ATOMIC; + else if (i == 2) + args->sync_type = ODP_SCHED_SYNC_ORDERED; + else + args->sync_type = ODP_SCHED_SYNC_PARALLEL; + break; + case 'r': + args->sample_per_prio = 1; + break; + case 'h': + usage(); + exit(EXIT_SUCCESS); + break; + + default: + break; + } + } + + /* Make sure arguments are valid */ + if (args->cpu_count > MAX_WORKERS) + args->cpu_count = MAX_WORKERS; + if (args->prio[LO_PRIO].queues > MAX_QUEUES) + args->prio[LO_PRIO].queues = MAX_QUEUES; + if (args->prio[HI_PRIO].queues > MAX_QUEUES) + args->prio[HI_PRIO].queues = MAX_QUEUES; + if (!args->prio[HI_PRIO].queues && !args->prio[LO_PRIO].queues) { + printf("No queues configured\n"); + usage(); + exit(EXIT_FAILURE); + } +} + +/** + * Test main function + */ +int main(int argc, char *argv[]) +{ + odp_instance_t instance; + odph_odpthread_t *thread_tbl; + odph_odpthread_params_t thr_params; + odp_cpumask_t cpumask; + odp_pool_t pool; + odp_pool_param_t params; + odp_shm_t shm; + test_globals_t *globals; + test_args_t args; + char cpumaskstr[ODP_CPUMASK_STR_SIZE]; + int i, j; + int ret = 0; + int num_workers = 0; + + printf("\nODP scheduling latency benchmark starts\n\n"); + + memset(&args, 0, sizeof(args)); + parse_args(argc, argv, &args); + + /* ODP global init */ + if (odp_init_global(&instance, NULL, NULL)) { + LOG_ERR("ODP global init failed.\n"); + return -1; + } + + /* + * Init this thread. It makes also ODP calls when + * setting up resources for worker threads. + */ + if (odp_init_local(instance, ODP_THREAD_CONTROL)) { + LOG_ERR("ODP global init failed.\n"); + return -1; + } + + printf("\n"); + printf("ODP system info\n"); + printf("---------------\n"); + printf("ODP API version: %s\n", odp_version_api_str()); + printf("ODP impl name: %s\n", odp_version_impl_name()); + printf("ODP impl details: %s\n", odp_version_impl_str()); + printf("CPU model: %s\n", odp_cpu_model_str()); + printf("CPU freq (hz): %" PRIu64 "\n", odp_cpu_hz_max()); + printf("Cache line size: %i\n", odp_sys_cache_line_size()); + printf("Max CPU count: %i\n", odp_cpu_count()); + + /* Get default worker cpumask */ + if (args.cpu_count) + num_workers = args.cpu_count; + + num_workers = odp_cpumask_default_worker(&cpumask, num_workers); + args.cpu_count = num_workers; + + (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr)); + + printf("Worker threads: %i\n", num_workers); + printf("First CPU: %i\n", odp_cpumask_first(&cpumask)); + printf("CPU mask: %s\n\n", cpumaskstr); + + thread_tbl = calloc(sizeof(odph_odpthread_t), num_workers); + if (!thread_tbl) { + LOG_ERR("no memory for thread_tbl\n"); + return -1; + } + + shm = odp_shm_reserve("test_globals", + sizeof(test_globals_t), ODP_CACHE_LINE_SIZE, 0); + if (shm == ODP_SHM_INVALID) { + LOG_ERR("Shared memory reserve failed.\n"); + return -1; + } + + globals = odp_shm_addr(shm); + memset(globals, 0, sizeof(test_globals_t)); + memcpy(&globals->args, &args, sizeof(test_args_t)); + + /* + * Create event pool + */ + odp_pool_param_init(¶ms); + params.buf.size = sizeof(test_event_t); + params.buf.align = 0; + params.buf.num = EVENT_POOL_SIZE; + params.type = ODP_POOL_BUFFER; + + pool = odp_pool_create("event_pool", ¶ms); + + if (pool == ODP_POOL_INVALID) { + LOG_ERR("Pool create failed.\n"); + return -1; + } + globals->pool = pool; + + /* + * Create queues for schedule test + */ + for (i = 0; i < NUM_PRIOS; i++) { + char name[] = "sched_XX_YY"; + odp_queue_t queue; + odp_queue_param_t param; + int prio; + + if (i == HI_PRIO) + prio = ODP_SCHED_PRIO_HIGHEST; + else + prio = ODP_SCHED_PRIO_LOWEST; + + name[6] = '0' + (prio / 10); + name[7] = '0' + prio - (10 * (prio / 10)); + + odp_queue_param_init(¶m); + param.type = ODP_QUEUE_TYPE_SCHED; + param.sched.prio = prio; + param.sched.sync = args.sync_type; + param.sched.group = ODP_SCHED_GROUP_ALL; + + for (j = 0; j < args.prio[i].queues; j++) { + name[9] = '0' + j / 10; + name[10] = '0' + j - 10 * (j / 10); + + queue = odp_queue_create(name, ¶m); + + if (queue == ODP_QUEUE_INVALID) { + LOG_ERR("Scheduled queue create failed.\n"); + return -1; + } + + globals->queue[i][j] = queue; + } + } + + odp_barrier_init(&globals->barrier, num_workers); + + /* Create and launch worker threads */ + memset(&thr_params, 0, sizeof(thr_params)); + thr_params.thr_type = ODP_THREAD_WORKER; + thr_params.instance = instance; + thr_params.start = run_thread; + thr_params.arg = NULL; + odph_odpthreads_create(thread_tbl, &cpumask, &thr_params); + + /* Wait for worker threads to terminate */ + odph_odpthreads_join(thread_tbl); + free(thread_tbl); + + printf("ODP scheduling latency test complete\n\n"); + + for (i = 0; i < NUM_PRIOS; i++) { + odp_queue_t queue; + int num_queues; + + num_queues = args.prio[i].queues; + + for (j = 0; j < num_queues; j++) { + queue = globals->queue[i][j]; + ret += odp_queue_destroy(queue); + } + } + + ret += odp_shm_free(shm); + ret += odp_pool_destroy(pool); + ret += odp_term_local(); + ret += odp_term_global(instance); + + return ret; +}