mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
This is intended to address currently flaky timeouts that are likely caused by the size of the prelude. I'm addressing a performance bottleneck in AnalyzeProgram with trace output. Trying to omit prelude traces reduces most trace output significantly, and I think it'll scale better as the prelude size increases. The basic mechanics here are: - In order to consistently track whether tracing is on, I've added a TraceStream class, explorer/interpreter/trace_stream.h. - The AST now has a num_prelude_declarations field, so that it's provided where the boundary is. - In order to mark where we try to skip prelude output, I've added calls to set_in_prelude in type_checker. - In exec_program, I just use num_prelude_declarations directly to skip over. - Everywhere checks TraceStream::is_enabled before printing, similar to the std::optional check that was previously used. This does add some timing output in order to better diagnose where slowness is coming from, when tracing. It also adds "verbose" targets to make it easier to get the trace output. So for example, here's a timing for zero.carbon: ``` Timings: - Parse: 13ms - AddPrelude: 25ms - AnalyzeProgram: 116ms - ExecProgram: 12ms ``` If I make a small change to just not set skipping_prelude (essentially getting back to current output): ``` - Parse: 13ms - AddPrelude: 25ms - AnalyzeProgram: 2359ms - ExecProgram: 57ms ``` Thus in this trivial example, I'm eliminating about 95% of the execution time. Note this approach could still be refined in a few ways: - We could add a flag to allow overriding in_prelude. It should be a small amount of work after this change. But it's a little consistent with how parser_debug works, that it won't print prelude output by default (unless there's an error). - Execution could skip messages involving initialization of globals declared in the prelude. This is a little noisy right now, but I don't think it's significant for performance because ExecProgram is tiny. - Once files are more separated, we should be able to change the num_prelude_declarations/set_in_prelude approach. Co-authored-by: Richard Smith <richard@metafoo.co.uk>
164 lines
5.5 KiB
C++
164 lines
5.5 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#include "explorer/main.h"
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <chrono>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "common/error.h"
|
|
#include "explorer/common/arena.h"
|
|
#include "explorer/common/nonnull.h"
|
|
#include "explorer/interpreter/exec_program.h"
|
|
#include "explorer/interpreter/trace_stream.h"
|
|
#include "explorer/syntax/parse.h"
|
|
#include "explorer/syntax/prelude.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/InitLLVM.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
namespace Carbon {
|
|
|
|
namespace cl = llvm::cl;
|
|
namespace path = llvm::sys::path;
|
|
|
|
auto ExplorerMain(int argc, char** argv, void* static_for_main_addr,
|
|
llvm::StringRef relative_prelude_path) -> int {
|
|
llvm::setBugReportMsg(
|
|
"Please report issues to "
|
|
"https://github.com/carbon-language/carbon-lang/issues and include the "
|
|
"crash backtrace.\n");
|
|
llvm::InitLLVM init_llvm(argc, argv);
|
|
|
|
// Printing to stderr should flush stdout. This is most noticeable when stderr
|
|
// is piped to stdout.
|
|
llvm::errs().tie(&llvm::outs());
|
|
|
|
cl::opt<std::string> input_file_name(cl::Positional, cl::desc("<input file>"),
|
|
cl::Required);
|
|
cl::opt<bool> parser_debug("parser_debug",
|
|
cl::desc("Enable debug output from the parser"));
|
|
cl::opt<std::string> trace_file_name(
|
|
"trace_file",
|
|
cl::desc("Output file for tracing; set to `-` to output to stdout."));
|
|
|
|
// Use the executable path as a base for the relative prelude path.
|
|
std::string exe =
|
|
llvm::sys::fs::getMainExecutable(argv[0], static_for_main_addr);
|
|
llvm::StringRef install_path = path::parent_path(exe);
|
|
llvm::SmallString<256> default_prelude_file(install_path);
|
|
path::append(default_prelude_file,
|
|
path::begin(relative_prelude_path, path::Style::posix),
|
|
path::end(relative_prelude_path));
|
|
std::string default_prelude_file_str(default_prelude_file);
|
|
cl::opt<std::string> prelude_file_name("prelude", cl::desc("<prelude file>"),
|
|
cl::init(default_prelude_file_str));
|
|
|
|
cl::ParseCommandLineOptions(argc, argv);
|
|
|
|
// Set up a stream for trace output.
|
|
std::unique_ptr<llvm::raw_ostream> scoped_trace_stream;
|
|
TraceStream trace_stream;
|
|
if (!trace_file_name.empty()) {
|
|
if (trace_file_name == "-") {
|
|
trace_stream.set_stream(&llvm::outs());
|
|
} else {
|
|
std::error_code err;
|
|
scoped_trace_stream =
|
|
std::make_unique<llvm::raw_fd_ostream>(trace_file_name, err);
|
|
if (err) {
|
|
llvm::errs() << err.message() << "\n";
|
|
return EXIT_FAILURE;
|
|
}
|
|
trace_stream.set_stream(scoped_trace_stream.get());
|
|
}
|
|
}
|
|
|
|
auto time_start = std::chrono::system_clock::now();
|
|
|
|
Arena arena;
|
|
AST ast;
|
|
if (ErrorOr<AST> parse_result = Parse(&arena, input_file_name, parser_debug);
|
|
parse_result.ok()) {
|
|
ast = *std::move(parse_result);
|
|
} else {
|
|
llvm::errs() << "SYNTAX ERROR: " << parse_result.error() << "\n";
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
auto time_after_parse = std::chrono::system_clock::now();
|
|
|
|
AddPrelude(prelude_file_name, &arena, &ast.declarations,
|
|
&ast.num_prelude_declarations);
|
|
|
|
auto time_after_prelude = std::chrono::system_clock::now();
|
|
|
|
// Semantically analyze the parsed program.
|
|
if (ErrorOr<AST> analyze_result = AnalyzeProgram(&arena, ast, &trace_stream);
|
|
analyze_result.ok()) {
|
|
ast = *std::move(analyze_result);
|
|
} else {
|
|
llvm::errs() << "COMPILATION ERROR: " << analyze_result.error() << "\n";
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
auto time_after_analyze = std::chrono::system_clock::now();
|
|
|
|
// Run the program.
|
|
auto ret = EXIT_SUCCESS;
|
|
if (ErrorOr<int> exec_result = ExecProgram(&arena, ast, &trace_stream);
|
|
exec_result.ok()) {
|
|
// Print the return code to stdout.
|
|
llvm::outs() << "result: " << *exec_result << "\n";
|
|
|
|
// When there's a dedicated trace file, print the return code to it too.
|
|
if (scoped_trace_stream) {
|
|
trace_stream << "result: " << *exec_result << "\n";
|
|
}
|
|
} else {
|
|
llvm::errs() << "RUNTIME ERROR: " << exec_result.error() << "\n";
|
|
ret = EXIT_FAILURE;
|
|
}
|
|
|
|
auto time_after_exec = std::chrono::system_clock::now();
|
|
|
|
if (trace_stream.is_enabled()) {
|
|
trace_stream << "Timings:\n"
|
|
<< "- Parse: "
|
|
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
time_after_parse - time_start)
|
|
.count()
|
|
<< "ms\n"
|
|
<< "- AddPrelude: "
|
|
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
time_after_prelude - time_after_parse)
|
|
.count()
|
|
<< "ms\n"
|
|
<< "- AnalyzeProgram: "
|
|
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
time_after_analyze - time_after_prelude)
|
|
.count()
|
|
<< "ms\n"
|
|
<< "- ExecProgram: "
|
|
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
time_after_exec - time_after_analyze)
|
|
.count()
|
|
<< "ms\n";
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
} // namespace Carbon
|