diff --git a/arangosh/V8Client/SharedCounter.h b/arangosh/V8Client/BenchmarkCounter.h similarity index 91% rename from arangosh/V8Client/SharedCounter.h rename to arangosh/V8Client/BenchmarkCounter.h index 931cb2bfb6..5ab92802ba 100644 --- a/arangosh/V8Client/SharedCounter.h +++ b/arangosh/V8Client/BenchmarkCounter.h @@ -25,8 +25,8 @@ /// @author Copyright 2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// -#ifndef TRIAGENS_V8_CLIENT_SHARED_COUNTER_H -#define TRIAGENS_V8_CLIENT_SHARED_COUNTER_H 1 +#ifndef TRIAGENS_V8_CLIENT_BENCHMARK_COUNTER_H +#define TRIAGENS_V8_CLIENT_BENCHMARK_COUNTER_H 1 #include "Basics/Common.h" @@ -39,7 +39,7 @@ namespace triagens { namespace v8client { // ----------------------------------------------------------------------------- -// --SECTION-- class SharedCounter +// --SECTION-- class BenchmarkCounter // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- @@ -52,15 +52,15 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// template - class SharedCounter { + class BenchmarkCounter { public: //////////////////////////////////////////////////////////////////////////////// -/// @brief create a shared counter +/// @brief create the counter //////////////////////////////////////////////////////////////////////////////// - SharedCounter (T initialValue, const T maxValue) : + BenchmarkCounter (T initialValue, const T maxValue) : _mutex(), _value(initialValue), _maxValue(maxValue), @@ -68,10 +68,10 @@ namespace triagens { } //////////////////////////////////////////////////////////////////////////////// -/// @brief destroy a shared counter +/// @brief destroy the counter //////////////////////////////////////////////////////////////////////////////// - ~SharedCounter () { + ~BenchmarkCounter () { } //////////////////////////////////////////////////////////////////////////////// @@ -112,16 +112,22 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// T next (const T value) { + T realValue = value; + + if (value == 0) { + realValue = 1; + } + MUTEX_LOCKER(this->_mutex); T oldValue = _value; - if (oldValue + value > _maxValue) { + if (oldValue + realValue > _maxValue) { _value = _maxValue; return _maxValue - oldValue; } - _value += value; - return value; + _value += realValue; + return realValue; } //////////////////////////////////////////////////////////////////////////////// diff --git a/arangosh/V8Client/BenchmarkOperation.h b/arangosh/V8Client/BenchmarkOperation.h new file mode 100644 index 0000000000..e7c22a34b7 --- /dev/null +++ b/arangosh/V8Client/BenchmarkOperation.h @@ -0,0 +1,112 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief interface definition for benchmark operations +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2004-2012 triagens GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// +/// @author Jan Steemann +/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TRIAGENS_V8_CLIENT_BENCHMARK_OPERATION_H +#define TRIAGENS_V8_CLIENT_BENCHMARK_OPERATION_H 1 + +#include "Basics/Common.h" +#include "SimpleHttpClient/SimpleHttpClient.h" + +using namespace std; +using namespace triagens::basics; +using namespace triagens::httpclient; +using namespace triagens::rest; + +namespace triagens { + namespace v8client { + +// ----------------------------------------------------------------------------- +// --SECTION-- class BenchmarkOperation +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup V8Client +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief simple interface for benchmark operations +//////////////////////////////////////////////////////////////////////////////// + + struct BenchmarkOperation { + +//////////////////////////////////////////////////////////////////////////////// +/// @brief ctor, derived class can implemented something sensible +//////////////////////////////////////////////////////////////////////////////// + + BenchmarkOperation () { + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief dtor, derived class can implemented something sensible +//////////////////////////////////////////////////////////////////////////////// + + virtual ~BenchmarkOperation () { + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief return the URL of the operation to execute +//////////////////////////////////////////////////////////////////////////////// + + virtual const string& url () = 0; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief return the HTTP method of the operation to execute +//////////////////////////////////////////////////////////////////////////////// + + virtual const SimpleHttpClient::http_method type () = 0; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief return the payload (body) of the HTTP request to execute +//////////////////////////////////////////////////////////////////////////////// + + virtual const char* payload (size_t* length) = 0; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief return the HTTP headers for the oepration to execute +//////////////////////////////////////////////////////////////////////////////// + + virtual const map& headers () = 0; + + }; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +#endif + +// ----------------------------------------------------------------------------- +// --SECTION-- END-OF-FILE +// ----------------------------------------------------------------------------- + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" +// End: diff --git a/arangosh/V8Client/BenchmarkThread.h b/arangosh/V8Client/BenchmarkThread.h index 49931c249a..931780d89b 100644 --- a/arangosh/V8Client/BenchmarkThread.h +++ b/arangosh/V8Client/BenchmarkThread.h @@ -35,10 +35,11 @@ #include "Basics/ConditionLocker.h" #include "Basics/ConditionVariable.h" #include "Basics/Thread.h" -#include "V8Client/SharedCounter.h" #include "SimpleHttpClient/SimpleClient.h" #include "SimpleHttpClient/SimpleHttpClient.h" #include "SimpleHttpClient/GeneralClientConnection.h" +#include "V8Client/BenchmarkCounter.h" +#include "V8Client/BenchmarkOperation.h" using namespace std; using namespace triagens::basics; @@ -47,48 +48,7 @@ using namespace triagens::rest; namespace triagens { namespace v8client { - - struct BenchmarkRequest { - BenchmarkRequest (const char* url, - map params, - char* (*genFunc)(), - SimpleHttpClient::http_method type) : - url(url), - params(params), - genFunc(genFunc), - type(type), - ptr(0) { - }; - - ~BenchmarkRequest () { - if (ptr) { - TRI_Free(TRI_UNKNOWN_MEM_ZONE, ptr); - } - } - - void createString () { - if (genFunc == NULL) { - cerr << "invalid call to createString" << endl; - exit(EXIT_FAILURE); - } - ptr = (void*) genFunc(); - } - - char* getString () { - return (char*) ptr; - } - - size_t getStringLength () { - return strlen((char*) ptr); - } - - string url; - map params; - char* (*genFunc)(); - SimpleHttpClient::http_method type; - void* ptr; - }; - + // ----------------------------------------------------------------------------- // --SECTION-- class BenchmarkThread // ----------------------------------------------------------------------------- @@ -99,18 +59,18 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// class BenchmarkThread : public Thread { - public: - typedef BenchmarkRequest (*GenFunc)(); - BenchmarkThread (GenFunc generate, + public: + + BenchmarkThread (BenchmarkOperation* operation, ConditionVariable* condition, const unsigned long batchSize, - SharedCounter* operationsCounter, + BenchmarkCounter* operationsCounter, Endpoint* endpoint, const string& username, const string& password) : Thread("arangob"), - _generate(generate), + _operation(operation), _startCondition(condition), _batchSize(batchSize), _operationsCounter(operationsCounter), @@ -174,7 +134,8 @@ namespace triagens { } delete result; - + + // wait for start condition to be broadcasted { ConditionLocker guard(_startCondition); guard.wait(); @@ -187,125 +148,34 @@ namespace triagens { break; } - if (_batchSize == 1) { - executeRequest(); + if (_batchSize < 1) { + executeSingleRequest(); } else { - executeRequest(numOps); + executeBatchRequest(numOps); } } } - void executeRequest (const unsigned long numOperations) { - /* - PB_ArangoMessage messages; - PB_ArangoBatchMessage* batch; - PB_ArangoBlobRequest* blob; - PB_ArangoKeyValue* kv; - for (unsigned long i = 0; i < numOperations; ++i) { - BenchmarkRequest r = _generate(); - - batch = messages.add_messages(); - batch->set_type(PB_BLOB_REQUEST); - blob = batch->mutable_blobrequest(); - - blob->set_requesttype(getRequestType(r.type)); - blob->set_url(r.url); - - if (_useJson) { - r.createJson(blob); - blob->set_contenttype(PB_JSON_CONTENT); - } - else { - r.createString(blob); - blob->set_contenttype(PB_NO_CONTENT); - } - - for (map::const_iterator it = r.params.begin(); it != r.params.end(); ++it) { - kv = blob->add_values(); - kv->set_key((*it).first); - kv->set_value((*it).second); - } - } - - size_t messageSize = messages.ByteSize(); - char* message = new char[messageSize]; - - if (message == 0) { - cerr << "out of memory" << endl; - exit(EXIT_FAILURE); - } - - if (! messages.SerializeToArray(message, messageSize)) { - cerr << "out of memory" << endl; - exit(EXIT_FAILURE); - } - - map headerFields; - headerFields["Content-Type"] = BinaryMessage::getContentType(); - - //std::cout << "body length: " << messageSize << ", hash: " << TRI_FnvHashPointer(message, (size_t) messageSize) << "\n"; - - Timing timer(Timing::TI_WALLCLOCK); - SimpleHttpResult* result = _client->request(SimpleHttpClient::POST, "/_api/batch", message, (size_t) messageSize, headerFields); - _time += ((double) timer.time()) / 1000000.0; - delete[] message; - - if (result == 0) { - _operationsCounter->incFailures(); - return; - } - - if (_endpoint->isBinary()) { - PB_ArangoMessage returnMessage; - - if (! returnMessage.ParseFromArray(result->getBody().str().c_str(), result->getContentLength())) { - _operationsCounter->incFailures(); - } - else { - for (int i = 0; i < returnMessage.messages_size(); ++i) { - if (returnMessage.messages(i).blobresponse().status() >= 400) { - _operationsCounter->incFailures(); - } - } - } - } - else { - if (result->getHttpReturnCode() >= 400) { - _operationsCounter->incFailures(); - } - } - delete result; - */ + void executeBatchRequest (const unsigned long numOperations) { } - void executeRequest () { - BenchmarkRequest r = _generate(); - string url = r.url; - - bool found = false; - for (map::const_iterator i = r.params.begin(); i != r.params.end(); ++i) { - if (! found) { - url.append("?"); - found = true; - } - else { - url.append("&"); - } - - url.append((*i).first); - url.append("="); - url.append((*i).second); - } - - r.createString(); - - map headerFields; + void executeSingleRequest () { Timing timer(Timing::TI_WALLCLOCK); - SimpleHttpResult* result = _client->request(r.type, url, r.getString(), r.getStringLength(), headerFields); + const SimpleHttpClient::http_method type = _operation->type(); + const string url = _operation->url(); + size_t payloadLength = 0; + const char* payload = _operation->payload(&payloadLength); + const map& headers = _operation->headers(); + + SimpleHttpResult* result = _client->request(type, + url, + payload, + payloadLength, + headers); _time += ((double) timer.time()) / 1000000.0; if (result == 0) { @@ -341,7 +211,11 @@ namespace triagens { private: - GenFunc _generate; +//////////////////////////////////////////////////////////////////////////////// +/// @brief the operation to benchmark +//////////////////////////////////////////////////////////////////////////////// + + BenchmarkOperation* _operation; //////////////////////////////////////////////////////////////////////////////// /// @brief condition variable @@ -356,10 +230,10 @@ namespace triagens { const unsigned long _batchSize; //////////////////////////////////////////////////////////////////////////////// -/// @brief shared operations counter +/// @brief benchmark counter //////////////////////////////////////////////////////////////////////////////// - SharedCounter* _operationsCounter; + BenchmarkCounter* _operationsCounter; //////////////////////////////////////////////////////////////////////////////// /// @brief endpoint to use diff --git a/arangosh/V8Client/arangob.cpp b/arangosh/V8Client/arangob.cpp index ec5d63697b..e10a8b76df 100644 --- a/arangosh/V8Client/arangob.cpp +++ b/arangosh/V8Client/arangob.cpp @@ -44,8 +44,9 @@ #include "Rest/Initialise.h" #include "SimpleHttpClient/SimpleHttpClient.h" #include "SimpleHttpClient/SimpleHttpResult.h" +#include "V8Client/BenchmarkCounter.h" +#include "V8Client/BenchmarkOperation.h" #include "V8Client/BenchmarkThread.h" -#include "V8Client/SharedCounter.h" using namespace std; using namespace triagens::basics; @@ -85,12 +86,43 @@ static int Operations = 1000; /// @brief number of operations in one batch //////////////////////////////////////////////////////////////////////////////// -static int BatchSize = 1; +static int BatchSize = 0; //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// + +struct VersionTest : public BenchmarkOperation { + VersionTest () + : BenchmarkOperation () { + } + + ~VersionTest () { + } + + const string& url () { + static string url = "/_api/version"; + + return url; + } + + const SimpleHttpClient::http_method type () { + return SimpleHttpClient::GET; + } + + const char* payload (size_t* length) { + static const char* payload = ""; + *length = 0; + return payload; + } + + const map& headers () { + static const map headers; + return headers; + } +}; + // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- @@ -110,7 +142,7 @@ static void ParseProgramOptions (int argc, char* argv[]) { description ("concurrency", &Concurrency, "number of parallel connections") ("requests", &Operations, "total number of operations") - ("batch-size", &BatchSize, "number of operations in one batch") + ("batch-size", &BatchSize, "number of operations in one batch (0 disables batching") ; BaseClient.setupGeneral(description); @@ -136,60 +168,6 @@ static void ParseProgramOptions (int argc, char* argv[]) { /// @{ //////////////////////////////////////////////////////////////////////////////// -char* SEmpty () { - return TRI_DuplicateStringZ(TRI_UNKNOWN_MEM_ZONE, ""); -} - -BenchmarkRequest VersionFunc () { - map params; - BenchmarkRequest r("/_api/version", params, &SEmpty, SimpleHttpClient::GET); - - return r; -} - -char* SFunc1 () { - return TRI_DuplicateStringZ(TRI_UNKNOWN_MEM_ZONE, "{\"some value\" : 1}"); -} - -BenchmarkRequest InsertFunc1 () { - map params; - params["createCollection"] = "true"; - params["collection"] = "BenchmarkInsert"; - - BenchmarkRequest r("/_api/document", params, &SFunc1, SimpleHttpClient::POST); - - return r; -} - -char* SFunc2 () { - StringBuffer s(TRI_UNKNOWN_MEM_ZONE); - - s.appendChar('{'); - for (size_t i = 0; i < 1; ++i) { - s.appendText("\"some value"); - s.appendInteger(i); - s.appendText("\":"); - s.appendDecimal((double) i); - if (i < 0) { - s.appendChar(','); - } - } - - s.appendChar('}'); - - return TRI_DuplicateStringZ(TRI_UNKNOWN_MEM_ZONE, s.c_str()); -} - -BenchmarkRequest InsertFunc2 () { - map params; - params["createCollection"] = "true"; - params["collection"] = "BenchmarkInsert"; - - BenchmarkRequest r("/_api/document", params, &SFunc2, SimpleHttpClient::POST); - - return r; -} - //////////////////////////////////////////////////////////////////////////////// /// @brief main //////////////////////////////////////////////////////////////////////////////// @@ -220,16 +198,20 @@ int main (int argc, char* argv[]) { } - SharedCounter operationsCounter(0, (unsigned long) Operations); + BenchmarkCounter operationsCounter(0, (unsigned long) Operations); ConditionVariable startCondition; + VersionTest benchmarkOperation; + vector endpoints; vector threads; + + // start client threads for (int i = 0; i < Concurrency; ++i) { Endpoint* endpoint = Endpoint::clientFactory(BaseClient.endpointString()); endpoints.push_back(endpoint); - BenchmarkThread* thread = new BenchmarkThread(&InsertFunc2, + BenchmarkThread* thread = new BenchmarkThread(&benchmarkOperation, &startCondition, (unsigned long) BatchSize, &operationsCounter, @@ -240,12 +222,14 @@ int main (int argc, char* argv[]) { threads.push_back(thread); thread->start(); } - + + // give all threads a chance to start so they will not miss the broadcast usleep(500000); Timing timer(Timing::TI_WALLCLOCK); + // broadcast the start signal to all threads { ConditionLocker guard(&startCondition); guard.broadcast(); @@ -269,8 +253,8 @@ int main (int argc, char* argv[]) { } cout << "Total number of operations: " << Operations << ", batch size: " << BatchSize << ", concurrency level: " << Concurrency << endl; + cout << "Request/response duration: " << fixed << requestTime << " s" << endl; cout << "Total duration: " << fixed << time << " s" << endl; - cout << "Total request duration: " << fixed << requestTime << " s" << endl; cout << "Duration per operation: " << fixed << (time / Operations) << " s" << endl; cout << "Duration per operation per thread: " << fixed << (time / (double) Operations * (double) Concurrency) << " s" << endl << endl;